diff --git a/src/flint/types/fmpz.pxd b/src/flint/types/fmpz.pxd index f48b059c..4477daf0 100644 --- a/src/flint/types/fmpz.pxd +++ b/src/flint/types/fmpz.pxd @@ -1,20 +1,44 @@ from cpython.long cimport PyLong_Check from flint.flint_base.flint_base cimport flint_scalar from flint.utils.conversion cimport chars_from_str -from flint.flintlib.types.flint cimport slong, pylong_as_slong +from flint.flintlib.types.flint cimport slong, ulong, pylong_as_slong from flint.flintlib.types.flint cimport PyObject -from flint.flintlib.functions.fmpz cimport fmpz_t, fmpz_set_str, fmpz_set_si +from flint.flintlib.functions.fmpz cimport fmpz_t, fmpz_set_si, fmpz_set_signed_ui_array +import sys cdef int fmpz_set_any_ref(fmpz_t x, obj) cdef fmpz_get_intlong(fmpz_t x) +cdef int is_big_endian = int(sys.byteorder == "big") + +cdef inline ulong ulong_from_little_endian(unsigned char *ptr): + # Read a ulong from little-endian bytes + cdef ulong w = 0 + for i in range(sizeof(ulong) // 8): + w = (w << 8) | ptr[i] + return w + cdef inline int fmpz_set_pylong(fmpz_t x, obj): cdef int overflow cdef slong longval + cdef slong size + cdef bytes b + cdef ulong w + cdef ulong *words + cdef int i + longval = pylong_as_slong(obj, &overflow) if overflow: - s = "%x" % obj - fmpz_set_str(x, chars_from_str(s), 16) + # make sure the sign bit fits + # we need 8 * sizeof(ulong) * size > obj.bit_length() + size = obj.bit_length() // (8 * sizeof(ulong)) + 1 + b = obj.to_bytes(sizeof(ulong) * size, "little", signed=True) + # b is a local Python object, we access the internal pointer + words = (b) + if is_big_endian: + for i in range(size): + words[i] = ulong_from_little_endian((words + i)) + fmpz_set_signed_ui_array(x, words, size) else: fmpz_set_si(x, longval) diff --git a/src/flint/types/fmpz.pyx b/src/flint/types/fmpz.pyx index 6f3cf677..390282aa 100644 --- a/src/flint/types/fmpz.pyx +++ b/src/flint/types/fmpz.pyx @@ -2,7 +2,7 @@ from flint.flint_base.flint_base cimport flint_scalar from flint.utils.typecheck cimport typecheck from flint.utils.conversion cimport chars_from_str from flint.utils.conversion cimport str_from_chars, _str_trunc -cimport libc.stdlib +from libc.stdlib cimport malloc, free from flint.flintlib.types.flint cimport FMPZ_REF, FMPZ_TMP, FMPZ_UNKNOWN, COEFF_IS_MPZ from flint.flintlib.functions.flint cimport flint_free @@ -14,16 +14,33 @@ from flint.flintlib.functions.partitions cimport * from flint.utils.flint_exceptions import DomainError - cdef fmpz_get_intlong(fmpz_t x): """ Convert fmpz_t to a Python int or long. """ - cdef char * s + cdef slong size + cdef ulong * words + cdef int i + cdef fmpz_struct xabs[1] if COEFF_IS_MPZ(x[0]): - s = fmpz_get_str(NULL, 16, x) - v = int(str_from_chars(s), 16) - flint_free(s) + # Python from signed bytes is slow so we convert the absolute value. + # we need 8 * sizeof(ulong) * size >= fmpz_bits(x) + size = fmpz_bits(x) // (8 * sizeof(ulong)) + 1 + words = malloc(size * sizeof(ulong)) + if fmpz_sgn(x) == -1: + fmpz_init(xabs) + fmpz_abs(xabs, x) + fmpz_get_ui_array(words, size, xabs) + fmpz_clear(xabs) + else: + fmpz_get_ui_array(words, size, x) + if is_big_endian: + for i in range(size): + words[i] = ulong_from_little_endian((words + i)) + v = int.from_bytes((words)[:size * sizeof(ulong)], "little") + if fmpz_sgn(x) == -1: + v = -v + free(words) return v else: return x[0]