package org.geogebra.common.kernel.commands;
import java.util.ArrayList;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoDependentFunction;
import org.geogebra.common.kernel.algos.AlgoFunctionFreehand;
import org.geogebra.common.kernel.algos.AlgoFunctionInterval;
import org.geogebra.common.kernel.arithmetic.Command;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.arithmetic.Traversing.VariablePolyReplacer;
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.GeoList;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.main.MyError;
import org.geogebra.common.plugin.Operation;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Function[ <GeoFunction>, <NumberValue>, <NumberValue> ]
*/
public class CmdFunction extends CommandProcessor {
/**
* Create new command processor
*
* @param kernel
* kernel
*/
public CmdFunction(Kernel kernel) {
super(kernel);
}
@Override
@SuppressFBWarnings({ "SF_SWITCH_FALLTHROUGH",
"missing break is deliberate" })
public GeoElement[] process(Command c, EvalInfo info) throws MyError {
int n = c.getArgumentNumber();
boolean[] ok = new boolean[n];
EvalInfo argInfo = info.withLabels(false);
String varName = null;
ExpressionNode expr;
boolean mayUseIndependent;
String label;
FunctionVariable fv;
switch (n) {
case 0:
return CmdDataFunction.emptyFunction(kernelA, c.getLabel());
case 1:
GeoElement[] arg = resArgs(c);
if (arg[0].isGeoList()) {
AlgoFunctionFreehand algo = new AlgoFunctionFreehand(cons,
c.getLabel(), (GeoList) arg[0]);
GeoElement[] ret = { algo.getFunction() };
return ret;
}
throw argErr(app, c, arg[0]);
case 4:
varName = c.getArgument(1).toString(StringTemplate.defaultTemplate);
c.setArgument(1, c.getArgument(2));
c.setArgument(2, c.getArgument(3));
// fall through
case 3:
// file might be saved with old Function[sin(x),1,2]
if (!cons.isFileLoading()) {
fv = null;
if (varName != null || kernelA.getConstruction()
.getRegisteredFunctionVariable() != null) {
if (varName == null) {
varName = kernelA.getConstruction()
.getRegisteredFunctionVariable();
}
fv = new FunctionVariable(kernelA, varName);
int r = c.getArgument(0).replaceVariables(varName, fv);
c.getArgument(0).replaceVariables(varName, fv);
if (r > 0) {
c.getArgument(1).replaceVariables(varName, fv);
}
}
// new code: convert Function[sin(x),1,2] to If[1<=x<=2, sin(x)]
arg = resArgs(c);
if ((ok[0] = (arg[0].isGeoFunctionable()))
&& (ok[1] = (arg[1] instanceof GeoNumberValue))
&& (ok[2] = (arg[2] instanceof GeoNumberValue))) {
label = c.getLabel();
GeoFunction geoFun = ((GeoFunctionable) arg[0])
.getGeoFunction();
GeoNumberValue low = (GeoNumberValue) arg[1];
GeoNumberValue high = (GeoNumberValue) arg[2];
if (fv == null) {
fv = new FunctionVariable(kernelA);
}
// construct the equivalent of parsing a<=x<=b
ExpressionNode left = new ExpressionNode(kernelA, low,
Operation.LESS_EQUAL, fv);
ExpressionNode right = new ExpressionNode(kernelA, fv,
Operation.LESS_EQUAL, high);
ExpressionNode interval = new ExpressionNode(kernelA, left,
Operation.AND_INTERVAL, right);
Function intervalFun = new Function(interval, fv);
AlgoDependentFunction intervalAlgo = new AlgoDependentFunction(
cons, intervalFun, false);
GeoFunction intervalGeo = intervalAlgo.getFunction();
ArrayList<GeoFunction> conditions = new ArrayList<GeoFunction>();
conditions.add(intervalGeo);
mayUseIndependent = false;
// copied from CmdIf from here
expr = new ExpressionNode(kernelA,
wrap(conditions.get(0), fv, mayUseIndependent),
Operation.IF, wrap(geoFun, fv, mayUseIndependent));
Function fun = new Function(expr, fv);
GeoFunction gf;
if (mayUseIndependent) {
gf = new GeoFunction(cons, fun);
} else {
AlgoDependentFunction algo = new AlgoDependentFunction(
cons, fun, true);
gf = algo.getFunction();
}
gf.setLabel(label);
gf.validate(label == null);
return new GeoElement[] { gf };
}
throw argErr(app, c, getBadArg(ok, arg));
}
// old code, just for when file loading
if (varName != null || kernelA.getConstruction()
.getRegisteredFunctionVariable() != null) {
if (varName == null) {
varName = kernelA.getConstruction()
.getRegisteredFunctionVariable();
}
fv = new FunctionVariable(kernelA, varName);
int r = c.getArgument(0).replaceVariables(varName, fv);
c.getArgument(0).replaceVariables(varName, fv);
if (r > 0) {
boolean oldFlag = kernelA.getConstruction()
.isSuppressLabelsActive();
kernelA.getConstruction().setSuppressLabelCreation(true);
c.getArgument(1).resolveVariables(argInfo);
c.getArgument(2).resolveVariables(argInfo);
EvalInfo silent = new EvalInfo(false);
GeoFunction condFun;
if (c.getArgument(0).unwrap() instanceof Command) {
condFun = (GeoFunction) kernelA.getAlgebraProcessor()
.processCommand(
(Command) c.getArgument(0).unwrap(),
silent)[0];
} else {
c.getArgument(0).resolveVariables(argInfo);
condFun = (GeoFunction) kernelA.getAlgebraProcessor()
.processFunction(
new Function(c.getArgument(0), fv),
silent)[0];
}
GeoElement low = kernelA.getAlgebraProcessor()
.processExpressionNode(c.getArgument(1), silent)[0];
GeoElement high = kernelA.getAlgebraProcessor()
.processExpressionNode(c.getArgument(2), silent)[0];
if (!(low instanceof NumberValue)) {
throw argErr(app, c, low);
}
if (!(high instanceof NumberValue)) {
throw argErr(app, c, high);
}
c.getArgument(1).replaceVariables(varName, fv);
c.getArgument(0).resolveVariables(argInfo);
kernelA.getConstruction().setSuppressLabelCreation(oldFlag);
return new GeoElement[] { Function(c.getLabel(), condFun,
(GeoNumberValue) low, (GeoNumberValue) high) };
}
}
arg = resArgs(c);
if ((ok[0] = (arg[0].isGeoFunctionable()))
&& (ok[1] = (arg[1] instanceof GeoNumberValue))
&& (ok[2] = (arg[2] instanceof GeoNumberValue))) {
GeoElement[] ret = { Function(c.getLabel(),
((GeoFunctionable) arg[0]).getGeoFunction(),
(GeoNumberValue) arg[1], (GeoNumberValue) arg[2]) };
return ret;
}
throw argErr(app, c, getBadArg(ok, arg));
default:
throw argNumErr(app, c, n);
}
}
private ExpressionNode wrap(GeoFunction boolFun, FunctionVariable fv,
boolean mayUseIndependent) {
if (!mayUseIndependent) {
return new ExpressionNode(kernelA, boolFun, Operation.FUNCTION, fv);
}
return boolFun.getFunctionExpression().deepCopy(kernelA)
.traverse(VariablePolyReplacer.getReplacer(fv)).wrap();
}
/**
* function limited to interval [a, b]
*/
final private GeoFunction Function(String label, GeoFunction f,
GeoNumberValue a, GeoNumberValue b) {
AlgoFunctionInterval algo = new AlgoFunctionInterval(cons, label, f, a,
b);
GeoFunction g = algo.getFunction();
return g;
}
}