/* 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. */ /** * stores a (coefficient, variables) pair<BR> * example: Term("-45yx") stores coefficient -45 * and variables "xy". Variables are sorted alphabetically. */ package org.geogebra.common.kernel.arithmetic; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.plugin.Operation; /** * A term is a pair of coefficient and variables in a Polynomial, e.g. {4, "x"}, * {a, "xy"} */ public class Term implements Comparable<Object> { /** coefficient */ ExpressionValue coefficient; // has to evaluate() to NumberValue private StringBuilder variables; // private Kernel kernel; /** * @param coeff * coefficient * @param vars * variables string, eg. "xxy" */ public Term(ExpressionValue coeff, String vars) { this(coeff, new StringBuilder(vars)); } /** * @param kernel * kernel * @param coeff * coefficient * @param vars * variables string, eg. "xxy" */ public Term(Kernel kernel, double coeff, String vars) { this(new MyDouble(kernel, coeff), new StringBuilder(vars)); } /** * @param coeff * coefficient * @param vars * variables StringBuilder */ public Term(ExpressionValue coeff, StringBuilder vars) { setCoefficient(coeff); variables = vars; } /** * Copy constructor * * @param t * term to copy * @param kernel * kernel for coefficient */ public Term(Term t, Kernel kernel) { variables = new StringBuilder(t.variables.toString()); setCoefficient(ExpressionNode.copy(t.coefficient, kernel)); } /** * @return coefficient */ public ExpressionValue getCoefficient() { return coefficient; } /** * @param coeff * changes coefficient */ void setCoefficient(ExpressionValue coeff) { coefficient = coeff; } /** * @return variables */ String getVars() { return variables.toString(); } /** * @param vars * new variables string, eg. "xxy" */ void setVariables(String vars) { variables.setLength(0); variables.append(vars); } /** * @param vars * new variables as string */ void setVariables(StringBuilder vars) { variables.setLength(0); variables.append(vars); } /** * @return true if this has no variables */ boolean hasNoVars() { return (variables.length() == 0); } /** * @return true if coeff is integer */ boolean hasIntegerCoeff() { return Kernel.isInteger(coefficient.evaluateDouble()); } /** * Total degree of this term * * @return degree */ int degree() { return variables.length(); } /** * degree of eg x xxxyy returns 3 for x, 2 for y * * @param var * term whose degree we want * @return degree */ int degree(char var) { int count = 0; for (int i = 0; i < variables.length(); i++) { if (variables.charAt(i) == var) { count++; } } return count; } /** * add a number to this term's coefficient * * @param number * number to add * @param kernel * kernel */ void addToCoefficient(ExpressionValue number, Kernel kernel) { setCoefficient(add(coefficient, number, kernel)); } // return a + b private ExpressionValue add(ExpressionValue a, ExpressionValue b, Kernel kernel) { boolean aconst = false;// a.isConstant(); boolean bconst = false;// b.isConstant(); double aval, bval; // add constant? if (aconst && bconst) { aval = a.evaluateDouble(); bval = b.evaluateDouble(); return new MyDouble(kernel, aval + bval); } else if (aconst) { aval = a.evaluateDouble(); if (aval == 0.0d) { return b; } if (b.isExpressionNode()) { ExpressionNode ben = (ExpressionNode) b; if (ben.getLeft().isConstant()) { switch (ben.getOperation()) { // a + (b.left + b.right) = (a + b.left) + b.right case PLUS: return add(add(a, ben.getLeft(), kernel), ben.getRight(), kernel); // a + (b.left - b.right) = (a + b.left) - b.right case MINUS: return sub(add(a, ben.getLeft(), kernel), ben.getRight(), kernel); default: break; } } } // else return new ExpressionNode(kernel, a, Operation.PLUS, b); } else if (bconst) { return add(b, a, kernel); // get the constant to the left } else { return new ExpressionNode(kernel, a, Operation.PLUS, b); } } private ExpressionValue sub(ExpressionValue a, ExpressionValue b, Kernel kernel) { return add(a, multiply(new MyDouble(kernel, -1.0d), b, kernel), kernel); } /** * multiply this term with another term * * @param t * multiplier * @param kernel * kernel */ void multiply(Term t, Kernel kernel) { setCoefficient(multiply(coefficient, t.coefficient, kernel)); variables.append(t.variables); sort(variables); } /** * multiply this term with another term return a new Term * * Term mult(Term t) { StringBuilder sb = new StringBuilder(); * * // concatenate and sort (variables + t.variables) sb = * sb.append(variables); sb = sb.append(t.variables); sort(sb); Term ret = * new Term( coefficient, sb ); ret.multiply(t.coefficient); return ret; } */ /** * multiply this term with a number * * @param number * multiplier * @param kernel * kernel */ void multiply(ExpressionValue number, Kernel kernel) { setCoefficient(multiply(coefficient, number, kernel)); } // c = a * b private ExpressionValue multiply(ExpressionValue a, ExpressionValue b, Kernel kernel) { // multiply constant? boolean aconst = a.isConstant(); boolean bconst = b.isConstant(); double aval, bval; if (aconst && bconst) { aval = a.evaluateDouble(); bval = b.evaluateDouble(); return new MyDouble(kernel, aval * bval); } else if (aconst) { aval = a.evaluateDouble(); if (aval == 0.0d) { return new MyDouble(kernel, 0.0d); } else if (aval == 1.0d) { return b; } else { if (b instanceof ExpressionNode) { ExpressionNode ben = (ExpressionNode) b; if (ben.getLeft().isConstant()) { switch (ben.getOperation()) { // a * (b.left * b.right) = (a * b.left) * b.right case MULTIPLY: return multiply(multiply(a, ben.getLeft(), kernel), ben.getRight(), kernel); // a * (b.left / b.right) = (a * b.left) / b.right case DIVIDE: return divide(multiply(a, ben.getLeft(), kernel), ben.getRight(), kernel); default: break; } } } return new ExpressionNode(kernel, a, Operation.MULTIPLY, b); } } else if (bconst) { // a * b = b * a return multiply(b, a, kernel); // get the constant to the left } else { return new ExpressionNode(kernel, a, Operation.MULTIPLY, b); } } /** * divide this term with a number * * @param number * divisor * @param kernel * kernel */ void divide(ExpressionValue number, Kernel kernel) { setCoefficient(divide(coefficient, number, kernel)); } // c = a / b private ExpressionValue divide(ExpressionValue a, ExpressionValue b, Kernel kernel) { // divide constants boolean aconst = a.isConstant(); boolean bconst = b.isConstant(); double aval, bval; if (aconst && bconst) { aval = a.evaluateDouble(); bval = b.evaluateDouble(); return new MyDouble(kernel, aval / bval); } else if (aconst) { aval = a.evaluateDouble(); if (aval == 0.0d) { return new MyDouble(kernel, 0.0d); } if (b instanceof ExpressionNode) { ExpressionNode ben = (ExpressionNode) b; switch (ben.getOperation()) { // a / (b.left / b.right) = (a / b.left) * b.right case DIVIDE: return multiply(divide(a, ben.getLeft(), kernel), ben.getRight(), kernel); // TODO muliply? default: break; } } return new ExpressionNode(kernel, a, Operation.DIVIDE, b); } else if (bconst) { bval = b.evaluateDouble(); if (bval == 1.0d) { return a; } return new ExpressionNode(kernel, a, Operation.DIVIDE, b); } else { return new ExpressionNode(kernel, a, Operation.DIVIDE, b); } } // sort single characters: "yx" -> "xy" private static void sort(StringBuilder sb) { int len = sb.length(); char[] chVariables = new char[len]; // to sort, copy characters into a char array sb.getChars(0, len, chVariables, 0); java.util.Arrays.sort(chVariables, 0, len); sb.setLength(0); sb.append(chVariables); } @Override public boolean equals(Object o) { Term t; if (o instanceof Term) { t = (Term) o; return (coefficient == t.coefficient && variables.toString().equals(t.variables.toString())); } return false; } @Override public int hashCode() { assert false : "hashCode not designed"; return 42; // any arbitrary constant will do } /** * @param var * var name * @return True if contins given variable */ boolean contains(String var) { return (variables.toString().indexOf(var) >= 0); } @Override public int compareTo(Object o) { if (o instanceof Term) { return ((Term) o).variables.toString() .compareTo(variables.toString()); } return Integer.MAX_VALUE; } @Override @Deprecated public String toString() { return toString(StringTemplate.defaultTemplate); } /** * Serialize to string according to given template * * @param tpl * template * @return string representation */ public String toString(StringTemplate tpl) { if (ExpressionNode.isEqualString(coefficient, 0, true)) { return "0"; } if (ExpressionNode.isEqualString(coefficient, 1, true)) { if (variableString(tpl).length() > 0) { return variableString(tpl); } return "1"; } StringBuilder sb = new StringBuilder(); String var = variableString(tpl); if (ExpressionNode.isEqualString(coefficient, -1, true) && var.length() > 0) { sb.append('-'); sb.append(var); } else { sb.append(coeffString(coefficient, tpl)); if (var != null) { sb.append(' '); sb.append(var); } } return sb.toString(); } private String coeffString(ExpressionValue ev, StringTemplate tpl) { if (ev instanceof GeoElement) { return ((GeoElement) ev).getLabel(tpl); } else if (ev instanceof ExpressionNode) { ExpressionNode n = (ExpressionNode) ev; if (n.isLeaf() || ExpressionNode.opID(n) >= Operation.MULTIPLY.ordinal() || variables.length() == 0) { return n.toString(tpl); } StringBuilder sb = new StringBuilder(); sb.append('('); sb.append(n.toString(tpl)); sb.append(')'); return sb.toString(); } else { return ev.toString(tpl); } } private String variableString(StringTemplate tpl) { String str = variables.toString(); if ((tpl.hasCASType()) && variables.length() >= 1) { StringBuilder sb = new StringBuilder("("); for (int i = 0; i < str.length(); i++) { if (i > 0) { sb.append('*'); } sb.append(tpl.printVariableName(str.charAt(0) + "")); } sb.append(')'); return sb.toString(); } switch (variables.length()) { case 1: return str; case 2: if ("xx".equals(str)) { return "x\u00b2"; } if ("yy".equals(str)) { return "y\u00b2"; } if ("xy".equals(str)) { return "xy"; } default: return ""; } } } // end of class Term