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