# Power series package
# A power series is a channel, along which flow rational
# coefficients.  A denominator of zero signifies the end.

type rat: struct of {
	num, den : int;	# numerator, denominator
};
type item:rat;

include "/usr/rob/src/squint/progs/drat"

type PS: dch;	# power series
type PS2: array[2] of dch; # pair of power series

mkPS:=mkdch;
mkPS2:= mkdch2;

# 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

i2tor:= 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});
		   };
};

itor:= prog(u:int) of rat{
	become i2tor(u,1);
};

zero:= itor(0);
one:= itor(1);

# End mark and end test

finis:= i2tor(1,0);

end:= prog(u:rat) of int {
	become u.den==0;
};

# Operations on rationals

add:= prog(u:rat,v:rat) of rat{
	g:=gcd(u.den,v.den);
	become i2tor(u.num*(v.den/g)+v.num*(u.den/g),u.den*(v.den/g));
};

mul:= prog(u:rat,v:rat) of rat{
	g1:=gcd(u.num,v.den);
	g2:=gcd(u.den,v.num);
	become mk(rat={(u.num/g1)*(v.num/g2),(u.den/g2)*(v.den/g1)});
};

neg:= prog(u:rat) of rat{
	become i2tor(-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
	1/u.num;	# induce a stop on zero-divide
	become i2tor(u.den,u.num);
};

ratprint:= prog(u:rat){
	if(u.den==1) print(u.num);
	else print(u.num, "/", u.den);
	print(" ");
};

# Print n terms of a power series

Printn:= prog(U:PS,n:int){
	done:=0;
	for(; !done&&n>0; n=n-1) {
		u:=get(U);
		if(end(u)) done=1;
		else ratprint(u);
	}
	print ("\n");
};

Print:= prog(U:PS){
	become Printn(U,1000000000);
};

# 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:= get(U);
	if(end(y)) become zero;
	else 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.

# Make a pair of power series identical to a given power series

Split:=prog(U:PS) of dch2{
	UU:=mkdch2();
	begin split(U,UU);
	become UU;
};

# Add two power series

Add:= prog(U:PS,V:PS) of PS{
	Z:=mkPS();
	begin prog(){
		uv:array[2] of rat;
		for(;;) {
			<-Z.req;
			uv=get2(U,V);
			switch(end(uv[0])+2*end(uv[1])){
			case 0:
				Z.dat <-= add(uv[0],uv[1]);
			case 1:
				Z.dat <-= uv[1];
				become copy(V,Z);
			case 2:
				Z.dat <-= uv[0];
				become copy(U,Z);
			case 3:
				Z.dat <-= finis;
			}
		}
	}();
	become Z;
};

# Multiply a power series by a constant

Cmul:= prog(c:rat,U:PS) of PS{
	Z:=mkPS();
	begin prog(){
		done:=0;
		while(!done) {
			<-Z.req;
			u:=get(U);
			if(end(u)) done=1;
			else Z.dat <-= mul(c,u);
		}
		Z.dat <-= finis;
	}();
	become Z;
};

# Subtract

Sub:= prog(U:PS,V:PS) of PS{
	become Add(U,Cmul(neg(one),V));
};

# Multiply a power series by the monomial x^n

Monmul:= prog(U:PS,n:int) of PS{
	Z:=mkPS();
	begin prog(){
		for(; n>0; n=n-1) put(zero,Z);
		become copy(U,Z);
	}();
	become Z;
};

# Multiply by x

Xmul:= prog(U:PS) of PS{
	become Monmul(U,1);
};

Rep:= prog(c:rat) of PS{
	Z:=mkPS();
	begin repeat(c,Z);
	become Z;
};

# Monomial c*x^n

Mon:= prog(c:rat,n:int)of PS{
	Z:=mkPS();
	begin prog(){
		if(c.num!=0) {
			for(; n>0; n=n-1) put(zero,Z);
			put(c,Z);
		}
		put(finis,Z);
	}();
	become Z;
};

Shift:= prog(c:rat,U:PS) of PS{
	Z:=mkPS();
	begin prog(){
		put(c,Z);
		become copy(U,Z);
	}();
	become Z;
};

# simple pole at 1: 1/(1-x) = 1 1 1 1 1 ...

Ones:= Rep(one);

# Convert array of coefficients, constant term first
# to a (finite) power series

Poly:= prog(a:array of rat) of PS{
	Z:=mkPS();
	begin prog(){
		j:=0;
		done:=0;
		for(j=len(a); !done&&j>0; j=j-1) 
			if(a[j-1].num!=0) done=1;
		i:=0;
		for(; i<j; i=i+1) put(a[i],Z);
		put(finis,Z);
	}();
	become Z;
};

# Multiply. The algorithm is
# 	let U = u + x*UU
#	let V = v + x*VV
#	then UV = u*v + x*(u*VV+v*UU) + x*x*UU*VV

rec Mul:= prog(U:PS,V:PS) of PS{
	Z:=mkPS();
	begin prog(){
		<-Z.req;
		uv:=get2(U,V);
		if(end(uv[0])||end(uv[1]))
			Z.dat<-=finis;
		else {
			Z.dat<-=mul(uv[0],uv[1]);
			UU:=Split(U);
			VV:=Split(V);
			W := Add(Cmul(uv[0],VV[0]),Cmul(uv[1],UU[0]));
			<-Z.req;
			Z.dat<-=get(W);
			become copy(Add(W,Mul(UU[1],VV[1])),Z);
		}
	}();
	become Z;
};

# Differentiate
 
Diff:= prog(U:PS) of PS{
	Z:=mkPS();
	begin prog(){
		<-Z.req;
		u:=get(U);
		if(!end(u)) {
			i:int;
			done:=0;
			for(i=1; !done; i=i+1) {
				u = get(U);
				if(end(u)) done=1;
				else {
					Z.dat <-= mul(itor(i),u);
					<-Z.req;
				}
			}
		}
		Z.dat <-= finis;;
	}();
	become Z;
};

# Integrate, with const of integration

Integ:= prog(c:rat,U:PS) of PS{
	Z:=mkPS();
	begin prog(){
		put(c,Z);
		i : int;
		done:=0;
		for(i=1; !done; i=i+1) {
			<-Z.req;
			u:=get(U);
			if(end(u)) done=1;
			Z.dat<-=mul(i2tor(1,i),u);
		}
		Z.dat <-= finis;
	}();
	become Z;
};

# Binomial theorem (1+x)^c

Binom:= prog(c:rat) of PS{
	Z:=mkPS();
	begin prog(){
		n:= 1;
		t:rat = itor(1);
		while(c.num!=0){
			put(t,Z);
			t = mul(mul(t,c),i2tor(1,n));
			c = sub(c,one);
			n = n+1;
		}
		put(finis,Z);
	}();
	become Z;
};

# Reciprocal of a power series
#	let U = u + x*UU
#	let Z = z + x*ZZ
#	(u+x*UU)*(z+x*ZZ) = 1
#	z = 1/u
#	u*ZZ + z*UU +x*UU*ZZ = 0
#	ZZ = -UU*(z+x*ZZ)/u;

Recip:= prog(U:PS) of PS{
	Z:=mkPS();
	ZZ:=mkPS2();
	begin prog(){
		<-Z.req;
		z:=inv(get(U));
		Z.dat <-= z;
		split(Mul(Cmul(neg(z),U),Shift(z,ZZ[0])),ZZ);
		become copy(ZZ[1],Z);
	}();
	become Z;
};

# Exponential of a power series with constant term 0
# (nonzero constant term would make nonrational coefficients)
# bug: the constant term is simply ignored
#	Z = exp(U)
#	DZ = Z*DU
#	integrate to get Z

Exp:= prog(U:PS) of PS{
	ZZ:=mkPS2();
	split(Integ(one,Mul(ZZ[0],Diff(U))),ZZ);
	become ZZ[1];
};

# Substitute V for x in U, where the leading term of V is zero
#	let U = u + x*UU
#	let V = v + x*VV
# 	then S(U,V) = u + VV*S(V,UU)
# bug: a nonzero constant term is ignored

rec Subst:= prog(U:PS,V:PS) of PS {
	Z:= mkPS();
	begin prog(){
		VV:=Split(V);
		<-Z.req;
		u:=get(U);
		Z.dat<-=u;
		if(!end(u))
			if(end(get(VV[0]))) put(finis,Z);
			else become copy(Mul(VV[0],Subst(U,VV[1])),Z);
	}();
	become Z;
};

"# powser";

Print(Exp(Ones));
