diff --git a/apps/regression/model/model.cpp b/apps/regression/model/model.cpp index 2df92feff..fb62cd1b0 100644 --- a/apps/regression/model/model.cpp +++ b/apps/regression/model/model.cpp @@ -19,7 +19,8 @@ double Model::levelSet(double * modelCoefficients, double xMin, double step, dou Expression yExpression = Number::DecimalNumber(y); PoincareHelpers::Simplify(&yExpression, *context); Expression modelExpression = simplifiedExpression(modelCoefficients, context); - double result = modelExpression.nextIntersection("x", xMin, step, xMax, *context, Preferences::sharedPreferences()->angleUnit(), yExpression).abscissa; + Preferences * preferences = Preferences::sharedPreferences(); + double result = modelExpression.nextIntersection("x", xMin, step, xMax, *context, preferences->complexFormat(), preferences->angleUnit(), yExpression).abscissa; return result; } diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index e0fb7f8ab..04f3e652a 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -251,13 +251,12 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { Poincare::Symbol vn1Symbol("v(n+1)", 6); Poincare::Symbol unSymbol("u(n)", 4); Poincare::Symbol un1Symbol("u(n+1)", 6); - Preferences * preferences = Poincare::Preferences::sharedPreferences(); switch (m_type) { case Type::Explicit: { ctx.setValueForSymbol(un, unSymbol); ctx.setValueForSymbol(vn, vnSymbol); - return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)n, ctx, preferences->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)n, ctx); } case Type::SingleRecurrence: { @@ -268,7 +267,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { ctx.setValueForSymbol(unm1, unSymbol); ctx.setValueForSymbol(vn, vn1Symbol); ctx.setValueForSymbol(vnm1, vnSymbol); - return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)(n-1), ctx, preferences->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-1), ctx); } default: { @@ -282,7 +281,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { ctx.setValueForSymbol(unm2, unSymbol); ctx.setValueForSymbol(vnm1, vn1Symbol); ctx.setValueForSymbol(vnm2, vnSymbol); - return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)(n-2), ctx, preferences->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-2), ctx); } } } diff --git a/apps/shared/function.cpp b/apps/shared/function.cpp index efa4cc6e5..3fff69cd8 100644 --- a/apps/shared/function.cpp +++ b/apps/shared/function.cpp @@ -1,4 +1,5 @@ #include "function.h" +#include "poincare_helpers.h" #include #include #include @@ -41,7 +42,7 @@ void Function::setActive(bool active) { template T Function::templatedApproximateAtAbscissa(T x, Poincare::Context * context) const { - return expression(context).approximateWithValueForSymbol(symbol(), x, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(context), symbol(), x, *context); } } diff --git a/apps/shared/poincare_helpers.h b/apps/shared/poincare_helpers.h index eb4162dcb..589d536a4 100644 --- a/apps/shared/poincare_helpers.h +++ b/apps/shared/poincare_helpers.h @@ -10,7 +10,8 @@ namespace Shared { namespace PoincareHelpers { inline Poincare::Layout CreateLayout(const Poincare::Expression e) { - return e.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + return e.createLayout(preferences->displayMode(), preferences->numberOfSignificantDigits()); } template @@ -24,25 +25,39 @@ inline int Serialize(const Poincare::Expression e, char * buffer, int bufferSize template inline Poincare::Expression Approximate(const Poincare::Expression e, Poincare::Context & context) { - Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat(); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat(); complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, e, context); - return e.approximate(context, complexFormat, Poincare::Preferences::sharedPreferences()->angleUnit()); + return e.approximate(context, complexFormat, preferences->angleUnit()); } template inline T ApproximateToScalar(const Poincare::Expression e, Poincare::Context & context) { - return e.approximateToScalar(context, Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat(); + complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, e, context); + return e.approximateToScalar(context, complexFormat, preferences->angleUnit()); +} + +template +inline T ApproximateWithValueForSymbol(const Poincare::Expression e, const char * symbol, T x, Poincare::Context & context) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat(); + complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, e, context); + return e.approximateWithValueForSymbol(symbol, x, context, complexFormat, preferences->angleUnit()); } template inline T ApproximateToScalar(const char * text, Poincare::Context & context) { - Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat(); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat(); complexFormat = Poincare::Expression::UpdatedComplexFormatWithTextInput(complexFormat, text); - return Poincare::Expression::approximateToScalar(text, context, complexFormat, Poincare::Preferences::sharedPreferences()->angleUnit()); + return Poincare::Expression::approximateToScalar(text, context, complexFormat, preferences->angleUnit()); } inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Context & context) { - return Poincare::Expression::ParseAndSimplify(text, context, Poincare::Preferences::sharedPreferences()->complexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + return Poincare::Expression::ParseAndSimplify(text, context, preferences->complexFormat(), preferences->angleUnit()); } inline void Simplify(Poincare::Expression * e, Poincare::Context & context, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat()) { @@ -51,7 +66,8 @@ inline void Simplify(Poincare::Expression * e, Poincare::Context & context, Poin } inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context & context) { - Poincare::Expression::ParseAndSimplifyAndApproximate(text, simplifiedExpression, approximateExpression, context, Poincare::Preferences::sharedPreferences()->complexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Expression::ParseAndSimplifyAndApproximate(text, simplifiedExpression, approximateExpression, context, preferences->complexFormat(), preferences->angleUnit()); } inline void SimplifyAndApproximate(Poincare::Expression * e, Poincare::Expression * approximate, Poincare::Context & context, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat()) { diff --git a/apps/shared/storage_cartesian_function.cpp b/apps/shared/storage_cartesian_function.cpp index 33a785ca3..1dc520041 100644 --- a/apps/shared/storage_cartesian_function.cpp +++ b/apps/shared/storage_cartesian_function.cpp @@ -103,22 +103,26 @@ double StorageCartesianFunction::sumBetweenBounds(double start, double end, Poin Expression::Coordinate2D StorageCartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextMinimum(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit()); + Preferences * preferences = Preferences::sharedPreferences(); + return expressionReduced(context).nextMinimum(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit()); } Expression::Coordinate2D StorageCartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextMaximum(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit()); + Preferences * preferences = Preferences::sharedPreferences(); + return expressionReduced(context).nextMaximum(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit()); } double StorageCartesianFunction::nextRootFrom(double start, double step, double max, Context * context) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextRoot(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit()); + Preferences * preferences = Preferences::sharedPreferences(); + return expressionReduced(context).nextRoot(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit()); } Expression::Coordinate2D StorageCartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Expression e) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextIntersection(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit(), e); + Preferences * preferences = Preferences::sharedPreferences(); + return expressionReduced(context).nextIntersection(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit(), e); } StorageCartesianFunction::CartesianFunctionRecordData * StorageCartesianFunction::recordData() const { diff --git a/apps/shared/storage_function.cpp b/apps/shared/storage_function.cpp index afb572634..02fa70fd4 100644 --- a/apps/shared/storage_function.cpp +++ b/apps/shared/storage_function.cpp @@ -1,4 +1,5 @@ #include "storage_function.h" +#include "poincare_helpers.h" #include #include "poincare/src/parsing/parser.h" #include @@ -76,7 +77,7 @@ T StorageFunction::templatedApproximateAtAbscissa(T x, Poincare::Context * conte return NAN; } const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).approximateWithValueForSymbol(unknownX, x, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expressionReduced(context), unknownX, x, *context); } StorageFunction::FunctionRecordData * StorageFunction::recordData() const { diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index 708837b4d..b7b444985 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -78,7 +78,8 @@ bool EquationStore::haveMoreApproximationSolutions(Context * context) { return false; } double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; - return !std::isnan(definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context, Preferences::sharedPreferences()->angleUnit())); + Preferences * preferences = Preferences::sharedPreferences(); + return !std::isnan(definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context, preferences->complexFormat(), preferences->angleUnit())); } void EquationStore::approximateSolve(Poincare::Context * context) { @@ -87,8 +88,9 @@ void EquationStore::approximateSolve(Poincare::Context * context) { m_numberOfSolutions = 0; double start = m_intervalApproximateSolutions[0]; double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; + Preferences * preferences = Preferences::sharedPreferences(); for (int i = 0; i < k_maxNumberOfApproximateSolutions; i++) { - m_approximateSolutions[i] = definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], start, step, m_intervalApproximateSolutions[1], *context, Preferences::sharedPreferences()->angleUnit()); + m_approximateSolutions[i] = definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], start, step, m_intervalApproximateSolutions[1], *context, preferences->complexFormat(), preferences->angleUnit()); if (std::isnan(m_approximateSolutions[i])) { break; } else { diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 1576a71a7..ae6d8dde2 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -167,6 +167,7 @@ public: Expression defaultReplaceUnknown(const Symbol & symbol); /* Complex */ + static void SetEncounterComplex(bool encounterComplex); static Preferences::ComplexFormat UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput); static Preferences::ComplexFormat UpdatedComplexFormatWithExpressionInput(Preferences::ComplexFormat complexFormat, const Expression & e, Context & context); bool isReal(Context & context) const { return node()->isReal(context); } @@ -193,20 +194,21 @@ public: Expression degreeToRadian(); /* Approximation Helper */ + // These methods reset the sApproximationEncounterComplex 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::AngleUnit angleUnit) const; + template U approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; template static U approximateToScalar(const char * text, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); - template U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const; + template U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* Expression roots/extrema solver */ struct Coordinate2D { double abscissa; double value; }; - Coordinate2D nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const; - Coordinate2D nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const; - double nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const; - Coordinate2D nextIntersection(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; + Coordinate2D nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + Coordinate2D nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + double nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + Coordinate2D nextIntersection(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; /* This class is meant to contain data about named functions (e.g. sin, tan...) * in one place: their name, their number of children and a pointer to a builder. @@ -298,7 +300,7 @@ private: Expression defaultShallowBeautify() { return *this; } /* Approximation */ - template Evaluation approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation approximateToEvaluation(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* Properties */ Expression defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression); @@ -315,13 +317,13 @@ private: constexpr static double k_sqrtEps = 1.4901161193847656E-8; // sqrt(DBL_EPSILON) constexpr static double k_goldenRatio = 0.381966011250105151795413165634361882279690820194237137864; // (3-sqrt(5))/2 constexpr static double k_maxFloat = 1e100; - typedef double (*EvaluationAtAbscissa)(const char * symbol, double abscissa, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1); - Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const; - void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; - Coordinate2D brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; - double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; - void bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; - double brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; + typedef double (*EvaluationAtAbscissa)(const char * symbol, double abscissa, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1); + Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const; + void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; + Coordinate2D brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; + double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; + void bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; + double brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; }; } diff --git a/poincare/src/absolute_value.cpp b/poincare/src/absolute_value.cpp index 8a053454b..e3b97bf27 100644 --- a/poincare/src/absolute_value.cpp +++ b/poincare/src/absolute_value.cpp @@ -49,7 +49,7 @@ Expression AbsoluteValue::shallowReduce(Context & context, Preferences::ComplexF #endif Expression c = childAtIndex(0); if (c.isReal(context)) { - float app = c.approximateToScalar(context, angleUnit); + float app = c.node()->approximate(float(), context, angleUnit).toScalar(); if (!std::isnan(app) && app >= Expression::epsilon()) { // abs(a) = a with a > 0 replaceWithInPlace(c); diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 4237baf1b..6f735d972 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -22,6 +22,9 @@ namespace Poincare { template void ComplexNode::setComplex(std::complex c) { + if (c.imag() != 0.0) { + Expression::SetEncounterComplex(true); + } this->real(c.real()); this->imag(c.imag()); if (this->real() == -0) { diff --git a/poincare/src/complex_argument.cpp b/poincare/src/complex_argument.cpp index c0aec6212..c4c6d9b0d 100644 --- a/poincare/src/complex_argument.cpp +++ b/poincare/src/complex_argument.cpp @@ -48,7 +48,7 @@ Expression ComplexArgument::shallowReduce(Context & context, Preferences::Comple #endif bool real = c.isReal(context); if (real) { - float app = c.approximateToScalar(context, angleUnit); + float app = c.node()->approximate(float(), context, angleUnit).toScalar(); if (!std::isnan(app) && app >= Expression::epsilon()) { // arg(x) = 0 if x > 0 Expression result = Rational(0); diff --git a/poincare/src/derivative.cpp b/poincare/src/derivative.cpp index 2fde528f2..b59f02091 100644 --- a/poincare/src/derivative.cpp +++ b/poincare/src/derivative.cpp @@ -76,7 +76,10 @@ Evaluation DerivativeNode::templatedApproximate(Context& context, Preferences template T DerivativeNode::approximateWithArgument(T x, Context & context, Preferences::AngleUnit angleUnit) const { assert(childAtIndex(1)->type() == Type::Symbol); - return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast(childAtIndex(1))->name(), x, context, angleUnit); + VariableContext variableContext = VariableContext(static_cast(childAtIndex(1))->name(), &context); + variableContext.setApproximationForVariable(x); + // Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounterComplex flag + return childAtIndex(0)->approximate(T(), variableContext, angleUnit).toScalar(); } template diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 63606669c..efb8737fa 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -14,6 +14,7 @@ namespace Poincare { bool Expression::sSymbolReplacementsCountLock = false; +static bool sApproximationEncounterComplex = false; /* Constructor & Destructor */ @@ -269,10 +270,15 @@ Expression Expression::makePositiveAnyNegativeNumeralFactor(Context & context, P } template -Evaluation Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + sApproximationEncounterComplex = false; // Reset interrupting flag because some evaluation methods use it sSimplificationHasBeenInterrupted = false; - return node()->approximate(U(), context, angleUnit); + Evaluation e = node()->approximate(U(), context, angleUnit); + if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounterComplex) { + e = Complex::Undefined(); + } + return e; } Expression Expression::defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression) { @@ -314,6 +320,10 @@ Expression Expression::defaultReplaceUnknown(const Symbol & symbol) { /* Complex */ +void Expression::SetEncounterComplex(bool encounterComplex) { + sApproximationEncounterComplex = encounterComplex; +} + Preferences::ComplexFormat Expression::UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput) { if (complexFormat == Preferences::ComplexFormat::Real && strchr(textInput, Ion::Charset::IComplex) != nullptr) { return Preferences::ComplexFormat::Cartesian; @@ -538,27 +548,27 @@ Expression Expression::setSign(ExpressionNode::Sign s, Context * context, Prefer template Expression Expression::approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - return isUninitialized() ? Undefined() : approximateToEvaluation(context, angleUnit).complexToExpression(complexFormat); + return isUninitialized() ? Undefined() : approximateToEvaluation(context, complexFormat, angleUnit).complexToExpression(complexFormat); } template -U Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const { - return approximateToEvaluation(context, angleUnit).toScalar(); +U Expression::approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return approximateToEvaluation(context, complexFormat, angleUnit).toScalar(); } template U Expression::approximateToScalar(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { Expression exp = ParseAndSimplify(text, context, UpdatedComplexFormatWithTextInput(complexFormat, text), angleUnit); assert(!exp.isUninitialized()); - return exp.approximateToScalar(context, angleUnit); + return exp.approximateToScalar(context, complexFormat, angleUnit); } template -U Expression::approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const { +U Expression::approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { VariableContext variableContext = VariableContext(symbol, &context); variableContext.setApproximationForVariable(x); - return approximateToScalar(variableContext, angleUnit); + return approximateToScalar(variableContext, complexFormat, angleUnit); } template @@ -580,7 +590,9 @@ bool Expression::isMinusOne(const Expression e) { } Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Preferences::ComplexFormat complexFormat, bool undefined, bool isZeroRa, bool isOneRa, bool isZeroTb, bool isOneTb, bool isNegativeRa, bool isNegativeTb) { - if (undefined) { + if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounterComplex) { + return Unreal(); + } else if (undefined) { return Undefined(); } switch (complexFormat) { @@ -651,37 +663,42 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre /* Expression roots/extrema solver*/ -typename Expression::Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const { - return nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit); +typename Expression::Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context); + return nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit); } -typename Expression::Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const { - Coordinate2D minimumOfOpposite = nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { - return -expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit); +typename Expression::Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context); + Coordinate2D minimumOfOpposite = nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { + return -expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit); return {.abscissa = minimumOfOpposite.abscissa, .value = -minimumOfOpposite.value}; } -double Expression::nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const { - return nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit, nullptr); +double Expression::nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context); + return nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit, nullptr); } -typename Expression::Coordinate2D Expression::nextIntersection(const char * symbol, double start, double step, double max, Poincare::Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { - double resultAbscissa = nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit, expression); - typename Expression::Coordinate2D result = {.abscissa = resultAbscissa, .value = approximateWithValueForSymbol(symbol, resultAbscissa, context, angleUnit)}; +typename Expression::Coordinate2D Expression::nextIntersection(const char * symbol, double start, double step, double max, Poincare::Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { + complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context); + complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, expression, context); + double resultAbscissa = nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit, expression); + typename Expression::Coordinate2D result = {.abscissa = resultAbscissa, .value = approximateWithValueForSymbol(symbol, resultAbscissa, context, complexFormat, angleUnit)}; if (std::fabs(result.value) < step*k_solverPrecision) { result.value = 0.0; } return result; } -typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression, bool lookForRootMinimum) const { +typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression, bool lookForRootMinimum) const { Coordinate2D result = {.abscissa = NAN, .value = NAN}; if (start == max || step == 0.0) { return result; @@ -690,13 +707,13 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char double x = start; bool endCondition = false; do { - bracketMinimum(symbol, x, step, max, bracket, evaluate, context, angleUnit, expression); - result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, angleUnit, expression); + bracketMinimum(symbol, x, step, max, bracket, evaluate, context, complexFormat, angleUnit, expression); + result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, expression); x = bracket[1]; // Because of float approximation, exact zero is never reached if (std::fabs(result.abscissa) < std::fabs(step)*k_solverPrecision) { result.abscissa = 0; - result.value = evaluate(symbol, 0, context, angleUnit, *this, expression); + result.value = evaluate(symbol, 0, context, complexFormat, angleUnit, *this, expression); } /* Ignore extremum whose value is undefined or too big because they are * really unlikely to be local extremum. */ @@ -718,13 +735,13 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char return result; } -void Expression::bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +void Expression::bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { Coordinate2D p[3]; - p[0] = {.abscissa = start, .value = evaluate(symbol, start, context, angleUnit, *this, expression)}; - p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, angleUnit, *this, expression)}; + p[0] = {.abscissa = start, .value = evaluate(symbol, start, context, complexFormat, angleUnit, *this, expression)}; + p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, complexFormat, angleUnit, *this, expression)}; double x = start+2.0*step; while (step > 0.0 ? x <= max : x >= max) { - p[2] = {.abscissa = x, .value = evaluate(symbol, x, context, angleUnit, *this, expression)}; + p[2] = {.abscissa = x, .value = evaluate(symbol, x, context, complexFormat, angleUnit, *this, expression)}; if ((p[0].value > p[1].value || std::isnan(p[0].value)) && (p[2].value > p[1].value || std::isnan(p[2].value)) && (!std::isnan(p[0].value) || !std::isnan(p[2].value))) { result[0] = p[0].abscissa; result[1] = p[1].abscissa; @@ -743,11 +760,11 @@ void Expression::bracketMinimum(const char * symbol, double start, double step, result[2] = NAN; } -typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { /* Bibliography: R. P. Brent, Algorithms for finding zeros and extrema of * functions without calculating derivatives */ if (ax > bx) { - return brentMinimum(symbol, bx, ax, evaluate, context, angleUnit, expression); + return brentMinimum(symbol, bx, ax, evaluate, context, complexFormat, angleUnit, expression); } double e = 0.0; double a = ax; @@ -755,7 +772,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double x = a+k_goldenRatio*(b-a); double v = x; double w = x; - double fx = evaluate(symbol, x, context, angleUnit, *this, expression); + double fx = evaluate(symbol, x, context, complexFormat, angleUnit, *this, expression); double fw = fx; double fv = fw; @@ -767,10 +784,10 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double tol1 = k_sqrtEps*std::fabs(x)+1E-10; double tol2 = 2.0*tol1; if (std::fabs(x-m) <= tol2-0.5*(b-a)) { - double middleFax = evaluate(symbol, (x+a)/2.0, context, angleUnit, *this, expression); - double middleFbx = evaluate(symbol, (x+b)/2.0, context, angleUnit, *this, expression); - double fa = evaluate(symbol, a, context, angleUnit, *this, expression); - double fb = evaluate(symbol, b, context, angleUnit, *this, expression); + double middleFax = evaluate(symbol, (x+a)/2.0, context, complexFormat, angleUnit, *this, expression); + double middleFbx = evaluate(symbol, (x+b)/2.0, context, complexFormat, angleUnit, *this, expression); + double fa = evaluate(symbol, a, context, complexFormat, angleUnit, *this, expression); + double fb = evaluate(symbol, b, context, complexFormat, angleUnit, *this, expression); if (middleFax-fa <= k_sqrtEps && fx-middleFax <= k_sqrtEps && fx-middleFbx <= k_sqrtEps && middleFbx-fb <= k_sqrtEps) { Coordinate2D result = {.abscissa = x, .value = fx}; return result; @@ -803,7 +820,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, d = k_goldenRatio*e; } u = x + (std::fabs(d) >= tol1 ? d : (d>0 ? tol1 : -tol1)); - fu = evaluate(symbol, u, context, angleUnit, *this, expression); + fu = evaluate(symbol, u, context, complexFormat, angleUnit, *this, expression); if (fu <= fx) { if (u 0.0 ? x <= max : x >= max)); double extremumMax = std::isnan(result) ? max : result; Coordinate2D resultExtremum[2] = { - nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { + nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { if (expression1.isUninitialized()) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } else { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } - }, context, angleUnit, expression, true), - nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { + }, context, complexFormat, angleUnit, expression, true), + nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { if (expression1.isUninitialized()) { - return -expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return -expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } else { - return expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } - }, context, angleUnit, expression, true)}; + }, context, complexFormat, angleUnit, expression, true)}; for (int i = 0; i < 2; i++) { if (!std::isnan(resultExtremum[i].abscissa) && (std::isnan(result) || std::fabs(result - start) > std::fabs(resultExtremum[i].abscissa - start))) { result = resultExtremum[i].abscissa; @@ -878,12 +895,12 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st return result; } -void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { double a = start; double b = start+step; while (step > 0.0 ? b <= max : b >= max) { - double fa = evaluation(symbol, a, context, angleUnit, *this, expression); - double fb = evaluation(symbol, b, context, angleUnit,* this, expression); + double fa = evaluation(symbol, a, context, complexFormat, angleUnit, *this, expression); + double fb = evaluation(symbol, b, context, complexFormat, angleUnit,* this, expression); if (fa*fb <= 0) { result[0] = a; result[1] = b; @@ -896,17 +913,17 @@ void Expression::bracketRoot(const char * symbol, double start, double step, dou result[1] = NAN; } -double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { if (ax > bx) { - return brentRoot(symbol, bx, ax, precision, evaluation, context, angleUnit, expression); + return brentRoot(symbol, bx, ax, precision, evaluation, context, complexFormat, angleUnit, expression); } double a = ax; double b = bx; double c = bx; double d = b-a; double e = b-a; - double fa = evaluation(symbol, a, context, angleUnit, *this, expression); - double fb = evaluation(symbol, b, context, angleUnit, *this, expression); + double fa = evaluation(symbol, a, context, complexFormat, angleUnit, *this, expression); + double fb = evaluation(symbol, b, context, complexFormat, angleUnit, *this, expression); double fc = fb; for (int i = 0; i < 100; i++) { if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) { @@ -926,7 +943,7 @@ double Expression::brentRoot(const char * symbol, double ax, double bx, double p double tol1 = 2.0*DBL_EPSILON*std::fabs(b)+0.5*precision; double xm = 0.5*(c-b); if (std::fabs(xm) <= tol1 || fb == 0.0) { - double fbcMiddle = evaluation(symbol, 0.5*(b+c), context, angleUnit, *this, expression); + double fbcMiddle = evaluation(symbol, 0.5*(b+c), context, complexFormat, angleUnit, *this, expression); double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb); if (isContinuous) { return b; @@ -964,7 +981,7 @@ double Expression::brentRoot(const char * symbol, double ax, double bx, double p } else { b += xm > 0.0 ? tol1 : tol1; } - fb = evaluation(symbol, b, context, angleUnit, *this, expression); + fb = evaluation(symbol, b, context, complexFormat, angleUnit, *this, expression); } return NAN; } @@ -975,16 +992,16 @@ 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 float Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const; -template double Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) 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(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); template double Expression::approximateToScalar(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); -template Evaluation Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const; -template Evaluation Expression::approximateToEvaluation(Context& context, 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) const; -template float Expression::approximateWithValueForSymbol(const char * symbol, float x, Context & context, Preferences::AngleUnit angleUnit) const; -template double Expression::approximateWithValueForSymbol(const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit) 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/function.cpp b/poincare/src/function.cpp index f21d0066e..89d0a11a8 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -85,7 +85,7 @@ Evaluation FunctionNode::templatedApproximate(Context& context, Preferences:: if (e.isUninitialized()) { return Complex::Undefined(); } - return e.approximateToEvaluation(context, angleUnit); + return e.node()->approximate(T(), context, angleUnit); } Function::Function(const char * name, size_t length) : diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp index 93f159d9a..8d4383147 100644 --- a/poincare/src/integral.cpp +++ b/poincare/src/integral.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,11 @@ Evaluation IntegralNode::templatedApproximate(Context & context, Preferences: template T IntegralNode::functionValueAtAbscissa(T x, Context & context, Preferences::AngleUnit angleUnit) const { - return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast(childAtIndex(1))->name(), x, context, angleUnit); + // Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounterComplex flag + assert(childAtIndex(1)->type() == Type::Symbol); + VariableContext variableContext = VariableContext(static_cast(childAtIndex(1))->name(), &context); + variableContext.setApproximationForVariable(x); + return childAtIndex(0)->approximate(T(), variableContext, angleUnit).toScalar(); } #ifdef LAGRANGE_METHOD diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index c9cc6b31f..19226410f 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -47,7 +47,7 @@ Evaluation StoreNode::templatedApproximate(Context& context, Preferences::Ang if (e.isUninitialized()) { return Complex::Undefined(); } - return e.approximateToEvaluation(context, angleUnit); + return e.node()->approximate(T(), context, angleUnit); } Expression Store::shallowReduce(Context & context) { diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 8a83647a1..5ccccc661 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -132,7 +132,7 @@ Evaluation SymbolNode::templatedApproximate(Context& context, Preferences::An if (e.isUninitialized()) { return Complex::Undefined(); } - return e.approximateToEvaluation(context, angleUnit); + return e.node()->approximate(T(), context, angleUnit); } Symbol::Symbol(const char * name, int length) : SymbolAbstract(TreePool::sharedPool()->createTreeNode(SymbolAbstract::AlignedNodeSize(length, sizeof(SymbolNode)))) { diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index 92622c91c..07c761684 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -36,7 +36,7 @@ float Trigonometry::characteristicXRange(const Expression & e, Context & context /* To compute a, the slope of the expression child(0), we compute the * derivative of child(0) for any x value. */ Poincare::Derivative derivative = Poincare::Derivative::Builder(e.childAtIndex(0).clone(), Symbol(x, 1), Float(1.0f)); - float a = derivative.approximateToScalar(context, angleUnit); + float a = derivative.node()->approximate(float(), context, angleUnit).toScalar(); float pi = angleUnit == Preferences::AngleUnit::Radian ? M_PI : 180.0f; return 2.0f*pi/std::fabs(a); } @@ -248,7 +248,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c // Step 1. Look for an expression of type "arccos(cos(x))", return x if (AreInverseFunctions(e.childAtIndex(0), e)) { - float trigoOp = e.childAtIndex(0).childAtIndex(0).approximateToScalar(context, angleUnit); + float trigoOp = e.childAtIndex(0).childAtIndex(0).node()->approximate(float(), context, angleUnit).toScalar(); if ((e.type() == ExpressionNode::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) || (e.type() == ExpressionNode::Type::ArcSine && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) || (e.type() == ExpressionNode::Type::ArcTangent && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f)) { @@ -260,7 +260,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c // Step 2. Special case for arctan(sin(x)/cos(x)) if (e.type() == ExpressionNode::Type::ArcTangent && ExpressionIsEquivalentToTangent(e.childAtIndex(0))) { - float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).approximateToScalar(context, angleUnit); + float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).node()->approximate(float(), context, angleUnit).toScalar(); if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) { Expression result = e.childAtIndex(0).childAtIndex(1).childAtIndex(0); e.replaceWithInPlace(result); @@ -449,7 +449,7 @@ Expression Trigonometry::table(const Expression e, ExpressionNode::Type type, Co return Expression(); } // We approximate the given expression to quickly compare it to the cheat table entries. - float eValue = e.approximateToScalar(context, angleUnit); + float eValue = e.node()->approximate(float(), context, angleUnit).toScalar(); if (std::isnan(eValue) || std::isinf(eValue)) { return Expression(); } diff --git a/poincare/test/addition.cpp b/poincare/test/addition.cpp index 5adf26374..42c2c4af1 100644 --- a/poincare/test/addition.cpp +++ b/poincare/test/addition.cpp @@ -12,7 +12,7 @@ using namespace Poincare; static inline void assert_approximation_equals(const Expression i, float f) { Shared::GlobalContext c; - quiz_assert(i.approximateToScalar(c, Preferences::AngleUnit::Degree) == f); + quiz_assert(i.approximateToScalar(c, Cartesian, Degree) == f); } static inline void assert_parsed_expression_is_equal_to(const char * exp, Expression e) { diff --git a/poincare/test/function.cpp b/poincare/test/function.cpp index d8d9d654a..32c272f4f 100644 --- a/poincare/test/function.cpp +++ b/poincare/test/function.cpp @@ -11,7 +11,7 @@ using namespace Poincare; template void assert_exp_is_bounded(Expression exp, T lowBound, T upBound, bool upBoundIncluded = false) { Shared::GlobalContext globalContext; - T result = exp.approximateToScalar(globalContext, Radian); + T result = exp.approximateToScalar(globalContext, Cartesian, Radian); quiz_assert(result >= lowBound); quiz_assert(result < upBound || (result == upBound && upBoundIncluded)); } diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index f35ebadd0..b72b72352 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -159,10 +159,10 @@ void assert_parsed_expression_evaluates_to(const char * expression, const char * } template -void assert_parsed_expression_approximates_with_value_for_symbol(Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::AngleUnit angleUnit) { +void assert_parsed_expression_approximates_with_value_for_symbol(Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::ComplexFormat complexFormat, Poincare::Preferences::AngleUnit angleUnit) { Shared::GlobalContext globalContext; - T result = expression.approximateWithValueForSymbol(symbol, value, globalContext, angleUnit); - quiz_assert(std::fabs(result - approximation) < 10.0*Expression::epsilon()); + T result = expression.approximateWithValueForSymbol(symbol, value, globalContext, complexFormat, angleUnit); + quiz_assert((std::isnan(result) && std::isnan(approximation)) || std::fabs(result - approximation) < 10.0*Expression::epsilon()); } void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) { @@ -219,5 +219,5 @@ void assert_expression_layout_serialize_to(Poincare::Layout layout, const char * template void assert_parsed_expression_evaluates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); template void assert_parsed_expression_evaluates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); -template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, float, float, Poincare::Preferences::AngleUnit); -template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, double, double, Poincare::Preferences::AngleUnit); +template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, float, float, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); +template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, double, double, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); diff --git a/poincare/test/helper.h b/poincare/test/helper.h index e445f5488..ca04f6cb7 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -30,7 +30,7 @@ template void assert_parsed_expression_evaluates_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); template -void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::AngleUnit angleUnit = Degree); +void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::Preferences::AngleUnit angleUnit = Degree); void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::Preferences::AngleUnit angleUnit = Poincare::Preferences::AngleUnit::Radian, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::ComplexFormat::Cartesian); void assert_parsed_expression_serialize_to(Poincare::Expression expression, const char * serializedExpression, Poincare::Preferences::PrintFloatMode mode = DecimalMode, int numberOfSignifiantDigits = 7); diff --git a/poincare/test/user_variable.cpp b/poincare/test/user_variable.cpp index 5ac0ce40c..fc657cd3c 100644 --- a/poincare/test/user_variable.cpp +++ b/poincare/test/user_variable.cpp @@ -163,6 +163,17 @@ QUIZ_CASE(poincare_user_variable_functions_with_context) { // Clean the storage for other tests Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); + + // f: x->R(-1) + assert_simplify("R(-1)*R(-1)>f(x)"); + // Approximate f(?) with ? = 5 + // Cartesian + assert_parsed_expression_approximates_with_value_for_symbol(Function("f", 1, Symbol(Symbol::SpecialSymbols::UnknownX)), x, 1.0, -1.0); + // Real + assert_parsed_expression_approximates_with_value_for_symbol(Function("f", 1, Symbol(Symbol::SpecialSymbols::UnknownX)), x, 1.0, (double)NAN, Real); + + // Clean the storage for other tests + Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); } QUIZ_CASE(poincare_user_variable_properties) {