diff --git a/poincare/include/poincare/addition.h b/poincare/include/poincare/addition.h index 705ac88ec..92b6878a6 100644 --- a/poincare/include/poincare/addition.h +++ b/poincare/include/poincare/addition.h @@ -20,6 +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; /* 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/expression.h b/poincare/include/poincare/expression.h index 44d845bb0..122de8db0 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -222,10 +222,12 @@ public: * variables would overflow the maxNumberOfVariables, getVariables return -1 */ static constexpr int k_maxNumberOfVariables = 6; virtual int getVariables(char * variables) const; - /* getPolynomialCoefficients fill the table coefficients with the expressions - * of the first 4 polynomial coefficients. coefficients is null-terminated - * and has up to 4 entries. */ - //virtual void getPolynomialCoefficients(char symbolName, Expression ** coefficients) 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; /* Comparison */ /* isIdenticalTo is the "easy" equality, it returns true if both trees have @@ -244,6 +246,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); /* Evaluation Engine * The function evaluate creates a new expression and thus mallocs memory. @@ -293,7 +296,6 @@ private: /* Layout Engine */ virtual ExpressionLayout * privateCreateLayout(PrintFloat::Mode floatDisplayMode, ComplexFormat complexFormat) const = 0; /* Simplification */ - static void Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit, bool recursively = true); Expression * deepBeautify(Context & context, AngleUnit angleUnit); Expression * deepReduce(Context & context, AngleUnit angleUnit); // TODO: should be virtual pure diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index ad7729729..246357838 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -23,6 +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; /* 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 c619b8ba6..581d04c1d 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -22,6 +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; 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 9b33fbdb4..c64d2d348 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -37,6 +37,7 @@ 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; Sign sign() const override; bool isMatrixSymbol() const; bool isScalarSymbol() const; diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index 2f99efc13..d403a64d5 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -35,6 +35,25 @@ int Addition::polynomialDegree(char symbolName) const { return degree; } +int Addition::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { + int deg = polynomialDegree(symbolName); + if (deg < 0 || deg > k_maxNumberOfPolynomialCoefficients-1) { + return -1; + } + for (int k = 0; k < deg+1; k++) { + coefficients[k] = new Addition(); + } + Expression * intermediateCoefficients[k_maxNumberOfPolynomialCoefficients]; + for (int i = 0; i < numberOfOperands(); i++) { + int d = operand(i)->getPolynomialCoefficients(symbolName, intermediateCoefficients); + assert(d < k_maxNumberOfPolynomialCoefficients-1); + for (int j = 0; j < d+1; j++) { + static_cast(coefficients[j])->addOperand(intermediateCoefficients[j]); + } + } + return deg; +} + /* Layout */ bool Addition::needParenthesisWithParent(const Expression * e) const { diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 7b3153362..44308035b 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -219,6 +219,15 @@ int Expression::getVariables(char * variables) const { return numberOfVariables; } +int Expression::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { + int deg = polynomialDegree(symbolName); + if (deg == 0) { + coefficients[0] = clone(); + return 0; + } + return -1; +} + bool Expression::isOfType(Type * types, int length) const { for (int i = 0; i < length; i++) { if (type() == types[i]) { diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index ad04e6e16..d5ced1d38 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -43,6 +43,33 @@ int Multiplication::polynomialDegree(char symbolName) const { return degree; } +int Multiplication::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { + int deg = polynomialDegree(symbolName); + if (deg < 0 || deg + 1 > k_maxNumberOfPolynomialCoefficients) { + return -1; + } + int degA = operand(0)->getPolynomialCoefficients(symbolName, coefficients); + assert(degA + 1 <= k_maxNumberOfPolynomialCoefficients); + Expression * intermediateCoefficients[k_maxNumberOfPolynomialCoefficients]; + for (int i = 1; i < numberOfOperands(); i++) { + int degB = operand(i)->getPolynomialCoefficients(symbolName, intermediateCoefficients); + assert(degB + 1 <= k_maxNumberOfPolynomialCoefficients); + for (int j = deg; j > 0; j--) { + Addition * a = new Addition(); + for (int l = 0; l <= j; l++) { + if (l <= degB && j-l <= degA) { + a->addOperand(new Multiplication(intermediateCoefficients[l], coefficients[j-l], true)); + } + } + if (j <= degA) { delete coefficients[j]; }; + if (j <= degB) { delete intermediateCoefficients[j]; }; + coefficients[j] = a; + } + coefficients[0] = new Multiplication(coefficients[0], intermediateCoefficients[0], false); + } + return deg; +} + bool Multiplication::needParenthesisWithParent(const Expression * e) const { Type types[] = {Type::Division, Type::Power, Type::Factorial}; return e->isOfType(types, 3); diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 523081caa..4bc1632f2 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -79,6 +79,33 @@ int Power::polynomialDegree(char symbolName) const { return -1; } +int Power::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { + int deg = polynomialDegree(symbolName); + if (deg <= 0) { + return deg; + } + /* Here we only consider the case x^4 as getPolynomialCoefficients is + * supposed to be called after reducing the expression. */ + if (operand(0)->type() == Type::Symbol && static_cast(operand(0))->name() == symbolName && operand(1)->type() == Type::Rational) { + const Rational * r = static_cast(operand(1)); + if (!r->denominator().isOne() || r->sign() == Sign::Negative) { + return -1; + } + if (Integer::NaturalOrder(r->numerator(), Integer(Integer::k_maxExtractableInteger)) > 0) { + return -1; + } + int n = r->numerator().extractedInt(); + if (n < k_maxNumberOfPolynomialCoefficients) { + for (int i = 0; i < n; i++) { + coefficients[i] = new Rational(0); + } + coefficients[n] = new Rational(1); + return n; + } + } + return -1; +} + Expression * Power::setSign(Sign s, Context & context, AngleUnit angleUnit) { assert(s == Sign::Positive); assert(operand(0)->sign() == Sign::Negative); diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index c4e6aaa26..ee4a30758 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -133,6 +133,16 @@ int Symbol::getVariables(char * variables) const { return variablesLength; } +int Symbol::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const { + if (m_name == symbolName) { + coefficients[0] = new Rational(0); + coefficients[1] = new Rational(1); + return 1; + } + coefficients[0] = clone(); + return 0; +} + Expression * Symbol::replaceSymbolWithExpression(char symbol, Expression * expression) { if (m_name == symbol) { Expression * value = expression->clone(); diff --git a/poincare/test/properties.cpp b/poincare/test/properties.cpp index 907740c79..524aa785d 100644 --- a/poincare/test/properties.cpp +++ b/poincare/test/properties.cpp @@ -102,3 +102,30 @@ QUIZ_CASE(poincare_get_variables) { assert_parsed_expression_has_variables("abcde", "abcde"); assert_parsed_expression_has_variables("x^2+2*y+k!*A+w", "xykw"); } + +void assert_parsed_expression_has_polynomial_coefficient(const char * expression, char symbolName, const char ** coefficients, Expression::AngleUnit angleUnit = Expression::AngleUnit::Degree) { + GlobalContext globalContext; + Expression * e = parse_expression(expression); + Expression::Reduce(&e, globalContext, angleUnit); + Expression * coefficientBuffer[Poincare::Expression::k_maxNumberOfPolynomialCoefficients]; + int d = e->getPolynomialCoefficients(symbolName, coefficientBuffer); + for (int i = 0; i <= d; i++) { + Expression * f = parse_expression(coefficients[i]); + Expression::Reduce(&coefficientBuffer[i], globalContext, angleUnit); + Expression::Reduce(&f, globalContext, angleUnit); + assert(coefficientBuffer[i]->isIdenticalTo(f)); + delete f; + delete coefficientBuffer[i]; + } + assert(coefficients[d+1] == 0); + delete e; +} + +QUIZ_CASE(poincare_get_polynomial_coefficients) { + const char * coefficient0[] = {"2", "1", "1", 0}; + assert_parsed_expression_has_polynomial_coefficient("x^2+x+2", 'x', coefficient0); + const char * coefficient1[] = {"12+(-6)*P", "12", "3", 0}; //3*x^2+12*x-6*π+12 + assert_parsed_expression_has_polynomial_coefficient("3*(x+2)^2-6*P", 'x', coefficient1); + const char * coefficient2[] = {"2+32*x", "2", "6", "2", 0}; //2*n^3+6*n^2-2*n+2+32*x + assert_parsed_expression_has_polynomial_coefficient("2*(n+1)^3-4n+32*x", 'n', coefficient2); +}