[Fix] Fix conflicts

This commit is contained in:
Quentin Guidée
2020-03-11 19:12:20 +01:00
61 changed files with 396 additions and 265 deletions

View File

@@ -6,7 +6,7 @@ using namespace Poincare;
namespace Calculation {
ComplexGraphView::ComplexGraphView(ComplexModel * complexModel) :
CurveView(complexModel),
LabeledCurveView(complexModel),
m_complex(complexModel)
{
}

View File

@@ -1,24 +1,19 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H
#include "../../shared/curve_view.h"
#include "../../shared/labeled_curve_view.h"
#include "complex_model.h"
#include "illustration_cell.h"
namespace Calculation {
class ComplexGraphView : public Shared::CurveView {
class ComplexGraphView : public Shared::LabeledCurveView {
public:
ComplexGraphView(ComplexModel * complexModel);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override {
return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]);
}
// '-' + significant digits + ".E-" + 2 digits (the represented dot is a float, so it is bounded by 1E38 and 1E-38
size_t labelMaxGlyphLengthSize() const override { return 1 + Poincare::Preferences::VeryShortNumberOfSignificantDigits + 3 + 2; }
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
ComplexModel * m_complex;
};

View File

@@ -12,7 +12,6 @@ public:
TrigonometryGraphView(TrigonometryModel * model);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override { return nullptr; }
TrigonometryModel * m_model;
};

View File

@@ -58,7 +58,7 @@ App::App(Snapshot * snapshot) :
m_listFooter(&m_listHeader, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey),
m_listHeader(&m_listStackViewController, &m_listFooter, &m_listController),
m_listStackViewController(&m_tabViewController, &m_listHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController),
m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController),
m_graphStackViewController(&m_tabViewController, &m_graphHeader),

View File

@@ -11,8 +11,8 @@ 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 * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion),
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),
m_view(curveViewRange, m_cursor, &m_bannerView, &m_cursorView),
m_graphRange(curveViewRange),

View File

@@ -15,7 +15,7 @@ namespace Graph {
class GraphController : public Shared::FunctionGraphController, public GraphControllerHelper {
public:
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
I18n::Message emptyMessage() override;
void viewWillAppear() override;
bool displayDerivativeInBanner() const { return m_displayDerivativeInBanner; }

View File

@@ -38,7 +38,7 @@ App * App::Snapshot::unpack(Container * container) {
}
void App::Snapshot::reset() {
m_store.deleteAllPairs();
m_store.reset();
m_modelVersion = 0;
m_rangeVersion = 0;
setActiveTab(0);
@@ -59,7 +59,7 @@ App::App(Snapshot * snapshot, Poincare::Context * parentContext) :
m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()),
m_calculationAlternateEmptyViewController(&m_calculationHeader, &m_calculationController, &m_calculationController),
m_calculationHeader(&m_tabViewController, &m_calculationAlternateEmptyViewController, &m_calculationController),
m_graphController(&m_graphAlternateEmptyViewController, this, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()),
m_graphController(&m_graphAlternateEmptyViewController, this, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()),
m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController),
m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController),
m_graphStackViewController(&m_tabViewController, &m_graphHeader),

View File

@@ -31,6 +31,7 @@ public:
int * graphSelectedDotIndex() { return &m_graphSelectedDotIndex; }
int * selectedSeriesIndex() { return &m_selectedSeriesIndex; }
uint32_t * modelVersion() { return &m_modelVersion; }
uint32_t * previousModelsVersions() { return m_store.seriesChecksum(); }
uint32_t * rangeVersion() { return &m_rangeVersion; }
private:
void tidy() override;

View File

@@ -14,8 +14,8 @@ 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 * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, store, &m_view, cursor, modelVersion, rangeVersion),
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) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, store, &m_view, cursor, modelVersion, previousModelsVersions, rangeVersion),
m_crossCursorView(),
m_roundCursorView(),
m_bannerView(this, inputEventHandlerDelegate, this),
@@ -351,6 +351,11 @@ uint32_t GraphController::modelVersion() {
return m_store->storeChecksum();
}
uint32_t GraphController::modelVersionAtIndex(size_t i) {
assert(i < numberOfMemoizedVersions());
return *(m_store->seriesChecksum() + i);
}
uint32_t GraphController::rangeVersion() {
return m_store->rangeChecksum();
}

View File

@@ -16,7 +16,7 @@ namespace Regression {
class GraphController : public Shared::InteractiveCurveViewController {
public:
GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex);
GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex);
ViewController * initialisationParameterController() override;
bool isEmpty() const override;
I18n::Message emptyMessage() override;
@@ -43,7 +43,9 @@ private:
// InteractiveCurveViewController
void initCursorParameters() override;
uint32_t modelVersion() override;
uint32_t modelVersionAtIndex(size_t i) override;
uint32_t rangeVersion() override;
size_t numberOfMemoizedVersions() const override { return Store::k_numberOfSeries; }
int selectedCurveIndex() const override { return *m_selectedSeriesIndex; }
bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const override;
Poincare::Coordinate2D<double> xyValues(int curveIndex, double x, Poincare::Context * context) const override;

View File

@@ -9,10 +9,8 @@ using namespace Shared;
namespace Regression {
GraphView::GraphView(Store * store, CurveViewCursor * cursor, BannerView * bannerView, Shared::CursorView * cursorView) :
CurveView(store, cursor, bannerView, cursorView),
m_store(store),
m_xLabels{},
m_yLabels{}
LabeledCurveView(store, cursor, bannerView, cursorView),
m_store(store)
{
}
@@ -41,13 +39,4 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const {
}
}
char * GraphView::label(Axis axis, int index) const {
if (axis == Axis::Vertical) {
assert(index < k_maxNumberOfXLabels);
return (char *)m_yLabels[index];
}
assert(index < k_maxNumberOfYLabels);
return (char *)m_xLabels[index];
}
}

View File

@@ -3,19 +3,16 @@
#include "store.h"
#include "../constant.h"
#include "../shared/curve_view.h"
#include "../shared/labeled_curve_view.h"
namespace Regression {
class GraphView : public Shared::CurveView {
class GraphView : public Shared::LabeledCurveView {
public:
GraphView(Store * store, Shared::CurveViewCursor * cursor, Shared::BannerView * bannerView, Shared::CursorView * cursorView);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override;
Store * m_store;
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
};
}

View File

@@ -19,13 +19,14 @@ static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regressio
Store::Store() :
InteractiveCurveViewRange(),
DoublePairStore(),
m_seriesChecksum{0, 0, 0},
m_angleUnit(Poincare::Preferences::AngleUnit::Degree)
{
for (int i = 0; i < k_numberOfSeries; i++) {
m_regressionTypes[i] = Model::Type::Linear;
m_regressionChanged[i] = false;
}
resetMemoization();
}
void Store::reset() {
deleteAllPairs();
resetMemoization();
}
void Store::tidy() {
@@ -188,6 +189,13 @@ double Store::doubleCastedNumberOfPairsOfSeries(int series) const {
return DoublePairStore::numberOfPairsOfSeries(series);
}
void Store::resetMemoization() {
assert(((int)Model::Type::Linear) == 0);
memset(m_seriesChecksum, 0, sizeof(m_seriesChecksum));
memset(m_regressionTypes, 0, sizeof(m_regressionTypes));
memset(m_regressionChanged, 0, sizeof(m_regressionChanged));
}
float Store::maxValueOfColumn(int series, int i) const {
float maxColumn = -FLT_MAX;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {

View File

@@ -22,6 +22,7 @@ class Store : public Shared::InteractiveCurveViewRange, public Shared::DoublePai
public:
Store();
void reset();
// Clean pool
void tidy();
@@ -35,6 +36,7 @@ public:
assert((int)m_regressionTypes[series] >= 0 && (int)m_regressionTypes[series] < Model::k_numberOfModels);
return regressionModel((int)m_regressionTypes[series]);
}
uint32_t * seriesChecksum() { return m_seriesChecksum; }
// Dots
/* Return the closest dot to abscissa x above the regression curve if
@@ -70,6 +72,7 @@ public:
double squaredCorrelationCoefficient(int series) const;
private:
constexpr static float k_displayHorizontalMarginRatio = 0.05f;
void resetMemoization();
float maxValueOfColumn(int series, int i) const; //TODO LEA why float ?
float minValueOfColumn(int series, int i) const; //TODO LEA why float ?
Model * regressionModel(int index);

View File

@@ -55,7 +55,7 @@ App::App(Snapshot * snapshot) :
m_listFooter(&m_listHeader, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey),
m_listHeader(nullptr, &m_listFooter, &m_listController),
m_listStackViewController(&m_tabViewController, &m_listHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->functionStore(), snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->functionStore(), snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController),
m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController),
m_graphStackViewController(&m_tabViewController, &m_graphHeader),

View File

@@ -11,8 +11,8 @@ 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 * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion),
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),
m_view(sequenceStore, graphRange, m_cursor, &m_bannerView, &m_cursorView),
m_graphRange(graphRange),

View File

@@ -14,7 +14,7 @@ namespace Sequence {
class GraphController final : public Shared::FunctionGraphController {
public:
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
I18n::Message emptyMessage() override;
void viewWillAppear() override;
TermSumController * termSumController() { return &m_termSumController; }

View File

@@ -28,7 +28,6 @@ void Sequence::tidy() {
m_firstInitialCondition.tidyName();
m_secondInitialCondition.tidy();
m_secondInitialCondition.tidyName();
m_nameLayout = Layout();
}
Sequence::Type Sequence::type() const {
@@ -81,13 +80,10 @@ void Sequence::setInitialRank(int rank) {
}
Poincare::Layout Sequence::nameLayout() {
if (m_nameLayout.isUninitialized()) {
m_nameLayout = HorizontalLayout::Builder(
CodePointLayout::Builder(fullName()[0], KDFont::SmallFont),
VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Subscript)
);
}
return m_nameLayout;
return HorizontalLayout::Builder(
CodePointLayout::Builder(fullName()[0], KDFont::SmallFont),
VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Subscript)
);
}
bool Sequence::isDefined() {

View File

@@ -24,8 +24,9 @@ public:
DoubleRecurrence = 2
};
Sequence(Ion::Storage::Record record = Record()) :
Function(record),
m_nameLayout() {}
Function(record)
{
}
I18n::Message parameterMessageName() const override;
CodePoint symbol() const override { return 'n'; }
void tidy() override;
@@ -111,7 +112,7 @@ private:
public:
SequenceModel() : Shared::ExpressionModel(), m_name() {}
void tidyName() { m_name = Poincare::Layout(); }
virtual Poincare::Layout name(Sequence * sequence);
Poincare::Layout name(Sequence * sequence);
protected:
virtual void buildName(Sequence * sequence) = 0;
Poincare::Layout m_name;
@@ -153,7 +154,6 @@ private:
DefinitionModel m_definition;
FirstInitialConditionModel m_firstInitialCondition;
SecondInitialConditionModel m_secondInitialCondition;
Poincare::Layout m_nameLayout;
};
}

View File

@@ -45,6 +45,7 @@ app_shared_src = $(addprefix apps/shared/,\
interactive_curve_view_controller.cpp \
interval.cpp \
interval_parameter_controller.cpp \
labeled_curve_view.cpp \
language_controller.cpp \
layout_field_delegate.cpp \
list_parameter_controller.cpp \

View File

@@ -1,11 +1,12 @@
#ifndef SHARED_CURSOR_VIEW_H
#define SHARED_CURSOR_VIEW_H
#include <escher.h>
#include <escher/transparent_view.h>
#include <escher/palette.h>
namespace Shared {
class CursorView : public View {
class CursorView : public TransparentView {
public:
virtual void setCursorFrame(KDRect frame, bool force) { View::setFrame(frame, force); }
void drawRect(KDContext * ctx, KDRect rect) const override;

View File

@@ -100,7 +100,7 @@ private:
float min(Axis axis) const;
float max(Axis axis) const;
float gridUnit(Axis axis) const;
virtual char * label(Axis axis, int index) const = 0;
virtual char * label(Axis axis, int index) const { return nullptr; }
virtual size_t labelMaxGlyphLengthSize() const { return k_labelBufferMaxGlyphLength; }
int numberOfLabels(Axis axis) const;
/* Recursively join two dots (dichotomy). The method stops when the

View File

@@ -11,11 +11,14 @@ FunctionApp::Snapshot::Snapshot() :
m_rangeVersion(0),
m_angleUnitVersion(Preferences::AngleUnit::Radian)
{
assert(m_previousModelsVersions[0] == 0);
}
void FunctionApp::Snapshot::reset() {
m_indexFunctionSelectedByCursor = 0;
m_modelVersion = 0;
assert(sizeof(m_previousModelsVersions) == sizeof(uint32_t) * FunctionGraphController::sNumberOfMemoizedModelVersions);
memset(m_previousModelsVersions, 0, sizeof(m_previousModelsVersions));
m_rangeVersion = 0;
setActiveTab(0);
}

View File

@@ -2,6 +2,7 @@
#define SHARED_FUNCTION_APP_H
#include "expression_field_delegate_app.h"
#include "function_graph_controller.h"
#include "function_store.h"
#include "curve_view_cursor.h"
#include "values_controller.h"
@@ -15,6 +16,7 @@ public:
Snapshot();
CurveViewCursor * cursor() { return &m_cursor; }
uint32_t * modelVersion() { return &m_modelVersion; }
uint32_t * previousModelsVersions() { return m_previousModelsVersions; }
uint32_t * rangeVersion() { return &m_rangeVersion; }
Poincare::Preferences::AngleUnit * angleUnitVersion() { return &m_angleUnitVersion; }
virtual FunctionStore * functionStore() = 0;
@@ -26,6 +28,7 @@ public:
private:
int m_indexFunctionSelectedByCursor;
uint32_t m_modelVersion;
uint32_t m_previousModelsVersions[FunctionGraphController::sNumberOfMemoizedModelVersions];
uint32_t m_rangeVersion;
Poincare::Preferences::AngleUnit m_angleUnitVersion;
};

View File

@@ -15,8 +15,8 @@ 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 * rangeVersion, Preferences::AngleUnit * angleUnitVersion) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, interactiveRange, curveView, cursor, modelVersion, rangeVersion),
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),
m_angleUnitVersion(angleUnitVersion),
m_indexFunctionSelectedByCursor(indexFunctionSelectedByCursor)
@@ -183,6 +183,10 @@ uint32_t FunctionGraphController::modelVersion() {
return functionStore()->storeChecksum();
}
uint32_t FunctionGraphController::modelVersionAtIndex(size_t i) {
return functionStore()->storeChecksumAtIndex(i);
}
uint32_t FunctionGraphController::rangeVersion() {
return interactiveCurveViewRange()->rangeChecksum();
}

View File

@@ -13,7 +13,8 @@ namespace Shared {
class FunctionGraphController : public InteractiveCurveViewController, public FunctionBannerDelegate {
public:
FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion);
static constexpr size_t sNumberOfMemoizedModelVersions = 5;
FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion);
bool isEmpty() const override;
ViewController * initialisationParameterController() override;
void didBecomeFirstResponder() override;
@@ -49,7 +50,9 @@ private:
// InteractiveCurveViewController
bool moveCursorVertically(int direction) override;
uint32_t modelVersion() override;
uint32_t modelVersionAtIndex(size_t i) override;
uint32_t rangeVersion() override;
size_t numberOfMemoizedVersions() const override { return sNumberOfMemoizedModelVersions; }
InitialisationParameterController m_initialisationParameterController;
Poincare::Preferences::AngleUnit * m_angleUnitVersion;

View File

@@ -8,13 +8,11 @@ namespace Shared {
FunctionGraphView::FunctionGraphView(InteractiveCurveViewRange * graphRange,
CurveViewCursor * cursor, BannerView * bannerView, CursorView * cursorView) :
CurveView(graphRange, cursor, bannerView, cursorView),
LabeledCurveView(graphRange, cursor, bannerView, cursorView),
m_selectedRecord(),
m_highlightedStart(NAN),
m_highlightedEnd(NAN),
m_shouldColorHighlighted(false),
m_xLabels{},
m_yLabels{},
m_context(nullptr)
{
}
@@ -67,10 +65,6 @@ void FunctionGraphView::setAreaHighlightColor(bool highlightColor) {
}
}
char * FunctionGraphView::label(Axis axis, int index) const {
return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]);
}
void FunctionGraphView::reloadBetweenBounds(float start, float end) {
if (start == end) {
return;

View File

@@ -2,14 +2,14 @@
#define SHARED_FUNCTION_GRAPH_VIEW_H
#include <escher.h>
#include "curve_view.h"
#include "labeled_curve_view.h"
#include "function.h"
#include "../constant.h"
#include "interactive_curve_view_range.h"
namespace Shared {
class FunctionGraphView : public CurveView {
class FunctionGraphView : public LabeledCurveView {
public:
FunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor,
BannerView * bannerView, CursorView * cursorView);
@@ -26,9 +26,6 @@ protected:
float m_highlightedEnd;
bool m_shouldColorHighlighted;
private:
char * label(Axis axis, int index) const override;
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
Poincare::Context * m_context;
};

View File

@@ -6,4 +6,11 @@ uint32_t FunctionStore::storeChecksum() {
return Ion::Storage::sharedStorage()->checksum();
}
uint32_t FunctionStore::storeChecksumAtIndex(size_t i) {
if (numberOfActiveFunctions() <= i) {
return 0;
}
return activeRecordAtIndex(i).checksum();
}
}

View File

@@ -13,6 +13,7 @@ class FunctionStore : public ExpressionModelStore {
public:
FunctionStore() : ExpressionModelStore() {}
uint32_t storeChecksum();
uint32_t storeChecksumAtIndex(size_t i);
int numberOfActiveFunctions() const {
return numberOfModelsSatisfyingTest(&isFunctionActive, nullptr);
}

View File

@@ -7,10 +7,11 @@ using namespace Poincare;
namespace Shared {
InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion) :
InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion) :
SimpleInteractiveCurveViewController(parentResponder, cursor),
ButtonRowDelegate(header, nullptr),
m_modelVersion(modelVersion),
m_previousModelsVersions(previousModelsVersions),
m_rangeVersion(rangeVersion),
m_rangeParameterController(this, inputEventHandlerDelegate, interactiveRange),
m_zoomParameterController(this, interactiveRange, curveView),
@@ -132,11 +133,43 @@ Responder * InteractiveCurveViewController::defaultController() {
return tabController();
}
bool InteractiveCurveViewController::previousModelsWereAllDeleted() {
bool result = true;
const int modelsCount = numberOfCurves();
const int memoizationCount = numberOfMemoizedVersions();
// Look for a current model that is the same as in the previous version
for (int i = 0; i < modelsCount; i++) {
uint32_t currentVersion = modelVersionAtIndex(i);
for (int j = 0; j < memoizationCount; j++) {
uint32_t * previousVersion = m_previousModelsVersions + j;
if (currentVersion == *previousVersion) {
result = false;
break;
}
}
if (!result) {
break;
}
}
// Update the memoization
for (int i = 0; i < memoizationCount; i++) {
uint32_t * previousVersion = m_previousModelsVersions + i;
uint32_t newVersion = modelVersionAtIndex(i);
if (*previousVersion != newVersion) {
*previousVersion = newVersion;
}
}
return result;
}
void InteractiveCurveViewController::viewWillAppear() {
SimpleInteractiveCurveViewController::viewWillAppear();
uint32_t newModelVersion = modelVersion();
if (*m_modelVersion != newModelVersion) {
if (*m_modelVersion == 0 || numberOfCurves() == 1 || shouldSetDefaultOnModelChange()) {
// Put previousModelsWereAllDeleted first to update the model versions
if (previousModelsWereAllDeleted() || *m_modelVersion == 0 || numberOfCurves() == 1 || shouldSetDefaultOnModelChange()) {
interactiveCurveViewRange()->setDefault();
}
*m_modelVersion = newModelVersion;

View File

@@ -12,7 +12,7 @@ namespace Shared {
class InteractiveCurveViewController : public SimpleInteractiveCurveViewController, public InteractiveCurveViewRangeDelegate, public ButtonRowDelegate, public AlternateEmptyViewDefaultDelegate {
public:
InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion);
InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion);
const char * title() override;
bool handleEvent(Ion::Events::Event event) override;
@@ -28,6 +28,8 @@ public:
Responder * defaultController() override;
bool previousModelsWereAllDeleted();
void viewWillAppear() override;
void viewDidDisappear() override;
void willExitResponderChain(Responder * nextFirstResponder) override;
@@ -39,6 +41,7 @@ protected:
virtual void initCursorParameters() = 0;
virtual bool moveCursorVertically(int direction) = 0;
virtual uint32_t modelVersion() = 0;
virtual uint32_t modelVersionAtIndex(size_t i) = 0;
virtual uint32_t rangeVersion() = 0;
bool isCursorVisible();
@@ -66,7 +69,9 @@ private:
float addMargin(float x, float range, bool isVertical, bool isMin) override;
virtual bool shouldSetDefaultOnModelChange() const { return false; }
virtual size_t numberOfMemoizedVersions() const = 0;
uint32_t * m_modelVersion;
uint32_t * m_previousModelsVersions;
uint32_t * m_rangeVersion;
RangeParameterController m_rangeParameterController;
ZoomParameterController m_zoomParameterController;

View File

@@ -0,0 +1,31 @@
#include "labeled_curve_view.h"
namespace Shared {
char * HorizontallyLabeledCurveView::label(Axis axis, int index) const {
if (axis == Axis::Horizontal) {
assert(index < k_maxNumberOfXLabels);
return m_xLabels[index];
}
return nullptr;
}
char * VerticallyLabeledCurveView::label(Axis axis, int index) const {
if (axis == Axis::Vertical) {
assert(index < k_maxNumberOfYLabels);
return m_yLabels[index];
}
return nullptr;
}
char * LabeledCurveView::label(Axis axis, int index) const {
if (axis == Axis::Horizontal) {
assert(index < k_maxNumberOfXLabels);
return m_xLabels[index];
} else {
assert(index < k_maxNumberOfYLabels);
return m_yLabels[index];
}
}
}

View File

@@ -0,0 +1,37 @@
#ifndef SHARED_LABELED_CURVE_VIEW_H
#define SHARED_LABELED_CURVE_VIEW_H
#include "curve_view.h"
/* This CurveView subclass provides label storage for common use cases */
namespace Shared {
class HorizontallyLabeledCurveView : public CurveView {
public:
using CurveView::CurveView;
private:
char * label(Axis axis, int index) const override;
mutable char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
};
class VerticallyLabeledCurveView : public CurveView {
public:
using CurveView::CurveView;
private:
char * label(Axis axis, int index) const override;
mutable char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
};
class LabeledCurveView : public CurveView {
public:
using CurveView::CurveView;
private:
char * label(Axis axis, int index) const override;
mutable char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
mutable char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
};
}
#endif

View File

@@ -49,6 +49,14 @@ void RoundCursorView::setCursorFrame(KDRect f, bool force) {
CursorView::setCursorFrame(f, force);
}
void RoundCursorView::markRectAsDirty(KDRect rect) {
/* The CursorView class inherits from TransparentView, so does
* RoundCursorView. The method markRectAsDirty is thus overriden to avoid
* marking as dirty the background of the RoundCursorView in its superview.
*/
View::markRectAsDirty(rect);
}
#ifdef GRAPH_CURSOR_SPEEDUP
bool RoundCursorView::eraseCursorIfPossible() {
if (!m_underneathPixelBufferLoaded) {

View File

@@ -24,6 +24,7 @@ public:
void resetMemoization() const { m_underneathPixelBufferLoaded = false; }
#endif
private:
void markRectAsDirty(KDRect rect) override;
#ifdef GRAPH_CURSOR_SPEEDUP
bool eraseCursorIfPossible();
#endif

View File

@@ -3,7 +3,6 @@
#include <poincare/empty_layout.h>
#include <poincare/condensed_sum_layout.h>
#include <poincare/layout_helper.h>
#include <poincare/preferences.h>
#include "poincare_helpers.h"
#include <assert.h>
@@ -150,14 +149,13 @@ void SumGraphController::reloadBannerView() {
m_legendView.setEditableZone(m_cursor->x());
result = NAN;
}
m_legendView.setSumSymbol(m_step, m_startSum, endSum, result, functionLayout);
m_legendView.setSumLayout(m_step, m_startSum, endSum, result, functionLayout);
}
/* Legend View */
SumGraphController::LegendView::LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol) :
m_sum(0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground),
m_sumLayout(),
m_legend(k_font, I18n::Message::Default, 0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground),
m_editableZone(controller, m_textBuffer, k_editableZoneBufferSize, TextField::maxBufferSize(), inputEventHandlerDelegate, controller, k_font, 0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground),
m_sumSymbol(sumSymbol)
@@ -179,54 +177,41 @@ void SumGraphController::LegendView::setLegendMessage(I18n::Message message, Ste
}
void SumGraphController::LegendView::setEditableZone(double d) {
constexpr int precision = Preferences::MediumNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(d, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
char buffer[k_valuesBufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(d, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal);
m_editableZone.setText(buffer);
}
void SumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, Layout functionLayout) {
void SumGraphController::LegendView::setSumLayout(Step step, double start, double end, double result, Layout functionLayout) {
assert(step == Step::Result || functionLayout.isUninitialized());
constexpr int sigmaLength = 2;
const CodePoint sigma[sigmaLength] = {' ', m_sumSymbol};
if (step == Step::FirstParameter) {
m_sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength);
} else if (step == Step::SecondParameter) {
constexpr int precision = Preferences::MediumNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(start, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
m_sumLayout = CondensedSumLayout::Builder(
LayoutHelper::CodePointString(sigma, sigmaLength),
Poincare::Layout sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength);
if (step != Step::FirstParameter) {
char buffer[k_valuesBufferSize];
Layout endLayout;
if (step == Step::SecondParameter) {
endLayout = EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false);
} else {
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(end, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal);
endLayout = LayoutHelper::String(buffer, strlen(buffer), k_font);
}
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(start, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal);
sumLayout = CondensedSumLayout::Builder(
sumLayout,
LayoutHelper::String(buffer, strlen(buffer), k_font),
EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false));
} else {
constexpr int precision = Preferences::LargeNumberOfSignificantDigits;
constexpr int sizeForPrecision = PrintFloat::charSizeForFloatsWithPrecision(precision);
constexpr int bufferSize = 2 + sizeForPrecision;
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(start, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
Layout start = LayoutHelper::String(buffer, strlen(buffer), k_font);
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(end, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
Layout end = LayoutHelper::String(buffer, strlen(buffer), k_font);
m_sumLayout = CondensedSumLayout::Builder(
LayoutHelper::CodePointString(sigma, sigmaLength),
start,
end);
strlcpy(buffer, "= ", 3);
PoincareHelpers::ConvertFloatToText<double>(result, buffer+2, bufferSize-2, precision);
m_sumLayout = HorizontalLayout::Builder(
m_sumLayout,
functionLayout,
LayoutHelper::String(buffer, strlen(buffer), k_font));
}
m_sum.setLayout(m_sumLayout);
if (step == Step::Result) {
m_sum.setAlignment(0.5f, 0.5f);
} else {
m_sum.setAlignment(0.0f, 0.5f);
endLayout);
if (step == Step::Result) {
PoincareHelpers::ConvertFloatToText<double>(result, buffer, k_valuesBufferSize, k_valuesPrecision);
sumLayout = HorizontalLayout::Builder(
sumLayout,
functionLayout,
LayoutHelper::String("= ", 2, k_font),
LayoutHelper::String(buffer, strlen(buffer), k_font));
}
}
m_sum.setLayout(sumLayout);
m_sum.setAlignment(0.5f * (step == Step::Result), 0.5f);
layoutSubviews(step, false);
}

View File

@@ -55,9 +55,11 @@ private:
void drawRect(KDContext * ctx, KDRect rect) const override;
void setLegendMessage(I18n::Message message, Step step);
void setEditableZone(double d);
void setSumSymbol(Step step, double start, double end, double result, Poincare::Layout functionLayout);
void setSumLayout(Step step, double start, double end, double result, Poincare::Layout functionLayout);
private:
constexpr static KDCoordinate k_editableZoneBufferSize = Poincare::PrintFloat::k_maxFloatCharSize;
constexpr static size_t k_editableZoneBufferSize = Poincare::PrintFloat::k_maxFloatCharSize;
constexpr static int k_valuesPrecision = Poincare::Preferences::MediumNumberOfSignificantDigits;
constexpr static int k_valuesBufferSize = Poincare::PrintFloat::charSizeForFloatsWithPrecision(k_valuesPrecision);
constexpr static KDCoordinate k_legendHeight = 35;
constexpr static const KDFont * k_font = KDFont::SmallFont;
static KDCoordinate editableZoneWidth() { return 12*k_font->glyphSize().width(); }
@@ -69,7 +71,6 @@ private:
void layoutSubviews(bool force = false) override;
void layoutSubviews(Step step, bool force);
ExpressionView m_sum;
Poincare::Layout m_sumLayout;
MessageTextView m_legend;
TextField m_editableZone;
char m_textBuffer[k_editableZoneBufferSize];

View File

@@ -12,8 +12,4 @@ void BoxAxisView::drawRect(KDContext * ctx, KDRect rect) const {
drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, false, false, true, k_axisMargin);
}
char * BoxAxisView::label(Axis axis, int index) const {
return axis == Axis::Vertical ? nullptr : (char *)m_labels[index];
}
}

View File

@@ -3,24 +3,21 @@
#include "box_range.h"
#include "store.h"
#include "../shared/curve_view.h"
#include "../shared/labeled_curve_view.h"
#include "../constant.h"
#include <poincare/print_float.h>
namespace Statistics {
class BoxAxisView : public Shared::CurveView {
class BoxAxisView : public Shared::HorizontallyLabeledCurveView {
public:
BoxAxisView(Store * store) :
CurveView(&m_boxRange),
m_labels{},
HorizontallyLabeledCurveView(&m_boxRange),
m_boxRange(BoxRange(store))
{}
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
constexpr static KDCoordinate k_axisMargin = 3;
char * label(Axis axis, int index) const override;
char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
BoxRange m_boxRange;
};

View File

@@ -37,7 +37,6 @@ private:
static constexpr KDCoordinate k_quantileBarWidth = 2;
KDCoordinate boxLowerBoundPixel() const;
KDCoordinate boxUpperBoundPixel() const;
char * label(Axis axis, int index) const override { return nullptr; }
Store * m_store;
BoxRange m_boxRange;
int m_series;

View File

@@ -8,10 +8,9 @@ using namespace Shared;
namespace Statistics {
HistogramView::HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor, KDColor notSelectedHistogramColor, KDColor selectedBarColor) :
CurveView(store, nullptr, bannerView, nullptr),
HorizontallyLabeledCurveView(store, nullptr, bannerView, nullptr),
m_controller(controller),
m_store(store),
m_labels{},
m_highlightedBarStart(NAN),
m_highlightedBarEnd(NAN),
m_series(series),
@@ -63,10 +62,6 @@ void HistogramView::setHighlight(float start, float end) {
}
}
char * HistogramView::label(Axis axis, int index) const {
return axis == Axis::Vertical ? nullptr : (char *)m_labels[index];
}
float HistogramView::EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context) {
Store * store = (Store *)model;
float totalSize = ((float *)context)[0];

View File

@@ -5,13 +5,13 @@
#include <poincare/print_float.h>
#include "store.h"
#include "../constant.h"
#include "../shared/curve_view.h"
#include "../shared/labeled_curve_view.h"
namespace Statistics {
class HistogramController;
class HistogramView : public Shared::CurveView {
class HistogramView : public Shared::HorizontallyLabeledCurveView {
public:
HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor = Palette::StatisticsSelected, KDColor notSelectedHistogramColor = Palette::StatisticsNotSelected, KDColor selectedBarColor = Palette::StatisticsSelected);
int series() const { return m_series; }
@@ -21,10 +21,8 @@ public:
void setHighlight(float start, float end);
void setDisplayLabels(bool display) { m_displayLabels = display; }
private:
char * label(Axis axis, int index) const override;
HistogramController * m_controller;
Store * m_store;
char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
static float EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context);
float m_highlightedBarStart;
float m_highlightedBarEnd;

2
docs/build/index.md vendored
View File

@@ -54,7 +54,7 @@ git clone https://github.com/numworks/epsilon.git
## Run Epsilon on your computer
Once the SDK has been installed, just open your terminal (Msys2, Terminal.app, xterm, etc...) and type the following commands:
Once the SDK has been installed, just open your terminal (Msys2, Terminal.app, xterm) and type the following commands:
```
make PLATFORM=simulator clean

View File

@@ -46,12 +46,11 @@ The choice of a programming language is a controversial topic. Not all of them c
- It is a [system](https://en.wikipedia.org/wiki/System_programming_language) programming language, which is something we need since we have to write some low-level code.
- It has excellent tooling: several extremly high-quality compilers
- It is used for several high-profile projects LLVM, WebKit, MySQL, Photoshop, etc... This ensures a strong ecosystem of tools, code and documentation.
- It is used for several high-profile projects LLVM, WebKit, MySQL, Photoshop This ensures a strong ecosystem of tools, code and documentation.
- It easily allows Object-Oriented Programming, which is a convenient abstraction.
Of course knowing a tool means knowing its limits. C++ isn't exempt of defaults:
- It *is* a complicated language. The C++ 11 specification is 1300 pages long.
- It *is* a complicated language. The C++ 11 specification is 1'300 pages long.
- It allows for a lot of abstractions, which is a double-edged sword. It can allow for some very tricky code, and it's very easy to have complex operations being run without noticing.
If you want to contribute to Epsilon, you'll need to learn some C++.
@@ -66,7 +65,7 @@ The stack memory is possibly the most used area of memory. It contains all local
#### Heap memory
Unfortunately, local variables can't answer all use cases, and sometimes one need to allocate memory that lives longer than a function call. This is traditionally done by using a pair of *malloc* / *free* functions.
Unfortunately, local variables can't answer all use cases, and sometimes one need to allocate memory that lives longer than a function call. This is traditionally done by using a pair of `malloc` / `free` functions.
This raises a lot of potential problems that can trigger unpredictable dynamic behaviors:
@@ -77,7 +76,7 @@ This raises a lot of potential problems that can trigger unpredictable dynamic b
<dd class="text-justify">Memory allocation has to be contiguous. So the allocation algorithm has to use a smart heuristic to ensure that it will not fragment its allocated space too much.</dd>
</dl>
Some automatic memory management solutions do exist (garbage collection, smart pointers), but they all come with a cost. We decided to manually manage dynamic memory, but to use it as sparingly as possible.
We decided to avoid `malloc` altogether and to use a mix of static allocation and a pool of relocatable garbage-collected nodes for manipulating mathematical expressions.
### Writing code that runs on the bare metal
@@ -90,8 +89,8 @@ In practice, this means that the firmware will need to know in advance how the m
- Where will we store read-only variables?
- Where will the code live in memory?
The firmware will also need to take special care of the system initialization. There is no such thing as a "main" function on a firmware. Instead, on Cortex-M4 devices, after reset the CPU simply jumps to the address contained at address 0x00000000 (which happens to be the first bytes of flash memory). So if your firmware starts by 0x12345678, code execution will start at address 0x12345678.
The firmware will also need to take special care of the system initialization. There is no such thing as a "main" function on a firmware. Instead, on Cortex-M devices, after reset the CPU simply jumps to the address contained at address 0x00000000 (which happens to be the first bytes of flash memory). So if your firmware starts by 0x12345678, code execution will start at address 0x12345678.
Enforcing such a careful memory layout would be an impossible job without the proper tool. Fortunately, embedded linkers can be scripted and allow this kind of tailor-made configuration. You'll find Epsilon's linker script in "ion/src/device/boot/flash.ld" - it is heavily commented and should be self-explanatory.
Enforcing such a careful memory layout would be an impossible job without the proper tool. Fortunately, embedded linkers can be scripted and allow this kind of tailor-made configuration. You'll find Epsilon's linker script in [ion/src/device/n0110/flash.ld](https://github.com/numworks/epsilon/blob/master/ion/src/device/n0110/flash.ld) - it is heavily commented and should be self-explanatory.
That being said, there are additional things the OS usually takes care of which we need to do ourselves : for example, initialize global variables to zero. This is done in the "ion/src/device/boot/rt0.cpp" file, which is worth reading too.
That being said, there are additional things the OS usually takes care of which we need to do ourselves : for example, initialize global variables to zero. This is done in the [ion/src/device/shared/boot/rt0.cpp](https://github.com/numworks/epsilon/blob/master/ion/src/device/shared/boot/rt0.cpp) file, which is worth reading too.

View File

@@ -11,25 +11,25 @@ By providing multiple implementations of the Ion functions, we therefore can get
## Device
This is the reference platform corresponding to the actual device. To really understand what the code is doing, you'll need to refer to our <a href="../../../hardware/electrical/">Electrical Engineering</a> pages. Among other thing, Ion is responsible for handling the boot process and the memory layout of the code on the device:
This is the reference platform corresponding to the actual device. To really understand what the code is doing, you'll need to refer to our [Electrical Engineering](https://www.numworks.com/resources/engineering/hardware/electrical/) pages. Among other thing, Ion is responsible for handling the boot process and the memory layout of the code on the device:
### Boot
On boot, the Cortex core interprets the beginning of Flash memory as an array of addresses, each having a specific meaning. For example, the first value gives the address of the stack and the second one the address the processor jumps to right after reset. This list of addresses is called the <a href="https://github.com/numworks/epsilon/blob/master/ion/src/device/boot/isr.c">ISR table</a>.
On boot, the Cortex core interprets the beginning of Flash memory as an array of addresses, each having a specific meaning. For example, the first value gives the address of the stack and the second one the address the processor jumps to right after reset. This list of addresses is called the [ISR table](https://github.com/numworks/epsilon/blob/master/ion/src/device/shared/boot/isr.c).
### Memory layout
Like we saw in the previous paragraph, the MCU has a specific memory layout (for example, Flash starts at address 0x08000000) and expects certain values at certain addresses. To ensure the firmware is laid out in memory exactly how the processor expects it, we use a custom <a href="https://github.com/numworks/epsilon/blob/master/ion/src/device/boot/flash.ld">linker script</a>.
Like we saw in the previous paragraph, the MCU has a specific memory layout (for example, Flash starts at address 0x08000000) and expects certain values at certain addresses. To ensure the firmware is laid out in memory exactly how the processor expects it, we use a custom [linker script](https://github.com/numworks/epsilon/blob/master/ion/src/device/n0110/flash.ld).
## Simulator
The simulator platform implements Ion calls using the <a href="http://www.fltk.org/">FLTK</a> library. The result is a native GUI program that can run under a variety of operating systems such as Windows, macOS or most Linux distributions.
The simulator platform implements Ion calls using the [SDL](https://www.libsdl.org/) library. The result is a native GUI program that can run under a variety of operating systems such as Windows, macOS or most Linux distributions.
It's very practical to code using the simulator, but one has to pay attention to the differences from an actual device: it'll be significantly faster, and will have a lot more memory. So code written using the simulator should always be thoroughly tested on an actual device.
## Emscripten
The emscripten platform implements Ion calls for a Web browser. This lets us build a version of Epsilon that can run directly in a browser, as shown in our <a href="/simulator/">online simulator</a>. The C++ code is transpiled to JavaScript using Emscripten, and then packaged in a webpage.
The emscripten platform implements Ion calls for a Web browser. This lets us build a version of Epsilon that can run directly in a browser, as shown in our [online simulator](https://www.numworks.com/simulator/). The C++ code is transpiled to JavaScript using Emscripten, and then packaged in a webpage.
Building on Emscripten takes quite a lot of time so you will most likely not want to use it for development purposes. But obviously it's a very neat feature for end users who can give the calculator a spin straight from their browser.

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,5 +1,5 @@
---
title: Poincare
title: Poincaré
---
# Poincaré
@@ -9,7 +9,7 @@ Poincare takes text input such as `1+2*3` and turns it into a tree structure, th
Each node of a tree represents either an operator or a value. All nodes have a type (`Type::Addition`, `Type::Multiplication`...) and some also store a value (ie `Type::Rational`).
![A example expression tree of Epsilon software](<%= p "expression_tree.svg" %>){:class="img-right"}
![A example expression tree of Epsilon software](<%= p "expression-tree.svg" %>){:class="img-right"}
According to their types, expressions are childless (`Type::Rational`) or store pointers to their children (we call those children operands). To ease tree traversal, each node also keeps a pointer to its parent: that information is somewhat redundant but makes dealing with the expression tree much easier. `Multiplication` and `Addition` are the only type that can hold an infinite number of operands. Other expressions have a fixed number of operands: for instance, an `AbsoluteValue` will only ever have one child.
## RTTI: Run-time type information
@@ -43,7 +43,6 @@ To sort those operands, we defined an order on expressions with the following fe
* The order relationship is depth-first recursive: if two expressions are equal in type and values, we compare their operands starting with the last.
* To compare two expressions, we first sort their commutative children to ensure the unicity of expression representations. This guarantees that the order is total on expressions.
![Order relationship on expressions](<%= p "order.svg" %>){:class="img-responsive"}
In the example, both root nodes are r so we compare their last operands. Both are equal to $$\pi$$ so we compare the next operands. As 3 > 2, we can conclude on the order relation between the expressions.

View File

@@ -4,7 +4,7 @@
#include <escher/view.h>
class TransparentView : public View {
public:
protected:
void markRectAsDirty(KDRect rect) override;
};

View File

@@ -2,7 +2,7 @@
void TransparentView::markRectAsDirty(KDRect rect) {
if (m_superview) {
m_superview->markRectAsDirty(KDRect(rect.translatedBy(m_frame.origin())));
m_superview->markRectAsDirty(rect.translatedBy(m_frame.origin()));
}
View::markRectAsDirty(rect);
}

View File

@@ -53,6 +53,7 @@ Event getEvent(int * timeout);
ShiftAlphaStatus shiftAlphaStatus();
void setShiftAlphaStatus(ShiftAlphaStatus s);
void removeShift();
bool isShiftActive();
bool isAlphaActive();
bool isLockActive();

View File

@@ -11,6 +11,16 @@ ShiftAlphaStatus shiftAlphaStatus() {
return sShiftAlphaStatus;
}
void removeShift() {
if (sShiftAlphaStatus == ShiftAlphaStatus::Shift) {
sShiftAlphaStatus = ShiftAlphaStatus::Default;
} else if (sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlpha ) {
sShiftAlphaStatus = ShiftAlphaStatus::Alpha;
} else if (sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlphaLock) {
sShiftAlphaStatus = ShiftAlphaStatus::AlphaLock;
}
}
bool isShiftActive() {
return sShiftAlphaStatus == ShiftAlphaStatus::Shift || sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlpha || sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlphaLock;
}

View File

@@ -64,10 +64,14 @@ namespace Ion {
namespace Events {
static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) {
/* If an event is detected, we want to remove the Shift modifier to mimic the
* device behaviour. If no event was detected, we restore the previous
* ShiftAlphaStatus. */
Ion::Events::ShiftAlphaStatus previousShiftAlphaStatus = Ion::Events::shiftAlphaStatus();
Ion::Events::removeShift();
if (event.keysym.mod & KMOD_CTRL) {
switch (event.keysym.sym) {
case SDLK_BACKSPACE:
return Clear;
case SDLK_x:
return Cut;
case SDLK_c:
@@ -88,14 +92,8 @@ static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) {
}
}
switch (event.keysym.sym) {
case SDLK_ESCAPE:
return Home;
case SDLK_RETURN:
return OK;
case SDLK_v:
return Var;
case SDLK_BACKSPACE:
return Clear;
case SDLK_x:
return Exp;
case SDLK_n:
@@ -124,38 +122,12 @@ static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) {
return Ans;
}
}
if (event.keysym.mod & KMOD_SHIFT) {
switch(event.keysym.sym) {
case SDLK_UP:
return ShiftUp;
case SDLK_DOWN:
return ShiftDown;
case SDLK_LEFT:
return ShiftLeft;
case SDLK_RIGHT:
return ShiftRight;
}
}
switch(event.keysym.sym) {
case SDLK_UP:
return Up;
case SDLK_DOWN:
return Down;
case SDLK_LEFT:
return Left;
case SDLK_RIGHT:
return Right;
case SDLK_RETURN:
return EXE;
case SDLK_ESCAPE:
return Back;
case SDLK_TAB:
return Toolbox;
case SDLK_BACKSPACE:
return Backspace;
case SDLK_AC_BACK:
return Termination;
}
// No event was detected, restore the previous ShiftAlphaStatus.
Ion::Events::setShiftAlphaStatus(previousShiftAlphaStatus);
return None;
}
@@ -180,6 +152,12 @@ static Event eventFromSDLTextInputEvent(SDL_TextInputEvent event) {
}
char character = event.text[0];
if (character >= 32 && character < 127) {
/* We remove the shift, otherwise it might stay activated when it shouldn't.
* For instance on a French keyboard, to input "1", we first press "Shift"
* (which activates the Shift modifier on the calculator), then we press
* "&", transformed by eventFromSDLTextInputEvent into the text "1". If we
* do not remove the Shift here, it would still be pressed afterwards. */
Ion::Events::removeShift();
return sEventForASCIICharAbove32[character-32];
}
return None;
@@ -193,6 +171,7 @@ Event getPlatformEvent() {
}
#endif
SDL_Event event;
Event result = None;
while (SDL_PollEvent(&event)) {
// The while is important: it'll do a fast-pass over all useless SDL events
if (event.type == SDL_WINDOWEVENT) {
@@ -201,16 +180,35 @@ Event getPlatformEvent() {
}
}
if (event.type == SDL_QUIT) {
return Termination;
result = Termination;
break;
}
if (event.type == SDL_KEYDOWN) {
return eventFromSDLKeyboardEvent(event.key);
if (IonSimulatorSDLKeyDetectedByScan(event.key.keysym.scancode)) {
continue;
}
result = eventFromSDLKeyboardEvent(event.key);
break;
}
if (event.type == SDL_TEXTINPUT) {
return eventFromSDLTextInputEvent(event.text);
result = eventFromSDLTextInputEvent(event.text);
break;
}
}
return None;
if (result != None) {
/* When events are not being processed - for instance when a Python script
* is being executed - SDL puts all ingoing events in a queue.
* When events processing goes back to normal, all the queued events are
* processed, which can result in weird behaviours (for instance, really
* fast navigation in the calculator, "instantanate" text input, ...).
* These behaviours are even more visible if the script contains an infinite
* loop.
* To prevent that, we flush all queued events after encountering a non-null
* event -> the first event from the queue will still be processed, but not
* the subsequent ones. */
SDL_FlushEvents(0, UINT32_MAX);
}
return result;
}
}

View File

@@ -22,6 +22,34 @@ void IonSimulatorKeyboardKeyUp(int keyNumber) {
namespace Ion {
namespace Keyboard {
class KeySDLKeyPair {
public:
constexpr KeySDLKeyPair(Ion::Keyboard::Key key, SDL_Scancode SDLKey) :
m_key(key),
m_SDLKey(SDLKey)
{}
Ion::Keyboard::Key key() const { return m_key; }
SDL_Scancode SDLKey() const { return m_SDLKey; }
private:
Ion::Keyboard::Key m_key;
SDL_Scancode m_SDLKey;
};
constexpr static KeySDLKeyPair sKeyPairs[] = {
KeySDLKeyPair(Ion::Keyboard::Key::Down, SDL_SCANCODE_DOWN),
KeySDLKeyPair(Ion::Keyboard::Key::Up, SDL_SCANCODE_UP),
KeySDLKeyPair(Ion::Keyboard::Key::Left, SDL_SCANCODE_LEFT),
KeySDLKeyPair(Ion::Keyboard::Key::Right, SDL_SCANCODE_RIGHT),
KeySDLKeyPair(Ion::Keyboard::Key::Shift, SDL_SCANCODE_LSHIFT),
KeySDLKeyPair(Ion::Keyboard::Key::Shift, SDL_SCANCODE_RSHIFT),
KeySDLKeyPair(Ion::Keyboard::Key::EXE, SDL_SCANCODE_RETURN),
KeySDLKeyPair(Ion::Keyboard::Key::Back, SDL_SCANCODE_ESCAPE),
KeySDLKeyPair(Ion::Keyboard::Key::Toolbox, SDL_SCANCODE_TAB),
KeySDLKeyPair(Ion::Keyboard::Key::Backspace, SDL_SCANCODE_BACKSPACE)
};
constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeySDLKeyPair);
State scan() {
// We need to tell SDL to get new state from the host OS
SDL_PumpEvents();
@@ -32,13 +60,12 @@ State scan() {
// Grab this opportunity to refresh the display if needed
Simulator::Main::refresh();
#if EPSILON_SDL_SCREEN_ONLY
// In this case, keyboard states will be sent over another channel
return sKeyboardState;
#else
// Start with a "clean" state
State state;
#if EPSILON_SDL_SCREEN_ONLY
// In this case, keyboard states are sent over another channel.
state = sKeyboardState;
#else
// Register a key for the mouse, if any
SDL_Point p;
Uint32 mouseState = SDL_GetMouseState(&p.x, &p.y);
@@ -46,10 +73,27 @@ State scan() {
Key k = Simulator::Layout::keyAt(&p);
state.setKey(k);
}
return state;
#endif
// Catch the physical keyboard events
const uint8_t * SDLstate = SDL_GetKeyboardState(NULL);
for (int i = 0; i < sNumberOfKeyPairs; i++) {
KeySDLKeyPair pair = sKeyPairs[i];
if (SDLstate[pair.SDLKey()]) {
state.setKey(pair.key());
}
}
return state;
}
}
}
bool IonSimulatorSDLKeyDetectedByScan(SDL_Scancode key) {
for (int i = 0; i < Ion::Keyboard::sNumberOfKeyPairs; i++) {
if (key == Ion::Keyboard::sKeyPairs[i].SDLKey()) {
return true;
}
}
return false;
}

View File

@@ -2,6 +2,7 @@
#define ION_SIMULATOR_PLATFORM_H
#include <SDL.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
@@ -14,14 +15,12 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi
char * IonSimulatorGetLanguageCode();
#if EPSILON_SDL_SCREEN_ONLY
void IonSimulatorKeyboardKeyDown(int keyNumber);
void IonSimulatorKeyboardKeyUp(int keyNumber);
void IonSimulatorEventsPushEvent(int eventNumber);
#endif
bool IonSimulatorSDLKeyDetectedByScan(SDL_Scancode key);
void IonSimulatorCallbackDidRefresh();
void IonSimulatorCallbackDidScanKeyboard();

View File

@@ -95,9 +95,7 @@ public:
static GridLayout Builder() { return TreeHandle::NAryBuilder<GridLayout,GridLayoutNode>(); }
void setDimensions(int rows, int columns);
void addChildAtIndex(Layout l, int index, int currentNumberOfChildren, LayoutCursor * cursor) {
Layout::addChildAtIndex(l, index, currentNumberOfChildren, cursor);
}
using Layout::addChildAtIndex;
int numberOfRows() const { return node()->numberOfRows(); }
int numberOfColumns() const { return node()->numberOfColumns(); }
private:

View File

@@ -62,7 +62,6 @@ public:
// Rendering
void draw(KDContext * ctx, KDPoint p, KDColor expressionColor = Palette::PrimaryText, KDColor backgroundColor = Palette::BackgroundHard, Layout * selectionStart = nullptr, Layout * selectionEnd = nullptr, KDColor selectionColor = KDColorRed);
KDPoint origin();
KDPoint absoluteOrigin();
KDSize layoutSize();
KDCoordinate baseline();

View File

@@ -50,21 +50,21 @@ Expression AbsoluteValue::shallowReduce(ExpressionNode::ReductionContext reducti
}
// |x| = ±x if x is real
if (c.isReal(reductionContext.context())) {
float app = c.node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar();
if (!std::isnan(app) &&
((c.isNumber() && app >= 0) || app >= Expression::Epsilon<float>())) {
/* abs(a) = a with a >= 0
* To check that a > 0, if a is a number we can use float comparison;
* in other cases, we are more conservative and rather check that
* a > epsilon ~ 1E-7 to avoid potential error due to float precision. */
replaceWithInPlace(c);
return c;
} else if (!std::isnan(app) &&
((c.isNumber() && app < 0.0f) || app <= -Expression::Epsilon<float>())) {
// abs(a) = -a with a < 0 (same comment as above to check that a < 0)
Multiplication m = Multiplication::Builder(Rational::Builder(-1), c);
replaceWithInPlace(m);
return m.shallowReduce(reductionContext);
double app = c.node()->approximate(double(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar();
if (!std::isnan(app)) {
if ((c.isNumber() && app >= 0) || app >= Expression::Epsilon<double>()) {
/* abs(a) = a with a >= 0
* To check that a > 0, if a is a number we can use float comparison;
* in other cases, we are more conservative and rather check that
* a > epsilon ~ 1E-7 to avoid potential error due to float precision. */
replaceWithInPlace(c);
return c;
} else if ((c.isNumber() && app < 0.0f) || app <= -Expression::Epsilon<double>()) {
// abs(a) = -a with a < 0 (same comment as above to check that a < 0)
Multiplication m = Multiplication::Builder(Rational::Builder(-1), c);
replaceWithInPlace(m);
return m.shallowReduce(reductionContext);
}
}
}
// |a+ib| = sqrt(a^2+b^2)

View File

@@ -58,22 +58,22 @@ bool CodePointLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goin
}
return false;
}
}
if (isMultiplicationCodePoint()) {
/* We want '*' to be collapsable only if the following brother is not a
* fraction, so that the user can write intuitively "1/2 * 3/4". */
Layout thisRef = CodePointLayout(this);
Layout parent = thisRef.parent();
if (!parent.isUninitialized()) {
int indexOfThis = parent.indexOfChild(thisRef);
Layout brother;
if (indexOfThis > 0 && goingLeft) {
brother = parent.childAtIndex(indexOfThis-1);
} else if (indexOfThis < parent.numberOfChildren() - 1 && !goingLeft) {
brother = parent.childAtIndex(indexOfThis+1);
}
if (!brother.isUninitialized() && brother.type() == LayoutNode::Type::FractionLayout) {
return false;
if (isMultiplicationCodePoint()) {
/* We want '*' to be collapsable only if the following brother is not a
* fraction, so that the user can write intuitively "1/2 * 3/4". */
Layout thisRef = CodePointLayout(this);
Layout parent = thisRef.parent();
if (!parent.isUninitialized()) {
int indexOfThis = parent.indexOfChild(thisRef);
Layout brother;
if (indexOfThis > 0 && goingLeft) {
brother = parent.childAtIndex(indexOfThis-1);
} else if (indexOfThis < parent.numberOfChildren() - 1 && !goingLeft) {
brother = parent.childAtIndex(indexOfThis+1);
}
if (!brother.isUninitialized() && brother.type() == LayoutNode::Type::FractionLayout) {
return false;
}
}
}
}

View File

@@ -39,16 +39,6 @@ void LayoutNode::draw(KDContext * ctx, KDPoint p, KDColor expressionColor, KDCol
}
}
KDPoint LayoutNode::origin() {
LayoutNode * p = parent();
if (p == nullptr) {
return absoluteOrigin();
} else {
return KDPoint(absoluteOrigin().x() - p->absoluteOrigin().x(),
absoluteOrigin().y() - p->absoluteOrigin().y());
}
}
KDPoint LayoutNode::absoluteOrigin() {
LayoutNode * p = parent();
if (!m_positioned) {

View File

@@ -51,8 +51,7 @@ bool Parser::IsSpecialIdentifierName(const char * name, size_t nameLength) {
Token::CompareNonNullTerminatedName(name, nameLength, "w_") == 0 ||
Token::CompareNonNullTerminatedName(name, nameLength, "u") == 0 ||
Token::CompareNonNullTerminatedName(name, nameLength, "v") == 0 ||
Token::CompareNonNullTerminatedName(name, nameLength, "w") == 0 ||
Token::CompareNonNullTerminatedName(name, nameLength, "log_") == 0
Token::CompareNonNullTerminatedName(name, nameLength, "w") == 0
);
}
@@ -445,10 +444,10 @@ void Parser::parseSpecialIdentifier(Expression & leftHandSide) {
leftHandSide = Undefined::Builder();
} else if (m_currentToken.compareTo(Unreal::Name()) == 0) {
leftHandSide = Unreal::Builder();
} else if (m_currentToken.compareTo("u") == 0
|| m_currentToken.compareTo("v") == 0
|| m_currentToken.compareTo("w") == 0)
{
} else {
assert(m_currentToken.compareTo("u") == 0
|| m_currentToken.compareTo("v") == 0
|| m_currentToken.compareTo("w") == 0);
/* Special case for sequences (e.g. "u(n)", "u{n}", ...)
* We know that m_currentToken.text()[0] is either 'u', 'v' or 'w', so we do
* not need to pass a code point to parseSequence. */