This commit is contained in:
Talia-12 2023-05-31 15:43:41 +10:00
parent 3c73a7da98
commit 7e4ca87a91
14 changed files with 533 additions and 0 deletions

View file

@ -0,0 +1,25 @@
package at.petrak.hexcasting.api.casting.arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.math.HexDir;
import at.petrak.hexcasting.api.casting.math.HexPattern;
public interface Arithmetic {
public String arithName();
public abstract Iterable<HexPattern> opTypes();
public abstract Operator getOperator(HexPattern pattern);
public static HexPattern ADD = HexPattern.fromAngles("waaw", HexDir.NORTH_EAST);
public static HexPattern SUB = HexPattern.fromAngles("wddw", HexDir.NORTH_WEST);
public static HexPattern MUL = HexPattern.fromAngles("waqaw", HexDir.SOUTH_EAST);
public static HexPattern DIV = HexPattern.fromAngles("wdedw", HexDir.NORTH_EAST);
public static HexPattern ABS = HexPattern.fromAngles("wqaqw", HexDir.NORTH_EAST);
public static HexPattern POW = HexPattern.fromAngles("wedew", HexDir.NORTH_WEST);
public static HexPattern FLOOR = HexPattern.fromAngles("ewq", HexDir.EAST);
public static HexPattern CEIL = HexPattern.fromAngles("qwe", HexDir.EAST);
public static HexPattern MOD = HexPattern.fromAngles("addwaad", HexDir.NORTH_EAST);
public static HexPattern PACK = HexPattern.fromAngles("eqqqqq", HexDir.EAST);
public static HexPattern UNPACK = HexPattern.fromAngles("qeeeee", HexDir.EAST);
}

View file

@ -0,0 +1,50 @@
package at.petrak.hexcasting.api.casting.arithmetic;
import at.petrak.hexcasting.api.casting.iota.Iota;
@FunctionalInterface
public interface IotaMultiPredicate {
boolean test(Iterable<Iota> iotas);
static IotaMultiPredicate all(IotaPredicate child) {
return new All(child);
}
static IotaMultiPredicate bofa(IotaPredicate first, IotaPredicate second) {
return new Any(first, second);
}
static IotaMultiPredicate any(IotaPredicate needs, IotaPredicate fallback) {
return new Any(needs, fallback);
}
record Bofa(IotaPredicate first, IotaPredicate second) implements IotaMultiPredicate {
@Override
public boolean test(Iterable<Iota> iotas) {
var it = iotas.iterator();
return it.hasNext() && first.test(it.next()) && it.hasNext() && second.test(it.next()) && !it.hasNext();
}
}
record Any(IotaPredicate needs, IotaPredicate fallback) implements IotaMultiPredicate {
@Override
public boolean test(Iterable<Iota> iotas) {
var ok = false;
for (var iota : iotas) {
if (needs.test(iota)) {
ok = true;
} else if (!fallback.test(iota)) {
return false;
}
}
return ok;
}
}
record All(IotaPredicate inner) implements IotaMultiPredicate {
@Override
public boolean test(Iterable<Iota> iotas) {
for (var iota : iotas) {
if (!inner.test(iota)) {
return false;
}
}
return true;
}
}
}

View file

@ -0,0 +1,31 @@
package at.petrak.hexcasting.api.casting.arithmetic;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.IotaType;
@FunctionalInterface
public interface IotaPredicate {
boolean test(Iota iota);
static IotaPredicate or(IotaPredicate left, IotaPredicate right) {
return new Or(left, right);
}
static IotaPredicate ofType(IotaType<?> type) {
return new OfType(type);
}
record Or(IotaPredicate left, IotaPredicate right) implements IotaPredicate {
@Override
public boolean test(Iota iota) {
return left.test(iota) || right.test(iota);
}
}
record OfType(IotaType<?> type) implements IotaPredicate {
@Override
public boolean test(Iota iota) {
return iota.getType().equals(this.type);
}
}
}

View file

@ -0,0 +1,24 @@
package at.petrak.hexcasting.api.casting.arithmetic;
import java.util.Iterator;
public record IterPair<T>(T left, T right) implements Iterable<T> {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
int ix;
@Override
public boolean hasNext() {
return ix < 2;
}
@Override
public T next() {
switch (ix++) {
case 0: return left;
case 1: return right;
}
return null;
}
};
}
}

View file

@ -0,0 +1,74 @@
package at.petrak.hexcasting.api.casting.arithmetic.engine;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class ArithmeticEngine {
private record OpCandidates(HexPattern pattern, int arity, List<Operator> operators) {
public void addOp(Operator next) {
if (next.arity != arity) {
throw new IllegalArgumentException("Operators exist of differing arity!");
}
operators.add(next);
}
}
public final Arithmetic[] arithmetics;
private final Map<HexPattern, OpCandidates> operators = new HashMap<>();
private final Map<HashCons, Operator> cache = new HashMap<>();
public ArithmeticEngine(Arithmetic... arithmetics) {
this.arithmetics = arithmetics;
for (var arith : arithmetics) {
for (var op : arith.opTypes()) {
operators.compute(op, ($, info) -> {
var operator = arith.getOperator(op);
if (info == null) {
info = new OpCandidates(op, operator.arity, new ArrayList<>());
}
info.addOp(operator);
return info;
});
}
}
}
public Iterable<HexPattern> operatorSyms() {
return operators.keySet();
}
public @Nullable Iota run(HexPattern operator, Stack<Iota> iotas) {
var candidates = operators.get(operator);
if (candidates == null)
return null; // not an operator
HashCons hash = new HashCons.Pattern(operator);
var args = new ArrayList<Iota>(candidates.arity());
for (var i = 0; i < candidates.arity(); i++) {
if (iotas.isEmpty()) {
throw new IllegalStateException("Not enough args on stack for operator: " + operator);
}
var iota = iotas.pop();
hash = new HashCons.Pair(iota.getType(), hash);
args.add(iota);
}
Collections.reverse(args);
var op = resolveCandidates(args, hash, candidates);
return op.apply(args);
}
public Operator resolveCandidates(List<Iota> args, HashCons hash, OpCandidates candidates) {
return cache.computeIfAbsent(hash, $ -> {
for (var op : candidates.operators()) {
if (op.accepts.test(args)) {
return op;
}
}
throw new IllegalArgumentException("No implementation candidates for op " + candidates.pattern() + " on args: " + args);
});
}
}

View file

@ -0,0 +1,12 @@
package at.petrak.hexcasting.api.casting.arithmetic.engine;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.math.HexPattern;
// Helper type for hashing lists of types.
public sealed interface HashCons {
public record Pattern(HexPattern operator) implements HashCons {}
public record Pair(IotaType<?> head, HashCons tail) implements HashCons {}
}

View file

@ -0,0 +1,69 @@
package at.petrak.hexcasting.api.casting.arithmetic.impls;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBinary;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorUnary;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import java.util.List;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;
import static at.petrak.hexcasting.api.casting.arithmetic.operator.Operator.downcast;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.*;
public enum DoubleArithmetic implements Arithmetic {
INSTANCE;
public static final List<HexPattern> OPS = List.of(
ADD,
SUB,
MUL,
DIV,
ABS,
POW,
FLOOR,
CEIL,
MOD
);
public static final IotaMultiPredicate ACCEPTS = IotaMultiPredicate.all(IotaPredicate.ofType(DOUBLE));
@Override
public String arithName() {
return "double_math";
}
@Override
public Iterable<HexPattern> opTypes() {
return OPS;
}
@Override
public Operator getOperator(HexPattern pattern) {
switch (pattern) {
case ADD: return make2(Double::sum);
case SUB: return make2((p, q) -> p - q);
case MUL: return make2((p, q) -> p * q);
case DIV: return make2((p, q) -> p / q);
case ABS: return make1(Math::abs);
case POW: return make2(Math::pow);
case FLOOR: return make1(Math::floor);
case CEIL: return make1(Math::ceil);
case MOD: return make2((p, q) -> p % q);
}
return null;
}
public static OperatorUnary make1(DoubleUnaryOperator op) {
return new OperatorUnary(ACCEPTS, i -> new DoubleIota(op.applyAsDouble(downcast(i, DOUBLE).getDouble())));
}
public static OperatorBinary make2(DoubleBinaryOperator op) {
return new OperatorBinary(ACCEPTS, (i, j) -> new DoubleIota(op.applyAsDouble(downcast(i, DOUBLE).getDouble(), downcast(j, DOUBLE).getDouble())));
}
}

View file

@ -0,0 +1,47 @@
package at.petrak.hexcasting.api.casting.arithmetic.impls;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBinary;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import java.util.List;
public enum StringArithmetic implements Arithmetic {
INSTANCE;
public static final List<HexPattern> OPS = List.of(
new ADD,
new MUL,
new ABS
);
@Override
public String arithName() {
return "string_math";
}
@Override
public Iterable<HexPattern> opTypes() {
return OPS;
}
@Override
public Operator getOperator(HexPattern pattern) {
switch (pattern) {
case ADD: return new OperatorBinary(
IotaMultiPredicate.all(IotaPredicate.ofType(HexIotaTypes.BOOLEAN)),
(p, q) -> new Iota(p.downcast(String.class) + q.downcast(String.class)));
case MUL: return new OperatorBinary(
IotaMultiPredicate.bofa(IotaPredicate.ofClass(String.class), IotaPredicate.ofClass(Double.class)),
(p, q) -> new Iota(new String(new char[q.downcast(Double.class).intValue()]).replace("\0", p.downcast(String.class))));
case ABS: return new OperatorUnary(
IotaMultiPredicate.all(IotaPredicate.ofClass(String.class)),
s -> new Iota((double) s.downcast(String.class).length()));
}
return null;
}
}

View file

@ -0,0 +1,58 @@
package at.petrak.hexcasting.api.casting.arithmetic.impls;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import java.util.ArrayList;
import java.util.List;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.*;
public enum Vec3Arithmetic implements Arithmetic {
INSTANCE;
public static final List<HexPattern> OPS;
static {
var ops = new ArrayList<>(DoubleArithmetic.OPS);
ops.add(PACK);
ops.add(UNPACK);
ops.remove(FLOOR);
ops.remove(CEIL);
OPS = ops;
}
public static final IotaMultiPredicate ACCEPTS = IotaMultiPredicate.any(IotaPredicate.ofType(VEC3), IotaPredicate.ofType(DOUBLE));
@Override
public String arithName() {
return "vec3_math";
}
@Override
public Iterable<Symbol> opTypes() {
return OPS;
}
@Override
public Operator getOperator(Symbol name) {
switch (name.inner()) {
case "pack": return OperatorPack.INSTANCE;
case "add": return make2(name, null);
case "sub": return make2(name, null);
case "mul": return make2(name, Vec3::dot);
case "div": return make2(name, Vec3::cross);
case "abs": return make1(Vec3::len);
case "pow": return make2(name, Vec3::proj);
case "mod": return make2(name, null);
}
return null;
}
public static OperatorUnary make1(Function<Vec3, Object> op) {
return new OperatorUnary(ACCEPTS, i -> new Iota(op.apply(i.downcast(Vec3.class))));
}
public static OperatorVec3Delegating make2(Symbol name, BiFunction<Vec3, Vec3, Object> op) {
return new OperatorVec3Delegating(op, name);
}
}

View file

@ -0,0 +1,26 @@
package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota;
public abstract class Operator {
public final int arity;
public final IotaMultiPredicate accepts;
public Operator(int arity, IotaMultiPredicate accepts) {
this.arity = arity;
this.accepts = accepts;
}
public abstract Iota apply(Iterable<Iota> iotas);
@SuppressWarnings("unchecked")
public static <T extends Iota> T downcast(Iota iota, IotaType<T> iotaType) {
if (iota.getType() != iotaType)
throw new IllegalStateException("Attempting to downcast " + iota + " to type: " + iotaType);
return (T) iota;
}
}

View file

@ -0,0 +1,22 @@
package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import java.util.function.BinaryOperator;
public class OperatorBinary extends Operator {
public BinaryOperator<Iota> inner;
public OperatorBinary(IotaMultiPredicate accepts, BinaryOperator<Iota> inner) {
super(2, accepts);
this.inner = inner;
}
@Override
public Iota apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
return inner.apply(it.next(), it.next());
}
}

View file

@ -0,0 +1,27 @@
package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Vec3Iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.world.phys.Vec3;
public class OperatorPack extends Operator {
private OperatorPack() {
super(3, IotaMultiPredicate.all(IotaPredicate.ofType(HexIotaTypes.DOUBLE)));
}
public static OperatorPack INSTANCE = new OperatorPack();
@Override
public Iota apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
return new Vec3Iota(new Vec3(
downcast(it.next(), HexIotaTypes.DOUBLE).getDouble(),
downcast(it.next(), HexIotaTypes.DOUBLE).getDouble(),
downcast(it.next(), HexIotaTypes.DOUBLE).getDouble()
));
}
}

View file

@ -0,0 +1,21 @@
package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import java.util.function.UnaryOperator;
public class OperatorUnary extends Operator {
public UnaryOperator<Iota> inner;
public OperatorUnary(IotaMultiPredicate accepts, UnaryOperator<Iota> inner) {
super(1, accepts);
this.inner = inner;
}
@Override
public Iota apply(Iterable<Iota> iotas) {
return inner.apply(iotas.iterator().next());
}
}

View file

@ -0,0 +1,47 @@
package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IterPair;
import at.petrak.hexcasting.api.casting.arithmetic.impls.DoubleArithmetic;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Vec3Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import kotlin.Pair;
import net.minecraft.world.phys.Vec3;
import java.util.Objects;
import java.util.function.BiFunction;
public class OperatorVec3Delegating extends Operator {
private final BiFunction<Vec3, Vec3, Iota> op;
private final Operator fb;
public OperatorVec3Delegating(BiFunction<Vec3, Vec3, Iota> core, HexPattern fallback) {
super(2, IotaMultiPredicate.any(IotaPredicate.ofType(HexIotaTypes.VEC3), IotaPredicate.ofType(HexIotaTypes.DOUBLE)));
op = core;
fb = Objects.requireNonNull(DoubleArithmetic.INSTANCE.getOperator(fallback));
}
@Override
public Iota apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
var left = it.next();
var right = it.next();
if (op != null && left instanceof Vec3Iota lh && right instanceof Vec3Iota rh) {
return op.apply(lh.getVec3(), rh.getVec3());
}
var lh = left instanceof Vec3Iota l ? l.getVec3() : triplicate(downcast(left, HexIotaTypes.DOUBLE).getDouble());
var rh = right instanceof Vec3Iota r ? r.getVec3() : triplicate(downcast(right, HexIotaTypes.DOUBLE).getDouble());
return new Vec3Iota(new Vec3(
downcast(fb.apply(new IterPair<>(new DoubleIota(lh.x()), new DoubleIota(rh.x()))), HexIotaTypes.DOUBLE).getDouble(),
downcast(fb.apply(new IterPair<>(new DoubleIota(lh.y()), new DoubleIota(rh.y()))), HexIotaTypes.DOUBLE).getDouble(),
downcast(fb.apply(new IterPair<>(new DoubleIota(lh.z()), new DoubleIota(rh.z()))), HexIotaTypes.DOUBLE).getDouble()
));
}
public static Vec3 triplicate(double in) {
return new Vec3(in, in, in);
}
}