initial commit

This commit is contained in:
2026-01-29 23:20:12 +05:00
parent fb1ce36970
commit 67357cf271
76 changed files with 7115 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
package expression.common;
import base.Functional;
import base.Pair;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public record Expr<C, V>(Node<C> node, List<Pair<String, V>> variables) {
public <T> List<Pair<String, T>> variables(final BiFunction<String, V, T> f) {
return Functional.map(
variables,
variable -> variable.second(f.apply(variable.first(), variable.second()))
);
}
public <T> Expr<C, T> convert(final BiFunction<String, V, T> f) {
return of(node, variables(f));
}
public Expr<C, V> node(final Function<Node<C>, Node<C>> f) {
return of(f.apply(node), variables);
}
public static <C, V> Expr<C, V> of(final Node<C> node, final List<Pair<String, V>> variables) {
return new Expr<>(node, variables);
}
}

View File

@@ -0,0 +1,94 @@
package expression.common;
import base.ExtendedRandom;
import base.Functional;
import base.Pair;
import expression.ToMiniString;
import java.util.List;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ExpressionKind<E extends ToMiniString, C> {
private final Type<C> type;
private final Class<E> kind;
private final Variables<E> variables;
private final Evaluator<E, C> evaluator;
public ExpressionKind(
final Type<C> type,
final Class<E> kind,
final Variables<E> variables,
final Evaluator<E, C> evaluator
) {
this.type = type;
this.kind = kind;
this.variables = variables;
this.evaluator = evaluator;
}
public ExpressionKind(
final Type<C> type,
final Class<E> kind,
final List<Pair<String, E>> variables,
final Evaluator<E, C> evaluator
) {
this(type, kind, (r, c) -> variables, evaluator);
}
public C evaluate(final E expression, final List<String> variables, final List<C> values) throws Exception {
return evaluator.evaluate(expression, variables, values);
}
public E cast(final Object expression) {
return kind.cast(expression);
}
public String getName() {
return kind.getSimpleName();
}
public E constant(final C value) {
return cast(type.constant(value));
}
public C randomValue(final ExtendedRandom random) {
return type.randomValue(random);
}
public List<List<C>> allValues(final int length, final List<Integer> values) {
return Functional.allValues(fromInts(values), length);
}
public List<C> fromInts(final List<Integer> values) {
return Functional.map(values, this::fromInt);
}
public C fromInt(final int value) {
return type.fromInt(value);
}
@Override
public String toString() {
return kind.getName();
}
public ExpressionKind<E, C> withVariables(final Variables<E> variables) {
return new ExpressionKind<>(type, kind, variables, evaluator);
}
public Variables<E> variables() {
return variables;
}
@FunctionalInterface
public interface Variables<E> {
List<Pair<String, E>> generate(final ExtendedRandom random, final int count);
}
@FunctionalInterface
public interface Evaluator<E, R> {
R evaluate(final E expression, final List<String> vars, final List<R> values) throws Exception;
}
}

View File

@@ -0,0 +1,173 @@
package expression.common;
import base.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Generator<C, E> {
private final Supplier<C> constant;
private final List<Named<Integer>> ops;
private final ExpressionKind.Variables<E> variables;
private final Set<String> forbidden;
private final ExtendedRandom random;
private final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests;
public Generator(
final Supplier<C> constant,
final List<Named<Integer>> ops,
final ExpressionKind.Variables<E> variables,
final Set<String> forbidden,
final ExtendedRandom random,
final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests
) {
this.constant = constant;
this.ops = List.copyOf(ops);
this.variables = variables;
this.forbidden = Set.copyOf(forbidden);
this.random = random;
this.basicTests = List.copyOf(basicTests);
}
public static <C> Builder<C> builder(final Supplier<C> constant, final ExtendedRandom random) {
return new Builder<>(random, constant);
}
public void testRandom(
final TestCounter counter,
final int denominator,
final Consumer<Expr<C, E>> consumer
) {
final int d = Math.max(TestCounter.DENOMINATOR, denominator);
testRandom(counter, consumer, 1, 100, 100 / d, (vars, depth) -> generateFullDepth(vars, Math.min(depth, 3)));
testRandom(counter, consumer, 2, 1000 / d, 1, this::generateSize);
testRandom(counter, consumer, 3, 12, 100 / d, this::generateFullDepth);
testRandom(counter, consumer, 4, 777 / d, 1, this::generatePartialDepth);
}
private void testRandom(
final TestCounter counter,
final Consumer<Expr<C, E>> consumer,
final int seq,
final int levels,
final int perLevel,
final BiFunction<List<Node<C>>, Integer, Node<C>> generator
) {
counter.scope("Random tests #" + seq, () -> {
final int total = levels * perLevel;
int generated = 0;
for (int level = 0; level < levels; level++) {
for (int j = 0; j < perLevel; j++) {
if (generated % 100 == 0) {
progress(counter, total, generated);
}
generated++;
final List<Pair<String, E>> vars = variables(random.nextInt(10) + 1);
consumer.accept(Expr.of(generator.apply(Functional.map(vars, v -> Node.op(v.first())), level), vars));
}
}
progress(counter, generated, total);
});
}
private static void progress(final TestCounter counter, final int total, final int generated) {
counter.format("Completed %4d out of %d%n", generated, total);
}
private Node<C> generate(
final List<Node<C>> variables,
final boolean nullary,
final Supplier<Node<C>> unary,
final Supplier<Pair<Node<C>, Node<C>>> binary
) {
if (nullary || ops.isEmpty()) {
return random.nextBoolean() ? random.randomItem(variables) : Node.constant(constant.get());
} else {
final Named<Integer> op = random.randomItem(ops);
if (Math.abs(op.value()) == 1) {
return Node.op(op.name(), (op.value() + 1) >> 1, unary.get());
} else {
final Pair<Node<C>, Node<C>> pair = binary.get();
return Node.op(op.name(), pair.first(), pair.second());
}
}
}
private Node<C> generate(final List<Node<C>> variables, final boolean nullary, final Supplier<Node<C>> child) {
return generate(variables, nullary, child, () -> Pair.of(child.get(), child.get()));
}
private Node<C> generateFullDepth(final List<Node<C>> variables, final int depth) {
return generate(variables, depth == 0, () -> generateFullDepth(variables, depth - 1));
}
private Node<C> generatePartialDepth(final List<Node<C>> variables, final int depth) {
return generate(variables, depth == 0, () -> generatePartialDepth(variables, random.nextInt(depth)));
}
private Node<C> generateSize(final List<Node<C>> variables, final int size) {
final int first = size <= 1 ? 0 : random.nextInt(size);
return generate(
variables,
size == 0,
() -> generateSize(variables, size - 1),
() -> Pair.of(
generateSize(variables, first),
generateSize(variables, size - 1 - first)
)
);
}
public void testBasic(final Consumer<Expr<C, E>> consumer) {
basicTests.forEach(test -> {
final List<Pair<String, E>> vars = variables(random.nextInt(5) + 3);
test.apply(Functional.map(vars, v -> Node.op(v.first())))
.map(node -> Expr.of(node, vars))
.forEachOrdered(consumer);
});
}
public List<Pair<String, E>> variables(final int count) {
List<Pair<String, E>> vars;
do {
vars = variables.generate(random, count);
} while (vars.stream().map(Pair::first).anyMatch(forbidden::contains));
return vars;
}
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public static final class Builder<C> {
private final ExtendedRandom random;
private final Supplier<C> constant;
private final List<Named<Integer>> ops = new ArrayList<>();
private final Set<String> forbidden = new HashSet<>();
private Builder(final ExtendedRandom random, final Supplier<C> constant) {
this.random = random;
this.constant = constant;
}
public void add(final String name, final int arity) {
ops.add(Named.of(name, arity));
forbidden.add(name);
}
public <E> Generator<C, E> build(
final ExpressionKind.Variables<E> variables,
final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests
) {
return new Generator<>(constant, ops, variables, forbidden, random, basicTests);
}
}
}

View File

@@ -0,0 +1,106 @@
package expression.common;
import java.util.function.Function;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public abstract class Node<T> {
private Node() {
}
public abstract <R> R get(Const<T, R> con, Nullary<R> nul, Unary<Node<T>, R> un, Binary<Node<T>, R> bin);
public abstract <R> R cata(Const<T, R> con, Nullary<R> nul, Unary<R, R> un, Binary<R, R> bin);
public final String toPolish() {
return cata(
T::toString,
name -> name,
(name, priority, a) -> a + " " + name + ":1",
(name, a1, a2) -> a1 + " " + a2 + " " + name + ":2"
);
}
@Override
public final String toString() {
return cata(
T::toString,
name -> name,
(name, priority, a) -> name.equals("[") ? "[" + a + "]" :
(priority & 1) == 1 ? "(" + name + " " + a + ")" : "(" + a + " " + name + ")",
(name, a1, a2) -> "(" + a1 + " " + name + " " + a2 + ")"
);
}
public static <T> Node<T> constant(final T value) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return con.apply(value);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return con.apply(value);
}
};
}
public static <T> Node<T> op(final String name) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return nul.apply(name);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return nul.apply(name);
}
};
}
public static <T> Node<T> op(final String name, final int priority, final Node<T> arg) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return un.apply(name, priority, arg);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return un.apply(name, priority, arg.cata(con, nul, un, bin));
}
};
}
public static <T> Node<T> op(final String name, final Node<T> arg1, final Node<T> arg2) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return bin.apply(name, arg1, arg2);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return bin.apply(name, arg1.cata(con, nul, un, bin), arg2.cata(con, nul, un, bin));
}
};
}
@FunctionalInterface
public interface Const<T, R> extends Function<T, R> {}
@FunctionalInterface
public interface Nullary<R> extends Function<String, R> {}
@FunctionalInterface
public interface Unary<T, R> {
R apply(String name, int priority, T arg);
}
@FunctionalInterface
public interface Binary<T, R> {
R apply(String name, T arg1, T arg2);
}
}

View File

@@ -0,0 +1,96 @@
package expression.common;
import base.ExtendedRandom;
import java.util.List;
import java.util.Map;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class NodeRenderer<C> {
public static final String PAREN = "[";
public static final List<Paren> DEFAULT_PARENS = List.of(paren("(", ")"));
public static final Mode MINI_MODE = Mode.SIMPLE_MINI; // Replace by TRUE_MINI for some challenge;
public static final Settings FULL = Mode.FULL.settings(0);
public static final Settings FULL_EXTRA = Mode.FULL.settings(Integer.MAX_VALUE / 4);
public static final Settings SAME = Mode.SAME.settings(0);
public static final Settings MINI = MINI_MODE.settings(0);
public static final Settings TRUE_MINI = Mode.TRUE_MINI.settings(0);
private final Renderer<C, Settings, Node<C>> renderer;
private final Map<String, String> brackets;
private final ExtendedRandom random;
public NodeRenderer(
final Renderer<C, Settings, Node<C>> renderer,
final Map<String, String> brackets,
final ExtendedRandom random
) {
this.renderer = renderer;
this.brackets = Map.copyOf(brackets);
this.random = random;
}
public static <C> Node<C> paren(final boolean condition, final Node<C> node) {
return condition ? Node.op(PAREN, 1, node) : node;
}
public static Paren paren(final String open, final String close) {
return new Paren(open, close);
}
public Node<C> renderToNode(final Settings settings, final Expr<C, ?> expr) {
final Expr<C, Node<C>> convert = expr.convert((name, variable) -> Node.op(name));
return renderer.render(convert, settings);
}
public String render(final Node<C> node, final List<Paren> parens) {
return node.cata(
String::valueOf,
name -> name,
(name, priority, arg) ->
name == PAREN ? random.randomItem(parens).apply(arg) :
priority == Integer.MAX_VALUE ? name + arg + brackets.get(name) :
(priority & 1) == 1 ? name + arg :
arg + name,
(name, a, b) -> a + " " + name + " " + b
);
}
public String render(final Expr<C, ?> expr, final Settings settings) {
return render(renderToNode(settings, expr), settings.parens());
}
public enum Mode {
FULL, SAME, TRUE_MINI, SIMPLE_MINI;
public Settings settings(final int limit) {
return new Settings(this, limit);
}
}
public record Paren(String open, String close) {
String apply(final String expression) {
return open() + expression + close();
}
}
public record Settings(Mode mode, int limit, List<Paren> parens) {
public Settings(final Mode mode, final int limit) {
this(mode, limit, DEFAULT_PARENS);
}
public <C> Node<C> extra(Node<C> node, final ExtendedRandom random) {
while (random.nextInt(Integer.MAX_VALUE) < limit) {
node = paren(true, node);
}
return node;
}
public Settings withParens(final List<Paren> parens) {
return this.parens.equals(parens) ? this : new Settings(mode, limit, List.copyOf(parens));
}
}
}

View File

@@ -0,0 +1,145 @@
package expression.common;
import base.ExtendedRandom;
import base.Functional;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
public class NodeRendererBuilder<C> {
private final Renderer.Builder<C, NodeRenderer.Settings, Node<C>> nodeRenderer = Renderer.builder(Node::constant);
private final Map<String, Priority> priorities = new HashMap<>();
private final Map<String, String> brackets = new HashMap<>();
private final ExtendedRandom random;
public NodeRendererBuilder(final ExtendedRandom random) {
this.random = random;
nodeRenderer.unary(NodeRenderer.PAREN, (mode, arg) -> NodeRenderer.paren(true, arg));
}
public void unary(final String name, final int priority) {
final String space = name.equals("-") || Character.isLetter(name.charAt(0)) ? " " : "";
nodeRenderer.unary(
name,
(settings, arg) -> settings.extra(Node.op(name, priority, inner(settings, priority, arg, space)), random)
);
}
public void unary(final String left, final String right) {
brackets.put(left, right);
nodeRenderer.unary(
left,
(settings, arg) -> settings.extra(Node.op(left, Integer.MAX_VALUE, arg), random)
);
}
private Node<C> inner(final NodeRenderer.Settings settings, final int priority, final Node<C> arg, final String space) {
if (settings.mode() == NodeRenderer.Mode.FULL) {
return NodeRenderer.paren(true, arg);
} else {
final String op = arg.get(
c -> space,
n -> space,
(n, p, a) ->
priority > unaryPriority(arg) ? NodeRenderer.PAREN :
NodeRenderer.PAREN.equals(n) ? "" :
space,
(n, a, b) -> NodeRenderer.PAREN
);
return op.isEmpty() ? arg : Node.op(op, Priority.MAX.priority | 1, arg);
}
}
private static <C> Integer unaryPriority(final Node<C> node) {
return node.get(c -> Integer.MAX_VALUE, n -> Integer.MAX_VALUE, (n, p, a) -> p, (n, a, b) -> Integer.MIN_VALUE);
}
public void binary(final String name, final int priority) {
final Priority mp = new Priority(name, priority);
priorities.put(name, mp);
nodeRenderer.binary(name, (settings, l, r) -> settings.extra(process(settings, mp, l, r), random));
}
private Node<C> process(final NodeRenderer.Settings settings, final Priority mp, final Node<C> l, final Node<C> r) {
if (settings.mode() == NodeRenderer.Mode.FULL) {
return NodeRenderer.paren(true, op(mp, l, r));
}
final Priority lp = priority(l);
final Priority rp = priority(r);
final int rc = rp.compareLevels(mp);
// :NOTE: Especially ugly code, do not replicate
final boolean advanced = settings.mode() == NodeRenderer.Mode.SAME
|| mp.has(2)
|| mp.has(1) && (mp != rp || (settings.mode() == NodeRenderer.Mode.TRUE_MINI && hasOther(r, rp)));
final Node<C> al = NodeRenderer.paren(lp.compareLevels(mp) < 0, l);
if (rc == 0 && !advanced) {
return get(r, null, (n, a, b) -> rp.op(mp.op(al, a), b));
} else {
return mp.op(al, NodeRenderer.paren(rc == 0 && advanced || rc < 0, r));
}
}
private boolean hasOther(final Node<C> node, final Priority priority) {
return get(node, () -> false, (name, l, r) -> {
final Priority p = Functional.get(priorities, name);
if (p.compareLevels(priority) != 0) {
return false;
}
return p != priority || hasOther(l, priority);
});
}
private Node<C> op(final Priority mp, final Node<C> l, final Node<C> r) {
return mp.op(l, r);
}
private Priority priority(final Node<C> node) {
return get(node, () -> Priority.MAX, (n, a, b) -> Functional.get(priorities, n));
}
private <R> R get(final Node<C> node, final Supplier<R> common, final Node.Binary<Node<C>, R> binary) {
return node.get(
c -> common.get(),
n -> common.get(),
(n, p, a) -> common.get(),
binary
);
}
public NodeRenderer<C> build() {
return new NodeRenderer<>(nodeRenderer.build(), brackets, random);
}
// :NOTE: Especially ugly bit-fiddling, do not replicate
private record Priority(String op, int priority) {
private static final int Q = 3;
private static final Priority MAX = new Priority("MAX", Integer.MAX_VALUE - Q);
private int compareLevels(final Priority that) {
return (priority | Q) - (that.priority | Q);
}
@Override
public String toString() {
return String.format("Priority(%s, %d, %d)", op, priority | Q, priority & Q);
}
public <C> Node<C> op(final Node<C> l, final Node<C> r) {
return Node.op(op, l, r);
}
private boolean has(final int value) {
return (priority & Q) == value;
}
}
}

View File

@@ -0,0 +1,60 @@
package expression.common;
import base.Either;
import java.util.function.LongUnaryOperator;
import java.util.function.Supplier;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class Reason {
public static final Reason OVERFLOW = new Reason("Overflow");
public static final Reason DBZ = new Reason("Division by zero");
private final String description;
public Reason(final String description) {
this.description = description;
}
public static <T> Either<Reason, T> eval(final Supplier<T> action) {
try {
return Either.right(action.get());
} catch (final ReasonException e) {
return Either.left(e.reason);
}
}
public static int overflow(final long value) {
return value < Integer.MIN_VALUE || Integer.MAX_VALUE < value
? OVERFLOW.error()
: (int) value;
}
public <T> T error() {
throw new ReasonException(this);
}
public LongUnaryOperator less(final long limit, final LongUnaryOperator op) {
return a -> a < limit ? error() : op.applyAsLong(a);
}
public LongUnaryOperator greater(final int limit, final LongUnaryOperator op) {
return a -> a > limit ? error() : op.applyAsLong(a);
}
private static class ReasonException extends RuntimeException {
private final Reason reason;
public ReasonException(final Reason reason) {
super(reason.description);
this.reason = reason;
}
}
@Override
public String toString() {
return String.format("Reason(%s)", description);
}
}

View File

@@ -0,0 +1,60 @@
package expression.common;
import base.Functional;
import base.Pair;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public interface Renderer<C, S, R> {
static <C, S, R> Builder<C, S, R> builder(final Node.Const<C, R> constant) {
return new Builder<>(constant);
}
R render(final Expr<C, R> expr, final S settings);
@FunctionalInterface
interface UnaryOperator<S, R> {
R apply(S settings, R arg);
}
@FunctionalInterface
interface BinaryOperator<S, R> {
R apply(S settings, R arg1, R arg2);
}
final class Builder<C, S, R> {
private final Node.Const<C, R> constant;
private final Map<String, UnaryOperator<S, R>> unary = new HashMap<>();
private final Map<String, BinaryOperator<S, R>> binary = new HashMap<>();
private Builder(final Node.Const<C, R> constant) {
this.constant = constant;
}
public void unary(final String name, final UnaryOperator<S, R> op) {
unary.put(name, op);
}
public void binary(final String name, final BinaryOperator<S, R> op) {
binary.put(name, op);
}
public Renderer<C, S, R> build() {
return (expr, settings) -> {
final Map<String, R> vars = expr.variables().stream()
.collect(Collectors.toMap(Pair::first, Pair::second));
return expr.node().cata(
constant,
name -> Functional.get(vars, name),
(name, p, arg) -> Functional.get(unary, name).apply(settings, arg),
(name, arg1, arg2) -> Functional.get(binary, name).apply(settings, arg1, arg2)
);
};
}
}
}

View File

@@ -0,0 +1,56 @@
package expression.common;
import base.Pair;
import base.TestCounter;
import expression.ToMiniString;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
public class TestGenerator<C, E extends ToMiniString> {
private final Generator<C, E> generator;
private final NodeRenderer<C> renderer;
public TestGenerator(final Generator<C, E> generator, final NodeRenderer<C> renderer) {
this.generator = generator;
this.renderer = renderer;
}
public void testBasic(final Consumer<Test<C, E>> test) {
generator.testBasic(consumer(test));
}
public void testRandom(final TestCounter counter, final int denominator, final Consumer<Test<C, E>> test) {
generator.testRandom(counter, denominator, consumer(test));
}
private Consumer<Expr<C, E>> consumer(final Consumer<TestGenerator.Test<C, E>> consumer) {
return expr -> consumer.accept(new TestGenerator.Test<>(expr, renderer));
}
public List<Pair<String, E>> variables(final int count) {
return generator.variables(count);
}
public String render(final Expr<C, ?> expr, final NodeRenderer.Settings settings) {
return renderer.render(expr, settings);
}
public static class Test<C, E> {
public final Expr<C, E> expr;
private final Map<NodeRenderer.Settings, String> rendered = new HashMap<>();
private final NodeRenderer<C> renderer;
public Test(final Expr<C, E> expr, final NodeRenderer<C> renderer) {
this.expr = expr;
this.renderer = renderer;
}
public String render(final NodeRenderer.Settings settings) {
return rendered.computeIfAbsent(settings, s -> renderer.render(expr, s));
}
}
}

View File

@@ -0,0 +1,145 @@
package expression.common;
import base.ExtendedRandom;
import base.Functional;
import expression.ToMiniString;
import expression.common.ExpressionKind.Variables;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class TestGeneratorBuilder<C> {
private final ExtendedRandom random;
private final Generator.Builder<C> generator;
private final NodeRendererBuilder<C> renderer;
private final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests = new ArrayList<>();
private final List<Node<C>> consts;
private final boolean verbose;
public TestGeneratorBuilder(
final ExtendedRandom random,
final Supplier<C> constant,
final List<C> constants,
final boolean verbose
) {
this.random = random;
this.verbose = verbose;
generator = Generator.builder(constant, random);
renderer = new NodeRendererBuilder<>(random);
consts = Functional.map(constants, Node::constant);
basicTests.add(vars -> consts.stream());
basicTests.add(List::stream);
}
private Node<C> c() {
return random.randomItem(consts);
}
private Node<C> v(final List<Node<C>> variables) {
return random.randomItem(variables);
}
private static <C> Node<C> f(final String name, final int priority, final Node<C> arg) {
return Node.op(name, priority, arg);
}
private static <C> Node<C> f(final String left, final Node<C> arg) {
return Node.op(left, Integer.MAX_VALUE, arg);
}
private static <C> Node<C> f(final String name, final Node<C> arg1, final Node<C> arg2) {
return Node.op(name, arg1, arg2);
}
@SafeVarargs
private void basicTests(final Function<List<Node<C>>, Node<C>>... tests) {
Arrays.stream(tests).map(test -> test.andThen(Stream::of)).forEachOrdered(basicTests::add);
}
public void unary(final String name, final int priority) {
generator.add(name, (priority & 1) * 2 - 1);
renderer.unary(name, priority);
if (verbose) {
basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream()).map(a -> f(name, priority, a)));
} else {
basicTests(vars -> f(name, priority, c()), vars -> f(name, priority, v(vars)));
}
final Function<List<Node<C>>, Node<C>> p1 = vars -> f(name, priority, f(name, priority, f("+", v(vars), c())));
final Function<List<Node<C>>, Node<C>> p2 = vars -> f("*", v(vars), f("*", v(vars), f(name, priority, c())));
basicTests(
vars -> f(name, priority, f("+", v(vars), v(vars))),
vars -> f(name, priority, f(name, priority, v(vars))),
vars -> f(name, priority, f("/", f(name, priority, v(vars)), f("+", v(vars), v(vars)))),
p1,
p2,
vars -> f("+", p1.apply(vars), p2.apply(vars))
);
}
public void unary(final String left, final String right) {
generator.add(left, 1);
renderer.unary(left, right);
if (verbose) {
basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream()).map(a -> f(left, a)));
} else {
basicTests(vars -> f(left, c()), vars -> f(left, v(vars)));
}
final Function<List<Node<C>>, Node<C>> p1 = vars -> f(left, f(left, f("+", v(vars), c())));
final Function<List<Node<C>>, Node<C>> p2 = vars -> f("*", v(vars), f("*", v(vars), f(left, c())));
basicTests(
vars -> f(left, f("+", v(vars), v(vars))),
vars -> f(left, f(left, v(vars))),
vars -> f(left, f("/", f(left, v(vars)), f("+", v(vars), v(vars)))),
p1,
p2,
vars -> f("+", p1.apply(vars), p2.apply(vars))
);
}
public void binary(final String name, final int priority) {
generator.add(name, 2);
renderer.binary(name, priority);
if (verbose) {
basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream().limit(3))
.flatMap(a -> consts.stream().map(b -> f(name, a, b))));
} else {
basicTests(
vars -> f(name, c(), c()),
vars -> f(name, v(vars), c()),
vars -> f(name, c(), v(vars)),
vars -> f(name, v(vars), v(vars))
);
}
final Function<List<Node<C>>, Node<C>> p1 = vars -> f(name, f(name, f("+", v(vars), c()), v(vars)), v(vars));
final Function<List<Node<C>>, Node<C>> p2 = vars -> f("*", v(vars), f("*", v(vars), f(name, c(), v(vars))));
basicTests(
vars -> f(name, f(name, v(vars), v(vars)), v(vars)),
vars -> f(name, v(vars), f(name, v(vars), v(vars))),
vars -> f(name, f(name, v(vars), v(vars)), f(name, v(vars), v(vars))),
vars -> f(name, f("-", f(name, v(vars), v(vars)), c()), f("+", v(vars), v(vars))),
p1,
p2,
vars -> f("+", p1.apply(vars), p2.apply(vars))
);
}
public <E extends ToMiniString> TestGenerator<C,E> build(final Variables<E> variables) {
return new TestGenerator<>(generator.build(variables, basicTests), renderer.build());
}
}

View File

@@ -0,0 +1,51 @@
package expression.common;
import base.Asserts;
import base.ExtendedRandom;
import expression.Const;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;
import java.util.function.IntFunction;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class Type<C> {
private final IntFunction<C> fromInt;
private final Function<ExtendedRandom, C> random;
private final Function<C, Const> constant;
public Type(final IntFunction<C> fromInt, final Function<ExtendedRandom, C> random, final Class<?> type) {
this.fromInt = fromInt;
this.random = random;
try {
final MethodHandle constructor = MethodHandles.publicLookup()
.findConstructor(Const.class, MethodType.methodType(void.class, type));
constant = c -> {
try {
return (Const) constructor.invoke(c);
} catch (final Throwable e) {
throw Asserts.error("Cannot create new Const(%s): %s", c, e);
}
};
} catch (final IllegalAccessException | NoSuchMethodException e) {
throw Asserts.error("Cannot find constructor Const(%s): %s", type, e);
}
}
public Const constant(final C value) {
return constant.apply(value);
}
public C fromInt(final int value) {
return fromInt.apply(value);
}
public C randomValue(final ExtendedRandom random) {
return this.random.apply(random);
}
}

View File

@@ -0,0 +1,7 @@
/**
* Expressions generators for expression-based homeworks
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package expression.common;