package org.geogebra.common.kernel.arithmetic;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoSurfaceCartesian3D;
import org.geogebra.common.kernel.CASGenericInterface;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic3D.MyVec3DNode;
import org.geogebra.common.kernel.geos.GeoCasCell;
import org.geogebra.common.kernel.geos.GeoDummyVariable;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.kernelND.GeoCurveCartesianND;
import org.geogebra.common.plugin.Operation;
/**
* Expands f as f(x) or f(x,y) in CAS
*
* @author Zbynek Konecny
*/
public class FunctionExpander implements Traversing {
private ExpressionValue expand(GeoElement geo) {
if (geo instanceof FunctionalNVar) {
return ((FunctionalNVar) geo).getFunctionExpression()
.deepCopy(geo.getKernel()).traverse(this);
}
if (geo instanceof GeoCasCell) {
return ((GeoCasCell) geo).getOutputValidExpression()
.deepCopy(geo.getKernel()).traverse(this).unwrap();
}
return geo;
}
private boolean contains(GeoDummyVariable gdv) {
if (variables == null) {
return false;
}
for (FunctionVariable funvar : variables) {
if (funvar.toString(StringTemplate.defaultTemplate).equals(
gdv.toString(StringTemplate.defaultTemplate))) {
return true;
}
}
return false;
}
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof ExpressionNode) {
boolean surface = false;
final ExpressionNode en = (ExpressionNode) ev;
if (en.getOperation() == Operation.FUNCTION
|| en.getOperation() == Operation.FUNCTION_NVAR
|| en.getOperation() == Operation.VEC_FUNCTION) {
ExpressionValue geo = en.getLeft().unwrap();
ExpressionValue deriv = null;
if (geo instanceof ExpressionNode
&& ((ExpressionNode) geo).getOperation() == Operation.DERIVATIVE) {
// template not important, right it is a constant
// MyDouble anyway
deriv = ((ExpressionNode) geo).getRight().evaluate(
StringTemplate.defaultTemplate);
geo = ((ExpressionNode) geo).getLeft().unwrap();
}
if (geo instanceof GeoDummyVariable) {
geo = ((GeoDummyVariable) geo).getElementWithSameName();
}
ExpressionNode en2 = null;
FunctionVariable[] fv = null;
if (geo instanceof GeoCurveCartesianND) {
Kernel kernel = ((GeoCurveCartesianND) geo).getKernel();
ExpressionValue en2x = ((GeoCurveCartesianND) geo)
.getFun(0).getFunctionExpression().getCopy(kernel)
.traverse(this);
ExpressionValue en2y = ((GeoCurveCartesianND) geo)
.getFun(1).getFunctionExpression()
.getCopy(kernel).traverse(this);
if (((GeoCurveCartesianND) geo).getDimension() > 2) {
ExpressionValue en2z = ((GeoCurveCartesianND) geo)
.getFun(2).getFunctionExpression()
.getCopy(kernel).traverse(this);
en2 = new MyVec3DNode(kernel, en2x, en2y, en2z).wrap();
} else {
en2 = new MyVecNode(kernel, en2x, en2y).wrap();
}
fv = ((GeoCurveCartesianND) geo).getFunctionVariables();
}
if (geo instanceof FunctionalNVar) {
en2 = (ExpressionNode) ((FunctionalNVar) geo)
.getFunctionExpression()
.getCopy(((FunctionalNVar) geo).getKernel())
.traverse(this);
fv = ((FunctionalNVar) geo).getFunction()
.getFunctionVariables();
}
if (geo instanceof GeoCasCell) {
ValidExpression ve = ((GeoCasCell) geo)
.getOutputValidExpression();
// related to #4126 -- maybe not needed though
if (((GeoCasCell) geo).isKeepInputUsed()) {
ve = expand((GeoCasCell) geo).wrap();
}
en2 = ve.unwrap() instanceof FunctionNVar ? ((FunctionNVar) ve
.unwrap()).getExpression() : ve.wrap();
en2 = en2.traverse(this).wrap();
if (en2.getLeft() instanceof GeoSurfaceCartesian3D) {
FunctionNVar[] fun = ((GeoSurfaceCartesian3D) en2
.getLeft()).getFunctions();
MyVec3DNode vect = new MyVec3DNode(
((ExpressionNode) ev).getKernel(),
fun[0].getExpression(), fun[1].getExpression(),
fun[2].getExpression());
en2 = new ExpressionNode(en.getKernel(), vect);
if (en.getRight() instanceof MyList
&& ((MyList) en.getRight()).getListElement(0) instanceof ExpressionNode
&& ((ExpressionNode) ((MyList) en.getRight())
.getListElement(0)).getLeft() instanceof MyList) {
en.setRight(((ExpressionNode) ((MyList) en
.getRight()).getListElement(0)).getLeft());
}
} else {
en2 = en2.getCopy(((GeoCasCell) geo).getKernel());
}
fv = ((GeoCasCell) geo).getFunctionVariables();
}
if (geo instanceof GeoSurfaceCartesian3D) {
surface = true;
if (en.getRight() instanceof MyList
&& ((MyList) en.getRight()).getListElement(
0) instanceof ExpressionNode
&& ((ExpressionNode) ((MyList) en.getRight())
.getListElement(0))
.getLeft() instanceof MyList) {
en.setRight(((ExpressionNode) ((MyList) en.getRight())
.getListElement(0)).getLeft());
}
FunctionNVar[] fun = ((GeoSurfaceCartesian3D) geo)
.getFunctions();
fv = fun[0].getFunctionVariables();
Kernel kernel = fun[0].getKernel();
MyVec3DNode vect = new MyVec3DNode(
((ExpressionNode) ev).getKernel(), fun[0]
.getExpression().deepCopy(kernel), fun[1]
.getExpression().deepCopy(kernel), fun[2]
.getExpression().deepCopy(kernel));
en2 = new ExpressionNode(en.getKernel(), vect);
}
if (deriv != null) {
CASGenericInterface cas = en.getKernel().getGeoGebraCAS()
.getCurrentCAS();
Command derivCommand = new Command(en.getKernel(),
"Derivative", false);
derivCommand.addArgument(en2);
derivCommand.addArgument(fv[0].wrap());
derivCommand.addArgument(deriv.wrap());
en2 = cas.evaluateToExpression(derivCommand, null,
en.getKernel()).wrap();
}
if (fv != null) {
ExpressionValue argument = en.getRight().wrap()
.getCopy(en.getKernel()).traverse(this).unwrap();
ExpressionValue ithArg = argument;
VariableReplacer vr = VariableReplacer.getReplacer(en
.getKernel());
// variables have to be replaced with one traversing
// or else replacing f(x,y) with f(y,x)
// will result in f(x, x)
for (int i = 0; i < fv.length; i++) {
if (en.getOperation() == Operation.FUNCTION_NVAR
|| surface) {
ithArg = ((MyList) argument).getListElement(i);
}
VariableReplacer.addVars(fv[i].getSetVarString(),
ithArg);
}
en2 = en2.traverse(vr).wrap();
return en2;
}
} else if (en.getOperation() == Operation.DERIVATIVE) {
// should not get there
} else {
GeoElement geo = null;
if (en.getLeft() instanceof GeoDummyVariable
&& !contains((GeoDummyVariable) en.getLeft())) {
geo = ((GeoDummyVariable) en.getLeft())
.getElementWithSameName();
if (geo != null) {
en.setLeft(expand(geo));
}
}
if (en.getLeft() instanceof Variable) {
geo = ((Variable) en.getLeft())
.getKernel()
.getConstruction()
.lookupLabel(
en.getLeft().toString(
StringTemplate.defaultTemplate));
if (geo != null) {
ExpressionNode en2 = (ExpressionNode) ((FunctionalNVar) geo)
.getFunctionExpression()
.getCopy(((FunctionalNVar) geo).getKernel())
.traverse(this);
return en2;
}
}
}
if (en.getRight() != null) {
GeoElement geo = null;
if (en.getRight() instanceof GeoDummyVariable
&& !contains((GeoDummyVariable) en.getRight())) {
geo = ((GeoDummyVariable) en.getRight())
.getElementWithSameName();
if (geo != null) {
en.setRight(expand(geo));
}
}
}
} else if (ev instanceof GeoDummyVariable
&& !contains((GeoDummyVariable) ev)) {
GeoElement geo = ((GeoDummyVariable) ev).getElementWithSameName();
if (geo != null) {
return expand(geo);
}
} else if (ev instanceof GeoCasCell) {
// expanding the cell here is necessary #4126
if (((GeoCasCell) ev).isKeepInputUsed()) {
return expand((GeoCasCell) ev);
}
return ((GeoCasCell) ev).getOutputValidExpression().wrap()
.getCopy(((GeoCasCell) ev).getKernel());
} else if (ev instanceof FunctionNVar) {
variables = ((FunctionNVar) ev).fVars;
}
return ev;
}
private static FunctionExpander collector = new FunctionExpander();
// store function variables if needed
private FunctionVariable[] variables = null;
/**
* Resets and returns the collector
*
* @return function expander
*/
public static FunctionExpander getCollector() {
collector.variables = null;
return collector;
}
}