Skip to content

Commit d47e0b5

Browse files
committed
Add exact division for fmpz
fmpz(4) / fmpz(2) -> fmpz(2) fmpz(5) / fmpz(2) -> DomainError
1 parent ea65d5f commit d47e0b5

File tree

2 files changed

+94
-32
lines changed

2 files changed

+94
-32
lines changed

src/flint/test/test.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def test_fmpz():
9090
assert int(f) == i
9191
assert flint.fmpz(f) == f
9292
assert flint.fmpz(str(i)) == f
93+
assert raises(lambda: flint.fmpz(1,2), TypeError)
9394
assert raises(lambda: flint.fmpz("qwe"), ValueError)
9495
assert raises(lambda: flint.fmpz([]), TypeError)
9596
for s in L:
@@ -162,6 +163,9 @@ def test_fmpz():
162163
# XXX: Handle negative modulus like int?
163164
assert raises(lambda: pow(flint.fmpz(2), 2, -1), ValueError)
164165

166+
assert raises(lambda: pow(flint.fmpz(2), "asd", 2), TypeError)
167+
assert raises(lambda: pow(flint.fmpz(2), 2, "asd"), TypeError)
168+
165169
f = flint.fmpz(2)
166170
assert f.numerator == f
167171
assert type(f.numerator) is flint.fmpz
@@ -2276,6 +2280,44 @@ def test_fmpz_mod_mat():
22762280
assert raises(lambda: flint.fmpz_mod_mat(A, c11), TypeError)
22772281

22782282

2283+
def test_division_scalar():
2284+
Z = flint.fmpz
2285+
Q = flint.fmpq
2286+
F17 = lambda x: flint.nmod(x, 17)
2287+
ctx = flint.fmpz_mod_ctx(163)
2288+
F163 = lambda a: flint.fmpz_mod(a, ctx)
2289+
# fmpz exact division
2290+
for (a, b) in [(Z(4), Z(2)), (Z(4), 2), (4, Z(2))]:
2291+
assert a / b == Z(2)
2292+
for (a, b) in [(Z(5), Z(2)), (Z(5), 2), (5, Z(2))]:
2293+
assert raises(lambda: a / b, DomainError)
2294+
# fmpz Euclidean division
2295+
for (a, b) in [(Z(5), Z(2)), (Z(5), 2), (5, Z(2))]:
2296+
assert a // b == 2
2297+
assert a % b == 1
2298+
assert divmod(a, b) == (2, 1)
2299+
# field division
2300+
for (a, b) in [(Q(5), Q(2)), (Q(5), 2), (5, Q(2))]:
2301+
assert a / b == Q(5,2)
2302+
for (a, b) in [(F17(5), F17(2)), (F17(5), 2), (5, F17(2))]:
2303+
assert a / b == F17(11)
2304+
for (a, b) in [(F163(5), F163(2)), (F163(5), 2), (5, F163(2))]:
2305+
assert a / b == F163(84)
2306+
# divmod with fields - should this give remainder zero instead of error?
2307+
for K in [Q, F17, F163]:
2308+
for (a, b) in [(K(5), K(2)), (K(5), 2), (5, K(2))]:
2309+
assert raises(lambda: divmod(a, b), TypeError)
2310+
# Zero division
2311+
for R in [Z, Q, F17, F163]:
2312+
assert raises(lambda: R(5) / 0, ZeroDivisionError)
2313+
assert raises(lambda: R(5) / R(0), ZeroDivisionError)
2314+
assert raises(lambda: 5 / R(0), ZeroDivisionError)
2315+
# Bad types
2316+
for R in [Z, Q, F17, F163]:
2317+
assert raises(lambda: R(5) / "AAA", TypeError)
2318+
assert raises(lambda: "AAA" / R(5), TypeError)
2319+
2320+
22792321
def _all_polys():
22802322
return [
22812323
# (poly_type, scalar_type, is_field)
@@ -3023,7 +3065,7 @@ def test_all_tests():
30233065
test_fmpz_mod_poly,
30243066
test_fmpz_mod_mat,
30253067

3026-
test_arb,
3068+
test_division_scalar,
30273069

30283070
test_polys,
30293071

@@ -3048,7 +3090,9 @@ def test_all_tests():
30483090
test_matrices_rref,
30493091
test_matrices_solve,
30503092

3093+
test_arb,
3094+
30513095
test_pickling,
3052-
test_all_tests,
30533096

3097+
test_all_tests,
30543098
]

src/flint/types/fmpz.pyx

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from cpython.version cimport PY_MAJOR_VERSION
2-
31
from flint.flint_base.flint_base cimport flint_scalar
42
from flint.utils.typecheck cimport typecheck
53
from flint.utils.conversion cimport chars_from_str
@@ -12,6 +10,9 @@ from flint.flintlib.fmpz_factor cimport *
1210
from flint.flintlib.arith cimport *
1311
from flint.flintlib.partitions cimport *
1412

13+
from flint.utils.flint_exceptions import DomainError
14+
15+
1516
cdef fmpz_get_intlong(fmpz_t x):
1617
"""
1718
Convert fmpz_t to a Python int or long.
@@ -29,10 +30,6 @@ cdef int fmpz_set_any_ref(fmpz_t x, obj):
2930
if typecheck(obj, fmpz):
3031
x[0] = (<fmpz>obj).val[0]
3132
return FMPZ_REF
32-
if PY_MAJOR_VERSION < 3 and PyInt_Check(obj):
33-
fmpz_init(x)
34-
fmpz_set_si(x, PyInt_AS_LONG(obj))
35-
return FMPZ_TMP
3633
if PyLong_Check(obj):
3734
fmpz_init(x)
3835
fmpz_set_pylong(x, obj)
@@ -103,9 +100,6 @@ cdef class fmpz(flint_scalar):
103100
def __int__(self):
104101
return fmpz_get_intlong(self.val)
105102

106-
def __long__(self):
107-
return long(fmpz_get_intlong(self.val))
108-
109103
def __index__(self):
110104
return fmpz_get_intlong(self.val)
111105

@@ -134,27 +128,18 @@ cdef class fmpz(flint_scalar):
134128
cdef fmpz_struct * sval
135129
cdef int ttype
136130
sval = &((<fmpz>s).val[0])
137-
if PY_MAJOR_VERSION < 3 and PyInt_Check(t):
138-
tl = PyInt_AS_LONG(t)
139-
if op == 2: res = fmpz_cmp_si(sval, tl) == 0
140-
elif op == 3: res = fmpz_cmp_si(sval, tl) != 0
141-
elif op == 0: res = fmpz_cmp_si(sval, tl) < 0
142-
elif op == 1: res = fmpz_cmp_si(sval, tl) <= 0
143-
elif op == 4: res = fmpz_cmp_si(sval, tl) > 0
144-
elif op == 5: res = fmpz_cmp_si(sval, tl) >= 0
145-
else:
146-
ttype = fmpz_set_any_ref(tval, t)
147-
if ttype != FMPZ_UNKNOWN:
148-
if op == 2: res = fmpz_equal(sval, tval)
149-
elif op == 3: res = not fmpz_equal(sval, tval)
150-
elif op == 0: res = fmpz_cmp(sval, tval) < 0
151-
elif op == 1: res = fmpz_cmp(sval, tval) <= 0
152-
elif op == 4: res = fmpz_cmp(sval, tval) > 0
153-
elif op == 5: res = fmpz_cmp(sval, tval) >= 0
154-
if ttype == FMPZ_TMP:
155-
fmpz_clear(tval)
156-
if ttype == FMPZ_UNKNOWN:
157-
return NotImplemented
131+
ttype = fmpz_set_any_ref(tval, t)
132+
if ttype != FMPZ_UNKNOWN:
133+
if op == 2: res = fmpz_equal(sval, tval)
134+
elif op == 3: res = not fmpz_equal(sval, tval)
135+
elif op == 0: res = fmpz_cmp(sval, tval) < 0
136+
elif op == 1: res = fmpz_cmp(sval, tval) <= 0
137+
elif op == 4: res = fmpz_cmp(sval, tval) > 0
138+
elif op == 5: res = fmpz_cmp(sval, tval) >= 0
139+
if ttype == FMPZ_TMP:
140+
fmpz_clear(tval)
141+
if ttype == FMPZ_UNKNOWN:
142+
return NotImplemented
158143
return res
159144

160145
def bit_length(self):
@@ -265,6 +250,39 @@ cdef class fmpz(flint_scalar):
265250
if ttype == FMPZ_TMP: fmpz_clear(tval)
266251
return u
267252

253+
def __truediv__(s, t):
254+
cdef fmpz_struct tval[1]
255+
cdef fmpz_struct rval[1]
256+
cdef int ttype
257+
258+
ttype = fmpz_set_any_ref(tval, t)
259+
if ttype == FMPZ_UNKNOWN:
260+
return NotImplemented
261+
262+
if fmpz_is_zero(tval):
263+
if ttype == FMPZ_TMP:
264+
fmpz_clear(tval)
265+
raise ZeroDivisionError("fmpz division by zero")
266+
267+
q = fmpz.__new__(fmpz)
268+
fmpz_init(rval)
269+
fmpz_fdiv_qr((<fmpz>q).val, rval, (<fmpz>s).val, tval)
270+
exact = fmpz_is_zero(rval)
271+
fmpz_clear(rval)
272+
273+
if ttype == FMPZ_TMP: fmpz_clear(tval)
274+
275+
if exact:
276+
return q
277+
else:
278+
raise DomainError("fmpz division is not exact")
279+
280+
def __rtruediv__(s, t):
281+
t = any_as_fmpz(t)
282+
if t is NotImplemented:
283+
return t
284+
return t.__truediv__(s)
285+
268286
def __floordiv__(s, t):
269287
cdef fmpz_struct tval[1]
270288
cdef int ttype = FMPZ_UNKNOWN

0 commit comments

Comments
 (0)