Skip to content

Commit c7ded0c

Browse files
Faster fmpz/int conversion using from_bytes/to_bytes
1 parent d5c40a4 commit c7ded0c

File tree

3 files changed

+45
-10
lines changed

3 files changed

+45
-10
lines changed

src/flint/types/arb.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ from flint.flintlib.functions.acb_dirichlet cimport *
3131

3232
cimport libc.stdlib
3333
cimport cython
34+
import sys # no-cython-lint
3435

3536
ctx = thectx
3637

src/flint/types/fmpz.pxd

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,46 @@
11
from cpython.long cimport PyLong_Check
22
from flint.flint_base.flint_base cimport flint_scalar
33
from flint.utils.conversion cimport chars_from_str
4-
from flint.flintlib.types.flint cimport slong, pylong_as_slong
4+
from flint.flintlib.types.flint cimport slong, ulong, pylong_as_slong
55
from flint.flintlib.types.flint cimport PyObject
6-
from flint.flintlib.functions.fmpz cimport fmpz_t, fmpz_set_str, fmpz_set_si
6+
from flint.flintlib.functions.fmpz cimport fmpz_t, fmpz_set_si, fmpz_set_signed_ui_array
7+
import sys
78

89
cdef int fmpz_set_any_ref(fmpz_t x, obj)
910
cdef fmpz_get_intlong(fmpz_t x)
1011

12+
cdef extern from "byteswap.h":
13+
"""
14+
#if FLINT_BITS == 32
15+
#define bswap_ulong bswap_32
16+
#elif FLINT_BITS == 64
17+
#define bswap_ulong bswap_64
18+
#else
19+
#error FLINT_BITS must be 32 or 64
20+
#endif
21+
"""
22+
ulong bswap_ulong(ulong x)
23+
1124
cdef inline int fmpz_set_pylong(fmpz_t x, obj):
1225
cdef int overflow
1326
cdef slong longval
27+
cdef slong size
28+
cdef bytes b
29+
cdef ulong *words
30+
1431
longval = pylong_as_slong(<PyObject*>obj, &overflow)
1532
if overflow:
16-
s = "%x" % obj
17-
fmpz_set_str(x, chars_from_str(s), 16)
33+
# make sure the sign bit fits
34+
# we need sizeof(ulong) * size > obj.bit_length()
35+
size = obj.bit_length() // sizeof(ulong) + 1
36+
b = obj.to_bytes(sizeof(ulong) * size, "little", signed=True)
37+
# b is a local Python object, we access the internal pointer
38+
words = <ulong*>(<char *>b)
39+
# sys must be imported by the caller
40+
if sys.byteorder == "big":
41+
for i in range(size):
42+
words[i] = bswap_ulong(words[i])
43+
fmpz_set_signed_ui_array(x, words, size)
1844
else:
1945
fmpz_set_si(x, longval)
2046

src/flint/types/fmpz.pyx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ from flint.flint_base.flint_base cimport flint_scalar
22
from flint.utils.typecheck cimport typecheck
33
from flint.utils.conversion cimport chars_from_str
44
from flint.utils.conversion cimport str_from_chars, _str_trunc
5-
cimport libc.stdlib
5+
from libc.stdlib cimport malloc, free
6+
import sys
67

78
from flint.flintlib.types.flint cimport FMPZ_REF, FMPZ_TMP, FMPZ_UNKNOWN, COEFF_IS_MPZ
89
from flint.flintlib.functions.flint cimport flint_free
@@ -14,16 +15,23 @@ from flint.flintlib.functions.partitions cimport *
1415

1516
from flint.utils.flint_exceptions import DomainError
1617

17-
1818
cdef fmpz_get_intlong(fmpz_t x):
1919
"""
2020
Convert fmpz_t to a Python int or long.
2121
"""
22-
cdef char * s
22+
cdef slong size
23+
cdef ulong * words
2324
if COEFF_IS_MPZ(x[0]):
24-
s = fmpz_get_str(NULL, 16, x)
25-
v = int(str_from_chars(s), 16)
26-
flint_free(s)
25+
# make sure the sign bit fits
26+
# we need sizeof(ulong) * size > fmpz_bits(x)
27+
size = fmpz_bits(x) // sizeof(ulong) + 1
28+
words = <ulong *>malloc(size * sizeof(ulong))
29+
fmpz_get_signed_ui_array(words, size, x)
30+
if sys.byteorder == "big":
31+
for i in range(size):
32+
words[i] = bswap_ulong(words[i])
33+
v = int.from_bytes((<char *>words)[:size * sizeof(ulong)], "little", signed=True)
34+
free(words)
2735
return v
2836
else:
2937
return <slong>x[0]

0 commit comments

Comments
 (0)