Upload files to "java/expression/common"
This commit is contained in:
35
java/expression/common/Expr.java
Normal file
35
java/expression/common/Expr.java
Normal 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);
|
||||
}
|
||||
}
|
||||
108
java/expression/common/ExpressionKind.java
Normal file
108
java/expression/common/ExpressionKind.java
Normal 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;
|
||||
}
|
||||
}
|
||||
233
java/expression/common/Generator.java
Normal file
233
java/expression/common/Generator.java
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
173
java/expression/common/Node.java
Normal file
173
java/expression/common/Node.java
Normal 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);
|
||||
}
|
||||
}
|
||||
114
java/expression/common/NodeRenderer.java
Normal file
114
java/expression/common/NodeRenderer.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user