Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 78 additions & 71 deletions peps/pep-0791.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,27 @@ Abstract
========

This PEP proposes a new module for number-theoretical, combinatorial and other
functions defined for integer arguments, like
:external+py3.14:func:`math.gcd` or :external+py3.14:func:`math.isqrt`.
functions defined for integer arguments, like :external+py3.14:func:`math.gcd`
or :external+py3.14:func:`math.isqrt`.


Motivation
==========

The :external+py3.14:mod:`math` documentation says: "This module provides access
to the mathematical functions defined by the C standard." But,
over time the module was populated with functions that aren't related to
the C standard or floating-point arithmetics. Now it's much harder to describe
The :external+py3.14:mod:`math` documentation says: "This module provides
access to the mathematical functions defined by the C standard." But, over
time the module was populated with functions that aren't related to the C
standard or floating-point arithmetics. Now it's much harder to describe
module scope, content and interfaces (returned values or accepted arguments).

For example, the :external+py3.14:mod:`math` module documentation says: "Except
when explicitly noted otherwise, all return values are floats." This is no
longer true: *None* of the functions listed in the `Number-theoretic
functions <https://docs.python.org/3.14/library/math.html#number-theoretic-functions>`_
subsection of the documentation return a float, but the
documentation doesn't say so. In the documentation for the proposed ``intmath`` module the sentence "All
return values are integers." would be accurate. In a similar way we
can simplify the description of the accepted arguments for functions in both the
For example, the following statement from the documentation: "Except when
explicitly noted otherwise, all return values are floats." This is no longer
true: *None* of the functions listed in the `Number-theoretic functions
<https://docs.python.org/3.14/library/math.html#number-theoretic-functions>`_
subsection of the documentation return a float, but the documentation doesn't
say so. In the documentation for the proposed ``intmath`` module the sentence
"All return values are integers" would be accurate. In a similar way we can
simplify the description of the accepted arguments for functions in both the
new module and in :external+py3.14:mod:`math`.

Now it's a lot harder to satisfy people's expectations about the module
Expand All @@ -45,12 +45,13 @@ return an exact answer? Many languages, Python packages (like :pypi:`scipy`)
or pocket calculators have functions with same or similar name, that return a
floating-point value, which is only an approximation in this example.

Apparently, the :external+py3.14:mod:`math` module can't serve as a catch-all place
for mathematical functions since we also have the :external+py3.14:mod:`cmath` and
:external+py3.14:mod:`statistics` modules. Let's do the same for integer-related
functions. It provides shared context, which reduces verbosity in the
documentation and conceptual load. It also aids discoverability through
grouping related functions and makes IDE suggestions more helpful.
Apparently, the :external+py3.14:mod:`math` module can't serve as a catch-all
place for mathematical functions since we also have the
:external+py3.14:mod:`cmath` and :external+py3.14:mod:`statistics` modules.
Let's do the same for integer-related functions. It provides shared context,
which reduces verbosity in the documentation and conceptual load. It also aids
discoverability through grouping related functions and makes IDE suggestions
more helpful.

Currently the :external+py3.14:mod:`math` module code in the CPython is around
4200LOC, from which the new module code is roughly 1/3 (1300LOC). This is
Expand All @@ -62,61 +63,67 @@ And this situation tends to get worse. When the module split `was first
proposed
<https://mail.python.org/archives/list/python-ideas@python.org/thread/YYJ5YJBJNCVXQWK5K3WSVNMPUSV56LOR/>`_,
there were only two integer-related functions:
:external+py3.14:func:`~math.factorial` (accepting also :class:`float`'s, like other
functions in the module) and :external+py3.14:func:`~math.gcd` (moved from the
::external+py3.14:mod:`fractions` module). Then
:external+py3.14:func:`~math.factorial` (accepting also :class:`float`'s, like
other functions in the module) and :external+py3.14:func:`~math.gcd` (moved
from the :external+py3.14:mod:`fractions` module). Then
:external+py3.14:func:`~math.isqrt`, :external+py3.14:func:`~math.comb` and
:external+py3.14:func:`~math.perm` were added, and addition of the new module
was `proposed second time <https://github.com/python/cpython/issues/81313>`_,
so all new functions would go directly to it, without littering the
:external+py3.14:mod:`math` namespace.
Now there are six functions and :external+py3.14:func:`~math.factorial` doesn't accept
:class:`float`'s anymore.
:external+py3.14:mod:`math` namespace. Now there are six functions and
:external+py3.14:func:`~math.factorial` doesn't accept :class:`float`\ s
anymore.

Some possible additions, among those proposed in the initial discussion thread
and issue
`python/cpython#81313 <https://github.com/python/cpython/issues/81313>`_ are:
and issue `python/cpython#81313
<https://github.com/python/cpython/issues/81313>`_ are:

* ``c_div()`` and ``n_div()`` --- for integer division with rounding towards
positive infinity (ceiling divide) and to the nearest integer, see `relevant
discussion thread <https://discuss.python.org/t/91269>`_. This is reinvented
several times in the stdlib, e.g. in the :mod:`datetime` and the
several times in the stdlib, e.g. in :mod:`datetime` and
:mod:`fractions`.
* ``gcdext()`` --- to solve linear `Diophantine equation <https://en.wikipedia.org/wiki/Diophantine_equation>`_ in two variables (the
* ``gcdext()`` --- to solve linear `Diophantine equation
<https://en.wikipedia.org/wiki/Diophantine_equation>`_ in two variables (the
:external+py3.14:class:`int` implementation actually includes an extended
Euclidean algorithm)
* ``isqrt_rem()`` --- to return both an integer square root and a remainder (which is non-zero only if
the integer isn't a perfect square)
* ``ilog()`` --- integer logarithm, :external+py3.14:func:`math.log`
has special handling for integer arguments. It's unique (with respect to other module
functions) and not documented so far, see issue
`python/cpython#120950 <https://github.com/python/cpython/issues/120950>`_.
* ``fibonacci()`` --- `Fibonacci sequence <https://en.wikipedia.org/wiki/Fibonacci_sequence>`_.
* ``isqrt_rem()`` --- to return both an integer square root and a remainder
(which is non-zero only if the integer isn't a perfect square)

* ``ilog()`` --- integer logarithm, :external+py3.14:func:`math.log` has
special handling for integer arguments. It's unique (with respect to other
module functions) and not documented so far, see issue `python/cpython#120950
<https://github.com/python/cpython/issues/120950>`_.
* ``fibonacci()`` --- `Fibonacci sequence
<https://en.wikipedia.org/wiki/Fibonacci_sequence>`_.


Rationale
=========

Why not fix the :external+py3.14:mod:`math` module documentation instead?
Sure, we can be much more vague in the module preamble (i.e. roughly say
that "the :external+py3.14:mod:`math` module contains some mathematical
functions"), we can accurately describe input/output for each function
and it's behavior (e.g. whether the :external+py3.14:func:`~math.factorial`
output is exact or not, like e.g. the `scipy.special.factorial <https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.factorial.html#scipy.special.factorial>`_, per default).

But the major issue is that the current module mixes different, almost non-interlaced
application domains. Adding more documentation will just highlight this and
make the issue worse for end users (more text to read/skip). And it will not
fix issue with discoverability (to know in which module to find a function, and
that it can be found at all, you need to look at all the functions in the
module), nor with tab-completion.
Sure, we can be much more vague in the module preamble (i.e. roughly say that
"the :external+py3.14:mod:`math` module contains some mathematical functions"),
we can accurately describe input/output for each function and its behavior
(e.g. whether the :external+py3.14:func:`~math.factorial` output is exact or
not, like the `scipy.special.factorial
<https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.factorial.html#scipy.special.factorial>`_,
per default).

But the major issue is that the current module mixes different, almost
non-interlaced application domains. Adding more documentation will just
highlight this and make the issue worse for end users (more text to read/skip).
And it will not fix the issue with discoverability (to know in which module to find
a function, and that it can be found at all, you need to look at all the
functions in the module), nor with tab-completion.


Specification
=============

The PEP proposes moving the following integer-related functions to a new
module, called ``intmath``:
module, called ``intmath`` (see `Open Issues`_ for alternatives for
it's name):

* :external+py3.14:func:`~math.comb`
* :external+py3.14:func:`~math.factorial`
Expand Down Expand Up @@ -163,8 +170,8 @@ How to Teach This
=================

The new module will be a place for functions, that 1) accept
:external+py3.14:class:`int`-like arguments and also return integers, and 2) are
also in the field of arbitrary-precision integer arithmetic, i.e. have no
:external+py3.14:class:`int`-like arguments and also return integers, and 2)
are also in the field of arbitrary-precision integer arithmetic, i.e. have no
dependency on the platform floating-point format or behaviour and/or on the
platform math library (``libm``).

Expand All @@ -183,20 +190,6 @@ Reference Implementation
Rejected ideas
==============

Module name
-----------

`Polling showed <https://discuss.python.org/t/92548/67>`_ ``intmath`` as most
popular candidate with ``imath`` as a second winner.

Other proposed names include ``ntheory`` (like SymPy's submodule),
``integermath``, ``zmath``, ``dmath`` and ``imaths``.

As a variant, the new module can be added as a submodule of the
:external+py3.14:mod:`math`: ``integer`` (most preferred), ``discrete``
or ``ntheory``.


isqrt() renaming
---------------------------------------------

Expand All @@ -207,16 +200,30 @@ is ultimately a different function: it is the floor of the square root. It
would be confusing to give it the same name (under a different module).


Open Issues
===========

`Polling showed <https://discuss.python.org/t/92548/67>`_ ``intmath`` as most
popular candidate with ``imath`` as a second winner.

Other proposed names include ``ntheory`` (like SymPy's submodule),
``integermath``, ``zmath``, ``dmath`` and ``imaths``.

As a variant, the new module can be added as a submodule of the
:external+py3.14:mod:`math`: ``integer`` (most preferred), ``discrete`` or
``ntheory`` (author preference).


Acknowledgements
================

Thanks to Tim Peters for reviving the idea of splitting the :external+py3.14:mod:`math`
module. Thanks to Neil Girdhar for substantial improvements of
the initial draft.
Thanks to Tim Peters for reviving the idea of splitting the
:external+py3.14:mod:`math` module. Thanks to Neil Girdhar for substantial
improvements of the initial draft.


Copyright
=========

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.
This document is placed in the public domain or under the CC0-1.0-Universal
license, whichever is more permissive.