diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 0464e1e48..c619b8ba6 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -25,7 +25,7 @@ public: template static Complex compute(const Complex c, const Complex d); private: constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25; - constexpr static int k_maxIntegerPower = 100; + constexpr static int k_maxExactPowerMatrix = 100; /* Property */ Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override; /* Layout */ @@ -52,10 +52,9 @@ private: static bool TermIsARationalSquareRootOrRational(const Expression * e); static const Rational * RadicandInExpression(const Expression * e); static const Rational * RationalFactorInExpression(const Expression * e); - static bool RationalExponentShouldNotBeReduced(const Rational * r); + static bool RationalExponentShouldNotBeReduced(const Rational * b, const Rational * r); /* Evaluation */ constexpr static int k_maxApproximatePowerMatrix = 1000; - constexpr static int k_maxExactPowerMatrix = 100; template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n) { return nullptr; } template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * d); template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { return nullptr; } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index b8ca902ff..e1ebbc8ce 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -282,9 +282,7 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { // p^q with p, q rationals if (!letPowerAtRoot && operand(1)->type() == Type::Rational) { Rational * exp = static_cast(editableOperand(1)); - /* First, we check that the simplification does not involve too complex power - * of integers (ie 3^999) that would take too much time to compute. */ - if (RationalExponentShouldNotBeReduced(exp)) { + if (RationalExponentShouldNotBeReduced(a, exp)) { return this; } return simplifyRationalRationalPower(this, a, exp, context, angleUnit); @@ -368,9 +366,9 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { Addition * a = static_cast(editableOperand(1)); // Check is b is rational if (a->operand(0)->type() == Type::Rational) { - /* First, we check that the simplification does not involve too complex power - * of integers (ie 3^999) that would take too much time to compute. */ - if (RationalExponentShouldNotBeReduced(static_cast(a->operand(0)))) { + const Rational * rationalBase = static_cast(operand(0)); + const Rational * rationalIndex = static_cast(a->operand(0)); + if (RationalExponentShouldNotBeReduced(rationalBase, rationalIndex)) { return this; } Power * p1 = static_cast(clone()); @@ -810,10 +808,25 @@ bool Power::isNthRootOfUnity() const { return false; } -bool Power::RationalExponentShouldNotBeReduced(const Rational * r) { +bool Power::RationalExponentShouldNotBeReduced(const Rational * b, const Rational * r) { + /* We check that the simplification does not involve too complex power of + * integers (ie 3^999, 120232323232^50) that would take too much time to + * compute: + * - we cap the exponent at k_maxExactPowerMatrix + * - we cap the resulting power at DBL_MAX + * The complexity of computing a power of rational is mainly due to computing + * the GCD of the resulting numerator and denominator. Euclide algorithm's + * complexity is apportionned to the number of decimal digits in the smallest + * integer. */ Integer maxIntegerExponent = r->numerator(); maxIntegerExponent.setNegative(false); - if (Integer::NaturalOrder(maxIntegerExponent, Integer(k_maxIntegerPower)) > 0) { + if (Integer::NaturalOrder(maxIntegerExponent, Integer(k_maxExactPowerMatrix)) > 0) { + return true; + } + double index = maxIntegerExponent.approximate(); + double powerNumerator = std::pow(std::fabs(b->numerator().approximate()), index); + double powerDenominator = std::pow(std::fabs(b->denominator().approximate()), index); + if (std::isnan(powerNumerator) || std::isnan(powerDenominator) || std::isinf(powerNumerator) || std::isinf(powerDenominator)) { return true; } return false; diff --git a/poincare/src/round.cpp b/poincare/src/round.cpp index cfdf2a3a9..ee38ad9a8 100644 --- a/poincare/src/round.cpp +++ b/poincare/src/round.cpp @@ -35,11 +35,12 @@ Expression * Round::shallowReduce(Context& context, AngleUnit angleUnit) { if (!r2->denominator().isOne()) { return replaceWith(new Undefined(), true); } - if (Power::RationalExponentShouldNotBeReduced(r2)) { + const Rational ten(10); + if (Power::RationalExponentShouldNotBeReduced(&ten, r2)) { return this; } - Rational err = Rational::Power(Rational(10), r2->numerator()); - Rational mult = Rational::Multiplication(*r1, Rational(err)); + Rational err = Rational::Power(ten, r2->numerator()); + Rational mult = Rational::Multiplication(*r1, err); IntegerDivision d = Integer::Division(mult.numerator(), mult.denominator()); Integer rounding = d.quotient; if (Rational::NaturalOrder(Rational(d.remainder, mult.denominator()), Rational(1,2)) >= 0) { diff --git a/poincare/test/power.cpp b/poincare/test/power.cpp index f718a82b6..7ba577021 100644 --- a/poincare/test/power.cpp +++ b/poincare/test/power.cpp @@ -27,6 +27,12 @@ QUIZ_CASE(poincare_power_evaluate) { Complex f[1] = {Complex::Float(std::exp(-M_PI_2))}; assert_parsed_expression_evaluates_to("I^I", f); + + Complex g[1] = {Complex::Float(1.489846)}; + assert_parsed_expression_evaluates_to("1.006666666666667^60", g); + + Complex h[1] = {Complex::Float(1.48984570830164)}; + assert_parsed_expression_evaluates_to("1.006666666666667^60", h); } QUIZ_CASE(poincare_power_simplify) { @@ -84,4 +90,5 @@ QUIZ_CASE(poincare_power_simplify) { assert_parsed_expression_simplify_to("(5*P+R(2))^(-5)", "1/(4*R(2)+100*P+500*R(2)*P^2+2500*P^3+3125*R(2)*P^4+3125*P^5)"); assert_parsed_expression_simplify_to("(1+R(2)+R(3))^5", "296+224*R(2)+184*R(3)+120*R(6)"); assert_parsed_expression_simplify_to("(P+R(2)+R(3)+x)^(-3)", "1/(11*R(2)+9*R(3)+15*x+6*R(6)*x+3*R(2)*x^2+3*R(3)*x^2+x^3+15*P+6*R(6)*P+6*R(2)*x*P+6*R(3)*x*P+3*x^2*P+3*R(2)*P^2+3*R(3)*P^2+3*x*P^2+P^3)"); + assert_parsed_expression_simplify_to("1.006666666666667^60", "(1006666666666667/1000000000000000)^60"); }