package openmods.calc; import openmods.calc.CalcTestUtils.CalcCheck; import openmods.calc.CalcTestUtils.SymbolStub; import openmods.calc.types.fraction.FractionCalculatorFactory; import org.apache.commons.lang3.math.Fraction; import org.junit.Test; public class FractionCalculatorTest { private final Calculator<Fraction, ExprType> sut = FractionCalculatorFactory.createDefault(); public CalcCheck<Fraction> prefix(String value) { return CalcCheck.create(sut, value, ExprType.PREFIX); } public CalcCheck<Fraction> infix(String value) { return CalcCheck.create(sut, value, ExprType.INFIX); } public CalcCheck<Fraction> postfix(String value) { return CalcCheck.create(sut, value, ExprType.POSTFIX); } public CalcCheck<Fraction> compiled(IExecutable<Fraction> expr) { return CalcCheck.create(sut, expr); } public static Fraction f(int value) { return Fraction.getFraction(value, 1); } public static Fraction f(int numerator, int denominator) { return Fraction.getFraction(numerator, denominator); } @Test public void testBasicPrefix() { prefix("(+ 1 2)").expectResult(f(3)); prefix("(* 2 3)").expectResult(f(6)); prefix("(- 1)").expectResult(f(-1)); prefix("(* (- 1) (+ 2 3))").expectResult(f(-5)); prefix("(/ 10 2)").expectResult(f(5)); prefix("(max 1)").expectResult(f(1)); prefix("(max 1 2)").expectResult(f(2)); prefix("(max 1 2 3)").expectResult(f(3)); } @Test public void testBasicPostfix() { postfix("1 2 +").expectResult(f(3)); postfix("2 3 *").expectResult(f(6)); postfix("10 2 /").expectResult(f(5)); postfix("1 2 / 1 2 / +").expectResult(f(1)); postfix("1 2 / 1 +").expectResult(f(3, 2)); } @Test public void testPostfixSymbols() { sut.environment.setGlobalSymbol("a", f(5)); postfix("a").expectResult(f(5)); postfix("a$0").expectResult(f(5)); postfix("a$,1").expectResult(f(5)); postfix("a$0,1").expectResult(f(5)); postfix("@a").expectResult(f(5)); } @Test(expected = RuntimeException.class) public void testPostfixFunctionGet() { postfix("@abs").execute(); } @Test public void testPostfixStackOperations() { postfix("2 dup +").expectResult(f(4)); postfix("1 2 3 pop +").expectResult(f(3)); postfix("2 3 swap -").expectResult(f(1)); } @Test public void testPostfixDupWithArgs() { postfix("0 1 2 dup$2").expectResults(f(0), f(1), f(2), f(1), f(2)); } @Test public void testPostfixDupWithReturns() { postfix("1 2 dup$,4").expectResults(f(1), f(2), f(2), f(2), f(2)); } @Test public void testPostfixDupWithArgsAndReturns() { postfix("1 2 3 4 dup$3,5").expectResults(f(1), f(2), f(3), f(4), f(2), f(3)); } @Test public void testPostfixPopWithArgs() { postfix("1 2 3 4 pop$3").expectResults(f(1)); } @Test public void testVariadicPostfixFunctions() { postfix("max$0").expectResult(f(0)); postfix("1 max$1").expectResult(f(1)); postfix("1 2 max$2").expectResult(f(2)); postfix("3 2 1 sum$3").expectResult(f(6)); postfix("3 2 1 avg$3").expectResult(f(2)); } @Test public void testBasicInfix() { infix("1 + 2").expectResult(f(3)); infix("2 * 3").expectResult(f(6)); infix("10(2)").expectResult(f(20)); infix("10 / 2").expectResult(f(5)); infix("1/2 + 1/2").expectResult(f(1)); infix("0.5 + 1/2").expectResult(f(1)); infix("-3/2").expectResult(f(-3, 2)); } @Test public void testBasicOrdering() { infix("1 + 2 - 3").expectResult(f(0)); infix("1 + 2 * 3").expectResult(f(7)); infix("1 + (2 * 3)").expectResult(f(7)); infix("(1 + 2) * 3").expectResult(f(9)); infix("-(1 + 2) * 3").expectResult(f(-9)); infix("(1 + 2) * -3").expectResult(f(-9)); infix("--3").expectResult(f(3)); } @Test public void testBasicInfixFunctions() { infix("min(2,3)").expectResult(f(2)); infix("max(2,3)").expectResult(f(3)); infix("5max(2,3)").expectResult(f(15)); infix("sqrt(9/4)").expectResult(f(3, 2)); infix("2-max(2,3)").expectResult(f(-1)); infix("int(7/3)").expectResult(f(2)); infix("frac(7/3)").expectResult(f(1, 3)); infix("numerator(7/3)").expectResult(f(7)); infix("denominator(7/3)").expectResult(f(3)); } @Test public void testVariadicInfixFunctions() { infix("max()").expectResult(f(0)); infix("max(1)").expectResult(f(1)); infix("max(1,2)").expectResult(f(2)); infix("max(3,2,1)").expectResult(f(3)); } @Test(expected = Exception.class) public void testTooManyParameters() { infix("abs(0, 1)").execute(); } @Test public void testParserSwitch() { infix("2 + prefix(5)").expectResult(f(7)); infix("2 + prefix((+ 5 6))").expectResult(f(13)); prefix("(+ 2 (infix 5))").expectResult(f(7)); prefix("(+ 2 (infix 5 + 6))").expectResult(f(13)); } @Test public void testNestedParserSwitch() { infix("infix(5 + 2)").expectResult(f(7)); infix("infix(infix(5 + 2))").expectResult(f(7)); prefix("(prefix (+ 2 5))").expectResult(f(7)); prefix("(prefix (prefix (+ 2 5)))").expectResult(f(7)); infix("prefix((infix 2 + 5))").expectResult(f(7)); prefix("(infix prefix((+ 2 5)))").expectResult(f(7)); } @Test public void testConstantEvaluatingBrackets() { final SymbolStub<Fraction> stub = new SymbolStub<Fraction>() .allowCalls() .expectArgs(f(1), f(2)) .verifyArgCount() .setReturns(f(5), f(6), f(7)) .verifyReturnCount(); sut.environment.setGlobalSymbol("dummy", stub); final IExecutable<Fraction> expr = sut.compilers.compile(ExprType.POSTFIX, "[1 2 dummy$2,3]"); stub.checkCallCount(1); compiled(expr).expectResults(f(5), f(6), f(7)); stub.checkCallCount(1); } @Test public void testNestedConstantEvaluatingBrackets() { final SymbolStub<Fraction> stub = new SymbolStub<Fraction>() .allowGets() .setGetValue(f(5)); sut.environment.setGlobalSymbol("dummy", stub); final IExecutable<Fraction> expr = sut.compilers.compile(ExprType.POSTFIX, "[4 [@dummy 3 -] *]"); stub.checkGetCount(1); compiled(expr).expectResults(f(8)); stub.checkGetCount(1); } @Test public void testConstantEvaluatingSymbol() { final SymbolStub<Fraction> stub = new SymbolStub<Fraction>() .allowCalls() .expectArgs(f(1), f(2)) .verifyArgCount() .setReturns(f(5)) .verifyReturnCount(); sut.environment.setGlobalSymbol("dummy", stub); final IExecutable<Fraction> expr = sut.compilers.compile(ExprType.INFIX, "9 + const(dummy(1,2) + 3)"); stub.checkCallCount(1); compiled(expr).expectResults(f(17)); stub.checkCallCount(1); compiled(expr).expectResults(f(17)); stub.checkCallCount(1); } @Test public void testSimpleLet() { infix("let([x:2,y:3], x + y)").expectResult(f(5)); infix("let([x=2,y=3], x + y)").expectResult(f(5)); prefix("(let [(:x 2) (:y 3)] (+ x y))").expectResult(f(5)); prefix("(let [(=x 2) (=y 3)] (+ x y))").expectResult(f(5)); } @Test public void testLetWithExpression() { infix("let([x:1 + 2,y:3 + 4], x + y)").expectResult(f(10)); prefix("(let [(:x (+ 1 2)) (:y (+ 3 4))] (+ x y))").expectResult(f(10)); } @Test public void testNestedLet() { infix("let([x:2,y:3], let([w:x+y,z:x-y], w + z))").expectResult(f(4)); prefix("(let [(:x 2) (:y 3)] (let [(:w (+ x y)) (:z (- x y))] (+ w z)))").expectResult(f(4)); } @Test public void testFunctionLet() { infix("let([x():2], x())").expectResult(f(2)); prefix("(let [(:(x) 2)] (x))").expectResult(f(2)); infix("let([x(a,b):a+b], x(1,2))").expectResult(f(3)); prefix("(let [(: (x a b) (+ a b))], (x 1 2))").expectResult(f(3)); } @Test public void testLetScoping() { infix("let([x:2], let([y:x], let([x:3], y)))").expectResult(f(2)); infix("let([x:2], let([f(a):a+x], let([x:3], f(4))))").expectResult(f(6)); infix("let([x:5], let([x:2, y:x], x + y))").expectResult(f(7)); } @Test public void testFailSymbol() { infix("fail('welp')").expectThrow(ExecutionErrorException.class, "welp"); infix("fail()").expectThrow(ExecutionErrorException.class, null); } }