Repository: sagemath/sage

Issue:
bug in `revert` method for lazy power series
### Is there an existing issue for this?

- [X] I have searched the existing issues for a bug report that matches the one I want to file, without success.

### Did you read the documentation and troubleshoot guide?

- [X] I have read the documentation and troubleshoot guide

### Environment

```markdown
- **OS**:
- **Sage Version**: 10.0.beta3
```


### Steps To Reproduce

_No response_

### Expected Behavior

```
sage: L = LazyPowerSeriesRing(QQ, 'z')
sage: f = lambda n: 1 if ZZ(n).is_power_of(2) else 0
sage: lazy_f = L(f); lazy_f
z + z^2 + z^4 + O(z^7)
sage: lazy_f.revert()
z - z^2 + 2*z^3 - 6*z^4 + 20*z^5 - 70*z^6 + 256*z^7 + O(z^8)
```

### Actual Behavior

```
sage: L = LazyPowerSeriesRing(QQ, 'z')
sage: f = lambda n: 1 if ZZ(n).is_power_of(2) else 0
sage: lazy_f = L(f); lazy_f
z + z^2 + z^4 + O(z^7)
sage: lazy_f.revert()
<repr(<sage.rings.lazy_series_ring.LazyPowerSeriesRing_with_category.element_class at 0x7f3209489720>) failed: ValueError: inverse does not exist>
```

### Additional Information

Strangely, when we ask for any slice of the coefficients, then method starts to work:

```
sage: L = LazyPowerSeriesRing(QQ, 'z')
sage: f = lambda n: 1 if ZZ(n).is_power_of(2) else 0
sage: lazy_f = L(f); lazy_f
z + z^2 + z^4 + O(z^7)
sage: lazy_f.revert()
<repr(<sage.rings.lazy_series_ring.LazyPowerSeriesRing_with_category.element_class at 0x7f3209489720>) failed: ValueError: inverse does not exist>
sage: lazy_f[:1]
[]
sage: lazy_f.revert()
z - z^2 + 2*z^3 - 6*z^4 + 20*z^5 - 70*z^6 + 256*z^7 + O(z^8)
```

@mantepse @dcoudert @tscrim 
improve approximate order on __getitem__ calls
<div id="comment:0"></div>

We improve the approximate order of a stream whenever `Stream_inexact.__getitem__` is called.

This may slow down `Stream_inexact.__getitem__` but should speed up other operations.

It must be checked carefully whether this is an overall improvement or not.

Depends on #34552

CC:  @tscrim

Component: **combinatorics**

Keywords: **LazyPowerSeries**

Author: **Martin Rubey**

Branch/Commit: **[u/mantepse/improve_approximate_order_on___getitem___calls](https://github.com/sagemath/sagetrac-mirror/tree/u/mantepse/improve_approximate_order_on___getitem___calls) @ [`5a2cb8b`](https://github.com/sagemath/sagetrac-mirror/commit/5a2cb8be80981af79580cb5359205a33ffabff02)**

_Issue created by migration from https://trac.sagemath.org/ticket/34556_




Discussion:

Branch: **[u/mantepse/improve_approximate_order_on___getitem___calls](https://github.com/sagemath/sagetrac-mirror/tree/u/mantepse/improve_approximate_order_on___getitem___calls)**

Resolving patch:
diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py
--- a/src/sage/data_structures/stream.py
+++ b/src/sage/data_structures/stream.py
@@ -122,13 +122,13 @@ class Stream():
         If an approximate order or even the true order is known, it
         must be set after calling ``super().__init__``.
 
-        Otherwise, a lazy attribute `_approximate_order` has to be
+        Otherwise, a lazy attribute ``_approximate_order`` has to be
         defined.  Any initialization code depending on the
         approximate orders of input streams can be put into this
         definition.
 
         However, keep in mind that (trivially) this initialization
-        code is not executed if `_approximate_order` is set to a
+        code is not executed if ``_approximate_order`` is set to a
         value before it is accessed.
 
     """
@@ -159,11 +159,15 @@ def _approximate_order(self):
 
     def __ne__(self, other):
         """
-        Check inequality of ``self`` and ``other``.
+        Return whether ``self`` and ``other`` are known to be different.
 
         The default is to always return ``False`` as it usually
         cannot be decided whether they are equal.
 
+        INPUT:
+
+        - ``other`` -- a stream
+
         EXAMPLES::
 
             sage: from sage.data_structures.stream import Stream
@@ -172,6 +176,7 @@ def __ne__(self, other):
             False
             sage: CS != Stream(-2)
             False
+
         """
         return False
 
@@ -204,14 +209,7 @@ class Stream_inexact(Stream):
     - ``is_sparse`` -- boolean; whether the implementation of the stream is sparse
     - ``true_order`` -- boolean; if the approximate order is the actual order
 
-    .. TODO::
-
-        The ``approximate_order`` is currently only updated when
-        invoking :meth:`order`.  It might make sense to update it
-        whenever the coefficient one larger than the current
-        ``approximate_order`` is computed, since in some methods this
-        will allow shortcuts.
-
+    If the cache is dense, it begins with the first non-zero term.
     """
     def __init__(self, is_sparse, true_order):
         """
@@ -235,27 +233,6 @@ def __init__(self, is_sparse, true_order):
             self._cache = list()
             self._iter = self.iterate_coefficients()
 
-    @lazy_attribute
-    def _offset(self):
-        """
-        Return the offset of a stream with a dense cache.
-
-        EXAMPLES::
-
-            sage: from sage.data_structures.stream import Stream_function
-            sage: f = Stream_function(lambda n: n, False, -3)
-            sage: f._offset
-            -3
-            sage: [f[i] for i in range(-3, 5)]
-            [-3, -2, -1, 0, 1, 2, 3, 4]
-            sage: f._cache
-            [-3, -2, -1, 0, 1, 2, 3, 4]
-        """
-        # self[n] = self._cache[n-self._offset]
-        if self._is_sparse:
-            raise ValueError("_offset is only for dense streams")
-        return self._approximate_order
-
     def is_nonzero(self):
         r"""
         Return ``True`` if and only if the cache contains a nonzero element.
@@ -364,38 +341,67 @@ def __getitem__(self, n):
             sage: [f[i] for i in range(10)]
             [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
             sage: f._cache
-            {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
+            {1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
 
             sage: f = Stream_function(lambda n: n^2, False, 0)
             sage: f[3]
             9
             sage: f._cache
-            [0, 1, 4, 9]
+            [1, 4, 9]
             sage: [f[i] for i in range(10)]
             [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
             sage: f._cache
-            [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+            [1, 4, 9, 16, 25, 36, 49, 64, 81]
         """
         if n < self._approximate_order:
             return ZZ.zero()
 
         if self._is_sparse:
             try:
-                c = self._cache[n]
+                return self._cache[n]
             except KeyError:
-                c = self.get_coefficient(n)
+                pass
+
+            c = self.get_coefficient(n)
+            if self._true_order or n > self._approximate_order:
                 self._cache[n] = c
-        else:
-            i = n - self._offset
-            if i >= len(self._cache):
-                a = len(self._cache) + self._offset
-                # It is important to extend by generator:
-                # self._iter might recurse, and thereby extend the
-                # cache itself, too.
-                self._cache.extend(next(self._iter) for _ in range(a, n+1))
-            c = self._cache[i]
+                return c
 
-        return c
+            if c:
+                self._true_order = True
+                self._cache[n] = c
+                return c
+
+            # self._approximate_order is not in self._cache if
+            # self._true_order is False
+            ao = self._approximate_order + 1
+            while ao in self._cache:
+                if self._cache[ao]:
+                    self._true_order = True
+                    break
+                ao += 1
+            self._approximate_order = ao
+            return c
+
+        # Dense implementation
+        while not self._true_order and n >= self._approximate_order:
+            c = next(self._iter)
+            if c:
+                self._true_order = True
+                self._cache.append(c)
+            else:
+                self._approximate_order += 1
+
+        if self._true_order:
+            # It is important to extend by generator:
+            # self._iter might recurse, and thereby extend the
+            # cache itself, too.
+            i = n - self._approximate_order
+            self._cache.extend(next(self._iter)
+                               for _ in range(i - len(self._cache) + 1))
+            return self._cache[i]
+
+        return ZZ.zero()
 
     def iterate_coefficients(self):
         """
@@ -427,48 +433,37 @@ def order(self):
             sage: f = Stream_function(lambda n: n, True, 0)
             sage: f.order()
             1
+
+        TESTS::
+
+            sage: f = Stream_function(lambda n: n*(n+1), False, -1)
+            sage: f.order()
+            1
+            sage: f._true_order
+            True
+
+            sage: f = Stream_function(lambda n: n*(n+1), True, -1)
+            sage: f.order()
+            1
+            sage: f._true_order
+            True
         """
         if self._true_order:
             return self._approximate_order
-        if self._is_sparse:
-            n = self._approximate_order
-            cache = self._cache
-            while True:
-                if n in cache:
-                    if cache[n]:
-                        self._approximate_order = n
-                        self._true_order = True
-                        return n
-                    n += 1
-                else:
-                    if self[n]:
-                        self._approximate_order = n
-                        self._true_order = True
-                        return n
-                    n += 1
-        else:
-            n = self._approximate_order
-            cache = self._cache
-            while True:
-                if n - self._offset < len(cache):
-                    if cache[n - self._offset]:
-                        self._approximate_order = n
-                        self._true_order = True
-                        return n
-                    n += 1
-                else:
-                    if self[n]:
-                        self._approximate_order = n
-                        self._true_order = True
-                        return n
-                    n += 1
+        n = self._approximate_order
+        while not self[n]:
+            n += 1
+        return n
 
     def __ne__(self, other):
         """
-        Check inequality of ``self`` and ``other``.
+        Return whether ``self`` and ``other`` are known to be different.
+
+        Only the elements in the caches are considered.
 
-        Check if there are any differences in the caches to see if they
-        are known to be not equal.
+        INPUT:
+
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -534,31 +529,30 @@ def __ne__(self, other):
             True
             sage: g != f
             True
+
         """
+        # TODO: more cases, in particular mixed implementations,
+        # could be detected
         if not isinstance(other, Stream_inexact):
             return False
 
-        if self._is_sparse:
+        if self._is_sparse and other._is_sparse:
             for i in self._cache:
                 if i in other._cache and other._cache[i] != self._cache[i]:
                     return True
-        else:  # they are dense
-            # Make ``self`` have the smaller approximate order.
-            if self._approximate_order > other._approximate_order:
-                self, other = other, self
-            saorder = self._approximate_order
-            soffset = self._offset
-            oaorder = other._approximate_order
-            ooffset = other._offset
-            end = min(oaorder, soffset + len(self._cache))
-            for i in range(saorder, end):
-                if self._cache[i-soffset]:
-                    return True
-            # now check all known values
-            end = min(soffset + len(self._cache), ooffset + len(other._cache))
-            for i in range(oaorder, end):
-                if self._cache[i-soffset] != other._cache[i-ooffset]:
-                    return True
+
+        elif not self._is_sparse and not other._is_sparse:
+            if ((self._true_order
+                 and other._approximate_order > self._approximate_order)
+                or (other._true_order
+                    and self._approximate_order > other._approximate_order)):
+                return True
+
+            if not self._true_order or not other._true_order:
+                return False
+
+            if any(i != j for i, j in zip(self._cache, other._cache)):
+                return True
 
         return False
 
@@ -763,7 +757,9 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test the equality between ``self`` and ``other``.
+        Return whether ``self`` and ``other`` are known to be equal.
+
+        If ``other`` is also exact, equality is computable.
 
         INPUT:
 
@@ -795,6 +791,7 @@ def __eq__(self, other):
             sage: t = Stream_exact([2], order=-1, degree=5, constant=1)
             sage: s == t
             False
+
         """
         return (isinstance(other, type(self))
                 and self._degree == other._degree
@@ -804,8 +801,10 @@ def __eq__(self, other):
 
     def __ne__(self, other):
         """
-        Test inequality between ``self`` and ``other``, where
-        other is exact or inexact, but not zero.
+        Return whether ``self`` and ``other`` are known to be different.
+
+        The argument ``other`` may be exact or inexact, but is
+        assumed to be non-zero.
 
         INPUT:
 
@@ -850,9 +849,12 @@ def __ne__(self, other):
                 if self[i] != other._cache[i]:
                     return True
         else:
-            if other._offset > self._approximate_order:
-                return False
-            return any(self[i] != c for i, c in enumerate(other._cache, other._offset))
+            if other._true_order:
+                return any(self[i] != c
+                           for i, c in enumerate(other._cache,
+                                                 other._approximate_order))
+            if other._approximate_order > self._approximate_order:
+                return True
 
         return False
 
@@ -1092,11 +1094,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test equality.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a stream of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -1176,11 +1178,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test equality.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a :class:`Stream` of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -1239,11 +1241,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test the equality between ``self`` and ``other``.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a :class:`Stream` of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -1328,7 +1330,11 @@ def order(self):
 
     def __eq__(self, other):
         """
-        Check equality of ``self`` and ``other``.
+        Return whether ``self`` and ``other`` are known to be equal.
+
+        INPUT:
+
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -1853,16 +1859,19 @@ def get_coefficient(self, n):
         fv = self._left._approximate_order
         gv = self._right._approximate_order
         if n < 0:
-            return sum(self._left[i] * self._neg_powers[-i][n]
-                       for i in range(fv, n // gv + 1))
+            return sum(f_coeff_i * self._neg_powers[-i][n]
+                       for i in range(fv, n // gv + 1)
+                       if (f_coeff_i := self._left[i]))
         # n > 0
         while len(self._pos_powers) <= n // gv:
             # TODO: possibly we always want a dense cache here?
             self._pos_powers.append(Stream_cauchy_mul(self._pos_powers[-1], self._right, self._is_sparse))
-        ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(fv, 0))
+        ret = sum(f_coeff_i * self._neg_powers[-i][n] for i in range(fv, 0)
+                  if (f_coeff_i := self._left[i]))
         if n == 0:
             ret += self._left[0]
-        return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // gv+1))
+        return ret + sum(f_coeff_i * self._pos_powers[i][n] for i in range(1, n // gv + 1)
+                         if (f_coeff_i := self._left[i]))
 
 
 class Stream_plethysm(Stream_binary):
@@ -2230,11 +2239,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test equality.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a stream of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -2685,11 +2694,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test equality.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a stream of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -2800,11 +2809,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test equality.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a stream of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
@@ -2929,11 +2938,11 @@ def __hash__(self):
 
     def __eq__(self, other):
         """
-        Test equality.
+        Return whether ``self`` and ``other`` are known to be equal.
 
         INPUT:
 
-        - ``other`` -- a stream of coefficients
+        - ``other`` -- a stream
 
         EXAMPLES::
 
diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py
--- a/src/sage/rings/lazy_series.py
+++ b/src/sage/rings/lazy_series.py
@@ -39,7 +39,7 @@
     sage: s.coefficient(10)
     10
     sage: s._coeff_stream._cache
-    {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 10: 10}
+    {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 10: 10}
 
 Using the dense implementation, all coefficients up to the required
 coefficient are computed. ::
@@ -50,7 +50,7 @@
     sage: s.coefficient(10)
     10
     sage: s._coeff_stream._cache
-    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
 We can do arithmetic with lazy power series::
 
@@ -94,7 +94,7 @@
     sage: h.parent()
     Lazy Laurent Series Ring in z over Rational Field
     sage: h
-    4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + O(z^7)
+    4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + 130*z^7 + O(z^8)
     sage: hinv = h^-1; hinv
     1/4*z^-1 - 3/8 + 1/16*z - 17/32*z^2 + 5/64*z^3 - 29/128*z^4 + 165/256*z^5 + O(z^6)
     sage: hinv.valuation()
@@ -366,7 +366,7 @@ def __getitem__(self, n):
             sage: f[:3]
             []
             sage: f._coeff_stream._cache
-            {1: 0, 2: 0}
+            {}
         """
         R = self.parent()._internal_poly_ring.base_ring()
         coeff_stream = self._coeff_stream
@@ -489,7 +489,7 @@ def map_coefficients(self, f):
             sage: m = L(lambda n: n, valuation=0); m
             z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7)
             sage: m.map_coefficients(lambda c: c + 1)
-            2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7)
+            2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + 8*z^7 + O(z^8)
 
         Similarly for Dirichlet series::
 
@@ -497,7 +497,7 @@ def map_coefficients(self, f):
             sage: s = L(lambda n: n-1); s
             1/(2^z) + 2/3^z + 3/4^z + 4/5^z + 5/6^z + 6/7^z + O(1/(8^z))
             sage: s.map_coefficients(lambda c: c + 1)
-            2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + O(1/(8^z))
+            2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + 8/8^z + O(1/(9^z))
 
         Similarly for multivariate power series::
 
@@ -540,7 +540,7 @@ def map_coefficients(self, f):
             sage: m = L(lambda n: n, valuation=0); m
             z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7)
             sage: m.map_coefficients(lambda c: c + 1)
-            2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7)
+            2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + 8*z^7 + O(z^8)
 
         Test the zero series::
 
@@ -2620,7 +2620,7 @@ def _mul_(self, other):
             z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7)
             sage: N = M * (1 - M)
             sage: N
-            z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + O(z^7)
+            z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 - 49*z^7 + O(z^8)
 
             sage: p = (1 - z)*(1 + z^2)^3 * z^-2
             sage: p
@@ -2648,7 +2648,7 @@ def _mul_(self, other):
             sage: N = L(lambda n: 1, valuation=0); N
             1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7)
             sage: M * N
-            z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + O(z^7)
+            z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + 28*z^7 + O(z^8)
 
             sage: L.one() * M is M
             True
@@ -2765,7 +2765,7 @@ def __pow__(self, n):
             sage: M = L(lambda n: n, valuation=0); M
             z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7)
             sage: M^2
-            z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + O(z^7)
+            z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + 56*z^7 + 84*z^8 + O(z^9)
 
         We can create a really large power of a monomial, even with
         the dense implementation::
@@ -2780,7 +2780,7 @@ def __pow__(self, n):
             sage: M = L(lambda n: n, valuation=0); M
             z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7)
             sage: M^2
-            z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + O(z^7)
+            z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + 56*z^7 + 84*z^8 + O(z^9)
 
         Lazy Laurent series that are known to be exact can be raised
         to the power ``n``::
@@ -2947,7 +2947,7 @@ def _div_(self, other):
             sage: N = L(lambda n: 1, 0); N
             1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7)
             sage: P = M / N; P
-            z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7)
+            z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + O(z^8)
 
         Lazy Laurent series that have a sparse implementation can be divided::
 
@@ -2957,7 +2957,7 @@ def _div_(self, other):
             sage: N = L(lambda n: 1, 0); N
             1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7)
             sage: P = M / N; P
-            z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7)
+            z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + O(z^8)
 
         If the division of exact Lazy Laurent series yields a Laurent
         polynomial, it is represented as an exact series::
@@ -3455,7 +3455,7 @@ def __call__(self, g, *, check=True):
             sage: f = L(lambda n: n, valuation=0); f
             z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7)
             sage: f(z^2)
-            z^2 + 2*z^4 + 3*z^6 + O(z^7)
+            z^2 + 2*z^4 + 3*z^6 + 4*z^8 + O(z^9)
 
             sage: f = L(lambda n: n, valuation=-2); f
             -2*z^-2 - z^-1 + z + 2*z^2 + 3*z^3 + 4*z^4 + O(z^5)
@@ -3499,7 +3499,7 @@ def __call__(self, g, *, check=True):
             sage: f(0)
             0
             sage: f(y)
-            y + 2*y^2 + 3*y^3 + 4*y^4 + 5*y^5 + 6*y^6 + O(y^7)
+            y + 2*y^2 + 3*y^3 + 4*y^4 + 5*y^5 + 6*y^6 + 7*y^7 + O(y^8)
             sage: fp = f(y - y)
             sage: fp == 0
             True
@@ -4405,7 +4405,7 @@ def __call__(self, *g, check=True):
             sage: fg = 1 / (1 - g - g*g); fg
             1 + 1/(2^s) + 1/(3^s) + 3/4^s + 1/(5^s) + 5/6^s + 1/(7^s) + O(1/(8^s))
             sage: fog - fg
-            O(1/(7^s))
+            O(1/(8^s))
 
             sage: f = 1 / (1 - 2*a)
             sage: f(g)
@@ -4507,6 +4507,21 @@ def __call__(self, *g, check=True):
             sage: T(1-x-2*y + x*y^2)(1/(1-a), 3)
             3 + 8*a + 8*a^2 + 8*a^3 + 8*a^4 + 8*a^5 + 8*a^6 + O(a,b)^7
 
+        Check that issue :trac:`35261` is fixed::
+
+            sage: L.<z> = LazyPowerSeriesRing(QQ)
+            sage: fun = lambda n: 1 if ZZ(n).is_power_of(2) else 0
+            sage: f = L(fun)
+            sage: g = L.undefined(valuation=1)
+            sage: g.define((~f.shift(-1)(g)).shift(1))
+            sage: g
+            z - z^2 + 2*z^3 - 6*z^4 + 20*z^5 - 70*z^6 + 256*z^7 + O(z^8)
+
+            sage: f = L(fun)
+            sage: g = L.undefined(valuation=1)
+            sage: g.define((z - (f - z)(g)))
+            sage: g
+            z - z^2 + 2*z^3 - 6*z^4 + 20*z^5 - 70*z^6 + 256*z^7 + O(z^8)
         """
         fP = parent(self)
         if len(g) != fP._arity:
@@ -4712,6 +4727,15 @@ def revert(self):
             sage: f = L([-1], valuation=1, degree=3, constant=-1)
             sage: f.revert()
             (-z) + z^3 + (-z^4) + (-2*z^5) + 6*z^6 + z^7 + O(z^8)
+
+        Check that issue :trac:`35261` is fixed::
+
+            sage: L.<z> = LazyPowerSeriesRing(QQ)
+            sage: f = L(lambda n: 1 if ZZ(n).is_power_of(2) else 0)
+            sage: f
+            z + z^2 + z^4 + O(z^7)
+            sage: f.revert()
+            z - z^2 + 2*z^3 - 6*z^4 + 20*z^5 - 70*z^6 + 256*z^7 + O(z^8)
         """
         P = self.parent()
         if P._arity != 1:
@@ -5499,7 +5523,7 @@ def revert(self):
             sage: L = LazySymmetricFunctions(h)
             sage: f = L(lambda n: h[n]) - 1
             sage: f(f.revert())
-            h[1] + O^7
+            h[1] + O^8
 
         TESTS::
 
diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py
--- a/src/sage/rings/lazy_series_ring.py
+++ b/src/sage/rings/lazy_series_ring.py
@@ -293,7 +293,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No
             sage: L.has_coerce_map_from(R)
             True
             sage: L(R(lambda n: n))
-            z + z^3 + z^5 + O(z^7)
+            z + z^3 + z^5 + z^7 + O(z^8)
             sage: L(R([2,4,6])) == L.zero()
             True
             sage: L(R([2,4,6], valuation=2, constant=4)) == L.zero()


Test patch: