Skip to content

Commit bfe4869

Browse files
committed
Full coverage of nmod_poly and various fixes.
1 parent 6373ec9 commit bfe4869

File tree

9 files changed

+160
-62
lines changed

9 files changed

+160
-62
lines changed

src/flint/test/test_all.py

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,17 +1113,27 @@ def set_bad(i):
11131113
raises(lambda: Q(1,2,[3,4]) * Q(1,3,[5,6,7]), ValueError)
11141114
raises(lambda: Q(1,2,[3,4]) * Z(1,3,[5,6,7]), ValueError)
11151115
raises(lambda: Z(1,2,[3,4]) * Q(1,3,[5,6,7]), ValueError)
1116+
11161117
A = Q([[3,4],[5,7]]) / 11
11171118
X = Q([[1,2],[3,4]])
11181119
B = A*X
11191120
assert A.solve(B) == X
11201121
for algorithm in None, "fflu", "dixon":
11211122
assert A.solve(B, algorithm=algorithm) == X
1123+
for _ in range(2):
1124+
A = Q(flint.fmpz_mat.randtest(30, 30, 10))
1125+
if A.det() == 0:
1126+
continue
1127+
B = Q(flint.fmpz_mat.randtest(30, 1, 10))
1128+
X = A.solve(B)
1129+
assert A*X == B
1130+
11221131
assert raises(lambda: A.solve(B, algorithm="invalid"), ValueError)
11231132
assert raises(lambda: A.solve(None), TypeError)
11241133
assert raises(lambda: A.solve([1,2]), TypeError)
11251134
assert raises(lambda: A.solve(Q([[1,2]])), ValueError)
11261135
assert raises(lambda: Q([[1,2],[2,4]]).solve(Q([[1],[2]])), ZeroDivisionError)
1136+
11271137
M = Q([[1,2,3],[flint.fmpq(1,2),5,6]])
11281138
Mcopy = Q(M)
11291139
Mrref = Q([[1,0,flint.fmpq(3,4)],[0,1,flint.fmpq(9,8)]])
@@ -1466,16 +1476,24 @@ def set_bad2():
14661476
assert raises(set_bad2, TypeError)
14671477
assert bool(P([], 5)) is False
14681478
assert bool(P([1], 5)) is True
1479+
14691480
assert P([1,2,1],3).gcd(P([1,1],3)) == P([1,1],3)
1470-
raises(lambda: P([1,2],3).gcd([]), TypeError)
1471-
raises(lambda: P([1,2],3).gcd(P([1,2],5)), ValueError)
1481+
assert raises(lambda: P([1,2],3).gcd([]), TypeError)
1482+
assert raises(lambda: P([1,2],3).gcd(P([1,2],5)), ValueError)
1483+
assert P([1,2,1],3).xgcd(P([1,1],3)) == (P([1, 1], 3), P([0], 3), P([1], 3))
1484+
assert raises(lambda: P([1,2],3).xgcd([]), TypeError)
1485+
assert raises(lambda: P([1,2],3).xgcd(P([1,2],5)), ValueError)
1486+
assert raises(lambda: P([1,2],6).xgcd(P([1,2],6)), DomainError)
1487+
14721488
p3 = P([1,2,3,4,5,6],7)
14731489
f3 = (N(6,7), [(P([6, 1],7), 5)])
14741490
assert p3.factor() == f3
14751491
# XXX: factor ignores an invalid algorithm string
14761492
for alg in [None, 'berlekamp', 'cantor-zassenhaus']:
14771493
assert p3.factor(alg) == f3
14781494
assert p3.factor(algorithm=alg) == f3
1495+
assert raises(lambda: p3.factor(algorithm="invalid"), ValueError)
1496+
14791497
assert P([1], 11).roots() == []
14801498
assert P([1, 2, 3], 11).roots() == [(8, 1), (6, 1)]
14811499
assert P([1, 6, 1, 8], 11).roots() == [(5, 3)]
@@ -1608,6 +1626,33 @@ def test_nmod_series():
16081626
# XXX: currently no code in nmod_series.pyx
16091627
pass
16101628

1629+
1630+
def test_nmod_contexts():
1631+
# XXX: Generalise this test to cover fmpz_mod, fq_default, etc.
1632+
C = flint.nmod_ctx
1633+
CP = flint.nmod_poly_ctx
1634+
G = flint.nmod
1635+
P = flint.nmod_poly
1636+
1637+
for c, name in [(C, 'nmod'), (CP, 'nmod_poly')]:
1638+
ctx = c.new(17)
1639+
assert ctx.modulus() == 17
1640+
assert str(ctx) == f"Context for {name} with modulus: 17"
1641+
assert repr(ctx) == f"{name}_ctx(17)"
1642+
assert raises(lambda: c(3), TypeError)
1643+
assert raises(lambda: ctx.new(3.0), TypeError)
1644+
1645+
ctx = C.new(17)
1646+
assert ctx(3) == G(3,17) == G(3, ctx)
1647+
assert raises(lambda: ctx(3.0), TypeError)
1648+
assert raises(lambda: G(3, []), TypeError)
1649+
1650+
ctx_poly = CP.new(17)
1651+
assert ctx_poly([1,2,3]) == P([1,2,3],17) == P([1,2,3], ctx_poly)
1652+
assert raises(lambda: ctx_poly([1,2.0,3]), TypeError)
1653+
assert raises(lambda: P([1,2,3], []), TypeError)
1654+
1655+
16111656
def test_arb():
16121657
A = flint.arb
16131658
assert A(3) > A(2.5)
@@ -2211,7 +2256,7 @@ def test_fmpz_mod_poly():
22112256

22122257
f_inv = f.inverse_series_trunc(2)
22132258
assert (f * f_inv) % R_test([0,0,1]) == 1
2214-
assert raises(lambda: R_cmp([0,0,1]).inverse_series_trunc(2), ValueError)
2259+
assert raises(lambda: R_cmp([0,0,1]).inverse_series_trunc(2), ZeroDivisionError)
22152260

22162261
# Resultant
22172262
f1 = R_test([-3, 1])
@@ -2846,6 +2891,50 @@ def setbad(obj, i, val):
28462891
if type(p) == flint.fq_default_poly:
28472892
assert raises(lambda: p.integral(), NotImplementedError)
28482893

2894+
if characteristic == 0:
2895+
assert not hasattr(P(0), "inverse_series_trunc")
2896+
elif composite_characteristic:
2897+
x = P([0, 1])
2898+
if type(x) is flint.fmpz_mod_poly:
2899+
assert (1 + x).inverse_series_trunc(4) == 1 - x + x**2 - x**3
2900+
if characteristic.gcd(3) != 1:
2901+
assert (3 + x).inverse_series_trunc(4) == 1 - x + x**2 - x**3
2902+
else:
2903+
assert (3 + x).inverse_series_trunc(4)\
2904+
== S(1)/3 - S(1)/9*x + S(1)/27*x**2 - S(1)/81*x**3
2905+
elif type(x) is flint.nmod_poly:
2906+
assert raises(lambda: (1 + x).inverse_series_trunc(4), DomainError)
2907+
else:
2908+
assert False
2909+
else:
2910+
x = P([0, 1])
2911+
assert (1 + x).inverse_series_trunc(4) == 1 - x + x**2 - x**3
2912+
assert (3 + x).inverse_series_trunc(4)\
2913+
== S(1)/3 - S(1)/9*x + S(1)/27*x**2 - S(1)/81*x**3
2914+
assert raises(lambda: (1 + x).inverse_series_trunc(-1), ValueError)
2915+
assert raises(lambda: x.inverse_series_trunc(4), ZeroDivisionError)
2916+
2917+
if characteristic == 0:
2918+
assert not hasattr(P(0), "pow_mod")
2919+
elif composite_characteristic:
2920+
pass
2921+
else:
2922+
x = P([0, 1])
2923+
assert (1 + x).pow_mod(4, x**2 + 1) == -4
2924+
assert (3 + x).pow_mod(4, x**2 + 1) == 96*x + 28
2925+
assert x.pow_mod(4, x**2 + 1) == 1
2926+
2927+
assert x.pow_mod(2**127, x - 1) == 1
2928+
assert (1 + x).pow_mod(2**127, x - 1) == pow(2, 2**127, characteristic)
2929+
2930+
if type(x) is not flint.fq_default_poly:
2931+
assert (1 + x).pow_mod(2**127, x - 1, S(1)/2) == pow(2, 2**127, characteristic)
2932+
assert raises(lambda: (1 + x).pow_mod(2**127, x - 1, []), TypeError)
2933+
2934+
assert raises(lambda: (1 + x).pow_mod(4, []), TypeError)
2935+
assert raises(lambda: (1 + x).pow_mod([], x), TypeError)
2936+
assert raises(lambda: (1 + x).pow_mod(-1, x), ValueError)
2937+
28492938

28502939
def _all_mpolys():
28512940
return [
@@ -4045,6 +4134,8 @@ def test_matrices_pow():
40454134
# XXX: Allow unimodular matrices?
40464135
assert raises(lambda: M1234**-1, DomainError)
40474136

4137+
assert raises(lambda: pow(M1234, 2, 3), NotImplementedError)
4138+
40484139
Mr = M([[1, 2, 3], [4, 5, 6]])
40494140
assert raises(lambda: Mr**0, ValueError)
40504141
assert raises(lambda: Mr**1, ValueError)
@@ -4608,6 +4699,8 @@ def test_all_tests():
46084699
test_nmod_mat,
46094700
test_nmod_series,
46104701

4702+
test_nmod_contexts,
4703+
46114704
test_fmpz_mod,
46124705
test_fmpz_mod_dlog,
46134706
test_fmpz_mod_poly,

src/flint/types/fmpq_mat.pyx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ cdef class fmpq_mat(flint_mat):
9999
cdef bint r
100100
if op != 2 and op != 3:
101101
raise TypeError("matrices cannot be ordered")
102-
s = any_as_fmpq_mat(s)
103-
if t is NotImplemented:
104-
return s
105102
t = any_as_fmpq_mat(t)
106103
if t is NotImplemented:
107104
return t
@@ -268,9 +265,6 @@ cdef class fmpq_mat(flint_mat):
268265
def __truediv__(s, t):
269266
return fmpq_mat._div_(s, t)
270267

271-
def __div__(s, t):
272-
return fmpq_mat._div_(s, t)
273-
274268
def inv(self):
275269
"""
276270
Returns the inverse matrix of *self*.
@@ -488,7 +482,7 @@ cdef class fmpq_mat(flint_mat):
488482
if self.nrows() != self.ncols():
489483
raise ValueError("matrix must be square")
490484
if z is not None:
491-
raise TypeError("fmpq_mat does not support modular exponentiation")
485+
raise NotImplementedError("fmpq_mat does not support modular exponentiation")
492486

493487
n = int(n)
494488
if n == 0:

src/flint/types/fmpz_mod_mat.pyx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,10 @@ cdef class fmpz_mod_mat(flint_mat):
467467
return self._scalarmul(e)
468468
return NotImplemented
469469

470-
def __pow__(self, other):
470+
def __pow__(self, other, m=None):
471471
"""``M ** n``: Raise a matrix to an integer power."""
472+
if m is not None:
473+
raise NotImplementedError("fmpz_mod_mat pow: modulo not supported")
472474
if not isinstance(other, int):
473475
return NotImplemented
474476
return self._pow(other)

src/flint/types/fmpz_mod_poly.pyx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,17 +1434,27 @@ cdef class fmpz_mod_poly(flint_poly):
14341434
"""
14351435
cdef fmpz_t f
14361436
cdef fmpz_mod_poly res
1437+
cdef bint is_one
1438+
1439+
if n < 1:
1440+
raise ValueError(f"{n = } must be positive")
1441+
1442+
if self.constant_coefficient() == 0:
1443+
raise ZeroDivisionError("fmpz_mod_poly inverse_series_trunc: zero constant term")
14371444

14381445
res = self.ctx.new_ctype_poly()
14391446
fmpz_init(f)
14401447
fmpz_mod_poly_inv_series_f(
14411448
f, res.val, self.val, n, res.ctx.mod.val
14421449
)
1443-
if not fmpz_is_one(f):
1444-
fmpz_clear(f)
1450+
is_one = fmpz_is_one(f)
1451+
fmpz_clear(f)
1452+
1453+
if not is_one:
14451454
raise ValueError(
14461455
f"Cannot compute inverse series of {self} modulo x^{n}"
14471456
)
1457+
14481458
return res
14491459

14501460
def resultant(self, other):

src/flint/types/fq_default_poly.pyx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,9 @@ cdef class fq_default_poly(flint_poly):
12401240
"""
12411241
cdef fq_default_poly res
12421242

1243+
if n < 1:
1244+
raise ValueError(f"{n = } must be positive")
1245+
12431246
if self.constant_coefficient().is_zero():
12441247
raise ZeroDivisionError("constant coefficient must be invertible")
12451248

src/flint/types/nmod.pxd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ cdef class nmod_ctx:
1111
cdef bint _is_prime
1212

1313
@staticmethod
14-
cdef any_as_nmod_ctx(obj)
14+
cdef nmod_ctx any_as_nmod_ctx(obj)
1515
@staticmethod
16-
cdef _get_ctx(int mod)
16+
cdef nmod_ctx _get_ctx(int mod)
1717
@staticmethod
18-
cdef _new_ctx(ulong mod)
18+
cdef nmod_ctx _new_ctx(ulong mod)
1919

2020
@cython.final
2121
cdef int any_as_nmod(self, mp_limb_t * val, obj) except -1

src/flint/types/nmod.pyx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,27 @@ cdef class nmod_ctx:
4646
@staticmethod
4747
def new(mod):
4848
"""Get an nmod context with modulus ``mod``."""
49-
return nmod_ctx._get_ctx(mod)
49+
return nmod_ctx.any_as_nmod_ctx(mod)
5050

5151
@staticmethod
52-
cdef any_as_nmod_ctx(obj):
52+
cdef nmod_ctx any_as_nmod_ctx(obj):
5353
"""Convert an ``nmod_ctx`` or ``int`` to an ``nmod_ctx``."""
5454
if typecheck(obj, nmod_ctx):
5555
return obj
5656
if typecheck(obj, int):
5757
return nmod_ctx._get_ctx(obj)
58-
return NotImplemented
58+
raise TypeError("Invalid context/modulus for nmod: %s" % obj)
5959

6060
@staticmethod
61-
cdef _get_ctx(int mod):
61+
cdef nmod_ctx _get_ctx(int mod):
6262
"""Retrieve an nmod context from the cache or create a new one."""
6363
ctx = _nmod_ctx_cache.get(mod)
6464
if ctx is None:
6565
ctx = _nmod_ctx_cache.setdefault(mod, nmod_ctx._new_ctx(mod))
6666
return ctx
6767

6868
@staticmethod
69-
cdef _new_ctx(ulong mod):
69+
cdef nmod_ctx _new_ctx(ulong mod):
7070
"""Create a new nmod context."""
7171
cdef nmod_ctx ctx = nmod_ctx.__new__(nmod_ctx)
7272
nmod_init(&ctx.mod, mod)
@@ -160,7 +160,8 @@ cdef class nmod_ctx:
160160
161161
"""
162162
r = self.new_nmod()
163-
self.any_as_nmod(&r.val, val)
163+
if not self.any_as_nmod(&r.val, val):
164+
raise TypeError("cannot create nmod from object of type %s" % type(val))
164165
return r
165166

166167

@@ -174,11 +175,7 @@ cdef class nmod(flint_scalar):
174175
175176
"""
176177
def __init__(self, val, mod):
177-
cdef nmod_ctx ctx
178-
c = nmod_ctx.any_as_nmod_ctx(mod)
179-
if c is NotImplemented:
180-
raise TypeError("Invalid context/modulus for nmod: %s" % mod)
181-
ctx = c
178+
cdef nmod_ctx ctx = nmod_ctx.any_as_nmod_ctx(mod)
182179
if not ctx.any_as_nmod(&self.val, val):
183180
raise TypeError("cannot create nmod from object of type %s" % type(val))
184181
self.ctx = ctx

src/flint/types/nmod_poly.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ cdef class nmod_poly_ctx:
1616
cdef nmod_ctx scalar_ctx
1717

1818
@staticmethod
19-
cdef any_as_nmod_poly_ctx(obj)
19+
cdef nmod_poly_ctx any_as_nmod_poly_ctx(obj)
2020
@staticmethod
2121
cdef nmod_poly_ctx _get_ctx(int mod)
2222
@staticmethod

0 commit comments

Comments
 (0)