Merge branch 'omega-hotfix' into omega-dev

This commit is contained in:
Quentin
2020-04-16 14:24:47 +02:00
335 changed files with 2114 additions and 1706 deletions

View File

@@ -77,4 +77,4 @@ jobs:
with:
name: epsilon-linux.bin
path: output/release/simulator/linux/epsilon.bin
- run: make -j2 PLATFORM=simulator test.headless.bin
- run: make -j2 PLATFORM=simulator test.headless.bin

View File

@@ -57,6 +57,9 @@ $(call object_for,apps/apps_container_storage.cpp apps/apps_container.cpp apps/m
SFLAGS += -I$(BUILD_DIR)
i18n_files += $(addprefix apps/language_,$(addsuffix .universal.i18n, $(EPSILON_I18N)))
ifeq ($(EPSILON_GETOPT),1)
i18n_files += $(addprefix apps/language_,$(addsuffix _iso6391.universal.i18n, $(EPSILON_I18N)))
endif
i18n_files += $(addprefix apps/,\
shared.de.i18n\
shared.en.i18n\
@@ -83,7 +86,7 @@ $(eval $(call rule_for, \
I18N, \
apps/i18n.cpp, \
$(i18n_files), \
$$(PYTHON) apps/i18n.py --codepoints $(code_points) --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^, \
$$(PYTHON) apps/i18n.py --codepoints $(code_points) --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ --generateISO6391locales $$(EPSILON_GETOPT), \
global \
))

View File

@@ -36,7 +36,7 @@ public:
MathToolbox * mathToolbox();
VariableBoxController * variableBoxController();
void suspend(bool checkIfOnOffKeyReleased = false);
virtual bool dispatchEvent(Ion::Events::Event event) override;
bool dispatchEvent(Ion::Events::Event event) override;
bool switchTo(App::Snapshot * snapshot) override;
void run() override;
bool updateBatteryState();

View File

@@ -39,7 +39,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
* and the line of equation (real*t,imag*t).
* (a*cos(t), b*sin(t)) = (real*t,imag*t) --> tan(t) = sign(a)*sign(b) (± π)
* --> t = π/4 [π/2] according to sign(a) and sign(b). */
float th = real < 0.0f ? 3.0f*M_PI/4.0f : M_PI/4.0f;
float th = real < 0.0f ? (float)(3.0*M_PI_4) : (float)M_PI_4;
th = imag < 0.0f ? -th : th;
// Compute ellipsis parameters a and b
float factor = 5.0f;
@@ -48,7 +48,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
// Avoid flat ellipsis for edge cases (for real = 0, the case imag = 0 is excluded)
if (real == 0.0f) {
a = 1.0f/factor;
th = imag < 0.0f ? -M_PI/2.0f : M_PI/2.0f;
th = imag < 0.0f ? (float)-M_PI_2 : (float)M_PI_2;
}
std::complex<float> parameters(a,b);
drawCurve(ctx, rect, 0.0f, 1.0f, 0.01f,

View File

@@ -28,7 +28,7 @@ public:
protected:
constexpr static int k_maxNumberOfCells = 4;
virtual int textAtIndex(char * buffer, size_t bufferSize, int index) override;
int textAtIndex(char * buffer, size_t bufferSize, int index) override;
Poincare::Expression m_expression;
// Memoization of layouts
mutable Poincare::Layout m_layouts[k_maxNumberOfCells];

View File

@@ -18,7 +18,7 @@ public:
float yMax() const override { return yCenter() + yHalfRange(); }
void setAngle(float f) { m_angle = f; }
float angle() const { return m_angle*M_PI/Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
float angle() const { return m_angle*(float)M_PI/(float)Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
private:
constexpr static float k_xHalfRange = 2.1f;
// We center the yRange around the semi-circle where the angle is

View File

@@ -7,14 +7,13 @@
#include <poincare/unreal.h>
#include <string.h>
#include <cmath>
#include <algorithm>
using namespace Poincare;
using namespace Shared;
namespace Calculation {
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
bool Calculation::operator==(const Calculation& c) {
return strcmp(inputText(), c.inputText()) == 0
&& strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), c.approximateOutputText(NumberOfSignificantDigits::Maximal)) == 0
@@ -164,11 +163,11 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre
singleLine = exactOutputWidth + inputWidth < maxWidth - 40;
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
KDCoordinate exactOutputBaseline = exactLayout.baseline();
result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin;
result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin;
} else {
if (allExpressionsInline) {
KDCoordinate exactOutputBaseline = exactLayout.baseline();
result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline);
result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline);
} else {
result = inputHeight + exactOutputHeight + doubleMargin;
}
@@ -198,11 +197,11 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre
if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) {
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin;
result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin;
} else {
if (allExpressionsInline) {
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline);
result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline);
} else {
result = inputHeight + approximateOutputHeight + doubleMargin;
}
@@ -216,12 +215,12 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre
singleLine = exactOutputWidth + approximateOutputWidth + inputWidth < maxWidth - 70;
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) {
result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin;
result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin;
} else {
if (allExpressionsInline) {
result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline));
result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline));
} else {
KDCoordinate outputHeight = maxCoordinate(exactOutputBaseline, approximateOutputBaseline) + maxCoordinate(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline);
KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline);
result = inputHeight + outputHeight + doubleMargin;
}
}

View File

@@ -5,12 +5,10 @@
#include <poincare/exception_checkpoint.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
namespace Calculation {
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
/* HistoryViewCellDataSource */
HistoryViewCellDataSource::HistoryViewCellDataSource() :
@@ -182,16 +180,16 @@ void HistoryViewCell::layoutSubviews(bool force) {
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
m_inputView.setFrame(KDRect(
0, 0,
minCoordinate(maxFrameWidth, inputSize.width()),
std::min(maxFrameWidth, inputSize.width()),
inputSize.height()),
force);
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
int singleLine = outputSize.width() + inputSize.width() < bounds().width() - 6;
int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (std::max(0, inputSize.height() - outputSize.height()) / 2) + std::max(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
m_scrollableOutputView.setFrame(KDRect(
maxCoordinate(0, maxFrameWidth - outputSize.width()),
std::max(0, maxFrameWidth - outputSize.width()),
outputHeight,
minCoordinate(maxFrameWidth, outputSize.width()),
std::min(maxFrameWidth, outputSize.width()),
outputSize.height()),
force);
}

View File

@@ -3,6 +3,7 @@
#include "script.h"
#include "variable_box_controller.h"
#include <apps/i18n.h>
#include <algorithm>
#include <assert.h>
#include <escher/metric.h>
#include <poincare/preferences.h>
@@ -16,8 +17,6 @@ extern "C" {
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
static const char * sStandardPromptText = ">>> ";
ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore
@@ -488,7 +487,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
/* Copy the script name without the extension ".py". The '.' is overwritten
* by the null terminating char. */
int copySizeWithNullTerminatingZero = minInt(k_maxImportCommandSize - currentChar, strlen(scriptName) - strlen(ScriptStore::k_scriptExtension));
int copySizeWithNullTerminatingZero = std::min(k_maxImportCommandSize - currentChar, strlen(scriptName) - strlen(ScriptStore::k_scriptExtension));
assert(copySizeWithNullTerminatingZero >= 0);
assert(copySizeWithNullTerminatingZero <= k_maxImportCommandSize - currentChar);
strlcpy(command+currentChar, scriptName, copySizeWithNullTerminatingZero);

View File

@@ -4,11 +4,10 @@
#include <apps/i18n.h>
#include <apps/global_preferences.h>
#include <assert.h>
#include <algorithm>
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) :
HighlightCell(),
Responder(parentResponder),
@@ -70,7 +69,7 @@ const char * ConsoleEditCell::shiftCurrentTextAndClear() {
char * textFieldBuffer = const_cast<char *>(m_textField.text());
char * newTextPosition = textFieldBuffer + 1;
assert(previousBufferSize > 0);
size_t copyLength = minInt(previousBufferSize - 1, strlen(textFieldBuffer));
size_t copyLength = std::min(previousBufferSize - 1, strlen(textFieldBuffer));
memmove(newTextPosition, textFieldBuffer, copyLength);
newTextPosition[copyLength] = 0;
textFieldBuffer[0] = 0;

View File

@@ -1,10 +1,9 @@
#include "console_store.h"
#include <string.h>
#include <algorithm>
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
void ConsoleStore::startNewSession() {
if (k_historySize < 1) {
return;
@@ -12,7 +11,7 @@ void ConsoleStore::startNewSession() {
m_history[0] = makePrevious(m_history[0]);
for (int i = 0; i < k_historySize - 1; i++) {
for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
if (m_history[i+1] == 0) {
return ;
@@ -25,7 +24,7 @@ void ConsoleStore::startNewSession() {
ConsoleLine ConsoleStore::lineAtIndex(int i) const {
assert(i >= 0 && i < numberOfLines());
int currentLineIndex = 0;
for (int j=0; j<k_historySize; j++) {
for (size_t j=0; j<k_historySize; j++) {
if (m_history[j] == 0) {
currentLineIndex++;
j++;
@@ -43,7 +42,7 @@ int ConsoleStore::numberOfLines() const {
return 0;
}
int result = 0;
for (int i = 0; i < k_historySize - 1; i++) {
for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
result++;
if (m_history[i+1] == 0) {
@@ -96,14 +95,14 @@ const char * ConsoleStore::push(const char marker, const char * text) {
if (ConsoleLine::sizeOfConsoleLine(textLength) > k_historySize - 1) {
textLength = k_historySize - 1 - 1 - 1; // Marker, null termination and null marker.
}
int i = indexOfNullMarker();
size_t i = indexOfNullMarker();
// If needed, make room for the text we want to push.
while (i + ConsoleLine::sizeOfConsoleLine(textLength) > k_historySize - 1) {
deleteFirstLine();
i = indexOfNullMarker();
}
m_history[i] = marker;
strlcpy(&m_history[i+1], text, minInt(k_historySize-(i+1),textLength+1));
strlcpy(&m_history[i+1], text, std::min(k_historySize-(i+1),textLength+1));
m_history[i+1+textLength+1] = 0;
return &m_history[i+1];
}
@@ -113,11 +112,11 @@ ConsoleLine::Type ConsoleStore::lineTypeForMarker(char marker) const {
return static_cast<ConsoleLine::Type>(marker-1);
}
int ConsoleStore::indexOfNullMarker() const {
size_t ConsoleStore::indexOfNullMarker() const {
if (m_history[0] == 0) {
return 0;
}
for (int i=0; i<k_historySize; i++) {
for (size_t i=0; i<k_historySize; i++) {
if (m_history[i] == 0 && m_history[i+1] == 0) {
return (i+1);
}
@@ -129,13 +128,13 @@ int ConsoleStore::indexOfNullMarker() const {
void ConsoleStore::deleteLineAtIndex(int index) {
assert(index >=0 && index < numberOfLines());
int currentLineIndex = 0;
for (int i = 0; i < k_historySize - 1; i++) {
for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
currentLineIndex++;
continue;
}
if (currentLineIndex == index) {
int nextLineStart = i;
size_t nextLineStart = i;
while (m_history[nextLineStart] != 0 && nextLineStart < k_historySize - 2) {
nextLineStart++;
}
@@ -158,7 +157,7 @@ void ConsoleStore::deleteFirstLine() {
secondLineMarkerIndex++;
}
secondLineMarkerIndex++;
for (int i=0; i<k_historySize - secondLineMarkerIndex; i++) {
for (size_t i=0; i<k_historySize - secondLineMarkerIndex; i++) {
m_history[i] = m_history[secondLineMarkerIndex+i];
}
}
@@ -174,7 +173,7 @@ void ConsoleStore::deleteLastLine() {
}
int currentLineIndex = 1;
int lastLineMarkerIndex = 0;
for (int i=0; i<k_historySize; i++) {
for (size_t i=0; i<k_historySize; i++) {
if (m_history[i] == 0) {
currentLineIndex++;
if (currentLineIndex == lineCount) {

View File

@@ -23,7 +23,7 @@ private:
static constexpr char CurrentSessionResultMarker = 0x02;
static constexpr char PreviousSessionCommandMarker = 0x03;
static constexpr char PreviousSessionResultMarker = 0x04;
static constexpr int k_historySize = 1024;
static constexpr size_t k_historySize = 1024;
static char makePrevious(char marker) {
if (marker == CurrentSessionCommandMarker || marker == CurrentSessionResultMarker) {
return marker + 0x02;
@@ -32,7 +32,7 @@ private:
}
const char * push(const char marker, const char * text);
ConsoleLine::Type lineTypeForMarker(char marker) const;
int indexOfNullMarker() const;
size_t indexOfNullMarker() const;
void deleteLineAtIndex(int index);
void deleteFirstLine();
/* When there is no room left to store a new ConsoleLine, we have to delete

View File

@@ -9,6 +9,7 @@ extern "C" {
#include "py/lexer.h"
}
#include <stdlib.h>
#include <algorithm>
namespace Code {
@@ -21,8 +22,6 @@ constexpr KDColor StringColor = Palette::CodeString;
constexpr KDColor BackgroundColor = Palette::CodeBackground;
constexpr KDColor HighlightColor = Palette::CodeBackgroundSelected;
static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; }
static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
if (tokenKind == MP_TOKEN_STRING) {
return StringColor;
@@ -84,7 +83,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
fromColumn,
lineStart,
minPointer(text + byteLength, lineEnd) - lineStart,
std::min(text + byteLength, lineEnd) - lineStart,
StringColor,
BackgroundColor,
selectionStart,
@@ -107,7 +106,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
fromColumn,
spacesStart,
minPointer(text + byteLength, firstNonSpace) - spacesStart,
std::min(text + byteLength, firstNonSpace) - spacesStart,
StringColor,
BackgroundColor,
selectionStart,
@@ -135,7 +134,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenEnd),
tokenEnd,
minPointer(text + byteLength, tokenFrom) - tokenEnd,
std::min(text + byteLength, tokenFrom) - tokenEnd,
StringColor,
BackgroundColor,
selectionStart,

View File

@@ -8,7 +8,7 @@
class HighContrastButton : public Button {
public:
using Button::Button;
virtual KDColor highlightedBackgroundColor() const override { return Palette::ButtonBackgroundSelectedHighContrast; }
KDColor highlightedBackgroundColor() const override { return Palette::ButtonBackgroundSelectedHighContrast; }
};
class ExamPopUpController : public ViewController {

View File

@@ -1,16 +1,12 @@
#include "graph_controller.h"
#include "../app.h"
#include <algorithm>
using namespace Poincare;
using namespace Shared;
namespace Graph {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
static inline double minDouble(double x, double y) { return x < y ? x : y; }
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
@@ -52,10 +48,10 @@ void GraphController::interestingFunctionRange(ExpiringPointer<ContinuousFunctio
float x = xy.x1();
float y = xy.x2();
if (!std::isnan(x) && !std::isinf(x) && !std::isnan(y) && !std::isinf(y)) {
*xm = minFloat(*xm, x);
*xM = maxFloat(*xM, x);
*ym = minFloat(*ym, y);
*yM = maxFloat(*yM, y);
*xm = std::min(*xm, x);
*xM = std::max(*xM, x);
*ym = std::min(*ym, y);
*yM = std::max(*yM, y);
}
}
}
@@ -102,8 +98,8 @@ void GraphController::interestingRanges(float * xm, float * xM, float * ym, floa
* y-range for even functions (y = 1/x). */
assert(!std::isnan(f->tMin()));
assert(!std::isnan(f->tMax()));
const double tMin = maxFloat(f->tMin(), resultxMin);
const double tMax = minFloat(f->tMax(), resultxMax);
const double tMin = std::max(f->tMin(), resultxMin);
const double tMax = std::min(f->tMax(), resultxMax);
const double step = (tMax - tMin) / (2.0 * (m_view.bounds().width() - 1.0));
interestingFunctionRange(f, tMin, tMax, step, &resultxMin, &resultxMax, &resultyMin, &resultyMax);
}
@@ -129,12 +125,12 @@ float GraphController::interestingXHalfRange() const {
ExpiringPointer<ContinuousFunction> f = store->modelForRecord(store->activeRecordAtIndex(i));
float fRange = f->expressionReduced(context).characteristicXRange(context, Poincare::Preferences::sharedPreferences()->angleUnit());
if (!std::isnan(fRange) && !std::isinf(fRange)) {
characteristicRange = maxFloat(fRange, characteristicRange);
characteristicRange = std::max(fRange, characteristicRange);
}
// Compute the combined range of the functions
assert(f->plotType() == ContinuousFunction::PlotType::Cartesian); // So that tMin tMax represents xMin xMax
tMin = minDouble(tMin, f->tMin());
tMax = maxDouble(tMax, f->tMax());
tMin = std::min<double>(tMin, f->tMin());
tMax = std::max<double>(tMax, f->tMax());
}
constexpr float rangeMultiplicator = 1.6f;
if (characteristicRange > 0.0f ) {
@@ -145,7 +141,7 @@ float GraphController::interestingXHalfRange() const {
if (tMin >= -defaultXHalfRange && tMax <= defaultXHalfRange) {
/* If the combined Range of the functions is smaller than the default range,
* use it. */
float f = rangeMultiplicator * (float)maxDouble(std::fabs(tMin), std::fabs(tMax));
float f = rangeMultiplicator * (float)std::max(std::fabs(tMin), std::fabs(tMax));
return (std::isnan(f) || std::isinf(f)) ? defaultXHalfRange : f;
}
return defaultXHalfRange;
@@ -226,7 +222,7 @@ void GraphController::jumpToLeftRightCurve(double t, int direction, int function
if (currentXDelta <= xDelta) {
double potentialNextTMin = f->tMin();
double potentialNextTMax = f->tMax();
double potentialNextT = maxDouble(potentialNextTMin, minDouble(potentialNextTMax, t));
double potentialNextT = std::max(potentialNextTMin, std::min(potentialNextTMax, t));
Coordinate2D<double> xy = f->evaluateXYAtParameter(potentialNextT, App::app()->localContext());
if (currentXDelta < xDelta || std::abs(xy.x2() - m_cursor->y()) < std::abs(nextY - m_cursor->y())) {
nextCurveIndex = i;

View File

@@ -3,15 +3,13 @@
#include "../app.h"
#include "../../shared/poincare_helpers.h"
#include <poincare/preferences.h>
#include <algorithm>
using namespace Shared;
using namespace Poincare;
namespace Graph {
static inline double minDouble(double x, double y) { return x < y ? x : y; }
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCursor * cursor, int direction, Shared::InteractiveCurveViewRange * range, int numberOfStepsInGradUnit, Ion::Storage::Record record, bool fast) {
ExpiringPointer<ContinuousFunction> function = App::app()->functionStore()->modelForRecord(record);
double tCursorPosition = cursor->t();
@@ -34,7 +32,7 @@ bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCurso
step *= 5.0;
}
t += dir * step;
t = maxDouble(tMin, minDouble(tMax, t));
t = std::max(tMin, std::min(tMax, t));
Coordinate2D<double> xy = function->evaluateXYAtParameter(t, App::app()->localContext());
cursor->moveTo(t, xy.x1(), xy.x2());
return true;

View File

@@ -1,12 +1,10 @@
#include "text_field_function_title_cell.h"
#include "list_controller.h"
#include <assert.h>
#include <algorithm>
namespace Graph {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
TextFieldFunctionTitleCell::TextFieldFunctionTitleCell(ListController * listController, Orientation orientation, const KDFont * font) :
Shared::FunctionTitleCell(orientation),
Responder(listController),
@@ -56,9 +54,9 @@ void TextFieldFunctionTitleCell::layoutSubviews(bool force) {
KDRect frame = subviewFrame();
m_textField.setFrame(frame, force);
KDCoordinate maxTextFieldX = frame.width() - m_textField.minimalSizeForOptimalDisplay().width();
float horizontalAlignment = maxFloat(
float horizontalAlignment = std::max(
0.0f,
minFloat(
std::min(
1.0f,
((float)(maxTextFieldX - k_textFieldRightMargin))/((float)maxTextFieldX)));
m_textField.setAlignment(horizontalAlignment, verticalAlignment());

View File

@@ -17,12 +17,12 @@ public:
void viewWillAppear() override;
void viewDidDisappear() override;
virtual int numberOfRows() const override;
virtual int numberOfColumns() const override;
virtual KDCoordinate cellHeight() override;
virtual KDCoordinate cellWidth() override;
virtual HighlightCell * reusableCell(int index) override;
virtual int reusableCellCount() const override;
int numberOfRows() const override;
int numberOfColumns() const override;
KDCoordinate cellHeight() override;
KDCoordinate cellWidth() override;
HighlightCell * reusableCell(int index) override;
int reusableCellCount() const override;
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
private:

View File

@@ -19,9 +19,13 @@ parser.add_argument('--implementation', help='the .cpp file to generate')
parser.add_argument('--locales', nargs='+', help='locale to actually generate')
parser.add_argument('--codepoints', help='the code_points.h file')
parser.add_argument('--files', nargs='+', help='an i18n file')
parser.add_argument('--generateISO6391locales', type=int, nargs='+', help='whether to generate the ISO6391 codes for the languages (for instance "en" for english)')
args = parser.parse_args()
def generate_ISO6391():
return args.generateISO6391locales[0] == 1
def has_glyph(glyph):
return glyph in codepoints
@@ -143,6 +147,14 @@ def print_header(data, path, locales):
for locale in locales:
f.write(" Message::Language" + locale.upper() + ",\n")
f.write("};\n\n")
if generate_ISO6391():
# Language ISO639-1 codes
f.write("constexpr const Message LanguageISO6391Names[NumberOfLanguages] = {\n");
for locale in locales:
f.write(" Message::LanguageISO6391" + locale.upper() + ",\n")
f.write("};\n\n")
f.write("}\n\n")
f.write("#endif\n")
f.close()

View File

@@ -0,0 +1 @@
LanguageISO6391DE = "de"

View File

@@ -0,0 +1 @@
LanguageISO6391EN = "en"

View File

@@ -0,0 +1 @@
LanguageISO6391ES = "es"

View File

@@ -0,0 +1 @@
LanguageISO6391FR = "fr"

View File

@@ -0,0 +1 @@
LanguageISO6391HU = "hu"

View File

@@ -0,0 +1 @@
LanguageISO6391PT = "pt"

View File

@@ -31,11 +31,13 @@ void ion_main(int argc, const char * const argv[]) {
* $ ./epsilon.elf --language fr
*/
if (strcmp(argv[i], "--language") == 0 && argc > i+1) {
const char * languageIdentifiers[] = {"none", "en", "fr", "es", "de", "pt"};
const char * requestedLanguageId = argv[i+1];
for (int i=0; i<sizeof(languageIdentifiers)/sizeof(languageIdentifiers[0]); i++) {
if (strcmp(requestedLanguageId, languageIdentifiers[i]) == 0) {
GlobalPreferences::sharedGlobalPreferences()->setLanguage((I18n::Language)i);
if (strcmp(requestedLanguageId, "none") == 0) {
continue;
}
for (int j = 0; j < I18n::NumberOfLanguages; j++) {
if (strcmp(requestedLanguageId, I18n::translate(I18n::LanguageISO6391Names[j])) == 0) {
GlobalPreferences::sharedGlobalPreferences()->setLanguage((I18n::Language)j);
break;
}
}

View File

@@ -2,12 +2,10 @@
#include "responder_image_cell.h"
#include <apps/i18n.h>
#include <assert.h>
#include <algorithm>
namespace Probability {
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
CalculationCell::CalculationCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate) :
m_text(KDFont::LargeFont, I18n::Message::Default, 0.5f, 0.5f),
m_calculation(parentResponder, inputEventHandlerDelegate, textFieldDelegate),
@@ -78,7 +76,7 @@ KDCoordinate CalculationCell::calculationCellWidth() const {
KDCoordinate glyphWidth = KDFont::LargeFont->glyphSize().width();
KDCoordinate minTextFieldWidth = 4 * glyphWidth + TextCursorView::k_width;
KDCoordinate maxTextFieldWidth = 14 * glyphWidth + TextCursorView::k_width;
return minCoordinate(maxTextFieldWidth, maxCoordinate(minTextFieldWidth, calculationCellWidth));
return std::min(maxTextFieldWidth, std::max(minTextFieldWidth, calculationCellWidth));
}
}

View File

@@ -15,6 +15,7 @@
#include "images/focused_calcul4_icon.h"
#include <poincare/preferences.h>
#include <assert.h>
#include <algorithm>
#include <cmath>
using namespace Poincare;
@@ -22,7 +23,7 @@ using namespace Shared;
namespace Probability {
static inline int minInt(int x, int y) { return x < y ? x : y; }
constexpr int CalculationController::k_titleBufferSize;
CalculationController::ContentView::ContentView(SelectableTableView * selectableTableView, Distribution * distribution, Calculation * calculation) :
m_titleView(KDFont::SmallFont, I18n::Message::ComputeProbability, 0.5f, 0.5f, Palette::SecondaryText, Palette::BackgroundApps),
@@ -293,7 +294,7 @@ void CalculationController::updateTitle() {
}
currentChar += UTF8Decoder::CodePointToChars(' ', m_titleBuffer + currentChar, k_titleBufferSize - currentChar);
}
m_titleBuffer[minInt(currentChar, k_titleBufferSize) - 1] = 0;
m_titleBuffer[std::min(currentChar, k_titleBufferSize) - 1] = 0;
}
}

View File

@@ -66,7 +66,7 @@ double BinomialDistribution::cumulativeDistributiveInverseForProbability(double
}
double BinomialDistribution::rightIntegralInverseForProbability(double * probability) {
if (m_parameter1 == 0.0 && (m_parameter2 == 0.0 || m_parameter2 == 1.0)) {
if (m_parameter1 == 0.0f && (m_parameter2 == 0.0f || m_parameter2 == 1.0f)) {
return NAN;
}
if (*probability <= 0.0) {

View File

@@ -1,11 +1,10 @@
#include "chi_squared_distribution.h"
#include "regularized_gamma.h"
#include <cmath>
#include <algorithm>
namespace Probability {
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
float ChiSquaredDistribution::xMin() const {
return -k_displayLeftMarginRatio * xMax();
}
@@ -44,7 +43,7 @@ double ChiSquaredDistribution::cumulativeDistributiveFunctionAtAbscissa(double x
return 0.0;
}
double result = 0.0;
if (regularizedGamma(m_parameter1/2.0, x/2.0, k_regularizedGammaPrecision, k_maxRegularizedGammaIterations, &result)) {
if (regularizedGamma(m_parameter1/2.0f, x/2.0, k_regularizedGammaPrecision, k_maxRegularizedGammaIterations, &result)) {
return result;
}
return NAN;
@@ -79,7 +78,7 @@ double ChiSquaredDistribution::cumulativeDistributiveInverseForProbability(doubl
2.0 * *probability * std::exp(std::lgamma(ceilKOver2)) / (exp(-kOver2Minus1) * std::pow(kOver2Minus1, kOver2Minus1)) :
30.0; // Ad hoc value
xmax = std::isnan(xmax) ? 1000000000.0 : xmax;
return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, FLT_EPSILON, maxDouble(xMax(), xmax));
return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, FLT_EPSILON, std::max<double>(xMax(), xmax));
}
}

View File

@@ -3,11 +3,10 @@
#include <poincare/regularized_incomplete_beta_function.h>
#include <cmath>
#include <float.h>
#include <algorithm>
namespace Probability {
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
float FisherDistribution::xMin() const {
return -k_displayLeftMarginRatio * xMax();
}
@@ -74,7 +73,7 @@ double FisherDistribution::cumulativeDistributiveInverseForProbability(double *
if (*probability < DBL_EPSILON) {
return 0.0;
}
return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, DBL_EPSILON, maxDouble(xMax(), 100.0)); // Ad-hoc value;
return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, DBL_EPSILON, std::max<double>(xMax(), 100.0)); // Ad-hoc value;
}
float FisherDistribution::mode() const {

View File

@@ -23,7 +23,7 @@ float StudentDistribution::evaluateAtAbscissa(float x) const {
}
bool StudentDistribution::authorizedValueAtIndex(float x, int index) const {
return x >= FLT_EPSILON && x <= 200.0; // We cannot draw the curve for x > 200 (coefficient() is too small)
return x >= FLT_EPSILON && x <= 200.0f; // We cannot draw the curve for x > 200 (coefficient() is too small)
}
double StudentDistribution::cumulativeDistributiveFunctionAtAbscissa(double x) const {
@@ -55,7 +55,7 @@ double StudentDistribution::cumulativeDistributiveInverseForProbability(double *
float StudentDistribution::lnCoefficient() const {
const float k = m_parameter1;
return std::lgamma((k+1.0f)/2.0f) - std::lgamma(k/2.0f) - (M_PI+k)/2.0f;
return std::lgamma((k+1.0f)/2.0f) - std::lgamma(k/2.0f) - ((float)M_PI+k)/2.0f;
}
}

View File

@@ -4,7 +4,7 @@
#include <poincare/code_point_layout.h>
#include <poincare/vertical_offset_layout.h>
#include <poincare/preferences.h>
#include <algorithm>
#include <assert.h>
using namespace Poincare;
@@ -12,8 +12,6 @@ using namespace Shared;
namespace Regression {
static inline int maxInt(int x, int y) { return x > y ? x : y; }
CalculationController::CalculationController(Responder * parentResponder, ButtonRowController * header, Store * store) :
TabTableController(parentResponder),
ButtonRowDelegate(header, nullptr),
@@ -375,7 +373,7 @@ int CalculationController::maxNumberOfCoefficients() const {
int numberOfDefinedSeries = m_store->numberOfNonEmptySeries();
for (int i = 0; i < numberOfDefinedSeries; i++) {
int currentNumberOfCoefs = m_store->modelForSeries(m_store->indexOfKthNonEmptySeries(i))->numberOfCoefficients();
maxNumberCoefficients = maxInt(maxNumberCoefficients, currentNumberOfCoefs);
maxNumberCoefficients = std::max(maxNumberCoefficients, currentNumberOfCoefs);
}
return maxNumberCoefficients;
}

View File

@@ -4,14 +4,11 @@
#include "../apps_container.h"
#include <poincare/preferences.h>
#include <cmath>
#include <algorithm>
using namespace Poincare;
using namespace Shared;
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
static inline int maxInt(int x, int y) { return x > y ? x : y; }
namespace Regression {
GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) :
@@ -165,7 +162,7 @@ void GraphController::reloadBannerView() {
}
if (!coefficientsAreDefined) {
// Force the "Data not suitable" message to be on the next line
int numberOfCharToCompleteLine = maxInt(Ion::Display::Width / BannerView::Font()->glyphSize().width() - strlen(I18n::translate(formula)), 0);
int numberOfCharToCompleteLine = std::max<int>(Ion::Display::Width / BannerView::Font()->glyphSize().width() - strlen(I18n::translate(formula)), 0);
numberOfChar = 0;
// Padding
Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, numberOfCharToCompleteLine - 1);
@@ -390,8 +387,8 @@ InteractiveCurveViewRangeDelegate::Range GraphController::computeYRange(Interact
for (int series = 0; series < Store::k_numberOfSeries; series++) {
for (int k = 0; k < m_store->numberOfPairsOfSeries(series); k++) {
if (m_store->xMin() <= m_store->get(series, 0, k) && m_store->get(series, 0, k) <= m_store->xMax()) {
minY = minFloat(minY, m_store->get(series, 1, k));
maxY = maxFloat(maxY, m_store->get(series, 1, k));
minY = std::min<float>(minY, m_store->get(series, 1, k));
maxY = std::max<float>(maxY, m_store->get(series, 1, k));
}
}
}

View File

@@ -19,31 +19,29 @@ namespace Regression {
Layout CubicModel::layout() {
if (m_layout.isUninitialized()) {
constexpr int size = 15;
Layout layoutChildren[size] = {
m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('3', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('3', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('2', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('2', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('c', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('d', k_layoutFont),
};
m_layout = HorizontalLayout::Builder(layoutChildren, size);
});
}
return m_layout;
}
@@ -82,25 +80,28 @@ Expression CubicModel::expression(double * modelCoefficients) {
double b = modelCoefficients[1];
double c = modelCoefficients[2];
double d = modelCoefficients[3];
Expression addChildren[] = {
Multiplication::Builder(
// a*x^3+b*x^2+c*x+d
return Addition::Builder({
Multiplication::Builder({
Number::DecimalNumber(a),
Power::Builder(
Symbol::Builder('x'),
Decimal::Builder(3.0))),
Multiplication::Builder(
Decimal::Builder(3.0)
)
}),
Multiplication::Builder({
Number::DecimalNumber(b),
Power::Builder(
Symbol::Builder('x'),
Decimal::Builder(2.0))),
Multiplication::Builder(
Decimal::Builder(2.0)
)
}),
Multiplication::Builder({
Number::DecimalNumber(c),
Symbol::Builder('x')),
Symbol::Builder('x')
}),
Number::DecimalNumber(d)
};
// a*x^3+b*x^2+c*x+d
Expression result = Addition::Builder(addChildren, 4);
return result;
});
}
}

View File

@@ -13,21 +13,19 @@ namespace Regression {
Layout ExponentialModel::layout() {
if (m_layout.isUninitialized()) {
constexpr int size = 4;
Layout layoutChildren[size] = {
m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('e', k_layoutFont),
VerticalOffsetLayout::Builder(
HorizontalLayout::Builder(
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont)
),
VerticalOffsetLayoutNode::Position::Superscript
)
};
m_layout = HorizontalLayout::Builder(layoutChildren, size);
HorizontalLayout::Builder({
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont)
}),
VerticalOffsetLayoutNode::Position::Superscript
)
});
}
return m_layout;
}

View File

@@ -16,7 +16,7 @@ public:
int numberOfCoefficients() const override { return 2; }
int bannerLinesCount() const override { return 2; }
protected:
virtual bool dataSuitableForFit(Store * store, int series) const override;
bool dataSuitableForFit(Store * store, int series) const override;
};
}

View File

@@ -13,29 +13,25 @@ namespace Regression {
Layout LogisticModel::layout() {
if (m_layout.isUninitialized()) {
constexpr int exponentSize = 4;
Layout exponentLayoutChildren[exponentSize] = {
CodePointLayout::Builder('-', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont)
};
constexpr int denominatorSize = 6;
Layout layoutChildren[denominatorSize] = {
CodePointLayout::Builder('1', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('e', k_layoutFont),
VerticalOffsetLayout::Builder(
HorizontalLayout::Builder(exponentLayoutChildren, exponentSize),
m_layout = FractionLayout::Builder(
CodePointLayout::Builder('c', k_layoutFont),
HorizontalLayout::Builder({
CodePointLayout::Builder('1', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('e', k_layoutFont),
VerticalOffsetLayout::Builder(
HorizontalLayout::Builder({
CodePointLayout::Builder('-', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont)
}),
VerticalOffsetLayoutNode::Position::Superscript
)
};
m_layout = FractionLayout::Builder(
CodePointLayout::Builder('c', k_layoutFont),
HorizontalLayout::Builder(layoutChildren, denominatorSize)
);
})
);
}
return m_layout;
}

View File

@@ -12,17 +12,15 @@ namespace Regression {
Layout PowerModel::layout() {
if (m_layout.isUninitialized()) {
constexpr int size = 4;
Layout layoutChildren[size] = {
m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('b', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
};
m_layout = HorizontalLayout::Builder(layoutChildren, size);
CodePointLayout::Builder('b', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
});
}
return m_layout;
}

View File

@@ -19,23 +19,21 @@ namespace Regression {
Layout QuadraticModel::layout() {
if (m_layout.isUninitialized()) {
constexpr int size = 10;
Layout layoutChildren[size] = {
m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('2', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('2', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('c', k_layoutFont),
};
m_layout = HorizontalLayout::Builder(layoutChildren, size);
});
}
return m_layout;
}
@@ -69,19 +67,20 @@ Expression QuadraticModel::expression(double * modelCoefficients) {
double b = modelCoefficients[1];
double c = modelCoefficients[2];
// a*x^2+b*x+c
Expression addChildren[] = {
Multiplication::Builder(
return Addition::Builder({
Multiplication::Builder({
Number::DecimalNumber(a),
Power::Builder(
Symbol::Builder('x'),
Decimal::Builder(2.0))),
Multiplication::Builder(
Decimal::Builder(2.0)
)
}),
Multiplication::Builder({
Number::DecimalNumber(b),
Symbol::Builder('x')),
Symbol::Builder('x')
}),
Number::DecimalNumber(c)
};
Expression result = Addition::Builder(addChildren, 3);
return result;
});
}
}

View File

@@ -19,39 +19,37 @@ namespace Regression {
Layout QuarticModel::layout() {
if (m_layout.isUninitialized()) {
constexpr int size = 20;
Layout layoutChildren[size] = {
m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('4', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('4', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('3', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('3', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('c', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
CodePointLayout::Builder('2', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('2', k_layoutFont),
VerticalOffsetLayoutNode::Position::Superscript
),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('d', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('e', k_layoutFont),
};
m_layout = HorizontalLayout::Builder(layoutChildren, size);
});
}
return m_layout;
}
@@ -96,34 +94,35 @@ Expression QuarticModel::expression(double * modelCoefficients) {
double c = modelCoefficients[2];
double d = modelCoefficients[3];
double e = modelCoefficients[4];
Expression addChildren[] = {
// a*x^4
Multiplication::Builder(
// a*x^4+b*x^3+c*x^2+d*x+e
return Addition::Builder({
Multiplication::Builder({
Number::DecimalNumber(a),
Power::Builder(
Symbol::Builder('x'),
Decimal::Builder(4.0))),
// b*x^3
Multiplication::Builder(
Decimal::Builder(4.0)
)
}),
Multiplication::Builder({
Number::DecimalNumber(b),
Power::Builder(
Symbol::Builder('x'),
Decimal::Builder(3.0))),
// c*x^2
Multiplication::Builder(
Decimal::Builder(3.0)
)
}),
Multiplication::Builder({
Number::DecimalNumber(c),
Power::Builder(
Symbol::Builder('x'),
Decimal::Builder(2.0))),
// d*x
Multiplication::Builder(
Decimal::Builder(2.0)
)
}),
Multiplication::Builder({
Number::DecimalNumber(d),
Symbol::Builder('x')),
// e
Symbol::Builder('x')
}),
Number::DecimalNumber(e)
};
Expression result = Addition::Builder(addChildren, 5);
return result;
});
}
}

View File

@@ -5,14 +5,12 @@
#include <float.h>
#include <cmath>
#include <string.h>
#include <algorithm>
using namespace Shared;
namespace Regression {
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static_assert(Model::k_numberOfModels == 9, "Number of models changed, Regression::Store() needs to adapt");
static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regression::Store() needs to adapt (m_seriesChecksum)");
@@ -146,8 +144,8 @@ void Store::setDefault() {
float maxX = -FLT_MAX;
for (int series = 0; series < k_numberOfSeries; series++) {
if (!seriesIsEmpty(series)) {
minX = minFloat(minX, minValueOfColumn(series, 0));
maxX = maxFloat(maxX, maxValueOfColumn(series, 0));
minX = std::min(minX, minValueOfColumn(series, 0));
maxX = std::max(maxX, maxValueOfColumn(series, 0));
}
}
float range = maxX - minX;
@@ -199,7 +197,7 @@ void Store::resetMemoization() {
float Store::maxValueOfColumn(int series, int i) const {
float maxColumn = -FLT_MAX;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
maxColumn = maxFloat(maxColumn, m_data[series][i][k]);
maxColumn = std::max<float>(maxColumn, m_data[series][i][k]);
}
return maxColumn;
}
@@ -207,7 +205,7 @@ float Store::maxValueOfColumn(int series, int i) const {
float Store::minValueOfColumn(int series, int i) const {
float minColumn = FLT_MAX;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
minColumn = minFloat(minColumn, m_data[series][i][k]);
minColumn = std::min<float>(minColumn, m_data[series][i][k]);
}
return minColumn;
}

View File

@@ -2,14 +2,13 @@
#include <cmath>
#include <ion.h>
#include <poincare/preferences.h>
#include <algorithm>
using namespace Shared;
using namespace Poincare;
namespace Sequence {
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
CurveViewRange::CurveViewRange(InteractiveCurveViewRangeDelegate * delegate) :
InteractiveCurveViewRange(delegate)
{
@@ -37,7 +36,7 @@ void CurveViewRange::normalize() {
float xMean = xCenter();
float yMean = yCenter();
const float unit = maxFloat(xGridUnit(), yGridUnit());
const float unit = std::max(xGridUnit(), yGridUnit());
// Compute the X
const float newXHalfRange = NormalizedXHalfRange(unit);

View File

@@ -2,15 +2,13 @@
#include <cmath>
#include <limits.h>
#include "../app.h"
#include <algorithm>
using namespace Shared;
using namespace Poincare;
namespace Sequence {
static inline int minInt(int x, int y) { return (x < y ? x : y); }
static inline int maxInt(int x, int y) { return (x > y ? x : y); }
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
@@ -39,7 +37,7 @@ float GraphController::interestingXMin() const {
int nbOfActiveModels = functionStore()->numberOfActiveFunctions();
for (int i = 0; i < nbOfActiveModels; i++) {
Sequence * s = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
nmin = minInt(nmin, s->initialRank());
nmin = std::min(nmin, s->initialRank());
}
assert(nmin < INT_MAX);
return nmin;
@@ -53,8 +51,8 @@ float GraphController::interestingXHalfRange() const {
for (int i = 0; i < nbOfActiveModels; i++) {
Sequence * s = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
int firstInterestingIndex = s->initialRank();
nmin = minInt(nmin, firstInterestingIndex);
nmax = maxInt(nmax, firstInterestingIndex + standardRange);
nmin = std::min(nmin, firstInterestingIndex);
nmax = std::max(nmax, firstInterestingIndex + static_cast<int>(standardRange));
}
assert(nmax - nmin >= standardRange);
return nmax - nmin;

View File

@@ -1,12 +1,11 @@
#include "list_controller.h"
#include "../app.h"
#include <assert.h>
#include <algorithm>
using namespace Shared;
using namespace Poincare;
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
namespace Sequence {
ListController::ListController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, ButtonRowController * footer) :
@@ -55,7 +54,7 @@ KDCoordinate ListController::expressionRowHeight(int j) {
return defaultHeight;
}
KDCoordinate sequenceHeight = layout.layoutSize().height();
return maxCoordinate(defaultHeight, sequenceHeight + 2*k_expressionCellVerticalMargin);
return std::max<KDCoordinate>(defaultHeight, sequenceHeight + 2*k_expressionCellVerticalMargin);
}
void ListController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {

View File

@@ -7,7 +7,7 @@ EditionLinear = "Linear "
Edition2D = "Natürlich "
ComplexFormat = "Komplex"
ExamMode = "Prüfungsmodus"
ExamModeActive = "Wieder starten Modus"
ExamModeActive = "Modus erneut starten"
ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten,"
ToDeactivateExamMode2 = "schließen Sie den Rechner an einen"
ToDeactivateExamMode3 = "Computer oder eine Steckdose an."

View File

@@ -7,13 +7,12 @@
#include <poincare/code_point_layout.h>
#include <poincare/fraction_layout.h>
#include <poincare/vertical_offset_layout.h>
#include <algorithm>
using namespace Poincare;
namespace Settings {
static inline int maxInt(int x, int y) { return x > y ? x : y; }
PreferencesController::PreferencesController(Responder * parentResponder) :
GenericSubController(parentResponder)
{
@@ -233,7 +232,7 @@ void PreferencesController::setPreferenceWithValueIndex(I18n::Message message, i
/* In Engineering mode, the number of significant digits cannot be lower
* than 3, because we need to be able to display 100 for instance. */
// TODO: Add warning about signifiant digits change ?
preferences->setNumberOfSignificantDigits(maxInt(preferences->numberOfSignificantDigits(), 3));
preferences->setNumberOfSignificantDigits(std::max<int>(preferences->numberOfSignificantDigits(), 3));
}
} else if (message == I18n::Message::EditionMode) {
preferences->setEditionMode((Preferences::EditionMode)valueIndex);

View File

@@ -1,13 +1,12 @@
#include "selectable_view_with_messages.h"
#include <apps/i18n.h>
#include <assert.h>
#include <algorithm>
using namespace Shared;
namespace Settings {
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
SelectableViewWithMessages::SelectableViewWithMessages(SelectableTableView * selectableTableView) :
m_selectableTableView(selectableTableView),
m_numberOfMessages(0)
@@ -52,7 +51,7 @@ void SelectableViewWithMessages::layoutSubviews(bool force) {
// Layout the text
KDCoordinate textHeight = KDFont::SmallFont->glyphSize().height();
KDCoordinate defOrigin = maxCoordinate(bounds().height() - Metric::CommonBottomMargin - m_numberOfMessages*textHeight, tableHeight);
KDCoordinate defOrigin = std::max<KDCoordinate>(bounds().height() - Metric::CommonBottomMargin - m_numberOfMessages*textHeight, tableHeight);
for (int i = 0; i < m_numberOfMessages; i++) {
m_messageLines[i].setFrame(KDRect(0, defOrigin, bounds().width(), textHeight), force);

View File

@@ -14,14 +14,12 @@
#include <apps/i18n.h>
#include <float.h>
#include <cmath>
#include <algorithm>
using namespace Poincare;
namespace Shared {
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
static inline double minDouble(double x, double y) { return x < y ? x : y; }
void ContinuousFunction::DefaultName(char buffer[], size_t bufferSize) {
constexpr int k_maxNumberOfDefaultLetterNames = 4;
static constexpr const char k_defaultLetterNames[k_maxNumberOfDefaultLetterNames] = {
@@ -312,14 +310,14 @@ Coordinate2D<double> ContinuousFunction::nextIntersectionFrom(double start, doub
constexpr int bufferSize = CodePoint::MaxCodePointCharLength + 1;
char unknownX[bufferSize];
SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknown);
double domainMin = maxDouble(tMin(), eDomainMin);
double domainMax = minDouble(tMax(), eDomainMax);
double domainMin = std::max<double>(tMin(), eDomainMin);
double domainMax = std::min<double>(tMax(), eDomainMax);
if (step > 0.0f) {
start = maxDouble(start, domainMin);
max = minDouble(max, domainMax);
start = std::max(start, domainMin);
max = std::min(max, domainMax);
} else {
start = minDouble(start, domainMax);
max = maxDouble(max, domainMin);
start = std::min(start, domainMax);
max = std::max(max, domainMin);
}
return PoincareHelpers::NextIntersection(expressionReduced(context), unknownX, start, step, max, context, e);
}
@@ -330,19 +328,19 @@ Coordinate2D<double> ContinuousFunction::nextPointOfInterestFrom(double start, d
char unknownX[bufferSize];
SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknown);
if (step > 0.0f) {
start = maxDouble(start, tMin());
max = minDouble(max, tMax());
start = std::max<double>(start, tMin());
max = std::min<double>(max, tMax());
} else {
start = minDouble(start, tMax());
max = maxDouble(max, tMin());
start = std::min<double>(start, tMax());
max = std::max<double>(max, tMin());
}
return compute(expressionReduced(context), unknownX, start, step, max, context);
}
Poincare::Expression ContinuousFunction::sumBetweenBounds(double start, double end, Poincare::Context * context) const {
assert(plotType() == PlotType::Cartesian);
start = maxDouble(start, tMin());
end = minDouble(end, tMax());
start = std::max<double>(start, tMin());
end = std::min<double>(end, tMax());
return Poincare::Integral::Builder(expressionReduced(context).clone(), Poincare::Symbol::Builder(UCodePointUnknown), Poincare::Float<double>::Builder(start), Poincare::Float<double>::Builder(end)); // Integral takes ownership of args
/* TODO: when we approximate integral, we might want to simplify the integral
* here. However, we might want to do it once for all x (to avoid lagging in

View File

@@ -4,6 +4,7 @@
#include <poincare/print_float.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <float.h>
@@ -11,11 +12,6 @@ using namespace Poincare;
namespace Shared {
static inline int minInt(int x, int y) { return x < y ? x : y; }
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView,
CursorView * cursorView, View * okView, bool displayBanner) :
View(),
@@ -182,8 +178,8 @@ void CurveView::computeLabels(Axis axis) {
* them from overprinting one another.*/
int labelMaxGlyphLength = labelMaxGlyphLengthSize();
if (axis == Axis::Horizontal) {
float pixelsPerLabel = maxFloat(0.0f, ((float)Ion::Display::Width)/((float)axisLabelsCount) - k_labelMargin);
labelMaxGlyphLength = minInt(labelMaxGlyphLengthSize(), pixelsPerLabel/k_font->glyphSize().width());
float pixelsPerLabel = std::max(0.0f, ((float)Ion::Display::Width)/((float)axisLabelsCount) - k_labelMargin);
labelMaxGlyphLength = std::min<int>(labelMaxGlyphLengthSize(), pixelsPerLabel/k_font->glyphSize().width());
}
if (labelValue < step && labelValue > -step) {
@@ -590,7 +586,7 @@ void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd
x = xy.x1();
y = xy.x2();
if (colorUnderCurve && !std::isnan(x) && colorLowerBound < x && x < colorUpperBound && !(std::isnan(y) || std::isinf(y))) {
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, x, minFloat(0.0f, y), maxFloat(0.0f, y), color, 1);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, x, std::min(0.0f, y), std::max(0.0f, y), color, 1);
}
joinDots(ctx, rect, xyEvaluation, model, context, drawStraightLinesEarly, previousT, previousX, previousY, t, x, y, color, thick, k_maxNumberOfIterations);
} while (true);
@@ -599,8 +595,8 @@ void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd
void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForParameter xyEvaluation, void * model, void * context, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound) const {
float rectLeft = pixelToFloat(Axis::Horizontal, rect.left() - k_externRectMargin);
float rectRight = pixelToFloat(Axis::Horizontal, rect.right() + k_externRectMargin);
float tStart = std::isnan(rectLeft) ? xMin : maxFloat(xMin, rectLeft);
float tEnd = std::isnan(rectRight) ? xMax : minFloat(xMax, rectRight);
float tStart = std::isnan(rectLeft) ? xMin : std::max(xMin, rectLeft);
float tEnd = std::isnan(rectRight) ? xMax : std::min(xMax, rectRight);
assert(!std::isnan(tStart) && !std::isnan(tEnd));
if (std::isinf(tStart) || std::isinf(tEnd) || tStart > tEnd) {
return;
@@ -701,8 +697,8 @@ static void clipBarycentricCoordinatesBetweenBounds(float & start, float & end,
end = 0;
}
} else {
start = maxFloat(start, (bounds[(p1f > p2f) ? lower : upper] - p2f)/(p1f-p2f));
end = minFloat( end , (bounds[(p1f > p2f) ? upper : lower] - p2f)/(p1f-p2f));
start = std::max(start, (bounds[(p1f > p2f) ? lower : upper] - p2f)/(p1f-p2f));
end = std::min( end , (bounds[(p1f > p2f) ? upper : lower] - p2f)/(p1f-p2f));
}
}

View File

@@ -3,11 +3,10 @@
#include "../constant.h"
#include <assert.h>
#include <cmath>
#include <algorithm>
using namespace Poincare;
static inline int maxInt(int x, int y) { return x > y ? x : y; }
namespace Shared {
EditableCellTableViewController::EditableCellTableViewController(Responder * parentResponder) :
@@ -60,7 +59,7 @@ bool EditableCellTableViewController::textFieldDidFinishEditing(TextField * text
int EditableCellTableViewController::numberOfRows() const {
int numberOfModelElements = 0;
for (int i = 0; i < numberOfColumns(); i++) {
numberOfModelElements = maxInt(numberOfModelElements, numberOfElementsInColumn(i));
numberOfModelElements = std::max(numberOfModelElements, numberOfElementsInColumn(i));
}
return 1 + numberOfModelElements + (numberOfModelElements < maxNumberOfElements());
}

View File

@@ -11,31 +11,31 @@ class ExpiringPointer {
friend class ExpiringPointer;
public:
ExpiringPointer(T * rawPointer) : m_rawPointer(rawPointer) {
#if DEBUG
#ifndef NDEBUG
s_global = rawPointer;
#endif
}
T * pointer() { return m_rawPointer; }
T *operator->() {
#if DEBUG
#ifndef NDEBUG
assert(m_rawPointer != nullptr && m_rawPointer == s_global);
#endif
return m_rawPointer;
}
T &operator*() {
#if DEBUG
#ifndef NDEBUG
assert(m_rawPointer != nullptr && m_rawPointer == s_global);
#endif
return *m_rawPointer;
}
private:
#if DEBUG
#ifndef NDEBUG
static T * s_global;
#endif
T * m_rawPointer;
};
#if DEBUG
#ifndef NDEBUG
template<class T>
T * ExpiringPointer<T>::s_global = nullptr;
#endif

View File

@@ -10,7 +10,7 @@ class ExpressionFieldDelegateApp : public TextFieldDelegateApp, public LayoutFie
public:
virtual ~ExpressionFieldDelegateApp() = default;
bool layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) override;
virtual bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
protected:
ExpressionFieldDelegateApp(Snapshot * snapshot, ViewController * rootViewController);
};

View File

@@ -7,14 +7,13 @@
#include <string.h>
#include <cmath>
#include <assert.h>
#include <algorithm>
using namespace Ion;
using namespace Poincare;
namespace Shared {
static inline int maxInt(int x, int y) { return x > y ? x : y; }
ExpressionModel::ExpressionModel() :
m_expression(),
m_layout(),
@@ -126,7 +125,7 @@ Ion::Storage::Record::ErrorStatus ExpressionModel::setExpressionContent(Ion::Sto
size_t newDataSize = previousDataSize - previousExpressionSize + newExpressionSize;
void * expAddress = expressionAddress(record);
// Update size of record to maximal size between previous and new data
newData.size = maxInt(previousDataSize, newDataSize);
newData.size = std::max(previousDataSize, newDataSize);
Ion::Storage::Record::ErrorStatus error = record->setValue(newData);
if (error != Ion::Storage::Record::ErrorStatus::None) {
assert(error == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable);

View File

@@ -1,11 +1,10 @@
#include "expression_model_list_controller.h"
#include <apps/constant.h>
#include <poincare/symbol.h>
#include <algorithm>
namespace Shared {
static inline int minInt(int x, int y) { return x < y ? x : y; }
/* Table Data Source */
ExpressionModelListController::ExpressionModelListController(Responder * parentResponder, I18n::Message text) :
@@ -121,7 +120,7 @@ int ExpressionModelListController::memoizedIndexFromCumulatedHeight(KDCoordinate
KDCoordinate currentCumulatedHeight = memoizedCumulatedHeightFromIndex(currentSelectedRow);
if (offsetY > currentCumulatedHeight) {
int iMax = minInt(k_memoizedCellsCount/2 + 1, rowsCount - currentSelectedRow);
int iMax = std::min(k_memoizedCellsCount/2 + 1, rowsCount - currentSelectedRow);
for (int i = 0; i < iMax; i++) {
currentCumulatedHeight+= memoizedRowHeight(currentSelectedRow + i);
if (offsetY <= currentCumulatedHeight) {
@@ -129,7 +128,7 @@ int ExpressionModelListController::memoizedIndexFromCumulatedHeight(KDCoordinate
}
}
} else {
int iMax = minInt(k_memoizedCellsCount/2, currentSelectedRow);
int iMax = std::min(k_memoizedCellsCount/2, currentSelectedRow);
for (int i = 1; i <= iMax; i++) {
currentCumulatedHeight-= memoizedRowHeight(currentSelectedRow-i);
if (offsetY > currentCumulatedHeight) {

View File

@@ -5,16 +5,12 @@
#include <assert.h>
#include <cmath>
#include <float.h>
#include <algorithm>
using namespace Poincare;
namespace Shared {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
static inline double minDouble(double x, double y) { return x < y ? x : y; }
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
FunctionGraphController::FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, interactiveRange, curveView, cursor, modelVersion, previousModelsVersions, rangeVersion),
m_initialisationParameterController(this, interactiveRange),
@@ -92,13 +88,13 @@ InteractiveCurveViewRangeDelegate::Range FunctionGraphController::computeYRange(
if (std::isnan(tMin)) {
tMin = xMin;
} else if (f->shouldClipTRangeToXRange()) {
tMin = maxFloat(tMin, xMin);
tMin = std::max<double>(tMin, xMin);
}
double tMax = f->tMax();
if (std::isnan(tMax)) {
tMax = xMax;
} else if (f->shouldClipTRangeToXRange()) {
tMax = minFloat(tMax, xMax);
tMax = std::min<double>(tMax, xMax);
}
/* In practice, a step smaller than a pixel's width is needed for sampling
* the values of a function. Otherwise some relevant extremal values may be
@@ -113,8 +109,8 @@ InteractiveCurveViewRangeDelegate::Range FunctionGraphController::computeYRange(
if (!std::isnan(x) && !std::isinf(x) && x >= xMin && x <= xMax) {
float y = xy.x2();
if (!std::isnan(y) && !std::isinf(y)) {
min = minFloat(min, y);
max = maxFloat(max, y);
min = std::min(min, y);
max = std::max(max, y);
}
}
}
@@ -167,7 +163,7 @@ bool FunctionGraphController::moveCursorVertically(int direction) {
double clippedT = m_cursor->t();
if (!std::isnan(f->tMin())) {
assert(!std::isnan(f->tMax()));
clippedT = minDouble(f->tMax(), maxDouble(f->tMin(), clippedT));
clippedT = std::min<double>(f->tMax(), std::max<double>(f->tMin(), clippedT));
}
Poincare::Coordinate2D<double> cursorPosition = f->evaluateXYAtParameter(clippedT, context);
m_cursor->moveTo(clippedT, cursorPosition.x1(), cursorPosition.x2());

View File

@@ -69,8 +69,8 @@ void FunctionGraphView::reloadBetweenBounds(float start, float end) {
if (start == end) {
return;
}
float pixelLowerBound = floatToPixel(Axis::Horizontal, start)-2.0;
float pixelUpperBound = floatToPixel(Axis::Horizontal, end)+4.0;
float pixelLowerBound = floatToPixel(Axis::Horizontal, start) - 2.0f;
float pixelUpperBound = floatToPixel(Axis::Horizontal, end) + 4.0f;
/* We exclude the banner frame from the dirty zone to avoid unnecessary
* redrawing */
KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound,

View File

@@ -1,10 +1,11 @@
#include "function_list_controller.h"
#include "function_app.h"
#include "function_expression_cell.h"
#include <algorithm>
namespace Shared {
static inline int maxInt(int x, int y) { return x > y ? x : y; }
constexpr KDCoordinate FunctionListController::k_minTitleColumnWidth;
FunctionListController::FunctionListController(Responder * parentResponder, ButtonRowController * header, ButtonRowController * footer, I18n::Message text) :
ExpressionModelListController(parentResponder, text),
@@ -243,7 +244,7 @@ void FunctionListController::computeTitlesColumnWidth(bool forceMax) {
return;
}
KDCoordinate maxTitleWidth = maxFunctionNameWidth()+k_functionTitleSumOfMargins;
m_titlesColumnWidth = maxInt(maxTitleWidth, k_minTitleColumnWidth);
m_titlesColumnWidth = std::max(maxTitleWidth, k_minTitleColumnWidth);
}
TabViewController * FunctionListController::tabController() const {
@@ -262,7 +263,7 @@ KDCoordinate FunctionListController::maxFunctionNameWidth() {
const char * functionName = record.fullName();
const char * dotPosition = strchr(functionName, Ion::Storage::k_dotChar);
assert(dotPosition != nullptr);
maxNameLength = maxInt(maxNameLength, dotPosition-functionName);
maxNameLength = std::max(maxNameLength, static_cast<int>(dotPosition-functionName));
}
return nameWidth(maxNameLength + Function::k_parenthesedArgumentCodePointLength);
}

View File

@@ -1,11 +1,9 @@
#include "function_title_cell.h"
#include <assert.h>
#include <algorithm>
namespace Shared {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
void FunctionTitleCell::setOrientation(Orientation orientation) {
m_orientation = orientation;
reloadCell();
@@ -51,9 +49,9 @@ KDRect FunctionTitleCell::subviewFrame() const {
float FunctionTitleCell::verticalAlignment() const {
assert(m_orientation == Orientation::VerticalIndicator);
return maxFloat(
return std::max(
0.0f,
minFloat(
std::min(
1.0f,
m_baseline < 0 ? 0.5f : verticalAlignmentGivenExpressionBaselineAndRowHeight(m_baseline, subviewFrame().height())));
}

View File

@@ -5,13 +5,12 @@
#include <stddef.h>
#include <assert.h>
#include <poincare/preferences.h>
#include <algorithm>
using namespace Poincare;
namespace Shared {
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
uint32_t InteractiveCurveViewRange::rangeChecksum() {
float data[5] = {xMin(), xMax(), yMin(), yMax(), m_yAuto ? 1.0f : 0.0f};
size_t dataLengthInBytes = 5*sizeof(float);
@@ -94,7 +93,7 @@ void InteractiveCurveViewRange::normalize() {
* 1cm = 2 current units. */
m_yAuto = false;
const float unit = maxFloat(xGridUnit(), yGridUnit());
const float unit = std::max(xGridUnit(), yGridUnit());
// Set x range
float newXHalfRange = NormalizedXHalfRange(unit);
@@ -160,7 +159,7 @@ void InteractiveCurveViewRange::setDefault() {
yRange = yMax() - yMin();
float xyRatio = xRange/yRange;
const float unit = maxFloat(xGridUnit(), yGridUnit());
const float unit = std::max(xGridUnit(), yGridUnit());
const float newXHalfRange = NormalizedXHalfRange(unit);
const float newYHalfRange = NormalizedYHalfRange(unit);
float normalizedXYRatio = newXHalfRange/newYHalfRange;

View File

@@ -2,12 +2,10 @@
#include <assert.h>
#include <ion.h>
#include <poincare/ieee754.h>
#include <algorithm>
namespace Shared {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
void Range1D::setMin(float min, float lowerMaxFloat, float upperMaxFloat) {
min = clipped(min, false, lowerMaxFloat, upperMaxFloat);
if (std::isnan(min)) {
@@ -43,7 +41,7 @@ float Range1D::defaultRangeLengthFor(float position) {
float Range1D::clipped(float x, bool isMax, float lowerMaxFloat, float upperMaxFloat) {
float maxF = isMax ? upperMaxFloat : lowerMaxFloat;
float minF = isMax ? -lowerMaxFloat : -upperMaxFloat;
return maxFloat(minF, minFloat(x, maxF));
return std::max(minF, std::min(x, maxF));
}
}

View File

@@ -1,12 +1,11 @@
#include "scrollable_multiple_expressions_view.h"
#include <apps/i18n.h>
#include <assert.h>
#include <algorithm>
using namespace Poincare;
namespace Shared {
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
AbstractScrollableMultipleExpressionsView::ContentCell::ContentCell() :
m_rightExpressionView(),
m_approximateSign(KDFont::LargeFont, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyVeryDark),
@@ -75,7 +74,7 @@ KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOpt
centeredExpressionSize = m_centeredExpressionView.minimalSizeForOptimalDisplay();
width += centeredExpressionSize.width() + 2*Metric::CommonLargeMargin + m_approximateSign.minimalSizeForOptimalDisplay().width();
}
KDCoordinate height = maxCoordinate(maxCoordinate(centeredBaseline, rightBaseline), leftViewBaseline) + maxCoordinate(maxCoordinate(centeredExpressionSize.height()-centeredBaseline, rightExpressionSize.height()-rightBaseline), leftSize.height()-leftViewBaseline);
KDCoordinate height = std::max(std::max(centeredBaseline, rightBaseline), leftViewBaseline) + std::max(std::max(centeredExpressionSize.height()-centeredBaseline, rightExpressionSize.height()-rightBaseline), leftSize.height()-leftViewBaseline);
return KDSize(width, height);
}
@@ -134,7 +133,7 @@ void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool
KDSize rightExpressionSize = m_rightExpressionView.minimalSizeForOptimalDisplay();
KDCoordinate rightBaseline = m_rightExpressionView.layout().isUninitialized() ? 0 : m_rightExpressionView.layout().baseline();
// Compute baseline
KDCoordinate baseline = maxCoordinate(maxCoordinate(leftViewBaseline, rightBaseline), centeredBaseline);
KDCoordinate baseline = std::max(std::max(leftViewBaseline, rightBaseline), centeredBaseline);
// Layout left view
KDCoordinate currentWidth = 0;
if (leftExpressionView()) {

View File

@@ -4,11 +4,10 @@
#include "../constant.h"
#include <escher/metric.h>
#include <assert.h>
#include <algorithm>
using namespace Poincare;
static inline int minInt(int x, int y) { return x < y ? x : y; }
namespace Shared {
StoreController::ContentView::ContentView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate) :
@@ -245,7 +244,7 @@ bool StoreController::privateFillColumnWithFormula(Expression formula, Expressio
if (numberOfValuesToCompute == -1) {
numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series);
} else {
numberOfValuesToCompute = minInt(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series));
numberOfValuesToCompute = std::min(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series));
}
index++;
}

View File

@@ -17,7 +17,7 @@ public:
virtual bool XNTCanBeOverriden() const { return true; }
virtual CodePoint XNT() { return 'x'; }
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
virtual bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
bool isAcceptableText(const char * text);
template<typename T>
bool hasUndefinedValue(const char * text, T & value, bool enablePlusInfinity = false, bool enableMinusInfinity = false);

View File

@@ -3,12 +3,15 @@
#include <poincare/preferences.h>
#include <assert.h>
#include <limits.h>
#include <algorithm>
using namespace Poincare;
namespace Shared {
static inline int minInt(int x, int y) { return x < y ? x : y; }
constexpr int ValuesController::k_maxNumberOfDisplayableRows;
// TODO: use std::abs
static inline int absInt(int x) { return x < 0 ? -x : x; }
// Constructor and helpers
@@ -338,9 +341,9 @@ char * ValuesController::memoizedBufferForCell(int i, int j) {
}
// Compute the buffer of the new cells of the memoized table
int maxI = numberOfValuesColumns() - m_firstMemoizedColumn;
for (int ii = 0; ii < minInt(nbOfMemoizedColumns, maxI); ii++) {
for (int ii = 0; ii < std::min(nbOfMemoizedColumns, maxI); ii++) {
int maxJ = numberOfElementsInColumn(absoluteColumnForValuesColumn(ii+m_firstMemoizedColumn)) - m_firstMemoizedRow;
for (int jj = 0; jj < minInt(k_maxNumberOfDisplayableRows, maxJ); jj++) {
for (int jj = 0; jj < std::min(k_maxNumberOfDisplayableRows, maxJ); jj++) {
// Escape if already filled
if (ii >= -offsetI && ii < -offsetI + nbOfMemoizedColumns && jj >= -offsetJ && jj < -offsetJ + k_maxNumberOfDisplayableRows) {
continue;

View File

@@ -23,13 +23,13 @@ public:
TELEMETRY_ID("Values");
// Responder
virtual bool handleEvent(Ion::Events::Event event) override;
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;
void willExitResponderChain(Responder * nextFirstResponder) override;
// TableViewDataSource
int numberOfColumns() const override;
virtual void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;
int typeAtLocation(int i, int j) override;

View File

@@ -20,13 +20,13 @@ public:
const char * title() override;
void viewWillAppear() override;
void didBecomeFirstResponder() override;
virtual int numberOfRows() const override { return 1; }
int numberOfRows() const override { return 1; }
KDCoordinate cellHeight() override { return Metric::ParameterCellHeight; }
virtual HighlightCell * reusableCell(int index) override {
HighlightCell * reusableCell(int index) override {
assert(index == 0);
return &m_copyColumn;
}
virtual int reusableCellCount() const override { return 1; }
int reusableCellCount() const override { return 1; }
void setRecord(Ion::Storage::Record record) { m_record = record; }
protected:
MessageTableCellWithChevron m_copyColumn;

View File

@@ -28,7 +28,8 @@ i18n_files += $(addprefix apps/solver/,\
)
tests_src += $(addprefix apps/solver/test/,\
equation_store.cpp\
equation_store.cpp \
helpers.cpp \
)
$(eval $(call depends_on_image,apps/solver/app.cpp,apps/solver/solver_icon.png))

View File

@@ -10,14 +10,13 @@
#include <poincare/symbol_abstract.h>
#include <poincare/symbol.h>
#include <poincare/vertical_offset_layout.h>
#include <algorithm>
using namespace Poincare;
using namespace Shared;
namespace Solver {
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
constexpr KDColor SolutionsController::ContentView::k_backgroundColor;
SolutionsController::ContentView::ContentView(SolutionsController * controller) :
@@ -260,7 +259,7 @@ KDCoordinate SolutionsController::rowHeight(int j) {
Poincare::Layout approximateLayout = m_equationStore->exactSolutionLayoutAtIndex(j, false);
KDCoordinate exactLayoutHeight = exactLayout.layoutSize().height();
KDCoordinate approximateLayoutHeight = approximateLayout.layoutSize().height();
KDCoordinate layoutHeight = maxCoordinate(exactLayout.baseline(), approximateLayout.baseline()) + maxCoordinate(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline());
KDCoordinate layoutHeight = std::max(exactLayout.baseline(), approximateLayout.baseline()) + std::max(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline());
return layoutHeight + 2 * Metric::CommonSmallMargin;
}
if (j == rowOfUserVariablesMessage) {

View File

@@ -23,8 +23,8 @@ public:
/* AlternateEmptyViewDefaultDelegate */
bool isEmpty() const override;
virtual I18n::Message emptyMessage() override;
virtual Responder * defaultController() override;
I18n::Message emptyMessage() override;
Responder * defaultController() override;
/* TableViewDataSource */
int numberOfRows() const override;
int numberOfColumns() const override { return 2; }

View File

@@ -1,388 +1,181 @@
#include <quiz.h>
#include <apps/shared/global_context.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <cmath>
#include "../equation_store.h"
#include "../../../poincare/test/helper.h"
using namespace Poincare;
namespace Solver {
void addEquationWithText(EquationStore * equationStore, const char * text, Context * context) {
Ion::Storage::Record::ErrorStatus err = equationStore->addEmptyModel();
quiz_assert_print_if_failure(err == Ion::Storage::Record::ErrorStatus::None, text);
(void) err; // Silence warning in DEBUG=0
Ion::Storage::Record record = equationStore->recordAtIndex(equationStore->numberOfModels()-1);
Shared::ExpiringPointer<Equation> model = equationStore->modelForRecord(record);
model->setContent(text, context);
}
void assert_equation_system_exact_solve_to(const char * equations[], EquationStore::Error error, EquationStore::Type type, const char * variables[], const char * solutions[], int numberOfSolutions, bool didReplaceFunctionsButNotSymbols = false) {
Shared::GlobalContext globalContext;
EquationStore equationStore;
int index = 0;
while (equations[index] != 0) {
addEquationWithText(&equationStore, equations[index++], &globalContext);
}
bool replaceFunctionsButNotSymbols = false;
EquationStore::Error err = equationStore.exactSolve(&globalContext, &replaceFunctionsButNotSymbols);
quiz_assert_print_if_failure(err == error, equations[0]);
quiz_assert_print_if_failure(replaceFunctionsButNotSymbols == didReplaceFunctionsButNotSymbols, equations[0]);
if (err != EquationStore::Error::NoError) {
equationStore.removeAll();
return;
}
quiz_assert_print_if_failure(equationStore.type() == type, equations[0]);
quiz_assert_print_if_failure(equationStore.numberOfSolutions() == numberOfSolutions, equations[0]);
if (numberOfSolutions == INT_MAX) {
equationStore.removeAll();
return;
}
if (type == EquationStore::Type::LinearSystem) {
for (int i = 0; i < numberOfSolutions; i++) {
quiz_assert_print_if_failure(strcmp(equationStore.variableAtIndex(i),variables[i]) == 0, equations[0]);
}
} else {
quiz_assert_print_if_failure(strcmp(equationStore.variableAtIndex(0), variables[0]) == 0, equations[0]);
}
constexpr int bufferSize = 200;
char buffer[bufferSize];
for (int i = 0; i < numberOfSolutions; i++) {
equationStore.exactSolutionLayoutAtIndex(i, true).serializeForParsing(buffer, bufferSize);
quiz_assert_print_if_failure(strcmp(buffer, solutions[i]) == 0, equations[0]);
}
equationStore.removeAll();
}
void assert_equation_approximate_solve_to(const char * equations, double xMin, double xMax, const char * variable, double solutions[], int numberOfSolutions, bool hasMoreSolutions) {
Shared::GlobalContext globalContext;
EquationStore equationStore;
addEquationWithText(&equationStore, equations, &globalContext);
bool replaceFunctionsButNotSymbols = false;
EquationStore::Error err = equationStore.exactSolve(&globalContext, &replaceFunctionsButNotSymbols);
quiz_assert(err == EquationStore::Error::RequireApproximateSolution);
equationStore.setIntervalBound(0, xMin);
equationStore.setIntervalBound(1, xMax);
equationStore.approximateSolve(&globalContext, replaceFunctionsButNotSymbols);
quiz_assert(equationStore.numberOfSolutions() == numberOfSolutions);
quiz_assert(strcmp(equationStore.variableAtIndex(0), variable)== 0);
for (int i = 0; i < numberOfSolutions; i++) {
quiz_assert(std::fabs(equationStore.approximateSolutionAtIndex(i) - solutions[i]) < 1E-5);
}
quiz_assert(equationStore.haveMoreApproximationSolutions(&globalContext, replaceFunctionsButNotSymbols) == hasMoreSolutions);
equationStore.removeAll();
}
#include "helpers.h"
QUIZ_CASE(equation_solve) {
// x+y+z+a+b+c+d = 0
const char * variables1[] = {""};
const char * equations0[] = {"x+y+z+a+b+c+d=0", 0};
assert_equation_system_exact_solve_to(equations0, EquationStore::Error::TooManyVariables, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, 0);
assert_solves_to_error("x+y+z+a+b+c+d=0", TooManyVariables);
assert_solves_to_error("x^2+y=0", NonLinearSystem);
assert_solves_to_error("cos(x)=0", RequireApproximateSolution);
// x^2+y = 0
const char * equations1[] = {"x^2+y=0", 0};
assert_equation_system_exact_solve_to(equations1, EquationStore::Error::NonLinearSystem, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, 0);
assert_solves_to_no_solution("2=0");
assert_solves_to_no_solution("x-x+2=0");
// cos(x) = 0
const char * equations2[] = {"cos(x)=0", 0};
assert_equation_system_exact_solve_to(equations2, EquationStore::Error::RequireApproximateSolution, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, 0);
assert_solves_to_infinite_solutions("0=0");
assert_solves_to_infinite_solutions("x-x=0");
// 2 = 0
const char * equations3[] = {"2=0", 0};
assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, 0);
// 0 = 0
const char * equations4[] = {"0=0", 0};
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, INT_MAX);
assert_solves_to("2x+3=4", "x=1/2");
assert_solves_to("3×x^2-4x+4=2", {"x=2/3-(√(2)/3)𝐢", "x=2/3+(√(2)/3)𝐢", "delta=-8"});
assert_solves_to("2×x^2-4×x+4=3", {"x=(-√(2)+2)/2", "x=(√(2)+2)/2", "delta=8"});
assert_solves_to("2×x^2-4×x+2=0", {"x=1", "delta=0"});
assert_solves_to(
"x^2+x+1=3×x^2+π×x-√(5)",
{
"x=(√(π^2-2π+8√(5)+9)-π+1)/4",
"x=(-√(π^2-2π+8×√(5)+9)-π+1)/4",
"delta=π^2-2π+8√(5)+9"
}
);
assert_solves_to("(x-3)^2=0", {"x=3", "delta=0"});
// x-x+2 = 0
const char * equations5[] = {"x-x+2=0", 0};
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, 0);
// x-x= 0
const char * equations6[] = {"x-x=0", 0};
assert_equation_system_exact_solve_to(equations6, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, INT_MAX);
const char * variablesx[] = {"x", ""};
// 2x+3=4
const char * equations7[] = {"2x+3=4", 0};
const char * solutions7[] = {"\u0012\u00121\u0013/\u00122\u0013\u0013"}; // 1/2
assert_equation_system_exact_solve_to(equations7, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions7, 1);
// 3x^2-4x+4=2
const char * equations8[] = {"3×x^2-4x+4=2", 0};
const char * solutions8[] = {"\u0012\u00122\u0013/\u00123\u0013\u0013-\u0012\u0012\u00122\u0013\u0013/\u00123\u0013\u0013𝐢","\u0012\u00122\u0013/\u00123\u0013\u0013+\u0012\u0012\u00122\u0013\u0013/\u00123\u0013\u0013𝐢", "-8"}; // 2/3-(√(2)/3)𝐢, 2/3+(√(2)/3)𝐢
assert_equation_system_exact_solve_to(equations8, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions8, 3);
// 2×x^2-4×x+4=3
const char * equations9[] = {"2×x^2-4×x+4=3", 0};
const char * solutions9[] = {"\u0012\u0012-√\u00122\u0013+2\u0013/\u00122\u0013\u0013","\u0012\u0012\u00122\u0013+2\u0013/\u00122\u0013\u0013", "8"}; // (-√(2)+2)/2, (√(2)+2)/2, 8
assert_equation_system_exact_solve_to(equations9, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions9, 3);
// 2×x^2-4×x+2=0
const char * equations10[] = {"2×x^2-4×x+2=0", 0};
const char * solutions10[] = {"1", "0"};
assert_equation_system_exact_solve_to(equations10, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions10, 2);
// x^2+x+1=3×x^2+pi×x-√(5)
const char * equations11[] = {"x^2+x+1=3×x^2+π×x-√(5)", 0};
const char * solutions11[] = {"\u0012\u0012\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "\u0012\u0012-√\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "π^\u00122\u0013-2π+8√\u00125\u0013+9"}; // (√(π^2-2π+8√(5)+9)-π+1)/4, (-√(π^2-2π+8×√(5)+9)-π+1)/4, π^2-2π+8√(5)+9
assert_equation_system_exact_solve_to(equations11, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions11, 3);
// (x-3)^2
const char * equations21[] = {"(x-3)^2=0", 0};
const char * solutions21[] = {"3", "0"};
assert_equation_system_exact_solve_to(equations21, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions21, 2);
// TODO
// x^3 - 4x^2 + 6x - 24 = 0
//const char * equations10[] = {"2×x^2-4×x+4=3", 0};
//assert_equation_system_exact_solve_to(equations10, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, {"x", ""}, {"4", "𝐢×√(6)", "-𝐢×√(6)", "-11616"}, 4);
//x^3+x^2+1=0
// x^3-3x-2=0
/* TODO: Cubic
* x^3-4x^2+6x-24=0
* x^3+x^2+1=0
* x^3-3x-2=0 */
// Linear System
const char * equations12[] = {"x+y=0", 0};
assert_equation_system_exact_solve_to(equations12, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, INT_MAX);
const char * variablesxy[] = {"x", "y", ""};
const char * equations13[] = {"x+y=0", "3x+y=-5", 0};
const char * solutions13[] = {"-\u0012\u00125\u0013/\u00122\u0013\u0013", "\u0012\u00125\u0013/\u00122\u0013\u0013"}; // -5/2; 5/2
assert_equation_system_exact_solve_to(equations13, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesxy, solutions13, 2);
const char * variablesxyz[] = {"x", "y", "z", ""};
const char * equations14[] = {"x+y=0", "3x+y+z=-5", "4z-π=0", 0};
const char * solutions14[] = {"\u0012\u0012-π-20\u0013/\u00128\u0013\u0013", "\u0012\u0012π+20\u0013/\u00128\u0013\u0013", "\u0012\u0012π\u0013/\u00124\u0013\u0013"}; // (-π-20)/8, (π+20)/8, π/4
assert_equation_system_exact_solve_to(equations14, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesxyz, solutions14, 3);
const char * variablesxyzabc[] = {"x", "y", "z", "a", "b", "c"};
const char * equations22[] = {"x+y=0", "3x+y+z=-5", "4z-π=0", "a+b+c=0", "a = 3", "c = a+2", 0};
const char * solutions22[] = {"\u0012\u0012-π-20\u0013/\u00128\u0013\u0013", "\u0012\u0012π+20\u0013/\u00128\u0013\u0013", "\u0012\u0012π\u0013/\u00124\u0013\u0013", "3", "-8", "5"}; // (-π-20)/8, (π+20)/8, π/4, 3, 5, -8
assert_equation_system_exact_solve_to(equations22, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesxyzabc, solutions22, 6);
assert_solves_to_infinite_solutions("x+y=0");
assert_solves_to({"x+y=0", "3x+y=-5"}, {"x=-5/2", "y=5/2"});
assert_solves_to(
{
"x+y=0",
"3x+y+z=-5",
"4z-π=0"
},
{
"x=(-π-20)/8",
"y=(π+20)/8",
"z=π/4"
}
);
assert_solves_to(
{
"x+y=0",
"3x+y+z=-5",
"4z-π=0",
"a+b+c=0",
"a=3",
"c=a+2"
},
{
"x=(-π-20)/8",
"y=(π+20)/8",
"z=π/4",
"a=3",
"b=-8",
"c=5"
}
);
/* This test case needs the user defined variable. Indeed, in the equation
* store, m_variables is just before m_userVariables, so bad fetching in
* m_variables might fetch into m_userVariables and create problems. */
assert_simplify("0→x");
const char * variablesbDeyzt[] = {"b", "D", "e", "y", "z", "t"};
const char * equations23[] = {"b=0", "D=0", "e=0", "", "x+y+z+t=0", 0};
assert_equation_system_exact_solve_to(equations23, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesbDeyzt, nullptr, INT_MAX);
Ion::Storage::sharedStorage()->recordNamed("x.exp").destroy();
set("x", "0");
assert_solves_to_infinite_solutions({
"b=0",
"D=0",
"e=0",
"x+y+z+t=0"
});
unset("x");
// Monovariable non-polynomial equation
double solutions15[] = {-90.0, 90.0};
assert_equation_approximate_solve_to("cos(x)=0", -100.0, 100.0, "x", solutions15, 2, false);
double solutions16[] = {-810.0, -630.0, -450.0, -270.0, -90.0, 90.0, 270.0, 450.0, 630.0, 810.0};
assert_equation_approximate_solve_to("cos(x)=0", -900.0, 1000.0, "x", solutions16, 10, true);
double solutions17[] = {0};
assert_equation_approximate_solve_to("√(y)=0", -900.0, 1000.0, "y", solutions17, 1, false);
assert_solves_numerically_to("cos(x)=0", -100, 100, {-90.0, 90.0});
assert_solves_numerically_to("cos(x)=0", -900, 1000, {-810.0, -630.0, -450.0, -270.0, -90.0, 90.0, 270.0, 450.0, 630.0, 810.0});
assert_solves_numerically_to("√(y)=0", -900, 1000, {0}, "y");
// Long variable names
const char * variablesabcde[] = {"abcde", ""};
const char * equations18[] = {"2abcde+3=4", 0};
const char * solutions18[] = {"\u0012\u00121\u0013/\u00122\u0013\u0013"}; // 1/2
assert_equation_system_exact_solve_to(equations18, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesabcde, solutions18, 1);
const char * variablesBig1Big2[] = {"Big1", "Big2", ""};
const char * equations19[] = {"Big1+Big2=0", "3Big1+Big2=-5", 0};
const char * solutions19[] = {"-\u0012\u00125\u0013/\u00122\u0013\u0013", "\u0012\u00125\u0013/\u00122\u0013\u0013"}; // -5/2, 5/2
assert_equation_system_exact_solve_to(equations19, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesBig1Big2, solutions19, 2);
assert_solves_to("2abcde+3=4", "abcde=1/2");
assert_solves_to({"Big1+Big2=0", "3Big1+Big2=-5"}, {"Big1=-5/2", "Big2=5/2"});
// conj(x)*x+1 = 0
const char * equations20one = "conj(x)*x+1=0";
const char * equations20[] = {equations20one, 0};
assert_equation_system_exact_solve_to(equations20, EquationStore::Error::RequireApproximateSolution, EquationStore::Type::LinearSystem, (const char **)variables1, nullptr, 0);
assert_equation_approximate_solve_to(equations20one, -100.0, 100.0, "x", nullptr, 0, false);
assert_solves_to_error("conj(x)*x+1=0", RequireApproximateSolution);
assert_solves_numerically_to("conj(x)*x+1=0", -100, 100, {});
}
QUIZ_CASE(equation_solve_complex_format) {
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Real);
const char * variablesx[] = {"x", ""};
// x+I = 0 --> x = -𝐢
const char * equations0[] = {"x+𝐢=0", 0};
const char * solutions0[] = {"-𝐢"};
assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1);
QUIZ_CASE(equation_solve_complex_real) {
set_complex_format(Real);
assert_solves_to("x+𝐢=0", "x=-𝐢"); // We still want complex solutions if the input has some complex value
assert_solves_to_error("x+√(-1)=0", EquationUnreal);
assert_solves_to("x^2+x+1=0", {"delta=-3"});
assert_solves_to_error("x^2-√(-1)=0", EquationUnreal);
assert_solves_to_error("x+√(-1)×√(-1)=0", EquationUnreal);
assert_solves_to("root(-8,3)*x+3=0", "x=3/2");
reset_complex_format();
}
// x+√(-1) = 0 --> Not defined in R
const char * equations1[] = {"x+√(-1)=0", 0};
assert_equation_system_exact_solve_to(equations1, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0);
QUIZ_CASE(equation_solve_complex_cartesian) {
set_complex_format(Cartesian);
assert_solves_to("x+𝐢=0", "x=-𝐢");
assert_solves_to("x+√(-1)=0", "x=-𝐢");
assert_solves_to({"x^2+x+1=0"}, {"x=-1/2-((√(3))/2)𝐢", "x=-1/2+((√(3))/2)𝐢", "delta=-3"});
assert_solves_to("x^2-√(-1)=0", {"x=-√(2)/2-(√(2)/2)𝐢", "x=√(2)/2+(√(2)/2)𝐢", "delta=4𝐢"});
assert_solves_to("x+√(-1)×√(-1)=0", "x=1");
assert_solves_to("root(-8,3)*x+3=0", "x=-3/4+(3√(3)/4)*𝐢");
reset_complex_format();
}
// x^2+x+1=0 --> No solution in R
const char * equations2[] = {"x^2+x+1=0", 0};
const char * delta2[] = {"-3"};
assert_equation_system_exact_solve_to(equations2, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, delta2, 1);
// x^2-√(-1)=0 --> Not defined in R
const char * equations3[] = {"x^2-√(-1)=0", 0};
assert_equation_system_exact_solve_to(equations3, EquationStore::Error::EquationUnreal, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, nullptr, 0);
// x+√(-1)×√(-1) = 0 --> Not defined in R
const char * equations4[] = {"x+√(-1)×√(-1)=0", 0};
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0);
// root(-8,3)*x+3 = 0 --> 3/2 in R
const char * equations5[] = {"root(-8,3)*x+3=0", 0};
const char * solutions5[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013"};
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5, 1);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
// x+𝐢 = 0 --> x = -𝐢
assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1);
// x+√(-1) = 0 --> x = -𝐢
assert_equation_system_exact_solve_to(equations1, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1);
// x^2+x+1=0
const char * solutions2[] = {"-\u0012\u00121\u0013/\u00122\u0013\u0013-\u0012\u0012\u00123\u0013\u0013/\u00122\u0013\u0013𝐢","-\u0012\u00121\u0013/\u00122\u0013\u0013+\u0012\u0012\u00123\u0013\u0013/\u00122\u0013\u0013𝐢", "-3"}; // -1/2-((√(3))/2)𝐢, -1/2+((√(3))/2)𝐢, -3
assert_equation_system_exact_solve_to(equations2, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions2, 3);
// x^2-√(-1)=0
const char * solutions3[] = {"-\u0012\u0012\u00122\u0013\u0013/\u00122\u0013\u0013-\u0012\u0012\u00122\u0013\u0013/\u00122\u0013\u0013𝐢", "\u0012\u0012\u00122\u0013\u0013/\u00122\u0013\u0013+\u0012\u0012\u00122\u0013\u0013/\u00122\u0013\u0013𝐢","4𝐢"}; // -√(2)/2-(√(2)/2)𝐢, √(2)/2+(√(2)/2)𝐢, 4𝐢
assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3, 3);
// x+√(-1)×√(-1) = 0
const char * solutions4[] = {"1"};
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions4, 1);
const char * solutions5Cartesain[] = {"-\u0012\u00123\u0013/\u00124\u0013\u0013+\u0012\u00123√\u00123\u0013\u0013/\u00124\u0013\u0013𝐢"}; //-3/4+(3√3/4)*𝐢
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Cartesain, 1);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar);
// x+𝐢 = 0 --> x = e^(-π/2×i)
const char * solutions0Polar[] = {"^\u0012-\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ^(-(π/2)𝐢)
assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0Polar, 1);
// x+√(-1) = 0 --> x = e^(-π/2𝐢)
assert_equation_system_exact_solve_to(equations1, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0Polar, 1);
// x^2+x+1=0
const char * solutions2Polar[] = {"^\u0012-\u0012\u0012\u0013/\u00123\u0013\u0013𝐢\u0013","^\u0012\u0012\u0012\u0013/\u00123\u0013\u0013𝐢\u0013", "3^\u0012π·𝐢\u0013"}; // ^(-(2π/3)𝐢), ^((2π/3)𝐢), 3^(π𝐢)
assert_equation_system_exact_solve_to(equations2, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions2Polar, 3);
// x^2-√(-1)=0
const char * solutions3Polar[] = {"^\u0012-\u0012\u0012\u0013/\u00124\u0013\u0013𝐢\u0013", "^\u0012\u0012\u0012π\u0013/\u00124\u0013\u0013𝐢\u0013", "4^\u0012\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ^(-(3×π/4)𝐢)"‰, "^((π/4)𝐢)", "4^((π/2)𝐢)
assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3Polar, 3);
const char * solutions5Polar[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013^\u0012\u0012\u0012\u0013/\u00123\u0013\u0013𝐢\u0013"}; //3/2^\u0012\u00122π\u0012/3\u0013𝐢"};
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Polar, 1);
// Put back the complex format
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Real);
QUIZ_CASE(equation_solve_complex_polar) {
set_complex_format(Polar);
assert_solves_to("x+𝐢=0", "x=^(-(π/2)𝐢)");
assert_solves_to("x+√(-1)=0", "x=^(-(π/2)𝐢)");
assert_solves_to("x^2+x+1=0", {"x=^(-(2π/3)𝐢)", "x=^((2π/3)𝐢)", "delta=3^(π𝐢)"});
assert_solves_to("x^2-√(-1)=0", {"x=^(-(3π/4)𝐢)", "x=^((π/4)𝐢)", "delta=4^((π/2)𝐢)"});
assert_solves_to("root(-8,3)*x+3=0", "x=3/2×^((2π/3)𝐢)");
reset_complex_format();
}
QUIZ_CASE(equation_and_symbolic_computation) {
// x+a=0 : non linear system
const char * equation1[] = {"x+a=0", 0};
assert_equation_system_exact_solve_to(equation1, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, nullptr, nullptr, INT_MAX);
assert_solves_to_infinite_solutions("x+a=0");
// -3->a
Shared::GlobalContext globalContext;
Expression::ParseAndSimplify("-3→a", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
set("a", "-3");
assert_solves_to("x+a=0", "x=3");
// x+a = 0 : x = 3
const char * variables1[] = {"x", ""};
const char * solutions1[] = {"3"};
assert_equation_system_exact_solve_to(equation1, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables1, solutions1, 1);
assert_solves_to("a=0", "a=0");
/* The equation has no solution since the user defined a = -3. So a is not
* replaced with its context value, and the solution is a = 0. */
/* a = 0 : the equation has no solution as the user defined a = -3, so a is
* not replaced with its context value and the result is a = 0. */
const char * equation2[] = {"a=0", 0};
const char * variables2[] = {"a", ""};
const char * solutions2[] = {"0"};
assert_equation_system_exact_solve_to(equation2, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables2, solutions2, 1, true);
set("b", "-4");
assert_solves_to_infinite_solutions("a+b=0");
/* The equation has no solution since the user defined a = -3 and b = -4.
* So neither a nor b are replaced with their context values. Therefore the
* solution is an infinity of solutions. */
// 4->b
Expression::ParseAndSimplify("-4→b", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
assert_solves_to("a+b+c=0", "c=7");
/* a + b = 0 : the equation has no solution as the user defined a = -3, and
* b = -4 so a and b are not replaced with their context values and the result
* is an infinity of solutions. */
const char * equation3[] = {"a+b=0", 0};
const char * variables3[] = {"a", "b", ""};
assert_equation_system_exact_solve_to(equation3, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables3, nullptr, INT_MAX, true);
assert_solves_to({"a+c=0", "a=3"}, {"a=3", "c=-3"});
/* The system has no solution since the user defined a = -3. So a is not
* replaced with its context value, and the solution is a = 3 and c = -3. */
// a + b + c = 0 : the equation has the solution c = -7
const char * equation4[] = {"a+b+c=0", 0};
const char * variables4[] = {"c", ""};
const char * solutions4[] = {"7"};
assert_equation_system_exact_solve_to(equation4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables4, solutions4, 1);
set("f(x)", "x+1");
/* a + c = 0 and a = 3: the system has no solution as the user defined a = -3,
* so a is not replaced with its context value and the result is a = 3 and
* c = -3. */
const char * equation5[] = {"a+c=0", "a=3", 0};
const char * variables5[] = {"a", "c", ""};
const char * solutions5[] = {"3", "-3"};
assert_equation_system_exact_solve_to(equation5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables5, solutions5, 2, true);
assert_solves_to("f(x)=0", "x=-1");
// x+1->f(x)
Expression::ParseAndSimplify("x+1→f(x)", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
assert_solves_to("f(a)=0", "a=-1");
/* The equation has no solution since the user defined a = -3. So a is not
* replaced with its context value, and the solution is a = -1. */
// f(x) = 0 : x = -1
const char * equation6[] = {"f(x)=0", 0};
const char * variables6[] = {"x", ""};
const char * solutions6[] = {"-1",};
assert_equation_system_exact_solve_to(equation6, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables6, solutions6, 1);
set("g(x)", "a+x+2");
/* f(a) = 0 : the equation has no solution as the user defined a = -3, so a is
* not replaced with its context value and the result is a = -1. */
const char * equation7[] = {"f(a)=0", 0};
const char * variables7[] = {"a", ""};
const char * solutions7[] = {"-1",};
assert_equation_system_exact_solve_to(equation7, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables7, solutions7, 1, true);
assert_solves_to("g(x)=0", "x=1");
// a+x+1->g(x)
Expression::ParseAndSimplify("a+x+2→g(x)", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
assert_solves_to("g(a)=0", "a=-1");
/* The equation has no solution since the user defined a = -3. So a is not
* replaced with its context value, and the equation becomes a + a + 2 = 0.
* The solution is therefore a = -1. */
// g(x) = 0 : x = 2
const char * equation8[] = {"g(x)=0", 0};
const char * variables8[] = {"x", ""};
const char * solutions8[] = {"1",};
assert_equation_system_exact_solve_to(equation8, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables8, solutions8, 1);
set("d", "5");
set("c", "d");
set("h(x)", "c+d+3");
assert_solves_to({"h(x)=0", "c=-3"}, {"c=-3", "d=0"});
// c and d context values should not be used
/* g(a) = 0 : the equation has no solution as the user defined a = -3, so a is
* not replaced with its context value and the equation becomes a+a+2=0. The
* solution is a = -1. */
const char * equation9[] = {"g(a)=0", 0};
const char * variables9[] = {"a", ""};
const char * solutions9[] = {"-1",};
assert_equation_system_exact_solve_to(equation9, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables9, solutions9, 1, true);
/* c = d
* d = 5
* h(x) = c + d + 3
* /c = -3
* \h(x) = 0
* c and d context values should not be used, and the solution is c = -3, d = 0 */
Expression::ParseAndSimplify("5→d", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
Expression::ParseAndSimplify("d→c", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
Expression::ParseAndSimplify("c+d+3→h(x)", &globalContext, Preferences::ComplexFormat::Polar, Preferences::AngleUnit::Degree);
const char * equation10[] = {"h(x)=0", "c = -3", 0};
const char * variables10[] = {"c", "d", ""};
const char * solutions10[] = {"-3", "0"};
assert_equation_system_exact_solve_to(equation10, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables10, solutions10, 2, true);
const char * equation11[] = {"c+d=5", "c-d=1", 0};
const char * variables11[] = {"c", "d", ""};
const char * solutions11[] = {"3", "2"};
assert_equation_system_exact_solve_to(equation11, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variables11, solutions11, 2, true);
// Clean the storage
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("b.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("c.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("d.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
Ion::Storage::sharedStorage()->recordNamed("g.func").destroy();
Ion::Storage::sharedStorage()->recordNamed("h.func").destroy();
}
assert_solves_to({"c+d=5", "c-d=1"}, {"c=3", "d=2"});
unset("a");
unset("b");
unset("c");
unset("d");
unset("e");
unset("f");
unset("g");
unset("h");
}

View File

@@ -0,0 +1,158 @@
#include <quiz.h>
#include <apps/shared/global_context.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <cmath>
#include "../equation_store.h"
#include "helpers.h"
using namespace Solver;
using namespace Poincare;
// Private sub-helpers
template <typename T>
void solve_and_process_error(std::initializer_list<const char *> equations, T && lambda) {
Shared::GlobalContext globalContext;
EquationStore equationStore;
for (const char * equation : equations) {
Ion::Storage::Record::ErrorStatus err = equationStore.addEmptyModel();
quiz_assert_print_if_failure(err == Ion::Storage::Record::ErrorStatus::None, equation);
Ion::Storage::Record record = equationStore.recordAtIndex(equationStore.numberOfModels()-1);
Shared::ExpiringPointer<Equation> model = equationStore.modelForRecord(record);
model->setContent(equation, &globalContext);
}
bool replaceFunctionsButNotSymbols = false;
EquationStore::Error err = equationStore.exactSolve(&globalContext, &replaceFunctionsButNotSymbols);
lambda(&equationStore, err);
equationStore.removeAll();
}
template <typename T>
void solve_and(std::initializer_list<const char *> equations, T && lambda) {
solve_and_process_error(equations, [lambda](EquationStore * store, EquationStore::Error error) {
quiz_assert(error == NoError);
lambda(store);
});
}
// Helpers
void assert_solves_to_error(const char * equation, EquationStore::Error error) {
solve_and_process_error({equation},[error](EquationStore * store, EquationStore::Error e){
quiz_assert(e == error);
});
}
void assert_solves_to_infinite_solutions(std::initializer_list<const char *> equations) {
solve_and(equations, [](EquationStore * store){
quiz_assert(store->numberOfSolutions() == INT_MAX);
});
}
void assert_solves_to(std::initializer_list<const char *> equations, std::initializer_list<const char *> solutions) {
solve_and(equations, [solutions](EquationStore * store){
Shared::GlobalContext globalContext;
int i = 0;
for (const char * solution : solutions) {
// Solutions are specified under the form "foo=bar"
constexpr int maxSolutionLength = 100;
char editableSolution[maxSolutionLength];
strlcpy(editableSolution, solution, maxSolutionLength);
char * equal = strchr(editableSolution, '=');
quiz_assert(equal != nullptr);
*equal = 0;
const char * expectedVariable = editableSolution;
if (store->type() != EquationStore::Type::PolynomialMonovariable) {
/* For some reason the EquationStore returns up to 3 results but always
* just one variable, so we don't check variable name...
* TODO: Change this poor behavior. */
const char * obtainedVariable = store->variableAtIndex(i);
quiz_assert(strcmp(obtainedVariable, expectedVariable) == 0);
}
/* Now for the ugly part!
* At the moment, the EquationStore doesn't let us retrieve solutions as
* Expression. We can only get Layout. It somewhat makes sense for how it
* is used in the app, but it's a nightmare to test, so changing this
* behavior is a TODO. */
const char * expectedValue = equal + 1;
/* We compare Expressions, by parsing the expected Expression and
* serializing and parsing the obtained layout. We need to ignore the
* parentheses during the comparison, because to create an expression from
* a const char * we need to add parentheses that are not necessary when
* creating an expression from a layout. */
Expression expectedExpression = Expression::Parse(expectedValue, &globalContext, false);
quiz_assert(!expectedExpression.isUninitialized());
Layout obtainedLayout = store->exactSolutionLayoutAtIndex(i, true);
constexpr int bufferSize = 200;
char obtainedLayoutBuffer[bufferSize];
obtainedLayout.serializeForParsing(obtainedLayoutBuffer, bufferSize);
Expression obtainedExpression = Expression::Parse(obtainedLayoutBuffer, &globalContext, false);
quiz_assert(expectedExpression.isIdenticalToWithoutParentheses(obtainedExpression));
i++;
}
quiz_assert(store->numberOfSolutions() == i);
});
}
void assert_solves_numerically_to(const char * equation, double min, double max, std::initializer_list<double> solutions, const char * variable) {
solve_and_process_error({equation},[min,max,solutions,variable](EquationStore * store, EquationStore::Error e){
Shared::GlobalContext globalContext;
quiz_assert(e == RequireApproximateSolution);
store->setIntervalBound(0, min);
store->setIntervalBound(1, max);
store->approximateSolve(&globalContext, false);
quiz_assert(strcmp(store->variableAtIndex(0), variable)== 0);
int i = 0;
for (double solution : solutions) {
quiz_assert(std::fabs(store->approximateSolutionAtIndex(i++) - solution) < 1E-5);
}
quiz_assert(store->numberOfSolutions() == i);
});
}
void set_complex_format(Preferences::ComplexFormat format) {
Preferences::sharedPreferences()->setComplexFormat(format);
}
void reset_complex_format() {
Preferences defaultPreferences;
Preferences::sharedPreferences()->setComplexFormat(defaultPreferences.complexFormat());
}
void set(const char * variable, const char * value) {
const char * assign = "";
char buffer[32];
assert(strlen(value) + strlen(assign) + strlen(variable) < sizeof(buffer));
buffer[0] = 0;
strlcat(buffer, value, sizeof(buffer));
strlcat(buffer, assign, sizeof(buffer));
strlcat(buffer, variable, sizeof(buffer));
Shared::GlobalContext globalContext;
Expression::ParseAndSimplify(
buffer,
&globalContext,
Preferences::sharedPreferences()->complexFormat(),
Preferences::sharedPreferences()->angleUnit()
);
}
void unset(const char * variable) {
// The variable is either an expression or a function
Ion::Storage::sharedStorage()->destroyRecordWithBaseNameAndExtension(variable, "exp");
Ion::Storage::sharedStorage()->destroyRecordWithBaseNameAndExtension(variable, "func");
}

View File

@@ -0,0 +1,49 @@
#ifndef APPS_SOLVER_TEST_HELPERS_H
#define APPS_SOLVER_TEST_HELPERS_H
#include <poincare/preferences.h>
#include <initializer_list>
#include "../equation_store.h"
#include <poincare/test/helper.h>
#define bring_in(prefix, value) static const prefix value = prefix::value;
bring_in(Solver::EquationStore::Error, EquationUnreal);
bring_in(Solver::EquationStore::Error, NoError);
bring_in(Solver::EquationStore::Error, NonLinearSystem);
bring_in(Solver::EquationStore::Error, RequireApproximateSolution);
bring_in(Solver::EquationStore::Error, TooManyVariables);
// Custom assertions
void assert_solves_to(std::initializer_list<const char *> equations, std::initializer_list<const char *> solutions);
void assert_solves_numerically_to(const char * equation, double min, double max, std::initializer_list<double> solutions, const char * variable = "x");
void assert_solves_to_error(const char * equation, Solver::EquationStore::Error error);
void assert_solves_to_infinite_solutions(std::initializer_list<const char *> equations);
// Shorthands
inline void assert_solves_to_no_solution(const char * equation) {
/* Note: Doesn't really work with quadratic equations that will always report
* at least a delta value. */
assert_solves_to({equation}, {});
}
inline void assert_solves_to_infinite_solutions(const char * equation) {
assert_solves_to_infinite_solutions({equation});
}
inline void assert_solves_to(const char * equation, const char * solution) {
assert_solves_to({equation}, {solution});
}
inline void assert_solves_to(const char * equation, std::initializer_list<const char *> solutions) {
assert_solves_to({equation}, solutions);
}
// Helpers
void set_complex_format(Poincare::Preferences::ComplexFormat format);
void reset_complex_format();
void set(const char * variable, const char * value);
void unset(const char * variable);
#endif

View File

@@ -4,6 +4,7 @@
#include "app.h"
#include <poincare/ieee754.h>
#include <poincare/preferences.h>
#include <algorithm>
#include <cmath>
#include <assert.h>
#include <float.h>
@@ -13,9 +14,6 @@ using namespace Shared;
namespace Statistics {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
HistogramController::HistogramController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex, int * selectedSeriesIndex) :
MultipleDataViewController(parentResponder, store, selectedBarIndex, selectedSeriesIndex),
ButtonRowDelegate(header, nullptr),
@@ -193,8 +191,8 @@ void HistogramController::preinitXRangeParameters() {
float maxValue = -FLT_MAX;
for (int i = 0; i < Store::k_numberOfSeries; i ++) {
if (!m_store->seriesIsEmpty(i)) {
minValue = minFloat(minValue, m_store->minValue(i));
maxValue = maxFloat(maxValue, m_store->maxValue(i));
minValue = std::min<float>(minValue, m_store->minValue(i));
maxValue = std::max<float>(maxValue, m_store->maxValue(i));
}
}
m_store->setXMin(minValue);

View File

@@ -25,7 +25,7 @@ UnitDistanceFoot = "Foot"
UnitDistanceYard = "Yard"
UnitDistanceMile = "Mile"
UnitDistanceAstronomicalUnit = "Unité astronomique"
UnitDistanceLightYear = "Année lumière"
UnitDistanceLightYear = "Année-lumière"
UnitDistanceParsec = "Parsec"
UnitMassImperialMenu = "US Customary"
UnitMassPound = "Pound"
@@ -112,7 +112,7 @@ UnitVolumeLiterMilli = "Millilitre"
Toolbox = "Boîte à outils"
AbsoluteValue = "Valeur absolue"
NthRoot = "Racine n-ième"
BasedLogarithm = "Logarithme base a"
BasedLogarithm = "Logarithme de base a"
Calculation = "Calcul"
ComplexNumber = "Nombres complexes"
Combinatorics = "Dénombrement"
@@ -123,8 +123,8 @@ Identity = "Matrice identité de taille n"
Lists = "Listes"
HyperbolicTrigonometry = "Trigonométrie hyperbolique"
Fluctuation = "Intervalle de fluctuation"
DerivateNumber = "Nombre derivé"
Integral = "Intégrale"
DerivateNumber = "Nombre derivé de f en a"
Integral = "Intégrale de f sur [a;b]"
Sum = "Somme"
Product = "Produit"
ComplexAbsoluteValue = "Module"
@@ -134,22 +134,22 @@ ImaginaryPart = "Partie imaginaire"
Conjugate = "Conjugué"
Combination = "k parmi n"
Permutation = "Arrangement"
GreatCommonDivisor = "PGCD"
LeastCommonMultiple = "PPCM"
GreatCommonDivisor = "PGCD de p et q"
LeastCommonMultiple = "PPCM de p et q"
Remainder = "Reste de la division de p par q"
Quotient = "Quotient de la division de p par q"
Inverse = "Inverse"
Determinant = "Déterminant"
Transpose = "Transposée"
Trace = "Trace"
Dimension = "Taille"
Inverse = "Inverse de M"
Determinant = "Déterminant de M"
Transpose = "Transposée de M"
Trace = "Trace de M"
Dimension = "Taille de M"
Sort = "Tri croissant"
InvSort = "Tri décroissant"
Maximum = "Maximum"
Minimum = "Minimum"
Floor = "Partie entière"
FracPart = "Partie fractionnaire"
Ceiling = "Plafond"
Floor = "Partie entière par défaut"
FracPart = "Partie décimale"
Ceiling = "Partie entière par excès"
Rounding = "Arrondi à n décimales"
HyperbolicCosine = "Cosinus hyperbolique"
HyperbolicSine = "Sinus hyperbolique"
@@ -161,8 +161,8 @@ Prediction95 = "Intervalle fluctuation 95% (Term)"
Prediction = "Intervalle fluctuation simple (2de)"
Confidence = "Intervalle de confiance"
RandomAndApproximation = "Aléatoire et approximation"
RandomFloat = "Nombre décimal dans [0,1["
RandomInteger = "Entier aléatoire dans [a,b]"
RandomFloat = "Nombre décimal dans [0;1["
RandomInteger = "Entier aléatoire dans [a;b]"
PrimeFactorDecomposition = "Décomposition en facteurs premiers"
NormCDF = "P(X<a) où X suit N(μ,σ2)"
NormCDF2 = "P(a<X<b) où X suit N(μ,σ2)"

View File

@@ -8,14 +8,12 @@
#include <poincare/matrix_layout.h>
#include <poincare/preferences.h>
#include <assert.h>
#include <algorithm>
using namespace Poincare;
using namespace Shared;
using namespace Ion;
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
static inline KDCoordinate maxInt(int x, int y) { return x > y ? x : y; }
VariableBoxController::VariableBoxController() :
NestedMenuController(nullptr, I18n::Message::Variables),
m_currentPage(Page::RootMenu),
@@ -129,7 +127,7 @@ KDCoordinate VariableBoxController::rowHeight(int index) {
if (m_currentPage != Page::RootMenu) {
Layout layoutR = expressionLayoutForRecord(recordAtIndex(index), index);
if (!layoutR.isUninitialized()) {
return maxCoordinate(layoutR.layoutSize().height()+k_leafMargin, Metric::ToolboxRowHeight);
return std::max<KDCoordinate>(layoutR.layoutSize().height()+k_leafMargin, Metric::ToolboxRowHeight);
}
}
return NestedMenuController::rowHeight(index);
@@ -289,7 +287,7 @@ void VariableBoxController::destroyRecordAtRowIndex(int rowIndex) {
// The deleted row is after the memoization
return;
}
for (int i = maxInt(0, rowIndex - m_firstMemoizedLayoutIndex); i < k_maxNumberOfDisplayedRows - 1; i++) {
for (int i = std::max(0, rowIndex - m_firstMemoizedLayoutIndex); i < k_maxNumberOfDisplayedRows - 1; i++) {
m_layouts[i] = m_layouts[i+1];
}
m_layouts[k_maxNumberOfDisplayedRows - 1] = Layout();

View File

@@ -2,7 +2,6 @@ HOSTCC = gcc
HOSTCXX = g++
PYTHON = python3
SFLAGS += -DDEBUG=$(DEBUG)
SFLAGS += -DLEDS_CHOICE=$(LEDS_CHOICE)
ifdef USERNAME
SFLAGS += -DUSERNAME="$(USERNAME)"
@@ -17,9 +16,15 @@ CXXFLAGS = -std=c++11 -fno-exceptions -fno-rtti -fno-threadsafe-statics
# Flags - Optimizations
ifeq ($(DEBUG),1)
SFLAGS = -O0 -g
SFLAGS += -O0 -g
else
SFLAGS = -Os
SFLAGS += -Os
SFLAGS += -DNDEBUG
endif
ifeq ($(ASAN),1)
SFLAGS += -fsanitize=address
LDFLAGS += -fsanitize=address
endif
# Flags - Header search path

View File

@@ -8,8 +8,8 @@ import urllib.parse
# ELF analysis
def loadable_sections(elf_file, address_prefix = ""):
objdump_section_headers_pattern = re.compile("^\s+\d+\s+(\.[\w\.]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+("+address_prefix+"[0-9a-f]+)\s+([0-9a-f]+).*LOAD", flags=re.MULTILINE)
def loadable_sections(elf_file):
objdump_section_headers_pattern = re.compile("^\s+\d+\s+(\.[\w\.]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)", flags=re.MULTILINE)
objdump_output = subprocess.check_output(["arm-none-eabi-objdump", "-h", "-w", elf_file]).decode('utf-8')
sections = []
for (name, size, vma, lma, offset) in re.findall(objdump_section_headers_pattern, objdump_output):
@@ -21,19 +21,16 @@ def loadable_sections(elf_file, address_prefix = ""):
# Data filtering
def biggest_sections(sections, n):
sorted_sections = sorted(sections, key=lambda s: s['size'], reverse=True)
return sorted_sections[:n]
def total_size(sections):
return sum(map(lambda s: s['size'], sections))
def row_for_elf(elf, columns):
def row_for_elf(elf, requested_section_prefixes):
sections = loadable_sections(elf)
result = {}
for s in biggest_sections(sections, columns):
result[s['name']] = s['size']
result['Total'] = total_size(sections)
for prefix in requested_section_prefixes:
for s in sections:
section_name = s['name']
if s['name'].startswith(prefix):
if not prefix in result:
result[prefix] = 0
result[prefix] += s['size']
return result
@@ -119,7 +116,7 @@ def format_table(table):
parser = argparse.ArgumentParser(description='Compute binary size metrics')
parser.add_argument('files', type=str, nargs='+', help='an ELF file')
parser.add_argument('--labels', type=str, nargs='+', help='label for ELF file')
parser.add_argument('--number-of-sections', type=int, default=2, help='Number of detailed sections')
parser.add_argument('--sections', type=str, nargs='+', help='Section (prefix) to list')
parser.add_argument('--escape', action='store_true', help='Escape the output')
args = parser.parse_args()
@@ -131,7 +128,7 @@ for i,filename in enumerate(args.files):
label = os.path.basename(filename)
if args.labels and i < len(args.labels):
label = args.labels[i]
table.append({'label': label, 'values': row_for_elf(filename, args.number_of_sections)})
table.append({'label': label, 'values': row_for_elf(filename, args.sections)})
formatted_table = format_table(table)
if args.escape:

View File

@@ -1,9 +1,4 @@
TOOLCHAIN ?= host-gcc
USE_LIBA ?= 0
ION_KEYBOARD_LAYOUT = layout_B2
EXE = bin
ifeq ($(DEBUG),1)
else
SFLAGS += -DNDEBUG
endif
EXE = bin

View File

@@ -17,4 +17,4 @@ SFLAGS += -DEPSILON_SIMULATOR_HAS_LIBPNG=$(EPSILON_SIMULATOR_HAS_LIBPNG)
ifeq ($(EPSILON_SIMULATOR_HAS_LIBPNG),1)
SFLAGS += `libpng-config --cflags`
LDFLAGS += `libpng-config --ldflags`
endif
endif

View File

@@ -2,3 +2,7 @@ export AFL_QUIET = 1
CC = afl-clang
CXX = afl-clang++
LD = afl-clang++
ifeq ($(ASAN),1)
export AFL_USE_ASAN = 1
endif

View File

@@ -53,6 +53,7 @@ escher_src += $(addprefix escher/src/,\
message_table_cell_with_message.cpp \
message_table_cell_with_switch.cpp \
message_text_view.cpp \
metric.cpp \
modal_view_controller.cpp \
nested_menu_controller.cpp \
palette.cpp \

View File

@@ -11,7 +11,7 @@ public:
EditableTextCell * editableTextCell();
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
virtual Responder * responder() override {
Responder * responder() override {
return this;
}
const char * text() const override {

View File

@@ -9,7 +9,7 @@ class MessageTableCell : public TableCell {
public:
MessageTableCell(I18n::Message label = (I18n::Message)0, const KDFont * font = KDFont::SmallFont, Layout layout = Layout::HorizontalLeftOverlap);
View * labelView() const override;
virtual void setHighlighted(bool highlight) override;
void setHighlighted(bool highlight) override;
void setMessage(I18n::Message message);
virtual void setTextColor(KDColor color);
void setMessageFont(const KDFont * font);

View File

@@ -19,7 +19,7 @@ public:
void viewDidDisappear() override;
//ListViewDataSource
virtual KDCoordinate rowHeight(int j) override;
KDCoordinate rowHeight(int j) override;
HighlightCell * reusableCell(int index, int type) override;
protected:
class Stack {

View File

@@ -106,8 +106,8 @@ protected:
void layoutSubviews(bool force = false) override;
virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); }
#if ESCHER_VIEW_LOGGING
virtual const char * className() const override;
virtual void logAttributes(std::ostream &os) const override;
const char * className() const override;
void logAttributes(std::ostream &os) const override;
#endif
View * m_contentView;
private:

View File

@@ -10,8 +10,8 @@ public:
KDCoordinate margin() const { return m_margin; }
protected:
#if ESCHER_VIEW_LOGGING
virtual const char * className() const override;
virtual void logAttributes(std::ostream &os) const override;
const char * className() const override;
void logAttributes(std::ostream &os) const override;
#endif
KDColor m_color;
KDCoordinate m_margin;

View File

@@ -25,9 +25,9 @@ public:
void selectRow(int j);
void selectColumn(int i);
void reloadData(bool setFirstResponder = true);
virtual bool handleEvent(Ion::Events::Event event) override;
virtual void didEnterResponderChain(Responder * previousFirstResponder) override;
virtual void willExitResponderChain(Responder * nextFirstResponder) override;
bool handleEvent(Ion::Events::Event event) override;
void didEnterResponderChain(Responder * previousFirstResponder) override;
void willExitResponderChain(Responder * nextFirstResponder) override;
void deselectTable(bool withinTemporarySelection = false);
bool selectCellAtLocation(int i, int j, bool setFirstResponder = true, bool withinTemporarySelection = false);
HighlightCell * selectedCell();

View File

@@ -18,15 +18,15 @@ public:
int reusableCellCount(int type) override;
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
int typeAtLocation(int i, int j) override;
virtual const ToolboxMessageTree * rootModel() const = 0;
protected:
constexpr static int k_maxMessageSize = 100;
bool selectSubMenu(int selectedRow) override;
bool returnToPreviousMenu() override;
virtual int maxNumberOfDisplayedRows() = 0;
virtual MessageTableCellWithMessage * leafCellAtIndex(int index) override = 0;
virtual MessageTableCellWithChevron * nodeCellAtIndex(int index) override = 0;
virtual const ToolboxMessageTree * rootModel() const = 0;
MessageTableCellWithMessage * leafCellAtIndex(int index) override = 0;
MessageTableCellWithChevron * nodeCellAtIndex(int index) override = 0;
mutable ToolboxMessageTree * m_messageTreeModel;
/* m_messageTreeModel points at the messageTree of the tree (describing the
* whole model) where we are located. It enables to know which rows are leaves

View File

@@ -12,9 +12,9 @@ protected:
#if ESCHER_VIEW_LOGGING
const char * className() const override;
#endif
virtual int numberOfSubviews() const override;
virtual void layoutSubviews(bool force = false) override;
virtual View * subviewAtIndex(int index) override;
int numberOfSubviews() const override;
void layoutSubviews(bool force = false) override;
View * subviewAtIndex(int index) override;
View * m_contentView;
private:
const Window * window() const override;

View File

@@ -1,15 +1,14 @@
#include <escher/clipboard.h>
#include <algorithm>
static Clipboard s_clipboard;
static inline int minInt(int x, int y) { return x < y ? x : y; }
Clipboard * Clipboard::sharedClipboard() {
return &s_clipboard;
}
void Clipboard::store(const char * storedText, int length) {
strlcpy(m_textBuffer, storedText, length == -1 ? TextField::maxBufferSize() : minInt(TextField::maxBufferSize(), length + 1));
strlcpy(m_textBuffer, storedText, length == -1 ? TextField::maxBufferSize() : std::min(TextField::maxBufferSize(), length + 1));
}
void Clipboard::reset() {

View File

@@ -1,9 +1,10 @@
#include <escher/expression_field.h>
#include <poincare/preferences.h>
#include <assert.h>
#include <algorithm>
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
constexpr KDCoordinate ExpressionField::k_maximalHeight;
constexpr KDCoordinate ExpressionField::k_minimalHeight;
ExpressionField::ExpressionField(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
Responder(parentResponder),
@@ -117,6 +118,6 @@ bool ExpressionField::handleEventWithText(const char * text, bool indentation, b
KDCoordinate ExpressionField::inputViewHeight() const {
return k_separatorThickness
+ (editionIsInTextField() ? k_minimalHeight :
minCoordinate(k_maximalHeight,
maxCoordinate(k_minimalHeight, m_layoutField.minimalSizeForOptimalDisplay().height())));
std::min(k_maximalHeight,
std::max(k_minimalHeight, m_layoutField.minimalSizeForOptimalDisplay().height())));
}

View File

@@ -1,10 +1,9 @@
#include <escher/expression_view.h>
#include <escher/palette.h>
#include <algorithm>
using namespace Poincare;
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
ExpressionView::ExpressionView(float horizontalAlignment, float verticalAlignment,
KDColor textColor, KDColor backgroundColor, Poincare::Layout * selectionStart, Poincare::Layout * selectionEnd ) :
m_layout(),
@@ -65,7 +64,7 @@ KDSize ExpressionView::minimalSizeForOptimalDisplay() const {
KDPoint ExpressionView::drawingOrigin() const {
KDSize expressionSize = m_layout.layoutSize();
return KDPoint(m_horizontalMargin + m_horizontalAlignment*(m_frame.width() - 2*m_horizontalMargin - expressionSize.width()), maxCoordinate(0, m_verticalAlignment*(m_frame.height() - expressionSize.height())));
return KDPoint(m_horizontalMargin + m_horizontalAlignment*(m_frame.width() - 2*m_horizontalMargin - expressionSize.width()), std::max<KDCoordinate>(0, m_verticalAlignment*(m_frame.height() - expressionSize.height())));
}
KDPoint ExpressionView::absoluteDrawingOrigin() const {

View File

@@ -5,11 +5,10 @@
#include <poincare/horizontal_layout.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
using namespace Poincare;
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
LayoutField::ContentView::ContentView() :
m_cursor(),
m_expressionView(0.0f, 0.5f, Palette::PrimaryText, Palette::BackgroundHard, &m_selectionStart, &m_selectionEnd),
@@ -335,29 +334,30 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool
return true;
}
Poincare::LayoutCursor * cursor = m_contentView.cursor();
// Handle special cases
if (strcmp(text, Ion::Events::Division.text()) == 0) {
m_contentView.cursor()->addFractionLayoutAndCollapseSiblings();
cursor->addFractionLayoutAndCollapseSiblings();
} else if (strcmp(text, Ion::Events::Exp.text()) == 0) {
m_contentView.cursor()->addEmptyExponentialLayout();
cursor->addEmptyExponentialLayout();
} else if (strcmp(text, Ion::Events::Power.text()) == 0) {
m_contentView.cursor()->addEmptyPowerLayout();
cursor->addEmptyPowerLayout();
} else if (strcmp(text, Ion::Events::Sqrt.text()) == 0) {
m_contentView.cursor()->addEmptySquareRootLayout();
cursor->addEmptySquareRootLayout();
} else if (strcmp(text, Ion::Events::Square.text()) == 0) {
m_contentView.cursor()->addEmptySquarePowerLayout();
cursor->addEmptySquarePowerLayout();
} else if (strcmp(text, Ion::Events::EE.text()) == 0) {
m_contentView.cursor()->addEmptyTenPowerLayout();
cursor->addEmptyTenPowerLayout();
} else if ((strcmp(text, "[") == 0) || (strcmp(text, "]") == 0)) {
m_contentView.cursor()->addEmptyMatrixLayout();
cursor->addEmptyMatrixLayout();
} else if((strcmp(text, Ion::Events::Multiplication.text())) == 0){
m_contentView.cursor()->addMultiplicationPointLayout();
cursor->addMultiplicationPointLayout();
} else {
Expression resultExpression = Expression::Parse(text, nullptr);
if (resultExpression.isUninitialized()) {
// The text is not parsable (for instance, ",") and is added char by char.
KDSize previousLayoutSize = minimalSizeForOptimalDisplay();
m_contentView.cursor()->insertText(text, forceCursorRightOfText);
cursor->insertText(text, forceCursorRightOfText);
reload(previousLayoutSize);
return true;
}
@@ -431,6 +431,30 @@ void LayoutField::deleteSelection() {
m_contentView.deleteSelection();
}
#define static_assert_immediately_follows(a, b) static_assert( \
static_cast<uint8_t>(a) + 1 == static_cast<uint8_t>(b), \
"Ordering error" \
)
#define static_assert_sequential(a, b, c, d) \
static_assert_immediately_follows(a, b); \
static_assert_immediately_follows(b, c); \
static_assert_immediately_follows(c, d);
static_assert_sequential(
Ion::Events::Left,
Ion::Events::Up,
Ion::Events::Down,
Ion::Events::Right
);
static inline bool IsMoveEvent(Ion::Events::Event event) {
return
static_cast<uint8_t>(event) >= static_cast<uint8_t>(Ion::Events::Left) &&
static_cast<uint8_t>(event) <= static_cast<uint8_t>(Ion::Events::Right);
}
bool LayoutField::privateHandleEvent(Ion::Events::Event event) {
if (m_delegate && m_delegate->layoutFieldDidReceiveEvent(this, event)) {
return true;
@@ -455,7 +479,7 @@ bool LayoutField::privateHandleEvent(Ion::Events::Event event) {
/* if move event was not caught neither by privateHandleMoveEvent nor by
* layoutFieldShouldFinishEditing, we handle it here to avoid bubbling the
* event up. */
if ((event == Ion::Events::Up || event == Ion::Events::Down || event == Ion::Events::Left || event == Ion::Events::Right) && isEditing()) {
if (IsMoveEvent(event) && isEditing()) {
return true;
}
if ((event == Ion::Events::OK || event == Ion::Events::EXE) && !isEditing()) {
@@ -476,11 +500,7 @@ bool LayoutField::privateHandleEvent(Ion::Events::Event event) {
setEditing(true);
}
if (event.hasText()) {
if(event.text() == "%" && Ion::Events::isLockActive() ){
m_contentView.cursor()->performBackspace();
} else {
handleEventWithText(event.text());
}
handleEventWithText(event.text());
} else if (event == Ion::Events::Paste) {
handleEventWithText(Clipboard::sharedClipboard()->storedText(), false, true);
} else {
@@ -508,15 +528,6 @@ bool LayoutField::privateHandleEvent(Ion::Events::Event event) {
return false;
}
#define static_assert_immediately_follows(a, b) static_assert( \
static_cast<uint8_t>(a) + 1 == static_cast<uint8_t>(b), \
"Ordering error" \
)
#define static_assert_sequential(a, b, c, d) \
static_assert_immediately_follows(a, b); \
static_assert_immediately_follows(b, c); \
static_assert_immediately_follows(c, d);
static_assert_sequential(
LayoutCursor::Direction::Left,
@@ -525,18 +536,6 @@ static_assert_sequential(
LayoutCursor::Direction::Right
);
static_assert_sequential(
Ion::Events::Left,
Ion::Events::Up,
Ion::Events::Down,
Ion::Events::Right
);
static inline bool IsMoveEvent(Ion::Events::Event event) {
return
static_cast<uint8_t>(event) >= static_cast<uint8_t>(Ion::Events::Left) &&
static_cast<uint8_t>(event) <= static_cast<uint8_t>(Ion::Events::Right);
}
static inline LayoutCursor::Direction DirectionForMoveEvent(Ion::Events::Event event) {
assert(IsMoveEvent(event));
@@ -612,8 +611,8 @@ void LayoutField::scrollToBaselinedRect(KDRect rect, KDCoordinate baseline) {
scrollToContentRect(rect, true);
// Show the rect area around its baseline
KDCoordinate underBaseline = rect.height() - baseline;
KDCoordinate minAroundBaseline = minCoordinate(baseline, underBaseline);
minAroundBaseline = minCoordinate(minAroundBaseline, bounds().height() / 2);
KDCoordinate minAroundBaseline = std::min(baseline, underBaseline);
minAroundBaseline = std::min<KDCoordinate>(minAroundBaseline, bounds().height() / 2);
KDRect balancedRect(rect.x(), rect.y() + baseline - minAroundBaseline, rect.width(), 2 * minAroundBaseline);
scrollToContentRect(balancedRect, true);
}

33
escher/src/metric.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include <escher/metric.h>
constexpr KDCoordinate Metric::CellMargin;
constexpr KDCoordinate Metric::CommonLeftMargin;
constexpr KDCoordinate Metric::CommonRightMargin;
constexpr KDCoordinate Metric::CommonTopMargin;
constexpr KDCoordinate Metric::CommonBottomMargin;
constexpr KDCoordinate Metric::CommonLargeMargin;
constexpr KDCoordinate Metric::CommonSmallMargin;
constexpr KDCoordinate Metric::TitleBarExternHorizontalMargin;
constexpr KDCoordinate Metric::TitleBarHeight;
constexpr KDCoordinate Metric::ParameterCellHeight;
constexpr KDCoordinate Metric::ModalTopMargin;
constexpr KDCoordinate Metric::ModalBottomMargin;
constexpr KDCoordinate Metric::TableCellVerticalMargin;
constexpr KDCoordinate Metric::TableCellHorizontalMargin;
constexpr KDCoordinate Metric::TabHeight;
constexpr KDCoordinate Metric::ScrollStep;
constexpr KDCoordinate Metric::PopUpLeftMargin;
constexpr KDCoordinate Metric::PopUpRightMargin;
constexpr KDCoordinate Metric::PopUpTopMargin;
constexpr KDCoordinate Metric::ExamPopUpTopMargin;
constexpr KDCoordinate Metric::ExamPopUpBottomMargin;
constexpr KDCoordinate Metric::StoreRowHeight;
constexpr KDCoordinate Metric::ToolboxRowHeight;
constexpr KDCoordinate Metric::StackTitleHeight;
constexpr KDCoordinate Metric::FractionAndConjugateHorizontalOverflow;
constexpr KDCoordinate Metric::FractionAndConjugateHorizontalMargin;
constexpr KDCoordinate Metric::MinimalBracketAndParenthesisHeight;
constexpr KDCoordinate Metric::CellSeparatorThickness;
constexpr KDCoordinate Metric::TableSeparatorThickness;
constexpr KDCoordinate Metric::ExpressionViewHorizontalMargin;
constexpr KDCoordinate Metric::EllipsisCellWidth;

View File

@@ -4,9 +4,7 @@
extern "C" {
#include <assert.h>
}
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
#include <algorithm>
ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) :
View(),
@@ -76,8 +74,8 @@ void ScrollView::scrollToContentPoint(KDPoint p, bool allowOverscroll) {
// Handle cases when the size of the view has decreased.
setContentOffset(KDPoint(
minCoordinate(contentOffset().x(), maxCoordinate(minimalSizeForOptimalDisplay().width() - bounds().width(), 0)),
minCoordinate(contentOffset().y(), maxCoordinate(minimalSizeForOptimalDisplay().height() - bounds().height(), 0))));
std::min(contentOffset().x(), std::max<KDCoordinate>(minimalSizeForOptimalDisplay().width() - bounds().width(), KDCoordinate{0})),
std::min(contentOffset().y(), std::max<KDCoordinate>(minimalSizeForOptimalDisplay().height() - bounds().height(), 0))));
}
void ScrollView::scrollToContentRect(KDRect rect, bool allowOverscroll) {

View File

@@ -1,9 +1,7 @@
#include <escher/scrollable_view.h>
#include <escher/metric.h>
#include <assert.h>
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
#include <algorithm>
ScrollableView::ScrollableView(Responder * parentResponder, View * view, ScrollViewDataSource * dataSource) :
Responder(parentResponder),
@@ -17,25 +15,25 @@ bool ScrollableView::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Left) {
KDCoordinate movementToEdge = contentOffset().x();
if (movementToEdge > 0) {
translation = KDPoint(-minCoordinate(Metric::ScrollStep, movementToEdge), 0);
translation = KDPoint(-std::min(Metric::ScrollStep, movementToEdge), 0);
}
}
if (event == Ion::Events::Right) {
KDCoordinate movementToEdge = minimalSizeForOptimalDisplay().width() - bounds().width() - contentOffset().x();
if (movementToEdge > 0) {
translation = KDPoint(minCoordinate(Metric::ScrollStep, movementToEdge), 0);
translation = KDPoint(std::min(Metric::ScrollStep, movementToEdge), 0);
}
}
if (event == Ion::Events::Up) {
KDCoordinate movementToEdge = contentOffset().y();
if (movementToEdge > 0) {
translation = KDPoint(0, -minCoordinate(Metric::ScrollStep, movementToEdge));
translation = KDPoint(0, -std::min(Metric::ScrollStep, movementToEdge));
}
}
if (event == Ion::Events::Down) {
KDCoordinate movementToEdge = minimalSizeForOptimalDisplay().height() - bounds().height() - contentOffset().y();
if (movementToEdge > 0) {
translation = KDPoint(0, minCoordinate(Metric::ScrollStep, movementToEdge));
translation = KDPoint(0, std::min(Metric::ScrollStep, movementToEdge));
}
}
if (translation != KDPointZero) {
@@ -51,7 +49,7 @@ void ScrollableView::reloadScroll(bool forceReLayout) {
KDSize ScrollableView::contentSize() const {
KDSize viewSize = ScrollView::contentSize();
KDCoordinate viewWidth = maxCoordinate(viewSize.width(), maxContentWidthDisplayableWithoutScrolling());
KDCoordinate viewHeight = maxCoordinate(viewSize.height(), maxContentHeightDisplayableWithoutScrolling());
KDCoordinate viewWidth = std::max(viewSize.width(), maxContentWidthDisplayableWithoutScrolling());
KDCoordinate viewHeight = std::max(viewSize.height(), maxContentHeightDisplayableWithoutScrolling());
return KDSize(viewWidth, viewHeight);
}

View File

@@ -1,9 +1,7 @@
#include <escher/table_cell.h>
#include <escher/palette.h>
#include <escher/metric.h>
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
#include <algorithm>
TableCell::TableCell(Layout layout) :
Bordered(),
@@ -95,21 +93,21 @@ void TableCell::layoutSubviews(bool force) {
KDCoordinate y = k_separatorThickness;
if (label) {
y += k_verticalMargin;
KDCoordinate labelHeight = minCoordinate(labelSize.height(), height - y - k_separatorThickness - k_verticalMargin);
KDCoordinate labelHeight = std::min<KDCoordinate>(labelSize.height(), height - y - k_separatorThickness - k_verticalMargin);
label->setFrame(KDRect(horizontalMargin, y, width-2*horizontalMargin, labelHeight), force);
y += labelHeight + k_verticalMargin;
}
horizontalMargin = k_separatorThickness + k_horizontalMargin;
y = maxCoordinate(y, height - k_separatorThickness - withMargin(accessorySize.height(), Metric::TableCellVerticalMargin) - withMargin(subAccessorySize.height(), 0));
y = std::max<KDCoordinate>(y, height - k_separatorThickness - withMargin(accessorySize.height(), Metric::TableCellVerticalMargin) - withMargin(subAccessorySize.height(), 0));
if (subAccessory) {
KDCoordinate subAccessoryHeight = minCoordinate(subAccessorySize.height(), height - y - k_separatorThickness - Metric::TableCellVerticalMargin);
KDCoordinate subAccessoryHeight = std::min<KDCoordinate>(subAccessorySize.height(), height - y - k_separatorThickness - Metric::TableCellVerticalMargin);
accessory->setFrame(KDRect(horizontalMargin, y, width - 2*horizontalMargin, subAccessoryHeight), force);
y += subAccessoryHeight;
}
horizontalMargin = k_separatorThickness + accessoryMargin();
y = maxCoordinate(y, height - k_separatorThickness - withMargin(accessorySize.height(), Metric::TableCellVerticalMargin));
y = std::max<KDCoordinate>(y, height - k_separatorThickness - withMargin(accessorySize.height(), Metric::TableCellVerticalMargin));
if (accessory) {
KDCoordinate accessoryHeight = minCoordinate(accessorySize.height(), height - y - k_separatorThickness - Metric::TableCellVerticalMargin);
KDCoordinate accessoryHeight = std::min<KDCoordinate>(accessorySize.height(), height - y - k_separatorThickness - Metric::TableCellVerticalMargin);
accessory->setFrame(KDRect(horizontalMargin, y, width - 2*horizontalMargin, accessoryHeight), force);
}
} else {
@@ -137,29 +135,29 @@ void TableCell::layoutSubviews(bool force) {
KDCoordinate verticalMargin = k_separatorThickness;
KDCoordinate x = 0;
KDCoordinate labelX = k_separatorThickness + labelMargin();
KDCoordinate subAccessoryX = maxCoordinate(k_separatorThickness + k_horizontalMargin, width - k_separatorThickness - withMargin(accessorySize.width(), accessoryMargin()) - withMargin(subAccessorySize.width(), 0));
KDCoordinate accessoryX = maxCoordinate(k_separatorThickness + accessoryMargin(), width - k_separatorThickness - withMargin(accessorySize.width(), accessoryMargin()));
KDCoordinate subAccessoryX = std::max(k_separatorThickness + k_horizontalMargin, width - k_separatorThickness - withMargin(accessorySize.width(), accessoryMargin()) - withMargin(subAccessorySize.width(), 0));
KDCoordinate accessoryX = std::max(k_separatorThickness + accessoryMargin(), width - k_separatorThickness - withMargin(accessorySize.width(), accessoryMargin()));
if (label) {
x = labelX;
KDCoordinate labelWidth = minCoordinate(labelSize.width(), width - x - k_separatorThickness - labelMargin());
KDCoordinate labelWidth = std::min<KDCoordinate>(labelSize.width(), width - x - k_separatorThickness - labelMargin());
if (m_layout == Layout::HorizontalRightOverlap) {
labelWidth = minCoordinate(labelWidth, subAccessoryX - x - labelMargin());
labelWidth = std::min<KDCoordinate>(labelWidth, subAccessoryX - x - labelMargin());
}
label->setFrame(KDRect(x, verticalMargin, labelWidth, height-2*verticalMargin), force);
x += labelWidth + labelMargin();
}
if (subAccessory) {
x = maxCoordinate(x, subAccessoryX);
KDCoordinate subAccessoryWidth = minCoordinate(subAccessorySize.width(), width - x - k_separatorThickness - k_horizontalMargin);
x = std::max(x, subAccessoryX);
KDCoordinate subAccessoryWidth = std::min<KDCoordinate>(subAccessorySize.width(), width - x - k_separatorThickness - k_horizontalMargin);
if (m_layout == Layout::HorizontalRightOverlap) {
subAccessoryWidth = minCoordinate(subAccessoryWidth, accessoryX - x);
subAccessoryWidth = std::min<KDCoordinate>(subAccessoryWidth, accessoryX - x);
}
subAccessory->setFrame(KDRect(x, verticalMargin, subAccessoryWidth, height-2*verticalMargin), force);
x += subAccessoryWidth;
}
if (accessory) {
x = maxCoordinate(x, accessoryX);
KDCoordinate accessoryWidth = minCoordinate(accessorySize.width(), width - x - k_separatorThickness - accessoryMargin());
x = std::max(x, accessoryX);
KDCoordinate accessoryWidth = std::min<KDCoordinate>(accessorySize.width(), width - x - k_separatorThickness - accessoryMargin());
accessory->setFrame(KDRect(x, verticalMargin, accessoryWidth, height-2*verticalMargin), force);
}
}

View File

@@ -4,8 +4,7 @@
extern "C" {
#include <assert.h>
}
#define MIN(x,y) ((x)<(y) ? (x) : (y))
#include <algorithm>
TableView::TableView(TableViewDataSource * dataSource, ScrollViewDataSource * scrollDataSource) :
ScrollView(&m_contentView, scrollDataSource),
@@ -200,7 +199,7 @@ int TableView::ContentView::numberOfFullyDisplayableColumns() const {
int TableView::ContentView::numberOfDisplayableRows() const {
int rowOffset = rowsScrollingOffset();
int displayedHeightWithOffset = m_dataSource->indexFromCumulatedHeight(m_tableView->bounds().height() + m_tableView->contentOffset().y());
return MIN(
return std::min(
m_dataSource->numberOfRows(),
displayedHeightWithOffset + 1
) - rowOffset;
@@ -209,7 +208,7 @@ int TableView::ContentView::numberOfDisplayableRows() const {
int TableView::ContentView::numberOfDisplayableColumns() const {
int columnOffset = columnsScrollingOffset();
int displayedWidthWithOffset = m_dataSource->indexFromCumulatedWidth(m_tableView->bounds().width() + m_tableView->contentOffset().x());
return MIN(
return std::min(
m_dataSource->numberOfColumns(),
displayedWidthWithOffset + 1
) - columnOffset;

Some files were not shown because too many files have changed in this diff Show More