[poincare] Create a a flag on Expression that is set when the

approximation encouters a complex value

All approximation methods take the complex format into account.
This commit is contained in:
Émilie Feral
2018-12-21 17:55:58 +01:00
committed by Léa Saviot
parent c3ad0e027c
commit ecf3f2ea0f
23 changed files with 187 additions and 122 deletions

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -1,4 +1,5 @@
#include "function.h"
#include "poincare_helpers.h"
#include <string.h>
#include <cmath>
#include <assert.h>
@@ -41,7 +42,7 @@ void Function::setActive(bool active) {
template<typename T>
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);
}
}

View File

@@ -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 <class T>
@@ -24,25 +25,39 @@ inline int Serialize(const Poincare::Expression e, char * buffer, int bufferSize
template <class T>
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<T>(context, complexFormat, Poincare::Preferences::sharedPreferences()->angleUnit());
return e.approximate<T>(context, complexFormat, preferences->angleUnit());
}
template <class T>
inline T ApproximateToScalar(const Poincare::Expression e, Poincare::Context & context) {
return e.approximateToScalar<T>(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<T>(context, complexFormat, preferences->angleUnit());
}
template <class T>
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<T>(symbol, x, context, complexFormat, preferences->angleUnit());
}
template <class T>
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<T>(text, context, complexFormat, Poincare::Preferences::sharedPreferences()->angleUnit());
return Poincare::Expression::approximateToScalar<T>(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()) {

View File

@@ -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 {

View File

@@ -1,4 +1,5 @@
#include "storage_function.h"
#include "poincare_helpers.h"
#include <poincare/symbol.h>
#include "poincare/src/parsing/parser.h"
#include <string.h>
@@ -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 {

View File

@@ -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 {

View File

@@ -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<typename U> static U epsilon();
template<typename U> Expression approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
template<typename U> U approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const;
template<typename U> U approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
template<typename U> static U approximateToScalar(const char * text, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
template<typename U> U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const;
template<typename U> 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<typename U> Evaluation<U> approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const;
template<typename U> Evaluation<U> 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;
};
}

View File

@@ -49,7 +49,7 @@ Expression AbsoluteValue::shallowReduce(Context & context, Preferences::ComplexF
#endif
Expression c = childAtIndex(0);
if (c.isReal(context)) {
float app = c.approximateToScalar<float>(context, angleUnit);
float app = c.node()->approximate(float(), context, angleUnit).toScalar();
if (!std::isnan(app) && app >= Expression::epsilon<float>()) {
// abs(a) = a with a > 0
replaceWithInPlace(c);

View File

@@ -22,6 +22,9 @@ namespace Poincare {
template<typename T>
void ComplexNode<T>::setComplex(std::complex<T> c) {
if (c.imag() != 0.0) {
Expression::SetEncounterComplex(true);
}
this->real(c.real());
this->imag(c.imag());
if (this->real() == -0) {

View File

@@ -48,7 +48,7 @@ Expression ComplexArgument::shallowReduce(Context & context, Preferences::Comple
#endif
bool real = c.isReal(context);
if (real) {
float app = c.approximateToScalar<float>(context, angleUnit);
float app = c.node()->approximate(float(), context, angleUnit).toScalar();
if (!std::isnan(app) && app >= Expression::epsilon<float>()) {
// arg(x) = 0 if x > 0
Expression result = Rational(0);

View File

@@ -76,7 +76,10 @@ Evaluation<T> DerivativeNode::templatedApproximate(Context& context, Preferences
template<typename T>
T DerivativeNode::approximateWithArgument(T x, Context & context, Preferences::AngleUnit angleUnit) const {
assert(childAtIndex(1)->type() == Type::Symbol);
return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast<SymbolNode *>(childAtIndex(1))->name(), x, context, angleUnit);
VariableContext variableContext = VariableContext(static_cast<SymbolNode *>(childAtIndex(1))->name(), &context);
variableContext.setApproximationForVariable<T>(x);
// Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounterComplex flag
return childAtIndex(0)->approximate(T(), variableContext, angleUnit).toScalar();
}
template<typename T>

View File

@@ -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<typename U>
Evaluation<U> Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const {
Evaluation<U> 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<U> e = node()->approximate(U(), context, angleUnit);
if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounterComplex) {
e = Complex<U>::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<typename U>
Expression Expression::approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
return isUninitialized() ? Undefined() : approximateToEvaluation<U>(context, angleUnit).complexToExpression(complexFormat);
return isUninitialized() ? Undefined() : approximateToEvaluation<U>(context, complexFormat, angleUnit).complexToExpression(complexFormat);
}
template<typename U>
U Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const {
return approximateToEvaluation<U>(context, angleUnit).toScalar();
U Expression::approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
return approximateToEvaluation<U>(context, complexFormat, angleUnit).toScalar();
}
template<typename U>
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<U>(context, angleUnit);
return exp.approximateToScalar<U>(context, complexFormat, angleUnit);
}
template<typename U>
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<U>(x);
return approximateToScalar<U>(variableContext, angleUnit);
return approximateToScalar<U>(variableContext, complexFormat, angleUnit);
}
template<typename U>
@@ -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<x) {
b = x;
@@ -837,7 +854,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol,
return result;
}
double Expression::nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
double Expression::nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
if (start == max || step == 0.0) {
return NAN;
}
@@ -846,27 +863,27 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st
static double precisionByGradUnit = 1E6;
double x = start+step;
do {
bracketRoot(symbol, x, step, max, bracket, evaluation, context, angleUnit, expression);
result = brentRoot(symbol, bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, angleUnit, expression);
bracketRoot(symbol, x, step, max, bracket, evaluation, context, complexFormat, angleUnit, expression);
result = brentRoot(symbol, bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, complexFormat, angleUnit, expression);
x = bracket[1];
} while (std::isnan(result) && (step > 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<double>();
template Expression Expression::approximate<float>(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
template Expression Expression::approximate<double>(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<float>(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
template double Expression::approximateToScalar<double>(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
template Evaluation<float> Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const;
template Evaluation<double> Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const;
template Evaluation<float> Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const;
template Evaluation<double> 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;
}

View File

@@ -85,7 +85,7 @@ Evaluation<T> FunctionNode::templatedApproximate(Context& context, Preferences::
if (e.isUninitialized()) {
return Complex<T>::Undefined();
}
return e.approximateToEvaluation<T>(context, angleUnit);
return e.node()->approximate(T(), context, angleUnit);
}
Function::Function(const char * name, size_t length) :

View File

@@ -5,6 +5,7 @@
#include <poincare/serialization_helper.h>
#include <poincare/symbol.h>
#include <poincare/undefined.h>
#include <poincare/variable_context.h>
#include <cmath>
#include <float.h>
#include <stdlib.h>
@@ -66,7 +67,11 @@ Evaluation<T> IntegralNode::templatedApproximate(Context & context, Preferences:
template<typename T>
T IntegralNode::functionValueAtAbscissa(T x, Context & context, Preferences::AngleUnit angleUnit) const {
return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast<SymbolNode *>(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<SymbolNode *>(childAtIndex(1))->name(), &context);
variableContext.setApproximationForVariable<T>(x);
return childAtIndex(0)->approximate(T(), variableContext, angleUnit).toScalar();
}
#ifdef LAGRANGE_METHOD

View File

@@ -47,7 +47,7 @@ Evaluation<T> StoreNode::templatedApproximate(Context& context, Preferences::Ang
if (e.isUninitialized()) {
return Complex<T>::Undefined();
}
return e.approximateToEvaluation<T>(context, angleUnit);
return e.node()->approximate(T(), context, angleUnit);
}
Expression Store::shallowReduce(Context & context) {

View File

@@ -132,7 +132,7 @@ Evaluation<T> SymbolNode::templatedApproximate(Context& context, Preferences::An
if (e.isUninitialized()) {
return Complex<T>::Undefined();
}
return e.approximateToEvaluation<T>(context, angleUnit);
return e.node()->approximate(T(), context, angleUnit);
}
Symbol::Symbol(const char * name, int length) : SymbolAbstract(TreePool::sharedPool()->createTreeNode<SymbolNode>(SymbolAbstract::AlignedNodeSize(length, sizeof(SymbolNode)))) {

View File

@@ -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<float>(1.0f));
float a = derivative.approximateToScalar<float>(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<float>(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<float>(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<float>(context, angleUnit);
float eValue = e.node()->approximate(float(), context, angleUnit).toScalar();
if (std::isnan(eValue) || std::isinf(eValue)) {
return Expression();
}

View File

@@ -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<float>(c, Preferences::AngleUnit::Degree) == f);
quiz_assert(i.approximateToScalar<float>(c, Cartesian, Degree) == f);
}
static inline void assert_parsed_expression_is_equal_to(const char * exp, Expression e) {

View File

@@ -11,7 +11,7 @@ using namespace Poincare;
template<typename T>
void assert_exp_is_bounded(Expression exp, T lowBound, T upBound, bool upBoundIncluded = false) {
Shared::GlobalContext globalContext;
T result = exp.approximateToScalar<T>(globalContext, Radian);
T result = exp.approximateToScalar<T>(globalContext, Cartesian, Radian);
quiz_assert(result >= lowBound);
quiz_assert(result < upBound || (result == upBound && upBoundIncluded));
}

View File

@@ -159,10 +159,10 @@ void assert_parsed_expression_evaluates_to(const char * expression, const char *
}
template<typename T>
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>());
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<T>());
}
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<float>(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int);
template void assert_parsed_expression_evaluates_to<double>(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);

View File

@@ -30,7 +30,7 @@ template<typename T>
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<typename T>
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);

View File

@@ -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) {