Files
prog-intro-2025/java/expression/parser/Operations.java
2026-01-29 23:20:12 +05:00

149 lines
5.7 KiB
Java

package expression.parser;
import expression.ToMiniString;
import expression.common.ExpressionKind;
import expression.common.Reason;
import java.math.BigInteger;
import java.util.function.*;
import java.util.stream.LongStream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class Operations {
// === Base
public static final Operation NEGATE = unary("-", 1, a -> -a);
@SuppressWarnings("Convert2MethodRef")
public static final Operation ADD = binary("+", 1600, (a, b) -> a + b);
public static final Operation SUBTRACT = binary("-", 1602, (a, b) -> a - b);
public static final Operation MULTIPLY = binary("*", 2001, (a, b) -> a * b);
public static final Operation DIVIDE = binary("/", 2002, (a, b) -> b == 0 ? Reason.DBZ.error() : a / b);
// === MinMax
public static final Operation MIN = binary("min", 401, Math::min);
public static final Operation MAX = binary("max", 401, Math::max);
// === Reverse
private static Operation digits(final String name, final boolean mask, final int r, final LongBinaryOperator q) {
return unary(name, 1, v -> LongStream.iterate(mask ? v & 0xffff_ffffL : v, n -> n != 0, n -> n / r)
.map(n -> n % r)
.reduce(0, q));
}
public static final Operation REVERSE = digits("reverse", false, 10, (a, b) -> a * 10 + b);
// === Digits
public static final Operation DIGITS = digits("digits", false, 10, Long::sum);
// === Floor and Ceiling
private static long floor(final long a) {
return (a >= 0 ? a : a - FLOOR_CEILING_STEP + 1) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP;
}
private static long ceiling(final long a) {
return (a >= 0 ? a + FLOOR_CEILING_STEP - 1: a) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP;
}
public static final int FLOOR_CEILING_STEP = 1000;
public static final Operation FLOOR = unary("floor", 1, Operations::floor);
public static final Operation CEILING = unary("ceiling", 1, Operations::ceiling);
// === Set, Clear
@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
public static final Operation SET = binary("set", 202, (a, b) -> a | (1 << b));
@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
public static final Operation CLEAR = binary("clear", 202, (a, b) -> a & ~(1 << b));
// === Pow, Log
public static final Operation POW_O = binary("**", 2402, (a, b) ->
b < 0 ? 1 : BigInteger.valueOf(a).modPow(BigInteger.valueOf(b), BigInteger.valueOf(1L << 32)).intValue());
public static final Operation LOG_O = binary("//", 2402, (a, b) ->
a == 0 && b > 0 ? Integer.MIN_VALUE :
a <= 0 || b <= 0 || a == 1 && b == 1 ? 0 :
a > 1 && b == 1 ? Integer.MAX_VALUE
: LongStream.iterate(b, v -> v <= a, v -> v * b).count()
);
private static final Reason INVALID_POW = new Reason("Invalid power");
public static final Operation POW = binary("**", 2402, Operations::powC);
private static long powC(final long a, final long b) {
if (b < 0 || a == 0 && b == 0) {
return INVALID_POW.error();
}
if (Math.abs(a) > 1 && b > 32) {
return Reason.OVERFLOW.error();
}
final BigInteger result = BigInteger.valueOf(a).pow((int) b);
if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || BigInteger.valueOf(Integer.MAX_VALUE).compareTo(result) < 0) {
return Reason.OVERFLOW.error();
}
return result.intValue();
}
private static final Reason INVALID_LOG = new Reason("Invalid log");
public static final Operation LOG = binary("//", 2402, (a, b) ->
a <= 0 || b <= 1 ? INVALID_LOG.error() : (int) (Math.log(a) / Math.log(b)));
// Pow2, Log2
private static final Reason NEG_LOG = new Reason("Logarithm of negative value");
public static final Operation LOG_2
= unary("log₂", 1, NEG_LOG.less(1, a-> (long) (Math.log(a) / Math.log(2))));
private static final Reason NEG_POW = new Reason("Exponentiation to negative power");
public static final Operation POW_2
= unary("pow₂", 1, NEG_POW.less(0, Reason.OVERFLOW.greater(31, a -> (long) Math.pow(2, a))));
// === High, Low
public static final Operation HIGH = unary("high", 1, v -> Integer.highestOneBit((int) v));
public static final Operation LOW = unary("low", 1, v -> Integer.lowestOneBit((int) v));
// === Common
private Operations() {
}
public static Operation unary(final String name, final int priority, final LongUnaryOperator op) {
return unary(name, priority, (a, c) -> op.applyAsLong(a));
}
public static Operation unary(final String left, final String right, final LongUnaryOperator op) {
return unary(left, right, (a, c) -> op.applyAsLong(a));
}
public static Operation unary(final String name, final int priority, final BiFunction<Long, LongToIntFunction, Long> op) {
return tests -> tests.unary(name, priority, op);
}
public static Operation unary(final String left, final String right, final BiFunction<Long, LongToIntFunction, Long> op) {
return tests -> tests.unary(left, right, op);
}
public static Operation binary(final String name, final int priority, final LongBinaryOperator op) {
return tests -> tests.binary(name, priority, op);
}
public static <E extends ToMiniString, C> Operation kind(
final ExpressionKind<E, C> kind,
final ParserTestSet.Parser<E> parser
) {
return factory -> factory.kind(kind, parser);
}
@FunctionalInterface
public interface Operation extends Consumer<ParserTester> {}
}