From 94f2fb4d2813dd06d22ee88b3efbbd3597339866 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 29 Jan 2026 07:27:53 +0300 Subject: [PATCH 1/4] Add signatures for _mpmath_normalize/_mpmath_create --- gmp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gmp.c b/gmp.c index 657cf852..0c51db04 100644 --- a/gmp.c +++ b/gmp.c @@ -2739,9 +2739,13 @@ static PyMethodDef gmp_functions[] = { ("perm($module, n, k=None, /)\n--\n\nNumber of ways to choose k" " items from n items without repetition and with order.")}, {"_mpmath_normalize", (PyCFunction)gmp__mpmath_normalize, METH_FASTCALL, - NULL}, - {"_mpmath_create", (PyCFunction)gmp__mpmath_create, METH_FASTCALL, NULL}, - {"_free_cache", gmp__free_cache, METH_NOARGS, "Free mpz's cache."}, + ("_mpmath_normalize($module, sign, man, exp, bc, prec, rnd, /)\n--\n\n" + "Helper function for mpmath.")}, + {"_mpmath_create", (PyCFunction)gmp__mpmath_create, METH_FASTCALL, + ("_mpmath_create($module, man, exp, prec=0, rnd='d', /)\n--\n\n" + "Helper function for mpmath.")}, + {"_free_cache", gmp__free_cache, METH_NOARGS, + "_free_cache($module)\n--\n\nFree mpz's cache."}, {NULL} /* sentinel */ }; From 1031d19e615e9210360e13c2762f80c902e66cd0 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 1 Feb 2026 08:23:20 +0300 Subject: [PATCH 2/4] Drop setuptools_scm dependency Closes #302 --- meson.build | 2 +- pyproject.toml | 2 +- scripts/gitversion.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 scripts/gitversion.py diff --git a/meson.build b/meson.build index a3f18626..3f4f9c4f 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('gmp', 'c', - version: run_command('python', '-m', 'setuptools_scm', + version: run_command('python', 'scripts/gitversion.py', check: true).stdout().strip(), default_options: ['c_std=c17']) py = import('python').find_installation(pure: false) diff --git a/pyproject.toml b/pyproject.toml index 1bbf790c..3ebe6015 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] build-backend = "mesonpy" -requires = ["meson-python", "setuptools_scm[toml]>=9"] +requires = ["meson-python"] [project] name = "python-gmp" diff --git a/scripts/gitversion.py b/scripts/gitversion.py new file mode 100644 index 00000000..edafffa5 --- /dev/null +++ b/scripts/gitversion.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +import os +import re +import subprocess + + +def git_version(): + # Append last commit date and hash to dev version information, + # if available + return version, git_hash + + +if __name__ == "__main__": + try: + p = subprocess.Popen(["git", "describe"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=os.path.dirname(__file__)) + except FileNotFoundError: + exit(1) + else: + out, err = p.communicate() + if p.returncode: + exit(p.returncode) + out = out.decode("ascii").removesuffix("\n") + + version, *other = out.removesuffix("\n").split("-") + if other: + g, h = other + m = re.match("(.*)([0-9]+)", version) + version = m[1] + str(int(m[2])+1) + ".dev" + g + git_hash = h + else: + git_hash = "" + + if git_hash: + version += "+" + git_hash + print(version) + exit(0) From cb155025f1bae83db13c61c01ca08746c9b8774f Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 1 Feb 2026 16:14:00 +0300 Subject: [PATCH 3/4] Simplify README.rst --- README.rst | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/README.rst b/README.rst index e251f98f..edb9dabc 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,13 @@ Python-GMP ========== -Python extension module, gmp, providing safe bindings to the GNU GMP (version -6.3.0 or later required) via the `ZZ library `_. -This module shouldn't crash the interpreter. +Python extension module, providing bindings to the GNU GMP via the `ZZ library +`_. This module shouldn't crash the interpreter. The gmp can be used as a `gmpy2`_/`python-flint`_ replacement to provide -integer type (`mpz`_), compatible with Python's `int`_. It includes few -functions (`comb`_, `factorial`_, `gcd`_, `isqrt`_, `lcm`_ and `perm`_), -compatible with the Python stdlib's module `math`_. +integer type (`mpz`_), compatible with Python's `int`_. It also includes +functions, compatible with the Python stdlib's submodule `math.integer +`_. This module requires Python 3.11 or later versions and has been tested with CPython 3.11 through 3.14, with PyPy3.11 7.3.20 and with GraalPy 25.0. @@ -22,17 +21,16 @@ Motivation ---------- The CPython (and most other Python implementations, like PyPy) is optimized to -work with small integers. Algorithms used here for "big enough" integers -usually aren't best known in the field. Fortunately, it's possible to use -bindings (for example, the `gmpy2`_ package) to the GNU Multiple Precision -Arithmetic Library (GMP), which aims to be faster than any other bignum library -for all operand sizes. - -But such extension modules usually rely on default GMP's memory allocation -functions and can't recover from errors such as out of memory. So, it's easy -to crash the Python interpreter during the interactive session. Following -example with the gmpy2 will work if you set address space limit for the Python -interpreter (e.g. by ``prlimit`` command on Linux): +work with small (machine-sized) integers. Algorithms used here for big +integers usually aren't best known in the field. Fortunately, it's possible to +use bindings (for example, the `gmpy2`_ package) to the GNU GMP, which aims to +be faster than any other bignum library for all operand sizes. + +But such extension modules usually rely on default GMP's memory management and +can't recover from allocation failure. So, it's easy to crash the Python +interpreter during the interactive session. Following example with the gmpy2 +will work if you set address space limit for the Python interpreter (e.g. by +``prlimit`` command on Linux): .. code:: pycon @@ -70,19 +68,11 @@ Warning on --disable-alloca configure option -------------------------------------------- You should use the GNU GMP library, compiled with the '--disable-alloca' -configure option to prevent using alloca() for temporary workspace allocation -(and use the heap instead), or this module can't prevent a crash in case of a -stack overflow. +configure option to prevent using alloca() for temporary workspace allocation, +or this module may crash the interpreter in case of a stack overflow. .. _gmpy2: https://pypi.org/project/gmpy2/ .. _python-flint: https://pypi.org/project/python-flint/ .. _mpz: https://python-gmp.readthedocs.io/en/latest/#gmp.mpz .. _int: https://docs.python.org/3/library/functions.html#int -.. _factorial: https://python-gmp.readthedocs.io/en/latest/#gmp.factorial -.. _gcd: https://python-gmp.readthedocs.io/en/latest/#gmp.gcd -.. _isqrt: https://python-gmp.readthedocs.io/en/latest/#gmp.isqrt -.. _lcm: https://python-gmp.readthedocs.io/en/latest/#gmp.lcm -.. _comb: https://python-gmp.readthedocs.io/en/latest/#gmp.comb -.. _perm: https://python-gmp.readthedocs.io/en/latest/#gmp.perm -.. _math: https://docs.python.org/3/library/math.html#number-theoretic-functions From 6b5110bad91a262f9ffb7246a304a6bc66706c21 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 2 Feb 2026 04:13:10 +0300 Subject: [PATCH 4/4] Add few benchmarks Closes #36 --- bench/README.rst | 11 +++++++++++ bench/collatz.py | 42 ++++++++++++++++++++++++++++++++++++++++++ bench/mul.py | 24 ++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 bench/README.rst create mode 100644 bench/collatz.py create mode 100644 bench/mul.py diff --git a/bench/README.rst b/bench/README.rst new file mode 100644 index 00000000..474d3da7 --- /dev/null +++ b/bench/README.rst @@ -0,0 +1,11 @@ +This directory holds some basic benchmarks for the gmp extension. + +It's possible to run them also with gmpy2's and flint's integer types: + +.. code:: sh + + ( export T="gmpy2.mpz"; \ + python bench/mul.py -q --copy-env --rigorous -o $T.json ) + +Beware, that the gmp prefers clang over gcc and extensions might +use different compiler options per default. diff --git a/bench/collatz.py b/bench/collatz.py new file mode 100644 index 00000000..19826554 --- /dev/null +++ b/bench/collatz.py @@ -0,0 +1,42 @@ +# collatz.py + +import os + +import pyperf + +if os.getenv("T") == "gmpy2.mpz": + from gmpy2 import mpz +elif os.getenv("T") == "flint.fmpz": + from flint import fmpz as mpz +else: + from gmp import mpz + +zero = mpz(0) +one = mpz(1) +two = mpz(2) +three = mpz(3) + +# https://en.wikipedia.org/wiki/Collatz_conjecture + +def collatz0(n): + total = 0 + n = mpz(n) + while n > one: + n = n*three + one if n & one else n//two + total += 1 + return total + +def collatz1(n): + total = 0 + n = mpz(n) + while n > 1: + n = n*3 + 1 if n & 1 else n//2 + total += 1 + return total + +runner = pyperf.Runner() +for f in [collatz0, collatz1]: + for v in ["97", "871", "(1<<128)+31"]: + h = f"{f.__name__}({v})" + i = eval(v) + runner.bench_func(h, f, i) diff --git a/bench/mul.py b/bench/mul.py new file mode 100644 index 00000000..65ff8973 --- /dev/null +++ b/bench/mul.py @@ -0,0 +1,24 @@ +# mul.py + +import os +from operator import mul + +import pyperf + +if os.getenv("T") == "gmpy2.mpz": + from gmpy2 import mpz +elif os.getenv("T") == "flint.fmpz": + from flint import fmpz as mpz +else: + from gmp import mpz + +values = ["1<<7", "1<<38", "1<<300", "1<<3000"] + +runner = pyperf.Runner() +for v in values: + i = eval(v) + bn = '("'+v+'")**2' + x = mpz(i) + # make y != x to avoid a quick mpn_sqr() path on gmp/flint + y = mpz(i + 1) + runner.bench_func(bn, mul, x, y)