/* Steven Andrews, 1/28/97 */
/* modified 11/99 */
/* Added code called '2004 version' during 6/04.  It is separate from rest of code. */
/* See documentation called dynsys doc */
/* Copyright 2003 by Steven Andrews.  Permission is granted
   for non-commercial use of and modifications to the code. */

#include "dynsys.h"
#include "Plot.h"
#include "Utility.h"
#include "Rn.h"
#include "DiskIO.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stddef.h>

#define Pmax 10


int ODEeuler(void (*eqm)(float *,float *,float *),float *k,int p,float *u,float tf,float dt,int (*trackfn)(float,int,float *)) {
	float dudt[Pmax],t;
	int i,z=0;

	for(t=0;t<tf;t+=dt)	{
		(*eqm)(u,k,dudt);
		for(i=0;i<p;i++)	u[i]+=dudt[i]*dt;
		if(trackfn)
			if((z=trackfn(t,p,u))!=0) break; }
	return z; }


int ODErk4(void (*eqm)(float *,float *,float *),float *k,int p,float *u,float tf,float dt,int (*trackfn)(float,int,float *)) {
	float ut[Pmax],k1[Pmax],k2[Pmax],k3[Pmax],k4[Pmax];
	float t,dt2,dt3,dt6;
	int i,z=0;
	
	dt2=dt*0.5;
	dt3=dt/3.0;
	dt6=dt/6.0;
	for(t=0;t<tf;t+=dt)	{
		(*eqm)(u,k,k1);
		for(i=0;i<p;i++)	ut[i]=u[i]+k1[i]*dt2;
		(*eqm)(ut,k,k2);
		for(i=0;i<p;i++)	ut[i]=u[i]+k2[i]*dt2;
		(*eqm)(ut,k,k3);
		for(i=0;i<p;i++)	ut[i]=u[i]+k3[i]*dt;
		(*eqm)(ut,k,k4);
		for(i=0;i<p;i++)	u[i]+=(k1[i]+k4[i])*dt6+(k2[i]+k3[i])*dt3;
		if(trackfn)
			if((z=trackfn(t,p,u))!=0) break; }
	return z; }


int ODErkas(void (*eqm)(float *,float *,float *),float *k,int p,float *u,float tf,float dt,int (*trackfn)(float,int,float *)) {
	float ut[Pmax],k1[Pmax],k2[Pmax],k3[Pmax],k4[Pmax],k5[Pmax];
	float ubig[Pmax],usmall[Pmax],uscal[Pmax],eps,err,diff,safe;
	float t,dt2,dt3,dt4,dt6,dt12;
	int i,z=0;
	
	(*eqm)(u,k,k1);												/* define a scaling vector */
	for(i=0;i<p;i++)	uscal[i]=fabs(u[i])+fabs(dt*k1[i]);
	eps=0;
	safe=0.95;														/* safety factor */
	
	for(t=0;t<tf;) {
		dt2=dt*0.5;
		dt3=dt/3.0;
		dt4=dt/4.0;
		dt6=dt/6.0;
		dt12=dt/12.0;
		
		for(i=0;i<p;i++)	ut[i]=u[i]+k1[i]*dt2;	/* take size dt step	*/
		(*eqm)(ut,k,k2);
		for(i=0;i<p;i++)	ut[i]=u[i]+k2[i]*dt2;
		(*eqm)(ut,k,k3);
		for(i=0;i<p;i++)	ut[i]=u[i]+k3[i]*dt;
		(*eqm)(ut,k,k4);
		for(i=0;i<p;i++)	ubig[i]=u[i]+(k1[i]+k4[i])*dt6+(k2[i]+k3[i])*dt3;
		
		for(i=0;i<p;i++)	ut[i]=u[i]+k1[i]*dt4;	/* take size dt2 step	*/
		(*eqm)(ut,k,k2);
		for(i=0;i<p;i++)	ut[i]=u[i]+k2[i]*dt4;
		(*eqm)(ut,k,k3);
		for(i=0;i<p;i++)	ut[i]=u[i]+k3[i]*dt2;
		(*eqm)(ut,k,k4);
		for(i=0;i<p;i++)	usmall[i]=u[i]+(k1[i]+k4[i])*dt12+(k2[i]+k3[i])*dt6;
		
		(*eqm)(usmall,k,k5);								/* take second dt2 step	*/
		for(i=0;i<p;i++)	ut[i]=usmall[i]+k5[i]*dt4;
		(*eqm)(ut,k,k2);
		for(i=0;i<p;i++)	ut[i]=usmall[i]+k2[i]*dt4;
		(*eqm)(ut,k,k3);
		for(i=0;i<p;i++)	ut[i]=usmall[i]+k3[i]*dt2;
		(*eqm)(ut,k,k4);
		for(i=0;i<p;i++)	usmall[i]+=(k5[i]+k4[i])*dt12+(k2[i]+k3[i])*dt6;
		
		err=0;															/* compute error	*/
		for(i=0;i<p;i++) {
			diff=fabs(usmall[i]-ubig[i])/uscal[i];
			err=(err>diff)?err:diff; }
		
		if(!eps) eps=err;
		if(err<=eps) {
			t+=dt;
			for(i=0;i<p;i++) u[i]=usmall[i];
			dt=safe*dt*pow(eps/err,0.25);
			if(t+dt>tf) dt=tf-t;
			if(trackfn)
				if((z=trackfn(t,p,u))!=0) break;
			(*eqm)(u,k,k1); }
		else
			dt=safe*dt*pow(eps/err,0.2); }
	return z; }


/* k value is omega^2 (=k/m) */
void EQMsho(float *u,float *k,float *dudt) {
	dudt[0]=u[1];
	dudt[1]=-k[0]*u[0]; }


/* k values are: sigma, r, b. Suggested values: 10, 28, 8/3 */
void EQMlorenz(float *u,float *k,float *dudt) {
	dudt[0]=k[0]*(u[1]-u[0]);
	dudt[1]=k[1]*u[0]-u[1]-u[0]*u[2];
	dudt[2]=u[0]*u[1]-k[2]*u[2]; }


phptr phptalloc(int sp,int fp,int fs) {
	phptr u;
	int ok;

	ok=1;
	u=(phptr) malloc(sizeof(struct phpt));
	ok=ok&&u;
	if(!ok) return 0;
	u->sp=sp;
	u->fp=fp;
	u->fs=fs;
	u->sa=u->fa=NULL;
	if(sp)	{u->sa=allocV(sp);ok=ok&&u->sa;}
	if(fp&&fs)	{u->fa=allocM(fs,fp);ok=ok&&u->fa;}
	if(ok) return u;
	else {phptfree(u);return 0; }}


void phptfree(phptr u) {
	if(!u) return;
	if(u->sa) freeV(u->sa);
	if(u->fa) freeM(u->fa);
	free(u); }


int phptsave(phptr u,char *fnames,char *fnamef) {
	int ok;
	
	ok=1;
	if(u->sp)	ok=ok&&SaveData(u->sa,u->sp,1,fnames,0);
	if(u->fp&&u->fs)	ok=ok&&SaveData(u->fa,u->fs,u->fp,fnamef,0);
	return ok; }


phptr phptload(char *fnames,char *fnamef) {
	phptr u;
	int m,n;
	float *a;

	u=phptalloc(0,0,0);
	if(!u) return NULL;
	if(LoadData2(&a,&m,&n,fnames,0)) {
		u->sp=m;
		u->sa=a; }
	if(LoadData2(&a,&m,&n,fnamef,0)) {
		u->fs=m;
		u->fp=n;
		u->fa=a; }
	return u; }


int ODEFeuler(void (*eqm)(phptr,void *,phptr),void *k,phptr u,float *Dt,float intpar[],int (*trackfn)(float,phptr,void *),void *trackptr) {
	phptr dudt;
	float *s,*f,*dsdt,*dfdt;
	float t,tf,dt;
	unsigned int sp,fp,fs;
	int i,j,z=0;

	sp=u->sp;
	fp=u->fp;
	fs=u->fs;
	s=u->sa;
	f=u->fa;
	dudt=phptalloc(sp,fp,fs);
	if(!dudt) return 1;
	dsdt=dudt->sa;
	dfdt=dudt->fa;
	dt=intpar[0];
	tf=*Dt;
	if(trackfn)	trackfn(0,u,trackptr);
	for(t=0;t<tf;t+=dt) {
		(*eqm)(u,k,dudt);
		for(i=0;i<sp;i++)	s[i]+=dsdt[i]*dt;
		for(j=0;j<fp;j++)	for(i=0;i<fs;i++)	f[fp*i+j]+=dfdt[fp*i+j]*dt;
		if(trackfn)
			if((z=trackfn(t,u,trackptr))!=0)	{t+=dt;break;}}
	*Dt=t;
	phptfree(dudt);
	return z; }


int ODEFrk4(void (*eqm)(phptr,void *,phptr),void *k,phptr u,float *Dt,float intpar[],int (*trackfn)(float,phptr,void *),void *trackptr) {
	phptr ut,k1,k2,k3,k4;
	unsigned int sp,fp,fs;
	float *s,*st,*sk1,*sk2,*sk3,*sk4;
	float *f,*ft,*fk1,*fk2,*fk3,*fk4;
	float t,tf,dt,dt2,dt3,dt6;
	int i,j,z=0;

	sp=u->sp;
	fp=u->fp;
	fs=u->fs;
	s=u->sa;
	f=u->fa;
	ut=phptalloc(sp,fp,fs);
	k1=phptalloc(sp,fp,fs);
	k2=phptalloc(sp,fp,fs);
	k3=phptalloc(sp,fp,fs);
	k4=phptalloc(sp,fp,fs);
	if(!(ut&&k1&&k2&&k3&&k4)) return 1;
	st=ut->sa;
	ft=ut->fa;
	sk1=k1->sa;
	fk1=k1->fa;
	sk2=k2->sa;
	fk2=k2->fa;
	sk3=k3->sa;
	fk3=k3->fa;
	sk4=k4->sa;
	fk4=k4->fa;
	dt=intpar[0];
	dt2=dt/2.0;
	dt3=dt/3.0;
	dt6=dt/6.0;
	tf=*Dt;
	if(trackfn)	trackfn(0,u,trackptr);
	
	for(t=0;t<tf;t+=dt)	{
		(*eqm)(u,k,k1);
		for(i=0;i<sp;i++) st[i]=s[i]+sk1[i]*dt2;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk1[fp*i+j]*dt2;
		(*eqm)(ut,k,k2);
		for(i=0;i<sp;i++) st[i]=s[i]+sk2[i]*dt2;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk2[fp*i+j]*dt2;
		(*eqm)(ut,k,k3);
		for(i=0;i<sp;i++) st[i]=s[i]+sk3[i]*dt;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk3[fp*i+j]*dt;
		(*eqm)(ut,k,k4);
		for(i=0;i<sp;i++) s[i]+=(sk1[i]+sk4[i])*dt6+(sk2[i]+sk3[i])*dt3;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) f[fp*i+j]+=(fk1[fp*i+j]+fk4[fp*i+j])*dt6+(fk2[fp*i+j]+fk3[fp*i+j])*dt3;

		if(trackfn)
			if((z=trackfn(t,u,trackptr))!=0)	{t+=dt;break;}}
	*Dt=t;
	phptfree(ut);
	phptfree(k1);
	phptfree(k2);
	phptfree(k3);
	phptfree(k4);
	return z; }


int ODEFrkas(void (*eqm)(phptr,void *,phptr),void *k,phptr u,float *Dt,float intpar[],int (*trackfn)(float,phptr,void *),void *trackptr) {
	phptr ut,k1,k2,k3,k4,k5;
	phptr ubig,usmall,uscal;
	unsigned int sp,fp,fs;
	float *s,*st,*sk1,*sk2,*sk3,*sk4,*sk5,*sbig,*ssmall,*sscal;
	float *f,*ft,*fk1,*fk2,*fk3,*fk4,*fk5,*fbig,*fsmall,*fscal;
	float eps,err,sum,diff,safe;
	float t,tf,dt,dt2,dt3,dt4,dt6,dt12;
	int i,j,z=0;

	sp=u->sp;
	fp=u->fp;
	fs=u->fs;
	s=u->sa;
	f=u->fa;
	ut=phptalloc(sp,fp,fs);
	k1=phptalloc(sp,fp,fs);
	k2=phptalloc(sp,fp,fs);
	k3=phptalloc(sp,fp,fs);
	k4=phptalloc(sp,fp,fs);
	k5=phptalloc(sp,fp,fs);
	ubig=phptalloc(sp,fp,fs);
	usmall=phptalloc(sp,fp,fs);
	uscal=phptalloc(sp,fp,fs);
	if(!(ut&&k1&&k2&&k3&&k4&&k5&&ubig&&usmall&&uscal)) return 1;
	st=ut->sa;
	ft=ut->fa;
	sk1=k1->sa;
	fk1=k1->fa;
	sk2=k2->sa;
	fk2=k2->fa;
	sk3=k3->sa;
	fk3=k3->fa;
	sk4=k4->sa;
	fk4=k4->fa;
	sk5=k5->sa;
	fk5=k5->fa;
	sbig=ubig->sa;
	fbig=ubig->fa;
	ssmall=usmall->sa;
	fsmall=usmall->fa;
	sscal=uscal->sa;
	fscal=uscal->fa;

	tf=*Dt;
	dt=intpar[0];
	
	(*eqm)(u,k,k1);										/* define scaling vector */
	for(i=0;i<sp;i++) sscal[i]=fabs(s[i])+fabs(dt*sk1[i]);
	for(j=0;j<fp;j++)	{
		sum=0;
		for(i=0;i<fs;i++) sum+=fabs(f[fp*i+j])+fabs(dt*fk1[fp*i+j]);
		sum/=fs;
		for(i=0;i<fs;i++) fscal[fp*i+j]=sum; }
	eps=0;
	safe=0.95;
	if(trackfn)	trackfn(0,u,trackptr);
	
	for(t=0;t<tf;) {
		dt2=dt/2.0;
		dt3=dt/3.0;
		dt4=dt/4.0;
		dt6=dt/6.0;
		dt12=dt/12.0;

		for(i=0;i<sp;i++) st[i]=s[i]+sk1[i]*dt2;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk1[fp*i+j]*dt2;
		(*eqm)(ut,k,k2);
		for(i=0;i<sp;i++) st[i]=s[i]+sk2[i]*dt2;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk2[fp*i+j]*dt2;
		(*eqm)(ut,k,k3);
		for(i=0;i<sp;i++) st[i]=s[i]+sk3[i]*dt;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk3[fp*i+j]*dt;
		(*eqm)(ut,k,k4);
		for(i=0;i<sp;i++) sbig[i]=s[i]+(sk1[i]+sk4[i])*dt6+(sk2[i]+sk3[i])*dt3;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) fbig[fp*i+j]=f[fp*i+j]+(fk1[fp*i+j]+fk4[fp*i+j])*dt6+(fk2[fp*i+j]+fk3[fp*i+j])*dt3;

		for(i=0;i<sp;i++) st[i]=s[i]+sk1[i]*dt4;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk1[fp*i+j]*dt4;
		(*eqm)(ut,k,k2);
		for(i=0;i<sp;i++) st[i]=s[i]+sk2[i]*dt4;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk2[fp*i+j]*dt4;
		(*eqm)(ut,k,k3);
		for(i=0;i<sp;i++) st[i]=s[i]+sk3[i]*dt2;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=f[fp*i+j]+fk3[fp*i+j]*dt2;
		(*eqm)(ut,k,k4);
		for(i=0;i<sp;i++) ssmall[i]=s[i]+(sk1[i]+sk4[i])*dt12+(sk2[i]+sk3[i])*dt6;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) fsmall[fp*i+j]=f[fp*i+j]+(fk1[fp*i+j]+fk4[fp*i+j])*dt12+(fk2[fp*i+j]+fk3[fp*i+j])*dt6;

		(*eqm)(usmall,k,k5);
		for(i=0;i<sp;i++) st[i]=ssmall[i]+sk5[i]*dt4;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=fsmall[fp*i+j]+fk5[fp*i+j]*dt4;
		(*eqm)(ut,k,k2);
		for(i=0;i<sp;i++) st[i]=ssmall[i]+sk2[i]*dt4;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=fsmall[fp*i+j]+fk2[fp*i+j]*dt4;
		(*eqm)(ut,k,k3);
		for(i=0;i<sp;i++) st[i]=ssmall[i]+sk3[i]*dt2;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) ft[fp*i+j]=fsmall[fp*i+j]+fk3[fp*i+j]*dt2;
		(*eqm)(ut,k,k4);
		for(i=0;i<sp;i++) ssmall[i]+=(sk5[i]+sk4[i])*dt12+(sk2[i]+sk3[i])*dt6;
		for(j=0;j<fp;j++) for(i=0;i<fs;i++) fsmall[fp*i+j]+=(fk5[fp*i+j]+fk4[fp*i+j])*dt12+(fk2[fp*i+j]+fk3[fp*i+j])*dt6;

		err=0;
		for(i=0;i<sp;i++) {
			diff=fabs(ssmall[i]-sbig[i])/sscal[i];
			err=(err>diff)?err:diff; }
		for(j=0;j<fp;j++) for(i=0;i<fs;i++)	{
			diff=fabs(fsmall[fp*i+j]-fbig[fp*i+j])/fscal[fp*i+j];
			err=(err>diff)?err:diff; }

		if(!eps) eps=err;
		if(err<=eps) {
			t+=dt;
			for(i=0;i<sp;i++) s[i]=ssmall[i];
			for(j=0;j<fp;j++) for(i=0;i<fs;i++) f[fp*i+j]=fsmall[fp*i+j];
			dt=safe*dt*pow(eps/err,0.25);
			if(t+dt>tf) dt=tf-t;
			if(trackfn)
				if((z=trackfn(t,u,trackptr))!=0)	break;
			(*eqm)(u,k,k1); }
		else
			dt=safe*dt*pow(eps/err,0.2); }
	
	*Dt=t;
	phptfree(ut);
	phptfree(k1);
	phptfree(k2);
	phptfree(k3);
	phptfree(k4);
	phptfree(k5);
	phptfree(ubig);
	phptfree(usmall);
	phptfree(uscal);
	return z; }


// all derivatives are 0
void EQMFzero(phptr u,void *k,phptr dudt) {
	int i,j,fp;
	
	k=NULL;												// to avoid warning
	fp=u->fp;
	for(i=0;i<dudt->sp;i++)	dudt->sa[i]=0;
	for(j=0;j<dudt->fp;j++)	for(i=0;i<dudt->fs;i++)	dudt->fa[fp*i+j]=0;
	return; }


// k[0] is w^2; sa[0] is position, sa[1] is velocity
void EQMFsho(phptr u,void *k,phptr dudt) {
	dudt->sa[0]=u->sa[1];
	dudt->sa[1]=-((float *)k)[0]*u->sa[0];
	return; }


// diffusion equation; k[0] is diffusion constant; u[row,0]=concentration; u->fp=1
void EQMFdiff(phptr u,void *k,phptr dudt) {
	int i,m;
	float alpha2;
	
	alpha2=((float *)k)[0];
	m=u->fs;
	dudt->fa[0]=alpha2*(u->fa[1]-u->fa[0]);
	for(i=1;i<m-1;i++)
		dudt->fa[i]=alpha2*(u->fa[i+1]+u->fa[i-1]-2*u->fa[i]);
	dudt->fa[m-1]=alpha2*(u->fa[m-2]-u->fa[m-1]);
	return; }


// wave equation on periodic string
// k[0]=c^2; u[row,0]=position, u[row,1]=velocity; u->fp=2
void EQMFwave(phptr u,void *k,phptr dudt) {
	int i,m;
	float c2;
	
	c2=((float *)k)[0];
	m=u->fs;
	dudt->fa[0]=u->fa[1];
	dudt->fa[1]=c2*(u->fa[2]+u->fa[2*(m-1)]-2*u->fa[0]);
	for(i=1;i<m-1;i++) {
		dudt->fa[2*i]=u->fa[2*i+1];
		dudt->fa[2*i+1]=c2*(u->fa[2*(i+1)]+u->fa[2*(i-1)]-2*u->fa[2*i]); }
	dudt->fa[2*(m-1)]=u->fa[2*(m-1)+1];
	dudt->fa[2*(m-1)+1]=c2*(u->fa[0]+u->fa[2*(m-2)]-2*u->fa[2*(m-1)]);
	return; }


/* displays first two scalers		*/
int TKFticker(float t,phptr u,void *tkptr) {
	tkptr=NULL;											// to avoid warning
	printf("%f\t%f\t%f\n",t,u->sa[0],u->sa[1]);
	return inkey(); }


/* plots first scaler						*/
int TKFtimeplot(float t,phptr u,void *tkptr) {
	tkptr=NULL;											// to avoid warning
	PlotPt(t,u->sa[0]);
	return inkey(); }


/* plots first field						*/
/* tkptr is float[], [0]=xmin, [1]=xmax, [2]=ymin, [3]=ymax	*/
int TKFshowfield(float t,phptr u,void *tkptr) {
	int i,fp;
	float xmin,xmax,ymin,ymax,dx;
	
	t=0;																// to avoid warning
	fp=u->fp;
	xmin=((float *)tkptr)[0];
	xmax=((float *)tkptr)[1];
	ymin=((float *)tkptr)[2];
	ymax=((float *)tkptr)[3];
	dx=(xmax-xmin)/u->fs;
	SetScales(xmin,xmax,ymin,ymax);
	PlotClear();
	DrawAxes();
	PlotPt(xmin,u->fa[0]);
	for(i=1;i<u->fs;i++) PlotLine(xmin+i*dx,u->fa[fp*i]);
	return inkey(); }


/******************** Start of 2004 version *****************************/

odeptr allocodestruct(int dim,int order,float *dtptr,void *systemptr,int (*eqm)(void *)) {
	int i;
	odeptr ode;

	if(!(order==1||order==2||order==4||order==5)) return NULL;
	ode=(odeptr) malloc(sizeof(struct odestruct));
	if(!ode) return NULL;
	ode->dim=dim;
	ode->order=order;
	ode->dtptr=dtptr;
	ode->dtsugg=0;
	ode->dtmax=0;
	ode->eps=0;
	ode->systemptr=systemptr;
	ode->eqm=eqm;
	ode->state0=ode->state1=NULL;
	ode->scale=NULL;
	ode->k1=ode->k2=ode->k3=ode->k4=NULL;
	ode->state0=(float**) calloc(dim,sizeof(float*));
	ode->state1=(float**) calloc(dim,sizeof(float*));
	if(!ode->state0||!ode->state1) {freeodestruct(ode);return NULL;}
	for(i=0;i<dim;i++) ode->state0[i]=ode->state1[i]=NULL;
	if(order>=2) {
		ode->k1=(float *) calloc(dim,sizeof(float));
		if(!ode->k1) {freeodestruct(ode);return NULL;}
		for(i=0;i<dim;i++) ode->k1[i]=0; }
	if(order>=4) {
		ode->k2=(float *) calloc(dim,sizeof(float));
		if(!ode->k2) {freeodestruct(ode);return NULL;}
		for(i=0;i<dim;i++) ode->k2[i]=0; }
	if(order>=5) {
		ode->scale=(float*) calloc(dim,sizeof(float));
		if(!ode->scale) {freeodestruct(ode);return NULL;}
		for(i=0;i<dim;i++) ode->scale[i]=1.0;
		ode->k3=(float *) calloc(dim,sizeof(float));
		if(!ode->k3) {freeodestruct(ode);return NULL;}
		ode->k4=(float *) calloc(dim,sizeof(float));
		if(!ode->k4) {freeodestruct(ode);return NULL;}
		for(i=0;i<dim;i++) ode->k3[i]=ode->k4[i]=0; }
	return ode; }


void freeodestruct(odeptr ode) {
	if(!ode) return;
	if(ode->state0) free(ode->state0);
	if(ode->state1) free(ode->state1);
	if(ode->scale) free(ode->scale);
	if(ode->k1) free(ode->k1);
	if(ode->k2) free(ode->k2);
	free(ode);
	return; }


int runodestruct(odeptr ode) {
	int dim,order,i,er;
	static int redoctr=0;
	float **s0,**s1,dt,dt2,dt3,dt4,dt6,dt12;
	float *k1,*k2,*k3,*k4;
	float diff,diffmax,*scale;
	void *systemptr;

	dim=ode->dim;
	order=ode->order;
	systemptr=ode->systemptr;
	s0=ode->state0;
	s1=ode->state1;
	if(order==1) {																	// *** ORDER 1 ***
		dt=*ode->dtptr;
		for(i=0;i<dim;i++) *s1[i]=*s0[i];
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) *s0[i]=*s1[i]+dt**s0[i]; }
	else if(order==2) {															// *** ORDER 2 ***
		dt=*ode->dtptr;
		k1=ode->k1;
		dt2=dt/2.0;
		for(i=0;i<dim;i++) *s1[i]=*s0[i];
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k1[i]=*s1[i];
			*s1[i]+=dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			*s1[i]=k1[i];
			*s0[i]=k1[i]+dt**s0[i]; }}
	else if(order==4) {															// *** ORDER 4 ***
		dt=*ode->dtptr;
		k1=ode->k1;
		k2=ode->k2;
		dt2=dt/2.0;
		dt3=dt/3.0;
		dt6=dt/6.0;
		for(i=0;i<dim;i++) *s1[i]=*s0[i];
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k1[i]=*s1[i];																// k1[i] is old state
			k2[i]=k1[i]+dt6**s0[i];
			*s1[i]+=dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]+=dt3**s0[i];
			*s1[i]=k1[i]+dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]+=dt3**s0[i];
			*s1[i]=k1[i]+dt**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			*s0[i]=k2[i]+dt6**s0[i];
			*s1[i]=k1[i]; }}
	else if(order==5) {															// *** ORDER 5 ***
		dt=ode->dtsugg>0?ode->dtsugg:*ode->dtptr;
		if(ode->dtmax&&dt>ode->dtmax) dt=ode->dtmax;
		scale=ode->scale;
		k1=ode->k1;
		k2=ode->k2;
		k3=ode->k3;
		k4=ode->k4;
		dt2=dt/2.0;
		dt3=dt/3.0;
		dt4=dt/4.0;
		dt6=dt/6.0;
		dt12=dt/12.0;
		for(i=0;i<dim;i++) *s1[i]=*s0[i];
		if((er=(ode->eqm)(systemptr))) return er;			// *** start of dt step
		for(i=0;i<dim;i++) {
			k1[i]=*s1[i];																// k1 is old state
			k3[i]=*s0[i];
			*s1[i]+=dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]=k1[i]+dt6*k3[i]+dt3**s0[i];
			*s1[i]=k1[i]+dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]+=dt3**s0[i];
			*s1[i]=k1[i]+dt**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++)
			k4[i]=k2[i]+dt6**s0[i];											// k4 is result from dt step

		for(i=0;i<dim;i++)														// start of first dt2 step
			*s1[i]=k1[i]+dt4*k3[i];
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]=k1[i]+dt12*k3[i]+dt6**s0[i];
			*s1[i]=k1[i]+dt4**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]+=dt6**s0[i];
			*s1[i]=k1[i]+dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++)
			*s1[i]=k3[i]=k2[i]+dt12**s0[i];							// k3 is result from dt2 step

		if((er=(ode->eqm)(systemptr))) return er;			// start of second dt2 step
		for(i=0;i<dim;i++) {
			k2[i]=k3[i]+dt12**s0[i];
			*s1[i]+=dt4**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]+=dt6**s0[i];
			*s1[i]=k3[i]+dt4**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			k2[i]+=dt6**s0[i];
			*s1[i]=k3[i]+dt2**s0[i]; }
		if((er=(ode->eqm)(systemptr))) return er;
		for(i=0;i<dim;i++) {
			*s1[i]=k1[i];																// s1 is old state
			*s0[i]=k1[i]=k2[i]+dt12**s0[i]; }						// s0 and k1 are result from second dt2 step

		*ode->dtptr=dt;
		diffmax=0;
		for(i=0;i<dim;i++) {
			diff=fabs(k1[i]-k4[i])/scale[i];
			if(!(diff+1.0>diff)) break;
			if(diff>diffmax) diffmax=diff; }
		if(!(diff+1.0>diff)) {
			ode->dtsugg=dt*0.5;
			for(i=0;i<dim;i++) *s0[i]=*s1[i];
			if(redoctr++==20) return -1;
			return runodestruct(ode); }
		else if(diffmax==0)
			ode->dtsugg=dt*1.1;
		else if(!ode->eps)
			ode->eps=diffmax;
		else if(diffmax<=ode->eps)
			ode->dtsugg=dt*0.95*pow(ode->eps/diffmax,0.25);
		else {
			ode->dtsugg=dt*0.95*pow(ode->eps/diffmax,0.2);
			for(i=0;i<dim;i++) *s0[i]=*s1[i];
			if(redoctr++==20) return -1;
			return runodestruct(ode); }}
		if(ode->dtmax&&ode->dtsugg>ode->dtmax) ode->dtsugg=ode->dtmax;
		redoctr=0;
	return 0; }


/* Example program for 2004 routines */


typedef struct shostate {
	float position[2];
	float velocity[2];
	float omega2;
	float gamma;
	float time; } *shostateptr;

int odestructsho(void *system);


int odestructsho(void *system) {
	shostateptr sys;

	sys=(shostateptr) system;
	sys->position[0]=sys->velocity[1];
	sys->velocity[0]=-sys->omega2*sys->position[1]-2.0*sys->gamma*sys->velocity[1];
	return 0; }


int odestructexample(void) {
	odeptr ode;
	float dt;
	shostateptr sys;
	int er,order;

	order=5;
	dt=0.2;
	sys=(shostateptr)malloc(sizeof(struct shostate));
	if(!sys) return 0;
	sys->position[0]=sys->position[1]=1.0;
	sys->velocity[0]=sys->velocity[1]=-0.1;
	sys->omega2=0.5;
	sys->gamma=0.1;
	sys->time=0;
	ode=allocodestruct(2,order,&dt,(void*)sys,&odestructsho);
	if(!ode) return 0;
	ode->state0[0]=&sys->position[0];
	ode->state1[0]=&sys->position[1];
	ode->state0[1]=&sys->velocity[0];
	ode->state1[1]=&sys->velocity[1];
	if(order==5) ode->dtmax=1.0/sqrt(sys->omega2);	// this keeps amplitude and phase good to at least t=500
	for(;sys->time<500;sys->time+=dt) {
		printf("t,x,v: %f %e %e\n",sys->time,sys->position[0],sys->velocity[0]);
		er=runodestruct(ode);
		if(er) break; }
	freeodestruct(ode);
	return 0; }



