diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index dff4782ed..9c3307846 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -260,8 +260,8 @@ public: /* Approximation Helper */ // These methods reset the sApproximationEncounteredComplex flag. They should not be use to implement node approximation template static U Epsilon(); - template Expression approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; - template U approximateToScalar(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template Expression approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce = false) const; + template U approximateToScalar(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce = false) const; template static U ApproximateToScalar(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition); template U approximateWithValueForSymbol(const char * symbol, U x, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* Expression roots/extrema solver */ @@ -433,7 +433,7 @@ private: Expression defaultUnaryFunctionDifferential() { return *this; } /* Approximation */ - template Evaluation approximateToEvaluation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template Evaluation approximateToEvaluation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce = false) const; /* Properties */ int defaultGetPolynomialCoefficients(Context * context, const char * symbol, Expression expression[]) const; diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index c971e28b7..a9f91ed8a 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -239,8 +239,8 @@ public: typedef float SinglePrecision; typedef double DoublePrecision; constexpr static int k_maxNumberOfSteps = 10000; - virtual Evaluation approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const = 0; - virtual Evaluation approximate(DoublePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const = 0; + virtual Evaluation approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce = false) const = 0; + virtual Evaluation approximate(DoublePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce = false) const = 0; /* Simplification */ /*!*/ virtual void deepReduceChildren(ReductionContext reductionContext); diff --git a/poincare/src/absolute_value.cpp b/poincare/src/absolute_value.cpp index 905f08d22..dd37f3e1f 100644 --- a/poincare/src/absolute_value.cpp +++ b/poincare/src/absolute_value.cpp @@ -52,7 +52,7 @@ Expression AbsoluteValue::shallowReduce(ExpressionNode::ReductionContext reducti } // |x| = ±x if x is real if (c.isReal(reductionContext.context())) { - double app = c.node()->approximate(double(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar(); + double app = c.node()->approximate(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true).toScalar(); if (!std::isnan(app)) { if ((c.isNumber() && app >= 0) || app >= Expression::Epsilon()) { /* abs(a) = a with a >= 0 diff --git a/poincare/src/complex_argument.cpp b/poincare/src/complex_argument.cpp index 9e1ef1bf8..02f258abc 100644 --- a/poincare/src/complex_argument.cpp +++ b/poincare/src/complex_argument.cpp @@ -51,7 +51,7 @@ Expression ComplexArgument::shallowReduce(ExpressionNode::ReductionContext reduc } bool real = c.isReal(reductionContext.context()); if (real) { - float app = c.node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar(); + float app = c.node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true).toScalar(); if (!std::isnan(app) && app >= Expression::Epsilon()) { // arg(x) = 0 if x > 0 Expression result = Rational::Builder(0); diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index a389510e2..fb57e0dd3 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -375,7 +375,7 @@ Expression Expression::defaultHandleUnitsInChildren() { } Expression Expression::shallowReduceUsingApproximation(ExpressionNode::ReductionContext reductionContext) { - double approx = node()->approximate(double(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar(); + double approx = node()->approximate(double(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true).toScalar(); /* If approx is capped by the largest integer such as all smaller integers can * be exactly represented in IEEE754, approx is the exact result (no * precision were loss). */ @@ -445,11 +445,11 @@ Expression Expression::makePositiveAnyNegativeNumeralFactor(ExpressionNode::Redu } template -Evaluation Expression::approximateToEvaluation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Evaluation Expression::approximateToEvaluation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const { sApproximationEncounteredComplex = false; // Reset interrupting flag because some evaluation methods use it sSimplificationHasBeenInterrupted = false; - Evaluation e = node()->approximate(U(), context, complexFormat, angleUnit); + Evaluation e = node()->approximate(U(), context, complexFormat, angleUnit, withinReduce); if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounteredComplex) { e = Complex::Undefined(); } @@ -854,14 +854,13 @@ Expression Expression::setSign(ExpressionNode::Sign s, ExpressionNode::Reduction /* Evaluation */ template -Expression Expression::approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - return isUninitialized() ? Undefined::Builder() : approximateToEvaluation(context, complexFormat, angleUnit).complexToExpression(complexFormat); +Expression Expression::approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const { + return isUninitialized() ? Undefined::Builder() : approximateToEvaluation(context, complexFormat, angleUnit, withinReduce).complexToExpression(complexFormat); } - template -U Expression::approximateToScalar(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - return approximateToEvaluation(context, complexFormat, angleUnit).toScalar(); +U Expression::approximateToScalar(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const { + return approximateToEvaluation(context, complexFormat, angleUnit, withinReduce).toScalar(); } template @@ -1155,17 +1154,17 @@ void Expression::bracketRoot(const char * symbol, double start, double step, dou template float Expression::Epsilon(); template double Expression::Epsilon(); -template Expression Expression::approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; -template Expression Expression::approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +template Expression Expression::approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const; +template Expression Expression::approximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const; -template float Expression::approximateToScalar(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; -template double Expression::approximateToScalar(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; +template float Expression::approximateToScalar(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const; +template double Expression::approximateToScalar(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const; -template float Expression::ApproximateToScalar(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::SymbolicComputation symbolicComputation); -template double Expression::ApproximateToScalar(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::SymbolicComputation symbolicComputation); +template float Expression::ApproximateToScalar(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::SymbolicComputation symbolicComputation, bool withinReduce); +template double Expression::ApproximateToScalar(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::SymbolicComputation symbolicComputation, bool withinReduce); -template Evaluation Expression::approximateToEvaluation(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; -template Evaluation Expression::approximateToEvaluation(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; +template Evaluation Expression::approximateToEvaluation(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const; +template Evaluation Expression::approximateToEvaluation(Context * context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit, bool withinReduce) const; template float Expression::approximateWithValueForSymbol(const char * symbol, float x, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; template double Expression::approximateWithValueForSymbol(const char * symbol, double x, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; diff --git a/poincare/src/hyperbolic_trigonometric_function.cpp b/poincare/src/hyperbolic_trigonometric_function.cpp index 6ed6d6baa..76dfa541f 100644 --- a/poincare/src/hyperbolic_trigonometric_function.cpp +++ b/poincare/src/hyperbolic_trigonometric_function.cpp @@ -45,7 +45,8 @@ Expression HyperbolicTrigonometricFunction::shallowReduce(ExpressionNode::Reduct && e.approximateToScalar( reductionContext.context(), reductionContext.complexFormat(), - reductionContext.angleUnit()) >= 1.0)) + reductionContext.angleUnit(), + true) >= 1.0)) { result = e; } @@ -58,7 +59,8 @@ Expression HyperbolicTrigonometricFunction::shallowReduce(ExpressionNode::Reduct && e.approximateToScalar( reductionContext.context(), reductionContext.complexFormat(), - reductionContext.angleUnit()) >= 0.0)) + reductionContext.angleUnit(), + true) >= 0.0)) { result = e; } @@ -79,7 +81,8 @@ Expression HyperbolicTrigonometricFunction::shallowReduce(ExpressionNode::Reduct && std::fabs(e.approximateToScalar( reductionContext.context(), reductionContext.complexFormat(), - reductionContext.angleUnit())) < 1.0)) + reductionContext.angleUnit()), + true) < 1.0)) { result = e; } diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index d207bc989..1a093958a 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -300,7 +300,7 @@ Expression Logarithm::simpleShallowReduce(Context * context, Preferences::Comple } bool isNegative = true; Expression result; - Evaluation baseApproximation = b.node()->approximate(1.0f, context, complexFormat, angleUnit); + Evaluation baseApproximation = b.node()->approximate(1.0f, context, complexFormat, angleUnit, true); std::complex logDenominator = std::log10(static_cast&>(baseApproximation).stdComplex()); if (logDenominator.imag() != 0.0f || logDenominator.real() == 0.0f) { result = Undefined::Builder(); diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index 1c7fea1d3..8aa47751c 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -224,7 +224,7 @@ Matrix Matrix::rowCanonize(ExpressionNode::ReductionContext reductionContext, Ex float bestPivot = 0.0; while (iPivot_temp < m) { // Using float to find the biggest pivot is sufficient. - float pivot = AbsoluteValue::Builder(matrixChild(iPivot_temp, k).clone()).approximateToScalar(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + float pivot = AbsoluteValue::Builder(matrixChild(iPivot_temp, k).clone()).approximateToScalar(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true); // Handle very low pivots if (pivot == 0.0f && matrixChild(iPivot_temp, k).nullStatus(reductionContext.context()) != ExpressionNode::NullStatus::Null) { pivot = FLT_MIN; diff --git a/poincare/src/n_ary_expression.cpp b/poincare/src/n_ary_expression.cpp index a64be96c9..a7fba4e9b 100644 --- a/poincare/src/n_ary_expression.cpp +++ b/poincare/src/n_ary_expression.cpp @@ -89,7 +89,7 @@ Expression NAryExpression::checkChildrenAreRationalIntegersAndUpdate(ExpressionN return replaceWithUndefinedInPlace(); } // If c was complex but with a null imaginary part, real part is checked. - float app = c.approximateToScalar(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + float app = c.approximateToScalar(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true); if (std::isfinite(app) && app != std::round(app)) { return replaceWithUndefinedInPlace(); } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index a259bd46e..5a76875eb 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -779,7 +779,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex * - (a^b)^(-1) has to be reduced to avoid infinite loop discussed above; * - if a^b is unreal, a^(-b) also. */ if (!cMinusOne && reductionContext.complexFormat() == Preferences::ComplexFormat::Real) { - Expression approximation = powerBase.approximate(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + Expression approximation = powerBase.approximate(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true); if (approximation.type() == ExpressionNode::Type::Unreal) { // The inner power is unreal, return "unreal" replaceWithInPlace(approximation); diff --git a/poincare/src/sign_function.cpp b/poincare/src/sign_function.cpp index 335c02543..69447828d 100644 --- a/poincare/src/sign_function.cpp +++ b/poincare/src/sign_function.cpp @@ -67,7 +67,7 @@ Expression SignFunction::shallowReduce(ExpressionNode::ReductionContext reductio if (s == ExpressionNode::Sign::Negative) { resultSign = Rational::Builder(-1); } else { - Evaluation childApproximated = child.node()->approximate(1.0f, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + Evaluation childApproximated = child.node()->approximate(1.0f, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true); assert(childApproximated.type() == EvaluationNode::Type::Complex); Complex c = static_cast&>(childApproximated); if (std::isnan(c.imag()) || std::isnan(c.real()) || c.imag() != 0) { diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index a3eb84177..e0ff2bc93 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -286,7 +286,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Expression // Step 1. Look for an expression of type "acos(cos(x))", return x if (AreInverseFunctions(e.childAtIndex(0), e)) { - float x = e.childAtIndex(0).childAtIndex(0).node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), angleUnit).toScalar(); + float x = e.childAtIndex(0).childAtIndex(0).nodex()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), angleUnit, true).toScalar(); if (!(std::isinf(x) || std::isnan(x))) { Expression result = e.childAtIndex(0).childAtIndex(0); // We translate the result within [-π,π] for acos(cos), [-π/2,π/2] for asin(sin) and atan(tan) @@ -314,7 +314,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Expression // Step 2. Special case for atan(sin(x)/cos(x)) if (e.type() == ExpressionNode::Type::ArcTangent && ExpressionIsEquivalentToTangent(e.childAtIndex(0))) { - float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), angleUnit).toScalar(); + float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), angleUnit, true).toScalar(); if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) { Expression result = e.childAtIndex(0).childAtIndex(1).childAtIndex(0); e.replaceWithInPlace(result); diff --git a/poincare/src/trigonometry_cheat_table.cpp b/poincare/src/trigonometry_cheat_table.cpp index 6cf2ca3e9..bcd1e2905 100644 --- a/poincare/src/trigonometry_cheat_table.cpp +++ b/poincare/src/trigonometry_cheat_table.cpp @@ -59,7 +59,7 @@ Expression TrigonometryCheatTable::simplify(const Expression e, ExpressionNode:: } // Approximate e to quickly compare it to cheat table entries - float eValue = e.node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar(); + float eValue = e.node()->approximation(float(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), true).toScalar(); if (std::isnan(eValue) || std::isinf(eValue)) { return Expression(); } diff --git a/poincare/src/variable_context.cpp b/poincare/src/variable_context.cpp index 92d54c364..1020e3cd1 100644 --- a/poincare/src/variable_context.cpp +++ b/poincare/src/variable_context.cpp @@ -33,7 +33,7 @@ const Expression VariableContext::expressionForSymbolAbstract(const SymbolAbstra Symbol unknownSymbol = Symbol::Builder(UCodePointUnknown); if (m_name != nullptr && strcmp(m_name, unknownSymbol.name()) == 0) { assert(std::isnan(unknownSymbolValue)); - unknownSymbolValue = m_value.approximateToScalar(this, Preferences::sharedPreferences()->complexFormat(),Preferences::sharedPreferences()->angleUnit()); + unknownSymbolValue = m_value.approximateToScalar(this, Preferences::sharedPreferences()->complexFormat(), Preferences::sharedPreferences()->angleUnit(), true); } return ContextWithParent::expressionForSymbolAbstract(symbol, clone, unknownSymbolValue); }