From a89878a24d6093ca842f57ad05936739acdb4606 Mon Sep 17 00:00:00 2001 From: Hugo Saint-Vignes Date: Fri, 25 Sep 2020 11:52:23 +0200 Subject: [PATCH] [poincare] Allow more BinomialCoefficient exact results Change-Id: I9dbbf9471ee6d9e12fe4861a5b11990858382562 --- poincare/src/binomial_coefficient.cpp | 28 +++++++++++++++++---------- poincare/test/approximation.cpp | 19 +++++++++--------- poincare/test/simplification.cpp | 5 +++++ 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/poincare/src/binomial_coefficient.cpp b/poincare/src/binomial_coefficient.cpp index 7beacb5fb..a14ef2029 100644 --- a/poincare/src/binomial_coefficient.cpp +++ b/poincare/src/binomial_coefficient.cpp @@ -87,32 +87,40 @@ Expression BinomialCoefficient::shallowReduce(Context * context) { } if (!r0.isInteger()) { - // Generalized binomial coefficient + // Generalized binomial coefficient (n is not an integer) return *this; } Integer n = r0.signedIntegerNumerator(); Integer k = r1.signedIntegerNumerator(); + /* Check for situations where there should be no reduction in order to avoid + * too long computation and a huge result. The binomial coefficient will be + * approximatively evaluated later. */ if (n.isLowerThan(k)) { - // Generalized binomial coefficient - return *this; - } - /* If n is too big, we do not reduce in order to avoid too long computation. - * The binomial coefficient will be approximatively evaluated later. */ - if (Integer(k_maxNValue).isLowerThan(n)) { + // Generalized binomial coefficient (n < k) + if (!n.isNegative()) { + // When n is an integer and 0 <= n < k, binomial(n,k) is 0. + return Rational::Builder(0); + } + if (Integer(k_maxNValue).isLowerThan(Integer::Subtraction(k, n))) { + return *this; + } + } else if (Integer(k_maxNValue).isLowerThan(n)) { return *this; } Rational result = Rational::Builder(1); Integer kBis = Integer::Subtraction(n, k); - k = kBis.isLowerThan(k) ? kBis : k; - int clippedK = k.extractedInt(); // Authorized because k < n < k_maxNValue + // Take advantage of symmetry if n >= k + k = !n.isLowerThan(k) && kBis.isLowerThan(k) ? kBis : k; + int clippedK = k.extractedInt(); // Authorized because k < k_maxNValue for (int i = 0; i < clippedK; i++) { Integer nMinusI = Integer::Subtraction(n, Integer(i)); Integer kMinusI = Integer::Subtraction(k, Integer(i)); Rational factor = Rational::Builder(nMinusI, kMinusI); result = Rational::Multiplication(result, factor); } - // As we cap the n < k_maxNValue = 300, result < binomial(300, 150) ~2^89 + // As we cap the n < k_maxNValue = 300, result < binomial(300, 150) ~10^89 + // If n was negative, k - n < k_maxNValue, result < binomial(-150,150) ~10^88 assert(!result.numeratorOrDenominatorIsInfinity()); replaceWithInPlace(result); return std::move(result); diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index 30b2d57d5..9852fb2bd 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -249,6 +249,16 @@ QUIZ_CASE(poincare_approximation_function) { assert_expression_approximates_to("binomial(10, 4)", "210"); assert_expression_approximates_to("binomial(10, 4)", "210"); + assert_expression_approximates_to("binomial(12, 3)", "220"); + assert_expression_approximates_to("binomial(12, 3)", "220"); + assert_expression_approximates_to("binomial(-4.6, 3)", "-28.336"); + assert_expression_approximates_to("binomial(-4.6, 3)", "-28.336"); + assert_expression_approximates_to("binomial(π, 3)", "1.280108"); + assert_expression_approximates_to("binomial(π, 3)", "1.2801081307019"); + assert_expression_approximates_to("binomial(7, 9)", "0"); + assert_expression_approximates_to("binomial(7, 9)", "0"); + assert_expression_approximates_to("binomial(-7, 9)", "-5005"); + assert_expression_approximates_to("binomial(-7, 9)", "-5005"); assert_expression_approximates_to("binompdf(4.4, 9, 0.7)", "0.0735138", Degree, Metric, Cartesian, 6); // FIXME: precision problem assert_expression_approximates_to("binompdf(4.4, 9, 0.7)", "0.073513818"); @@ -461,15 +471,6 @@ QUIZ_CASE(poincare_approximation_function) { assert_expression_approximation_is_bounded("randint(4,45)", 4.0f, 45.0f, true); assert_expression_approximation_is_bounded("randint(4,45)", 4.0, 45.0, true); - - assert_expression_approximates_to("binomial(12, 3)", "220"); - assert_expression_approximates_to("binomial(12, 3)", "220"); - - assert_expression_approximates_to("binomial(-4.6, 3)", "-28.336"); - assert_expression_approximates_to("binomial(-4.6, 3)", "-28.336"); - - assert_expression_approximates_to("binomial(π, 3)", "1.280108"); - assert_expression_approximates_to("binomial(π, 3)", "1.2801081307019"); } QUIZ_CASE(poincare_approximation_trigonometry_functions) { diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 51debe5b5..b8bc06276 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -687,6 +687,11 @@ QUIZ_CASE(poincare_simplification_function) { assert_parsed_expression_simplify_to("arg(1+𝐢)", "π/4"); assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); + assert_parsed_expression_simplify_to("binomial(10,20)", "0"); + assert_parsed_expression_simplify_to("binomial(-10,10)", "92378"); + assert_parsed_expression_simplify_to("binomial(2.5,3)", "binomial(5/2,3)"); + assert_parsed_expression_simplify_to("binomial(-200,120)", "binomial(-200,120)"); + assert_parsed_expression_simplify_to("binomial(400,1)", "binomial(400,1)"); assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); assert_parsed_expression_simplify_to("ceil(2π)", "7"); assert_parsed_expression_simplify_to("ceil(123456789012345678901234567892/3)", "41152263004115226300411522631");