package org.geogebra.common.cas;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.geogebra.common.kernel.CASException;
import org.geogebra.common.kernel.CASGenericInterface;
import org.geogebra.common.kernel.GeoGebraCasInterface;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic.Command;
import org.geogebra.common.kernel.arithmetic.Equation;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionNodeConstants.StringType;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.MyArbitraryConstant;
import org.geogebra.common.kernel.arithmetic.MyList;
import org.geogebra.common.kernel.arithmetic.Traversing.DummyVariableCollector;
import org.geogebra.common.kernel.arithmetic.ValidExpression;
import org.geogebra.common.kernel.commands.Commands;
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.GeoElementND;
import org.geogebra.common.main.App;
import org.geogebra.common.util.MaxSizeHashMap;
import org.geogebra.common.util.debug.Log;
/**
* This class provides an interface for GeoGebra to use an underlying computer
* algebra system like Giac.
*
* @author Markus Hohenwarter
*/
public class GeoGebraCAS implements GeoGebraCasInterface {
private App app;
private CASparser casParser;
private CASGenericInterface cas;
private ArrayList<String> varSwaps = new ArrayList<String>();
/**
* Creates new CAS interface
*
* @param kernel
* kernel
*/
public GeoGebraCAS(Kernel kernel) {
app = kernel.getApplication();
casParser = new CASparser(kernel.getParser(),
kernel.getApplication().getParserFunctions());
// DO NOT init underlying CAS here to avoid hanging animation,
// see http://www.geogebra.org/trac/ticket/1565
// getCurrentCAS();
}
@Override
public CASparser getCASparser() {
return casParser;
}
@Override
public synchronized CASGenericInterface getCurrentCAS() {
if (cas == null) {
app.setWaitCursor();
initCurrentCAS();
app.setDefaultCursor();
}
return cas;
}
/**
* Initializes underlying CAS
*/
public synchronized void initCurrentCAS() {
if (cas == null) {
setCurrentCAS();
}
}
@Override
public synchronized void setCurrentCAS() {
try {
cas = getGiac();
app.getSettings().getCasSettings().addListener(cas);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @return Giac
*/
private synchronized CASGenericInterface getGiac() {
if (cas == null) {
cas = app.getCASFactory().newGiac(casParser, app.getKernel());
}
return cas;
}
@Override
public String evaluateGeoGebraCAS(ValidExpression casInput,
MyArbitraryConstant arbconst, StringTemplate tpl, GeoCasCell cell,
Kernel kernel) throws CASException {
if (!app.getSettings().getCasSettings().isEnabled()
&& getCurrentCAS() != null) {
return "?";
}
String result = null;
CASException exception = null;
try {
result = getCurrentCAS().evaluateGeoGebraCAS(casInput, arbconst,
tpl, cell, kernel);
} catch (CASException ce) {
exception = ce;
}
// check if keep input command was successful
// e.g. for KeepInput[Substitute[...]]
// otherwise return input
if (cell != null && cell.isKeepInputUsed()
&& (exception != null || "?".equals(result))) {
// return original input
return casInput.toString(tpl);
}
// pass on exception
if (exception != null) {
throw exception;
}
// success
if (result != null) {
// get names of escaped global variables right
// e.g. "ggbcasvar1a" needs to be changed to "a"
// e.g. "ggbtmpvara" needs to be changed to "a"
result = Kernel.removeCASVariablePrefix(result, " ");
}
return result;
}
@Override
final public String evaluateGeoGebraCAS(String exp,
MyArbitraryConstant arbconst, StringTemplate tpl, Kernel kernel)
throws CASException {
try {
ValidExpression inVE = casParser.parseGeoGebraCASInput(exp, null);
String ret = evaluateGeoGebraCAS(inVE, arbconst, tpl, null, kernel);
if (ret == null) {
throw new CASException(new Exception(app.getLocalization()
.getError("CAS.GeneralErrorMessage")));
}
return ret;
} catch (Throwable t) {
t.printStackTrace();
throw new CASException(t);
}
}
@Override
final public String evaluateRaw(String exp) throws Throwable {
if (app.getSettings().getCasSettings().isEnabled()) {
return getCurrentCAS().evaluateRaw(exp);
}
return "?";
}
/**
* Evaluates an expression given in MPReduce/Giac syntax.
*
* @return result string (null possible)
* @param exp
* the expression
* @throws CASException
* if there is a timeout or the expression cannot be evaluated
*/
final public String evaluate(String exp) throws CASException {
if (app.getSettings().getCasSettings().isEnabled()) {
return getCurrentCAS().evaluateCAS(exp);
}
return "?";
}
// these variables are cached to gain some speed in getPolynomialCoeffs
private Map<String, String[]> getPolynomialCoeffsCache = new MaxSizeHashMap<String, String[]>(
Kernel.GEOGEBRA_CAS_CACHE_SIZE);
private StringBuilder getPolynomialCoeffsSB = new StringBuilder();
private StringBuilder sbPolyCoeffs = new StringBuilder();
@Override
final public String[] getPolynomialCoeffs(final String polyExpr,
final String variable) {
getPolynomialCoeffsSB.setLength(0);
getPolynomialCoeffsSB.append(polyExpr);
getPolynomialCoeffsSB.append(',');
getPolynomialCoeffsSB.append(variable);
String[] result = getPolynomialCoeffsCache
.get(getPolynomialCoeffsSB.toString());
if (result != null) {
return result.length == 0 ? null : result;
}
sbPolyCoeffs.setLength(0);
sbPolyCoeffs.append("when(is\\_polynomial("); // first check if
// expression is
// polynomial
sbPolyCoeffs.append(polyExpr);
sbPolyCoeffs.append(',');
sbPolyCoeffs.append(variable);
sbPolyCoeffs.append("),");
sbPolyCoeffs.append("coeff("); // if it is then return with it's
// coefficients
sbPolyCoeffs.append(getPolynomialCoeffsSB.toString());
sbPolyCoeffs.append("),{})"); // if not return {}
try {
// expand expression and get coefficients of
// "3*a*x^2 + b" in form "{ b, 0, 3*a }"
String tmp = evaluate(sbPolyCoeffs.toString());
// not a polynomial -- cache
if ("{}".equals(tmp)) {
getPolynomialCoeffsCache.put(getPolynomialCoeffsSB.toString(),
new String[0]);
return null;
}
// invalid output -- don't cache
if ("?".equals(tmp) || "".equals(tmp) || tmp == null) {
return null;
}
// not a polynomial: result still includes the variable, e.g. "x"
if (tmp.indexOf(variable) >= 0) {
return null;
}
// get names of escaped global variables right
// e.g. "ggbcasvara" needs to be changed to "a"
tmp = Kernel.removeCASVariablePrefix(tmp);
tmp = tmp.substring(1, tmp.length() - 1); // strip '{' and '}'
result = tmp.split(",");
getPolynomialCoeffsCache.put(getPolynomialCoeffsSB.toString(),
result);
return result;
} catch (Throwable e) {
Log.debug("GeoGebraCAS.getPolynomialCoeffs(): " + e.getMessage());
// e.printStackTrace();
}
return null;
}
final private static String toString(final ExpressionValue ev,
final boolean symbolic, StringTemplate tpl) {
/*
* previously this method also replaced f by f(x), but FunctionExpander
* takes care of that now
*/
if (symbolic) {
return ev.wrap().toString(tpl);
}
return ev.toValueString(tpl);
}
@Override
final synchronized public String getCASCommand(final String name,
final ArrayList<ExpressionNode> args, boolean symbolic,
StringTemplate tpl) {
return getCASCommand(name, args, symbolic, tpl, true);
}
private int counter = 1;
final synchronized private String getCASCommand(final String name,
final ArrayList<ExpressionNode> args, boolean symbolic,
StringTemplate tpl, boolean allowOutsourcing) {
StringBuilder sbCASCommand = new StringBuilder(80);
// build command key as name + ".N"
sbCASCommand.setLength(0);
sbCASCommand.append(name);
sbCASCommand.append(".N");
String translation = casParser
.getTranslatedCASCommand(sbCASCommand.toString());
// check for eg Sum.N=sum(%)
if (translation != null) {
sbCASCommand.setLength(0);
for (int i = 0; i < translation.length(); i++) {
char ch = translation.charAt(i);
if (ch == '%') {
if (args.size() == 1) { // might be a list as the argument
ExpressionValue ev = args.get(0).unwrap();
String str = toString(ev, symbolic, tpl);
sbCASCommand.append(str);
} else {
getCurrentCAS().appendListStart(sbCASCommand);
for (int j = 0; j < args.size(); j++) {
ExpressionValue ev = args.get(j);
sbCASCommand.append(toString(ev, symbolic, tpl));
sbCASCommand.append(',');
}
// remove last comma
sbCASCommand.setLength(sbCASCommand.length() - 1);
getCurrentCAS().appendListEnd(sbCASCommand);
}
} else {
sbCASCommand.append(ch);
}
}
return sbCASCommand.toString();
}
// build command key as name + "." + args.size()
// remove 'N'
sbCASCommand.setLength(sbCASCommand.length() - 1);
// check if completion of variable list is needed
boolean paramEquExists = checkForParamEquExistance(args, name);
// check if list of vars needs completion
boolean varComplNeeded = false;
String complOfVarsStr = "";
if (paramEquExists) {
// store nr of variables from input
if (args.get(1).getLeft() instanceof MyList) {
casParser.setNrOfVars(((MyList) args.get(1).getLeft()).size());
} else {
casParser.setNrOfVars(1);
}
// set of variables in list of equations
Set<String> varsInEqus = new HashSet<String>();
// set of variables in list of variables
Set<String> vars = new HashSet<String>();
// get list of equations
MyList listOfEqus = (MyList) args.get(0).getLeft();
for (int i = 0; i < listOfEqus.size(); i++) {
// get variables of current equation
HashSet<GeoElement> varsInCurrEqu = listOfEqus.getListElement(i)
.getVariables();
// add to set of vars form equations
for (GeoElement geo : varsInCurrEqu) {
varsInEqus
.add(geo.toString(StringTemplate.defaultTemplate));
}
}
// case we have list of vars in input as second argument
if (args.get(1).getLeft() instanceof MyList) {
MyList listOfVars = (MyList) args.get(1).getLeft();
// collect vars from input list of vars
for (int i = 0; i < listOfVars.size(); i++) {
vars.add(listOfVars.getItem(i)
.toString(StringTemplate.defaultTemplate));
}
}
// case input list of vars was one variable
else {
vars.add(args.get(1).getLeft()
.toString(StringTemplate.defaultTemplate));
}
// set of vars from equations, but unknown from list of vars
varsInEqus.removeAll(vars);
// case the nr of variables was already nr of vars in equations
if (varsInEqus.isEmpty()) {
casParser.setNrOfVars(0);
}
for (String str : varsInEqus) {
if (!"x".equals(str) && !"y".equals(str) && !"z".equals(str)) {
// add current variable to the completion string
complOfVarsStr += ",ggbtmpvar" + str;
} else {
complOfVarsStr += ", " + str;
}
// get equation of current variable
ValidExpression node = app.getKernel().getConstruction()
.geoCeListLookup(str);
// get variables of obtained equation
HashSet<GeoElement> varsFromEquOfCurrVars = node == null
? new HashSet<GeoElement>() : node.getVariables();
HashSet<String> stringVarsFromEquOfCurrVars = new HashSet<String>(
varsFromEquOfCurrVars.size());
// collect labels of variables from obtained equation
for (GeoElement geo : varsFromEquOfCurrVars) {
String geoStr = geo
.toString(StringTemplate.defaultTemplate);
if (!geoStr.equals(str)) {
stringVarsFromEquOfCurrVars.add(
geo.toString(StringTemplate.defaultTemplate));
}
}
// we need only the dependent variables of the current
// equation
stringVarsFromEquOfCurrVars.removeAll(vars);
// the current equation depends only on the input variable
// list
if (stringVarsFromEquOfCurrVars.isEmpty()) {
varComplNeeded = true;
}
// we found unknown variable
else {
varComplNeeded = false;
Log.debug(str + " contains unknown variable");
break;
}
}
}
boolean argIsList = false;
boolean isAssumeInEqus = false;
boolean skipEqu = false;
MyList equsForArgs = new MyList(this.app.getKernel());
StringBuilder assumesForArgs = new StringBuilder();
if (args.size() == 1 && args.get(0).isExpressionNode()
&& "Point".equals(name)) {
ExpressionNode node = args.get(0);
if (node.isLeaf() && node.getLeft() instanceof MyList) {
if (((ExpressionNode) ((MyList) node.getLeft())
.getListElement(0)).getLeft().isNumberValue()) {
sbCASCommand.append(1);
} else {
sbCASCommand.append((((MyList) node.getLeft()).size()));
argIsList = true;
}
}
}
// case solve with list of equations
else if ("Solve".equals(name) && args.size() == 2
&& args.get(0).getLeft() instanceof MyList && !varComplNeeded) {
// get list of equations from args
MyList listOfEqus = (MyList) args.get(0).getLeft();
// case Solve[ <List of Equations>, <List of Variables> ]
if (args.get(1).getLeft() instanceof MyList) {
// get list of parameters
MyList listOfVars = (MyList) args.get(1).getLeft();
for (int k = 0; k < listOfEqus.size(); k++) {
// get vars of current equation
// 2 = 2 should be handled as equation, not assumption
boolean contains = isEquation(listOfEqus.getListElement(k),
listOfVars);
boolean linear = false;
// check if equation can be used with assume
if (!contains) {
linear = isLinear(listOfEqus.getListElement(k));
}
// if contains other vars as parameters
// that means that the current equation is an assumption
if (!contains && linear) {
if (!isAssumeInEqus) {
isAssumeInEqus = true;
// call Solve.3
sbCASCommand.append(3);
}
// add current equation to assumptions
ExpressionValue ev = listOfEqus.getListElement(k);
assumesForArgs.append(toString(ev, symbolic, tpl));
assumesForArgs.append("),assume(");
}
// we found an equation which should be solved
else if (contains) {
// add current equation to list of equations
ExpressionValue ev = listOfEqus.getListElement(k);
equsForArgs.addListElement(ev);
}
}
if (!isAssumeInEqus
&& listOfEqus.size() != equsForArgs.size()) {
skipEqu = true;
}
}
// case Solve[ <List of Equations>, <Variable> ]
else if (args.get(1).getLeft() instanceof GeoDummyVariable) {
// get parameter
GeoDummyVariable var = (GeoDummyVariable) args.get(1).getLeft();
for (int k = 0; k < listOfEqus.size(); k++) {
// get current equation
HashSet<GeoElement> varsInEqu = listOfEqus.getListElement(k)
.getVariables();
Iterator<GeoElement> it = varsInEqu.iterator();
boolean contains = false;
// check if current equation contains only var which is not
// the parameter
while (it.hasNext()) {
GeoElement currVar = it.next();
if (currVar.toString(StringTemplate.defaultTemplate)
.equals(var.toString(
StringTemplate.defaultTemplate))) {
contains = true;
break;
}
}
boolean linear = false;
// check if we could use equation with assume
if (!contains) {
linear = isLinear(listOfEqus.getItem(k));
}
// the current equation is an assumption
if (!contains && linear) {
if (!isAssumeInEqus) {
isAssumeInEqus = true;
// call Solve.3
sbCASCommand.append(3);
}
// add current equation to assumptions
ExpressionValue ev = listOfEqus.getListElement(k);
assumesForArgs.append(toString(ev, symbolic, tpl));
assumesForArgs.append("),assume(");
}
// the current equation is an equation which should be
// solved
else if (contains) {
// add current equation to the list of equations
ExpressionValue ev = listOfEqus.getListElement(k);
equsForArgs.addListElement(ev);
}
}
if (!isAssumeInEqus
&& listOfEqus.size() != equsForArgs.size()) {
skipEqu = true;
}
}
}
// add eg '3'
else {
sbCASCommand.append(args.size());
}
// remove unwanted part from list of assumptions
// remove: ",assume("
if (isAssumeInEqus) {
assumesForArgs.setLength(assumesForArgs.length() - 9);
}
// if nr of arguments wasn't appended, append it
else if (sbCASCommand.toString().equals("Solve.")) {
sbCASCommand.append(args.size());
}
boolean outsourced = false;
// check if there is support in the outsourced CAS (now SingularWS) for
// this command:
if (allowOutsourcing && app.getSingularWS() != null
&& app.singularWSisAvailable()) {
translation = app
.singularWSgetTranslatedCASCommand(sbCASCommand.toString());
if (translation != null) {
outsourced = true;
}
}
// get translation ggb -> Giac
if (!outsourced) {
translation = translateCommandSignature(sbCASCommand.toString());
}
sbCASCommand.setLength(0);
// no translation found:
// use key as function name
if (translation == null) {
Kernel kern = app.getKernel();
boolean silent = kern.isSilentMode();
// convert command names x, y, z to xcoord, ycoord, ycoord to
// protect it in CAS
// see http://www.geogebra.org/trac/ticket/1440
boolean handled = false;
if (name.length() == 1) {
char ch = name.charAt(0);
if (ch == 'x' || ch == 'y' || ch == 'z') {
if (args.get(0).evaluatesToList()) {
sbCASCommand
.append(toString(args.get(0), symbolic, tpl));
sbCASCommand.append('[');
switch (ch) {
case 'x':
default:
sbCASCommand.append('0');
break;
case 'y':
sbCASCommand.append('1');
break;
case 'z':
sbCASCommand.append('2');
break;
}
sbCASCommand.append(']');
return sbCASCommand.toString();
} else if (args.get(0).hasCoords()) {
sbCASCommand.append(ch);
sbCASCommand.append("coord(");
} else {
sbCASCommand.append('(');
sbCASCommand.append(tpl.printVariableName(ch + ""));
sbCASCommand.append(")*(");
}
handled = true;
}
} else {
try {
Commands c = Commands.valueOf(name);
if (c != null) {
kern.setSilentMode(true);
StringBuilder sb = new StringBuilder(name);
sb.append('[');
for (int i = 0; i < args.size(); i++) {
if (i > 0) {
sb.append(',');
}
sb.append(args.get(i).toOutputValueString(
StringTemplate.defaultTemplate));
}
sb.append(']');
GeoElementND[] ggbResult = kern.getAlgebraProcessor()
.processAlgebraCommandNoExceptions(
sb.toString(), false);
kern.setSilentMode(silent);
if (ggbResult != null && ggbResult.length > 0
&& ggbResult[0] != null) {
return ggbResult[0].toValueString(tpl);
}
}
} catch (Exception e) {
kern.setSilentMode(silent);
Log.info(name + " not known command or function");
}
}
// standard case: add ggbcasvar prefix to name for CAS
if (!handled) {
// sbCASCommand.append("re(");
sbCASCommand.append(tpl.printVariableName(name));
sbCASCommand.append('(');
}
for (int i = 0; i < args.size(); i++) {
ExpressionValue ev = args.get(i);
sbCASCommand.append(toString(ev, symbolic, tpl));
sbCASCommand.append(',');
}
sbCASCommand.setCharAt(sbCASCommand.length() - 1, ')');
if (!handled) {
// sbCASCommand.append(")");
}
}
// translation found:
// replace %0, %1, etc. in translation by command arguments
else {
if ("Evaluate".equals(name) && args.size() == 1
&& args.get(0).unwrap() instanceof Command
&& "Evaluate".equals(
((Command) args.get(0).unwrap()).getName())) {
return toString(args.get(0), symbolic, tpl);
}
for (int i = 0; i < translation.length(); i++) {
char ch = translation.charAt(i);
StringTemplate tplToUse = tpl;
if (ch == '%') {
if (translation.charAt(i + 1) == '%') {
// eg %%0
// numeric = true;
tplToUse = tpl.deriveNumericGiac();
i++;
}
// get number after %
i++;
int pos = translation.charAt(i) - '0';
ExpressionValue ev;
if (argIsList) {
ev = ((MyList) args.get(0).getLeft())
.getListElement(pos);
sbCASCommand.append(toString(ev, symbolic, tplToUse));
} else if ("Solve".equals(name)) {
// case we have assumptions in equation list
if (isAssumeInEqus && args.size() != 3) {
// append list of equations
if (pos == 0) {
sbCASCommand.append(toString(equsForArgs,
symbolic, tplToUse));
}
// append list of assumptions
else if (pos == 2) {
sbCASCommand.append(assumesForArgs.toString());
}
// append list of variables
else {
ev = args.get(pos);
sbCASCommand.append(
toString(ev, symbolic, tplToUse));
}
} else if (pos == 2 && args.size() == 3
&& args.get(2).getLeft() instanceof MyList) {
// case solve with list of assumptions
// append assume for each assumption
MyList list = (MyList) args.get(2).getLeft();
for (int k = 0; k < list.size(); k++) {
ev = list.getItem(k);
sbCASCommand.append(
toString(ev, symbolic, tplToUse));
sbCASCommand.append("),assume(");
}
sbCASCommand.setLength(sbCASCommand.length() - 9);
} else if (pos >= 0 && pos < args.size()) {
if (skipEqu && pos == 0) {
ev = equsForArgs;
} else {
ev = args.get(pos);
}
// we need completion of variable list
if (varComplNeeded && pos == 1) {
String listOfVars = toString(ev, symbolic,
tplToUse);
if (!listOfVars.startsWith("{")) {
// add { with the defined vars by user
sbCASCommand.append("{");
sbCASCommand.append(listOfVars);
} else {
// add defined vars by user
sbCASCommand.append(listOfVars);
}
// skip unneeded }
if (listOfVars.endsWith("}")) {
sbCASCommand.setLength(
sbCASCommand.length() - 1);
}
// add completion of list of vars
sbCASCommand.append(complOfVarsStr);
sbCASCommand.append("}");
} else {
sbCASCommand.append(
toString(ev, symbolic, tplToUse));
}
}
} else if (pos >= 0 && pos < args.size()) {
// success: insert argument(pos)
ev = args.get(pos);
// needed for #5506
if ("SolveODE".equals(name)
&& ((ExpressionNode) ev)
.getLeft() instanceof MyList
&& args.size() > 2) {
sbCASCommand.append(toString(
((MyList) (args.get(pos).getLeft()))
.getListElement(0),
symbolic, tplToUse));
} else {
sbCASCommand
.append(toString(ev, symbolic, tplToUse));
}
} else {
// failed
sbCASCommand.append(ch);
sbCASCommand.append(translation.charAt(i));
}
// @ is a hack: only use the value if it does not contain ()
// to avoid (1,2)' in CAS
} else if (ch == '@') {
// get number after %
i++;
int pos = translation.charAt(i) - '0';
if (pos >= 0 && pos < args.size()) {
// success: insert argument(pos)
ExpressionValue ev = args.get(pos);
if (toString(ev, symbolic, tplToUse).matches("[^(),]*")) {
sbCASCommand
.append(toString(ev, symbolic, tplToUse));
} else {
sbCASCommand.append("x");
}
} else {
// failed
sbCASCommand.append(ch);
sbCASCommand.append(translation.charAt(i));
}
} else {
sbCASCommand.append(ch);
}
}
}
if (outsourced) {
try {
String retval = app
.singularWSdirectCommand(sbCASCommand.toString());
if (retval == null || "".equals(retval)) {
// if there was a problem, try again without using Singular:
return getCASCommand(name, args, symbolic, tpl, false);
}
return retval;
} catch (Throwable e) {
// try again without Singular:
return getCASCommand(name, args, symbolic, tpl, false);
}
}
// change variables to y and x for command SolveODE
if ("SolveODE".equals(name) && args.size() >= 2) {
return sbCASCommand.toString().replaceAll("unicode39u", "\'");
// return switchVarsToSolveODE(args, sbCASCommand);
} else if ("Solutions".equals(name) && args.size() == 1) {
return switchVarsToSolutions(args, sbCASCommand);
}
return sbCASCommand.toString();
}
@Override
public String translateCommandSignature(String string) {
String translation = casParser.getTranslatedCASCommand(string);
if (translation != null) {
translation = translation.replaceAll("arg0", "arg0" + counter);
translation = translation.replaceAll("arg1", "arg1" + counter);
counter++;
}
return translation;
}
private static boolean isLinear(ExpressionValue listElement) {
if (listElement.isExpressionNode() && ((ExpressionNode) listElement)
.getLeft() instanceof Equation) {
Equation equation = (Equation) ((ExpressionNode) listElement)
.getLeft();
HashSet<GeoElement> vars = equation.getVariables();
equation.initEquation();
// assume can accept only equation in first degree and with one
// variable
if (equation.degree() == 1 && vars.size() == 1) {
return true;
}
}
return false;
}
// method to check if we should make completion of variable list
private static boolean checkForParamEquExistance(
ArrayList<ExpressionNode> args, String name) {
// case we have command Solve[<Equation list>, <Variable list>]
if ("Solve".equals(name) && args.size() == 2) {
if (args.get(0).getLeft() instanceof MyList
&& (args.get(1).getLeft() instanceof MyList || args.get(1)
.getLeft() instanceof GeoDummyVariable)) {
// list of equations
MyList listOfEquations = (MyList) args.get(0).getLeft();
// analyze if first equation is a parametric equation
if (listOfEquations.getItem(0).isExpressionNode()
&& ((ExpressionNode) listOfEquations.getItem(0))
.getLeft() instanceof Equation) {
Equation equation = (Equation) ((ExpressionNode) listOfEquations
.getItem(0)).getLeft();
if (equation.getLHS().evaluatesTo3DVector()) {
return true;
}
}
}
}
return false;
}
private static boolean isEquation(ExpressionValue listElement,
MyList listOfVars) {
// TODO Auto-generated method stub
boolean contains = true;
// fix for GGB-134
boolean oldFlag = listOfVars.getKernel()
.isResolveUnkownVarsAsDummyGeos();
listOfVars.getKernel().setResolveUnkownVarsAsDummyGeos(true);
HashSet<GeoElement> varsInEqu = listElement.getVariables();
listOfVars.getKernel().setResolveUnkownVarsAsDummyGeos(oldFlag);
if (varsInEqu != null) {
contains = false;
Iterator<GeoElement> it = varsInEqu.iterator();
// check if current equation contains other vars as
// parameters
while (it.hasNext()) {
GeoElement var = it.next();
for (int i = 0; i < listOfVars.size(); i++) {
if (listOfVars.getListElement(i)
.toString(StringTemplate.defaultTemplate)
.equals(var.toString(
StringTemplate.defaultTemplate))) {
contains = true;
break;
}
}
if (contains) {
break;
}
}
}
return contains;
}
private static String switchVarsToSolutions(ArrayList<ExpressionNode> args,
StringBuilder sbCASCommand) {
Set<String> setOfDummyVars = new TreeSet<String>();
args.get(0)
.traverse(DummyVariableCollector.getCollector(setOfDummyVars));
String newSbCASCommand = sbCASCommand.toString();
// equation dependents from one variable
if (setOfDummyVars.size() == 1) {
Iterator<String> ite = setOfDummyVars.iterator();
String var = ite.next();
// if not x then switch
if (!"x".equals(var)) {
newSbCASCommand = newSbCASCommand.replaceFirst(",x\\)",
",ggbtmpvar" + var + ")");
}
return newSbCASCommand;
}
// equation dependents from more than one variable
StringBuilder listOfVars = new StringBuilder();
Iterator<String> ite = setOfDummyVars.iterator();
// create list of variables
while (ite.hasNext()) {
String currVar = ite.next();
if (!"x".equals(currVar) && !"y".equals(currVar)) {
listOfVars.append(",ggbtmpvar");
} else {
listOfVars.append(",");
}
listOfVars.append(currVar);
}
if (listOfVars.length() > 0) {
listOfVars = listOfVars.deleteCharAt(0);
newSbCASCommand = newSbCASCommand.replaceFirst(",x\\)",
",{" + listOfVars.toString() + "})");
}
return newSbCASCommand;
}
@Override
final public boolean isCommandAvailable(final Command cmd) {
StringBuilder sbCASCommand = new StringBuilder();
sbCASCommand.append(cmd.getName());
sbCASCommand.append(".");
sbCASCommand.append(cmd.getArgumentNumber());
if (casParser.isCommandAvailable(sbCASCommand.toString())) {
return true;
}
Log.debug("NOT AVAILABLE" + sbCASCommand);
sbCASCommand.setLength(0);
sbCASCommand.append(cmd.getName());
sbCASCommand.append(".N");
if (casParser.isCommandAvailable(sbCASCommand.toString())) {
return true;
}
return false;
}
@Override
public boolean isStructurallyEqual(final ValidExpression inputVE,
final String localizedInput, Kernel kernel) {
try {
// current input
String input1normalized = casParser.toString(inputVE,
StringTemplate.get(StringType.GEOGEBRA_XML));
// new input
ValidExpression ve2 = casParser
.parseGeoGebraCASInputAndResolveDummyVars(localizedInput,
kernel, null);
String input2normalized = casParser.toString(ve2,
StringTemplate.get(StringType.GEOGEBRA_XML));
// compare if the parsed expressions are equal
return input1normalized.equals(input2normalized);
} catch (Throwable th) {
Log.debug("Invalid selection: " + localizedInput);
return false;
}
}
@Override
public Set<String> getAvailableCommandNames() {
Set<String> cmdSet = new HashSet<String>();
for (String signature : casParser.getTranslationRessourceBundle()
.keySet()) {
String cmd = signature.substring(0, signature.indexOf('.'));
if (!"Evaluate".equals(cmd)) {
cmdSet.add(cmd);
}
}
return cmdSet;
}
@Override
public void clearCache() {
getPolynomialCoeffsCache.clear();
}
/**
* @return swaps in form a->b
*/
public ArrayList<String> getVarSwaps() {
return varSwaps;
}
}