/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ /* * Command.java * * Created on 05. September 2001, 12:05 */ package org.geogebra.common.kernel.arithmetic; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.arithmetic.Traversing.FVarCollector; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.plugin.Operation; import org.geogebra.common.util.GgbMat; import org.geogebra.common.util.debug.Log; /** * MyList is used to store a list of ExpressionNode objects read by the parser * and to evaluate them. So a MyList object is used when a list is entered (e.g. * {2, 3, 7, 9}) and also when a list is used for arithmetic operations. * * @see ExpressionNode#evaluate(StringTemplate) * * @author Markus Hohenwarter */ public class MyList extends ValidExpression implements ListValue, ReplaceChildrenByValues, GetItem { private Kernel kernel; private int matrixRows = -1; // -1 means not calculated, 0 means not a // matrix private int matrixCols = -1; // // list for list elements private ArrayList<ExpressionValue> listElements; /** * Creates new MyList * * @param kernel * kernel */ public MyList(Kernel kernel) { this(kernel, 20); } /** * Creates new MyList of given length * * @param kernel * kernel * @param size * length of the list */ public MyList(Kernel kernel, int size) { this.kernel = kernel; listElements = new ArrayList<ExpressionValue>(size); } /** * Create new MyList * * @param kernel * kernel * @param isFlatList * true for flat lists, false for matrices etc. */ public MyList(Kernel kernel, boolean isFlatList) { this(kernel); if (isFlatList) { // make sure isMatrix() returns false (fast) // see #1384 matrixRows = matrixCols = 0; } } /** * Adds expression value to the list * * @param arg * element to add */ public void addListElement(ExpressionValue arg) { listElements.add(arg); matrixRows = -1; // reset matrixCols = -1; } /** * Tries to return this list as an array of double values * * @return array of double values from this list */ @Override public double[] toDouble(int offset) { int length = listElements.size(); try { double[] valueArray = new double[length - offset]; for (int i = offset; i < length; i++) { valueArray[i - offset] = listElements.get(i).evaluateDouble(); } return valueArray; } catch (Exception e) { return null; } } /** * Replaces all Variable objects with the given varName in this list by the * given FunctionVariable object. * * @param varName * variable name * @param fVar * replacement variable * @return number of replacements done */ public int replaceVariables(String varName, FunctionVariable fVar) { int replacements = 0; for (int i = 0; i < listElements.size(); i++) { ExpressionValue element = listElements.get(i); if (element instanceof ExpressionNode) { replacements += ((ExpressionNode) element) .replaceVariables(varName, fVar); } else if (element instanceof Variable) { if (varName.equals(((Variable) element) .getName(StringTemplate.defaultTemplate))) { listElements.set(i, fVar); replacements++; } } } return replacements; } /** * Applies an operation to this list using the given value: * <this> <operation> <value>. * * @param operation * int value like ExpressionNode.MULTIPLY * @param value * value that should be applied to this list using the given * operation * @author Markus Hohenwarter * @param tpl * string template in case of concatenation */ final public void applyRight(Operation operation, ExpressionValue value, StringTemplate tpl) { apply(operation, value, true, tpl); } /** * Applies an operation to this list using the given value: * <value> <operation> <this>. * * @param operation * int value like ExpressionNode.MULTIPLY * @param value * value that should be applied to this list using the given * operation * @author Markus Hohenwarter * @param tpl * string template in case of string concatenation */ final public void applyLeft(Operation operation, ExpressionValue value, StringTemplate tpl) { apply(operation, value, false, tpl); } private boolean isDefined = true; final private void matrixMultiply(MyList LHlist, MyList RHlist) { int LHcols = LHlist.getMatrixCols(), LHrows = LHlist.getMatrixRows(); int RHcols = RHlist.getMatrixCols(); // RHlist.getMatrixRows(); ExpressionNode totalNode; ExpressionNode tempNode; listElements.clear(); if (LHcols != RHlist.getMatrixRows()) { isDefined = false; return; } isDefined = true; for (int row = 0; row < LHrows; row++) { MyList col1 = new MyList(kernel); for (int col = 0; col < RHcols; col++) { ExpressionValue totalVal = new ExpressionNode(kernel, new MyDouble(kernel, 0.0d)); for (int i = 0; i < LHcols; i++) { ExpressionValue leftV = getCell(LHlist, i, row); ExpressionValue rightV = getCell(RHlist, col, i); tempNode = new ExpressionNode(kernel, leftV, Operation.MULTIPLY, rightV); // multiply two cells... ExpressionValue operationResult = tempNode .evaluate(StringTemplate.defaultTemplate); totalNode = new ExpressionNode(kernel, totalVal, Operation.PLUS, operationResult); // totalNode.setLeft(operationResult); // totalNode.setRight(totalVal); // totalNode.setOperation(ExpressionNode.PLUS); // ...then add the result to a running total totalVal = totalNode .evaluate(StringTemplate.defaultTemplate); } tempNode = new ExpressionNode(kernel, totalVal); col1.addListElement(tempNode); } ExpressionNode col1a = new ExpressionNode(kernel, col1); listElements.add(col1a); } matrixRows = -1; // reset matrixCols = -1; } /** * Applies an operation to this list using the given value. * * @param operation * int value like ExpressionNode.MULTIPLY * @param value * value that should be applied to this list using the given * operation * @param right * true for <this> <operation> <value>, false for * <value> <operation> <this> * @param tpl * string template in case we do string concatenation here * @author Markus Hohenwarter */ public void apply(Operation operation, ExpressionValue value, boolean right, StringTemplate tpl) { int size = size(); // if (!right) // Application.debug("apply: " + value + " < op: " + operation + " > " + // this); // else // Application.debug("apply: " + this + " < op: " + operation + " > " + // value); // matrix ^ integer if (right && operation == Operation.POWER && value instanceof NumberValue && isMatrix()) { double power = ((NumberValue) value).getDouble(); // Application.debug("matrix ^ "+power); if (!Kernel.isInteger(power)) { listElements.clear(); return; } power = Math.round(power); if (power == 0) { setIdentityMatrix(); } if (power < 0) { MyList invert = this.invert(); listElements = invert.listElements; power *= -1; if (!invert.isDefined) { this.isDefined = false; return; } if (power == 1) { MyList RHlist = this.deepCopy(kernel); RHlist.setIdentityMatrix(); matrixMultiply(this.deepCopy(kernel), RHlist); return; } } if (power != 1) { MyList LHlist, RHlist; RHlist = this.deepCopy(kernel); while (power > 1.0) { LHlist = this.deepCopy(kernel); matrixMultiply(LHlist, RHlist); power--; } return; // finished matrix multiplication successfully } // else power = 1, so drop through to standard list code below } // expression value is list MyList valueList = value instanceof ListValue ? ((ListValue) value).getMyList() : null; // Michael Borcherds 2008-04-14 BEGIN // check for matrix multiplication eg // {{1,3,5},{2,4,6}}*{{11,14},{12,15},{13,16}} // try{ if (operation == Operation.MULTIPLY && valueList != null) { MyList LHlist, RHlist; if (!right) { LHlist = valueList; RHlist = this.deepCopy(kernel); } else { RHlist = valueList; LHlist = this.deepCopy(kernel); } boolean isMatrix = (LHlist.isMatrix() && RHlist.isMatrix()); if (isMatrix) { matrixMultiply(LHlist, RHlist); return; // finished matrix multiplication successfully } } // } // catch (Exception e) { } // not valid matrices // Michael Borcherds 2008-04-14 END matrixRows = -1; // reset matrixCols = -1; if (needsExpand()) { expand(); size = size(); } // return empty list if sizes don't match if (size == 0 || (valueList != null && valueList.size() > size)) { listElements.clear(); return; } // temp ExpressionNode to do evaluation of single elements ExpressionNode tempNode = new ExpressionNode(kernel, listElements.get(0)); tempNode.setOperation(operation); boolean b = kernel.getConstruction().isSuppressLabelsActive(); kernel.getConstruction().setSuppressLabelCreation(true); if (valueList != null && valueList.needsExpand()) { valueList = valueList.deepCopy(kernel); valueList.expand(); } if (valueList != null && valueList.size() != size) { listElements.clear(); return; } for (int i = 0; i < size; i++) { // try { // singleValue to apply to i-th element of this list // since evaluate() might change the value of left operand, we need // a deep copy here // see #460 ExpressionValue singleValue = valueList == null ? value.deepCopy(kernel) : valueList.getListElement(i); addResult(listElements.get(i), i, tempNode, singleValue, right, tpl); // } // catch (MyError err) { // err.printStackTrace(); // Application.debug(err.getLocalizedMessage()); // return empty list if any of the elements aren't numbers // listElements.clear(); // return; // } } kernel.getConstruction().setSuppressLabelCreation(b); // Application.debug(" gives : " + this); } private boolean needsExpand() { int size = size(); for (int i = 0; i < size; i++) { if (getListElement(i).unwrap().isGeoElement()) { AlgoElement algo = ((GeoElement) getListElement(i).unwrap()) .getParentAlgorithm(); if (algo != null && algo.getOutputLength() > 1 && algo.hasSingleOutputType()) { return true; } } } return false; } private void expand() { ArrayList<ExpressionValue> expElements = new ArrayList<ExpressionValue>(); for (int i = 0; i < listElements.size(); i++) { AlgoElement algo = null; ExpressionValue ev = getListElement(i).unwrap(); if (ev.isGeoElement()) { algo = ((GeoElement) ev).getParentAlgorithm(); } if (algo != null && algo.getOutputLength() > 1 && algo.hasSingleOutputType()) { for (int k = 0; k < algo.getOutputLength(); k++) { if ((algo.getOutput(k).isDefined() || algo.getOutput(k) == ev) && !expElements.contains(algo.getOutput(k))) { expElements.add(algo.getOutput(k)); } } } else { expElements.add(ev); } } this.listElements = expElements; } private void addResult(ExpressionValue myValue, int j, ExpressionNode tempNode, ExpressionValue singleValue, boolean right, StringTemplate tpl) { // apply operation using singleValue if (right) { // this operation value tempNode.setLeft(myValue); tempNode.setRight(singleValue); } else { // value operation this tempNode.setLeft(singleValue); tempNode.setRight(myValue); } // evaluate operation ExpressionValue operationResult = tempNode.evaluate(tpl); if (tempNode.containsFreeFunctionVariable(null)) { FunctionNVar toProc = kernel.getAlgebraProcessor() .makeFunctionNVar(tempNode.deepCopy(kernel)); Set<String> fvSet = new TreeSet<String>(); FVarCollector fvc = FVarCollector.getCollector(fvSet); tempNode.traverse(fvc); if (toProc instanceof Function) { operationResult = kernel.getAlgebraProcessor().processFunction( (Function) toProc, new EvalInfo(false))[0]; } else { operationResult = kernel.getAlgebraProcessor() .processFunctionNVar(toProc, new EvalInfo(false))[0]; } } // Application.debug(" tempNode : " + tempNode + ", result: " // + operationResult); if (operationResult instanceof NumberValue) { operationResult = operationResult.isGeoElement() ? operationResult.deepCopy(kernel) : ((NumberValue) operationResult) .toGeoElement(kernel.getConstruction()); ((GeoElement) operationResult) .setDefinition(tempNode.deepCopy(kernel)); } // set listElement to operation result if (!operationResult.isExpressionNode()) { operationResult = new ExpressionNode(kernel, operationResult); } listElements.set(j, operationResult); } private void setIdentityMatrix() { isMatrix(); listElements.clear(); if (matrixRows == matrixCols) { for (int row = 0; row < matrixRows; row++) { MyList col1 = new MyList(kernel); for (int col = 0; col < matrixCols; col++) { ExpressionNode md = new ExpressionNode(kernel, new MyDouble(kernel, row == col ? 1 : 0)); col1.addListElement(md); } ExpressionNode col1a = new ExpressionNode(kernel, col1); listElements.add(col1a); } } } /** * @return 0 if not a matrix * * @author Michael Borcherds */ public int getMatrixRows() { // check if already calculated if (matrixRows != -1 && matrixCols != -1) { return matrixRows; } isMatrix(); // do calculation return matrixRows; } /** * @return 0 if not a matrix * * @author Michael Borcherds */ public int getMatrixCols() { // check if already calculated if (matrixRows != -1 && matrixCols != -1) { return matrixCols; } isMatrix(); // do calculation return matrixCols; } /** * Inverts matrix (doesn't do any internal changes) * * @return inversion of this */ public MyList invert() { GgbMat g = new GgbMat(this); g.inverseImmediate(); MyList gl = new MyList(kernel); g.getMyList(gl, kernel); gl.isDefined = !g.isUndefined(); return gl; } /** * @return true if this list is a matrix */ @Override public boolean isMatrix() { return isMatrix(this); } /** * Removes all elements from this list */ public void clear() { listElements.clear(); } private static boolean isEquation(ExpressionValue ex) { if (ex instanceof Equation || (ex instanceof ExpressionNode && ((ExpressionNode) ex).getLeft() instanceof Equation)) { return true; } return false; } private boolean isMatrix(MyList LHlist) { // check if already calculated if (matrixRows > 0 && matrixCols > 0) { return true; } if (matrixRows == 0 && matrixCols == 0) { return false; } try { boolean isMatrix = true; int LHrows = LHlist.size(), LHcols = 0; // Application.debug("MULT LISTS"+size); // check LHlist is a matrix if (isEquation(LHlist.getListElement(0))) { return false; } ExpressionValue singleValue = LHlist.getListElement(0) .evaluate(StringTemplate.defaultTemplate); if (singleValue == null) { matrixRows = matrixCols = 0; return false; } if (singleValue instanceof ListValue) { LHcols = ((ListValue) singleValue).getMyList().size(); if (LHcols > 0 && isEquation( ((ListValue) singleValue).getListElement(0))) { return false; } // Application.debug("LHrows"+LHrows); if (LHrows > 1) { for (int i = 1; i < LHrows; i++) // check all rows same // length { // Application.debug(i); if (isEquation(LHlist.getListElement(i))) { return false; } singleValue = LHlist.getListElement(i) .evaluate(StringTemplate.defaultTemplate); // Application.debug("size"+((ListValue)singleValue).getMyList().size()); if (singleValue.evaluatesToList()) { MyList list = ((ListValue) singleValue).getMyList(); if (list.size() != LHcols) { isMatrix = false; } else if ((list.size() > 0) && isEquation(list.getListElement(0))) { isMatrix = false; } } else { isMatrix = false; } } } } else { isMatrix = false; } // Application.debug("isMatrix="+isMatrix); if (isMatrix) { matrixCols = LHcols; matrixRows = LHrows; } else { matrixCols = 0; matrixRows = 0; } return isMatrix; } catch (Throwable e) { matrixRows = matrixCols = 0; return false; } } // Michael Borcherds 2008-04-15 /** * @param list * matrix * @param row * row number (starts with 0) * @param col * col number (starts with 0) * @return cell of a list at given position */ public static ExpressionValue getCell(MyList list, int row, int col) { ExpressionValue singleValue = list.getListElement(col) .evaluate(StringTemplate.defaultTemplate); if (singleValue instanceof ListValue) { ExpressionValue ret = (((ListValue) singleValue).getMyList() .getListElement(row)) .evaluate(StringTemplate.defaultTemplate); // if (ret.isListValue()) Application.debug("isList*********"); return ret; } return null; } @Override public String toValueString(StringTemplate tpl) { return toString(tpl, true, true); /* * int size = listElements.size(); for (int i=0; i < size; i++) { * ((ExpressionValue) listElements.get(i)).evaluate(); } */ } @Override public String toLaTeXString(boolean symbolic, StringTemplate tpl) { StringBuilder toLaTeXString = new StringBuilder(); if (size() == 0) { // in schools the emptyset symbol is typically not used, see # // return "\\emptyset"; // correctly return "\\left\\{ \\right\\}"; } else if (isMatrix() && !(getListElement(0).unwrap() instanceof ListValue && getListElement(0).getListDepth() > 1)) { toLaTeXString.append("\\left(\\begin{array}{"); for (int i = 0; i < matrixCols; i++) { // nice alignment for eg {{-1,1},{1,-1}} in CAS toLaTeXString.append("r"); } toLaTeXString.append("}"); for (int i = 0; i < size(); i++) { ListValue singleValue = (ListValue) getListElement(i) .evaluate(StringTemplate.defaultTemplate); if (singleValue.size() > 0) { toLaTeXString.append(singleValue.getListElement(0) .toLaTeXString(symbolic, tpl)); for (int j = 1; j < singleValue.size(); j++) { toLaTeXString.append("&"); toLaTeXString.append(singleValue.getListElement(j) .toLaTeXString(symbolic, tpl)); } } toLaTeXString.append("\\\\"); } toLaTeXString.append("\\end{array}\\right)"); } else { toLaTeXString.append(" \\left\\{ "); toLaTeXString.append(toLaTeXStringNoBrackets(symbolic, tpl)); toLaTeXString.append(" \\right\\} "); } return toLaTeXString.toString(); } /** * @param symbolic * whether to substitute numbers * @param tpl * output template * @return string representation without brackets */ public String toLaTeXStringNoBrackets(boolean symbolic, StringTemplate tpl) { StringBuilder toLaTeXString = new StringBuilder(); // first (n-1) elements int lastIndex = listElements.size() - 1; if (lastIndex > -1) { for (int i = 0; i < lastIndex; i++) { ExpressionValue exp = listElements.get(i); toLaTeXString.append(exp.toLaTeXString(symbolic, tpl)); toLaTeXString.append(", "); } // last element ExpressionValue exp = listElements.get(lastIndex); toLaTeXString.append(exp.toLaTeXString(symbolic, tpl)); } return toLaTeXString.toString(); } @Override public String toString(StringTemplate tpl) { return toString(tpl, false, true); } /** * Adapted from GeoList * * @param tpl * output template * @param valueMode * true to substitute numbers * @param printBrackets * true to include {} (needs to be false for f(x,y) as {x,y} is * MyList too * @return string representation of this list */ public String toString(StringTemplate tpl, boolean valueMode, boolean printBrackets) { StringBuilder sb = new StringBuilder(); if (printBrackets) { tpl.leftCurlyBracket(sb); } // first (n-1) elements int lastIndex = listElements.size() - 1; if (lastIndex > -1) { for (int i = 0; i < lastIndex; i++) { ExpressionValue exp = listElements.get(i); sb.append(valueMode ? exp.toOutputValueString(tpl) : exp.toString(tpl)); // .toOutputValueString()); sb.append(kernel.getLocalization().unicodeComma); sb.append(" "); } // last element ExpressionValue exp = listElements.get(lastIndex); sb.append(valueMode ? exp.toOutputValueString(tpl) : exp.toString(tpl)); } if (printBrackets) { tpl.rightCurlyBracket(sb); } return sb.toString(); } @Override public int size() { return listElements.size(); } @Override public void resolveVariables(EvalInfo info) { for (int i = 0; i < listElements.size(); i++) { ExpressionValue en = listElements.get(i); en.resolveVariables(info); } } /** * @param i * index * @return i-th element of the list */ @Override public ExpressionValue getListElement(int i) { return listElements.get(i); } /** * Replaces element for given index, index must be within (0, length -1) * * @param i * index (0 based) * @param ev * new value * @return old value */ public ExpressionValue setListElement(int i, ExpressionValue ev) { return listElements.set(i, ev); } /* * public String toString() { } */ @Override public boolean isConstant() { int size = listElements.size(); for (int i = 0; i < size; i++) { if (!listElements.get(i).isConstant()) { return false; } } return true; } @Override public boolean isLeaf() { return true; } @Override public boolean isNumberValue() { return false; } @Override public MyList deepCopy(Kernel kernel1) { // copy arguments int size = listElements.size(); MyList c = new MyList(kernel1, size()); for (int i = 0; i < size; i++) { c.addListElement(listElements.get(i).deepCopy(kernel1)); } return c; } /** * Deep copy, except for geo elements * * @param kernel1 * kernel for result * @return deep copy, except for geo elements */ public ExpressionValue deepCopyExGeo(Kernel kernel1) { // copy arguments int size = listElements.size(); MyList c = new MyList(kernel1, size()); for (int i = 0; i < size; i++) { c.addListElement(ExpressionNode.copy(listElements.get(i), kernel1)); } return c; } @Override public HashSet<GeoElement> getVariables() { HashSet<GeoElement> varSet = new HashSet<GeoElement>(); int size = listElements.size(); for (int i = 0; i < size; i++) { HashSet<GeoElement> s = listElements.get(i).getVariables(); if (s != null) { varSet.addAll(s); } } return varSet; } @Override final public boolean contains(ExpressionValue ev) { return ev == this; } @Override public MyList getMyList() { if (isInTree()) { // used in expression node tree: be careful return deepCopy(kernel); } // not used anywhere: reuse this object return this; } /** * @param a * needle * @param myList * haystack * @return true iff myList contains a */ public static boolean isElementOf(ExpressionValue a, MyList myList) { // Application.debug(a.getClass()+""); for (int i = 0; i < myList.size(); i++) { ExpressionValue ev = myList.getListElement(i) .evaluate(StringTemplate.defaultTemplate); // g:x=0, g isElementOf {x=0} gives null here // see #1535 if (ev == null) { Log.warn(myList.getListElement(i) + " cannot be evaluated"); continue; } if (ExpressionNode.isEqual(a, ev)) { return true; } } return false; } /** * @param list1 * haystack * @param list2 * list of needles * @param tpl * string template * @return true iff list2 is subset of list1 */ public static boolean listContains(MyList list1, MyList list2, StringTemplate tpl) { if (list2.size() == 0) { // the empty set is a subset of all sets return true; } // removed, bug: {1, 2, 2, 2} IS_SUBSET_OF {3, 2, 1} // if (list1.size() < list2.size()) // return false; for (int i = 0; i < list2.size(); i++) { ExpressionValue ev2 = list2.getListElement(i).evaluate(tpl); boolean hasEqualMember = false; for (int j = 0; j < list1.size(); j++) { ExpressionValue ev1 = list1.getListElement(j).evaluate(tpl); if (ExpressionNode.isEqual(ev1, ev2)) { hasEqualMember = true; break; } } if (!hasEqualMember) { return false; } } return true; } /** * @param list1 * haystack * @param list2 * list of needles * @param tpl * template (in case there are string concatenations) * @return true iff list2 is proper subset of list1 */ public static boolean listContainsStrict(MyList list1, MyList list2, StringTemplate tpl) { // removed, bug: {1, 2, 2, 2} IS_STRICT_SUBSET_OF {3, 2, 1} // if (list1.size() <= list2.size()) // return false; // the empty set is a strict subset of everything except itself if (list2.size() == 0) { return (list1.size() != 0); } for (int i = 0; i < list2.size(); i++) { ExpressionValue ev2 = list2.getListElement(i).evaluate(tpl); boolean hasEqualMember = false; for (int j = 0; j < list1.size(); j++) { ExpressionValue ev1 = list1.getListElement(j).evaluate(tpl); if (ExpressionNode.isEqual(ev1, ev2)) { hasEqualMember = true; break; } } if (!hasEqualMember) { return false; } } // now must check sets aren't equal for (int i = 0; i < list1.size(); i++) { ExpressionValue ev1 = list1.getListElement(i) .evaluate(StringTemplate.defaultTemplate); boolean hasEqualMember = false; for (int j = 0; j < list2.size(); j++) { ExpressionValue ev2 = list2.getListElement(j) .evaluate(StringTemplate.defaultTemplate); if (ExpressionNode.isEqual(ev1, ev2)) { hasEqualMember = true; break; } } // we've found an element without a match // so lists are not equal if (!hasEqualMember) { return true; } } // lists are equal return false; } /** * @param kernel * kernel * @param list1 * minuend * @param list2 * subtrahend * @return set difference of the lists */ public static MyList setDifference(Kernel kernel, MyList list1, MyList list2) { if (list2.size() == 0) { return list1; } MyList ret = new MyList(kernel); if (list1.size() == 0) { return ret; } for (int i = 0; i < list1.size(); i++) { ExpressionValue ev0 = list1.getListElement(i); ExpressionValue ev1 = ev0.evaluate(StringTemplate.defaultTemplate); boolean addToList = true; for (int j = 0; j < list2.size(); j++) { ExpressionValue ev2 = list2.getListElement(j) .evaluate(StringTemplate.defaultTemplate); if (ExpressionNode.isEqual(ev1, ev2)) { addToList = false; break; } } if (addToList) { ret.addListElement(ev0); } } return ret; } @Override public String toOutputValueString(StringTemplate tpl) { return toValueString(tpl); } /** * Computes vector product of this and other list, the result is stored in * this list * * @param list * other list */ public void vectorProduct(MyList list) { // tempX/Y needed because a and c can be the same variable ExpressionValue ax = getListElement(0); ExpressionValue ay = getListElement(1); ExpressionValue bx = list.getListElement(0); ExpressionValue by = list.getListElement(1); ExpressionNode en = new ExpressionNode(kernel, ax, Operation.MULTIPLY, by); ExpressionNode en2 = new ExpressionNode(kernel, ay, Operation.MULTIPLY, bx); ExpressionNode x, y, z; if (list.size() == 2 || size() == 2) { listElements.add(2, new ExpressionNode(kernel, en, Operation.MINUS, en2)); listElements.set(0, new ExpressionNode(kernel, new MyDouble(kernel, 0.0), Operation.NO_OPERATION, null)); listElements.set(1, new ExpressionNode(kernel, new MyDouble(kernel, 0.0), Operation.NO_OPERATION, null)); return; } // size 3 z = new ExpressionNode(kernel, en, Operation.MINUS, en2); ExpressionValue az = getListElement(2); ExpressionValue bz = list.getListElement(2); en = new ExpressionNode(kernel, ay, Operation.MULTIPLY, bz); en2 = new ExpressionNode(kernel, az, Operation.MULTIPLY, by); x = new ExpressionNode(kernel, en, Operation.MINUS, en2); en = new ExpressionNode(kernel, az, Operation.MULTIPLY, bx); en2 = new ExpressionNode(kernel, ax, Operation.MULTIPLY, bz); y = new ExpressionNode(kernel, en, Operation.MINUS, en2); listElements.set(0, x); listElements.set(1, y); listElements.set(2, z); // double tempX = a.y * b.z - a.z * b.y; // double tempY = - a.x * b.z + a.z * b.x; // c.z = a.x * b.y - a.y * b.x; // c.x = tempX; // c.y = tempY; } /** * @return kernel */ public Kernel getKernel() { return kernel; } /** * @return true iff this list is defined (not result of e.g. singular matrix * inverse) */ public boolean isDefined() { return isDefined; } @Override public void replaceChildrenByValues(GeoElement geo) { for (int i = 0; i < size(); i++) { ExpressionValue insert = getListElement(i); if (insert instanceof ReplaceChildrenByValues) { ((ReplaceChildrenByValues) insert).replaceChildrenByValues(geo); } } } /** * Same as deep copy, but doesn't deep copy elements * * @param kernel2 * kernel * @return copy of this list */ public MyList getCopy(Kernel kernel2) { MyList ret = new MyList(kernel, size()); for (int i = 0; i < size(); i++) { ret.listElements .add(ExpressionNode.copy(listElements.get(i), kernel2)); } return ret; } @Override public ExpressionValue traverse(Traversing t) { ExpressionValue v = t.process(this); for (int i = 0; i < size(); i++) { ExpressionValue insert = getListElement(i); listElements.set(i, insert.traverse(t)); } return v; } @Override public boolean inspect(Inspecting t) { if (t.check(this)) { return true; } for (int i = 0; i < size(); i++) { if ((getListElement(i)).inspect(t)) { return true; } } return false; } @Override public ExpressionValue getItem(int i) { return listElements.get(i); } @Override public int getLength() { return listElements.size(); } @Override public ExpressionNode wrap() { return new ExpressionNode(kernel, this); } /** * @param val * value to be added to the end * @param offset * number of elements to skip */ public void addQue(double val, int offset) { if (listElements.size() < offset + 1) { return; } MyDouble removed = (MyDouble) listElements.get(offset); for (int read = offset + 1; read < listElements.size(); read++) { listElements.set(read - 1, listElements.get(read)); } removed.add(val - removed.getDouble()); listElements.set(listElements.size() - 1, removed); } @Override public int getListDepth() { return isMatrix() ? 2 : 1; } @Override public ValueType getValueType() { return ValueType.LIST; } /** * @param xEval * list or simple value * @param idx * index * @return value from list at given index or simple value */ public static ExpressionValue get(ExpressionValue xEval, int idx) { return xEval instanceof ListValue ? ((ListValue) xEval).getListElement(idx) : xEval; } }