package jscl.math;
import jscl.math.function.*;
import jscl.math.polynomial.Basis;
import jscl.math.polynomial.Monomial;
import jscl.math.polynomial.Polynomial;
import jscl.math.polynomial.UnivariatePolynomial;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public final class Simplification {
private final Map<Variable, Generic> cache = new TreeMap<Variable, Generic>();
private final List<Constraint> constraints = new ArrayList<Constraint>();
Generic result;
boolean linear;
private Simplification() {
}
public static Generic compute(@Nonnull Generic generic) {
final Simplification s = new Simplification();
s.computeValue(generic);
return s.getValue();
}
void computeValue(Generic generic) {
Debug.println("simplification");
Debug.increment();
final Variable t = new TechnicalVariable("t");
linear = false;
process(new Constraint(t, t.expressionValue().subtract(generic), false));
UnivariatePolynomial p = polynomial(t);
switch (p.degree()) {
case 0:
result = generic;
break;
case 1:
result = new Root(p, 0).selfSimplify();
break;
// case 2:
// int n=branch(generic,p);
// if(n<p.degree()) linear(new Root(p,n).expressionValue());
// else linear(generic);
// break;
default:
linear(generic);
}
Debug.decrement();
}
void linear(Generic generic) {
Variable t = new TechnicalVariable("t");
linear = true;
constraints.clear();
process(new Constraint(t, t.expressionValue().subtract(generic), false));
UnivariatePolynomial p = polynomial(t);
switch (p.degree()) {
case 0:
result = generic;
break;
default:
result = new Root(p, 0).selfSimplify();
}
}
int branch(Generic generic, UnivariatePolynomial polynomial) {
int n = polynomial.degree();
Variable t = new TechnicalVariable("t");
linear = true;
for (int i = 0; i < n; i++) {
constraints.clear();
process(new Constraint(t, t.expressionValue().subtract(generic.subtract(new Root(polynomial, i).expressionValue())), false));
Generic a = polynomial(t).solve();
if (a != null && a.signum() == 0) {
return i;
}
}
return n;
}
UnivariatePolynomial polynomial(Variable t) {
Polynomial fact = Polynomial.factory(t);
int n = constraints.size();
Generic a[] = new Generic[n];
Variable unk[] = new Variable[n];
if (linear) {
int j = 0;
for (Constraint constraint : constraints) {
if (constraint.reduce) {
a[j] = constraint.generic;
unk[j] = constraint.unknown;
j++;
}
}
int k = 0;
for (Constraint c : constraints) {
if (!c.reduce) {
a[j] = c.generic;
unk[j] = c.unknown;
j++;
k++;
}
}
a = solve(a, unk, k);
for (Generic anA : a) {
UnivariatePolynomial p = (UnivariatePolynomial) fact.valueOf(anA);
if (p.degree() == 1) return p;
}
return null;
} else {
for (int i = 0; i < n; i++) {
Constraint c = constraints.get(i);
a[i] = c.generic;
unk[i] = c.unknown;
}
a = solve(a, unk, n);
return (UnivariatePolynomial) fact.valueOf(a[0]);
}
}
Generic[] solve(Generic generic[], Variable unknown[], int n) {
Variable unk[] = Basis.augmentUnknown(unknown, generic);
return Basis.compute(generic, unk, Monomial.kthElimination(n)).elements();
}
void process(@Nonnull Constraint c) {
constraints.add(c);
int n1 = 0;
int n2 = 0;
do {
n1 = n2;
n2 = constraints.size();
for (int i = n1; i < n2; i++) {
subProcess(constraints.get(i));
}
} while (n1 < n2);
}
void subProcess(@Nonnull Constraint c) {
for (Variable v : c.generic.variables()) {
if (constraints.contains(new Constraint(v))) {
continue;
}
Constraint result = null;
if (v instanceof Fraction) {
final Generic parameters[] = ((Fraction) v).getParameters();
result = new Constraint(v, v.expressionValue().multiply(parameters[1]).subtract(parameters[0]), false);
} else if (v instanceof Sqrt) {
if (linear) {
result = linearConstraint(v);
}
if (result == null) {
final Generic parameters[] = ((Sqrt) v).getParameters();
result = new Constraint(v, v.expressionValue().pow(2).subtract(parameters[0]), true);
}
} else if (v instanceof Cubic) {
if (linear) {
result = linearConstraint(v);
}
if (result == null) {
final Generic parameters[] = ((Cubic) v).getParameters();
result = new Constraint(v, v.expressionValue().pow(3).subtract(parameters[0]), true);
}
} else if (v instanceof Pow) {
try {
Root r = ((Pow) v).rootValue();
int d = r.degree();
if (linear) {
result = linearConstraint(v);
}
if (result == null) {
final Generic parameters[] = r.getParameters();
result = new Constraint(v, v.expressionValue().pow(d).subtract(parameters[0].negate()), d > 1);
}
} catch (NotRootException e) {
result = linearConstraint(v);
}
} else if (v instanceof Root) {
try {
Root r = (Root) v;
int d = r.degree();
int n = r.subscript().integerValue().intValue();
if (linear) {
result = linearConstraint(v);
}
if (result == null) {
final Generic parameters[] = r.getParameters();
result = new Constraint(v, Root.sigma(parameters, d - n).multiply(JsclInteger.valueOf(-1).pow(d - n)).multiply(parameters[d]).subtract(parameters[n]), d > 1);
}
} catch (NotIntegerException e) {
result = linearConstraint(v);
}
} else {
result = linearConstraint(v);
}
if (result != null) {
constraints.add(result);
}
}
}
@Nullable
private Constraint linearConstraint(@Nonnull Variable v) {
Generic s = cache.get(v);
if (s == null) {
s = v.simplify();
cache.put(v, s);
}
Generic a = v.expressionValue().subtract(s);
if (a.signum() != 0) {
return new Constraint(v, a, false);
} else {
return null;
}
}
Generic getValue() {
return result;
}
}
class Constraint {
Variable unknown;
Generic generic;
boolean reduce;
Constraint(Variable unknown, Generic generic, boolean reduce) {
this.unknown = unknown;
this.generic = generic;
this.reduce = reduce;
}
Constraint(Variable unknown) {
this(unknown, null, false);
}
public boolean equals(Object obj) {
return unknown.compareTo(((Constraint) obj).unknown) == 0;
}
}