xref: /csrg-svn/usr.bin/spline/spline.c (revision 48312)
1*48312Sbostic /*-
2*48312Sbostic  * Copyright (c) 1991 The Regents of the University of California.
3*48312Sbostic  * All rights reserved.
4*48312Sbostic  *
5*48312Sbostic  * %sccs.include.proprietary.c%
6*48312Sbostic  */
7*48312Sbostic 
824983Ssam #ifndef lint
9*48312Sbostic char copyright[] =
10*48312Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
11*48312Sbostic  All rights reserved.\n";
12*48312Sbostic #endif /* not lint */
1324983Ssam 
14*48312Sbostic #ifndef lint
15*48312Sbostic static char sccsid[] = "@(#)spline.c	4.7 (Berkeley) 04/18/91";
16*48312Sbostic #endif /* not lint */
17*48312Sbostic 
181099Sbill #include <stdio.h>
199366Sshannon #include <math.h>
201099Sbill 
211099Sbill #define NP 1000
229366Sshannon #define INF HUGE
231099Sbill 
241099Sbill struct proj { int lbf,ubf; float a,b,lb,ub,quant,mult,val[NP]; } x,y;
251099Sbill float *diag, *r;
261099Sbill float dx = 1.;
271099Sbill float ni = 100.;
281099Sbill int n;
291099Sbill int auta;
301099Sbill int periodic;
311099Sbill float konst = 0.0;
321099Sbill float zero = 0.;
331099Sbill 
341099Sbill /* Spline fit technique
351099Sbill let x,y be vectors of abscissas and ordinates
3624983Ssam     h   be vector of differences hi=xi-xi-1
371099Sbill     y"  be vector of 2nd derivs of approx function
381099Sbill If the points are numbered 0,1,2,...,n+1 then y" satisfies
391099Sbill (R W Hamming, Numerical Methods for Engineers and Scientists,
401099Sbill 2nd Ed, p349ff)
4124983Ssam 	hiy"i-1+2(hi+hi+1)y"i+hi+1y"i+1
421099Sbill 
4324983Ssam 	= 6[(yi+1-yi)/hi+1-(yi-yi-1)/hi]   i=1,2,...,n
441099Sbill 
4524983Ssam where y"0 = y"n+1 = 0
461099Sbill This is a symmetric tridiagonal system of the form
471099Sbill 
4824983Ssam 	| a1 h2               |  |y"1|      |b1|
4924983Ssam 	| h2 a2 h3            |  |y"2|      |b2|
5024983Ssam 	|    h3 a3 h4         |  |y"3|  =   |b3|
511099Sbill 	|         .           |  | .|      | .|
521099Sbill 	|            .        |  | .|      | .|
531099Sbill It can be triangularized into
5424983Ssam 	| d1 h2               |  |y"1|      |r1|
5524983Ssam 	|    d2 h3            |  |y"2|      |r2|
5624983Ssam 	|       d3 h4         |  |y"3|  =   |r3|
571099Sbill 	|          .          |  | .|      | .|
581099Sbill 	|             .       |  | .|      | .|
591099Sbill where
6024983Ssam 	d1 = a1
611099Sbill 
6224983Ssam 	r0 = 0
631099Sbill 
6424983Ssam 	di = ai - hi2/di-1	1<i<_n
651099Sbill 
6631014Sbostic 	ri = bi - hiri-1/di-1i	1<_i<_n
671099Sbill 
681099Sbill the back solution is
6924983Ssam 	y"n = rn/dn
701099Sbill 
7124983Ssam 	y"i = (ri-hi+1y"i+1)/di	1<_i<n
721099Sbill 
7324983Ssam superficially, di and ri don't have to be stored for they can be
741099Sbill recalculated backward by the formulas
751099Sbill 
7624983Ssam 	di-1 = hi2/(ai-di)	1<i<_n
771099Sbill 
7824983Ssam 	ri-1 = (bi-ri)di-1/hi	1<i<_n
791099Sbill 
801099Sbill unhappily it turns out that the recursion forward for d
811099Sbill is quite strongly geometrically convergent--and is wildly
821099Sbill unstable going backward.
831099Sbill There's similar trouble with r, so the intermediate
841099Sbill results must be kept.
851099Sbill 
861099Sbill Note that n-1 in the program below plays the role of n+1 in the theory
871099Sbill 
8824983Ssam Other boundary conditions_________________________
891099Sbill 
901099Sbill The boundary conditions are easily generalized to handle
911099Sbill 
9224983Ssam 	y0" = ky1", yn+1"   = kyn"
931099Sbill 
941099Sbill for some constant k.  The above analysis was for k = 0;
951099Sbill k = 1 fits parabolas perfectly as well as stright lines;
961099Sbill k = 1/2 has been recommended as somehow pleasant.
971099Sbill 
9824983Ssam All that is necessary is to add h1 to a1 and hn+1 to an.
991099Sbill 
1001099Sbill 
10124983Ssam Periodic case_____________
1021099Sbill 
1031099Sbill To do this, add 1 more row and column thus
1041099Sbill 
10524983Ssam 	| a1 h2            h1 |  |y1"|     |b1|
10624983Ssam 	| h2 a2 h3            |  |y2"|     |b2|
10724983Ssam 	|    h3 a4 h4         |  |y3"|     |b3|
1081099Sbill 	|                     |  | .|  =  | .|
1091099Sbill 	|             .       |  | .|     | .|
11024983Ssam 	| h1            h0 a0 |  | .|     | .|
1111099Sbill 
11224983Ssam where h0=_ hn+1
1131099Sbill 
1141099Sbill The same diagonalization procedure works, except for
11524983Ssam the effect of the 2 corner elements.  Let si be the part
11624983Ssam of the last element in the ith "diagonalized" row that
1171099Sbill arises from the extra top corner element.
1181099Sbill 
11924983Ssam 		s1 = h1
1201099Sbill 
12124983Ssam 		si = -si-1hi/di-1	2<_i<_n+1
1221099Sbill 
1231099Sbill After "diagonalizing", the lower corner element remains.
12424983Ssam Call ti the bottom element that appears in the ith colomn
1251099Sbill as the bottom element to its left is eliminated
1261099Sbill 
12724983Ssam 		t1 = h1
1281099Sbill 
12924983Ssam 		ti = -ti-1hi/di-1
1301099Sbill 
13124983Ssam Evidently ti = si.
1321099Sbill Elimination along the bottom row
1331099Sbill introduces further corrections to the bottom right element
1341099Sbill and to the last element of the right hand side.
1351099Sbill Call these corrections u and v.
1361099Sbill 
13724983Ssam 	u1 = v1 = 0
1381099Sbill 
13924983Ssam 	ui = ui-1-si-1*ti-1/di-1
1401099Sbill 
14124983Ssam 	vi = vi-1-ri-1*ti-1/di-1	2<_i<_n+1
1421099Sbill 
1431099Sbill The back solution is now obtained as follows
1441099Sbill 
14524983Ssam 	y"n+1 = (rn+1+vn+1)/(dn+1+sn+1+tn+1+un+1)
1461099Sbill 
14724983Ssam 	y"i = (ri-hi+1*yi+1-si*yn+1)/di	1<_i<_n
1481099Sbill 
14924983Ssam Interpolation in the interval xi<_x<_xi+1 is by the formula
1501099Sbill 
15124983Ssam 	y = yix+ + yi+1x- -(h2i+1/6)[y"i(x+-x+3)+y"i+1(x--x-3)]
1521099Sbill where
15324983Ssam 	x+ = xi+1-x
1541099Sbill 
15524983Ssam 	x- = x-xi
1561099Sbill */
1571099Sbill 
1581099Sbill float
1591099Sbill rhs(i){
1601099Sbill 	int i_;
1611099Sbill 	double zz;
1621099Sbill 	i_ = i==n-1?0:i;
1631099Sbill 	zz = (y.val[i]-y.val[i-1])/(x.val[i]-x.val[i-1]);
1641099Sbill 	return(6*((y.val[i_+1]-y.val[i_])/(x.val[i+1]-x.val[i]) - zz));
1651099Sbill }
1661099Sbill 
1671099Sbill spline(){
1681099Sbill 	float d,s,u,v,hi,hi1;
1691099Sbill 	float h;
1701099Sbill 	float D2yi,D2yi1,D2yn1,x0,x1,yy,a;
1711099Sbill 	int end;
1721099Sbill 	float corr;
1731099Sbill 	int i,j,m;
1741099Sbill 	if(n<3) return(0);
1751099Sbill 	if(periodic) konst = 0;
1761099Sbill 	d = 1;
1771099Sbill 	r[0] = 0;
1781099Sbill 	s = periodic?-1:0;
1791099Sbill 	for(i=0;++i<n-!periodic;){	/* triangularize */
1801099Sbill 		hi = x.val[i]-x.val[i-1];
1811099Sbill 		hi1 = i==n-1?x.val[1]-x.val[0]:
1821099Sbill 			x.val[i+1]-x.val[i];
1831099Sbill 		if(hi1*hi<=0) return(0);
1841099Sbill 		u = i==1?zero:u-s*s/d;
1851099Sbill 		v = i==1?zero:v-s*r[i-1]/d;
1861099Sbill 		r[i] = rhs(i)-hi*r[i-1]/d;
1871099Sbill 		s = -hi*s/d;
1881099Sbill 		a = 2*(hi+hi1);
1891099Sbill 		if(i==1) a += konst*hi;
1901099Sbill 		if(i==n-2) a += konst*hi1;
1911099Sbill 		diag[i] = d = i==1? a:
1921099Sbill 		    a - hi*hi/d;
1931099Sbill 		}
1941099Sbill 	D2yi = D2yn1 = 0;
1951099Sbill 	for(i=n-!periodic;--i>=0;){	/* back substitute */
1961099Sbill 		end = i==n-1;
1971099Sbill 		hi1 = end?x.val[1]-x.val[0]:
1981099Sbill 			x.val[i+1]-x.val[i];
1991099Sbill 		D2yi1 = D2yi;
2001099Sbill 		if(i>0){
2011099Sbill 			hi = x.val[i]-x.val[i-1];
2021099Sbill 			corr = end?2*s+u:zero;
2031099Sbill 			D2yi = (end*v+r[i]-hi1*D2yi1-s*D2yn1)/
2041099Sbill 				(diag[i]+corr);
2051099Sbill 			if(end) D2yn1 = D2yi;
2061099Sbill 			if(i>1){
2071099Sbill 				a = 2*(hi+hi1);
2081099Sbill 				if(i==1) a += konst*hi;
2091099Sbill 				if(i==n-2) a += konst*hi1;
2101099Sbill 				d = diag[i-1];
2111099Sbill 				s = -s*d/hi;
2121099Sbill 			}}
2131099Sbill 		else D2yi = D2yn1;
2141099Sbill 		if(!periodic) {
2151099Sbill 			if(i==0) D2yi = konst*D2yi1;
2161099Sbill 			if(i==n-2) D2yi1 = konst*D2yi;
2171099Sbill 			}
2181099Sbill 		if(end) continue;
2191099Sbill 		m = hi1>0?ni:-ni;
2201099Sbill 		m = 1.001*m*hi1/(x.ub-x.lb);
2211099Sbill 		if(m<=0) m = 1;
2221099Sbill 		h = hi1/m;
2231099Sbill 		for(j=m;j>0||i==0&&j==0;j--){	/* interpolate */
2241099Sbill 			x0 = (m-j)*h/hi1;
2251099Sbill 			x1 = j*h/hi1;
2261099Sbill 			yy = D2yi*(x0-x0*x0*x0)+D2yi1*(x1-x1*x1*x1);
2271099Sbill 			yy = y.val[i]*x0+y.val[i+1]*x1 -hi1*hi1*yy/6;
2281099Sbill 			printf("%f ",x.val[i]+j*h);
2291099Sbill 			printf("%f\n",yy);
2301099Sbill 			}
2311099Sbill 		}
2321099Sbill 	return(1);
2331099Sbill 	}
2341099Sbill readin() {
2351099Sbill 	for(n=0;n<NP;n++){
2361099Sbill 		if(auta) x.val[n] = n*dx+x.lb;
2371099Sbill 		else if(!getfloat(&x.val[n])) break;
2381099Sbill 		if(!getfloat(&y.val[n])) break; } }
2391099Sbill 
2401099Sbill getfloat(p)
2411099Sbill 	float *p;{
2421099Sbill 	char buf[30];
2431099Sbill 	register c;
2441099Sbill 	int i;
2451099Sbill 	extern double atof();
2461099Sbill 	for(;;){
2471099Sbill 		c = getchar();
2481099Sbill 		if (c==EOF) {
2491099Sbill 			*buf = '\0';
2501099Sbill 			return(0);
2511099Sbill 		}
2521099Sbill 		*buf = c;
2531099Sbill 		switch(*buf){
2541099Sbill 			case ' ':
2551099Sbill 			case '\t':
2561099Sbill 			case '\n':
2571099Sbill 				continue;}
2581099Sbill 		break;}
2591099Sbill 	for(i=1;i<30;i++){
2601099Sbill 		c = getchar();
2611099Sbill 		if (c==EOF) {
2621099Sbill 			buf[i] = '\0';
2631099Sbill 			break;
2641099Sbill 		}
2651099Sbill 		buf[i] = c;
2661099Sbill 		if('0'<=c && c<='9') continue;
2671099Sbill 		switch(c) {
2681099Sbill 			case '.':
2691099Sbill 			case '+':
2701099Sbill 			case '-':
2711099Sbill 			case 'E':
2721099Sbill 			case 'e':
2731099Sbill 				continue;}
2741099Sbill 		break; }
2751099Sbill 	buf[i] = ' ';
2761099Sbill 	*p = atof(buf);
2771099Sbill 	return(1); }
2781099Sbill 
2791099Sbill getlim(p)
2801099Sbill 	struct proj *p; {
2811099Sbill 	int i;
2821099Sbill 	for(i=0;i<n;i++) {
2831099Sbill 		if(!p->lbf && p->lb>(p->val[i])) p->lb = p->val[i];
2841099Sbill 		if(!p->ubf && p->ub<(p->val[i])) p->ub = p->val[i]; }
2851099Sbill 	}
2861099Sbill 
2871099Sbill 
2881099Sbill main(argc,argv)
2891099Sbill 	char *argv[];{
2901099Sbill 	extern char *malloc();
2911099Sbill 	int i;
2921099Sbill 	x.lbf = x.ubf = y.lbf = y.ubf = 0;
2931099Sbill 	x.lb = INF;
2941099Sbill 	x.ub = -INF;
2951099Sbill 	y.lb = INF;
2961099Sbill 	y.ub = -INF;
2971099Sbill 	while(--argc > 0) {
2981099Sbill 		argv++;
2991099Sbill again:		switch(argv[0][0]) {
3001099Sbill 		case '-':
3011099Sbill 			argv[0]++;
3021099Sbill 			goto again;
3031099Sbill 		case 'a':
3041099Sbill 			auta = 1;
3051099Sbill 			numb(&dx,&argc,&argv);
3061099Sbill 			break;
3071099Sbill 		case 'k':
3081099Sbill 			numb(&konst,&argc,&argv);
3091099Sbill 			break;
3101099Sbill 		case 'n':
3111099Sbill 			numb(&ni,&argc,&argv);
3121099Sbill 			break;
3131099Sbill 		case 'p':
3141099Sbill 			periodic = 1;
3151099Sbill 			break;
3161099Sbill 		case 'x':
3171099Sbill 			if(!numb(&x.lb,&argc,&argv)) break;
3181099Sbill 			x.lbf = 1;
3191099Sbill 			if(!numb(&x.ub,&argc,&argv)) break;
3201099Sbill 			x.ubf = 1;
3211099Sbill 			break;
3221099Sbill 		default:
32345246Sbostic 			(void)fprintf(stderr, "spline: illegal option -- %c\n",
32445246Sbostic 			    argv[0][0]);
32545246Sbostic 			(void)fprintf(stderr, "usage: spline [-aknpx]\n");
3261099Sbill 			exit(1);
3271099Sbill 		}
3281099Sbill 	}
3291099Sbill 	if(auta&&!x.lbf) x.lb = 0;
3301099Sbill 	readin();
3311099Sbill 	getlim(&x);
3321099Sbill 	getlim(&y);
3331099Sbill 	i = (n+1)*sizeof(dx);
3341099Sbill 	diag = (float *)malloc((unsigned)i);
3351099Sbill 	r = (float *)malloc((unsigned)i);
3361099Sbill 	if(r==NULL||!spline()) for(i=0;i<n;i++){
3371099Sbill 		printf("%f ",x.val[i]);
3381099Sbill 		printf("%f\n",y.val[i]); }
33932746Sbostic 	exit(0);
3401099Sbill }
3411099Sbill numb(np,argcp,argvp)
3421099Sbill 	int *argcp;
3431099Sbill 	float *np;
3441099Sbill 	char ***argvp;{
3451099Sbill 	double atof();
3461099Sbill 	char c;
3471099Sbill 	if(*argcp<=1) return(0);
3481099Sbill 	c = (*argvp)[1][0];
3491099Sbill 	if(!('0'<=c&&c<='9' || c=='-' || c== '.' )) return(0);
3501099Sbill 	*np = atof((*argvp)[1]);
3511099Sbill 	(*argcp)--;
3521099Sbill 	(*argvp)++;
3531099Sbill 	return(1); }
354