diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e5cbc908..202e2405 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -5,11 +5,11 @@ jobs: timeout-minutes: 20 runs-on: ${{ matrix.os }} env: - CFLAGS: -Wall -Werror -std=c17 -Wconversion + CFLAGS: -Wall -Werror -Wconversion strategy: matrix: os: [ubuntu-26.04] - python-version: [pypy3.12, "graalpy-25", "3.x"] + python-version: [pypy3.12, "graalpy-25", "3.x", 3.16] steps: - uses: actions/checkout@v7 with: @@ -18,6 +18,7 @@ jobs: with: python-version: ${{ matrix.python-version }} allow-prereleases: true + cache: 'pip' - run: sudo apt-get update - run: sudo apt-get install autoconf-archive lcov - name: Setup locales diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 15a85611..8d3d86b4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-yaml - id: check-toml @@ -10,13 +10,13 @@ repos: args: [--pytest-test-first] exclude: ^tests/utils.py$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.3 + rev: v0.17.4 hooks: - id: ruff name: Run ruff args: [--exit-non-zero-on-fix] - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v1.0.0 + rev: v1.0.2 hooks: - id: sphinx-lint name: Run sphinx-lint diff --git a/README.rst b/README.rst index 8e3a59c4..bbab3374 100644 --- a/README.rst +++ b/README.rst @@ -10,9 +10,9 @@ 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.15, with PyPy3.12 8.0.0 and with GraalPy 25.3.4.1. -Free-threading builds of the CPython are supported. +This module requires Python 3.12 or later versions and has been tested with +CPython 3.12 through 3.16, with PyPy3.12 and with GraalPy3.13. Free-threading +builds of the CPython are supported. Releases are available in the Python Package Index (PyPI) at https://pypi.org/project/python-gmp/. @@ -36,8 +36,6 @@ will work if you set address space limit for the Python interpreter (e.g. by .. code:: pycon >>> import gmpy2 - >>> gmpy2.__version__ - '2.2.1' >>> z = gmpy2.mpz(29925959575501) >>> while True: # this loop will crash interpter ... z = z*z diff --git a/extra.c b/extra.c index 2afb33b5..7cab482f 100644 --- a/extra.c +++ b/extra.c @@ -8,8 +8,7 @@ static const zz_layout bytes_layout = {8, 1, 1, 0}; zz_err -zz_get_bytes(const zz_t *u, size_t length, bool is_signed, - unsigned char **buffer) +zz_get_bytes(const zz_t *u, size_t len, bool is_signed, unsigned char **buf) { zz_t tmp; bool is_negative = zz_isneg(u); @@ -21,7 +20,7 @@ zz_get_bytes(const zz_t *u, size_t length, bool is_signed, if (!is_signed) { return ZZ_BUF; } - if (zz_set(1, &tmp) || zz_mul_2exp(&tmp, 8*length, &tmp) + if (zz_set(1, &tmp) || zz_mul_2exp(&tmp, 8*len, &tmp) || zz_add(&tmp, u, &tmp)) { /* LCOV_EXCL_START */ @@ -34,43 +33,42 @@ zz_get_bytes(const zz_t *u, size_t length, bool is_signed, size_t nbits = zz_bitlen(u); - if (zz_isneg(u) || nbits > 8*length + if (zz_isneg(u) || nbits > 8*len || (is_signed && ((!nbits && is_negative) - || (nbits && (nbits == 8 * length ? !is_negative : is_negative))))) + || (nbits && (nbits == 8 * len ? !is_negative : is_negative))))) { zz_clear(&tmp); return ZZ_BUF; } - size_t gap = length - (nbits + bits_per_digit/8 - 1)/(bits_per_digit/8); + size_t gap = len - (nbits + bits_per_digit/8 - 1)/(bits_per_digit/8); - (void)zz_export(u, bytes_layout, length - gap, *buffer + gap); - memset(*buffer, is_negative ? 0xFF : 0, gap); + (void)zz_export(u, bytes_layout, len - gap, *buf + gap); + memset(*buf, is_negative ? 0xFF : 0, gap); zz_clear(&tmp); return ZZ_OK; } zz_err -zz_set_bytes(const unsigned char *buffer, size_t length, bool is_signed, - zz_t *u) +zz_set_bytes(const unsigned char *buf, size_t len, bool is_signed, zz_t *u) { - if (!length) { + if (!len) { return zz_set(0, u); } - zz_err ret = zz_import(length, buffer, bytes_layout, u); + zz_err ret = zz_import(len, buf, bytes_layout, u); if (ret) { return ret; /* LCOV_EXCL_LINE */ } (void)zz_abs(u, u); - if (is_signed && zz_bitlen(u) == 8*(size_t)length) { + if (is_signed && zz_bitlen(u) == 8*(size_t)len) { zz_t tmp; /* we assume, that bytes were created by zz_get_bytes(), else there could be overflow in zz_mul_2exp() */ if (zz_init(&tmp) || zz_set(1, &tmp) - || zz_mul_2exp(&tmp, 8*length, &tmp) + || zz_mul_2exp(&tmp, 8*len, &tmp) || zz_sub(&tmp, u, u) || zz_neg(u, u)) { /* LCOV_EXCL_START */ @@ -150,13 +148,13 @@ zz_divnear(const zz_t *u, const zz_t *v, zz_t *q, zz_t *r) } zz_err -zz_truediv(const zz_t *u, const zz_t *v, double *res) +zz_truediv(const zz_t *u, const zz_t *v, double *w) { if (zz_iszero(v)) { return ZZ_VAL; } if (zz_iszero(u)) { - *res = zz_isneg(v) ? -0.0 : 0.0; + *w = zz_isneg(v) ? -0.0 : 0.0; return ZZ_OK; } @@ -167,7 +165,7 @@ zz_truediv(const zz_t *u, const zz_t *v, double *res) return ZZ_BUF; } if (ubits < vbits && vbits - ubits > -DBL_MIN_EXP + DBL_MANT_DIG + 1) { - *res = zz_isneg(u) != zz_isneg(v) ? -0.0 : 0.0; + *w = zz_isneg(u) != zz_isneg(v) ? -0.0 : 0.0; return ZZ_OK; } @@ -241,13 +239,13 @@ zz_truediv(const zz_t *u, const zz_t *v, double *res) /* LCOV_EXCL_STOP */ } zz_clear(&b); - (void)zz_get(&a, res); + (void)zz_get(&a, w); zz_clear(&a); - *res = ldexp(*res, -shift); + *w = ldexp(*w, -shift); if (zz_isneg(u) != zz_isneg(v)) { - *res = -*res; + *w = -*w; } - if (isinf(*res)) { + if (isinf(*w)) { return ZZ_BUF; } return ZZ_OK; @@ -285,7 +283,7 @@ zz_rshift(const zz_t *u, const zz_t *v, zz_t *w) zz_err zz_mpmath_normalize(zz_bitcnt_t prec, zz_rnd rnd, bool *negative, - zz_t *man, zz_t *exp, zz_bitcnt_t *bc) + zz_t *man, zz_t *exp, zz_bitcnt_t *bc) { /* If the mantissa is 0, return the normalized representation. */ if (zz_iszero(man)) { diff --git a/extra.h b/extra.h index cbd90457..eba1b89e 100644 --- a/extra.h +++ b/extra.h @@ -5,17 +5,17 @@ extern uint8_t bits_per_digit; -zz_err zz_get_bytes(const zz_t *u, size_t length, bool is_signed, - unsigned char **buffer); -zz_err zz_set_bytes(const unsigned char *buffer, size_t length, +zz_err zz_get_bytes(const zz_t *u, size_t len, bool is_signed, + unsigned char **buf); +zz_err zz_set_bytes(const unsigned char *buf, size_t len, bool is_signed, zz_t *u); #define zz_quo(u, v, w) zz_div((u), (v), (w), NULL) #define zz_rem(u, v, w) zz_div((u), (v), NULL, (w)) zz_err zz_divnear(const zz_t *u, const zz_t *v, zz_t *q, zz_t *r); -zz_err zz_truediv(const zz_t *u, const zz_t *v, double *res); +zz_err zz_truediv(const zz_t *u, const zz_t *v, double *w); zz_err zz_lshift(const zz_t *u, const zz_t *v, zz_t *w); zz_err zz_rshift(const zz_t *u, const zz_t *v, zz_t *w); diff --git a/gmp.c b/gmp.c index 19e409e7..57b24bbb 100644 --- a/gmp.c +++ b/gmp.c @@ -1,10 +1,21 @@ -#include "mpz.h" +#include "utils.h" #include "extra.h" #ifdef Py_GIL_DISABLED # include #endif +typedef struct { + PyObject_HEAD + Py_hash_t hash; + zz_t z; +} MPZ_Object; + +extern PyTypeObject MPZ_Type; + +#define MPZ_CheckExact(u) Py_IS_TYPE((u), &MPZ_Type) +#define MPZ_Check(u) PyObject_TypeCheck((u), &MPZ_Type) + #ifdef ON_CPYTHON # define MAX_FREELIST_SIZE 100 # define MAX_FREELIST_SIZEOF 256 @@ -271,20 +282,20 @@ revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r) } Py_LOCAL(PyObject *) -MPZ_to_bytes(MPZ_Object *u, Py_ssize_t length, int is_little, int is_signed) +MPZ_to_bytes(MPZ_Object *u, Py_ssize_t len, int is_little, int is_signed) { - PyObject *bytes = PyBytes_FromStringAndSize(NULL, length); + PyObject *bytes = PyBytes_FromStringAndSize(NULL, len); if (!bytes) { return NULL; /* LCOV_EXCL_LINE */ } - unsigned char *buffer = (unsigned char *)PyBytes_AsString(bytes); - zz_err ret = zz_get_bytes(&u->z, (size_t)length, is_signed, &buffer); + unsigned char *buf = (unsigned char *)PyBytes_AsString(bytes); + zz_err ret = zz_get_bytes(&u->z, (size_t)len, is_signed, &buf); if (ret == ZZ_OK) { - if (is_little && length) { - revstr(buffer, 0, length - 1); + if (is_little && len) { + revstr(buf, 0, len - 1); } return bytes; } @@ -296,7 +307,7 @@ MPZ_to_bytes(MPZ_Object *u, Py_ssize_t length, int is_little, int is_signed) else { #if (PY_VERSION_HEX < 0x030D08F0 || (PY_VERSION_HEX >= 0x030E0000 \ && PY_VERSION_HEX < 0x030E00C3)) - if (!length && zz_cmp(&u->z, -1) == ZZ_EQ) { + if (!len && zz_cmp(&u->z, -1) == ZZ_EQ) { return bytes; } #endif @@ -315,20 +326,20 @@ Py_LOCAL(MPZ_Object *) MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) { PyObject *bytes = PyObject_Bytes(obj); - unsigned char *buffer; - Py_ssize_t length; + unsigned char *buf; + Py_ssize_t len; if (bytes == NULL) { return NULL; } - if (PyBytes_AsStringAndSize(bytes, (char **)&buffer, &length) == -1) { + if (PyBytes_AsStringAndSize(bytes, (char **)&buf, &len) == -1) { return NULL; /* LCOV_EXCL_LINE */ } - if (!length) { + if (!len) { is_little = 0; } if (is_little) { - unsigned char *tmp = malloc((size_t)length); + unsigned char *tmp = malloc((size_t)len); if (!tmp) { /* LCOV_EXCL_START */ @@ -336,9 +347,9 @@ MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_STOP */ } - memcpy(tmp, buffer, (size_t)length); - revstr(tmp, 0, length - 1); - buffer = tmp; + memcpy(tmp, buf, (size_t)len); + revstr(tmp, 0, len - 1); + buf = tmp; } MPZ_Object *res = MPZ_new(); @@ -347,17 +358,17 @@ MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) /* LCOV_EXCL_START */ Py_DECREF(bytes); if (is_little) { - free(buffer); + free(buf); } return NULL; /* LCOV_EXCL_STOP */ } - zz_err ret = zz_set_bytes(buffer, (size_t)length, is_signed, &res->z); + zz_err ret = zz_set_bytes(buf, (size_t)len, is_signed, &res->z); Py_DECREF(bytes); if (is_little) { - free(buffer); + free(buf); } if (ret) { /* LCOV_EXCL_START */ @@ -374,7 +385,7 @@ MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) } Py_LOCAL(PyObject *) -new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) +new_impl(PyObject *arg, PyObject *base_arg) { int base = 10; @@ -510,14 +521,14 @@ new(PyTypeObject *type, PyObject *args, PyObject *keywds) } if (argc == 1 && !keywds) { arg = PyTuple_GetItem(args, 0); - return new_impl(type, arg, Py_None); + return new_impl(arg, Py_None); } if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|O", kwlist, &arg, &base)) { return NULL; } - return new_impl(type, arg, base); + return new_impl(arg, base); } Py_LOCAL(void) @@ -550,7 +561,7 @@ dealloc(PyObject *self) } Py_LOCAL(PyObject *) -vectorcall(PyObject *type, PyObject *const *args, size_t nargsf, +vectorcall(PyObject *Py_UNUSED(type), PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); @@ -568,11 +579,10 @@ vectorcall(PyObject *type, PyObject *const *args, size_t nargsf, return NULL; } if (argidx[1] >= 0) { - return new_impl((PyTypeObject *)type, args[argidx[0]], - args[argidx[1]]); + return new_impl(args[argidx[0]], args[argidx[1]]); } else if (argidx[0] >= 0) { - return new_impl((PyTypeObject *)type, args[argidx[0]], Py_None); + return new_impl(args[argidx[0]], Py_None); } else { return (PyObject *)MPZ_new(); @@ -591,25 +601,6 @@ str(PyObject *self) return MPZ_to_str((MPZ_Object *)self, 10, false); } -#define Number_Check(op) (PyFloat_Check((op)) || PyComplex_Check((op))) - -#define CHECK_OP(u, a) \ - if (MPZ_Check(a)) { \ - u = (MPZ_Object *)Py_NewRef(a); \ - } \ - else if (PyLong_Check(a)) { \ - u = MPZ_from_int(a); \ - if (!u) { \ - goto end; \ - } \ - } \ - else if (Number_Check(a)) { \ - goto numbers; \ - } \ - else { \ - goto fallback; \ - } - Py_LOCAL(PyObject *) to_float(PyObject *self) { @@ -626,7 +617,7 @@ to_float(PyObject *self) } Py_LOCAL_INLINE(int64_t) -PyLong_AsSdigit_t(PyObject *obj, int *error) +PyLong_AsInt64AndOverflow(PyObject *obj, int *error) { #if SIZEOF_LONG == 8 long value = PyLong_AsLongAndOverflow(obj, error); @@ -640,37 +631,60 @@ PyLong_AsSdigit_t(PyObject *obj, int *error) return (int64_t)value; } +#define Number_Check(op) (PyFloat_Check((op)) || PyComplex_Check((op))) + +#define CHECK_OP(u, a) \ + if (MPZ_Check(a)) { \ + u = (MPZ_Object *)Py_NewRef(a); \ + } \ + else if (PyLong_Check(a)) { \ + u = MPZ_from_int(a); \ + if (!u) { \ + goto end; \ + } \ + } \ + else if (Number_Check(a)) { \ + goto numbers; \ + } \ + else { \ + goto fallback; \ + } + +#define CHECK_OPv2(u, a) \ + if (MPZ_Check(a)) { \ + u = (MPZ_Object *)Py_NewRef(a); \ + } \ + else if (PyLong_Check(a)) { \ + int error; \ + \ + ival = PyLong_AsInt64AndOverflow(a, &error); \ + if (error) { \ + u = MPZ_from_int(a); \ + if (!u) { \ + goto end; \ + } \ + } \ + } \ + else if (Number_Check(a)) { \ + goto numbers; \ + } \ + else { \ + goto fallback; \ + } + Py_LOCAL(PyObject *) richcompare(PyObject *self, PyObject *other, int op) { - MPZ_Object *u = (MPZ_Object *)self; + MPZ_Object *u = (MPZ_Object *)self, *v = NULL; zz_ord r; + int64_t ival = 0; - if (MPZ_Check(other)) { - r = zz_cmp(&u->z, &((MPZ_Object *)other)->z); - } - else if (PyLong_Check(other)) { - int error; - int64_t temp = PyLong_AsSdigit_t(other, &error); - - if (!error) { - r = zz_cmp(&u->z, temp); - } - else { - MPZ_Object *v = MPZ_from_int(other); - - if (!v) { - goto end; /* LCOV_EXCL_LINE */ - } - r = zz_cmp(&u->z, &v->z); - Py_DECREF(v); - } - } - else if (Number_Check(other)) { - goto numbers; + CHECK_OPv2(v, other); + if (v) { + r = zz_cmp(&u->z, &v->z); } else { - goto fallback; + r = zz_cmp(&u->z, ival); } switch (op) { case Py_LT: @@ -787,28 +801,6 @@ to_bool(PyObject *self) return !zz_iszero(&((MPZ_Object *)self)->z); } -#define CHECK_OPv2(u, a) \ - if (MPZ_Check(a)) { \ - u = (MPZ_Object *)Py_NewRef(a); \ - } \ - else if (PyLong_Check(a)) { \ - int error; \ - \ - ival = PyLong_AsSdigit_t(a, &error); \ - if (error) { \ - u = MPZ_from_int(a); \ - if (!u) { \ - goto end; \ - } \ - } \ - } \ - else if (Number_Check(a)) { \ - goto numbers; \ - } \ - else { \ - goto fallback; \ - } - #define BINOP(suff, slot) \ Py_LOCAL(PyObject *) \ nb_##suff(PyObject *self, PyObject *other) \ @@ -818,7 +810,6 @@ to_bool(PyObject *self) \ CHECK_OPv2(u, self); \ CHECK_OPv2(v, other); \ - \ res = MPZ_new(); \ if (!res) { \ goto end; \ @@ -1033,8 +1024,8 @@ nb_truediv(PyObject *self, PyObject *other) \ CHECK_OP_INT(u, self); \ CHECK_OP_INT(v, other); \ - \ res = MPZ_new(); \ + \ zz_err ret = ZZ_OK; \ \ if (res && (ret = zz_##suff(&u->z, &v->z, &res->z))) { \ @@ -1066,14 +1057,14 @@ BINOP_INT(lshift) BINOP_INT(rshift) Py_LOCAL(PyObject *) -power(PyObject *self, PyObject *other, PyObject *module) +power(PyObject *self, PyObject *other, PyObject *modulus) { MPZ_Object *res = NULL; MPZ_Object *u = NULL, *v = NULL; CHECK_OP(u, self); CHECK_OP(v, other); - if (Py_IsNone(module)) { + if (Py_IsNone(modulus)) { if (zz_isneg(&v->z)) { PyObject *uf, *vf, *resf; @@ -1128,7 +1119,7 @@ power(PyObject *self, PyObject *other, PyObject *module) else { MPZ_Object *w = NULL; - CHECK_OP_INT(w, module); + CHECK_OP_INT(w, modulus); zz_err ret = ZZ_OK; @@ -1213,13 +1204,13 @@ static PyNumberMethods as_number = { }; Py_LOCAL_INLINE(PyObject *) -get_copy(PyObject *self, void *Py_UNUSED(closure)) +get_copy(PyObject *self, void *Py_UNUSED(args)) { return Py_NewRef(self); } Py_LOCAL(PyObject *) -get_one(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) +get_one(PyObject *Py_UNUSED(self), void *Py_UNUSED(args)) { MPZ_Object *res = MPZ_new(); @@ -1230,7 +1221,7 @@ get_one(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) } Py_LOCAL_INLINE(PyObject *) -get_zero(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) +get_zero(PyObject *Py_UNUSED(self), void *Py_UNUSED(args)) { return (PyObject *)MPZ_new(); } @@ -1337,9 +1328,9 @@ to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, } Py_LOCAL(PyObject *) -_from_bytes(PyObject *Py_UNUSED(type), PyObject *arg) +_from_bytes(PyObject *Py_UNUSED(type), PyObject *bytes) { - return (PyObject *)MPZ_from_bytes(arg, 0, 1); + return (PyObject *)MPZ_from_bytes(bytes, 0, 1); } Py_LOCAL(PyObject *) @@ -1502,7 +1493,7 @@ __round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } Py_LOCAL(PyObject *) -__reduce_ex__(PyObject *self, PyObject *Py_UNUSED(args)) +__reduce_ex__(PyObject *self, PyObject *Py_UNUSED(protocol)) { MPZ_Object *u = (MPZ_Object *)self; zz_bitcnt_t len = zz_bitlen(&u->z); @@ -1513,7 +1504,7 @@ __reduce_ex__(PyObject *self, PyObject *Py_UNUSED(args)) } Py_LOCAL(PyObject *) -__sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +__sizeof__(PyObject *self, PyObject *Py_UNUSED(args)) { MPZ_Object *u = (MPZ_Object *)self; @@ -2082,12 +2073,12 @@ The signed argument indicates whether two’s complement is used."}, ("digits($self, base=10)\n--\n\n" "Return string representing self in the given base.\n\n" "Values for base can range between 2 to 36.")}, - {"_from_bytes", _from_bytes, METH_O | METH_CLASS, NULL}, + {"_from_bytes", _from_bytes, METH_O | METH_CLASS, + "_from_bytes($type, bytes, /)\n--\n\nReconstruct an integer from bytes."}, {NULL} /* sentinel */ }; -PyDoc_STRVAR(mpz_doc, - "mpz(number=0, /)\nmpz(string, /, base=10)\n\n\ +PyDoc_STRVAR(mpz_doc, "mpz(number=0, /)\nmpz(string, /, base=10)\n\n\ Convert a number or a string to an integer. If numeric argument is not\n\ an int subclass, return same as mpz(int(number)).\n\n\ If argument is not a number or if base is given, then it must be a string,\n\ @@ -2146,14 +2137,13 @@ gmp_gcd(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) } Py_LOCAL(PyObject *) -gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, - Py_ssize_t nargs) +gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) { if (nargs != 2) { PyErr_SetString(PyExc_TypeError, "gcdext() expects two arguments"); return NULL; } - MPZ_Object *x = NULL, *y = NULL; + MPZ_Object *u = NULL, *v = NULL; MPZ_Object *g = MPZ_new(), *s = MPZ_new(), *t = MPZ_new(); if (!g || !s || !t) { @@ -2164,13 +2154,13 @@ gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, return PyErr_NoMemory(); /* LCOV_EXCL_STOP */ } - CHECK_OP_INT(x, args[0]); - CHECK_OP_INT(y, args[1]); + CHECK_OP_INT(u, args[0]); + CHECK_OP_INT(v, args[1]); - zz_err ret = zz_gcdext(&x->z, &y->z, &g->z, &s->z, &t->z); + zz_err ret = zz_gcdext(&u->z, &v->z, &g->z, &s->z, &t->z); - Py_DECREF(x); - Py_DECREF(y); + Py_DECREF(u); + Py_DECREF(v); if (ret == ZZ_MEM) { return PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } @@ -2184,8 +2174,8 @@ gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, Py_DECREF(g); Py_DECREF(s); Py_DECREF(t); - Py_XDECREF((PyObject *)x); - Py_XDECREF((PyObject *)y); + Py_XDECREF((PyObject *)u); + Py_XDECREF((PyObject *)v); return NULL; } @@ -2229,18 +2219,18 @@ gmp_lcm(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) } Py_LOCAL(PyObject *) -gmp_isqrt(PyObject *Py_UNUSED(module), PyObject *arg) +gmp_isqrt(PyObject *Py_UNUSED(module), PyObject *n) { - MPZ_Object *x, *root = MPZ_new(); + MPZ_Object *u, *root = MPZ_new(); if (!root) { return NULL; /* LCOV_EXCL_LINE */ } - CHECK_OP_INT(x, arg); + CHECK_OP_INT(u, n); - zz_err ret = zz_sqrtrem(&x->z, &root->z, NULL); + zz_err ret = zz_sqrtrem(&u->z, &root->z, NULL); - Py_DECREF(x); + Py_DECREF(u); if (ret == ZZ_OK) { return (PyObject *)root; } @@ -2257,9 +2247,9 @@ gmp_isqrt(PyObject *Py_UNUSED(module), PyObject *arg) } Py_LOCAL(PyObject *) -gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *arg) +gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *n) { - MPZ_Object *x, *root = MPZ_new(), *rem = MPZ_new(); + MPZ_Object *u, *root = MPZ_new(), *rem = MPZ_new(); PyObject *tup = NULL; if (!root || !rem) { @@ -2269,11 +2259,11 @@ gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *arg) return NULL; /* LCOV_EXCL_STOP */ } - CHECK_OP_INT(x, arg); + CHECK_OP_INT(u, n); - zz_err ret = zz_sqrtrem(&x->z, &root->z, &rem->z); + zz_err ret = zz_sqrtrem(&u->z, &root->z, &rem->z); - Py_DECREF(x); + Py_DECREF(u); if (ret == ZZ_OK) { tup = PyTuple_Pack(2, root, rem); } @@ -2291,32 +2281,32 @@ gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *arg) } Py_LOCAL(PyObject *) -gmp_fac(PyObject *Py_UNUSED(module), PyObject *arg) +gmp_fac(PyObject *Py_UNUSED(module), PyObject *n) { - MPZ_Object *x, *res = MPZ_new(); + MPZ_Object *u, *res = MPZ_new(); if (!res) { return NULL; /* LCOV_EXCL_LINE */ } - CHECK_OP_INT(x, arg); - if (zz_isneg(&x->z)) { + CHECK_OP_INT(u, n); + if (zz_isneg(&u->z)) { PyErr_SetString(PyExc_ValueError, "fac() not defined for negative values"); goto err; } - uint64_t n; + uint64_t x; - if (zz_get(&x->z, &n)) { + if (zz_get(&u->z, &x)) { overflow: PyErr_Format(PyExc_OverflowError, "fac() argument should not exceed %ld", ULONG_MAX); goto err; } - Py_DECREF((PyObject *)x); + Py_DECREF((PyObject *)u); - zz_err ret = zz_fac((zz_digit_t)n, &res->z); + zz_err ret = zz_fac((zz_digit_t)x, &res->z); if (ret) { /* LCOV_EXCL_START */ @@ -2342,14 +2332,14 @@ gmp_comb(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) return NULL; } - MPZ_Object *x, *y, *res = MPZ_new(); + MPZ_Object *u, *v, *res = MPZ_new(); if (!res) { return NULL; /* LCOV_EXCL_LINE */ } - CHECK_OP_INT(x, args[0]); - CHECK_OP_INT(y, args[1]); - if (zz_isneg(&x->z) || zz_isneg(&y->z)) { + CHECK_OP_INT(u, args[0]); + CHECK_OP_INT(v, args[1]); + if (zz_isneg(&u->z) || zz_isneg(&v->z)) { PyErr_SetString(PyExc_ValueError, "comb() not defined for negative values"); goto err; @@ -2357,15 +2347,15 @@ gmp_comb(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) uint64_t n, k; - if (zz_get(&x->z, &n) || zz_get(&y->z, &k)) { + if (zz_get(&u->z, &n) || zz_get(&v->z, &k)) { overflow: PyErr_Format(PyExc_OverflowError, "comb() arguments should not exceed %ld", ULONG_MAX); goto err; } - Py_DECREF((PyObject *)x); - Py_DECREF((PyObject *)y); + Py_DECREF((PyObject *)u); + Py_DECREF((PyObject *)v); zz_err ret = zz_bin(n, k, &res->z); @@ -2396,14 +2386,14 @@ gmp_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return gmp_fac(module, args[0]); } - MPZ_Object *x, *y, *res = MPZ_new(); + MPZ_Object *u, *v, *res = MPZ_new(); if (!res) { return NULL; /* LCOV_EXCL_LINE */ } - CHECK_OP_INT(x, args[0]); - CHECK_OP_INT(y, args[1]); - if (zz_isneg(&x->z) || zz_isneg(&y->z)) { + CHECK_OP_INT(u, args[0]); + CHECK_OP_INT(v, args[1]); + if (zz_isneg(&u->z) || zz_isneg(&v->z)) { PyErr_SetString(PyExc_ValueError, "perm() not defined for negative values"); goto err; @@ -2411,15 +2401,15 @@ gmp_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) uint64_t n, k; - if (zz_get(&x->z, &n) || zz_get(&y->z, &k)) { + if (zz_get(&u->z, &n) || zz_get(&v->z, &k)) { overflow: PyErr_Format(PyExc_OverflowError, "perm() arguments should not exceed %ld", ULONG_MAX); goto err; } - Py_DECREF((PyObject *)x); - Py_DECREF((PyObject *)y); + Py_DECREF((PyObject *)u); + Py_DECREF((PyObject *)v); if (k > n) { return (PyObject *)res; } @@ -2435,7 +2425,7 @@ gmp_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) zz_err ret = zz_fac((zz_digit_t)n, &res->z); - if (ret || zz_fac((zz_digit_t)(n-k), &den->z) + if (ret || zz_fac((zz_digit_t)(n - k), &den->z) || zz_quo(&res->z, &den->z, &res->z)) { /* LCOV_EXCL_START */ @@ -2644,7 +2634,7 @@ static PyMethodDef gmp_functions[] = { ("gcd($module, /, *integers)\n--\n\n" "Greatest Common Divisor.")}, {"gcdext", (PyCFunction)gmp_gcdext, METH_FASTCALL, - ("gcdext($module, x, y, /)\n--\n\n" + ("gcdext($module, n, k, /)\n--\n\n" "Compute extended GCD.")}, {"lcm", (PyCFunction)gmp_lcm, METH_FASTCALL, ("lcm($module, /, *integers)\n--\n\n" @@ -2807,7 +2797,8 @@ static struct PyModuleDef gmp_module = { PyModuleDef_HEAD_INIT, .m_name = "gmp", .m_doc = "Bindings to the GNU GMP for Python, providing int-compatible\n\ -integer type and functions as in the math.integer submodule.", +integer type and functions as in the math.integer submodule.\n\n\ +This module has no integer string conversion length limitation.", .m_size = 0, .m_methods = gmp_functions, .m_slots = gmp_slots, diff --git a/mpz.h b/mpz.h deleted file mode 100644 index 91714ce6..00000000 --- a/mpz.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MPZ_H -#define MPZ_H - -#include "utils.h" -#include "zz/zz.h" - -typedef struct { - PyObject_HEAD - Py_hash_t hash; - zz_t z; -} MPZ_Object; - -extern PyTypeObject MPZ_Type; - -#define MPZ_CheckExact(u) Py_IS_TYPE((u), &MPZ_Type) -#define MPZ_Check(u) PyObject_TypeCheck((u), &MPZ_Type) - -#endif /* MPZ_H */ diff --git a/pyproject.toml b/pyproject.toml index 093d8d09..611815f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ requires = ["meson-python"] name = "python-gmp" dynamic = ["version"] description = "Safe bindings to the GNU GMP library" -keywords = ["gmp", "multiple-precision", "arbitrary-precision", "bignum"] +keywords = ["gmp", "multiple-precision", "arbitrary-precision", "bigint"] license = "MIT" license-files = ["LICENSE"] readme = "README.rst" @@ -20,11 +20,11 @@ classifiers = ["Development Status :: 5 - Production/Stable", "Programming Language :: C", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.15", + "Programming Language :: Python :: 3.16", "Programming Language :: Python :: Free Threading :: 3 - Stable", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", @@ -35,7 +35,7 @@ classifiers = ["Development Status :: 5 - Production/Stable", "Operating System :: MacOS", "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Software Development :: Libraries :: Python Modules"] -requires-python = ">= 3.11" +requires-python = ">= 3.12" [project.urls] Homepage = "https://github.com/diofant/python-gmp" @@ -44,10 +44,9 @@ Homepage = "https://github.com/diofant/python-gmp" Documentation = "https://python-gmp.readthedocs.io/en/latest/" [project.optional-dependencies] -tests = ["pytest", "hypothesis; implementation_name != 'pypy'", - "hypothesis<=6.150.0; implementation_name == 'pypy'"] +tests = ["pytest", "hypothesis"] ci = ["python-gmp[tests]", "pytest-xdist"] -develop = ["python-gmp[tests]", "prek", "pyperf"] +develop = ["python-gmp[tests]", "prek", "pyperf", "sphinx"] [tool.ruff] line-length = 79 @@ -58,7 +57,7 @@ select = ["E", "F", "I", "PT", "W", "Q", "SIM", "B", "UP", "RUF"] [tool.cibuildwheel] build-frontend = {name="build", args=["--verbose", "-Csetup-args=--vsenv"]} enable = "pypy cpython-prerelease graalpy" -skip = "gp3*win*amd64* gp312_250-macos*" +skip = "gp3*win*amd64* gp312*" before-all = "sh scripts/cibw_before_all.sh" test-extras = "ci" diff --git a/tests/test_mpz.py b/tests/test_mpz.py index 5c051cce..e8600f81 100644 --- a/tests/test_mpz.py +++ b/tests/test_mpz.py @@ -723,9 +723,8 @@ def test_getters(x): @given(bigints()) def test_methods(x): mx = mpz(x) - methods = ["conjugate", "bit_length", "as_integer_ratio", "bit_count"] - if sys.version_info >= (3, 12): - methods.append("is_integer") + methods = ["conjugate", "bit_length", "as_integer_ratio", "bit_count", + "is_integer"] for name in methods: meth = operator.methodcaller(name) assert meth(mx) == meth(x) diff --git a/tests/utils.py b/tests/utils.py index b52c8329..7ea79434 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -15,7 +15,6 @@ BITS_PER_DIGIT = mpz_info.bits_per_digit SIZEOF_DIGIT = mpz_info.sizeof_digit BITCNT_MAX = mpz_info.bitcnt_max -MAX_FACTORIAL_CACHE = 1000 def python_gcdext(a, b): diff --git a/utils.h b/utils.h index 25227ed5..0be36672 100644 --- a/utils.h +++ b/utils.h @@ -8,10 +8,8 @@ # pragma GCC diagnostic ignored "-Wsign-conversion" #endif -#include "pythoncapi_compat.h" - #define PY_SSIZE_T_CLEAN -#include +#include "pythoncapi_compat.h" #if defined(__GNUC__) || defined(__clang__) # pragma GCC diagnostic pop