[apps/shared] Split ExpressionModelHandle into ExpressionModelHandle and

SingleExpressionModelHandle (in perspective of Sequence model who will
be composed of 3 ExpressionModelHandle)
This commit is contained in:
Émilie Feral
2019-02-25 14:22:34 +01:00
parent 6566b08400
commit 3088a4ee23
19 changed files with 216 additions and 136 deletions

View File

@@ -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];
}

View File

@@ -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];
};

View File

@@ -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 \

View File

@@ -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;
}
}

View File

@@ -4,47 +4,37 @@
#include <poincare/context.h>
#include <poincare/expression.h>
#include <poincare/layout.h>
#include <ion/storage.h>
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;
};

View File

@@ -0,0 +1,27 @@
#include "single_expression_model_handle.h"
#include "global_context.h"
#include "poincare_helpers.h"
#include <poincare/horizontal_layout.h>
#include <string.h>
#include <cmath>
#include <assert.h>
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();
}
}

View File

@@ -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<ExpressionModelHandle *>(handle()); }
virtual const ExpressionModelHandle * handle() const = 0;
virtual size_t metaDataSize() const = 0;
};
}
#endif

View File

@@ -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();

View File

@@ -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;
};
}

View File

@@ -145,7 +145,7 @@ KDCoordinate StorageExpressionModelListController::expressionRowHeight(int j) {
if (isAddEmptyRow(j)) {
return Metric::StoreRowHeight;
}
ExpiringPointer<ExpressionModelHandle> m = modelStore()->modelForRecord(modelStore()->recordAtIndex(j));
ExpiringPointer<SingleExpressionModelHandle> 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<ExpressionModelHandle> m = modelStore()->modelForRecord(modelStore()->recordAtIndex(j));
ExpiringPointer<SingleExpressionModelHandle> 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<ExpressionModelHandle> model = modelStore()->modelForRecord(record);
ExpiringPointer<SingleExpressionModelHandle> 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<ExpressionModelHandle> model) {
void StorageExpressionModelListController::reinitSelectedExpression(ExpiringPointer<SingleExpressionModelHandle> 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<ExpressionModelHandle> model = modelStore()->modelForRecord(record);
ExpiringPointer<SingleExpressionModelHandle> 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<ExpressionModelHandle> model = modelStore()->modelForRecord(record);
ExpiringPointer<SingleExpressionModelHandle> model = modelStore()->modelForRecord(record);
return (model->setContent(text) == Ion::Storage::Record::ErrorStatus::None);
}

View File

@@ -25,7 +25,7 @@ protected:
bool handleEventOnExpression(Ion::Events::Event event);
virtual void addEmptyModel();
virtual void didChangeModelsList() { resetMemoization(); }
virtual void reinitSelectedExpression(ExpiringPointer<ExpressionModelHandle> model);
virtual void reinitSelectedExpression(ExpiringPointer<SingleExpressionModelHandle> model);
virtual void editExpression(Ion::Events::Event event);
virtual bool editSelectedRecordWithText(const char * text);
virtual bool removeModelRow(Ion::Storage::Record record);

View File

@@ -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)) {

View File

@@ -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 <ion/storage.h>
#include <assert.h>
@@ -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<ExpressionModelHandle> modelForRecord(Ion::Storage::Record record) const { return ExpiringPointer<ExpressionModelHandle>(privateModelForRecord(record)); }
Ion::Storage::Record definedRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](SingleExpressionModelHandle * m) { return m->isDefined(); }); }
ExpiringPointer<SingleExpressionModelHandle> modelForRecord(Ion::Storage::Record record) const { return ExpiringPointer<SingleExpressionModelHandle>(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

View File

@@ -2,11 +2,11 @@
#define SHARED_STORAGE_FUNCTION_H
#include <poincare/function.h>
#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;

View File

@@ -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<StorageFunction *>(m)->isActive(); }); }
Ion::Storage::Record activeRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](ExpressionModelHandle * m) { return m->isDefined() && static_cast<StorageFunction *>(m)->isActive(); }); }
int numberOfActiveFunctions() const { return numberOfModelsSatisfyingTest([](SingleExpressionModelHandle * m) { return m->isDefined() && static_cast<StorageFunction *>(m)->isActive(); }); }
Ion::Storage::Record activeRecordAtIndex(int i) const { return recordStatifyingTestAtIndex(i, [](SingleExpressionModelHandle * m) { return m->isDefined() && static_cast<StorageFunction *>(m)->isActive(); }); }
ExpiringPointer<StorageFunction> modelForRecord(Ion::Storage::Record record) const { return ExpiringPointer<StorageFunction>(static_cast<StorageFunction *>(privateModelForRecord(record))); }

View File

@@ -6,6 +6,7 @@
#include <poincare/rational.h>
#include <ion/unicode/utf8_helper.h>
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<const Constant &>(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<const Equal&>(e).standardEquation(*context, Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), expressionClone(), *context), preferences->angleUnit());
m_standardForm = static_cast<const Equal&>(e).standardEquation(*context, Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), expressionClone(record), *context), preferences->angleUnit());
} else {
assert(e.type() == ExpressionNode::Type::Rational && static_cast<const Rational&>(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<const Constant &>(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;
}
}

View File

@@ -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;
};
}

View File

@@ -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];
}

View File

@@ -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);