xref: /csrg-svn/usr.bin/spline/spline.c (revision 9366)
1*9366Sshannon static char *sccsid = "@(#)spline.c	4.2 (Berkeley) 11/27/82";
21099Sbill #include <stdio.h>
3*9366Sshannon #include <math.h>
41099Sbill 
51099Sbill #define NP 1000
6*9366Sshannon #define INF HUGE
71099Sbill 
81099Sbill struct proj { int lbf,ubf; float a,b,lb,ub,quant,mult,val[NP]; } x,y;
91099Sbill float *diag, *r;
101099Sbill float dx = 1.;
111099Sbill float ni = 100.;
121099Sbill int n;
131099Sbill int auta;
141099Sbill int periodic;
151099Sbill float konst = 0.0;
161099Sbill float zero = 0.;
171099Sbill 
181099Sbill /* Spline fit technique
191099Sbill let x,y be vectors of abscissas and ordinates
201099Sbill     h   be vector of differences h9i8=x9i8-x9i-1988
211099Sbill     y"  be vector of 2nd derivs of approx function
221099Sbill If the points are numbered 0,1,2,...,n+1 then y" satisfies
231099Sbill (R W Hamming, Numerical Methods for Engineers and Scientists,
241099Sbill 2nd Ed, p349ff)
251099Sbill 	h9i8y"9i-1988+2(h9i8+h9i+18)y"9i8+h9i+18y"9i+18
261099Sbill 
271099Sbill 	= 6[(y9i+18-y9i8)/h9i+18-(y9i8-y9i-18)/h9i8]   i=1,2,...,n
281099Sbill 
291099Sbill where y"908 = y"9n+18 = 0
301099Sbill This is a symmetric tridiagonal system of the form
311099Sbill 
321099Sbill 	| a918 h928               |  |y"918|      |b918|
331099Sbill 	| h928 a928 h938            |  |y"928|      |b928|
341099Sbill 	|    h938 a938 h948         |  |y"938|  =   |b938|
351099Sbill 	|         .           |  | .|      | .|
361099Sbill 	|            .        |  | .|      | .|
371099Sbill It can be triangularized into
381099Sbill 	| d918 h928               |  |y"918|      |r918|
391099Sbill 	|    d928 h938            |  |y"928|      |r928|
401099Sbill 	|       d938 h948         |  |y"938|  =   |r938|
411099Sbill 	|          .          |  | .|      | .|
421099Sbill 	|             .       |  | .|      | .|
431099Sbill where
441099Sbill 	d918 = a918
451099Sbill 
461099Sbill 	r908 = 0
471099Sbill 
481099Sbill 	d9i8 = a9i8 - h9i8829/d9i-18	1<i<_n
491099Sbill 
501099Sbill 	r9i8 = b9i8 - h9i8r9i-18/d9i-1i8	1<_i<_n
511099Sbill 
521099Sbill the back solution is
531099Sbill 	y"9n8 = r9n8/d9n8
541099Sbill 
551099Sbill 	y"9i8 = (r9i8-h9i+18y"9i+18)/d9i8	1<_i<n
561099Sbill 
571099Sbill superficially, d9i8 and r9i8 don't have to be stored for they can be
581099Sbill recalculated backward by the formulas
591099Sbill 
601099Sbill 	d9i-18 = h9i8829/(a9i8-d9i8)	1<i<_n
611099Sbill 
621099Sbill 	r9i-18 = (b9i8-r9i8)d9i-18/h9i8	1<i<_n
631099Sbill 
641099Sbill unhappily it turns out that the recursion forward for d
651099Sbill is quite strongly geometrically convergent--and is wildly
661099Sbill unstable going backward.
671099Sbill There's similar trouble with r, so the intermediate
681099Sbill results must be kept.
691099Sbill 
701099Sbill Note that n-1 in the program below plays the role of n+1 in the theory
711099Sbill 
721099Sbill Other boundary conditions_________________________
731099Sbill 
741099Sbill The boundary conditions are easily generalized to handle
751099Sbill 
761099Sbill 	y908" = ky918", y9n+18"   = ky9n8"
771099Sbill 
781099Sbill for some constant k.  The above analysis was for k = 0;
791099Sbill k = 1 fits parabolas perfectly as well as stright lines;
801099Sbill k = 1/2 has been recommended as somehow pleasant.
811099Sbill 
821099Sbill All that is necessary is to add h918 to a918 and h9n+18 to a9n8.
831099Sbill 
841099Sbill 
851099Sbill Periodic case_____________
861099Sbill 
871099Sbill To do this, add 1 more row and column thus
881099Sbill 
891099Sbill 	| a918 h928            h918 |  |y918"|     |b918|
901099Sbill 	| h928 a928 h938            |  |y928"|     |b928|
911099Sbill 	|    h938 a948 h948         |  |y938"|     |b938|
921099Sbill 	|                     |  | .|  =  | .|
931099Sbill 	|             .       |  | .|     | .|
941099Sbill 	| h918            h908 a908 |  | .|     | .|
951099Sbill 
961099Sbill where h908=_ h9n+18
971099Sbill 
981099Sbill The same diagonalization procedure works, except for
991099Sbill the effect of the 2 corner elements.  Let s9i8 be the part
1001099Sbill of the last element in the i8th9 "diagonalized" row that
1011099Sbill arises from the extra top corner element.
1021099Sbill 
1031099Sbill 		s918 = h918
1041099Sbill 
1051099Sbill 		s9i8 = -s9i-18h9i8/d9i-18	2<_i<_n+1
1061099Sbill 
1071099Sbill After "diagonalizing", the lower corner element remains.
1081099Sbill Call t9i8 the bottom element that appears in the i8th9 colomn
1091099Sbill as the bottom element to its left is eliminated
1101099Sbill 
1111099Sbill 		t918 = h918
1121099Sbill 
1131099Sbill 		t9i8 = -t9i-18h9i8/d9i-18
1141099Sbill 
1151099Sbill Evidently t9i8 = s9i8.
1161099Sbill Elimination along the bottom row
1171099Sbill introduces further corrections to the bottom right element
1181099Sbill and to the last element of the right hand side.
1191099Sbill Call these corrections u and v.
1201099Sbill 
1211099Sbill 	u918 = v918 = 0
1221099Sbill 
1231099Sbill 	u9i8 = u9i-18-s9i-18*t9i-18/d9i-18
1241099Sbill 
1251099Sbill 	v9i8 = v9i-18-r9i-18*t9i-18/d9i-18	2<_i<_n+1
1261099Sbill 
1271099Sbill The back solution is now obtained as follows
1281099Sbill 
1291099Sbill 	y"9n+18 = (r9n+18+v9n+18)/(d9n+18+s9n+18+t9n+18+u9n+18)
1301099Sbill 
1311099Sbill 	y"9i8 = (r9i8-h9i+18*y9i+18-s9i8*y9n+18)/d9i8	1<_i<_n
1321099Sbill 
1331099Sbill Interpolation in the interval x9i8<_x<_x9i+18 is by the formula
1341099Sbill 
1351099Sbill 	y = y9i8x9+8 + y9i+18x9-8 -(h8299i+18/6)[y"9i8(x9+8-x9+8839)+y"9i+18(x9-8-x9-8839)]
1361099Sbill where
1371099Sbill 	x9+8 = x9i+18-x
1381099Sbill 
1391099Sbill 	x9-8 = x-x9i8
1401099Sbill */
1411099Sbill 
1421099Sbill float
1431099Sbill rhs(i){
1441099Sbill 	int i_;
1451099Sbill 	double zz;
1461099Sbill 	i_ = i==n-1?0:i;
1471099Sbill 	zz = (y.val[i]-y.val[i-1])/(x.val[i]-x.val[i-1]);
1481099Sbill 	return(6*((y.val[i_+1]-y.val[i_])/(x.val[i+1]-x.val[i]) - zz));
1491099Sbill }
1501099Sbill 
1511099Sbill spline(){
1521099Sbill 	float d,s,u,v,hi,hi1;
1531099Sbill 	float h;
1541099Sbill 	float D2yi,D2yi1,D2yn1,x0,x1,yy,a;
1551099Sbill 	int end;
1561099Sbill 	float corr;
1571099Sbill 	int i,j,m;
1581099Sbill 	if(n<3) return(0);
1591099Sbill 	if(periodic) konst = 0;
1601099Sbill 	d = 1;
1611099Sbill 	r[0] = 0;
1621099Sbill 	s = periodic?-1:0;
1631099Sbill 	for(i=0;++i<n-!periodic;){	/* triangularize */
1641099Sbill 		hi = x.val[i]-x.val[i-1];
1651099Sbill 		hi1 = i==n-1?x.val[1]-x.val[0]:
1661099Sbill 			x.val[i+1]-x.val[i];
1671099Sbill 		if(hi1*hi<=0) return(0);
1681099Sbill 		u = i==1?zero:u-s*s/d;
1691099Sbill 		v = i==1?zero:v-s*r[i-1]/d;
1701099Sbill 		r[i] = rhs(i)-hi*r[i-1]/d;
1711099Sbill 		s = -hi*s/d;
1721099Sbill 		a = 2*(hi+hi1);
1731099Sbill 		if(i==1) a += konst*hi;
1741099Sbill 		if(i==n-2) a += konst*hi1;
1751099Sbill 		diag[i] = d = i==1? a:
1761099Sbill 		    a - hi*hi/d;
1771099Sbill 		}
1781099Sbill 	D2yi = D2yn1 = 0;
1791099Sbill 	for(i=n-!periodic;--i>=0;){	/* back substitute */
1801099Sbill 		end = i==n-1;
1811099Sbill 		hi1 = end?x.val[1]-x.val[0]:
1821099Sbill 			x.val[i+1]-x.val[i];
1831099Sbill 		D2yi1 = D2yi;
1841099Sbill 		if(i>0){
1851099Sbill 			hi = x.val[i]-x.val[i-1];
1861099Sbill 			corr = end?2*s+u:zero;
1871099Sbill 			D2yi = (end*v+r[i]-hi1*D2yi1-s*D2yn1)/
1881099Sbill 				(diag[i]+corr);
1891099Sbill 			if(end) D2yn1 = D2yi;
1901099Sbill 			if(i>1){
1911099Sbill 				a = 2*(hi+hi1);
1921099Sbill 				if(i==1) a += konst*hi;
1931099Sbill 				if(i==n-2) a += konst*hi1;
1941099Sbill 				d = diag[i-1];
1951099Sbill 				s = -s*d/hi;
1961099Sbill 			}}
1971099Sbill 		else D2yi = D2yn1;
1981099Sbill 		if(!periodic) {
1991099Sbill 			if(i==0) D2yi = konst*D2yi1;
2001099Sbill 			if(i==n-2) D2yi1 = konst*D2yi;
2011099Sbill 			}
2021099Sbill 		if(end) continue;
2031099Sbill 		m = hi1>0?ni:-ni;
2041099Sbill 		m = 1.001*m*hi1/(x.ub-x.lb);
2051099Sbill 		if(m<=0) m = 1;
2061099Sbill 		h = hi1/m;
2071099Sbill 		for(j=m;j>0||i==0&&j==0;j--){	/* interpolate */
2081099Sbill 			x0 = (m-j)*h/hi1;
2091099Sbill 			x1 = j*h/hi1;
2101099Sbill 			yy = D2yi*(x0-x0*x0*x0)+D2yi1*(x1-x1*x1*x1);
2111099Sbill 			yy = y.val[i]*x0+y.val[i+1]*x1 -hi1*hi1*yy/6;
2121099Sbill 			printf("%f ",x.val[i]+j*h);
2131099Sbill 			printf("%f\n",yy);
2141099Sbill 			}
2151099Sbill 		}
2161099Sbill 	return(1);
2171099Sbill 	}
2181099Sbill readin() {
2191099Sbill 	for(n=0;n<NP;n++){
2201099Sbill 		if(auta) x.val[n] = n*dx+x.lb;
2211099Sbill 		else if(!getfloat(&x.val[n])) break;
2221099Sbill 		if(!getfloat(&y.val[n])) break; } }
2231099Sbill 
2241099Sbill getfloat(p)
2251099Sbill 	float *p;{
2261099Sbill 	char buf[30];
2271099Sbill 	register c;
2281099Sbill 	int i;
2291099Sbill 	extern double atof();
2301099Sbill 	for(;;){
2311099Sbill 		c = getchar();
2321099Sbill 		if (c==EOF) {
2331099Sbill 			*buf = '\0';
2341099Sbill 			return(0);
2351099Sbill 		}
2361099Sbill 		*buf = c;
2371099Sbill 		switch(*buf){
2381099Sbill 			case ' ':
2391099Sbill 			case '\t':
2401099Sbill 			case '\n':
2411099Sbill 				continue;}
2421099Sbill 		break;}
2431099Sbill 	for(i=1;i<30;i++){
2441099Sbill 		c = getchar();
2451099Sbill 		if (c==EOF) {
2461099Sbill 			buf[i] = '\0';
2471099Sbill 			break;
2481099Sbill 		}
2491099Sbill 		buf[i] = c;
2501099Sbill 		if('0'<=c && c<='9') continue;
2511099Sbill 		switch(c) {
2521099Sbill 			case '.':
2531099Sbill 			case '+':
2541099Sbill 			case '-':
2551099Sbill 			case 'E':
2561099Sbill 			case 'e':
2571099Sbill 				continue;}
2581099Sbill 		break; }
2591099Sbill 	buf[i] = ' ';
2601099Sbill 	*p = atof(buf);
2611099Sbill 	return(1); }
2621099Sbill 
2631099Sbill getlim(p)
2641099Sbill 	struct proj *p; {
2651099Sbill 	int i;
2661099Sbill 	for(i=0;i<n;i++) {
2671099Sbill 		if(!p->lbf && p->lb>(p->val[i])) p->lb = p->val[i];
2681099Sbill 		if(!p->ubf && p->ub<(p->val[i])) p->ub = p->val[i]; }
2691099Sbill 	}
2701099Sbill 
2711099Sbill 
2721099Sbill main(argc,argv)
2731099Sbill 	char *argv[];{
2741099Sbill 	extern char *malloc();
2751099Sbill 	int i;
2761099Sbill 	x.lbf = x.ubf = y.lbf = y.ubf = 0;
2771099Sbill 	x.lb = INF;
2781099Sbill 	x.ub = -INF;
2791099Sbill 	y.lb = INF;
2801099Sbill 	y.ub = -INF;
2811099Sbill 	while(--argc > 0) {
2821099Sbill 		argv++;
2831099Sbill again:		switch(argv[0][0]) {
2841099Sbill 		case '-':
2851099Sbill 			argv[0]++;
2861099Sbill 			goto again;
2871099Sbill 		case 'a':
2881099Sbill 			auta = 1;
2891099Sbill 			numb(&dx,&argc,&argv);
2901099Sbill 			break;
2911099Sbill 		case 'k':
2921099Sbill 			numb(&konst,&argc,&argv);
2931099Sbill 			break;
2941099Sbill 		case 'n':
2951099Sbill 			numb(&ni,&argc,&argv);
2961099Sbill 			break;
2971099Sbill 		case 'p':
2981099Sbill 			periodic = 1;
2991099Sbill 			break;
3001099Sbill 		case 'x':
3011099Sbill 			if(!numb(&x.lb,&argc,&argv)) break;
3021099Sbill 			x.lbf = 1;
3031099Sbill 			if(!numb(&x.ub,&argc,&argv)) break;
3041099Sbill 			x.ubf = 1;
3051099Sbill 			break;
3061099Sbill 		default:
3071099Sbill 			fprintf(stderr, "Bad agrument\n");
3081099Sbill 			exit(1);
3091099Sbill 		}
3101099Sbill 	}
3111099Sbill 	if(auta&&!x.lbf) x.lb = 0;
3121099Sbill 	readin();
3131099Sbill 	getlim(&x);
3141099Sbill 	getlim(&y);
3151099Sbill 	i = (n+1)*sizeof(dx);
3161099Sbill 	diag = (float *)malloc((unsigned)i);
3171099Sbill 	r = (float *)malloc((unsigned)i);
3181099Sbill 	if(r==NULL||!spline()) for(i=0;i<n;i++){
3191099Sbill 		printf("%f ",x.val[i]);
3201099Sbill 		printf("%f\n",y.val[i]); }
3211099Sbill }
3221099Sbill numb(np,argcp,argvp)
3231099Sbill 	int *argcp;
3241099Sbill 	float *np;
3251099Sbill 	char ***argvp;{
3261099Sbill 	double atof();
3271099Sbill 	char c;
3281099Sbill 	if(*argcp<=1) return(0);
3291099Sbill 	c = (*argvp)[1][0];
3301099Sbill 	if(!('0'<=c&&c<='9' || c=='-' || c== '.' )) return(0);
3311099Sbill 	*np = atof((*argvp)[1]);
3321099Sbill 	(*argcp)--;
3331099Sbill 	(*argvp)++;
3341099Sbill 	return(1); }
3351099Sbill 
336