diff --git a/apps/graph/storage_cartesian_function_store.cpp b/apps/graph/storage_cartesian_function_store.cpp index 4b145b352..57a0721bd 100644 --- a/apps/graph/storage_cartesian_function_store.cpp +++ b/apps/graph/storage_cartesian_function_store.cpp @@ -20,7 +20,7 @@ void StorageCartesianFunctionStore::setMemoizedModelAtIndex(int cacheIndex, Ion: m_functions[cacheIndex] = StorageCartesianFunction(record); } -ExpressionModelHandle * StorageCartesianFunctionStore::memoizedModelAtIndex(int cacheIndex) const { +SingleExpressionModelHandle * StorageCartesianFunctionStore::memoizedModelAtIndex(int cacheIndex) const { assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels()); return &m_functions[cacheIndex]; } diff --git a/apps/graph/storage_cartesian_function_store.h b/apps/graph/storage_cartesian_function_store.h index 865faad0e..f81cd49b5 100644 --- a/apps/graph/storage_cartesian_function_store.h +++ b/apps/graph/storage_cartesian_function_store.h @@ -16,7 +16,7 @@ private: Ion::Storage::Record::ErrorStatus addEmptyModel() override; const char * modelExtension() const override { return Shared::GlobalContext::funcExtension; } void setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record record) const override; - Shared::ExpressionModelHandle * memoizedModelAtIndex(int cacheIndex) const override; + Shared::SingleExpressionModelHandle * memoizedModelAtIndex(int cacheIndex) const override; mutable Shared::StorageCartesianFunction m_functions[k_maxNumberOfMemoizedModels]; }; diff --git a/apps/shared/Makefile b/apps/shared/Makefile index bc6af6116..9176ba56d 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -52,6 +52,7 @@ app_src += $(addprefix apps/shared/,\ scrollable_exact_approximate_expressions_view.cpp \ separator_even_odd_buffer_text_cell.cpp \ simple_interactive_curve_view_controller.cpp \ + single_expression_model_handle.cpp\ storage_cartesian_function.cpp \ storage_expression_model_list_controller.cpp \ storage_expression_model_store.cpp \ diff --git a/apps/shared/expression_model_handle.cpp b/apps/shared/expression_model_handle.cpp index cf8f424e3..65ea9d96e 100644 --- a/apps/shared/expression_model_handle.cpp +++ b/apps/shared/expression_model_handle.cpp @@ -11,16 +11,15 @@ using namespace Poincare; namespace Shared { -ExpressionModelHandle::ExpressionModelHandle(Storage::Record record) : - Storage::Record(record), +ExpressionModelHandle::ExpressionModelHandle() : m_expression(), m_layout(), m_circular(-1) { } -void ExpressionModelHandle::text(char * buffer, size_t bufferSize) const { - Expression e = expressionClone(); +void ExpressionModelHandle::text(const Storage::Record * record, char * buffer, size_t bufferSize) const { + Expression e = expressionClone(record); if (e.isUninitialized() && bufferSize > 0) { buffer[0] = 0; } else { @@ -28,37 +27,35 @@ void ExpressionModelHandle::text(char * buffer, size_t bufferSize) const { } } -bool ExpressionModelHandle::isCircularlyDefined(Poincare::Context * context) const { +bool ExpressionModelHandle::isCircularlyDefined(const Storage::Record * record, Poincare::Context * context) const { if (m_circular == -1) { - m_circular = Expression::ExpressionWithoutSymbols(expressionClone(), *context).isUninitialized(); + m_circular = Expression::ExpressionWithoutSymbols(expressionClone(record), *context).isUninitialized(); } return m_circular; } -Expression ExpressionModelHandle::expressionReduced(Poincare::Context * context) const { +Expression ExpressionModelHandle::expressionReduced(const Storage::Record * record, Poincare::Context * context) const { if (m_expression.isUninitialized()) { - assert(!isNull()); - Ion::Storage::Record::Data recordData = value(); - m_expression = Expression::ExpressionFromAddress(expressionAddressForValue(recordData), expressionSizeForValue(recordData)); + assert(record->fullName() != nullptr); + m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); PoincareHelpers::Simplify(&m_expression, *context); // simplify might return an uninitialized Expression if interrupted if (m_expression.isUninitialized()) { - m_expression = Expression::ExpressionFromAddress(expressionAddressForValue(recordData), expressionSizeForValue(recordData)); + m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); } } return m_expression; } -Expression ExpressionModelHandle::expressionClone() const { - assert(!isNull()); - Ion::Storage::Record::Data recordData = value(); +Expression ExpressionModelHandle::expressionClone(const Storage::Record * record) const { + assert(record->fullName() != nullptr); /* A new Expression has to be created at each call (because it might be tempered with after calling) */ - return Expression::ExpressionFromAddress(expressionAddressForValue(recordData), expressionSizeForValue(recordData)); + return Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); } -Layout ExpressionModelHandle::layout() { +Layout ExpressionModelHandle::layout(const Storage::Record * record) const { if (m_layout.isUninitialized()) { - m_layout = PoincareHelpers::CreateLayout(expressionClone()); + m_layout = PoincareHelpers::CreateLayout(expressionClone(record)); if (m_layout.isUninitialized()) { m_layout = HorizontalLayout::Builder(); } @@ -66,46 +63,20 @@ Layout ExpressionModelHandle::layout() { return m_layout; } -bool ExpressionModelHandle::isDefined() { - return !isEmpty(); +Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setContent(Ion::Storage::Record * record, const char * c, char symbol) { + Expression e = ExpressionModelHandle::BuildExpressionFromText(c, symbol); + return setExpressionContent(record, e); } -bool ExpressionModelHandle::isEmpty() { - return value().size <= metaDataSize(); -} - -void ExpressionModelHandle::tidyExpressionModel() { - m_layout = Layout(); - m_expression = Expression(); - m_circular = 0; -} - -void ExpressionModelHandle::tidy() { - tidyExpressionModel(); -} - -Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setContent(const char * c) { - Expression expressionToStore; - // if c = "", we want to reinit the Expression - if (c && *c != 0) { - // Compute the expression to store, without replacing symbols - expressionToStore = Expression::Parse(c); - if (!expressionToStore.isUninitialized() && symbol() != 0) { - expressionToStore = expressionToStore.replaceUnknown(Symbol::Builder(symbol())); - } - } - return setExpressionContent(expressionToStore); -} - -Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setExpressionContent(Expression & expressionToStore) { - assert(!isNull()); +Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setExpressionContent(Ion::Storage::Record * record, Expression & expressionToStore) { + assert(record->fullName() != nullptr); // Prepare the new data to store - Ion::Storage::Record::Data newData = value(); + Ion::Storage::Record::Data newData = record->value(); size_t expressionToStoreSize = expressionToStore.isUninitialized() ? 0 : expressionToStore.size(); - newData.size = metaDataSize() + expressionToStoreSize; + newData.size = newData.size - expressionSize(record) + expressionToStoreSize; // Set the data - Ion::Storage::Record::ErrorStatus error = setValue(newData); + Ion::Storage::Record::ErrorStatus error = record->setValue(newData); if (error != Ion::Storage::Record::ErrorStatus::None) { assert(error == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable); return error; @@ -113,21 +84,31 @@ Ion::Storage::Record::ErrorStatus ExpressionModelHandle::setExpressionContent(Ex // Copy the expression if needed if (!expressionToStore.isUninitialized()) { - memcpy(expressionAddressForValue(value()), expressionToStore.addressInPool(), expressionToStore.size()); + memcpy(expressionAddress(record), expressionToStore.addressInPool(), expressionToStore.size()); } - /* We cannot call tidy here because tidy is a virtual function and does not - * do the same thing for all children class. And here we want to delete only - * the elements relative to the ExpressionModel. */ - tidyExpressionModel(); + /* Here we delete only the elements relative to the expression model kept in + * this handle. */ + tidy(); return error; } -void * ExpressionModelHandle::expressionAddressForValue(Ion::Storage::Record::Data recordData) const { - return (char *)recordData.buffer+metaDataSize(); +void ExpressionModelHandle::tidy() const { + m_layout = Layout(); + m_expression = Expression(); + m_circular = 0; } -size_t ExpressionModelHandle::expressionSizeForValue(Ion::Storage::Record::Data recordData) const { - return recordData.size-metaDataSize(); +Poincare::Expression ExpressionModelHandle::BuildExpressionFromText(const char * c, char symbol) { + Expression expressionToStore; + // if c = "", we want to reinit the Expression + if (c && *c != 0) { + // Compute the expression to store, without replacing symbols + expressionToStore = Expression::Parse(c); + if (!expressionToStore.isUninitialized() && symbol != 0) { + expressionToStore = expressionToStore.replaceUnknown(Symbol::Builder(symbol)); + } + } + return expressionToStore; } } diff --git a/apps/shared/expression_model_handle.h b/apps/shared/expression_model_handle.h index b034e6ec9..08f1accf2 100644 --- a/apps/shared/expression_model_handle.h +++ b/apps/shared/expression_model_handle.h @@ -4,47 +4,37 @@ #include #include #include -#include namespace Shared { // ExpressionModelHandle is a handle to Ion::Record -class ExpressionModelHandle : public Ion::Storage::Record { - // TODO find better name (once we remove ExpressionModel?) +class ExpressionModelHandle { public: - ExpressionModelHandle(Ion::Storage::Record record); - virtual char symbol() const { return 0; }; - void text(char * buffer, size_t bufferSize) const; - Poincare::Expression expressionReduced(Poincare::Context * context) const; - Poincare::Expression expressionClone() const; - Poincare::Layout layout(); - /* TODO This comment will be true when Sequence inherits from this class - * Here, isDefined is the exact contrary of isEmpty. However, for Sequence - * inheriting from ExpressionModel, isEmpty and isDefined have not exactly - * opposite meaning. For instance, u(n+1)=u(n) & u(0) = ... is not empty and - * not defined. We thus have to keep both methods. */ - virtual bool isDefined(); - virtual bool isEmpty(); - virtual bool shouldBeClearedBeforeRemove() { return !isEmpty(); } - /* tidy is responsible to tidy the whole model whereas tidyExpressionModel - * tidies only the members associated with the ExpressionModel. In - * ExpressionModelHandle, tidy and tidyExpressionModel trigger the same - * behaviour but it is not true for its child classes (for example, in - * Sequence). */ - virtual void tidy(); - virtual void tidyExpressionModel(); - virtual Ion::Storage::Record::ErrorStatus setContent(const char * c); - Ion::Storage::Record::ErrorStatus setExpressionContent(Poincare::Expression & e); + ExpressionModelHandle(); + + // Getters + void text(const Ion::Storage::Record * record, char * buffer, size_t bufferSize) const; + Poincare::Expression expressionReduced(const Ion::Storage::Record * record, Poincare::Context * context) const; + Poincare::Expression expressionClone(const Ion::Storage::Record * record) const; + Poincare::Layout layout(const Ion::Storage::Record * record) const; + + // Setters + virtual Ion::Storage::Record::ErrorStatus setContent(Ion::Storage::Record * record, const char * c, char symbol = 0); + Ion::Storage::Record::ErrorStatus setExpressionContent(Ion::Storage::Record * record, Poincare::Expression & e); + + // Property + bool isCircularlyDefined(const Ion::Storage::Record * record, Poincare::Context * context) const; + virtual void * expressionAddress(const Ion::Storage::Record * record) const = 0; + + virtual void tidy() const; protected: - virtual size_t metaDataSize() const = 0; - Ion::Storage::Record record() const; - bool isCircularlyDefined(Poincare::Context * context) const; + // Setters helper + static Poincare::Expression BuildExpressionFromText(const char * c, char symbol = 0); mutable Poincare::Expression m_expression; mutable Poincare::Layout m_layout; private: - void * expressionAddressForValue(Ion::Storage::Record::Data recordData) const; - size_t expressionSizeForValue(Ion::Storage::Record::Data recordData) const; + virtual size_t expressionSize(const Ion::Storage::Record * record) const = 0; mutable int m_circular; }; diff --git a/apps/shared/single_expression_model_handle.cpp b/apps/shared/single_expression_model_handle.cpp new file mode 100644 index 000000000..ef0037c73 --- /dev/null +++ b/apps/shared/single_expression_model_handle.cpp @@ -0,0 +1,27 @@ +#include "single_expression_model_handle.h" +#include "global_context.h" +#include "poincare_helpers.h" +#include +#include +#include +#include + +using namespace Ion; +using namespace Poincare; + +namespace Shared { + +SingleExpressionModelHandle::SingleExpressionModelHandle(Storage::Record record) : + Storage::Record(record) +{ +} + +bool SingleExpressionModelHandle::isDefined() { + return !isEmpty(); +} + +bool SingleExpressionModelHandle::isEmpty() { + return value().size <= metaDataSize(); +} + +} diff --git a/apps/shared/single_expression_model_handle.h b/apps/shared/single_expression_model_handle.h new file mode 100644 index 000000000..8590515ed --- /dev/null +++ b/apps/shared/single_expression_model_handle.h @@ -0,0 +1,44 @@ +#ifndef SHARED_SINGLE_EXPRESSION_MODEL_HANDLE_H +#define SHARED_SINGLE_EXPRESSION_MODEL_HANDLE_H + +#include "expression_model_handle.h" + +namespace Shared { + + +class SingleExpressionModelHandle : public Ion::Storage::Record { +public: + SingleExpressionModelHandle(Ion::Storage::Record record); + + // Property + virtual char symbol() const { return 0; }; + void text(char * buffer, size_t bufferSize) const { return handle()->text(this, buffer, bufferSize); } + Poincare::Expression expressionReduced(Poincare::Context * context) const { return handle()->expressionReduced(this, context); } + Poincare::Expression expressionClone() const { return handle()->expressionClone(this); } + Poincare::Layout layout() { return handle()->layout(this); } + /* TODO This comment will be true when Sequence inherits from this class + * Here, isDefined is the exact contrary of isEmpty. However, for Sequence + * inheriting from ExpressionModel, isEmpty and isDefined have not exactly + * opposite meaning. For instance, u(n+1)=u(n) & u(0) = ... is not empty and + * not defined. We thus have to keep both methods. */ + virtual bool isDefined(); + virtual bool isEmpty(); + virtual bool shouldBeClearedBeforeRemove() { return !isEmpty(); } + /* tidy is responsible to tidy the whole model whereas tidyExpressionModel + * tidies only the members associated with the ExpressionModel. In + * ExpressionModelHandle, tidy and tidyExpressionModel trigger the same + * behaviour but it is not true for its child classes (for example, in + * Sequence). */ + virtual void tidy() { handle()->tidy(); } + virtual Ion::Storage::Record::ErrorStatus setContent(const char * c) { return editableHandle()->setContent(this, c, symbol()); } + Ion::Storage::Record::ErrorStatus setExpressionContent(Poincare::Expression & e) { return editableHandle()->setExpressionContent(this, e); } +protected: + bool isCircularlyDefined(Poincare::Context * context) const { return handle()->isCircularlyDefined(this, context); } + ExpressionModelHandle * editableHandle() { return const_cast(handle()); } + virtual const ExpressionModelHandle * handle() const = 0; + virtual size_t metaDataSize() const = 0; +}; + +} + +#endif diff --git a/apps/shared/storage_cartesian_function.cpp b/apps/shared/storage_cartesian_function.cpp index 5bdf59ced..ad76296f0 100644 --- a/apps/shared/storage_cartesian_function.cpp +++ b/apps/shared/storage_cartesian_function.cpp @@ -123,6 +123,14 @@ Expression::Coordinate2D StorageCartesianFunction::nextIntersectionFrom(double s return PoincareHelpers::NextIntersection(expressionReduced(context), unknownX, start, step, max, *context, e); } +void * StorageCartesianFunction::Handle::expressionAddress(const Ion::Storage::Record * record) const { + return (char *)record->value().buffer+sizeof(CartesianFunctionRecordData); +} + +size_t StorageCartesianFunction::Handle::expressionSize(const Ion::Storage::Record * record) const { + return record->value().size-sizeof(CartesianFunctionRecordData); +} + StorageCartesianFunction::CartesianFunctionRecordData * StorageCartesianFunction::recordData() const { assert(!isNull()); Ion::Storage::Record::Data d = value(); diff --git a/apps/shared/storage_cartesian_function.h b/apps/shared/storage_cartesian_function.h index 722a07d99..0fb157e4d 100644 --- a/apps/shared/storage_cartesian_function.h +++ b/apps/shared/storage_cartesian_function.h @@ -16,16 +16,21 @@ public: StorageCartesianFunction(Ion::Storage::Record record = Record()) : StorageFunction(record) {} + + // Derivative bool displayDerivative() const; void setDisplayDerivative(bool display); int derivativeNameWithArgument(char * buffer, size_t bufferSize, char arg); double approximateDerivative(double x, Poincare::Context * context) const; + // Integral double sumBetweenBounds(double start, double end, Poincare::Context * context) const override; + // Extremum Poincare::Expression::Coordinate2D nextMinimumFrom(double start, double step, double max, Poincare::Context * context) const; Poincare::Expression::Coordinate2D nextMaximumFrom(double start, double step, double max, Poincare::Context * context) const; + // Roots double nextRootFrom(double start, double step, double max, Poincare::Context * context) const; Poincare::Expression::Coordinate2D nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Poincare::Expression expression) const; -protected: +private: class CartesianFunctionRecordData : public FunctionRecordData { public: CartesianFunctionRecordData(KDColor color) : @@ -40,9 +45,16 @@ protected: * the expression of the function, directly copied from the pool. */ //char m_expression[0]; }; -private: + class Handle : public ExpressionModelHandle { + public: + void * expressionAddress(const Ion::Storage::Record * record) const override; + private: + size_t expressionSize(const Ion::Storage::Record * record) const override; + }; size_t metaDataSize() const override { return sizeof(CartesianFunctionRecordData); } + const ExpressionModelHandle * handle() const override { return &m_handle; } CartesianFunctionRecordData * recordData() const; + Handle m_handle; }; } diff --git a/apps/shared/storage_expression_model_list_controller.cpp b/apps/shared/storage_expression_model_list_controller.cpp index 55ad03508..95c88cc1c 100644 --- a/apps/shared/storage_expression_model_list_controller.cpp +++ b/apps/shared/storage_expression_model_list_controller.cpp @@ -145,7 +145,7 @@ KDCoordinate StorageExpressionModelListController::expressionRowHeight(int j) { if (isAddEmptyRow(j)) { return Metric::StoreRowHeight; } - ExpiringPointer m = modelStore()->modelForRecord(modelStore()->recordAtIndex(j)); + ExpiringPointer m = modelStore()->modelForRecord(modelStore()->recordAtIndex(j)); if (m->layout().isUninitialized()) { return Metric::StoreRowHeight; } @@ -156,7 +156,7 @@ KDCoordinate StorageExpressionModelListController::expressionRowHeight(int j) { void StorageExpressionModelListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) { EvenOddExpressionCell * myCell = (EvenOddExpressionCell *)cell; - ExpiringPointer m = modelStore()->modelForRecord(modelStore()->recordAtIndex(j)); + ExpiringPointer m = modelStore()->modelForRecord(modelStore()->recordAtIndex(j)); myCell->setLayout(m->layout()); } @@ -176,7 +176,7 @@ bool StorageExpressionModelListController::handleEventOnExpression(Ion::Events:: } if (event == Ion::Events::Backspace && !isAddEmptyRow(selectedRow())) { Ion::Storage::Record record = modelStore()->recordAtIndex(modelIndexForRow(selectedRow())); - ExpiringPointer model = modelStore()->modelForRecord(record); + ExpiringPointer model = modelStore()->modelForRecord(record); if (model->shouldBeClearedBeforeRemove()) { reinitSelectedExpression(model); } else { @@ -206,7 +206,7 @@ void StorageExpressionModelListController::addEmptyModel() { editExpression(Ion::Events::OK); } -void StorageExpressionModelListController::reinitSelectedExpression(ExpiringPointer model) { +void StorageExpressionModelListController::reinitSelectedExpression(ExpiringPointer model) { model->setContent(""); // Reset memoization of the selected cell which always corresponds to the k_memoizedCellsCount/2 memoized cell resetMemoizationForIndex(k_memoizedCellsCount/2); @@ -219,7 +219,7 @@ void StorageExpressionModelListController::editExpression(Ion::Events::Event eve char initialTextContent[initialTextContentMaxSize]; if (event == Ion::Events::OK || event == Ion::Events::EXE) { Ion::Storage::Record record = modelStore()->recordAtIndex(modelIndexForRow(selectedRow())); - ExpiringPointer model = modelStore()->modelForRecord(record); + ExpiringPointer model = modelStore()->modelForRecord(record); model->text(initialTextContent, initialTextContentMaxSize); initialText = initialTextContent; // Replace Poincare::Symbol::SpecialSymbols::UnknownX with 'x' @@ -246,7 +246,7 @@ bool StorageExpressionModelListController::editSelectedRecordWithText(const char // Reset memoization of the selected cell which always corresponds to the k_memoizedCellsCount/2 memoized cell resetMemoizationForIndex(k_memoizedCellsCount/2); Ion::Storage::Record record = modelStore()->recordAtIndex(modelIndexForRow(selectedRow())); - ExpiringPointer model = modelStore()->modelForRecord(record); + ExpiringPointer model = modelStore()->modelForRecord(record); return (model->setContent(text) == Ion::Storage::Record::ErrorStatus::None); } diff --git a/apps/shared/storage_expression_model_list_controller.h b/apps/shared/storage_expression_model_list_controller.h index f4976ccbf..dd9b7107b 100644 --- a/apps/shared/storage_expression_model_list_controller.h +++ b/apps/shared/storage_expression_model_list_controller.h @@ -25,7 +25,7 @@ protected: bool handleEventOnExpression(Ion::Events::Event event); virtual void addEmptyModel(); virtual void didChangeModelsList() { resetMemoization(); } - virtual void reinitSelectedExpression(ExpiringPointer model); + virtual void reinitSelectedExpression(ExpiringPointer model); virtual void editExpression(Ion::Events::Event event); virtual bool editSelectedRecordWithText(const char * text); virtual bool removeModelRow(Ion::Storage::Record record); diff --git a/apps/shared/storage_expression_model_store.cpp b/apps/shared/storage_expression_model_store.cpp index 2a385730c..335d51a18 100644 --- a/apps/shared/storage_expression_model_store.cpp +++ b/apps/shared/storage_expression_model_store.cpp @@ -15,14 +15,14 @@ Ion::Storage::Record StorageExpressionModelStore::recordAtIndex(int i) const { return Ion::Storage::sharedStorage()->recordWithExtensionAtIndex(modelExtension(), i); } -ExpressionModelHandle * StorageExpressionModelStore::privateModelForRecord(Ion::Storage::Record record) const { +SingleExpressionModelHandle * StorageExpressionModelStore::privateModelForRecord(Ion::Storage::Record record) const { for (int i = 0; i < maxNumberOfMemoizedModels(); i++) { if (!memoizedModelAtIndex(i)->isNull() && *memoizedModelAtIndex(i) == record) { return memoizedModelAtIndex(i); } } setMemoizedModelAtIndex(m_oldestMemoizedIndex, record); - ExpressionModelHandle * result = memoizedModelAtIndex(m_oldestMemoizedIndex); + SingleExpressionModelHandle * result = memoizedModelAtIndex(m_oldestMemoizedIndex); m_oldestMemoizedIndex = (m_oldestMemoizedIndex+1) % maxNumberOfMemoizedModels(); return result; } @@ -44,7 +44,7 @@ void StorageExpressionModelStore::tidy() { int StorageExpressionModelStore::numberOfModelsSatisfyingTest(ModelTest test) const { int result = 0; int i = 0; - ExpressionModelHandle * m = privateModelForRecord(recordAtIndex(i++)); + SingleExpressionModelHandle * m = privateModelForRecord(recordAtIndex(i++)); while (!m->isNull()) { if (test(m)) { result++; @@ -59,7 +59,7 @@ Ion::Storage::Record StorageExpressionModelStore::recordStatifyingTestAtIndex(in int index = 0; int currentModelIndex = 0; Ion::Storage::Record r = recordAtIndex(currentModelIndex++); - ExpressionModelHandle * m = privateModelForRecord(r); + SingleExpressionModelHandle * m = privateModelForRecord(r); while (!m->isNull()) { assert(currentModelIndex <= numberOfModels()); if (test(m)) { diff --git a/apps/shared/storage_expression_model_store.h b/apps/shared/storage_expression_model_store.h index d681c0749..41cb5115c 100644 --- a/apps/shared/storage_expression_model_store.h +++ b/apps/shared/storage_expression_model_store.h @@ -1,7 +1,7 @@ #ifndef SHARED_STORAGE_EXPRESSION_MODEL_STORE_H #define SHARED_STORAGE_EXPRESSION_MODEL_STORE_H -#include "expression_model_handle.h" +#include "single_expression_model_handle.h" #include "expiring_pointer.h" #include #include @@ -19,10 +19,10 @@ public: // By default, the number of models is not bounded virtual int maxNumberOfModels() const { return -1; } int numberOfModels() const; - int numberOfDefinedModels() const { return numberOfModelsSatisfyingTest([](ExpressionModelHandle * m) { return m->isDefined(); }); } + int numberOfDefinedModels() const { return numberOfModelsSatisfyingTest([](SingleExpressionModelHandle * m) { return m->isDefined(); }); } Ion::Storage::Record recordAtIndex(int i) const; - Ion::Storage::Record definedRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](ExpressionModelHandle * m) { return m->isDefined(); }); } - ExpiringPointer modelForRecord(Ion::Storage::Record record) const { return ExpiringPointer(privateModelForRecord(record)); } + Ion::Storage::Record definedRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](SingleExpressionModelHandle * m) { return m->isDefined(); }); } + ExpiringPointer modelForRecord(Ion::Storage::Record record) const { return ExpiringPointer(privateModelForRecord(record)); } // Add and Remove virtual Ion::Storage::Record::ErrorStatus addEmptyModel() = 0; @@ -35,14 +35,14 @@ public: protected: constexpr static int k_maxNumberOfMemoizedModels = 10; int maxNumberOfMemoizedModels() const { return maxNumberOfModels() < 0 ? k_maxNumberOfMemoizedModels : maxNumberOfModels(); } - typedef bool (*ModelTest)(ExpressionModelHandle * model); + typedef bool (*ModelTest)(SingleExpressionModelHandle * model); int numberOfModelsSatisfyingTest(ModelTest test) const; Ion::Storage::Record recordStatifyingTestAtIndex(int i, ModelTest test) const; - ExpressionModelHandle * privateModelForRecord(Ion::Storage::Record record) const; + SingleExpressionModelHandle * privateModelForRecord(Ion::Storage::Record record) const; private: void resetMemoizedModelsExceptRecord(const Ion::Storage::Record record = Ion::Storage::Record()) const; virtual void setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record) const = 0; - virtual ExpressionModelHandle * memoizedModelAtIndex(int cacheIndex) const = 0; + virtual SingleExpressionModelHandle * memoizedModelAtIndex(int cacheIndex) const = 0; virtual const char * modelExtension() const = 0; /* Memoization of k_maxNumberOfMemoizedModels. When the required model is not * present, we override the m_oldestMemoizedIndex model. This actually diff --git a/apps/shared/storage_function.h b/apps/shared/storage_function.h index 45596bbc7..9f8e50a19 100644 --- a/apps/shared/storage_function.h +++ b/apps/shared/storage_function.h @@ -2,11 +2,11 @@ #define SHARED_STORAGE_FUNCTION_H #include -#include "expression_model_handle.h" +#include "single_expression_model_handle.h" namespace Shared { -class StorageFunction : public ExpressionModelHandle { +class StorageFunction : public SingleExpressionModelHandle { public: enum class NameNotCompliantError { None = 0, @@ -20,7 +20,7 @@ public: static bool BaseNameCompliant(const char * baseName, NameNotCompliantError * error = nullptr); // Constructors - StorageFunction(Ion::Storage::Record record) : ExpressionModelHandle(record){} + StorageFunction(Ion::Storage::Record record) : SingleExpressionModelHandle(record){} // Properties bool isActive() const; diff --git a/apps/shared/storage_function_store.h b/apps/shared/storage_function_store.h index a5b08608e..767d9c173 100644 --- a/apps/shared/storage_function_store.h +++ b/apps/shared/storage_function_store.h @@ -14,8 +14,8 @@ public: StorageFunctionStore() : StorageExpressionModelStore() {} uint32_t storeChecksum(); // An active function must be defined to be counted - int numberOfActiveFunctions() const { return numberOfModelsSatisfyingTest([](ExpressionModelHandle * m) { return m->isDefined() && static_cast(m)->isActive(); }); } - Ion::Storage::Record activeRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](ExpressionModelHandle * m) { return m->isDefined() && static_cast(m)->isActive(); }); } + int numberOfActiveFunctions() const { return numberOfModelsSatisfyingTest([](SingleExpressionModelHandle * m) { return m->isDefined() && static_cast(m)->isActive(); }); } + Ion::Storage::Record activeRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](SingleExpressionModelHandle * m) { return m->isDefined() && static_cast(m)->isActive(); }); } ExpiringPointer modelForRecord(Ion::Storage::Record record) const { return ExpiringPointer(static_cast(privateModelForRecord(record))); } diff --git a/apps/solver/equation.cpp b/apps/solver/equation.cpp index af923c078..7352ba583 100644 --- a/apps/solver/equation.cpp +++ b/apps/solver/equation.cpp @@ -6,6 +6,7 @@ #include #include +using namespace Ion; using namespace Poincare; using namespace Shared; @@ -14,20 +15,17 @@ namespace Solver { constexpr char Equation::extension[]; Equation::Equation(Ion::Storage::Record record) : - ExpressionModelHandle(record), - m_standardForm() + SingleExpressionModelHandle(record) { } -void Equation::tidyExpressionModel() { - ExpressionModelHandle::tidyExpressionModel(); - // Free the pool of the m_standardForm - m_standardForm = Expression(); +bool Equation::containsIComplex(Context * context) const { + return expressionClone().recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Constant && static_cast(e).isIComplex(); }, *context, true); } -Expression Equation::standardForm(Context * context) const { +Expression Equation::Handle::standardForm(const Storage::Record * record, Context * context) const { if (m_standardForm.isUninitialized()) { - const Expression e = expressionReduced(context); + const Expression e = expressionReduced(record, context); if (e.type() == ExpressionNode::Type::Unreal) { m_standardForm = Unreal::Builder(); return m_standardForm; @@ -38,7 +36,7 @@ Expression Equation::standardForm(Context * context) const { } if (e.type() == ExpressionNode::Type::Equal) { Preferences * preferences = Preferences::sharedPreferences(); - m_standardForm = static_cast(e).standardEquation(*context, Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), expressionClone(), *context), preferences->angleUnit()); + m_standardForm = static_cast(e).standardEquation(*context, Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), expressionClone(record), *context), preferences->angleUnit()); } else { assert(e.type() == ExpressionNode::Type::Rational && static_cast(e).isOne()); // The equality was reduced which means the equality was always true. @@ -48,8 +46,18 @@ Expression Equation::standardForm(Context * context) const { return m_standardForm; } -bool Equation::containsIComplex(Context * context) const { - return expressionClone().recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Constant && static_cast(e).isIComplex(); }, *context, true); +void Equation::Handle::tidy() const { + ExpressionModelHandle::tidy(); + // Free the pool of the m_standardForm + m_standardForm = Expression(); +} + +void * Equation::Handle::expressionAddress(const Ion::Storage::Record * record) const { + return (char *)record->value().buffer; +} + +size_t Equation::Handle::expressionSize(const Ion::Storage::Record * record) const { + return record->value().size; } } diff --git a/apps/solver/equation.h b/apps/solver/equation.h index 222bb19b3..6db7e8249 100644 --- a/apps/solver/equation.h +++ b/apps/solver/equation.h @@ -1,24 +1,33 @@ #ifndef SOLVER_EQUATION_h #define SOLVER_EQUATION_h -#include "../shared/expression_model_handle.h" +#include "../shared/single_expression_model_handle.h" namespace Solver { -class Equation : public Shared::ExpressionModelHandle { +class Equation : public Shared::SingleExpressionModelHandle { public: Equation(Ion::Storage::Record record = Record()); - void tidyExpressionModel() override; bool shouldBeClearedBeforeRemove() override { return false; } - Poincare::Expression standardForm(Poincare::Context * context) const; + Poincare::Expression standardForm(Poincare::Context * context) const { return m_handle.standardForm(this, context); } bool containsIComplex(Poincare::Context * context) const; static constexpr char extension[] = "eq"; // TODO: store this elsewhere? private: + class Handle : public Shared::ExpressionModelHandle { + public: + Poincare::Expression standardForm(const Ion::Storage::Record * record, Poincare::Context * context) const; + void tidy() const override; + void * expressionAddress(const Ion::Storage::Record * record) const override; + private: + size_t expressionSize(const Ion::Storage::Record * record) const override; + mutable Poincare::Expression m_standardForm; + }; size_t metaDataSize() const override { return 0; } - mutable Poincare::Expression m_standardForm; + const Shared::ExpressionModelHandle * handle() const override { return &m_handle; } + Handle m_handle; }; } diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index 116fbce32..3cbe557bd 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -49,7 +49,7 @@ void EquationStore::setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record m_equations[cacheIndex] = Equation(record); } -ExpressionModelHandle * EquationStore::memoizedModelAtIndex(int cacheIndex) const { +SingleExpressionModelHandle * EquationStore::memoizedModelAtIndex(int cacheIndex) const { assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels()); return &m_equations[cacheIndex]; } diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index 175d20177..2d03d4a05 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -82,7 +82,7 @@ private: /* We don't really use model memoization as the number of Equation is limited * and we keep enough Equations to store them all. */ void setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record record) const override; - Shared::ExpressionModelHandle * memoizedModelAtIndex(int cacheIndex) const override; + Shared::SingleExpressionModelHandle * memoizedModelAtIndex(int cacheIndex) const override; Error resolveLinearSystem(Poincare::Expression solutions[k_maxNumberOfExactSolutions], Poincare::Expression solutionApproximations[k_maxNumberOfExactSolutions], Poincare::Expression coefficients[k_maxNumberOfEquations][Poincare::Expression::k_maxNumberOfVariables], Poincare::Expression constants[k_maxNumberOfEquations], Poincare::Context * context); Error oneDimensialPolynomialSolve(Poincare::Expression solutions[k_maxNumberOfExactSolutions], Poincare::Expression solutionApproximations[k_maxNumberOfExactSolutions], Poincare::Expression polynomialCoefficients[Poincare::Expression::k_maxNumberOfPolynomialCoefficients], int degree, Poincare::Context * context);