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