mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
Merge branch 'omega-hotfix' into omega-dev
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user