gh-81313: Add the math.integer module (PEP-791) (GH-133909)
This commit is contained in:
85
Doc/library/math.integer.rst
Normal file
85
Doc/library/math.integer.rst
Normal file
@@ -0,0 +1,85 @@
|
||||
:mod:`math.integer` --- integer-specific mathematics functions
|
||||
==============================================================
|
||||
|
||||
.. module:: math.integer
|
||||
:synopsis: Integer-specific mathematics functions.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
--------------
|
||||
|
||||
This module provides access to the mathematical functions defined for integer arguments.
|
||||
These functions accept integers and objects that implement the
|
||||
:meth:`~object.__index__` method which is used to convert the object to an integer
|
||||
number.
|
||||
|
||||
The following functions are provided by this module. All return values are
|
||||
computed exactly and are integers.
|
||||
|
||||
|
||||
.. function:: comb(n, k, /)
|
||||
|
||||
Return the number of ways to choose *k* items from *n* items without repetition
|
||||
and without order.
|
||||
|
||||
Evaluates to ``n! / (k! * (n - k)!)`` when ``k <= n`` and evaluates
|
||||
to zero when ``k > n``.
|
||||
|
||||
Also called the binomial coefficient because it is equivalent
|
||||
to the coefficient of k-th term in polynomial expansion of
|
||||
``(1 + x)ⁿ``.
|
||||
|
||||
Raises :exc:`ValueError` if either of the arguments are negative.
|
||||
|
||||
|
||||
.. function:: factorial(n, /)
|
||||
|
||||
Return factorial of the nonnegative integer *n*.
|
||||
|
||||
|
||||
.. function:: gcd(*integers)
|
||||
|
||||
Return the greatest common divisor of the specified integer arguments.
|
||||
If any of the arguments is nonzero, then the returned value is the largest
|
||||
positive integer that is a divisor of all arguments. If all arguments
|
||||
are zero, then the returned value is ``0``. ``gcd()`` without arguments
|
||||
returns ``0``.
|
||||
|
||||
|
||||
.. function:: isqrt(n, /)
|
||||
|
||||
Return the integer square root of the nonnegative integer *n*. This is the
|
||||
floor of the exact square root of *n*, or equivalently the greatest integer
|
||||
*a* such that *a*\ ² |nbsp| ≤ |nbsp| *n*.
|
||||
|
||||
For some applications, it may be more convenient to have the least integer
|
||||
*a* such that *n* |nbsp| ≤ |nbsp| *a*\ ², or in other words the ceiling of
|
||||
the exact square root of *n*. For positive *n*, this can be computed using
|
||||
``a = 1 + isqrt(n - 1)``.
|
||||
|
||||
|
||||
.. |nbsp| unicode:: 0xA0
|
||||
:trim:
|
||||
|
||||
|
||||
.. function:: lcm(*integers)
|
||||
|
||||
Return the least common multiple of the specified integer arguments.
|
||||
If all arguments are nonzero, then the returned value is the smallest
|
||||
positive integer that is a multiple of all arguments. If any of the arguments
|
||||
is zero, then the returned value is ``0``. ``lcm()`` without arguments
|
||||
returns ``1``.
|
||||
|
||||
|
||||
.. function:: perm(n, k=None, /)
|
||||
|
||||
Return the number of ways to choose *k* items from *n* items
|
||||
without repetition and with order.
|
||||
|
||||
Evaluates to ``n! / (n - k)!`` when ``k <= n`` and evaluates
|
||||
to zero when ``k > n``.
|
||||
|
||||
If *k* is not specified or is ``None``, then *k* defaults to *n*
|
||||
and the function returns ``n!``.
|
||||
|
||||
Raises :exc:`ValueError` if either of the arguments are negative.
|
||||
@@ -27,15 +27,6 @@ noted otherwise, all return values are floats.
|
||||
|
||||
|
||||
==================================================== ============================================
|
||||
**Number-theoretic functions**
|
||||
--------------------------------------------------------------------------------------------------
|
||||
:func:`comb(n, k) <comb>` Number of ways to choose *k* items from *n* items without repetition and without order
|
||||
:func:`factorial(n) <factorial>` *n* factorial
|
||||
:func:`gcd(*integers) <gcd>` Greatest common divisor of the integer arguments
|
||||
:func:`isqrt(n) <isqrt>` Integer square root of a nonnegative integer *n*
|
||||
:func:`lcm(*integers) <lcm>` Least common multiple of the integer arguments
|
||||
:func:`perm(n, k) <perm>` Number of ways to choose *k* items from *n* items without repetition and with order
|
||||
|
||||
**Floating point arithmetic**
|
||||
--------------------------------------------------------------------------------------------------
|
||||
:func:`ceil(x) <ceil>` Ceiling of *x*, the smallest integer greater than or equal to *x*
|
||||
@@ -126,92 +117,6 @@ noted otherwise, all return values are floats.
|
||||
==================================================== ============================================
|
||||
|
||||
|
||||
Number-theoretic functions
|
||||
--------------------------
|
||||
|
||||
.. function:: comb(n, k)
|
||||
|
||||
Return the number of ways to choose *k* items from *n* items without repetition
|
||||
and without order.
|
||||
|
||||
Evaluates to ``n! / (k! * (n - k)!)`` when ``k <= n`` and evaluates
|
||||
to zero when ``k > n``.
|
||||
|
||||
Also called the binomial coefficient because it is equivalent
|
||||
to the coefficient of k-th term in polynomial expansion of
|
||||
``(1 + x)ⁿ``.
|
||||
|
||||
Raises :exc:`TypeError` if either of the arguments are not integers.
|
||||
Raises :exc:`ValueError` if either of the arguments are negative.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
.. function:: factorial(n)
|
||||
|
||||
Return factorial of the nonnegative integer *n*.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Floats with integral values (like ``5.0``) are no longer accepted.
|
||||
|
||||
|
||||
.. function:: gcd(*integers)
|
||||
|
||||
Return the greatest common divisor of the specified integer arguments.
|
||||
If any of the arguments is nonzero, then the returned value is the largest
|
||||
positive integer that is a divisor of all arguments. If all arguments
|
||||
are zero, then the returned value is ``0``. ``gcd()`` without arguments
|
||||
returns ``0``.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Added support for an arbitrary number of arguments. Formerly, only two
|
||||
arguments were supported.
|
||||
|
||||
|
||||
.. function:: isqrt(n)
|
||||
|
||||
Return the integer square root of the nonnegative integer *n*. This is the
|
||||
floor of the exact square root of *n*, or equivalently the greatest integer
|
||||
*a* such that *a*\ ² |nbsp| ≤ |nbsp| *n*.
|
||||
|
||||
For some applications, it may be more convenient to have the least integer
|
||||
*a* such that *n* |nbsp| ≤ |nbsp| *a*\ ², or in other words the ceiling of
|
||||
the exact square root of *n*. For positive *n*, this can be computed using
|
||||
``a = 1 + isqrt(n - 1)``.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
.. function:: lcm(*integers)
|
||||
|
||||
Return the least common multiple of the specified integer arguments.
|
||||
If all arguments are nonzero, then the returned value is the smallest
|
||||
positive integer that is a multiple of all arguments. If any of the arguments
|
||||
is zero, then the returned value is ``0``. ``lcm()`` without arguments
|
||||
returns ``1``.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
||||
|
||||
.. function:: perm(n, k=None)
|
||||
|
||||
Return the number of ways to choose *k* items from *n* items
|
||||
without repetition and with order.
|
||||
|
||||
Evaluates to ``n! / (n - k)!`` when ``k <= n`` and evaluates
|
||||
to zero when ``k > n``.
|
||||
|
||||
If *k* is not specified or is ``None``, then *k* defaults to *n*
|
||||
and the function returns ``n!``.
|
||||
|
||||
Raises :exc:`TypeError` if either of the arguments are not integers.
|
||||
Raises :exc:`ValueError` if either of the arguments are negative.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
Floating point arithmetic
|
||||
-------------------------
|
||||
|
||||
@@ -812,6 +717,75 @@ Special functions
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
Number-theoretic functions
|
||||
--------------------------
|
||||
|
||||
For backward compatibility, the :mod:`math` module provides also aliases of
|
||||
the following functions from the :mod:`math.integer` module:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - .. function:: comb(n, k)
|
||||
:no-typesetting:
|
||||
|
||||
:func:`comb(n, k) <math.integer.comb>`
|
||||
- Number of ways to choose *k* items from *n* items without repetition
|
||||
and without order
|
||||
|
||||
* - .. function:: factorial(n)
|
||||
:no-typesetting:
|
||||
|
||||
:func:`factorial(n) <math.integer.factorial>`
|
||||
- *n* factorial
|
||||
|
||||
* - .. function:: gcd(*integers)
|
||||
:no-typesetting:
|
||||
|
||||
:func:`gcd(*integers) <math.integer.gcd>`
|
||||
- Greatest common divisor of the integer arguments
|
||||
|
||||
* - .. function:: isqrt(n)
|
||||
:no-typesetting:
|
||||
|
||||
:func:`isqrt(n) <math.integer.isqrt>`
|
||||
- Integer square root of a nonnegative integer *n*
|
||||
|
||||
* - .. function:: lcm(*integers)
|
||||
:no-typesetting:
|
||||
|
||||
:func:`lcm(*integers) <math.integer.lcm>`
|
||||
- Least common multiple of the integer arguments
|
||||
|
||||
* - .. function:: perm(n, k)
|
||||
:no-typesetting:
|
||||
|
||||
:func:`perm(n, k) <math.integer.perm>`
|
||||
- Number of ways to choose *k* items from *n* items without repetition
|
||||
and with order
|
||||
|
||||
.. versionadded:: 3.5
|
||||
The :func:`gcd` function.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
The :func:`comb`, :func:`perm` and :func:`isqrt` functions.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
The :func:`lcm` function.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Added support for an arbitrary number of arguments in the :func:`gcd`
|
||||
function.
|
||||
Formerly, only two arguments were supported.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Floats with integral values (like ``5.0``) are no longer accepted in the
|
||||
:func:`factorial` function.
|
||||
|
||||
.. deprecated:: next
|
||||
These aliases are :term:`soft deprecated` in favor of the
|
||||
:mod:`math.integer` functions.
|
||||
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
@@ -894,5 +868,5 @@ Constants
|
||||
Module :mod:`cmath`
|
||||
Complex number versions of many of these functions.
|
||||
|
||||
.. |nbsp| unicode:: 0xA0
|
||||
:trim:
|
||||
Module :mod:`math.integer`
|
||||
Integer-specific mathematics functions.
|
||||
|
||||
@@ -19,6 +19,7 @@ The following modules are documented in this chapter:
|
||||
|
||||
numbers.rst
|
||||
math.rst
|
||||
math.integer.rst
|
||||
cmath.rst
|
||||
decimal.rst
|
||||
fractions.rst
|
||||
|
||||
@@ -311,7 +311,12 @@ Other language changes
|
||||
New modules
|
||||
===========
|
||||
|
||||
* None yet.
|
||||
math.integer
|
||||
------------
|
||||
|
||||
This module provides access to the mathematical functions for integer
|
||||
arguments (:pep:`791`).
|
||||
(Contributed by Serhiy Storchaka in :gh:`81313`.)
|
||||
|
||||
|
||||
Improved modules
|
||||
|
||||
@@ -17,7 +17,8 @@ extern int _PyImport_IsInitialized(PyInterpreterState *);
|
||||
// Export for 'pyexpat' shared extension
|
||||
PyAPI_FUNC(int) _PyImport_SetModule(PyObject *name, PyObject *module);
|
||||
|
||||
extern int _PyImport_SetModuleString(const char *name, PyObject* module);
|
||||
// Export for 'math' shared extension
|
||||
PyAPI_FUNC(int) _PyImport_SetModuleString(const char *name, PyObject* module);
|
||||
|
||||
extern void _PyImport_AcquireLock(PyInterpreterState *interp);
|
||||
extern void _PyImport_ReleaseLock(PyInterpreterState *interp);
|
||||
|
||||
@@ -307,7 +307,7 @@ class CAPITest(unittest.TestCase):
|
||||
CURRENT_THREAD_REGEX +
|
||||
r' File .*, line 6 in <module>\n'
|
||||
r'\n'
|
||||
r'Extension modules: _testcapi \(total: 1\)\n')
|
||||
r'Extension modules: ')
|
||||
else:
|
||||
# Python built with NDEBUG macro defined:
|
||||
# test _Py_CheckFunctionResult() instead.
|
||||
|
||||
@@ -55,56 +55,6 @@ def to_ulps(x):
|
||||
return n
|
||||
|
||||
|
||||
# Here's a pure Python version of the math.factorial algorithm, for
|
||||
# documentation and comparison purposes.
|
||||
#
|
||||
# Formula:
|
||||
#
|
||||
# factorial(n) = factorial_odd_part(n) << (n - count_set_bits(n))
|
||||
#
|
||||
# where
|
||||
#
|
||||
# factorial_odd_part(n) = product_{i >= 0} product_{0 < j <= n >> i; j odd} j
|
||||
#
|
||||
# The outer product above is an infinite product, but once i >= n.bit_length,
|
||||
# (n >> i) < 1 and the corresponding term of the product is empty. So only the
|
||||
# finitely many terms for 0 <= i < n.bit_length() contribute anything.
|
||||
#
|
||||
# We iterate downwards from i == n.bit_length() - 1 to i == 0. The inner
|
||||
# product in the formula above starts at 1 for i == n.bit_length(); for each i
|
||||
# < n.bit_length() we get the inner product for i from that for i + 1 by
|
||||
# multiplying by all j in {n >> i+1 < j <= n >> i; j odd}. In Python terms,
|
||||
# this set is range((n >> i+1) + 1 | 1, (n >> i) + 1 | 1, 2).
|
||||
|
||||
def count_set_bits(n):
|
||||
"""Number of '1' bits in binary expansion of a nonnnegative integer."""
|
||||
return 1 + count_set_bits(n & n - 1) if n else 0
|
||||
|
||||
def partial_product(start, stop):
|
||||
"""Product of integers in range(start, stop, 2), computed recursively.
|
||||
start and stop should both be odd, with start <= stop.
|
||||
|
||||
"""
|
||||
numfactors = (stop - start) >> 1
|
||||
if not numfactors:
|
||||
return 1
|
||||
elif numfactors == 1:
|
||||
return start
|
||||
else:
|
||||
mid = (start + numfactors) | 1
|
||||
return partial_product(start, mid) * partial_product(mid, stop)
|
||||
|
||||
def py_factorial(n):
|
||||
"""Factorial of nonnegative integer n, via "Binary Split Factorial Formula"
|
||||
described at http://www.luschny.de/math/factorial/binarysplitfact.html
|
||||
|
||||
"""
|
||||
inner = outer = 1
|
||||
for i in reversed(range(n.bit_length())):
|
||||
inner *= partial_product((n >> i + 1) + 1 | 1, (n >> i) + 1 | 1)
|
||||
outer *= inner
|
||||
return outer << (n - count_set_bits(n))
|
||||
|
||||
def ulp_abs_check(expected, got, ulp_tol, abs_tol):
|
||||
"""Given finite floats `expected` and `got`, check that they're
|
||||
approximately equal to within the given number of ulps or the
|
||||
@@ -547,33 +497,6 @@ class MathTests(unittest.TestCase):
|
||||
self.ftest('fabs(0)', math.fabs(0), 0)
|
||||
self.ftest('fabs(1)', math.fabs(1), 1)
|
||||
|
||||
def testFactorial(self):
|
||||
self.assertEqual(math.factorial(0), 1)
|
||||
total = 1
|
||||
for i in range(1, 1000):
|
||||
total *= i
|
||||
self.assertEqual(math.factorial(i), total)
|
||||
self.assertEqual(math.factorial(i), py_factorial(i))
|
||||
self.assertRaises(ValueError, math.factorial, -1)
|
||||
self.assertRaises(ValueError, math.factorial, -10**100)
|
||||
|
||||
def testFactorialNonIntegers(self):
|
||||
self.assertRaises(TypeError, math.factorial, 5.0)
|
||||
self.assertRaises(TypeError, math.factorial, 5.2)
|
||||
self.assertRaises(TypeError, math.factorial, -1.0)
|
||||
self.assertRaises(TypeError, math.factorial, -1e100)
|
||||
self.assertRaises(TypeError, math.factorial, decimal.Decimal('5'))
|
||||
self.assertRaises(TypeError, math.factorial, decimal.Decimal('5.2'))
|
||||
self.assertRaises(TypeError, math.factorial, "5")
|
||||
|
||||
# Other implementations may place different upper bounds.
|
||||
@support.cpython_only
|
||||
def testFactorialHugeInputs(self):
|
||||
# Currently raises OverflowError for inputs that are too large
|
||||
# to fit into a C long.
|
||||
self.assertRaises(OverflowError, math.factorial, 10**100)
|
||||
self.assertRaises(TypeError, math.factorial, 1e100)
|
||||
|
||||
def testFloor(self):
|
||||
self.assertRaises(TypeError, math.floor)
|
||||
self.assertEqual(int, type(math.floor(0.5)))
|
||||
@@ -1175,68 +1098,6 @@ class MathTests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
math.dist([1, 2], [3, 4, 5])
|
||||
|
||||
def testIsqrt(self):
|
||||
# Test a variety of inputs, large and small.
|
||||
test_values = (
|
||||
list(range(1000))
|
||||
+ list(range(10**6 - 1000, 10**6 + 1000))
|
||||
+ [2**e + i for e in range(60, 200) for i in range(-40, 40)]
|
||||
+ [3**9999, 10**5001]
|
||||
)
|
||||
|
||||
for value in test_values:
|
||||
with self.subTest(value=value):
|
||||
s = math.isqrt(value)
|
||||
self.assertIs(type(s), int)
|
||||
self.assertLessEqual(s*s, value)
|
||||
self.assertLess(value, (s+1)*(s+1))
|
||||
|
||||
# Negative values
|
||||
with self.assertRaises(ValueError):
|
||||
math.isqrt(-1)
|
||||
|
||||
# Integer-like things
|
||||
s = math.isqrt(True)
|
||||
self.assertIs(type(s), int)
|
||||
self.assertEqual(s, 1)
|
||||
|
||||
s = math.isqrt(False)
|
||||
self.assertIs(type(s), int)
|
||||
self.assertEqual(s, 0)
|
||||
|
||||
class IntegerLike(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __index__(self):
|
||||
return self.value
|
||||
|
||||
s = math.isqrt(IntegerLike(1729))
|
||||
self.assertIs(type(s), int)
|
||||
self.assertEqual(s, 41)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
math.isqrt(IntegerLike(-3))
|
||||
|
||||
# Non-integer-like things
|
||||
bad_values = [
|
||||
3.5, "a string", decimal.Decimal("3.5"), 3.5j,
|
||||
100.0, -4.0,
|
||||
]
|
||||
for value in bad_values:
|
||||
with self.subTest(value=value):
|
||||
with self.assertRaises(TypeError):
|
||||
math.isqrt(value)
|
||||
|
||||
@support.bigmemtest(2**32, memuse=0.85)
|
||||
def test_isqrt_huge(self, size):
|
||||
if size & 1:
|
||||
size += 1
|
||||
v = 1 << size
|
||||
w = math.isqrt(v)
|
||||
self.assertEqual(w.bit_length(), size // 2 + 1)
|
||||
self.assertEqual(w.bit_count(), 1)
|
||||
|
||||
def test_lcm(self):
|
||||
lcm = math.lcm
|
||||
self.assertEqual(lcm(0, 0), 0)
|
||||
@@ -2392,140 +2253,6 @@ class MathTests(unittest.TestCase):
|
||||
self.assertEqual(type(prod([1, decimal.Decimal(2.0), 3, 4, 5, 6])),
|
||||
decimal.Decimal)
|
||||
|
||||
def testPerm(self):
|
||||
perm = math.perm
|
||||
factorial = math.factorial
|
||||
# Test if factorial definition is satisfied
|
||||
for n in range(500):
|
||||
for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)):
|
||||
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))
|
||||
|
||||
# Test one argument form
|
||||
for n in range(20):
|
||||
self.assertEqual(perm(n), factorial(n))
|
||||
self.assertEqual(perm(n, None), factorial(n))
|
||||
|
||||
# Raises TypeError if any argument is non-integer or argument count is
|
||||
# not 1 or 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)
|
||||
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)
|
||||
|
||||
# Returns zero if k is greater than n
|
||||
self.assertEqual(perm(1, 2), 0)
|
||||
self.assertEqual(perm(1, 2**1000), 0)
|
||||
|
||||
n = 2**1000
|
||||
self.assertEqual(perm(n, 0), 1)
|
||||
self.assertEqual(perm(n, 1), n)
|
||||
self.assertEqual(perm(n, 2), n * (n-1))
|
||||
if support.check_impl_detail(cpython=True):
|
||||
self.assertRaises(OverflowError, 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(IntSubclass(5), IntSubclass(2)), 20)
|
||||
self.assertEqual(perm(MyIndexable(5), MyIndexable(2)), 20)
|
||||
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
|
||||
factorial = math.factorial
|
||||
# Test if factorial definition is satisfied
|
||||
for n in range(500):
|
||||
for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)):
|
||||
self.assertEqual(comb(n, k), factorial(n)
|
||||
// (factorial(k) * factorial(n - k)))
|
||||
|
||||
# Test for Pascal's identity
|
||||
for n in range(1, 100):
|
||||
for k in range(1, n):
|
||||
self.assertEqual(comb(n, k), comb(n - 1, k - 1) + comb(n - 1, k))
|
||||
|
||||
# Test corner cases
|
||||
for n in range(100):
|
||||
self.assertEqual(comb(n, 0), 1)
|
||||
self.assertEqual(comb(n, n), 1)
|
||||
|
||||
for n in range(1, 100):
|
||||
self.assertEqual(comb(n, 1), n)
|
||||
self.assertEqual(comb(n, n - 1), n)
|
||||
|
||||
# Test Symmetry
|
||||
for n in range(100):
|
||||
for k in range(n // 2):
|
||||
self.assertEqual(comb(n, k), comb(n, n - k))
|
||||
|
||||
# Raises TypeError if any argument is non-integer or argument count is
|
||||
# not 2
|
||||
self.assertRaises(TypeError, comb, 10, 1.0)
|
||||
self.assertRaises(TypeError, comb, 10, decimal.Decimal(1.0))
|
||||
self.assertRaises(TypeError, comb, 10, "1")
|
||||
self.assertRaises(TypeError, comb, 10.0, 1)
|
||||
self.assertRaises(TypeError, comb, decimal.Decimal(10.0), 1)
|
||||
self.assertRaises(TypeError, comb, "10", 1)
|
||||
|
||||
self.assertRaises(TypeError, comb, 10)
|
||||
self.assertRaises(TypeError, comb, 10, 1, 3)
|
||||
self.assertRaises(TypeError, comb)
|
||||
|
||||
# Raises Value error if not k or n are negative numbers
|
||||
self.assertRaises(ValueError, comb, -1, 1)
|
||||
self.assertRaises(ValueError, comb, -2**1000, 1)
|
||||
self.assertRaises(ValueError, comb, 1, -1)
|
||||
self.assertRaises(ValueError, comb, 1, -2**1000)
|
||||
|
||||
# Returns zero if k is greater than n
|
||||
self.assertEqual(comb(1, 2), 0)
|
||||
self.assertEqual(comb(1, 2**1000), 0)
|
||||
|
||||
n = 2**1000
|
||||
self.assertEqual(comb(n, 0), 1)
|
||||
self.assertEqual(comb(n, 1), n)
|
||||
self.assertEqual(comb(n, 2), n * (n-1) // 2)
|
||||
self.assertEqual(comb(n, n), 1)
|
||||
self.assertEqual(comb(n, n-1), n)
|
||||
self.assertEqual(comb(n, n-2), n * (n-1) // 2)
|
||||
if support.check_impl_detail(cpython=True):
|
||||
self.assertRaises(OverflowError, comb, n, n//2)
|
||||
|
||||
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)
|
||||
for k in range(3):
|
||||
self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int)
|
||||
self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int)
|
||||
|
||||
@requires_IEEE_754
|
||||
def test_nextafter(self):
|
||||
# around 2^52 and 2^63
|
||||
|
||||
403
Lib/test/test_math_integer.py
Normal file
403
Lib/test/test_math_integer.py
Normal file
@@ -0,0 +1,403 @@
|
||||
from decimal import Decimal
|
||||
from fractions import Fraction
|
||||
import unittest
|
||||
from test import support
|
||||
|
||||
|
||||
class IntSubclass(int):
|
||||
pass
|
||||
|
||||
# Class providing an __index__ method.
|
||||
class MyIndexable(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __index__(self):
|
||||
return self.value
|
||||
|
||||
# Here's a pure Python version of the math.integer.factorial algorithm, for
|
||||
# documentation and comparison purposes.
|
||||
#
|
||||
# Formula:
|
||||
#
|
||||
# factorial(n) = factorial_odd_part(n) << (n - count_set_bits(n))
|
||||
#
|
||||
# where
|
||||
#
|
||||
# factorial_odd_part(n) = product_{i >= 0} product_{0 < j <= n >> i; j odd} j
|
||||
#
|
||||
# The outer product above is an infinite product, but once i >= n.bit_length,
|
||||
# (n >> i) < 1 and the corresponding term of the product is empty. So only the
|
||||
# finitely many terms for 0 <= i < n.bit_length() contribute anything.
|
||||
#
|
||||
# We iterate downwards from i == n.bit_length() - 1 to i == 0. The inner
|
||||
# product in the formula above starts at 1 for i == n.bit_length(); for each i
|
||||
# < n.bit_length() we get the inner product for i from that for i + 1 by
|
||||
# multiplying by all j in {n >> i+1 < j <= n >> i; j odd}. In Python terms,
|
||||
# this set is range((n >> i+1) + 1 | 1, (n >> i) + 1 | 1, 2).
|
||||
|
||||
def count_set_bits(n):
|
||||
"""Number of '1' bits in binary expansion of a nonnnegative integer."""
|
||||
return 1 + count_set_bits(n & n - 1) if n else 0
|
||||
|
||||
def partial_product(start, stop):
|
||||
"""Product of integers in range(start, stop, 2), computed recursively.
|
||||
start and stop should both be odd, with start <= stop.
|
||||
|
||||
"""
|
||||
numfactors = (stop - start) >> 1
|
||||
if not numfactors:
|
||||
return 1
|
||||
elif numfactors == 1:
|
||||
return start
|
||||
else:
|
||||
mid = (start + numfactors) | 1
|
||||
return partial_product(start, mid) * partial_product(mid, stop)
|
||||
|
||||
def py_factorial(n):
|
||||
"""Factorial of nonnegative integer n, via "Binary Split Factorial Formula"
|
||||
described at http://www.luschny.de/math/factorial/binarysplitfact.html
|
||||
|
||||
"""
|
||||
inner = outer = 1
|
||||
for i in reversed(range(n.bit_length())):
|
||||
inner *= partial_product((n >> i + 1) + 1 | 1, (n >> i) + 1 | 1)
|
||||
outer *= inner
|
||||
return outer << (n - count_set_bits(n))
|
||||
|
||||
|
||||
class IntMathTests(unittest.TestCase):
|
||||
import math.integer as module
|
||||
|
||||
def assertIntEqual(self, actual, expected):
|
||||
self.assertEqual(actual, expected)
|
||||
self.assertIs(type(actual), int)
|
||||
|
||||
def test_factorial(self):
|
||||
factorial = self.module.factorial
|
||||
self.assertEqual(factorial(0), 1)
|
||||
total = 1
|
||||
for i in range(1, 1000):
|
||||
total *= i
|
||||
self.assertEqual(factorial(i), total)
|
||||
self.assertEqual(factorial(i), py_factorial(i))
|
||||
|
||||
self.assertIntEqual(factorial(False), 1)
|
||||
self.assertIntEqual(factorial(True), 1)
|
||||
for i in range(3):
|
||||
expected = factorial(i)
|
||||
self.assertIntEqual(factorial(IntSubclass(i)), expected)
|
||||
self.assertIntEqual(factorial(MyIndexable(i)), expected)
|
||||
|
||||
self.assertRaises(ValueError, factorial, -1)
|
||||
self.assertRaises(ValueError, factorial, -10**1000)
|
||||
|
||||
def test_factorial_non_integers(self):
|
||||
factorial = self.module.factorial
|
||||
self.assertRaises(TypeError, factorial, 5.0)
|
||||
self.assertRaises(TypeError, factorial, 5.2)
|
||||
self.assertRaises(TypeError, factorial, -1.0)
|
||||
self.assertRaises(TypeError, factorial, -1e100)
|
||||
self.assertRaises(TypeError, factorial, Decimal('5'))
|
||||
self.assertRaises(TypeError, factorial, Decimal('5.2'))
|
||||
self.assertRaises(TypeError, factorial, Fraction(5, 1))
|
||||
self.assertRaises(TypeError, factorial, "5")
|
||||
|
||||
# Other implementations may place different upper bounds.
|
||||
@support.cpython_only
|
||||
def test_factorial_huge_inputs(self):
|
||||
factorial = self.module.factorial
|
||||
# Currently raises OverflowError for inputs that are too large
|
||||
# to fit into a C long.
|
||||
self.assertRaises(OverflowError, factorial, 10**100)
|
||||
self.assertRaises(TypeError, factorial, 1e100)
|
||||
|
||||
def test_gcd(self):
|
||||
gcd = self.module.gcd
|
||||
self.assertEqual(gcd(0, 0), 0)
|
||||
self.assertEqual(gcd(1, 0), 1)
|
||||
self.assertEqual(gcd(-1, 0), 1)
|
||||
self.assertEqual(gcd(0, 1), 1)
|
||||
self.assertEqual(gcd(0, -1), 1)
|
||||
self.assertEqual(gcd(7, 1), 1)
|
||||
self.assertEqual(gcd(7, -1), 1)
|
||||
self.assertEqual(gcd(-23, 15), 1)
|
||||
self.assertEqual(gcd(120, 84), 12)
|
||||
self.assertEqual(gcd(84, -120), 12)
|
||||
self.assertEqual(gcd(1216342683557601535506311712,
|
||||
436522681849110124616458784), 32)
|
||||
c = 652560
|
||||
x = 434610456570399902378880679233098819019853229470286994367836600566
|
||||
y = 1064502245825115327754847244914921553977
|
||||
a = x * c
|
||||
b = y * c
|
||||
self.assertEqual(gcd(a, b), c)
|
||||
self.assertEqual(gcd(b, a), c)
|
||||
self.assertEqual(gcd(-a, b), c)
|
||||
self.assertEqual(gcd(b, -a), c)
|
||||
self.assertEqual(gcd(a, -b), c)
|
||||
self.assertEqual(gcd(-b, a), c)
|
||||
self.assertEqual(gcd(-a, -b), c)
|
||||
self.assertEqual(gcd(-b, -a), c)
|
||||
c = 576559230871654959816130551884856912003141446781646602790216406874
|
||||
a = x * c
|
||||
b = y * c
|
||||
self.assertEqual(gcd(a, b), c)
|
||||
self.assertEqual(gcd(b, a), c)
|
||||
self.assertEqual(gcd(-a, b), c)
|
||||
self.assertEqual(gcd(b, -a), c)
|
||||
self.assertEqual(gcd(a, -b), c)
|
||||
self.assertEqual(gcd(-b, a), c)
|
||||
self.assertEqual(gcd(-a, -b), c)
|
||||
self.assertEqual(gcd(-b, -a), c)
|
||||
|
||||
self.assertRaises(TypeError, gcd, 120.0, 84)
|
||||
self.assertRaises(TypeError, gcd, 120, 84.0)
|
||||
self.assertIntEqual(gcd(IntSubclass(120), IntSubclass(84)), 12)
|
||||
self.assertIntEqual(gcd(MyIndexable(120), MyIndexable(84)), 12)
|
||||
|
||||
def test_lcm(self):
|
||||
lcm = self.module.lcm
|
||||
self.assertEqual(lcm(0, 0), 0)
|
||||
self.assertEqual(lcm(1, 0), 0)
|
||||
self.assertEqual(lcm(-1, 0), 0)
|
||||
self.assertEqual(lcm(0, 1), 0)
|
||||
self.assertEqual(lcm(0, -1), 0)
|
||||
self.assertEqual(lcm(7, 1), 7)
|
||||
self.assertEqual(lcm(7, -1), 7)
|
||||
self.assertEqual(lcm(-23, 15), 345)
|
||||
self.assertEqual(lcm(120, 84), 840)
|
||||
self.assertEqual(lcm(84, -120), 840)
|
||||
self.assertEqual(lcm(1216342683557601535506311712,
|
||||
436522681849110124616458784),
|
||||
16592536571065866494401400422922201534178938447014944)
|
||||
|
||||
x = 43461045657039990237
|
||||
y = 10645022458251153277
|
||||
for c in (652560,
|
||||
57655923087165495981):
|
||||
a = x * c
|
||||
b = y * c
|
||||
d = x * y * c
|
||||
self.assertEqual(lcm(a, b), d)
|
||||
self.assertEqual(lcm(b, a), d)
|
||||
self.assertEqual(lcm(-a, b), d)
|
||||
self.assertEqual(lcm(b, -a), d)
|
||||
self.assertEqual(lcm(a, -b), d)
|
||||
self.assertEqual(lcm(-b, a), d)
|
||||
self.assertEqual(lcm(-a, -b), d)
|
||||
self.assertEqual(lcm(-b, -a), d)
|
||||
|
||||
self.assertEqual(lcm(), 1)
|
||||
self.assertEqual(lcm(120), 120)
|
||||
self.assertEqual(lcm(-120), 120)
|
||||
self.assertEqual(lcm(120, 84, 102), 14280)
|
||||
self.assertEqual(lcm(120, 0, 84), 0)
|
||||
|
||||
self.assertRaises(TypeError, lcm, 120.0)
|
||||
self.assertRaises(TypeError, lcm, 120.0, 84)
|
||||
self.assertRaises(TypeError, lcm, 120, 84.0)
|
||||
self.assertRaises(TypeError, lcm, 120, 0, 84.0)
|
||||
self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840)
|
||||
|
||||
def test_isqrt(self):
|
||||
isqrt = self.module.isqrt
|
||||
# Test a variety of inputs, large and small.
|
||||
test_values = (
|
||||
list(range(1000))
|
||||
+ list(range(10**6 - 1000, 10**6 + 1000))
|
||||
+ [2**e + i for e in range(60, 200) for i in range(-40, 40)]
|
||||
+ [3**9999, 10**5001]
|
||||
)
|
||||
|
||||
for value in test_values:
|
||||
with self.subTest(value=value):
|
||||
s = isqrt(value)
|
||||
self.assertIs(type(s), int)
|
||||
self.assertLessEqual(s*s, value)
|
||||
self.assertLess(value, (s+1)*(s+1))
|
||||
|
||||
# Negative values
|
||||
with self.assertRaises(ValueError):
|
||||
isqrt(-1)
|
||||
|
||||
# Integer-like things
|
||||
self.assertIntEqual(isqrt(True), 1)
|
||||
self.assertIntEqual(isqrt(False), 0)
|
||||
self.assertIntEqual(isqrt(MyIndexable(1729)), 41)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
isqrt(MyIndexable(-3))
|
||||
|
||||
# Non-integer-like things
|
||||
bad_values = [
|
||||
3.5, "a string", Decimal("3.5"), 3.5j,
|
||||
100.0, -4.0,
|
||||
]
|
||||
for value in bad_values:
|
||||
with self.subTest(value=value):
|
||||
with self.assertRaises(TypeError):
|
||||
isqrt(value)
|
||||
|
||||
@support.bigmemtest(2**32, memuse=0.85)
|
||||
def test_isqrt_huge(self, size):
|
||||
isqrt = self.module.isqrt
|
||||
if size & 1:
|
||||
size += 1
|
||||
v = 1 << size
|
||||
w = isqrt(v)
|
||||
self.assertEqual(w.bit_length(), size // 2 + 1)
|
||||
self.assertEqual(w.bit_count(), 1)
|
||||
|
||||
def test_perm(self):
|
||||
perm = self.module.perm
|
||||
factorial = self.module.factorial
|
||||
# Test if factorial definition is satisfied
|
||||
for n in range(500):
|
||||
for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)):
|
||||
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))
|
||||
|
||||
# Test one argument form
|
||||
for n in range(20):
|
||||
self.assertEqual(perm(n), factorial(n))
|
||||
self.assertEqual(perm(n, None), factorial(n))
|
||||
|
||||
# Raises TypeError if any argument is non-integer or argument count is
|
||||
# not 1 or 2
|
||||
self.assertRaises(TypeError, perm, 10, 1.0)
|
||||
self.assertRaises(TypeError, perm, 10, Decimal(1.0))
|
||||
self.assertRaises(TypeError, perm, 10, Fraction(1, 1))
|
||||
self.assertRaises(TypeError, perm, 10, "1")
|
||||
self.assertRaises(TypeError, perm, 10.0, 1)
|
||||
self.assertRaises(TypeError, perm, Decimal(10.0), 1)
|
||||
self.assertRaises(TypeError, perm, Fraction(10, 1), 1)
|
||||
self.assertRaises(TypeError, perm, "10", 1)
|
||||
|
||||
self.assertRaises(TypeError, perm)
|
||||
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)
|
||||
|
||||
# Returns zero if k is greater than n
|
||||
self.assertEqual(perm(1, 2), 0)
|
||||
self.assertEqual(perm(1, 2**1000), 0)
|
||||
|
||||
n = 2**1000
|
||||
self.assertEqual(perm(n, 0), 1)
|
||||
self.assertEqual(perm(n, 1), n)
|
||||
self.assertEqual(perm(n, 2), n * (n-1))
|
||||
if support.check_impl_detail(cpython=True):
|
||||
self.assertRaises(OverflowError, perm, n, n)
|
||||
|
||||
for n, k in (True, True), (True, False), (False, False):
|
||||
self.assertIntEqual(perm(n, k), 1)
|
||||
self.assertEqual(perm(IntSubclass(5), IntSubclass(2)), 20)
|
||||
self.assertEqual(perm(MyIndexable(5), MyIndexable(2)), 20)
|
||||
for k in range(3):
|
||||
self.assertIs(type(perm(IntSubclass(5), IntSubclass(k))), int)
|
||||
self.assertIs(type(perm(MyIndexable(5), MyIndexable(k))), int)
|
||||
|
||||
def test_comb(self):
|
||||
comb = self.module.comb
|
||||
factorial = self.module.factorial
|
||||
# Test if factorial definition is satisfied
|
||||
for n in range(500):
|
||||
for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)):
|
||||
self.assertEqual(comb(n, k), factorial(n)
|
||||
// (factorial(k) * factorial(n - k)))
|
||||
|
||||
# Test for Pascal's identity
|
||||
for n in range(1, 100):
|
||||
for k in range(1, n):
|
||||
self.assertEqual(comb(n, k), comb(n - 1, k - 1) + comb(n - 1, k))
|
||||
|
||||
# Test corner cases
|
||||
for n in range(100):
|
||||
self.assertEqual(comb(n, 0), 1)
|
||||
self.assertEqual(comb(n, n), 1)
|
||||
|
||||
for n in range(1, 100):
|
||||
self.assertEqual(comb(n, 1), n)
|
||||
self.assertEqual(comb(n, n - 1), n)
|
||||
|
||||
# Test Symmetry
|
||||
for n in range(100):
|
||||
for k in range(n // 2):
|
||||
self.assertEqual(comb(n, k), comb(n, n - k))
|
||||
|
||||
# Raises TypeError if any argument is non-integer or argument count is
|
||||
# not 2
|
||||
self.assertRaises(TypeError, comb, 10, 1.0)
|
||||
self.assertRaises(TypeError, comb, 10, Decimal(1.0))
|
||||
self.assertRaises(TypeError, comb, 10, "1")
|
||||
self.assertRaises(TypeError, comb, 10.0, 1)
|
||||
self.assertRaises(TypeError, comb, Decimal(10.0), 1)
|
||||
self.assertRaises(TypeError, comb, "10", 1)
|
||||
|
||||
self.assertRaises(TypeError, comb, 10)
|
||||
self.assertRaises(TypeError, comb, 10, 1, 3)
|
||||
self.assertRaises(TypeError, comb)
|
||||
|
||||
# Raises Value error if not k or n are negative numbers
|
||||
self.assertRaises(ValueError, comb, -1, 1)
|
||||
self.assertRaises(ValueError, comb, -2**1000, 1)
|
||||
self.assertRaises(ValueError, comb, 1, -1)
|
||||
self.assertRaises(ValueError, comb, 1, -2**1000)
|
||||
|
||||
# Returns zero if k is greater than n
|
||||
self.assertEqual(comb(1, 2), 0)
|
||||
self.assertEqual(comb(1, 2**1000), 0)
|
||||
|
||||
n = 2**1000
|
||||
self.assertEqual(comb(n, 0), 1)
|
||||
self.assertEqual(comb(n, 1), n)
|
||||
self.assertEqual(comb(n, 2), n * (n-1) // 2)
|
||||
self.assertEqual(comb(n, n), 1)
|
||||
self.assertEqual(comb(n, n-1), n)
|
||||
self.assertEqual(comb(n, n-2), n * (n-1) // 2)
|
||||
if support.check_impl_detail(cpython=True):
|
||||
self.assertRaises(OverflowError, comb, n, n//2)
|
||||
|
||||
for n, k in (True, True), (True, False), (False, False):
|
||||
self.assertIntEqual(comb(n, k), 1)
|
||||
self.assertEqual(comb(IntSubclass(5), IntSubclass(2)), 10)
|
||||
self.assertEqual(comb(MyIndexable(5), MyIndexable(2)), 10)
|
||||
for k in range(3):
|
||||
self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int)
|
||||
self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int)
|
||||
|
||||
|
||||
class MathTests(IntMathTests):
|
||||
import math as module
|
||||
|
||||
|
||||
class MiscTests(unittest.TestCase):
|
||||
|
||||
def test_module_name(self):
|
||||
import math.integer
|
||||
self.assertEqual(math.integer.__name__, 'math.integer')
|
||||
for name in dir(math.integer):
|
||||
if not name.startswith('_'):
|
||||
obj = getattr(math.integer, name)
|
||||
self.assertEqual(obj.__module__, 'math.integer')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1 @@
|
||||
Add the :mod:`math.integer` module (:pep:`791`).
|
||||
@@ -156,6 +156,7 @@ PYTHONPATH=$(COREPYTHONPATH)
|
||||
#binascii binascii.c
|
||||
#cmath cmathmodule.c
|
||||
#math mathmodule.c
|
||||
#_math_integer mathintegermodule.c
|
||||
#mmap mmapmodule.c
|
||||
#select selectmodule.c
|
||||
#_sysconfig _sysconfig.c
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
@MODULE__HEAPQ_TRUE@_heapq _heapqmodule.c
|
||||
@MODULE__JSON_TRUE@_json _json.c
|
||||
@MODULE__LSPROF_TRUE@_lsprof _lsprof.c rotatingtree.c
|
||||
@MODULE__MATH_INTEGER_TRUE@_math_integer mathintegermodule.c
|
||||
@MODULE__PICKLE_TRUE@_pickle _pickle.c
|
||||
@MODULE__QUEUE_TRUE@_queue _queuemodule.c
|
||||
@MODULE__RANDOM_TRUE@_random _randommodule.c
|
||||
|
||||
159
Modules/clinic/mathintegermodule.c.h
generated
Normal file
159
Modules/clinic/mathintegermodule.c.h
generated
Normal file
@@ -0,0 +1,159 @@
|
||||
/*[clinic input]
|
||||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
||||
|
||||
PyDoc_STRVAR(math_integer_gcd__doc__,
|
||||
"gcd($module, /, *integers)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Greatest Common Divisor.");
|
||||
|
||||
#define MATH_INTEGER_GCD_METHODDEF \
|
||||
{"gcd", _PyCFunction_CAST(math_integer_gcd), METH_FASTCALL, math_integer_gcd__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_integer_gcd_impl(PyObject *module, PyObject * const *args,
|
||||
Py_ssize_t args_length);
|
||||
|
||||
static PyObject *
|
||||
math_integer_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject * const *__clinic_args;
|
||||
Py_ssize_t args_length;
|
||||
|
||||
__clinic_args = args;
|
||||
args_length = nargs;
|
||||
return_value = math_integer_gcd_impl(module, __clinic_args, args_length);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_integer_lcm__doc__,
|
||||
"lcm($module, /, *integers)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Least Common Multiple.");
|
||||
|
||||
#define MATH_INTEGER_LCM_METHODDEF \
|
||||
{"lcm", _PyCFunction_CAST(math_integer_lcm), METH_FASTCALL, math_integer_lcm__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_integer_lcm_impl(PyObject *module, PyObject * const *args,
|
||||
Py_ssize_t args_length);
|
||||
|
||||
static PyObject *
|
||||
math_integer_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject * const *__clinic_args;
|
||||
Py_ssize_t args_length;
|
||||
|
||||
__clinic_args = args;
|
||||
args_length = nargs;
|
||||
return_value = math_integer_lcm_impl(module, __clinic_args, args_length);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_integer_isqrt__doc__,
|
||||
"isqrt($module, n, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the integer part of the square root of the input.");
|
||||
|
||||
#define MATH_INTEGER_ISQRT_METHODDEF \
|
||||
{"isqrt", (PyCFunction)math_integer_isqrt, METH_O, math_integer_isqrt__doc__},
|
||||
|
||||
PyDoc_STRVAR(math_integer_factorial__doc__,
|
||||
"factorial($module, n, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Find n!.");
|
||||
|
||||
#define MATH_INTEGER_FACTORIAL_METHODDEF \
|
||||
{"factorial", (PyCFunction)math_integer_factorial, METH_O, math_integer_factorial__doc__},
|
||||
|
||||
PyDoc_STRVAR(math_integer_perm__doc__,
|
||||
"perm($module, n, k=None, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Number of ways to choose k items from n items without repetition and with order.\n"
|
||||
"\n"
|
||||
"Evaluates to n! / (n - k)! when k <= n and evaluates\n"
|
||||
"to zero when k > n.\n"
|
||||
"\n"
|
||||
"If k is not specified or is None, then k defaults to n\n"
|
||||
"and the function returns n!.\n"
|
||||
"\n"
|
||||
"Raises ValueError if either of the arguments are negative.");
|
||||
|
||||
#define MATH_INTEGER_PERM_METHODDEF \
|
||||
{"perm", _PyCFunction_CAST(math_integer_perm), METH_FASTCALL, math_integer_perm__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_integer_perm_impl(PyObject *module, PyObject *n, PyObject *k);
|
||||
|
||||
static PyObject *
|
||||
math_integer_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *n;
|
||||
PyObject *k = Py_None;
|
||||
|
||||
if (!_PyArg_CheckPositional("perm", nargs, 1, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
n = args[0];
|
||||
if (nargs < 2) {
|
||||
goto skip_optional;
|
||||
}
|
||||
k = args[1];
|
||||
skip_optional:
|
||||
return_value = math_integer_perm_impl(module, n, k);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_integer_comb__doc__,
|
||||
"comb($module, n, k, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Number of ways to choose k items from n items without repetition and without order.\n"
|
||||
"\n"
|
||||
"Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates\n"
|
||||
"to zero when k > n.\n"
|
||||
"\n"
|
||||
"Also called the binomial coefficient because it is equivalent\n"
|
||||
"to the coefficient of k-th term in polynomial expansion of the\n"
|
||||
"expression (1 + x)**n.\n"
|
||||
"\n"
|
||||
"Raises ValueError if either of the arguments are negative.");
|
||||
|
||||
#define MATH_INTEGER_COMB_METHODDEF \
|
||||
{"comb", _PyCFunction_CAST(math_integer_comb), METH_FASTCALL, math_integer_comb__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_integer_comb_impl(PyObject *module, PyObject *n, PyObject *k);
|
||||
|
||||
static PyObject *
|
||||
math_integer_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *n;
|
||||
PyObject *k;
|
||||
|
||||
if (!_PyArg_CheckPositional("comb", nargs, 2, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
n = args[0];
|
||||
k = args[1];
|
||||
return_value = math_integer_comb_impl(module, n, k);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=34697570c923a3af input=a9049054013a1b77]*/
|
||||
157
Modules/clinic/mathmodule.c.h
generated
157
Modules/clinic/mathmodule.c.h
generated
@@ -8,60 +8,6 @@ preserve
|
||||
#endif
|
||||
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
||||
|
||||
PyDoc_STRVAR(math_gcd__doc__,
|
||||
"gcd($module, /, *integers)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Greatest Common Divisor.");
|
||||
|
||||
#define MATH_GCD_METHODDEF \
|
||||
{"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_gcd_impl(PyObject *module, PyObject * const *args,
|
||||
Py_ssize_t args_length);
|
||||
|
||||
static PyObject *
|
||||
math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject * const *__clinic_args;
|
||||
Py_ssize_t args_length;
|
||||
|
||||
__clinic_args = args;
|
||||
args_length = nargs;
|
||||
return_value = math_gcd_impl(module, __clinic_args, args_length);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_lcm__doc__,
|
||||
"lcm($module, /, *integers)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Least Common Multiple.");
|
||||
|
||||
#define MATH_LCM_METHODDEF \
|
||||
{"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_lcm_impl(PyObject *module, PyObject * const *args,
|
||||
Py_ssize_t args_length);
|
||||
|
||||
static PyObject *
|
||||
math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject * const *__clinic_args;
|
||||
Py_ssize_t args_length;
|
||||
|
||||
__clinic_args = args;
|
||||
args_length = nargs;
|
||||
return_value = math_lcm_impl(module, __clinic_args, args_length);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_ceil__doc__,
|
||||
"ceil($module, x, /)\n"
|
||||
"--\n"
|
||||
@@ -235,24 +181,6 @@ PyDoc_STRVAR(math_fsum__doc__,
|
||||
#define MATH_FSUM_METHODDEF \
|
||||
{"fsum", (PyCFunction)math_fsum, METH_O, math_fsum__doc__},
|
||||
|
||||
PyDoc_STRVAR(math_isqrt__doc__,
|
||||
"isqrt($module, n, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the integer part of the square root of the input.");
|
||||
|
||||
#define MATH_ISQRT_METHODDEF \
|
||||
{"isqrt", (PyCFunction)math_isqrt, METH_O, math_isqrt__doc__},
|
||||
|
||||
PyDoc_STRVAR(math_factorial__doc__,
|
||||
"factorial($module, n, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Find n!.");
|
||||
|
||||
#define MATH_FACTORIAL_METHODDEF \
|
||||
{"factorial", (PyCFunction)math_factorial, METH_O, math_factorial__doc__},
|
||||
|
||||
PyDoc_STRVAR(math_trunc__doc__,
|
||||
"trunc($module, x, /)\n"
|
||||
"--\n"
|
||||
@@ -1107,89 +1035,6 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_perm__doc__,
|
||||
"perm($module, n, k=None, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Number of ways to choose k items from n items without repetition and with order.\n"
|
||||
"\n"
|
||||
"Evaluates to n! / (n - k)! when k <= n and evaluates\n"
|
||||
"to zero when k > n.\n"
|
||||
"\n"
|
||||
"If k is not specified or is None, then k defaults to n\n"
|
||||
"and the function returns n!.\n"
|
||||
"\n"
|
||||
"Raises TypeError if either of the arguments are not integers.\n"
|
||||
"Raises ValueError if either of the arguments are negative.");
|
||||
|
||||
#define MATH_PERM_METHODDEF \
|
||||
{"perm", _PyCFunction_CAST(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 = Py_None;
|
||||
|
||||
if (!_PyArg_CheckPositional("perm", nargs, 1, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
n = args[0];
|
||||
if (nargs < 2) {
|
||||
goto skip_optional;
|
||||
}
|
||||
k = args[1];
|
||||
skip_optional:
|
||||
return_value = math_perm_impl(module, n, k);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_comb__doc__,
|
||||
"comb($module, n, k, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Number of ways to choose k items from n items without repetition and without order.\n"
|
||||
"\n"
|
||||
"Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates\n"
|
||||
"to zero when k > n.\n"
|
||||
"\n"
|
||||
"Also called the binomial coefficient because it is equivalent\n"
|
||||
"to the coefficient of k-th term in polynomial expansion of the\n"
|
||||
"expression (1 + x)**n.\n"
|
||||
"\n"
|
||||
"Raises TypeError if either of the arguments are not integers.\n"
|
||||
"Raises ValueError if either of the arguments are negative.");
|
||||
|
||||
#define MATH_COMB_METHODDEF \
|
||||
{"comb", _PyCFunction_CAST(math_comb), METH_FASTCALL, math_comb__doc__},
|
||||
|
||||
static PyObject *
|
||||
math_comb_impl(PyObject *module, PyObject *n, PyObject *k);
|
||||
|
||||
static PyObject *
|
||||
math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *n;
|
||||
PyObject *k;
|
||||
|
||||
if (!_PyArg_CheckPositional("comb", nargs, 2, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
n = args[0];
|
||||
k = args[1];
|
||||
return_value = math_comb_impl(module, n, k);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_nextafter__doc__,
|
||||
"nextafter($module, x, y, /, *, steps=None)\n"
|
||||
"--\n"
|
||||
@@ -1318,4 +1163,4 @@ math_ulp(PyObject *module, PyObject *arg)
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=4fb180d4c25ff8fa input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=23b2453ba77453e5 input=a9049054013a1b77]*/
|
||||
|
||||
1293
Modules/mathintegermodule.c
Normal file
1293
Modules/mathintegermodule.c
Normal file
File diff suppressed because it is too large
Load Diff
1243
Modules/mathmodule.c
1243
Modules/mathmodule.c
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ extern PyObject* PyInit_errno(void);
|
||||
extern PyObject* PyInit_faulthandler(void);
|
||||
extern PyObject* PyInit__tracemalloc(void);
|
||||
extern PyObject* PyInit_gc(void);
|
||||
extern PyObject* PyInit__math_integer(void);
|
||||
extern PyObject* PyInit_math(void);
|
||||
extern PyObject* PyInit_nt(void);
|
||||
extern PyObject* PyInit__operator(void);
|
||||
@@ -100,6 +101,7 @@ struct _inittab _PyImport_Inittab[] = {
|
||||
{"errno", PyInit_errno},
|
||||
{"faulthandler", PyInit_faulthandler},
|
||||
{"gc", PyInit_gc},
|
||||
{"_math_integer", PyInit__math_integer},
|
||||
{"math", PyInit_math},
|
||||
{"nt", PyInit_nt}, /* Use the NT os functions, not posix */
|
||||
{"_operator", PyInit__operator},
|
||||
|
||||
@@ -479,6 +479,7 @@
|
||||
<ClCompile Include="..\Modules\hmacmodule.c" />
|
||||
<ClCompile Include="..\Modules\itertoolsmodule.c" />
|
||||
<ClCompile Include="..\Modules\main.c" />
|
||||
<ClCompile Include="..\Modules\mathintegermodule.c" />
|
||||
<ClCompile Include="..\Modules\mathmodule.c" />
|
||||
<ClCompile Include="..\Modules\md5module.c" />
|
||||
<ClCompile Include="..\Modules\mmapmodule.c" />
|
||||
|
||||
@@ -1070,6 +1070,9 @@
|
||||
<ClCompile Include="..\Modules\main.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\mathintegermodule.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\mathmodule.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
|
||||
1
Python/stdlib_module_names.h
generated
1
Python/stdlib_module_names.h
generated
@@ -51,6 +51,7 @@ static const char* _Py_stdlib_module_names[] = {
|
||||
"_lsprof",
|
||||
"_lzma",
|
||||
"_markupbase",
|
||||
"_math_integer",
|
||||
"_md5",
|
||||
"_multibytecodec",
|
||||
"_multiprocessing",
|
||||
|
||||
@@ -255,14 +255,14 @@ impl_definition block
|
||||
cls: Class | None = None
|
||||
|
||||
for idx, field in enumerate(fields):
|
||||
fullname = ".".join(fields[:idx + 1])
|
||||
if not isinstance(parent, Class):
|
||||
if field in parent.modules:
|
||||
parent = module = parent.modules[field]
|
||||
if fullname in parent.modules:
|
||||
parent = module = parent.modules[fullname]
|
||||
continue
|
||||
if field in parent.classes:
|
||||
parent = cls = parent.classes[field]
|
||||
else:
|
||||
fullname = ".".join(fields[idx:])
|
||||
fail(f"Parent class or module {fullname!r} does not exist.")
|
||||
|
||||
return module, cls
|
||||
|
||||
28
configure
generated
vendored
28
configure
generated
vendored
@@ -814,6 +814,8 @@ MODULE__BISECT_FALSE
|
||||
MODULE__BISECT_TRUE
|
||||
MODULE__ASYNCIO_FALSE
|
||||
MODULE__ASYNCIO_TRUE
|
||||
MODULE__MATH_INTEGER_FALSE
|
||||
MODULE__MATH_INTEGER_TRUE
|
||||
MODULE_ARRAY_FALSE
|
||||
MODULE_ARRAY_TRUE
|
||||
MODULE_TIME_FALSE
|
||||
@@ -31334,6 +31336,28 @@ then :
|
||||
|
||||
|
||||
|
||||
fi
|
||||
|
||||
|
||||
if test "$py_cv_module__math_integer" != "n/a"
|
||||
then :
|
||||
py_cv_module__math_integer=yes
|
||||
fi
|
||||
if test "$py_cv_module__math_integer" = yes; then
|
||||
MODULE__MATH_INTEGER_TRUE=
|
||||
MODULE__MATH_INTEGER_FALSE='#'
|
||||
else
|
||||
MODULE__MATH_INTEGER_TRUE='#'
|
||||
MODULE__MATH_INTEGER_FALSE=
|
||||
fi
|
||||
|
||||
as_fn_append MODULE_BLOCK "MODULE__MATH_INTEGER_STATE=$py_cv_module__math_integer$as_nl"
|
||||
if test "x$py_cv_module__math_integer" = xyes
|
||||
then :
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
|
||||
|
||||
@@ -34447,6 +34471,10 @@ if test -z "${MODULE_ARRAY_TRUE}" && test -z "${MODULE_ARRAY_FALSE}"; then
|
||||
as_fn_error $? "conditional \"MODULE_ARRAY\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${MODULE__MATH_INTEGER_TRUE}" && test -z "${MODULE__MATH_INTEGER_FALSE}"; then
|
||||
as_fn_error $? "conditional \"MODULE__MATH_INTEGER\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${MODULE__ASYNCIO_TRUE}" && test -z "${MODULE__ASYNCIO_FALSE}"; then
|
||||
as_fn_error $? "conditional \"MODULE__ASYNCIO\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
|
||||
@@ -7881,6 +7881,7 @@ PY_STDLIB_MOD_SIMPLE([time], [], [$TIMEMODULE_LIB])
|
||||
|
||||
dnl always enabled extension modules
|
||||
PY_STDLIB_MOD_SIMPLE([array])
|
||||
PY_STDLIB_MOD_SIMPLE([_math_integer])
|
||||
PY_STDLIB_MOD_SIMPLE([_asyncio])
|
||||
PY_STDLIB_MOD_SIMPLE([_bisect])
|
||||
PY_STDLIB_MOD_SIMPLE([_csv])
|
||||
|
||||
Reference in New Issue
Block a user