package org.geogebra.common.kernel.prover;
import org.geogebra.common.cas.GeoGebraCAS;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.algos.AlgoAnglePoints;
import org.geogebra.common.kernel.algos.AlgoDependentNumber;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.SymbolicParametersBotanaAlgo;
import org.geogebra.common.kernel.algos.SymbolicParametersBotanaAlgoAre;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ValidExpression;
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.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.prover.polynomial.PPolynomial;
import org.geogebra.common.kernel.prover.polynomial.PVariable;
/**
* Decides if the objects are equal. Can be embedded into the Prove command to
* work symbolically.
*
* @author Simon Weitzhofer 17 May 2012
* @author Zoltan Kovacs <zoltan@geogebra.org>
*/
public class AlgoAreEqual extends AlgoElement
implements SymbolicParametersBotanaAlgoAre {
private GeoElement inputElement1; // input
private GeoElement inputElement2; // input
private GeoBoolean outputBoolean; // output
private PPolynomial[][] botanaPolynomials;
/**
* Tests if two objects are equal
*
* @param cons
* The construction the objects depend on
* @param inputElement1
* the first object
* @param inputElement2
* the second object
*/
public AlgoAreEqual(Construction cons, GeoElement inputElement1,
GeoElement inputElement2) {
super(cons);
this.inputElement1 = inputElement1;
this.inputElement2 = inputElement2;
outputBoolean = new GeoBoolean(cons);
setInputOutput();
compute();
}
/**
* Tests if two objects are equal
*
* @param cons
* The construction the objects depend on
* @param label
* the label for the AlgoAreEqual object
* @param inputElement1
* the first object
* @param inputElement2
* the second object
*/
public AlgoAreEqual(Construction cons, String label,
GeoElement inputElement1, GeoElement inputElement2) {
this(cons, inputElement1, inputElement2);
outputBoolean.setLabel(label);
}
@Override
public Commands getClassName() {
return Commands.AreEqual;
}
@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
}
/**
* Gets the result of the test
*
* @return true if the objects are equal and false otherwise
*/
public GeoBoolean getResult() {
return outputBoolean;
}
@Override
public final void compute() {
// Formerly we used this:
// outputBoolean.setValue(ExpressionNodeEvaluator.evalEquals(kernel,
// inputElement1, inputElement2).getBoolean());
// But this way is more useful eg for segments, polygons
// ie compares endpoints NOT just length
// #5331
// The formerly used computation is now implemented in AlgoAreCongruent.
outputBoolean.setValue(inputElement1.isEqual(inputElement2));
}
@Override
public PPolynomial[][] getBotanaPolynomials()
throws NoSymbolicParametersException {
if (botanaPolynomials != null) {
return botanaPolynomials;
}
if (inputElement1 instanceof GeoPoint
&& inputElement2 instanceof GeoPoint) {
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;
}
if (inputElement1 instanceof GeoSegment
&& inputElement2 instanceof GeoSegment) {
// currently unimplemented
throw new NoSymbolicParametersException();
}
if (inputElement1 instanceof GeoLine
&& inputElement2 instanceof GeoLine) {
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[2][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: 1) |AC|^2 = 0, 2) |AB|^2 = |CD|^2
botanaPolynomials[0][0] = PPolynomial.sqrDistance(v1[0], v1[1],
v2[0], v2[1]);
botanaPolynomials[1][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[4][1];
PVariable[] v1 = new PVariable[10];
PVariable[] v2 = new PVariable[10];
v1 = ((GeoConic) inputElement1).getBotanaVars(inputElement1);
v2 = ((GeoConic) inputElement2).getBotanaVars(inputElement2);
// We want to prove: 1) A, B, A' coll. 2) A, B, B' coll. 3) F=F'
// f1 = f'1
botanaPolynomials[0][0] = new PPolynomial(v1[8])
.subtract(new PPolynomial(v2[8]));
// f2 = f'2
botanaPolynomials[1][0] = new PPolynomial(v1[9])
.subtract(new PPolynomial(v2[9]));
// A, B, A'
botanaPolynomials[2][0] = PPolynomial.collinear(v1[4], v1[5],
v1[6], v1[7], v2[4], v2[5]);
// A, B, B'
botanaPolynomials[3][0] = PPolynomial.collinear(v1[4], v1[5],
v1[6], v1[7], v2[6], v2[7]);
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);
botanaPolynomials = new PPolynomial[3][1];
// We want to prove: 1) A = D 2) B = E 3) C = F
botanaPolynomials[0][0] = PPolynomial.sqrDistance(vA[0], vA[1],
vD[0], vD[0]);
botanaPolynomials[1][0] = PPolynomial.sqrDistance(vB[0], vB[1],
vE[0], vE[1]);
botanaPolynomials[2][0] = PPolynomial.sqrDistance(vC[0], vC[1],
vF[0], vF[1]);
return botanaPolynomials;
}
// distance between 2 point without segment
if (inputElement1 instanceof GeoNumeric
&& inputElement2 instanceof GeoNumeric
&& (inputElement1.getParentAlgorithm())
.getRelatedModeID() == EuclidianConstants.MODE_DISTANCE
&& (inputElement2.getParentAlgorithm())
.getRelatedModeID() == EuclidianConstants.MODE_DISTANCE) {
// We check whether their length are equal.
botanaPolynomials = new PPolynomial[1][1];
PVariable[] v1 = new PVariable[4];
PVariable[] v2 = new PVariable[4];
// get coordinates of the start and end points
v1 = ((SymbolicParametersBotanaAlgo) inputElement1
.getParentAlgorithm()).getBotanaVars(inputElement1); // AB
v2 = ((SymbolicParametersBotanaAlgo) inputElement2
.getParentAlgorithm()).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;
}
/*
* Equality of two expressions, one of them is a segment, or both are
* expressions.
*/
if ((inputElement1 instanceof GeoNumeric
&& inputElement2 instanceof GeoSegment)
|| (inputElement2 instanceof GeoNumeric
&& inputElement1 instanceof GeoSegment)
|| (inputElement1 instanceof GeoNumeric
&& inputElement2 instanceof GeoNumeric)) {
GeoNumeric n1, n2 = null;
GeoSegment s = null;
if (inputElement1 instanceof GeoNumeric) {
n1 = (GeoNumeric) inputElement1;
if (inputElement2 instanceof GeoNumeric) {
n2 = (GeoNumeric) inputElement2;
} else {
s = (GeoSegment) inputElement2;
}
} else {
n1 = (GeoNumeric) inputElement2;
s = (GeoSegment) inputElement1;
}
GeoGebraCAS cas = (GeoGebraCAS) kernel.getGeoGebraCAS();
ValidExpression resultVE;
if ((inputElement1 instanceof GeoNumeric
&& inputElement2 instanceof GeoNumeric)) {
// Create n1-n2=var as a ValidExpression
resultVE = cas.getCASparser()
.parseGeoGebraCASInputAndResolveDummyVars(
n1.getDefinition() + "-" + n2.getDefinition(),
kernel, null);
} else {
// Create n1-s=var as a ValidExpression
resultVE = cas.getCASparser()
.parseGeoGebraCASInputAndResolveDummyVars(
n1.getDefinition() + "-" + s.getLabelSimple(),
kernel, null);
}
// Convert the ValidExpression to ExpressionNode
ExpressionNode en = new ExpressionNode(kernel, resultVE);
// Silently create an AlgoDependentNumber from the ExpressionNode
AlgoDependentNumber adn = new AlgoDependentNumber(
n1.getConstruction(), en, false, null, false, false);
// Obtain the polynomials
PPolynomial[] result = adn.getBotanaPolynomials(n1); // n1 unused
int no = result.length;
botanaPolynomials = new PPolynomial[1][no + 1];
for (int i = 0; i < no; ++i) {
botanaPolynomials[0][i + 1] = result[i];
}
PVariable[] botanaVars = adn.getBotanaVars(n1); // n1 unused
// Add the equation var=0 to the polynomial list
botanaPolynomials[0][0] = new PPolynomial(botanaVars[0]);
// This AlgoDependentNumber is not needed anymore
n1.getConstruction().removeFromAlgorithmList(adn);
return botanaPolynomials;
}
// area of two polygons
// area of polygon is the sum of areas of triangles in polygon
if (inputElement1 instanceof GeoNumeric
&& inputElement2 instanceof GeoNumeric
&& (inputElement1.getParentAlgorithm())
.getRelatedModeID() == EuclidianConstants.MODE_AREA
&& (inputElement2.getParentAlgorithm())
.getRelatedModeID() == EuclidianConstants.MODE_AREA) {
// get botanaVars of points of first polygon
PVariable[] v1 = ((SymbolicParametersBotanaAlgo) inputElement1
.getParentAlgorithm()).getBotanaVars(inputElement1);
// get botanaVars of points of first polygon
PVariable[] v2 = ((SymbolicParametersBotanaAlgo) inputElement2
.getParentAlgorithm()).getBotanaVars(inputElement2);
// add areas of triangles in first polygon
PPolynomial det1sum = PPolynomial.area(v1[0], v1[1], v1[2], v1[3],
v1[4], v1[5]);
for (int i = 4; i < v1.length - 3; i = i + 2) {
det1sum = det1sum.add(PPolynomial.area(v1[0], v1[1], v1[i],
v1[i + 1], v1[i + 2], v1[i + 3]));
}
// add areas of triangles in second polygon
PPolynomial det2sum = PPolynomial.area(v2[0], v2[1], v2[2], v2[3],
v2[4], v2[5]);
for (int i = 4; i < v2.length - 3; i = i + 2) {
det2sum = det2sum.add(PPolynomial.area(v2[0], v2[1], v2[i],
v2[i + 1], v2[i + 2], v2[i + 3]));
}
botanaPolynomials = new PPolynomial[1][1];
botanaPolynomials[0][0] = (PPolynomial.sqr(det1sum))
.subtract(PPolynomial.sqr(det2sum));
return botanaPolynomials;
}
// TODO: Implement circles etc.
throw new NoSymbolicParametersException();
}
}