Hello,

> Mike, could you point me to your code?

I've attached the _very_ rough version I started awhile back.  It just
does FormalPowerSeries and DataStream.

> I actually wonder why you would
> reprogram it in Sage and not interfacing the aldor-combinat library?
> Is there a problem with the license of the aldor compiler
> (aldor-combinat itself is GPL2).

Well, tree-like structures ( and hence species ) should be a standard
part of Sage.  If we were to use aldor-combinat to do those
computations, then aldor would need to become a standard part of Sage.
 This presents a number of issues:

* Aldor must build cleanly on all the platforms Sage builds on.  This
is something that someone much more familiar with Aldor.  The first
step would be to create an experimental spkg.
* I have no idea how (if possible) to interface an Aldor program from
C.  pexpect interfaces are slow.
* licensing issues need to be sorted out since the APL2 is not
GPL-compatible.  I don't know if it'd even be possible to distribute
Aldor as part of Sage.

Also, part of it was just a chance for me to learn more about species.
 I definitely cannot program in Aldor, and one of the reasons why
Python has been so nice to work with is that is just sort of stays out
of the way.  I'd much think about programming math instead of
programming languages.

--Mike

--~--~---------~--~----~------------~-------~--~----~
To post to this group, send email to sage-devel@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/sage-devel
URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/
-~----------~----~----~----~------~----~------~--~---

from stream import Stream
from series_order import InfiniteSeriesOrder, UnknownSeriesOrder, bounded_decrement, increment
from sage.rings.all import Integer
import pdb

inf = InfiniteSeriesOrder()
unk = UnknownSeriesOrder()


"""

#Test Plus 1
sage: from sage.combinat.species.series import *
sage: from sage.combinat.species.stream import Stream
sage: gs0 = FormalPowerSeries(Stream(const=0))
sage: gs1 = FormalPowerSeries(Stream(const=1))
sage: sum1 = gs0 + gs1
sage: sum2 = gs1 + gs1
sage: sum3 = gs1 + gs0
sage: [gs0.coefficient(i) for i in range(11)]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [gs1.coefficient(i) for i in range(11)]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
sage: [sum1.coefficient(i) for i in range(11)]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
sage: [sum2.coefficient(i) for i in range(11)]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
sage: [sum3.coefficient(i) for i in range(11)]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

#Test Plus 2
sage: l1 = [1,2,4,8,0]
sage: l2 = [-1, 0,-1,-9,22,0]
sage: gs1 = FormalPowerSeries(Stream(l1))
sage: gs2 = FormalPowerSeries(Stream(l2))
sage: sum = gs1 + gs2
sage: sum2 = gs2 + gs1
sage: [ sum.coefficient(i) for i in range(5) ]
[0,  2, 3, -1, 22]
sage: [ sum.coefficient(i) for i in range(5, 11) ]
[0, 0, 0, 0, 0, 0]
sage: [ sum2.coefficient(i) for i in range(5) ]
[0,  2, 3, -1, 22]
sage: [ sum2.coefficient(i) for i in range(5, 11) ]
[0, 0, 0, 0, 0, 0]


#Test Zero
sage: s = FormalPowerSeries(Stream([0,2,3,0]))
sage: s.zero()
False
sage: s = FormalPowerSeries(0)
sage: s.zero()
True
sage: s = FormalPowerSeries(Stream([0]))
sage: s.zero()
False
sage: s.coefficient(0)
0
sage: s.coefficient(1)
0
sage: s.zero()
True

#Test Times 1
sage: gs0 = FormalPowerSeries(Stream(const=0))
sage: gs1 = FormalPowerSeries(Stream(const=1))
sage: prod0 = gs0 * gs1
sage: prod1 = gs1 * gs0
sage: prod2 = gs1 * gs1
sage: [prod0.coefficient(i) for i in range(11)]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [prod1.coefficient(i) for i in range(11)]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [prod2.coefficient(i) for i in range(11)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

#Test Times 2
sage: l1 = [1,2,4,8,0]
sage: l2 = [-1, 0,-1,-9,22,0]
sage: gs1 = FormalPowerSeries(Stream(l1))
sage: gs2 = FormalPowerSeries(Stream(l2))
sage: prod1 = gs1 * gs2
sage: prod2 = gs2 * gs1
sage: [prod1.coefficient(i) for i in range(11)]
[-1, -2, -5, -19, 0, 0, 16, 176, 0, 0, 0]
sage: [prod2.coefficient(i) for i in range(11)]
[-1, -2, -5, -19, 0, 0, 16, 176, 0, 0, 0]

#Test Recursive 0
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: s.set(one+monom*s)
sage: s.aorder
0
sage: s.order
Unknown series order
sage: [s.coefficient(i) for i in range(6)]
[1, 1, 1, 1, 1, 1]

#Test Recursive 1
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: s.set(one+monom*s*s)
sage: s.aorder
0
sage: s.order
Unknown series order
sage: [s.coefficient(i) for i in range(6)]
[1, 1, 2, 5, 14, 42]

#Test Recursive 1b
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: s.set(monom + s*s)
sage: s.aorder
1
sage: s.order
Unknown series order
sage: [s.coefficient(i) for i in range(7)]
[0, 1, 1, 2, 5, 14, 42]


#Test Recursive 2
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: t = FormalPowerSeries()
sage: t._name = 't'
sage: s.set(one+monom*t*t*t)
sage: t.set(one+monom*s*s)
sage: [s.coefficient(i) for i in range(9)]
[1, 1, 3, 9, 34, 132, 546, 2327, 10191]
sage: [t.coefficient(i) for i in range(9)]
[1, 1, 2, 7, 24, 95, 386, 1641, 7150]

#Test Recursive 2b
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: t = FormalPowerSeries()
sage: t._name = 't'
sage: s.set(monom + t*t*t)
sage: t.set(monom + s*s)
sage: [s.coefficient(i) for i in range(9)]
[0, 1, 0, 1, 3, 3, 7, 30, 63]
sage: [t.coefficient(i) for i in range(9)]
[0, 1, 1, 0, 2, 6, 7, 20, 75]

#Test Recursive 3
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: s.set(one+monom*s*s*s)
sage: [s.coefficient(i) for i in range(10)]
[1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675]

#Test PlusTimes 1
sage: s = (one + monom) * (one + monom)
sage: [s.coefficient(i) for i in range(6)]
[1, 2, 1, 0, 0, 0]

#Test Compose 1
sage: s = FormalPowerSeries(Stream(const=1))
sage: t = FormalPowerSeries(Stream([0,0,1]))
sage: u = s(t)
sage: u.coefficients(11)
[1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

#Test Compose 2
sage: s = FormalPowerSeries(Stream(const=1))
sage: t = FormalPowerSeries(Stream([0,0,1,0]))
sage: u = s(t)
sage: u.aorder
0
sage: u.order
Unknown series order
sage: u.coefficients(10)
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
sage: u.aorder
0
sage: u.order
0

#Test Compose 3
# s = 1/(1-x), t = x/(1-x)
# s(t) = (1-x)/(1-2x)
sage: s = FormalPowerSeries(Stream(const=1))
sage: t = FormalPowerSeries(Stream([0,1]))
sage: u = s(t)
sage: u.coefficients(14)
[1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]

#Test Differentiate 1
sage: s = FormalPowerSeries(Stream(const=1))
sage: u = s.differentiate()
sage: u.coefficients(10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


#Test Differentiate 2
sage: s = FormalPowerSeries()
sage: s._name = 's'
sage: s.set(one+monom*s*s)
sage: u = s.differentiate()
sage: u.coefficients(5) #[1*1, 2*2, 3*5, 4*14, 5*42]
[1, 4, 15, 56, 210]

#Test Differentiate 3
sage: s = FormalPowerSeries(Stream(const=1))
sage: t = FormalPowerSeries(Stream([0,1]))
sage: u = s(t).differentiate()
sage: v = (s.differentiate().compose(t))*t.differentiate()
sage: u.coefficients(11)
[1, 4, 12, 32, 80, 192, 448, 1024, 2304, 5120, 11264]
sage: v.coefficients(11)
[1, 4, 12, 32, 80, 192, 448, 1024, 2304, 5120, 11264]

#Test Differentiate 4
sage: s = FormalPowerSeries(); s._name='s'
sage: t = FormalPowerSeries(); t._name='t'
sage: s.set(monom+t*t*t)
sage: t.set(monom+s*s)
sage: u = (s*t).differentiate()
sage: v = s.differentiate()*t + s*t.differentiate()
sage: u.coefficients(10)
[0, 2, 3, 4, 30, 72, 133, 552, 1791, 4260]
sage: v.coefficients(10)
[0, 2, 3, 4, 30, 72, 133, 552, 1791, 4260]
sage: u.coefficients(10) == v.coefficients(10)
True

#Test Integrate 1a
sage: s = zero
sage: t = s.integrate()
sage: t.zero()
True

#Test Integrate 1b
sage: s = zero
sage: t = s.integrate(1)
sage: t.coefficients(6)
[1, 0, 0, 0, 0, 0]
sage: t.stream.is_constant()
True

#Test Integrate 2a
sage: s = term(1, 0)
sage: t = s.integrate()
sage: t.coefficients(6)
[0, 1, 0, 0, 0, 0]
sage: t.stream.is_constant()
True

#Test Integrate 2b
sage: s = term(1,0)
sage: t = s.integrate(1)
sage: t.coefficients(6)
[1, 1, 0, 0, 0, 0]
sage: t.stream.is_constant()
True

#Test Integrate 3a
sage: s = term(1, 4)
sage: t = s.integrate()
sage: t.coefficients(10)
[0, 0, 0, 0, 0, 1/5, 0, 0, 0, 0]

#Test Integrate 3b
sage: s = term(1,4)
sage: t = s.integrate(1)
sage: t.coefficients(10)
[1, 0, 0, 0, 0, 1/5, 0, 0, 0, 0]

#Test Sum 1
sage: s = FormalPowerSeries(Stream(const=1))
sage: g = [s]*6 + [zero]
sage: t = fps_sum_generator(g)
sage: t.coefficients(10)
[1, 2, 3, 4, 5, 6, 6, 6, 6, 6]

#Test Sum 2
sage: s = FormalPowerSeries(Stream(const=1))
sage: def g():
...       while True:
...           yield s
sage: t = fps_sum_generator(g())
sage: t.coefficients(9)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

#Test Product 1
sage: s1 = FormalPowerSeries(Stream([1,1,0]))
sage: s2 = FormalPowerSeries(Stream([1,0,1,0]))
sage: s3 = FormalPowerSeries(Stream([1,0,0,1,0]))
sage: s4 = FormalPowerSeries(Stream([1,0,0,0,1,0]))
sage: s5 = FormalPowerSeries(Stream([1,0,0,0,0,1,0]))
sage: s6 = FormalPowerSeries(Stream([1,0,0,0,0,0,1,0]))
sage: s = [s1, s2, s3, s4, s5, s6]
sage: def g():
...       for a in s:
...           yield a
sage: p = fps_product_generator(g())
sage: p.coefficients(26)
[1, 1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0]


#Test Product 2
sage: def m(n):
...       n -= 1
...       yield 1
...       while True:
...           for i in range(n):
...               yield 0
...           yield 1
...
sage: def s(n):
...       q = 1/n
...       n -= 1
...       yield 0
...       while True:
...           for i in range(n):
...               yield 0
...           yield q
...

sage: def lhs_gen():
...       n = 1
...       while True:
...           yield FormalPowerSeries(Stream(m(n)))
...           n += 1
...

sage: def rhs_gen():
...       n = 1
...       while True:
...           yield FormalPowerSeries(Stream(s(n)))
...           n += 1
...

sage: lhs = fps_product_generator(lhs_gen())
sage: rhs = fps_sum_generator(rhs_gen()).exponentiate()
sage: lhs.coefficients(10)
[1, 1, 2, 3, 5, 7, 11, 15, 22, 30]
sage: rhs.coefficients(10)
[1, 1, 2, 3, 5, 7, 11, 15, 22, 30]

              

#Test exponentiate
sage: def inv_factorial():
...       q = 1
...       yield 0
...       yield q
...       n = 2
...       while True:
...           q = q / n
...           yield q
...           n += 1

sage: f = FormalPowerSeries(Stream(inv_factorial())) #e^(x)-1
sage: u = f.exponentiate()
sage: g = inv_factorial()
sage: z1 = [1,1,2,5,15,52,203,877,4140,21147,115975]
sage: l1 = [z*g.next() for z in z1]
sage: l1 = [1] + l1[1:]
sage: u.coefficients(11)
[1, 1, 1, 5/6, 5/8, 13/30, 203/720, 877/5040, 23/224, 1007/17280, 4639/145152]
sage: l1 == u.coefficients(11)
True
              
"""

def uninitialized():
    raise RuntimeError, "we should never be here"

def FormalPowerSeries(x=None):
    if x is None:
        return FormalPowerSeries_class(stream=None, order=unk, aorder=unk,
                                       aorder_changed=True, compute_aorder=uninitialized,
                                       is_initialized=False)
    if hasattr(x, 'next'):
        return FormalPowerSeries(Stream(x))
        
    if isinstance(x, Stream):
        return FormalPowerSeries_class(stream=x, order=unk, aorder=0,
                                       aorder_changed=False, compute_aorder=do_nothing,
                                       is_initialized=True)
    if x == 0:
        return zero
    if x == 1:
        return one
    if x == 'monom':
        return monom

    raise TypeError, "unable to create a formal power series from x (= %s)"%x


                    
class FormalPowerSeries_class:
    def __init__(self, stream=None, order=None, aorder=None, aorder_changed=True, is_initialized=False, compute_aorder=None, name=None):
        self.stream = stream

        if order is None:
            self.order = unk
        else:
            self.order = order

        if aorder is None:
            self.aorder = unk
        else:
            self.aorder = aorder

        if self.aorder == inf:
            self.order = inf

        self.aorder_changed = aorder_changed
        self.is_initialized = is_initialized

        self.compute_aorder = compute_aorder

        self._name = name

    def __repr__(self):
        if self._name:
            return self._name

        if self.is_initialized:
            l = str(self.stream.list)
        else:
            l = 'uninitialized'
        return "Formal power series: %s"%l

    def initialize(self):
        
        if self.order != unk:
            return

        if self.aorder == inf:
            raise ValueError, "aorder can never be infinity since order would have to be infinity"
        
        elif self.aorder != unk and self.is_initialized:
            #Try to improve the approximate order
            i = self.aorder
            c = self.stream
            n = len(c)

            if i == 0 and n > 0:
                if self.stream[i] == 0:
                    i += 1
                    self.aorder += 1

                

            #Try to recognize the zero series
            if i >= n:
                #For non-constant series, we cannot do anythin
                if not self.stream.is_constant():
                    return

                if self.stream[ len(self.stream) - 1] == 0:
                    self.aorder = inf
                    self.order  = inf
                    return

            while i < n and c[i] == 0:
                i += 1
                self.aorder = i

            if i < n:
                self.order = i

        else:

            #if self.is_initialized != False:
            #    raise ValueError, "the series must be uninitialized at this point"

            self.compute_aorder()

            #if self.is_initialized != True:
            #raise ValueError, "the series must be initialized at this point"
    
        if hasattr(self, '_references_me'):
            self._references_me._copy(self)

    def initialize_coefficient_stream(self,compute_coefficients):
        ao = self.aorder
        assert ao != unk

        if ao == inf:
            self.order = inf
            self.stream = Stream(0)
        else:
            self.stream = Stream(gen=compute_coefficients(ao))

        self.is_initialized = True

    def coefficients(self, n):
        return [self.coefficient(i) for i in range(n)]

    def zero(self):
        self.initialize()
        return self.order == inf

    def set_approximate_order(self, new_order):
        self.aorder_changed = ( self.aorder != new_order )
        self.aorder = new_order
        return self.aorder_changed

    def _copy(self, x):
        self.order  = x.order
        self.aorder = x.aorder
        self.aorder_changed = x.aorder_changed
        self.compute_aorder = x.compute_aorder
        self.is_initialized = x.is_initialized
        self.stream = x.stream
        
    def set(self, x):
        self._copy(x)
        x._references_me = self
        reprx = repr(x)
        if "=" in reprx:
            self._name += " = (" + reprx +")"
        else:
            self._name += " = " + reprx

    def coefficient(self, n):
        #pdb.set_trace()
        if self.zero():
            return 0
        if n < self.aorder:
            return 0

        assert self.is_initialized

        return self.stream[n]

    def get_aorder(self):
        self.initialize()
        return self.aorder

    def get_order(self):
        self.initialize()
        return self.order

    def get_stream(self):
        self.initialize()
        return self.stream


    def approximate_order_two(self, x, y, new_order, compute_coefficients):
        """approximate_order_two(\n%s, \n%s, \n%s, \n%s)"""%(x,y,new_order,compute_coefficients)
        if self.is_initialized:
            return

        ochanged = self.aorder_changed
        must_initialize_coefficient_stream = ( self.aorder == unk or self.is_initialized == False)

        def a_order(x,y):
            #print "(%s,%s) -> %s"%(x.aorder, y.aorder, new_order(x.aorder, y.aorder))
            return new_order(x.aorder, y.aorder)

        ao = a_order(x,y)

        #start at maximum
        if ao == unk:
            ao = inf

        tchanged = self.set_approximate_order(ao)

        if ochanged or tchanged:
            x.compute_aorder()
            y.compute_aorder()
            ao = a_order(x,y)
            tchanged = self.set_approximate_order(ao)

        #assert self.is_initialized == False

        if must_initialize_coefficient_stream:
            self.initialize_coefficient_stream(compute_coefficients)

        if hasattr(self, '_references_me'):
            self._references_me._copy(self)

    def approximate_order_one(self, x, new_order, compute_coefficients):
        """approximate_order_two(\n%s, \n%s, \n%s)"""%(x,new_order,compute_coefficients)        
        if self.is_initialized:
            return

        ochanged = self.aorder_changed
        must_initialize_coefficient_stream = ( self.aorder == unk or self.is_initialized == False)

        def a_order(z):
            #print "%s -> %s"%(z.aorder, new_order(z.aorder))
            return new_order(z.aorder)

        ao = a_order(x)

        #start at maximum
        if ao == unk:
            ao = inf

        tchanged = self.set_approximate_order(ao)

        if ochanged or tchanged:
            x.compute_aorder()
            ao = a_order(x)
            tchanged = self.set_approximate_order(ao)

        assert self.is_initialized == False

        if must_initialize_coefficient_stream:
            self.initialize_coefficient_stream(compute_coefficients)

        if hasattr(self, '_references_me'):
            self._references_me._copy(self)
            
    def approximate_order_zero(self, new_order, compute_coefficients):
        """approximate_order_zero(\n%s, \n%s)"""%(new_order,compute_coefficients)
        if self.is_initialized:
            return

        assert self.aorder == unk

        ao = new_order()

        self.set_approximate_order(ao)

        self.initialize_coefficient_stream(compute_coefficients)

        if hasattr(self, '_references_me'):
            self._references_me._copy(self)

    def new0(self,compute_coefficients, order_op, name=None):
        new_fps =  self.new_from_ao(name=name)
        def f0():
            """new0 - f0: %s, %s"""%(order_op, compute_coefficients)
            new_fps.approximate_order_zero(order_op, compute_coefficients)
        new_fps.compute_aorder = f0
        return new_fps

    def new1(self, compute_coefficients, order_op, x, name=None):
        new_fps = self.new_from_ao( name=name )
        def f1():
            """new1 - f1: %s, %s, %s"""%(x, order_op, compute_coefficients)
            new_fps.approximate_order_one(x, order_op, compute_coefficients)
        new_fps.compute_aorder = f1
        return new_fps

    def new2(self,compute_coefficients, order_op, x, y, name=None):
        new_fps = self.new_from_ao(name=name )
        def f2():
            new_fps.approximate_order_two(x, y, order_op, compute_coefficients)
        f2.__doc__ =  """new2 - f2: %s, %s, %s, %s"""%(x, y, order_op, compute_coefficients)
        new_fps.compute_aorder = f2
        
        return new_fps
    
    def new_from_ao(self, name=None):
        return FormalPowerSeries_class(stream=None, order=unk, aorder=self.aorder,
                                       aorder_changed=True, compute_aorder=None,
                                       is_initialized=False, name=name)
   
    def __add__(self, y):
        def plus(ao):
            for n in range(ao):
                yield 0

            n = ao
            while True:
                yield self.coefficient(n) + y.coefficient(n)
                n += 1


        return self.new2(plus, min, self, y, name=repr(self)+" + " +repr(y))


    def __mul__(self, y):
        def times(ao):
            for n in range(ao):
                yield 0

            n = ao
            while True:
                low = self.aorder
                high = n - y.aorder
                nth_coefficient = 0

                #Handle the zero series
                if low == inf or high == inf:     #
                    yield 0                       #
                    n += 1                        #
                    continue                      #
                
                for k in range(low, high+1):
                    cx = self.coefficient(k)
                    if cx == 0:
                        continue
                    nth_coefficient += cx * y.coefficient(n-k)
                yield nth_coefficient
                n += 1

        reprself = repr(self)
        repry = repr(y)

        if "+" in reprself:
            reprself = "(" + reprself + ")"
        if "+" in repry:
            repry = "(" + repry + ")"
        return self.new2(times, lambda a,b:a+b, self, y, name=reprself+"*"+repry)
                

    def __call__(self, y):
        def compose(ao):
            assert y.coefficient(0) == 0
            yield self.coefficient(0)

            def g():
                n = 1
                while True:
                    yield self.coefficient(n)
                    n += 1

            rest_x = FormalPowerSeries(g())
            z = rest_x.compose(y)*y

            is_zero = z.zero()

            n = 1
            while True:
                yield z.coefficient(n)
                n += 1
    
        return self.new2(compose, lambda a,b:a*b, self, y, name="(%s)o(%s)"%(repr(self), repr(y)))

    def add(self, y):
        return self + y

    def times(self, y):
        return self * y

    def compose(self, y):
        return self(y)


    def differentiate(self):
        def diff(ao):
            #Fixme! (take care of approximate order stuff)
            #for n in range(ao-1):
            #    yield 0

            n = 1
            while True:
                yield n*self.coefficient(n)
                n += 1
                

        return self.new1(diff, bounded_decrement, self,  name="D(%s)"%repr(self))


    def integrate(self, integration_constant = 0):
        def integrate(ao):
            for n in range(ao):
                yield 0

            n = ao
            while True:
                yield (Integer(1)/Integer(n))*self.coefficient(n-1)
                n += 1


        if integration_constant == 0:
            return self.new1(integrate, increment, self, name="I(%s)"%repr(self))
        else:
            return _new(0, Stream(self._integrate_nonzero(integration_constant)), name="I(%s,%s)"%(repr(self), integration_constant))

    def _integrate_nonzero(self, integration_constant):
        yield integration_constant
        aox = self.aorder
        assert aox != unk

        if aox == inf:
            while True:
                yield 0
        else:
            for n in range(aox):
                yield 0
            n = aox + 1
            while True:
                yield (Integer(1)/Integer(n))*self.coefficient(n-1)
                n += 1

    def exponentiate(self):
        s = FormalPowerSeries(); s._name = "s"
        s.set( (self.differentiate()*s).integrate(1) )
        s._name = "EXP(%s)"%repr(self)
        return s
    
def do_nothing(*args, **kwargs):
##     assert t.aorder != unk
##     assert t.is_initialized == True
    return

def new_from_ao(approximate_order):
    return FormalPowerSeries_class(stream=None, order=unk, aorder=unk,
                                   aorder_changed=True, compute_aorder=approximate_order,
                                   is_initialized=False)





#Finite Sum      
def sum_coefficients(a):
    last = len(a) - 1
    assert last >= 0
    n = 0
    while True:
        r = sum( [a[i].coefficient(n) for i in range(len(a))] )
        yield r
        n += 1
        
def fps_sum(a):
    return FormalPowerSeries( sum_coefficients(a) )


#Potentially infinite sum
def fps_sum_generator(g):
    s = Stream(g)

    def h():
        n = 0
        while True:
            r = s[n].coefficient(n) #compute [x^n] s(n)
            for i in range(len(s)-1):
                r += s[i].coefficient(n)
            yield r
            n += 1

    return FormalPowerSeries(h())

#Potentially infinite product
def fps_product_generator(g):
    def h():
        z = g.next()
        yield z.coefficient(0)
        yield z.coefficient(1)

        n = 2

        for x in g:
            z = z * x
            yield z.coefficient(n)
            n += 1

        while True:
            yield z.coefficient(n)
            n += 1

    return FormalPowerSeries(h())
        



#################################
def _new(o, g, name=None):
    return FormalPowerSeries_class(stream=g, order=o, aorder=o,
                                       aorder_changed=False, compute_aorder=do_nothing,
                                       is_initialized=True, name=name)

zero = _new(inf, Stream([0]), name="0")
one  = _new(0, Stream([1,0]), name="1")
monom = _new(1, Stream([0,1,0]), name="monom")

def term(r, n):
    if r == 0:
        return zero
    else:
        s = [0]*n+[r,0]
        return _new(int(n), Stream(s), name="term(%s,%s)"%(r,n))
from sage.rings.all import Integer

class SeriesOrderElement:
    pass

class InfiniteSeriesOrder(SeriesOrderElement):
    def __init__(self):
        pass

    def __repr__(self):
        return "Infinite series order"

    def __add__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return self

        if isinstance(x, InfiniteSeriesOrder):
            return self

        if isinstance(x, UnknownSeriesOrder):
            return x

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"
        
    __radd__ = __add__


    def __mul__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return self

        if isinstance(x, InfiniteSeriesOrder):
            return self

        if isinstance(x, UnknownSeriesOrder):
            return x
        
        raise TypeError, "x must be a positive integer or a SeriesOrderElement"

    def __rmul__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            if x == 0:
                return 0
            else:
                return self

        if isinstance(x, InfiniteSeriesOrder):
            return self

        if isinstance(x, UnknownSeriesOrder):
            return x
        
        raise TypeError, "x must be a positive integer or a SeriesOrderElement"

    def __lt__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return False

        if isinstance(x, InfiniteSeriesOrder):
            return False

        if isinstance(x, UnknownSeriesOrder):
            return False
     

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"

    
    def __gt__(self, x):
        return True
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return False

        if isinstance(x, InfiniteSeriesOrder):
            return False

        if isinstance(x, UnknownSeriesOrder):
            return False
     

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"       

class UnknownSeriesOrder(SeriesOrderElement):
    def __init__(self):
        pass

    def __repr__(self):
        return "Unknown series order"

    def __add__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return self

        if isinstance(x, SeriesOrderElement):
            return self

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"
        
    __radd__ = __add__


    def __mul__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return self

        if isinstance(x, SeriesOrderElement):
            return self

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"

    def __rmul__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return self

        if isinstance(x, SeriesOrderElement):
            return self

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"

    def __lt__(self, x):
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return True

        if isinstance(x, SeriesOrderElement):
            return True

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"       

    def __gt__(self, x):
        return False
        if isinstance(x, (int, Integer)):
            if x < 0:
                raise ValueError, "x must be a positive integer"
            return False

        if isinstance(x, InfiniteSeriesOrder):
            return False

        if isinstance(x, UnknownSeriesOrder):
            return False
     

        raise TypeError, "x must be a positive integer or a SeriesOrderElement"       

def bounded_decrement(x):
    if isinstance(x, SeriesOrderElement):
        return x
    
    if isinstance(x, (int, Integer)):
        if x < 0:
            raise ValueError, "x must be a positive integer"
        return max(0, x - 1)

    raise TypeError, "x must be a positive integer or a SeriesOrderElement"       
    

def increment(x):
    if isinstance(x, SeriesOrderElement):
        return x + 1
    
    if isinstance(x, (int, Integer)):
        if x < 0:
            raise ValueError, "x must be a positive integer"
        return x+1

    raise TypeError, "x must be a positive integer or a SeriesOrderElement"       
       
import itertools

def _integers_from(n):
    while True:
        yield n
        n += 1

class Stream:
    """

    EXAMPLES:
        sage: from itertools import izip
        sage: s = Stream(0)
        sage: len(s)
        1
        sage: [x for x in s.elements()]
        [0]
        sage: [x for (x,i) in izip(s, range(4))]
        [0, 0, 0, 0]
        sage: len(s)
        1

        sage: l = [4,3,5,7,4,1,9,7]
        sage: s = Stream(l)
        sage: s[3]
        8
        sage: len(s)
        4
        sage: s[3]
        8
        sage: len(s)
        4
        sage: s[1]
        3
        sage: len(s)
        4
        sage: s[4]
        4
        sage: len(s)
        5
        sage: [i for i in s.elements()]
        [4, 3, 5, 7, 4, 1, 9, 7]
        sage: len(s)
        8

        sage: l = [4,3,5,2,6,1]
        sage: s = Stream(l)
        sage: s[3]
        2
        sage: len(s)
        4
        sage: g = iter(l)
        sage: s.set_gen(g)
        sage: s[5]
        3
        sage: len(s)
        6
        sage: [x for x in s.elements()]
        [4, 3, 5, 2, 6, 1, 4, 3, 5, 2]
        sage: len(s)
        10

        sage: s = Stream(const=4)
        sage: g = iter(s)
        sage: n = s.elements()
        sage: l1 = [x for (x,i) in izip(g, range(10))]
        sage: l2 = [x for (x,i) in izip(n, range(10))]
        sage: l = [4 for k in range(10)]
        sage: l == l1
        True
        sage: l2
        [4]

        sage: t = Stream([2,3,5,7,11])
        sage: g3 = t.elements(2)
        sage: l3 = [x for (x,i) in izip(g3, range(10))]
        sage: l3
        [5, 7, 11]
        sage: g4 = t.elements(10)
        sage: l4 = [x for (x,i) in izip(g4, range(10))]
        sage: l4
        [11]

        sage: l = ['Hello', ' ', 'World', '!']
        sage: s = Stream(l)
        sage: len(s)
        0
        sage: s[2]
        "World"
        sage: len(s)
        3
        sage: u = ""
        sage: for i in range(len(s)): u += s[i]
        sage: u
        "Hello World"
        sage: v = ""
        sage: for i in range(10): v += s[i]
        sage: v
        "Hello World!!!!!!!"
        sage: len(s)
        4

        sage: l = [2,3,5,7,11,0]
        sage: s = Stream(l)
        sage: s.is_constant()
        False
        sage: s[3]
        7
        sage: s.is_constant()
        False
        sage: s[5]
        0
        sage: s.is_constant()
        False
        sage: s[6]
        0
        sage: s.is_constant()
        True

        sage: s = Stream(const='I am constant.')
        sage: s.is_constant()
        True

        sage: s = Stream([1,2,3,4,5,6])
        sage: a = []
        sage: b = []
        sage: for (i,x) in enumerate(s.elements()):
                  a = [x] + a
                  b = [ s[2*i] ] + b
                  
        sage: a
        [6, 5, 4, 3, 2, 1]
        sage: b
        [6, 6, 6, 5, 3, 1]


        sage: fib = Stream()
        sage: def g():
                  yeild 1
                  yield 1
                  n = 0
                  while True:
                      yield fib[n] + fib[n+1]
                      n += 1

        sage: fib.set_gen(g())
        sage: [x for (x,i) in izip(fib, range(11))]
        [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 69]


        sage: def h(l):
                  if len(l) < 2:
                      return 1
                  return l[-1] + l[-2]
        sage: fib = Stream(func = h)
        sage: [x for (x,i) in izip(fib, range(11))]
        [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 69]


        sage: s = Stream()
        sage: l = Stream([1, 0])
        sage: y = Stream([0,1,0])
        sage: s.set_gen(iter(l + y * s * s))
        sage: [x for (x,i) in izip(s, range(6))]
        [1, 1, 2, 5, 14, 42]

        sage: def f(l):
                  if len(l) == 0:
                      return 0
                  return l[-1] + 1
                  
        sage: o = Stream(func=f)
        sage: [x for (x,i) in izip(o, range(10))]
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: double = lambda z: 2*z
        sage: t = o.map(double)
        sage: [x for (x,i) in izip(t, range(10))]
        [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

        sage: double = lambda z: 2*z
        sage: o = Stream([0,1,2,3])
        sage: [x for (x,i) in izip(o, range(6))]
        [0, 1, 2, 3, 3, 3]
        sage: t = o.map(double)
        sage: [x for (x,i) in izip(t, range(6))]
        [0, 2, 4, 6, 6, 6]

        sage: r = [4, 3, 5, 2, 6, 1, 1, 1, 1, 1]
        sage: l = [4, 3, 5, 2, 6, 1]
        sage: s = Stream(l)
        sage: s[3] = -1
        sage: [x for (x,i) in izip(s, r)]
        [4, 3, 5, -1, 6, 1, 1, 1, 1, 1]
        sage: s[5] = -2
        sage: [x for (x,i) in izip(s, r)]
        [4, 3, 5, -1, 6, -2, 1, 1, 1, 1]
        sage: s[6] = -3
        sage: [x for (x,i) in izip(s, r)]
        [4, 3, 5, -1, 6, -2, -3, 1, 1, 1]
        sage: s[8] = -4
        sage: [x for (x,i) in izip(s, r)]
        [4, 3, 5, -1, 6, -2, -3, 1, -4, 1]
        sage: a = Stream(const=0)
        sage: a[2] = 3
        sage: [x for (x,i) in izip(a, range(4))]
        [0, 0, 3, 0]
    """
    
    def __init__(self, gen=None, const=None, list=None, constant=True, func=None):

        if func != None:
            if gen != None:
                raise ValueError, "you cannot specify both a function and a generator"

            def g():
                while True:
                    try:
                        yield func(self.list)
                    except:
                        break

            gen = g()

        if hasattr(gen, '__iter__'):
            gen = iter(gen)

        #Constant stream
        if const != None:
            self.list = [ const ]
            self.last_index = 0
            self.gen = None
            self.constant = const
            self.end_reached = True
        else:
            self.list = [  ]
            self.last_index = -1
            self.gen = gen
            self.constant = constant
            self.end_reached = False

    def __setitem__(self, i, t):
        self[i]
        if i < len(self.list):
            self.list[i] = t
        else:
            self.list += [ self.constant ] * (i+1 - len(self.list))
            self.last_index = i
            self.list[i] = t


    def set_gen(self, gen):
        self.gen = gen
        self.end_reached = False

    def map(self, f):
        return Stream(gen=(f(x) for x in self))

    def __getitem__(self, i):
        if i <= self.last_index:
            return self.list[i]
        elif self.end_reached:
            if self.constant != "False":
                return self.constant
            else:
                raise IndexError, "out of position"
        else:
            while self.last_index < i:
                try:
                    self.list.append(self.gen.next())
                    self.last_index += 1
                except StopIteration:
                    self.end_reached = True
                    self.constant = self.list[-1]
                    return self[i]

            return self.list[i]


    def __add__(self, ds):
        if not isinstance(ds, Stream):
            raise TypeError, "ds must be a Stream"
        return Stream(gen=(a+b for (a,b) in itertools.izip(self,ds)))

    def __mul__(self, ds):
        if not isinstance(ds, Stream):
            raise TypeError, "ds must be a Stream"
        
        def times(n):
            t = 0
            for k in range(n+1):
                sk = self[k]
                if sk == 0:
                    continue
                t += sk *ds[n-k]
            return t

        return Stream(gen=(times(n) for n in _integers_from(0)))
            
        
    def __iter__(self):
        i = 0
        while True:
            try:
                yield self[i]
            except IndexError:
                break
            i += 1

    def __len__(self):
        return len(self.list)

    def number_computed(self):
        return self.__len__()

    def data(self):
        return self.list

    def is_constant(self):
        return self.end_reached

    def elements(self, start_index=0):
        i = start_index
        while not self.end_reached:
            try:
                self[i]
            except IndexError:
                break
            i += 1
        if start_index >= len(self.list):
            return iter(self.list[-1:])
        else:
            return iter(self.list[start_index:])
        
##         if start_index < 0:
##             raise ValueError, "start_index must be >= 0"

##         i = start_index
##         while True:
##             print i, self.last_index, len(self.list), self.end_reached
##             z = self[i]
##             print i, self.last_index, len(self.list), self.end_reached
##             print "-"*20
##             if self.end_reached and i >= len(self.list):
##                 if start_index >= len(self.list):
##                     yield z
##                 break
##             yield z
##             i += 1


one  = Stream(const=1)
zero = Stream(const=0)
x    = Stream([0,1,0])
ints = Stream(_integers_from(0))

Reply via email to