first commit
This commit is contained in:
25
java/expression/generic/ArithmeticType.java
Normal file
25
java/expression/generic/ArithmeticType.java
Normal 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");
|
||||
}
|
||||
}
|
||||
61
java/expression/generic/BigIntegerType.java
Normal file
61
java/expression/generic/BigIntegerType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
56
java/expression/generic/DoubleType.java
Normal file
56
java/expression/generic/DoubleType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
56
java/expression/generic/FloatType.java
Normal file
56
java/expression/generic/FloatType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
10
java/expression/generic/GenericExpr.java
Normal file
10
java/expression/generic/GenericExpr.java
Normal 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);
|
||||
}
|
||||
239
java/expression/generic/GenericParser.java
Normal file
239
java/expression/generic/GenericParser.java
Normal 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++;
|
||||
}
|
||||
}
|
||||
135
java/expression/generic/GenericTabulator.java
Normal file
135
java/expression/generic/GenericTabulator.java
Normal 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;
|
||||
}
|
||||
}
|
||||
184
java/expression/generic/GenericTest.java
Normal file
184
java/expression/generic/GenericTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
366
java/expression/generic/GenericTester.java
Normal file
366
java/expression/generic/GenericTester.java
Normal 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);
|
||||
}
|
||||
}
|
||||
71
java/expression/generic/IntCheckedType.java
Normal file
71
java/expression/generic/IntCheckedType.java
Normal 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");
|
||||
}
|
||||
}
|
||||
61
java/expression/generic/IntTruncType.java
Normal file
61
java/expression/generic/IntTruncType.java
Normal 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));
|
||||
}
|
||||
}
|
||||
57
java/expression/generic/IntUncheckedType.java
Normal file
57
java/expression/generic/IntUncheckedType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
57
java/expression/generic/ShortType.java
Normal file
57
java/expression/generic/ShortType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
18
java/expression/generic/Tabulator.java
Normal file
18
java/expression/generic/Tabulator.java
Normal 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;
|
||||
}
|
||||
8
java/expression/generic/package-info.java
Normal file
8
java/expression/generic/package-info.java
Normal 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;
|
||||
Reference in New Issue
Block a user