Clone the Makefile.am from join-test.
[coreutils.git] / src / spline.c
blob4ce9e0366f48ce6bf8e02729cfe13546b6d7035e
1 /* spline.c -- Do spline interpolation. */
3 /******************************************************************************
4 David L. Fox
5 2140 Braun Dr.
6 Golden, CO 80401
8 This program has been placed in the public domain by its author.
10 Version Date Change
11 1.1 17 Dec 1985 Modify getdata() to realloc() more memory if needed.
12 1.0 14 May 1985
14 spline [options] [file]
16 Spline reads pairs of numbers from file (or the standard input, if file is missing).
17 The pairs are interpreted as abscissas and ordinates of a function. The output of
18 spline consists of similar pairs generated from the input by interpolation with
19 cubic splines. (See R. W. Hamming, Numerical Methods for Scientists and Engineers,
20 2nd ed., pp. 349ff.) There are sufficiently many points in the output to appear
21 smooth when plotted (e.g., by graph(1)). The output points are approximately evenly
22 spaced and include the input points.
24 There may be one or more options of the form: -o [argument [arg2] ].
26 The available options are:
28 -a Generate abscissa values automatically. The input consists of a list of
29 ordinates. The generated abscissas are evenly spaced with spacing given by
30 the argument, or 1 if the next argument is not a number.
32 -f Set the first derivative of the spline function at the left and right end
33 points to the first and second arguments following -f. If only one numerical
34 argument follows -f then that value is used for both left and right end points.
36 -k The argument of the k option is the value of the constant used to calculate
37 the boundary values by y''[0] = ky''[1] and y''[n] = ky''[n-1]. The default
38 value is k = 0.
40 -m The argument gives the maximum number of input data points. The default is 1000.
41 If the input contains more than this many points additional memory will be
42 allocated. This option may be used to slightly increase efficiency for large
43 data sets or reduce the amount of memory required for small data sets.
45 -n The number of output points is given by the argument. The default value is 100.
47 -p The splines used for interpolation are forced to be periodic, i.e. y'[0] = y'[n].
48 The first and last ordinates should be equal.
50 -s Set the second derivative of the spline function at the left and right end
51 points to the first and second arguments following -s. If only one numerical
52 argument follows -s then that value is used for both left and right end points.
54 -x The argument (and arg2, if present) are the lower (and upper) limits on the
55 abscissa values in the output. If the x option is not specified these limits
56 are calculated from the data. Automatic abscissas start at the lower limit
57 (default 0).
59 The data need not be monotonic in x but all the x values must be distinct.
60 Non-numeric data in the input is ignored.
61 ******************************************************************************/
63 #include <a:stdio.h>
64 #include <ctype.h>
66 /* The constant DOUBLE is a compile time switch.
67 If #define DOUBLE appears here double pecision variables are
68 used to store all data and parameters.
69 Otherwise, float variables are used for the data.
70 For most smoothing and and interpolation applications single
71 precision is more than adequate.
72 Double precision is used to solve the system of linear equations
73 in either case.
75 /* #define DOUBLE */
77 #ifdef DOUBLE
78 #define double real; /* Type used for data storage. */
79 #define IFMT "%F" /* Input format. */
80 #define OFMT "%18.14g %18.14g\n" /* Output format. */
81 #else
82 #define float real; /* Type used for data storage. */
83 #define IFMT "%f" /* Input format. */
84 #define OFMT "%8.5g %8.5g\n" /* Output format. */
85 #endif
87 /* Numerical constants: These may be machine and/or precision dependent. */
88 #define HUGE (1.e38) /* Largest floating point number. */
89 #define EPS 1.e-5 /* Test for zero when solving linear equations. */
91 /* Default parameters */
92 #define NPTS 1000
93 #define NINT 100
94 #define DEFSTEP 1. /* Default step size for automatic abcissas. */
95 #define DEFBVK 0. /* Default boundary value constant. */
97 /* Boundary condition types. */
98 #define EXTRAP 0 /* Extrapolate second derivative:
99 y''(0) = k * y''(1), y''(n) = k * y''(n-1) */
100 #define FDERIV 1 /* Fixed first derivatives y'(0) and y'(n). */
101 #define SDERIV 2 /* Fixed second derivatives y''(0) and y''(n). */
102 #define PERIOD 3 /* Periodic: derivatives equal at end points. */
104 /* Token types for command line processing. */
105 #define OPTION 1
106 #define NUMBER 2
107 #define OTHER 3
109 /* Define error numbers. */
110 #define MEMERR 1
111 #define NODATA 2
112 #define BADOPT 4
113 #define BADFILE 5
114 #define XTRAARG 6
115 #define XTRAPTS 7
116 #define SINGERR 8
117 #define DUPX 9
118 #define BADBC 10
119 #define RANGERR 11
121 /* Constants and flags are global. */
122 int aflag = FALSE; /* Automatic abscissa flag. */
123 real step = DEFSTEP; /* Spacing for automatic abscissas. */
124 int bdcode = EXTRAP; /* Type of boundary conditions:
125 0 extrapolate f'' with coeficient k.
126 1 first derivatives given
127 2 second derivatives given
128 3 periodic */
129 real leftd = 0., /* Boundary values of derivatives. */
130 rightd = 0.;
131 real k = DEFBVK; /* Boundary value constant. */
132 int rflag = 0; /* 0: take range from data, 1: min. given, 2: min. & max. given. */
133 real xmin = HUGE, xmax; /* Output range. */
134 unsigned nint = NINT; /* Number of intervals in output. */
135 unsigned maxpts = NPTS; /* Maximun number of points. */
136 unsigned nknots = 0; /* Number of input points. */
137 int sflag = FALSE; /* If TRUE data must be sorted. */
138 char *datafile; /* Name of data file */
140 int xcompare(); /* Function to compare x values of two data points. */
142 struct pair {
143 real x, y;
144 } *data; /* Points to list of data points. */
146 struct bandm {
147 double diag, upper, rhs;
148 } *eqns; /* Points to elements of band matrix used to calculate
149 coefficients of the splines. */
151 main(argc, argv)
152 int argc;
153 char **argv;
155 setup(argc, argv);
156 if (nknots == 1) { /* Cannot interpolate with one data point. */
157 printf(OFMT,data->x, data->y);
158 exit(0);
160 if (sflag) /* Sort data if needed. */
161 qsort(data, nknots, sizeof(struct pair), xcompare);
162 calcspline();
163 interpolate();
166 /* xcompare -- Compare abcissas of two data points (for qsort). */
167 xcompare(arg1, arg2)
168 struct pair *arg1, *arg2;
170 if (arg1->x > arg2->x)
171 return(1);
172 else if (arg1->x < arg2->x)
173 return(-1);
174 else
175 return(0);
178 /* error -- Print error message and abort fatal errors. */
179 error(errno, auxmsg)
180 int errno;
181 char *auxmsg;
182 { char *msg;
183 int fatal, usemsg;
184 static char *usage =
185 "usage: spline [options] [file]\noptions:\n",
186 *options = "-a spacing\n-k const\n-n intervals\n-m points\n-p\n-x xmin xmax\n";
188 fatal = TRUE; /* Default is fatal error. */
189 usemsg = FALSE; /* Default no usage message. */
190 fprintf(stderr, "spline: ");
191 switch(errno) {
192 case MEMERR:
193 msg = "not enough memory for %u data points\n";
194 break;
195 case NODATA:
196 msg = "data file %s empty\n";
197 break;
198 case BADOPT:
199 msg = "unknown option: %c\n";
200 usemsg = TRUE;
201 break;
202 case BADFILE:
203 msg = "cannot open file: %s\n";
204 break;
205 case XTRAARG:
206 msg = "extra argument ignored: %s\n";
207 fatal = FALSE;
208 usemsg = TRUE;
209 break;
210 case XTRAPTS:
211 fatal = FALSE;
212 msg = "%s";
213 break;
214 case DUPX:
215 msg = "duplicate abcissa value: %s\n";
216 break;
217 case RANGERR:
218 msg = "xmax < xmin not allowed %s\n";
219 break;
220 /* The following errors "can't happen." */
221 /* If they occur some sort of bug is indicated. */
222 case SINGERR:
223 msg = "singular matrix encountered %s\n";
224 break;
225 case BADBC:
226 msg = "internal error: bad boundary value code %d\n";
227 break;
228 default:
229 fprintf(stderr, "unknown error number: %d\n", errno);
230 exit(1);
232 fprintf(stderr, msg, auxmsg);
233 if (usemsg)
234 fprintf(stderr,"%s%s", usage, options);
235 if (fatal)
236 exit(1);
239 /* setup -- Initalize all constants and read data. */
240 setup(argc, argv)
241 int argc;
242 char **argv;
243 { char *malloc();
244 FILE fdinp, /* Source of input. */
245 doarg();
247 fdinp = doarg(argc, argv); /* Process command line arguments. */
249 /* Allocate memory for data and band matrix of coefficients. */
250 if ((data = malloc(maxpts*sizeof(struct pair))) == NULL) {
251 error(MEMERR, (char *)maxpts);
254 getdata(fdinp); /* Read data from fdinp. */
255 if (fdinp != stdin)
256 fclose(fdinp); /* Close input data file. */
257 if (nknots == 0) {
258 error(NODATA, datafile);
260 /* Allocate memory for calculation of spline coefficients. */
261 if ((eqns = malloc((nknots+1)*sizeof(struct bandm))) == NULL) {
262 error(MEMERR, (char *)nknots);
266 /* doarg -- Process arguments. */
267 FILE
268 doarg(argc, argv)
269 int argc;
270 char **argv;
271 { int i, type;
272 double atof();
273 char *s, str[15];
274 FILE fdinp;
276 s = argv[i=1];
277 type = gettok(&i, &s, argv, str);
278 do {
279 if (type == OPTION) {
280 switch(*str) {
281 case 'a': /* Automatic abscissa. */
282 aflag = TRUE;
283 rflag = rflag < 2 ? 1 : rflag;
284 if (xmin == HUGE) /* Initialize xmin, if needed. */
285 xmin = 0.;
286 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
287 if ((step = atof(str)) <= 0.)
288 error(RANGERR,"");
289 type = gettok(&i, &s, argv, str);
291 break;
292 case 'f': /* Fix first derivative at boundaries. */
293 bdcode = FDERIV;
294 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
295 leftd = atof(str);
296 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
297 rightd = atof(str);
298 type = gettok(&i, &s, argv, str);
300 else {
301 rightd = leftd;
304 break;
305 case 'k': /* Set boundary value constant. */
306 bdcode = EXTRAP;
307 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
308 k = atof(str);
309 type = gettok(&i, &s, argv, str);
311 break;
312 case 'm': /* Set number of intervals in output. */
313 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
314 maxpts = (unsigned)atoi(str);
315 type = gettok(&i, &s, argv, str);
317 break;
318 case 'n': /* Set number of intervals in output. */
319 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
320 nint = (unsigned)atoi(str);
321 type = gettok(&i, &s, argv, str);
323 break;
324 case 'p': /* Require periodic interpolation function. */
325 bdcode = PERIOD;
326 type = gettok(&i, &s, argv, str);
327 break;
328 case 's': /* Fix second derivative at boundaries. */
329 bdcode = SDERIV;
330 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
331 leftd = atof(str);
332 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
333 rightd = atof(str);
334 type = gettok(&i, &s, argv, str);
336 else {
337 rightd = leftd;
340 break;
341 case 'x': /* Set range of x values. */
342 rflag = 1;
343 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
344 xmin = atof(str);
345 if ((type = gettok(&i, &s, argv, str)) == NUMBER) {
346 xmax = atof(str);
347 rflag = 2;
348 type = gettok(&i, &s, argv, str);
349 if (xmax <= xmin)
350 error(RANGERR, "");
353 break;
354 default:
355 error(BADOPT, (char *)argv[i][1]);
358 else {
359 if (argc > i) {
360 datafile = argv[i];
361 if ((fdinp = fopen(argv[i++], "r")) == NULL) {
362 error(BADFILE, argv[i-1]);
364 if (argc > i)
365 error(XTRAARG, argv[i]);
367 else
368 fdinp = stdin;
369 break;
371 } while (i < argc);
372 return fdinp;
375 /* gettok -- Get one token from command line, return type. */
376 gettok(indexp, locp, argv, str)
377 int *indexp; /* Pointer to index in argv array. */
378 char **locp; /* Pointer to current location in argv[*indexp]. */
379 char **argv;
380 char *str;
381 { char *s;
382 char *strcpy(), *strchr();
383 int type;
385 s = *locp;
386 while (isspace(*s) || *s == ',')
387 ++s;
388 if (*s == '\0') /* Look at next element in argv. */
389 s = argv[++*indexp];
390 if (*s == '-' && isalpha(s[1])) {
391 /* Found an option. */
392 *str = *++s;
393 str[1] = '\0';
394 ++s;
395 type = OPTION;
397 else if (is_number(s)) {
398 while (is_number(s))
399 *str++ = *s++;
400 *str = '\0';
401 type = NUMBER;
403 else {
404 strcpy(str, s);
405 s = strchr(s, '\0');
406 type = OTHER;
408 *locp = s;
409 return(type);
412 /* is_number -- Return TRUE if argument is the ASCII representation of a number. */
413 is_number(string)
414 char *string;
416 if (isdigit(*string) ||
417 *string == '.' ||
418 *string == '+' ||
419 (*string == '-' && (isdigit(string[1]) || string[1] == '.')))
420 return(TRUE);
421 else
422 return(FALSE);
425 /* getdata -- Read data points from fdinp. */
426 getdata(fdinp)
427 FILE fdinp;
428 { int n, i;
429 real lastx, min, max;
430 struct pair *dp;
431 char msg[60], *realloc();
433 nknots = 0;
434 lastx = -HUGE;
435 dp = data; /* Point to head of list of data. */
436 min = HUGE;
437 max = -HUGE;
438 do {
439 if (aflag) { /* Generate abcissa. */
440 dp->x = xmin + nknots*step;
441 n = 0;
443 else { /* Read abcissa. */
444 while ((n = (fscanf(fdinp,IFMT,&dp->x))) == 0)
445 ; /* Skip non-numeric input. */
447 if (n == 1) {
448 if (min > dp->x)
449 min = dp->x;
450 if (max < dp->x)
451 max = dp->x;
452 if (lastx > dp->x) { /* Check for monotonicity. */
453 sflag = TRUE; /* Data must be sorted. */
455 lastx = dp->x;
457 /* Read ordinate. */
458 while ((n = (fscanf(fdinp, IFMT, &dp->y))) == 0)
459 ; /* Skip non-numeric input. */
460 if (++nknots >= maxpts) { /* Too many points, allocate more memory. */
461 if ((data = realloc(data, (maxpts *= 2)*sizeof(struct pair))) == NULL) {
462 error(MEMERR, (char *)maxpts);
464 dp = data + nknots;
465 sprintf(msg, "more than %d input points, more memory allocated\n",
466 maxpts/2);
467 error(XTRAPTS,msg);
469 else
470 ++dp;
471 } while (n == 1);
472 --nknots;
473 if (aflag) { /* Compute maximum ordinate. */
474 max = xmin + (nknots-1)*step;
476 if (rflag < 2) {
477 xmax = max;
478 if (rflag < 1)
479 xmin = min;
483 /* calcspline -- Calculate coeficients of spline functions. */
484 calcspline()
486 calccoef();
487 solvband();
490 /* calccoef -- Calculate coefficients of linear equations determining spline functions. */
491 calccoef()
492 { int i, j;
493 struct bandm *ptr, tmp1, tmp2;
494 double h, h1;
495 char str[10];
497 ptr = eqns;
498 /* Initialize first row of matrix. */
499 if ((h1 = data[1].x - data[0].x) == 0.) {
500 sprintf(str, "%8.5g", data[0].x);
501 error(DUPX, str);
503 switch(bdcode) { /* First equation depends on boundary conditions. */
504 case EXTRAP:
505 ptr->upper = -k;
506 ptr->diag = 1.;
507 ptr->rhs = 0.;
508 break;
509 case FDERIV: /* Fixed first derivatives at ends. */
510 ptr->upper = 1.;
511 ptr->diag = 2.;
512 h = data[1].x - data[0].x;
513 ptr->rhs = 6.*((data[1].y - data[0].y)/(h*h) - leftd/h);
514 break;
515 case SDERIV:
516 ptr->upper = 0.;
517 ptr->diag = 1.;
518 ptr->rhs = leftd;
519 break;
520 case PERIOD: /* Periodic splines. */
521 ptr->upper = 1.;
522 ptr->diag = 2.;
523 break;
524 default:
525 error(BADBC, (char *) bdcode);
527 ++ptr;
529 /* Initialize rows 1 to n-1, sub-diagonal band is assumed to be 1. */
530 for (i=1; i < nknots-1; ++i, ++ptr) {
531 h = h1;
532 if ((h1 = data[i+1].x - data[i].x) == 0.) {
533 sprintf(str, "%8.5g", data[i].x);
534 error(DUPX, str);
536 ptr->diag = 2.*(h + h1)/h;
537 ptr->upper = h1/h;
538 ptr->rhs = 6.*((data[i+1].y-data[i].y)/(h*h1) -
539 (data[i].y - data[i-1].y)/(h*h));
541 /* Initialize last row. */
542 switch(bdcode) {
543 case EXTRAP: /* Standard case. */
544 ptr->diag = 1.;
545 ptr->upper = -k; /* Upper holds actual sub-diagonal value. */
546 ptr->rhs = 0.;
547 break;
548 case FDERIV: /* Fixed first derivatives at ends. */
549 ptr->upper = 1.;
550 ptr->diag = 2.;
551 h = data[nknots-1].x - data[nknots-2].x;
552 ptr->rhs = 6.*((data[nknots-2].y - data[nknots-1].y)/(h*h) + rightd/h);
553 break;
554 case SDERIV:
555 ptr->upper = 0.;
556 ptr->diag = 1.;
557 ptr->rhs = rightd;
558 break;
559 case PERIOD: /* Use periodic boundary conditions. */
560 /* First and last row are not in tridiagonal form. */
561 h = data[1].x - data[0].x;
562 h1 = data[nknots-1].x - data[nknots-2].x;
563 ptr->diag = 1.;
564 ptr->upper = 0.;
565 tmp1.diag = -1.;
566 tmp1.upper = 0;
567 tmp1.rhs = 0.;
568 tmp2.diag = 2.*h1/h;
569 tmp2.upper = h1/h;
570 tmp2.rhs = 6.*((data[1].y - data[0].y)/(h*h) -
571 (data[nknots-1].y - data[nknots-2].y)/(h1*h));
572 /* Transform periodic boundary equations to tri-diagonal form. */
573 for (i = 1; i < nknots - 1; ++i) {
574 tmp1.upper /= tmp1.diag;
575 tmp1.rhs /= tmp1.diag;
576 ptr->diag /= tmp1.diag;
577 ptr->upper /= tmp1.diag;
578 tmp1.diag = tmp1.upper - eqns[i].diag;
579 tmp1.upper = -eqns[i].upper;
580 tmp1.rhs -= eqns[i].rhs;
581 tmp2.upper /= tmp2.diag;
582 tmp2.rhs /= tmp2.diag;
583 eqns->diag /= tmp2.diag;
584 eqns->upper /= tmp2.diag;
585 tmp2.diag = tmp2.upper - eqns[nknots-1-i].diag/eqns[nknots-1-i].upper;
586 tmp2.upper = -1./eqns[nknots-1-i].upper;
587 tmp2.rhs -= eqns[nknots-1-i].rhs/eqns[nknots-1-i].upper;
589 /* Add in remaining terms of boundary condition equation. */
590 ptr->upper += tmp1.diag;
591 ptr->diag += tmp1.upper;
592 ptr->rhs = tmp1.rhs;
593 eqns->diag += tmp2.upper;
594 eqns->upper += tmp2.diag;
595 eqns->rhs = tmp2.rhs;
596 break;
597 default:
598 error(BADBC, (char *) bdcode);
602 /* solvband -- Solve band matrix for spline functions. */
603 solvband()
604 { int i, flag;
605 struct bandm *ptr;
606 double k1;
607 double fabs();
608 int fcompare();
610 ptr = eqns;
611 flag = FALSE;
612 /* Make a pass to triangularize matrix. */
613 for (i=1; i < nknots - 1; ++i, ++ptr) {
614 if (fabs(ptr->diag) < EPS) {
615 /* Near zero on diagonal, pivot. */
616 if (fabs(ptr->upper) < EPS)
617 error(SINGERR, "");
618 flag = TRUE;
619 ptr->diag = i; /* Keep row index in diag.
620 Actual value of diag is always 1. */
621 if (i == nknots - 2) {
622 flag = 2;
623 /* Exchange next to last and last rows. */
624 k1 = ptr->rhs/ptr->upper;
625 if (fabs((ptr+1)->upper) < EPS)
626 error(SINGERR, "");
627 ptr->rhs = (ptr+1)->rhs/(ptr+1)->upper;
628 ptr->upper = (ptr+1)->diag/(ptr+1)->upper;
629 (ptr+1)->upper = 0.;
630 (ptr+1)->rhs = k1;
632 else {
633 ptr->rhs = (ptr+1)->rhs - (k1 = ptr->rhs/ptr->upper)*(ptr+1)->diag;
634 ptr->upper = (ptr+1)->upper; /* This isn't super-diagonal element
635 but rather one to its right.
636 Must watch for this when
637 back substituting. */
638 (++ptr)->diag = i-1;
639 ++i;
640 ptr->upper = 0;
641 ptr->rhs = k1;
642 (ptr+1)->rhs -= ptr->rhs;
645 else {
646 ptr->upper /= ptr->diag;
647 ptr->rhs /= ptr->diag;
648 ptr->diag = i-1; /* Used to reorder solution if needed. */
649 (ptr+1)->diag -= ptr->upper;
650 (ptr+1)->rhs -= ptr->rhs;
653 /* Last row is a special case. */
654 if (flag != 2) {
655 /* If flag == 2 last row is already computed. */
656 ptr->upper /= ptr->diag;
657 ptr->rhs /= ptr->diag;
658 ptr->diag = ptr - eqns;
659 ++ptr;
660 ptr->diag -= (ptr-1)->upper * ptr->upper;
661 ptr->rhs -= (ptr-1)->rhs * ptr->upper;
662 ptr->rhs /= ptr->diag;
663 ptr->diag = ptr - eqns;
666 /* Now make a pass back substituting for solution. */
667 --ptr;
668 for ( ; ptr >= eqns; --ptr) {
669 if ((int)ptr->diag != ptr - eqns) {
670 /* This row and one above have been exchanged in a pivot. */
671 --ptr;
672 ptr->rhs -= ptr->upper * (ptr+2)->rhs;
674 else
675 ptr->rhs -= ptr->upper * (ptr+1)->rhs;
677 if (flag) { /* Undo reordering done by pivoting. */
678 qsort(eqns, nknots, sizeof(struct bandm), fcompare);
682 /* fcompare -- Compare two floating point numbers. */
683 fcompare(arg1, arg2)
684 real *arg1, *arg2;
686 if (arg1 > arg2)
687 return(1);
688 else if (arg1 < arg2)
689 return(-1);
690 else
691 return(0);
694 /* interpolate -- Do spline interpolation. */
695 interpolate()
696 { int i;
697 struct pair *dp;
698 struct bandm *ep;
699 real h, xi, yi, hi, xu, xl, limit;
701 h = (xmax - xmin)/nint;
702 ep = eqns;
703 dp = data + 1;
704 for (xi = xmin; xi < xmax + 0.25*h; xi += h) {
705 while (dp->x < xi && dp < data + nknots - 1) {
706 ++dp; /* Skip to correct interval. */
707 ++ep;
709 if (dp < data + nknots - 1 && dp->x < xmax)
710 limit = dp->x;
711 else
712 limit = xmax;
713 for ( ; xi < limit - 0.25*h; xi += h) {
714 /* Do interpolation. */
715 hi = dp->x - (dp-1)->x;
716 xu = dp->x - xi;
717 xl = xi - (dp-1)->x;
718 yi = ((ep+1)->rhs*xl*xl/(6.*hi) + dp->y/hi - (ep+1)->rhs*hi/6.)*xl +
719 (ep->rhs*xu*xu/(6.*hi) + (dp-1)->y/hi - ep->rhs*hi/6.)*xu;
720 printf(OFMT, xi, yi);
722 if (limit != dp->x) { /* Interpolate. */
723 hi = dp->x - (dp-1)->x;
724 xu = dp->x - xmax;
725 xl = xmax - (dp-1)->x;
726 yi = ((ep+1)->rhs*xl*xl/(6.*hi) + dp->y/hi - (ep+1)->rhs*hi/6.)*xl +
727 (ep->rhs*xu*xu/(6.*hi) + (dp-1)->y/hi - ep->rhs*hi/6.)*xu;
728 printf(OFMT, xmax, yi);
730 else { /* Print knot. */
731 printf(OFMT, dp->x, dp->y);
732 xi = dp->x;