ExpressionBuilderTest.java

/*
 * Copyright 2014 Frank Asseg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.objecthunter.exp4j;

import net.objecthunter.exp4j.function.Function;
import net.objecthunter.exp4j.operator.Operator;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static java.lang.Math.*;
import static org.junit.Assert.*;

public class ExpressionBuilderTest {

    @Test
    public void testExpressionBuilder1() {
        double result = new ExpressionBuilder("2+1")
                .build()
                .evaluate();
        assertEquals(3d, result, 0d);
    }

    @Test
    public void testExpressionBuilder2() {
        double result = new ExpressionBuilder("cos(x)")
                .variables("x")
                .build()
                .setVariable("x", Math.PI)
                .evaluate();
        double expected = cos(Math.PI);
        assertEquals(-1d, result, 0d);
    }

    @Test
    public void testExpressionBuilder3() {
        double x = Math.PI;
        double result = new ExpressionBuilder("sin(x)-log(3*x/4)")
                .variables("x")
                .build()
                .setVariable("x", x)
                .evaluate();

        double expected = sin(x) - log(3 * x / 4);
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testExpressionBuilder4() {
        Function log2 = new Function("log2", 1) {

            @Override
            public double apply(double... args) {
                return Math.log(args[0]) / Math.log(2);
            }
        };
        double result = new ExpressionBuilder("log2(4)")
                .function(log2)
                .build()
                .evaluate();

        double expected = 2;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testExpressionBuilder5() {
        Function avg = new Function("avg", 4) {

            @Override
            public double apply(double... args) {
                double sum = 0;
                for (double arg : args) {
                    sum += arg;
                }
                return sum / args.length;
            }
        };
        double result = new ExpressionBuilder("avg(1,2,3,4)")
                .function(avg)
                .build()
                .evaluate();

        double expected = 2.5d;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testExpressionBuilder6() {
        Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

            @Override
            public double apply(double... args) {
                final int arg = (int) args[0];
                if ((double) arg != args[0]) {
                    throw new IllegalArgumentException("Operand for factorial has to be an integer");
                }
                if (arg < 0) {
                    throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
                }
                double result = 1;
                for (int i = 1; i <= arg; i++) {
                    result *= i;
                }
                return result;
            }
        };

        double result = new ExpressionBuilder("3!")
                .operator(factorial)
                .build()
                .evaluate();

        double expected = 6d;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testExpressionBuilder7() {
        ValidationResult res = new ExpressionBuilder("x")
                .variables("x")
                .build()
                .validate();
        assertFalse(res.isValid());
        assertEquals(res.getErrors().size(), 1);
    }

    @Test
    public void testExpressionBuilder8() {
        ValidationResult res = new ExpressionBuilder("x*y*z")
                .variables("x", "y", "z")
                .build()
                .validate();
        assertFalse(res.isValid());
        assertEquals(res.getErrors().size(), 3);
    }

    @Test
    public void testExpressionBuilder9() {
        ValidationResult res = new ExpressionBuilder("x")
                .variables("x")
                .build()
                .setVariable("x", 1d)
                .validate();
        assertTrue(res.isValid());
    }

    @Test
    public void testValidationDocExample() {
        Expression e = new ExpressionBuilder("x")
                .variables("x")
                .build();
        ValidationResult res = e.validate();
        assertFalse(res.isValid());
        assertEquals(1, res.getErrors().size());

        e.setVariable("x", 1d);
        res = e.validate();
        assertTrue(res.isValid());
    }

    @Test
    public void testExpressionBuilder10() {
        double result = new ExpressionBuilder("1e1")
                .build()
                .evaluate();
        assertEquals(10d, result, 0d);
    }

    @Test
    public void testExpressionBuilder11() {
        double result = new ExpressionBuilder("1.11e-1")
                .build()
                .evaluate();
        assertEquals(0.111d, result, 0d);
    }

    @Test
    public void testExpressionBuilder12() {
        double result = new ExpressionBuilder("1.11e+1")
                .build()
                .evaluate();
        assertEquals(11.1d, result, 0d);
    }

    @Test
    public void testExpressionBuilder13() {
        double result = new ExpressionBuilder("-3^2")
                .build()
                .evaluate();
        assertEquals(-9d, result, 0d);
    }

    @Test
    public void testExpressionBuilder14() {
        double result = new ExpressionBuilder("(-3)^2")
                .build()
                .evaluate();
        assertEquals(9d, result, 0d);
    }

    @Test(expected = ArithmeticException.class)
    public void testExpressionBuilder15() {
        double result = new ExpressionBuilder("-3/0")
                .build()
                .evaluate();
    }

    @Test
    public void testExpressionBuilder16() {
        double result = new ExpressionBuilder("log(x) - y * (sqrt(x^cos(y)))")
                .variables("x", "y")
                .build()
                .setVariable("x", 1d)
                .setVariable("y", 2d)
                .evaluate();
    }

    @Test
    public void testExpressionBuilder17() {
        Expression e = new ExpressionBuilder("x-y*")
                .variables("x", "y")
                .build();
        ValidationResult res = e.validate(false);
        assertFalse(res.isValid());
        assertEquals(1, res.getErrors().size());
        assertEquals("Too many operators", res.getErrors().get(0));
    }

    @Test
    public void testExpressionBuilder18() {
        Expression e = new ExpressionBuilder("log(x) - y *")
                .variables("x", "y")
                .build();
        ValidationResult res = e.validate(false);
        assertFalse(res.isValid());
        assertEquals(1, res.getErrors().size());
        assertEquals("Too many operators", res.getErrors().get(0));
    }

    @Test
    public void testExpressionBuilder19() {
        Expression e = new ExpressionBuilder("x - y *")
                .variables("x", "y")
                .build();
        ValidationResult res = e.validate(false);
        assertFalse(res.isValid());
        assertEquals(1, res.getErrors().size());
        assertEquals("Too many operators", res.getErrors().get(0));
    }

    /* legacy tests from earlier exp4j versions */

    @Test
    public void testFunction1() {
        Function custom = new Function("timespi") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.PI;
            }
        };
        Expression e = new ExpressionBuilder("timespi(x)")
                .function(custom)
                .variables("x")
                .build()
                .setVariable("x", 1);
        double result = e.evaluate();
        assertEquals(result, PI, 0.0);
    }

    @Test
    public void testFunction2() {
        Function custom = new Function("loglog") {

            @Override
            public double apply(double... values) {
                return Math.log(Math.log(values[0]));
            }
        };
        Expression e = new ExpressionBuilder("loglog(x)")
                .variables("x")
                .function(custom)
                .build()
                .setVariable("x", 1);
        double result = e.evaluate();
        assertEquals(result, log(log(1)), 0.0);
    }

    @Test
    public void testFunction3() {
        Function custom1 = new Function("foo") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.E;
            }
        };
        Function custom2 = new Function("bar") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.PI;
            }
        };
        Expression e = new ExpressionBuilder("foo(bar(x))")
                .function(custom1)
                .function(custom2)
                .variables("x")
                .build()
                .setVariable("x", 1);
        double result = e.evaluate();
        assertEquals(result, 1 * E * PI, 0.0);
    }

    @Test
    public void testFunction4() {
        Function custom1 = new Function("foo") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.E;
            }
        };
        double varX = 32.24979131d;
        Expression e = new ExpressionBuilder("foo(log(x))")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", varX);
        double result = e.evaluate();
        assertEquals(result, log(varX) * E, 0.0);
    }

    @Test
    public void testFunction5() {
        Function custom1 = new Function("foo") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.E;
            }
        };
        Function custom2 = new Function("bar") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.PI;
            }
        };
        double varX = 32.24979131d;
        Expression e = new ExpressionBuilder("bar(foo(log(x)))")
                .variables("x")
                .function(custom1)
                .function(custom2)
                .build()
                .setVariable("x", varX);
        double result = e.evaluate();
        assertEquals(result, log(varX) * E * PI, 0.0);
    }

    @Test
    public void testFunction6() {
        Function custom1 = new Function("foo") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.E;
            }
        };
        Function custom2 = new Function("bar") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.PI;
            }
        };
        double varX = 32.24979131d;
        Expression e = new ExpressionBuilder("bar(foo(log(x)))")
                .variables("x")
                .functions(custom1, custom2)
                .build()
                .setVariable("x", varX);
        double result = e.evaluate();
        assertEquals(result, log(varX) * E * PI, 0.0);
    }

    @Test
    public void testFunction7() {
        Function custom1 = new Function("half") {

            @Override
            public double apply(double... values) {
                return values[0] / 2;
            }
        };
        Expression e = new ExpressionBuilder("half(x)")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", 1d);
        assertEquals(0.5d, e.evaluate(), 0.0);
    }

    @Test
    public void testFunction10() {
        Function custom1 = new Function("max", 2) {

            @Override
            public double apply(double... values) {
                return values[0] < values[1] ? values[1] : values[0];
            }
        };
        Expression e =
                new ExpressionBuilder("max(x,y)")
                        .variables("x", "y")
                        .function(custom1)
                        .build()
                        .setVariable("x", 1d)
                        .setVariable("y", 2d);
        assertEquals(2, e.evaluate(), 0.0);
    }

    @Test
    public void testFunction11() {
        Function custom1 = new Function("power", 2) {

            @Override
            public double apply(double... values) {
                return Math.pow(values[0], values[1]);
            }
        };
        Expression e =
                new ExpressionBuilder("power(x,y)")
                        .variables("x", "y")
                        .function(custom1)
                        .build()
                        .setVariable("x", 2d)
                        .setVariable("y",
                                4d);
        assertEquals(pow(2, 4), e.evaluate(), 0.0);
    }

    @Test
    public void testFunction12() {
        Function custom1 = new Function("max", 5) {

            @Override
            public double apply(double... values) {
                double max = values[0];
                for (int i = 1; i < numArguments; i++) {
                    if (values[i] > max) {
                        max = values[i];
                    }
                }
                return max;
            }
        };
        Expression e = new ExpressionBuilder("max(1,2.43311,51.13,43,12)")
                .function(custom1)
                .build();
        assertEquals(51.13d, e.evaluate(), 0.0);
    }

    @Test
    public void testFunction13() {
        Function custom1 = new Function("max", 3) {

            @Override
            public double apply(double... values) {
                double max = values[0];
                for (int i = 1; i < numArguments; i++) {
                    if (values[i] > max) {
                        max = values[i];
                    }
                }
                return max;
            }
        };
        double varX = Math.E;
        Expression e = new ExpressionBuilder("max(log(x),sin(x),x)")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", varX);
        assertEquals(varX, e.evaluate(), 0.0);
    }

    @Test
    public void testFunction14() {
        Function custom1 = new Function("multiply", 2) {

            @Override
            public double apply(double... values) {
                return values[0] * values[1];
            }
        };
        double varX = 1;
        Expression e = new ExpressionBuilder("multiply(sin(x),x+1)")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", varX);
        double expected = Math.sin(varX) * (varX + 1);
        double actual = e.evaluate();
        assertEquals(expected, actual, 0.0);
    }

    @Test
    public void testFunction15() {
        Function custom1 = new Function("timesPi") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.PI;
            }
        };
        double varX = 1;
        Expression e = new ExpressionBuilder("timesPi(x^2)")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", varX);
        double expected = varX * Math.PI;
        double actual = e.evaluate();
        assertEquals(expected, actual, 0.0);
    }

    @Test
    public void testFunction16() {
        Function custom1 = new Function("multiply", 3) {

            @Override
            public double apply(double... values) {
                return values[0] * values[1] * values[2];
            }
        };
        double varX = 1;
        Expression e = new ExpressionBuilder("multiply(sin(x),x+1^(-2),log(x))")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", varX);
        double expected = Math.sin(varX) * Math.pow((varX + 1), -2) * Math.log(varX);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testFunction17() {
        Function custom1 = new Function("timesPi") {

            @Override
            public double apply(double... values) {
                return values[0] * Math.PI;
            }
        };
        double varX = Math.E;
        Expression e = new ExpressionBuilder("timesPi(log(x^(2+1)))")
                .variables("x")
                .function(custom1)
                .build()
                .setVariable("x", varX);
        double expected = Math.log(Math.pow(varX, 3)) * Math.PI;
        assertEquals(expected, e.evaluate(), 0.0);
    }

    // thanks to Marcin Domanski who issued
    // http://jira.congrace.de/jira/browse/EXP-11
    // i have this test, which fails in 0.2.9
    @Test
    public void testFunction18() {
        Function minFunction = new Function("min", 2) {

            @Override
            public double apply(double[] values) {
                double currentMin = Double.POSITIVE_INFINITY;
                for (double value : values) {
                    currentMin = Math.min(currentMin, value);
                }
                return currentMin;
            }
        };
        ExpressionBuilder b = new ExpressionBuilder("-min(5, 0) + 10")
                .function(minFunction);
        double calculated = b.build().evaluate();
        assertEquals(10, calculated, 0.0);
    }

    // thanks to Sylvain Machefert who issued
    // http://jira.congrace.de/jira/browse/EXP-11
    // i have this test, which fails in 0.3.2
    @Test
    public void testFunction19() {
        Function minFunction = new Function("power", 2) {

            @Override
            public double apply(double[] values) {
                return Math.pow(values[0], values[1]);
            }
        };
        ExpressionBuilder b = new ExpressionBuilder("power(2,3)")
                .function(minFunction);
        double calculated = b.build().evaluate();
        assertEquals(Math.pow(2, 3), calculated, 0d);
    }

    // thanks to Narendra Harmwal who noticed that getArgumentCount was not
    // implemented
    // this test has been added in 0.3.5
    @Test
    public void testFunction20() {
        Function maxFunction = new Function("max", 3) {

            @Override
            public double apply(double... values) {
                double max = values[0];
                for (int i = 1; i < numArguments; i++) {
                    if (values[i] > max) {
                        max = values[i];
                    }
                }
                return max;
            }
        };
        ExpressionBuilder b = new ExpressionBuilder("max(1,2,3)")
                .function(maxFunction);
        double calculated = b.build().evaluate();
        assertEquals(3, maxFunction.getNumArguments());
        assertEquals(3, calculated, 0.0);
    }

    @Test
    public void testOperators1() {
        Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

            @Override
            public double apply(double... args) {
                final int arg = (int) args[0];
                if ((double) arg != args[0]) {
                    throw new IllegalArgumentException("Operand for factorial has to be an integer");
                }
                if (arg < 0) {
                    throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
                }
                double result = 1;
                for (int i = 1; i <= arg; i++) {
                    result *= i;
                }
                return result;
            }
        };

        Expression e = new ExpressionBuilder("1!").operator(factorial)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("2!").operator(factorial)
                .build();
        assertEquals(2d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("3!").operator(factorial)
                .build();
        assertEquals(6d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("4!").operator(factorial)
                .build();
        assertEquals(24d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("5!").operator(factorial)
                .build();
        assertEquals(120d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("11!").operator(factorial)
                .build();
        assertEquals(39916800d, e.evaluate(), 0.0);
    }

    @Test
    public void testOperators2() {
        Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

            @Override
            public double apply(double... args) {
                final int arg = (int) args[0];
                if ((double) arg != args[0]) {
                    throw new IllegalArgumentException("Operand for factorial has to be an integer");
                }
                if (arg < 0) {
                    throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
                }
                double result = 1;
                for (int i = 1; i <= arg; i++) {
                    result *= i;
                }
                return result;
            }
        };
        Expression e = new ExpressionBuilder("2^3!").operator(factorial)
                .build();
        assertEquals(64d, e.evaluate(), 0d);
        e = new ExpressionBuilder("3!^2").operator(factorial)
                .build();
        assertEquals(36d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("-(3!)^-1").operator(factorial)
                .build();
        double actual = e.evaluate();
        assertEquals(Math.pow(-6d, -1), actual, 0d);
    }

    @Test
    public void testOperators3() {
        Operator gteq = new Operator(">=", 2, true, Operator.PRECEDENCE_ADDITION - 1) {

            @Override
            public double apply(double[] values) {
                if (values[0] >= values[1]) {
                    return 1d;
                } else {
                    return 0d;
                }
            }
        };
        Expression e = new ExpressionBuilder("1>=2").operator(gteq)
                .build();
        assertEquals(0d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("2>=1").operator(gteq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("-2>=1").operator(gteq)
                .build();
        assertEquals(0d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("-2>=-1").operator(gteq)
                .build();
        assertEquals(0d, e.evaluate(), 0.0);
    }

    @Test
    public void testModulo1() {
        double result = new ExpressionBuilder("33%(20/2)%2")
                .build().evaluate();
        assertEquals(1d, result, 0.0);
    }

    @Test
    public void testOperators4() {
        Operator greaterEq = new Operator(">=", 2, true, 4) {

            @Override
            public double apply(double[] values) {
                if (values[0] >= values[1]) {
                    return 1d;
                } else {
                    return 0d;
                }
            }
        };
        Operator greater = new Operator(">", 2, true, 4) {

            @Override
            public double apply(double[] values) {
                if (values[0] > values[1]) {
                    return 1d;
                } else {
                    return 0d;
                }
            }
        };
        Operator newPlus = new Operator(">=>", 2, true, 4) {

            @Override
            public double apply(double[] values) {
                return values[0] + values[1];
            }
        };
        Expression e = new ExpressionBuilder("1>2").operator(greater)
                .build();
        assertEquals(0d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("2>=2").operator(greaterEq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1>=>2").operator(newPlus)
                .build();
        assertEquals(3d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1>=>2>2").operator(greater).operator(newPlus)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1>=>2>2>=1").operator(greater).operator(newPlus)
                .operator(greaterEq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1 >=> 2 > 2 >= 1").operator(greater).operator(newPlus)
                .operator(greaterEq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1 >=> 2 >= 2 > 1").operator(greater).operator(newPlus)
                .operator(greaterEq)
                .build();
        assertEquals(0d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1 >=> 2 >= 2 > 0").operator(greater).operator(newPlus)
                .operator(greaterEq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("1 >=> 2 >= 2 >= 1").operator(greater).operator(newPlus)
                .operator(greaterEq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidOperator1() {
        Operator fail = new Operator("2", 2, true, 1) {

            @Override
            public double apply(double[] values) {
                return 0;
            }
        };
        new ExpressionBuilder("1").operator(fail)
                .build();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidFunction1() {
        Function func = new Function("1gd") {

            @Override
            public double apply(double... args) {
                return 0;
            }
        };
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidFunction2() {
        Function func = new Function("+1gd") {

            @Override
            public double apply(double... args) {
                return 0;
            }
        };
    }

    @Test
    public void testExpressionBuilder01() {
        Expression e = new ExpressionBuilder("7*x + 3*y")
                .variables("x", "y")
                .build()
                .setVariable("x", 1)
                .setVariable("y", 2);
        double result = e.evaluate();
        assertEquals(13d, result, 0.0);
    }

    @Test
    public void testExpressionBuilder02() {
        Expression e = new ExpressionBuilder("7*x + 3*y")
                .variables("x", "y")
                .build()
                .setVariable("x", 1)
                .setVariable("y", 2);
        double result = e.evaluate();
        assertEquals(13d, result, 0.0);
    }

    @Test
    public void testExpressionBuilder03() {
        double varX = 1.3d;
        double varY = 4.22d;
        Expression e = new ExpressionBuilder("7*x + 3*y - log(y/x*12)^y")
                .variables("x", "y")
                .build()
                .setVariable("x", varX)
                .setVariable("y",
                        varY);
        double result = e.evaluate();
        assertEquals(result, 7 * varX + 3 * varY - pow(log(varY / varX * 12), varY), 0.0);
    }

    @Test
    public void testExpressionBuilder04() {
        double varX = 1.3d;
        double varY = 4.22d;
        Expression e =
                new ExpressionBuilder("7*x + 3*y - log(y/x*12)^y")
                        .variables("x", "y")
                        .build()
                        .setVariable("x", varX)
                        .setVariable("y", varY);
        double result = e.evaluate();
        assertEquals(result, 7 * varX + 3 * varY - pow(log(varY / varX * 12), varY), 0.0);
        varX = 1.79854d;
        varY = 9281.123d;
        e.setVariable("x", varX);
        e.setVariable("y", varY);
        result = e.evaluate();
        assertEquals(result, 7 * varX + 3 * varY - pow(log(varY / varX * 12), varY), 0.0);
    }

    @Test
    public void testExpressionBuilder05() {
        double varX = 1.3d;
        double varY = 4.22d;
        Expression e = new ExpressionBuilder("3*y")
                .variables("y")
                .build()
                .setVariable("x", varX)
                .setVariable("y", varY);
        double result = e.evaluate();
        assertEquals(result, 3 * varY, 0.0);
    }

    @Test
    public void testExpressionBuilder06() {
        double varX = 1.3d;
        double varY = 4.22d;
        double varZ = 4.22d;
        Expression e = new ExpressionBuilder("x * y * z")
                .variables("x", "y", "z")
                .build();
        e.setVariable("x", varX);
        e.setVariable("y", varY);
        e.setVariable("z", varZ);
        double result = e.evaluate();
        assertEquals(result, varX * varY * varZ, 0.0);
    }

    @Test
    public void testExpressionBuilder07() {
        double varX = 1.3d;
        Expression e = new ExpressionBuilder("log(sin(x))")
                .variables("x")
                .build()
                .setVariable("x", varX);
        double result = e.evaluate();
        assertEquals(result, log(sin(varX)), 0.0);
    }

    @Test
    public void testExpressionBuilder08() {
        double varX = 1.3d;
        Expression e = new ExpressionBuilder("log(sin(x))")
                .variables("x")
                .build()
                .setVariable("x", varX);
        double result = e.evaluate();
        assertEquals(result, log(sin(varX)), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testSameName() {
        Function custom = new Function("bar") {

            @Override
            public double apply(double... values) {
                return values[0] / 2;
            }
        };
        double varBar = 1.3d;
        Expression e = new ExpressionBuilder("bar(bar)")
                .variables("bar")
                .function(custom)
                .build()
                .setVariable("bar", varBar);
        ValidationResult res = e.validate();
        assertFalse(res.isValid());
        assertEquals(1, res.getErrors().size());
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidFunction() {
        double varY = 4.22d;
        Expression e = new ExpressionBuilder("3*invalid_function(y)")
                .variables("<")
                .build()
                .setVariable("y", varY);
        e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testMissingVar() {
        double varY = 4.22d;
        Expression e = new ExpressionBuilder("3*y*z")
                .variables("y", "z")
                .build()
                .setVariable("y", varY);
        e.evaluate();
    }

    @Test
    public void testUnaryMinusPowerPrecedence() {
        Expression e = new ExpressionBuilder("-1^2")
                .build();
        assertEquals(-1d, e.evaluate(), 0d);
    }

    @Test
    public void testUnaryMinus() {
        Expression e = new ExpressionBuilder("-1")
                .build();
        assertEquals(-1d, e.evaluate(), 0d);
    }

    @Test
    public void testExpression1() {
        String expr;
        double expected;
        expr = "2 + 4";
        expected = 6d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression10() {
        String expr;
        double expected;
        expr = "1 * 1.5 + 1";
        expected = 1 * 1.5 + 1;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression11() {
        double x = 1d;
        double y = 2d;
        String expr = "log(x) ^ sin(y)";
        double expected = Math.pow(Math.log(x), Math.sin(y));
        Expression e = new ExpressionBuilder(expr)
                .variables("x", "y")
                .build()
                .setVariable("x", x)
                .setVariable("y", y);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression12() {
        String expr = "log(2.5333333333)^(0-1)";
        double expected = Math.pow(Math.log(2.5333333333d), -1);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression13() {
        String expr = "2.5333333333^(0-1)";
        double expected = Math.pow(2.5333333333d, -1);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression14() {
        String expr = "2 * 17.41 + (12*2)^(0-1)";
        double expected = 2 * 17.41d + Math.pow((12 * 2), -1);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression15() {
        String expr = "2.5333333333 * 17.41 + (12*2)^log(2.764)";
        double expected = 2.5333333333d * 17.41d + Math.pow((12 * 2), Math.log(2.764d));
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression16() {
        String expr = "2.5333333333/2 * 17.41 + (12*2)^(log(2.764) - sin(5.6664))";
        double expected = 2.5333333333d / 2 * 17.41d + Math.pow((12 * 2), Math.log(2.764d) - Math.sin(5.6664d));
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression17() {
        String expr = "x^2 - 2 * y";
        double x = Math.E;
        double y = Math.PI;
        double expected = x * x - 2 * y;
        Expression e = new ExpressionBuilder(expr)
                .variables("x", "y")
                .build()
                .setVariable("x", x)
                .setVariable("y", y);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression18() {
        String expr = "-3";
        double expected = -3;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression19() {
        String expr = "-3 * -24.23";
        double expected = -3 * -24.23d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression2() {
        String expr;
        double expected;
        expr = "2+3*4-12";
        expected = 2 + 3 * 4 - 12;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression20() {
        String expr = "-2 * 24/log(2) -2";
        double expected = -2 * 24 / Math.log(2) - 2;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression21() {
        String expr = "-2 *33.34/log(x)^-2 + 14 *6";
        double x = 1.334d;
        double expected = -2 * 33.34 / Math.pow(Math.log(x), -2) + 14 * 6;
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build()
                .setVariable("x", x);
        assertEquals(expected, e.evaluate(), 0d);
    }

    @Test
    public void testExpressionPower() {
        String expr = "2^-2";
        double expected = Math.pow(2, -2);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0d);
    }

    @Test
    public void testExpressionMultiplication() {
        String expr = "2*-2";
        double expected = -4d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0d);
    }

    @Test
    public void testExpression22() {
        String expr = "-2 *33.34/log(x)^-2 + 14 *6";
        double x = 1.334d;
        double expected = -2 * 33.34 / Math.pow(Math.log(x), -2) + 14 * 6;
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build()
                .setVariable("x", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression23() {
        String expr = "-2 *33.34/(log(foo)^-2 + 14 *6) - sin(foo)";
        double x = 1.334d;
        double expected = -2 * 33.34 / (Math.pow(Math.log(x), -2) + 14 * 6) - Math.sin(x);
        Expression e = new ExpressionBuilder(expr)
                .variables("foo")
                .build()
                .setVariable("foo", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression24() {
        String expr = "3+4-log(23.2)^(2-1) * -1";
        double expected = 3 + 4 - Math.pow(Math.log(23.2), (2 - 1)) * -1;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression25() {
        String expr = "+3+4-+log(23.2)^(2-1) * + 1";
        double expected = 3 + 4 - Math.log(23.2d);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression26() {
        String expr = "14 + -(1 / 2.22^3)";
        double expected = 14 + -(1d / Math.pow(2.22d, 3d));
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression27() {
        String expr = "12^-+-+-+-+-+-+---2";
        double expected = Math.pow(12, -2);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression28() {
        String expr = "12^-+-+-+-+-+-+---2 * (-14) / 2 ^ -log(2.22323) ";
        double expected = Math.pow(12, -2) * -14 / Math.pow(2, -Math.log(2.22323));
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression29() {
        String expr = "24.3343 % 3";
        double expected = 24.3343 % 3;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testVarName1() {
        String expr = "12.23 * foo.bar";
        Expression e = new ExpressionBuilder(expr)
                .variables("foo.bar")
                .build()
                .setVariable("foo.bar", 1d);
        assertEquals(12.23, e.evaluate(), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testMisplacedSeparator() {
        String expr = "12.23 * ,foo";
        Expression e = new ExpressionBuilder(expr)
                .build()
                .setVariable(",foo", 1d);
        assertEquals(12.23, e.evaluate(), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidVarName() {
        String expr = "12.23 * @foo";
        Expression e = new ExpressionBuilder(expr)
                .build()
                .setVariable("@foo", 1d);
        assertEquals(12.23, e.evaluate(), 0.0);
    }

    @Test
    public void testVarMap() {
        String expr = "12.23 * foo - bar";
        Map<String, Double> variables = new HashMap<>();
        variables.put("foo", 2d);
        variables.put("bar", 3.3d);
        Expression e = new ExpressionBuilder(expr)
                .variables(variables.keySet())
                .build()
                .setVariables(variables);
        assertEquals(12.23d * 2d - 3.3d, e.evaluate(), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidNumberOfArguments1() {
        String expr = "log(2,2)";
        Expression e = new ExpressionBuilder(expr)
                .build();
        e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidNumberOfArguments2() {
        Function avg = new Function("avg", 4) {

            @Override
            public double apply(double... args) {
                double sum = 0;
                for (double arg : args) {
                    sum += arg;
                }
                return sum / args.length;
            }
        };
        String expr = "avg(2,2)";
        Expression e = new ExpressionBuilder(expr)
                .build();
        e.evaluate();
    }

    @Test
    public void testExpression3() {
        String expr;
        double expected;
        expr = "2+4*5";
        expected = 2 + 4 * 5;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression30() {
        String expr = "24.3343 % 3 * 20 ^ -(2.334 % log(2 / 14))";
        double expected = 24.3343d % 3 * Math.pow(20, -(2.334 % Math.log(2d / 14d)));
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression31() {
        String expr = "-2 *33.34/log(y_x)^-2 + 14 *6";
        double x = 1.334d;
        double expected = -2 * 33.34 / Math.pow(Math.log(x), -2) + 14 * 6;
        Expression e = new ExpressionBuilder(expr)
                .variables("y_x")
                .build()
                .setVariable("y_x", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression32() {
        String expr = "-2 *33.34/log(y_2x)^-2 + 14 *6";
        double x = 1.334d;
        double expected = -2 * 33.34 / Math.pow(Math.log(x), -2) + 14 * 6;
        Expression e = new ExpressionBuilder(expr)
                .variables("y_2x")
                .build()
                .setVariable("y_2x", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression33() {
        String expr = "-2 *33.34/log(_y)^-2 + 14 *6";
        double x = 1.334d;
        double expected = -2 * 33.34 / Math.pow(Math.log(x), -2) + 14 * 6;
        Expression e = new ExpressionBuilder(expr)
                .variables("_y")
                .build()
                .setVariable("_y", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression34() {
        String expr = "-2 + + (+4) +(4)";
        double expected = -2 + 4 + 4;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression40() {
        String expr = "1e1";
        double expected = 10d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression41() {
        String expr = "1e-1";
        double expected = 0.1d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    /*
     * Added tests for expressions with scientific notation see http://jira.congrace.de/jira/browse/EXP-17
     */
    @Test
    public void testExpression42() {
        String expr = "7.2973525698e-3";
        double expected = 7.2973525698e-3d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression43() {
        String expr = "6.02214E23";
        double expected = 6.02214e23d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        double result = e.evaluate();
        assertEquals(expected, result, 0.0);
    }

    @Test
    public void testExpression44() {
        String expr = "6.02214E23";
        double expected = 6.02214e23d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test(expected = NumberFormatException.class)
    public void testExpression45() {
        String expr = "6.02214E2E3";
        new ExpressionBuilder(expr)
                .build();
    }

    @Test(expected = NumberFormatException.class)
    public void testExpression46() {
        String expr = "6.02214e2E3";
        new ExpressionBuilder(expr)
                .build();
    }

    // tests for EXP-20: No exception is thrown for unmatched parenthesis in
    // build
    // Thanks go out to maheshkurmi for reporting
    @Test(expected = IllegalArgumentException.class)
    public void testExpression48() {
        String expr = "(1*2";
        Expression e = new ExpressionBuilder(expr)
                .build();
        double result = e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testExpression49() {
        String expr = "{1*2";
        Expression e = new ExpressionBuilder(expr)
                .build();
        double result = e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testExpression50() {
        String expr = "[1*2";
        Expression e = new ExpressionBuilder(expr)
                .build();
        double result = e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testExpression51() {
        String expr = "(1*{2+[3}";
        Expression e = new ExpressionBuilder(expr)
                .build();
        double result = e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testExpression52() {
        String expr = "(1*(2+(3";
        Expression e = new ExpressionBuilder(expr)
                .build();
        double result = e.evaluate();
    }

    @Test
    public void testExpression53() {
        String expr = "14 * 2x";
        Expression exp = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        exp.setVariable("x", 1.5d);
        assertTrue(exp.validate().isValid());
        assertEquals(14d * 2d * 1.5d, exp.evaluate(), 0d);
    }

    @Test
    public void testExpression54() {
        String expr = "2 ((-(x)))";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        e.setVariable("x", 1.5d);
        assertEquals(-3d, e.evaluate(), 0d);
    }

    @Test
    public void testExpression55() {
        String expr = "2 sin(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        e.setVariable("x", 2d);
        assertEquals(sin(2d) * 2, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression56() {
        String expr = "2 sin(3x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        e.setVariable("x", 2d);
        assertEquals(sin(6d) * 2d, e.evaluate(), 0.0);
    }

    @Test
    public void testDocumentationExample1() {
        Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
                .variables("x", "y")
                .build()
                .setVariable("x", 2.3)
                .setVariable("y", 3.14);
        double result = e.evaluate();
        double expected = 3 * Math.sin(3.14d) - 2d / (2.3d - 2d);
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testDocumentationExample2() throws Exception {
        ExecutorService exec = Executors.newFixedThreadPool(1);
        Expression e = new ExpressionBuilder("3log(y)/(x+1)")
                .variables("x", "y")
                .build()
                .setVariable("x", 2.3)
                .setVariable("y", 3.14);
        Future<Double> result = e.evaluateAsync(exec);
        double expected = 3 * Math.log(3.14d) / (3.3);
        assertEquals(expected, result.get(), 0d);
    }

    @Test
    public void testDocumentationExample3() {
        double result = new ExpressionBuilder("2cos(xy)")
                .variables("x", "y")
                .build()
                .setVariable("x", 0.5d)
                .setVariable("y", 0.25d)
                .evaluate();
        assertEquals(2d * Math.cos(0.5d * 0.25d), result, 0d);
    }

    @Test
    public void testDocumentationExample4() {
        String expr = "pi+��+e+��";
        double expected = 2 * Math.PI + Math.E + 1.61803398874d;
        Expression e = new ExpressionBuilder(expr).build();
        assertEquals(expected, e.evaluate(), 0d);
    }

    @Test
    public void testDocumentationExample5() {
        String expr = "7.2973525698e-3";
        double expected = Double.parseDouble(expr);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0d);
    }


    @Test
    public void testDocumentationExample6() {
        Function logb = new Function("logb", 2) {
            @Override
            public double apply(double... args) {
                return Math.log(args[0]) / Math.log(args[1]);
            }
        };
        double result = new ExpressionBuilder("logb(8, 2)")
                .function(logb)
                .build()
                .evaluate();
        double expected = 3;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testDocumentationExample7() {
        Function avg = new Function("avg", 4) {

            @Override
            public double apply(double... args) {
                double sum = 0;
                for (double arg : args) {
                    sum += arg;
                }
                return sum / args.length;
            }
        };
        double result = new ExpressionBuilder("avg(1,2,3,4)")
                .function(avg)
                .build()
                .evaluate();

        double expected = 2.5d;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testDocumentationExample8() {
        Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

            @Override
            public double apply(double... args) {
                final int arg = (int) args[0];
                if ((double) arg != args[0]) {
                    throw new IllegalArgumentException("Operand for factorial has to be an integer");
                }
                if (arg < 0) {
                    throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
                }
                double result = 1;
                for (int i = 1; i <= arg; i++) {
                    result *= i;
                }
                return result;
            }
        };

        double result = new ExpressionBuilder("3!")
                .operator(factorial)
                .build()
                .evaluate();

        double expected = 6d;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testDocumentationExample9() {
        Operator gteq = new Operator(">=", 2, true, Operator.PRECEDENCE_ADDITION - 1) {

            @Override
            public double apply(double[] values) {
                if (values[0] >= values[1]) {
                    return 1d;
                } else {
                    return 0d;
                }
            }
        };

        Expression e = new ExpressionBuilder("1>=2").operator(gteq)
                .build();
        assertEquals(0d, e.evaluate(), 0.0);
        e = new ExpressionBuilder("2>=1").operator(gteq)
                .build();
        assertEquals(1d, e.evaluate(), 0.0);
    }

    @Test(expected = ArithmeticException.class)
    public void testDocumentationExample10() {
        Operator reciprocal = new Operator("$", 1, true, Operator.PRECEDENCE_DIVISION) {
            @Override
            public double apply(final double... args) {
                if (args[0] == 0d) {
                    throw new ArithmeticException("Division by zero!");
                }
                return 1d / args[0];
            }
        };
        Expression e = new ExpressionBuilder("0$").operator(reciprocal).build();
        e.evaluate();
    }

    @Test
    public void testDocumentationExample11() {
        Expression e = new ExpressionBuilder("x")
                .variable("x")
                .build();

        ValidationResult res = e.validate();
        assertFalse(res.isValid());
        assertEquals(1, res.getErrors().size());

        e.setVariable("x", 1d);
        res = e.validate();
        assertTrue(res.isValid());
    }

    @Test
    public void testDocumentationExample12() {
        Expression e = new ExpressionBuilder("x")
                .variable("x")
                .build();

        ValidationResult res = e.validate(false);
        assertTrue(res.isValid());
        assertNull(res.getErrors());
    }

    // Thanks go out to Johan Bj��rk for reporting the division by zero problem EXP-22
    // https://www.objecthunter.net/jira/browse/EXP-22
    @Test(expected = ArithmeticException.class)
    public void testExpression57() {
        String expr = "1 / 0";
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(Double.POSITIVE_INFINITY, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression58() {
        String expr = "17 * sqrt(-1) * 12";
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertTrue(Double.isNaN(e.evaluate()));
    }

    // Thanks go out to Alex Dolinsky for reporting the missing exception when an empty
    // expression is passed as in new ExpressionBuilder("")
    @Test(expected = IllegalArgumentException.class)
    public void testExpression59() {
        Expression e = new ExpressionBuilder("")
                .build();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testExpression60() {
        Expression e = new ExpressionBuilder("   ")
                .build();
        e.evaluate();
    }

    @Test(expected = ArithmeticException.class)
    public void testExpression61() {
        Expression e = new ExpressionBuilder("14 % 0")
                .build();
        e.evaluate();
    }

    // https://www.objecthunter.net/jira/browse/EXP-24
    // thanks go out to R��mi for the issue report
    @Test
    public void testExpression62() {
        Expression e = new ExpressionBuilder("x*1.0e5+5")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(E * 1.0 * pow(10, 5) + 5, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression63() {
        Expression e = new ExpressionBuilder("log10(5)")
                .build();
        assertEquals(Math.log10(5), e.evaluate(), 0d);
    }

    @Test
    public void testExpression64() {
        Expression e = new ExpressionBuilder("log2(5)")
                .build();
        assertEquals(Math.log(5) / Math.log(2), e.evaluate(), 0d);
    }

    @Test
    public void testExpression65() {
        Expression e = new ExpressionBuilder("2log(e)")
                .variables("e")
                .build()
                .setVariable("e", Math.E);

        assertEquals(2d, e.evaluate(), 0d);
    }

    @Test
    public void testExpression66() {
        Expression e = new ExpressionBuilder("log(e)2")
                .variables("e")
                .build()
                .setVariable("e", Math.E);

        assertEquals(2d, e.evaluate(), 0d);
    }

    @Test
    public void testExpression67() {
        Expression e = new ExpressionBuilder("2esin(pi/2)")
                .variables("e", "pi")
                .build()
                .setVariable("e", Math.E)
                .setVariable("pi", Math.PI);

        assertEquals(2 * Math.E * Math.sin(Math.PI / 2d), e.evaluate(), 0d);
    }

    @Test
    public void testExpression68() {
        Expression e = new ExpressionBuilder("2x")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(2 * Math.E, e.evaluate(), 0d);
    }

    @Test
    public void testExpression69() {
        Expression e = new ExpressionBuilder("2x2")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(4 * Math.E, e.evaluate(), 0d);
    }

    @Test
    public void testExpression70() {
        Expression e = new ExpressionBuilder("2xx")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(2 * Math.E * Math.E, e.evaluate(), 0d);
    }

    @Test
    public void testExpression71() {
        Expression e = new ExpressionBuilder("x2x")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(2 * Math.E * Math.E, e.evaluate(), 0d);
    }

    @Test
    public void testExpression72() {
        Expression e = new ExpressionBuilder("2cos(x)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(2 * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression73() {
        Expression e = new ExpressionBuilder("cos(x)2")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(2 * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression74() {
        Expression e = new ExpressionBuilder("cos(x)(-2)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(-2d * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression75() {
        Expression e = new ExpressionBuilder("(-2)cos(x)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(-2d * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression76() {
        Expression e = new ExpressionBuilder("(-x)cos(x)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(-E * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression77() {
        Expression e = new ExpressionBuilder("(-xx)cos(x)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(-E * E * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression78() {
        Expression e = new ExpressionBuilder("(xx)cos(x)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(E * E * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression79() {
        Expression e = new ExpressionBuilder("cos(x)(xx)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(E * E * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression80() {
        Expression e = new ExpressionBuilder("cos(x)(xy)")
                .variables("x", "y")
                .build()
                .setVariable("x", Math.E)
                .setVariable("y", Math.sqrt(2));
        assertEquals(sqrt(2) * E * Math.cos(Math.E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression81() {
        Expression e = new ExpressionBuilder("cos(xy)")
                .variables("x", "y")
                .build()
                .setVariable("x", Math.E)
                .setVariable("y", Math.sqrt(2));
        assertEquals(cos(sqrt(2) * E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression82() {
        Expression e = new ExpressionBuilder("cos(2x)")
                .variables("x")
                .build()
                .setVariable("x", Math.E);
        assertEquals(cos(2 * E), e.evaluate(), 0d);
    }

    @Test
    public void testExpression83() {
        Expression e = new ExpressionBuilder("cos(xlog(xy))")
                .variables("x", "y")
                .build()
                .setVariable("x", Math.E)
                .setVariable("y", Math.sqrt(2));
        assertEquals(cos(E * log(E * sqrt(2))), e.evaluate(), 0d);
    }

    @Test
    public void testExpression84() {
        Expression e = new ExpressionBuilder("3x_1")
                .variables("x_1")
                .build()
                .setVariable("x_1", Math.E);
        assertEquals(3d * E, e.evaluate(), 0d);
    }

    @Test
    public void testExpression85() {
        Expression e = new ExpressionBuilder("1/2x")
                .variables("x")
                .build()
                .setVariable("x", 6);
        assertEquals(3d, e.evaluate(), 0d);
    }

    // thanks got out to David Sills
    @Test(expected = IllegalArgumentException.class)
    public void testSpaceBetweenNumbers() {
        Expression e = new ExpressionBuilder("1 1")
                .build();
    }

    // thanks go out to Janny for providing the tests and the bug report
    @Test
    public void testUnaryMinusInParenthesisSpace() {
        ExpressionBuilder b = new ExpressionBuilder("( -1)^2");
        double calculated = b.build().evaluate();
        assertEquals(1d, calculated, 0.0);
    }

    @Test
    public void testUnaryMinusSpace() {
        ExpressionBuilder b = new ExpressionBuilder(" -1 + 2");
        double calculated = b.build().evaluate();
        assertEquals(1d, calculated, 0.0);
    }

    @Test
    public void testUnaryMinusSpaces() {
        ExpressionBuilder b = new ExpressionBuilder(" -1 + + 2 +   -   1");
        double calculated = b.build().evaluate();
        assertEquals(0d, calculated, 0.0);
    }

    @Test
    public void testUnaryMinusSpace1() {
        ExpressionBuilder b = new ExpressionBuilder("-1");
        double calculated = b.build().evaluate();
        assertEquals(calculated, -1d, 0.0);
    }

    @Test
    public void testExpression4() {
        String expr;
        double expected;
        expr = "2+4 * 5";
        expected = 2 + 4 * 5;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression5() {
        String expr;
        double expected;
        expr = "(2+4)*5";
        expected = (2 + 4) * 5;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression6() {
        String expr;
        double expected;
        expr = "(2+4)*5 + 2.5*2";
        expected = (2 + 4) * 5 + 2.5 * 2;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression7() {
        String expr;
        double expected;
        expr = "(2+4)*5 + 10/2";
        expected = (2 + 4) * 5 + 10 / 2;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression8() {
        String expr;
        double expected;
        expr = "(2 * 3 +4)*5 + 10/2";
        expected = (2 * 3 + 4) * 5 + 10 / 2;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testExpression9() {
        String expr;
        double expected;
        expr = "(2 * 3 +4)*5 +4 + 10/2";
        expected = 59; //(2 * 3 + 4) * 5 + 4 + 10 / 2 = 59
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testFailUnknownFunction1() {
        String expr;
        expr = "lig(1)";
        Expression e = new ExpressionBuilder(expr)
                .build();
        e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testFailUnknownFunction2() {
        String expr;
        expr = "galength(1)";
        new ExpressionBuilder(expr)
                .build().evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testFailUnknownFunction3() {
        String expr;
        expr = "tcos(1)";
        Expression exp = new ExpressionBuilder(expr)
                .build();
        double result = exp.evaluate();
        System.out.println(result);
    }

    @Test
    public void testFunction22() {
        String expr;
        expr = "cos(cos_1)";
        Expression e = new ExpressionBuilder(expr)
                .variables("cos_1")
                .build()
                .setVariable("cos_1", 1d);
        assertEquals(e.evaluate(), cos(1d), 0.0);
    }

    @Test
    public void testFunction23() {
        String expr;
        expr = "log1p(1)";
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(log1p(1d), e.evaluate(), 0d);
    }

    @Test
    public void testFunction24() {
        String expr;
        expr = "pow(3,3)";
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(27d, e.evaluate(), 0d);
    }

    @Test
    public void testPostfix1() {
        String expr;
        double expected;
        expr = "2.2232^0.1";
        expected = Math.pow(2.2232d, 0.1d);
        double actual = new ExpressionBuilder(expr)
                .build().evaluate();
        assertEquals(expected, actual, 0.0);
    }

    @Test
    public void testPostfixEverything() {
        String expr;
        double expected;
        expr = "(sin(12) + log(34)) * 3.42 - cos(2.234-log(2))";
        expected = (Math.sin(12) + Math.log(34)) * 3.42 - Math.cos(2.234 - Math.log(2));
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixExponentiation1() {
        String expr;
        double expected;
        expr = "2^3";
        expected = Math.pow(2, 3);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixExponentiation2() {
        String expr;
        double expected;
        expr = "24 + 4 * 2^3";
        expected = 24 + 4 * Math.pow(2, 3);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixExponentiation3() {
        String expr;
        double expected;
        double x = 4.334d;
        expr = "24 + 4 * 2^x";
        expected = 24 + 4 * Math.pow(2, x);
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build()
                .setVariable("x", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixExponentiation4() {
        String expr;
        double expected;
        double x = 4.334d;
        expr = "(24 + 4) * 2^log(x)";
        expected = (24 + 4) * Math.pow(2, Math.log(x));
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build()
                .setVariable("x", x);
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction1() {
        String expr;
        double expected;
        expr = "log(1) * sin(0)";
        expected = Math.log(1) * Math.sin(0);
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction10() {
        String expr;
        double expected;
        expr = "cbrt(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.cbrt(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction11() {
        String expr;
        double expected;
        expr = "cos(x) - (1/cbrt(x))";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            if (x == 0d) continue;
            expected = Math.cos(x) - (1 / Math.cbrt(x));
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction12() {
        String expr;
        double expected;
        expr = "acos(x) * expm1(asin(x)) - exp(atan(x)) + floor(x) + cosh(x) - sinh(cbrt(x))";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected =
                    Math.acos(x) * Math.expm1(Math.asin(x)) - Math.exp(Math.atan(x)) + Math.floor(x) + Math.cosh(x)
                            - Math.sinh(Math.cbrt(x));
            if (Double.isNaN(expected)) {
                assertTrue(Double.isNaN(e.setVariable("x", x).evaluate()));
            } else {
                assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
            }
        }
    }

    @Test
    public void testPostfixFunction13() {
        String expr;
        double expected;
        expr = "acos(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.acos(x);
            if (Double.isNaN(expected)) {
                assertTrue(Double.isNaN(e.setVariable("x", x).evaluate()));
            } else {
                assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
            }
        }
    }

    @Test
    public void testPostfixFunction14() {
        String expr;
        double expected;
        expr = " expm1(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.expm1(x);
            if (Double.isNaN(expected)) {
                assertTrue(Double.isNaN(e.setVariable("x", x).evaluate()));
            } else {
                assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
            }
        }
    }

    @Test
    public void testPostfixFunction15() {
        String expr;
        double expected;
        expr = "asin(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.asin(x);
            if (Double.isNaN(expected)) {
                assertTrue(Double.isNaN(e.setVariable("x", x).evaluate()));
            } else {
                assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
            }
        }
    }

    @Test
    public void testPostfixFunction16() {
        String expr;
        double expected;
        expr = " exp(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.exp(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction17() {
        String expr;
        double expected;
        expr = "floor(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.floor(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction18() {
        String expr;
        double expected;
        expr = " cosh(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.cosh(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction19() {
        String expr;
        double expected;
        expr = "sinh(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.sinh(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction20() {
        String expr;
        double expected;
        expr = "cbrt(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.cbrt(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction21() {
        String expr;
        double expected;
        expr = "tanh(x)";
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        for (double x = -10; x < 10; x = x + 0.5d) {
            expected = Math.tanh(x);
            assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
        }
    }

    @Test
    public void testPostfixFunction2() {
        String expr;
        double expected;
        expr = "log(1)";
        expected = 0d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction3() {
        String expr;
        double expected;
        expr = "sin(0)";
        expected = 0d;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction5() {
        String expr;
        double expected;
        expr = "ceil(2.3) +1";
        expected = Math.ceil(2.3) + 1;
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction6() {
        String expr;
        double expected;
        double x = 1.565d;
        double y = 2.1323d;
        expr = "ceil(x) + 1 / y * abs(1.4)";
        expected = Math.ceil(x) + 1 / y * Math.abs(1.4);
        Expression e = new ExpressionBuilder(expr)
                .variables("x", "y")
                .build();
        assertEquals(expected, e.setVariable("x", x)
                .setVariable("y", y).evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction7() {
        String expr;
        double expected;
        double x = Math.E;
        expr = "tan(x)";
        expected = Math.tan(x);
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction8() {
        String expr;
        double expected;
        expr = "2^3.4223232 + tan(e)";
        expected = Math.pow(2, 3.4223232d) + Math.tan(Math.E);
        Expression e = new ExpressionBuilder(expr)
                .variables("e")
                .build();
        assertEquals(expected, e.setVariable("e", E).evaluate(), 0.0);
    }

    @Test
    public void testPostfixFunction9() {
        String expr;
        double expected;
        double x = Math.E;
        expr = "cbrt(x)";
        expected = Math.cbrt(x);
        Expression e = new ExpressionBuilder(expr)
                .variables("x")
                .build();
        assertEquals(expected, e.setVariable("x", x).evaluate(), 0.0);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testPostfixInvalidVariableName() {
        String expr;
        double expected;
        double x = 4.5334332d;
        double log = Math.PI;
        expr = "x * pi";
        expected = x * log;
        Expression e = new ExpressionBuilder(expr)
                .variables("x", "pi")
                .build();
        assertEquals(expected, e.setVariable("x", x)
                .setVariable("log", log).evaluate(), 0.0);
    }

    @Test
    public void testPostfixParenthesis() {
        String expr;
        double expected;
        expr = "(3 + 3 * 14) * (2 * (24-17) - 14)/((34) -2)";
        expected = 0; //(3 + 3 * 14) * (2 * (24-17) - 14)/((34) -2) = 0
        Expression e = new ExpressionBuilder(expr)
                .build();
        assertEquals(expected, e.evaluate(), 0.0);
    }

    @Test
    public void testPostfixVariables() {
        String expr;
        double expected;
        double x = 4.5334332d;
        double pi = Math.PI;
        expr = "x * pi";
        expected = x * pi;
        Expression e = new ExpressionBuilder(expr)
                .variables("x", "pi")
                .build();
        assertEquals(expected, e.setVariable("x", x)
                .setVariable("pi", pi).evaluate(), 0.0);
    }

    @Test
    public void testUnicodeVariable1() {
        Expression e = new ExpressionBuilder("��")
                .variable("��")
                .build()
                .setVariable("��", E);
        assertEquals(E, e.evaluate(), 0d);
    }

    @Test
    public void testUnicodeVariable2() {
        Expression e = new ExpressionBuilder("log(3��+1)")
                .variable("��")
                .build()
                .setVariable("��", E);
        assertEquals(log(3 * E + 1), e.evaluate(), 0d);
    }

    @Test
    public void testUnicodeVariable3() {
        Function log = new Function("������", 1) {

            @Override
            public double apply(double... args) {
                return log(args[0]);
            }
        };

        Expression e = new ExpressionBuilder("������(��)")
                .variable("��")
                .function(log)
                .build()
                .setVariable("��", PI);
        assertEquals(log(PI), e.evaluate(), 0d);
    }

    @Test
    public void testUnicodeVariable4() {
        Function log = new Function("��_����", 1) {

            @Override
            public double apply(double... args) {
                return log(args[0]);
            }
        };

        Expression e = new ExpressionBuilder("3��_����(����6)")
                .variables("��", "��")
                .function(log)
                .build()
                .setVariable("��", PI)
                .setVariable("��", E);
        assertEquals(3 * log(PI * E * 6), e.evaluate(), 0d);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testImplicitMultiplicationOffNumber() {
        Expression e = new ExpressionBuilder("var_12")
                .variable("var_1")
                .implicitMultiplication(false)
                .build();
        e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testImplicitMultiplicationOffVariable() {
        Expression e = new ExpressionBuilder("var_1var_1")
                .variable("var_1")
                .implicitMultiplication(false)
                .build();
        e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testImplicitMultiplicationOffParentheses() {
        Expression e = new ExpressionBuilder("var_1(2)")
                .variable("var_1")
                .implicitMultiplication(false)
                .build();
        e.evaluate();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testImplicitMultiplicationOffFunction() {
        Expression e = new ExpressionBuilder("var_1log(2)")
                .variable("var_1")
                .implicitMultiplication(false)
                .build()
                .setVariable("var_1", 2);
        e.evaluate();
    }

    @Test
    public void testImplicitMultiplicationOnNumber() {
        Expression e = new ExpressionBuilder("var_12")
                .variable("var_1")
                .build()
                .setVariable("var_1", 2);
        assertEquals(4d, e.evaluate(), 0d);
    }

    @Test
    public void testImplicitMultiplicationOnVariable() {
        Expression e = new ExpressionBuilder("var_1var_1")
                .variable("var_1")
                .build()
                .setVariable("var_1", 2);
        assertEquals(4d, e.evaluate(), 0d);
    }

    @Test
    public void testImplicitMultiplicationOnParentheses() {
        Expression e = new ExpressionBuilder("var_1(2)")
                .variable("var_1")
                .build()
                .setVariable("var_1", 2);
        assertEquals(4d, e.evaluate(), 0d);
    }

    @Test
    public void testImplicitMultiplicationOnFunction() {
        Expression e = new ExpressionBuilder("var_1log(2)")
                .variable("var_1")
                .build()
                .setVariable("var_1", 2);
        assertEquals(2 * log(2), e.evaluate(), 0d);
    }

    // thanks go out to vandanagopal for reporting the issue
    // https://github.com/fasseg/exp4j/issues/23
    @Test
    public void testSecondArgumentNegative() {
        Function round = new Function("MULTIPLY", 2) {
            @Override
            public double apply(double... args) {
                return Math.round(args[0] * args[1]);
            }
        };
        double result = new ExpressionBuilder("MULTIPLY(2,-1)")
                .function(round)
                .build()
                .evaluate();
        assertEquals(-2d, result, 0d);
    }

    // Test for https://github.com/fasseg/exp4j/issues/65
    @Test
    public void testVariableWithDot() {
        double result = new ExpressionBuilder("2*SALARY.Basic")
                .variable("SALARY.Basic")
                .build()
                .setVariable("SALARY.Basic", 1.5d)
                .evaluate();
        assertEquals(3d, result, 0d);
    }

    @Test
    public void testTwoAdjacentOperators() {
        final Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

            @Override
            public double apply(double... args) {
                final int arg = (int) args[0];
                if ((double) arg != args[0]) {
                    throw new IllegalArgumentException("Operand for factorial has to be an integer");
                }
                if (arg < 0) {
                    throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
                }
                double result = 1;
                for (int i = 1; i <= arg; i++) {
                    result *= i;
                }
                return result;
            }
        };

        double result = new ExpressionBuilder("3!+2")
                .operator(factorial)
                .build()
                .evaluate();

        double expected = 8d;
        assertEquals(expected, result, 0d);
    }

    @Test
    public void testGetVariableNames1() {
        Expression e = new ExpressionBuilder("b*a-9.24c")
                .variables("b", "a", "c")
                .build();
        Set<String> variableNames = e.getVariableNames();
        assertTrue(variableNames.contains("a"));
        assertTrue(variableNames.contains("b"));
        assertTrue(variableNames.contains("c"));
    }

    @Test
    public void testGetVariableNames2() {
        Expression e = new ExpressionBuilder("log(bar)-FOO.s/9.24c")
                .variables("bar", "FOO.s", "c")
                .build();
        Set<String> variableNames = e.getVariableNames();
        assertTrue(variableNames.contains("bar"));
        assertTrue(variableNames.contains("FOO.s"));
        assertTrue(variableNames.contains("c"));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testSameVariableAndBuiltinFunctionName() {
        Expression e = new ExpressionBuilder("log10(log10)")
                .variables("log10")
                .build();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testSameVariableAndUserFunctionName() {
        Expression e = new ExpressionBuilder("2*tr+tr(2)")
                .variables("tr")
                .function(new Function("tr") {
                    @Override
                    public double apply(double... args) {
                        return 0;
                    }
                })
                .build();
    }

    @Test
    public void testSignum() {
        Expression e = new ExpressionBuilder("signum(1)")
                .build();
        assertEquals(1, e.evaluate(), 0d);

        e = new ExpressionBuilder("signum(-1)")
                .build();
        assertEquals(-1, e.evaluate(), 0d);

        e = new ExpressionBuilder("signum(--1)")
                .build();
        assertEquals(1, e.evaluate(), 0d);

        e = new ExpressionBuilder("signum(+-1)")
                .build();
        assertEquals(-1, e.evaluate(), 0d);

        e = new ExpressionBuilder("-+1")
                .build();
        assertEquals(-1, e.evaluate(), 0d);

        e = new ExpressionBuilder("signum(-+1)")
                .build();
        assertEquals(-1, e.evaluate(), 0d);
    }

    @Test
    public void testCustomPercent() {
        Function percentage = new Function("percentage", 2) {
            @Override
            public double apply(double... args) {
                double val = args[0];
                double percent = args[1];
                if (percent < 0) {
                    return val - val * Math.abs(percent) / 100d;
                } else {
                    return val - val * percent / 100d;
                }
            }
        };

        Expression e = new ExpressionBuilder("percentage(1000,-10)")
                .function(percentage)
                .build();
        assertEquals(0d, 900, e.evaluate());

        e = new ExpressionBuilder("percentage(1000,12)")
                .function(percentage)
                .build();
        assertEquals(0d, 1000d * 0.12d, e.evaluate());
    }
}