diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index cfd5f855c..6b3c9766f 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -434,11 +434,26 @@ void ConsoleController::printText(const char * text, size_t length) { flushOutputAccumulationBufferToStore(); micropython_port_vm_hook_refresh_print(); } +#if __EMSCRIPTEN__ + /* If we called micropython_port_interrupt_if_needed here, we would need to + * put in the WHITELIST all the methods that call + * ConsoleController::printText, which means all the MicroPython methods that + * call print... This is a lot of work + might reduce the performance as + * emterpreted code is slower. + * + * We thus do not allow print interruption on the web simulator. It would be + * better to allow it, but the biggest problem was on the device anyways + * -> It is much quicker to interrupt Python on the web simulator than on the + * device. + * + * TODO: Allow print interrpution on emscripten -> maybe by using WASM=1 ? */ +#else /* micropython_port_vm_hook_loop is not enough to detect user interruptions, * because it calls micropython_port_interrupt_if_needed every 20000 * operations, and a print operation is quite long. We thus explicitely call * micropython_port_interrupt_if_needed here. */ micropython_port_interrupt_if_needed(); +#endif } void ConsoleController::autoImportScript(Script script, bool force) { diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index d1a887c03..dea1fe006 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -413,10 +413,8 @@ private: constexpr static double k_maxFloat = 1e100; Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, Solver::ValueAtAbscissa 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], Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; - Coordinate2D brentMinimum(const char * symbol, double ax, double bx, Solver::ValueAtAbscissa 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, Solver::ValueAtAbscissa 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], Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; - double brentRoot(const char * symbol, double ax, double bx, double precision, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; }; } diff --git a/poincare/include/poincare/tree_handle.h b/poincare/include/poincare/tree_handle.h index 68f99b41a..5e2a07d9b 100644 --- a/poincare/include/poincare/tree_handle.h +++ b/poincare/include/poincare/tree_handle.h @@ -128,7 +128,7 @@ protected: void setIdentifierAndRetain(uint16_t newId); void setTo(const TreeHandle & tr); - static bool hasNode(uint16_t identifier) { return identifier < TreeNode::NoNodeIdentifier; } + static bool hasNode(uint16_t identifier) { return TreeNode::IsValidIdentifier(identifier); } /* Hierarchy operations */ // Add diff --git a/poincare/include/poincare/tree_node.h b/poincare/include/poincare/tree_node.h index f46d6ff9a..71d1df351 100644 --- a/poincare/include/poincare/tree_node.h +++ b/poincare/include/poincare/tree_node.h @@ -172,6 +172,8 @@ public: void log(std::ostream & stream, bool recursive = true); #endif + static bool IsValidIdentifier(uint16_t id) { return id < NoNodeIdentifier; } + protected: TreeNode() : m_identifier(NoNodeIdentifier), diff --git a/poincare/include/poincare/tree_pool.h b/poincare/include/poincare/tree_pool.h index 7291bfb79..cf5108d73 100644 --- a/poincare/include/poincare/tree_pool.h +++ b/poincare/include/poincare/tree_pool.h @@ -27,7 +27,7 @@ public: // Node TreeNode * node(uint16_t identifier) const { - assert(identifier >= 0 && identifier < MaxNumberOfNodes); + assert(TreeNode::IsValidIdentifier(identifier) && identifier < MaxNumberOfNodes); if (m_nodeForIdentifierOffset[identifier] != UINT16_MAX) { return const_cast(reinterpret_cast(m_alignedBuffer + m_nodeForIdentifierOffset[identifier])); } @@ -125,7 +125,7 @@ private: } } void push(uint16_t i) { - assert(m_currentIndex >= 0 && m_currentIndex < MaxNumberOfNodes); + assert(TreeNode::IsValidIdentifier(m_currentIndex) && m_currentIndex < MaxNumberOfNodes); m_availableIdentifiers[m_currentIndex++] = i; } uint16_t pop() { diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index a4793bc69..1bdfc8e71 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -1003,7 +1003,7 @@ Coordinate2D Expression::nextIntersection(const char * symbol, double st return expression0->approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1->approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); }, context, complexFormat, angleUnit, expression); Coordinate2D result(resultAbscissa, approximateWithValueForSymbol(symbol, resultAbscissa, context, complexFormat, angleUnit)); - if (std::fabs(result.x2()) < step*k_solverPrecision) { + if (std::fabs(result.x2()) < std::fabs(step)*k_solverPrecision) { result.setX2(0.0); } return result; @@ -1019,7 +1019,7 @@ Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, do bool endCondition = false; do { bracketMinimum(symbol, x, step, max, bracket, evaluate, context, complexFormat, angleUnit, expression); - result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, expression); + result = Solver::BrentMinimum(bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, this, symbol, &expression); x = bracket[1]; // Because of float approximation, exact zero is never reached if (std::fabs(result.x1()) < std::fabs(step)*k_solverPrecision) { @@ -1077,19 +1077,6 @@ void Expression::bracketMinimum(const char * symbol, double start, double step, result[2] = NAN; } -Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { - return Solver::BrentMinimum( - ax, - bx, - evaluation, - context, - complexFormat, - angleUnit, - this, - symbol, - &expression); -} - double Expression::nextIntersectionWithExpression(const char * symbol, double start, double step, double max, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { if (start == max || step == 0.0) { return NAN; @@ -1100,7 +1087,7 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st double x = start+step; do { 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); + result = Solver::BrentRoot(bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, complexFormat, angleUnit, this, symbol, &expression); x = bracket[1]; } while (std::isnan(result) && (step > 0.0 ? x <= max : x >= max)); @@ -1149,20 +1136,6 @@ 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, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { - return Solver::BrentRoot( - ax, - bx, - precision, - evaluation, - context, - complexFormat, - angleUnit, - this, - symbol, - &expression); -} - template float Expression::Epsilon(); template double Expression::Epsilon(); diff --git a/poincare/src/solver.cpp b/poincare/src/solver.cpp index d78b1e5bf..33914717e 100644 --- a/poincare/src/solver.cpp +++ b/poincare/src/solver.cpp @@ -133,12 +133,12 @@ double Solver::BrentRoot(double ax, double bx, double precision, ValueAtAbscissa double xm = 0.5*(c-b); if (std::fabs(xm) <= tol1 || fb == 0.0) { double fbcMiddle = evaluation(0.5*(b+c), context, complexFormat, angleUnit, context1, context2, context3); - double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb); + bool isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb); if (isContinuous) { return b; } } - if (std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(b)) { + if (std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(fb)) { double s = fb/fa; double p = 2.0*xm*s; double q = 1.0-s; @@ -168,7 +168,7 @@ double Solver::BrentRoot(double ax, double bx, double precision, ValueAtAbscissa if (std::fabs(d) > tol1) { b += d; } else { - b += xm > 0.0 ? tol1 : tol1; + b += xm > 0.0 ? tol1 : -tol1; } fb = evaluation(b, context, complexFormat, angleUnit, context1, context2, context3); } diff --git a/poincare/src/tree_pool.cpp b/poincare/src/tree_pool.cpp index 00895e05d..7ab75d0d7 100644 --- a/poincare/src/tree_pool.cpp +++ b/poincare/src/tree_pool.cpp @@ -14,7 +14,7 @@ namespace Poincare { TreePool * TreePool::SharedStaticPool = nullptr; void TreePool::freeIdentifier(uint16_t identifier) { - if (identifier >= 0 && identifier < MaxNumberOfNodes) { + if (TreeNode::IsValidIdentifier(identifier) && identifier < MaxNumberOfNodes) { m_nodeForIdentifierOffset[identifier] = UINT16_MAX; m_identifiers.push(identifier); } diff --git a/poincare/test/function_solver.cpp b/poincare/test/function_solver.cpp index e95d77b30..4dee3c65f 100644 --- a/poincare/test/function_solver.cpp +++ b/poincare/test/function_solver.cpp @@ -1,13 +1,13 @@ #include -#include #include "helper.h" using namespace Poincare; -enum class ExtremumType : uint8_t { +enum class PointOfInterestType { Maximum, Minimum, - Root + Root, + Intersection, }; bool doubles_are_approximately_equal(double d1, double d2) { @@ -21,220 +21,267 @@ bool doubles_are_approximately_equal(double d1, double d2) { return std::abs(d1-d2) < 0.00001; } -void assert_next_extrema_are( - ExtremumType extremumType, - int numberOfExtrema, - Coordinate2D * extrema, - Expression e, +void assert_points_of_interest_are( + PointOfInterestType type, + int numberOfPointsOfInterest, + Coordinate2D * pointsOfInterest, + const char * expression1, + const char * expression2, const char * symbol, - Context * context, - double start = -1.0, - double step = 0.1, - double max = 100.0, + double start, + double step, + double max, Preferences::ComplexFormat complexFormat = Preferences::ComplexFormat::Real, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { - double currentStart = start; - for (int i = 0; i < numberOfExtrema; i++) { - quiz_assert_log_if_failure(!std::isnan(currentStart), e); - 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 = Coordinate2D(e.nextRoot(symbol, currentStart, step, max, context, complexFormat, angleUnit), 0.0 ); + Shared::GlobalContext context; + Poincare::Expression e1 = parse_expression(expression1, &context, false); + Poincare::Expression e2; + if (expression2) { + assert(type == PointOfInterestType::Intersection); + e2 = parse_expression(expression2, &context, false); + } + for (int i = 0; i < numberOfPointsOfInterest; i++) { + quiz_assert_log_if_failure(!std::isnan(start), e1); + Coordinate2D nextPointOfInterest; + if (type == PointOfInterestType::Maximum) { + nextPointOfInterest = e1.nextMaximum(symbol, start, step, max, &context, complexFormat, angleUnit); + } else if (type == PointOfInterestType::Minimum) { + nextPointOfInterest = e1.nextMinimum(symbol, start, step, max, &context, complexFormat, angleUnit); + } else if (type == PointOfInterestType::Root) { + nextPointOfInterest = Coordinate2D(e1.nextRoot(symbol, start, step, max, &context, complexFormat, angleUnit), 0.0); + } else if (type == PointOfInterestType::Intersection) { + nextPointOfInterest = e1.nextIntersection(symbol, start, step, max, &context, complexFormat, angleUnit, e2); } - currentStart = nextExtrema.x1() + step; quiz_assert_log_if_failure( - (doubles_are_approximately_equal(extrema[i].x1(), nextExtrema.x1())) - && (doubles_are_approximately_equal(extrema[i].x2(), nextExtrema.x2())), - e); + doubles_are_approximately_equal(pointsOfInterest[i].x1(), nextPointOfInterest.x1()) && + doubles_are_approximately_equal(pointsOfInterest[i].x2(), nextPointOfInterest.x2()), + e1); + start = nextPointOfInterest.x1() + step; } } QUIZ_CASE(poincare_function_extremum) { - const char * symbol = "a"; - int symbolLength = strlen(symbol); - Shared::GlobalContext globalContext; { - // cos - Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); { constexpr int numberOfMaxima = 3; 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); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "cos(a)", nullptr, "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfMaxima = 3; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(360.0, 1.0), + Coordinate2D(0.0, 1.0), + Coordinate2D(NAN, NAN)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "cos(a)", nullptr, "a", 500.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; 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); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "cos(a)", nullptr, "a", 0.0, 0.1, 300.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(180.0, -1.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "cos(a)", nullptr, "a", 300.0, -0.1, 0.0); } } { - // x^2 - Expression e = Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)); { constexpr int numberOfMaxima = 1; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(NAN, NAN)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "a^2", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMaxima = 1; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, NAN)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "a^2", nullptr, "a", 100.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(0.0, 0.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "a^2", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(0.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "a^2", nullptr, "a", 100.0, -0.1, -1.0); } } - { - // 3 - Expression e = Rational::Builder(3); { constexpr int numberOfMaxima = 1; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(NAN, 3.0)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "3", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMaxima = 1; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, 3.0)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "3", nullptr, "a", 100.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(NAN, 3.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "3", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(NAN, 3.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "3", nullptr, "a", 100.0, -0.1, -1.0); } } - { - // 0 - Expression e = Rational::Builder(0); { constexpr int numberOfMaxima = 1; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(NAN, 0.0)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "0", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMaxima = 1; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "0", nullptr, "a", 100.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(NAN, 0.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "0", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(NAN, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "0", nullptr, "a", 100.0, -0.1, -1.0); } } } QUIZ_CASE(poincare_function_root) { - const char * symbol = "a"; - int symbolLength = strlen(symbol); - Shared::GlobalContext globalContext; { - // cos - Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); constexpr int numberOfRoots = 3; 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); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "cos(a)", nullptr, "a", 0.0, 0.1, 500.0); + } + { + constexpr int numberOfRoots = 3; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(450.0, 0.0), + Coordinate2D(270.0, 0.0), + Coordinate2D(90.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "cos(a)", nullptr, "a", 500.0, -0.1, 0.0); } { - // x^2 - Expression e = Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)); constexpr int numberOfRoots = 1; Coordinate2D roots[numberOfRoots] = { Coordinate2D(0.0, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfRoots = 1; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(0.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2", nullptr, "a", 100.0, -0.1, -1.0); } { - // x^2-4 - Expression e = Subtraction::Builder(Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)), Rational::Builder(4)); constexpr int numberOfRoots = 2; 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); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2-4", nullptr, "a", -5.0, 0.1, 100.0); + } + { + constexpr int numberOfRoots = 2; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(2.0, 0.0), + Coordinate2D(-2.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2-4", nullptr, "a", 100.0, -0.1, -5.0); } { - // 3 - Expression e = Rational::Builder(3); constexpr int numberOfRoots = 1; Coordinate2D roots[numberOfRoots] = { Coordinate2D(NAN, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "3", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfRoots = 1; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(NAN, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "3", nullptr, "a", 100.0, -0.1, -1.0); } - { - // 0 - Expression e = Rational::Builder(0); constexpr int numberOfRoots = 1; Coordinate2D roots[numberOfRoots] = { Coordinate2D(-0.9, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "0", nullptr, "a", -1.0, 0.1, 100.0); } - -} - -void assert_next_intersections_are( - Expression otherExpression, - int numberOfIntersections, - Coordinate2D * intersections, - Expression e, - const char * symbol, - Context * context, - double start = -1.0, - double step = 0.1, - double max = 500.0, - Preferences::ComplexFormat complexFormat = Preferences::ComplexFormat::Real, - Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) -{ - double currentStart = start; - for (int i = 0; i < numberOfIntersections; i++) { - quiz_assert_log_if_failure(!std::isnan(currentStart), e); - Coordinate2D nextIntersection = e.nextIntersection(symbol, currentStart, step, max, context, complexFormat, angleUnit, otherExpression); - currentStart = nextIntersection.x1() + step; - quiz_assert_log_if_failure( - (doubles_are_approximately_equal(intersections[i].x1(), nextIntersection.x1())) - && (doubles_are_approximately_equal(intersections[i].x2(), nextIntersection.x2())), - e); - } -} -QUIZ_CASE(poincare_function_intersection) { - const char * symbol = "a"; - int symbolLength = strlen(symbol); - Shared::GlobalContext globalContext; - Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); - { - // cos with y=2 - Expression otherExpression = Rational::Builder(2); + constexpr int numberOfRoots = 1; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(99.8, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "0", nullptr, "a", 100.0, -0.1, -1.0); + } +} + +QUIZ_CASE(poincare_function_intersection) { + { constexpr int numberOfIntersections = 1; Coordinate2D intersections[numberOfIntersections] = { Coordinate2D(NAN, NAN)}; - assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "2", "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfIntersections = 1; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(NAN, NAN)}; + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "2", "a", 500.0, -0.1, -1.0); } - { - // cos with y=1 - Expression otherExpression = Rational::Builder(1); constexpr int numberOfIntersections = 2; Coordinate2D intersections[numberOfIntersections] = { Coordinate2D(0.0, 1.0), Coordinate2D(360.0, 1.0)}; - assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "1", "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfIntersections = 2; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(360.0, 1.0), + Coordinate2D(0.0, 1.0)}; + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "1", "a", 500.0, -0.1, -1.0); } - { - // cos with y=0 - Expression otherExpression = Rational::Builder(0); constexpr int numberOfIntersections = 3; 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); + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "0", "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfIntersections = 3; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(450.0, 0.0), + Coordinate2D(270.0, 0.0), + Coordinate2D(90.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "0", "a", 500.0, -0.1, -1.0); } }