xref: /csrg-svn/usr.bin/spline/spline.c (revision 31014)
124983Ssam #ifndef lint
2*31014Sbostic static char *sccsid = "@(#)spline.c	4.4 (Berkeley) 05/02/87";
324983Ssam #endif
424983Ssam 
51099Sbill #include <stdio.h>
69366Sshannon #include <math.h>
71099Sbill 
81099Sbill #define NP 1000
99366Sshannon #define INF HUGE
101099Sbill 
111099Sbill struct proj { int lbf,ubf; float a,b,lb,ub,quant,mult,val[NP]; } x,y;
121099Sbill float *diag, *r;
131099Sbill float dx = 1.;
141099Sbill float ni = 100.;
151099Sbill int n;
161099Sbill int auta;
171099Sbill int periodic;
181099Sbill float konst = 0.0;
191099Sbill float zero = 0.;
201099Sbill 
211099Sbill /* Spline fit technique
221099Sbill let x,y be vectors of abscissas and ordinates
2324983Ssam     h   be vector of differences hi=xi-xi-1
241099Sbill     y"  be vector of 2nd derivs of approx function
251099Sbill If the points are numbered 0,1,2,...,n+1 then y" satisfies
261099Sbill (R W Hamming, Numerical Methods for Engineers and Scientists,
271099Sbill 2nd Ed, p349ff)
2824983Ssam 	hiy"i-1+2(hi+hi+1)y"i+hi+1y"i+1
291099Sbill 
3024983Ssam 	= 6[(yi+1-yi)/hi+1-(yi-yi-1)/hi]   i=1,2,...,n
311099Sbill 
3224983Ssam where y"0 = y"n+1 = 0
331099Sbill This is a symmetric tridiagonal system of the form
341099Sbill 
3524983Ssam 	| a1 h2               |  |y"1|      |b1|
3624983Ssam 	| h2 a2 h3            |  |y"2|      |b2|
3724983Ssam 	|    h3 a3 h4         |  |y"3|  =   |b3|
381099Sbill 	|         .           |  | .|      | .|
391099Sbill 	|            .        |  | .|      | .|
401099Sbill It can be triangularized into
4124983Ssam 	| d1 h2               |  |y"1|      |r1|
4224983Ssam 	|    d2 h3            |  |y"2|      |r2|
4324983Ssam 	|       d3 h4         |  |y"3|  =   |r3|
441099Sbill 	|          .          |  | .|      | .|
451099Sbill 	|             .       |  | .|      | .|
461099Sbill where
4724983Ssam 	d1 = a1
481099Sbill 
4924983Ssam 	r0 = 0
501099Sbill 
5124983Ssam 	di = ai - hi2/di-1	1<i<_n
521099Sbill 
53*31014Sbostic 	ri = bi - hiri-1/di-1i	1<_i<_n
541099Sbill 
551099Sbill the back solution is
5624983Ssam 	y"n = rn/dn
571099Sbill 
5824983Ssam 	y"i = (ri-hi+1y"i+1)/di	1<_i<n
591099Sbill 
6024983Ssam superficially, di and ri don't have to be stored for they can be
611099Sbill recalculated backward by the formulas
621099Sbill 
6324983Ssam 	di-1 = hi2/(ai-di)	1<i<_n
641099Sbill 
6524983Ssam 	ri-1 = (bi-ri)di-1/hi	1<i<_n
661099Sbill 
671099Sbill unhappily it turns out that the recursion forward for d
681099Sbill is quite strongly geometrically convergent--and is wildly
691099Sbill unstable going backward.
701099Sbill There's similar trouble with r, so the intermediate
711099Sbill results must be kept.
721099Sbill 
731099Sbill Note that n-1 in the program below plays the role of n+1 in the theory
741099Sbill 
7524983Ssam Other boundary conditions_________________________
761099Sbill 
771099Sbill The boundary conditions are easily generalized to handle
781099Sbill 
7924983Ssam 	y0" = ky1", yn+1"   = kyn"
801099Sbill 
811099Sbill for some constant k.  The above analysis was for k = 0;
821099Sbill k = 1 fits parabolas perfectly as well as stright lines;
831099Sbill k = 1/2 has been recommended as somehow pleasant.
841099Sbill 
8524983Ssam All that is necessary is to add h1 to a1 and hn+1 to an.
861099Sbill 
871099Sbill 
8824983Ssam Periodic case_____________
891099Sbill 
901099Sbill To do this, add 1 more row and column thus
911099Sbill 
9224983Ssam 	| a1 h2            h1 |  |y1"|     |b1|
9324983Ssam 	| h2 a2 h3            |  |y2"|     |b2|
9424983Ssam 	|    h3 a4 h4         |  |y3"|     |b3|
951099Sbill 	|                     |  | .|  =  | .|
961099Sbill 	|             .       |  | .|     | .|
9724983Ssam 	| h1            h0 a0 |  | .|     | .|
981099Sbill 
9924983Ssam where h0=_ hn+1
1001099Sbill 
1011099Sbill The same diagonalization procedure works, except for
10224983Ssam the effect of the 2 corner elements.  Let si be the part
10324983Ssam of the last element in the ith "diagonalized" row that
1041099Sbill arises from the extra top corner element.
1051099Sbill 
10624983Ssam 		s1 = h1
1071099Sbill 
10824983Ssam 		si = -si-1hi/di-1	2<_i<_n+1
1091099Sbill 
1101099Sbill After "diagonalizing", the lower corner element remains.
11124983Ssam Call ti the bottom element that appears in the ith colomn
1121099Sbill as the bottom element to its left is eliminated
1131099Sbill 
11424983Ssam 		t1 = h1
1151099Sbill 
11624983Ssam 		ti = -ti-1hi/di-1
1171099Sbill 
11824983Ssam Evidently ti = si.
1191099Sbill Elimination along the bottom row
1201099Sbill introduces further corrections to the bottom right element
1211099Sbill and to the last element of the right hand side.
1221099Sbill Call these corrections u and v.
1231099Sbill 
12424983Ssam 	u1 = v1 = 0
1251099Sbill 
12624983Ssam 	ui = ui-1-si-1*ti-1/di-1
1271099Sbill 
12824983Ssam 	vi = vi-1-ri-1*ti-1/di-1	2<_i<_n+1
1291099Sbill 
1301099Sbill The back solution is now obtained as follows
1311099Sbill 
13224983Ssam 	y"n+1 = (rn+1+vn+1)/(dn+1+sn+1+tn+1+un+1)
1331099Sbill 
13424983Ssam 	y"i = (ri-hi+1*yi+1-si*yn+1)/di	1<_i<_n
1351099Sbill 
13624983Ssam Interpolation in the interval xi<_x<_xi+1 is by the formula
1371099Sbill 
13824983Ssam 	y = yix+ + yi+1x- -(h2i+1/6)[y"i(x+-x+3)+y"i+1(x--x-3)]
1391099Sbill where
14024983Ssam 	x+ = xi+1-x
1411099Sbill 
14224983Ssam 	x- = x-xi
1431099Sbill */
1441099Sbill 
1451099Sbill float
1461099Sbill rhs(i){
1471099Sbill 	int i_;
1481099Sbill 	double zz;
1491099Sbill 	i_ = i==n-1?0:i;
1501099Sbill 	zz = (y.val[i]-y.val[i-1])/(x.val[i]-x.val[i-1]);
1511099Sbill 	return(6*((y.val[i_+1]-y.val[i_])/(x.val[i+1]-x.val[i]) - zz));
1521099Sbill }
1531099Sbill 
1541099Sbill spline(){
1551099Sbill 	float d,s,u,v,hi,hi1;
1561099Sbill 	float h;
1571099Sbill 	float D2yi,D2yi1,D2yn1,x0,x1,yy,a;
1581099Sbill 	int end;
1591099Sbill 	float corr;
1601099Sbill 	int i,j,m;
1611099Sbill 	if(n<3) return(0);
1621099Sbill 	if(periodic) konst = 0;
1631099Sbill 	d = 1;
1641099Sbill 	r[0] = 0;
1651099Sbill 	s = periodic?-1:0;
1661099Sbill 	for(i=0;++i<n-!periodic;){	/* triangularize */
1671099Sbill 		hi = x.val[i]-x.val[i-1];
1681099Sbill 		hi1 = i==n-1?x.val[1]-x.val[0]:
1691099Sbill 			x.val[i+1]-x.val[i];
1701099Sbill 		if(hi1*hi<=0) return(0);
1711099Sbill 		u = i==1?zero:u-s*s/d;
1721099Sbill 		v = i==1?zero:v-s*r[i-1]/d;
1731099Sbill 		r[i] = rhs(i)-hi*r[i-1]/d;
1741099Sbill 		s = -hi*s/d;
1751099Sbill 		a = 2*(hi+hi1);
1761099Sbill 		if(i==1) a += konst*hi;
1771099Sbill 		if(i==n-2) a += konst*hi1;
1781099Sbill 		diag[i] = d = i==1? a:
1791099Sbill 		    a - hi*hi/d;
1801099Sbill 		}
1811099Sbill 	D2yi = D2yn1 = 0;
1821099Sbill 	for(i=n-!periodic;--i>=0;){	/* back substitute */
1831099Sbill 		end = i==n-1;
1841099Sbill 		hi1 = end?x.val[1]-x.val[0]:
1851099Sbill 			x.val[i+1]-x.val[i];
1861099Sbill 		D2yi1 = D2yi;
1871099Sbill 		if(i>0){
1881099Sbill 			hi = x.val[i]-x.val[i-1];
1891099Sbill 			corr = end?2*s+u:zero;
1901099Sbill 			D2yi = (end*v+r[i]-hi1*D2yi1-s*D2yn1)/
1911099Sbill 				(diag[i]+corr);
1921099Sbill 			if(end) D2yn1 = D2yi;
1931099Sbill 			if(i>1){
1941099Sbill 				a = 2*(hi+hi1);
1951099Sbill 				if(i==1) a += konst*hi;
1961099Sbill 				if(i==n-2) a += konst*hi1;
1971099Sbill 				d = diag[i-1];
1981099Sbill 				s = -s*d/hi;
1991099Sbill 			}}
2001099Sbill 		else D2yi = D2yn1;
2011099Sbill 		if(!periodic) {
2021099Sbill 			if(i==0) D2yi = konst*D2yi1;
2031099Sbill 			if(i==n-2) D2yi1 = konst*D2yi;
2041099Sbill 			}
2051099Sbill 		if(end) continue;
2061099Sbill 		m = hi1>0?ni:-ni;
2071099Sbill 		m = 1.001*m*hi1/(x.ub-x.lb);
2081099Sbill 		if(m<=0) m = 1;
2091099Sbill 		h = hi1/m;
2101099Sbill 		for(j=m;j>0||i==0&&j==0;j--){	/* interpolate */
2111099Sbill 			x0 = (m-j)*h/hi1;
2121099Sbill 			x1 = j*h/hi1;
2131099Sbill 			yy = D2yi*(x0-x0*x0*x0)+D2yi1*(x1-x1*x1*x1);
2141099Sbill 			yy = y.val[i]*x0+y.val[i+1]*x1 -hi1*hi1*yy/6;
2151099Sbill 			printf("%f ",x.val[i]+j*h);
2161099Sbill 			printf("%f\n",yy);
2171099Sbill 			}
2181099Sbill 		}
2191099Sbill 	return(1);
2201099Sbill 	}
2211099Sbill readin() {
2221099Sbill 	for(n=0;n<NP;n++){
2231099Sbill 		if(auta) x.val[n] = n*dx+x.lb;
2241099Sbill 		else if(!getfloat(&x.val[n])) break;
2251099Sbill 		if(!getfloat(&y.val[n])) break; } }
2261099Sbill 
2271099Sbill getfloat(p)
2281099Sbill 	float *p;{
2291099Sbill 	char buf[30];
2301099Sbill 	register c;
2311099Sbill 	int i;
2321099Sbill 	extern double atof();
2331099Sbill 	for(;;){
2341099Sbill 		c = getchar();
2351099Sbill 		if (c==EOF) {
2361099Sbill 			*buf = '\0';
2371099Sbill 			return(0);
2381099Sbill 		}
2391099Sbill 		*buf = c;
2401099Sbill 		switch(*buf){
2411099Sbill 			case ' ':
2421099Sbill 			case '\t':
2431099Sbill 			case '\n':
2441099Sbill 				continue;}
2451099Sbill 		break;}
2461099Sbill 	for(i=1;i<30;i++){
2471099Sbill 		c = getchar();
2481099Sbill 		if (c==EOF) {
2491099Sbill 			buf[i] = '\0';
2501099Sbill 			break;
2511099Sbill 		}
2521099Sbill 		buf[i] = c;
2531099Sbill 		if('0'<=c && c<='9') continue;
2541099Sbill 		switch(c) {
2551099Sbill 			case '.':
2561099Sbill 			case '+':
2571099Sbill 			case '-':
2581099Sbill 			case 'E':
2591099Sbill 			case 'e':
2601099Sbill 				continue;}
2611099Sbill 		break; }
2621099Sbill 	buf[i] = ' ';
2631099Sbill 	*p = atof(buf);
2641099Sbill 	return(1); }
2651099Sbill 
2661099Sbill getlim(p)
2671099Sbill 	struct proj *p; {
2681099Sbill 	int i;
2691099Sbill 	for(i=0;i<n;i++) {
2701099Sbill 		if(!p->lbf && p->lb>(p->val[i])) p->lb = p->val[i];
2711099Sbill 		if(!p->ubf && p->ub<(p->val[i])) p->ub = p->val[i]; }
2721099Sbill 	}
2731099Sbill 
2741099Sbill 
2751099Sbill main(argc,argv)
2761099Sbill 	char *argv[];{
2771099Sbill 	extern char *malloc();
2781099Sbill 	int i;
2791099Sbill 	x.lbf = x.ubf = y.lbf = y.ubf = 0;
2801099Sbill 	x.lb = INF;
2811099Sbill 	x.ub = -INF;
2821099Sbill 	y.lb = INF;
2831099Sbill 	y.ub = -INF;
2841099Sbill 	while(--argc > 0) {
2851099Sbill 		argv++;
2861099Sbill again:		switch(argv[0][0]) {
2871099Sbill 		case '-':
2881099Sbill 			argv[0]++;
2891099Sbill 			goto again;
2901099Sbill 		case 'a':
2911099Sbill 			auta = 1;
2921099Sbill 			numb(&dx,&argc,&argv);
2931099Sbill 			break;
2941099Sbill 		case 'k':
2951099Sbill 			numb(&konst,&argc,&argv);
2961099Sbill 			break;
2971099Sbill 		case 'n':
2981099Sbill 			numb(&ni,&argc,&argv);
2991099Sbill 			break;
3001099Sbill 		case 'p':
3011099Sbill 			periodic = 1;
3021099Sbill 			break;
3031099Sbill 		case 'x':
3041099Sbill 			if(!numb(&x.lb,&argc,&argv)) break;
3051099Sbill 			x.lbf = 1;
3061099Sbill 			if(!numb(&x.ub,&argc,&argv)) break;
3071099Sbill 			x.ubf = 1;
3081099Sbill 			break;
3091099Sbill 		default:
3101099Sbill 			fprintf(stderr, "Bad agrument\n");
3111099Sbill 			exit(1);
3121099Sbill 		}
3131099Sbill 	}
3141099Sbill 	if(auta&&!x.lbf) x.lb = 0;
3151099Sbill 	readin();
3161099Sbill 	getlim(&x);
3171099Sbill 	getlim(&y);
3181099Sbill 	i = (n+1)*sizeof(dx);
3191099Sbill 	diag = (float *)malloc((unsigned)i);
3201099Sbill 	r = (float *)malloc((unsigned)i);
3211099Sbill 	if(r==NULL||!spline()) for(i=0;i<n;i++){
3221099Sbill 		printf("%f ",x.val[i]);
3231099Sbill 		printf("%f\n",y.val[i]); }
3241099Sbill }
3251099Sbill numb(np,argcp,argvp)
3261099Sbill 	int *argcp;
3271099Sbill 	float *np;
3281099Sbill 	char ***argvp;{
3291099Sbill 	double atof();
3301099Sbill 	char c;
3311099Sbill 	if(*argcp<=1) return(0);
3321099Sbill 	c = (*argvp)[1][0];
3331099Sbill 	if(!('0'<=c&&c<='9' || c=='-' || c== '.' )) return(0);
3341099Sbill 	*np = atof((*argvp)[1]);
3351099Sbill 	(*argcp)--;
3361099Sbill 	(*argvp)++;
3371099Sbill 	return(1); }
338