From ace75e6eadd7eebc58c9c70ae7bdf4f3b3e615bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 29 May 2018 16:21:51 +0200 Subject: [PATCH] [solver] Move Poincare::Equal::solve() to Solver app to consider system instead of single expression --- apps/shared/expression_model_store.h | 2 +- apps/solver/Makefile | 1 + apps/solver/equation.cpp | 45 ++++++ apps/solver/equation.h | 12 ++ apps/solver/equation_store.cpp | 178 +++++++++++++++++++++ apps/solver/equation_store.h | 30 +++- poincare/include/poincare/addition.h | 2 +- poincare/include/poincare/equal.h | 4 +- poincare/include/poincare/expression.h | 13 +- poincare/include/poincare/multiplication.h | 2 +- poincare/include/poincare/power.h | 2 +- poincare/include/poincare/symbol.h | 3 +- poincare/src/addition.cpp | 2 +- poincare/src/equal.cpp | 151 +---------------- poincare/src/expression.cpp | 57 ++++++- poincare/src/multiplication.cpp | 2 +- poincare/src/power.cpp | 2 +- poincare/src/symbol.cpp | 11 +- 18 files changed, 351 insertions(+), 168 deletions(-) create mode 100644 apps/solver/equation.cpp diff --git a/apps/shared/expression_model_store.h b/apps/shared/expression_model_store.h index ce9077841..bb2cee5de 100644 --- a/apps/shared/expression_model_store.h +++ b/apps/shared/expression_model_store.h @@ -18,7 +18,7 @@ public: virtual void removeAll(); int numberOfModels() const { return m_numberOfModels; }; virtual int maxNumberOfModels() const = 0; - void tidy(); + virtual void tidy(); protected: virtual ExpressionModel * emptyModel() = 0; virtual ExpressionModel * nullModel() = 0; diff --git a/apps/solver/Makefile b/apps/solver/Makefile index 0d4774bf8..e6996c14a 100644 --- a/apps/solver/Makefile +++ b/apps/solver/Makefile @@ -4,6 +4,7 @@ snapshot_headers += apps/solver/app.h app_objs += $(addprefix apps/solver/,\ app.o\ equation_models_parameter_controller.o\ + equation.o\ equation_store.o\ list_controller.o\ ) diff --git a/apps/solver/equation.cpp b/apps/solver/equation.cpp new file mode 100644 index 000000000..ee3ae35b0 --- /dev/null +++ b/apps/solver/equation.cpp @@ -0,0 +1,45 @@ +#include "equation.h" + +using namespace Poincare; + +namespace Solver { + +Equation::Equation() : + Shared::ExpressionModel(), + m_standardForm(nullptr) +{ +} + +Equation& Equation::operator=(const Equation& other) { + Shared::ExpressionModel::operator=(other); + return *this; +} + +Equation::~Equation() { + if (m_standardForm) { + delete m_standardForm; + m_standardForm = nullptr; + } +} + +void Equation::setContent(const char * c) { + tidy(); + ExpressionModel::setContent(c); +} + +void Equation::tidy() { + ExpressionModel::tidy(); + if (m_standardForm) { + delete m_standardForm; + m_standardForm = nullptr; + } +} + +Expression * Equation::standardForm(Context * context) const { + if (m_standardForm == nullptr) { + m_standardForm = static_cast(expression(context))->standardEquation(*context); + } + return m_standardForm; +} + +} diff --git a/apps/solver/equation.h b/apps/solver/equation.h index d2c57f4c1..e615454cc 100644 --- a/apps/solver/equation.h +++ b/apps/solver/equation.h @@ -6,6 +6,18 @@ namespace Solver { class Equation : public Shared::ExpressionModel { +public: + Equation(); + ~Equation(); + Equation& operator=(const Equation& other); + Equation& operator=(Equation&& other) = delete; + Equation(const Equation& other) = delete; + Equation(Equation&& other) = delete; + void setContent(const char * c) override; + void tidy() override; + Poincare::Expression * standardForm(Poincare::Context * context) const; +private: + mutable Poincare::Expression * m_standardForm; }; } diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index f2a3e2a56..d29163a12 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -1,7 +1,21 @@ #include "equation_store.h" +using namespace Poincare; + namespace Solver { +EquationStore::EquationStore() : + m_equations{}, + m_form(Form::LinearSystem), + m_numberOfSolutions(0), + m_exactSolutions{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr} +{ +} + +EquationStore::~EquationStore() { + tidySolution(); +} + Equation * EquationStore::emptyModel() { static Equation e; return &e; @@ -11,4 +25,168 @@ void EquationStore::setModelAtIndex(Shared::ExpressionModel * e, int i) { m_equations[i] = *(static_cast(e));; } +void EquationStore::tidy() { + ExpressionModelStore::tidy(); + tidySolution(); +} + +EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { + tidySolution(); + char variables[Expression::k_maxNumberOfVariables+1] = {0}; + int numberOfVariables; + for (int i = 0; i < numberOfModels(); i++) { + numberOfVariables = m_equations[i].standardForm(context)->getVariables(variables); + if (numberOfVariables < 0) { + return Error::TooManyVariables; + } + } + + // 1-- Linear System + /* Create matrix coefficients and vector constants as: + * coefficients*(x y z ...) = constants */ + Expression * coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables]; + Expression * constants[k_maxNumberOfEquations]; + bool success = true; + for (int i = 0; i < numberOfModels(); i++) { + success = success && m_equations[i].standardForm(context)->getLinearCoefficients(variables, coefficients[i], &constants[i], *context); + if (!success) { + for (int j = 0; j < i; j++) { + for (int k = 0; k < numberOfVariables; k++) { + delete coefficients[j][k]; + } + delete constants[j]; + } + if (numberOfModels() > 1 || numberOfVariables > 1) { + return Error::NonLinearSystem; + } else { + break; + } + } + } + if (success) { + for (int i = 0; i < numberOfModels(); i++) { + for (int k = 0; k < numberOfVariables; k++) { + Expression::Reduce(&coefficients[i][k], *context); + } + Expression::Reduce(&constants[i], *context); + } + m_form = Form::LinearSystem; + return resolveLinearSystem(coefficients, constants, context); + } + assert(numberOfVariables == 1 && numberOfModels() == 1); + char x = variables[0]; + Expression * polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; + int degree = m_equations[0].standardForm(context)->getPolynomialCoefficients(x, polynomialCoefficients); + if (degree < 0) { + m_form = Form::Monovariable; + return Error::RequireApproximateSolution; + } + m_form = Form::PolynomialMonovariable; + for (int i = 0; i <= degree; i++) { + Expression::Reduce(&polynomialCoefficients[i], *context); + } + return oneDimensialPolynomialSolve(polynomialCoefficients, degree, context); +} + +EquationStore::Error EquationStore::resolveLinearSystem(Expression * coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables], Expression * constants[k_maxNumberOfEquations], Context * context) { + m_numberOfSolutions = 5; + m_exactSolutions[0] = new Rational(1); + m_exactSolutions[1] = new Rational(2); + m_exactSolutions[2] = new Rational(3); + m_exactSolutions[3] = new Rational(4); + m_exactSolutions[4] = new Rational(5); + return Error::NoError; +} + +EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression * coefficients[Expression::k_maxNumberOfPolynomialCoefficients], int degree, Context * context) { + assert(degree == 2); + Expression * deltaDenominator[3] = {new Rational(4), coefficients[0]->clone(), coefficients[2]->clone()}; + Expression * delta = new Subtraction(new Power(coefficients[1]->clone(), new Rational(2), false), new Multiplication(deltaDenominator, 3, false), false); + Expression::Simplify(&delta, *context); + if (delta->isRationalZero()) { + m_exactSolutions[0] = new Division(new Opposite(coefficients[1], false), new Multiplication(new Rational(2), coefficients[2]), false); + m_numberOfSolutions = 1; + } else { + m_exactSolutions[0] = new Division(new Subtraction(new Opposite(coefficients[1]->clone(), false), new SquareRoot(delta->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]->clone()), false); + m_exactSolutions[1] = new Division(new Addition(new Opposite(coefficients[1], false), new SquareRoot(delta->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]), false); + m_numberOfSolutions = 2; + } + m_exactSolutions[m_numberOfSolutions] = delta; + delete coefficients[0]; + return Error::NoError; +#if 0 + if (degree == 3) { + Expression * a = coefficients[3]; + Expression * b = coefficients[2]; + Expression * c = coefficients[1]; + Expression * d = coefficients[0]; + // Delta = b^2*c^2+18abcd-27a^2*d^2-4ac^3-4db^3 + Expression * mult0Operands[2] = {new Power(b->clone(), new Rational(2), false), new Power(c->clone(), new Rational(2), false)}; + Expression * mult1Operands[5] = {new Rational(18), a->clone(), b->clone(), c->clone(), d->clone()}; + Expression * mult2Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), new Power(d->clone(), new Rational(2), false)}; + Expression * mult3Operands[3] = {new Rational(-4), a->clone(), new Power(c->clone(), new Rational(3), false)}; + Expression * mult4Operands[3] = {new Rational(-4), d->clone(), new Power(b->clone(), new Rational(3), false)}; + Expression * add0Operands[5] = {new Multiplication(mult0Operands, 2, false), new Multiplication(mult1Operands, 5, false), new Multiplication(mult2Operands, 3, false), new Multiplication(mult3Operands, 3, false), new Multiplication(mult4Operands, 3, false)}; + Expression * delta = new Addition(add0Operands, 5, false); + Simplify(&delta, *context); + // Delta0 = b^2-3ac + Expression * mult5Operands[3] = {new Rational(3), a->clone(), c->clone()}; + Expression * delta0 = new Subtraction(new Power(b->clone(), new Rational(2), false), new Multiplication(mult5Operands, 3, false), false); + Reduce(&delta0, *context); + if (delta->isRationalZero()) { + if (delta0->isRationalZero()) { + // delta0 = 0 && delta = 0 --> x0 = -b/(3a) + delete delta0; + m_exactSolutions[0] = new Opposite(new Division(b, new Multiplication(new Rational(3), a, false), false), false); + m_numberOfSolutions = 1; + delete c; + delete d; + } else { + // delta = 0 --> x0 = (9ad-bc)/(2delta0) + // --> x1 = (4abc-9a^2d-b^3)/(a*delta0) + Expression * mult6Operands[3] = {new Rational(9), a, d}; + m_exactSolutions[0] = new Division(new Subtraction(new Multiplication(mult6Operands, 3, false), new Multiplication(b, c, false), false), new Multiplication(new Rational(2), delta0, false), false); + Expression * mult7Operands[4] = {new Rational(4), a->clone(), b->clone(), c->clone()}; + Expression * mult8Operands[3] = {new Rational(-9), new Power(a->clone(), new Rational(2), false), d->clone()}; + Expression * add1Operands[3] = {new Multiplication(mult7Operands, 4, false), new Multiplication(mult8Operands,3, false), new Opposite(new Power(b->clone(), new Rational(3), false), false)}; + m_exactSolutions[1] = new Division(new Addition(add1Operands, 3, false), new Multiplication(a->clone(), delta0, false), false); + m_numberOfSolutions = 2; + } + } else { + // delta1 = 2b^3-9abc+27a^2*d + Expression * mult9Operands[4] = {new Rational(-9), a, b, c}; + Expression * mult10Operands[3] = {new Rational(27), new Power(a->clone(), new Rational(2), false), d}; + Expression * add2Operands[3] = {new Multiplication(new Rational(2), new Power(b->clone(), new Rational(3), false), false), new Multiplication(mult9Operands, 4, false), new Multiplication(mult10Operands, 3, false)}; + Expression * delta1 = new Addition(add2Operands, 3, false); + // C = Root((delta1+sqrt(-27a^2*delta))/2, 3) + Expression * mult11Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), (*delta)->clone()}; + Expression * c = new Power(new Division(new Addition(delta1, new SquareRoot(new Multiplication(mult11Operands, 3, false), false), false), new Rational(2), false), new Rational(1,3), false); + Expression * unary3roots[2] = {new Addition(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false), new Subtraction(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false)}; + // x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root + for (int k = 0; k < 3; k++) { + Expression * ccopy = c; + Expression * delta0copy = delta0; + if (k < 2) { + ccopy = new Multiplication(c->clone(), unary3roots[k], false); + delta0copy = delta0->clone(); + } + Expression * add3Operands[3] = {b->clone(), ccopy, new Division(delta0copy, ccopy->clone(), false)}; + m_exactSolutions[k] = new Multiplication(new Division(new Rational(-1), new Multiplication(new Rational(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false); + } + m_numberOfSolutions = 3; + } + m_exactSolutions[m_numberOfSolutions] = delta; + } +#endif +} + +void EquationStore::tidySolution() { + for (int i = 0; i < k_maxNumberOfExactSolutions; i++) { + if (m_exactSolutions[i]) { + delete m_exactSolutions[i]; + m_exactSolutions[i] = nullptr; + } + } +} + } diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index 98da959d9..267a54f58 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -9,20 +9,44 @@ namespace Solver { class EquationStore : public Shared::ExpressionModelStore { public: - EquationStore() {} + enum class Form { + LinearSystem, + PolynomialMonovariable, + Monovariable, + }; + enum class Error : int16_t { + NoError = 0, + TooManyVariables = -1, + NonLinearSystem = -2, + RequireApproximateSolution = -3 + }; + EquationStore(); + ~EquationStore(); Equation * modelAtIndex(int i) override { assert(i>=0 && i Poincare::Expression::k_maxPolynomialDegree ? Poincare::Expression::k_maxNumberOfVariables : Poincare::Expression::k_maxPolynomialDegree; + static constexpr int k_maxNumberOfApproximateSolutions = 10; Equation * emptyModel() override; Equation * nullModel() override { return emptyModel(); } void setModelAtIndex(Shared::ExpressionModel * f, int i) override; - static constexpr int k_maxNumberOfEquations = Poincare::Expression::k_maxNumberOfVariables; // Enable the same number of equations as the number of unknown variables - Equation m_equations[k_maxNumberOfEquations];; + Error resolveLinearSystem(Poincare::Expression * coefficients[k_maxNumberOfEquations][Poincare::Expression::k_maxNumberOfVariables], Poincare::Expression * constants[k_maxNumberOfEquations], Poincare::Context * context); + Error oneDimensialPolynomialSolve(Poincare::Expression * polynomialCoefficients[Poincare::Expression::k_maxNumberOfPolynomialCoefficients], int degree, Poincare::Context * context); + void tidySolution(); + + Equation m_equations[k_maxNumberOfEquations]; + Form m_form; + int m_numberOfSolutions; + Poincare::Expression * m_exactSolutions[k_maxNumberOfExactSolutions]; + double m_approximateSolutions[k_maxNumberOfApproximateSolutions]; }; } diff --git a/poincare/include/poincare/addition.h b/poincare/include/poincare/addition.h index 92b6878a6..cf468914a 100644 --- a/poincare/include/poincare/addition.h +++ b/poincare/include/poincare/addition.h @@ -20,7 +20,7 @@ public: Type type() const override; Expression * clone() const override; int polynomialDegree(char symbolName) const override; - int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override; + int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override; /* Evaluation */ template static Complex compute(const Complex c, const Complex d); template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { diff --git a/poincare/include/poincare/equal.h b/poincare/include/poincare/equal.h index 7ae06ced2..14d775f23 100644 --- a/poincare/include/poincare/equal.h +++ b/poincare/include/poincare/equal.h @@ -13,9 +13,9 @@ public: Type type() const override; Expression * clone() const override; int polynomialDegree(char symbolName) const override; - int solve(Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const; + // For the equation A = B, create the reduced expression A-B + Expression * standardEquation(Context & context, AngleUnit angleUnit = AngleUnit::Default) const; private: - int oneDimensialPolynomialSolve(Expression ** coefficients, int degree, Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const; /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Layout */ diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 122de8db0..7649e21f6 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -196,6 +196,7 @@ public: Unknown = 0, Positive = 1 }; + bool isRationalZero() const; virtual Sign sign() const { return Sign::Unknown; } typedef bool (*ExpressionTest)(const Expression * e, Context & context); bool recursivelyMatches(ExpressionTest test, Context & context) const; @@ -222,12 +223,18 @@ public: * variables would overflow the maxNumberOfVariables, getVariables return -1 */ static constexpr int k_maxNumberOfVariables = 6; virtual int getVariables(char * variables) const; + /* getLinearCoefficients return false if the expression is not linear with + * the variables hold in 'variables'. Otherwise, it fills 'coefficients' with + * the coefficients of the variables hold in 'variables' (following the same + * order) and 'constant' with the constant of the expression. */ + bool getLinearCoefficients(char * variables, Expression * coefficients[], Expression * constant[], Context & context) const; /* getPolynomialCoefficients fills the table coefficients with the expressions * of the first 5 polynomial coefficients and return polynomialDegree. * coefficients has up to 5 entries. It supposed to be called on Reduced * expression. */ - static constexpr int k_maxNumberOfPolynomialCoefficients = 4+1; - virtual int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const; + static constexpr int k_maxPolynomialDegree = 2; + static constexpr int k_maxNumberOfPolynomialCoefficients = k_maxPolynomialDegree+1; + virtual int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const; /* Comparison */ /* isIdenticalTo is the "easy" equality, it returns true if both trees have @@ -246,7 +253,7 @@ public: /* Simplification */ static Expression * ParseAndSimplify(const char * text, Context & context, AngleUnit angleUnit = AngleUnit::Default); static void Simplify(Expression ** expressionAddress, Context & context, AngleUnit angleUnit = AngleUnit::Default); - static void Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit, bool recursively = true); + static void Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit = AngleUnit::Default, bool recursively = true); /* Evaluation Engine * The function evaluate creates a new expression and thus mallocs memory. diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index 246357838..f1ba2c7eb 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -23,7 +23,7 @@ public: Expression * clone() const override; Sign sign() const override; int polynomialDegree(char symbolName) const override; - int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override; + int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override; /* Evaluation */ template static Complex compute(const Complex c, const Complex d); template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * m) { diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 581d04c1d..e688d7b5b 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -22,7 +22,7 @@ public: Expression * clone() const override; Sign sign() const override; int polynomialDegree(char symbolName) const override; - int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override; + int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override; template static Complex compute(const Complex c, const Complex d); private: constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25; diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index c64d2d348..2cc45b0fe 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -37,10 +37,11 @@ public: Expression * clone() const override; int polynomialDegree(char symbolName) const override; int getVariables(char * variables) const override; - int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override; + int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override; Sign sign() const override; bool isMatrixSymbol() const; bool isScalarSymbol() const; + bool isVariableSymbol() const; bool isApproximate(Context & context) const; float characteristicXRange(Context & context, AngleUnit angleUnit = AngleUnit::Default) const override; bool hasAnExactRepresentation(Context & context) const; diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index d403a64d5..c51060e2b 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -35,7 +35,7 @@ int Addition::polynomialDegree(char symbolName) const { return degree; } -int Addition::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { +int Addition::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const { int deg = polynomialDegree(symbolName); if (deg < 0 || deg > k_maxNumberOfPolynomialCoefficients-1) { return -1; diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index 71b3b9a62..58db799c9 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -33,44 +33,10 @@ int Equal::polynomialDegree(char symbolName) const { return -1; } -int Equal::solve(Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const { +Expression * Equal::standardEquation(Context & context, AngleUnit angleUnit) const { Expression * sub = new Subtraction(operand(0), operand(1), true); Reduce(&sub, context, angleUnit); - char variables[k_maxNumberOfVariables+1] = {0}; - int var = sub->getVariables(variables); - if (var < 0) { - delete sub; - // Too many unknown variables - return -10; - } - if (var == 0) { - int sol = -1; - if (sub->type() == Type::Rational && static_cast(sub)->isZero()) { - // 0 == 0 - sol = INT_MAX; - } - delete sub; - return sol; - } - if (var == 1) { - char variable = variables[0]; - Expression * coefficients[k_maxNumberOfPolynomialCoefficients]; - int deg = sub->getPolynomialCoefficients(variable, coefficients); - if (deg < 0 || deg > 2) { - // Case 0: 1 unknown variable, non-polynomial --> approximate solution - return 0; - } - delete sub; - for (int i = 0; i <= deg; i++) { - Reduce(&coefficients[i], context, angleUnit); - } - // Case 1: 1 unknown variable, polynomial of degree <= 2 --> Exact solution - return oneDimensialPolynomialSolve(coefficients, deg, solutions, delta, context, angleUnit); - } - assert(var > 1); - // Case 2: several variables, linear --> ? - delete sub; - return 0; + return sub; } Expression * Equal::shallowReduce(Context& context, AngleUnit angleUnit) { @@ -84,119 +50,6 @@ Expression * Equal::shallowReduce(Context& context, AngleUnit angleUnit) { return this; } -int Equal::oneDimensialPolynomialSolve(Expression ** coefficients, int degree, Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const { - int sol = -1; - if (degree == 0) { - if (coefficients[0]->type() == Type::Rational && static_cast(coefficients[0])->isZero()) { - // 0 == 0 - sol = INT_MAX; - } - delete coefficients[0]; - return sol; - } - switch (degree) { - case 1: - { - // ax+b = 0 - solutions[0] = new Division(new Opposite(coefficients[0], false), coefficients[1], false); - sol = 1; - break; - } - case 2: - { - Expression * deltaDenominator[3] = {new Rational(4), coefficients[0]->clone(), coefficients[2]->clone()}; - *delta = new Subtraction(new Power(coefficients[1]->clone(), new Rational(2), false), new Multiplication(deltaDenominator, 3, false), false); - Simplify(delta, context, angleUnit); - if ((*delta)->type() == Type::Rational && static_cast(*delta)->isZero()) { - solutions[0] = new Division(new Opposite(coefficients[1], false), new Multiplication(new Rational(2), coefficients[2]), false); - sol = 1; - } else { - solutions[0] = new Division(new Subtraction(new Opposite(coefficients[1]->clone(), false), new SquareRoot((*delta)->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]->clone()), false); - solutions[1] = new Division(new Addition(new Opposite(coefficients[1], false), new SquareRoot((*delta)->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]), false); - sol = 2; - } - delete coefficients[0]; - break; - } -#if 0 - case 3: - { - Expression * a = coefficients[3]; - Expression * b = coefficients[2]; - Expression * c = coefficients[1]; - Expression * d = coefficients[0]; - // Delta = b^2*c^2+18abcd-27a^2*d^2-4ac^3-4db^3 - Expression * mult0Operands[2] = {new Power(b->clone(), new Rational(2), false), new Power(c->clone(), new Rational(2), false)}; - Expression * mult1Operands[5] = {new Rational(18), a->clone(), b->clone(), c->clone(), d->clone()}; - Expression * mult2Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), new Power(d->clone(), new Rational(2), false)}; - Expression * mult3Operands[3] = {new Rational(-4), a->clone(), new Power(c->clone(), new Rational(3), false)}; - Expression * mult4Operands[3] = {new Rational(-4), d->clone(), new Power(b->clone(), new Rational(3), false)}; - Expression * add0Operands[5] = {new Multiplication(mult0Operands, 2, false), new Multiplication(mult1Operands, 5, false), new Multiplication(mult2Operands, 3, false), new Multiplication(mult3Operands, 3, false), new Multiplication(mult4Operands, 3, false)}; - *delta = new Addition(add0Operands, 5, false); - Simplify(delta, context, angleUnit); - // Delta0 = b^2-3ac - Expression * mult5Operands[3] = {new Rational(3), a->clone(), c->clone()}; - Expression * delta0 = new Subtraction(new Power(b->clone(), new Rational(2), false), new Multiplication(mult5Operands, 3, false), false); - Reduce(&delta0, context, angleUnit); - if ((*delta)->type() == Type::Rational && static_cast(*delta)->isZero()) { - if (delta0->type() == Type::Rational && static_cast(delta0)->isZero()) { - // delta0 = 0 && delta = 0 --> x0 = -b/(3a) - delete delta0; - solutions[0] = new Opposite(new Division(b, new Multiplication(new Rational(3), a, false), false), false); - sol = 1; - delete c; - delete d; - break; - } - // delta = 0 --> x0 = (9ad-bc)/(2delta0) - // --> x1 = (4abc-9a^2d-b^3)/(a*delta0) - Expression * mult6Operands[3] = {new Rational(9), a, d}; - solutions[0] = new Division(new Subtraction(new Multiplication(mult6Operands, 3, false), new Multiplication(b, c, false), false), new Multiplication(new Rational(2), delta0, false), false); - Expression * mult7Operands[4] = {new Rational(4), a->clone(), b->clone(), c->clone()}; - Expression * mult8Operands[3] = {new Rational(-9), new Power(a->clone(), new Rational(2), false), d->clone()}; - Expression * add1Operands[3] = {new Multiplication(mult7Operands, 4, false), new Multiplication(mult8Operands,3, false), new Opposite(new Power(b->clone(), new Rational(3), false), false)}; - solutions[1] = new Division(new Addition(add1Operands, 3, false), new Multiplication(a->clone(), delta0, false), false); - sol = 2; - break; - } else { - // delta1 = 2b^3-9abc+27a^2*d - Expression * mult9Operands[4] = {new Rational(-9), a, b, c}; - Expression * mult10Operands[3] = {new Rational(27), new Power(a->clone(), new Rational(2), false), d}; - Expression * add2Operands[3] = {new Multiplication(new Rational(2), new Power(b->clone(), new Rational(3), false), false), new Multiplication(mult9Operands, 4, false), new Multiplication(mult10Operands, 3, false)}; - Expression * delta1 = new Addition(add2Operands, 3, false); - // C = Root((delta1+sqrt(-27a^2*delta))/2, 3) - Expression * mult11Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), (*delta)->clone()}; - Expression * c = new Power(new Division(new Addition(delta1, new SquareRoot(new Multiplication(mult11Operands, 3, false), false), false), new Rational(2), false), new Rational(1,3), false); - Expression * unary3roots[2] = {new Addition(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false), new Subtraction(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false)}; - // x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root - for (int k = 0; k < 3; k++) { - Expression * ccopy = c; - Expression * delta0copy = delta0; - if (k < 2) { - ccopy = new Multiplication(c->clone(), unary3roots[k], false); - delta0copy = delta0->clone(); - } - Expression * add3Operands[3] = {b->clone(), ccopy, new Division(delta0copy, ccopy->clone(), false)}; - solutions[k] = new Multiplication(new Division(new Rational(-1), new Multiplication(new Rational(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false); - } - sol = 3; - break; - } - } -#endif - default: - { - assert(false); - sol = 0; - break; - } - } - for (int i = 0; i < sol; i++) { - Simplify(&solutions[i], context, angleUnit); - } - return sol; -} - ExpressionLayout * Equal::privateCreateLayout(PrintFloat::Mode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != PrintFloat::Mode::Default); assert(complexFormat != ComplexFormat::Default); diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 44308035b..255a02a9c 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -157,6 +157,10 @@ bool Expression::hasAncestor(const Expression * e) const { /* Properties */ +bool Expression::isRationalZero() const { + return type() == Type::Rational && static_cast(this)->isZero(); +} + bool Expression::recursivelyMatches(ExpressionTest test, Context & context) const { if (test(this, context)) { return true; @@ -219,7 +223,58 @@ int Expression::getVariables(char * variables) const { return numberOfVariables; } -int Expression::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { +bool Expression::getLinearCoefficients(char * variables, Expression * coefficients[], Expression ** constant, Context & context) const { + char * x = variables; + while (*x != 0) { + int degree = polynomialDegree(*x); + if (degree > 1 || degree < 0) { + return false; + } + x++; + } + Expression * equation = clone(); + x = variables; + int index = 0; + Expression * polynomialCoefficients[k_maxNumberOfPolynomialCoefficients]; + while (*x != 0) { + int degree = equation->getPolynomialCoefficients(*x, polynomialCoefficients); + if (degree == 1) { + coefficients[index] = polynomialCoefficients[1]; + } else { + assert(degree == 0); + coefficients[index] = new Rational(0); + } + delete equation; + equation = polynomialCoefficients[0]; + x++; + index++; + } + *constant = equation; + // xy = 0? + bool isMultivariablePolynomial = (*constant)->recursivelyMatches([](const Expression * e, Context & context) { + return e->type() == Type::Symbol && static_cast(e)->isVariableSymbol(); + }, context); + for (int i = 0; i < index; i++) { + if (isMultivariablePolynomial) { + break; + } + isMultivariablePolynomial |= coefficients[i]->recursivelyMatches([](const Expression * e, Context & context) { + return e->type() == Type::Symbol && static_cast(e)->isVariableSymbol(); + }, context); + } + if (isMultivariablePolynomial) { + for (int i = 0; i < index; i++) { + delete coefficients[i]; + coefficients[i] = nullptr; + } + delete *constant; + *constant = nullptr; + return false; + } + return true; +} + +int Expression::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const { int deg = polynomialDegree(symbolName); if (deg == 0) { coefficients[0] = clone(); diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index d5ced1d38..f03e06fb0 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -43,7 +43,7 @@ int Multiplication::polynomialDegree(char symbolName) const { return degree; } -int Multiplication::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { +int Multiplication::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const { int deg = polynomialDegree(symbolName); if (deg < 0 || deg + 1 > k_maxNumberOfPolynomialCoefficients) { return -1; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 4bc1632f2..26d7f32fb 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -79,7 +79,7 @@ int Power::polynomialDegree(char symbolName) const { return -1; } -int Power::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { +int Power::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const { int deg = polynomialDegree(symbolName); if (deg <= 0) { return deg; diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index ee4a30758..2f61fc72c 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -115,7 +115,7 @@ int Symbol::polynomialDegree(char symbol) const { int Symbol::getVariables(char * variables) const { size_t variablesLength = strlen(variables); - if (m_name >= 'a' && m_name <= 'z') { + if (isVariableSymbol()) { char * currentChar = variables; while (*currentChar != 0) { if (*currentChar == m_name) { @@ -133,7 +133,7 @@ int Symbol::getVariables(char * variables) const { return variablesLength; } -int Symbol::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { +int Symbol::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const { if (m_name == symbolName) { coefficients[0] = new Rational(0); coefficients[1] = new Rational(1); @@ -300,6 +300,13 @@ bool Symbol::isScalarSymbol() const { return false; } +bool Symbol::isVariableSymbol() const { + if (m_name >= 'a' && m_name <= 'z') { + return true; + } + return false; +} + int Symbol::simplificationOrderSameType(const Expression * e, bool canBeInterrupted) const { assert(e->type() == Expression::Type::Symbol); if ((uint8_t)m_name == ((uint8_t)static_cast(e)->name())) {