@@ -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
89from flint.utils.typecheck cimport typecheck
910from 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)
5356from 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