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