package org.geogebra.common.kernel.prover; import java.math.BigInteger; import java.util.HashMap; import java.util.HashSet; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.algos.AlgoAnglePoints; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.algos.SymbolicParameters; import org.geogebra.common.kernel.algos.SymbolicParametersAlgo; import org.geogebra.common.kernel.algos.SymbolicParametersBotanaAlgoAre; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoAngle; import org.geogebra.common.kernel.geos.GeoBoolean; import org.geogebra.common.kernel.geos.GeoConic; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoSegment; import org.geogebra.common.kernel.geos.GeoVector; import org.geogebra.common.kernel.prover.polynomial.PPolynomial; import org.geogebra.common.kernel.prover.polynomial.PVariable; import org.geogebra.common.util.ExtendedBoolean; import org.geogebra.common.util.debug.Log; /** * Decides if two objects are congruent. Currently only just a few special cases * are implemented. The other cases return undefined at the moment. * * @author Zoltan Kovacs <zoltan@geogebra.org> */ public class AlgoAreCongruent extends AlgoElement implements SymbolicParametersBotanaAlgoAre, SymbolicParametersAlgo { private GeoElement inputElement1; // input private GeoElement inputElement2; // input private GeoBoolean outputBoolean; // output private PPolynomial[] polynomials; private PPolynomial[][] botanaPolynomials; /** * Creates a new AlgoAreCongruent function * * @param cons * the Construction * @param a * the first object * @param b * the second object */ public AlgoAreCongruent(final Construction cons, final GeoElement a, final GeoElement b) { super(cons); this.inputElement1 = a; this.inputElement2 = b; outputBoolean = new GeoBoolean(cons); setInputOutput(); compute(); } /** * Creates a new AlgoAreCongruent function * * @param cons * the Construction * @param label * the label for the AlgoAreCongruent object * @param a * the first object * @param b * the second object */ public AlgoAreCongruent(final Construction cons, final String label, final GeoElement a, final GeoElement b) { this(cons, a, b); outputBoolean.setLabel(label); } @Override public Commands getClassName() { return Commands.AreCongruent; } @Override protected void setInputOutput() { input = new GeoElement[2]; input[0] = inputElement1; input[1] = inputElement2; super.setOutputLength(1); super.setOutput(0, outputBoolean); setDependencies(); // done by AlgoElement } /** * Returns the result of the test * * @return true if the two objects are congruent, false if not, undefined if * testing is unimplemented in that case */ public GeoBoolean getResult() { return outputBoolean; } @Override public final void compute() { ExtendedBoolean congruent = inputElement1.isCongruent(inputElement2); if (!ExtendedBoolean.UNKNOWN.equals(congruent)) { outputBoolean.setDefined(); outputBoolean.setValue(congruent.boolVal()); } else { outputBoolean.setUndefinedProverOnly(); } } @Override public SymbolicParameters getSymbolicParameters() { return new SymbolicParameters(this); } @Override public void getFreeVariables(HashSet<PVariable> variables) throws NoSymbolicParametersException { if ((inputElement1 instanceof GeoSegment) || (inputElement2 instanceof GeoSegment)) { throw new NoSymbolicParametersException(); } if (inputElement1 != null && inputElement2 != null) { if (((inputElement1 instanceof GeoPoint) && (inputElement2 instanceof GeoPoint)) || ((inputElement1 instanceof GeoLine) && (inputElement2 instanceof GeoLine)) || ((inputElement1 instanceof GeoVector) && (inputElement2 instanceof GeoVector))) { ((SymbolicParametersAlgo) inputElement1) .getFreeVariables(variables); ((SymbolicParametersAlgo) inputElement2) .getFreeVariables(variables); return; } } throw new NoSymbolicParametersException(); } @Override public int[] getDegrees() throws NoSymbolicParametersException { if ((inputElement1 instanceof GeoSegment) || (inputElement2 instanceof GeoSegment)) { throw new NoSymbolicParametersException(); } if (inputElement1 != null && inputElement2 != null) { if (((inputElement1 instanceof GeoPoint) && (inputElement2 instanceof GeoPoint)) || ((inputElement1 instanceof GeoLine) && (inputElement2 instanceof GeoLine)) || ((inputElement1 instanceof GeoVector) && (inputElement2 instanceof GeoVector))) { int[] degrees1 = ((SymbolicParametersAlgo) inputElement1) .getDegrees(); int[] degrees2 = ((SymbolicParametersAlgo) inputElement2) .getDegrees(); int[] degrees = new int[1]; degrees[0] = Math.max( Math.max(degrees1[0] + degrees2[2], degrees2[0] + degrees1[2]), Math.max(degrees1[1] + degrees2[2], degrees2[1] + degrees1[2])); return degrees; } } throw new NoSymbolicParametersException(); } @Override public BigInteger[] getExactCoordinates( HashMap<PVariable, BigInteger> values) throws NoSymbolicParametersException { if ((inputElement1 instanceof GeoSegment) || (inputElement2 instanceof GeoSegment)) { throw new NoSymbolicParametersException(); } if (inputElement1 != null && inputElement2 != null) { if (((inputElement1 instanceof GeoPoint) && (inputElement2 instanceof GeoPoint)) || ((inputElement1 instanceof GeoLine) && (inputElement2 instanceof GeoLine)) || ((inputElement1 instanceof GeoVector) && (inputElement2 instanceof GeoVector))) { BigInteger[] coords1 = ((SymbolicParametersAlgo) inputElement1) .getExactCoordinates(values); BigInteger[] coords2 = ((SymbolicParametersAlgo) inputElement2) .getExactCoordinates(values); BigInteger[] coords = new BigInteger[1]; coords[0] = coords1[0].multiply(coords2[2]) .subtract(coords2[0].multiply(coords1[2])).abs() .add(coords1[1].multiply(coords2[2]) .subtract(coords2[1].multiply(coords1[2])) .abs()); return coords; } } throw new NoSymbolicParametersException(); } @Override public PPolynomial[] getPolynomials() throws NoSymbolicParametersException { Log.debug(polynomials); if (polynomials != null) { return polynomials; } if ((inputElement1 instanceof GeoSegment) || (inputElement2 instanceof GeoSegment)) { throw new NoSymbolicParametersException(); } if (inputElement1 != null && inputElement2 != null) { if (((inputElement1 instanceof GeoPoint) && (inputElement2 instanceof GeoPoint)) || ((inputElement1 instanceof GeoLine) && (inputElement2 instanceof GeoLine)) || ((inputElement1 instanceof GeoVector) && (inputElement2 instanceof GeoVector))) { PPolynomial[] coords1 = ((SymbolicParametersAlgo) inputElement1) .getPolynomials(); PPolynomial[] coords2 = ((SymbolicParametersAlgo) inputElement2) .getPolynomials(); polynomials = new PPolynomial[2]; polynomials[0] = coords1[0].multiply(coords2[2]) .subtract(coords2[0].multiply(coords1[2])); polynomials[1] = coords1[1].multiply(coords2[2]) .subtract(coords2[1].multiply(coords1[2])); return polynomials; } } throw new NoSymbolicParametersException(); } @Override public PPolynomial[][] getBotanaPolynomials() throws NoSymbolicParametersException { if (botanaPolynomials != null) { return botanaPolynomials; } if (inputElement1 instanceof GeoPoint && inputElement2 instanceof GeoPoint) { // Same as in AreEqual. botanaPolynomials = new PPolynomial[2][1]; PVariable[] v1 = new PVariable[2]; PVariable[] v2 = new PVariable[2]; v1 = ((GeoPoint) inputElement1).getBotanaVars(inputElement1); // A=(x1,y1) v2 = ((GeoPoint) inputElement2).getBotanaVars(inputElement2); // B=(x2,y2) // We want to prove: 1) x1-x2==0, 2) y1-y2==0 botanaPolynomials[0][0] = new PPolynomial(v1[0]) .subtract(new PPolynomial(v2[0])); botanaPolynomials[1][0] = new PPolynomial(v1[1]) .subtract(new PPolynomial(v2[1])); return botanaPolynomials; } // Order is important here: a GeoSegment is also a GeoLine! if (inputElement1 instanceof GeoSegment && inputElement2 instanceof GeoSegment) { // We check whether their length are equal. botanaPolynomials = new PPolynomial[1][1]; PVariable[] v1 = new PVariable[4]; PVariable[] v2 = new PVariable[4]; v1 = ((GeoSegment) inputElement1).getBotanaVars(inputElement1); // AB v2 = ((GeoSegment) inputElement2).getBotanaVars(inputElement2); // CD // We want to prove: d(AB)=d(CD) => // (a1-b1)^2+(a2-b2)^2=(c1-d1)^2+(c2-d2)^2 // => (a1-b1)^2+(a2-b2)^2-(c1-d1)^2-(c2-d2)^2 PPolynomial a1 = new PPolynomial(v1[0]); PPolynomial a2 = new PPolynomial(v1[1]); PPolynomial b1 = new PPolynomial(v1[2]); PPolynomial b2 = new PPolynomial(v1[3]); PPolynomial c1 = new PPolynomial(v2[0]); PPolynomial c2 = new PPolynomial(v2[1]); PPolynomial d1 = new PPolynomial(v2[2]); PPolynomial d2 = new PPolynomial(v2[3]); botanaPolynomials[0][0] = ((PPolynomial.sqr(a1.subtract(b1)) .add(PPolynomial.sqr(a2.subtract(b2)))) .subtract(PPolynomial.sqr(c1.subtract(d1)))) .subtract(PPolynomial.sqr(c2.subtract(d2))); return botanaPolynomials; } if (inputElement1 instanceof GeoLine && inputElement2 instanceof GeoLine) { // Same as in AreEqual. botanaPolynomials = new PPolynomial[2][1]; PVariable[] v1 = new PVariable[4]; PVariable[] v2 = new PVariable[4]; v1 = ((GeoLine) inputElement1).getBotanaVars(inputElement1); // AB v2 = ((GeoLine) inputElement2).getBotanaVars(inputElement2); // CD // We want to prove: 1) ABC collinear, 2) ABD collinear botanaPolynomials[0][0] = PPolynomial.collinear(v1[0], v1[1], v1[2], v1[3], v2[0], v2[1]); botanaPolynomials[1][0] = PPolynomial.collinear(v1[0], v1[1], v1[2], v1[3], v2[2], v2[3]); return botanaPolynomials; } if (inputElement1 instanceof GeoConic && inputElement2 instanceof GeoConic) { if (((GeoConic) inputElement1).isCircle() && ((GeoConic) inputElement2).isCircle()) { botanaPolynomials = new PPolynomial[1][1]; PVariable[] v1 = new PVariable[4]; PVariable[] v2 = new PVariable[4]; // circle with center A and point B v1 = ((GeoConic) inputElement1).getBotanaVars(inputElement1); // circle with center C and point D v2 = ((GeoConic) inputElement2).getBotanaVars(inputElement2); // We want to prove: |AB|^2 = |CD|^2 botanaPolynomials[0][0] = PPolynomial .sqrDistance(v1[0], v1[1], v1[2], v1[3]) .subtract(PPolynomial.sqrDistance(v2[0], v2[1], v2[2], v2[3])); return botanaPolynomials; } if (((GeoConic) inputElement1).isParabola() && ((GeoConic) inputElement2).isParabola()) { botanaPolynomials = new PPolynomial[1][5]; PVariable[] v1 = new PVariable[10]; PVariable[] v2 = new PVariable[10]; v1 = ((GeoConic) inputElement1).getBotanaVars(inputElement1); v2 = ((GeoConic) inputElement2).getBotanaVars(inputElement2); // auxiliary points PVariable[] auxVars = new PVariable[4]; // P - first auxiliary point auxVars[0] = new PVariable(kernel); auxVars[1] = new PVariable(kernel); // P' - second auxiliary point auxVars[2] = new PVariable(kernel); auxVars[3] = new PVariable(kernel); // We want to prove, that the distance between foci points and // directrixes are equal // FP orthogonal to AB botanaPolynomials[0][0] = PPolynomial.perpendicular(v1[8], v1[9], auxVars[0], auxVars[1], v1[4], v1[5], v1[6], v1[7]); // A, B, P collinear botanaPolynomials[0][1] = PPolynomial.collinear(auxVars[0], auxVars[1], v1[4], v1[5], v1[6], v1[7]); // F'P' orthogonal to A'B' botanaPolynomials[0][2] = PPolynomial.perpendicular(v2[8], v2[9], auxVars[2], auxVars[3], v2[4], v2[5], v2[6], v2[7]); // A', B', P' collinear botanaPolynomials[0][3] = PPolynomial.collinear(auxVars[2], auxVars[3], v2[4], v2[5], v2[6], v2[7]); // |FP|^2 = |F'P'|^2 botanaPolynomials[0][4] = PPolynomial .sqrDistance(v1[8], v1[9], auxVars[0], auxVars[1]) .subtract(PPolynomial.sqrDistance(v2[8], v2[9], auxVars[2], auxVars[3])); return botanaPolynomials; } } if (inputElement1 instanceof GeoAngle && inputElement2 instanceof GeoAngle) { AlgoAnglePoints algo1 = (AlgoAnglePoints) inputElement1 .getParentAlgorithm(); // get points of first angle GeoPoint A = (GeoPoint) algo1.input[0]; GeoPoint B = (GeoPoint) algo1.input[1]; GeoPoint C = (GeoPoint) algo1.input[2]; PVariable[] vA = A.getBotanaVars(A); PVariable[] vB = B.getBotanaVars(B); PVariable[] vC = C.getBotanaVars(C); AlgoAnglePoints algo2 = (AlgoAnglePoints) inputElement2 .getParentAlgorithm(); // get points of second angle GeoPoint D = (GeoPoint) algo2.input[0]; GeoPoint E = (GeoPoint) algo2.input[1]; GeoPoint F = (GeoPoint) algo2.input[2]; PVariable[] vD = D.getBotanaVars(D); PVariable[] vE = E.getBotanaVars(E); PVariable[] vF = F.getBotanaVars(F); PPolynomial a1 = new PPolynomial(vB[0]); PPolynomial a2 = new PPolynomial(vB[1]); PPolynomial b1 = new PPolynomial(vA[0]); PPolynomial b2 = new PPolynomial(vA[1]); PPolynomial c1 = new PPolynomial(vC[0]); PPolynomial c2 = new PPolynomial(vC[1]); PPolynomial d1 = new PPolynomial(vE[0]); PPolynomial d2 = new PPolynomial(vE[1]); PPolynomial e1 = new PPolynomial(vD[0]); PPolynomial e2 = new PPolynomial(vD[1]); PPolynomial f1 = new PPolynomial(vF[0]); PPolynomial f2 = new PPolynomial(vF[1]); PPolynomial p1 = a1.subtract(c1).multiply(b1.subtract(a1)); PPolynomial p2 = a2.subtract(c2).multiply(b2.subtract(a2)); // (CA*AB)^2 PPolynomial numerator1 = PPolynomial.sqr(p1.add(p2)); PPolynomial p3 = PPolynomial.sqr(a1.subtract(c1)) .add(PPolynomial.sqr(a2.subtract(c2))); PPolynomial p4 = PPolynomial.sqr(b1.subtract(a1)) .add(PPolynomial.sqr(b2.subtract(a2))); // ||CA||^2 * ||AB||^2 PPolynomial denominator1 = p3.multiply(p4); PPolynomial p5 = d1.subtract(f1).multiply(e1.subtract(d1)); PPolynomial p6 = d2.subtract(f2).multiply(e2.subtract(d2)); // (FD*DE)^2 PPolynomial numerator2 = PPolynomial.sqr(p5.add(p6)); PPolynomial p7 = PPolynomial.sqr(d1.subtract(f1)) .add(PPolynomial.sqr(d2.subtract(f2))); PPolynomial p8 = PPolynomial.sqr(e1.subtract(d1)) .add(PPolynomial.sqr(e2.subtract(d2))); // ||FD||^2 * ||DE||^2 PPolynomial denominator2 = p7.multiply(p8); // We want to prove: (CA*AB)^2 / (||CA||^2 * ||AB||^2) = (FD*DE)^2 / // (||FD||^2 * ||DE||^2) botanaPolynomials = new PPolynomial[1][1]; botanaPolynomials[0][0] = numerator1.multiply(denominator2) .subtract(denominator1.multiply(numerator2)); return botanaPolynomials; } throw new NoSymbolicParametersException(); } }