diff --git a/java/expression/High.java b/java/expression/High.java new file mode 100644 index 0000000..da9a38a --- /dev/null +++ b/java/expression/High.java @@ -0,0 +1,51 @@ +package expression; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; + +/** + * @author Doschennikov Nikita (me@fymio.us) + */ +public class High extends AbstractExpression { + private final AbstractExpression operand; + + public High(AbstractExpression operand) { + this.operand = operand; + } + + private static int highestBit(int n) { + if (n == 0) return 0; + n |= (n >> 1); + n |= (n >> 2); + n |= (n >> 4); + n |= (n >> 8); + n |= (n >> 16); + return n - (n >>> 1); + } + + @Override public int evaluate(int x) { return highestBit(operand.evaluate(x)); } + @Override public int evaluate(int x, int y, int z) { return highestBit(operand.evaluate(x,y,z)); } + @Override public int evaluate(List vars) { return highestBit(operand.evaluate(vars)); } + @Override public BigInteger evaluateBi(List vars) { throw new UnsupportedOperationException(); } + @Override public BigDecimal evaluateBd(List vars) { throw new UnsupportedOperationException(); } + + @Override public String toString() { return "high(" + operand + ")"; } + + @Override + public String toMiniString() { + if (operand instanceof AbstractBinaryOperation) { + return "high(" + operand.toMiniString() + ")"; + } + return "high " + operand.toMiniString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof High)) return false; + return operand.equals(((High) obj).operand); + } + + @Override public int hashCode() { return operand.hashCode() ^ 0x48494748; } +} \ No newline at end of file diff --git a/java/expression/ListExpression.java b/java/expression/ListExpression.java new file mode 100644 index 0000000..5e18ea4 --- /dev/null +++ b/java/expression/ListExpression.java @@ -0,0 +1,337 @@ +package expression; + +import base.Asserts; +import base.ExtendedRandom; +import base.Pair; +import base.TestCounter; +import expression.common.ExpressionKind; +import expression.common.Type; +import java.util.List; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings("ClassReferencesSubclass") +@FunctionalInterface +public interface ListExpression extends ToMiniString { + int evaluate(List variables); + + // Tests follow. You may temporarily remove everything til the end. + + Add EXAMPLE = new Add( + new Subtract(new Variable(0), new Const(1)), + new Multiply(new Variable(1), new Const(10)) + ); + + Type TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class); + ExpressionKind KIND = new ExpressionKind<>( + TYPE, + ListExpression.class, + (r, c) -> + IntStream.range(0, c) + .mapToObj(name -> + Pair.of( + "$" + name, + new Variable(name) + ) + ) + .toList(), + (expr, variables, values) -> expr.evaluate(values) + ); + + private static Const c(final Integer c) { + return TYPE.constant(c); + } + + @SuppressWarnings({ "PointlessArithmeticExpression", "Convert2MethodRef" }) + static ExpressionTester tester(final TestCounter counter) { + Asserts.assertEquals( + "Example toString()", + "(($0 - 1) + ($1 * 10))", + EXAMPLE.toString() + ); + Asserts.assertEquals( + EXAMPLE + " at (2, 3)", + 31, + EXAMPLE.evaluate(List.of(2, 3)) + ); + + final Variable vx = new Variable(0); + return new ExpressionTester<>( + counter, + KIND, + c -> vars -> c, + (op, a, b) -> vars -> op.apply(a.evaluate(vars), b.evaluate(vars)), + (a, b) -> a + b, + (a, b) -> a - b, + (a, b) -> a * b, + (a, b) -> a / b + ) + .basic("10", "10", vars -> 10, c(10)) + .basic("$x", "$x", ListExpression::x, vx) + .basic("($x + 2)", "$x + 2", vars -> x(vars) + 2, new Add(vx, c(2))) + .basic( + "(2 - $x)", + "2 - $x", + vars -> 2 - x(vars), + new Subtract(c(2), vx) + ) + .basic( + "(3 * $x)", + "3 * $x", + vars -> 3 * x(vars), + new Multiply(c(3), vx) + ) + .basic( + "($x + $x)", + "$x + $x", + vars -> x(vars) + x(vars), + new Add(vx, vx) + ) + .basic( + "($x / -2)", + "$x / -2", + vars -> -x(vars) / 2, + new Divide(vx, c(-2)) + ) + .basic("(2 + $x)", "2 + $x", vars -> 2 + x(vars), new Add(c(2), vx)) + .basic( + "((1 + 2) + 3)", + "1 + 2 + 3", + vars -> 6, + new Add(new Add(c(1), c(2)), c(3)) + ) + .basic( + "(1 + (2 + 3))", + "1 + 2 + 3", + vars -> 6, + new Add(c(1), new Add(c(2), c(3))) + ) + .basic( + "((1 - 2) - 3)", + "1 - 2 - 3", + vars -> -4, + new Subtract(new Subtract(c(1), c(2)), c(3)) + ) + .basic( + "(1 - (2 - 3))", + "1 - (2 - 3)", + vars -> 2, + new Subtract(c(1), new Subtract(c(2), c(3))) + ) + .basic( + "((1 * 2) * 3)", + "1 * 2 * 3", + vars -> 6, + new Multiply(new Multiply(c(1), c(2)), c(3)) + ) + .basic( + "(1 * (2 * 3))", + "1 * 2 * 3", + vars -> 6, + new Multiply(c(1), new Multiply(c(2), c(3))) + ) + .basic( + "((10 / 2) / 3)", + "10 / 2 / 3", + vars -> 10 / 2 / 3, + new Divide(new Divide(c(10), c(2)), c(3)) + ) + .basic( + "(10 / (3 / 2))", + "10 / (3 / 2)", + vars -> 10 / (3 / 2), + new Divide(c(10), new Divide(c(3), c(2))) + ) + .basic( + "(10 * (3 / 2))", + "10 * (3 / 2)", + vars -> 10 * (3 / 2), + new Multiply(c(10), new Divide(c(3), c(2))) + ) + .basic( + "(10 + (3 - 2))", + "10 + 3 - 2", + vars -> 10 + (3 - 2), + new Add(c(10), new Subtract(c(3), c(2))) + ) + .basic( + "(($x * $x) + (($x - 1) / 10))", + "$x * $x + ($x - 1) / 10", + vars -> x(vars) * x(vars) + (x(vars) - 1) / 10, + new Add( + new Multiply(vx, vx), + new Divide(new Subtract(vx, c(1)), c(10)) + ) + ) + .basic( + "($x * -1000000000)", + "$x * -1000000000", + vars -> x(vars) * -1_000_000_000, + new Multiply(vx, c(-1_000_000_000)) + ) + .basic( + "(10 / $x)", + "10 / $x", + vars -> 10 / x(vars), + new Divide(c(10), vx) + ) + .basic( + "($x / $x)", + "$x / $x", + vars -> x(vars) / x(vars), + new Divide(vx, vx) + ) + .advanced("(2 + 1)", "2 + 1", vars -> 2 + 1, new Add(c(2), c(1))) + .advanced( + "($x - 1)", + "$x - 1", + vars -> x(vars) - 1, + new Subtract(vx, c(1)) + ) + .advanced( + "(1 * 2)", + "1 * 2", + vars -> 1 * 2, + new Multiply(c(1), c(2)) + ) + .advanced( + "($x / 1)", + "$x / 1", + vars -> x(vars) / 1, + new Divide(vx, c(1)) + ) + .advanced( + "(1 + (2 + 1))", + "1 + 2 + 1", + vars -> 1 + 2 + 1, + new Add(c(1), new Add(c(2), c(1))) + ) + .advanced( + "($x - ($x - 1))", + "$x - ($x - 1)", + vars -> x(vars) - (x(vars) - 1), + new Subtract(vx, new Subtract(vx, c(1))) + ) + .advanced( + "(2 * ($x / 1))", + "2 * ($x / 1)", + vars -> 2 * (x(vars) / 1), + new Multiply(c(2), new Divide(vx, c(1))) + ) + .advanced( + "(2 / ($x - 1))", + "2 / ($x - 1)", + vars -> 2 / (x(vars) - 1), + new Divide(c(2), new Subtract(vx, c(1))) + ) + .advanced( + "((1 * 2) + $x)", + "1 * 2 + $x", + vars -> 1 * 2 + x(vars), + new Add(new Multiply(c(1), c(2)), vx) + ) + .advanced( + "(($x - 1) - 2)", + "$x - 1 - 2", + vars -> x(vars) - 1 - 2, + new Subtract(new Subtract(vx, c(1)), c(2)) + ) + .advanced( + "(($x / 1) * 2)", + "$x / 1 * 2", + vars -> (x(vars) / 1) * 2, + new Multiply(new Divide(vx, c(1)), c(2)) + ) + .advanced( + "((2 + 1) / 1)", + "(2 + 1) / 1", + vars -> (2 + 1) / 1, + new Divide(new Add(c(2), c(1)), c(1)) + ) + .advanced( + "(1 + (1 + (2 + 1)))", + "1 + 1 + 2 + 1", + vars -> 1 + 1 + 2 + 1, + new Add(c(1), new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "($x - ((1 * 2) + $x))", + "$x - (1 * 2 + $x)", + vars -> x(vars) - (1 * 2 + x(vars)), + new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx)) + ) + .advanced( + "($x * (2 / ($x - 1)))", + "$x * (2 / ($x - 1))", + vars -> x(vars) * (2 / (x(vars) - 1)), + new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1)))) + ) + .advanced( + "($x / (1 + (2 + 1)))", + "$x / (1 + 2 + 1)", + vars -> x(vars) / (1 + 2 + 1), + new Divide(vx, new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "((1 * 2) + (2 + 1))", + "1 * 2 + 2 + 1", + vars -> 1 * 2 + 2 + 1, + new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1))) + ) + .advanced( + "((2 + 1) - (2 + 1))", + "2 + 1 - (2 + 1)", + vars -> 2 + 1 - (2 + 1), + new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1))) + ) + .advanced( + "(($x - 1) * ($x / 1))", + "($x - 1) * ($x / 1)", + vars -> (x(vars) - 1) * (x(vars) / 1), + new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1))) + ) + .advanced( + "(($x - 1) / (1 * 2))", + "($x - 1) / (1 * 2)", + vars -> (x(vars) - 1) / (1 * 2), + new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2))) + ) + .advanced( + "((($x - 1) - 2) + $x)", + "$x - 1 - 2 + $x", + vars -> x(vars) - 1 - 2 + x(vars), + new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx) + ) + .advanced( + "(((1 * 2) + $x) - 1)", + "1 * 2 + $x - 1", + vars -> 1 * 2 + x(vars) - 1, + new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1)) + ) + .advanced( + "(((2 + 1) / 1) * $x)", + "(2 + 1) / 1 * $x", + vars -> ((2 + 1) / 1) * x(vars), + new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx) + ) + .advanced( + "((2 / ($x - 1)) / 2)", + "2 / ($x - 1) / 2", + vars -> 2 / (x(vars) - 1) / 2, + new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2)) + ); + } + + private static Integer x(final List vars) { + return vars.get(0); + } + + static void main(final String... args) { + TripleExpression.SELECTOR.variant( + "List", + ExpressionTest.v(ListExpression::tester) + ).main(args); + } +} diff --git a/java/expression/Log.java b/java/expression/Log.java new file mode 100644 index 0000000..afc219b --- /dev/null +++ b/java/expression/Log.java @@ -0,0 +1,46 @@ +package expression; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Doschennikov Nikita (me@fymio.us) + */ +public class Log extends AbstractBinaryOperation { + public Log(AbstractExpression l, AbstractExpression r) { super(l, r); } + + @Override protected String getOperator() { return "//"; } + @Override protected int getPriority() { return 3; } + @Override protected boolean isRightAssoc() { return true; } + + @Override + protected int applyInt(int a, int b) { + if (a <= 0) throw new ArithmeticException("logarithm of non-positive number"); + if (b <= 1) throw new ArithmeticException("logarithm base must be > 1"); + int result = 0; + int power = 1; + while (power <= a / b) { + power *= b; + result++; + } + return result; + } + + @Override + protected BigInteger applyBi(BigInteger a, BigInteger b) { + if (a.signum() <= 0) throw new ArithmeticException("logarithm of non-positive number"); + if (b.compareTo(BigInteger.TWO) < 0) throw new ArithmeticException("logarithm base must be > 1"); + int result = 0; + BigInteger power = BigInteger.ONE; + while (power.multiply(b).compareTo(a) <= 0) { + power = power.multiply(b); + result++; + } + return BigInteger.valueOf(result); + } + + @Override + protected BigDecimal applyBd(BigDecimal a, BigDecimal b) { + throw new UnsupportedOperationException("log not supported for BigDecimal"); + } +} \ No newline at end of file diff --git a/java/expression/Log2.java b/java/expression/Log2.java new file mode 100644 index 0000000..f972e47 --- /dev/null +++ b/java/expression/Log2.java @@ -0,0 +1,52 @@ +package expression; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; + +/** + * @author Doschennikov Nikita (me@fymio.us) + */ +public class Log2 extends AbstractExpression { + private final AbstractExpression operand; + + public Log2(AbstractExpression operand) { + this.operand = operand; + } + + private static int log2(int n) { + if (n <= 0) throw new ArithmeticException("log₂ of non-positive number: " + n); + int result = 0; + int v = n; + while (v > 1) { + v >>= 1; + result++; + } + return result; + } + + @Override public int evaluate(int x) { return log2(operand.evaluate(x)); } + @Override public int evaluate(int x, int y, int z) { return log2(operand.evaluate(x,y,z)); } + @Override public int evaluate(List vars) { return log2(operand.evaluate(vars)); } + @Override public BigInteger evaluateBi(List vars) { throw new UnsupportedOperationException(); } + @Override public BigDecimal evaluateBd(List vars) { throw new UnsupportedOperationException(); } + + @Override public String toString() { return "log₂(" + operand + ")"; } + + @Override + public String toMiniString() { + if (operand instanceof AbstractBinaryOperation) { + return "log₂(" + operand.toMiniString() + ")"; + } + return "log₂ " + operand.toMiniString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Log2)) return false; + return operand.equals(((Log2) obj).operand); + } + + @Override public int hashCode() { return operand.hashCode() ^ 0x4C4F4732; } +} \ No newline at end of file diff --git a/java/expression/Low.java b/java/expression/Low.java new file mode 100644 index 0000000..4b18c02 --- /dev/null +++ b/java/expression/Low.java @@ -0,0 +1,41 @@ +package expression; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; + +/** + * @author Doschennikov Nikita (me@fymio.us) + */ +public class Low extends AbstractExpression { + private final AbstractExpression operand; + + public Low(AbstractExpression operand) { + this.operand = operand; + } + + @Override public int evaluate(int x) { int n = operand.evaluate(x); return n & -n; } + @Override public int evaluate(int x, int y, int z) { int n = operand.evaluate(x,y,z); return n & -n; } + @Override public int evaluate(List vars) { int n = operand.evaluate(vars); return n & -n; } + @Override public BigInteger evaluateBi(List vars) { throw new UnsupportedOperationException(); } + @Override public BigDecimal evaluateBd(List vars) { throw new UnsupportedOperationException(); } + + @Override public String toString() { return "low(" + operand + ")"; } + + @Override + public String toMiniString() { + if (operand instanceof AbstractBinaryOperation) { + return "low(" + operand.toMiniString() + ")"; + } + return "low " + operand.toMiniString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Low)) return false; + return operand.equals(((Low) obj).operand); + } + + @Override public int hashCode() { return operand.hashCode() ^ 0x4C4F5700; } +} \ No newline at end of file