package org.geogebra.common.kernel.cas;
import java.util.ArrayList;
import java.util.Map;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic.MyArbitraryConstant;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.commands.EvalInfo;
import org.geogebra.common.kernel.geos.CasEvaluableFunction;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoFunctionable;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.util.debug.Log;
/**
* @author zbynek
*
*/
public class AlgoSolveODECas extends AlgoUsingTempCASalgo {
private CasEvaluableFunction f;
private GeoElementND g;
private GeoPointND pt;
/**
* @param cons
* construction
* @param label
* label for output
* @param f
* input function
* @param info
* eval info
*/
public AlgoSolveODECas(Construction cons, String label,
CasEvaluableFunction f, EvalInfo info) {
super(cons);
this.nocas = !info.isUsingCAS();
cons.addCASAlgo(this);
this.f = f;
/** g is created in compute */
compute();
setInputOutput();
g.setLabel(label);
}
/**
* @param cons
* construction
* @param label
* label for output
* @param f
* input function
* @param pt
* point through which the integral line should go
*/
public AlgoSolveODECas(Construction cons, String label,
CasEvaluableFunction f, GeoPointND pt) {
super(cons);
cons.addCASAlgo(this);
this.f = f;
this.pt = pt;
/** g is created in compute */
compute();
setInputOutput();
g.setLabel(label);
}
@Override
public Commands getClassName() {
return Commands.SolveODE;
}
private MyArbitraryConstant arbconst = new MyArbitraryConstant(this);
@Override
protected void setInputOutput() {
if (pt == null) {
input = new GeoElement[] { f.toGeoElement() };
} else {
input = new GeoElement[] { f.toGeoElement(), pt.toGeoElement() };
}
setOnlyOutput(g);
setDependencies();
}
private String oldCASstring;
private boolean nocas = false;
@Override
public void compute() {
if (!f.isDefined() && g != null) {
g.setUndefined();
return;
}
// function expression
String funExp = f.toString(StringTemplate.prefixedDefault);
// remove f(x,y) =
if (funExp.indexOf('=') > -1) {
funExp = funExp.split("=")[1];
}
// get function and function variable string using temp variable
// prefixes,
// e.g. f(x) = a x^2 returns {"ggbtmpvara ggbtmpvarx^2", "ggbtmpvarx"}
StringBuilder sb = new StringBuilder();
sb.setLength(0);
if (pt != null) {
sb.append("SolveODEPoint(");
sb.append(funExp);
sb.append(",");
sb.append(pt.toValueString(StringTemplate.prefixedDefault));
} else {
sb.append("SolveODE(");
sb.append(funExp);
}
sb.append(")");
String casString = sb.toString();
if (!casString.equals(oldCASstring)) {
updateG(casString);
oldCASstring = casString;
}
if (pt != null && arbconst.getTotalNumberOfConsts() == 1) {
findPathThroughPoint();
}
}
private void findPathThroughPoint() {
GeoNumeric c1 = arbconst.getConst(0);
if (c1 == null) {
g.setUndefined();
return;
}
c1.setAlgebraVisible(false);
}
private void updateG(String casString) {
String functionOut;
boolean ok = false;
try {
// TODO put caching back
functionOut = kernel.evaluateGeoGebraCAS(casString,
nocas ? getSilentArbConst() : arbconst);
boolean flag = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
GeoElementND[] res = kernel.getAlgebraProcessor()
.processAlgebraCommandNoExceptionsOrErrors(functionOut,
false);
cons.setSuppressLabelCreation(flag);
if (res != null && res.length > 0) {
// convert eg y=2 into a function
if (res[0].isGeoFunctionable()) {
res[0] = ((GeoFunctionable) res[0]).getGeoFunction();
}
if (g == null) {
g = res[0];
} else {
g.set(res[0]);
}
ok = true;
}
} catch (Throwable e) {
Log.debug("AlgoSolveODECas: " + e.getMessage());
}
if (!ok) {
if (g != null) {
g.setUndefined();
} else {
g = new GeoFunction(cons);
}
}
}
private MyArbitraryConstant getSilentArbConst() {
return new MyArbitraryConstant(this) {
@Override
protected GeoNumeric nextConst(ArrayList<GeoNumeric> consts2,
Map<Integer, GeoNumeric> map, String prefix, double index) {
return new GeoNumeric(cons, 0.0);
}
};
}
/**
* @return resulting function, conic or line
*/
public GeoElement getResult() {
return g.toGeoElement();
}
@Override
public void refreshCASResults() {
this.oldCASstring = "";
}
}