diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py index d9984a656b1..998f5fc8951 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py @@ -62,8 +62,12 @@ def __init__(self, R): self._R = R names = R.variable_names() self._one_element = self.element_class(self, R.one()) + category = R.category() + if "Enumerated" in category.axioms(): + # this ring should also be countable, but __iter__ is not yet implemented + category = category._without_axiom("Enumerated") CommutativeRing.__init__(self, R.base_ring(), names=names, - category=R.category()) + category=category) ernames = [] for n in names: ernames.append(n) diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 963fa8de570..2153e019235 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -153,6 +153,7 @@ from sage.rings.ring import Ring, CommutativeRing from sage.structure.element import RingElement import sage.rings.rational_field as rational_field +from sage.rings.infinity import Infinity from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer @@ -274,7 +275,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, and Category of commutative algebras over (finite enumerated fields and subquotients of monoids and quotients of semigroups) - and Category of infinite sets + and Category of infinite enumerated sets TESTS: @@ -287,7 +288,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, Check that category for zero ring:: sage: PolynomialRing(Zmod(1), 'x').category() - Category of finite commutative rings + Category of finite enumerated commutative rings Check ``is_finite`` inherited from category (:issue:`24432`):: @@ -306,7 +307,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, # We trust that, if category is given, it is useful and does not need to be joined # with the default category if base_ring.is_zero(): - category = categories.rings.Rings().Commutative().Finite() + category = categories.rings.Rings().Commutative().Finite().Enumerated() else: defaultcat = polynomial_default_category(base_ring.category(), 1) category = check_default_category(defaultcat, category) @@ -1033,6 +1034,65 @@ def __hash__(self): h = self._cached_hash = hash((self.base_ring(),self.variable_name())) return h + def __iter__(self): + r""" + Return iterator over the elements of this polynomial ring. + + EXAMPLES:: + + sage: from itertools import islice + sage: R. = GF(3)[] + sage: list(islice(iter(R), 10)) + [0, 1, 2, x, x + 1, x + 2, 2*x, 2*x + 1, 2*x + 2, x^2] + + TESTS:: + + sage: R. = Integers(1)[] + sage: [*R] + [0] + sage: R. = QQ[] + sage: list(islice(iter(R), 10)) # when this is implemented add Enumerated() to category(R) + Traceback (most recent call last): + ... + NotImplementedError: iteration over infinite base ring not yet implemented + """ + # adapted from sage.modules.free_module.FreeModule_generic.__iter__ + R = self.base_ring() + if R.cardinality() == Infinity: + raise NotImplementedError("iteration over infinite base ring not yet implemented") + iters = [] + zero = R.zero() + v = [] + n = 0 + yield self.zero() + if R.is_zero(): + return + while True: + if n == len(iters): + iters.append(iter(R)) + v.append(next(iters[n])) + assert v[n] == zero, ("first element of iteration must be zero otherwise result " + "of this and free module __iter__ will be incorrect") + try: + v[n] = next(iters[n]) + yield self(v) + n = 0 + except StopIteration: + iters[n] = iter(R) + v[n] = next(iters[n]) + assert v[n] == zero + n += 1 + + def _an_element_(self): + """ + Return an arbitrary element of this polynomial ring. + + Strictly speaking this is not necessary because it is already provided by the category + framework, but before :issue:`39399` this returns the generator, we keep the behavior + because it is more convenient. + """ + return self.gen() + def _repr_(self): try: return self._cached_repr diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 78074c8a1d2..f89ffdbc90a 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -892,6 +892,7 @@ def _multi_variate(base_ring, names, sparse=None, order='degrevlex', implementat from sage import categories from sage.categories.algebras import Algebras # Some fixed categories, in order to avoid the function call overhead +_EnumeratedSets = categories.sets_cat.Sets().Enumerated() _FiniteSets = categories.sets_cat.Sets().Finite() _InfiniteSets = categories.sets_cat.Sets().Infinite() _EuclideanDomains = categories.euclidean_domains.EuclideanDomains() @@ -938,12 +939,27 @@ def polynomial_default_category(base_ring_category, n_variables): True sage: QQ['s']['t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ['s'].category()).WithBasis().Infinite() True + + TESTS:: + + sage: category(GF(7)['x']) + Join of Category of euclidean domains + and Category of algebras with basis over + (finite enumerated fields and subquotients of monoids + and quotients of semigroups) + and Category of commutative algebras over + (finite enumerated fields and subquotients of monoids + and quotients of semigroups) + and Category of infinite enumerated sets """ category = Algebras(base_ring_category).WithBasis() if n_variables: # here we assume the base ring to be nonzero category = category.Infinite() + if base_ring_category.is_subcategory(_FiniteSets) and n_variables == 1: + # base_ring_category.is_subcategory(_EnumeratedSets) suffices but this is not yet implemented + category = category.Enumerated() else: if base_ring_category.is_subcategory(_Fields): category = category & _Fields