2020-09-10 05:01:30 +02:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
2021-01-19 18:04:15 +01:00
using System.Collections.Generic ;
2020-09-10 05:01:30 +02:00
using System.Globalization ;
2022-08-26 17:40:15 +02:00
using System.Text.RegularExpressions ;
2020-09-10 05:01:30 +02:00
using Mages.Core ;
2021-01-20 11:38:52 +01:00
namespace Microsoft.PowerToys.Run.Plugin.Calculator
2020-09-10 05:01:30 +02:00
{
public class CalculateEngine
{
2021-01-19 18:04:15 +01:00
private readonly Engine _magesEngine = new Engine ( new Configuration
{
Scope = new Dictionary < string , object >
{
{ "e" , Math . E } , // e is not contained in the default mages engine
} ,
} ) ;
2020-09-10 05:01:30 +02:00
public const int RoundingDigits = 10 ;
2022-03-14 16:46:08 +01:00
/// <summary>
/// Interpret
/// </summary>
/// <param name="cultureInfo">Use CultureInfo.CurrentCulture if something is user facing</param>
2022-06-02 11:44:12 +02:00
public CalculateResult Interpret ( string input , CultureInfo cultureInfo , out string error )
2020-09-10 05:01:30 +02:00
{
2022-06-02 11:44:12 +02:00
error = default ;
2020-09-10 05:01:30 +02:00
if ( ! CalculateHelper . InputValid ( input ) )
{
return default ;
2022-08-26 17:40:15 +02:00
}
// check for division by zero
// We check if the string contains a slash followed by space (optional) and zero. Whereas the zero must not followed by dot or comma as this indicates a number with decimal digits.
if ( new Regex ( "\\/\\s*0(?![,\\.])" ) . Match ( input ) . Success )
{
error = Properties . Resources . wox_plugin_calculator_division_by_zero ;
return default ;
2020-09-10 05:01:30 +02:00
}
2021-12-28 04:59:31 -08:00
// mages has quirky log representation
// mage has log == ln vs log10
input = input .
Replace ( "log(" , "log10(" , true , CultureInfo . CurrentCulture ) .
Replace ( "ln(" , "log(" , true , CultureInfo . CurrentCulture ) ;
2023-03-10 21:48:04 +01:00
input = CalculateHelper . FixHumanMultiplicationExpressions ( input ) ;
2020-09-10 05:01:30 +02:00
var result = _magesEngine . Interpret ( input ) ;
// This could happen for some incorrect queries, like pi(2)
if ( result = = null )
{
2022-06-02 11:44:12 +02:00
error = Properties . Resources . wox_plugin_calculator_expression_not_complete ;
2020-09-10 05:01:30 +02:00
return default ;
}
result = TransformResult ( result ) ;
2022-06-02 11:44:12 +02:00
if ( result is string )
{
error = result as string ;
return default ;
}
2020-09-10 05:01:30 +02:00
if ( string . IsNullOrEmpty ( result ? . ToString ( ) ) )
{
return default ;
}
var decimalResult = Convert . ToDecimal ( result , cultureInfo ) ;
2020-10-08 08:57:17 -07:00
var roundedResult = Round ( decimalResult ) ;
2020-09-10 05:01:30 +02:00
return new CalculateResult ( )
{
Result = decimalResult ,
RoundedResult = roundedResult ,
} ;
}
2020-10-08 08:57:17 -07:00
public static decimal Round ( decimal value )
{
return Math . Round ( value , RoundingDigits , MidpointRounding . AwayFromZero ) ;
}
2022-06-02 11:44:12 +02:00
private static dynamic TransformResult ( object result )
2020-09-10 05:01:30 +02:00
{
if ( result . ToString ( ) = = "NaN" )
{
return Properties . Resources . wox_plugin_calculator_not_a_number ;
}
if ( result is Function )
{
return Properties . Resources . wox_plugin_calculator_expression_not_complete ;
}
2022-06-02 11:44:12 +02:00
if ( result is double [ , ] )
{
// '[10,10]' is interpreted as array by mages engine
return Properties . Resources . wox_plugin_calculator_double_array_returned ;
}
2020-09-10 05:01:30 +02:00
return result ;
}
}
}