From 735fc6e1bba0e63679c141a2aff0325f37c93021 Mon Sep 17 00:00:00 2001 From: Hugo Saint-Vignes Date: Fri, 29 May 2020 15:42:35 +0200 Subject: [PATCH] [poincare] Generalize Binomial Coefficient n to any real Change-Id: I3cda882e67db4becfe232c6428e14905e922662b --- poincare/src/binomial_coefficient.cpp | 38 +++++++++++++++------------ poincare/test/approximation.cpp | 9 +++++++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/poincare/src/binomial_coefficient.cpp b/poincare/src/binomial_coefficient.cpp index 248c91ce3..7beacb5fb 100644 --- a/poincare/src/binomial_coefficient.cpp +++ b/poincare/src/binomial_coefficient.cpp @@ -40,18 +40,23 @@ Complex BinomialCoefficientNode::templatedApproximate(Context * context, Pref template T BinomialCoefficientNode::compute(T k, T n) { - k = k > (n-k) ? n-k : k; - if (std::isnan(n) || std::isnan(k) || n != std::round(n) || k != std::round(k) || k > n || k < 0 || n < 0) { + if (std::isnan(n) || std::isnan(k) || k != std::round(k) || k < 0) { return NAN; } + // Generalized definition allows any n value + bool generalized = (n != std::round(n) || n < k); + // Take advantage of symmetry + k = (!generalized && k > (n - k)) ? n - k : k; + T result = 1; for (int i = 0; i < k; i++) { - result *= (n-(T)i)/(k-(T)i); + result *= (n - (T)i) / (k - (T)i); if (std::isinf(result) || std::isnan(result)) { return result; } } - return std::round(result); + // If not generalized, the output must be round + return generalized ? result : std::round(result); } @@ -70,28 +75,27 @@ Expression BinomialCoefficient::shallowReduce(Context * context) { return replaceWithUndefinedInPlace(); } - if (c0.type() == ExpressionNode::Type::Rational) { - Rational r0 = static_cast(c0); - if (!r0.isInteger() || r0.isNegative()) { - return replaceWithUndefinedInPlace(); - } - } - if (c1.type() == ExpressionNode::Type::Rational) { - Rational r1 = static_cast(c1); - if (!r1.isInteger() || r1.isNegative()) { - return replaceWithUndefinedInPlace(); - } - } if (c0.type() != ExpressionNode::Type::Rational || c1.type() != ExpressionNode::Type::Rational) { return *this; } + Rational r0 = static_cast(c0); Rational r1 = static_cast(c1); + if (!r1.isInteger() || r1.isNegative()) { + return replaceWithUndefinedInPlace(); + } + + if (!r0.isInteger()) { + // Generalized binomial coefficient + return *this; + } + Integer n = r0.signedIntegerNumerator(); Integer k = r1.signedIntegerNumerator(); if (n.isLowerThan(k)) { - return replaceWithUndefinedInPlace(); + // 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. */ diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index b48a45200..ac54076c8 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -426,6 +426,15 @@ 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) {