public void ParseUnitTest() { UnitsCore uc = new UnitsCore(); var cm = new Unit("cm", Dimensions.Length, 0.01); var ft = new Unit("ft", Dimensions.Length, 0.3048); var min = new Unit("min", Dimensions.Time, 60.0); var s = new Unit("s", Dimensions.Time, 1.0); var cfm = new Unit("CFM", ft.Pow(3) / min); uc.RegisterUnit(cm); uc.RegisterUnit(s); var u1 = uc.ParseUnit("cm"); Assert.IsTrue(u1 == cm); var u2 = uc.ParseUnit("cm*cm"); Assert.IsTrue(u2 == cm * cm); var u3 = uc.ParseUnit("cm^2"); Assert.IsTrue(u3 == cm * cm); var u4 = uc.ParseUnit("cm^2.5/cm^1.5"); Assert.IsTrue(u4 == cm); //flow over area -> speed var u5 = uc.ParseUnit("cm^3*s^-1/cm^2"); Assert.IsTrue(u5 == cm / s); }
public void BuildTest() { UnitsCore uc = new UnitsCore(); uc.RegisterUnit(new Unit("Pa", Dimensions.Pressure, 1)); var tokenizer = new TokenReader(); var tokens = tokenizer.Read("-1+2-3[Pa]^2*33.21+1e-8"); var ast = new AstBuilder(null).Build(tokens, uc); }
public void ParseNumberTest() { UnitsCore uc = new UnitsCore(); var kPa = new Unit("kPa", Dimensions.Pressure, 1000); uc.RegisterUnit(kPa); var un = uc.ParseNumber("101.325[kPa]"); Assert.AreEqual(un.Number, 101.325, 1e-8); Assert.IsTrue(un.Unit == kPa); }
/// <summary> /// Creates a new instance of the <see cref="CalculationEngine"/> class. /// </summary> /// <param name="cultureInfo"> /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. /// </param> /// <param name="executionMode">The execution mode that must be used for formula execution.</param> /// <param name="cacheEnabled">Enable or disable caching of mathematical formulas.</param> /// <param name="optimizerEnabled">Enable or disable optimizing of formulas.</param> /// <param name="defaultFunctions">Enable or disable the default functions.</param> /// <param name="defaultConstants">Enable or disable the default constants.</param> public CalculationEngine(UnitsCore unitsCore, CultureInfo cultureInfo, ExecutionMode executionMode, bool cacheEnabled, bool optimizerEnabled, bool defaultFunctions, bool defaultConstants) { this.executionFormulaCache = new MemoryCache <string, Func <IDictionary <string, ExecutionResult>, ExecutionResult> >(); this.FunctionRegistry = new FunctionRegistry(false); this.ConstantRegistry = new ConstantRegistry(false); this.cultureInfo = cultureInfo; this.cacheEnabled = cacheEnabled; this.optimizerEnabled = optimizerEnabled; this.unitsCore = unitsCore; if (executionMode == ExecutionMode.Interpreted) { executor = new Interpreter(); } else if (executionMode == ExecutionMode.Compiled) { executor = new DynamicCompiler(); } else { throw new ArgumentException(string.Format("Unsupported execution mode \"{0}\".", executionMode), "executionMode"); } optimizer = new Optimizer(new Interpreter()); // We run the optimizer with the interpreter // Register the default constants of Jace.NET into the constant registry if (defaultConstants) { RegisterDefaultConstants(); } // Register the default functions of Jace.NET into the function registry if (defaultFunctions) { RegisterDefaultFunctions(); } }
/// <summary> /// Creates a new instance of the <see cref="CalculationEngine"/> class. /// </summary> /// <param name="cultureInfo"> /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. /// </param> /// <param name="executionMode">The execution mode that must be used for formula execution.</param> /// <param name="cacheEnabled">Enable or disable caching of mathematical formulas.</param> /// <param name="optimizerEnabled">Enable or disable optimizing of formulas.</param> public CalculationEngine(UnitsCore unitsCore, CultureInfo cultureInfo, ExecutionMode executionMode, bool cacheEnabled, bool optimizerEnabled) : this(unitsCore, cultureInfo, executionMode, cacheEnabled, optimizerEnabled, true, true) { }
/// <summary> /// Creates a new instance of the <see cref="CalculationEngine"/> class. The optimizer and /// cache are enabled. /// </summary> /// <param name="cultureInfo"> /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. /// </param> /// <param name="executionMode">The execution mode that must be used for formula execution.</param> public CalculationEngine(UnitsCore unitsCore, CultureInfo cultureInfo, ExecutionMode executionMode) : this(unitsCore, cultureInfo, executionMode, true, true) { }
/// <summary> /// Creates a new instance of the <see cref="CalculationEngine"/> class. The dynamic compiler /// is used for formula execution and the optimizer and cache are enabled. /// </summary> /// <param name="cultureInfo"> /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers. /// </param> public CalculationEngine(UnitsCore unitsCore, CultureInfo cultureInfo) : this(unitsCore, cultureInfo, ExecutionMode.Compiled) { }
/// <summary> /// Creates a new instance of the <see cref="CalculationEngine"/> class with /// default parameters. /// </summary> public CalculationEngine(UnitsCore unitsCore) : this(unitsCore, CultureInfo.CurrentCulture, ExecutionMode.Compiled) { }
public void CalculateTest() { UnitsCore uc = new UnitsCore(); var inch = new Unit("in", Dimensions.Length, 0.0254); var foot = new Unit("ft", 12 * inch); var sec = new Unit("s", Dimensions.Time, 1.0); var min = new Unit("min", Dimensions.Time, 60); var cfm = new Unit("CFM", foot.Pow(3) / min); uc.RegisterUnit(cfm); uc.RegisterUnit(inch); uc.RegisterUnit(foot); uc.RegisterUnit(sec); var oneFoot = new UnitNumber(1, foot); var ce = new CalculationEngine(uc); var vars = new Dictionary <string, ExecutionResult>(); vars.Add("A", new ExecutionResult(1)); vars.Add("B", new ExecutionResult(oneFoot)); ExecutionResult result; //A number result = ce.Calculate("1.515"); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, 1.515, 1e-8); //A number with unit result = ce.Calculate("1.0[ft]"); Assert.AreEqual(result.DataType, DataType.UnitNumber); Assert.IsTrue((UnitNumber)result.Value == oneFoot); result = ce.Calculate("-1.0[ft]"); Assert.AreEqual(result.DataType, DataType.UnitNumber); Assert.IsTrue((UnitNumber)result.Value == -oneFoot); //Unitless calculations result = ce.Calculate("1+0.515"); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, 1.515, 1e-8); //Calculations with unit result = ce.Calculate("0.5[ft]+6[in]"); Assert.AreEqual(result.DataType, DataType.UnitNumber); Assert.IsTrue((UnitNumber)result.Value == oneFoot); //Constant result = ce.Calculate("pi"); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, Math.PI, 1e-8); result = ce.Calculate("-pi"); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, -Math.PI, 1e-8); result = ce.Calculate("1-pi"); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, 1 - Math.PI, 1e-8); //Variable result = ce.Calculate("B", vars); Assert.AreEqual(result.DataType, DataType.UnitNumber); Assert.IsTrue((UnitNumber)result.Value == oneFoot); result = ce.Calculate("-B", vars); Assert.AreEqual(result.DataType, DataType.UnitNumber); Assert.IsTrue((UnitNumber)result.Value == -oneFoot); result = ce.Calculate("1[ft]-B", vars); Assert.AreEqual(result.DataType, DataType.UnitNumber); Assert.IsTrue((UnitNumber)result.Value == oneFoot - oneFoot); //Function with number result = ce.Calculate("sin(3.1415926535897932384626433832795)", vars); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, Math.Sin(Math.PI), 1e-8); result = ce.Calculate("sin(3.1415926535897932384626433832795/3)", vars); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, Math.Sin(Math.PI / 3), 1e-8); //Function with constant result = ce.Calculate("sin(pi)", vars); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, Math.Sin(Math.PI), 1e-8); result = ce.Calculate("sin(pi/3)", vars); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, Math.Sin(Math.PI / 3), 1e-8); //Function with variable result = ce.Calculate("sin(A)", vars); Assert.AreEqual(result.DataType, DataType.Number); Assert.AreEqual((double)result.Value, Math.Sin(1.0), 1e-8); }
public Operation Build(IList <Token> tokens, UnitsCore unitsCore) { resultStack.Clear(); operatorStack.Clear(); parameterCount.Clear(); foreach (Token token in tokens) { object value = token.Value; switch (token.TokenType) { case TokenType.Number: resultStack.Push(new FloatingPointConstant((double)token.Value)); break; case TokenType.Text: if (functionRegistry.IsFunctionName((string)token.Value)) { operatorStack.Push(token); parameterCount.Push(1); } else { resultStack.Push(new Variable(((string)token.Value).ToLowerInvariant())); } break; case TokenType.Unit: resultStack.Push(new ChangeUnit(resultStack.Pop(), unitsCore.ParseUnit((string)token.Value))); //perform unit conversion break; case TokenType.LeftBracket: operatorStack.Push(token); break; case TokenType.RightBracket: PopOperations(true, token); //parameterCount.Pop(); break; case TokenType.ArgumentSeparator: PopOperations(false, token); parameterCount.Push(parameterCount.Pop() + 1); break; case TokenType.Operation: Token operation1Token = token; char operation1 = (char)operation1Token.Value; while (operatorStack.Count > 0 && (operatorStack.Peek().TokenType == TokenType.Operation || operatorStack.Peek().TokenType == TokenType.Text)) { Token operation2Token = operatorStack.Peek(); bool isFunctionOnTopOfStack = operation2Token.TokenType == TokenType.Text; if (!isFunctionOnTopOfStack) { char operation2 = (char)operation2Token.Value; if ((IsLeftAssociativeOperation(operation1) && operationPrecedence[operation1] <= operationPrecedence[operation2]) || (operationPrecedence[operation1] < operationPrecedence[operation2])) { operatorStack.Pop(); resultStack.Push(ConvertOperation(operation2Token)); } else { break; } } else { operatorStack.Pop(); resultStack.Push(ConvertFunction(operation2Token)); } } operatorStack.Push(operation1Token); break; } } PopOperations(false, null); VerifyResultStack(); return(resultStack.First()); }