# power series package # a power series is a channel, along which flow the coefficients type rat: struct of { num, den : int; # numerator, denominator }; type pol: array of rat; # polynomial type PS: chan of rat; # power series type PS2: array[2] of PS; # pair of power series mkPS2:= prog() of PS2{ # make a pair become mk(PS2 = {mk(PS), mk(PS)}); }; # Conventions # Upper-case for power series. # Lower-case for rationals. # Input variables: U,V,... # Output variables: ...,Y,Z # Integer gcd; needed for rational arithmetic rec gcd:= prog(u:int,v:int) of int{ become val{ if(u<0) result gcd(-u,v); else if(u>v) result gcd(v,u); else if(u==0) result v; else result gcd(v%u,u); }; }; # Make a rational from two ints and from one int pairtorat:= prog(u:int,v:int) of rat{ g:= gcd(u,v); become val { if(v >= 0) result mk(rat={u/g,v/g}); else result mk(rat={-u/g,-v/g}); }; }; inttorat:= prog(u:int) of rat{ become pairtorat(u,1); }; zero:= inttorat(0); one:= inttorat(1); # Operations on rationals add:= prog(u:rat,v:rat) of rat{ become pairtorat(u.num*v.den+v.num*u.den, u.den*v.den); }; mul:= prog(u:rat,v:rat) of rat{ become pairtorat(u.num*v.num,u.den*v.den); }; neg:= prog(u:rat) of rat{ become pairtorat(-u.num,u.den); }; sub:= prog(u:rat,v:rat) of rat{ become add(u,neg(v)); }; inv:= prog(u:rat) of rat{ # invert a rat become pairtorat(u.den,u.num); }; ratprint:= prog(u:rat){ if(u.den==1) print(u.num); else print(u.num, "/", u.den); print(" "); }; # Print a power series Print:= prog(U:PS){ for(;;) ratprint(<-U); }; Printn:= prog(U:PS,n:int){ for(; n>0; n=n-1) ratprint(<-U); print ("\n"); }; # Evaluate n terms of power series U at x=c rec eval:= prog(c:rat,U:PS,n:int) of rat{ if(n==0) become zero; y:= <-U; become add(y,mul(c,eval(c,U,n-1))); }; # Power-series constructors return channels on which power # series flow. They start an encapsulated generator that # puts the terms of the series on the channel. # Often the generator is anonymous; but some generators # are useful in their own right (e.g. split, rep) # add two power series Add:= prog(U:PS,V:PS) of PS{ Z:=mk(PS); begin prog(){ for(;;) Z<-=add(<-U,<-V); }(); become Z; }; Cmul:= prog(c:rat,U:PS) of PS{ Z:=mk(PS); begin prog(){ for(;;) Z<- = mul(c,<-U); }(); become Z; }; # subtract Sub:= prog(U:PS,V:PS) of PS{ become Add(U,Cmul(neg(one),V)); }; # Service routine: copy input to output copy:= prog(U:PS,Z:PS){ for(;;) Z<-=<-U; }; # shift a power series and insert a new constant term # Z = c + xU Shift:= prog(c:rat,U:PS) of PS{ Z:=mk(PS); begin prog(){ Z<-=c; become copy(U,Z); }(); become Z; }; # Multiply by monomial x^n # [cheaper than Mul(Mon(intorat(1),n),U)] Monmul:= prog(U:PS,n:int) of PS{ Z:=mk(PS); begin prog(){ for(; n>0; n=n-1) Z<-=zero; become copy(U,Z); }(); become Z; }; # multiply by x Xmul:= prog(U:PS) of PS{ become Monmul(U,1); }; # repeat the constant c rep:= prog(c:rat,Z:PS){ for(;;) Z<-=c; }; Rep:= prog(c:rat) of PS{ Z:=mk(PS); begin rep(c,Z); become Z; }; # Monomial c*x^n Mon:= prog(c:rat,n:int)of PS{ Z:=mk(PS); begin prog(){ for(; n>0; n=n-1) Z<-=zero; Z<-= c; become rep(zero,Z); }(); become Z; }; # constant: c 0 0 0 0 ... Con:= prog(c:rat) of PS{ become Shift(c,Rep(zero)); }; # simple pole at 1: 1/(1-x) = 1 1 1 1 1 ... Ones:= Rep(one); # convert polynomial to power series; poly is an # array of coefficients, constant term first Poly:= prog(a:array of rat) of PS{ Z:=mk(PS); begin prog(){ i:=0; for(; i