mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-19 05:40:38 +01:00
[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:
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
27
apps/shared/single_expression_model_handle.cpp
Normal file
27
apps/shared/single_expression_model_handle.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
44
apps/shared/single_expression_model_handle.h
Normal file
44
apps/shared/single_expression_model_handle.h
Normal 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
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))); }
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user