diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6f4fdfd..6de0c852 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: workflow_dispatch: schedule: - cron: '0 0 * * 2' +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: linter: uses: ./.github/workflows/linter.yml diff --git a/extra.c b/extra.c new file mode 100644 index 00000000..2afb33b5 --- /dev/null +++ b/extra.c @@ -0,0 +1,375 @@ +#include "extra.h" + +#include +#include +#include +#include + +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_t tmp; + bool is_negative = zz_isneg(u); + + if (zz_init(&tmp)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + if (is_negative) { + if (!is_signed) { + return ZZ_BUF; + } + if (zz_set(1, &tmp) || zz_mul_2exp(&tmp, 8*length, &tmp) + || zz_add(&tmp, u, &tmp)) + { + /* LCOV_EXCL_START */ + zz_clear(&tmp); + return ZZ_MEM; + /* LCOV_EXCL_STOP */ + } + u = &tmp; + } + + size_t nbits = zz_bitlen(u); + + if (zz_isneg(u) || nbits > 8*length + || (is_signed && ((!nbits && is_negative) + || (nbits && (nbits == 8 * length ? !is_negative : is_negative))))) + { + zz_clear(&tmp); + return ZZ_BUF; + } + + size_t gap = length - (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); + zz_clear(&tmp); + return ZZ_OK; +} + +zz_err +zz_set_bytes(const unsigned char *buffer, size_t length, bool is_signed, + zz_t *u) +{ + if (!length) { + return zz_set(0, u); + } + + zz_err ret = zz_import(length, buffer, 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) { + 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_sub(&tmp, u, u) || zz_neg(u, u)) + { + /* LCOV_EXCL_START */ + zz_clear(&tmp); + return ZZ_MEM; + /* LCOV_EXCL_STOP */ + } + zz_clear(&tmp); + } + return ZZ_OK; +} + +zz_err +zz_divnear(const zz_t *u, const zz_t *v, zz_t *q, zz_t *r) +{ + if (!q || !r) { + assert(q != NULL || r != NULL); + if (!q) { + zz_t tmp; + + if (zz_init(&tmp)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + + zz_err ret = zz_divnear(u, v, &tmp, r); + + zz_clear(&tmp); + return ret; + } + else { + zz_t tmp; + + if (zz_init(&tmp)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + + zz_err ret = zz_divnear(u, v, q, &tmp); + + zz_clear(&tmp); + return ret; + } + } + + zz_err ret = zz_div(u, v, q, r); + + if (ret) { + /* LCOV_EXCL_START */ +err: + zz_clear(q); + zz_clear(r); + return ret; + /* LCOV_EXCL_STOP */ + } + + zz_ord unexpect = zz_isneg(v) ? ZZ_LT : ZZ_GT; + zz_t halfQ; + + if (zz_init(&halfQ) || zz_quo_2exp(v, 1, &halfQ)) { + /* LCOV_EXCL_START */ + zz_clear(&halfQ); + goto err; + /* LCOV_EXCL_STOP */ + } + + zz_ord cmp = zz_cmp(r, &halfQ); + + zz_clear(&halfQ); + if (cmp == ZZ_EQ && !zz_isodd(v) && !zz_iszero(q) && zz_isodd(q)) { + cmp = unexpect; + } + /* No overflow is possible, since q can't have maximal value + below (that means v==1) and r have same sign as v (with |r| < |v|) */ + if (cmp == unexpect && (zz_add(q, 1, q) || zz_sub(r, v, r))) { + goto err; /* LCOV_EXCL_LINE */ + } + return ZZ_OK; +} + +zz_err +zz_truediv(const zz_t *u, const zz_t *v, double *res) +{ + if (zz_iszero(v)) { + return ZZ_VAL; + } + if (zz_iszero(u)) { + *res = zz_isneg(v) ? -0.0 : 0.0; + return ZZ_OK; + } + + zz_bitcnt_t ubits = zz_bitlen(u); + zz_bitcnt_t vbits = zz_bitlen(v); + + if (ubits > vbits && ubits - vbits > DBL_MAX_EXP) { + 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; + return ZZ_OK; + } + + int shift = (int)(vbits - ubits); + int n = shift, whole = n / bits_per_digit; + zz_t a, b; + + if (zz_init(&a) || zz_init(&b) || zz_abs(u, &a) || zz_abs(v, &b)) { + /* LCOV_EXCL_START */ +tmp_clear: + zz_clear(&a); + zz_clear(&b); + return ZZ_MEM; + /* LCOV_EXCL_STOP */ + } + if (shift < 0) { + const zz_t *t = u; + + u = v; + v = t; + n = -n; + whole = -whole; + } + /* -shift - 1 -shift + find shift satisfying 2 <= |a/b| < 2 */ + n %= bits_per_digit; + for (zz_size_t i = v->size; i--;) { + zz_digit_t du, dv = v->digits[i]; + + if (i >= whole) { + if (i - whole < u->size) { + du = u->digits[i - whole] << n; + } + else { + du = 0; + } + if (n && i > whole) { + du |= u->digits[i - whole - 1] >> (bits_per_digit - n); + } + } + else { + du = 0; + } + if (du < dv) { + if (shift < 0) { + shift--; + } + break; + } + if (du > dv) { + if (shift >= 0) { + shift--; + } + break; + } + } + shift += DBL_MANT_DIG; + /* No overflow is possible in shifts, since shift value + much smaller here than zz_get_bitcnt_max() */ + if (shift > 0 && zz_mul_2exp(&a, (uint64_t)shift, &a)) { + goto tmp_clear; /* LCOV_EXCL_LINE */ + } + if (shift < 0 && zz_mul_2exp(&b, (uint64_t)-shift, &b)) { + goto tmp_clear; /* LCOV_EXCL_LINE */ + } + if (zz_divnear(&a, &b, &a, NULL)) { + /* LCOV_EXCL_START */ + zz_clear(&a); + zz_clear(&b); + return ZZ_MEM; + /* LCOV_EXCL_STOP */ + } + zz_clear(&b); + (void)zz_get(&a, res); + zz_clear(&a); + *res = ldexp(*res, -shift); + if (zz_isneg(u) != zz_isneg(v)) { + *res = -*res; + } + if (isinf(*res)) { + return ZZ_BUF; + } + return ZZ_OK; +} + +zz_err +zz_lshift(const zz_t *u, const zz_t *v, zz_t *w) +{ + if (zz_isneg(v)) { + return ZZ_VAL; + } + + uint64_t shift; + + if (zz_get(v, &shift)) { + return ZZ_BUF; + } + return zz_mul_2exp(u, shift, w); +} + +zz_err +zz_rshift(const zz_t *u, const zz_t *v, zz_t *w) +{ + if (zz_isneg(v)) { + return ZZ_VAL; + } + + uint64_t shift; + + if (zz_get(v, &shift)) { + return zz_set(zz_isneg(u) ? -1 : 0, w); + } + return zz_quo_2exp(u, shift, 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) +{ + /* If the mantissa is 0, return the normalized representation. */ + if (zz_iszero(man)) { + *negative = false; + *bc = 0; + return zz_set(0, exp); + } + /* if size <= prec and the number is odd return it */ + if (*bc <= prec && zz_isodd(man)) { + return ZZ_OK; + } + if (*bc > prec) { + zz_bitcnt_t shift = *bc - prec; + +do_rnd: + switch (rnd) { + case ZZ_RNDD: + rnd = *negative ? ZZ_RNDA : ZZ_RNDZ; + goto do_rnd; + case ZZ_RNDU: + rnd = *negative ? ZZ_RNDZ : ZZ_RNDA; + goto do_rnd; + case ZZ_RNDZ: + zz_quo_2exp(man, shift, man); + break; + case ZZ_RNDA: + zz_neg(man, man); + zz_quo_2exp(man, shift, man); + zz_abs(man, man); + break; + case ZZ_RNDN: + default: + { + bool t = zz_lsbpos(man) + 2 <= shift; + + if (zz_quo_2exp(man, shift - 1, man)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + t = zz_isodd(man) && (man->digits[0]&2 || t); + if (zz_quo_2exp(man, 1, man)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + if (t && zz_add(man, 1, man)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + } + } + + zz_t tmp; + zz_err ret = ZZ_OK; + + if (zz_init(&tmp) || (ret = zz_add(exp, shift, exp))) { + /* LCOV_EXCL_START */ + zz_clear(&tmp); + return ret; + /* LCOV_EXCL_STOP */ + } + zz_clear(&tmp); + *bc = prec; + } + + zz_bitcnt_t zbits = 0; + + /* Strip trailing 0 bits. */ + if (!zz_iszero(man) && (zbits = zz_lsbpos(man))) { + if (zz_quo_2exp(man, zbits, man)) { + return ZZ_MEM; /* LCOV_EXCL_LINE */ + } + } + + zz_t tmp; + zz_err ret = ZZ_OK; + + if (zz_init(&tmp) || (ret = zz_add(exp, zbits, exp))) { + /* LCOV_EXCL_START */ + zz_clear(&tmp); + return ret; + /* LCOV_EXCL_STOP */ + } + zz_clear(&tmp); + *bc -= zbits; + /* Check if one less than a power of 2 was rounded up. */ + if (zz_cmp(man, 1) == ZZ_EQ) { + *bc = 1; + } + return ZZ_OK; +} diff --git a/extra.h b/extra.h new file mode 100644 index 00000000..19279451 --- /dev/null +++ b/extra.h @@ -0,0 +1,33 @@ +#ifndef EXTRA_H +#define EXTRA_H + +#include "zz/zz.h" + +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, + 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_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); + +typedef enum { + ZZ_RNDD = 0, + ZZ_RNDN = 1, + ZZ_RNDU = 2, + ZZ_RNDZ = 3, + ZZ_RNDA = 4, +} zz_rnd; + +zz_err zz_mpmath_normalize(zz_bitcnt_t prec, zz_rnd rnd, bool *negative, + zz_t *man, zz_t *exp, zz_bitcnt_t *bc); + +#endif /* EXTRA_H */ diff --git a/gmp.c b/gmp.c index 9485fd03..65fe44b5 100644 --- a/gmp.c +++ b/gmp.c @@ -1,11 +1,9 @@ #include "mpz.h" +#include "extra.h" -#include -#include -#include -#include -#include -#include +#ifdef Py_GIL_DISABLED +# include +#endif #ifdef ON_CPYTHON # define MAX_FREELIST_SIZE 100 @@ -305,51 +303,6 @@ revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r) } } -static const zz_layout bytes_layout = {8, 1, 1, 0}; - -static zz_err -zz_get_bytes(const zz_t *u, size_t length, bool is_signed, - unsigned char **buffer) -{ - zz_t tmp; - bool is_negative = zz_isneg(u); - - if (zz_init(&tmp)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - if (is_negative) { - if (!is_signed) { - return ZZ_BUF; - } - if (zz_set(1, &tmp) || zz_mul_2exp(&tmp, 8*length, &tmp) - || zz_add(&tmp, u, &tmp)) - { - /* LCOV_EXCL_START */ - zz_clear(&tmp); - return ZZ_MEM; - /* LCOV_EXCL_STOP */ - } - u = &tmp; - } - - size_t nbits = zz_bitlen(u); - - if (zz_isneg(u) || nbits > 8*length - || (is_signed && ((!nbits && is_negative) - || (nbits && (nbits == 8 * length ? !is_negative : is_negative))))) - { - zz_clear(&tmp); - return ZZ_BUF; - } - - size_t gap = length - (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); - zz_clear(&tmp); - return ZZ_OK; -} - static PyObject * MPZ_to_bytes(MPZ_Object *u, Py_ssize_t length, int is_little, int is_signed) { @@ -391,39 +344,6 @@ MPZ_to_bytes(MPZ_Object *u, Py_ssize_t length, int is_little, int is_signed) /* LCOV_EXCL_STOP */ } -static zz_err -zz_set_bytes(const unsigned char *buffer, size_t length, bool is_signed, - zz_t *u) -{ - if (!length) { - return zz_set(0, u); - } - - zz_err ret = zz_import(length, buffer, 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) { - 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_sub(&tmp, u, u) || zz_neg(u, u)) - { - /* LCOV_EXCL_START */ - zz_clear(&tmp); - return ZZ_MEM; - /* LCOV_EXCL_STOP */ - } - zz_clear(&tmp); - } - return ZZ_OK; -} - static MPZ_Object * MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) { @@ -795,9 +715,16 @@ static Py_hash_t hash(PyObject *self) { MPZ_Object *u = (MPZ_Object *)self; +#ifdef Py_GIL_DISABLED + Py_hash_t hash = atomic_load_explicit((const _Atomic(Py_hash_t) + *)&u->hash_cache, + memory_order_relaxed); +#else + Py_hash_t hash = u->hash_cache; +#endif - if (u->hash_cache != -1) { - return u->hash_cache; + if (hash != -1) { + return hash; } zz_t w; @@ -805,22 +732,24 @@ hash(PyObject *self) if (zz_init(&w)) { return -1; /* LCOV_EXCL_LINE */ } - assert((int64_t)INT64_MAX > pyhash_modulus); (void)zz_div(&u->z, (int64_t)pyhash_modulus, NULL, &w); - - Py_hash_t r; - assert(sizeof(Py_hash_t) == 8); - (void)zz_get(&w, (int64_t *)&r); + (void)zz_get(&w, (int64_t *)&hash); zz_clear(&w); - if (zz_isneg(&u->z) && r) { - r = -(pyhash_modulus - r); + if (zz_isneg(&u->z) && hash) { + hash = -(pyhash_modulus - hash); } - if (r == -1) { - r = -2; + if (hash == -1) { + hash = -2; } - return u->hash_cache = r; +#ifdef Py_GIL_DISABLED + atomic_store_explicit((_Atomic(Py_hash_t) *)&u->hash_cache, + hash, memory_order_relaxed); + return hash; +#else + return u->hash_cache = hash; +#endif } #define UNOP(suff, func) \ @@ -866,18 +795,26 @@ 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)) { \ - ; \ - } \ - 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_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) \ @@ -885,6 +822,7 @@ to_bool(PyObject *self) nb_##suff(PyObject *self, PyObject *other) \ { \ MPZ_Object *u = NULL, *v = NULL, *res = NULL; \ + int64_t ival = 0; \ \ CHECK_OPv2(u, self); \ CHECK_OPv2(v, other); \ @@ -897,33 +835,14 @@ to_bool(PyObject *self) zz_err ret = ZZ_OK; \ \ if (!u) { \ - int error; \ - int64_t temp = PyLong_AsSdigit_t(self, &error); \ - \ - if (!error) { \ - ret = zz_##suff(temp, &v->z, &res->z); \ - goto done; \ - } \ - u = MPZ_from_int(self); \ - if (!u) { \ - goto end; \ - } \ + ret = zz_##suff(ival, &v->z, &res->z); \ } \ - if (!v) { \ - int error; \ - int64_t temp = PyLong_AsSdigit_t(other, &error); \ - \ - if (!error) { \ - ret = zz_##suff(&u->z, temp, &res->z); \ - goto done; \ - } \ - v = MPZ_from_int(other); \ - if (!v) { \ - goto end; \ - } \ + else if (!v) { \ + ret = zz_##suff(&u->z, ival, &res->z); \ + } \ + else { \ + ret = zz_##suff(&u->z, &v->z, &res->z); \ } \ - ret = zz_##suff(&u->z, &v->z, &res->z); \ -done: \ if (ret) { \ Py_CLEAR(res); \ if (ret == ZZ_VAL) { \ @@ -981,13 +900,21 @@ BINOP(add, PyNumber_Add) BINOP(sub, PyNumber_Subtract) BINOP(mul, PyNumber_Multiply) -#define zz_quo_(u, v, w) zz_div((u), (v), (w), NULL) -#define zz_rem_(u, v, w) zz_div((u), (v), NULL, (w)) - /* can't overflow */ BINOP(quo_, PyNumber_FloorDivide) BINOP(rem_, PyNumber_Remainder) +#define CHECK_OP_INT(u, a) \ + if (MPZ_Check(a)) { \ + u = (MPZ_Object *)Py_NewRef(a); \ + } \ + else { \ + u = MPZ_from_int(a); \ + if (!u) { \ + goto end; \ + } \ + } \ + static PyObject * nb_divmod(PyObject *self, PyObject *other) { @@ -997,8 +924,8 @@ nb_divmod(PyObject *self, PyObject *other) if (!res) { return NULL; /* LCOV_EXCL_LINE */ } - CHECK_OP(u, self); - CHECK_OP(v, other); + CHECK_OP_INT(u, self); + CHECK_OP_INT(v, other); MPZ_Object *q = MPZ_new(); MPZ_Object *r = MPZ_new(); @@ -1036,182 +963,6 @@ nb_divmod(PyObject *self, PyObject *other) Py_XDECREF((PyObject *)v); return NULL; /* LCOV_EXCL_STOP */ -fallback: -numbers: - Py_DECREF(res); - Py_XDECREF((PyObject *)u); - Py_XDECREF((PyObject *)v); - Py_RETURN_NOTIMPLEMENTED; -} - -static zz_err -zz_divnear(const zz_t *u, const zz_t *v, zz_t *q, zz_t *r) -{ - if (!q || !r) { - assert(q != NULL || r != NULL); - if (!q) { - zz_t tmp; - - if (zz_init(&tmp)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - - zz_err ret = zz_divnear(u, v, &tmp, r); - - zz_clear(&tmp); - return ret; - } - else { - zz_t tmp; - - if (zz_init(&tmp)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - - zz_err ret = zz_divnear(u, v, q, &tmp); - - zz_clear(&tmp); - return ret; - } - } - - zz_err ret = zz_div(u, v, q, r); - - if (ret) { - /* LCOV_EXCL_START */ -err: - zz_clear(q); - zz_clear(r); - return ret; - /* LCOV_EXCL_STOP */ - } - - zz_ord unexpect = zz_isneg(v) ? ZZ_LT : ZZ_GT; - zz_t halfQ; - - if (zz_init(&halfQ) || zz_quo_2exp(v, 1, &halfQ)) { - /* LCOV_EXCL_START */ - zz_clear(&halfQ); - goto err; - /* LCOV_EXCL_STOP */ - } - - zz_ord cmp = zz_cmp(r, &halfQ); - - zz_clear(&halfQ); - if (cmp == ZZ_EQ && !zz_isodd(v) && !zz_iszero(q) && zz_isodd(q)) { - cmp = unexpect; - } - /* No overflow is possible, since q can't have maximal value - below (that means v==1) and r have same sign as v (with |r| < |v|) */ - if (cmp == unexpect && (zz_add(q, 1, q) || zz_sub(r, v, r))) { - goto err; /* LCOV_EXCL_LINE */ - } - return ZZ_OK; -} - -static zz_err -zz_truediv(const zz_t *u, const zz_t *v, double *res) -{ - if (zz_iszero(v)) { - return ZZ_VAL; - } - if (zz_iszero(u)) { - *res = zz_isneg(v) ? -0.0 : 0.0; - return ZZ_OK; - } - - zz_bitcnt_t ubits = zz_bitlen(u); - zz_bitcnt_t vbits = zz_bitlen(v); - - if (ubits > vbits && ubits - vbits > DBL_MAX_EXP) { - 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; - return ZZ_OK; - } - - int shift = (int)(vbits - ubits); - int n = shift, whole = n / bits_per_digit; - zz_t a, b; - - if (zz_init(&a) || zz_init(&b) || zz_abs(u, &a) || zz_abs(v, &b)) { - /* LCOV_EXCL_START */ -tmp_clear: - zz_clear(&a); - zz_clear(&b); - return ZZ_MEM; - /* LCOV_EXCL_STOP */ - } - if (shift < 0) { - const zz_t *t = u; - - u = v; - v = t; - n = -n; - whole = -whole; - } - /* -shift - 1 -shift - find shift satisfying 2 <= |a/b| < 2 */ - n %= bits_per_digit; - for (zz_size_t i = v->size; i--;) { - zz_digit_t du, dv = v->digits[i]; - - if (i >= whole) { - if (i - whole < u->size) { - du = u->digits[i - whole] << n; - } - else { - du = 0; - } - if (n && i > whole) { - du |= u->digits[i - whole - 1] >> (bits_per_digit - n); - } - } - else { - du = 0; - } - if (du < dv) { - if (shift < 0) { - shift--; - } - break; - } - if (du > dv) { - if (shift >= 0) { - shift--; - } - break; - } - } - shift += DBL_MANT_DIG; - /* No overflow is possible in shifts, since shift value - much smaller here than zz_get_bitcnt_max() */ - if (shift > 0 && zz_mul_2exp(&a, (uint64_t)shift, &a)) { - goto tmp_clear; /* LCOV_EXCL_LINE */ - } - if (shift < 0 && zz_mul_2exp(&b, (uint64_t)-shift, &b)) { - goto tmp_clear; /* LCOV_EXCL_LINE */ - } - if (zz_divnear(&a, &b, &a, NULL)) { - /* LCOV_EXCL_START */ - zz_clear(&a); - zz_clear(&b); - return ZZ_MEM; - /* LCOV_EXCL_STOP */ - } - zz_clear(&b); - (void)zz_get(&a, res); - zz_clear(&a); - *res = ldexp(*res, -shift); - if (zz_isneg(u) != zz_isneg(v)) { - *res = -*res; - } - if (isinf(*res)) { - return ZZ_BUF; - } - return ZZ_OK; } static PyObject * @@ -1282,17 +1033,6 @@ nb_truediv(PyObject *self, PyObject *other) return res; } -#define CHECK_OP_INT(u, a) \ - if (MPZ_Check(a)) { \ - u = (MPZ_Object *)Py_NewRef(a); \ - } \ - else { \ - u = MPZ_from_int(a); \ - if (!u) { \ - goto end; \ - } \ - } \ - #define BINOP_INT(suff) \ static PyObject * \ nb_##suff(PyObject *self, PyObject *other) \ @@ -1330,37 +1070,6 @@ nb_truediv(PyObject *self, PyObject *other) BINOP_INT(and) BINOP_INT(or) BINOP_INT(xor) - -static inline zz_err -zz_lshift(const zz_t *u, const zz_t *v, zz_t *w) -{ - if (zz_isneg(v)) { - return ZZ_VAL; - } - - uint64_t shift; - - if (zz_get(v, &shift)) { - return ZZ_BUF; - } - return zz_mul_2exp(u, shift, w); -} - -static inline zz_err -zz_rshift(const zz_t *u, const zz_t *v, zz_t *w) -{ - if (zz_isneg(v)) { - return ZZ_VAL; - } - - uint64_t shift; - - if (zz_get(v, &shift)) { - return zz_set(zz_isneg(u) ? -1 : 0, w); - } - return zz_quo_2exp(u, shift, w); -} - BINOP_INT(lshift) BINOP_INT(rshift) @@ -2302,14 +2011,6 @@ gmp_perm(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return NULL; } -typedef enum { - ZZ_RNDD = 0, - ZZ_RNDN = 1, - ZZ_RNDU = 2, - ZZ_RNDZ = 3, - ZZ_RNDA = 4, -} zz_rnd; - static zz_rnd get_round_mode(PyObject *rndstr) { @@ -2345,97 +2046,6 @@ get_round_mode(PyObject *rndstr) return rnd; } -static zz_err -zz_mpmath_normalize(zz_bitcnt_t prec, zz_rnd rnd, bool *negative, - zz_t *man, zz_t *exp, zz_bitcnt_t *bc) -{ - /* If the mantissa is 0, return the normalized representation. */ - if (zz_iszero(man)) { - *negative = false; - *bc = 0; - return zz_set(0, exp); - } - /* if size <= prec and the number is odd return it */ - if (*bc <= prec && zz_isodd(man)) { - return ZZ_OK; - } - if (*bc > prec) { - zz_bitcnt_t shift = *bc - prec; - -do_rnd: - switch (rnd) { - case ZZ_RNDD: - rnd = *negative ? ZZ_RNDA : ZZ_RNDZ; - goto do_rnd; - case ZZ_RNDU: - rnd = *negative ? ZZ_RNDZ : ZZ_RNDA; - goto do_rnd; - case ZZ_RNDZ: - zz_quo_2exp(man, shift, man); - break; - case ZZ_RNDA: - zz_neg(man, man); - zz_quo_2exp(man, shift, man); - zz_abs(man, man); - break; - case ZZ_RNDN: - default: - { - bool t = zz_lsbpos(man) + 2 <= shift; - - if (zz_quo_2exp(man, shift - 1, man)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - t = zz_isodd(man) && (man->digits[0]&2 || t); - if (zz_quo_2exp(man, 1, man)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - if (t && zz_add(man, 1, man)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - } - } - - zz_t tmp; - zz_err ret = ZZ_OK; - - if (zz_init(&tmp) || (ret = zz_add(exp, shift, exp))) { - /* LCOV_EXCL_START */ - zz_clear(&tmp); - return ret; - /* LCOV_EXCL_STOP */ - } - zz_clear(&tmp); - *bc = prec; - } - - zz_bitcnt_t zbits = 0; - - /* Strip trailing 0 bits. */ - if (!zz_iszero(man) && (zbits = zz_lsbpos(man))) { - if (zz_quo_2exp(man, zbits, man)) { - return ZZ_MEM; /* LCOV_EXCL_LINE */ - } - } - - zz_t tmp; - zz_err ret = ZZ_OK; - - if (zz_init(&tmp) || (ret = zz_add(exp, zbits, exp))) { - /* LCOV_EXCL_START */ - zz_clear(&tmp); - return ret; - /* LCOV_EXCL_STOP */ - } - zz_clear(&tmp); - *bc -= zbits; - /* Check if one less than a power of 2 was rounded up. */ - if (zz_cmp(man, 1) == ZZ_EQ) { - *bc = 1; - } - return ZZ_OK; -} - static PyObject * gmp__mpmath_normalize(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { diff --git a/meson.build b/meson.build index ad9a2222..887a50f0 100644 --- a/meson.build +++ b/meson.build @@ -2,9 +2,14 @@ project('gmp', 'c', version: run_command('python', 'scripts/gitversion.py', check: true).stdout().strip(), default_options: ['c_std=c17']) +cc = meson.get_compiler('c') py = import('python').find_installation(pure: false) +freethreaded = py.get_variable('Py_GIL_DISABLED', 0) == 1 +if freethreaded and cc.get_id() == 'msvc' + add_project_arguments('/experimental:c11atomics', language: 'c') +endif libzz = dependency('zz', version: '>= 0.9.0a4') -py.extension_module('gmp', ['fmt.c', 'gmp.c', 'utils.c'], +py.extension_module('gmp', ['fmt.c', 'extra.c', 'gmp.c', 'utils.c'], install: true, dependencies: libzz) install_dir = py.get_install_dir() diff --git a/pyproject.toml b/pyproject.toml index 01fa2c7d..c766a600 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ license-files = ["LICENSE"] readme = "README.rst" authors = [{name = "Sergey B Kirpichev", email = "skirpichev@gmail.com"}] maintainers = [{name = "Sergey B Kirpichev", email = "skirpichev@gmail.com"}] -classifiers = ["Development Status :: 4 - Beta", +classifiers = ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "Natural Language :: English", @@ -25,7 +25,7 @@ classifiers = ["Development Status :: 4 - Beta", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.15", - "Programming Language :: Python :: Free Threading :: 2 - Beta", + "Programming Language :: Python :: Free Threading :: 3 - Stable", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: GraalPy",