package openmods.calc; import openmods.calc.CalcTestUtils.CalcCheck; import openmods.calc.CalcTestUtils.SymbolStub; import openmods.calc.types.fp.DoubleCalculatorFactory; import org.junit.Test; public class DoubleCalculatorTest { private final Calculator<Double, ExprType> sut = DoubleCalculatorFactory.createDefault(); public CalcCheck<Double> prefix(String value) { return CalcCheck.create(sut, value, ExprType.PREFIX); } public CalcCheck<Double> infix(String value) { return CalcCheck.create(sut, value, ExprType.INFIX); } public CalcCheck<Double> postfix(String value) { return CalcCheck.create(sut, value, ExprType.POSTFIX); } public CalcCheck<Double> compiled(IExecutable<Double> expr) { return CalcCheck.create(sut, expr); } @Test public void testBasicPrefix() { prefix("(+ 1 2)").expectResult(3.0); prefix("(* 2 3)").expectResult(6.0); prefix("(- 1)").expectResult(-1.0); prefix("(* (- 1) (+ 2 3))").expectResult(-5.0); prefix("(/ 10 2)").expectResult(5.0); prefix("(^ 2 5)").expectResult(32.0); prefix("(max 1)").expectResult(1.0); prefix("(max 1 2)").expectResult(2.0); prefix("(max 1 2 3)").expectResult(3.0); } @Test public void testBasicPostfix() { postfix("1 2 +").expectResult(3.0); postfix("0.5 0.5 +").expectResult(1.0); postfix("0.25 0.25 +").expectResult(0.5); postfix("2 3 *").expectResult(6.0); postfix("10 2 /").expectResult(5.0); postfix("2 5 ^").expectResult(32.0); } @Test public void testPostfixSymbols() { sut.environment.setGlobalSymbol("a", 5.0); postfix("a").expectResult(5.0); postfix("a$0").expectResult(5.0); postfix("a$,1").expectResult(5.0); postfix("a$0,1").expectResult(5.0); postfix("@a").expectResult(5.0); } @Test(expected = RuntimeException.class) public void testPostfixFunctionGet() { postfix("@abs").execute(); } @Test public void testPostfixStackOperations() { postfix("2 dup +").expectResult(4.0); postfix("1 2 3 pop +").expectResult(3.0); postfix("2 3 swap -").expectResult(1.0); } @Test public void testPostfixDupWithArgs() { postfix("0 1 2 dup$2").expectResults(0.0, 1.0, 2.0, 1.0, 2.0); } @Test public void testPostfixDupWithReturns() { postfix("1 2 dup$,4").expectResults(1.0, 2.0, 2.0, 2.0, 2.0); } @Test public void testPostfixDupWithArgsAndReturns() { postfix("1 2 3 4 dup$3,5").expectResults(1.0, 2.0, 3.0, 4.0, 2.0, 3.0); } @Test public void testPostfixPopWithArgs() { postfix("1 2 3 4 pop$3").expectResults(1.0); } @Test public void testVariadicPostfixFunctions() { postfix("max$0").expectResult(0.0); postfix("1 max$1").expectResult(1.0); postfix("1 2 max$2").expectResult(2.0); postfix("3 2 1 sum$3").expectResult(6.0); postfix("3 2 1 avg$3").expectResult(2.0); postfix("2 4 INF 1 max$4").expectResult(Double.POSITIVE_INFINITY); } @Test public void testBasicInfix() { infix("1 + 2").expectResult(3.0); infix("2 * 3").expectResult(6.0); infix("10 / 2").expectResult(5.0); infix("2 ^ 5").expectResult(32.0); infix("-PI").expectResult(-Math.PI); infix("2*E").expectResult(2 * Math.E); infix("2*-E").expectResult(2 * -Math.E); infix("2E").expectResult(2 * Math.E); infix("2(E)").expectResult(2 * Math.E); infix("-2*-3*10^3").expectResult(6e3); infix("-2*-3*10^+3").expectResult(6e3); infix("-2*-3*10^-3").expectResult(6e-3); infix("2*10^2+3*10^3").expectResult(3200.0); infix("2*10^2*3*10^3").expectResult(6e5); } @Test public void testCallOfGetterSymbol() { infix("PI()").expectResult(Math.PI); } @Test public void testBasicOrdering() { infix("1 + 2 - 3").expectResult(0.0); infix("1 + 2 * 3").expectResult(7.0); infix("1 + (2 * 3)").expectResult(7.0); infix("(1 + 2) * 3").expectResult(9.0); infix("-(1 + 2) * 3").expectResult(-9.0); infix("(1 + 2) * -3").expectResult(-9.0); infix("--3").expectResult(3.0); infix("2 * 2 ^ 2").expectResult(8.0); infix("2 * (2 ^ 2)").expectResult(8.0); infix("(2 * 2) ^ 2").expectResult(16.0); } @Test public void testBasicInfixFunctions() { infix("sin(0)").expectResult(0.0); infix("cos(0)").expectResult(1.0); infix("1+cos(0)").expectResult(2.0); infix("5cos(0)").expectResult(5.0); infix("min(2,3)").expectResult(2.0); infix("max(2,3)").expectResult(3.0); infix("2-max(2,3)").expectResult(-1.0); } @Test public void testVariadicInfixFunctions() { infix("max()").expectResult(0.0); infix("max(1)").expectResult(1.0); infix("max(1,2)").expectResult(2.0); infix("max(3,2,1)").expectResult(3.0); infix("max(2,4,INF,1)").expectResult(Double.POSITIVE_INFINITY); } @Test(expected = Exception.class) public void testTooManyParameters() { infix("sin(0, 1)").execute(); } @Test(expected = Exception.class) public void testTooFewParameters() { infix("atan2(0)").execute(); } @Test public void testParserSwitch() { infix("2 + prefix(5)").expectResult(7.0); infix("2 + prefix((+ 5 6))").expectResult(13.0); prefix("(+ 2 (infix 5))").expectResult(7.0); prefix("(+ 2 (infix 5 + 6))").expectResult(13.0); } @Test public void testNestedParserSwitch() { infix("infix(5 + 2)").expectResult(7.0); infix("infix(infix(5 + 2))").expectResult(7.0); prefix("(prefix (+ 2 5))").expectResult(7.0); prefix("(prefix (prefix (+ 2 5)))").expectResult(7.0); infix("prefix((infix 2 + 5))").expectResult(7.0); prefix("(infix prefix((+ 2 5)))").expectResult(7.0); } @Test public void testConstantEvaluatingBrackets() { final SymbolStub<Double> stub = new SymbolStub<Double>() .allowCalls() .expectArgs(1.0, 2.0) .verifyArgCount() .setReturns(5.0, 6.0, 7.0) .verifyReturnCount(); sut.environment.setGlobalSymbol("dummy", stub); final IExecutable<Double> expr = sut.compilers.compile(ExprType.POSTFIX, "[1 2 dummy$2,3]"); stub.checkCallCount(1); compiled(expr).expectResults(5.0, 6.0, 7.0); stub.checkCallCount(1); } @Test public void testNestedConstantEvaluatingBrackets() { final SymbolStub<Double> stub = new SymbolStub<Double>() .allowGets() .setGetValue(5.0); sut.environment.setGlobalSymbol("dummy", stub); final IExecutable<Double> expr = sut.compilers.compile(ExprType.POSTFIX, "[4 [@dummy 3 -] *]"); stub.checkGetCount(1); compiled(expr).expectResults(8.0); stub.checkGetCount(1); } @Test public void testConstantEvaluatingSymbol() { final SymbolStub<Double> stub = new SymbolStub<Double>() .allowCalls() .expectArgs(1.0, 2.0) .verifyArgCount() .setReturns(5.0) .verifyReturnCount(); sut.environment.setGlobalSymbol("dummy", stub); final IExecutable<Double> expr = sut.compilers.compile(ExprType.INFIX, "9 + const(dummy(1,2) + 3)"); stub.checkCallCount(1); compiled(expr).expectResults(17.0); stub.checkCallCount(1); compiled(expr).expectResults(17.0); stub.checkCallCount(1); } @Test public void testSimpleLet() { infix("let([x:2,y:3], x + y)").expectResult(5.0); infix("let([x=2,y=3], x + y)").expectResult(5.0); prefix("(let [(:x 2) (:y 3)] (+ x y))").expectResult(5.0); prefix("(let [(=x 2) (=y 3)] (+ x y))").expectResult(5.0); } @Test public void testLetWithExpression() { infix("let([x:1 + 2,y:3 + 4], x + y)").expectResult(10.0); prefix("(let [(:x (+ 1 2)) (:y (+ 3 4))] (+ x y))").expectResult(10.0); } @Test public void testNestedLet() { infix("let([x:2,y:3], let([w:x+y,z:x-y], w + z))").expectResult(4.0); prefix("(let [(:x 2) (:y 3)] (let [(:w (+ x y)) (:z (- x y))] (+ w z)))").expectResult(4.0); } @Test public void testFunctionLet() { infix("let([x():2], x())").expectResult(2.0); prefix("(let [(:(x) 2)] (x))").expectResult(2.0); infix("let([x(a,b):a+b], x(1,2))").expectResult(3.0); prefix("(let [(: (x a b) (+ a b))], (x 1 2))").expectResult(3.0); } @Test public void testLetScoping() { infix("let([x:2], let([y:x], let([x:3], y)))").expectResult(2.0); infix("let([x:2], let([f(a):a+x], let([x:3], f(4))))").expectResult(6.0); infix("let([x:5], let([x:2, y:x], x + y))").expectResult(7.0); } @Test public void testFailSymbol() { infix("fail('welp')").expectThrow(ExecutionErrorException.class, "welp"); infix("fail()").expectThrow(ExecutionErrorException.class, null); } }