diff --git a/apps/graph/graph/calculation_graph_controller.cpp b/apps/graph/graph/calculation_graph_controller.cpp index b3c857a0b..a912e9f61 100644 --- a/apps/graph/graph/calculation_graph_controller.cpp +++ b/apps/graph/graph/calculation_graph_controller.cpp @@ -19,14 +19,14 @@ CalculationGraphController::CalculationGraphController(Responder * parentRespond void CalculationGraphController::viewWillAppear() { assert(!m_record.isNull()); - Expression::Coordinate2D pointOfInterest = computeNewPointOfInteresetFromAbscissa(m_graphRange->xMin(), 1); - if (std::isnan(pointOfInterest.abscissa)) { + Coordinate2D pointOfInterest = computeNewPointOfInteresetFromAbscissa(m_graphRange->xMin(), 1); + if (std::isnan(pointOfInterest.abscissa())) { m_isActive = false; m_graphView->setCursorView(nullptr); m_graphView->setBannerView(&m_defaultBannerView); } else { m_isActive = true; - m_cursor->moveTo(pointOfInterest.abscissa, pointOfInterest.value); + m_cursor->moveTo(pointOfInterest.abscissa(), pointOfInterest.value()); m_graphRange->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio); m_bannerView->setNumberOfSubviews(Shared::XYBannerView::k_numberOfSubviews); reloadBannerView(); @@ -44,7 +44,7 @@ void CalculationGraphController::reloadBannerView() { reloadBannerViewForCursorOnFunction(m_cursor, m_record, functionStore(), CartesianFunction::Symbol()); } -Expression::Coordinate2D CalculationGraphController::computeNewPointOfInteresetFromAbscissa(double start, int direction) { +Coordinate2D CalculationGraphController::computeNewPointOfInteresetFromAbscissa(double start, int direction) { double step = m_graphRange->xGridUnit()/10.0; step = direction < 0 ? -step : step; double max = direction > 0 ? m_graphRange->xMax() : m_graphRange->xMin(); @@ -69,11 +69,11 @@ bool CalculationGraphController::handleEnter() { } bool CalculationGraphController::moveCursorHorizontally(int direction) { - Expression::Coordinate2D newPointOfInterest = computeNewPointOfInteresetFromAbscissa(m_cursor->x(), direction); - if (std::isnan(newPointOfInterest.abscissa)) { + Coordinate2D newPointOfInterest = computeNewPointOfInteresetFromAbscissa(m_cursor->x(), direction); + if (std::isnan(newPointOfInterest.abscissa())) { return false; } - m_cursor->moveTo(newPointOfInterest.abscissa, newPointOfInterest.value); + m_cursor->moveTo(newPointOfInterest.abscissa(), newPointOfInterest.value()); return true; } diff --git a/apps/graph/graph/calculation_graph_controller.h b/apps/graph/graph/calculation_graph_controller.h index 976a63f6c..8b7afdcff 100644 --- a/apps/graph/graph/calculation_graph_controller.h +++ b/apps/graph/graph/calculation_graph_controller.h @@ -20,9 +20,9 @@ protected: float cursorBottomMarginRatio() override { return 0.15f; } BannerView * bannerView() override { return m_bannerView; } void reloadBannerView() override; - Poincare::Expression::Coordinate2D computeNewPointOfInteresetFromAbscissa(double start, int direction); + Poincare::Coordinate2D computeNewPointOfInteresetFromAbscissa(double start, int direction); CartesianFunctionStore * functionStore() const; - virtual Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) = 0; + virtual Poincare::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) = 0; GraphView * m_graphView; BannerView * m_bannerView; Shared::InteractiveCurveViewRange * m_graphRange; diff --git a/apps/graph/graph/extremum_graph_controller.cpp b/apps/graph/graph/extremum_graph_controller.cpp index d442d3e26..cf3971b69 100644 --- a/apps/graph/graph/extremum_graph_controller.cpp +++ b/apps/graph/graph/extremum_graph_controller.cpp @@ -15,7 +15,7 @@ const char * MinimumGraphController::title() { return I18n::translate(I18n::Message::Minimum); } -Expression::Coordinate2D MinimumGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { +Coordinate2D MinimumGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { return functionStore()->modelForRecord(m_record)->nextMinimumFrom(start, step, max, context); } @@ -28,7 +28,7 @@ const char * MaximumGraphController::title() { return I18n::translate(I18n::Message::Maximum); } -Expression::Coordinate2D MaximumGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { +Coordinate2D MaximumGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { return functionStore()->modelForRecord(m_record)->nextMaximumFrom(start, step, max, context); } diff --git a/apps/graph/graph/extremum_graph_controller.h b/apps/graph/graph/extremum_graph_controller.h index 3fd55107c..983336b90 100644 --- a/apps/graph/graph/extremum_graph_controller.h +++ b/apps/graph/graph/extremum_graph_controller.h @@ -10,7 +10,7 @@ public: MinimumGraphController(Responder * parentResponder, GraphView * graphView, BannerView * bannerView, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor); const char * title() override; private: - Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; + Poincare::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; }; class MaximumGraphController : public CalculationGraphController { @@ -18,7 +18,7 @@ public: MaximumGraphController(Responder * parentResponder, GraphView * graphView, BannerView * bannerView, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor); const char * title() override; private: - Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; + Poincare::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; }; } diff --git a/apps/graph/graph/intersection_graph_controller.cpp b/apps/graph/graph/intersection_graph_controller.cpp index 6b5f28fb5..d95c5fe9b 100644 --- a/apps/graph/graph/intersection_graph_controller.cpp +++ b/apps/graph/graph/intersection_graph_controller.cpp @@ -39,16 +39,16 @@ void IntersectionGraphController::reloadBannerView() { bannerView()->reload(); } -Poincare::Expression::Coordinate2D IntersectionGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { - Poincare::Expression::Coordinate2D result = {.abscissa = NAN, .value = NAN}; +Poincare::Coordinate2D IntersectionGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { + Poincare::Coordinate2D result = Poincare::Coordinate2D(NAN, NAN); for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) { Ion::Storage::Record record = functionStore()->activeRecordAtIndex(i); if (record != m_record) { Poincare::Expression e = functionStore()->modelForRecord(record)->expressionReduced(context); - Poincare::Expression::Coordinate2D intersection = functionStore()->modelForRecord(m_record)->nextIntersectionFrom(start, step, max, context, e); - if ((std::isnan(result.abscissa) || std::fabs(intersection.abscissa-start) < std::fabs(result.abscissa-start)) && !std::isnan(intersection.abscissa)) { + Poincare::Coordinate2D intersection = functionStore()->modelForRecord(m_record)->nextIntersectionFrom(start, step, max, context, e); + if ((std::isnan(result.abscissa()) || std::fabs(intersection.abscissa()-start) < std::fabs(result.abscissa()-start)) && !std::isnan(intersection.abscissa())) { m_intersectedRecord = record; - result = (std::isnan(result.abscissa) || std::fabs(intersection.abscissa-start) < std::fabs(result.abscissa-start)) ? intersection : result; + result = (std::isnan(result.abscissa()) || std::fabs(intersection.abscissa()-start) < std::fabs(result.abscissa()-start)) ? intersection : result; } } } diff --git a/apps/graph/graph/intersection_graph_controller.h b/apps/graph/graph/intersection_graph_controller.h index b48c73459..30c30386a 100644 --- a/apps/graph/graph/intersection_graph_controller.h +++ b/apps/graph/graph/intersection_graph_controller.h @@ -12,7 +12,7 @@ public: const char * title() override; private: void reloadBannerView() override; - Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; + Poincare::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; Ion::Storage::Record m_intersectedRecord; }; diff --git a/apps/graph/graph/preimage_graph_controller.cpp b/apps/graph/graph/preimage_graph_controller.cpp index 818de7210..868b427b5 100644 --- a/apps/graph/graph/preimage_graph_controller.cpp +++ b/apps/graph/graph/preimage_graph_controller.cpp @@ -21,7 +21,7 @@ PreimageGraphController::PreimageGraphController( { } -Poincare::Expression::Coordinate2D PreimageGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { +Poincare::Coordinate2D PreimageGraphController::computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) { Poincare::Expression expression = Poincare::Float::Builder(m_image); return functionStore()->modelForRecord(m_record)->nextIntersectionFrom(start, step, max, context, expression); } diff --git a/apps/graph/graph/preimage_graph_controller.h b/apps/graph/graph/preimage_graph_controller.h index b9a553e6a..67ebdd43c 100644 --- a/apps/graph/graph/preimage_graph_controller.h +++ b/apps/graph/graph/preimage_graph_controller.h @@ -16,7 +16,7 @@ public: double image() { return m_image; } void setImage(double value) { m_image = value; } private: - Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; + Poincare::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; double m_image; }; diff --git a/apps/graph/graph/root_graph_controller.cpp b/apps/graph/graph/root_graph_controller.cpp index 2beebad63..741a72961 100644 --- a/apps/graph/graph/root_graph_controller.cpp +++ b/apps/graph/graph/root_graph_controller.cpp @@ -15,8 +15,8 @@ const char * RootGraphController::title() { return I18n::translate(I18n::Message::Zeros); } -Expression::Coordinate2D RootGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) { - return {.abscissa = functionStore()->modelForRecord(m_record)->nextRootFrom(start, step, max, context), .value = 0.0}; +Coordinate2D RootGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) { + return Coordinate2D(functionStore()->modelForRecord(m_record)->nextRootFrom(start, step, max, context), 0.0); } } diff --git a/apps/graph/graph/root_graph_controller.h b/apps/graph/graph/root_graph_controller.h index 4db493145..e76130e76 100644 --- a/apps/graph/graph/root_graph_controller.h +++ b/apps/graph/graph/root_graph_controller.h @@ -10,7 +10,7 @@ public: RootGraphController(Responder * parentResponder, GraphView * graphView, BannerView * bannerView, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor); const char * title() override; private: - Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; + Poincare::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override; }; } diff --git a/apps/regression/model/model.cpp b/apps/regression/model/model.cpp index 78e93d45a..794ebdb5c 100644 --- a/apps/regression/model/model.cpp +++ b/apps/regression/model/model.cpp @@ -27,7 +27,7 @@ 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 = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, context, yExpression).abscissa; + double result = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, context, yExpression).abscissa(); return result; } diff --git a/apps/shared/cartesian_function.cpp b/apps/shared/cartesian_function.cpp index bb349abe6..d0913cd1f 100644 --- a/apps/shared/cartesian_function.cpp +++ b/apps/shared/cartesian_function.cpp @@ -104,14 +104,14 @@ double CartesianFunction::sumBetweenBounds(double start, double end, Poincare::C return PoincareHelpers::ApproximateToScalar(integral, context); } -Expression::Coordinate2D CartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const { +Coordinate2D CartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const { constexpr int bufferSize = CodePoint::MaxCodePointCharLength + 1; char unknownX[bufferSize]; SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknownX); return PoincareHelpers::NextMinimum(expressionReduced(context), unknownX, start, step, max, context); } -Expression::Coordinate2D CartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const { +Coordinate2D CartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const { constexpr int bufferSize = CodePoint::MaxCodePointCharLength + 1; char unknownX[bufferSize]; SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknownX); @@ -125,7 +125,7 @@ double CartesianFunction::nextRootFrom(double start, double step, double max, Co return PoincareHelpers::NextRoot(expressionReduced(context), unknownX, start, step, max, context); } -Expression::Coordinate2D CartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Expression e) const { +Coordinate2D CartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Expression e) const { constexpr int bufferSize = CodePoint::MaxCodePointCharLength + 1; char unknownX[bufferSize]; SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknownX); diff --git a/apps/shared/cartesian_function.h b/apps/shared/cartesian_function.h index 76decc8d4..16631f179 100644 --- a/apps/shared/cartesian_function.h +++ b/apps/shared/cartesian_function.h @@ -25,11 +25,11 @@ public: // Integral double sumBetweenBounds(double start, double end, Poincare::Context * context) const override; // Extremum - Poincare::Expression::Coordinate2D nextMinimumFrom(double start, double step, double max, Poincare::Context * context) const; - Poincare::Expression::Coordinate2D nextMaximumFrom(double start, double step, double max, Poincare::Context * context) const; + Poincare::Coordinate2D nextMinimumFrom(double start, double step, double max, Poincare::Context * context) const; + Poincare::Coordinate2D nextMaximumFrom(double start, double step, double max, Poincare::Context * context) const; // Roots double nextRootFrom(double start, double step, double max, Poincare::Context * context) const; - Poincare::Expression::Coordinate2D nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Poincare::Expression expression) const; + Poincare::Coordinate2D nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Poincare::Expression expression) const; private: /* CartesianFunctionRecordDataBuffer is the layout of the data buffer of Record * representing a CartesianFunction. See comment on diff --git a/apps/shared/poincare_helpers.h b/apps/shared/poincare_helpers.h index bc7e831c5..bc124f49d 100644 --- a/apps/shared/poincare_helpers.h +++ b/apps/shared/poincare_helpers.h @@ -69,13 +69,13 @@ inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expressi Poincare::Expression::ParseAndSimplifyAndApproximate(text, simplifiedExpression, approximateExpression, context, complexFormat, preferences->angleUnit(), symbolicComputation); } -inline typename Poincare::Expression::Coordinate2D NextMinimum(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context * context) { +inline typename Poincare::Coordinate2D NextMinimum(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context * context) { Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); return e.nextMinimum(symbol, start, step, max, context, complexFormat, preferences->angleUnit()); } -inline typename Poincare::Expression::Coordinate2D NextMaximum(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context * context) { +inline typename Poincare::Coordinate2D NextMaximum(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context * context) { Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); return e.nextMaximum(symbol, start, step, max, context, complexFormat, preferences->angleUnit()); @@ -87,7 +87,7 @@ inline double NextRoot(const Poincare::Expression e, const char * symbol, double return e.nextRoot(symbol, start, step, max, context, complexFormat, preferences->angleUnit()); } -inline typename Poincare::Expression::Coordinate2D NextIntersection(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context * context, const Poincare::Expression expression) { +inline typename Poincare::Coordinate2D NextIntersection(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context * context, const Poincare::Expression expression) { Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, expression, context); diff --git a/poincare/include/poincare/coordinate_2D.h b/poincare/include/poincare/coordinate_2D.h new file mode 100644 index 000000000..52480fd44 --- /dev/null +++ b/poincare/include/poincare/coordinate_2D.h @@ -0,0 +1,22 @@ +#ifndef POINCARE_COORDINATE_2D_H +#define POINCARE_COORDINATE_2D_H + +#include + +namespace Poincare { + +class Coordinate2D final { +public: + Coordinate2D (double abscissa = NAN, double value = NAN) : m_abscissa(abscissa), m_value(value) {} + double abscissa() const { return m_abscissa; } + double value() const { return m_value; } + void setAbscissa(double a) { m_abscissa = a; } + void setValue(double v) { m_value = v; } +private: + double m_abscissa; + double m_value; +}; + +} + +#endif diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 88f9c3303..0ab9807ea 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -2,6 +2,7 @@ #define POINCARE_EXPRESSION_REFERENCE_H #include +#include #include #include #include @@ -230,10 +231,6 @@ public: template static U ApproximateToScalar(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true); 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::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; diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 6eb4cedac..672e5f533 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -751,17 +751,17 @@ 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::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { 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::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { 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}; + return Coordinate2D(minimumOfOpposite.abscissa(), -minimumOfOpposite.value()); } double Expression::nextRoot(const char * symbol, double start, double step, double max, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { @@ -770,19 +770,19 @@ double Expression::nextRoot(const char * symbol, double start, double step, doub }, context, complexFormat, angleUnit, nullptr); } -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 { +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 { 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; + Coordinate2D result(resultAbscissa, approximateWithValueForSymbol(symbol, resultAbscissa, context, complexFormat, angleUnit)); + if (std::fabs(result.value()) < step*k_solverPrecision) { + result.setValue(0.0); } return result; } -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}; +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; if (start == max || step == 0.0) { return result; } @@ -794,44 +794,50 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char 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, complexFormat, angleUnit, *this, expression); + if (std::fabs(result.abscissa()) < std::fabs(step)*k_solverPrecision) { + result.setAbscissa(0); + result.setValue(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. */ - if (std::isnan(result.value) || std::fabs(result.value) > k_maxFloat) { - result.abscissa = NAN; + if (std::isnan(result.value()) || std::fabs(result.value()) > k_maxFloat) { + result.setAbscissa(NAN); } // Idem, exact 0 never reached - if (std::fabs(result.value) < std::fabs(step)*k_solverPrecision) { - result.value = 0; + if (std::fabs(result.value()) < std::fabs(step)*k_solverPrecision) { + result.setValue(0); } - endCondition = std::isnan(result.abscissa) && (step > 0.0 ? x <= max : x >= max); + endCondition = std::isnan(result.abscissa()) && (step > 0.0 ? x <= max : x >= max); if (lookForRootMinimum) { - endCondition |= std::fabs(result.value) > 0 && (step > 0.0 ? x <= max : x >= max); + endCondition |= std::fabs(result.value()) > 0 && (step > 0.0 ? x <= max : x >= max); } } while (endCondition); - if (lookForRootMinimum) { - result.abscissa = std::fabs(result.value) > 0 ? NAN : result.abscissa; + if (lookForRootMinimum && std::fabs(result.value()) > 0) { + result.setAbscissa(NAN); } return result; } 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, complexFormat, angleUnit, *this, expression)}; - p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, complexFormat, angleUnit, *this, expression)}; + Coordinate2D p[3] = { + Coordinate2D(start, evaluate(symbol, start, context, complexFormat, angleUnit, *this, expression)), + Coordinate2D(start+step, evaluate(symbol, start+step, context, complexFormat, angleUnit, *this, expression)), + Coordinate2D() + }; double x = start+2.0*step; while (step > 0.0 ? x <= max : x >= max) { - 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; - result[2] = p[2].abscissa; + p[2].setAbscissa(x); + p[2].setValue(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(); + result[2] = p[2].abscissa(); return; } - if (p[0].value > p[1].value && p[1].value == p[2].value) { + if (p[0].value() > p[1].value() && p[1].value() == p[2].value()) { } else { p[0] = p[1]; p[1] = p[2]; @@ -843,7 +849,7 @@ 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::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { +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) { @@ -872,8 +878,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, 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; + return Coordinate2D(x, fx); } } double p = 0; @@ -933,8 +938,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, } } } - Coordinate2D result = {.abscissa = x, .value = fx}; - return result; + return Coordinate2D(x, fx); } 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 { @@ -968,8 +972,8 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st } }, 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; + if (!std::isnan(resultExtremum[i].abscissa()) && (std::isnan(result) || std::fabs(result - start) > std::fabs(resultExtremum[i].abscissa() - start))) { + result = resultExtremum[i].abscissa(); } } if (std::fabs(result) < std::fabs(step)*k_solverPrecision) { diff --git a/poincare/test/function_solver.cpp b/poincare/test/function_solver.cpp index 62b692f11..0523a6afd 100644 --- a/poincare/test/function_solver.cpp +++ b/poincare/test/function_solver.cpp @@ -24,7 +24,7 @@ bool doubles_are_approximately_equal(double d1, double d2) { void assert_next_extrema_are( ExtremumType extremumType, int numberOfExtrema, - Expression::Coordinate2D * extrema, + Coordinate2D * extrema, Expression e, const char * symbol, Context * context, @@ -37,18 +37,18 @@ void assert_next_extrema_are( double currentStart = start; for (int i = 0; i < numberOfExtrema; i++) { quiz_assert_log_if_failure(!std::isnan(currentStart), e); - Expression::Coordinate2D nextExtrema = { .abscissa = NAN, .value = NAN }; + Coordinate2D nextExtrema; if (extremumType == ExtremumType::Maximum) { nextExtrema = e.nextMaximum(symbol, currentStart, step, max, context, complexFormat, angleUnit); } else if (extremumType == ExtremumType::Minimum) { nextExtrema = e.nextMinimum(symbol, currentStart, step, max, context, complexFormat, angleUnit); } else if (extremumType == ExtremumType::Root) { - nextExtrema = { .abscissa = e.nextRoot(symbol, currentStart, step, max, context, complexFormat, angleUnit), .value = 0.0 }; + nextExtrema = Coordinate2D(e.nextRoot(symbol, currentStart, step, max, context, complexFormat, angleUnit), 0.0 ); } - currentStart = nextExtrema.abscissa + step; + currentStart = nextExtrema.abscissa() + step; quiz_assert_log_if_failure( - (doubles_are_approximately_equal(extrema[i].abscissa, nextExtrema.abscissa)) - && (doubles_are_approximately_equal(extrema[i].value, nextExtrema.value)), + (doubles_are_approximately_equal(extrema[i].abscissa(), nextExtrema.abscissa())) + && (doubles_are_approximately_equal(extrema[i].value(), nextExtrema.value())), e); } } @@ -62,16 +62,16 @@ QUIZ_CASE(poincare_function_extremum) { Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); { constexpr int numberOfMaxima = 3; - Expression::Coordinate2D maxima[numberOfMaxima] = { - { .abscissa = 0.0, .value = 1.0}, - { .abscissa = 360.0, .value = 1.0}, - { .abscissa = NAN, .value = NAN}}; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(0.0, 1.0), + Coordinate2D(360.0, 1.0), + Coordinate2D(NAN, NAN)}; assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext, -1.0, 0.1, 500.0); } { constexpr int numberOfMinima = 1; - Expression::Coordinate2D minima[numberOfMinima] = { - { .abscissa = 180.0, .value = -1.0}}; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(180.0, -1.0)}; assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext, 0.0, 0.1, 300.0); } } @@ -80,14 +80,14 @@ QUIZ_CASE(poincare_function_extremum) { Expression e = Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)); { constexpr int numberOfMaxima = 1; - Expression::Coordinate2D maxima[numberOfMaxima] = { - { .abscissa = NAN, .value = NAN}}; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, NAN)}; assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); } { constexpr int numberOfMinima = 1; - Expression::Coordinate2D minima[numberOfMinima] = { - { .abscissa = 0.0, .value = 0.0}}; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(0.0, 0.0)}; assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); } } @@ -97,14 +97,14 @@ QUIZ_CASE(poincare_function_extremum) { Expression e = Rational::Builder(3); { constexpr int numberOfMaxima = 1; - Expression::Coordinate2D maxima[numberOfMaxima] = { - { .abscissa = NAN, .value = 3.0}}; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, 3.0)}; assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); } { constexpr int numberOfMinima = 1; - Expression::Coordinate2D minima[numberOfMinima] = { - { .abscissa = NAN, .value = 3.0}}; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(NAN, 3.0)}; assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); } } @@ -114,14 +114,14 @@ QUIZ_CASE(poincare_function_extremum) { Expression e = Rational::Builder(0); { constexpr int numberOfMaxima = 1; - Expression::Coordinate2D maxima[numberOfMaxima] = { - { .abscissa = NAN, .value = 0.0}}; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, 0.0)}; assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); } { constexpr int numberOfMinima = 1; - Expression::Coordinate2D minima[numberOfMinima] = { - { .abscissa = NAN, .value = 0.0}}; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(NAN, 0.0)}; assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); } } @@ -135,35 +135,35 @@ QUIZ_CASE(poincare_function_root) { // cos Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); constexpr int numberOfRoots = 3; - Expression::Coordinate2D roots[numberOfRoots] = { - { .abscissa = 90.0, .value = 0.0}, - { .abscissa = 270.0, .value = 0.0}, - { .abscissa = 450.0, .value = 0.0}}; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(90.0, 0.0), + Coordinate2D(270.0, 0.0), + Coordinate2D(450.0, 0.0)}; assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext, 0.0, 0.1, 500.0); } { // x^2 Expression e = Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)); constexpr int numberOfRoots = 1; - Expression::Coordinate2D roots[numberOfRoots] = { - { .abscissa = 0.0, .value = 0.0}}; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(0.0, 0.0)}; assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); } { // x^2-4 Expression e = Subtraction::Builder(Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)), Rational::Builder(4)); constexpr int numberOfRoots = 2; - Expression::Coordinate2D roots[numberOfRoots] = { - { .abscissa = -2.0, .value = 0.0}, - { .abscissa = 2.0, .value = 0.0}}; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(-2.0, 0.0), + Coordinate2D(2.0, 0.0)}; assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext, -5.0); } { // 3 Expression e = Rational::Builder(3); constexpr int numberOfRoots = 1; - Expression::Coordinate2D roots[numberOfRoots] = { - { .abscissa = NAN, .value = 0.0}}; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(NAN, 0.0)}; assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); } @@ -171,8 +171,8 @@ QUIZ_CASE(poincare_function_root) { // 0 Expression e = Rational::Builder(0); constexpr int numberOfRoots = 1; - Expression::Coordinate2D roots[numberOfRoots] = { - { .abscissa = -0.9, .value = 0.0}}; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(-0.9, 0.0)}; assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); } @@ -181,7 +181,7 @@ QUIZ_CASE(poincare_function_root) { void assert_next_intersections_are( Expression otherExpression, int numberOfIntersections, - Expression::Coordinate2D * intersections, + Coordinate2D * intersections, Expression e, const char * symbol, Context * context, @@ -194,11 +194,11 @@ void assert_next_intersections_are( double currentStart = start; for (int i = 0; i < numberOfIntersections; i++) { quiz_assert_log_if_failure(!std::isnan(currentStart), e); - Expression::Coordinate2D nextIntersection = e.nextIntersection(symbol, currentStart, step, max, context, complexFormat, angleUnit, otherExpression); - currentStart = nextIntersection.abscissa + step; + Coordinate2D nextIntersection = e.nextIntersection(symbol, currentStart, step, max, context, complexFormat, angleUnit, otherExpression); + currentStart = nextIntersection.abscissa() + step; quiz_assert_log_if_failure( - (doubles_are_approximately_equal(intersections[i].abscissa, nextIntersection.abscissa)) - && (doubles_are_approximately_equal(intersections[i].value, nextIntersection.value)), + (doubles_are_approximately_equal(intersections[i].abscissa(), nextIntersection.abscissa())) + && (doubles_are_approximately_equal(intersections[i].value(), nextIntersection.value())), e); } } @@ -212,8 +212,8 @@ QUIZ_CASE(poincare_function_intersection) { // cos with y=2 Expression otherExpression = Rational::Builder(2); constexpr int numberOfIntersections = 1; - Expression::Coordinate2D intersections[numberOfIntersections] = { - { .abscissa = NAN, .value = NAN}}; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(NAN, NAN)}; assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); } @@ -221,9 +221,9 @@ QUIZ_CASE(poincare_function_intersection) { // cos with y=1 Expression otherExpression = Rational::Builder(1); constexpr int numberOfIntersections = 2; - Expression::Coordinate2D intersections[numberOfIntersections] = { - { .abscissa = 0.0, .value = 1.0}, - { .abscissa = 360.0, .value = 1.0}}; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(0.0, 1.0), + Coordinate2D(360.0, 1.0)}; assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); } @@ -231,10 +231,10 @@ QUIZ_CASE(poincare_function_intersection) { // cos with y=0 Expression otherExpression = Rational::Builder(0); constexpr int numberOfIntersections = 3; - Expression::Coordinate2D intersections[numberOfIntersections] = { - { .abscissa = 90.0, .value = 0.0}, - { .abscissa = 270.0, .value = 0.0}, - { .abscissa = 450.0, .value = 0.0}}; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(90.0, 0.0), + Coordinate2D(270.0, 0.0), + Coordinate2D(450.0, 0.0)}; assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); } }