Merge branch 'numworks-master' into omega-hotfix

This commit is contained in:
Quentin Guidée
2020-02-28 19:50:56 +01:00
9 changed files with 184 additions and 149 deletions

View File

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

View File

@@ -413,10 +413,8 @@ private:
constexpr static double k_maxFloat = 1e100;
Coordinate2D<double> 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<double> 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;
};
}

View File

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

View File

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

View File

@@ -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<TreeNode *>(reinterpret_cast<const TreeNode *>(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() {

View File

@@ -1003,7 +1003,7 @@ Coordinate2D<double> 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<double> 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<double> 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<double> 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<float>();
template double Expression::Epsilon<double>();

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
#include <apps/shared/global_context.h>
#include <poincare/expression.h>
#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<double> * extrema,
Expression e,
void assert_points_of_interest_are(
PointOfInterestType type,
int numberOfPointsOfInterest,
Coordinate2D<double> * 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<double> 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<double>(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<double> 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<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(0.0, 1.0),
Coordinate2D<double>(360.0, 1.0),
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(360.0, 1.0),
Coordinate2D<double>(0.0, 1.0),
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(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<double> maxima[numberOfMaxima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> minima[numberOfMinima] = {
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(90.0, 0.0),
Coordinate2D<double>(270.0, 0.0),
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(450.0, 0.0),
Coordinate2D<double>(270.0, 0.0),
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(-2.0, 0.0),
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(2.0, 0.0),
Coordinate2D<double>(-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<double> roots[numberOfRoots] = {
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(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<double> roots[numberOfRoots] = {
Coordinate2D<double>(-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<double> * 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<double> 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<double> roots[numberOfRoots] = {
Coordinate2D<double>(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<double> intersections[numberOfIntersections] = {
Coordinate2D<double>(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<double> intersections[numberOfIntersections] = {
Coordinate2D<double>(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<double> intersections[numberOfIntersections] = {
Coordinate2D<double>(0.0, 1.0),
Coordinate2D<double>(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<double> intersections[numberOfIntersections] = {
Coordinate2D<double>(360.0, 1.0),
Coordinate2D<double>(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<double> intersections[numberOfIntersections] = {
Coordinate2D<double>(90.0, 0.0),
Coordinate2D<double>(270.0, 0.0),
Coordinate2D<double>(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<double> intersections[numberOfIntersections] = {
Coordinate2D<double>(450.0, 0.0),
Coordinate2D<double>(270.0, 0.0),
Coordinate2D<double>(90.0, 0.0)};
assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "0", "a", 500.0, -0.1, -1.0);
}
}