@@ -7,15 +7,16 @@ from flint.flintlib.types.flint cimport (
77from flint.utils.flint_exceptions import DomainError
88from flint.flintlib.types.mpoly cimport ordering_t
99from flint.flint_base.flint_context cimport thectx
10- from flint.flint_base.flint_base cimport Ordering
1110from flint.utils.typecheck cimport typecheck
1211cimport libc.stdlib
1312
1413from typing import Optional
14+ from collections.abc import Iterable
1515from flint.utils.flint_exceptions import IncompatibleContextError
1616
1717from flint.types.fmpz cimport fmpz, any_as_fmpz
1818
19+ import enum
1920
2021FLINT_BITS = _FLINT_BITS
2122FLINT_VERSION = _FLINT_VERSION.decode(" ascii" )
@@ -265,21 +266,42 @@ cdef class flint_poly(flint_elem):
265266 raise NotImplementedError (" Complex roots are not supported for this polynomial" )
266267
267268
269+ class Ordering (enum .StrEnum ):
270+ lex = " lex"
271+ deglex = " deglex"
272+ degrevlex = " degrevlex"
273+
274+
275+ cdef ordering_t ordering_py_to_c(ordering):
276+ if ordering == Ordering.lex:
277+ return ordering_t.ORD_LEX
278+ elif ordering == Ordering.deglex:
279+ return ordering_t.ORD_DEGLEX
280+ elif ordering == Ordering.degrevlex:
281+ return ordering_t.ORD_DEGREVLEX
282+
283+ cdef ordering_c_to_py(ordering_t ordering):
284+ if ordering == ordering_t.ORD_LEX:
285+ return Ordering.lex
286+ elif ordering == ordering_t.ORD_DEGLEX:
287+ return Ordering.deglex
288+ elif ordering == ordering_t.ORD_DEGREVLEX:
289+ return Ordering.degrevlex
290+ else :
291+ raise ValueError (" unimplemented term order %d " % ordering)
292+
293+
268294cdef class flint_mpoly_context(flint_elem):
269295 """
270296 Base class for multivariate ring contexts
271297 """
272298
273299 _ctx_cache = None
274300
275- def __init__ (self , int nvars , names ):
276- if nvars < 0 :
277- raise ValueError (" cannot have a negative amount of variables" )
278- elif len (names) != nvars:
279- raise ValueError (" number of variables must match number of variable names" )
301+ def __init__ (self , names: Iterable[str]):
280302 self .py_names = tuple (name.encode(" ascii" ) if not isinstance (name, bytes) else name for name in names)
281- self .c_names = < const char ** > libc.stdlib.malloc(nvars * sizeof(const char * ))
282- for i in range (nvars ):
303+ self .c_names = < const char ** > libc.stdlib.malloc(len (names) * sizeof(const char * ))
304+ for i in range (len (names) ):
283305 self .c_names[i] = self .py_names[i]
284306
285307 def __dealloc__ (self ):
@@ -292,18 +314,18 @@ cdef class flint_mpoly_context(flint_elem):
292314 def __repr__ (self ):
293315 return f" {self.__class__.__name__}({self.nvars()}, '{repr(self.ordering())}', {self.names()})"
294316
295- def name (self , long i ):
317+ def name (self , i: int ):
296318 if not 0 <= i < len (self .py_names):
297319 raise IndexError (" variable name index out of range" )
298320 return self .py_names[i].decode(" ascii" )
299321
300- def names (self ):
322+ def names (self ) -> tuple[str] :
301323 return tuple(name.decode("ascii") for name in self.py_names )
302324
303325 def gens(self ):
304326 return tuple (self .gen(i) for i in range (self .nvars()))
305327
306- def variable_to_index (self , var: Union[int , str]):
328+ def variable_to_index (self , var: Union[int , str]) -> int :
307329 """Convert a variable name string or possible index to its index in the context."""
308330 if isinstance(var , str ):
309331 try :
@@ -320,48 +342,55 @@ cdef class flint_mpoly_context(flint_elem):
320342 return i
321343
322344 @staticmethod
323- def create_variable_names (slong nvars , names: str ) :
345+ def create_variable_names (names: Iterable[ str | tuple[ str , int]]) -> tuple[str] :
324346 """
325- Create a tuple of variable names based on the comma separated ``names`` string.
326-
327- If ``names`` contains a single value, and ``nvars`` > 1, then the variables are numbered, e.g.
347+ Create a tuple of variable names based off either ``Iterable[str]``,
348+ ``tuple[str , int]``, or ``Iterable[tuple[str , int]]``.
328349
329- >>> flint_mpoly_context.create_variable_names(3, "x")
350+ >>> flint_mpoly_context.create_variable_names([('x', 3), 'y'])
351+ ('x0', 'x1', 'x2', 'y')
352+ >>> flint_mpoly_context.create_variable_names(('x', 3))
330353 ('x0', 'x1', 'x2')
331-
332354 """
333- nametup = tuple (name.strip() for name in names.split(' ,' ))
334- if len (nametup) != nvars:
335- if len (nametup) == 1 :
336- nametup = tuple (nametup[0 ] + str (i) for i in range (nvars))
355+ res: list[str] = []
356+
357+ # Provide a convenience method to avoid having to pass a nested tuple
358+ if len(names ) == 2 and isinstance(names[0], str ) and isinstance(names[1], int ):
359+ names = (names,)
360+
361+ for name in names:
362+ if isinstance (name, str ):
363+ res.append(name)
337364 else :
338- raise ValueError (" number of variables does not equal number of names" )
339- return nametup
365+ base, num = name
366+ if num < 0 :
367+ raise ValueError (" cannot create a negative number of variables" )
368+ res.extend(base + str (i) for i in range (num))
369+
370+ return tuple (res)
340371
341372 @classmethod
342- def create_context_key (cls , slong nvars = 1 , ordering = Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None ):
373+ def create_context_key (
374+ cls ,
375+ names: Iterable[str | tuple[str , int]],
376+ ordering: Ordering | str = Ordering.lex
377+ ):
343378 """
344- Create a key for the context cache via the number of variables, the ordering, and
345- either a variable name string, or a tuple of variable names.
379+ Create a key for the context cache via the variable names and the ordering.
346380 """
347381 # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering
348382 # object is not provided. This is pretty obtuse so we check its type ourselves
349- if not isinstance (ordering, Ordering):
350- raise TypeError (f" 'ordering' ('{ordering}') is not an instance of flint.Ordering" )
383+ # if not isinstance(ordering, Ordering):
384+ # raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering")
351385
352- if nametup is not None :
353- key = nvars, ordering, nametup
354- elif nametup is None and names is not None :
355- key = nvars, ordering, cls .create_variable_names(nvars, names)
356- else :
357- raise ValueError (" must provide either 'names' or 'nametup'" )
358- return key
386+ return cls .create_variable_names(names), Ordering(ordering) if not isinstance (ordering, Ordering) else ordering
359387
360388 @classmethod
361389 def get_context (cls , *args , **kwargs ):
362390 """
363- Retrieve a context via the number of variables, ``nvars``, the ordering, ``ordering``, and either a variable
364- name string, ``names``, or a tuple of variable names, ``nametup``.
391+ Retrieve or create a context via generator names, ``names`` and the ordering, ``ordering``.
392+
393+ See ``create_variable_names`` for naming schemes.
365394 """
366395 key = cls .create_context_key(* args, ** kwargs)
367396
@@ -373,10 +402,8 @@ cdef class flint_mpoly_context(flint_elem):
373402 @classmethod
374403 def from_context (cls , ctx: flint_mpoly_context ):
375404 return cls .get_context(
376- nvars = ctx.nvars(),
377405 ordering = ctx.ordering(),
378- names = None ,
379- nametup = ctx.names()
406+ names = ctx.names(),
380407 )
381408
382409 def _any_as_scalar (self , other ):
@@ -410,35 +437,29 @@ cdef class flint_mpoly_context(flint_elem):
410437 return self .from_dict({tuple (exp_vec): coeff})
411438
412439cdef class flint_mod_mpoly_context(flint_mpoly_context):
413- def __init__ (self , nvars , names , prime_modulus ):
414- super ().__init__(nvars, names)
440+ def __init__ (self , names , prime_modulus ):
441+ super ().__init__(names)
415442 self .__prime_modulus = < bint> prime_modulus
416443
417444 @classmethod
418445 def create_context_key (
419446 cls ,
420- slong nvars = 1 ,
421- ordering = Ordering.lex,
422- modulus = None ,
423- names: Optional[str] = "x",
424- nametup: Optional[tuple] = None ,
447+ names: Iterable[str | tuple[str , int]],
448+ modulus ,
449+ ordering: Ordering | str = Ordering.lex
425450 ):
426451 """
427- Create a key for the context cache via the number of variables, the ordering, the modulus, and either a
428- variable name string, or a tuple of variable names.
452+ Create a key for the context cache via the variable names, modulus, and the ordering.
429453 """
430- # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering
431- # object is not provided. This is pretty obtuse so we check its type ourselves
432- if not isinstance (ordering, Ordering):
433- raise TypeError (f" 'ordering' ('{ordering}') is not an instance of flint.Ordering" )
454+ return * super ().create_context_key(names, ordering), modulus
434455
435- if nametup is not None :
436- key = nvars, ordering, nametup, modulus
437- elif nametup is None and names is not None :
438- key = nvars, ordering, cls .create_variable_names(nvars, names), modulus
439- else :
440- raise ValueError ( " must provide either 'names' or 'nametup' " )
441- return key
456+ @ classmethod
457+ def from_context ( cls , ctx: flint_mod_mpoly_context ):
458+ return cls .get_context(
459+ names = ctx. names(),
460+ modulus = ctx.modulus(),
461+ ordering = ctx.ordering(),
462+ )
442463
443464 def is_prime (self ):
444465 """
@@ -898,26 +919,3 @@ cdef class flint_mat(flint_elem):
898919
899920 # supports mpmath conversions
900921 tolist = table
901-
902-
903- cdef ordering_t ordering_py_to_c(ordering): # Cython does not like an "Ordering" type hint here
904- if not isinstance (ordering, Ordering):
905- raise TypeError (f" 'ordering' ('{ordering}') is not an instance of flint.Ordering" )
906-
907- if ordering == Ordering.lex:
908- return ordering_t.ORD_LEX
909- elif ordering == Ordering.deglex:
910- return ordering_t.ORD_DEGLEX
911- elif ordering == Ordering.degrevlex:
912- return ordering_t.ORD_DEGREVLEX
913-
914-
915- cdef ordering_c_to_py(ordering_t ordering):
916- if ordering == ordering_t.ORD_LEX:
917- return Ordering.lex
918- elif ordering == ordering_t.ORD_DEGLEX:
919- return Ordering.deglex
920- elif ordering == ordering_t.ORD_DEGREVLEX:
921- return Ordering.degrevlex
922- else :
923- raise ValueError (" unimplemented term order %d " % ordering)
0 commit comments