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