Files
Upsilon/apps/graph/test/range.cpp
Gabriel Ozouf a1aefb0cb2 [apps/graph] Added tests on graph ranges
Change-Id: I6c080f40c934cd31083be3d19aa0a4d0bb33c5cc
2020-11-04 15:30:53 +01:00

183 lines
8.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <quiz.h>
#include "helper.h"
#include <cmath>
using namespace Poincare;
using namespace Shared;
namespace Graph {
void assert_range_inclusion_predicate(const char * definition, ContinuousFunction::PlotType type, const float * xList, size_t length, bool testInclusion) {
/* Test that all points (x, f(x)) for x in xList are either included in the
* range if testIncluded is true, or exculded from the range if testInclusion
* is false.
* If f(x) is not finite, only the presence of x in the horizontal range is
* tested. */
GlobalContext globalContext;
ContinuousFunctionStore functionStore;
ContinuousFunction * f = addFunction(definition, type, &functionStore, &globalContext);
float xMin = FLT_MAX, xMax = - FLT_MAX, yMin = FLT_MAX, yMax = - FLT_MAX;
f->rangeForDisplay(&xMin, &xMax, &yMin, &yMax, &globalContext);
assert(xMin <= xMax && yMin <= yMax);
for (size_t i = 0; i < length; i++) {
float x = xList[i];
float y = f->evaluateXYAtParameter(x, &globalContext).x2();
assert(std::isfinite(x));
if (!testInclusion) {
quiz_assert(xMin > x || x > xMax || (std::isfinite(y) && (yMin > y || y > yMax)));
} else {
/* The program can miss the exact abscissa of an extremum, resulting in
* bounds that are close from its value but that do not encompass it. We
* thus test the inclusion of (x, f(x)) along with two neighbouring
* points. */
float dx = (xMax - xMin) / (Ion::Display::Width / 2);
float y1 = f->evaluateXYAtParameter(x - dx, &globalContext).x2(), y2 = f->evaluateXYAtParameter(x + dx, &globalContext).x2();
quiz_assert(xMin <= x
&& x <= xMax
&& (!std::isfinite(y)
|| (yMin <= y && y <= yMax)
|| (yMin <= y1 && y1 <= yMax)
|| (yMin <= y2 && y2 <= yMax)));
}
}
}
void assert_range_includes_points(const char * definition, ContinuousFunction::PlotType type, const float * xList, size_t length) { assert_range_inclusion_predicate(definition, type, xList, length, true); }
void assert_range_excludes_points(const char * definition, ContinuousFunction::PlotType type, const float * xList, size_t length) { assert_range_inclusion_predicate(definition, type, xList, length, false); }
void assert_range_includes_single_point(const char * definition, ContinuousFunction::PlotType type, float x) { assert_range_includes_points(definition, type, &x, 1); }
void assert_range_excludes_single_point(const char * definition, ContinuousFunction::PlotType type, float x) { assert_range_excludes_points(definition, type, &x, 1); }
void assert_range_includes_and_bounds_asymptotes(const char * definition, const float * asymptotesXList, size_t length) {
/* The value for delta is the step the old algorithm used to sample a
* cartesian function, causing functions such as 1/x to be evaluated too
* close to 0. */
constexpr float delta = 1.f / 32.f;
for (size_t i = 0; i < length; i++) {
float x = asymptotesXList[i];
assert_range_includes_single_point(definition, Cartesian, x);
assert_range_excludes_single_point(definition, Cartesian, x - delta);
assert_range_excludes_single_point(definition, Cartesian, x + delta);
}
}
void assert_range_shows_enough_periods(const char * definition, float period, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) {
/* The graph should display at least 3 periods, but no more than 7. */
constexpr int lowNumberOfPeriods = 3, highNumberOfPeriods = 8;
GlobalContext globalContext;
ContinuousFunctionStore functionStore;
ContinuousFunction * f = addFunction(definition, Cartesian, &functionStore, &globalContext);
Preferences::sharedPreferences()->setAngleUnit(angleUnit);
if (angleUnit != Preferences::AngleUnit::Degree) {
f->setPlotType(Cartesian, angleUnit, &globalContext);
}
float xMin = FLT_MAX, xMax = - FLT_MAX, yMin = FLT_MAX, yMax = - FLT_MAX;
f->rangeForDisplay(&xMin, &xMax, &yMin, &yMax, &globalContext);
assert(xMin <= xMax && yMin <= yMax);
float numberOfPeriods = (xMax - xMin) / period;
quiz_assert(lowNumberOfPeriods <= numberOfPeriods && numberOfPeriods <= highNumberOfPeriods);
}
void assert_range_displays_entire_polar_function(const char * definition) {
GlobalContext globalContext;
ContinuousFunctionStore functionStore;
ContinuousFunction * f = addFunction(definition, Polar, &functionStore, &globalContext);
float xMin = FLT_MAX, xMax = - FLT_MAX, yMin = FLT_MAX, yMax = - FLT_MAX;
f->rangeForDisplay(&xMin, &xMax, &yMin, &yMax, &globalContext);
assert(xMin <= xMax && yMin <= yMax);
for (float t = f->tMin(); t < f->tMax(); t += f->rangeStep()) {
const Coordinate2D<float> xy = f->evaluateXYAtParameter(t, &globalContext);
if (!std::isfinite(xy.x1()) || !std::isfinite(xy.x2())) {
continue;
}
quiz_assert(xMin <= xy.x1() && xy.x1() <= xMax && yMin <= xy.x2() && xy.x2() <= yMax);
}
}
QUIZ_CASE(graph_range_polynomes) {
assert_range_includes_single_point("37", Cartesian, 0.f);
assert_range_includes_single_point("x-1", Cartesian, 1.f);
assert_range_includes_single_point("100+x", Cartesian, -100.f);
assert_range_includes_single_point("x^2-20*x+101", Cartesian, 10.f);
constexpr float array1[2] = {-2.f, 3.f};
assert_range_includes_points("(x+2)*(x-3)", Cartesian, array1, 2);
constexpr float array2[2] = {-1.f, 0.5f};
assert_range_includes_points("2*x^2+x-1", Cartesian, array2, 2);
constexpr float array3[6] = {-3.f, 3.f, -2.f, 2.f, -1.f, 1.f};
assert_range_includes_points("(x+3)(x+2)(x+1)(x-1)(x-2)(x-3)", Cartesian, array3, 6);
constexpr float array4[3] = {0.f, 4.f, 5.f};
assert_range_includes_points("x^5-5*x^4", Cartesian, array4, 3);
}
QUIZ_CASE(graph_range_exponential) {
assert_range_includes_single_point("^x", Cartesian, 0.f);
assert_range_excludes_single_point("^x", Cartesian, 4.f);
assert_range_includes_single_point("^(-x)", Cartesian, 0.f);
assert_range_excludes_single_point("^(-x)", Cartesian, -4.f);
constexpr float array1[3] = {1.f, 5.f, 1e-1f};
assert_range_includes_points("ln(x)", Cartesian, array1, 3);
assert_range_excludes_single_point("ln(x)", Cartesian, 1e-3f);
constexpr float array2[2] = {-1.f, 3.f};
assert_range_includes_points("2-^(-x)", Cartesian, array2, 2);
assert_range_excludes_single_point("2-^(-x)", Cartesian, 50.f);
assert_range_excludes_single_point("2-^(-x)", Cartesian, -10.f);
assert_range_includes_single_point("x^x", Cartesian, 0.5f);
assert_range_excludes_single_point("x^x", Cartesian, -5.f);
assert_range_includes_single_point("x^(1/x)", Cartesian, 1e-3f);
}
QUIZ_CASE(graph_range_fractions) {
constexpr float array1[1] = {0.f};
assert_range_includes_and_bounds_asymptotes("1/x", array1, 1);
assert_range_includes_and_bounds_asymptotes("1/x^2", array1, 1);
constexpr float array2[2] = {-2.f, 2.f};
assert_range_includes_and_bounds_asymptotes("1/(x^2-4)", array2, 2);
constexpr float array3[1] = {-100.f};
assert_range_includes_and_bounds_asymptotes("1/(x+100)", array3, 1);
}
QUIZ_CASE(graph_range_periodic) {
assert_range_shows_enough_periods("sin(x)", 2*M_PI, Preferences::AngleUnit::Radian);
assert_range_shows_enough_periods("sin(x)", 360.f);
assert_range_shows_enough_periods("sin(x)+10", 2*M_PI, Preferences::AngleUnit::Radian);
assert_range_shows_enough_periods("sin(x)+10", 360.f);
assert_range_shows_enough_periods("cos(x)", 2*M_PI, Preferences::AngleUnit::Radian);
assert_range_shows_enough_periods("cos(x)", 360.f);
assert_range_shows_enough_periods("sin(x)/x", 2*M_PI, Preferences::AngleUnit::Radian);
assert_range_shows_enough_periods("sin(x)/x", 360.f);
assert_range_shows_enough_periods("x*sin(x)", 2*M_PI, Preferences::AngleUnit::Radian);
assert_range_shows_enough_periods("x*sin(x)", 360.f);
assert_range_shows_enough_periods("ln(sin(x))", 2*M_PI, Preferences::AngleUnit::Radian);
assert_range_shows_enough_periods("ln(sin(x))", 360.f);
constexpr float array1[2] = {-1.f, 1.f};
assert_range_includes_points("x*sin(1/x)", Cartesian, array1, 2);
}
QUIZ_CASE(graph_range_polar) {
assert_range_displays_entire_polar_function("1");
assert_range_displays_entire_polar_function("θ");
assert_range_displays_entire_polar_function("sin(θ)");
assert_range_displays_entire_polar_function("sin(10θ)");
assert_range_displays_entire_polar_function("ln(θ)");
}
}