Upload files to "java/expression/common"

This commit is contained in:
2026-04-13 10:53:13 +03:00
parent bcf1fe50a8
commit e82fdac1c7
5 changed files with 663 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
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,108 @@
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,233 @@
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,173 @@
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,114 @@
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));
}
}
}