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.AlgoDependentFunctionNVar; import org.geogebra.common.kernel.algos.AlgoIf; import org.geogebra.common.kernel.arithmetic.Command; 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.FunctionNVar; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.arithmetic.FunctionalNVar; import org.geogebra.common.kernel.arithmetic.GetItem; import org.geogebra.common.kernel.arithmetic.Inspecting; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.arithmetic.MyList; import org.geogebra.common.kernel.arithmetic.MyNumberPair; import org.geogebra.common.kernel.arithmetic.Traversing.VariablePolyReplacer; import org.geogebra.common.kernel.geos.GeoBoolean; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoFunction; import org.geogebra.common.kernel.geos.GeoFunctionNVar; import org.geogebra.common.kernel.geos.GeoFunctionable; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.main.MyError; import org.geogebra.common.plugin.Operation; import org.geogebra.common.util.debug.Log; /** * If[ <GeoBoolean>, <GeoElement> ] If[ <GeoBoolean>, <GeoElement>, * <GeoElement> ] */ public class CmdIf extends CommandProcessor { /** * Create new command processor * * @param kernel * kernel */ public CmdIf(Kernel kernel) { super(kernel); } // @Override // public ExpressionValue simplify(Command c) { // return expandIf(kernelA, c); // } /** * @param kernelA * kernel * @param c * collection of arguments * @return node with OPERATION_IF_ELSE or OPERATION_IF_LIST */ public static ExpressionNode expandIf(Kernel kernelA, GetItem c) { MyList conditions = new MyList(kernelA); MyList alternatives = new MyList(kernelA); int num = c.getLength(); if (num == 3) { return new ExpressionNode(kernelA, new MyNumberPair(kernelA, c.getItem(0), c.getItem(1)), Operation.IF_ELSE, c.getItem(2)); } for (int i = 0; i < num - 1; i += 2) { conditions.addListElement(c.getItem(i)); } for (int i = 1; i < num; i += 2) { alternatives.addListElement(c.getItem(i)); } if (MyDouble.isOdd(num)) { alternatives.addListElement(c.getItem(num - 1)); } Log.debug(conditions.size() + ":" + alternatives.size()); return new ExpressionNode(kernelA, conditions, Operation.IF_LIST, alternatives); } // @Override /* * public ExpressionValue simplify(Command c) { ExpressionNode ret = null; * if (c.getArgumentNumber() == 2) { ret = new ExpressionNode(kernelA, * c.getArgument(0), Operation.IF, c.getArgument(1)); } if * (c.getArgumentNumber() == 3) { ret = new ExpressionNode(kernelA, new * MyNumberPair(kernelA, c.getArgument(0), c.getArgument(1)), * Operation.IF_ELSE, c.getArgument(2)); } if (ret != null) { * ret.resolveVariables(); return ret; } return super.simplify(c); } */ @Override public GeoElement[] process(Command c, EvalInfo info) throws MyError { int n = c.getArgumentNumber(); GeoElement[] arg; if (n < 2) { throw argNumErr(app, c, n); } if (kernelA.getConstruction().getRegisteredFunctionVariable() != null) { String[] varName = kernelA.getConstruction() .getRegisteredFunctionVariables(); FunctionVariable[] fv = new FunctionVariable[varName.length]; int r = kernelA.getAlgebraProcessor() .replaceVariables(c.getArgument(0), varName, fv); if (r > 0) { return specialFunction(c, varName, fv, info); } } arg = resArgs(c, info); if (arg[0] instanceof GeoBoolean) { // standard case: simple boolean condition ArrayList<GeoBoolean> cond = new ArrayList<GeoBoolean>(); ArrayList<GeoElement> alternatives = new ArrayList<GeoElement>(); for (int i = 0; i < n - 1; i += 2) { if (arg[i] instanceof GeoBoolean) { cond.add((GeoBoolean) arg[i]); } else { throw argErr(app, c, arg[i]); } alternatives.add(arg[i + 1]); } if (MyDouble.isOdd(n)) { alternatives.add(arg[n - 1]); } return new AlgoIf(cons, c.getLabel(), cond, alternatives) .getOutput(); } // SPECIAL CASE for functions: // boolean function in x as condition // example: If[ x < 2, x^2, x + 2 ] // DO NOT change instanceof here (see // GeoFunction.isGeoFunctionable()) ArrayList<FunctionalNVar> conditions = new ArrayList<FunctionalNVar>(); ArrayList<FunctionalNVar> functions = new ArrayList<FunctionalNVar>(); int vars = 1; for (int i = 0; i < n - 1; i += 2) { if (arg[i] instanceof FunctionalNVar && ((FunctionalNVar) arg[i]).isBooleanFunction()) { conditions.add((FunctionalNVar) arg[i]); vars = vars > 1 ? vars : ((FunctionalNVar) arg[i]) .getFunctionVariables().length; if ("y".equals(((FunctionalNVar) arg[i]) .getVarString(StringTemplate.defaultTemplate))) { vars = Math.max(vars, 2); } } else { throw argErr(app, c, arg[i]); } vars = checkAdd(c, functions, arg[i + 1], vars); } if (MyDouble.isOdd(n)) { vars = checkAdd(c, functions, arg[n - 1], vars); } return new GeoElement[] { If(c.getLabel(), conditions, functions, vars) }; } private int checkAdd(Command c, ArrayList<FunctionalNVar> functions, GeoElement fn, int vars) { if (fn.isGeoFunctionable() && !(fn instanceof GeoLine)) { /* * if(vars > 1){ GeoFunctionNVar cast = new * GeoFunctionNVar(kernelA.getConstruction()); cast.set(fn); * functions.add(cast); }else { functions.add(((GeoFunctionable) * fn).getGeoFunction()); } */ functions.add(((GeoFunctionable) fn).getGeoFunction()); if ("y".equals(((GeoFunctionable) fn).getGeoFunction() .getVarString(StringTemplate.defaultTemplate))) { return Math.max(vars, 2); } return vars; } else if (fn instanceof GeoFunctionNVar) { functions.add((GeoFunctionNVar) fn); return 2; } else { throw argErr(app, c, fn); } } private GeoElement[] specialFunction(Command c, String[] varName, FunctionVariable[] fv, EvalInfo info) { EvalInfo argInfo = info.withLabels(false); boolean oldFlag = kernelA.getConstruction().isSuppressLabelsActive(); kernelA.getConstruction().setSuppressLabelCreation(true); ArrayList<FunctionalNVar> conditions = new ArrayList<FunctionalNVar>(); ArrayList<FunctionalNVar> functions = new ArrayList<FunctionalNVar>(); int n = c.getArgumentNumber(); int vars = varName.length; for (int i = 0; i < n - 1; i += 2) { kernelA.getAlgebraProcessor().replaceVariables(c.getArgument(i), varName, fv); FunctionalNVar current = resolveFunction(c, i, fv, vars, argInfo); if (current.isBooleanFunction()) { conditions.add(current); } else { throw argErr(app, c, current); } kernelA.getAlgebraProcessor().replaceVariables(c.getArgument(i + 1), varName, fv); vars = checkAdd(c, functions, (GeoElement) resolveFunction(c, i + 1, fv, vars, argInfo), vars); } if (MyDouble.isOdd(n)) { kernelA.getAlgebraProcessor().replaceVariables(c.getArgument(n - 1), varName, fv); vars = checkAdd(c, functions, (GeoElement) resolveFunction(c, n - 1, fv, vars, argInfo), vars); } kernelA.getConstruction().setSuppressLabelCreation(oldFlag); return new GeoElement[] { If(c.getLabel(), conditions, functions, vars) }; } private FunctionalNVar resolveFunction(Command c, int i, FunctionVariable[] fv, int vars, EvalInfo argInfo) { ExpressionNode arg = c.getArgument(i); arg.resolveVariables(argInfo); // If we have a ready function rather than expression, just use it #4674 if (arg.unwrap() instanceof GeoFunction || arg.unwrap() instanceof GeoFunctionNVar) { return (FunctionalNVar) arg.unwrap(); } EvalInfo info = new EvalInfo(false); if (vars < 2) { return (GeoFunction) kernelA.getAlgebraProcessor() .processFunction(new Function(arg, fv[0]), info)[0]; } return (GeoFunctionNVar) kernelA.getAlgebraProcessor() .processFunctionNVar(new FunctionNVar(arg, fv), info)[0]; } /** * If-then-else construct for functions. example: If[ x < 2, x^2, x + 2 ] */ final private GeoElement If(String label, ArrayList<FunctionalNVar> conditions, ArrayList<FunctionalNVar> functions, int vars) { FunctionVariable[] fv; if (vars == conditions.get(0).getFunctionVariables().length) { fv = conditions.get(0).getFunctionVariables(); } else if (cons.getRegisteredFunctionVariable() != null) { int regVars = cons.getRegisteredFunctionVariables().length; fv = new FunctionVariable[regVars]; for (int i = 0; i < fv.length; i++) { fv[i] = new FunctionVariable(kernelA, cons.getRegisteredFunctionVariables()[i]); } } else { fv = new FunctionVariable[vars]; for (int i = 0; i < fv.length; i++) { fv[i] = new FunctionVariable(kernelA, ((char) ('x' + i)) + ""); } } ExpressionNode expr; boolean mayUseIndependent = true; for (int i = 0; i < functions.size(); i++) { if (Inspecting.dynamicGeosFinder.check(functions.get(i)) || (i < conditions.size() && Inspecting.dynamicGeosFinder .check(conditions.get(i)))) { mayUseIndependent = false; break; } } if (functions.size() == 1) { expr = new ExpressionNode(kernelA, wrap(conditions.get(0), fv, mayUseIndependent), Operation.IF, wrap(functions.get(0), fv, mayUseIndependent)); } else if (functions.size() == 2 && conditions.size() == 1) { expr = new ExpressionNode(kernelA, new MyNumberPair(kernelA, wrap(conditions.get(0), fv, mayUseIndependent), wrap(functions.get(0), fv, mayUseIndependent)), Operation.IF_ELSE, wrap(functions.get(1), fv, mayUseIndependent)); } else { MyList cond = new MyList(kernelA), funs = new MyList(kernelA); for (FunctionalNVar f : conditions) { cond.addListElement(wrap(f, fv, mayUseIndependent)); } for (FunctionalNVar f : functions) { funs.addListElement(wrap(f, fv, mayUseIndependent)); } expr = new ExpressionNode(kernelA, cond, Operation.IF_LIST, funs); } if (vars < 2) { Function fun = new Function(expr, fv[0]); GeoFunction gf; if (mayUseIndependent) { gf = new GeoFunction(cons, fun); } else { AlgoDependentFunction algo = new AlgoDependentFunction(cons, fun, true); gf = algo.getFunction(); } if (gf.validate(label == null)) { gf.setLabel(label); gf.validate(label == null); return gf; } throw new MyError(loc, "InvalidFunction"); } FunctionNVar fun = new FunctionNVar(expr, fv); if (mayUseIndependent) { GeoFunctionNVar ret = new GeoFunctionNVar(cons, fun); ret.setLabel(label); return ret; } AlgoDependentFunctionNVar algo = new AlgoDependentFunctionNVar(cons, label, fun); return algo.getFunction(); } private ExpressionNode wrap(FunctionalNVar boolFun, FunctionVariable[] fv, boolean mayUseIndependent) { if (!mayUseIndependent) { if (fv.length == 1) { return new ExpressionNode(kernelA, boolFun, Operation.FUNCTION, fv[0]); } MyList arg = new MyList(kernelA); for (int i = 0; i < fv.length; i++) { arg.addListElement(fv[i]); } return new ExpressionNode(kernelA, boolFun, Operation.FUNCTION_NVAR, arg); } ExpressionValue exp = boolFun.getFunctionExpression().deepCopy(kernelA); for (int i = 0; i < fv.length; i++) { exp = exp.traverse(VariablePolyReplacer.getReplacer(fv[i])); } return exp.wrap(); } }