first commit

This commit is contained in:
me
2026-04-08 21:25:17 +03:00
parent 3681b8eccd
commit 371b14c5e3
173 changed files with 14126 additions and 0 deletions

View File

@@ -0,0 +1,366 @@
package expression.generic;
import base.*;
import expression.ToMiniString;
import expression.common.*;
import expression.parser.ParserTestSet;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class GenericTester extends Tester {
private static final int SIZE = 10;
private static final int MAX = Integer.MAX_VALUE - 1;
private static final int MIN = Integer.MIN_VALUE;
private static final List<Pair<String, F<?>>> VARIABLES = List.of(
Pair.of("x", (x, y, z) -> x),
Pair.of("y", (x, y, z) -> y),
Pair.of("z", (x, y, z) -> z)
);
private static final ExpressionKind.Variables<F<?>> VARS = (c, r) ->
VARIABLES;
protected final List<Named<IF<?>>> tests = new ArrayList<>();
private final Tabulator tabulator = new GenericTabulator();
private final Set<String> operations = new HashSet<>();
private final List<NodeRenderer.Paren> parens = new ArrayList<>(
List.of(NodeRenderer.paren("(", ")"))
);
private final TestGeneratorBuilder<Integer> generator;
private final List<Mode.Builder<?>> modes = new ArrayList<>();
public GenericTester(final TestCounter counter) {
super(counter);
generator = new TestGeneratorBuilder<>(
random(),
random()::nextInt,
ParserTestSet.CONSTS,
false
);
}
protected void test(
final String expression,
final String name,
final IF<?> f
) {
tests.add(Named.of(name + ": " + expression, f));
}
@Override
public void test() {
final List<Mode> modes = this.modes.stream()
.map(mode -> mode.build(this))
.toList();
for (final Named<IF<?>> test : tests) {
final String[] parts = test.name().split(": ");
testFull(parts[0], parts[1], test.value());
}
final TestGenerator<Integer, F<?>> generator = this.generator.build(
VARS
);
counter.scope("basic", () -> generator.testBasic(test(true, modes)));
counter.scope("random", () ->
generator.testRandom(
counter,
20 + (TestCounter.DENOMINATOR - 1) * 2,
test(false, modes)
)
);
}
private void testFull(
final String mode,
final String expression,
final IF<?> f
) {
testShort(mode, expression, f);
test(mode, expression, f, MAX, -1, MAX, 0);
test(mode, expression, f, MIN, 0, MIN, 1);
}
private void testShort(
final String mode,
final String expression,
final IF<?> f
) {
test(mode, expression, f, 0, -1, 0, 1);
}
private void test(
final String mode,
final String expression,
final IF<?> f,
final int min,
final int dMin,
final int max,
final int dMax
) {
test(
mode,
expression,
f,
min + random().nextInt(SIZE) * dMin,
max + random().nextInt(SIZE) * dMax,
min + random().nextInt(SIZE) * dMin,
max + random().nextInt(SIZE) * dMax,
min + random().nextInt(SIZE) * dMin,
max + random().nextInt(SIZE) * dMax
);
}
private Consumer<TestGenerator.Test<Integer, F<?>>> test(
final boolean full,
final List<Mode> modes
) {
final NodeRenderer.Settings settings = NodeRenderer.FULL.withParens(
parens
);
return test ->
modes.forEach(mode ->
mode.test(test.expr, test.render(settings), full)
);
}
private <T> void test(
final String mode,
final String expression,
final IF<T> f,
final int x1,
final int x2,
final int y1,
final int y2,
final int z1,
final int z2
) {
final String context = String.format(
"mode=%s, x=[%d, %d] y=[%d, %d] z=[%d, %d], expression=%s%n",
mode,
x1,
x2,
y1,
y2,
z1,
z2,
expression
);
final Object[][][] result = counter.testV(() ->
TestCounter.get(() ->
tabulator.tabulate(mode, expression, x1, x2, y1, y2, z1, z2)
).either(
e -> counter.fail(e, "%s %s", "tabulate", context),
Function.identity()
)
);
IntStream.rangeClosed(x1, x2).forEach(x ->
IntStream.rangeClosed(y1, y2).forEach(y ->
IntStream.rangeClosed(z1, z2).forEach(z ->
counter.test(() -> {
final Object expected = TestCounter.get(() ->
f.apply(x, y, z)
).either(e -> null, Function.identity());
final Object actual = result[x - x1][y - y1][z - z1];
counter.checkTrue(
Objects.equals(actual, expected),
"table[%d][%d][%d](x=%d, y=%d, z=%d]) = %s (expected %s)%n%s",
x - x1,
y - y1,
z - z1,
x,
y,
z,
actual,
expected,
context
);
})
)
)
);
}
public void binary(final String name, final int priority) {
operations.add(name + ":2");
generator.binary(name, priority);
}
public void unary(final String name, final int priority) {
operations.add(name + ":1");
generator.unary(name, priority);
}
public void parens(final String... parens) {
assert parens.length % 2 == 0 : "Parens should come in pairs";
for (int i = 0; i < parens.length; i += 2) {
this.parens.add(NodeRenderer.paren(parens[i], parens[i + 1]));
}
}
/* package-private */ interface Mode {
static <T> Builder<T> builder(
final String mode,
final IntFunction<T> constant,
final IntUnaryOperator fixer
) {
return new Builder<>(mode, constant, fixer);
}
void test(
final Expr<Integer, ? extends F<?>> expr,
final String expression,
final boolean full
);
/* package-private */ final class Builder<
T
> implements Consumer<GenericTester> {
private final String mode;
private final IntFunction<T> constant;
private final IntUnaryOperator fixer;
private final List<Named<UnaryOperator<GenericTester.F<T>>>> unary =
new ArrayList<>();
private final List<
Named<BinaryOperator<GenericTester.F<T>>>
> binary = new ArrayList<>();
private Builder(
final String mode,
final IntFunction<T> constant,
final IntUnaryOperator fixer
) {
this.mode = mode;
this.constant = constant;
this.fixer = fixer;
}
public Builder<T> unary(
final String name,
final UnaryOperator<T> op
) {
unary.add(
Named.of(
name,
arg -> (x, y, z) -> op.apply(arg.apply(x, y, z))
)
);
return this;
}
public Builder<T> binary(
final String name,
final BinaryOperator<T> op
) {
binary.add(
Named.of(
name,
(a, b) ->
(x, y, z) ->
op.apply(a.apply(x, y, z), b.apply(x, y, z))
)
);
return this;
}
@Override
public void accept(final GenericTester tester) {
tester.modes.add(this);
}
private Mode build(final GenericTester tester) {
final Set<String> ops = Stream.concat(
unary.stream().map(op -> op.name() + ":1"),
binary.stream().map(op -> op.name() + ":2")
).collect(Collectors.toUnmodifiableSet());
final List<String> diff = tester.operations
.stream()
.filter(Predicate.not(ops::contains))
.toList();
Asserts.assertTrue(
String.format("Missing operations for %s: %s", mode, diff),
diff.isEmpty()
);
final Renderer.Builder<
Integer,
Unit,
GenericTester.F<T>
> builder = Renderer.builder(
value -> (x, y, z) -> constant.apply(value)
);
unary.forEach(op ->
builder.unary(op.name(), (unit, arg) ->
op.value().apply(arg)
)
);
binary.forEach(op ->
builder.binary(op.name(), (unit, a, b) ->
op.value().apply(a, b)
)
);
final Renderer<Integer, Unit, F<T>> renderer = builder.build();
final TestGenerator<Integer, F<?>> genRenderer =
tester.generator.build(VARS);
return (expr, expression, full) -> {
final String fixed =
fixer == IntUnaryOperator.identity()
? expression
: genRenderer.render(cata(expr), NodeRenderer.FULL);
@SuppressWarnings("unchecked")
final Expr<Integer, F<T>> converted = (Expr<
Integer,
F<T>
>) expr;
final F<T> expected = renderer.render(
converted,
Unit.INSTANCE
);
final IF<T> f = (x, y, z) ->
expected.apply(
constant.apply(x),
constant.apply(y),
constant.apply(z)
);
if (full) {
tester.testFull(mode, fixed, f);
} else {
tester.testShort(mode, fixed, f);
}
};
}
private Expr<Integer, ? extends F<?>> cata(
final Expr<Integer, ? extends F<?>> expr
) {
return expr.node(node ->
node.cata(
c -> Node.constant(fixer.applyAsInt(c)),
Node::op,
Node::op,
Node::op
)
);
}
}
}
@FunctionalInterface
protected interface IF<T> {
T apply(int x, int y, int z);
}
@FunctionalInterface
protected interface F<T> extends ToMiniString {
T apply(T x, T y, T z);
}
}