PEP: 791
Title: math.integer --- submodule for integer-specific mathematics functions
Author: Neil Girdhar <misterhsheik@gmail.com>,
        Sergey B Kirpichev <skirpichev@gmail.com>,
        Tim Peters <tim.peters@gmail.com>,
        Serhiy Storchaka <storchaka@gmail.com>
Sponsor: Victor Stinner <vstinner@python.org>
Discussions-To: https://discuss.python.org/t/92548
Status: Final
Type: Standards Track
Created: 12-May-2025
Python-Version: 3.15
Post-History: `12-Jul-2018 <https://mail.python.org/archives/list/python-ideas@python.org/thread/YYJ5YJBJNCVXQWK5K3WSVNMPUSV56LOR/>`__,
              `09-May-2025 <https://discuss.python.org/t/91337>`__,
              `19-May-2025 <https://discuss.python.org/t/92548>`__,
Resolution: `23-Oct-2025 <https://discuss.python.org/t/92548/154>`__


.. canonical-doc:: `math.integer — integer-specific mathematics functions <https://docs.python.org/3.15/library/math.integer.html>`_


Abstract
========

This PEP proposes a new submodule 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`.


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
module scope, content and interfaces (returned values or accepted arguments).

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 ``math.integer`` submodule 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 submodule and in :external+py3.14:mod:`math`.

Now it's a lot harder to satisfy people's expectations about the module
content.  For example, should they expect that ``math.factorial(100)`` will
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 (e.g. new
CPython's REPL) 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
comparable with the :external+py3.14:mod:`cmath` (1340LOC), which is *not* a
simple wrapper to the ``libm``, as most functions in the
:external+py3.14:mod:`math` module.

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.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.

Some possible additions, among those proposed in the initial discussion thread
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 :mod:`datetime` and :mod:`fractions`.
  And it's easy to do this wrongly, as demonstrated by the thread.
* ``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>`_.

Separated namespace eliminates possible name clash with existing
:external+py3.14:mod:`math`'s module functions.  For example, possible names
``ceil_div()`` or ``ceildiv()`` for integer ceiling division will interfere
with the :external+py3.14:func:`~math.ceil` (which is for :class:`float`'s and
*sometimes* does right things for integer division, as an accident --- but
`usually not <https://discuss.python.org/t/91269/6>`_).


Rationale
=========

Is this all about documentation, why not fix it instead?  No, it isn't.  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
submodule, called ``math.integer``:

* :external+py3.14:func:`~math.comb`
* :external+py3.14:func:`~math.factorial`
* :external+py3.14:func:`~math.gcd`
* :external+py3.14:func:`~math.isqrt`
* :external+py3.14:func:`~math.lcm`
* :external+py3.14:func:`~math.perm`

Their aliases in :external+py3.14:mod:`math` will be :term:`soft deprecated`.
This PEP doesn't introduce backward-incompatible changes.

Module functions will accept integers and objects that implement the
:external+py3.14:meth:`~object.__index__` method, which is used to convert the
object to an integer number.  Suitable functions must be computed exactly,
given sufficient time and memory.

The :pypi:`intmath` package, available on PyPI, will provide new submodule
content for older Python versions.


Possible Extensions
===================

New functions (like mentioned in `Motivation <Motivation_>`_ section) are not
part of this proposal.

Though, we should mention that, unless we can just provide bindings to some
well supported mathematical library like the GMP, the submodule scope should be
limited.  For example, no primality testing and factorization, as
production-quality implementatons will require a decent mathematical background
from contributors and belongs rather to specialized libraries.

When proposed function already exists in the :pypi:`gmpy2`, we should prefer a
compatible interface for the stdlib.


Backwards Compatibility
=======================

As aliases in :external+py3.14:mod:`math` will be kept indefinitely (their use
would be discouraged), there are no anticipated code breaks.


How to Teach This
=================

The new submodule 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
dependency on the platform floating-point format or behaviour and/or on the
platform math library (``libm``).

For users it would be natural first to look on the
:external+py3.14:class:`int`'s methods, which cover most basic use-cases (e.g.
:external+py3.14:meth:`int.bit_length` method), than to some dedicated place in
the stdlib.


Reference Implementation
========================

`python/cpython#133909 <https://github.com/python/cpython/pull/133909>`_


Rejected ideas
==============

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

There was a brief discussion about exposing :external+py3.14:func:`math.isqrt`
as ``sqrt`` in the new namespace in the same way that
:external+py3.14:func:`cmath.sqrt` is the complex version of
:external+py3.14:func:`math.sqrt`.  However, ``isqrt`` 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 submodule).


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``.

But the SC prefers a submodule rather than a new top-level module.  Most
popular variants of the :external+py3.14:mod:`math`'s submodule are:
``integer``, ``discrete`` or ``ntheory``.


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

Thanks to Victor Stinner for sponsoring this PEP.
Thanks to everyone who participated in the discussions on discuss.python.org,
providing feedback, especially to Oscar Benjamin, Steve Dower and Paul Moore.


Copyright
=========

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