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,25 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public interface ArithmeticType<T> {
T fromInt(int value);
T add(T a, T b);
T subtract(T a, T b);
T multiply(T a, T b);
T divide(T a, T b);
T negate(T a);
default T count(T a) {
throw new UnsupportedOperationException("count");
}
default T min(T a, T b) {
throw new UnsupportedOperationException("min");
}
default T max(T a, T b) {
throw new UnsupportedOperationException("max");
}
}

View File

@@ -0,0 +1,61 @@
package expression.generic;
import java.math.BigInteger;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class BigIntegerType implements ArithmeticType<BigInteger> {
public static final BigIntegerType INSTANCE = new BigIntegerType();
private BigIntegerType() {}
@Override
public BigInteger fromInt(int value) {
return BigInteger.valueOf(value);
}
@Override
public BigInteger add(BigInteger a, BigInteger b) {
return a.add(b);
}
@Override
public BigInteger subtract(BigInteger a, BigInteger b) {
return a.subtract(b);
}
@Override
public BigInteger multiply(BigInteger a, BigInteger b) {
return a.multiply(b);
}
@Override
public BigInteger divide(BigInteger a, BigInteger b) {
if (b.equals(BigInteger.ZERO)) throw new ArithmeticException(
"Division by zero"
);
return a.divide(b);
}
@Override
public BigInteger negate(BigInteger a) {
return a.negate();
}
@Override
public BigInteger count(BigInteger a) {
return BigInteger.valueOf(a.bitCount());
}
@Override
public BigInteger min(BigInteger a, BigInteger b) {
return a.min(b);
}
@Override
public BigInteger max(BigInteger a, BigInteger b) {
return a.max(b);
}
}

View File

@@ -0,0 +1,56 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class DoubleType implements ArithmeticType<Double> {
public static final DoubleType INSTANCE = new DoubleType();
private DoubleType() {}
@Override
public Double fromInt(int value) {
return (double) value;
}
@Override
public Double add(Double a, Double b) {
return a + b;
}
@Override
public Double subtract(Double a, Double b) {
return a - b;
}
@Override
public Double multiply(Double a, Double b) {
return a * b;
}
@Override
public Double divide(Double a, Double b) {
return a / b;
}
@Override
public Double negate(Double a) {
return -a;
}
@Override
public Double count(Double a) {
return (double) Long.bitCount(Double.doubleToLongBits(a));
}
@Override
public Double min(Double a, Double b) {
return Math.min(a, b);
}
@Override
public Double max(Double a, Double b) {
return Math.max(a, b);
}
}

View File

@@ -0,0 +1,56 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class FloatType implements ArithmeticType<Float> {
public static final FloatType INSTANCE = new FloatType();
private FloatType() {}
@Override
public Float fromInt(int value) {
return (float) value;
}
@Override
public Float add(Float a, Float b) {
return a + b;
}
@Override
public Float subtract(Float a, Float b) {
return a - b;
}
@Override
public Float multiply(Float a, Float b) {
return a * b;
}
@Override
public Float divide(Float a, Float b) {
return a / b;
}
@Override
public Float negate(Float a) {
return -a;
}
@Override
public Float count(Float a) {
return (float) Integer.bitCount(Float.floatToIntBits(a));
}
@Override
public Float min(Float a, Float b) {
return Math.min(a, b);
}
@Override
public Float max(Float a, Float b) {
return Math.max(a, b);
}
}

View File

@@ -0,0 +1,10 @@
package expression.generic;
import java.util.List;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public interface GenericExpr<T> {
T evaluate(ArithmeticType<T> type, List<T> vars);
}

View File

@@ -0,0 +1,239 @@
package expression.generic;
import java.util.List;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class GenericParser {
private static final List<String> VARIABLES = List.of("x", "y", "z");
private String src;
private int pos;
public <T> GenericExpr<T> parse(String expression) {
this.src = expression;
this.pos = 0;
GenericExpr<T> result = parseMinMax();
skipWhitespace();
if (pos < src.length()) {
throw new IllegalArgumentException(
"Unexpected character '" +
src.charAt(pos) +
"' at position " +
pos
);
}
return result;
}
private <T> GenericExpr<T> parseMinMax() {
GenericExpr<T> left = parseAddSub();
while (true) {
skipWhitespace();
if (matchKeyword("min")) {
GenericExpr<T> right = parseAddSub();
GenericExpr<T> l = left;
left = (type, vars) ->
type.min(
l.evaluate(type, vars),
right.evaluate(type, vars)
);
} else if (matchKeyword("max")) {
GenericExpr<T> right = parseAddSub();
GenericExpr<T> l = left;
left = (type, vars) ->
type.max(
l.evaluate(type, vars),
right.evaluate(type, vars)
);
} else {
break;
}
}
return left;
}
private <T> GenericExpr<T> parseAddSub() {
GenericExpr<T> left = parseMulDiv();
while (true) {
skipWhitespace();
if (pos < src.length() && src.charAt(pos) == '+') {
pos++;
GenericExpr<T> right = parseMulDiv();
GenericExpr<T> l = left;
left = (type, vars) ->
type.add(
l.evaluate(type, vars),
right.evaluate(type, vars)
);
} else if (
pos < src.length() && src.charAt(pos) == '-' && !nextIsKeyword()
) {
pos++;
GenericExpr<T> right = parseMulDiv();
GenericExpr<T> l = left;
left = (type, vars) ->
type.subtract(
l.evaluate(type, vars),
right.evaluate(type, vars)
);
} else {
break;
}
}
return left;
}
private <T> GenericExpr<T> parseMulDiv() {
GenericExpr<T> left = parseUnary();
while (true) {
skipWhitespace();
if (pos < src.length() && src.charAt(pos) == '*') {
pos++;
GenericExpr<T> right = parseUnary();
GenericExpr<T> l = left;
left = (type, vars) ->
type.multiply(
l.evaluate(type, vars),
right.evaluate(type, vars)
);
} else if (pos < src.length() && src.charAt(pos) == '/') {
pos++;
GenericExpr<T> right = parseUnary();
GenericExpr<T> l = left;
left = (type, vars) ->
type.divide(
l.evaluate(type, vars),
right.evaluate(type, vars)
);
} else {
break;
}
}
return left;
}
private <T> GenericExpr<T> parseUnary() {
skipWhitespace();
if (pos >= src.length()) {
throw new IllegalArgumentException(
"Unexpected end of expression at position " + pos
);
}
if (src.charAt(pos) == '-') {
pos++;
skipWhitespace();
if (pos < src.length() && Character.isDigit(src.charAt(pos))) {
return parseNumber(true);
}
GenericExpr<T> operand = parseUnary();
return (type, vars) -> type.negate(operand.evaluate(type, vars));
}
if (matchKeyword("count")) {
GenericExpr<T> operand = parseUnary();
return (type, vars) -> type.count(operand.evaluate(type, vars));
}
return parsePrimary();
}
private <T> GenericExpr<T> parsePrimary() {
skipWhitespace();
if (pos >= src.length()) {
throw new IllegalArgumentException("Unexpected end of expression");
}
char c = src.charAt(pos);
if (c == '(') {
pos++;
GenericExpr<T> inner = parseMinMax();
skipWhitespace();
expect();
return inner;
}
if (Character.isDigit(c)) {
return parseNumber(false);
}
if (Character.isLetter(c)) {
int start = pos;
while (
pos < src.length() && Character.isLetterOrDigit(src.charAt(pos))
) {
pos++;
}
String name = src.substring(start, pos);
int idx = VARIABLES.indexOf(name);
if (idx >= 0) {
return (type, vars) -> vars.get(idx);
}
throw new IllegalArgumentException(
"Unknown variable '" + name + "' at position " + start
);
}
throw new IllegalArgumentException(
"Unexpected character '" + c + "' at position " + pos
);
}
private <T> GenericExpr<T> parseNumber(boolean negative) {
int start = pos;
while (pos < src.length() && Character.isDigit(src.charAt(pos))) {
pos++;
}
if (start == pos) {
throw new IllegalArgumentException(
"Expected digit at position " + pos
);
}
String numStr = src.substring(start, pos);
long val = Long.parseLong(numStr);
if (negative) val = -val;
if (val < Integer.MIN_VALUE || val > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Integer overflow: " + val);
}
int intVal = (int) val;
return (type, vars) -> type.fromInt(intVal);
}
private boolean matchKeyword(String keyword) {
int i = pos;
for (char ch : keyword.toCharArray()) {
if (i >= src.length() || src.charAt(i) != ch) return false;
i++;
}
if (
i < src.length() && Character.isLetterOrDigit(src.charAt(i))
) return false;
pos = i;
return true;
}
private boolean nextIsKeyword() {
return false;
}
private void skipWhitespace() {
while (pos < src.length() && Character.isWhitespace(src.charAt(pos))) {
pos++;
}
}
private void expect() {
if (pos >= src.length() || src.charAt(pos) != ')') {
throw new IllegalArgumentException(
"Expected '" +
')' +
"' at position " +
pos +
(pos < src.length()
? ", got '" + src.charAt(pos) + "'"
: ", got end of input")
);
}
pos++;
}
}

View File

@@ -0,0 +1,135 @@
package expression.generic;
import java.math.BigInteger;
import java.util.List;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class GenericTabulator implements Tabulator {
@Override
public Object[][][] tabulate(
String mode,
String expression,
int x1,
int x2,
int y1,
int y2,
int z1,
int z2
) throws Exception {
return switch (mode) {
case "i" -> compute(
IntCheckedType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
case "d" -> compute(
DoubleType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
case "bi" -> compute(
BigIntegerType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
case "u" -> compute(
IntUncheckedType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
case "s" -> compute(
ShortType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
case "f" -> compute(
FloatType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
case "t", "it" -> compute(
IntTruncType.INSTANCE,
expression,
x1,
x2,
y1,
y2,
z1,
z2
);
default -> throw new IllegalArgumentException(
"Unknown mode: " + mode
);
};
}
private <T> Object[][][] compute(
ArithmeticType<T> type,
String expression,
int x1,
int x2,
int y1,
int y2,
int z1,
int z2
) {
GenericExpr<T> expr = new GenericParser().parse(expression);
int xLen = x2 - x1 + 1;
int yLen = y2 - y1 + 1;
int zLen = z2 - z1 + 1;
Object[][][] result = new Object[xLen][yLen][zLen];
for (int xi = 0; xi < xLen; xi++) {
for (int yi = 0; yi < yLen; yi++) {
for (int zi = 0; zi < zLen; zi++) {
T x = type.fromInt(x1 + xi);
T y = type.fromInt(y1 + yi);
T z = type.fromInt(z1 + zi);
try {
result[xi][yi][zi] = expr.evaluate(
type,
List.of(x, y, z)
);
} catch (Exception e) {
result[xi][yi][zi] = null;
}
}
}
}
return result;
}
}

View File

@@ -0,0 +1,184 @@
package expression.generic;
import base.Selector;
import java.math.BigInteger;
import java.util.function.*;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class GenericTest {
// === Base
private static final Consumer<GenericTester> ADD = binary("+", 200);
private static final Consumer<GenericTester> SUBTRACT = binary("-", -200);
private static final Consumer<GenericTester> MULTIPLY = binary("*", 301);
private static final Consumer<GenericTester> DIVIDE = binary("/", -300);
private static final Consumer<GenericTester> NEGATE = unary("-");
// === Cmm
private static final Consumer<GenericTester> COUNT = unary("count");
private static final Consumer<GenericTester> MIN = binary("min", 50);
private static final Consumer<GenericTester> MAX = binary("max", 50);
// === Checked integers
private static Integer i(final long v) {
if (v != (int) v) {
throw new ArithmeticException("Overflow");
}
return (int) v;
}
private static final GenericTester.Mode.Builder<Integer> INTEGER_CHECKED = mode("i", c -> c)
.binary("+", (a, b) -> i(a + (long) b))
.binary("-", (a, b) -> i(a - (long) b))
.binary("*", (a, b) -> i(a * (long) b))
.binary("/", (a, b) -> i(a / (long) b))
.unary("-", a -> i(- (long) a))
.unary("count", Integer::bitCount)
.binary("min", Math::min)
.binary("max", Math::max)
;
// === Doubles
private static final GenericTester.Mode.Builder<Double> DOUBLE = mode("d", c -> (double) c)
.binary("+", Double::sum)
.binary("-", (a, b) -> a - b)
.binary("*", (a, b) -> a * b)
.binary("/", (a, b) -> a / b)
.unary("-", a -> -a)
.unary("count", a -> (double) Long.bitCount(Double.doubleToLongBits(a)))
.binary("min", Math::min)
.binary("max", Math::max)
;
// === BigIntegers
private static final GenericTester.Mode.Builder<BigInteger> BIG_INTEGER = mode("bi", BigInteger::valueOf)
.binary("+", BigInteger::add)
.binary("-", BigInteger::subtract)
.binary("*", BigInteger::multiply)
.binary("/", BigInteger::divide)
.unary("-", BigInteger::negate)
.unary("count", a -> BigInteger.valueOf(a.bitCount()))
.binary("min", BigInteger::min)
.binary("max", BigInteger::max)
;
// === Unchecked integers
private static final GenericTester.Mode.Builder<Integer> INTEGER_UNCHECKED = mode("u", c -> c)
.binary("+", Integer::sum)
.binary("-", (a, b) -> a - b)
.binary("*", (a, b) -> a * b)
.binary("/", (a, b) -> a / b)
.unary("-", a -> -a)
.unary("count", Integer::bitCount)
.binary("min", Math::min)
.binary("max", Math::max)
;
// === Short
private static short s(final int x) {
return (short) x;
}
private static BinaryOperator<Short> s(final IntBinaryOperator op) {
return (a, b) -> s(op.applyAsInt(a, b));
}
private static final GenericTester.Mode.Builder<Short> SHORT = mode("s", c -> (short) c, c -> (short) c)
.binary("+", s(Integer::sum))
.binary("-", s((a, b) -> a - b))
.binary("*", s((a, b) -> a * b))
.binary("/", s((a, b) -> a / b))
.unary("-", a -> s(-a))
.unary("count", a -> s(Integer.bitCount(a & 0xffff)))
.binary("min", s(Math::min))
.binary("max", s(Math::max))
;
// == Floats
private static BinaryOperator<Float> f(final IntPredicate p) {
return (a, b) -> p.test(a.compareTo(b)) ? 1.0f : 0.0f;
}
private static final GenericTester.Mode.Builder<Float> FLOAT = mode("f", c -> (float) c)
.binary("+", Float::sum)
.binary("-", (a, b) -> a - b)
.binary("*", (a, b) -> a * b)
.binary("/", (a, b) -> a / b)
.unary("-", a -> -a)
.unary("count", a -> (float) Integer.bitCount(Float.floatToIntBits(a)))
.binary("min", Math::min)
.binary("max", Math::max)
;
// === Truncated integers
/* package-private */ static final int TRUNCATE = 10;
private static int it(final int v) {
return v / TRUNCATE * TRUNCATE;
}
private static final GenericTester.Mode.Builder<Integer> INTEGER_TRUNCATE = mode("it", GenericTest::it)
.binary("+", (a, b) -> it(a + b))
.binary("-", (a, b) -> it(a - b))
.binary("*", (a, b) -> it(a * b))
.binary("/", (a, b) -> it(a / b))
.unary("-", a -> it(-a))
.unary("count", a -> it(Integer.bitCount(a)))
.binary("min", Math::min)
.binary("max", Math::max)
;
// === Common
private GenericTest() {
}
/* package-private */ static Consumer<GenericTester> unary(final String name) {
return tester -> tester.unary(name, 1);
}
/* package-private */ static Consumer<GenericTester> binary(final String name, final int priority) {
return tester -> tester.binary(name, priority);
}
public static final Selector SELECTOR = Selector.composite(GenericTest.class, GenericTester::new, "easy", "hard")
.variant("Base", INTEGER_CHECKED, DOUBLE, BIG_INTEGER, ADD, SUBTRACT, MULTIPLY, DIVIDE, NEGATE)
.variant("3637", INTEGER_UNCHECKED, SHORT, FLOAT, COUNT, MIN, MAX)
.variant("3839", INTEGER_UNCHECKED, SHORT, INTEGER_TRUNCATE, COUNT, MIN, MAX)
.variant("3435", INTEGER_UNCHECKED, COUNT, MIN, MAX)
.variant("3233", INTEGER_UNCHECKED, SHORT, FLOAT)
.selector();
private static <T> GenericTester.Mode.Builder<T> mode(final String mode, final IntFunction<T> constant) {
return GenericTester.Mode.builder(mode, constant, IntUnaryOperator.identity());
}
private static <T> GenericTester.Mode.Builder<T> mode(final String mode, final IntFunction<T> constant, final IntUnaryOperator fixer) {
return GenericTester.Mode.builder(mode, constant, fixer);
}
public static void main(final String... args) {
SELECTOR.main(args);
}
}

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);
}
}

View File

@@ -0,0 +1,71 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class IntCheckedType implements ArithmeticType<Integer> {
public static final IntCheckedType INSTANCE = new IntCheckedType();
private IntCheckedType() {}
@Override
public Integer fromInt(int value) {
return value;
}
@Override
public Integer add(Integer a, Integer b) {
long result = (long) a + b;
checkOverflow(result);
return (int) result;
}
@Override
public Integer subtract(Integer a, Integer b) {
long result = (long) a - b;
checkOverflow(result);
return (int) result;
}
@Override
public Integer multiply(Integer a, Integer b) {
long result = (long) a * b;
checkOverflow(result);
return (int) result;
}
@Override
public Integer divide(Integer a, Integer b) {
if (b == 0) throw new ArithmeticException("Division by zero");
if (a == Integer.MIN_VALUE && b == -1) throw new ArithmeticException(
"Overflow"
);
return a / b;
}
@Override
public Integer negate(Integer a) {
if (a == Integer.MIN_VALUE) throw new ArithmeticException("Overflow");
return -a;
}
@Override
public Integer count(Integer a) {
return Integer.bitCount(a);
}
@Override
public Integer min(Integer a, Integer b) {
return Math.min(a, b);
}
@Override
public Integer max(Integer a, Integer b) {
return Math.max(a, b);
}
private static void checkOverflow(long result) {
if (result != (int) result) throw new ArithmeticException("Overflow");
}
}

View File

@@ -0,0 +1,61 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class IntTruncType implements ArithmeticType<Integer> {
public static final IntTruncType INSTANCE = new IntTruncType();
private IntTruncType() {}
private static int t(int v) {
return v - (v % 10);
}
@Override
public Integer fromInt(int value) {
return t(value);
}
@Override
public Integer add(Integer a, Integer b) {
return t(a + b);
}
@Override
public Integer subtract(Integer a, Integer b) {
return t(a - b);
}
@Override
public Integer multiply(Integer a, Integer b) {
return t(a * b);
}
@Override
public Integer divide(Integer a, Integer b) {
if (b == 0) throw new ArithmeticException("Division by zero");
return t(a / b);
}
@Override
public Integer negate(Integer a) {
return t(-a);
}
@Override
public Integer count(Integer a) {
return t(Integer.bitCount(a));
}
@Override
public Integer min(Integer a, Integer b) {
return t(Math.min(a, b));
}
@Override
public Integer max(Integer a, Integer b) {
return t(Math.max(a, b));
}
}

View File

@@ -0,0 +1,57 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class IntUncheckedType implements ArithmeticType<Integer> {
public static final IntUncheckedType INSTANCE = new IntUncheckedType();
private IntUncheckedType() {}
@Override
public Integer fromInt(int value) {
return value;
}
@Override
public Integer add(Integer a, Integer b) {
return a + b;
}
@Override
public Integer subtract(Integer a, Integer b) {
return a - b;
}
@Override
public Integer multiply(Integer a, Integer b) {
return a * b;
}
@Override
public Integer divide(Integer a, Integer b) {
if (b == 0) throw new ArithmeticException("Division by zero");
return a / b;
}
@Override
public Integer negate(Integer a) {
return -a;
}
@Override
public Integer count(Integer a) {
return Integer.bitCount(a);
}
@Override
public Integer min(Integer a, Integer b) {
return Math.min(a, b);
}
@Override
public Integer max(Integer a, Integer b) {
return Math.max(a, b);
}
}

View File

@@ -0,0 +1,57 @@
package expression.generic;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class ShortType implements ArithmeticType<Short> {
public static final ShortType INSTANCE = new ShortType();
private ShortType() {}
@Override
public Short fromInt(int value) {
return (short) value;
}
@Override
public Short add(Short a, Short b) {
return (short) (a + b);
}
@Override
public Short subtract(Short a, Short b) {
return (short) (a - b);
}
@Override
public Short multiply(Short a, Short b) {
return (short) (a * b);
}
@Override
public Short divide(Short a, Short b) {
if (b == 0) throw new ArithmeticException("Division by zero");
return (short) (a / b);
}
@Override
public Short negate(Short a) {
return (short) (-a);
}
@Override
public Short count(Short a) {
return (short) Integer.bitCount(a & 0xffff);
}
@Override
public Short min(Short a, Short b) {
return (short) Math.min(a, b);
}
@Override
public Short max(Short a, Short b) {
return (short) Math.max(a, b);
}
}

View File

@@ -0,0 +1,18 @@
package expression.generic;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
public interface Tabulator {
Object[][][] tabulate(
String mode,
String expression,
int x1,
int x2,
int y1,
int y2,
int z1,
int z2
) throws Exception;
}

View File

@@ -0,0 +1,8 @@
/**
* Tests for <a href="https://www.kgeorgiy.info/courses/paradigms/homeworks.html#expressions-generic">Generic Expressions</a> homework
* of <a href="https://www.kgeorgiy.info/courses/paradigms/">Paradigms of Programming</a> course.
*
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package expression.generic;