/* 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. */ package org.geogebra.common.kernel.arithmetic; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.algos.AlgoRootsPolynomial; import org.geogebra.common.kernel.geos.GeoConic; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoFunction; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.kernelND.GeoConicNDConstants; import org.geogebra.common.plugin.EuclidianStyleConstants; import org.geogebra.common.plugin.Operation; import org.geogebra.common.util.debug.Log; /** * stores left and right hand side of an inequality as Expressions */ public class Inequality { /** * Inequality type */ public enum IneqType { /** can be used e.g. by PointIn, but cannot be drawn */ INEQUALITY_INVALID, /** x > f(y) */ INEQUALITY_PARAMETRIC_X, /** y > f(x) */ INEQUALITY_PARAMETRIC_Y, /** f(x,y) >0, f is linear */ INEQUALITY_LINEAR, /** f(x,y) >0, f is quadratic */ INEQUALITY_CONIC, /** f(x,y) >0, degree of f greater than 2 */ INEQUALITY_IMPLICIT, /** inequality with one variable */ INEQUALITY_1VAR_X, /** inequality with one variable, called y */ INEQUALITY_1VAR_Y } private Operation op = Operation.LESS; private IneqType type; /* private GeoImplicitPoly impBorder; */ private GeoConic conicBorder; private GeoLine lineBorder; private GeoFunction funBorder; private GeoElement border; private Kernel kernel; private boolean isAboveBorder; private ExpressionNode normal; private FunctionVariable[] fv; private MyDouble coef; private GeoPoint[] zeros; // if variable x or y appears with 0 coef, we want to replace the // variable by 0 itself to avoid errors on computation private MyDouble zeroDummy0, zeroDummy1; /** * check whether ExpressionNodes are evaluable to instances of Polynomial or * NumberValue and build an Inequality out of them * * @param kernel * Kernel * @param lhs * left hand side of the equation * @param rhs * right hand side of the equation * @param op * operation * @param fv * variable */ public Inequality(Kernel kernel, ExpressionValue lhs, ExpressionValue rhs, Operation op, FunctionVariable[] fv) { this.op = op; this.kernel = kernel; this.fv = fv; if (op.equals(Operation.GREATER) || op.equals(Operation.GREATER_EQUAL)) { normal = new ExpressionNode(kernel, lhs, Operation.MINUS, rhs); } else { normal = new ExpressionNode(kernel, rhs, Operation.MINUS, lhs); } if (normal.getLeftTree().getOperation() == Operation.ABS && !normal.getRightTree().containsFreeFunctionVariable(null)) { normal.setLeft(normal.getLeftTree().getLeftTree().power(2)); normal.setRight(normal.getRightTree() .multiply(normal.getRightTree().abs())); } else if (normal.getRightTree().getOperation() == Operation.ABS && !normal.getLeftTree().containsFreeFunctionVariable(null)) { normal.setRight(normal.getRightTree().getLeftTree().power(2)); normal.setLeft( normal.getLeftTree().multiply(normal.getLeftTree().abs())); } else if (normal.getRightTree().getOperation() == Operation.ABS && normal.getLeftTree().getOperation() == Operation.ABS) { normal.setRight(normal.getRightTree().getLeftTree().power(2)); normal.setLeft(normal.getLeftTree().getLeftTree().power(2)); } update(); } private void update() { if (fv.length == 1) { init1varFunction(0); if (!funBorder.isPolynomialFunction(false)) { type = IneqType.INEQUALITY_INVALID; } else if (fv[0].toString(StringTemplate.defaultTemplate) .equals("y")) { type = IneqType.INEQUALITY_1VAR_Y; } else { type = IneqType.INEQUALITY_1VAR_X; } return; } if (zeroDummy0 != null) { normal.replace(zeroDummy0, fv[0]).wrap(); } if (zeroDummy1 != null) { normal.replace(zeroDummy1, fv[1]).wrap(); } Double coefY = normal.getCoefficient(fv[1]); Double coefX = normal.getCoefficient(fv[0]); Function fun = null; if (coefY != null && !Kernel.isZero(coefY) && !Double.isNaN(coefY) && coefX == null) { coef = new MyDouble(kernel, -coefY); isAboveBorder = coefY > 0; ExpressionNode m = new ExpressionNode(kernel, new ExpressionNode(kernel, normal, Operation.DIVIDE, coef), Operation.PLUS, fv[1]); m.simplifyLeafs(); fun = new Function(m, fv[0]); type = IneqType.INEQUALITY_PARAMETRIC_Y; } else if (coefX != null && !Kernel.isZero(coefX) && !Double.isNaN(coefX) && coefY == null) { coef = new MyDouble(kernel, -coefX); isAboveBorder = coefX > 0; ExpressionNode m = new ExpressionNode(kernel, new ExpressionNode(kernel, normal, Operation.DIVIDE, coef), Operation.PLUS, fv[0]); m.simplifyLeafs(); fun = new Function(m, fv[1]); type = IneqType.INEQUALITY_PARAMETRIC_X; } else if (coefX != null && Kernel.isZero(coefX) && coefY == null) { zeroDummy0 = new MyDouble(kernel, 0); normal.replace(fv[0], zeroDummy0).wrap(); init1varFunction(1); type = funBorder.isPolynomialFunction(false) ? IneqType.INEQUALITY_1VAR_Y : IneqType.INEQUALITY_INVALID; } else if (coefY != null && Kernel.isZero(coefY) && coefX == null) { zeroDummy1 = new MyDouble(kernel, 0); normal.replace(fv[1], zeroDummy1).wrap(); init1varFunction(0); type = funBorder.isPolynomialFunction(false) ? IneqType.INEQUALITY_1VAR_X : IneqType.INEQUALITY_INVALID; } else { FunctionVariable xVar = new FunctionVariable(kernel, "x"); FunctionVariable yVar = new FunctionVariable(kernel, "y"); ExpressionNode replaced = normal.deepCopy(kernel) .replace(fv[0], xVar).wrap().replace(fv[1], yVar).wrap(); Equation equ = new Equation(kernel, replaced, new MyDouble(kernel, 0)); equ.initEquation(); if (!equ.isPolynomial()) { type = IneqType.INEQUALITY_INVALID; return; } Polynomial newBorder = equ.getNormalForm(); if (newBorder.degree() < 2) { if (lineBorder == null) { lineBorder = new GeoLine(kernel.getConstruction()); } // if we got here coefX and coefY are null #5315 ExpressionValue[][] evs = equ.getNormalForm().getCoeff(); lineBorder.setCoords(coefX = GeoConic.evalCoeff(evs, 1, 0), coefY = GeoConic.evalCoeff(evs, 0, 1), GeoConic.evalCoeff(evs, 0, 0)); type = IneqType.INEQUALITY_LINEAR; border = lineBorder; isAboveBorder = coefY < 0 || coefY == 0.0 && coefX > 0; } else if (newBorder.degree() == 2) { if (conicBorder == null) { conicBorder = new GeoConic(kernel.getConstruction()); } // conicBorder.setLabel("res"); conicBorder.setCoeffs(equ.getNormalForm().getCoeff()); type = IneqType.INEQUALITY_CONIC; border = conicBorder; setAboveBorderFromConic(); } else { type = IneqType.INEQUALITY_INVALID; return; } // TODO implicit ineq /* * if (newBorder.isGeoLine()) { type = IneqType.INEQUALITY_CONIC; if * (conicBorder == null) conicBorder = new * GeoConic(kernel.getConstruction()); border = conicBorder; }} */ } Log.trace(type + ":" + coefX + "," + coefY); if (type == IneqType.INEQUALITY_PARAMETRIC_X || type == IneqType.INEQUALITY_PARAMETRIC_Y) { funBorder = new GeoFunction(kernel.getConstruction()); funBorder.setFunction(fun); if (type == IneqType.INEQUALITY_PARAMETRIC_X) { funBorder.swapEval(); } } if (funBorder != null) { border = funBorder; } if (isStrict()) { border.setLineType(EuclidianStyleConstants.LINE_TYPE_DASHED_SHORT); } else { border.setLineType(EuclidianStyleConstants.LINE_TYPE_FULL); } } private void setAboveBorderFromConic() { if (conicBorder.getType() == GeoConicNDConstants.CONIC_EMPTY || conicBorder .getType() == GeoConicNDConstants.CONIC_SINGLE_POINT) { isAboveBorder = conicBorder.evaluateInSignificantPoint() >= 0; return; } isAboveBorder = conicBorder.evaluateInSignificantPoint() < 0; } private void init1varFunction(int varIndex) { Construction cons = kernel.getConstruction(); boolean supress = cons.isSuppressLabelsActive(); cons.setSuppressLabelCreation(true); // funBorder for inequality f(x)>g(x) is function f(x)-g(x) funBorder = new GeoFunction(cons); funBorder.setFunction(new Function(normal, fv[varIndex])); zeros = RootMultiple(funBorder); // for (int i = 0; i < zeros.length; i++) { // Log.debug(i + ":" + zeros[i]); // } cons.setSuppressLabelCreation(supress); border = funBorder; if (isStrict()) { border.setLineType(EuclidianStyleConstants.LINE_TYPE_DASHED_SHORT); } else { border.setLineType(EuclidianStyleConstants.LINE_TYPE_FULL); } } final private static GeoPoint[] RootMultiple(GeoFunction f) { // allow functions that can be simplified to factors of polynomials if (!f.isPolynomialFunction(true)) { return null; } AlgoRootsPolynomial algo = new AlgoRootsPolynomial(f); GeoPoint[] g = algo.getRootPoints(); return g; } /** * Updates the coefficient k in y<k*f(x) for parametric, for implicit runs * full update. */ public void updateCoef() { Double coefVal = null, otherVal = null; if (type == IneqType.INEQUALITY_PARAMETRIC_Y) { coefVal = normal.getCoefficient(fv[1]); otherVal = normal.getCoefficient(fv[0]); } else if (type == IneqType.INEQUALITY_PARAMETRIC_X) { coefVal = normal.getCoefficient(fv[0]); otherVal = normal.getCoefficient(fv[1]); } if (coefVal == null || coefVal == 0 || (otherVal != null && Math.abs(otherVal) > Math.abs(coefVal))) { update(); } else { isAboveBorder = coefVal > 0; coef.set(-coefVal); } } // TODO remove? /** * @return implicit border */ /* * public GeoImplicitPoly getImpBorder() { return impBorder; } */ @Override final public String toString() { return "inequality"; } /** * @return true if strict */ public boolean isStrict() { return (op.equals(Operation.GREATER) || op.equals(Operation.LESS)); } /** * @return border for parametric equations */ public GeoFunction getFunBorder() { return funBorder; } /** * Returns true for parametric ineqs like y>border(x), false for y<border(x) * (for PARAMETRIC_X vars are swapped) * * @return true for parametric ineqs like y>border(x), false for y<border(x) * */ public boolean isAboveBorder() { return isAboveBorder; } /** * Returns border, which can be function, conic or implicit polynomial * * @return border */ public GeoElement getBorder() { return border; } /** * Returns type of ineq * * @return inequality type */ public IneqType getType() { return type; } /** * @return the conicBorder */ public GeoConic getConicBorder() { return conicBorder; } /** * @return the lineBorder */ public GeoLine getLineBorder() { return lineBorder; } /** * @return zero points for 1var ineqs */ public GeoPoint[] getZeros() { return zeros; } /** * @return operation in ineq */ public Operation getOperation() { return op; } /** * @return simple expression of inequality */ public ExpressionNode getNormalExpression() { return normal; } } // end of class Equation