Repository: sagemath/sage

Issue:
Performance of __getitem__ of CFiniteSequence
### Problem Description

The function `__getitem__` for CFiniteSequence employs the matrix multiplications. Let d be the length of the linear recurrence (= the degree of the denominator of the generating function). Then, the computation of the n-th element requires time proportinal to d^3 log n.
For example,
`sage: C(1/(1-x^1000))[10^18]`
requries a couple of seconds. This is because the current program computes 10^18-th power of a 1000 x 1000 matrix.

### Proposed Solution

There are mainly two algorithms requiring time proportinal to M(d) log n where M(d) is a time for computing a product of two polynomials of degree d. The naive algorithm for the polynomial multiplications gives M(d) = d^2. Other algorithms, e.g., Karatsuba, FFT, give asymptotically faster polynomial multiplications.
The newer algorithm for computing the n-th element of CFiniteSequence was proposed in https://arxiv.org/abs/2008.08822
This algorithm is easier to implement.

### Alternatives Considered

Fiduccia's algorithm (https://doi.org/10.1137/0214007) has asymptotically similar time complexity. But, it is 1.5 times slower than the above algorithm.

### Additional Information

_No response_

### 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.


Discussion:

Resolving patch:
diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py
--- a/src/sage/rings/cfinite_sequence.py
+++ b/src/sage/rings/cfinite_sequence.py
@@ -391,7 +391,6 @@ def __init__(self, parent, ogf):
             rem = num % den
             if den != 1:
                 self._a = R(num / den).list()
-                self._aa = (rem.valuation() * [0] + R(rem / den).list())[:self._deg]  # needed for _get_item_
             else:
                 self._a = num.list()
             if len(self._a) < alen:
@@ -645,33 +644,41 @@ def __getitem__(self, key):
             [0, 0, 1, 2, 3, 4, 5, 6, 7, 8]
             sage: s = C(x^3 * (1 - x)^-2); s[0:10]
             [0, 0, 0, 1, 2, 3, 4, 5, 6, 7]
+            sage: s = C(1/(1-x^1000)); s[10^18]
+            1
+            sage: s = C(1/(1-x^1000)); s[10^20]
+            1
+
+        REFERENCES:
+
+        - [BM2021]_
         """
         if isinstance(key, slice):
             m = max(key.start, key.stop)
             return [self[ii] for ii in range(*key.indices(m + 1))]
         elif isinstance(key, Integral):
-            from sage.matrix.constructor import Matrix
-            d = self._deg
-            if (self._off <= key and key < self._off + len(self._a)):
-                return self._a[key - self._off]
-            elif d == 0:
+            n = key - self._off
+            if n < 0:
                 return 0
-            (quo, rem) = self.numerator().quo_rem(self.denominator())
-            wp = quo[key - self._off]
-            if key < self._off:
-                return wp
-            A = Matrix(QQ, 1, d, self._c)
-            B = Matrix.identity(QQ, d - 1)
-            C = Matrix(QQ, d - 1, 1, 0)
-            if quo == 0:
-                off = self._off
-                V = Matrix(QQ, d, 1, self._a[:d][::-1])
+            den = self.denominator()
+            num = self.numerator()
+            if self._off >= 0:
+                num = num.shift(-self._off)
             else:
-                off = 0
-                V = Matrix(QQ, d, 1, self._aa[:d][::-1])
-            M = Matrix.block([[A], [B, C]], subdivide=False)
-
-            return wp + list(M ** (key - off) * V)[d - 1][0]
+                den = den.shift(self._off)
+            (quo, num) = num.quo_rem(den)
+            if quo.degree() < n:
+                wp = 0
+            else:
+                wp = quo[n]
+            P = self.parent().polynomial_ring()
+            x = P.gen()
+            while n:
+                nden = den(-x)
+                num = P((num * nden).list()[n % 2::2])
+                den = P((den * nden).list()[::2])
+                n //= 2
+            return wp + num[0] / den[0]
         else:
             raise TypeError("invalid argument type")
 


Test patch: