diff --git a/apps/graph/storage_cartesian_function_store.cpp b/apps/graph/storage_cartesian_function_store.cpp index ecf878b1a..4b145b352 100644 --- a/apps/graph/storage_cartesian_function_store.cpp +++ b/apps/graph/storage_cartesian_function_store.cpp @@ -16,12 +16,12 @@ Ion::Storage::Record::ErrorStatus StorageCartesianFunctionStore::addEmptyModel() } void StorageCartesianFunctionStore::setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record record) const { - assert(cacheIndex >= 0 && cacheIndex < k_maxNumberOfMemoizedModels); + assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels()); m_functions[cacheIndex] = StorageCartesianFunction(record); } ExpressionModelHandle * StorageCartesianFunctionStore::memoizedModelAtIndex(int cacheIndex) const { - assert(cacheIndex >= 0 && cacheIndex < k_maxNumberOfMemoizedModels); + assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels()); return &m_functions[cacheIndex]; } diff --git a/apps/shared/storage_expression_model_store.cpp b/apps/shared/storage_expression_model_store.cpp index 82ac68e7c..2a385730c 100644 --- a/apps/shared/storage_expression_model_store.cpp +++ b/apps/shared/storage_expression_model_store.cpp @@ -16,14 +16,14 @@ Ion::Storage::Record StorageExpressionModelStore::recordAtIndex(int i) const { } ExpressionModelHandle * StorageExpressionModelStore::privateModelForRecord(Ion::Storage::Record record) const { - for (int i = 0; i < k_maxNumberOfMemoizedModels; i++) { + 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); - m_oldestMemoizedIndex = (m_oldestMemoizedIndex+1) % k_maxNumberOfMemoizedModels; + m_oldestMemoizedIndex = (m_oldestMemoizedIndex+1) % maxNumberOfMemoizedModels(); return result; } @@ -77,7 +77,7 @@ Ion::Storage::Record StorageExpressionModelStore::recordStatifyingTestAtIndex(in void StorageExpressionModelStore::resetMemoizedModelsExceptRecord(const Ion::Storage::Record record) const { Ion::Storage::Record emptyRecord; - for (int i = 0; i < k_maxNumberOfMemoizedModels; i++) { + for (int i = 0; i < maxNumberOfMemoizedModels(); i++) { if (*memoizedModelAtIndex(i) != record) { setMemoizedModelAtIndex(i, emptyRecord); } diff --git a/apps/shared/storage_expression_model_store.h b/apps/shared/storage_expression_model_store.h index 0b19c057b..d681c0749 100644 --- a/apps/shared/storage_expression_model_store.h +++ b/apps/shared/storage_expression_model_store.h @@ -16,6 +16,8 @@ public: StorageExpressionModelStore(); // Getters + // 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(); }); } Ion::Storage::Record recordAtIndex(int i) const; @@ -28,10 +30,11 @@ public: void removeModel(Ion::Storage::Record record); // Other - void tidy(); + virtual void tidy(); void storageDidChangeForRecord(const Ion::Storage::Record record) const { resetMemoizedModelsExceptRecord(record); } protected: constexpr static int k_maxNumberOfMemoizedModels = 10; + int maxNumberOfMemoizedModels() const { return maxNumberOfModels() < 0 ? k_maxNumberOfMemoizedModels : maxNumberOfModels(); } typedef bool (*ModelTest)(ExpressionModelHandle * model); int numberOfModelsSatisfyingTest(ModelTest test) const; Ion::Storage::Record recordStatifyingTestAtIndex(int i, ModelTest test) const; diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index aabf245a2..116fbce32 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -1,4 +1,5 @@ #include "equation_store.h" +#include "../constant.h" #include "../shared/poincare_helpers.h" #include @@ -21,6 +22,7 @@ using namespace Shared; namespace Solver { EquationStore::EquationStore() : + StorageExpressionModelStore(), m_type(Type::LinearSystem), m_numberOfSolutions(0), m_exactSolutionExactLayouts{}, @@ -28,17 +30,32 @@ EquationStore::EquationStore() : { } -Equation * EquationStore::emptyModel() { - static Equation e; - return &e; +Ion::Storage::Record::ErrorStatus EquationStore::addEmptyModel() { + char name[3] = {'e', '?', 0}; // name is going to be e0 or e1 or ... e5 + int currentNumber = 0; + while (currentNumber < k_maxNumberOfEquations) { + name[1] = '0'+currentNumber; + if (Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(name, Equation::extension).isNull()) { + break; + } + currentNumber++; + } + assert(currentNumber < k_maxNumberOfEquations); + return Ion::Storage::sharedStorage()->createRecordWithExtension(name, Equation::extension, nullptr, 0); } -void EquationStore::setModelAtIndex(Shared::ExpressionModel * e, int i) { - m_equations[i] = *(static_cast(e));; +void EquationStore::setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record record) const { + assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels()); + m_equations[cacheIndex] = Equation(record); +} + +ExpressionModelHandle * EquationStore::memoizedModelAtIndex(int cacheIndex) const { + assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels()); + return &m_equations[cacheIndex]; } void EquationStore::tidy() { - ExpressionModelStore::tidy(); + StorageExpressionModelStore::tidy(); tidySolution(); } @@ -78,7 +95,7 @@ bool EquationStore::haveMoreApproximationSolutions(Context * context) { return false; } double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; - return !std::isnan(PoincareHelpers::NextRoot(definedModelAtIndex(0)->standardForm(context), m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context)); + return !std::isnan(PoincareHelpers::NextRoot(modelForRecord(definedRecordAtIndex(0))->standardForm(context), m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context)); } void EquationStore::approximateSolve(Poincare::Context * context) { @@ -88,7 +105,7 @@ void EquationStore::approximateSolve(Poincare::Context * context) { double start = m_intervalApproximateSolutions[0]; double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; for (int i = 0; i < k_maxNumberOfApproximateSolutions; i++) { - m_approximateSolutions[i] = PoincareHelpers::NextRoot(definedModelAtIndex(0)->standardForm(context), m_variables[0], start, step, m_intervalApproximateSolutions[1], *context); + m_approximateSolutions[i] = PoincareHelpers::NextRoot(modelForRecord(definedRecordAtIndex(0))->standardForm(context), m_variables[0], start, step, m_intervalApproximateSolutions[1], *context); if (std::isnan(m_approximateSolutions[i])) { break; } else { @@ -105,7 +122,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { m_variables[0][0] = 0; int numberOfVariables = 0; for (int i = 0; i < numberOfDefinedModels(); i++) { - const Expression e = definedModelAtIndex(i)->standardForm(context); + const Expression e = modelForRecord(definedRecordAtIndex(i))->standardForm(context); if (e.isUninitialized() || e.type() == ExpressionNode::Type::Undefined) { return Error::EquationUndefined; } @@ -130,7 +147,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { bool isLinear = true; // Invalid the linear system if one equation is non-linear Preferences * preferences = Preferences::sharedPreferences(); for (int i = 0; i < numberOfDefinedModels(); i++) { - isLinear = isLinear && definedModelAtIndex(i)->standardForm(context).getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], *context, updatedComplexFormat(), preferences->angleUnit()); + isLinear = isLinear && modelForRecord(definedRecordAtIndex(i))->standardForm(context).getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], *context, updatedComplexFormat(context), preferences->angleUnit()); if (!isLinear) { // TODO: should we clean pool allocated memory if the system is not linear #if 0 @@ -161,7 +178,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { // Step 2. Polynomial & Monovariable? assert(numberOfVariables == 1 && numberOfDefinedModels() == 1); Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; - int degree = definedModelAtIndex(0)->standardForm(context).getPolynomialReducedCoefficients(m_variables[0], polynomialCoefficients, *context, updatedComplexFormat(), preferences->angleUnit()); + int degree = modelForRecord(definedRecordAtIndex(0))->standardForm(context).getPolynomialReducedCoefficients(m_variables[0], polynomialCoefficients, *context, updatedComplexFormat(context), preferences->angleUnit()); if (degree == 2) { // Polynomial degree <= 2 m_type = Type::PolynomialMonovariable; @@ -189,14 +206,14 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { m_exactSolutionExactLayouts[solutionIndex] = PoincareHelpers::CreateLayout(exactSolutions[i]); m_exactSolutionApproximateLayouts[solutionIndex] = PoincareHelpers::CreateLayout(exactSolutionsApproximations[i]); // Check for identity between exact and approximate layouts - char exactBuffer[Shared::ExpressionModel::k_expressionBufferSize]; - char approximateBuffer[Shared::ExpressionModel::k_expressionBufferSize]; - m_exactSolutionExactLayouts[solutionIndex].serializeForParsing(exactBuffer, Shared::ExpressionModel::k_expressionBufferSize); - m_exactSolutionApproximateLayouts[solutionIndex].serializeForParsing(approximateBuffer, Shared::ExpressionModel::k_expressionBufferSize); + char exactBuffer[::Constant::MaxSerializedExpressionSize]; + char approximateBuffer[::Constant::MaxSerializedExpressionSize]; + m_exactSolutionExactLayouts[solutionIndex].serializeForParsing(exactBuffer, ::Constant::MaxSerializedExpressionSize); + m_exactSolutionApproximateLayouts[solutionIndex].serializeForParsing(approximateBuffer, ::Constant::MaxSerializedExpressionSize); m_exactSolutionIdentity[solutionIndex] = strcmp(exactBuffer, approximateBuffer) == 0; if (!m_exactSolutionIdentity[solutionIndex]) { - char buffer[Shared::ExpressionModel::k_expressionBufferSize]; - m_exactSolutionEquality[solutionIndex] = exactSolutions[i].isEqualToItsApproximationLayout(exactSolutionsApproximations[i], buffer, Shared::ExpressionModel::k_expressionBufferSize, preferences->complexFormat(), preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context); + char buffer[::Constant::MaxSerializedExpressionSize]; + m_exactSolutionEquality[solutionIndex] = exactSolutions[i].isEqualToItsApproximationLayout(exactSolutionsApproximations[i], buffer, ::Constant::MaxSerializedExpressionSize, preferences->complexFormat(), preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context); } solutionIndex++; } @@ -221,7 +238,7 @@ EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolution Ab.setDimensions(m, n+1); // Compute the rank of (A | b) - int rankAb = Ab.rank(*context, updatedComplexFormat(), angleUnit, true); + int rankAb = Ab.rank(*context, updatedComplexFormat(context), angleUnit, true); // Initialize the number of solutions m_numberOfSolutions = INT_MAX; @@ -246,7 +263,7 @@ EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolution m_numberOfSolutions = n; for (int i = 0; i < m_numberOfSolutions; i++) { exactSolutions[i] = Ab.matrixChild(i,n); - exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], *context, updatedComplexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); + exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], *context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit()); } } } @@ -258,7 +275,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact assert(degree == 2); // Compute delta = b*b-4ac Expression delta = Subtraction::Builder(Power::Builder(coefficients[1].clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), coefficients[0].clone(), coefficients[2].clone())); - delta = delta.simplify(*context, updatedComplexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); + delta = delta.simplify(*context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit()); if (delta.isUninitialized()) { delta = Poincare::Undefined::Builder(); } @@ -275,7 +292,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact } exactSolutions[m_numberOfSolutions-1] = delta; for (int i = 0; i < m_numberOfSolutions; i++) { - exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], *context, updatedComplexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); + exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], *context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit()); } return Error::NoError; #if 0 @@ -351,17 +368,17 @@ void EquationStore::tidySolution() { } } -Preferences::ComplexFormat EquationStore::updatedComplexFormat() { +Preferences::ComplexFormat EquationStore::updatedComplexFormat(Context * context) { Preferences::ComplexFormat complexFormat = Preferences::sharedPreferences()->complexFormat(); - if (complexFormat == Preferences::ComplexFormat::Real && isExplictlyComplex()) { + if (complexFormat == Preferences::ComplexFormat::Real && isExplictlyComplex(context)) { return Preferences::ComplexFormat::Cartesian; } return complexFormat; } -bool EquationStore::isExplictlyComplex() { +bool EquationStore::isExplictlyComplex(Context * context) { for (int i = 0; i < numberOfDefinedModels(); i++) { - if (definedModelAtIndex(i)->containsIComplex()) { + if (modelForRecord(definedRecordAtIndex(i))->containsIComplex(context)) { return true; } } diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index 510c6dbdf..175d20177 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -2,13 +2,13 @@ #define SOLVER_EQUATION_STORE_H #include "equation.h" -#include "../shared/expression_model_store.h" +#include "../shared/storage_expression_model_store.h" #include #include namespace Solver { -class EquationStore : public Shared::ExpressionModelStore { +class EquationStore : public Shared::StorageExpressionModelStore { public: enum class Type { LinearSystem, @@ -23,14 +23,14 @@ public: NonLinearSystem = -4, RequireApproximateSolution = -5, }; - /* EquationStore */ EquationStore(); - Equation * modelAtIndex(int i) override { - assert(i>=0 && i(Shared::ExpressionModelStore::definedModelAtIndex(i)); } + + /* StorageExpressionModelStore */ int maxNumberOfModels() const override { return k_maxNumberOfEquations; } + Shared::ExpiringPointer modelForRecord(Ion::Storage::Record record) const { return Shared::ExpiringPointer(static_cast(privateModelForRecord(record))); } + Ion::Storage::Record::ErrorStatus addEmptyModel() override; + + /* EquationStore */ Type type() const { return m_type; } @@ -69,24 +69,28 @@ public: bool haveMoreApproximationSolutions(Poincare::Context * context); void tidy() override; + static constexpr int k_maxNumberOfExactSolutions = Poincare::Expression::k_maxNumberOfVariables > Poincare::Expression::k_maxPolynomialDegree + 1? Poincare::Expression::k_maxNumberOfVariables : Poincare::Expression::k_maxPolynomialDegree + 1; static constexpr int k_maxNumberOfApproximateSolutions = 10; static constexpr int k_maxNumberOfSolutions = k_maxNumberOfExactSolutions > k_maxNumberOfApproximateSolutions ? k_maxNumberOfExactSolutions : k_maxNumberOfApproximateSolutions; private: static constexpr double k_precision = 0.01; static constexpr int k_maxNumberOfEquations = Poincare::Expression::k_maxNumberOfVariables; // Enable the same number of equations as the number of unknown variables - Equation * emptyModel() override; - Equation * nullModel() override { - return emptyModel(); - } - void setModelAtIndex(Shared::ExpressionModel * f, int i) override; + + // StorageExpressionModelStore + const char * modelExtension() const override { return Equation::extension; } + /* 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; + 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); void tidySolution(); - bool isExplictlyComplex(); - Poincare::Preferences::ComplexFormat updatedComplexFormat(); + bool isExplictlyComplex(Poincare::Context * context); + Poincare::Preferences::ComplexFormat updatedComplexFormat(Poincare::Context * context); - Equation m_equations[k_maxNumberOfEquations]; + mutable Equation m_equations[k_maxNumberOfEquations]; Type m_type; char m_variables[Poincare::Expression::k_maxNumberOfVariables][Poincare::SymbolAbstract::k_maxNameSize]; int m_numberOfSolutions;