From 4511c7a3f43d7c6349d18cf257909dc4130d7234 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 1 Jun 2019 22:54:26 +0300 Subject: [PATCH 1/5] bpo-35431: Add math.perm(). --- Doc/library/math.rst | 42 +++-- Lib/test/test_math.py | 55 +++++++ .../2019-06-01-22-54-03.bpo-35431.oGXBWN.rst | 1 + Modules/clinic/mathmodule.c.h | 37 ++++- Modules/mathmodule.c | 149 ++++++++++++------ 5 files changed, 223 insertions(+), 61 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-35431.oGXBWN.rst diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 206b06edd2a206..61dffe09569764 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -36,6 +36,21 @@ Number-theoretic and representation functions :class:`~numbers.Integral` value. +.. function:: comb(n, k) + + Return the number of ways to choose *k* items from *n* items without repetition + and without order. + + Also called the binomial coefficient. It is mathematically equal to the expression + ``n! / (k! (n - k)!)``. It is equivalent to the coefficient of the *k*-th term in the + polynomial expansion of the expression ``(1 + x) ** n``. + + Raises :exc:`TypeError` if the arguments not integers. + Raises :exc:`ValueError` if the arguments are negative or if *k* > *n*. + + .. versionadded:: 3.8 + + .. function:: copysign(x, y) Return a float with the magnitude (absolute value) of *x* but the sign of @@ -192,6 +207,18 @@ Number-theoretic and representation functions of *x* and are floats. +.. function:: perm(n, k) + + Return the number of ways to choose *k* items from *n* items without repetition. + + It is mathematically equal to the expression ``n! / (n - k)!``. + + Raises :exc:`TypeError` if the arguments not integers. + Raises :exc:`ValueError` if the arguments are negative or if *k* > *n*. + + .. versionadded:: 3.8 + + .. function:: prod(iterable, *, start=1) Calculate the product of all the elements in the input *iterable*. @@ -232,21 +259,6 @@ Number-theoretic and representation functions :meth:`x.__trunc__() `. -.. function:: comb(n, k) - - Return the number of ways to choose *k* items from *n* items without repetition - and without order. - - Also called the binomial coefficient. It is mathematically equal to the expression - ``n! / (k! (n - k)!)``. It is equivalent to the coefficient of the *k*-th term in the - polynomial expansion of the expression ``(1 + x) ** n``. - - Raises :exc:`TypeError` if the arguments not integers. - Raises :exc:`ValueError` if the arguments are negative or if *k* > *n*. - - .. versionadded:: 3.8 - - Note that :func:`frexp` and :func:`modf` have a different call/return pattern than their C equivalents: they take a single argument and return a pair of values, rather than returning their second return value through an 'output diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index e27092eefd6ed7..c595f02a790e26 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1862,6 +1862,61 @@ def test_fractions(self): self.assertAllClose(fraction_examples, rel_tol=1e-8) self.assertAllNotClose(fraction_examples, rel_tol=1e-9) + def testPerm(self): + perm = math.perm + factorial = math.factorial + # Test if factorial defintion is satisfied + for n in range(100): + for k in range(n + 1): + self.assertEqual(perm(n, k), + factorial(n) // factorial(n - k)) + + # Test for Pascal's identity + for n in range(1, 100): + for k in range(1, n): + self.assertEqual(perm(n, k), perm(n - 1, k - 1) * k + perm(n - 1, k)) + + # Test corner cases + for n in range(1, 100): + self.assertEqual(perm(n, 0), 1) + self.assertEqual(perm(n, 1), n) + self.assertEqual(perm(n, n), factorial(n)) + + # Raises TypeError if any argument is non-integer or argument count is + # not 2 + self.assertRaises(TypeError, perm, 10, 1.0) + self.assertRaises(TypeError, perm, 10, decimal.Decimal(1.0)) + self.assertRaises(TypeError, perm, 10, "1") + self.assertRaises(TypeError, perm, 10.0, 1) + self.assertRaises(TypeError, perm, decimal.Decimal(10.0), 1) + self.assertRaises(TypeError, perm, "10", 1) + + self.assertRaises(TypeError, perm, 10) + self.assertRaises(TypeError, perm, 10, 1, 3) + self.assertRaises(TypeError, perm) + + # Raises Value error if not k or n are negative numbers + self.assertRaises(ValueError, perm, -1, 1) + self.assertRaises(ValueError, perm, -2**1000, 1) + self.assertRaises(ValueError, perm, 1, -1) + self.assertRaises(ValueError, perm, 1, -2**1000) + + # Raises value error if k is greater than n + self.assertRaises(ValueError, perm, 1, 2) + self.assertRaises(ValueError, perm, 1, 2**1000) + + n = 2**1000 + self.assertEqual(perm(n, 0), 1) + self.assertEqual(perm(n, 1), n) + self.assertEqual(perm(n, 2), n * (n-1)) + self.assertRaises((OverflowError, MemoryError), perm, n, n) + + for n, k in (True, True), (True, False), (False, False): + self.assertEqual(perm(n, k), 1) + self.assertIs(type(perm(n, k)), int) + self.assertEqual(perm(MyIndexable(5), MyIndexable(2)), 20) + self.assertIs(type(perm(MyIndexable(5), MyIndexable(2))), int) + def testComb(self): comb = math.comb factorial = math.factorial diff --git a/Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-35431.oGXBWN.rst b/Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-35431.oGXBWN.rst new file mode 100644 index 00000000000000..f1b825890231d9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-35431.oGXBWN.rst @@ -0,0 +1 @@ +Added :func:`math.perm`. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 92ec4bec9bf17d..bd079ac0ee9167 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -638,6 +638,41 @@ math_prod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k return return_value; } +PyDoc_STRVAR(math_perm__doc__, +"perm($module, n, k, /)\n" +"--\n" +"\n" +"Number of ways to choose k items from n items without repetition.\n" +"\n" +"It is mathematically equal to the expression n! / (n - k)!.\n" +"\n" +"Raises TypeError if the arguments are not integers.\n" +"Raises ValueError if the arguments are negative or if k > n."); + +#define MATH_PERM_METHODDEF \ + {"perm", (PyCFunction)(void(*)(void))math_perm, METH_FASTCALL, math_perm__doc__}, + +static PyObject * +math_perm_impl(PyObject *module, PyObject *n, PyObject *k); + +static PyObject * +math_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *n; + PyObject *k; + + if (!_PyArg_CheckPositional("perm", nargs, 2, 2)) { + goto exit; + } + n = args[0]; + k = args[1]; + return_value = math_perm_impl(module, n, k); + +exit: + return return_value; +} + PyDoc_STRVAR(math_comb__doc__, "comb($module, n, k, /)\n" "--\n" @@ -674,4 +709,4 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=6709521e5e1d90ec input=a9049054013a1b77]*/ +/*[clinic end generated code: output=64b3f83fa16bf67b input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index bea4607b9be1ee..cd376691fcb7d8 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2998,27 +2998,8 @@ math_prod_impl(PyObject *module, PyObject *iterable, PyObject *start) } -/*[clinic input] -math.comb - - n: object - k: object - / - -Number of ways to choose k items from n items without repetition and without order. - -Also called the binomial coefficient. It is mathematically equal to the expression -n! / (k! * (n - k)!). It is equivalent to the coefficient of k-th term in -polynomial expansion of the expression (1 + x)**n. - -Raises TypeError if the arguments are not integers. -Raises ValueError if the arguments are negative or if k > n. - -[clinic start generated code]*/ - static PyObject * -math_comb_impl(PyObject *module, PyObject *n, PyObject *k) -/*[clinic end generated code: output=bd2cec8d854f3493 input=2f336ac9ec8242f9]*/ +perm_comb(PyObject *n, PyObject *k, int comb) { PyObject *result = NULL, *factor = NULL, *temp; int overflow, cmp; @@ -3028,35 +3009,60 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k) if (n == NULL) { return NULL; } + if (!PyLong_CheckExact(n)) { + Py_SETREF(n, _PyLong_Copy((PyLongObject *)n)); + if (n == NULL) { + return NULL; + } + } k = PyNumber_Index(k); if (k == NULL) { Py_DECREF(n); return NULL; } + if (!PyLong_CheckExact(k)) { + Py_SETREF(k, _PyLong_Copy((PyLongObject *)k)); + if (k == NULL) { + Py_DECREF(n); + return NULL; + } + } if (Py_SIZE(n) < 0) { PyErr_SetString(PyExc_ValueError, "n must be a non-negative integer"); goto error; } - /* k = min(k, n - k) */ - temp = PyNumber_Subtract(n, k); - if (temp == NULL) { - goto error; - } - if (Py_SIZE(temp) < 0) { - Py_DECREF(temp); - PyErr_SetString(PyExc_ValueError, - "k must be an integer less than or equal to n"); - goto error; - } - cmp = PyObject_RichCompareBool(k, temp, Py_GT); - if (cmp > 0) { - Py_SETREF(k, temp); + if (comb) { + /* k = min(k, n - k) */ + temp = PyNumber_Subtract(n, k); + if (temp == NULL) { + goto error; + } + if (Py_SIZE(temp) < 0) { + Py_DECREF(temp); + PyErr_SetString(PyExc_ValueError, + "k must be an integer less than or equal to n"); + goto error; + } + cmp = PyObject_RichCompareBool(temp, k, Py_LT); + if (cmp > 0) { + Py_SETREF(k, temp); + } + else { + Py_DECREF(temp); + if (cmp < 0) { + goto error; + } + } } else { - Py_DECREF(temp); - if (cmp < 0) { + cmp = PyObject_RichCompareBool(n, k, Py_LT); + if (cmp != 0) { + if (cmp > 0) { + PyErr_SetString(PyExc_ValueError, + "k must be an integer less than or equal to n"); + } goto error; } } @@ -3064,7 +3070,8 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k) factors = PyLong_AsLongLongAndOverflow(k, &overflow); if (overflow > 0) { PyErr_Format(PyExc_OverflowError, - "min(n - k, k) must not exceed %lld", + "%s must not exceed %lld", + comb ? "min(n - k, k)" : "k", LLONG_MAX); goto error; } @@ -3099,14 +3106,16 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k) goto error; } - temp = PyLong_FromUnsignedLongLong((unsigned long long)i + 1); - if (temp == NULL) { - goto error; - } - Py_SETREF(result, PyNumber_FloorDivide(result, temp)); - Py_DECREF(temp); - if (result == NULL) { - goto error; + if (comb) { + temp = PyLong_FromUnsignedLongLong((unsigned long long)i + 1); + if (temp == NULL) { + goto error; + } + Py_SETREF(result, PyNumber_FloorDivide(result, temp)); + Py_DECREF(temp); + if (result == NULL) { + goto error; + } } } Py_DECREF(factor); @@ -3125,6 +3134,55 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k) } +/*[clinic input] +math.perm + + n: object + k: object + / + +Number of ways to choose k items from n items without repetition. + +It is mathematically equal to the expression n! / (n - k)!. + +Raises TypeError if the arguments are not integers. +Raises ValueError if the arguments are negative or if k > n. +[clinic start generated code]*/ + +static PyObject * +math_perm_impl(PyObject *module, PyObject *n, PyObject *k) +/*[clinic end generated code: output=e021a25469653e23 input=bad86be85158ebfd]*/ +{ + return perm_comb(n, k, 0); +} + + +/*[clinic input] +math.comb + + n: object + k: object + / + +Number of ways to choose k items from n items without repetition and without order. + +Also called the binomial coefficient. It is mathematically equal to the expression +n! / (k! * (n - k)!). It is equivalent to the coefficient of k-th term in +polynomial expansion of the expression (1 + x)**n. + +Raises TypeError if the arguments are not integers. +Raises ValueError if the arguments are negative or if k > n. + +[clinic start generated code]*/ + +static PyObject * +math_comb_impl(PyObject *module, PyObject *n, PyObject *k) +/*[clinic end generated code: output=bd2cec8d854f3493 input=2f336ac9ec8242f9]*/ +{ + return perm_comb(n, k, 1); +} + + static PyMethodDef math_methods[] = { {"acos", math_acos, METH_O, math_acos_doc}, {"acosh", math_acosh, METH_O, math_acosh_doc}, @@ -3174,6 +3232,7 @@ static PyMethodDef math_methods[] = { {"tanh", math_tanh, METH_O, math_tanh_doc}, MATH_TRUNC_METHODDEF MATH_PROD_METHODDEF + MATH_PERM_METHODDEF MATH_COMB_METHODDEF {NULL, NULL} /* sentinel */ }; From 5b9aac0486089c9dfd5749b4c30aa723a0592fce Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 1 Jun 2019 23:48:37 +0300 Subject: [PATCH 2/5] Opened new issue 37128. --- ...-35431.oGXBWN.rst => 2019-06-01-22-54-03.bpo-37128.oGXBWN.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2019-06-01-22-54-03.bpo-35431.oGXBWN.rst => 2019-06-01-22-54-03.bpo-37128.oGXBWN.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-35431.oGXBWN.rst b/Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-37128.oGXBWN.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-35431.oGXBWN.rst rename to Misc/NEWS.d/next/Library/2019-06-01-22-54-03.bpo-37128.oGXBWN.rst From 29cc2500e258f535d2ca6d83574b3921b3e00000 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Jun 2019 08:55:24 +0300 Subject: [PATCH 3/5] Fix docs. --- Doc/library/math.rst | 3 ++- Modules/clinic/mathmodule.c.h | 4 ++-- Modules/mathmodule.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 61dffe09569764..0f11943845f161 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -209,7 +209,8 @@ Number-theoretic and representation functions .. function:: perm(n, k) - Return the number of ways to choose *k* items from *n* items without repetition. + Return the distinct number of ways to choose *k* items from *n* items + without repetition and with order. It is mathematically equal to the expression ``n! / (n - k)!``. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index bd079ac0ee9167..0efe5cc409ceb1 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -642,7 +642,7 @@ PyDoc_STRVAR(math_perm__doc__, "perm($module, n, k, /)\n" "--\n" "\n" -"Number of ways to choose k items from n items without repetition.\n" +"Number of ways to choose k items from n items without repetition and with order.\n" "\n" "It is mathematically equal to the expression n! / (n - k)!.\n" "\n" @@ -709,4 +709,4 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=64b3f83fa16bf67b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a82b0e705b6d0ec0 input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index cd376691fcb7d8..af3c1f5f6331f2 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3141,7 +3141,7 @@ math.perm k: object / -Number of ways to choose k items from n items without repetition. +Number of ways to choose k items from n items without repetition and with order. It is mathematically equal to the expression n! / (n - k)!. @@ -3151,7 +3151,7 @@ Raises ValueError if the arguments are negative or if k > n. static PyObject * math_perm_impl(PyObject *module, PyObject *n, PyObject *k) -/*[clinic end generated code: output=e021a25469653e23 input=bad86be85158ebfd]*/ +/*[clinic end generated code: output=e021a25469653e23 input=f71ee4f6ff26be24]*/ { return perm_comb(n, k, 0); } From a6085085a9c7c5d2f91fe25f75dd15fbb46099fd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Jun 2019 08:58:37 +0300 Subject: [PATCH 4/5] Decouple perm() and comb() implementations. --- Modules/mathmodule.c | 207 ++++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 69 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index af3c1f5f6331f2..6e1099321c5495 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2998,10 +2998,26 @@ math_prod_impl(PyObject *module, PyObject *iterable, PyObject *start) } +/*[clinic input] +math.perm + + n: object + k: object + / + +Number of ways to choose k items from n items without repetition and with order. + +It is mathematically equal to the expression n! / (n - k)!. + +Raises TypeError if the arguments are not integers. +Raises ValueError if the arguments are negative or if k > n. +[clinic start generated code]*/ + static PyObject * -perm_comb(PyObject *n, PyObject *k, int comb) +math_perm_impl(PyObject *module, PyObject *n, PyObject *k) +/*[clinic end generated code: output=e021a25469653e23 input=f71ee4f6ff26be24]*/ { - PyObject *result = NULL, *factor = NULL, *temp; + PyObject *result = NULL, *factor = NULL; int overflow, cmp; long long i, factors; @@ -3033,45 +3049,19 @@ perm_comb(PyObject *n, PyObject *k, int comb) "n must be a non-negative integer"); goto error; } - if (comb) { - /* k = min(k, n - k) */ - temp = PyNumber_Subtract(n, k); - if (temp == NULL) { - goto error; - } - if (Py_SIZE(temp) < 0) { - Py_DECREF(temp); + cmp = PyObject_RichCompareBool(n, k, Py_LT); + if (cmp != 0) { + if (cmp > 0) { PyErr_SetString(PyExc_ValueError, "k must be an integer less than or equal to n"); - goto error; - } - cmp = PyObject_RichCompareBool(temp, k, Py_LT); - if (cmp > 0) { - Py_SETREF(k, temp); - } - else { - Py_DECREF(temp); - if (cmp < 0) { - goto error; - } - } - } - else { - cmp = PyObject_RichCompareBool(n, k, Py_LT); - if (cmp != 0) { - if (cmp > 0) { - PyErr_SetString(PyExc_ValueError, - "k must be an integer less than or equal to n"); - } - goto error; } + goto error; } factors = PyLong_AsLongLongAndOverflow(k, &overflow); if (overflow > 0) { PyErr_Format(PyExc_OverflowError, - "%s must not exceed %lld", - comb ? "min(n - k, k)" : "k", + "k must not exceed %lld", LLONG_MAX); goto error; } @@ -3105,18 +3095,6 @@ perm_comb(PyObject *n, PyObject *k, int comb) if (result == NULL) { goto error; } - - if (comb) { - temp = PyLong_FromUnsignedLongLong((unsigned long long)i + 1); - if (temp == NULL) { - goto error; - } - Py_SETREF(result, PyNumber_FloorDivide(result, temp)); - Py_DECREF(temp); - if (result == NULL) { - goto error; - } - } } Py_DECREF(factor); @@ -3134,29 +3112,6 @@ perm_comb(PyObject *n, PyObject *k, int comb) } -/*[clinic input] -math.perm - - n: object - k: object - / - -Number of ways to choose k items from n items without repetition and with order. - -It is mathematically equal to the expression n! / (n - k)!. - -Raises TypeError if the arguments are not integers. -Raises ValueError if the arguments are negative or if k > n. -[clinic start generated code]*/ - -static PyObject * -math_perm_impl(PyObject *module, PyObject *n, PyObject *k) -/*[clinic end generated code: output=e021a25469653e23 input=f71ee4f6ff26be24]*/ -{ - return perm_comb(n, k, 0); -} - - /*[clinic input] math.comb @@ -3179,7 +3134,121 @@ static PyObject * math_comb_impl(PyObject *module, PyObject *n, PyObject *k) /*[clinic end generated code: output=bd2cec8d854f3493 input=2f336ac9ec8242f9]*/ { - return perm_comb(n, k, 1); + PyObject *result = NULL, *factor = NULL, *temp; + int overflow, cmp; + long long i, factors; + + n = PyNumber_Index(n); + if (n == NULL) { + return NULL; + } + if (!PyLong_CheckExact(n)) { + Py_SETREF(n, _PyLong_Copy((PyLongObject *)n)); + if (n == NULL) { + return NULL; + } + } + k = PyNumber_Index(k); + if (k == NULL) { + Py_DECREF(n); + return NULL; + } + if (!PyLong_CheckExact(k)) { + Py_SETREF(k, _PyLong_Copy((PyLongObject *)k)); + if (k == NULL) { + Py_DECREF(n); + return NULL; + } + } + + if (Py_SIZE(n) < 0) { + PyErr_SetString(PyExc_ValueError, + "n must be a non-negative integer"); + goto error; + } + /* k = min(k, n - k) */ + temp = PyNumber_Subtract(n, k); + if (temp == NULL) { + goto error; + } + if (Py_SIZE(temp) < 0) { + Py_DECREF(temp); + PyErr_SetString(PyExc_ValueError, + "k must be an integer less than or equal to n"); + goto error; + } + cmp = PyObject_RichCompareBool(temp, k, Py_LT); + if (cmp > 0) { + Py_SETREF(k, temp); + } + else { + Py_DECREF(temp); + if (cmp < 0) { + goto error; + } + } + + factors = PyLong_AsLongLongAndOverflow(k, &overflow); + if (overflow > 0) { + PyErr_Format(PyExc_OverflowError, + "min(n - k, k) must not exceed %lld", + LLONG_MAX); + goto error; + } + else if (overflow < 0 || factors < 0) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, + "k must be a non-negative integer"); + } + goto error; + } + + if (factors == 0) { + result = PyLong_FromLong(1); + goto done; + } + + result = n; + Py_INCREF(result); + if (factors == 1) { + goto done; + } + + factor = n; + Py_INCREF(factor); + for (i = 1; i < factors; ++i) { + Py_SETREF(factor, PyNumber_Subtract(factor, _PyLong_One)); + if (factor == NULL) { + goto error; + } + Py_SETREF(result, PyNumber_Multiply(result, factor)); + if (result == NULL) { + goto error; + } + + temp = PyLong_FromUnsignedLongLong((unsigned long long)i + 1); + if (temp == NULL) { + goto error; + } + Py_SETREF(result, PyNumber_FloorDivide(result, temp)); + Py_DECREF(temp); + if (result == NULL) { + goto error; + } + } + Py_DECREF(factor); + +done: + Py_DECREF(n); + Py_DECREF(k); + return result; + +error: + Py_XDECREF(factor); + Py_XDECREF(result); + Py_DECREF(n); + Py_DECREF(k); + return NULL; } From a75d23251683deec41dc464ec6ec392a4cbcf62c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Jun 2019 10:57:21 +0300 Subject: [PATCH 5/5] Tweak docs and add tests for int subclass. --- Doc/library/math.rst | 2 +- Lib/test/test_math.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 0f11943845f161..c5a77f1fab9fd6 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -209,7 +209,7 @@ Number-theoretic and representation functions .. function:: perm(n, k) - Return the distinct number of ways to choose *k* items from *n* items + Return the number of ways to choose *k* items from *n* items without repetition and with order. It is mathematically equal to the expression ``n! / (n - k)!``. diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index c595f02a790e26..96e0cf2fe67197 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -240,6 +240,9 @@ def result_check(expected, got, ulp_tol=5, abs_tol=0.0): else: return None +class IntSubclass(int): + pass + # Class providing an __index__ method. class MyIndexable(object): def __init__(self, value): @@ -1914,8 +1917,11 @@ def testPerm(self): for n, k in (True, True), (True, False), (False, False): self.assertEqual(perm(n, k), 1) self.assertIs(type(perm(n, k)), int) + self.assertEqual(perm(IntSubclass(5), IntSubclass(2)), 20) self.assertEqual(perm(MyIndexable(5), MyIndexable(2)), 20) - self.assertIs(type(perm(MyIndexable(5), MyIndexable(2))), int) + for k in range(3): + self.assertIs(type(perm(IntSubclass(5), IntSubclass(k))), int) + self.assertIs(type(perm(MyIndexable(5), MyIndexable(k))), int) def testComb(self): comb = math.comb @@ -1980,8 +1986,11 @@ def testComb(self): for n, k in (True, True), (True, False), (False, False): self.assertEqual(comb(n, k), 1) self.assertIs(type(comb(n, k)), int) + self.assertEqual(comb(IntSubclass(5), IntSubclass(2)), 10) self.assertEqual(comb(MyIndexable(5), MyIndexable(2)), 10) - self.assertIs(type(comb(MyIndexable(5), MyIndexable(2))), int) + for k in range(3): + self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int) + self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int) def test_main(): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy