package openmods.calc;
import java.math.BigInteger;
import openmods.calc.CalcTestUtils.CalcCheck;
import openmods.calc.CalcTestUtils.SymbolStub;
import openmods.calc.types.bigint.BigIntCalculatorFactory;
import org.junit.Test;
public class BigIntCalculatorTest {
private final Calculator<BigInteger, ExprType> sut = BigIntCalculatorFactory.createDefault();
public CalcCheck<BigInteger> prefix(String value) {
return CalcCheck.create(sut, value, ExprType.PREFIX);
}
public CalcCheck<BigInteger> infix(String value) {
return CalcCheck.create(sut, value, ExprType.INFIX);
}
public CalcCheck<BigInteger> postfix(String value) {
return CalcCheck.create(sut, value, ExprType.POSTFIX);
}
public CalcCheck<BigInteger> compiled(IExecutable<BigInteger> expr) {
return CalcCheck.create(sut, expr);
}
public static BigInteger v(long value) {
return BigInteger.valueOf(value);
}
@Test
public void testBasicPrefix() {
prefix("(+ 1 2)").expectResult(v(3));
prefix("(* 2 3)").expectResult(v(6));
prefix("(- 1)").expectResult(v(-1));
prefix("(* (- 1) (+ 2 3))").expectResult(v(-5));
prefix("(/ 10 2)").expectResult(v(5));
prefix("(** 2 5)").expectResult(v(32));
prefix("(| 0b010 0b101)").expectResult(v(7));
prefix("(max 1)").expectResult(v(1));
prefix("(max 1 2)").expectResult(v(2));
prefix("(max 1 2 3)").expectResult(v(3));
}
@Test
public void testBasicPostfix() {
postfix("1 2 +").expectResult(v(3));
postfix("2 3 *").expectResult(v(6));
postfix("10 2 /").expectResult(v(5));
postfix("2 5 **").expectResult(v(32));
postfix("0b010 0b101 |").expectResult(v(7));
postfix("0b100 0b101 &").expectResult(v(4));
postfix("0b110 0b101 ^").expectResult(v(3));
postfix("0b100 2 <<").expectResult(v(16));
postfix("0b100 2 >>").expectResult(v(1));
postfix("45 4 %").expectResult(v(1));
}
@Test
public void testPostfixSymbols() {
sut.environment.setGlobalSymbol("a", v(5));
postfix("a").expectResult(v(5));
postfix("a$0").expectResult(v(5));
postfix("a$,1").expectResult(v(5));
postfix("a$0,1").expectResult(v(5));
postfix("@a").expectResult(v(5));
}
@Test(expected = RuntimeException.class)
public void testPostfixFunctionGet() {
postfix("@abs").execute();
}
@Test
public void testPostfixStackOperations() {
postfix("2 dup +").expectResult(v(4));
postfix("1 2 3 pop +").expectResult(v(3));
postfix("2 3 swap -").expectResult(v(1));
}
@Test
public void testPostfixDupWithArgs() {
postfix("0 1 2 dup$2").expectResults(v(0), v(1), v(2), v(1), v(2));
}
@Test
public void testPostfixDupWithReturns() {
postfix("1 2 dup$,4").expectResults(v(1), v(2), v(2), v(2), v(2));
}
@Test
public void testPostfixDupWithArgsAndReturns() {
postfix("1 2 3 4 dup$3,5").expectResults(v(1), v(2), v(3), v(4), v(2), v(3));
}
@Test
public void testPostfixPopWithArgs() {
postfix("1 2 3 4 pop$3").expectResults(v(1));
}
@Test
public void testVariadicPostfixFunctions() {
postfix("max$0").expectResult(v(0));
postfix("1 max$1").expectResult(v(1));
postfix("1 2 max$2").expectResult(v(2));
postfix("3 2 1 sum$3").expectResult(v(6));
postfix("3 2 1 avg$3").expectResult(v(2));
}
@Test
public void testBasicInfix() {
infix("1+2").expectResult(v(3));
infix("2*3").expectResult(v(6));
infix("10/2").expectResult(v(5));
infix("2**5").expectResult(v(32));
infix("2(5)").expectResult(v(10));
infix("0b010|0b101").expectResult(v(7));
infix("0b100&0b101").expectResult(v(4));
infix("0b110^0b101").expectResult(v(3));
infix("0b100<<2").expectResult(v(16));
infix("0b100>>2").expectResult(v(1));
infix("45 % 4").expectResult(v(1));
}
@Test
public void testBasicOrdering() {
infix("1 + 2 - 3").expectResult(v(0));
infix("1 + 2 * 3").expectResult(v(7));
infix("1 + (2 * 3)").expectResult(v(7));
infix("(1 + 2) * 3").expectResult(v(9));
infix("-(1 + 2) * 3").expectResult(v(-9));
infix("(1 + 2) * -3").expectResult(v(-9));
infix("--3").expectResult(v(3));
infix("-~2").expectResult(v(3));
infix("2 * 2 ** 2").expectResult(v(8));
infix("2 * (2 ** 2)").expectResult(v(8));
infix("(2 * 2) ** 2").expectResult(v(16));
}
@Test
public void testBasicInfixFunctions() {
infix("gcd(6, 8)").expectResult(v(2));
infix("abs(-2)").expectResult(v(2));
infix("5*abs(-2)").expectResult(v(10));
infix("1+abs(-2)").expectResult(v(3));
infix("min(2,3)").expectResult(v(2));
infix("max(2,3)").expectResult(v(3));
infix("2-max(2,3)").expectResult(v(-1));
}
@Test
public void testVariadicInfixFunctions() {
infix("max()").expectResult(v(0));
infix("max(1)").expectResult(v(1));
infix("max(1,2)").expectResult(v(2));
infix("max(3,2,1)").expectResult(v(3));
}
@Test(expected = Exception.class)
public void testTooManyParameters() {
infix("abs(0, 1)").execute();
}
@Test(expected = Exception.class)
public void testTooFewParameters() {
infix("gcd(0)").execute();
}
@Test
public void testParserSwitch() {
infix("2 + prefix(5)").expectResult(v(7));
infix("2 + prefix((+ 5 6))").expectResult(v(13));
prefix("(+ 2 (infix 5))").expectResult(v(7));
prefix("(+ 2 (infix 5 + 6))").expectResult(v(13));
}
@Test
public void testNestedParserSwitch() {
infix("infix(5 + 2)").expectResult(v(7));
infix("infix(infix(5 + 2))").expectResult(v(7));
prefix("(prefix (+ 2 5))").expectResult(v(7));
prefix("(prefix (prefix (+ 2 5)))").expectResult(v(7));
infix("prefix((infix 2 + 5))").expectResult(v(7));
prefix("(infix prefix((+ 2 5)))").expectResult(v(7));
}
@Test
public void testConstantEvaluatingBrackets() {
final SymbolStub<BigInteger> stub = new SymbolStub<BigInteger>()
.allowCalls()
.expectArgs(v(1), v(2))
.verifyArgCount()
.setReturns(v(5), v(6), v(7))
.verifyReturnCount();
sut.environment.setGlobalSymbol("dummy", stub);
final IExecutable<BigInteger> expr = sut.compilers.compile(ExprType.POSTFIX, "[1 2 dummy$2,3]");
stub.checkCallCount(1);
compiled(expr).expectResults(v(5), v(6), v(7));
stub.checkCallCount(1);
}
@Test
public void testNestedConstantEvaluatingBrackets() {
final SymbolStub<BigInteger> stub = new SymbolStub<BigInteger>()
.allowGets()
.setGetValue(v(5));
sut.environment.setGlobalSymbol("dummy", stub);
final IExecutable<BigInteger> expr = sut.compilers.compile(ExprType.POSTFIX, "[4 [@dummy 3 -] *]");
stub.checkGetCount(1);
compiled(expr).expectResults(v(8));
stub.checkGetCount(1);
}
@Test
public void testConstantEvaluatingSymbol() {
final SymbolStub<BigInteger> stub = new SymbolStub<BigInteger>()
.allowCalls()
.expectArgs(v(1), v(2))
.verifyArgCount()
.setReturns(v(5))
.verifyReturnCount();
sut.environment.setGlobalSymbol("dummy", stub);
final IExecutable<BigInteger> expr = sut.compilers.compile(ExprType.INFIX, "9 + const(dummy(1,2) + 3)");
stub.checkCallCount(1);
compiled(expr).expectResults(v(17));
stub.checkCallCount(1);
compiled(expr).expectResults(v(17));
stub.checkCallCount(1);
}
@Test
public void testSimpleLet() {
infix("let([x:2,y:3], x + y)").expectResult(v(5));
infix("let([x=2,y=3], x + y)").expectResult(v(5));
prefix("(let [(:x 2) (:y 3)] (+ x y))").expectResult(v(5));
prefix("(let [(=x 2) (=y 3)] (+ x y))").expectResult(v(5));
}
@Test
public void testLetWithExpression() {
infix("let([x:1 + 2,y:3 + 4], x + y)").expectResult(v(10));
prefix("(let [(:x (+ 1 2)) (:y (+ 3 4))] (+ x y))").expectResult(v(10));
}
@Test
public void testNestedLet() {
infix("let([x:2,y:3], let([w:x+y,z:x-y], w + z))").expectResult(v(4));
prefix("(let [(:x 2) (:y 3)] (let [(:w (+ x y)) (:z (- x y))] (+ w z)))").expectResult(v(4));
}
@Test
public void testFunctionLet() {
infix("let([x():2], x())").expectResult(v(2));
prefix("(let [(:(x) 2)] (x))").expectResult(v(2));
infix("let([x(a,b):a+b], x(1,2))").expectResult(v(3));
prefix("(let [(: (x a b) (+ a b))], (x 1 2))").expectResult(v(3));
}
@Test
public void testLetScoping() {
infix("let([x:2], let([y:x], let([x:3], y)))").expectResult(v(2));
infix("let([x:2], let([f(a):a+x], let([x:3], f(4))))").expectResult(v(6));
infix("let([x:5], let([x:2, y:x], x + y))").expectResult(v(7));
}
@Test
public void testFailSymbol() {
infix("fail('welp')").expectThrow(ExecutionErrorException.class, "welp");
infix("fail()").expectThrow(ExecutionErrorException.class, null);
}
}