package org.geogebra.common.kernel.advanced;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.cas.AlgoCoefficients;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.plugin.Operation;
public class AlgoCompleteSquare extends AlgoElement {
private GeoFunction f, square;
private FunctionVariable fv;
private MyDouble a, h, k; // a(x-h)^2+k
private int lastDeg;
private AlgoCoefficients algoCoef;
public AlgoCompleteSquare(Construction cons, String label, GeoFunction f) {
super(cons);
this.f = f;
a = new MyDouble(kernel);
h = new MyDouble(kernel);
k = new MyDouble(kernel);
fv = new FunctionVariable(kernel);
ExpressionNode squareE = new ExpressionNode(kernel, a);
Function squareF = new Function(squareE, fv);
squareF.initFunction();
square = new GeoFunction(cons);
setInputOutput();
square.setFunction(squareF);
compute();
lastDeg = 0;
square.setLabel(label);
}
@Override
public void compute() {
int degInt;
GeoList coefs = null;
fv.setVarString(f.getVarString(StringTemplate.defaultTemplate));
// px^2+qx+r; p+q+r=s;
double r = f.value(0);
double s = f.value(1);
double p = 0.5 * (s + f.value(-1)) - r;
double q = s - p - r;
boolean isQuadratic = !f.isGeoFunctionConditional();
double[] checkpoints = { 1000, -1000, Math.PI, Math.E };
for (int i = 0; i < checkpoints.length; i++) {
double x = checkpoints[i];
if (!Kernel.isZero(p * x * x + q * x + r - f.value(x))) {
// Log.debug(p + "," + q + "," + r + ","
// + (p * x * x + q * x + r - f.evaluate(x)));
isQuadratic = false;
}
}
if (!isQuadratic) {
if (algoCoef == null) {
algoCoef = new AlgoCoefficients(cons, f);
algoCoef.setProtectedInput(true);
algoCoef.remove();
} else {
algoCoef.compute();
}
coefs = algoCoef.getResult();
degInt = coefs.size() - 1;
isQuadratic = coefs.isDefined() && coefs.get(0).isDefined();
for (int i = 1; i < degInt; i++) {
if (2 * i != degInt && !Kernel
.isZero(((GeoNumeric) coefs.get(i)).getDouble())) {
isQuadratic = false;
}
p = ((GeoNumeric) coefs.get(0)).getDouble();
q = ((GeoNumeric) coefs.get(degInt / 2)).getDouble();
r = ((GeoNumeric) coefs.get(degInt)).getDouble();
}
} else {
degInt = 2;
}
if (MyDouble.isOdd(degInt) || degInt < 2 || !isQuadratic
|| Kernel.isZero(p)) {
square.setUndefined();
return;
}
if (lastDeg != degInt) {
ExpressionNode squareE;
ExpressionValue fvPower;
if (degInt == 2) {
fvPower = fv;
} else {
int power = degInt / 2;
fvPower = new ExpressionNode(kernel, fv, Operation.POWER,
new MyDouble(kernel, power));
}
squareE = new ExpressionNode(kernel,
new ExpressionNode(kernel, a, Operation.MULTIPLY,
new ExpressionNode(kernel, fvPower, Operation.MINUS,
h).power(new MyDouble(kernel, 2))),
Operation.PLUS, k);
square.getFunction().setExpression(squareE);
}
lastDeg = degInt;
fv.setVarString(f.getVarString(StringTemplate.defaultTemplate));
// if one is undefined, others are as well
square.setDefined(!Double.isNaN(r));
a.set(p);
h.set(-q / (2 * p));
k.set(r - q * q / (p * 4));
}
@Override
protected void setInputOutput() {
input = new GeoElement[1];
input[0] = f;
setOutputLength(1);
setOutput(0, square);
setDependencies();
}
public GeoFunction getResult() {
return square;
}
@Override
public Commands getClassName() {
return Commands.CompleteSquare;
}
}