Decompiled source of Adletec Sonic v1.6.0

BepInEx/core/Adletec.Sonic/netstandard2.0/Adletec.Sonic.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using Adletec.Sonic.Execution;
using Adletec.Sonic.Operations;
using Adletec.Sonic.Parsing;
using Adletec.Sonic.Parsing.Tokenizing;
using Adletec.Sonic.Util;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("adletec Software Engineering")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("\n      sonic is a rapid evaluation engine for mathematical expressions. It can parse and execute strings containing mathematical expressions. It started as a fork of the dormant Jace.NET project.\n    ")]
[assembly: AssemblyFileVersion("1.6.0")]
[assembly: AssemblyInformationalVersion("1.0.0+51c5ceee8d6280c77fcafed9938f957b5e0baa86")]
[assembly: AssemblyProduct("sonic")]
[assembly: AssemblyTitle("Adletec.Sonic")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/adletec/sonic")]
[assembly: AssemblyVersion("1.6.0.0")]
namespace Adletec.Sonic
{
	public enum DataType
	{
		Integer,
		FloatingPoint
	}
	public delegate TResult DynamicFunc<T, TResult>(params T[] values);
	public class Evaluator : IEvaluator
	{
		private readonly TokenReader tokenReader;

		private readonly Optimizer optimizer;

		private readonly IExecutor executor;

		private readonly ExpressionValidator expressionValidator;

		private readonly VariableValidator variableValidator;

		private readonly MemoryCache<string, Func<IDictionary<string, double>, double>> executionFormulaCache;

		private readonly bool cacheEnabled;

		private readonly bool optimizerEnabled;

		private readonly bool guardedModeEnabled;

		private readonly bool validationEnabled;

		private readonly Random random;

		internal IFunctionRegistry FunctionRegistry { get; }

		internal IConstantRegistry ConstantRegistry { get; }

		public IEnumerable<FunctionInfo> Functions => FunctionRegistry;

		public IEnumerable<ConstantInfo> Constants => ConstantRegistry;

		public static Evaluator CreateWithDefaults()
		{
			return new EvaluatorBuilder().Build();
		}

		public static EvaluatorBuilder Create()
		{
			return new EvaluatorBuilder();
		}

		internal Evaluator(EvaluatorBuilder options)
		{
			bool caseSensitive = options.CaseSensitive;
			executionFormulaCache = new MemoryCache<string, Func<IDictionary<string, double>, double>>(options.CacheMaximumSize, options.CacheReductionSize);
			FunctionRegistry = new FunctionRegistry(caseSensitive, options.GuardedModeEnabled);
			ConstantRegistry = new ConstantRegistry(caseSensitive, options.GuardedModeEnabled);
			CultureInfo cultureInfo = options.CultureInfo;
			char argumentSeparator = options.ArgumentSeparator;
			tokenReader = new TokenReader(cultureInfo, argumentSeparator);
			cacheEnabled = options.CacheEnabled;
			optimizerEnabled = options.OptimizerEnabled;
			guardedModeEnabled = options.GuardedModeEnabled;
			validationEnabled = options.ValidationEnabled;
			random = new Random();
			switch (options.ExecutionMode)
			{
			case ExecutionMode.Interpreted:
				executor = new Interpreter(caseSensitive, guardedModeEnabled);
				break;
			case ExecutionMode.Compiled:
				executor = new DynamicCompiler(caseSensitive, guardedModeEnabled);
				break;
			default:
				throw new ArgumentException($"Unsupported execution mode \"{options.ExecutionMode}\".", "ExecutionMode");
			}
			optimizer = new Optimizer(new Interpreter());
			if (options.DefaultConstants)
			{
				RegisterDefaultConstants();
			}
			if (options.DefaultFunctions)
			{
				RegisterDefaultFunctions();
			}
			if (options.Constants != null)
			{
				foreach (ConstantDraft constant in options.Constants)
				{
					if (guardedModeEnabled && FunctionRegistry.IsFunctionName(constant.Name))
					{
						throw new ArgumentException("The constant name cannot be the same as a function name.");
					}
					ConstantRegistry.RegisterConstant(constant.Name, constant.Value);
				}
			}
			if (options.Functions != null)
			{
				foreach (FunctionDraft function in options.Functions)
				{
					if (guardedModeEnabled && ConstantRegistry.IsConstantName(function.Name))
					{
						throw new ArgumentException("The function name cannot be the same as a constant name.");
					}
					FunctionRegistry.RegisterFunction(function.Name, function.Function, function.IsIdempotent);
				}
			}
			expressionValidator = new ExpressionValidator(FunctionRegistry, cultureInfo);
			variableValidator = new VariableValidator();
		}

		public double Evaluate(string expression)
		{
			return Evaluate(expression, new Dictionary<string, double>());
		}

		public double Evaluate(string expression, IDictionary<string, double> variables)
		{
			return CreateDelegate(expression)(variables);
		}

		public Func<IDictionary<string, double>, double> CreateDelegate(string expression)
		{
			if (string.IsNullOrEmpty(expression))
			{
				throw new ArgumentNullException("expression");
			}
			if (IsInFormulaCache(expression, out var function))
			{
				return function;
			}
			Operation operation = BuildAbstractSyntaxTree(expression, ConstantRegistry, optimizerEnabled, validationEnabled);
			return BuildEvaluator(expression, operation);
		}

		public void Validate(string expression)
		{
			BuildAbstractSyntaxTree(expression, ConstantRegistry, optimize: false, validate: true);
		}

		public void Validate(string expression, IList<string> variables)
		{
			Operation operation = BuildAbstractSyntaxTree(expression, ConstantRegistry, optimizerEnabled, validate: true);
			variableValidator.Validate(operation, variables);
		}

		private void RegisterDefaultFunctions()
		{
			FunctionRegistry.RegisterFunction("sin", new Func<double, double>(Math.Sin), isIdempotent: true);
			FunctionRegistry.RegisterFunction("cos", new Func<double, double>(Math.Cos), isIdempotent: true);
			FunctionRegistry.RegisterFunction("csc", new Func<double, double>(MathUtil.Csc), isIdempotent: true);
			FunctionRegistry.RegisterFunction("sec", new Func<double, double>(MathUtil.Sec), isIdempotent: true);
			FunctionRegistry.RegisterFunction("asin", new Func<double, double>(Math.Asin), isIdempotent: true);
			FunctionRegistry.RegisterFunction("acos", new Func<double, double>(Math.Acos), isIdempotent: true);
			FunctionRegistry.RegisterFunction("tan", new Func<double, double>(Math.Tan), isIdempotent: true);
			FunctionRegistry.RegisterFunction("cot", new Func<double, double>(MathUtil.Cot), isIdempotent: true);
			FunctionRegistry.RegisterFunction("atan", new Func<double, double>(Math.Atan), isIdempotent: true);
			FunctionRegistry.RegisterFunction("acot", new Func<double, double>(MathUtil.Acot), isIdempotent: true);
			FunctionRegistry.RegisterFunction("loge", new Func<double, double>(Math.Log), isIdempotent: true);
			FunctionRegistry.RegisterFunction("log10", new Func<double, double>(Math.Log10), isIdempotent: true);
			FunctionRegistry.RegisterFunction("logn", new Func<double, double, double>(Math.Log), isIdempotent: true);
			FunctionRegistry.RegisterFunction("sqrt", new Func<double, double>(Math.Sqrt), isIdempotent: true);
			FunctionRegistry.RegisterFunction("abs", new Func<double, double>(Math.Abs), isIdempotent: true);
			FunctionRegistry.RegisterFunction("if", (Func<double, double, double, double>)((double a, double b, double c) => (a == 0.0) ? c : b), isIdempotent: true);
			FunctionRegistry.RegisterFunction("ifless", (Func<double, double, double, double, double>)((double a, double b, double c, double d) => (!(a < b)) ? d : c), isIdempotent: true);
			FunctionRegistry.RegisterFunction("ifmore", (Func<double, double, double, double, double>)((double a, double b, double c, double d) => (!(a > b)) ? d : c), isIdempotent: true);
			FunctionRegistry.RegisterFunction("ifequal", (Func<double, double, double, double, double>)((double a, double b, double c, double d) => (a != b) ? d : c), isIdempotent: true);
			FunctionRegistry.RegisterFunction("ceiling", new Func<double, double>(Math.Ceiling), isIdempotent: true);
			FunctionRegistry.RegisterFunction("floor", new Func<double, double>(Math.Floor), isIdempotent: true);
			FunctionRegistry.RegisterFunction("truncate", new Func<double, double>(Math.Truncate), isIdempotent: true);
			FunctionRegistry.RegisterFunction("round", new Func<double, double>(Math.Round), isIdempotent: true);
			FunctionRegistry.RegisterFunction("max", (DynamicFunc<double, double>)((double[] a) => a.Max()), isIdempotent: true);
			FunctionRegistry.RegisterFunction("min", (DynamicFunc<double, double>)((double[] a) => a.Min()), isIdempotent: true);
			FunctionRegistry.RegisterFunction("avg", (DynamicFunc<double, double>)((double[] a) => a.Average()), isIdempotent: true);
			FunctionRegistry.RegisterFunction("median", (DynamicFunc<double, double>)((double[] a) => a.Median()), isIdempotent: true);
			FunctionRegistry.RegisterFunction("sum", (DynamicFunc<double, double>)((double[] a) => a.Sum()), isIdempotent: true);
			FunctionRegistry.RegisterFunction("random", new Func<double>(random.NextDouble), isIdempotent: false);
		}

		private void RegisterDefaultConstants()
		{
			ConstantRegistry.RegisterConstant("e", Math.E);
			ConstantRegistry.RegisterConstant("pi", Math.PI);
		}

		private Operation BuildAbstractSyntaxTree(string expression, IConstantRegistry compiledConstants, bool optimize, bool validate)
		{
			List<Token> list = tokenReader.Read(expression);
			if (validate)
			{
				expressionValidator.Validate(list, expression);
			}
			Operation operation = new AstBuilder(FunctionRegistry, compiledConstants).Build(list);
			if (!optimize)
			{
				return operation;
			}
			return optimizer.Optimize(operation, FunctionRegistry, ConstantRegistry);
		}

		private Func<IDictionary<string, double>, double> BuildEvaluator(string formulaText, Operation operation)
		{
			if (!cacheEnabled)
			{
				return Evaluator(formulaText);
			}
			return executionFormulaCache.GetOrAdd(formulaText, Evaluator);
			Func<IDictionary<string, double>, double> Evaluator(string s)
			{
				Constant<double> constant = operation as Constant<double>;
				if (constant != null)
				{
					if (guardedModeEnabled)
					{
						return delegate(IDictionary<string, double> values)
						{
							VariableVerifier.VerifyVariableNames(values, ConstantRegistry, FunctionRegistry);
							return constant.Value;
						};
					}
					return (IDictionary<string, double> _) => constant.Value;
				}
				return executor.BuildFormula(operation, FunctionRegistry, ConstantRegistry);
			}
		}

		private bool IsInFormulaCache(string formulaText, out Func<IDictionary<string, double>, double> function)
		{
			function = null;
			if (cacheEnabled)
			{
				return executionFormulaCache.TryGetValue(formulaText, out function);
			}
			return false;
		}
	}
	public class EvaluatorBuilder
	{
		private const int DefaultCacheMaximumSize = 500;

		private const int DefaultCacheReductionSize = 50;

		private static readonly List<char> IllegalArgumentSeparators = new List<char>
		{
			' ', '+', '-', '*', '/', '^', '(', ')', '_', '%',
			'>', '<', '=', '&', '|', '≠', '≤', '≥'
		};

		internal CultureInfo CultureInfo { get; private set; } = CultureInfo.CurrentCulture;


		internal ExecutionMode ExecutionMode { get; private set; } = ExecutionMode.Compiled;


		internal bool CacheEnabled { get; private set; } = true;


		internal bool OptimizerEnabled { get; private set; } = true;


		internal bool CaseSensitive { get; private set; } = true;


		internal bool DefaultFunctions { get; private set; } = true;


		internal bool DefaultConstants { get; private set; } = true;


		internal int CacheMaximumSize { get; private set; } = 500;


		internal int CacheReductionSize { get; private set; } = 50;


		internal bool GuardedModeEnabled { get; private set; }

		internal bool ValidationEnabled { get; private set; } = true;


		internal char ArgumentSeparator { get; private set; } = ',';


		private char? OverrideArgumentSeparator { get; set; }

		internal List<FunctionDraft> Functions { get; }

		internal List<ConstantDraft> Constants { get; }

		public EvaluatorBuilder()
		{
			Functions = new List<FunctionDraft>();
			Constants = new List<ConstantDraft>();
		}

		public EvaluatorBuilder(EvaluatorBuilder evaluatorBuilder)
		{
			CultureInfo = evaluatorBuilder.CultureInfo;
			ExecutionMode = evaluatorBuilder.ExecutionMode;
			CacheEnabled = evaluatorBuilder.CacheEnabled;
			OptimizerEnabled = evaluatorBuilder.OptimizerEnabled;
			CaseSensitive = evaluatorBuilder.CaseSensitive;
			DefaultConstants = evaluatorBuilder.DefaultConstants;
			DefaultFunctions = evaluatorBuilder.DefaultFunctions;
			CacheMaximumSize = evaluatorBuilder.CacheMaximumSize;
			CacheReductionSize = evaluatorBuilder.CacheReductionSize;
			GuardedModeEnabled = evaluatorBuilder.GuardedModeEnabled;
			ValidationEnabled = evaluatorBuilder.ValidationEnabled;
			ArgumentSeparator = evaluatorBuilder.ArgumentSeparator;
			OverrideArgumentSeparator = evaluatorBuilder.OverrideArgumentSeparator;
			Functions = new List<FunctionDraft>(evaluatorBuilder.Functions);
			Constants = new List<ConstantDraft>(evaluatorBuilder.Constants);
		}

		public EvaluatorBuilder UseCulture(CultureInfo cultureInfo)
		{
			CultureInfo = cultureInfo;
			return this;
		}

		public EvaluatorBuilder UseArgumentSeparator(char argumentSeparator)
		{
			if (char.IsLetter(argumentSeparator) || char.IsDigit(argumentSeparator) || IllegalArgumentSeparators.Contains(argumentSeparator))
			{
				throw new ArgumentException("Illegal argument separator \"\". Character must be a symbol and must not be ", "argumentSeparator");
			}
			OverrideArgumentSeparator = argumentSeparator;
			return this;
		}

		public EvaluatorBuilder UseExecutionMode(ExecutionMode executionMode)
		{
			ExecutionMode = executionMode;
			return this;
		}

		public EvaluatorBuilder EnableCache()
		{
			CacheEnabled = true;
			return this;
		}

		public EvaluatorBuilder DisableCache()
		{
			CacheEnabled = false;
			return this;
		}

		public EvaluatorBuilder EnableOptimizer()
		{
			OptimizerEnabled = true;
			return this;
		}

		public EvaluatorBuilder DisableOptimizer()
		{
			OptimizerEnabled = false;
			return this;
		}

		public EvaluatorBuilder EnableCaseSensitivity()
		{
			CaseSensitive = true;
			return this;
		}

		public EvaluatorBuilder DisableCaseSensitivity()
		{
			CaseSensitive = false;
			return this;
		}

		public EvaluatorBuilder EnableDefaultFunctions()
		{
			DefaultFunctions = true;
			return this;
		}

		public EvaluatorBuilder DisableDefaultFunctions()
		{
			DefaultFunctions = false;
			return this;
		}

		public EvaluatorBuilder EnableDefaultConstants()
		{
			DefaultConstants = true;
			return this;
		}

		public EvaluatorBuilder DisableDefaultConstants()
		{
			DefaultConstants = false;
			return this;
		}

		public EvaluatorBuilder EnableGuardedMode()
		{
			GuardedModeEnabled = true;
			return this;
		}

		public EvaluatorBuilder DisableGuardedMode()
		{
			GuardedModeEnabled = false;
			return this;
		}

		public EvaluatorBuilder EnableValidation()
		{
			ValidationEnabled = true;
			return this;
		}

		public EvaluatorBuilder DisableValidation()
		{
			ValidationEnabled = false;
			return this;
		}

		public EvaluatorBuilder UseCacheMaximumSize(int cacheMaximumSize)
		{
			CacheMaximumSize = cacheMaximumSize;
			return this;
		}

		public EvaluatorBuilder UseCacheReductionSize(int cacheReductionSize)
		{
			CacheReductionSize = cacheReductionSize;
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder 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)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, function));
			return this;
		}

		public EvaluatorBuilder AddFunction(string functionName, DynamicFunc<double, double> functionDelegate, bool isIdempotent = true)
		{
			Functions.Add(new FunctionDraft(functionName, isIdempotent, functionDelegate));
			return this;
		}

		public EvaluatorBuilder AddConstant(string constantName, double value)
		{
			Constants.Add(new ConstantDraft(constantName, value));
			return this;
		}

		public Evaluator Build()
		{
			if (!OverrideArgumentSeparator.HasValue)
			{
				ArgumentSeparator = ((CultureInfo.NumberFormat.NumberDecimalSeparator[0] == ',') ? ';' : ',');
			}
			else
			{
				if (OverrideArgumentSeparator == CultureInfo.NumberFormat.NumberDecimalSeparator[0])
				{
					throw new ArgumentException($"The list separator \"{ArgumentSeparator}\" is the same as the decimal separator \"{CultureInfo.NumberFormat.NumberDecimalSeparator}\". This is not allowed.", "ArgumentSeparator");
				}
				ArgumentSeparator = OverrideArgumentSeparator.Value;
			}
			return new Evaluator(this);
		}

		protected bool Equals(EvaluatorBuilder other)
		{
			if (object.Equals(CultureInfo, other.CultureInfo) && ArgumentSeparator == other.ArgumentSeparator && OverrideArgumentSeparator == other.OverrideArgumentSeparator && ExecutionMode == other.ExecutionMode && CacheEnabled == other.CacheEnabled && OptimizerEnabled == other.OptimizerEnabled && CaseSensitive == other.CaseSensitive && DefaultFunctions == other.DefaultFunctions && DefaultConstants == other.DefaultConstants && CacheMaximumSize == other.CacheMaximumSize && CacheReductionSize == other.CacheReductionSize && Functions.SequenceEqual(other.Functions) && Constants.SequenceEqual(other.Constants) && GuardedModeEnabled == other.GuardedModeEnabled)
			{
				return ValidationEnabled == other.ValidationEnabled;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((EvaluatorBuilder)obj);
		}

		public override int GetHashCode()
		{
			return (int)(((((((((((((((((((((((uint)(((((((CultureInfo != null) ? CultureInfo.GetHashCode() : 0) * 397) ^ ArgumentSeparator.GetHashCode()) * 397) ^ OverrideArgumentSeparator.GetHashCode()) * 397) ^ (uint)ExecutionMode) * 397) ^ (uint)CacheEnabled.GetHashCode()) * 397) ^ (uint)OptimizerEnabled.GetHashCode()) * 397) ^ (uint)CaseSensitive.GetHashCode()) * 397) ^ (uint)DefaultFunctions.GetHashCode()) * 397) ^ (uint)DefaultConstants.GetHashCode()) * 397) ^ (uint)CacheMaximumSize) * 397) ^ (uint)CacheReductionSize) * 397) ^ (uint)GuardedModeEnabled.GetHashCode()) * 397) ^ (uint)ValidationEnabled.GetHashCode()) * 397) ^ (uint)((Functions != null) ? Functions.GetHashCode() : 0)) * 397) ^ ((Constants != null) ? Constants.GetHashCode() : 0);
		}
	}
	internal class FunctionDraft
	{
		public string Name { get; }

		public bool IsIdempotent { get; }

		public Delegate Function { get; }

		public FunctionDraft(string name, bool isIdempotent, Delegate function)
		{
			Name = name;
			IsIdempotent = isIdempotent;
			Function = function;
		}

		protected bool Equals(FunctionDraft other)
		{
			if (Name == other.Name && IsIdempotent == other.IsIdempotent)
			{
				return object.Equals(Function, other.Function);
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((FunctionDraft)obj);
		}

		public override int GetHashCode()
		{
			return (((((Name != null) ? Name.GetHashCode() : 0) * 397) ^ IsIdempotent.GetHashCode()) * 397) ^ (((object)Function != null) ? Function.GetHashCode() : 0);
		}
	}
	internal class ConstantDraft
	{
		public string Name { get; }

		public double Value { get; }

		public ConstantDraft(string name, double value)
		{
			Name = name;
			Value = value;
		}

		protected bool Equals(ConstantDraft other)
		{
			if (Name == other.Name)
			{
				return Value.Equals(other.Value);
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((ConstantDraft)obj);
		}

		public override int GetHashCode()
		{
			return (((Name != null) ? Name.GetHashCode() : 0) * 397) ^ Value.GetHashCode();
		}
	}
	public class FormulaContext
	{
		public IDictionary<string, double> Variables { get; private set; }

		public IFunctionRegistry FunctionRegistry { get; private set; }

		public IConstantRegistry ConstantRegistry { get; private set; }

		public FormulaContext(IDictionary<string, double> variables, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			Variables = variables;
			FunctionRegistry = functionRegistry;
			ConstantRegistry = constantRegistry;
		}
	}
	public interface IEvaluator
	{
		IEnumerable<FunctionInfo> Functions { get; }

		IEnumerable<ConstantInfo> Constants { get; }

		double Evaluate(string expression);

		double Evaluate(string expression, IDictionary<string, double> variables);

		Func<IDictionary<string, double>, double> CreateDelegate(string expression);

		void Validate(string expression);

		void Validate(string expression, IList<string> variables);
	}
	public class ParseException : Exception
	{
		public string Expression { get; }

		public ParseException(string message, string expression)
			: base(message)
		{
			Expression = expression;
		}
	}
	public class InvalidTokenParseException : ParseException
	{
		public int TokenPosition { get; }

		public string Token { get; }

		public InvalidTokenParseException(string message, string expression, int tokenPosition, string token)
			: base(message, expression)
		{
			TokenPosition = tokenPosition;
			Token = token;
		}
	}
	public class InvalidFloatingPointNumberParseException : ParseException
	{
		public int TokenPosition { get; }

		public string Token { get; }

		public InvalidFloatingPointNumberParseException(string message, string expression, int tokenPosition, string token)
			: base(message, expression)
		{
			TokenPosition = tokenPosition;
			Token = token;
		}
	}
	public class MissingLeftParenthesisParseException : ParseException
	{
		public int RightParenthesisPosition { get; }

		public MissingLeftParenthesisParseException(string message, string expression, int rightParenthesisPosition)
			: base(message, expression)
		{
			RightParenthesisPosition = rightParenthesisPosition;
		}
	}
	public class MissingRightParenthesisParseException : ParseException
	{
		public int LeftParenthesisPosition { get; }

		public MissingRightParenthesisParseException(string message, string expression, int leftParenthesisPosition)
			: base(message, expression)
		{
			LeftParenthesisPosition = leftParenthesisPosition;
		}
	}
	public class UnknownFunctionParseException : ParseException
	{
		public int FunctionNamePosition { get; }

		public string FunctionName { get; }

		public UnknownFunctionParseException(string message, string expression, int functionNamePosition, string functionName)
			: base(message, expression)
		{
			FunctionNamePosition = functionNamePosition;
			FunctionName = functionName;
		}
	}
	public class InvalidArgumentCountParseException : ParseException
	{
		public int FunctionNamePosition { get; }

		public string FunctionName { get; }

		public InvalidArgumentCountParseException(string message, string expression, int functionNamePosition, string functionName)
			: base(message, expression)
		{
			FunctionNamePosition = functionNamePosition;
			FunctionName = functionName;
		}
	}
	public class MissingOperandParseException : ParseException
	{
		public int OperatorPosition { get; }

		public string Operator { get; }

		public MissingOperandParseException(string message, string expression, int operatorPosition, string @operator)
			: base(message, expression)
		{
			OperatorPosition = operatorPosition;
			Operator = @operator;
		}
	}
	public class MissingQuoteParseException : ParseException
	{
		public int QuotePosition { get; }

		public MissingQuoteParseException(string message, string expression, int quotePosition)
			: base(message, expression)
		{
			QuotePosition = quotePosition;
		}
	}
	public class VariableNotDefinedException : Exception
	{
		public string VariableName { get; }

		public VariableNotDefinedException(string message, string variableName)
			: base(message)
		{
			VariableName = variableName;
		}

		public VariableNotDefinedException(string message, Exception innerException, string variableName)
			: base(message, innerException)
		{
			VariableName = variableName;
		}
	}
}
namespace Adletec.Sonic.Util
{
	internal static class EngineUtil
	{
		internal static IDictionary<string, double> ConvertToCaseInsensitiveDictionary(IDictionary<string, double> variables)
		{
			return new Dictionary<string, double>(variables, StringComparer.OrdinalIgnoreCase);
		}
	}
	public class FuncAdapter
	{
		public Delegate Wrap(IEnumerable<Adletec.Sonic.Execution.ParameterInfo> parameters, Func<IDictionary<string, double>, double> function)
		{
			Adletec.Sonic.Execution.ParameterInfo[] parameterArray = parameters.ToArray();
			return GenerateDelegate(parameterArray, function);
		}

		private Delegate GenerateDelegate(Adletec.Sonic.Execution.ParameterInfo[] parameterArray, Func<Dictionary<string, double>, double> function)
		{
			Type delegateType = GetDelegateType(parameterArray);
			Type typeFromHandle = typeof(Dictionary<string, double>);
			ParameterExpression parameterExpression = Expression.Variable(typeof(Dictionary<string, double>), "dictionary");
			BinaryExpression item = Expression.Assign(parameterExpression, Expression.New(typeFromHandle));
			ParameterExpression[] array = new ParameterExpression[parameterArray.Length];
			List<Expression> list = new List<Expression>();
			list.Add(item);
			for (int i = 0; i < parameterArray.Length; i++)
			{
				Type type = ((parameterArray[i].DataType == DataType.FloatingPoint) ? typeof(double) : typeof(int));
				array[i] = Expression.Parameter(type, parameterArray[i].Name);
				list.Add(Expression.Call(parameterExpression, typeFromHandle.GetRuntimeMethod("Add", new Type[2]
				{
					typeof(string),
					typeof(double)
				}), Expression.Constant(parameterArray[i].Name), Expression.Convert(array[i], typeof(double))));
			}
			InvocationExpression item2 = Expression.Invoke(Expression.Constant(function), parameterExpression);
			list.Add(item2);
			return Expression.Lambda(delegateType, Expression.Block(new ParameterExpression[1] { parameterExpression }, list), array).Compile();
		}

		private Type GetDelegateType(IList<Adletec.Sonic.Execution.ParameterInfo> parameters)
		{
			string text = $"System.Func`{parameters.Count + 1}";
			Type type = Type.GetType(text);
			if (type == null)
			{
				throw new InvalidOperationException("Couldn't get type of $" + text + ".");
			}
			Type[] array = new Type[parameters.Count + 1];
			for (int i = 0; i < parameters.Count; i++)
			{
				array[i] = ((parameters[i].DataType == DataType.FloatingPoint) ? typeof(double) : typeof(int));
			}
			array[^1] = typeof(double);
			return type.MakeGenericType(array);
		}
	}
	internal static class MathExtended
	{
		private static int Partition<T>(this IList<T> list, int start, int end, Random rnd = null) where T : IComparable<T>
		{
			if (rnd != null)
			{
				list.Swap(end, rnd.Next(start, end + 1));
			}
			T other = list[end];
			int num = start - 1;
			for (int i = start; i < end; i++)
			{
				if (list[i].CompareTo(other) <= 0)
				{
					list.Swap(i, ++num);
				}
			}
			list.Swap(end, ++num);
			return num;
		}

		public static T NthOrderStatistic<T>(this IList<T> list, int n, Random rnd = null) where T : IComparable<T>
		{
			return list.NthOrderStatistic(n, 0, list.Count - 1, rnd);
		}

		private static T NthOrderStatistic<T>(this IList<T> list, int n, int start, int end, Random rnd) where T : IComparable<T>
		{
			int num;
			while (true)
			{
				num = list.Partition(start, end, rnd);
				if (num == n)
				{
					break;
				}
				if (n < num)
				{
					end = num - 1;
				}
				else
				{
					start = num + 1;
				}
			}
			return list[num];
		}

		public static void Swap<T>(this IList<T> list, int i, int j)
		{
			if (i != j)
			{
				T value = list[i];
				list[i] = list[j];
				list[j] = value;
			}
		}

		public static T Median<T>(this IList<T> list) where T : IComparable<T>
		{
			return list.NthOrderStatistic((list.Count - 1) / 2);
		}

		public static double Median<T>(this IEnumerable<T> sequence, Func<T, double> getValue)
		{
			List<double> list = sequence.Select(getValue).ToList();
			int n = (list.Count - 1) / 2;
			return list.NthOrderStatistic(n);
		}
	}
	public static class MathUtil
	{
		public static double Cot(double a)
		{
			return 1.0 / Math.Tan(a);
		}

		public static double Acot(double d)
		{
			return Math.Atan(1.0 / d);
		}

		public static double Csc(double a)
		{
			return 1.0 / Math.Sin(a);
		}

		public static double Sec(double d)
		{
			return 1.0 / Math.Cos(d);
		}
	}
	public class MemoryCache<TKey, TValue>
	{
		private class CacheItem
		{
			private readonly MemoryCache<TKey, TValue> cache;

			public TValue Value { get; }

			public long LastAccessed { get; private set; }

			public CacheItem(MemoryCache<TKey, TValue> cache, TValue value)
			{
				this.cache = cache;
				Value = value;
				Accessed();
			}

			public void Accessed()
			{
				LastAccessed = Interlocked.Increment(ref cache.counter);
			}
		}

		private readonly int maximumSize;

		private readonly int reductionSize;

		private long counter;

		private readonly ConcurrentDictionary<TKey, CacheItem> dictionary;

		public TValue this[TKey key]
		{
			get
			{
				CacheItem cacheItem = dictionary[key];
				cacheItem.Accessed();
				return cacheItem.Value;
			}
		}

		public int Count => dictionary.Count;

		public MemoryCache(int maximumSize, int reductionSize)
		{
			if (maximumSize < 1)
			{
				throw new ArgumentOutOfRangeException("maximumSize", "The maximum allowed number of items in the cache must be at least one.");
			}
			if (reductionSize < 1)
			{
				throw new ArgumentOutOfRangeException("reductionSize", "The cache reduction size must be at least one.");
			}
			this.maximumSize = maximumSize;
			this.reductionSize = reductionSize;
			dictionary = new ConcurrentDictionary<TKey, CacheItem>();
		}

		public bool ContainsKey(TKey key)
		{
			return dictionary.ContainsKey(key);
		}

		public bool TryGetValue(TKey key, out TValue result)
		{
			if (dictionary.TryGetValue(key, out var value))
			{
				value.Accessed();
				result = value.Value;
				return true;
			}
			result = default(TValue);
			return false;
		}

		public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
		{
			if (valueFactory == null)
			{
				throw new ArgumentNullException("valueFactory");
			}
			return dictionary.GetOrAdd(key, delegate(TKey k)
			{
				EnsureCacheStorageAvailable();
				return new CacheItem(this, valueFactory(k));
			}).Value;
		}

		private void EnsureCacheStorageAvailable()
		{
			if (dictionary.Count < maximumSize)
			{
				return;
			}
			foreach (TKey item in (IEnumerable<TKey>)dictionary.ToArray().Where(delegate(KeyValuePair<TKey, CacheItem> p)
			{
				KeyValuePair<TKey, CacheItem> keyValuePair3 = p;
				if (keyValuePair3.Key != null)
				{
					keyValuePair3 = p;
					return keyValuePair3.Value != null;
				}
				return false;
			}).OrderBy(delegate(KeyValuePair<TKey, CacheItem> p)
			{
				KeyValuePair<TKey, CacheItem> keyValuePair2 = p;
				return keyValuePair2.Value.LastAccessed;
			})
				.Select(delegate(KeyValuePair<TKey, CacheItem> p)
				{
					KeyValuePair<TKey, CacheItem> keyValuePair = p;
					return keyValuePair.Key;
				})
				.Take(reductionSize)
				.ToList())
			{
				dictionary.TryRemove(item, out var _);
			}
		}
	}
}
namespace Adletec.Sonic.Parsing
{
	public class AstBuilder
	{
		private readonly IFunctionRegistry functionRegistry;

		private readonly IConstantRegistry constantRegistry;

		private readonly Dictionary<char, int> operationPrecedence = new Dictionary<char, int>();

		private readonly Stack<Operation> resultStack = new Stack<Operation>();

		private readonly Stack<Token> operatorStack = new Stack<Token>();

		private readonly Stack<int> dynamicFunctionArgumentCountStack = new Stack<int>();

		public AstBuilder(IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			this.functionRegistry = functionRegistry ?? throw new ArgumentNullException("functionRegistry");
			this.constantRegistry = constantRegistry ?? throw new ArgumentNullException("constantRegistry");
			operationPrecedence.Add('(', 0);
			operationPrecedence.Add('&', 1);
			operationPrecedence.Add('|', 1);
			operationPrecedence.Add('<', 2);
			operationPrecedence.Add('>', 2);
			operationPrecedence.Add('≤', 2);
			operationPrecedence.Add('≥', 2);
			operationPrecedence.Add('≠', 2);
			operationPrecedence.Add('=', 2);
			operationPrecedence.Add('+', 3);
			operationPrecedence.Add('-', 3);
			operationPrecedence.Add('*', 4);
			operationPrecedence.Add('/', 4);
			operationPrecedence.Add('%', 4);
			operationPrecedence.Add('_', 5);
			operationPrecedence.Add('^', 5);
		}

		public Operation Build(IList<Token> tokens)
		{
			for (int i = 0; i < tokens.Count; i++)
			{
				Token token = tokens[i];
				switch (token.TokenType)
				{
				case TokenType.Integer:
				{
					IntegerConstant item5 = new IntegerConstant((int)token.Value);
					resultStack.Push(item5);
					break;
				}
				case TokenType.FloatingPoint:
				{
					FloatingPointConstant item3 = new FloatingPointConstant((double)token.Value);
					resultStack.Push(item3);
					break;
				}
				case TokenType.Function:
					operatorStack.Push(token);
					dynamicFunctionArgumentCountStack.Push(1);
					break;
				case TokenType.Symbol:
				{
					string text = (string)token.Value;
					if (constantRegistry.IsConstantName(text))
					{
						FloatingPointConstant item = new FloatingPointConstant(constantRegistry.GetConstantInfo(text).Value);
						resultStack.Push(item);
					}
					else
					{
						Variable item2 = new Variable(text);
						resultStack.Push(item2);
					}
					break;
				}
				case TokenType.LeftParenthesis:
					operatorStack.Push(token);
					break;
				case TokenType.RightParenthesis:
					PopOperations();
					operatorStack.Pop();
					if (operatorStack.Count >= 1 && operatorStack.Peek().TokenType == TokenType.Function)
					{
						Operation item4 = ConvertFunction(operatorStack.Pop());
						resultStack.Push(item4);
					}
					break;
				case TokenType.ArgumentSeparator:
					PopOperations();
					dynamicFunctionArgumentCountStack.Push(dynamicFunctionArgumentCountStack.Pop() + 1);
					break;
				case TokenType.Operation:
				{
					char c = (char)token.Value;
					while (operatorStack.Count > 0 && operatorStack.Peek().TokenType == TokenType.Operation)
					{
						Token operationToken = operatorStack.Peek();
						char key = (char)operationToken.Value;
						if ((!IsLeftAssociativeOperation(c) || operationPrecedence[c] > operationPrecedence[key]) && operationPrecedence[c] >= operationPrecedence[key])
						{
							break;
						}
						operatorStack.Pop();
						resultStack.Push(ConvertOperation(operationToken));
					}
					operatorStack.Push(token);
					break;
				}
				default:
					throw new ArgumentOutOfRangeException("token", string.Format("Unexpected value \"{0}\" for {1}.", token, "token"));
				}
			}
			PopOperations();
			return resultStack.Pop();
		}

		private void PopOperations()
		{
			while (operatorStack.Count > 0 && operatorStack.Peek().TokenType != TokenType.LeftParenthesis)
			{
				Token token = operatorStack.Pop();
				switch (token.TokenType)
				{
				case TokenType.Operation:
					resultStack.Push(ConvertOperation(token));
					break;
				case TokenType.Function:
				{
					Operation item = ConvertFunction(token);
					resultStack.Push(item);
					break;
				}
				default:
					throw new InvalidOperationException("Unexpected token type.");
				}
			}
		}

		private Operation ConvertOperation(Token operationToken)
		{
			switch ((char)operationToken.Value)
			{
			case '+':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new Addition(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '-':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new Subtraction(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '*':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new Multiplication(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '/':
			{
				Operation divisor = resultStack.Pop();
				Operation dividend = resultStack.Pop();
				return new Division(DataType.FloatingPoint, dividend, divisor);
			}
			case '%':
			{
				Operation divisor = resultStack.Pop();
				Operation dividend = resultStack.Pop();
				return new Modulo(DataType.FloatingPoint, dividend, divisor);
			}
			case '_':
			{
				Operation argument2 = resultStack.Pop();
				return new UnaryMinus(argument2.DataType, argument2);
			}
			case '^':
			{
				Operation exponent = resultStack.Pop();
				Operation @base = resultStack.Pop();
				return new Exponentiation(DataType.FloatingPoint, @base, exponent);
			}
			case '&':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new And(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '|':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new Or(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '<':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new LessThan(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '≤':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new LessOrEqualThan(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '>':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new GreaterThan(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '≥':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new GreaterOrEqualThan(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '=':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new Equal(RequiredDataType(argument2, argument), argument2, argument);
			}
			case '≠':
			{
				Operation argument = resultStack.Pop();
				Operation argument2 = resultStack.Pop();
				return new NotEqual(RequiredDataType(argument2, argument), argument2, argument);
			}
			default:
				throw new ArgumentException($"Unknown operation \"{operationToken}\".", "operationToken");
			}
		}

		private Operation ConvertFunction(Token functionToken)
		{
			string functionName = (string)functionToken.Value;
			FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(functionName);
			int num = dynamicFunctionArgumentCountStack.Pop();
			int num2 = (functionInfo.IsDynamicFunc ? num : functionInfo.NumberOfParameters);
			List<Operation> list = new List<Operation>();
			for (int i = 0; i < num2; i++)
			{
				list.Add(resultStack.Pop());
			}
			list.Reverse();
			return new Function(DataType.FloatingPoint, functionName, list, functionInfo.IsIdempotent);
		}

		private static bool IsLeftAssociativeOperation(char character)
		{
			if (character != '*' && character != '+' && character != '-')
			{
				return character == '/';
			}
			return true;
		}

		private static DataType RequiredDataType(Operation argument1, Operation argument2)
		{
			if (argument1.DataType != DataType.FloatingPoint && argument2.DataType != DataType.FloatingPoint)
			{
				return DataType.Integer;
			}
			return DataType.FloatingPoint;
		}
	}
	public class ExpressionValidator
	{
		private class ValidationContext
		{
			public bool IsFunction { get; set; }

			public int ExpectedArgumentCount { get; set; }

			public int ActualArgumentCount { get; set; }

			public Token RootToken { get; set; }

			public bool IsDynamic { get; set; }
		}

		private readonly IFunctionRegistry functionRegistry;

		private readonly CultureInfo cultureInfo;

		public ExpressionValidator(IFunctionRegistry functionRegistry, CultureInfo cultureInfo)
		{
			this.functionRegistry = functionRegistry;
			this.cultureInfo = cultureInfo;
		}

		public void Validate(IList<Token> tokenList, string expression)
		{
			Stack<ValidationContext> stack = new Stack<ValidationContext>();
			if (tokenList.Count == 0)
			{
				throw new ArgumentException("Token list cannot be empty", "tokenList");
			}
			Token token = tokenList[0];
			switch (token.TokenType)
			{
			case TokenType.Operation:
				if (IsBinaryOperation(token))
				{
					ThrowMissingOperationArgumentParseException(token, expression, "There is no left argument.");
				}
				break;
			case TokenType.ArgumentSeparator:
				ThrowInvalidTokenParseException(token, expression);
				break;
			case TokenType.RightParenthesis:
				ThrowMissingLeftParenthesisParseException(token, expression);
				break;
			case TokenType.LeftParenthesis:
				stack.Push(new ValidationContext
				{
					IsFunction = false,
					ActualArgumentCount = 0,
					ExpectedArgumentCount = 0,
					RootToken = token
				});
				break;
			}
			for (int i = 1; i < tokenList.Count; i++)
			{
				Token token2 = tokenList[i];
				Token token3 = tokenList[i - 1];
				switch (token2.TokenType)
				{
				case TokenType.Operation:
					if (!IsUnaryOperation(token2))
					{
						switch (token3.TokenType)
						{
						case TokenType.LeftParenthesis:
						case TokenType.ArgumentSeparator:
							ThrowInvalidTokenParseException(token2, expression);
							break;
						case TokenType.Operation:
							ThrowMissingOperationArgumentParseException(token3, expression, "There is no right argument.");
							break;
						}
						break;
					}
					goto case TokenType.Integer;
				case TokenType.Integer:
				case TokenType.FloatingPoint:
				case TokenType.Symbol:
				case TokenType.Function:
				{
					TokenType tokenType = token3.TokenType;
					if ((uint)tokenType <= 3u || tokenType == TokenType.RightParenthesis)
					{
						ThrowInvalidTokenParseException(token2, expression);
					}
					if (IsStartOfArgument(stack, token3))
					{
						stack.Peek().ActualArgumentCount++;
					}
					break;
				}
				case TokenType.ArgumentSeparator:
					switch (token3.TokenType)
					{
					case TokenType.LeftParenthesis:
					case TokenType.ArgumentSeparator:
						ThrowInvalidTokenParseException(token2, expression);
						break;
					case TokenType.Operation:
						if (IsBinaryOperation(token3))
						{
							ThrowMissingOperationArgumentParseException(token2, expression, "There is no right argument.");
						}
						break;
					}
					if (stack.Count == 0)
					{
						ThrowInvalidTokenParseException(token2, expression, "Argument separator is outside of function.");
					}
					if (!stack.Peek().IsFunction)
					{
						ThrowInvalidTokenParseException(token2, expression, "Argument separator must be a direct child of a function.");
					}
					break;
				case TokenType.LeftParenthesis:
				{
					TokenType tokenType = token3.TokenType;
					if ((uint)tokenType <= 2u || tokenType == TokenType.RightParenthesis)
					{
						ThrowInvalidTokenParseException(token2, expression);
					}
					if (token3.TokenType == TokenType.Function)
					{
						FunctionInfo functionInfo = functionRegistry.GetFunctionInfo((string)token3.Value);
						if (functionInfo == null)
						{
							ThrowUnknownFunctionParseException(token3, expression);
						}
						stack.Push(new ValidationContext
						{
							IsFunction = true,
							IsDynamic = functionInfo.IsDynamicFunc,
							ExpectedArgumentCount = functionInfo.NumberOfParameters,
							ActualArgumentCount = 0,
							RootToken = token3
						});
					}
					else
					{
						if (IsStartOfArgument(stack, token3))
						{
							stack.Peek().ActualArgumentCount++;
						}
						stack.Push(new ValidationContext
						{
							IsFunction = false,
							ActualArgumentCount = 0,
							ExpectedArgumentCount = 0,
							RootToken = token2
						});
					}
					break;
				}
				case TokenType.RightParenthesis:
				{
					switch (token3.TokenType)
					{
					case TokenType.Operation:
						ThrowMissingOperationArgumentParseException(token2, expression, "There is no right argument.");
						break;
					case TokenType.ArgumentSeparator:
						ThrowInvalidTokenParseException(token2, expression, "Argument separator without following argument.");
						break;
					case TokenType.LeftParenthesis:
						if (!IsFunctionOnTopOfStack(stack))
						{
							ThrowInvalidTokenParseException(token2, expression, "Empty parentheses are not allowed.");
						}
						break;
					}
					if (stack.Count == 0)
					{
						ThrowMissingLeftParenthesisParseException(token2, expression);
					}
					ValidationContext validationContext = stack.Pop();
					if (validationContext.IsDynamic)
					{
						if (validationContext.ActualArgumentCount < 1)
						{
							ThrowInvalidDynamicFunctionArgumentCountParseException(validationContext.RootToken, expression);
						}
					}
					else if (validationContext.ExpectedArgumentCount != validationContext.ActualArgumentCount)
					{
						ThrowInvalidFunctionArgumentCountParseException(validationContext.RootToken, expression, validationContext.ExpectedArgumentCount, validationContext.ActualArgumentCount);
					}
					break;
				}
				default:
					throw new ArgumentOutOfRangeException();
				}
			}
			if (stack.Any())
			{
				ThrowMissingRightParenthesisParseException(stack.Peek().RootToken, expression);
			}
			if (tokenList[tokenList.Count - 1].TokenType == TokenType.Operation)
			{
				ThrowMissingOperationArgumentParseException(tokenList.Last(), expression, "There is no right argument.");
			}
		}

		private void ThrowInvalidTokenParseException(Token token, string expression, string message = null)
		{
			string text = ((token.Value is double num) ? num.ToString(cultureInfo) : token.Value.ToString());
			int startPosition = token.StartPosition;
			throw new InvalidTokenParseException($"Unexpected token at position {startPosition} in expression: \"{text}\". {message}", expression, startPosition, text);
		}

		private static void ThrowMissingOperationArgumentParseException(Token token, string expression, string message = null)
		{
			string text = token.Value.ToString();
			int startPosition = token.StartPosition;
			throw new MissingOperandParseException($"Missing argument for operation \"{text}\" at position {startPosition}. {message}", expression, startPosition, text);
		}

		private static void ThrowInvalidDynamicFunctionArgumentCountParseException(Token rootToken, string expression, string message = null)
		{
			string text = rootToken.Value.ToString();
			int startPosition = rootToken.StartPosition;
			throw new InvalidArgumentCountParseException($"Invalid argument count for dynamic function \"{text}\" at position {startPosition}. Expected to find at least one argument, but found none. {message}", expression, startPosition, text);
		}

		private static void ThrowInvalidFunctionArgumentCountParseException(Token rootToken, string expression, int expectedArguments, int foundArguments, string message = null)
		{
			string text = rootToken.Value.ToString();
			int startPosition = rootToken.StartPosition;
			throw new InvalidArgumentCountParseException($"Invalid argument count for function \"{text}\" at position {startPosition}. Expected {expectedArguments}, but found {foundArguments}. {message}", expression, startPosition, text);
		}

		private static void ThrowMissingLeftParenthesisParseException(Token token, string expression, string message = null)
		{
			int startPosition = token.StartPosition;
			throw new MissingLeftParenthesisParseException($"Missing left parenthesis for right parenthesis at position {startPosition}. {message}", expression, startPosition);
		}

		private static void ThrowMissingRightParenthesisParseException(Token token, string expression, string message = null)
		{
			int startPosition = token.StartPosition;
			throw new MissingRightParenthesisParseException($"Missing right parenthesis for left parenthesis at position {startPosition}. {message}", expression, startPosition);
		}

		private static void ThrowUnknownFunctionParseException(Token token, string expression, string message = null)
		{
			string arg = token.Value.ToString();
			int startPosition = token.StartPosition;
			throw new UnknownFunctionParseException($"Unknown function \"{arg}\" at position {startPosition}. {message}", expression, startPosition, token.Value.ToString());
		}

		private static bool IsUnaryOperation(Token token)
		{
			if (token.TokenType == TokenType.Operation)
			{
				return (char)token.Value == '_';
			}
			return false;
		}

		private static bool IsBinaryOperation(Token token)
		{
			if (token.TokenType == TokenType.Operation)
			{
				return (char)token.Value != '_';
			}
			return false;
		}

		private static bool IsStartOfArgument(Stack<ValidationContext> contextStack, Token previousToken)
		{
			if (IsFunctionOnTopOfStack(contextStack))
			{
				if (previousToken.TokenType != TokenType.ArgumentSeparator)
				{
					return previousToken.TokenType == TokenType.LeftParenthesis;
				}
				return true;
			}
			return false;
		}

		private static bool IsFunctionOnTopOfStack(Stack<ValidationContext> contextStack)
		{
			if (contextStack.Count == 0)
			{
				return false;
			}
			return contextStack.Peek().IsFunction;
		}
	}
	public class Optimizer
	{
		private readonly IExecutor executor;

		public Optimizer(IExecutor executor)
		{
			this.executor = executor;
		}

		public Operation Optimize(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			if (operation.GetType() == typeof(Addition))
			{
				Addition addition = (Addition)operation;
				addition.Argument1 = Optimize(addition.Argument1, functionRegistry, constantRegistry);
				addition.Argument2 = Optimize(addition.Argument2, functionRegistry, constantRegistry);
				if (!addition.Argument1.DependsOnVariables && !addition.Argument2.DependsOnVariables)
				{
					addition.DependsOnVariables = false;
				}
				if (addition.Argument1.IsIdempotent && addition.Argument2.IsIdempotent)
				{
					addition.IsIdempotent = true;
				}
			}
			else if (operation.GetType() == typeof(Subtraction))
			{
				Subtraction subtraction = (Subtraction)operation;
				subtraction.Argument1 = Optimize(subtraction.Argument1, functionRegistry, constantRegistry);
				subtraction.Argument2 = Optimize(subtraction.Argument2, functionRegistry, constantRegistry);
				if (!subtraction.Argument1.DependsOnVariables && !subtraction.Argument2.DependsOnVariables)
				{
					subtraction.DependsOnVariables = false;
				}
				if (subtraction.Argument1.IsIdempotent && subtraction.Argument2.IsIdempotent)
				{
					subtraction.IsIdempotent = true;
				}
			}
			else if (operation.GetType() == typeof(Multiplication))
			{
				Multiplication multiplication = (Multiplication)operation;
				multiplication.Argument1 = Optimize(multiplication.Argument1, functionRegistry, constantRegistry);
				multiplication.Argument2 = Optimize(multiplication.Argument2, functionRegistry, constantRegistry);
				if (IsZero(multiplication.Argument1) || IsZero(multiplication.Argument2))
				{
					return new FloatingPointConstant(0.0);
				}
				if (!multiplication.Argument1.DependsOnVariables && !multiplication.Argument2.DependsOnVariables)
				{
					multiplication.DependsOnVariables = false;
				}
				if (multiplication.Argument1.IsIdempotent && multiplication.Argument2.IsIdempotent)
				{
					multiplication.IsIdempotent = true;
				}
			}
			else if (operation.GetType() == typeof(Division))
			{
				Division division = (Division)operation;
				division.Dividend = Optimize(division.Dividend, functionRegistry, constantRegistry);
				division.Divisor = Optimize(division.Divisor, functionRegistry, constantRegistry);
				if (IsZero(division.Dividend))
				{
					return new FloatingPointConstant(0.0);
				}
				if (!division.Dividend.DependsOnVariables && !division.Divisor.DependsOnVariables)
				{
					division.DependsOnVariables = false;
				}
				if (division.Dividend.IsIdempotent && division.Divisor.IsIdempotent)
				{
					division.IsIdempotent = true;
				}
			}
			else if (operation.GetType() == typeof(Exponentiation))
			{
				Exponentiation exponentiation = (Exponentiation)operation;
				exponentiation.Base = Optimize(exponentiation.Base, functionRegistry, constantRegistry);
				exponentiation.Exponent = Optimize(exponentiation.Exponent, functionRegistry, constantRegistry);
				if (IsZero(exponentiation.Exponent))
				{
					return new FloatingPointConstant(1.0);
				}
				if (IsZero(exponentiation.Base))
				{
					return new FloatingPointConstant(0.0);
				}
				if (!exponentiation.Base.DependsOnVariables && !exponentiation.Exponent.DependsOnVariables)
				{
					exponentiation.DependsOnVariables = false;
				}
				if (exponentiation.Base.IsIdempotent && exponentiation.Exponent.IsIdempotent)
				{
					exponentiation.IsIdempotent = true;
				}
			}
			else if (operation.GetType() == typeof(Function))
			{
				Function function = (Function)operation;
				IList<Operation> list2 = (function.Arguments = function.Arguments.Select((Operation a) => Optimize(a, functionRegistry, constantRegistry)).ToList());
				function.IsIdempotent = functionRegistry.GetFunctionInfo(function.FunctionName).IsIdempotent;
				for (int i = 0; i < list2.Count; i++)
				{
					if (!function.DependsOnVariables && list2[i].DependsOnVariables)
					{
						function.DependsOnVariables = true;
					}
					if (function.IsIdempotent && !list2[i].IsIdempotent)
					{
						function.IsIdempotent = false;
					}
					if (function.DependsOnVariables && !function.IsIdempotent)
					{
						break;
					}
				}
				function.DependsOnVariables = list2.Any((Operation a) => a.DependsOnVariables);
			}
			if (!operation.DependsOnVariables && operation.IsIdempotent && operation.GetType() != typeof(IntegerConstant) && operation.GetType() != typeof(FloatingPointConstant))
			{
				return new FloatingPointConstant(executor.Execute(operation, functionRegistry, constantRegistry));
			}
			return operation;
		}

		private bool IsZero(Operation operation)
		{
			if (operation.GetType() == typeof(FloatingPointConstant))
			{
				return ((FloatingPointConstant)operation).Value == 0.0;
			}
			if (operation.GetType() == typeof(IntegerConstant))
			{
				return ((IntegerConstant)operation).Value == 0;
			}
			return false;
		}
	}
	public class TokenReader
	{
		private readonly CultureInfo cultureInfo;

		private readonly char decimalSeparator;

		private readonly char argumentSeparator;

		public TokenReader()
			: this(CultureInfo.InvariantCulture, ',')
		{
		}

		public TokenReader(CultureInfo cultureInfo, char argumentSeparator)
		{
			this.cultureInfo = cultureInfo;
			decimalSeparator = cultureInfo.NumberFormat.NumberDecimalSeparator[0];
			this.argumentSeparator = argumentSeparator;
			if (cultureInfo.NumberFormat.NumberDecimalSeparator.ToCharArray(0, 1)[0] == argumentSeparator)
			{
				throw new ArgumentException("argumentSeparator cannot be the same as NumberDecimalSeparator", "argumentSeparator");
			}
		}

		public List<Token> Read(string expression)
		{
			if (string.IsNullOrEmpty(expression))
			{
				throw new ArgumentNullException("expression");
			}
			List<Token> list = new List<Token>();
			char[] array = expression.ToCharArray();
			bool isFormulaSubPart = true;
			bool flag = false;
			for (int i = 0; i < array.Length; i++)
			{
				if (IsPartOfNumeric(array[i], isFirstCharacter: true, afterMinus: false, isFormulaSubPart))
				{
					if (array[i] == '-')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '_',
							StartPosition = i,
							Length = 1
						});
						continue;
					}
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.Append(array[i]);
					int num = i;
					while (++i < array.Length && IsPartOfNumeric(array[i], isFirstCharacter: false, array[i - 1] == '-', isFormulaSubPart))
					{
						if (flag && IsScientificNotation(array[i]))
						{
							throw new InvalidTokenParseException($"Invalid token \"{array[i]}\" detected at position {i}.", expression, i, array[i].ToString());
						}
						if (IsScientificNotation(array[i]))
						{
							flag = IsScientificNotation(array[i]);
							if (array.Length > i + 1 && array[i + 1] == '-')
							{
								stringBuilder.Append(array[i++]);
							}
						}
						stringBuilder.Append(array[i]);
					}
					if (int.TryParse(stringBuilder.ToString(), out var result))
					{
						list.Add(new Token
						{
							TokenType = TokenType.Integer,
							Value = result,
							StartPosition = num,
							Length = i - num
						});
						isFormulaSubPart = false;
					}
					else
					{
						if (!double.TryParse(stringBuilder.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, cultureInfo, out var result2))
						{
							throw new InvalidFloatingPointNumberParseException($"Invalid floating point number: {stringBuilder}", expression, num, stringBuilder.ToString());
						}
						list.Add(new Token
						{
							TokenType = TokenType.FloatingPoint,
							Value = result2,
							StartPosition = num,
							Length = i - num
						});
						flag = false;
						isFormulaSubPart = false;
					}
					if (i == array.Length)
					{
						continue;
					}
				}
				if (IsPartOfTextToken(array[i], isFirstCharacter: true))
				{
					string text = array[i].ToString() ?? "";
					int num2 = i;
					int j = i + 1;
					while (j < array.Length && IsPartOfTextToken(array[j], isFirstCharacter: false))
					{
						text += array[++i];
						j = i + 1;
					}
					int num3 = j;
					for (; array.Length > j && char.IsWhiteSpace(array[j]); j++)
					{
					}
					if (array.Length > j && array[j] == '(')
					{
						i = j;
						list.Add(new Token
						{
							TokenType = TokenType.Function,
							Value = text,
							StartPosition = num2,
							Length = num3 - num2
						});
						list.Add(new Token
						{
							TokenType = TokenType.LeftParenthesis,
							Value = '(',
							StartPosition = i,
							Length = 1
						});
						isFormulaSubPart = true;
					}
					else
					{
						list.Add(new Token
						{
							TokenType = TokenType.Symbol,
							Value = text,
							StartPosition = num2,
							Length = num3 - num2
						});
						isFormulaSubPart = false;
					}
					continue;
				}
				if (array[i] == argumentSeparator)
				{
					list.Add(new Token
					{
						TokenType = TokenType.ArgumentSeparator,
						Value = array[i],
						StartPosition = i,
						Length = 1
					});
					isFormulaSubPart = false;
					continue;
				}
				switch (array[i])
				{
				case '%':
				case '*':
				case '+':
				case '/':
				case '^':
				case '≠':
				case '≤':
				case '≥':
					list.Add(new Token
					{
						TokenType = TokenType.Operation,
						Value = array[i],
						StartPosition = i,
						Length = 1
					});
					isFormulaSubPart = true;
					break;
				case '-':
					if (IsUnaryMinus(array[i], list))
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '_',
							StartPosition = i,
							Length = 1
						});
					}
					else
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = array[i],
							StartPosition = i,
							Length = 1
						});
					}
					isFormulaSubPart = true;
					break;
				case '(':
					list.Add(new Token
					{
						TokenType = TokenType.LeftParenthesis,
						Value = array[i],
						StartPosition = i,
						Length = 1
					});
					isFormulaSubPart = true;
					break;
				case ')':
					list.Add(new Token
					{
						TokenType = TokenType.RightParenthesis,
						Value = array[i],
						StartPosition = i,
						Length = 1
					});
					isFormulaSubPart = false;
					break;
				case '<':
					if (i + 1 < array.Length && array[i + 1] == '=')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '≤',
							StartPosition = i++,
							Length = 2
						});
					}
					else
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '<',
							StartPosition = i,
							Length = 1
						});
					}
					isFormulaSubPart = false;
					break;
				case '>':
					if (i + 1 < array.Length && array[i + 1] == '=')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '≥',
							StartPosition = i++,
							Length = 2
						});
					}
					else
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '>',
							StartPosition = i,
							Length = 1
						});
					}
					isFormulaSubPart = false;
					break;
				case '!':
					if (i + 1 < array.Length && array[i + 1] == '=')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '≠',
							StartPosition = i++,
							Length = 2
						});
						isFormulaSubPart = false;
						break;
					}
					throw new InvalidTokenParseException($"Invalid token \"{array[i]}\" detected at position {i}.", expression, i, array[i].ToString());
				case '&':
					if (i + 1 < array.Length && array[i + 1] == '&')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '&',
							StartPosition = i++,
							Length = 2
						});
						isFormulaSubPart = false;
						break;
					}
					throw new InvalidTokenParseException($"Invalid token \"{array[i]}\" detected at position {i}.", expression, i, array[i].ToString());
				case '|':
					if (i + 1 < array.Length && array[i + 1] == '|')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '|',
							StartPosition = i++,
							Length = 2
						});
						isFormulaSubPart = false;
						break;
					}
					throw new InvalidTokenParseException($"Invalid token \"{array[i]}\" detected at position {i}.", expression, i, array[i].ToString());
				case '=':
					if (i + 1 < array.Length && array[i + 1] == '=')
					{
						list.Add(new Token
						{
							TokenType = TokenType.Operation,
							Value = '=',
							StartPosition = i++,
							Length = 2
						});
						isFormulaSubPart = false;
						break;
					}
					throw new InvalidTokenParseException($"Invalid token \"{array[i]}\" detected at position {i}.", expression, i, array[i].ToString());
				case '\'':
				{
					string text2 = "";
					int num4 = i;
					int num5 = i + 1;
					while (num5 < array.Length && array[num5] != '\'')
					{
						text2 += array[++i];
						num5 = i + 1;
					}
					if (num5 == array.Length)
					{
						throw new MissingQuoteParseException($"Missing corresponding quote to quote sign at position {num4}.", expression, num4);
					}
					i++;
					int num6 = ++num5;
					for (; array.Length > num5 && char.IsWhiteSpace(array[num5]); num5++)
					{
					}
					if (array.Length > num5 && array[num5] == '(')
					{
						i = num5;
						list.Add(new Token
						{
							TokenType = TokenType.Function,
							Value = text2,
							StartPosition = num4,
							Length = num6 - num4
						});
						list.Add(new Token
						{
							TokenType = TokenType.LeftParenthesis,
							Value = '(',
							StartPosition = i,
							Length = 1
						});
						isFormulaSubPart = true;
					}
					else
					{
						list.Add(new Token
						{
							TokenType = TokenType.Symbol,
							Value = text2,
							StartPosition = num4,
							Length = num6 - num4
						});
						isFormulaSubPart = false;
					}
					break;
				}
				default:
					throw new InvalidTokenParseException($"Invalid token \"{array[i]}\" detected at position {i}.", expression, i, array[i].ToString());
				case ' ':
					break;
				}
			}
			return list;
		}

		private bool IsPartOfNumeric(char character, bool isFirstCharacter, bool afterMinus, bool isFormulaSubPart)
		{
			if (character != decimalSeparator && (character < '0' || character > '9') && (!(isFormulaSubPart && isFirstCharacter) || character != '-') && (isFirstCharacter || afterMinus || character != 'e'))
			{
				if (!isFirstCharacter)
				{
					return character == 'E';
				}
				return false;
			}
			return true;
		}

		private bool IsPartOfTextToken(char character, bool isFirstCharacter)
		{
			if ((character < 'a' || character > 'z') && (character < 'A' || character > 'Z') && (isFirstCharacter || character < '0' || character > '9'))
			{
				if (!isFirstCharacter)
				{
					return character == '_';
				}
				return false;
			}
			return true;
		}

		private bool IsUnaryMinus(char currentToken, IList<Token> tokens)
		{
			if (currentToken != '-')
			{
				return false;
			}
			Token token = tokens[tokens.Count - 1];
			if (token.TokenType != TokenType.FloatingPoint && token.TokenType != 0 && token.TokenType != TokenType.Symbol)
			{
				return token.TokenType != TokenType.RightParenthesis;
			}
			return false;
		}

		private bool IsScientificNotation(char currentToken)
		{
			if (currentToken != 'e')
			{
				return currentToken == 'E';
			}
			return true;
		}
	}
	public enum TokenType
	{
		Integer,
		FloatingPoint,
		Symbol,
		Function,
		Operation,
		LeftParenthesis,
		RightParenthesis,
		ArgumentSeparator
	}
	public class VariableValidator
	{
		public void Validate(Operation operation, IList<string> variables)
		{
			Type type = operation.GetType();
			if (type == typeof(Addition))
			{
				Addition addition = (Addition)operation;
				Validate(addition.Argument1, variables);
				Validate(addition.Argument2, variables);
			}
			else if (type == typeof(Subtraction))
			{
				Subtraction subtraction = (Subtraction)operation;
				Validate(subtraction.Argument1, variables);
				Validate(subtraction.Argument2, variables);
			}
			else if (type == typeof(Multiplication))
			{
				Multiplication multiplication = (Multiplication)operation;
				Validate(multiplication.Argument1, variables);
				Validate(multiplication.Argument2, variables);
			}
			else if (type == typeof(Division))
			{
				Division division = (Division)operation;
				Validate(division.Dividend, variables);
				Validate(division.Divisor, variables);
			}
			else if (type == typeof(Exponentiation))
			{
				Exponentiation exponentiation = (Exponentiation)operation;
				Validate(exponentiation.Base, variables);
				Validate(exponentiation.Exponent, variables);
			}
			else if (type == typeof(Variable))
			{
				Variable variable = (Variable)operation;
				if (!variables.Contains(variable.Name))
				{
					throw new VariableNotDefinedException("Variable '" + variable.Name + "' is not defined.", variable.Name);
				}
			}
			else
			{
				if (!(type == typeof(Function)))
				{
					return;
				}
				foreach (Operation argument in ((Function)operation).Arguments)
				{
					Validate(argument, variables);
				}
			}
		}
	}
}
namespace Adletec.Sonic.Parsing.Tokenizing
{
	public struct Token
	{
		public int StartPosition;

		public int Length;

		public TokenType TokenType;

		public object Value;
	}
}
namespace Adletec.Sonic.Operations
{
	public class Addition : Operation
	{
		public Operation Argument1 { get; internal set; }

		public Operation Argument2 { get; internal set; }

		public Addition(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class And : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public And(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public abstract class Constant<T> : Operation
	{
		public T Value { get; }

		protected Constant(DataType dataType, T value)
			: base(dataType, dependsOnVariables: false, isIdempotent: true)
		{
			Value = value;
		}

		public override bool Equals(object obj)
		{
			if (obj is Constant<T> constant)
			{
				return Value.Equals(constant.Value);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return Value.GetHashCode();
		}
	}
	public class IntegerConstant : Constant<int>
	{
		public IntegerConstant(int value)
			: base(DataType.Integer, value)
		{
		}
	}
	public class FloatingPointConstant : Constant<double>
	{
		public FloatingPointConstant(double value)
			: base(DataType.FloatingPoint, value)
		{
		}
	}
	public class Division : Operation
	{
		public Operation Dividend { get; internal set; }

		public Operation Divisor { get; internal set; }

		public Division(DataType dataType, Operation dividend, Operation divisor)
			: base(dataType, dividend.DependsOnVariables || divisor.DependsOnVariables, dividend.IsIdempotent && divisor.IsIdempotent)
		{
			Dividend = dividend;
			Divisor = divisor;
		}
	}
	public class Equal : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public Equal(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class Exponentiation : Operation
	{
		public Operation Base { get; internal set; }

		public Operation Exponent { get; internal set; }

		public Exponentiation(DataType dataType, Operation @base, Operation exponent)
			: base(dataType, @base.DependsOnVariables || exponent.DependsOnVariables, @base.IsIdempotent && exponent.IsIdempotent)
		{
			Base = @base;
			Exponent = exponent;
		}
	}
	public class Function : Operation
	{
		private IList<Operation> arguments;

		public string FunctionName { get; private set; }

		public IList<Operation> Arguments
		{
			get
			{
				return arguments;
			}
			internal set
			{
				arguments = value;
				base.DependsOnVariables = arguments.FirstOrDefault((Operation o) => o.DependsOnVariables) != null;
			}
		}

		public Function(DataType dataType, string functionName, IList<Operation> arguments, bool isIdempotent)
			: base(dataType, arguments.FirstOrDefault((Operation o) => o.DependsOnVariables) != null, isIdempotent && arguments.All((Operation o) => o.IsIdempotent))
		{
			FunctionName = functionName;
			this.arguments = arguments;
		}
	}
	public class GreaterOrEqualThan : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public GreaterOrEqualThan(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class GreaterThan : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public GreaterThan(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class LessOrEqualThan : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public LessOrEqualThan(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class LessThan : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public LessThan(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class Modulo : Operation
	{
		public Operation Dividend { get; }

		public Operation Divisor { get; }

		public Modulo(DataType dataType, Operation dividend, Operation divisor)
			: base(dataType, dividend.DependsOnVariables || divisor.DependsOnVariables, dividend.IsIdempotent && divisor.IsIdempotent)
		{
			Dividend = dividend;
			Divisor = divisor;
		}
	}
	public class Multiplication : Operation
	{
		public Operation Argument1 { get; internal set; }

		public Operation Argument2 { get; internal set; }

		public Multiplication(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class NotEqual : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public NotEqual(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public abstract class Operation
	{
		public DataType DataType { get; private set; }

		public bool DependsOnVariables { get; internal set; }

		public bool IsIdempotent { get; internal set; }

		protected Operation(DataType dataType, bool dependsOnVariables, bool isIdempotent)
		{
			DataType = dataType;
			DependsOnVariables = dependsOnVariables;
			IsIdempotent = isIdempotent;
		}
	}
	public class Or : Operation
	{
		public Operation Argument1 { get; }

		public Operation Argument2 { get; }

		public Or(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class Subtraction : Operation
	{
		public Operation Argument1 { get; internal set; }

		public Operation Argument2 { get; internal set; }

		public Subtraction(DataType dataType, Operation argument1, Operation argument2)
			: base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
		{
			Argument1 = argument1;
			Argument2 = argument2;
		}
	}
	public class UnaryMinus : Operation
	{
		public Operation Argument { get; }

		public UnaryMinus(DataType dataType, Operation argument)
			: base(dataType, argument.DependsOnVariables, argument.IsIdempotent)
		{
			Argument = argument;
		}
	}
	public class Variable : Operation
	{
		public string Name { get; }

		public Variable(string name)
			: base(DataType.FloatingPoint, dependsOnVariables: true, isIdempotent: false)
		{
			Name = name;
		}

		public override bool Equals(object obj)
		{
			if (obj is Variable variable)
			{
				return Name.Equals(variable.Name);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return Name.GetHashCode();
		}
	}
}
namespace Adletec.Sonic.Execution
{
	public class ConstantInfo
	{
		public string ConstantName { get; private set; }

		public double Value { get; private set; }

		public ConstantInfo(string constantName, double value)
		{
			ConstantName = constantName;
			Value = value;
		}
	}
	public class ConstantRegistry : IConstantRegistry, IEnumerable<ConstantInfo>, IEnumerable
	{
		private readonly Dictionary<string, ConstantInfo> constants;

		private readonly bool guardedMode;

		public ConstantRegistry(bool caseSensitive, bool guardedMode)
		{
			constants = (caseSensitive ? new Dictionary<string, ConstantInfo>() : new Dictionary<string, ConstantInfo>(StringComparer.OrdinalIgnoreCase));
			this.guardedMode = guardedMode;
		}

		public IEnumerator<ConstantInfo> GetEnumerator()
		{
			return constants.Values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public ConstantInfo GetConstantInfo(string constantName)
		{
			if (string.IsNullOrEmpty(constantName))
			{
				throw new ArgumentNullException("constantName");
			}
			if (!constants.TryGetValue(constantName, out var value))
			{
				return null;
			}
			return value;
		}

		public bool IsConstantName(string constantName)
		{
			if (string.IsNullOrEmpty(constantName))
			{
				throw new ArgumentNullException("constantName");
			}
			return constants.ContainsKey(constantName);
		}

		public void RegisterConstant(string constantName, double value)
		{
			if (string.IsNullOrEmpty(constantName))
			{
				throw new ArgumentNullException("constantName");
			}
			if (guardedMode && constants.ContainsKey(constantName))
			{
				throw new ArgumentException("The constant \"" + constantName + "\" cannot be overwritten.");
			}
			ConstantInfo value2 = new ConstantInfo(constantName, value);
			constants[constantName] = value2;
		}
	}
	public class DynamicCompiler : IExecutor
	{
		private static class PrecompiledMethods
		{
			public static double GetVariableValueOrThrow(string variableName, FormulaContext context)
			{
				if (context.Variables.TryGetValue(variableName, out var value))
				{
					return value;
				}
				throw new VariableNotDefinedException("The variable \"" + variableName + "\" used is not defined.", variableName);
			}
		}

		private readonly string funcAssemblyQualifiedName;

		private readonly bool caseSensitive;

		private readonly bool guardedMode;

		public DynamicCompiler()
			: this(caseSensitive: false, guardedMode: false)
		{
		}

		public DynamicCompiler(bool caseSensitive, bool guardedMode)
		{
			this.caseSensitive = caseSensitive;
			this.guardedMode = guardedMode;
			funcAssemblyQualifiedName = typeof(Func<double, double, double, double, double, double, double, double, double, double>).GetTypeInfo().Assembly.FullName;
		}

		public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			return Execute(operation, functionRegistry, constantRegistry, new Dictionary<string, double>());
		}

		public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry, IDictionary<string, double> variables)
		{
			return BuildFormula(operation, functionRegistry, constantRegistry)(variables);
		}

		public Func<IDictionary<string, double>, double> BuildFormula(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			Func<FormulaContext, double> func = BuildFormulaInternal(operation, functionRegistry);
			if (caseSensitive)
			{
				if (guardedMode)
				{
					return delegate(IDictionary<string, double> variables)
					{
						VariableVerifier.VerifyVariableNames(variables, constantRegistry, functionRegistry);
						FormulaContext arg4 = new FormulaContext(variables, functionRegistry, constantRegistry);
						return func(arg4);
					};
				}
				return delegate(IDictionary<string, double> variables)
				{
					FormulaContext arg3 = new FormulaContext(variables, functionRegistry, constantRegistry);
					return func(arg3);
				};
			}
			if (guardedMode)
			{
				return delegate(IDictionary<string, double> variables)
				{
					variables = EngineUtil.ConvertToCaseInsensitiveDictionary(variables);
					VariableVerifier.VerifyVariableNames(variables, constantRegistry, functionRegistry);
					FormulaContext arg2 = new FormulaContext(variables, functionRegistry, constantRegistry);
					return func(arg2);
				};
			}
			return delegate(IDictionary<string, double> variables)
			{
				variables = EngineUtil.ConvertToCaseInsensitiveDictionary(variables);
				FormulaContext arg = new FormulaContext(variables, functionRegistry, constantRegistry);
				return func(arg);
			};
		}

		private Func<FormulaContext, double> BuildFormulaInternal(Operation operation, IFunctionRegistry functionRegistry)
		{
			ParameterExpression parameterExpression = Expression.Parameter(typeof(FormulaContext), "context");
			return Expression.Lambda<Func<FormulaContext, double>>(GenerateMethodBody(operation, parameterExpression, functionRegistry), new ParameterExpression[1] { parameterExpression }).Compile();
		}

		private Expression GenerateMethodBody(Operation operation, ParameterExpression contextParameter, IFunctionRegistry functionRegistry)
		{
			if (operation == null)
			{
				throw new ArgumentNullException("operation");
			}
			if (operation.GetType() == typeof(IntegerConstant))
			{
				return Expression.Constant((double)((IntegerConstant)operation).Value, typeof(double));
			}
			if (operation.GetType() == typeof(FloatingPointConstant))
			{
				return Expression.Constant(((FloatingPointConstant)operation).Value, typeof(double));
			}
			if (operation.GetType() == typeof(Variable))
			{
				Variable variable = (Variable)operation;
				Func<string, FormulaContext, double> del = PrecompiledMethods.GetVariableValueOrThrow;
				return Expression.Call(null, del.GetMethodInfo(), Expression.Constant(variable.Name), contextParameter);
			}
			if (operation.GetType() == typeof(Multiplication))
			{
				Multiplication multiplication = (Multiplication)operation;
				Expression left = GenerateMethodBody(multiplication.Argument1, contextParameter, functionRegistry);
				Expression right = GenerateMethodBody(multiplication.Argument2, contextParameter, functionRegistry);
				return Expression.Multiply(left, right);
			}
			if (operation.GetType() == typeof(Addition))
			{
				Addition addition = (Addition)operation;
				Expression left2 = GenerateMethodBody(addition.Argument1, contextParameter, functionRegistry);
				Expression right2 = GenerateMethodBody(addition.Argument2, contextParameter, functionRegistry);
				return Expression.Add(left2, right2);
			}
			if (operation.GetType() == typeof(Subtraction))
			{
				Subtraction subtraction = (Subtraction)operation;
				Expression left3 = GenerateMethodBody(subtraction.Argument1, contextParameter, functionRegistry);
				Expression right3 = GenerateMethodBody(subtraction.Argument2, contextParameter, functionRegistry);
				return Expression.Subtract(left3, right3);
			}
			if (operation.GetType() == typeof(Division))
			{
				Division division = (Division)operation;
				Expression left4 = GenerateMethodBody(division.Dividend, contextParameter, functionRegistry);
				Expression right4 = GenerateMethodBody(division.Divisor, contextParameter, functionRegistry);
				return Expression.Divide(left4, right4);
			}
			if (operation.GetType() == typeof(Modulo))
			{
				Modulo modulo = (Modulo)operation;
				Expression left5 = GenerateMethodBody(modulo.Dividend, contextParameter, functionRegistry);
				Expression right5 = GenerateMethodBody(modulo.Divisor, contextParameter, functionRegistry);
				return Expression.Modulo(left5, right5);
			}
			if (operation.GetType() == typeof(Exponentiation))
			{
				Exponentiation exponentiation = (Exponentiation)operation;
				Expression arg = GenerateMethodBody(exponentiation.Base, contextParameter, functionRegistry);
				Expression arg2 = GenerateMethodBody(exponentiation.Exponent, contextParameter, functionRegistry);
				return Expression.Call(null, typeof(Math).GetRuntimeMethod("Pow", new Type[2]
				{
					typeof(double),
					typeof(double)
				}), arg, arg2);
			}
			if (operation.GetType() == typeof(UnaryMinus))
			{
				UnaryMinus unaryMinus = (UnaryMinus)operation;
				return Expression.Negate(GenerateMethodBody(unaryMinus.Argument, contextParameter, functionRegistry));
			}
			if (operation.GetType() == typeof(And))
			{
				And and = (And)operation;
				BinaryExpression left6 = Expression.NotEqual(GenerateMethodBody(and.Argument1, contextParameter, functionRegistry), Expression.Constant(0.0));
				Expression right6 = Expression.NotEqual(GenerateMethodBody(and.Argument2, contextParameter, functionRegistry), Expression.Constant(0.0));
				return Expression.Condition(Expression.And(left6, right6), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(Or))
			{
				Or or = (Or)operation;
				BinaryExpression left7 = Expression.NotEqual(GenerateMethodBody(or.Argument1, contextParameter, functionRegistry), Expression.Constant(0.0));
				Expression right7 = Expression.NotEqual(GenerateMethodBody(or.Argument2, contextParameter, functionRegistry), Expression.Constant(0.0));
				return Expression.Condition(Expression.Or(left7, right7), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(LessThan))
			{
				LessThan lessThan = (LessThan)operation;
				Expression left8 = GenerateMethodBody(lessThan.Argument1, contextParameter, functionRegistry);
				Expression right8 = GenerateMethodBody(lessThan.Argument2, contextParameter, functionRegistry);
				return Expression.Condition(Expression.LessThan(left8, right8), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(LessOrEqualThan))
			{
				LessOrEqualThan lessOrEqualThan = (LessOrEqualThan)operation;
				Expression left9 = GenerateMethodBody(lessOrEqualThan.Argument1, contextParameter, functionRegistry);
				Expression right9 = GenerateMethodBody(lessOrEqualThan.Argument2, contextParameter, functionRegistry);
				return Expression.Condition(Expression.LessThanOrEqual(left9, right9), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(GreaterThan))
			{
				GreaterThan greaterThan = (GreaterThan)operation;
				Expression left10 = GenerateMethodBody(greaterThan.Argument1, contextParameter, functionRegistry);
				Expression right10 = GenerateMethodBody(greaterThan.Argument2, contextParameter, functionRegistry);
				return Expression.Condition(Expression.GreaterThan(left10, right10), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(GreaterOrEqualThan))
			{
				GreaterOrEqualThan greaterOrEqualThan = (GreaterOrEqualThan)operation;
				Expression left11 = GenerateMethodBody(greaterOrEqualThan.Argument1, contextParameter, functionRegistry);
				Expression right11 = GenerateMethodBody(greaterOrEqualThan.Argument2, contextParameter, functionRegistry);
				return Expression.Condition(Expression.GreaterThanOrEqual(left11, right11), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(Equal))
			{
				Equal equal = (Equal)operation;
				Expression left12 = GenerateMethodBody(equal.Argument1, contextParameter, functionRegistry);
				Expression right12 = GenerateMethodBody(equal.Argument2, contextParameter, functionRegistry);
				return Expression.Condition(Expression.Equal(left12, right12), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(NotEqual))
			{
				NotEqual notEqual = (NotEqual)operation;
				Expression left13 = GenerateMethodBody(notEqual.Argument1, contextParameter, functionRegistry);
				Expression right13 = GenerateMethodBody(notEqual.Argument2, contextParameter, functionRegistry);
				return Expression.Condition(Expression.NotEqual(left13, right13), Expression.Constant(1.0), Expression.Constant(0.0));
			}
			if (operation.GetType() == typeof(Function))
			{
				Function function = (Function)operation;
				FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(function.FunctionName);
				Type type;
				Type[] parameters;
				Expression[] array2;
				if (functionInfo.IsDynamicFunc)
				{
					type = typeof(DynamicFunc<double, double>);
					parameters = new Type[1] { typeof(double[]) };
					Expression[] array = new Expression[function.Arguments.Count];
					for (int j = 0; j < function.Arguments.Count; j++)
					{
						array[j] = GenerateMethodBody(function.Arguments[j], contextParameter, functionRegistry);
					}
					array2 = new Expression[1] { Expression.NewArrayInit(typeof(double), array) };
				}
				else
				{
					type = GetFuncType(functionInfo.NumberOfParameters);
					parameters = (from i in Enumerable.Range(0, functionInfo.NumberOfParameters)
						select typeof(double)).ToArray();
					array2 = new Expression[functionInfo.NumberOfParameters];
					for (int k = 0; k < functionInfo.NumberOfParameters; k++)
					{
						array2[k] = GenerateMethodBody(function.Arguments[k], contextParameter, functionRegistry);
					}
				}
				return Expression.Call(Expression.Convert(Expression.Property(Expression.Call(Expression.Property(contextParameter, "FunctionRegistry"), typeof(IFunctionRegistry).GetRuntimeMethod("GetFunctionInfo", new Type[1] { typeof(string) }), Expression.Constant(function.FunctionName)), "Function"), type), type.GetRuntimeMethod("Invoke", parameters), array2);
			}
			throw new ArgumentException("Unsupported operation \"" + operation.GetType().FullName + "\".", "operation");
		}

		private Type GetFuncType(int numberOfParameters)
		{
			string text = ((numberOfParameters < 9) ? $"System.Func`{numberOfParameters + 1}" : $"System.Func`{numberOfParameters + 1}, {funcAssemblyQualifiedName}");
			Type type = Type.GetType(text);
			if (type == null)
			{
				throw new InvalidOperationException("Couldn't get type of $" + text + ".");
			}
			Type[] array = new Type[numberOfParameters + 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = typeof(double);
			}
			return type.MakeGenericType(array);
		}
	}
	public enum ExecutionMode
	{
		Interpreted,
		Compiled
	}
	public class FunctionInfo
	{
		public string FunctionName { get; }

		public int NumberOfParameters { get; }

		public bool IsIdempotent { get; }

		public bool IsDynamicFunc { get; }

		public Delegate Function { get; }

		public FunctionInfo(string functionName, int numberOfParameters, bool isIdempotent, bool isDynamicFunc, Delegate function)
		{
			FunctionName = functionName;
			NumberOfParameters = numberOfParameters;
			IsIdempotent = isIdempotent;
			IsDynamicFunc = isDynamicFunc;
			Function = function;
		}
	}
	public class FunctionRegistry : IFunctionRegistry, IEnumerable<FunctionInfo>, IEnumerable
	{
		private const string DynamicFuncName = "Adletec.Sonic.DynamicFunc";

		private readonly Dictionary<string, FunctionInfo> functions;

		private readonly bool guardedMode;

		public FunctionRegistry(bool caseSensitive, bool guardedMode)
		{
			functions = (caseSensitive ? new Dictionary<string, FunctionInfo>() : new Dictionary<string, FunctionInfo>(StringComparer.OrdinalIgnoreCase));
			this.guardedMode = guardedMode;
		}

		public IEnumerator<FunctionInfo> GetEnumerator()
		{
			return functions.Values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public FunctionInfo GetFunctionInfo(string functionName)
		{
			if (string.IsNullOrEmpty(functionName))
			{
				throw new ArgumentNullException("functionName");
			}
			if (!functions.TryGetValue(functionName, out var value))
			{
				return null;
			}
			return value;
		}

		public void RegisterFunction(string functionName, Delegate function)
		{
			RegisterFunction(functionName, function, isIdempotent: true);
		}

		public void RegisterFunction(string functionName, Delegate function, bool isIdempotent)
		{
			if (string.IsNullOrEmpty(functionName))
			{
				throw new ArgumentNullException("functionName");
			}
			if ((object)function == null)
			{
				throw new ArgumentNullException("function");
			}
			Type type = function.GetType();
			bool flag = false;
			int num = -1;
			if (type.FullName != null && type.FullName.StartsWith("System.Func"))
			{
				Type[] genericTypeArguments = type.GenericTypeArguments;
				for (int i = 0; i < genericTypeArguments.Length; i++)
				{
					if (genericTypeArguments[i] != typeof(double))
					{
						throw new ArgumentException("Only doubles are supported as function arguments.", "function");
					}
				}
				num = function.GetMethodInfo().GetParameters().Count((System.Reflection.ParameterInfo p) => p.ParameterType == typeof(double));
			}
			else
			{
				if (type.FullName == null || !type.FullName.StartsWith("Adletec.Sonic.DynamicFunc"))
				{
					throw new ArgumentException("Only System.Func and Adletec.Sonic.DynamicFunc delegates are permitted.", "function");
				}
				flag = true;
			}
			if (guardedMode && functions.ContainsKey(functionName))
			{
				throw new ArgumentException("The function \"" + functionName + "\" cannot be overwritten.");
			}
			if (functions.ContainsKey(functionName) && functions[functionName].NumberOfParameters != num)
			{
				throw new ArgumentException("The number of parameters cannot be changed when overwriting a method.");
			}
			if (functions.ContainsKey(functionName) && functions[functionName].IsDynamicFunc != flag)
			{
				throw new ArgumentException("A Func can only be overwritten by another Func and a DynamicFunc can only be overwritten by another DynamicFunc.");
			}
			FunctionInfo value = new FunctionInfo(functionName, num, isIdempotent, flag, function);
			functions[functionName] = value;
		}

		public bool IsFunctionName(string functionName)
		{
			if (string.IsNullOrEmpty(functionName))
			{
				throw new ArgumentNullException("functionName");
			}
			return functions.ContainsKey(functionName);
		}
	}
	public interface IConstantRegistry : IEnumerable<ConstantInfo>, IEnumerable
	{
		ConstantInfo GetConstantInfo(string constantName);

		bool IsConstantName(string constantName);

		void RegisterConstant(string constantName, double value);
	}
	public interface IExecutor
	{
		double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry);

		double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry, IDictionary<string, double> variables);

		Func<IDictionary<string, double>, double> BuildFormula(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry);
	}
	public interface IFunctionRegistry : IEnumerable<FunctionInfo>, IEnumerable
	{
		FunctionInfo GetFunctionInfo(string functionName);

		bool IsFunctionName(string functionName);

		void RegisterFunction(string functionName, Delegate function);

		void RegisterFunction(string functionName, Delegate function, bool isIdempotent);
	}
	public class Interpreter : IExecutor
	{
		private readonly bool caseSensitive;

		private readonly bool guardedMode;

		public Interpreter()
			: this(caseSensitive: false, guardedMode: false)
		{
		}

		public Interpreter(bool caseSensitive, bool guardedMode)
		{
			this.caseSensitive = caseSensitive;
			this.guardedMode = guardedMode;
		}

		public Func<IDictionary<string, double>, double> BuildFormula(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			if (caseSensitive)
			{
				if (guardedMode)
				{
					return delegate(IDictionary<string, double> variables)
					{
						VariableVerifier.VerifyVariableNames(variables, constantRegistry, functionRegistry);
						return Execute(operation, functionRegistry, constantRegistry, variables);
					};
				}
				return (IDictionary<string, double> variables) => Execute(operation, functionRegistry, constantRegistry, variables);
			}
			if (guardedMode)
			{
				return delegate(IDictionary<string, double> variables)
				{
					variables = EngineUtil.ConvertToCaseInsensitiveDictionary(variables);
					VariableVerifier.VerifyVariableNames(variables, constantRegistry, functionRegistry);
					return Execute(operation, functionRegistry, constantRegistry, variables);
				};
			}
			return delegate(IDictionary<string, double> variables)
			{
				variables = EngineUtil.ConvertToCaseInsensitiveDictionary(variables);
				return Execute(operation, functionRegistry, constantRegistry, variables);
			};
		}

		public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
		{
			return Execute(operation, functionRegistry, constantRegistry, new Dictionary<string, double>());
		}

		public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry, IDictionary<string, double> variables)
		{
			return ExecuteInternal(operation, functionRegistry, variables);
		}

		private double ExecuteInternal(Operation operation, IFunctionRegistry functionRegistry, IDictionary<string, double> variables)
		{
			if (operation == null)
			{
				throw new ArgumentNullException("operation");
			}
			if (operation.GetType() == typeof(IntegerConstant))
			{
				return ((IntegerConstant)operation).Value;
			}
			if (operation.GetType() == typeof(FloatingPointConstant))
			{
				return ((FloatingPointConstant)operation).Value;
			}
			if (operation.GetType() == typeof(Variable))
			{
				Variable variable = (Variable)operation;
				if (variables.TryGetValue(variable.Name, out var value))
				{
					return value;
				}
				throw new VariableNotDefinedException("The variable \"" + variable.Name + "\" used is not defined.", variable.Name);
			}
			if (operation.GetType() == typeof(Multiplication))
			{
				Multiplication multiplication = (Multiplication)operation;