package org.geogebra.common.kernel.arithmetic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.geogebra.common.gui.view.spreadsheet.RelativeCopy;
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.commands.CommandProcessor;
import org.geogebra.common.kernel.geos.GeoDummyVariable;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoElementSpreadsheet;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.main.App;
import org.geogebra.common.plugin.Operation;
/**
* Traversing objects are allowed to traverse through an Equation, MyList,
* ExpressionNode and MyVecNode(3D) structure to perform some action, e.g.
* replace one type of objects by another or just count some occurences of
* certain types of objects.
*
* Each public class in this file which implements Traversing solves a usual
* task. To support transparency and good coding style, such tasks should be
* called by the same convention.
*
* Example code which collects GeoElements in an ExpressionNode with
* multiplicities: {@code
* ExpressionNode root = ...;
* HashMap<GeoElement, Integer> gSet = new HashMap<GeoElement, Integer>();
* GeoCollector gc = GeoCollector.getCollector(gSet);
* root.traverse(gc);
* } Now the GeoElements are collected in {@code gSet}.
*
* @author Zbynek Konecny
*/
public interface Traversing {
/**
* Processes a value locally (no recursion)
*
* @param ev
* value to process
* @return processed value
*/
public ExpressionValue process(final ExpressionValue ev);
/**
* Replaces one object by another
*/
public class Replacer implements Traversing {
private ExpressionValue oldObj;
private ExpressionValue newObj;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev == oldObj) {
return newObj;
}
return ev;
}
private static Replacer replacer = new Replacer();
/**
* Creates a replacer
*
* @param original
* object to be replaced
* @param replacement
* replacement
* @return replacer
*/
public static Replacer getReplacer(ExpressionValue original,
ExpressionValue replacement) {
replacer.oldObj = original;
replacer.newObj = replacement;
return replacer;
}
}
/**
* Like replacer, but creates deep copies
*
*/
public class CopyReplacer implements Traversing {
private ExpressionValue oldObj;
private ExpressionValue newObj;
private Kernel kernel;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev == oldObj) {
newObj = newObj.deepCopy(kernel);
return newObj;
}
return ev;
}
private static CopyReplacer replacer = new CopyReplacer();
/**
* Creates a replacer
*
* @param original
* object to be replaced
* @param replacement
* replacement
* @param kernel
* kernel for copies
* @return replacer
*/
public static CopyReplacer getReplacer(ExpressionValue original,
ExpressionValue replacement, Kernel kernel) {
replacer.oldObj = original;
replacer.newObj = replacement;
replacer.kernel = kernel;
return replacer;
}
}
/**
* Replaces dummy variable with given name
*
*/
public class CommandReplacer implements Traversing {
private Kernel kernel;
private boolean cas;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Command) {
Command c = (Command) ev;
String cmdName = kernel.getApplication()
.getReverseCommand(c.getName());
if (CommandProcessor.isCmdName(cmdName)
|| kernel.getMacro(c.getName()) != null) {
return ev;
}
MyList argList = new MyList(kernel);
for (int i = 0; i < c.getArgumentNumber(); i++) {
argList.addListElement(c.getItem(i).traverse(this));
}
ExpressionValue var = cas
? new GeoDummyVariable(kernel.getConstruction(),
c.getName())
: new Variable(kernel, c.getName());
return new ExpressionNode(kernel, var, Operation.FUNCTION_NVAR,
argList);
}
return ev;
}
private static CommandReplacer replacer = new CommandReplacer();
/**
* @param kernel
* kernel in which resulting variables live (also needed to
* check which commands are valid)
* @param cas
* whether this is for CAS
* @return replacer
*/
public static CommandReplacer getReplacer(Kernel kernel, boolean cas) {
replacer.kernel = kernel;
replacer.cas = cas;
return replacer;
}
}
/**
* Replaces dummy variable with given name
*
*/
public class CommandFunctionReplacer implements Traversing {
private String fn;
private GeoElement function;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev.isGeoElement() && fn
.equalsIgnoreCase(((GeoElement) ev).getLabelSimple())) {
return function;
}
if (ev instanceof Command && fn.equals(((Command) ev).getName())) {
Command c = (Command) ev;
MyList argList = new MyList(c.getKernel());
for (int i = 0; i < c.getArgumentNumber(); i++) {
argList.addListElement(c.getItem(i).traverse(this));
}
return new ExpressionNode(c.getKernel(), function,
Operation.FUNCTION_NVAR, argList);
}
return ev;
}
/**
* @param app
* application (needed to check which commands are valid)
* @param fn
* functionName
* @param function
* function
* @return replacer
*/
public static CommandFunctionReplacer getReplacer(App app, String fn,
GeoElement function) {
CommandFunctionReplacer replacer = new CommandFunctionReplacer();
replacer.fn = fn;
replacer.function = function;
return replacer;
}
}
/**
* Replaces all commands "ggbvect" by their first argument, sets the CAS
* vector flag for future serialization
*
*/
public class GgbVectRemover implements Traversing {
private static final GgbVectRemover remover = new GgbVectRemover();
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Command) {
Command command = (Command) ev;
if (command.getName().equals("ggbvect")) {
ExpressionNode en = command.getArgument(0);
ExpressionValue unwrapped = en.unwrap();
if (unwrapped instanceof MyVecNode) {
MyVecNode vecNode = (MyVecNode) unwrapped;
vecNode.setCASVector();
return vecNode;
} else if (unwrapped instanceof MyVec3DNode) {
MyVec3DNode vec3DNode = (MyVec3DNode) unwrapped;
vec3DNode.setCASVector();
return vec3DNode;
}
}
}
return ev;
}
/**
* @return instance of this traversing
*/
public static GgbVectRemover getInstance() {
return remover;
}
}
/**
* Replaces variables and polynomials
*
*/
public class VariablePolyReplacer implements Traversing {
private FunctionVariable fv;
private int replacements;
@Override
public ExpressionValue process(ExpressionValue ev) {
if ((ev instanceof Variable || ev instanceof FunctionVariable
|| ev instanceof GeoDummyVariable)
&& fv.toString(StringTemplate.defaultTemplate).equals(
ev.toString(StringTemplate.defaultTemplate))) {
replacements++;
return fv;
}
return ev;
}
/**
* @return number of replacements since getReplacer was called
*/
public int getReplacements() {
return replacements;
}
private static VariablePolyReplacer replacer = new VariablePolyReplacer();
/**
* @param fv
* function variable
* @return replacer
*/
public static VariablePolyReplacer getReplacer(FunctionVariable fv) {
replacer.fv = fv;
return replacer;
}
}
/**
* Replaces dummy variable with given name
*
*/
public class GeoDummyReplacer implements Traversing {
private String var;
private ExpressionValue newObj;
private boolean didReplacement;
private boolean replaceFVs;
@Override
public ExpressionValue process(ExpressionValue ev) {
boolean hitClass = ev instanceof GeoDummyVariable
|| (replaceFVs && ev instanceof FunctionVariable);
if (!hitClass || !var
.equals(ev.toString(StringTemplate.defaultTemplate))) {
return ev;
}
didReplacement = true;
return newObj;
}
private static GeoDummyReplacer replacer = new GeoDummyReplacer();
/**
* @param varStr
* variable name
* @param replacement
* replacement object
* @param replaceFVs
* whether function variables should be replaced also
* @return replacer
*/
public static GeoDummyReplacer getReplacer(String varStr,
ExpressionValue replacement, boolean replaceFVs) {
replacer.var = varStr;
replacer.newObj = replacement;
replacer.didReplacement = false;
replacer.replaceFVs = replaceFVs;
return replacer;
}
/**
* @return true if a replacement was done since getReplacer() call
*/
public boolean didReplacement() {
return didReplacement;
}
}
/**
* Replaces Variables with given name by given object
*
* @author Zbynek Konecny
*
*/
public class VariableReplacer implements Traversing {
private List<String> vars = new ArrayList<String>();
private List<ExpressionValue> newObjs = new ArrayList<ExpressionValue>();
private int replacements;
private Kernel kernel;
@Override
public ExpressionValue process(ExpressionValue ev) {
ExpressionValue val;
if ((val = contains(ev)) != null) {
return new ExpressionNode(kernel, val);
}
if (!(ev instanceof Variable || ev instanceof FunctionVariable
|| ev instanceof GeoDummyVariable)) {
return ev;
}
if ((val = getVar(
ev.toString(StringTemplate.defaultTemplate))) == null) {
return ev;
}
replacements++;
return val;
}
/**
* @return number of replacements since getReplacer was called
*/
public int getReplacements() {
return replacements;
}
private static ExpressionValue contains(ExpressionValue ev) {
for (int i = 0; i < replacer.newObjs.size(); i++) {
if (replacer.newObjs.get(i) == ev) {
return replacer.newObjs.get(i);
}
}
return null;
}
private static ExpressionValue getVar(String var) {
for (int i = 0; i < replacer.vars.size(); i++) {
if (var.equals(replacer.vars.get(i))) {
return replacer.newObjs.get(i);
}
}
return null;
}
/**
* @param varStr
* variable name
* @param replacement
* replacement object
*/
public static void addVars(String varStr, ExpressionValue replacement) {
replacer.vars.add(varStr);
replacer.newObjs.add(replacement);
}
private static VariableReplacer replacer = new VariableReplacer();
/**
* @param varStr
* variable name
* @param replacement
* replacement object
* @param kernel
* kernel
* @return replacer
*/
public static VariableReplacer getReplacer(String varStr,
ExpressionValue replacement, Kernel kernel) {
replacer.vars.clear();
replacer.newObjs.clear();
replacer.vars.add(varStr);
replacer.newObjs.add(replacement);
replacer.replacements = 0;
replacer.kernel = kernel;
return replacer;
}
/**
* When calling this method, make sure you initialize the replacer with
* the {@link #addVars(String, ExpressionValue)} method
*
* @param kernel1
* kernel
*
* @return replacer
*/
public static VariableReplacer getReplacer(Kernel kernel1) {
replacer.kernel = kernel1;
replacer.vars.clear();
replacer.newObjs.clear();
replacer.replacements = 0;
return replacer;
}
}
/**
* Replaces GeoNumerics with given expression
*/
public class GeoNumericReplacer implements Traversing {
private List<GeoNumeric> geoNums = new ArrayList<GeoNumeric>();
private List<ExpressionValue> newExps = new ArrayList<ExpressionValue>();
private int replacements;
private Kernel kernel;
@Override
public ExpressionValue process(ExpressionValue ev) {
ExpressionValue val;
if ((val = contains(ev)) != null) {
return new ExpressionNode(kernel, val);
}
if (!(ev instanceof GeoNumeric)) {
return ev;
}
if ((val = getGeoNum((GeoNumeric) ev)) == null) {
return ev;
}
replacements++;
return val;
}
/**
* @return number of replacements since getReplacer was called
*/
public int getReplacements() {
return replacements;
}
private static ExpressionValue contains(ExpressionValue ev) {
for (int i = 0; i < replacer.newExps.size(); i++) {
if (replacer.newExps.get(i) == ev) {
return replacer.newExps.get(i);
}
}
return null;
}
private static ExpressionValue getGeoNum(GeoNumeric geoNum) {
for (int i = 0; i < replacer.geoNums.size(); i++) {
if (geoNum.equals(replacer.geoNums.get(i))) {
return replacer.newExps.get(i);
}
}
return null;
}
/**
* @param geoNum
* geoNumeric to replace
* @param replacement
* replacement object
*/
public static void addVars(GeoNumeric geoNum,
ExpressionValue replacement) {
replacer.geoNums.add(geoNum);
replacer.newExps.add(replacement);
}
private static GeoNumericReplacer replacer = new GeoNumericReplacer();
/**
* @param geoNum
* geoNum to replace
* @param replacement
* replacement object
* @param kernel
* kernel
* @return replacer
*/
public static GeoNumericReplacer getReplacer(GeoNumeric geoNum,
ExpressionValue replacement, Kernel kernel) {
replacer.geoNums.clear();
replacer.newExps.clear();
replacer.geoNums.add(geoNum);
replacer.newExps.add(replacement);
replacer.replacements = 0;
replacer.kernel = kernel;
return replacer;
}
/**
* When calling this method, make sure you initialize the replacer with
* the {@link #addVars(GeoNumeric, ExpressionValue)} method
*
* @param kernel1
* kernel
*
* @return replacer
*/
public static GeoNumericReplacer getReplacer(Kernel kernel1) {
replacer.kernel = kernel1;
replacer.geoNums.clear();
replacer.newExps.clear();
replacer.replacements = 0;
return replacer;
}
}
/**
* Renames Spreadsheet Variables with new name according to offset (dx,dy)
*
* @author michael
*
*/
public class SpreadsheetVariableRenamer implements Traversing {
private int dx;
private int dy;
private ArrayList<Variable> variables = new ArrayList<Variable>();
/**
* Renames Spreadsheet Variables with new name according to offset
* (dx,dy)
*
* @param dx
* x-offset
* @param dy
* y-offset
*/
public SpreadsheetVariableRenamer(int dx, int dy) {
this.dx = dx;
this.dy = dy;
variables.clear();
}
@Override
public ExpressionValue process(ExpressionValue ev) {
// check variables to avoid problem with updating twice
// eg If[0 < A1 < 5, 0, 100] going to If[0 < A3 < 5, 0, 100]
if (ev instanceof Variable && !variables.contains(ev)) {
Variable v = (Variable) ev;
String name = v.getName(StringTemplate.defaultTemplate);
// Log.debug("found VARIABLE: "+name);
if (GeoElementSpreadsheet.spreadsheetPattern.test(name)) {
String newName = RelativeCopy.updateCellNameWithOffset(name,
dx, dy);
// Log.debug("FOUND SPREADSHEET VARIABLE: "+name + " -> " +
// newName);
// make sure new cell is autocreated if it doesn't exist
// already
v.getKernel().getConstruction().lookupLabel(newName, true);
// Log.debug("setting new name to: "+newName);
v.setName(newName);
variables.add(v);
}
} else if (ev instanceof GeoElement) {
GeoElement geo = (GeoElement) ev;
String name = geo.getLabelSimple();
if (GeoElementSpreadsheet.spreadsheetPattern.test(name)) {
String newName = RelativeCopy.updateCellNameWithOffset(name,
dx, dy);
// make sure new cell is autocreated if it doesn't exist
// already
// and return it
return geo.getConstruction().lookupLabel(newName, true);
}
}
return ev;
}
}
/**
* Replaces undefined variables by sliders
*
* @author michael
*
*/
public class ReplaceUndefinedVariables implements Traversing {
private final Kernel kernel;
private String[] except;
private TreeSet<GeoNumeric> undefined;
/**
* Replaces undefined variables by sliders
*
* @param kernel
* kernel
* @param undefined
* list of undefined vars (write only)
* @param skip
* list of labels to skip
*
*/
public ReplaceUndefinedVariables(Kernel kernel,
TreeSet<GeoNumeric> undefined, String[] skip) {
this.kernel = kernel;
this.undefined = undefined;
this.except = skip;
}
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Variable) {
Variable v = (Variable) ev;
String name = v.getName(StringTemplate.defaultTemplate);
ExpressionValue replace = kernel.lookupLabel(name, true,
kernel.isResolveUnkownVarsAsDummyGeos());
if (replace == null) {
replace = Variable.replacement(kernel, name);
}
if (replace instanceof Variable
&& !name.equals(kernel.getConstruction()
.getRegisteredFunctionVariable())
&& !isException(name)) {
name = ((Variable) replace)
.getName(StringTemplate.defaultTemplate);
boolean old = kernel.getConstruction()
.isSuppressLabelsActive();
kernel.getConstruction().setSuppressLabelCreation(false);
GeoNumeric slider = new GeoNumeric(kernel.getConstruction(),
1);
slider.setLabel(name);
kernel.getConstruction().setSuppressLabelCreation(old);
undefined.add(slider);
boolean visible = !kernel.getApplication()
.showView(App.VIEW_ALGEBRA)
|| kernel.getApplication()
.showAutoCreatedSlidersInEV();
GeoNumeric.setSliderFromDefault(slider, false, visible);
return ev;
}
}
return ev;
}
private boolean isException(String name) {
if (except == null) {
return false;
}
for (int i = 0; i < except.length; i++) {
if (except[i].equals(name)) {
return true;
}
}
return false;
}
}
/**
* Collect undefined variables
*
* @author michael
*
*/
public class CollectUndefinedVariables implements Traversing {
private TreeSet<String> tree = new TreeSet<String>();
private TreeSet<String> localTree = new TreeSet<String>();
/**
*
* @return list of undefined variables (repeats removed)
*/
public TreeSet<String> getResult() {
tree.removeAll(localTree);
return tree;
}
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Variable) {
Variable v = (Variable) ev;
String name = v.getName(StringTemplate.defaultTemplate);
if (v.getKernel().getApplication().getParserFunctions()
.isReserved(name)) {
return ev;
}
ExpressionValue ret;
ret = v.getKernel().lookupLabel(name);
if (ret == null) {
ret = Variable.replacement(v.getKernel(), name);
}
if (ret instanceof Variable && !v.getKernel().getConstruction()
.isRegistredFunctionVariable(name)) {
// Log.debug("found undefined variable: "
// + ((Variable) ret)
// .getName(StringTemplate.defaultTemplate));
tree.add(((Variable) ret)
.getName(StringTemplate.defaultTemplate));
}
} else if (ev instanceof Command) {// Iteration[a+1, a, {1},4]
Command com = (Command) ev;
if (("Sequence".equals(com.getName())
&& com.getArgumentNumber() > 2)
|| "KeepIf".equals(com.getName())
|| "CountIf".equals(com.getName())) {
localTree.add(com.getArgument(1)
.toString(StringTemplate.defaultTemplate));
} else if ("Surface".equals(com.getName())) {
int len = com.getArgumentNumber();
if (len > 6) {
localTree.add(com.getArgument(len - 3)
.toString(StringTemplate.defaultTemplate));
localTree.add(com.getArgument(len - 6)
.toString(StringTemplate.defaultTemplate));
}
} else if ("CurveCartesian".equals(com.getName())) {
int len = com.getArgumentNumber();
localTree.add(com.getArgument(len - 3)
.toString(StringTemplate.defaultTemplate));
} else if (("IterationList".equals(com.getName())
|| "Iteration".equals(com.getName()))
&& com.getArgumentNumber() > 3) {
for (int i = 1; i < com.getArgumentNumber() - 2; i++) {
localTree.add(com.getArgument(i)
.toString(StringTemplate.defaultTemplate));
}
} else if ("Zip".equals(com.getName())) {
for (int i = 1; i < com.getArgumentNumber(); i += 2) {
localTree.add(com.getArgument(i)
.toString(StringTemplate.defaultTemplate));
}
} else if ("TriangleCurve".equals(com.getName())) {
localTree.add("A");
localTree.add("B");
localTree.add("C");
}
}
return ev;
}
}
/**
* Collect FunctionVariables
*
* @author michael
*
*/
public class CollectFunctionVariables implements Traversing {
private ArrayList<FunctionVariable> al = new ArrayList<FunctionVariable>();
/**
*
* @return list of undefined variables (repeats removed)
*/
public ArrayList<FunctionVariable> getResult() {
return al;
}
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof FunctionVariable) {
al.add((FunctionVariable) ev);
}
return ev;
}
}
/**
* Replaces arbconst(), arbint(), arbcomplex() by auxiliary numerics
*/
public class ArbconstReplacer implements Traversing {
private MyArbitraryConstant arbconst;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (!ev.isExpressionNode()) {
return ev;
}
ExpressionNode en = (ExpressionNode) ev;
if (en.getOperation() == Operation.MULTIPLY) {
if (en.getLeft() != null && en.getLeftTree()
.getOperation() == Operation.ARBCONST) {
GeoNumeric newLeft = arbconst.nextConst(
en.getLeftTree().getLeft().evaluateDouble());
newLeft.setValue(1);
newLeft.update();
en.getRight().traverse(this);
en.setLeft(newLeft);
}
if (en.getRight() != null && en.getRightTree()
.getOperation() == Operation.ARBCONST) {
GeoNumeric newRight = arbconst.nextConst(
en.getRightTree().getLeft().evaluateDouble());
newRight.setValue(1);
newRight.update();
en.getLeft().traverse(this);
en.setRight(newRight);
}
return en;
}
if (en.getOperation() == Operation.ARBCONST) {
return arbconst.nextConst(en.getLeft().evaluateDouble());
}
if (en.getOperation() == Operation.ARBINT) {
return arbconst.nextInt(en.getLeft().evaluateDouble());
}
if (en.getOperation() == Operation.ARBCOMPLEX) {
return arbconst.nextComplex(en.getLeft().evaluateDouble());
}
return en;
}
private static ArbconstReplacer replacer = new ArbconstReplacer();
/**
* @param arbconst
* arbitrary constant handler
* @return replacer
*/
public static ArbconstReplacer getReplacer(
MyArbitraryConstant arbconst) {
replacer.arbconst = arbconst;
return replacer;
}
}
/**
* Replaces powers by roots or vice versa
*/
public class PowerRootReplacer implements Traversing {
private boolean toRoot;
/** functions with 100th root are numerically unstable */
private static int MAX_ROOT = 99;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (!ev.isExpressionNode()) {
return ev;
}
((ExpressionNode) ev).replacePowersRoots(toRoot, MAX_ROOT);
return ev;
}
private static PowerRootReplacer replacer = new PowerRootReplacer();
/**
* @param toRoot
* true to replace exponents by roots
* @return replacer
*/
public static PowerRootReplacer getReplacer(boolean toRoot) {
replacer.toRoot = toRoot;
return replacer;
}
}
/**
* Replaces diff function comming from GIAC
*
* @author Zbynek Konecny
*
*/
public class DiffReplacer implements Traversing {
@Override
public ExpressionValue process(ExpressionValue ev) {
if (!ev.isExpressionNode()) {
return ev;
}
ExpressionNode en = (ExpressionNode) ev;
if (en.getOperation() != Operation.DIFF) {
return ev;
}
Kernel kernel = en.getKernel();
ExpressionValue expr = en.getLeft();
ExpressionValue var = en.getRight();
ExpressionValue deg;
if (expr instanceof MyNumberPair) {
var = ((MyNumberPair) expr).y;
expr = ((MyNumberPair) expr).x;
deg = en.getRight();
} else {
deg = new MyDouble(kernel, 1);
}
String expStr = expr.toString(StringTemplate.defaultTemplate);
int nameEnd = expStr.indexOf('(');
if (expStr.indexOf('[') > 0) {
nameEnd = nameEnd > 0 ? Math.min(nameEnd, expStr.indexOf('['))
: expStr.indexOf('[');
}
String funLabel = nameEnd > 0 ? expStr.substring(0, nameEnd)
: expStr;
ExpressionValue diffArg = new MyDouble(kernel, Double.NaN);
ExpressionValue mult = new MyDouble(kernel, 1);
if (expr.unwrap() instanceof Command) {
diffArg = ((Command) expr.unwrap()).getArgument(0);
if (diffArg.unwrap() instanceof FunctionVariable && diffArg
.toString(StringTemplate.defaultTemplate)
.equals(var.toString(StringTemplate.defaultTemplate))) {
// keep mult
} else if (Kernel.isEqual(deg.evaluateDouble(), 1)) {
CASGenericInterface cas = kernel.getGeoGebraCAS()
.getCurrentCAS();
Command derivCommand = new Command(kernel, "Derivative",
false);
derivCommand.addArgument(diffArg.wrap());
derivCommand.addArgument(var.wrap());
derivCommand.addArgument(deg.wrap());
mult = cas.evaluateToExpression(derivCommand, null, kernel);
} else {
mult = new MyDouble(kernel, Double.NaN);
}
}
// derivative of f gives f'
ExpressionNode derivative = new ExpressionNode(kernel,
new Variable(kernel, funLabel), // function label "f"
Operation.DERIVATIVE, deg);
// function of given variable gives f'(t)
return new ExpressionNode(kernel, derivative, Operation.FUNCTION,
diffArg).multiplyR(mult); // Variable
}
/**
* Singleton instance
*/
public static final DiffReplacer INSTANCE = new DiffReplacer();
}
/**
* Goes through the ExpressionValue and collects all derivatives from
* expression nodes into arrays
*/
public class PrefixRemover implements Traversing {
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Variable) {
return new Variable(((Variable) ev).getKernel(),
ev.toString(StringTemplate.defaultTemplate)
.replace(Kernel.TMP_VARIABLE_PREFIX, ""));
}
return ev;
}
private static PrefixRemover collector = new PrefixRemover();
/**
* Resets and returns the collector
*
* @return derivative collector
*/
public static PrefixRemover getCollector() {
return collector;
}
}
/**
* Goes through the ExpressionValue and collects all derivatives from
* expression nodes into arrays
*/
public class CommandCollector implements Traversing {
private Set<Command> commands;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Command) {
commands.add((Command) ev);
}
return ev;
}
private static CommandCollector collector = new CommandCollector();
/**
* Resets and returns the collector
*
* @param commands
* set into which we want to collect the commands
* @return derivative collector
*/
public static CommandCollector getCollector(Set<Command> commands) {
collector.commands = commands;
return collector;
}
}
/**
* Collects all function variables
*
* @author Zbynek Konecny
*/
public class FVarCollector implements Traversing {
private Set<String> commands;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Equation) {
return ev.wrap();
}
if (ev instanceof FunctionVariable) {
commands.add(((FunctionVariable) ev).getSetVarString());
}
return ev;
}
private static FVarCollector collector = new FVarCollector();
/**
* Resets and returns the collector
*
* @param commands
* set into which we want to collect the commands
* @return derivative collector
*/
public static FVarCollector getCollector(Set<String> commands) {
collector.commands = commands;
return collector;
}
}
/**
* Collects all geos with multiplicities
*
* @author Zoltan Kovacs
*/
public class GeoCollector implements Traversing {
private HashMap<GeoElement, Integer> commands;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Equation) {
return ev.wrap();
}
if (ev instanceof GeoElement) {
int occurrence = 0;
if (commands.containsKey(ev)) {
occurrence = commands.get(ev);
}
commands.put((GeoElement) ev, occurrence + 1);
}
return ev;
}
private static GeoCollector collector = new GeoCollector();
/**
* Resets and returns the collector
*
* @param commands
* set into which we want to collect the commands
* @return derivative collector
*/
public static GeoCollector getCollector(
HashMap<GeoElement, Integer> commands) {
collector.commands = commands;
return collector;
}
}
/**
* Collects all function variables
*
* @author Zbynek Konecny
*/
public class NonFunctionCollector implements Traversing {
private Set<String> commands;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof ExpressionNode) {
ExpressionNode en = (ExpressionNode) ev;
if (en.getRight() instanceof GeoDummyVariable) {
add(((GeoDummyVariable) en.getRight()));
}
if (en.getOperation() == Operation.FUNCTION
|| en.getOperation() == Operation.FUNCTION_NVAR
|| en.getOperation() == Operation.DERIVATIVE) {
return en;
}
if (en.getLeft() instanceof GeoDummyVariable) {
add(((GeoDummyVariable) en.getLeft()));
}
}
return ev;
}
private void add(GeoDummyVariable dummy) {
String str = dummy.toString(StringTemplate.defaultTemplate);
if (dummy.getKernel().getApplication().getParserFunctions()
.isReserved(str)) {
return;
}
commands.add(str);
}
private static NonFunctionCollector collector = new NonFunctionCollector();
/**
* Resets and returns the collector
*
* @param commands
* set into which we want to collect the commands
* @return derivative collector
*/
public static NonFunctionCollector getCollector(Set<String> commands) {
collector.commands = commands;
return collector;
}
}
/**
* Collects all dummy variables and function variables except those that are
* in the role of a function eg. for f(x) we will collect x, but not f
*
* @author Balazs Bencze
*/
public class DummyVariableCollector implements Traversing {
private Set<String> commands;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof ExpressionNode) {
ExpressionNode en = (ExpressionNode) ev;
if (isVariable(en.getRight())) {
add(en.getRight());
}
if (en.getOperation() == Operation.FUNCTION
|| en.getOperation() == Operation.FUNCTION_NVAR
|| en.getOperation() == Operation.DERIVATIVE) {
return en;
}
if (isVariable(en.getLeft())) {
add(en.getLeft());
}
}
return ev;
}
private static boolean isVariable(ExpressionValue right) {
return right instanceof GeoDummyVariable
|| right instanceof FunctionVariable
|| right instanceof Variable;
}
private void add(ExpressionValue dummy) {
String str = dummy.toString(StringTemplate.defaultTemplate);
commands.add(str);
}
private static DummyVariableCollector collector = new DummyVariableCollector();
/**
* Resets and returns the collector
*
* @param commands
* set into which we want to collect the commands
* @return derivative collector
*/
public static DummyVariableCollector getCollector(
Set<String> commands) {
collector.commands = commands;
return collector;
}
}
/**
* Collects all GeoNumeric labels
*/
public class GeoNumericLabelCollector implements Traversing {
private Set<String> labels;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof ExpressionNode) {
ExpressionNode en = (ExpressionNode) ev;
if (en.getRight() instanceof GeoNumeric) {
add(en.getRight());
}
if (en.getLeft() instanceof GeoNumeric) {
add(en.getLeft());
}
}
return ev;
}
private void add(ExpressionValue geoNum) {
String str = ((GeoNumeric) geoNum).getLabelSimple();
if (str != null) {
labels.add(str);
}
}
private static GeoNumericLabelCollector collector = new GeoNumericLabelCollector();
/**
* Resets and returns the collector
*
* @param labels
* set into which we want to collect the geoNumeric labels
* @return derivative collector
*/
public static GeoNumericLabelCollector getCollector(
Set<String> labels) {
collector.labels = labels;
return collector;
}
}
/**
* Replaces function calls by multiplications in cases where left argument
* is clearly not a function (see NonFunctionCollector)
*
* @author Zbynek Konecny
*/
public class NonFunctionReplacer implements Traversing {
private Set<String> commands;
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof ExpressionNode) {
ExpressionNode en = (ExpressionNode) ev;
if (en.getOperation() == Operation.POWER
&& en.getLeft() instanceof Command) {
Command c = (Command) en.getLeft();
if (commands.contains(c.getName())) {
return new GeoDummyVariable(
c.getKernel().getConstruction(), c.getName())
.wrap()
.multiply(c.getArgument(0)
.traverse(this).wrap()
.power(en.getRight()));
}
}
if (en.getOperation() == Operation.FACTORIAL
&& en.getLeft() instanceof Command) {
Command c = (Command) en.getLeft();
if (commands.contains(c.getName())) {
return new GeoDummyVariable(
c.getKernel().getConstruction(), c.getName())
.wrap().multiply(
c.getArgument(0).traverse(this)
.wrap().factorial());
}
}
if (en.getOperation() == Operation.SQRT_SHORT
&& en.getLeft() instanceof Command) {
Command c = (Command) en.getLeft();
if (commands.contains(c.getName())) {
return new GeoDummyVariable(
c.getKernel().getConstruction(), c.getName())
.wrap().sqrt().multiply(c.getArgument(0)
.traverse(this));
}
}
}
if (ev instanceof Command) {
Command c = (Command) ev;
if (commands.contains(c.getName())
&& c.getArgumentNumber() == 1) {
return new GeoDummyVariable(c.getKernel().getConstruction(),
c.getName()).wrap()
.multiply(c.getArgument(0).traverse(this));
}
}
return ev;
}
private static NonFunctionReplacer collector = new NonFunctionReplacer();
/**
* Resets and returns the collector
*
* @param commands
* set into which we want to collect the commands
* @return derivative collector
*/
public static NonFunctionReplacer getCollector(Set<String> commands) {
collector.commands = commands;
return collector;
}
}
/**
* Returns the RHS side of an equation if LHS is y. E.g. y=x! returns x!.
*
* @author Balazs Bencze
*/
public class FunctionCreator implements Traversing {
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Equation) {
Equation eq = (Equation) ev;
if (eq.getLHS() != null
&& eq.getLHS().getLeft() instanceof GeoDummyVariable) {
GeoDummyVariable gdv = (GeoDummyVariable) eq.getLHS()
.getLeft();
if (gdv.toString(StringTemplate.defaultTemplate)
.equals("y")) {
return eq.getRHS().unwrap();
}
}
}
return ev;
}
private static FunctionCreator creator = new FunctionCreator();
/**
* @return instance of FunctionCreater
*/
public static FunctionCreator getCreator() {
return creator;
}
}
/**
* Removes commands from a given expression and returns the first argument
* of the command
*/
public class CommandRemover implements Traversing {
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Command) {
Command ec = (Command) ev;
for (int i = 0; i < commands.length; i++) {
if (ec.getName().equals(commands[i])) {
return ec.getArgument(0).unwrap();
}
}
}
return ev;
}
private static CommandRemover remover = new CommandRemover();
private static String[] commands;
/**
* Get the remover
*
* @param commands1
* commands to be removed
* @return command remover
*/
public static CommandRemover getRemover(String... commands1) {
commands = commands1;
return remover;
}
}
/**
* Replaces some of the unknown commands from CAS to known
* commands/expression node values to GeoGebra
*
* @author Balazs Bencze
*/
public class CASCommandReplacer implements Traversing {
@Override
public ExpressionValue process(ExpressionValue ev) {
if (ev instanceof Command) {
Command ec = (Command) ev;
if ("x".equals(ec.getName())) {
return new ExpressionNode(ec.getKernel(), ec.getArgument(0),
Operation.XCOORD, null);
} else if ("y".equals(ec.getName())) {
return new ExpressionNode(ec.getKernel(), ec.getArgument(0),
Operation.YCOORD, null);
} else if ("z".equals(ec.getName())) {
return new ExpressionNode(ec.getKernel(), ec.getArgument(0),
Operation.ZCOORD, null);
}
}
return ev;
}
/**
* Replacer object
*/
public final static CASCommandReplacer replacer = new CASCommandReplacer();
}
}