using System; 
 | 
using System.Collections.Generic; 
 | 
using System.Globalization; 
 | 
using System.Linq; 
 | 
using System.Text; 
 | 
using Jace.Execution; 
 | 
using Jace.Operations; 
 | 
using Jace.Tokenizer; 
 | 
using Jace.Util; 
 | 
  
 | 
namespace Jace 
 | 
{ 
 | 
    public delegate TResult DynamicFunc<T, TResult>(params T[] values); 
 | 
  
 | 
    /// <summary> 
 | 
    /// The CalculationEngine class is the main class of Jace.NET to convert strings containing 
 | 
    /// mathematical formulas into .NET Delegates and to calculate the result. 
 | 
    /// It can be configured to run in a number of modes based on the constructor parameters choosen. 
 | 
    /// </summary> 
 | 
    public class CalculationEngine 
 | 
    { 
 | 
        private readonly IExecutor executor; 
 | 
        private readonly Optimizer optimizer; 
 | 
        private readonly CultureInfo cultureInfo; 
 | 
        private readonly MemoryCache<string, Func<IDictionary<string, double>, double>> executionFormulaCache; 
 | 
        private readonly bool cacheEnabled; 
 | 
        private readonly bool optimizerEnabled; 
 | 
        private readonly bool caseSensitive; 
 | 
  
 | 
        private readonly Random random; 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new instance of the <see cref="CalculationEngine"/> class with 
 | 
        /// default parameters. 
 | 
        /// </summary> 
 | 
        public CalculationEngine() 
 | 
            : this(new JaceOptions()) 
 | 
        { 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new instance of the <see cref="CalculationEngine"/> class. The dynamic compiler 
 | 
        /// is used for formula execution and the optimizer and cache are enabled. 
 | 
        /// </summary> 
 | 
        /// <param name="cultureInfo"> 
 | 
        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. 
 | 
        /// </param> 
 | 
        public CalculationEngine(CultureInfo cultureInfo) 
 | 
            : this(new JaceOptions() { CultureInfo = cultureInfo }) 
 | 
        { 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new instance of the <see cref="CalculationEngine"/> class. The optimizer and  
 | 
        /// cache are enabled. 
 | 
        /// </summary> 
 | 
        /// <param name="cultureInfo"> 
 | 
        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. 
 | 
        /// </param> 
 | 
        /// <param name="executionMode">The execution mode that must be used for formula execution.</param> 
 | 
        public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode) 
 | 
            : this (new JaceOptions() { CultureInfo = cultureInfo, ExecutionMode = executionMode }) 
 | 
        { 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new instance of the <see cref="CalculationEngine"/> class. 
 | 
        /// </summary> 
 | 
        /// <param name="cultureInfo"> 
 | 
        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. 
 | 
        /// </param> 
 | 
        /// <param name="executionMode">The execution mode that must be used for formula execution.</param> 
 | 
        /// <param name="cacheEnabled">Enable or disable caching of mathematical formulas.</param> 
 | 
        /// <param name="optimizerEnabled">Enable or disable optimizing of formulas.</param> 
 | 
        /// <param name="adjustVariableCaseEnabled">Enable or disable auto lowercasing of variables.</param> 
 | 
        [Obsolete] 
 | 
        public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode, bool cacheEnabled, bool optimizerEnabled, bool adjustVariableCaseEnabled) 
 | 
            : this(new JaceOptions() { CultureInfo = cultureInfo, ExecutionMode = executionMode, CacheEnabled = cacheEnabled, OptimizerEnabled = optimizerEnabled, CaseSensitive = !adjustVariableCaseEnabled }) 
 | 
        { 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new instance of the <see cref="CalculationEngine"/> class. 
 | 
        /// </summary> 
 | 
        /// <param name="cultureInfo"> 
 | 
        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. 
 | 
        /// </param> 
 | 
        /// <param name="executionMode">The execution mode that must be used for formula execution.</param> 
 | 
        /// <param name="cacheEnabled">Enable or disable caching of mathematical formulas.</param> 
 | 
        /// <param name="optimizerEnabled">Enable or disable optimizing of formulas.</param> 
 | 
        /// <param name="adjustVariableCaseEnabled">Enable or disable converting to lower case.</param> 
 | 
        /// <param name="defaultFunctions">Enable or disable the default functions.</param> 
 | 
        /// <param name="defaultConstants">Enable or disable the default constants.</param> 
 | 
        /// <param name="cacheMaximumSize">Configure the maximum cache size for mathematical formulas.</param> 
 | 
        /// <param name="cacheReductionSize">Configure the cache reduction size for mathematical formulas.</param> 
 | 
        [Obsolete] 
 | 
        public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode, bool cacheEnabled, 
 | 
            bool optimizerEnabled, bool adjustVariableCaseEnabled, bool defaultFunctions, bool defaultConstants, int cacheMaximumSize, int cacheReductionSize) 
 | 
            : this(new JaceOptions() { CultureInfo = cultureInfo, ExecutionMode = executionMode, CacheEnabled = cacheEnabled, OptimizerEnabled = optimizerEnabled, 
 | 
                CaseSensitive = !adjustVariableCaseEnabled, DefaultFunctions = defaultFunctions, DefaultConstants = defaultConstants,  
 | 
                CacheMaximumSize = cacheMaximumSize, CacheReductionSize = cacheReductionSize }) 
 | 
        { 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Creates a new instance of the <see cref="CalculationEngine"/> class. 
 | 
        /// </summary> 
 | 
        /// <param name="options">The <see cref="JaceOptions"/> to configure the behaviour of the engine.</param> 
 | 
        public CalculationEngine(JaceOptions options) 
 | 
        { 
 | 
            this.executionFormulaCache = new MemoryCache<string, Func<IDictionary<string, double>, double>>(options.CacheMaximumSize, options.CacheReductionSize); 
 | 
            this.FunctionRegistry = new FunctionRegistry(false); 
 | 
            this.ConstantRegistry = new ConstantRegistry(false); 
 | 
            this.cultureInfo = options.CultureInfo; 
 | 
            this.cacheEnabled = options.CacheEnabled; 
 | 
            this.optimizerEnabled = options.OptimizerEnabled; 
 | 
            this.caseSensitive = options.CaseSensitive; 
 | 
  
 | 
            this.random = new Random(); 
 | 
  
 | 
            if (options.ExecutionMode == ExecutionMode.Interpreted) 
 | 
                executor = new Interpreter(caseSensitive); 
 | 
            else if (options.ExecutionMode == ExecutionMode.Compiled) 
 | 
                executor = new DynamicCompiler(caseSensitive); 
 | 
            else 
 | 
                throw new ArgumentException(string.Format("Unsupported execution mode \"{0}\".", options.ExecutionMode), 
 | 
                    "executionMode"); 
 | 
  
 | 
            optimizer = new Optimizer(new Interpreter()); // We run the optimizer with the interpreter  
 | 
  
 | 
            // Register the default constants of Jace.NET into the constant registry 
 | 
            if (options.DefaultConstants) 
 | 
                RegisterDefaultConstants(); 
 | 
  
 | 
            // Register the default functions of Jace.NET into the function registry 
 | 
            if (options.DefaultFunctions) 
 | 
                RegisterDefaultFunctions(); 
 | 
        } 
 | 
  
 | 
        internal IFunctionRegistry FunctionRegistry { get; private set; } 
 | 
  
 | 
        internal IConstantRegistry ConstantRegistry { get; private set; } 
 | 
  
 | 
        public IEnumerable<FunctionInfo> Functions { get { return FunctionRegistry; } } 
 | 
  
 | 
        public IEnumerable<ConstantInfo> Constants { get { return ConstantRegistry; } } 
 | 
  
 | 
        public double Calculate(string formulaText) 
 | 
        { 
 | 
            return Calculate(formulaText, new Dictionary<string, double>()); 
 | 
        } 
 | 
  
 | 
        public double Calculate(string formulaText, IDictionary<string, double> variables) 
 | 
        { 
 | 
            if (string.IsNullOrEmpty(formulaText)) 
 | 
                throw new ArgumentNullException("formulaText"); 
 | 
  
 | 
            if (variables == null) 
 | 
                throw new ArgumentNullException("variables"); 
 | 
  
 | 
            if (!caseSensitive) 
 | 
            { 
 | 
                variables = EngineUtil.ConvertVariableNamesToLowerCase(variables); 
 | 
            } 
 | 
            VerifyVariableNames(variables); 
 | 
  
 | 
            // Add the reserved variables to the dictionary 
 | 
            foreach (ConstantInfo constant in ConstantRegistry) 
 | 
                variables.Add(constant.ConstantName, constant.Value); 
 | 
  
 | 
            if (IsInFormulaCache(formulaText, null, out var function)) 
 | 
            { 
 | 
                return function(variables); 
 | 
            } 
 | 
            else 
 | 
            { 
 | 
                Operation operation = BuildAbstractSyntaxTree(formulaText, new ConstantRegistry(caseSensitive)); 
 | 
                function = BuildFormula(formulaText, null, operation); 
 | 
                return function(variables); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public FormulaBuilder Formula(string formulaText) 
 | 
        { 
 | 
            if (string.IsNullOrEmpty(formulaText)) 
 | 
                throw new ArgumentNullException("formulaText"); 
 | 
  
 | 
            return new FormulaBuilder(formulaText, caseSensitive, this); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Build a .NET func for the provided formula. 
 | 
        /// </summary> 
 | 
        /// <param name="formulaText">The formula that must be converted into a .NET func.</param> 
 | 
        /// <returns>A .NET func for the provided formula.</returns> 
 | 
        public Func<IDictionary<string, double>, double> Build(string formulaText) 
 | 
        { 
 | 
            if (string.IsNullOrEmpty(formulaText)) 
 | 
                throw new ArgumentNullException("formulaText"); 
 | 
  
 | 
            if (IsInFormulaCache(formulaText, null, out var result)) 
 | 
            { 
 | 
                return result; 
 | 
            } 
 | 
            else 
 | 
            { 
 | 
                Operation operation = BuildAbstractSyntaxTree(formulaText, new ConstantRegistry(caseSensitive)); 
 | 
                return BuildFormula(formulaText, null, operation); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Build a .NET func for the provided formula. 
 | 
        /// </summary> 
 | 
        /// <param name="formulaText">The formula that must be converted into a .NET func.</param> 
 | 
        /// <param name="constants">Constant values for variables defined into the formula. They variables will be replaced by the constant value at pre-compilation time.</param> 
 | 
        /// <returns>A .NET func for the provided formula.</returns> 
 | 
        public Func<IDictionary<string, double>, double> Build(string formulaText, IDictionary<string, double> constants) 
 | 
        { 
 | 
            if (string.IsNullOrEmpty(formulaText)) 
 | 
                throw new ArgumentNullException("formulaText"); 
 | 
  
 | 
  
 | 
            ConstantRegistry compiledConstants = new ConstantRegistry(caseSensitive); 
 | 
            if (constants != null) 
 | 
            { 
 | 
                foreach (var constant in constants) 
 | 
                { 
 | 
                    compiledConstants.RegisterConstant(constant.Key, constant.Value); 
 | 
                } 
 | 
            } 
 | 
  
 | 
            if (IsInFormulaCache(formulaText, compiledConstants, out var result)) 
 | 
            { 
 | 
                return result; 
 | 
            } 
 | 
            else 
 | 
            { 
 | 
                Operation operation = BuildAbstractSyntaxTree(formulaText, compiledConstants); 
 | 
                return BuildFormula(formulaText, compiledConstants,  operation); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);  
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a function to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="function">The implemenation of the function.</param> 
 | 
        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param> 
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        public void AddFunction(string functionName, DynamicFunc<double, double> functionDelegate, bool isIdempotent = true) 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction(functionName, functionDelegate, isIdempotent, true); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Add a constant to the calculation engine. 
 | 
        /// </summary> 
 | 
        /// <param name="constantName">The name of the constant. This name can be used in mathematical formulas.</param> 
 | 
        /// <param name="value">The value of the constant.</param> 
 | 
        public void AddConstant(string constantName, double value) 
 | 
        { 
 | 
            ConstantRegistry.RegisterConstant(constantName, value); 
 | 
        } 
 | 
  
 | 
        private void RegisterDefaultFunctions() 
 | 
        { 
 | 
            FunctionRegistry.RegisterFunction("sin", (Func<double, double>)Math.Sin, true, false); 
 | 
            FunctionRegistry.RegisterFunction("cos", (Func<double, double>)Math.Cos, true, false); 
 | 
            FunctionRegistry.RegisterFunction("csc", (Func<double, double>)MathUtil.Csc, true, false); 
 | 
            FunctionRegistry.RegisterFunction("sec", (Func<double, double>)MathUtil.Sec, true, false); 
 | 
            FunctionRegistry.RegisterFunction("asin", (Func<double, double>)Math.Asin, true, false); 
 | 
            FunctionRegistry.RegisterFunction("acos", (Func<double, double>)Math.Acos, true, false); 
 | 
            FunctionRegistry.RegisterFunction("tan", (Func<double, double>)Math.Tan, true, false); 
 | 
            FunctionRegistry.RegisterFunction("cot", (Func<double, double>)MathUtil.Cot, true, false); 
 | 
            FunctionRegistry.RegisterFunction("atan", (Func<double, double>)Math.Atan, true, false); 
 | 
            FunctionRegistry.RegisterFunction("acot", (Func<double, double>)MathUtil.Acot, true, false); 
 | 
            FunctionRegistry.RegisterFunction("loge", (Func<double, double>)Math.Log, true, false); 
 | 
            FunctionRegistry.RegisterFunction("log10", (Func<double, double>)Math.Log10, true, false); 
 | 
            FunctionRegistry.RegisterFunction("logn", (Func<double, double, double>)((a, b) => Math.Log(a, b)), true, false); 
 | 
            FunctionRegistry.RegisterFunction("sqrt", (Func<double, double>)Math.Sqrt, true, false); 
 | 
            FunctionRegistry.RegisterFunction("abs", (Func<double, double>)Math.Abs, true, false); 
 | 
            FunctionRegistry.RegisterFunction("if", (Func<double, double, double, double>)((a, b, c) => (a != 0.0 ? b : c)), true, false); 
 | 
            FunctionRegistry.RegisterFunction("ifless", (Func<double, double, double, double, double>)((a, b, c, d) => (a < b ? c : d)), true, false); 
 | 
            FunctionRegistry.RegisterFunction("ifmore", (Func<double, double, double, double, double>)((a, b, c, d) => (a > b ? c : d)), true, false); 
 | 
            FunctionRegistry.RegisterFunction("ifequal", (Func<double, double, double, double, double>)((a, b, c, d) => (a == b ? c : d)), true, false); 
 | 
            FunctionRegistry.RegisterFunction("ceiling", (Func<double, double>)Math.Ceiling, true, false); 
 | 
            FunctionRegistry.RegisterFunction("floor", (Func<double, double>)Math.Floor, true, false); 
 | 
            FunctionRegistry.RegisterFunction("truncate", (Func<double, double>)Math.Truncate, true, false); 
 | 
            FunctionRegistry.RegisterFunction("round", (Func<double, double>)Math.Round, true, false); 
 | 
  
 | 
            // Dynamic based arguments Functions 
 | 
            FunctionRegistry.RegisterFunction("max",  (DynamicFunc<double, double>)((a) => a.Max()), true, false); 
 | 
            FunctionRegistry.RegisterFunction("min", (DynamicFunc<double, double>)((a) => a.Min()), true, false); 
 | 
            FunctionRegistry.RegisterFunction("avg", (DynamicFunc<double, double>)((a) => a.Average()), true, false); 
 | 
            FunctionRegistry.RegisterFunction("median", (DynamicFunc<double, double>)((a) => MathExtended.Median(a)), true, false); 
 | 
  
 | 
            // Non Idempotent Functions 
 | 
            FunctionRegistry.RegisterFunction("random", (Func<double>)random.NextDouble, false, false); 
 | 
        } 
 | 
  
 | 
        private void RegisterDefaultConstants() 
 | 
        { 
 | 
            ConstantRegistry.RegisterConstant("e", Math.E, false); 
 | 
            ConstantRegistry.RegisterConstant("pi", Math.PI, false); 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Build the abstract syntax tree for a given formula. The formula string will 
 | 
        /// be first tokenized. 
 | 
        /// </summary> 
 | 
        /// <param name="formulaText">A string containing the mathematical formula that must be converted  
 | 
        /// into an abstract syntax tree.</param> 
 | 
        /// <returns>The abstract syntax tree of the formula.</returns> 
 | 
        private Operation BuildAbstractSyntaxTree(string formulaText, ConstantRegistry compiledConstants) 
 | 
        { 
 | 
            TokenReader tokenReader = new TokenReader(cultureInfo); 
 | 
            List<Token> tokens = tokenReader.Read(formulaText); 
 | 
             
 | 
            AstBuilder astBuilder = new AstBuilder(FunctionRegistry, caseSensitive, compiledConstants); 
 | 
            Operation operation = astBuilder.Build(tokens); 
 | 
  
 | 
            if (optimizerEnabled) 
 | 
                return optimizer.Optimize(operation, this.FunctionRegistry, this.ConstantRegistry); 
 | 
            else 
 | 
                return operation; 
 | 
        } 
 | 
  
 | 
        private Func<IDictionary<string, double>, double> BuildFormula(string formulaText, ConstantRegistry compiledConstants, Operation operation) 
 | 
        { 
 | 
            return executionFormulaCache.GetOrAdd(GenerateFormulaCacheKey(formulaText, compiledConstants), v => executor.BuildFormula(operation, this.FunctionRegistry, this.ConstantRegistry)); 
 | 
        } 
 | 
  
 | 
        private bool IsInFormulaCache(string formulaText, ConstantRegistry compiledConstants, out Func<IDictionary<string, double>, double> function) 
 | 
        { 
 | 
            function = null; 
 | 
            return cacheEnabled && executionFormulaCache.TryGetValue(GenerateFormulaCacheKey(formulaText, compiledConstants), out function); 
 | 
        } 
 | 
  
 | 
        private string GenerateFormulaCacheKey(string formulaText, ConstantRegistry compiledConstants) 
 | 
        { 
 | 
            return (compiledConstants != null && compiledConstants.Any()) ? $"{formulaText}@{String.Join(",", compiledConstants?.Select(x => $"{x.ConstantName}:{x.Value}"))}" : formulaText; 
 | 
        } 
 | 
  
 | 
        /// <summary> 
 | 
        /// Verify a collection of variables to ensure that all the variable names are valid. 
 | 
        /// Users are not allowed to overwrite reserved variables or use function names as variables. 
 | 
        /// If an invalid variable is detected an exception is thrown. 
 | 
        /// </summary> 
 | 
        /// <param name="variables">The colletion of variables that must be verified.</param> 
 | 
        internal void VerifyVariableNames(IDictionary<string, double> variables) 
 | 
        { 
 | 
            foreach (string variableName in variables.Keys) 
 | 
            { 
 | 
                if(ConstantRegistry.IsConstantName(variableName) && !ConstantRegistry.GetConstantInfo(variableName).IsOverWritable) 
 | 
                    throw new ArgumentException(string.Format("The name \"{0}\" is a reservered variable name that cannot be overwritten.", variableName), "variables"); 
 | 
  
 | 
                if (FunctionRegistry.IsFunctionName(variableName)) 
 | 
                    throw new ArgumentException(string.Format("The name \"{0}\" is a function name. Parameters cannot have this name.", variableName), "variables"); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
} 
 |