don't force extra keystroke after '=' operators
[unix-history] / usr / src / usr.bin / spline / spline.c
... / ...
CommitLineData
1#ifndef lint
2static char *sccsid = "@(#)spline.c 4.5 (Berkeley) %G%";
3#endif
4
5#include <stdio.h>
6#include <math.h>
7
8#define NP 1000
9#define INF HUGE
10
11struct proj { int lbf,ubf; float a,b,lb,ub,quant,mult,val[NP]; } x,y;
12float *diag, *r;
13float dx = 1.;
14float ni = 100.;
15int n;
16int auta;
17int periodic;
18float konst = 0.0;
19float zero = 0.;
20
21/* Spline fit technique
22let x,y be vectors of abscissas and ordinates
23 h be vector of differences hi=xi-xi-1
24 y" be vector of 2nd derivs of approx function
25If the points are numbered 0,1,2,...,n+1 then y" satisfies
26(R W Hamming, Numerical Methods for Engineers and Scientists,
272nd Ed, p349ff)
28 hiy"i-1+2(hi+hi+1)y"i+hi+1y"i+1
29
30 = 6[(yi+1-yi)/hi+1-(yi-yi-1)/hi] i=1,2,...,n
31
32where y"0 = y"n+1 = 0
33This is a symmetric tridiagonal system of the form
34
35 | a1 h2 | |y"1| |b1|
36 | h2 a2 h3 | |y"2| |b2|
37 | h3 a3 h4 | |y"3| = |b3|
38 | . | | .| | .|
39 | . | | .| | .|
40It can be triangularized into
41 | d1 h2 | |y"1| |r1|
42 | d2 h3 | |y"2| |r2|
43 | d3 h4 | |y"3| = |r3|
44 | . | | .| | .|
45 | . | | .| | .|
46where
47 d1 = a1
48
49 r0 = 0
50
51 di = ai - hi2/di-1 1<i<_n
52
53 ri = bi - hiri-1/di-1i 1<_i<_n
54
55the back solution is
56 y"n = rn/dn
57
58 y"i = (ri-hi+1y"i+1)/di 1<_i<n
59
60superficially, di and ri don't have to be stored for they can be
61recalculated backward by the formulas
62
63 di-1 = hi2/(ai-di) 1<i<_n
64
65 ri-1 = (bi-ri)di-1/hi 1<i<_n
66
67unhappily it turns out that the recursion forward for d
68is quite strongly geometrically convergent--and is wildly
69unstable going backward.
70There's similar trouble with r, so the intermediate
71results must be kept.
72
73Note that n-1 in the program below plays the role of n+1 in the theory
74
75Other boundary conditions_________________________
76
77The boundary conditions are easily generalized to handle
78
79 y0" = ky1", yn+1" = kyn"
80
81for some constant k. The above analysis was for k = 0;
82k = 1 fits parabolas perfectly as well as stright lines;
83k = 1/2 has been recommended as somehow pleasant.
84
85All that is necessary is to add h1 to a1 and hn+1 to an.
86
87
88Periodic case_____________
89
90To do this, add 1 more row and column thus
91
92 | a1 h2 h1 | |y1"| |b1|
93 | h2 a2 h3 | |y2"| |b2|
94 | h3 a4 h4 | |y3"| |b3|
95 | | | .| = | .|
96 | . | | .| | .|
97 | h1 h0 a0 | | .| | .|
98
99where h0=_ hn+1
100
101The same diagonalization procedure works, except for
102the effect of the 2 corner elements. Let si be the part
103of the last element in the ith "diagonalized" row that
104arises from the extra top corner element.
105
106 s1 = h1
107
108 si = -si-1hi/di-1 2<_i<_n+1
109
110After "diagonalizing", the lower corner element remains.
111Call ti the bottom element that appears in the ith colomn
112as the bottom element to its left is eliminated
113
114 t1 = h1
115
116 ti = -ti-1hi/di-1
117
118Evidently ti = si.
119Elimination along the bottom row
120introduces further corrections to the bottom right element
121and to the last element of the right hand side.
122Call these corrections u and v.
123
124 u1 = v1 = 0
125
126 ui = ui-1-si-1*ti-1/di-1
127
128 vi = vi-1-ri-1*ti-1/di-1 2<_i<_n+1
129
130The back solution is now obtained as follows
131
132 y"n+1 = (rn+1+vn+1)/(dn+1+sn+1+tn+1+un+1)
133
134 y"i = (ri-hi+1*yi+1-si*yn+1)/di 1<_i<_n
135
136Interpolation in the interval xi<_x<_xi+1 is by the formula
137
138 y = yix+ + yi+1x- -(h2i+1/6)[y"i(x+-x+3)+y"i+1(x--x-3)]
139where
140 x+ = xi+1-x
141
142 x- = x-xi
143*/
144
145float
146rhs(i){
147 int i_;
148 double zz;
149 i_ = i==n-1?0:i;
150 zz = (y.val[i]-y.val[i-1])/(x.val[i]-x.val[i-1]);
151 return(6*((y.val[i_+1]-y.val[i_])/(x.val[i+1]-x.val[i]) - zz));
152}
153
154spline(){
155 float d,s,u,v,hi,hi1;
156 float h;
157 float D2yi,D2yi1,D2yn1,x0,x1,yy,a;
158 int end;
159 float corr;
160 int i,j,m;
161 if(n<3) return(0);
162 if(periodic) konst = 0;
163 d = 1;
164 r[0] = 0;
165 s = periodic?-1:0;
166 for(i=0;++i<n-!periodic;){ /* triangularize */
167 hi = x.val[i]-x.val[i-1];
168 hi1 = i==n-1?x.val[1]-x.val[0]:
169 x.val[i+1]-x.val[i];
170 if(hi1*hi<=0) return(0);
171 u = i==1?zero:u-s*s/d;
172 v = i==1?zero:v-s*r[i-1]/d;
173 r[i] = rhs(i)-hi*r[i-1]/d;
174 s = -hi*s/d;
175 a = 2*(hi+hi1);
176 if(i==1) a += konst*hi;
177 if(i==n-2) a += konst*hi1;
178 diag[i] = d = i==1? a:
179 a - hi*hi/d;
180 }
181 D2yi = D2yn1 = 0;
182 for(i=n-!periodic;--i>=0;){ /* back substitute */
183 end = i==n-1;
184 hi1 = end?x.val[1]-x.val[0]:
185 x.val[i+1]-x.val[i];
186 D2yi1 = D2yi;
187 if(i>0){
188 hi = x.val[i]-x.val[i-1];
189 corr = end?2*s+u:zero;
190 D2yi = (end*v+r[i]-hi1*D2yi1-s*D2yn1)/
191 (diag[i]+corr);
192 if(end) D2yn1 = D2yi;
193 if(i>1){
194 a = 2*(hi+hi1);
195 if(i==1) a += konst*hi;
196 if(i==n-2) a += konst*hi1;
197 d = diag[i-1];
198 s = -s*d/hi;
199 }}
200 else D2yi = D2yn1;
201 if(!periodic) {
202 if(i==0) D2yi = konst*D2yi1;
203 if(i==n-2) D2yi1 = konst*D2yi;
204 }
205 if(end) continue;
206 m = hi1>0?ni:-ni;
207 m = 1.001*m*hi1/(x.ub-x.lb);
208 if(m<=0) m = 1;
209 h = hi1/m;
210 for(j=m;j>0||i==0&&j==0;j--){ /* interpolate */
211 x0 = (m-j)*h/hi1;
212 x1 = j*h/hi1;
213 yy = D2yi*(x0-x0*x0*x0)+D2yi1*(x1-x1*x1*x1);
214 yy = y.val[i]*x0+y.val[i+1]*x1 -hi1*hi1*yy/6;
215 printf("%f ",x.val[i]+j*h);
216 printf("%f\n",yy);
217 }
218 }
219 return(1);
220 }
221readin() {
222 for(n=0;n<NP;n++){
223 if(auta) x.val[n] = n*dx+x.lb;
224 else if(!getfloat(&x.val[n])) break;
225 if(!getfloat(&y.val[n])) break; } }
226
227getfloat(p)
228 float *p;{
229 char buf[30];
230 register c;
231 int i;
232 extern double atof();
233 for(;;){
234 c = getchar();
235 if (c==EOF) {
236 *buf = '\0';
237 return(0);
238 }
239 *buf = c;
240 switch(*buf){
241 case ' ':
242 case '\t':
243 case '\n':
244 continue;}
245 break;}
246 for(i=1;i<30;i++){
247 c = getchar();
248 if (c==EOF) {
249 buf[i] = '\0';
250 break;
251 }
252 buf[i] = c;
253 if('0'<=c && c<='9') continue;
254 switch(c) {
255 case '.':
256 case '+':
257 case '-':
258 case 'E':
259 case 'e':
260 continue;}
261 break; }
262 buf[i] = ' ';
263 *p = atof(buf);
264 return(1); }
265
266getlim(p)
267 struct proj *p; {
268 int i;
269 for(i=0;i<n;i++) {
270 if(!p->lbf && p->lb>(p->val[i])) p->lb = p->val[i];
271 if(!p->ubf && p->ub<(p->val[i])) p->ub = p->val[i]; }
272 }
273
274
275main(argc,argv)
276 char *argv[];{
277 extern char *malloc();
278 int i;
279 x.lbf = x.ubf = y.lbf = y.ubf = 0;
280 x.lb = INF;
281 x.ub = -INF;
282 y.lb = INF;
283 y.ub = -INF;
284 while(--argc > 0) {
285 argv++;
286again: switch(argv[0][0]) {
287 case '-':
288 argv[0]++;
289 goto again;
290 case 'a':
291 auta = 1;
292 numb(&dx,&argc,&argv);
293 break;
294 case 'k':
295 numb(&konst,&argc,&argv);
296 break;
297 case 'n':
298 numb(&ni,&argc,&argv);
299 break;
300 case 'p':
301 periodic = 1;
302 break;
303 case 'x':
304 if(!numb(&x.lb,&argc,&argv)) break;
305 x.lbf = 1;
306 if(!numb(&x.ub,&argc,&argv)) break;
307 x.ubf = 1;
308 break;
309 default:
310 fprintf(stderr, "Bad agrument\n");
311 exit(1);
312 }
313 }
314 if(auta&&!x.lbf) x.lb = 0;
315 readin();
316 getlim(&x);
317 getlim(&y);
318 i = (n+1)*sizeof(dx);
319 diag = (float *)malloc((unsigned)i);
320 r = (float *)malloc((unsigned)i);
321 if(r==NULL||!spline()) for(i=0;i<n;i++){
322 printf("%f ",x.val[i]);
323 printf("%f\n",y.val[i]); }
324 exit(0);
325}
326numb(np,argcp,argvp)
327 int *argcp;
328 float *np;
329 char ***argvp;{
330 double atof();
331 char c;
332 if(*argcp<=1) return(0);
333 c = (*argvp)[1][0];
334 if(!('0'<=c&&c<='9' || c=='-' || c== '.' )) return(0);
335 *np = atof((*argvp)[1]);
336 (*argcp)--;
337 (*argvp)++;
338 return(1); }