package mods.eln.solver;
import mods.eln.Eln;
import mods.eln.misc.FunctionTable;
import mods.eln.misc.INBTTReady;
import mods.eln.sim.IProcess;
import net.minecraft.nbt.NBTTagCompound;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
public class Equation implements IValue, INBTTReady {
LinkedList<String> stringList = new LinkedList<String>();
ArrayList<INBTTReady> nbtList = new ArrayList<INBTTReady>();
static final HashMap<Integer, ArrayList<IOperatorMapper>> staticOperatorList;
HashMap<Integer, ArrayList<IOperatorMapper>> operatorList;
static final String staticSeparatorList;
String separatorList;
int iterationLimit;
ArrayList<ISymbole> symbolList;
IValue root;
ArrayList<IProcess> processList = new ArrayList<IProcess>();
int operatorCount; // Juste a counter for fun
static {
staticSeparatorList = "+-*&|/^,()<>=!";
staticOperatorList = new HashMap<Integer, ArrayList<IOperatorMapper>>();
int priority = 0;
{
ArrayList<IOperatorMapper> list = new ArrayList<IOperatorMapper>();
list.add(new OperatorMapperFunc("min", 2, Min.class));
list.add(new OperatorMapperFunc("max", 2, Max.class));
list.add(new OperatorMapperFunc("sin", 1, Sin.class));
list.add(new OperatorMapperFunc("cos", 1, Cos.class));
list.add(new OperatorMapperFunc("asin", 1, Asin.class));
list.add(new OperatorMapperFunc("acos", 1, Acos.class));
list.add(new OperatorMapperFunc("abs", 1, Abs.class));
list.add(new OperatorMapperFunc("ramp", 1, Ramp.class));
list.add(new OperatorMapperFunc("integrate", 2, Integrator.class));
list.add(new OperatorMapperFunc("integrate", 3, IntegratorMinMax.class));
list.add(new OperatorMapperFunc("derivate", 1, Derivator.class));
list.add(new OperatorMapperFunc("pow", 2, Pow.class));
list.add(new OperatorMapperFunc("pid", 5, Pid.class));
list.add(new OperatorMapperFunc("pid", 7, PidMinMax.class));
list.add(new OperatorMapperFunc("batteryCharge", 1, BatteryCharge.class));
list.add(new OperatorMapperFunc("rs", 2, Rs.class));
list.add(new OperatorMapperFunc("rc", 2, RC.class));
list.add(new OperatorMapperFunc("if", 3, If.class));
list.add(new OperatorMapperFunc("scale", 5, Scale.class));
list.add(new OperatorMapperBracket());
staticOperatorList.put(priority++, list);
}
{
ArrayList<IOperatorMapper> list = new ArrayList<IOperatorMapper>();
staticOperatorList.put(priority++, list);
}
{
ArrayList<IOperatorMapper> list = new ArrayList<IOperatorMapper>();
list.add(new OperatorMapperA("-", Inv.class));
list.add(new OperatorMapperA("!", Not.class));
list.add(new OperatorMapperAB("*", Mul.class));
list.add(new OperatorMapperAB("/", Div.class));
staticOperatorList.put(priority++, list);
}
{
ArrayList<IOperatorMapper> list = new ArrayList<IOperatorMapper>();
list.add(new OperatorMapperAB("+", Add.class));
list.add(new OperatorMapperAB("-", Sub.class));
staticOperatorList.put(priority++, list);
}
{
ArrayList<IOperatorMapper> list = new ArrayList<IOperatorMapper>();
list.add(new OperatorMapperAB(">", Bigger.class));
list.add(new OperatorMapperAB("<", Smaller.class));
staticOperatorList.put(priority++, list);
}
{
ArrayList<IOperatorMapper> list = new ArrayList<IOperatorMapper>();
list.add(new OperatorMapperAB("=", Eguals.class));
list.add(new OperatorMapperAB("^", NotEguals.class));
list.add(new OperatorMapperAB("&", And.class));
list.add(new OperatorMapperAB("|", Or.class));
staticOperatorList.put(priority++, list);
}
}
public Equation() {
operatorList = new HashMap<Integer, ArrayList<IOperatorMapper>>();
separatorList = "";
symbolList = new ArrayList<ISymbole>();
}
public void setUpDefaultOperatorAndMapper() {
operatorList.putAll(staticOperatorList);
separatorList += staticSeparatorList;
}
public void addMapper(int priority, IOperatorMapper mapper) {
ArrayList<IOperatorMapper> list = operatorList.get(priority);
if (list == null) {
list = new ArrayList<IOperatorMapper>();
operatorList.put(priority, list);
}
list.add(mapper);
}
public void setIterationLimit(int iterationLimit) {
this.iterationLimit = iterationLimit;
}
public void addSymbol(ArrayList<ISymbole> symbolList) {
this.symbolList.addAll(symbolList);
}
public void preProcess(String exp) {
int idx;
exp = exp.replace(" ", "");
stringList.clear();
LinkedList<Object> list = new LinkedList<Object>();
String stack = "";
idx = 0;
while (idx != exp.length()) {
if (separatorList.contains(exp.subSequence(idx, idx + 1))) {
if (stack != "") {
list.add(stack);
stringList.add(stack);
stack = "";
}
list.add(exp.substring(idx, idx + 1));
} else {
stack += exp.charAt(idx);
}
idx++;
}
if (stack != "") {
list.add(stack);
stringList.add(stack);
}
int depthMax = getDepthMax(list);
int depth;
// Double str
{
idx = 0;
Iterator<Object> i = list.iterator();
while (i.hasNext()) {
Object o = i.next();
if (o instanceof String) {
String str = (String) o;
boolean find = false;
if (!find)
for (ISymbole s : symbolList) {
if (s.getName().equals(str)) {
list.set(idx, s);
find = true;
}
}
if (!find)
try {
double value = Double.parseDouble(str);
list.set(idx, new Constant(value));
find = true;
} catch (NumberFormatException e) {
}
if (!find) {
if (str.equals("PI") || str.equals("pi")) {
list.set(idx, new Constant(Math.PI));
}
}
}
idx++;
}
}
int priority = -1;
while (list.size() > 1 && iterationLimit != 0) {
iterationLimit--;
IValue a, b;
idx = 0;
depth = 0;
Iterator<Object> i = list.iterator();
priority++;
while (i.hasNext()) {
Object o = i.next();
if (o instanceof String) {
String str = (String) o;
if (operatorList.containsKey(priority)) {
int depthDelta = depth - depthMax;
boolean resetPriority = false;
for (IOperatorMapper mapper : operatorList.get(priority)) {
IOperator operator;
if ((operator = mapper.newOperator(str, depthDelta, list, idx)) != null) {
if (operator instanceof IProcess)
processList.add((IProcess) operator);
if (operator instanceof INBTTReady)
nbtList.add((INBTTReady) operator);
operatorCount += operator.getRedstoneCost();
resetPriority = true;
break;
}
}
if (resetPriority) {
depthMax = getDepthMax(list);
priority = -1;
break;
}
}
if (str.equals("("))
depth++;
if (str.equals(")"))
depth--;
}
idx++;
}
}
if (list.size() == 1) {
if (list.get(0) instanceof IValue) {
root = (IValue) list.get(0);
} else
root = null;
}
}
int getDepthMax(LinkedList<Object> list) {
int depth, depthMax;
{
depthMax = 0;
depth = 0;
Iterator<Object> i = list.iterator();
while (i.hasNext()) {
Object o = i.next();
if (o instanceof String) {
String str = (String) o;
if (str.equals("(")) depth++;
if (str.equals(")")) depth--;
depthMax = Math.max(depthMax, depth);
}
}
}
return depthMax;
}
public double getValue() {
if (root == null)
return 0.0;
return root.getValue();
}
public double getValue(double deltaT) {
if (root == null)
return 0.0;
for (IProcess p : processList) {
p.process(deltaT);
}
return root.getValue();
}
public boolean isValid() {
return root != null;
}
public static class Eguals extends OperatorAB {
@Override
public double getValue() {
return (a.getValue() > 0.5) == (b.getValue() > 0.5) ? 1.0 : 0.0;
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class NotEguals extends OperatorAB {
@Override
public double getValue() {
return (a.getValue() > 0.5) != (b.getValue() > 0.5) ? 1.0 : 0.0;
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Bigger extends OperatorAB {
@Override
public double getValue() {
return a.getValue() > b.getValue() ? 1.0 : 0.0;
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Smaller extends OperatorAB {
@Override
public double getValue() {
return a.getValue() < b.getValue() ? 1.0 : 0.0;
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class And extends OperatorAB {
@Override
public double getValue() {
return a.getValue() > 0.5 && b.getValue() > 0.5 ? 1.0 : 0.0;
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Or extends OperatorAB {
@Override
public double getValue() {
return a.getValue() > 0.5 || b.getValue() > 0.5 ? 1.0 : 0.0;
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Add extends OperatorAB {
@Override
public double getValue() {
return a.getValue() + b.getValue();
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Sub extends OperatorAB {
@Override
public double getValue() {
return a.getValue() - b.getValue();
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Mul extends OperatorAB {
@Override
public double getValue() {
return a.getValue() * b.getValue();
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Div extends OperatorAB {
@Override
public double getValue() {
return a.getValue() / b.getValue();
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Inv implements IOperator {
IValue a;
@Override
public double getValue() {
return -a.getValue();
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Not implements IOperator {
IValue a;
@Override
public double getValue() {
return 1.0 - a.getValue();
}
@Override
public void setOperator(IValue[] values) {
a = values[0];
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Bracket implements IOperator {
IValue a;
@Override
public double getValue() {
return a.getValue();
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 0;
}
}
public static class Abs implements IOperator {
IValue a;
@Override
public double getValue() {
return Math.abs(a.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 1;
}
}
public static class Sin implements IOperator {
IValue a;
@Override
public double getValue() {
return Math.sin(a.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Cos implements IOperator {
IValue a;
@Override
public double getValue() {
return Math.cos(a.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Asin implements IOperator {
IValue a;
@Override
public double getValue() {
return Math.asin(a.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Acos implements IOperator {
IValue a;
@Override
public double getValue() {
return Math.acos(a.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Pow implements IOperator {
IValue a, b;
@Override
public double getValue() {
return Math.pow(a.getValue(), b.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[0];
this.b = values[1];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Ramp implements IOperator, INBTTReady, IProcess {
public double counter = 0.0;
public IValue periode;
@Override
public double getValue() {
return counter;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
counter = nbt.getDouble(str + "counter");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setDouble(str + "counter", counter);
}
@Override
public void process(double time) {
double p = periode.getValue();
counter += time / p;
if (counter >= 1.0) counter -= 1.0;
if (counter >= 1.0) counter = 0;
}
@Override
public void setOperator(IValue[] values) {
this.periode = values[0];
}
@Override
public int getRedstoneCost() {
return 3;
}
}
public static class Integrator implements IOperator, INBTTReady, IProcess {
public double counter = 0.0;
public IValue probe, reset;
@Override
public double getValue() {
return counter;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
counter = nbt.getDouble(str + "counter");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setDouble(str + "counter", counter);
}
@Override
public void process(double time) {
counter += time * probe.getValue();
if (reset.getValue() > 0.5) counter = 0;
}
@Override
public void setOperator(IValue[] values) {
this.probe = values[0];
this.reset = values[1];
}
@Override
public int getRedstoneCost() {
return 4;
}
}
public static class IntegratorMinMax implements IOperator, INBTTReady, IProcess {
public double counter = 0.0;
public IValue probe, min, max;
@Override
public double getValue() {
return counter;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
counter = nbt.getDouble(str + "counter");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setDouble(str + "counter", counter);
}
@Override
public void process(double time) {
counter += time * probe.getValue();
if (counter < min.getValue()) counter = min.getValue();
if (counter > max.getValue()) counter = max.getValue();
}
@Override
public void setOperator(IValue[] values) {
this.probe = values[0];
this.min = values[1];
this.max = values[2];
}
@Override
public int getRedstoneCost() {
return 4;
}
}
public static class Derivator implements IOperator, INBTTReady, IProcess {
public double old = 0.0, value = 0.0;
public IValue probe;
@Override
public double getValue() {
return value;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
old = nbt.getDouble(str + "old");
value = nbt.getDouble(str + "value");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setDouble(str + "old", old);
nbt.setDouble(str + "value", value);
}
@Override
public void process(double time) {
double next = probe.getValue();
value = (next - old) / time;
old = next;
}
@Override
public void setOperator(IValue[] values) {
this.probe = values[0];
}
@Override
public int getRedstoneCost() {
return 3;
}
}
public static class Pid implements IOperator, INBTTReady, IProcess {
public double iStack = 0.0, oldError = 0, dValue = 0;
public IValue target, hit, p, i, d;
@Override
public double getValue() {
double value = oldError * p.getValue() + iStack + dValue * d.getValue();
return value;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
iStack = nbt.getDouble(str + "iStack");
oldError = nbt.getDouble(str + "oldError");
dValue = nbt.getDouble(str + "dValue");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setDouble(str + "iStack", iStack);
nbt.setDouble(str + "oldError", oldError);
nbt.setDouble(str + "dValue", dValue);
}
@Override
public void process(double time) {
double error = target.getValue() - hit.getValue();
iStack += error * time * i.getValue();
dValue = (error - oldError) / time;
if (iStack > 1) iStack = 1;
if (iStack < -1) iStack = -1;
oldError = error;
}
@Override
public void setOperator(IValue[] values) {
this.target = values[0];
this.hit = values[1];
this.p = values[2];
this.i = values[3];
this.d = values[4];
}
@Override
public int getRedstoneCost() {
return 12;
}
}
public static class PidMinMax extends Pid {
public IValue min, max;
@Override
public double getValue() {
return Math.max(min.getValue(), Math.min(max.getValue(), super.getValue()));
}
@Override
public void setOperator(IValue[] values) {
super.setOperator(values);
min = values[5];
max = values[6];
}
@Override
public int getRedstoneCost() {
return super.getRedstoneCost() + 2;
}
}
public static class Min implements IOperator {
public IValue a, b;
@Override
public double getValue() {
return Math.min(a.getValue(), b.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[1];
this.b = values[0];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Max implements IOperator {
public IValue a, b;
@Override
public double getValue() {
return Math.max(a.getValue(), b.getValue());
}
@Override
public void setOperator(IValue[] values) {
this.a = values[1];
this.b = values[0];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class Rs implements IOperator, INBTTReady {
public boolean state = false;
public IValue set, reset;
@Override
public double getValue() {
if (set.getValue() > 0.6) state = true;
if (reset.getValue() > 0.6) state = false;
return state ? 1.0 : 0.0;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
state = nbt.getBoolean(str + "state");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setBoolean(str + "state", state);
}
@Override
public void setOperator(IValue[] values) {
this.set = values[1];
this.reset = values[0];
}
@Override
public int getRedstoneCost() {
return 3;
}
}
public static class RC implements IOperator, INBTTReady, IProcess {
public double state;
public IValue tao, input;
@Override
public double getValue() {
return state;
}
@Override
public void process(double time) {
double tao = Math.max(time, this.tao.getValue());
state += (input.getValue() - state) / tao * time;
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
state = nbt.getDouble(str + "state");
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
nbt.setDouble(str + "state", state);
}
@Override
public void setOperator(IValue[] values) {
this.input = values[1];
this.tao = values[0];
}
@Override
public int getRedstoneCost() {
return 3;
}
}
public static class If implements IOperator {
public IValue condition, thenValue, elseValue;
@Override
public double getValue() {
return condition.getValue() > 0.5 ? thenValue.getValue() : elseValue.getValue();
}
@Override
public void setOperator(IValue[] values) {
this.condition = values[0];
this.thenValue = values[1];
this.elseValue = values[2];
}
@Override
public int getRedstoneCost() {
return 2;
}
}
public static class BatteryCharge implements IOperator {
public BatteryCharge() {
FunctionTable uFq = Eln.instance.batteryVoltageFunctionTable;
double q, dq = 0.01;
eMax = 0;
q = 0;
while (q <= 1.0) {
eMax += uFq.getValue(q) * dq;
q += dq;
}
}
double eMax;
public IValue probe;
@Override
public void setOperator(IValue[] values) {
this.probe = values[0];
}
@Override
public int getRedstoneCost() {
return 8;
}
@Override
public double getValue() {
FunctionTable uFq = Eln.instance.batteryVoltageFunctionTable;
double probeU = probe.getValue();
if (probeU > 1.5) return 1;
double q = 0, dq = 0.01;
double e = 0;
double u;
while ((u = uFq.getValue(q)) < probeU) {
e += u * dq;
q += dq;
}
return e / eMax;
}
}
/**
* Rescale input values.
* <p>
* scale(X, in0, in1, out0, out1) = (X - in0) / (in1 - in0) * (out1 - out0) + out0
*/
public static class Scale implements IOperator {
private IValue x, in0, in1, out0, out1;
@Override
public void setOperator(IValue[] values) {
x = values[0];
in0 = values[1];
in1 = values[2];
out0 = values[3];
out1 = values[4];
}
@Override
public int getRedstoneCost() {
return 5;
}
@Override
public double getValue() {
double xv = x.getValue(),
in0v = in0.getValue(), in1v = in1.getValue(),
out0v = out0.getValue(), out1v = out1.getValue();
return (xv - in0v) / (in1v - in0v) * (out1v - out0v) + out0v;
}
}
public boolean isSymboleUsed(ISymbole iSymbole) {
if (!isValid()) return false;
return stringList.contains(iSymbole.getName());
}
@Override
public void readFromNBT(NBTTagCompound nbt, String str) {
if (!isValid()) return;
int idx = 0;
for (INBTTReady o : nbtList) {
o.readFromNBT(nbt, str + idx);
idx++;
}
}
@Override
public void writeToNBT(NBTTagCompound nbt, String str) {
if (!isValid()) return;
int idx = 0;
for (INBTTReady o : nbtList) {
o.writeToNBT(nbt, str + idx);
idx++;
}
}
public int getOperatorCount() {
return operatorCount;
}
}