package org.geogebra.common.kernel.geos;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoMacroInterface;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.plugin.Operation;
import org.geogebra.common.util.lang.Unicode;
/**
* Boolean function of the type a(<|<=)x(<|<=)b
*
* @author Markus Hohenwarter
*
*/
public class GeoInterval extends GeoFunction {
/**
* Creates new GeoInterval
*
* @param c
* construction
* @param f
* boolean function
*/
public GeoInterval(Construction c, Function f) {
super(c, f);
}
/**
* Copy constructor
*
* @param geoInterval
* interval to copy
*/
public GeoInterval(GeoInterval geoInterval) {
super(geoInterval.cons);
set(geoInterval);
}
/**
* Creates new unlabeled interval
*
* @param cons
* construction
*/
public GeoInterval(Construction cons) {
super(cons);
}
@Override
public GeoElement copy() {
return new GeoInterval(this);
}
@Override
public void set(GeoElementND geo) {
GeoInterval geoFun = (GeoInterval) geo;
if (geo == null || geoFun.fun == null) {
fun = null;
isDefined = false;
return;
}
isDefined = geoFun.isDefined;
fun = new Function(geoFun.fun, kernel);
// macro OUTPUT
if (geo.getConstruction() != cons && isAlgoMacroOutput()) {
// this object is an output object of AlgoMacro
// we need to check the references to all geos in its function's
// expression
if (!geoFun.isIndependent()) {
AlgoMacroInterface algoMacro = (AlgoMacroInterface) getParentAlgorithm();
algoMacro.initFunction(this.fun);
}
}
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.INTERVAL;
}
private StringBuilder sbToString2;
@Override
public String toString(StringTemplate tpl) {
if (sbToString2 == null) {
sbToString2 = new StringBuilder();
} else {
sbToString2.setLength(0);
}
if (isLabelSet()) {
sbToString2.append(label);
sbToString2.append(": ");
}
sbToString2.append(toSymbolicString(tpl));
return sbToString2.toString();
}
@Override
public String toValueString(StringTemplate tpl) {
return toString(false, tpl);
}
// private double rightBound = Double.NaN;
// private double leftBound = Double.NaN;
private double[] leftRightBoundsField;
// private String rightStr = "", leftStr = "";
private String leftRightStrField[];
// private char rightInequality = ' ';
// private char leftInequality = ' ';
private char[] leftRightInequalityField;
/**
* Returns string description of the interval
*
* @param symbolic
* true for symbolic, false for numeric
* @return string description of the interval
*/
private String toString(boolean symbolic, StringTemplate tpl) {
// output as nice string eg 3 < x < 5
if (!isDefined()) {
return "?";
}
// return "3 < x < 5";//fun.toValueString();
ExpressionNode en = fun.getExpression();
if (en.getOperation().equals(Operation.AND)
|| en.getOperation().equals(Operation.AND_INTERVAL)) {
ExpressionValue left = en.getLeft();
ExpressionValue right = en.getRight();
if (left.isExpressionNode() && right.isExpressionNode()) {
updateBoundaries();
if (!Double.isNaN(leftRightBoundsField[1])
&& !Double.isNaN(leftRightBoundsField[0])
&& leftRightBoundsField[0] <= leftRightBoundsField[1]) {
sbToString.setLength(0);
sbToString.append(symbolic ? leftRightStrField[0]
: kernel.format(leftRightBoundsField[0], tpl));
sbToString.append(' ');
sbToString.append(leftRightInequalityField[0]);
sbToString.append(' ');
sbToString.append(getVarString(tpl));
sbToString.append(' ');
sbToString.append(leftRightInequalityField[1]);
sbToString.append(' ');
sbToString.append(symbolic ? leftRightStrField[1]
: kernel.format(leftRightBoundsField[1], tpl));
return sbToString.toString();
// return kernel.format(leftBound)
// +leftInequality+" x
// "+rightInequality+kernel.format(rightBound);
}
}
}
// eg x<3 && x>10
// Application.debug("fall through");
return symbolic ? super.toSymbolicString(tpl)
: super.toValueString(tpl);
}
private void updateBoundaries() {
if (leftRightBoundsField == null) {
leftRightBoundsField = new double[2];
leftRightBoundsField[0] = Double.NaN;
leftRightBoundsField[1] = Double.NaN;
}
if (leftRightStrField == null) {
leftRightStrField = new String[2];
}
if (leftRightInequalityField == null) {
leftRightInequalityField = new char[2];
}
updateBoundaries(fun.getExpression(), leftRightBoundsField,
leftRightStrField, leftRightInequalityField);
}
@Override
public String toSymbolicString(StringTemplate tpl) {
if (isDefined()) {
return toString(true, tpl);
}
return "?";
}
@Override
public String toLaTeXString(boolean symbolic, StringTemplate tpl) {
if (isDefined()) {
return fun.toLaTeXString(symbolic, tpl);
}
return "?";
}
@Override
public boolean isEqual(GeoElementND geo) {
return false;
}
/**
* Checks given node and updates the arrays
*
* @param en
* expression
* @param leftRightDouble
* bounds as doubles
* @param leftRightStr
* bounds as string
* @param leftRightInequalityChar
* inequality characters
*/
public static void updateBoundaries(ExpressionNode en,
double[] leftRightDouble, String[] leftRightStr,
char[] leftRightInequalityChar) {
char leftInequality, rightInequality;
double leftBound, rightBound;
leftBound = leftRightDouble[0];
rightBound = leftRightDouble[1];
leftInequality = leftRightInequalityChar[0];
rightInequality = leftRightInequalityChar[1];
if (en.getOperation().equals(Operation.AND)
|| en.getOperation().equals(Operation.AND_INTERVAL)) {
ExpressionValue left = en.getLeft();
ExpressionValue right = en.getRight();
ExpressionNode enLeft = (ExpressionNode) left;
ExpressionNode enRight = (ExpressionNode) right;
Operation opLeft = enLeft.getOperation();
Operation opRight = enRight.getOperation();
ExpressionValue leftLeft = enLeft.getLeft();
ExpressionValue leftRight = enLeft.getRight();
ExpressionValue rightLeft = enRight.getLeft();
ExpressionValue rightRight = enRight.getRight();
if ((opLeft.equals(Operation.LESS)
|| opLeft.equals(Operation.LESS_EQUAL))) {
if (leftLeft instanceof FunctionVariable
&& leftRight.isNumberValue()) {
rightInequality = opLeft.equals(Operation.LESS) ? '<'
: Unicode.LESS_EQUAL;
rightBound = setRightBound(leftRight, leftRightStr);
} else if (leftRight instanceof FunctionVariable
&& leftLeft.isNumberValue()) {
leftInequality = opLeft.equals(Operation.LESS) ? '<'
: Unicode.LESS_EQUAL;
leftBound = setLeftBound(leftLeft, leftRightStr);
}
} else if ((opLeft.equals(Operation.GREATER)
|| opLeft.equals(Operation.GREATER_EQUAL))) {
if (leftLeft instanceof FunctionVariable
&& leftRight.isNumberValue()) {
leftInequality = opLeft.equals(Operation.GREATER) ? '<'
: Unicode.LESS_EQUAL;
leftBound = setLeftBound(leftRight, leftRightStr);
} else if (leftRight instanceof FunctionVariable
&& leftLeft.isNumberValue()) {
rightInequality = opLeft.equals(Operation.GREATER) ? '<'
: Unicode.LESS_EQUAL;
rightBound = setRightBound(leftLeft, leftRightStr);
}
}
if ((opRight.equals(Operation.LESS)
|| opRight.equals(Operation.LESS_EQUAL))) {
if (rightLeft instanceof FunctionVariable
&& rightRight.isNumberValue()) {
rightInequality = opRight.equals(Operation.LESS) ? '<'
: Unicode.LESS_EQUAL;
rightBound = setRightBound(rightRight, leftRightStr);
} else if (rightRight instanceof FunctionVariable
&& rightLeft.isNumberValue()) {
leftInequality = opRight.equals(Operation.LESS) ? '<'
: Unicode.LESS_EQUAL;
leftBound = setLeftBound(rightLeft, leftRightStr);
}
} else if ((opRight.equals(Operation.GREATER)
|| opRight.equals(Operation.GREATER_EQUAL))) {
if (rightLeft instanceof FunctionVariable
&& rightRight.isNumberValue()) {
leftInequality = opRight.equals(Operation.GREATER) ? '<'
: Unicode.LESS_EQUAL;
leftBound = setLeftBound(rightRight, leftRightStr);
} else if (rightRight instanceof FunctionVariable
&& rightLeft.isNumberValue()) {
rightInequality = opRight.equals(Operation.GREATER) ? '<'
: Unicode.LESS_EQUAL;
rightBound = setRightBound(rightLeft, leftRightStr);
}
}
} else {
rightBound = Double.NaN;
leftBound = Double.NaN;
}
if (rightBound < leftBound) {
rightBound = Double.NaN;
leftBound = Double.NaN;
}
// values to return
leftRightDouble[0] = leftBound;
leftRightDouble[1] = rightBound;
leftRightInequalityChar[0] = leftInequality;
leftRightInequalityChar[1] = rightInequality;
}
private static double setLeftBound(ExpressionValue nv,
String[] leftRightStr) {
if (nv.isGeoElement()) {
leftRightStr[0] = ((GeoElement) nv)
.getLabel(StringTemplate.defaultTemplate);
} else {
leftRightStr[0] = nv.toString(StringTemplate.defaultTemplate);
}
return nv.evaluateDouble();
}
private static double setRightBound(ExpressionValue nv,
String[] leftRightStr) {
if (nv.isGeoElement()) {
leftRightStr[1] = ((GeoElement) nv)
.getLabel(StringTemplate.defaultTemplate);
} else {
leftRightStr[1] = nv.toString(StringTemplate.defaultTemplate);
}
return nv.evaluateDouble();
}
/**
* @return left bound of the interval
*/
public double getMin() {
updateBoundaries();
return leftRightBoundsField[0];
}
/**
* @return right bound of the interval
*/
public double getMax() {
updateBoundaries();
return leftRightBoundsField[1];
}
/**
* @return center of the interval (number)
*/
public double getMidPoint() {
updateBoundaries();
return (leftRightBoundsField[1] + leftRightBoundsField[0]) / 2;
}
@Override
public boolean isGeoInterval() {
return true;
}
@Override
public String toOutputValueString(StringTemplate tpl) {
return toValueString(tpl);
}
@Override
public int getMinimumLineThickness() {
return 0;
}
}