Example #1
0
    def __init__(self, ogf, *args, **kwargs):

        """
    Create a C-finite sequence given its ordinary generating function.

    INPUT:

    - ``ogf`` -- the ordinary generating function, a fraction of polynomials over the rationals


    OUTPUT:

    - A CFiniteSequence object

    EXAMPLES::

        sage: R.<x> = QQ[]
        sage: CFiniteSequence((2-x)/(1-x-x^2))     # the Lucas sequence
        C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1)
        sage: CFiniteSequence(x/(1-x)^3)           # triangular numbers
        C-finite sequence, generated by x/(-x^3 + 3*x^2 - 3*x + 1)
        
    Polynomials are interpreted as finite sequences, or recurrences of degree 0::
    
        sage: CFiniteSequence(x^2-4*x^5)
        Finite sequence [1, 0, 0, -4], offset = 2
        sage: CFiniteSequence(1)
        Finite sequence [1], offset = 0

    This implementation allows any polynomial fraction as o.g.f. by interpreting
    any power of `x` dividing the o.g.f. numerator or denominator as a right or left shift
    of the sequence offset::

        sage: CFiniteSequence(x^2+3/x)
        Finite sequence [3, 0, 0, 1], offset = -1
        sage: CFiniteSequence(1/x+4/x^3)
        Finite sequence [4, 0, 1], offset = -3
        sage: P = LaurentPolynomialRing(QQ.fraction_field(), 'X')
        sage: X=P.gen()
        sage: CFiniteSequence(1/(1-X))
        C-finite sequence, generated by 1/(-x + 1)
        
    The o.g.f. is always normalized to get a denominator constant coefficient of `+1`::
    
        sage: CFiniteSequence(1/(x-2))
        C-finite sequence, generated by -1/2/(-1/2*x + 1)
    
    TESTS::
    
        sage: P.<x> = QQ[]
        sage: CFiniteSequence(0.1/(1-x))
        Traceback (most recent call last):
        ...
        ValueError: O.g.f. base not rational.
        sage: P.<x,y> = QQ[]
        sage: CFiniteSequence(x*y)
        Traceback (most recent call last):
        ...
        NotImplementedError: Multidimensional o.g.f. not implemented.
        """

        self._br = ogf.base_ring()
        if (self._br <> QQ) and (self._br <> ZZ) and not is_FiniteField(self._br):
            raise ValueError('O.g.f. base not proper.')
        
        P = PolynomialRing(self._br, 'x')
        if ogf in QQ:
            ogf = P(ogf)
        if hasattr(ogf,'numerator'):
            try:
                num = P(ogf.numerator())
                den = P(ogf.denominator())
            except TypeError:
                if ogf.numerator().parent().ngens() > 1:
                    raise NotImplementedError('Multidimensional o.g.f. not implemented.')
                else:
                    raise ValueError('Numerator and denominator must be polynomials.')
        else:
            num = P(ogf)
            den = 1
        
        # Transform the ogf numerator and denominator to canonical form
        # to get the correct offset, degree, and recurrence coeffs and
        # start values.
        self._off = 0
        self._deg = 0
        if isinstance (ogf, FractionFieldElement) and den == 1:
            ogf = num        # case p(x)/1: fall through
            
        if isinstance (ogf, (FractionFieldElement, FpTElement)):
            x = P.gen()
            if num.constant_coefficient() == 0:
                self._off = num.valuation()
                num = P(num / x**self._off)
            elif den.constant_coefficient() == 0:
                self._off = -den.valuation()
                den = P(den * x**self._off)
            f = den.constant_coefficient()
            num = P(num / f)
            den = P(den / f)
            f = gcd(num, den)
            num = P(num / f) 
            den = P(den / f)
            self._deg = den.degree()
            self._c = [-den.list()[i] for i in range(1, self._deg + 1)]
            if self._off >= 0:
                num = x**self._off * num
            else:
                den = x**(-self._off) * den

            # determine start values (may be different from _get_item_ values)
            R = LaurentSeriesRing(self._br, 'x')
            rem = num % den
            alen = max(self._deg, num.degree() + 1)
            R.set_default_prec (alen)
            if den <> 1:
                self._a = R(num/(den+O(x**alen))).list()
                self._aa = R(rem/(den+O(x**alen))).list()[:self._deg]  # needed for _get_item_
            else:
                self._a = num.list()
            if len(self._a) < alen:
                self._a.extend([0] * (alen - len(self._a)))
                
            super(CFiniteSequence, self).__init__(P.fraction_field(), num, den, *args, **kwargs)
            
        elif ogf.parent().is_integral_domain():
            super(CFiniteSequence, self).__init__(ogf.parent().fraction_field(), P(ogf), 1, *args, **kwargs)
            self._c = []
            self._off = P(ogf).valuation()
            if ogf == 0:
                self._a = [0]
            else:
                self._a = ogf.parent()((ogf / (ogf.parent().gen())**self._off)).list()
        else:
            raise ValueError("Cannot convert a " + str(type(ogf)) + " to CFiniteSequence.")