package org.geogebra.common.kernel.algos; import java.math.BigInteger; import java.util.HashMap; import java.util.HashSet; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.prover.NoSymbolicParametersException; import org.geogebra.common.kernel.prover.polynomial.PPolynomial; import org.geogebra.common.kernel.prover.polynomial.PVariable; /** * This class provides all symbolic information necessary for the provers. * * @author Simon Weitzhofer * */ public class SymbolicParameters { private HashSet<PVariable> variables; private SymbolicParametersAlgo spa; /** * * @param spa * The algorithm which calculates the objects */ public SymbolicParameters(final SymbolicParametersAlgo spa) { this.spa = spa; } /** * Getter for the degrees * * @return the degrees of the polynomial * @throws NoSymbolicParametersException * if no symbolic parameters can be obtained */ public int[] getDegrees() throws NoSymbolicParametersException { return spa.getDegrees(); } /** * Returns the maximum degree of degree1 and degree2 in each coordinate. * * @param degree1 * The first degree * @param degree2 * The second degree * @return the maximum degrees */ public static int[] getMaxDegree(int[] degree1, int[] degree2) { if (degree1 == null || degree2 == null || degree1.length != 3 || degree2.length != 3) { return null; } int[] maxDegree = new int[3]; maxDegree[0] = Math.max(degree1[0], degree2[0]); maxDegree[1] = Math.max(degree1[1], degree2[1]); maxDegree[2] = Math.max(degree1[2], degree2[2]); return maxDegree; } /** * Returns the sum of degree1 and degree2 in each coordinate. * * @param degree1 * The first degree * @param degree2 * The second degree * @return the sum */ public static int[] addDegree(int[] degree1, int[] degree2) { if (degree1 == null || degree2 == null || degree1.length != 3 || degree2.length != 3) { return null; } int[] addDegree = new int[3]; addDegree[0] = degree1[0] + degree2[0]; addDegree[1] = degree1[1] + degree2[1]; addDegree[2] = degree1[2] + degree2[2]; return addDegree; } /** * calculates the maximum degrees possible of an cross product of two * vectors * * @param degree1 * the degrees of the coordinates of the first vector * @param degree2 * the degrees of the coordinates of the second vector * @return the degrees of the cross product of the two vectors */ public static int[] crossDegree(final int[] degree1, final int[] degree2) { int[] result = new int[3]; result[0] = Math.max(degree1[1] + degree2[2], degree1[2] + degree2[1]); result[1] = Math.max(degree1[0] + degree2[2], degree1[2] + degree2[0]); result[2] = Math.max(degree1[1] + degree2[0], degree1[0] + degree2[1]); return result; } /** * * Calculates the cross product of two vectors of dimension three. * * @param a * the first vector * @param b * the second vector * @return the cross product of the two vectors */ public static BigInteger[] crossProduct(final BigInteger[] a, final BigInteger[] b) { BigInteger[] result = new BigInteger[3]; result[0] = (a[1].multiply(b[2])).subtract(a[2].multiply(b[1])); result[1] = (a[2].multiply(b[0])).subtract(a[0].multiply(b[2])); result[2] = (a[0].multiply(b[1])).subtract(a[1].multiply(b[0])); return SymbolicParameters.reduce(result); } /** * Returns the number of free variables of the polynomial describing the * object * * @return the number of free variables * @throws NoSymbolicParametersException * if no symbolic parameters can be obtained */ public int getDimension() throws NoSymbolicParametersException { if (variables == null) { initFreeVariables(); } return variables.size(); } /** * Returns a set of all free variables of the polynomial describing the * object * * @return the set of all free variables * @throws NoSymbolicParametersException * if no symbolic parameters can be obtained */ public HashSet<PVariable> getFreeVariables() throws NoSymbolicParametersException { if (variables == null) { initFreeVariables(); } return variables; } /** * Calculates the homogeneous coordinates of the object when substituting * the variables by its values. * * @param values * a map of the values the variables are substituted with * @return the coordinates * @throws NoSymbolicParametersException * thrown if it is not possible to obtain the exact coordinates */ public BigInteger[] getExactCoordinates( final HashMap<PVariable, BigInteger> values) throws NoSymbolicParametersException { return spa.getExactCoordinates(values); } /** * Divides thru the greatest common divisor of the homogeneous coordinates * * @param vect * the homogeneous coordinates * @return the reduced homogeneous coordinates */ public static BigInteger[] reduce(final BigInteger[] vect) { BigInteger gcd = BigInteger.ZERO; for (int i = 0; i < vect.length; i++) { gcd = gcd.gcd(vect[i]); } if (gcd.equals(BigInteger.ZERO)) { gcd = BigInteger.ONE; } BigInteger[] result = new BigInteger[vect.length]; for (int i = 0; i < vect.length; i++) { result[i] = vect[i].divide(gcd); } return result; } /** * Gets the free variables * * @throws NoSymbolicParametersException */ private void initFreeVariables() throws NoSymbolicParametersException { variables = new HashSet<PVariable>(); spa.getFreeVariables(variables); } /** * Calculates the determinant of a 4 times 4 matrix * * @param matrix * matrix * @return the determinant */ public static BigInteger det4(final BigInteger[][] matrix) { return matrix[0][3].multiply(matrix[1][2]).multiply(matrix[2][1]) .multiply(matrix[3][0]) .subtract(matrix[0][2].multiply(matrix[1][3]) .multiply(matrix[2][1]).multiply(matrix[3][0])) .subtract(matrix[0][3].multiply(matrix[1][1]) .multiply(matrix[2][2]).multiply(matrix[3][0])) .add(matrix[0][1].multiply(matrix[1][3]).multiply(matrix[2][2]) .multiply(matrix[3][0])) .add(matrix[0][2].multiply(matrix[1][1]).multiply(matrix[2][3]) .multiply(matrix[3][0])) .subtract(matrix[0][1].multiply(matrix[1][2]) .multiply(matrix[2][3]).multiply(matrix[3][0])) .subtract(matrix[0][3].multiply(matrix[1][2]) .multiply(matrix[2][0]).multiply(matrix[3][1])) .add(matrix[0][2].multiply(matrix[1][3]).multiply(matrix[2][0]) .multiply(matrix[3][1])) .add(matrix[0][3].multiply(matrix[1][0]).multiply(matrix[2][2]) .multiply(matrix[3][1])) .subtract(matrix[0][0].multiply(matrix[1][3]) .multiply(matrix[2][2]).multiply(matrix[3][1])) .subtract(matrix[0][2].multiply(matrix[1][0]) .multiply(matrix[2][3]).multiply(matrix[3][1])) .add(matrix[0][0].multiply(matrix[1][2]).multiply(matrix[2][3]) .multiply(matrix[3][1])) .add(matrix[0][3].multiply(matrix[1][1]).multiply(matrix[2][0]) .multiply(matrix[3][2])) .subtract(matrix[0][1].multiply(matrix[1][3]) .multiply(matrix[2][0]).multiply(matrix[3][2])) .subtract(matrix[0][3].multiply(matrix[1][0]) .multiply(matrix[2][1]).multiply(matrix[3][2])) .add(matrix[0][0].multiply(matrix[1][3]).multiply(matrix[2][1]) .multiply(matrix[3][2])) .add(matrix[0][1].multiply(matrix[1][0]).multiply(matrix[2][3]) .multiply(matrix[3][2])) .subtract(matrix[0][0].multiply(matrix[1][1]) .multiply(matrix[2][3]).multiply(matrix[3][2])) .subtract(matrix[0][2].multiply(matrix[1][1]) .multiply(matrix[2][0]).multiply(matrix[3][3])) .add(matrix[0][1].multiply(matrix[1][2]).multiply(matrix[2][0]) .multiply(matrix[3][3])) .add(matrix[0][2].multiply(matrix[1][0]).multiply(matrix[2][1]) .multiply(matrix[3][3])) .subtract(matrix[0][0].multiply(matrix[1][2]) .multiply(matrix[2][1]).multiply(matrix[3][3])) .subtract(matrix[0][1].multiply(matrix[1][0]) .multiply(matrix[2][2]).multiply(matrix[3][3])) .add(matrix[0][0].multiply(matrix[1][1]).multiply(matrix[2][2]) .multiply(matrix[3][3])); } /** * Adds two extra points to the Botana point list (4 extra variables) * * @param input * Two EV points * @return List of Botana variables (4 elements) * @throws NoSymbolicParametersException * if it's not possible to obtain polynomials */ public static PVariable[] addBotanaVarsJoinPoints(GeoElementND[] input) throws NoSymbolicParametersException { PVariable[] botanaVars = new PVariable[4]; PVariable[] line1vars, line2vars; line1vars = ((SymbolicParametersBotanaAlgo) input[0]) .getBotanaVars(input[0]); line2vars = ((SymbolicParametersBotanaAlgo) input[1]) .getBotanaVars(input[1]); botanaVars[0] = line1vars[0]; botanaVars[1] = line1vars[1]; botanaVars[2] = line2vars[0]; botanaVars[3] = line2vars[1]; return botanaVars; } /** * Returns Botana polynomials for the midpoint of P and Q * * @param P * First endpoint of segment * @param Q * Other endpoint of segment * @param botanaVars * Botana variables for the new point (midpoint) * @return the polynomials (2 elements) * @throws NoSymbolicParametersException * if it's not possible to obtain polynomials */ public static PPolynomial[] botanaPolynomialsMidpoint(GeoElement P, GeoElement Q, PVariable[] botanaVars) throws NoSymbolicParametersException { PVariable[] fv1 = ((SymbolicParametersBotanaAlgo) P).getBotanaVars(P); PVariable[] fv2 = ((SymbolicParametersBotanaAlgo) Q).getBotanaVars(Q); PPolynomial[] botanaPolynomials = new PPolynomial[2]; // 2*m1-a1-b1, 2*m2-a2-b2 botanaPolynomials[0] = (new PPolynomial(2)) .multiply(new PPolynomial(botanaVars[0])) .subtract(new PPolynomial(fv1[0])) .subtract(new PPolynomial(fv2[0])); botanaPolynomials[1] = (new PPolynomial(2)) .multiply(new PPolynomial(botanaVars[1])) .subtract(new PPolynomial(fv1[1])) .subtract(new PPolynomial(fv2[1])); return botanaPolynomials; } /** * Returns Botana polynomials for the bisector of A and B * * @param Ax * Botana variable of A (x) * @param Ay * Botana variable of A (y) * @param Bx * Botana variable of B (x) * @param By * Botana variable of B (y) * @param botanaVars * Botana variables for the 2 new points (4 variables) * @return the polynomials (4 elements) */ public static PPolynomial[] botanaPolynomialsLineBisector(PVariable Ax, PVariable Ay, PVariable Bx, PVariable By, PVariable[] botanaVars) { PPolynomial[] botanaPolynomials = new PPolynomial[4]; PPolynomial a1 = new PPolynomial(Ax); PPolynomial a2 = new PPolynomial(Ay); PPolynomial c1 = new PPolynomial(botanaVars[0]); PPolynomial c2 = new PPolynomial(botanaVars[1]); PPolynomial d1 = new PPolynomial(botanaVars[2]); PPolynomial d2 = new PPolynomial(botanaVars[3]); // C will be the midpoint of AB // 2*c1-a1-b1, 2*c2-a2-b2 botanaPolynomials[0] = (new PPolynomial(2)).multiply(c1) .subtract(new PPolynomial(Ax)).subtract(new PPolynomial(Bx)); botanaPolynomials[1] = (new PPolynomial(2)).multiply(c2) .subtract(new PPolynomial(Ay)).subtract(new PPolynomial(By)); // D will be the rotation of A around C by 90 degrees // d2=c2+(c1-a1), d1=c1-(c2-a2) => d2-c2-c1+a1, d1-c1+c2-a2 botanaPolynomials[2] = ((d2.subtract(c2)).subtract(c1)).add(a1); botanaPolynomials[3] = ((d1.subtract(c1)).add(c2)).subtract(a2); return botanaPolynomials; } }