xref: /csrg-svn/usr.bin/spline/spline.c (revision 62252)
148312Sbostic /*-
2*62252Sbostic  * Copyright (c) 1991, 1993
3*62252Sbostic  *	The Regents of the University of California.  All rights reserved.
448312Sbostic  *
548312Sbostic  * %sccs.include.proprietary.c%
648312Sbostic  */
748312Sbostic 
824983Ssam #ifndef lint
9*62252Sbostic static char copyright[] =
10*62252Sbostic "@(#) Copyright (c) 1991, 1993\n\
11*62252Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1248312Sbostic #endif /* not lint */
1324983Ssam 
1448312Sbostic #ifndef lint
15*62252Sbostic static char sccsid[] = "@(#)spline.c	8.1 (Berkeley) 06/06/93";
1648312Sbostic #endif /* not lint */
1748312Sbostic 
181099Sbill #include <stdio.h>
199366Sshannon #include <math.h>
201099Sbill 
211099Sbill #define NP 1000
221099Sbill 
231099Sbill struct proj { int lbf,ubf; float a,b,lb,ub,quant,mult,val[NP]; } x,y;
241099Sbill float *diag, *r;
251099Sbill float dx = 1.;
261099Sbill float ni = 100.;
271099Sbill int n;
281099Sbill int auta;
291099Sbill int periodic;
301099Sbill float konst = 0.0;
311099Sbill float zero = 0.;
321099Sbill 
331099Sbill /* Spline fit technique
341099Sbill let x,y be vectors of abscissas and ordinates
3524983Ssam     h   be vector of differences hi=xi-xi-1
361099Sbill     y"  be vector of 2nd derivs of approx function
371099Sbill If the points are numbered 0,1,2,...,n+1 then y" satisfies
381099Sbill (R W Hamming, Numerical Methods for Engineers and Scientists,
391099Sbill 2nd Ed, p349ff)
4024983Ssam 	hiy"i-1+2(hi+hi+1)y"i+hi+1y"i+1
411099Sbill 
4224983Ssam 	= 6[(yi+1-yi)/hi+1-(yi-yi-1)/hi]   i=1,2,...,n
431099Sbill 
4424983Ssam where y"0 = y"n+1 = 0
451099Sbill This is a symmetric tridiagonal system of the form
461099Sbill 
4724983Ssam 	| a1 h2               |  |y"1|      |b1|
4824983Ssam 	| h2 a2 h3            |  |y"2|      |b2|
4924983Ssam 	|    h3 a3 h4         |  |y"3|  =   |b3|
501099Sbill 	|         .           |  | .|      | .|
511099Sbill 	|            .        |  | .|      | .|
521099Sbill It can be triangularized into
5324983Ssam 	| d1 h2               |  |y"1|      |r1|
5424983Ssam 	|    d2 h3            |  |y"2|      |r2|
5524983Ssam 	|       d3 h4         |  |y"3|  =   |r3|
561099Sbill 	|          .          |  | .|      | .|
571099Sbill 	|             .       |  | .|      | .|
581099Sbill where
5924983Ssam 	d1 = a1
601099Sbill 
6124983Ssam 	r0 = 0
621099Sbill 
6324983Ssam 	di = ai - hi2/di-1	1<i<_n
641099Sbill 
6531014Sbostic 	ri = bi - hiri-1/di-1i	1<_i<_n
661099Sbill 
671099Sbill the back solution is
6824983Ssam 	y"n = rn/dn
691099Sbill 
7024983Ssam 	y"i = (ri-hi+1y"i+1)/di	1<_i<n
711099Sbill 
7224983Ssam superficially, di and ri don't have to be stored for they can be
731099Sbill recalculated backward by the formulas
741099Sbill 
7524983Ssam 	di-1 = hi2/(ai-di)	1<i<_n
761099Sbill 
7724983Ssam 	ri-1 = (bi-ri)di-1/hi	1<i<_n
781099Sbill 
791099Sbill unhappily it turns out that the recursion forward for d
801099Sbill is quite strongly geometrically convergent--and is wildly
811099Sbill unstable going backward.
821099Sbill There's similar trouble with r, so the intermediate
831099Sbill results must be kept.
841099Sbill 
851099Sbill Note that n-1 in the program below plays the role of n+1 in the theory
861099Sbill 
8724983Ssam Other boundary conditions_________________________
881099Sbill 
891099Sbill The boundary conditions are easily generalized to handle
901099Sbill 
9124983Ssam 	y0" = ky1", yn+1"   = kyn"
921099Sbill 
931099Sbill for some constant k.  The above analysis was for k = 0;
941099Sbill k = 1 fits parabolas perfectly as well as stright lines;
951099Sbill k = 1/2 has been recommended as somehow pleasant.
961099Sbill 
9724983Ssam All that is necessary is to add h1 to a1 and hn+1 to an.
981099Sbill 
991099Sbill 
10024983Ssam Periodic case_____________
1011099Sbill 
1021099Sbill To do this, add 1 more row and column thus
1031099Sbill 
10424983Ssam 	| a1 h2            h1 |  |y1"|     |b1|
10524983Ssam 	| h2 a2 h3            |  |y2"|     |b2|
10624983Ssam 	|    h3 a4 h4         |  |y3"|     |b3|
1071099Sbill 	|                     |  | .|  =  | .|
1081099Sbill 	|             .       |  | .|     | .|
10924983Ssam 	| h1            h0 a0 |  | .|     | .|
1101099Sbill 
11124983Ssam where h0=_ hn+1
1121099Sbill 
1131099Sbill The same diagonalization procedure works, except for
11424983Ssam the effect of the 2 corner elements.  Let si be the part
11524983Ssam of the last element in the ith "diagonalized" row that
1161099Sbill arises from the extra top corner element.
1171099Sbill 
11824983Ssam 		s1 = h1
1191099Sbill 
12024983Ssam 		si = -si-1hi/di-1	2<_i<_n+1
1211099Sbill 
1221099Sbill After "diagonalizing", the lower corner element remains.
12324983Ssam Call ti the bottom element that appears in the ith colomn
1241099Sbill as the bottom element to its left is eliminated
1251099Sbill 
12624983Ssam 		t1 = h1
1271099Sbill 
12824983Ssam 		ti = -ti-1hi/di-1
1291099Sbill 
13024983Ssam Evidently ti = si.
1311099Sbill Elimination along the bottom row
1321099Sbill introduces further corrections to the bottom right element
1331099Sbill and to the last element of the right hand side.
1341099Sbill Call these corrections u and v.
1351099Sbill 
13624983Ssam 	u1 = v1 = 0
1371099Sbill 
13824983Ssam 	ui = ui-1-si-1*ti-1/di-1
1391099Sbill 
14024983Ssam 	vi = vi-1-ri-1*ti-1/di-1	2<_i<_n+1
1411099Sbill 
1421099Sbill The back solution is now obtained as follows
1431099Sbill 
14424983Ssam 	y"n+1 = (rn+1+vn+1)/(dn+1+sn+1+tn+1+un+1)
1451099Sbill 
14624983Ssam 	y"i = (ri-hi+1*yi+1-si*yn+1)/di	1<_i<_n
1471099Sbill 
14824983Ssam Interpolation in the interval xi<_x<_xi+1 is by the formula
1491099Sbill 
15024983Ssam 	y = yix+ + yi+1x- -(h2i+1/6)[y"i(x+-x+3)+y"i+1(x--x-3)]
1511099Sbill where
15224983Ssam 	x+ = xi+1-x
1531099Sbill 
15424983Ssam 	x- = x-xi
1551099Sbill */
1561099Sbill 
1571099Sbill float
rhs(i)1581099Sbill rhs(i){
1591099Sbill 	int i_;
1601099Sbill 	double zz;
1611099Sbill 	i_ = i==n-1?0:i;
1621099Sbill 	zz = (y.val[i]-y.val[i-1])/(x.val[i]-x.val[i-1]);
1631099Sbill 	return(6*((y.val[i_+1]-y.val[i_])/(x.val[i+1]-x.val[i]) - zz));
1641099Sbill }
1651099Sbill 
spline()1661099Sbill spline(){
1671099Sbill 	float d,s,u,v,hi,hi1;
1681099Sbill 	float h;
1691099Sbill 	float D2yi,D2yi1,D2yn1,x0,x1,yy,a;
1701099Sbill 	int end;
1711099Sbill 	float corr;
1721099Sbill 	int i,j,m;
1731099Sbill 	if(n<3) return(0);
1741099Sbill 	if(periodic) konst = 0;
1751099Sbill 	d = 1;
1761099Sbill 	r[0] = 0;
1771099Sbill 	s = periodic?-1:0;
1781099Sbill 	for(i=0;++i<n-!periodic;){	/* triangularize */
1791099Sbill 		hi = x.val[i]-x.val[i-1];
1801099Sbill 		hi1 = i==n-1?x.val[1]-x.val[0]:
1811099Sbill 			x.val[i+1]-x.val[i];
1821099Sbill 		if(hi1*hi<=0) return(0);
1831099Sbill 		u = i==1?zero:u-s*s/d;
1841099Sbill 		v = i==1?zero:v-s*r[i-1]/d;
1851099Sbill 		r[i] = rhs(i)-hi*r[i-1]/d;
1861099Sbill 		s = -hi*s/d;
1871099Sbill 		a = 2*(hi+hi1);
1881099Sbill 		if(i==1) a += konst*hi;
1891099Sbill 		if(i==n-2) a += konst*hi1;
1901099Sbill 		diag[i] = d = i==1? a:
1911099Sbill 		    a - hi*hi/d;
1921099Sbill 		}
1931099Sbill 	D2yi = D2yn1 = 0;
1941099Sbill 	for(i=n-!periodic;--i>=0;){	/* back substitute */
1951099Sbill 		end = i==n-1;
1961099Sbill 		hi1 = end?x.val[1]-x.val[0]:
1971099Sbill 			x.val[i+1]-x.val[i];
1981099Sbill 		D2yi1 = D2yi;
1991099Sbill 		if(i>0){
2001099Sbill 			hi = x.val[i]-x.val[i-1];
2011099Sbill 			corr = end?2*s+u:zero;
2021099Sbill 			D2yi = (end*v+r[i]-hi1*D2yi1-s*D2yn1)/
2031099Sbill 				(diag[i]+corr);
2041099Sbill 			if(end) D2yn1 = D2yi;
2051099Sbill 			if(i>1){
2061099Sbill 				a = 2*(hi+hi1);
2071099Sbill 				if(i==1) a += konst*hi;
2081099Sbill 				if(i==n-2) a += konst*hi1;
2091099Sbill 				d = diag[i-1];
2101099Sbill 				s = -s*d/hi;
2111099Sbill 			}}
2121099Sbill 		else D2yi = D2yn1;
2131099Sbill 		if(!periodic) {
2141099Sbill 			if(i==0) D2yi = konst*D2yi1;
2151099Sbill 			if(i==n-2) D2yi1 = konst*D2yi;
2161099Sbill 			}
2171099Sbill 		if(end) continue;
2181099Sbill 		m = hi1>0?ni:-ni;
2191099Sbill 		m = 1.001*m*hi1/(x.ub-x.lb);
2201099Sbill 		if(m<=0) m = 1;
2211099Sbill 		h = hi1/m;
2221099Sbill 		for(j=m;j>0||i==0&&j==0;j--){	/* interpolate */
2231099Sbill 			x0 = (m-j)*h/hi1;
2241099Sbill 			x1 = j*h/hi1;
2251099Sbill 			yy = D2yi*(x0-x0*x0*x0)+D2yi1*(x1-x1*x1*x1);
2261099Sbill 			yy = y.val[i]*x0+y.val[i+1]*x1 -hi1*hi1*yy/6;
2271099Sbill 			printf("%f ",x.val[i]+j*h);
2281099Sbill 			printf("%f\n",yy);
2291099Sbill 			}
2301099Sbill 		}
2311099Sbill 	return(1);
2321099Sbill 	}
readin()2331099Sbill readin() {
2341099Sbill 	for(n=0;n<NP;n++){
2351099Sbill 		if(auta) x.val[n] = n*dx+x.lb;
2361099Sbill 		else if(!getfloat(&x.val[n])) break;
2371099Sbill 		if(!getfloat(&y.val[n])) break; } }
2381099Sbill 
getfloat(p)2391099Sbill getfloat(p)
2401099Sbill 	float *p;{
2411099Sbill 	char buf[30];
2421099Sbill 	register c;
2431099Sbill 	int i;
2441099Sbill 	extern double atof();
2451099Sbill 	for(;;){
2461099Sbill 		c = getchar();
2471099Sbill 		if (c==EOF) {
2481099Sbill 			*buf = '\0';
2491099Sbill 			return(0);
2501099Sbill 		}
2511099Sbill 		*buf = c;
2521099Sbill 		switch(*buf){
2531099Sbill 			case ' ':
2541099Sbill 			case '\t':
2551099Sbill 			case '\n':
2561099Sbill 				continue;}
2571099Sbill 		break;}
2581099Sbill 	for(i=1;i<30;i++){
2591099Sbill 		c = getchar();
2601099Sbill 		if (c==EOF) {
2611099Sbill 			buf[i] = '\0';
2621099Sbill 			break;
2631099Sbill 		}
2641099Sbill 		buf[i] = c;
2651099Sbill 		if('0'<=c && c<='9') continue;
2661099Sbill 		switch(c) {
2671099Sbill 			case '.':
2681099Sbill 			case '+':
2691099Sbill 			case '-':
2701099Sbill 			case 'E':
2711099Sbill 			case 'e':
2721099Sbill 				continue;}
2731099Sbill 		break; }
2741099Sbill 	buf[i] = ' ';
2751099Sbill 	*p = atof(buf);
2761099Sbill 	return(1); }
2771099Sbill 
2781099Sbill getlim(p)
2791099Sbill 	struct proj *p; {
28050850Storek 	register int i;
28150850Storek 	if (!p->lbf) {
28250850Storek 		p->lb = p->val[0];
28350850Storek 		for(i=1;i<n;i++) if (p->lb>p->val[i]) p->lb = p->val[i]; }
28450850Storek 	if (!p->ubf) {
28550850Storek 		p->ub = p->val[0];
28650850Storek 		for(i=1;i<n;i++) if (p->ub<p->val[i]) p->ub = p->val[i]; }
2871099Sbill 	}
2881099Sbill 
2891099Sbill 
main(argc,argv)2901099Sbill main(argc,argv)
2911099Sbill 	char *argv[];{
2921099Sbill 	extern char *malloc();
2931099Sbill 	int i;
2941099Sbill 	x.lbf = x.ubf = y.lbf = y.ubf = 0;
29550850Storek 	x.lb = x.ub = y.lb = y.ub = 0;
2961099Sbill 	while(--argc > 0) {
2971099Sbill 		argv++;
2981099Sbill again:		switch(argv[0][0]) {
2991099Sbill 		case '-':
3001099Sbill 			argv[0]++;
3011099Sbill 			goto again;
3021099Sbill 		case 'a':
3031099Sbill 			auta = 1;
3041099Sbill 			numb(&dx,&argc,&argv);
3051099Sbill 			break;
3061099Sbill 		case 'k':
3071099Sbill 			numb(&konst,&argc,&argv);
3081099Sbill 			break;
3091099Sbill 		case 'n':
3101099Sbill 			numb(&ni,&argc,&argv);
3111099Sbill 			break;
3121099Sbill 		case 'p':
3131099Sbill 			periodic = 1;
3141099Sbill 			break;
3151099Sbill 		case 'x':
3161099Sbill 			if(!numb(&x.lb,&argc,&argv)) break;
3171099Sbill 			x.lbf = 1;
3181099Sbill 			if(!numb(&x.ub,&argc,&argv)) break;
3191099Sbill 			x.ubf = 1;
3201099Sbill 			break;
3211099Sbill 		default:
32245246Sbostic 			(void)fprintf(stderr, "spline: illegal option -- %c\n",
32345246Sbostic 			    argv[0][0]);
32445246Sbostic 			(void)fprintf(stderr, "usage: spline [-aknpx]\n");
3251099Sbill 			exit(1);
3261099Sbill 		}
3271099Sbill 	}
3281099Sbill 	if(auta&&!x.lbf) x.lb = 0;
3291099Sbill 	readin();
3301099Sbill 	getlim(&x);
3311099Sbill 	getlim(&y);
3321099Sbill 	i = (n+1)*sizeof(dx);
3331099Sbill 	diag = (float *)malloc((unsigned)i);
3341099Sbill 	r = (float *)malloc((unsigned)i);
3351099Sbill 	if(r==NULL||!spline()) for(i=0;i<n;i++){
3361099Sbill 		printf("%f ",x.val[i]);
3371099Sbill 		printf("%f\n",y.val[i]); }
33832746Sbostic 	exit(0);
3391099Sbill }
numb(np,argcp,argvp)3401099Sbill numb(np,argcp,argvp)
3411099Sbill 	int *argcp;
3421099Sbill 	float *np;
3431099Sbill 	char ***argvp;{
3441099Sbill 	double atof();
3451099Sbill 	char c;
3461099Sbill 	if(*argcp<=1) return(0);
3471099Sbill 	c = (*argvp)[1][0];
3481099Sbill 	if(!('0'<=c&&c<='9' || c=='-' || c== '.' )) return(0);
3491099Sbill 	*np = atof((*argvp)[1]);
3501099Sbill 	(*argcp)--;
3511099Sbill 	(*argvp)++;
3521099Sbill 	return(1); }
353