//		ret.terms[i]=complex(0.0,0.0);
//
//	C++ Routines to manipulate Taylor series for solving a differential
//  equation.
//
//  By Dennis J. Darland 
//  4/3/91
//  Public Domain
//  Version 1.6
//  Many enhancements expected.
//  PROVIDED WITH NO WARRANTY.
//  
#include "ats.h"
t_s::t_s()
	{
	alloc_terms=MAX_TERMS;
	}
t_s::~t_s()
	{
	}
t_s::t_s(t_s &a)
	{
	alloc_terms=MAX_TERMS;
	set_first(a.first_term);
	set_last(a.last_term);
	set_x0(a.x0);
	set_h(a.h);
	set_terms(*this,a.terms);
	}
void t_s::set_first(int i)
	{
	first_term=i;
	}
void t_s::set_last(int i)
	{
	last_term=i;
	}
void t_s::set_h(complex h_in)
	{
	h=h_in;
	}
void t_s::set_x0(complex x0_in)
	{
	x0=x0_in;
	}
void set_data(t_s &a,t_s &b)
	{
	a.set_first(b.first_term);
	a.set_last(b.last_term);
	a.set_x0(b.x0);
	a.set_h(b.h);
	a.alloc_terms=MAX_TERMS;
	}
void set_terms(t_s &ret,complex *values)
	{
	register int i;
	
	for (i=0;i<MAX_TERMS;i++)
		ret.terms[i]=values[i];
	}
complex ats(int m,t_s &a,t_s &b,int j)
	{
	// function to implement Liebniz rule
	complex cret;
	register int i;
	
	cret=complex(0.0,0.0);
	
	for (i=j;i<=m;i++)
		{
		cret += a.terms[i]*b.terms[m-i];
		}
	return(cret);
	}

complex att(int m,t_s &a,t_s &b,int j)
	{
	// function for modified Liebniz rule
	complex ret;
	register int i;
	
	ret=complex(0.0,0.0);
	if (j > m)
		return(ret);	
	for (i=j;i<=m;i++)
		{
		ret += a.terms[i]*b.terms[m-i+1]*complex(double(m-i+1),0.0);
		}
	ret /= complex(double(m+1),0.0);
	return(ret);
	}
t_s & t_s::operator+=(t_s &b)
	{
	// Add taylor series
	register int i;
	
	if ((first_term != b.first_term)
	|| (last_term != b.last_term))
		report_error(0);
	set_data(*this,b);
	for (i=first_term; i < last_term;i++)
		terms[i]+=b.terms[i];
	return (*this);
	}
t_s & t_s::operator-=(t_s &b)
	{
	// Subtract taylor series
	register int i;
	
	if ((first_term != b.first_term)
	|| (last_term != b.last_term))
		report_error(0);
	set_data(*this,b);
	for (i=first_term; i < last_term;i++)
		terms[i]-=b.terms[i];
	return (*this);
	}
t_s& t_s::operator=(t_s &b)
	{
	// assign taylor series
	register int i;
	set_data(*this,b);
	for (i=0; i < b.last_term;i++)
		this->terms[i]=b.terms[i];
	return(*this);
	}
void display(t_s &a,int plot)
	{
	static pt=0;
	FILE *plot_cmds;
	char fname[64];
	char cmd[81];
	
	// display taylor series
	register int i;
	
	sprintf(fname,"ram:mapleplot_%d",pt);
	pt++;
	plot_cmds = fopen("ram:plottmp","w");
	fprintf(plot_cmds,"plotdevice := amiga;\n");
	fprintf(plot_cmds,"plot([");
	cout << "alloc_terms=" << a.alloc_terms << "\n"; 
	cout << "first_term =" << a.first_term << "\n"; 
	cout << "last_term=" << a.last_term << "\n";
	cout << "h=" << a.h << "\n";
	cout << "x0=" << a.x0 << "\n";
	for (i=0;i<a.last_term;i++)
		{
		cout << "terms[" << i << "]= " << real(a.terms[i]) << " + "
		<< imag(a.terms[i]) << " I\n";
		if (i != 0)
			fprintf(plot_cmds,",");
		fprintf(plot_cmds,"%d,sign(%g)*(ln(1+abs(%g)))\n",
		i,real(a.terms[i]),real(a.terms[i]));
		}
	fprintf(plot_cmds,"]);\n");
	fprintf(plot_cmds,"quit\n");
	fclose(plot_cmds);
	sprintf(cmd,"iconz <ram:plottmp >%s pltcvt",fname);
	system(cmd);
	if (plot>1)
		{
		sprintf(cmd,"maple <%s",fname);
		system(cmd);
		}
	}
void t_s::report_error(int no)
	{
	cout << "ats error = " << no;
	}
t_s operator+(t_s &a,t_s &b)
	{
	// Add two taylor series
	register int i;
	t_s ret;
	complex ac,bc;
	
	set_data(ret,a);
	for (i=a.first_term;i<a.last_term;i++)
		{
		ac=a.terms[i];
		bc=b.terms[i];
		ret.terms[i]=ac+bc;
		}
	return(ret);
	}

t_s operator-(t_s &a,t_s &b)
	{
	// Subtract two taylor series
	register int i;
	t_s ret;
	complex ac,bc;
		
	set_data(ret,a);
	
	for (i=a.first_term;i<a.last_term;i++)
		{
		ac=a.terms[i];
		bc=b.terms[i];
		ret.terms[i]=ac-bc;
		}
	return(ret);
	}

t_s operator*(t_s &a,t_s &b)
	{
	// Multiply two taylor series
	register int i;
	t_s ret;
	
	set_data(ret,a);
	ret.terms[0]=a.terms[0]*b.terms[0];
	for (i=1;i<a.last_term;i++)
		ret.terms[i]=ats(i,a,b,0);
	return(ret);
	}


t_s operator/(t_s &a,t_s &b)
	{
	// divide two taylor series
	register int i;
	t_s ret;
	
	set_data(ret,a);
	ret.terms[0]=a.terms[0]/b.terms[0];
	for (i=1;i<a.last_term;i++)
		{
		ret.terms[i]=(a.terms[i]-ats(i,b,ret,1))/b.terms[0];
		}
	return(ret);
	}

t_s x(t_s & a)
	{
	// independant variable - x
	register int i;
	t_s ret;
	
	set_data(ret,a);
	ret.terms[0]=a.x0;
	ret.terms[1]=complex(1.0,0.0)*ret.h;
	for (i=2;i<a.last_term;i++)
		ret.terms[i]=complex(0.0,0.0);
	return(ret);
	}

t_s constant(t_s & a,complex b)
	{
	// constant - b
	register int i;
	t_s ret;
	
	set_data(ret,a);
	ret.terms[0]=b;
	for (i=1;i<a.last_term;i++)
		ret.terms[i]=complex(0.0,0.0);
	return(ret);
	}

t_s expt(t_s & y,t_s & y1)
	{
	// y to power y1
	register int k,ka;
	t_s ret,a1,a2;
	
	set_data(ret,y);
	set_data(a1,y);
	set_data(a2,y);
	ret.terms[0]=expt(y.terms[0],y1.terms[0]);
	a1.terms[0]=lnn(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		a1.terms[k]=(y.terms[k] - att(ka,y,a1,1))/y.terms[0];
		a2.terms[ka]=ats(k,y,a1,0)*complex(double(k),0.0)/y.h;
		ret.terms[k]=ats(ka,ret,a2,0)*y.h/complex(double(k),0.0);
		}
	return(ret);
	}

t_s sqrt(t_s & y)
	{
	// square root
	register int k;
	t_s ret;
	
	set_data(ret,y);
	ret.terms[0]=my_sqrt(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ret.terms[k]=complex(0.0,0.0);
		ret.terms[k]=(y.terms[k]-ats(k,ret,ret,1))/(ret.terms[0]*
		complex(2.0,0.0));
		}
	return(ret);
	}
	
t_s exp(t_s & y)
	{
	// e to y power
	register int k,ka;
	t_s ret;
	
	set_data(ret,y);
	ret.terms[0]=exp(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		ret.terms[k]=att(ka,ret,y,0);
		}
	return(ret);
	}
t_s log(t_s & y)
	{
	// ln of y
	register int k,ka;
	t_s ret;
	
	set_data(ret,y);
	ret.terms[0]=lnn(y.terms[0]);
	ret.terms[1]=y.terms[1]/y.terms[0];
	for (k=2;k<y.last_term;k++)
		{
		ka = k - 1;
		ret.terms[k]=(y.terms[k] - att(ka,y,ret,1))/y.terms[0];
		}
	return(ret);
	}

t_s sin(t_s & y)
	{
	// sin of y
	register int k,ka;
	t_s f,g;
	
	set_data(f,y);
	set_data(g,y);
	f.terms[0]=sin(y.terms[0]);
	g.terms[0]=cos(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=att(ka,g,y,0);
		g.terms[k]=complex(-1.0,0.0)*att(ka,f,y,0);
		}
	return(f);
	}
	
t_s cos(t_s & y)
	{
	// cos of y
	register int k,ka;
	t_s f,g;
	
	set_data(f,y);
	set_data(g,y);
	f.terms[0]=sin(y.terms[0]);
	g.terms[0]=cos(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=att(ka,g,y,0);
		g.terms[k]=complex(-1.0,0.0)*att(ka,f,y,0);
		}
	return(g);
	}
	
t_s tan(t_s & y)
	{
	// cos of y
	register int k,ka;
	t_s f,a1,a2;
	
	set_data(f,y);
	set_data(a1,y);
	set_data(a2,y);
	a1.terms[0]=sin(y.terms[0]);
	a2.terms[0]=cos(y.terms[0]);
	f.terms[0]=a1.terms[0]/a2.terms[0];
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		a1.terms[k]=att(ka,a2,y,0);
		a2.terms[k]=complex(-1.0,0.0)*att(ka,a1,y,0);
		f.terms[k]=(a1.terms[k] - ats(k,a2,f,1))/a2.terms[0];
		}
	return(f);
	}
	
t_s sinh(t_s & y)
	{
	// sinh of y
	register int k,ka;
	t_s f,g;
	
	set_data(f,y);
	set_data(g,y);
	f.terms[0]=sinh(y.terms[0]);
	g.terms[0]=cosh(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=att(ka,g,y,0);
		g.terms[k]=att(ka,f,y,0);
		}
	return(f);
	}
	
t_s cosh(t_s & y)
	{
	// cosh of y
	register int k,ka;
	t_s f,g;
	
	set_data(f,y);
	set_data(g,y);
	f.terms[0]=sinh(y.terms[0]);
	g.terms[0]=cosh(y.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=att(ka,g,y,0);
		g.terms[k]=att(ka,f,y,0);
		}
	return(g);
	}
	
t_s tanh(t_s & y)
	{
	// tanh of y
	register int k,ka;
	t_s f,a1,a2;
	
	set_data(f,y);
	set_data(a1,y);
	set_data(a2,y);
	a1.terms[0]=sinh(y.terms[0]);
	a2.terms[0]=cosh(y.terms[0]);
	f.terms[0]=a1.terms[0]/a2.terms[0];
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		a1.terms[k]=att(ka,a2,y,0);
		a2.terms[k]=att(ka,a1,y,0);
		f.terms[k]=(a1.terms[k] - ats(k,a2,f,1))/a2.terms[0];
		}
	return(f);
	}
	
t_s asin(t_s & y)
	{
	// asin of y
	register int k,ka;
	t_s f,a1;
	
	set_data(f,y);
	set_data(a1,y);
	f.terms[0]=asin(y.terms[0]);
	a1.terms[0]=cos(f.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=(y.terms[k] - att(ka,a1,f,1))/a1.terms[0];
		a1.terms[k]=complex(-1.0,0.0)*att(ka,y,f,0);
		}
	return(f);
	}
	
t_s acos(t_s & y)
	{
	// acos of y
	register int k,ka;
	t_s f,a1;
	
	set_data(f,y);
	set_data(a1,y);
	f.terms[0]=acos(y.terms[0]);
	a1.terms[0]=sin(f.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=complex(-1.0,0.0)*
		(y.terms[k] + att(ka,a1,f,1))/a1.terms[0];
		a1.terms[k]=att(ka,y,f,0);
		}
	return(f);
	}
	
t_s atan(t_s & y)
	{
	// atan of y
	register int k,ka;
	t_s f,a1,a2;
	
	set_data(f,y);
	set_data(a1,y);
	set_data(a2,y);
	f.terms[0]=atan(y.terms[0]);
	a1.terms[0]=sin(f.terms[0]);
	a2.terms[0]=cos(f.terms[0]);
	for (k=1;k<y.last_term;k++)
		{
		ka = k - 1;
		f.terms[k]=(ats(k,y,a2,1)-y.terms[0]*att(ka,a1,f,1) -
			att(ka,a2,f,1))/(a2.terms[0]+y.terms[0]*a1.terms[0]);
		a1.terms[k]=att(ka,a2,f,0);
		a2.terms[k]=complex(-1.0,0.0)*att(ka,a1,f,0);
		}
	return(f);
	}

t_s deriv(t_s &a) 
	{
	// derivative of a taylor series
	register int i;
	t_s ret;
		
	set_data(ret,a);
	
	for (i=a.first_term;i<a.last_term-1;i++)
		{
		if (i > 0)
			ret.terms[i]=a.terms[i+1]*complex(double(i),0.0)/a.h;
		else 
			ret.terms[i]=a.terms[i+1]/a.h;
		}
	ret.terms[a.last_term]=complex(0.0,0.0); // unknown but small
	return(ret);
	}
void diff_eq(t_s &ret2,t_s &y)
	{
	// solve 1st order ordinary differential equation provided in "equation".
	register int k;
	t_s work;
	t_s work2;
	
	work=y;
	work2=y;
	for (k=work.last_term;k<MAX_TERMS;k++)
		{
		// get derivative t_s (work) from function t_s work2
		equation(work,work2); 
		set_term_from_deriv(k,work,work2);
		}
	ret2 = work2;
	}
void higher_diff_eq(t_s &ret2,t_s &y,int n)
	{
	// solve 1st order ordinary differential equation provided in "equation".
	register int k;
	t_s work;
	t_s work2;
	
	work=y;
	work2=y;
	for (k=work.last_term;k<MAX_TERMS;k++)
		{
		// get derivative t_s (work) from function t_s work2
		equation(work,work2); 
		higher_set_term_from_deriv(k,work,work2,n);
		}
	ret2 = work2;
	}
void set_term_from_deriv(int i,t_s &dy,t_s &y)
	{
	if (i > 0) 
		y.terms[i]=dy.terms[i-1]*y.h/complex(double(i),0.0);
	else 
		y.terms[i]=dy.terms[i-1]*y.h;
	y.last_term++;
	}
void higher_set_term_from_deriv(int i,t_s &dy,t_s &y,int n)
	{
	complex adj1,adj2;
	
	adj1 = complex(1.0,0.0);
	adj2 = complex(1.0,0.0);
	
	for (int a=0;a<n;a++)
		adj1 *= dy.h;
	for (int b=0;b<n;b++)
		{
		if (i+b > 0)
			{
			adj2 *= complex(double(i+b),0.0);
			}
		}
	y.terms[i]=dy.terms[i-n]*adj1/adj2;
	y.last_term++;
	}
void jump(t_s &y)
	{
	t_s y2;
	
	y2 	= y;
	y2.terms[0] = complex(0.0,0.0);
	for (int i = 0;i < y.last_term;i++)
		{
		y2.terms[0] += y.terms[i];
		}
	y2.x0 = y.x0 + y.h;
	y2.last_term = 1;
	y = y2;
	}

void leap(t_s &y,int n)
	{
	t_s y2;
	complex adj1,adj2;
		
	y2 	= y;

	adj2 = complex(1.0,0.0); 
	for (int j = 0;j < n; j++)
		{
		y2.terms[j] = complex(0.0,0.0);
		for (int i = 0;i < y.last_term-j;i++)
			{
			adj1 = complex(1.0,0.0);
			for (int k = 1 ; k <= j; k++)
				{
				adj1 *= complex(double(i+k),0.0);
				}
			y2.terms[j] += y.terms[i+j]*adj1;
			}
		if (j > 0)
			{
			adj2 *= j;
			y2.terms[j] /= adj2;
			}
		}
	y2.x0 = y.x0 + y.h;
	y2.last_term = n;
	y = y2;
	}

int get_last_term(t_s &y)
	{
	return(y.last_term);
	}
	
double get_x0(t_s &y)
	{
	return(real(y.x0));
	}
double get_y0(t_s &y)
	{
	return(real(y.terms[0]));
	}

void adjust_h(t_s &y,complex new_h)
	{
	t_s y2;
	complex t,adj;
	
	t = new_h / y.h;
	adj = t;
	y2 	= y;
	for (int i = 1;i < y.last_term;i++)
		{
		y2.terms[i] = y.terms[i]*adj;
		adj *= t;
		}
	y2.h = new_h;
	y = y2;
	}
