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