Skip to content

Commit e8be1e3

Browse files
committed
Add fmpz_mod_mpoly. Based on fmpz_mpoly
1 parent 53a5dbb commit e8be1e3

3 files changed

Lines changed: 136 additions & 32 deletions

File tree

src/flint/flint_base/flint_base.pyx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,13 @@ cdef class flint_mpoly_context(flint_elem):
200200
return nametup
201201

202202
@classmethod
203-
def get_context(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None):
203+
def create_context_key(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None):
204204
"""
205-
Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable
206-
name string, `names`, or a tuple of variable names, `nametup`.
205+
Create a key for the context cache via the number of variables, the ordering, and
206+
either a variable name string, or a tuple of variable names.
207207
"""
208-
209208
# A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering
210-
# object is not provided. This is pretty obtuse so we check it's type ourselves
209+
# object is not provided. This is pretty obtuse so we check its type ourselves
211210
if not isinstance(ordering, Ordering):
212211
raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering")
213212

@@ -217,6 +216,15 @@ cdef class flint_mpoly_context(flint_elem):
217216
key = nvars, ordering, cls.create_variable_names(nvars, names)
218217
else:
219218
raise ValueError("must provide either `names` or `nametup`")
219+
return key
220+
221+
@classmethod
222+
def get_context(cls, *args, **kwargs):
223+
"""
224+
Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable
225+
name string, `names`, or a tuple of variable names, `nametup`.
226+
"""
227+
key = cls.create_context_key(*args, **kwargs)
220228

221229
ctx = cls._ctx_cache.get(key)
222230
if ctx is None:

src/flint/types/fmpz_mod_mpoly.pxd

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ cdef inline fmpz_mod_mpoly create_fmpz_mod_mpoly(fmpz_mod_mpoly_ctx ctx):
2323

2424
cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context):
2525
cdef fmpz_mod_mpoly_ctx_t val
26+
cdef readonly object __prime_modulus
2627

2728
cdef class fmpz_mod_mpoly(flint_mpoly):
2829
cdef fmpz_mod_mpoly_t val
2930
cdef fmpz_mod_mpoly_ctx ctx
3031
cdef bint _init
3132

3233
cdef class fmpz_mod_mpoly_vec:
33-
cdef fmpz_mod_mpoly_t *val
34+
cdef fmpz_mod_mpoly_struct *val
35+
cdef slong length
3436
cdef fmpz_mod_mpoly_ctx ctx
3537
cdef fmpz_mod_mpoly_struct **double_indirect

src/flint/types/fmpz_mod_mpoly.pyx

Lines changed: 120 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ from flint.flint_base.flint_base cimport (
44
ordering_py_to_c,
55
ordering_c_to_py,
66
)
7+
from flint.flint_base.flint_base import Ordering
78

89
from flint.utils.typecheck cimport typecheck
910
from flint.utils.flint_exceptions import DomainError, IncompatibleContextError
@@ -17,6 +18,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport (
1718
fmpz_mod_mpoly_add_fmpz,
1819
fmpz_mod_mpoly_clear,
1920
fmpz_mod_mpoly_compose_fmpz_mod_mpoly,
21+
fmpz_mod_mpoly_ctx_get_modulus,
2022
fmpz_mod_mpoly_ctx_init,
2123
fmpz_mod_mpoly_degrees_fmpz,
2224
fmpz_mod_mpoly_derivative,
@@ -49,6 +51,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport (
4951
fmpz_mod_mpoly_sub,
5052
fmpz_mod_mpoly_sub_fmpz,
5153
fmpz_mod_mpoly_total_degree_fmpz,
54+
fmpz_mod_mpoly_sqrt,
5255
)
5356
from flint.flintlib.fmpz_mod_mpoly_factor cimport (
5457
fmpz_mod_mpoly_factor,
@@ -77,10 +80,50 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context):
7780

7881
_ctx_cache = _fmpz_mod_mpoly_ctx_cache
7982

80-
def __init__(self, slong nvars, ordering, names):
81-
fmpz_mod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering))
83+
def __init__(self, slong nvars, ordering, names, modulus):
84+
cdef fmpz m
85+
if not typecheck(modulus, fmpz):
86+
m = any_as_fmpz(modulus)
87+
if m is NotImplemented:
88+
raise TypeError(f"modulus ({modulus}) is not coercible to fmpz")
89+
else:
90+
m = modulus
91+
fmpz_mod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering), m.val)
92+
self.__prime_modulus = None
8293
super().__init__(nvars, names)
8394

95+
@classmethod
96+
def create_context_key(
97+
cls,
98+
slong nvars=1,
99+
ordering=Ordering.lex,
100+
modulus = None,
101+
names: Optional[str] = "x",
102+
nametup: Optional[tuple] = None,
103+
):
104+
"""
105+
Create a key for the context cache via the number of variables, the ordering, the modulus, and either a
106+
variable name string, or a tuple of variable names.
107+
"""
108+
# A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering
109+
# object is not provided. This is pretty obtuse so we check its type ourselves
110+
if not isinstance(ordering, Ordering):
111+
raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering")
112+
elif not typecheck(modulus, fmpz):
113+
m = any_as_fmpz(modulus)
114+
if m is NotImplemented:
115+
raise TypeError(f"`modulus` ('{modulus}') is not coercible to fmpz")
116+
else:
117+
modulus = m
118+
119+
if nametup is not None:
120+
key = nvars, ordering, nametup, modulus
121+
elif nametup is None and names is not None:
122+
key = nvars, ordering, cls.create_variable_names(nvars, names), modulus
123+
else:
124+
raise ValueError("must provide either `names` or `nametup`")
125+
return key
126+
84127
def nvars(self):
85128
"""
86129
Return the number of variables in the context
@@ -103,6 +146,36 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context):
103146
"""
104147
return ordering_c_to_py(self.val.minfo.ord)
105148

149+
def modulus(self):
150+
"""
151+
Return the modulus of the context object.
152+
153+
>>> from flint import Ordering
154+
>>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 2, 'w')
155+
>>> ctx.modulus()
156+
2
157+
158+
"""
159+
cdef fmpz m = fmpz.__new__(fmpz)
160+
fmpz_mod_mpoly_ctx_get_modulus(m.val, self.val)
161+
return m
162+
163+
def is_prime(self):
164+
"""
165+
Return whether the modulus is prime
166+
167+
>>> from flint import Ordering
168+
>>> ctx = fmpz_mod_mpoly_ctx.get_context(2**127, Ordering.degrevlex, 'z')
169+
>>> ctx.is_prime()
170+
False
171+
>>> ctx = fmpz_mod_mpoly_ctx.get_context(2**127 - 1, Ordering.degrevlex, 'z')
172+
>>> ctx.is_prime()
173+
True
174+
"""
175+
if self.__prime_modulus is None:
176+
self.__prime_modulus = self.modulus().is_prime()
177+
return self.__prime_modulus
178+
106179
def gen(self, slong i):
107180
"""
108181
Return the `i`th generator of the polynomial ring
@@ -386,7 +459,10 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
386459

387460
def __divmod__(self, other):
388461
cdef fmpz_mod_mpoly res, res2
389-
if typecheck(other, fmpz_mod_mpoly):
462+
463+
if not self.ctx.is_prime():
464+
raise DomainError("division with non-prime modulus is not supported")
465+
elif typecheck(other, fmpz_mod_mpoly):
390466
if not other:
391467
raise ZeroDivisionError("fmpz_mod_mpoly division by zero")
392468
elif (<fmpz_mod_mpoly>self).ctx is not (<fmpz_mod_mpoly>other).ctx:
@@ -409,7 +485,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
409485

410486
def __rdivmod__(self, other):
411487
cdef fmpz_mod_mpoly res, res2
412-
if not self:
488+
if not self.ctx.is_prime():
489+
raise DomainError("division with non-prime modulus is not supported")
490+
elif not self:
413491
raise ZeroDivisionError("fmpz_mod_mpoly division by zero")
414492
other = any_as_fmpz(other)
415493
if other is not NotImplemented:
@@ -422,7 +500,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
422500

423501
def __floordiv__(self, other):
424502
cdef fmpz_mod_mpoly res
425-
if typecheck(other, fmpz_mod_mpoly):
503+
if not self.ctx.is_prime():
504+
raise DomainError("division with non-prime modulus is not supported")
505+
elif typecheck(other, fmpz_mod_mpoly):
426506
if not other:
427507
raise ZeroDivisionError("fmpz_mod_mpoly division by zero")
428508
elif (<fmpz_mod_mpoly>self).ctx is not (<fmpz_mod_mpoly>other).ctx:
@@ -443,7 +523,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
443523

444524
def __rfloordiv__(self, other):
445525
cdef fmpz_mod_mpoly res
446-
if not self:
526+
if not self.ctx.is_prime():
527+
raise DomainError("division with non-prime modulus is not supported")
528+
elif not self:
447529
raise ZeroDivisionError("fmpz_mod_mpoly division by zero")
448530
other = any_as_fmpz(other)
449531
if other is not NotImplemented:
@@ -457,7 +539,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
457539
cdef:
458540
fmpz_mod_mpoly res, div
459541

460-
if typecheck(other, fmpz_mod_mpoly):
542+
if not self.ctx.is_prime():
543+
raise DomainError("division with non-prime modulus is not supported")
544+
elif typecheck(other, fmpz_mod_mpoly):
461545
if not other:
462546
raise ZeroDivisionError("fmpz_mod_mpoly division by zero")
463547
elif self.ctx is not (<fmpz_mod_mpoly>other).ctx:
@@ -484,7 +568,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
484568

485569
def __rtruediv__(self, other):
486570
cdef fmpz_mod_mpoly res
487-
if not self:
571+
if not self.ctx.is_prime():
572+
raise DomainError("division with non-prime modulus is not supported")
573+
elif not self:
488574
raise ZeroDivisionError("fmpz_mod_mpoly division by zero")
489575
o = any_as_fmpz(other)
490576
if o is NotImplemented:
@@ -517,8 +603,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
517603

518604
V = fmpz_vec(args_fmpz, double_indirect=True)
519605
vres = fmpz.__new__(fmpz)
520-
if fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0:
521-
raise ValueError("unreasonably large polynomial") # pragma: no cover
606+
fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val)
522607
return vres
523608

524609
def iadd(self, other):
@@ -693,8 +778,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
693778

694779
fmpz_mod_mpoly_set(res.val, self.val, self.ctx.val)
695780
for i, arg in args:
696-
if fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (<fmpz>arg).val, self.ctx.val) == 0:
697-
raise ValueError("unreasonably large polynomial") # pragma: no cover
781+
fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (<fmpz>arg).val, self.ctx.val)
698782
return res
699783

700784
def compose(self, *args, ctx=None) -> fmpz_mod_mpoly:
@@ -853,7 +937,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
853937
4*x0*x1 + 1
854938
"""
855939
cdef fmpz_mod_mpoly res
856-
if not typecheck(other, fmpz_mod_mpoly):
940+
if not self.ctx.is_prime():
941+
raise DomainError("gcd with non-prime modulus is not supported")
942+
elif not typecheck(other, fmpz_mod_mpoly):
857943
raise TypeError("argument must be a fmpz_mod_mpoly")
858944
elif (<fmpz_mod_mpoly>self).ctx is not (<fmpz_mod_mpoly>other).ctx:
859945
raise IncompatibleContextError(f"{(<fmpz_mod_mpoly>self).ctx} is not {(<fmpz_mod_mpoly>other).ctx}")
@@ -872,6 +958,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
872958
4*x0*x1 + 1
873959
"""
874960
cdef fmpz_mod_mpoly res
961+
if not self.ctx.is_prime():
962+
raise DomainError("square root with non-prime modulus is not supported")
963+
875964
res = create_fmpz_mod_mpoly(self.ctx)
876965

877966
if fmpz_mod_mpoly_sqrt(res.val, self.val, self.ctx.val):
@@ -901,7 +990,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
901990
fmpz_mod_mpoly u
902991

903992
fmpz_mod_mpoly_factor_init(fac, self.ctx.val)
904-
fmpz_mod_mpoly_factor(fac, self.val, self.ctx.val)
993+
if not fmpz_mod_mpoly_factor(fac, self.val, self.ctx.val):
994+
raise RuntimeError("factorisation failed")
905995
res = [0] * fac.num
906996

907997
for i in range(fac.num):
@@ -940,7 +1030,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
9401030
fmpz_mod_mpoly u
9411031

9421032
fmpz_mod_mpoly_factor_init(fac, self.ctx.val)
943-
fmpz_mod_mpoly_factor_squarefree(fac, self.val, self.ctx.val)
1033+
if not fmpz_mod_mpoly_factor_squarefree(fac, self.val, self.ctx.val):
1034+
raise RuntimeError("factorisation failed")
9441035
res = [0] * fac.num
9451036

9461037
for i in range(fac.num):
@@ -1019,20 +1110,23 @@ cdef class fmpz_mod_mpoly_vec:
10191110

10201111
def __cinit__(self, iterable_or_len, fmpz_mod_mpoly_ctx ctx, bint double_indirect = False):
10211112
if isinstance(iterable_or_len, int):
1022-
length = iterable_or_len
1113+
self.length = iterable_or_len
10231114
else:
1024-
length = len(iterable_or_len)
1115+
self.length = len(iterable_or_len)
10251116

10261117
self.ctx = ctx
1027-
fmpz_mod_mpoly_vec_init(self.val, length, self.ctx.val)
1118+
1119+
self.val = <fmpz_mod_mpoly_struct *> libc.stdlib.malloc(self.length * sizeof(fmpz_mod_mpoly_struct))
1120+
for i in range(self.length):
1121+
fmpz_mod_mpoly_init(&self.val[i], self.ctx.val)
10281122

10291123
if double_indirect:
1030-
self.double_indirect = <fmpz_mod_mpoly_struct **> libc.stdlib.malloc(length * sizeof(fmpz_mod_mpoly_struct *))
1124+
self.double_indirect = <fmpz_mod_mpoly_struct **> libc.stdlib.malloc(self.length * sizeof(fmpz_mod_mpoly_struct *))
10311125
if self.double_indirect is NULL:
10321126
raise MemoryError("malloc returned a null pointer") # pragma: no cover
10331127

1034-
for i in range(length):
1035-
self.double_indirect[i] = fmpz_mod_mpoly_vec_entry(self.val, i)
1128+
for i in range(self.length):
1129+
self.double_indirect[i] = &self.val[i]
10361130
else:
10371131
self.double_indirect = NULL
10381132

@@ -1050,17 +1144,17 @@ cdef class fmpz_mod_mpoly_vec:
10501144
def __getitem__(self, x):
10511145
if not isinstance(x, int):
10521146
raise TypeError("index is not integer")
1053-
elif not 0 <= x < self.val.length:
1147+
elif not 0 <= x < self.length:
10541148
raise IndexError("index out of range")
10551149

10561150
cdef fmpz_mod_mpoly z = create_fmpz_mod_mpoly(self.ctx)
1057-
fmpz_mod_mpoly_set(z.val, f&self.val[x], self.ctx.val)
1151+
fmpz_mod_mpoly_set(z.val, &self.val[x], self.ctx.val)
10581152
return z
10591153

10601154
def __setitem__(self, x, y):
10611155
if not isinstance(x, int):
10621156
raise TypeError("index is not integer")
1063-
elif not 0 <= x < self.val.length:
1157+
elif not 0 <= x < self.length:
10641158
raise IndexError("index out of range")
10651159
elif not typecheck(y, fmpz_mod_mpoly):
10661160
raise TypeError("argument is not fmpz_mod_mpoly")
@@ -1073,8 +1167,8 @@ cdef class fmpz_mod_mpoly_vec:
10731167
return self.val.length
10741168

10751169
def __str__(self):
1076-
s = [None] * self.val.length
1077-
for i in range(self.val.length):
1170+
s = [None] * self.length
1171+
for i in range(self.length):
10781172
x = create_fmpz_mod_mpoly(self.ctx)
10791173
fmpz_mod_mpoly_set(x.val, &self.val[x], self.ctx.val)
10801174
s[i] = str(x)

0 commit comments

Comments
 (0)