Upload files to "java/expression"
This commit is contained in:
10
java/expression/DivisionByZeroException.java
Normal file
10
java/expression/DivisionByZeroException.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Doschennikov Nikita (me@fymio.us)
|
||||||
|
*/
|
||||||
|
public class DivisionByZeroException extends ArithmeticException {
|
||||||
|
public DivisionByZeroException() {
|
||||||
|
super("division by zero");
|
||||||
|
}
|
||||||
|
}
|
||||||
285
java/expression/Expression.java
Normal file
285
java/expression/Expression.java
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-argument arithmetic expression over integers.
|
||||||
|
*
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
@SuppressWarnings("ClassReferencesSubclass")
|
||||||
|
public interface Expression extends ToMiniString {
|
||||||
|
int evaluate(int x);
|
||||||
|
|
||||||
|
// Tests follow. You may temporarily remove everything til the end.
|
||||||
|
|
||||||
|
Subtract EXAMPLE = new Subtract(
|
||||||
|
new Multiply(new Const(2), new Variable("x")),
|
||||||
|
new Const(3)
|
||||||
|
);
|
||||||
|
|
||||||
|
Type<Integer> TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class);
|
||||||
|
ExpressionKind<Expression, Integer> KIND = new ExpressionKind<>(
|
||||||
|
TYPE,
|
||||||
|
Expression.class,
|
||||||
|
List.of(Pair.of("x", new Variable("x"))),
|
||||||
|
(expr, variables, values) -> expr.evaluate(values.get(0))
|
||||||
|
);
|
||||||
|
|
||||||
|
private static Const c(final int c) {
|
||||||
|
return new Const(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "PointlessArithmeticExpression", "Convert2MethodRef" })
|
||||||
|
static ExpressionTester<?, ?> tester(final TestCounter counter) {
|
||||||
|
Asserts.assertEquals(
|
||||||
|
"Example toString()",
|
||||||
|
"((2 * x) - 3)",
|
||||||
|
EXAMPLE.toString()
|
||||||
|
);
|
||||||
|
Asserts.assertEquals("Example at 5", 7, EXAMPLE.evaluate(5));
|
||||||
|
Asserts.assertTrue(
|
||||||
|
"Example equals 1",
|
||||||
|
new Multiply(new Const(2), new Variable("x")).equals(
|
||||||
|
new Multiply(new Const(2), new Variable("x"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Asserts.assertTrue(
|
||||||
|
"Example equals 2",
|
||||||
|
!new Multiply(new Const(2), new Variable("x")).equals(
|
||||||
|
new Multiply(new Variable("x"), new Const(2))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
final Variable vx = new Variable("x");
|
||||||
|
final Const c1 = c(1);
|
||||||
|
final Const c2 = c(2);
|
||||||
|
|
||||||
|
return new ExpressionTester<>(
|
||||||
|
counter,
|
||||||
|
KIND,
|
||||||
|
c -> x -> c,
|
||||||
|
(op, a, b) -> x -> op.apply(a.evaluate(x), b.evaluate(x)),
|
||||||
|
(a, b) -> a + b,
|
||||||
|
(a, b) -> a - b,
|
||||||
|
(a, b) -> a * b,
|
||||||
|
(a, b) -> a / b
|
||||||
|
)
|
||||||
|
.basic("10", "10", x -> 10, c(10))
|
||||||
|
.basic("x", "x", x -> x, vx)
|
||||||
|
.basic("(x + 2)", "x + 2", x -> x + 2, new Add(vx, c(2)))
|
||||||
|
.basic("(2 - x)", "2 - x", x -> 2 - x, new Subtract(c(2), vx))
|
||||||
|
.basic("(3 * x)", "3 * x", x -> 3 * x, new Multiply(c(3), vx))
|
||||||
|
.basic("(x + x)", "x + x", x -> x + x, new Add(vx, vx))
|
||||||
|
.basic("(x / -2)", "x / -2", x -> -x / 2, new Divide(vx, c(-2)))
|
||||||
|
.basic("(2 + x)", "2 + x", x -> 2 + x, new Add(c(2), vx))
|
||||||
|
.basic(
|
||||||
|
"((1 + 2) + 3)",
|
||||||
|
"1 + 2 + 3",
|
||||||
|
x -> 6,
|
||||||
|
new Add(new Add(c(1), c(2)), c(3))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(1 + (2 + 3))",
|
||||||
|
"1 + 2 + 3",
|
||||||
|
x -> 6,
|
||||||
|
new Add(c(1), new Add(c(2), c(3)))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"((1 - 2) - 3)",
|
||||||
|
"1 - 2 - 3",
|
||||||
|
x -> -4,
|
||||||
|
new Subtract(new Subtract(c(1), c(2)), c(3))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(1 - (2 - 3))",
|
||||||
|
"1 - (2 - 3)",
|
||||||
|
x -> 2,
|
||||||
|
new Subtract(c(1), new Subtract(c(2), c(3)))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"((1 * 2) * 3)",
|
||||||
|
"1 * 2 * 3",
|
||||||
|
x -> 6,
|
||||||
|
new Multiply(new Multiply(c(1), c(2)), c(3))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(1 * (2 * 3))",
|
||||||
|
"1 * 2 * 3",
|
||||||
|
x -> 6,
|
||||||
|
new Multiply(c(1), new Multiply(c(2), c(3)))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"((10 / 2) / 3)",
|
||||||
|
"10 / 2 / 3",
|
||||||
|
x -> 10 / 2 / 3,
|
||||||
|
new Divide(new Divide(c(10), c(2)), c(3))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(10 / (3 / 2))",
|
||||||
|
"10 / (3 / 2)",
|
||||||
|
x -> 10 / (3 / 2),
|
||||||
|
new Divide(c(10), new Divide(c(3), c(2)))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(10 * (3 / 2))",
|
||||||
|
"10 * (3 / 2)",
|
||||||
|
x -> 10 * (3 / 2),
|
||||||
|
new Multiply(c(10), new Divide(c(3), c(2)))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(10 + (3 - 2))",
|
||||||
|
"10 + 3 - 2",
|
||||||
|
x -> 10 + (3 - 2),
|
||||||
|
new Add(c(10), new Subtract(c(3), c(2)))
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"((x * x) + ((x - 1) / 10))",
|
||||||
|
"x * x + (x - 1) / 10",
|
||||||
|
x -> x * x + (x - 1) / 10,
|
||||||
|
new Add(
|
||||||
|
new Multiply(vx, vx),
|
||||||
|
new Divide(new Subtract(vx, c(1)), c(10))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.basic(
|
||||||
|
"(x * -1000000000)",
|
||||||
|
"x * -1000000000",
|
||||||
|
x -> x * -1_000_000_000,
|
||||||
|
new Multiply(vx, c(-1_000_000_000))
|
||||||
|
)
|
||||||
|
.basic("(10 / x)", "10 / x", x -> 10 / x, new Divide(c(10), vx))
|
||||||
|
.basic("(x / x)", "x / x", x -> x / x, new Divide(vx, vx))
|
||||||
|
.advanced("(2 + 1)", "2 + 1", x -> 2 + 1, new Add(c2, c1))
|
||||||
|
.advanced("(x - 1)", "x - 1", x -> x - 1, new Subtract(vx, c1))
|
||||||
|
.advanced("(1 * 2)", "1 * 2", x -> 1 * 2, new Multiply(c1, c2))
|
||||||
|
.advanced("(x / 1)", "x / 1", x -> x / 1, new Divide(vx, c1))
|
||||||
|
.advanced(
|
||||||
|
"(1 + (2 + 1))",
|
||||||
|
"1 + 2 + 1",
|
||||||
|
x -> 1 + 2 + 1,
|
||||||
|
new Add(c1, new Add(c2, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(x - (x - 1))",
|
||||||
|
"x - (x - 1)",
|
||||||
|
x -> x - (x - 1),
|
||||||
|
new Subtract(vx, new Subtract(vx, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(2 * (x / 1))",
|
||||||
|
"2 * (x / 1)",
|
||||||
|
x -> 2 * (x / 1),
|
||||||
|
new Multiply(c2, new Divide(vx, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(2 / (x - 1))",
|
||||||
|
"2 / (x - 1)",
|
||||||
|
x -> 2 / (x - 1),
|
||||||
|
new Divide(c2, new Subtract(vx, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((1 * 2) + x)",
|
||||||
|
"1 * 2 + x",
|
||||||
|
x -> 1 * 2 + x,
|
||||||
|
new Add(new Multiply(c1, c2), vx)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((x - 1) - 2)",
|
||||||
|
"x - 1 - 2",
|
||||||
|
x -> x - 1 - 2,
|
||||||
|
new Subtract(new Subtract(vx, c1), c2)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((x / 1) * 2)",
|
||||||
|
"x / 1 * 2",
|
||||||
|
x -> (x / 1) * 2,
|
||||||
|
new Multiply(new Divide(vx, c1), c2)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((2 + 1) / 1)",
|
||||||
|
"(2 + 1) / 1",
|
||||||
|
x -> (2 + 1) / 1,
|
||||||
|
new Divide(new Add(c2, c1), c1)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(1 + (1 + (2 + 1)))",
|
||||||
|
"1 + 1 + 2 + 1",
|
||||||
|
x -> 1 + 1 + 2 + 1,
|
||||||
|
new Add(c1, new Add(c1, new Add(c2, c1)))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(x - ((1 * 2) + x))",
|
||||||
|
"x - (1 * 2 + x)",
|
||||||
|
x -> x - (1 * 2 + x),
|
||||||
|
new Subtract(vx, new Add(new Multiply(c1, c2), vx))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(x * (2 / (x - 1)))",
|
||||||
|
"x * (2 / (x - 1))",
|
||||||
|
x -> x * (2 / (x - 1)),
|
||||||
|
new Multiply(vx, new Divide(c2, new Subtract(vx, c1)))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(x / (1 + (2 + 1)))",
|
||||||
|
"x / (1 + 2 + 1)",
|
||||||
|
x -> x / (1 + 2 + 1),
|
||||||
|
new Divide(vx, new Add(c1, new Add(c2, c1)))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((1 * 2) + (2 + 1))",
|
||||||
|
"1 * 2 + 2 + 1",
|
||||||
|
x -> 1 * 2 + 2 + 1,
|
||||||
|
new Add(new Multiply(c1, c2), new Add(c2, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((2 + 1) - (2 + 1))",
|
||||||
|
"2 + 1 - (2 + 1)",
|
||||||
|
x -> 2 + 1 - (2 + 1),
|
||||||
|
new Subtract(new Add(c2, c1), new Add(c2, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((x - 1) * (x / 1))",
|
||||||
|
"(x - 1) * (x / 1)",
|
||||||
|
x -> (x - 1) * (x / 1),
|
||||||
|
new Multiply(new Subtract(vx, c1), new Divide(vx, c1))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((x - 1) / (1 * 2))",
|
||||||
|
"(x - 1) / (1 * 2)",
|
||||||
|
x -> (x - 1) / (1 * 2),
|
||||||
|
new Divide(new Subtract(vx, c1), new Multiply(c1, c2))
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(((x - 1) - 2) + x)",
|
||||||
|
"x - 1 - 2 + x",
|
||||||
|
x -> x - 1 - 2 + x,
|
||||||
|
new Add(new Subtract(new Subtract(vx, c1), c2), vx)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(((1 * 2) + x) - 1)",
|
||||||
|
"1 * 2 + x - 1",
|
||||||
|
x -> 1 * 2 + x - 1,
|
||||||
|
new Subtract(new Add(new Multiply(c1, c2), vx), c1)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"(((2 + 1) / 1) * x)",
|
||||||
|
"(2 + 1) / 1 * x",
|
||||||
|
x -> ((2 + 1) / 1) * x,
|
||||||
|
new Multiply(new Divide(new Add(c2, c1), c1), vx)
|
||||||
|
)
|
||||||
|
.advanced(
|
||||||
|
"((2 / (x - 1)) / 2)",
|
||||||
|
"2 / (x - 1) / 2",
|
||||||
|
x -> 2 / (x - 1) / 2,
|
||||||
|
new Divide(new Divide(c2, new Subtract(vx, c1)), c2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
java/expression/ExpressionTest.java
Normal file
30
java/expression/ExpressionTest.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package expression;
|
||||||
|
|
||||||
|
import base.Selector;
|
||||||
|
import base.TestCounter;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
public final class ExpressionTest {
|
||||||
|
|
||||||
|
public static final Selector SELECTOR = new Selector(
|
||||||
|
ExpressionTest.class,
|
||||||
|
"easy",
|
||||||
|
"hard"
|
||||||
|
).variant("Base", v(Expression::tester));
|
||||||
|
|
||||||
|
private ExpressionTest() {}
|
||||||
|
|
||||||
|
public static Consumer<TestCounter> v(
|
||||||
|
final Function<TestCounter, ? extends ExpressionTester<?, ?>> tester
|
||||||
|
) {
|
||||||
|
return t -> tester.apply(t).test();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(final String... args) {
|
||||||
|
SELECTOR.main(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
415
java/expression/ExpressionTester.java
Normal file
415
java/expression/ExpressionTester.java
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
package expression;
|
||||||
|
|
||||||
|
import static base.Asserts.assertTrue;
|
||||||
|
|
||||||
|
import base.*;
|
||||||
|
import expression.common.*;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
public class ExpressionTester<E extends ToMiniString, C> extends Tester {
|
||||||
|
|
||||||
|
private final List<Integer> VALUES = IntStream.rangeClosed(-10, 10)
|
||||||
|
.boxed()
|
||||||
|
.toList();
|
||||||
|
private final ExpressionKind<E, C> kind;
|
||||||
|
|
||||||
|
private final List<Test> basic = new ArrayList<>();
|
||||||
|
private final List<Test> advanced = new ArrayList<>();
|
||||||
|
private final Set<String> used = new HashSet<>();
|
||||||
|
private final GeneratorBuilder generator;
|
||||||
|
|
||||||
|
private final List<Pair<ToMiniString, String>> prev = new ArrayList<>();
|
||||||
|
private final Map<String, C> mappings;
|
||||||
|
|
||||||
|
protected ExpressionTester(
|
||||||
|
final TestCounter counter,
|
||||||
|
final ExpressionKind<E, C> kind,
|
||||||
|
final Function<C, E> expectedConstant,
|
||||||
|
final Binary<C, E> binary,
|
||||||
|
final BinaryOperator<C> add,
|
||||||
|
final BinaryOperator<C> sub,
|
||||||
|
final BinaryOperator<C> mul,
|
||||||
|
final BinaryOperator<C> div,
|
||||||
|
final Map<String, C> mappings
|
||||||
|
) {
|
||||||
|
super(counter);
|
||||||
|
this.kind = kind;
|
||||||
|
this.mappings = mappings;
|
||||||
|
|
||||||
|
generator = new GeneratorBuilder(
|
||||||
|
expectedConstant,
|
||||||
|
kind::constant,
|
||||||
|
binary,
|
||||||
|
kind::randomValue
|
||||||
|
);
|
||||||
|
generator.binary("+", 1600, add, Add.class);
|
||||||
|
generator.binary("-", 1602, sub, Subtract.class);
|
||||||
|
generator.binary("*", 2001, mul, Multiply.class);
|
||||||
|
generator.binary("/", 2002, div, Divide.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExpressionTester(
|
||||||
|
final TestCounter counter,
|
||||||
|
final ExpressionKind<E, C> kind,
|
||||||
|
final Function<C, E> expectedConstant,
|
||||||
|
final Binary<C, E> binary,
|
||||||
|
final BinaryOperator<C> add,
|
||||||
|
final BinaryOperator<C> sub,
|
||||||
|
final BinaryOperator<C> mul,
|
||||||
|
final BinaryOperator<C> div
|
||||||
|
) {
|
||||||
|
this(
|
||||||
|
counter,
|
||||||
|
kind,
|
||||||
|
expectedConstant,
|
||||||
|
binary,
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
mul,
|
||||||
|
div,
|
||||||
|
Map.of()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return kind.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void test() {
|
||||||
|
counter.scope("Basic tests", () -> basic.forEach(Test::test));
|
||||||
|
counter.scope("Advanced tests", () -> advanced.forEach(Test::test));
|
||||||
|
counter.scope("Random tests", generator::testRandom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "ConstantValue", "EqualsWithItself" })
|
||||||
|
private void checkEqualsAndToString(
|
||||||
|
final String full,
|
||||||
|
final String mini,
|
||||||
|
final ToMiniString expression,
|
||||||
|
final ToMiniString copy
|
||||||
|
) {
|
||||||
|
checkToString("toString", full, expression.toString());
|
||||||
|
if (mode() > 0) {
|
||||||
|
checkToString("toMiniString", mini, expression.toMiniString());
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.test(() -> {
|
||||||
|
assertTrue("Equals to this", expression.equals(expression));
|
||||||
|
assertTrue("Equals to copy", expression.equals(copy));
|
||||||
|
assertTrue("Equals to null", !expression.equals(null));
|
||||||
|
assertTrue("Copy equals to null", !copy.equals(null));
|
||||||
|
});
|
||||||
|
|
||||||
|
final String expressionToString = Objects.requireNonNull(
|
||||||
|
expression.toString()
|
||||||
|
);
|
||||||
|
for (final Pair<ToMiniString, String> pair : prev) {
|
||||||
|
counter.test(() -> {
|
||||||
|
final ToMiniString prev = pair.first();
|
||||||
|
final String prevToString = pair.second();
|
||||||
|
final boolean equals = prevToString.equals(expressionToString);
|
||||||
|
assertTrue(
|
||||||
|
"Equals to " + prevToString,
|
||||||
|
prev.equals(expression) == equals
|
||||||
|
);
|
||||||
|
assertTrue(
|
||||||
|
"Equals to " + prevToString,
|
||||||
|
expression.equals(prev) == equals
|
||||||
|
);
|
||||||
|
assertTrue(
|
||||||
|
"Inconsistent hashCode for " + prev + " and " + expression,
|
||||||
|
(prev.hashCode() == expression.hashCode()) == equals
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkToString(
|
||||||
|
final String method,
|
||||||
|
final String expected,
|
||||||
|
final String actual
|
||||||
|
) {
|
||||||
|
counter.test(() ->
|
||||||
|
assertTrue(
|
||||||
|
String.format(
|
||||||
|
"Invalid %s\n expected: %s\n actual: %s",
|
||||||
|
method,
|
||||||
|
expected,
|
||||||
|
actual
|
||||||
|
),
|
||||||
|
expected.equals(actual)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void check(
|
||||||
|
final String full,
|
||||||
|
final E expected,
|
||||||
|
final E actual,
|
||||||
|
final List<String> variables,
|
||||||
|
final List<C> values
|
||||||
|
) {
|
||||||
|
final String vars = IntStream.range(0, variables.size())
|
||||||
|
.mapToObj(i -> variables.get(i) + "=" + values.get(i))
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
counter.test(() -> {
|
||||||
|
final Object expectedResult = evaluate(expected, variables, values);
|
||||||
|
final Object actualResult = evaluate(actual, variables, values);
|
||||||
|
final String reason = String.format(
|
||||||
|
"%s:%n expected `%s`,%n actual `%s`",
|
||||||
|
String.format("f(%s)\nwhere f is %s", vars, full),
|
||||||
|
Asserts.toString(expectedResult),
|
||||||
|
Asserts.toString(actualResult)
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
expectedResult != null &&
|
||||||
|
actualResult != null &&
|
||||||
|
expectedResult.getClass() == actualResult.getClass() &&
|
||||||
|
(expectedResult.getClass() == Double.class ||
|
||||||
|
expectedResult.getClass() == Float.class)
|
||||||
|
) {
|
||||||
|
final double expectedValue = (
|
||||||
|
(Number) expectedResult
|
||||||
|
).doubleValue();
|
||||||
|
final double actualValue = (
|
||||||
|
(Number) actualResult
|
||||||
|
).doubleValue();
|
||||||
|
Asserts.assertEquals(reason, expectedValue, actualValue, 1e-6);
|
||||||
|
} else {
|
||||||
|
assertTrue(
|
||||||
|
reason,
|
||||||
|
Objects.deepEquals(expectedResult, actualResult)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object evaluate(
|
||||||
|
final E expression,
|
||||||
|
final List<String> variables,
|
||||||
|
final List<C> values
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return kind.evaluate(expression, variables, values);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return e.getClass().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExpressionTester<E, C> basic(
|
||||||
|
final String full,
|
||||||
|
final String mini,
|
||||||
|
final E expected,
|
||||||
|
final E actual
|
||||||
|
) {
|
||||||
|
return basicF(full, mini, expected, vars -> actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExpressionTester<E, C> basicF(
|
||||||
|
final String full,
|
||||||
|
final String mini,
|
||||||
|
final E expected,
|
||||||
|
final Function<List<String>, E> actual
|
||||||
|
) {
|
||||||
|
return basic(new Test(full, mini, expected, actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionTester<E, C> basic(final Test test) {
|
||||||
|
Asserts.assertTrue(test.full, used.add(test.full));
|
||||||
|
basic.add(test);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExpressionTester<E, C> advanced(
|
||||||
|
final String full,
|
||||||
|
final String mini,
|
||||||
|
final E expected,
|
||||||
|
final E actual
|
||||||
|
) {
|
||||||
|
return advancedF(full, mini, expected, vars -> actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExpressionTester<E, C> advancedF(
|
||||||
|
final String full,
|
||||||
|
final String mini,
|
||||||
|
final E expected,
|
||||||
|
final Function<List<String>, E> actual
|
||||||
|
) {
|
||||||
|
Asserts.assertTrue(full, used.add(full));
|
||||||
|
advanced.add(new Test(full, mini, expected, actual));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E> Named<E> variable(
|
||||||
|
final String name,
|
||||||
|
final E expected
|
||||||
|
) {
|
||||||
|
return Named.of(name, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Binary<C, E> {
|
||||||
|
E apply(BinaryOperator<C> op, E a, E b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class Test {
|
||||||
|
|
||||||
|
private final String full;
|
||||||
|
private final String mini;
|
||||||
|
private final E expected;
|
||||||
|
private final Function<List<String>, E> actual;
|
||||||
|
|
||||||
|
private Test(
|
||||||
|
final String full,
|
||||||
|
final String mini,
|
||||||
|
final E expected,
|
||||||
|
final Function<List<String>, E> actual
|
||||||
|
) {
|
||||||
|
this.full = full;
|
||||||
|
this.mini = mini;
|
||||||
|
this.expected = expected;
|
||||||
|
this.actual = actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void test() {
|
||||||
|
final List<Pair<String, E>> variables = kind
|
||||||
|
.variables()
|
||||||
|
.generate(random(), 3);
|
||||||
|
final List<String> names = Functional.map(variables, Pair::first);
|
||||||
|
final E actual = kind.cast(this.actual.apply(names));
|
||||||
|
final String full = mangle(this.full, names);
|
||||||
|
final String mini = mangle(this.mini, names);
|
||||||
|
|
||||||
|
counter.test(() -> {
|
||||||
|
kind
|
||||||
|
.allValues(variables.size(), VALUES)
|
||||||
|
.forEach(values ->
|
||||||
|
check(mini, expected, actual, names, values)
|
||||||
|
);
|
||||||
|
checkEqualsAndToString(full, mini, actual, actual);
|
||||||
|
prev.add(Pair.of(actual, full));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mangle(String string, final List<String> names) {
|
||||||
|
for (int i = 0; i < names.size(); i++) {
|
||||||
|
string = string.replace("$" + (char) ('x' + i), names.get(i));
|
||||||
|
}
|
||||||
|
for (final Map.Entry<String, C> mapping : mappings.entrySet()) {
|
||||||
|
string = string.replace(
|
||||||
|
mapping.getKey(),
|
||||||
|
mapping.getValue().toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class GeneratorBuilder {
|
||||||
|
|
||||||
|
private final Generator.Builder<C> generator;
|
||||||
|
private final NodeRendererBuilder<C> renderer =
|
||||||
|
new NodeRendererBuilder<>(random());
|
||||||
|
private final Renderer.Builder<C, Unit, E> expected;
|
||||||
|
private final Renderer.Builder<C, Unit, E> actual;
|
||||||
|
private final Renderer.Builder<C, Unit, E> copy;
|
||||||
|
private final Binary<C, E> binary;
|
||||||
|
|
||||||
|
private GeneratorBuilder(
|
||||||
|
final Function<C, E> expectedConstant,
|
||||||
|
final Function<? super C, E> actualConstant,
|
||||||
|
final Binary<C, E> binary,
|
||||||
|
final Function<ExtendedRandom, C> randomValue
|
||||||
|
) {
|
||||||
|
generator = Generator.builder(
|
||||||
|
() -> randomValue.apply(random()),
|
||||||
|
random()
|
||||||
|
);
|
||||||
|
expected = Renderer.builder(expectedConstant::apply);
|
||||||
|
actual = Renderer.builder(actualConstant::apply);
|
||||||
|
copy = Renderer.builder(actualConstant::apply);
|
||||||
|
|
||||||
|
this.binary = binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void binary(
|
||||||
|
final String name,
|
||||||
|
final int priority,
|
||||||
|
final BinaryOperator<C> op,
|
||||||
|
final Class<?> type
|
||||||
|
) {
|
||||||
|
generator.add(name, 2);
|
||||||
|
renderer.binary(name, priority);
|
||||||
|
|
||||||
|
expected.binary(name, (unit, a, b) -> binary.apply(op, a, b));
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Constructor<? extends E> constructor = (Constructor<
|
||||||
|
? extends E
|
||||||
|
>) Arrays.stream(type.getConstructors())
|
||||||
|
.filter(cons -> Modifier.isPublic(cons.getModifiers()))
|
||||||
|
.filter(cons -> cons.getParameterCount() == 2)
|
||||||
|
.findFirst()
|
||||||
|
.orElseGet(() ->
|
||||||
|
counter.fail(
|
||||||
|
"%s(..., ...) constructor not found",
|
||||||
|
type.getSimpleName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Renderer.BinaryOperator<Unit, E> actual = (unit, a, b) -> {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(a, b);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return counter.fail(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.actual.binary(name, actual);
|
||||||
|
copy.binary(name, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testRandom() {
|
||||||
|
final NodeRenderer<C> renderer = this.renderer.build();
|
||||||
|
final Renderer<C, Unit, E> expectedRenderer = this.expected.build();
|
||||||
|
final Renderer<C, Unit, E> actualRenderer = this.actual.build();
|
||||||
|
final expression.common.Generator<C, E> generator =
|
||||||
|
this.generator.build(kind.variables(), List.of());
|
||||||
|
generator.testRandom(counter, 1, expr -> {
|
||||||
|
final String full = renderer.render(expr, NodeRenderer.FULL);
|
||||||
|
final String mini = renderer.render(expr, NodeRenderer.MINI);
|
||||||
|
final E expected = expectedRenderer.render(expr, Unit.INSTANCE);
|
||||||
|
final E actual = actualRenderer.render(expr, Unit.INSTANCE);
|
||||||
|
|
||||||
|
final List<Pair<String, E>> variables = expr.variables();
|
||||||
|
final List<String> names = Functional.map(
|
||||||
|
variables,
|
||||||
|
Pair::first
|
||||||
|
);
|
||||||
|
final List<C> values = Stream.generate(() ->
|
||||||
|
kind.randomValue(random())
|
||||||
|
)
|
||||||
|
.limit(variables.size())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
checkEqualsAndToString(
|
||||||
|
full,
|
||||||
|
mini,
|
||||||
|
actual,
|
||||||
|
copy.build().render(expr, Unit.INSTANCE)
|
||||||
|
);
|
||||||
|
check(full, expected, actual, names, values);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
java/expression/Floor.java
Normal file
75
java/expression/Floor.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package expression;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Doschennikov Nikita (me@fymio.us)
|
||||||
|
*/
|
||||||
|
public class Floor extends AbstractExpression {
|
||||||
|
|
||||||
|
private final AbstractExpression operand;
|
||||||
|
|
||||||
|
public Floor(AbstractExpression operand) {
|
||||||
|
this.operand = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int floorInt(int n) {
|
||||||
|
return Math.floorDiv(n, 1000) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int evaluate(int x) {
|
||||||
|
return floorInt(operand.evaluate(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int evaluate(int x, int y, int z) {
|
||||||
|
return floorInt(operand.evaluate(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int evaluate(List<Integer> vars) {
|
||||||
|
return floorInt(operand.evaluate(vars));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger evaluateBi(List<BigInteger> vars) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"floor not supported for BigInteger"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal evaluateBd(List<BigDecimal> vars) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"floor not supported for BigDecimal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "floor(" + operand + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toMiniString() {
|
||||||
|
if (operand instanceof AbstractBinaryOperation) {
|
||||||
|
return "floor(" + operand.toMiniString() + ")";
|
||||||
|
}
|
||||||
|
return "floor " + operand.toMiniString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (!(obj instanceof Floor)) return false;
|
||||||
|
return operand.equals(((Floor) obj).operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return operand.hashCode() ^ 0x464C4F52;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user