示例#1
0
    private void SetPlayer( )
    {
        // Specify skill level
        playerStatus.SkillLevel = 4;

        // CalcEngine uses Reflection to access the properties of the PlayderData object
        // so they can be used in expressions.
        calculator.DataContext = playerStatus;

        //// Calculate each of stat for a player
        playerStatus.STR = Convert.ToSingle(calculator.Evaluate(GetFormula("STR")));
        Debug.LogFormat("STR: {0}", playerStatus.STR);

        playerStatus.DEX = Convert.ToSingle(calculator.Evaluate(GetFormula("DEX")));
        Debug.LogFormat("DEX: {0}", playerStatus.DEX);

        playerStatus.ITL = Convert.ToSingle(calculator.Evaluate(GetFormula("ITL")));
        Debug.LogFormat("ITL: {0}", playerStatus.ITL);

        playerStatus.HP = Convert.ToSingle(calculator.Evaluate(GetFormula("HP")));
        Debug.LogFormat("HP: {0}", playerStatus.HP);

        playerStatus.MP = Convert.ToSingle(calculator.Evaluate(GetFormula("MP")));
        Debug.LogFormat("MP: {0}", playerStatus.MP);
    }
示例#2
0
        /// <summary>
        /// Uses CalcEngine to calculate an expression derived from an inspector formula.
        /// </summary>
        /// <param name="expression">The expression used to calculate.</param>
        /// <returns>A new value based on the expression and time in this phase.</returns>
        private double Calculate(Expression expression)
        {
            // Must update the Value variable stored in the CalcEngine
            CalculationEngine.Variables[PARAMETER_NAME] = CurrentCurveTime;

            // Calculate the result of the expression
            return((double)CalculationEngine.Evaluate(expression));
        }
示例#3
0
        public static void Test(this CalculationEngine engine, string expression, object expectedResult)
        {
            var result = engine.Evaluate(expression);

            if (result is double && expectedResult is int)
            {
                expectedResult = (double)(int)expectedResult;
            }
            if (!object.Equals(result, expectedResult))
            {
                var msg = string.Format("error: {0} gives {1}, should give {2}", expression, result, expectedResult);
                throw new Exception(msg);
            }
        }
        public static void CreateExcelSheetWithFunctions()
        {
            var ce  = new CalculationEngine();
            var exp = "stdev(metric1,metric3)";
            var dt  = DataTableForGrid.dt;

            if (!dt.Columns.Contains("STDEV"))
            {
                dt.Columns.Add("STDEV", typeof(double));
            }
            var columns = dt.Columns;

            foreach (DataRow row in dt.Rows)
            {
                var exprsn = exp;
                exprsn       = exprsn.Replace("metric1", row["Metric1"].ToString());
                exprsn       = exprsn.Replace("metric3", row["Metric3"].ToString());
                row["STDEV"] = ce.Evaluate(exprsn);
            }
        }
示例#5
0
文件: UI.cs 项目: gsriar/new2
        private void btnProcess_Click(object sender, EventArgs e)
        {
            OutAttrubute   outputAttribute;
            InputParameter inputParam = new InputParameter()
            {
                InputExcelFilePath  = txtClientDataFilePath.Text,
                ConfigExcelFilePath = txtConfigFilePath.Text,
                OutputFileName      = txtOutputFileNameSuffix.Text,
                CompletedFolder     = txtCompletedOutputFolder.Text,
                ErrorFolder         = txtErrorOutputFolder.Text,
                LogFolder           = txtLogOutputFolder.Text
            };

            using (CalculationEngine engine = new CalculationEngine(inputParam, WriteLog))
            {
                engine.Evaluate(out outputAttribute);

                logFile = outputAttribute.GetAttrib("log");
                csvFile = outputAttribute.GetAttrib("csv");
            }
        }
示例#6
0
        public static void Test <T>(this CalculationEngine engine, string expression, T expectedResult, string expectedParsedExpression = null)
        {
            var result = engine.Evaluate <T>(expression);

            if (expectedParsedExpression != null)
            {
                if (!string.Equals(expectedParsedExpression, engine.ParsedExpression))
                {
                    var msg = $"error: {engine.ParsedExpression} should equal {expectedParsedExpression}";
                    throw new Exception(msg);
                }
            }

            if (!Equals(result, expectedResult))
            {
                var msg = $"error: {expression} gives {result}, should give {expectedResult}";
                throw new Exception(msg);
            }

            Console.WriteLine(expression + " -> " + engine.ParsedExpression);
        }
示例#7
0
 static object CountIf(List<Expression> p)
 {
     CalculationEngine ce = new CalculationEngine();
     var cnt = 0.0;
     var ienum = p[0] as IEnumerable;
     if (ienum != null)
     {
         var crit = (string)p[1].Evaluate();
         foreach (var value in ienum)
         {
             if (!IsBlank(value))
             {
                 var exp = string.Format("{0}{1}", value, crit);
                 if ((bool)ce.Evaluate(exp))
                     cnt++;
             }
         }
     }
     return cnt;
 }
        public void Functions_Test()
        {
            CalculationEngine engine = new CalculationEngine(new CalculationEngineOptions
            {
                ServiceProvider = new ServiceProvider()
            });

            // adjust culture
            var cultureInfo = engine.CultureInfo;

            engine.CultureInfo = CultureInfo.InvariantCulture;

            engine.Test("1492.5373134328358", 1492.5373134328358);
            engine.Test("1 041.341 ", 1041.341);

            // test invalid parsing
            var ex = Assert.Throws <CalcEngineException>(() =>
            {
                var d = engine.Evaluate <string>("Max(1,2,3)KalleKusta");
            });

            Assert.That(ex.Message, Is.EqualTo("Syntax error. Expression: Max(1,2,3)[KalleKusta]"));

            // test internal operators
            engine.Test("0", 0.0);
            engine.Test("+1", 1.0);
            engine.Test("-1", -1.0);

            engine.Test("1+1", 1 + 1.0);
            engine.Test("1*2*3*4*5*6*7*8*9", 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9.0);
            engine.Test("1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+1/(1+1))))))))))", 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1 / (1 + 1.0)))))))))));
            engine.Test("((1+2)*(2+3)/(4+5))^0.123", Math.Pow((1 + 2) * (2 + 3) / (4 + 5.0), 0.123));
            engine.Test("10%", 0.1);
            engine.Test("1e+3", 1000.0);
            engine.Test("'0020'+1", 21);

            // test simple variables
            engine.Variables.Add("one", 1);
            engine.Variables.Add("x", 1);
            engine.Variables.Add("x2", "1");
            engine.Variables.Add("two", 2);
            engine.Test("one + two", 3);
            engine.Test("x2='1'", true);

            engine.Test("2*x+1", 3);
            engine.Test("(two + two)^2", 16);
            engine.Variables.Clear();

            // COMPARE TESTS
            engine.Test("5=5", true);
            engine.Test("'2'='2'", true);
            engine.Test("5==5", true);
            engine.Test("6==5", false);
            engine.Test("6<>5", true);
            engine.Test("6=5", false);
            engine.Test("6<5", false);
            engine.Test("6>5", true);
            engine.Test("5<=10", true);
            engine.Test("5>=3", true);
            engine.Test("'Viis'>='Üks'", false);

            // LOGICAL FUNCTION TESTS

            engine.Test("5<=6.0 && 6>=3", true);
            engine.Test("true", true);
            engine.Test("true  && true", true);
            engine.Test("true  && false", false);
            engine.Test("false && true", false);
            engine.Test("false && false", false);
            engine.Test("true  || true", true);
            engine.Test("true  || false", true);
            engine.Test("false || true", true);
            engine.Test("false || false", false);

            engine.Test("AND(true, true)", true);
            engine.Test("AND(true, false)", false);
            engine.Test("AND(false, true)", false);
            engine.Test("AND(false, false)", false);
            engine.Test("OR(true, true)", true);
            engine.Test("OR(true, false)", true);
            engine.Test("OR(false, true)", true);
            engine.Test("OR(false, false)", false);
            engine.Test("NOT(false)", true);
            engine.Test("NOT(true)", false);
            engine.Test("IF(5 > 4, true, false)", true);
            engine.Test("IF(5 > 14, true, false)", false);
            engine.Test("TRUE()", true);
            engine.Test("FALSE()", false);

            // MATH/TRIG FUNCTION TESTS
            engine.Test("ABS(-12)", 12.0);
            engine.Test("ABS(+12)", 12.0);
            engine.Test("ACOS(.23)", Math.Acos(.23));
            engine.Test("ASIN(.23)", Math.Asin(.23));
            engine.Test("ATAN(.23)", Math.Atan(.23));
            engine.Test("ATAN2(1,2)", Math.Atan2(1, 2));
            engine.Test("CEILING(1.8)", Math.Ceiling(1.8));
            engine.Test("COS(1.23)", Math.Cos(1.23));
            engine.Test("COSH(1.23)", Math.Cosh(1.23));
            engine.Test("EXP(1)", Math.Exp(1));
            engine.Test("FLOOR(1.8)", Math.Floor(1.8));
            engine.Test("INT(1.8)", 1);
            engine.Test("LOG(1.8)", Math.Log(1.8, 10));   // default base is 10
            engine.Test("LOG(1.8, 4)", Math.Log(1.8, 4)); // custom base
            engine.Test("LN(1.8)", Math.Log(1.8));        // real log
            engine.Test("LOG10(1.8)", Math.Log10(1.8));   // same as Log(1.8)
            engine.Test("PI()", Math.PI);
            engine.Test("POWER(2,4)", Math.Pow(2, 4));
            //engine.Test("RAND") <= 1.0);
            //engine.Test("RANDBETWEEN(4,5)") <= 5);
            engine.Test("SIGN(-5)", -1);
            engine.Test("SIGN(+5)", +1);
            engine.Test("SIGN(0)", 0);
            engine.Test("SIN(1.23)", Math.Sin(1.23));
            engine.Test("SINH(1.23)", Math.Sinh(1.23));
            engine.Test("SQRT(144)", Math.Sqrt(144));
            engine.Test("SUM(1, 2, 3, 4)", 1 + 2 + 3 + 4.0);
            engine.Test("MAX(1.4, 2, 3, 4.5)", 4.5);
            engine.Test("MIN(1.4, 2, 3, 4.5)", 1.4);
            engine.Test("TAN(1.23)", Math.Tan(1.23));
            engine.Test("TANH(1.23)", Math.Tanh(1.23));
            engine.Test("TRUNC(1.23)", 1.0);
            engine.Test("PI()", Math.PI);
            engine.Test("PI", Math.PI);
            engine.Test("LN(10)", Math.Log(10));
            engine.Test("LOG(10)", Math.Log10(10));
            engine.Test("EXP(10)", Math.Exp(10));
            engine.Test("SIN(PI()/4)", Math.Sin(Math.PI / 4));
            engine.Test("ASIN(PI()/4)", Math.Asin(Math.PI / 4));
            engine.Test("SINH(PI()/4)", Math.Sinh(Math.PI / 4));
            engine.Test("COS(90)", Math.Cos(90));
            engine.Test("COS(PI()/4)", Math.Cos(Math.PI / 4));
            engine.Test("ACOS(PI()/4)", Math.Acos(Math.PI / 4));
            engine.Test("COSH(PI()/4)", Math.Cosh(Math.PI / 4));
            engine.Test("TAN(PI()/4)", Math.Tan(Math.PI / 4));
            engine.Test("ATAN(PI()/4)", Math.Atan(Math.PI / 4));
            engine.Test("ATAN2(1,2)", Math.Atan2(1, 2));
            engine.Test("TANH(PI()/4)", Math.Tanh(Math.PI / 4));
            engine.Test("Number('0.54')*2", 0.54 * 2);
            engine.Test("Number('0092')", 92);

            // TEXT FUNCTION TESTS
            engine.Test("CHAR(65)", "A");
            //engine.Test("CODE(\"A\")", 65);
            engine.Test("CONCATENATE(\"(a\", \"b)\")", "(ab)");
            engine.Test("CONCATENATE(\"a\", \"b\")", "ab");
            engine.Test("CONCATENATE('a', 'b')", "ab");
            engine.Test("FIND(\"bra\", \"abracadabra\")", 2);
            engine.Test("FIND(\"BRA\", \"abracadabra\")", -1);
            engine.Test("LEFT(\"abracadabra\", 3)", "abr");
            engine.Test("LEFT(\"abracadabra\")", "a");
            engine.Test("LEN(\"abracadabra\")", 11);
            engine.Test("LOWER(\"ABRACADABRA\")", "abracadabra");
            engine.Test("MID(\"abracadabra\", 1, 3)", "abr");
            engine.Test("PROPER(\"abracadabra\")", "Abracadabra");
            engine.Test("REPLACE(\"abracadabra\", 1, 3, \"XYZ\")", "XYZacadabra");
            engine.Test("REPT(\"abr\", 3)", "abrabrabr");
            engine.Test("RIGHT(\"abracadabra\", 3)", "bra");
            engine.Test("SEARCH(\"bra\", \"abracadabra\")", 2);
            engine.Test("SEARCH(\"BRA\", \"abracadabra\")", 2);
            engine.Test("(SEARCH('432', '30X432')>0) || (Search('4DX', '4DX30P')>0)", true);

            engine.Test("SUBSTITUTE(\"abracadabra\", \"a\", \"b\")", "bbrbcbdbbrb");
            engine.Test("T(123)", "123");
            engine.Test("TEXT(1234, \"n2\")", "1,234.00");
            engine.Test("TRIM(\"   hello   \")", "hello");
            engine.Test("TRIM('00hello00', '00', true)", "hello00");
            engine.Test("TRIM('00hello00', '00', false)", "00hello");
            engine.Test("TRIM('00hello00', '00')", "hello");
            engine.Test("UPPER(\"abracadabra\")", "ABRACADABRA");
            engine.Test("VALUE(\"1234\")", 1234.0);
            engine.Test("PadLeft(1254, 6, \"0\")", "001254");
            engine.Test("PadRight(1254, 6, \"0\")", "125400");
            engine.Test("AffixIF(1254, \"p\", \"s\")", "p1254s");

            engine.Test("SUBSTITUTE(\"abcabcabc\", \"a\", \"b\")", "bbcbbcbbc");
            engine.Test("SUBSTITUTE(\"abcabcabc\", \"a\", \"b\", 1)", "bbcabcabc");
            engine.Test("SUBSTITUTE(\"abcabcabc\", \"a\", \"b\", 2)", "abcbbcabc");
            engine.Test("SUBSTITUTE(\"abcabcabc\", \"A\", \"b\", 2)", "abcabcabc"); // case-sensitive!

            // test taken from http://www.codeproject.com/KB/vb/FormulaEngine.aspx
            var a1  = "\"http://j-walk.com/ss/books\"";
            var exp = "RIGHT(A1,LEN(A1)-FIND(CHAR(1),SUBSTITUTE(A1,\"/\",CHAR(1),LEN(A1)-LEN(SUBSTITUTE(A1,\"/\",\"\")))))";

            engine.Test(exp.Replace("A1", a1), "books");


            // STATISTICAL FUNCTION TESTS
            engine.Test("Average(1, 3, 3, 1, true, false, \"hello\")", 2.0);
            engine.Test("AverageA(1, 3, 3, 1, true, false, \"hello\")", (1 + 3 + 3 + 1 + 1 + 0 + 0) / 7.0);
            engine.Test("Count(1, 3, 3, 1, true, false, \"hello\")", 4.0);
            engine.Test("CountA(1, 3, 3, 1, true, false, \"hello\")", 7.0);

            // restore culture
            engine.CultureInfo = cultureInfo;
        }
        public void Array_Test()
        {
            CalculationEngine engine = new CalculationEngine(new CalculationEngineOptions
            {
                ServiceProvider = new ServiceProvider(),
                FunctionTable   = CalculationEngine.DefaultFunctions.ToDictionary(definition => definition.FunctionName, comparer: StringComparer.OrdinalIgnoreCase)
            });

            // adjust culture
            var cultureInfo = engine.CultureInfo;

            engine.CultureInfo = CultureInfo.InvariantCulture;

            // test Array variables
            var array = new List <double> {
                0, 1, 2, 3
            }.ToArray();
            var strings = new List <string> {
                "K", "E", "S", "A"
            }.ToArray();

            engine.Variables.Add("A", array);
            engine.Variables.Add("B", strings);
            engine.Variables.Add("Decimal", 4.5M);
            engine.Variables.Add("Number1", 55);
            engine.Variables.Add("Double", 5.5);
            engine.Variables.Add("D310", 310M);
            engine.Variables.Add("D32", 32M);
            engine.Variables.Add("h", 1D / 60);
            engine.Variables.Add("m", 1D / 60 / 60);
            engine.Test("105m/(4*2380)", 105 * (1D / 60 / 60) / (4 * 2380));
            engine.Test("5h", 5 * 1D / 60);

            engine.Test("D310>=250 && D32<=315", true);

            engine.Test("A", array);
            engine.Test("B", strings);
            engine.Test("Max(Array(5,6,7))", 7);
            engine.Test("Number1", 55);
            engine.Test("Max(Number1, 5,33)", 55);
            engine.Test("Max(Number1, 5.33)", 55);
            engine.Test("Min(Number1, 5,88, Decimal)", 4.5M, "MIN(55, 5, 88, 4.5)/*{4.5}*/");

            Assert.That(new double[] { 0, 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("Range(0, 3)") as IEnumerable));
            Assert.That(new double[] { 0, 100, 200 }, Is.EquivalentTo(engine.Evaluate("Range(0, 200, 100)") as IEnumerable));
            Assert.That(new[] { "K", "E", "S" }, Is.EquivalentTo(engine.Evaluate("Array('K', 'E', 'S')") as IEnumerable));
            Assert.That(new double[] { 1, 3 }, Is.EquivalentTo(engine.Evaluate("Array(1, 3)") as IEnumerable));

            IList <dynamic> enumerable = engine.Evaluate("Array(1, '3')") as object[];

            Assert.That(new dynamic[] { 1, "3" }, Is.EquivalentTo(enumerable));

            Assert.That(new double[] { 0, 100, 200, 300, 400 }, Is.EquivalentTo(engine.Evaluate("Map(Range(0, 200, 100),Range(100, 400, 100))") as IEnumerable));


            Assert.That(new double[] { 0, 1, 2 }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), 3)") as IEnumerable));
            Assert.That(new double[] { 0, 1, 2 }, Is.EquivalentTo(engine.Evaluate("lt(Range(0, 3), 3)") as IEnumerable));
            Assert.That(new double[] { 1 }, Is.EquivalentTo(engine.Evaluate("LessThan(Array('1', '4'), 3)") as IEnumerable));
            Assert.That(new double[] { 0, 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("LessOrEqual(Range(0, 3), 3)") as IEnumerable));
            Assert.That(new double[] { 0, 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("le(Range(0, 3), 3)") as IEnumerable));

            Assert.That(new double[] { 2, 3 }, Is.EquivalentTo(engine.Evaluate("GreaterThan(Range(0, 3), 1)") as IEnumerable));
            Assert.That(new double[] { 2, 3 }, Is.EquivalentTo(engine.Evaluate("gt(Range(0, 3), 1)") as IEnumerable));
            Assert.That(new double[] { 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("GreaterOrEqual(Range(0, 3), 1)") as IEnumerable));
            Assert.That(new double[] { 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("ge(Range(0, 3), 1)") as IEnumerable));
            Assert.That(new[] { 4.5, 5.5 }, Is.EquivalentTo(engine.Evaluate("ge(Array(Decimal, Double), 1)") as IEnumerable));
            Assert.That(new[] { "6", "7" }, Is.EquivalentTo(engine.Evaluate("ArrayString('6;7')") as IEnumerable));

            engine.Test("Contains(Array(1,2), 2)", true);
            engine.Test("Contains(Array(1,2), 3)", false);
            engine.Test("Contains(Array('1','2'), 2)", true);
            engine.Test("Contains('1;2', '2')", true);
            engine.Test("Contains('1;2', 2)", true);
            engine.Test("Contains('2', 2)", true);
            engine.Test("Contains(2, '2')", true);
            engine.Test("Contains(Array(Number1, Double, '5.4'), 5.5)", true);
            engine.Test("Contains(Array(1,2,5,7), Array(5,7))", true);
            engine.Test("Contains(Array(1,2,5,7), Array(3,6))", false);
            engine.Test("Contains('n;m; s;!k', Array('s','u'))", true);
            engine.Test("Contains('n|m|s|!k', Array('s','u'), '|')", true);
            engine.Test("Contains('n;m;s;!k', Array('s','u','k'))", false);
            engine.Test("Contains('n;m;s;!k', Array('s','u','k'))", false);

            engine.Options.Functions.ContainsTrimStartChars = new List <char> {
                '0'
            };
            engine.Test("Contains('04;09;7;92;!08', '0092')", true);
            engine.Options.Functions.ContainsTrimStartChars = new List <char>();
            engine.Test("Contains('04;09;7;92;!08', '0092')", false);
            engine.Test("Contains('04;09;7;0092;!08', '92')", false);

            engine.Test("XLOOKUP('06', Array('06', '05', '04'), Array('25', '26', '27'), '20')", "25");
            engine.Test("XLOOKUP('04', Array('06', '05', '04'), Array('25', '26', '27'), '20')", "27");
            engine.Test("XLOOKUP('04', Array('06', '05', '04'), Array(25, 26, 27), '20')", 27);
            engine.Test("XLOOKUP('07', Array('06', '05', '04'), Array('25', '26', '27'), '20')", "20");

            engine.Variables.Add("dec1", 4.5M);
            engine.Variables.Add("doub1", 4.5);
            engine.Test("XLOOKUP(dec1, Array(doub1, 5, 6), Array(7, 8, 9), 0)", 7);

            engine.Options.Functions.ContainsTrimStartChars = new List <char> {
                '0'
            };
            engine.Test("1.2+IF((Contains('0018;0004;0014;0049', '18') && Contains('0092', '92')), 1, 0)+IF((Contains('0008', '0008') && Contains('0105', '105')), 1.5, 0)", 3.7);

            engine.Test("XLOOKUP('07', Array('06', '05', '04'), Array('25', '26', \r\n'27'), '20'\n)", "20");
            engine.Test("XLOOKUP('07', Array('06', '05', '04'), Array('25' /* 0068 */, '26', '27'), '20')", "20");

            engine.Test("XLOOKUP(7, Array(Range(4, 5), Range(6, 8), Range(9, 10)), Array('25', '26', '27'), '20')", "26");
            engine.Test("XLOOKUP(7, Array(Array(4, 5, 7), Array(6, 8), Array(9, 10)), Array('25', '26', '27'), '20')", "25");
        }
        public static void DataContext_Tests()
        {
            CalculationEngine engine = new CalculationEngine(new CalculationEngineOptions
            {
                ServiceProvider = new ServiceProvider()
            });

            // adjust culture
            var cultureInfo = engine.CultureInfo;

            engine.CultureInfo = CultureInfo.InvariantCulture;

            var p = TestPerson.CreateTestPerson();

            p.Parent           = TestPerson.CreateTestPerson();
            engine.DataContext = p;

            engine.Variables.Add("sp", 1);

            var s = @"3600 / (1sp * XLOOKUP( CONCATENATE(Specs('jaty'), '-', IF( Number(Specs('jawd')) >= 0068 && Number(Specs('jawd')) < 0110, '68/110', IF(Number(Specs('jawd')) >= 0110 && Number(Specs('jawd')) < 0150, '110/150', Specs('jawd') ) ), '-', IF(Contains('JH', Specs('jaap')), 'JH', 'JL'), '-', IF(Contains('PK;PH', Specs('jspe')), 'OV', 'VOL')), Array( '04-68/110-JL-VOL', '04-68/110-JL-OV', '04-68/110-JH-VOL', '04-68/110-JH-OV', '14-68/110-JL-VOL', '14-68/110-JL-OV', '14-68/110-JH-VOL', '14-68/110-JH-OV', '29-68/110-JL-VOL', '29-68/110-JL-OV', '29-68/110-JH-VOL', '29-68/110-JH-OV', '49-68/110-JL-VOL', '49-68/110-JL-OV', '49-68/110-JH-VOL', '49-68/110-JH-OV', '28-68/110-JL-VOL', '28-68/110-JL-OV', '28-68/110-JH-VOL', '28-68/110-JH-OV', '38-68/110-JL-VOL', '38-68/110-JL-OV', '38-68/110-JH-VOL', '38-68/110-JH-OV', '05-68/110-JL-VOL', '05-68/110-JL-OV', '05-68/110-JH-VOL', '05-68/110-JH-OV', '06-68/110-JL-VOL', '06-68/110-JL-OV', '06-68/110-JH-VOL', '06-68/110-JH-OV', '06-110/150-JL-VOL', '06-110/150-JL-OV', '06-110/150-JH-VOL', '06-110/150-JH-OV', '32-68/110-JL-VOL', '32-68/110-JL-OV', '32-68/110-JH-VOL', '32-68/110-JH-OV', '36-68/110-JL-VOL', '36-68/110-JL-OV', '36-68/110-JH-VOL', '36-68/110-JH-OV', '36-110/150-JL-VOL', '36-110/150-JL-OV', '36-110/150-JH-VOL', '36-110/150-JH-OV', '86-68/110-JL-VOL', '86-68/110-JL-OV', '86-68/110-JH-VOL', '86-68/110-JH-OV', 'C7-68/110-JL-VOL', 'C7-68/110-JL-OV', 'C7-68/110-JH-VOL', 'C7-68/110-JH-OV', 'C7-110/150-JL-VOL', 'C7-110/150-JL-OV', 'C7-110/150-JH-VOL', 'C7-110/150-JH-OV', '26-110/150-JL-VOL', '26-110/150-JL-OV', '26-110/150-JH-VOL', '26-110/150-JH-OV', '46-110/150-JL-VOL', '46-110/150-JL-OV', '46-110/150-JH-VOL', '46-110/150-JH-OV', '55-68/110-JL-VOL', '55-68/110-JL-OV', '55-68/110-JH-VOL', '55-68/110-JH-OV' ), Array( 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 39, 42, 35, 38, 39, 42, 35, 38, 37, 37, 41, 38, 39, 42, 35, 38, 39, 42, 35, 38, 37, 37, 41, 38, 39, 42, 35, 38, 39, 42, 35, 38, 37, 37, 41, 38, 37, 37, 41, 38, 37, 37, 41, 38, 39, 42, 35, 22 ), 38))";

            p.Specs.Add("jaty", "04");
            p.Specs.Add("jawd", "0092");
            p.Specs.Add("jspe", "00");
            p.Specs.Add("jaap", "JH");

            engine.Test(s, 76.5957446808511M);
            Assert.AreEqual("(3600 / 1 * 1 * XLOOKUP(CONCATENATE('04', '-', IF('68/110', IF('110/150', Specs('jawd'))/*{}*/)/*{'68/110'}*/, '-', IF(CONTAINS('JH', 'JH')/*{True}*/, 'JH', 'JL')/*{'JH'}*/, '-', IF(CONTAINS('PK;PH', '00')/*{False}*/, 'OV', 'VOL')/*{'VOL'}*/)/*{'04-68/110-JH-VOL'}*/, ['04-68/110-JL-VOL', '04-68/110-JL-OV', '04-68/110-JH-VOL', '04-68/110-JH-OV', '14-68/110-JL-VOL', '14-68/110-JL-OV', '14-68/110-JH-VOL', '14-68/110-JH-OV', '29-68/110-JL-VOL', '29-68/110-JL-OV', '29-68/110-JH-VOL', '29-68/110-JH-OV', '49-68/110-JL-VOL', '49-68/110-JL-OV', '49-68/110-JH-VOL', '49-68/110-JH-OV', '28-68/110-JL-VOL', '28-68/110-JL-OV', '28-68/110-JH-VOL', '28-68/110-JH-OV', '38-68/110-JL-VOL', '38-68/110-JL-OV', '38-68/110-JH-VOL', '38-68/110-JH-OV', '05-68/110-JL-VOL', '05-68/110-JL-OV', '05-68/110-JH-VOL', '05-68/110-JH-OV', '06-68/110-JL-VOL', '06-68/110-JL-OV', '06-68/110-JH-VOL', '06-68/110-JH-OV', '06-110/150-JL-VOL', '06-110/150-JL-OV', '06-110/150-JH-VOL', '06-110/150-JH-OV', '32-68/110-JL-VOL', '32-68/110-JL-OV', '32-68/110-JH-VOL', '32-68/110-JH-OV', '36-68/110-JL-VOL', '36-68/110-JL-OV', '36-68/110-JH-VOL', '36-68/110-JH-OV', '36-110/150-JL-VOL', '36-110/150-JL-OV', '36-110/150-JH-VOL', '36-110/150-JH-OV', '86-68/110-JL-VOL', '86-68/110-JL-OV', '86-68/110-JH-VOL', '86-68/110-JH-OV', 'C7-68/110-JL-VOL', 'C7-68/110-JL-OV', 'C7-68/110-JH-VOL', 'C7-68/110-JH-OV', 'C7-110/150-JL-VOL', 'C7-110/150-JL-OV', 'C7-110/150-JH-VOL', 'C7-110/150-JH-OV', '26-110/150-JL-VOL', '26-110/150-JL-OV', '26-110/150-JH-VOL', '26-110/150-JH-OV', '46-110/150-JL-VOL', '46-110/150-JL-OV', '46-110/150-JH-VOL', '46-110/150-JH-OV', '55-68/110-JL-VOL', '55-68/110-JL-OV', '55-68/110-JH-VOL', '55-68/110-JH-OV'], [38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 38, 38, 47, 47, 39, 42, 35, 38, 39, 42, 35, 38, 37, 37, 41, 38, 39, 42, 35, 38, 39, 42, 35, 38, 37, 37, 41, 38, 39, 42, 35, 38, 39, 42, 35, 38, 37, 37, 41, 38, 37, 37, 41, 38, 37, 37, 41, 38, 39, 42, 35, 22], 38)/*{47}*/)/*{76.5957446808511}*/", engine.ParsedExpression);

            engine.Test("Name", "Test Person");
            engine.Test("1h", 1D / 60);

            engine.Test("1mp*ChildrenDct('Test Child 2').Age", p.ChildrenDct["Test Child 2"].Age.Value);

            engine.Variables.Add("am", 1);
            engine.Test("HASH(Name)", "53A4E9EC08910DE9BB6EDAA99F8C867C");
            engine.Test("HASH('Name')", "49EE3087348E8D44E1FEDA1917443987");
            engine.Test("CONCATENATE(am, HASH('Name'))", "149EE3087348E8D44E1FEDA1917443987");
            engine.Functions.Remove("CODE");
            Assert.IsTrue(engine.Validate <bool>("Code='Code'"));
            engine.Test("Parent.Name", "Test Person");
            engine.Test("Name.Length * 2", p.Name.Length * 2);
            engine.Test("Children.Count", p.Children.Count);
            engine.Test("Children(2).Name", p.Children[2].Name);

            engine.Test("15*ChildrenDct(\"Test Child 2\").Age+14", (15 * p.ChildrenDct["Test Child 2"].Age + 14).Value);
            engine.Test("ChildrenDct('Test Child 2').Name", p.ChildrenDct["Test Child 2"].Name);
            engine.Test("ValueOr(ChildrenDct('Test Child 2').Nullable, 0) <> 0", false);
            engine.Test("ValueOr(ChildrenDct('Test Child 2').Nullable, 0) < 6", true);
            engine.Test("ValueOr(ChildrenDct('Test Child 2').Number, 0) <> 0", true);
            engine.ThrowOnInvalidBindingExpression = false;
            engine.Test("ValueOr(ChildrenDct('NotExist').Number, 3) == 3", true);
            engine.ThrowOnInvalidBindingExpression = true;
            Assert.Throws <CalcEngineBindingException>(() => { engine.Test("ChildrenDct('NotExist').Number", true); });

            var ex = Assert.Throws <CalcEngineBindingException>(() =>
            {
                var d = engine.Evaluate <double>("ChildrenAgeDct('Test Child 10') * 2");
            });

            Assert.That(ex.Message, Is.EqualTo("'ChildrenAgeDct' of 'Zirpl.CalcEngine.Tests.TestPerson' (TestPerson) don't have key(s) 'Test Child 10'"));

            Assert.AreEqual(0, engine.Evaluate <double>("ChildrenAgeDct('Test Child 10') * 2", false));
            Assert.AreEqual(2, engine.Evaluate <double>("ChildrenAgeDct('Test Child 10') + 2", false));

            engine.Test("ChildrenIdDct('16C5888C-6C75-43DD-A372-2A3398DAE038').Name", p.ChildrenDct["Test Child 1"].Name);
            engine.Test("ChildrenDct.Count", p.ChildrenDct.Count);

            // DataContext functions
            engine.RegisterFunction("GetParent", 0, (calculationEngine, parms) =>
            {
                var testPerson = calculationEngine.DataContext as TestPerson;
                return(testPerson.Name);
            });
            engine.Test("GetParent()", p.Name);

            engine.InValidation = true;
            Assert.That(new double[] { }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), ChildrenAgeDct('Test Child 2'))") as IEnumerable));
            Assert.That(new double[] { }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), ChildrenWeightDct('Test Child 2'))") as IEnumerable));
            Assert.That(new double[] { }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), ChildrenSalaryDct('Test Child 2'))") as IEnumerable));

            engine.InValidation = false;
            Assert.That(new double[] { 0, 1 }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), ChildrenAgeDct('Test Child 2'))") as IEnumerable));
            Assert.That(new double[] { 0, 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), ChildrenWeightDct('Test Child 2'))") as IEnumerable));
            Assert.That(new double[] { 0, 1, 2, 3 }, Is.EquivalentTo(engine.Evaluate("LessThan(Range(0, 3), ChildrenSalaryDct('Test Child 2'))") as IEnumerable));
        }