UNPKG

22.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.Len5 = exports.iterate = exports.Len3 = exports.errorNoConverge = exports.errorZeroOutside = exports.errorExtremumOutside = exports.errorNoExtremum = exports.errorNOutOfRange = exports.errorNoXRange = exports.errorNot5 = exports.errorNot4 = exports.errorNot3 = undefined;
7
8var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
9
10var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
11 * @copyright 2013 Sonia Keys
12 * @copyright 2016 commenthol
13 * @license MIT
14 * @module interpolation
15 */
16/**
17 * Interp: Chapter 3, Interpolation.
18 *
19 * Len3 and Len5 types
20 *
21 * These types allow interpolation from a table of equidistant x values
22 * and corresponding y values. Since the x values are equidistant,
23 * only the first and last values are supplied as arguments to the
24 * constructors. The interior x values are implicit. All y values must be
25 * supplied however. They are passed as a slice, and the length of y is fixed.
26 * For Len3 it must be 3 and for (Len5 it must be 5.0
27 *
28 * For these Len3 and Len5 functions, Meeus notes the importance of choosing
29 * the 3 or 5 rows of a larger table that will minimize the interpolating
30 * factor n. He does not provide algorithms for doing this however.
31 *
32 * For an example of a selection function, see len3ForInterpolateX. This
33 * was useful for computing Delta T.
34 */
35
36
37exports.len3ForInterpolateX = len3ForInterpolateX;
38exports.len4Half = len4Half;
39exports.lagrange = lagrange;
40exports.lagrangePoly = lagrangePoly;
41exports.linear = linear;
42
43var _base = require('./base');
44
45var _base2 = _interopRequireDefault(_base);
46
47function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
48
49function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
50
51function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
52
53var int = Math.trunc;
54
55/**
56 * Error values returned by functions and methods in this package.
57 * Defined here to help testing for specific errors.
58 */
59var errorNot3 = exports.errorNot3 = new Error('Argument y must be length 3');
60var errorNot4 = exports.errorNot4 = new Error('Argument y must be length 4');
61var errorNot5 = exports.errorNot5 = new Error('Argument y must be length 5');
62var errorNoXRange = exports.errorNoXRange = new Error('Argument x3 (or x5) cannot equal x1');
63var errorNOutOfRange = exports.errorNOutOfRange = new Error('Interpolating factor n must be in range -1 to 1');
64var errorNoExtremum = exports.errorNoExtremum = new Error('No extremum in table');
65var errorExtremumOutside = exports.errorExtremumOutside = new Error('Extremum falls outside of table');
66var errorZeroOutside = exports.errorZeroOutside = new Error('Zero falls outside of table');
67var errorNoConverge = exports.errorNoConverge = new Error('Failure to converge');
68
69/**
70 * Len3 allows second difference interpolation.
71 */
72
73var Len3 = exports.Len3 = function () {
74 /**
75 * NewLen3 prepares a Len3 object from a table of three rows of x and y values.
76 *
77 * X values must be equally spaced, so only the first and last are supplied.
78 * X1 must not equal to x3. Y must be a slice of three y values.
79 *
80 * @throws Error
81 * @param {Number} x1 - is the x value corresponding to the first y value of the table.
82 * @param {Number} x3 - is the x value corresponding to the last y value of the table.
83 * @param {Number[]} y - is all y values in the table. y.length should be >= 3.0
84 */
85 function Len3(x1, x3, y) {
86 _classCallCheck(this, Len3);
87
88 if (y.length !== 3) {
89 throw errorNot3;
90 }
91 if (x3 === x1) {
92 throw errorNoXRange;
93 }
94 this.x1 = x1;
95 this.x3 = x3;
96 this.y = y;
97 // differences. (3.1) p. 23
98 this.a = y[1] - y[0];
99 this.b = y[2] - y[1];
100 this.c = this.b - this.a;
101 // other intermediate values
102 this.abSum = this.a + this.b;
103 this.xSum = x3 + x1;
104 this.xDiff = x3 - x1;
105 }
106
107 /**
108 * InterpolateX interpolates for a given x value.
109 */
110
111
112 _createClass(Len3, [{
113 key: 'interpolateX',
114 value: function interpolateX(x) {
115 var n = (2 * x - this.xSum) / this.xDiff;
116 return this.interpolateN(n);
117 }
118
119 /**
120 * InterpolateXStrict interpolates for a given x value,
121 * restricting x to the range x1 to x3 given to the constructor NewLen3.
122 */
123
124 }, {
125 key: 'interpolateXStrict',
126 value: function interpolateXStrict(x) {
127 var n = (2 * x - this.xSum) / this.xDiff;
128 var y = this.interpolateNStrict(n, true);
129 return y;
130 }
131
132 /**
133 * InterpolateN interpolates for (a given interpolating factor n.
134 *
135 * This is interpolation formula (3.3)
136 *
137 * The interpolation factor n is x-x2 in units of the tabular x interval.
138 * (See Meeus p. 24.)
139 */
140
141 }, {
142 key: 'interpolateN',
143 value: function interpolateN(n) {
144 return this.y[1] + n * 0.5 * (this.abSum + n * this.c);
145 }
146
147 /**
148 * InterpolateNStrict interpolates for (a given interpolating factor n.
149 *
150 * N is restricted to the range [-1..1] corresponding to the range x1 to x3
151 * given to the constructor of Len3.
152 */
153
154 }, {
155 key: 'interpolateNStrict',
156 value: function interpolateNStrict(n) {
157 if (n < -1 || n > 1) {
158 throw errorNOutOfRange;
159 }
160 return this.interpolateN(n);
161 }
162
163 /**
164 * Extremum returns the x and y values at the extremum.
165 *
166 * Results are restricted to the range of the table given to the constructor
167 * new Len3.
168 */
169
170 }, {
171 key: 'extremum',
172 value: function extremum() {
173 if (this.c === 0) {
174 throw errorNoExtremum;
175 }
176 var n = this.abSum / (-2 * this.c); // (3.5), p. 25
177 if (n < -1 || n > 1) {
178 throw errorExtremumOutside;
179 }
180 var x = 0.5 * (this.xSum + this.xDiff * n);
181 var y = this.y[1] - this.abSum * this.abSum / (8 * this.c); // (3.4), p. 25
182 return [x, y];
183 }
184
185 /**
186 * Len3Zero finds a zero of the quadratic function represented by the table.
187 *
188 * That is, it returns an x value that yields y=0.
189 *
190 * Argument strong switches between two strategies for the estimation step.
191 * when iterating to converge on the zero.
192 *
193 * Strong=false specifies a quick and dirty estimate that works well
194 * for gentle curves, but can work poorly or fail on more dramatic curves.
195 *
196 * Strong=true specifies a more sophisticated and thus somewhat more
197 * expensive estimate. However, if the curve has quick changes, This estimate
198 * will converge more reliably and in fewer steps, making it a better choice.
199 *
200 * Results are restricted to the range of the table given to the constructor
201 * NewLen3.
202 */
203
204 }, {
205 key: 'zero',
206 value: function zero(strong) {
207 var _this = this;
208
209 var f = void 0;
210 if (strong) {
211 // (3.7), p. 27
212 f = function f(n0) {
213 return n0 - (2 * _this.y[1] + n0 * (_this.abSum + _this.c * n0)) / (_this.abSum + 2 * _this.c * n0);
214 };
215 } else {
216 // (3.6), p. 26
217 f = function f(n0) {
218 return -2 * _this.y[1] / (_this.abSum + _this.c * n0);
219 };
220 }
221
222 var _iterate = iterate(0, f),
223 _iterate2 = _slicedToArray(_iterate, 2),
224 n0 = _iterate2[0],
225 ok = _iterate2[1];
226
227 if (!ok) {
228 throw errorNoConverge;
229 }
230 if (n0 > 1 || n0 < -1) {
231 throw errorZeroOutside;
232 }
233 return 0.5 * (this.xSum + this.xDiff * n0); // success
234 }
235 }]);
236
237 return Len3;
238}();
239
240/**
241 * Len3ForInterpolateX is a special purpose Len3 constructor.
242 *
243 * Like NewLen3, it takes a table of x and y values, but it is not limited
244 * to tables of 3 rows. An X value is also passed that represents the
245 * interpolation target x value. Len3ForInterpolateX will locate the
246 * appropriate three rows of the table for interpolating for x, and initialize
247 * the Len3 object for those rows.
248 *
249 * @param {Number} x - is the target for interpolation
250 * @param {Number} x1 - is the x value corresponding to the first y value of the table.
251 * @param {Number} xn - is the x value corresponding to the last y value of the table.
252 * @param {Number[]} y - is all y values in the table. y.length should be >= 3.0
253 * @returns {Number} interpolation value
254 */
255
256
257function len3ForInterpolateX(x, x1, xN, y) {
258 var y3 = y;
259 if (y.length > 3) {
260 var interval = (xN - x1) / (y.length - 1);
261 if (interval === 0) {
262 throw errorNoXRange;
263 }
264 var nearestX = int((x - x1) / interval + 0.5);
265 if (nearestX < 1) {
266 nearestX = 1;
267 } else if (nearestX > y.length - 2) {
268 nearestX = y.length - 2;
269 }
270 y3 = y.slice(nearestX - 1, nearestX + 2);
271 xN = x1 + (nearestX + 1) * interval;
272 x1 = x1 + (nearestX - 1) * interval;
273 }
274 return new Len3(x1, xN, y3);
275}
276
277/**
278 * @private
279 * @param {Number} n0
280 * @param {Function} f
281 * @returns {Array}
282 * {Number} n1
283 * {Boolean} ok - if `false` failure to converge
284 */
285var iterate = exports.iterate = function iterate(n0, f) {
286 for (var limit = 0; limit < 50; limit++) {
287 var n1 = f(n0);
288 if (!isFinite(n1) || isNaN(n1)) {
289 break; // failure to converge
290 }
291 if (Math.abs((n1 - n0) / n0) < 1e-15) {
292 return [n1, true]; // success
293 }
294 n0 = n1;
295 }
296 return [0, false]; // failure to converge
297};
298
299/**
300 * Len4Half interpolates a center value from a table of four rows.
301 * @param {Number[]} y - 4 values
302 * @returns {Number} interpolation result
303 */
304function len4Half(y) {
305 if (y.length !== 4) {
306 throw errorNot4;
307 }
308 // (3.12) p. 32
309 return (9 * (y[1] + y[2]) - y[0] - y[3]) / 16;
310}
311
312/**
313 * Len5 allows fourth Difference interpolation.
314 */
315
316var Len5 = exports.Len5 = function () {
317 /**
318 * NewLen5 prepares a Len5 object from a table of five rows of x and y values.
319 *
320 * X values must be equally spaced, so only the first and last are suppliethis.
321 * X1 must not equal x5. Y must be a slice of five y values.
322 */
323 function Len5(x1, x5, y) {
324 _classCallCheck(this, Len5);
325
326 if (y.length !== 5) {
327 throw errorNot5;
328 }
329 if (x5 === x1) {
330 throw errorNoXRange;
331 }
332 this.x1 = x1;
333 this.x5 = x5;
334 this.y = y;
335 this.y3 = y[2];
336 // differences
337 this.a = y[1] - y[0];
338 this.b = y[2] - y[1];
339 this.c = y[3] - y[2];
340 this.d = y[4] - y[3];
341
342 this.e = this.b - this.a;
343 this.f = this.c - this.b;
344 this.g = this.d - this.c;
345
346 this.h = this.f - this.e;
347 this.j = this.g - this.f;
348
349 this.k = this.j - this.h;
350 // other intermediate values
351 this.xSum = x5 + x1;
352 this.xDiff = x5 - x1;
353 this.interpCoeff = [// (3.8) p. 28
354 this.y3, (this.b + this.c) / 2 - (this.h + this.j) / 12, this.f / 2 - this.k / 24, (this.h + this.j) / 12, this.k / 24];
355 }
356
357 /**
358 * InterpolateX interpolates for (a given x value.
359 */
360
361
362 _createClass(Len5, [{
363 key: 'interpolateX',
364 value: function interpolateX(x) {
365 var n = (4 * x - 2 * this.xSum) / this.xDiff;
366 return this.interpolateN(n);
367 }
368
369 /**
370 * InterpolateXStrict interpolates for a given x value,
371 * restricting x to the range x1 to x5 given to the the constructor NewLen5.
372 */
373
374 }, {
375 key: 'interpolateXStrict',
376 value: function interpolateXStrict(x) {
377 var n = (4 * x - 2 * this.xSum) / this.xDiff;
378 var y = this.interpolateNStrict(n);
379 return y;
380 }
381
382 /**
383 * InterpolateN interpolates for (a given interpolating factor n.
384 *
385 * The interpolation factor n is x-x3 in units of the tabular x interval.
386 * (See Meeus p. 28.)
387 */
388
389 }, {
390 key: 'interpolateN',
391 value: function interpolateN(n) {
392 return _base2.default.horner.apply(_base2.default, [n].concat(_toConsumableArray(this.interpCoeff)));
393 }
394
395 /**
396 * InterpolateNStrict interpolates for (a given interpolating factor n.
397 *
398 * N is restricted to the range [-1..1]. This is only half the range given
399 * to the constructor NewLen5, but is the recommendation given on p. 31.0
400 */
401
402 }, {
403 key: 'interpolateNStrict',
404 value: function interpolateNStrict(n) {
405 if (n < -1 || n > 1) {
406 throw errorNOutOfRange;
407 }
408 return _base2.default.horner.apply(_base2.default, [n].concat(_toConsumableArray(this.interpCoeff)));
409 }
410
411 /**
412 * Extremum returns the x and y values at the extremum.
413 *
414 * Results are restricted to the range of the table given to the constructor
415 * NewLen5. (Meeus actually recommends restricting the range to one unit of
416 * the tabular interval, but that seems a little harsh.)
417 */
418
419 }, {
420 key: 'extremum',
421 value: function extremum() {
422 // (3.9) p. 29
423 var nCoeff = [6 * (this.b + this.c) - this.h - this.j, 0, 3 * (this.h + this.k), 2 * this.k];
424 var den = this.k - 12 * this.f;
425 if (den === 0) {
426 throw errorExtremumOutside;
427 }
428
429 var _iterate3 = iterate(0, function (n0) {
430 return _base2.default.horner.apply(_base2.default, [n0].concat(nCoeff)) / den;
431 }),
432 _iterate4 = _slicedToArray(_iterate3, 2),
433 n0 = _iterate4[0],
434 ok = _iterate4[1];
435
436 if (!ok) {
437 throw errorNoConverge;
438 }
439 if (n0 < -2 || n0 > 2) {
440 throw errorExtremumOutside;
441 }
442 var x = 0.5 * this.xSum + 0.25 * this.xDiff * n0;
443 var y = _base2.default.horner.apply(_base2.default, [n0].concat(_toConsumableArray(this.interpCoeff)));
444 return [x, y];
445 }
446
447 /**
448 * Len5Zero finds a zero of the quartic function represented by the table.
449 *
450 * That is, it returns an x value that yields y=0.
451 *
452 * Argument strong switches between two strategies for the estimation step.
453 * when iterating to converge on the zero.
454 *
455 * Strong=false specifies a quick and dirty estimate that works well
456 * for gentle curves, but can work poorly or fail on more dramatic curves.
457 *
458 * Strong=true specifies a more sophisticated and thus somewhat more
459 * expensive estimate. However, if the curve has quick changes, This estimate
460 * will converge more reliably and in fewer steps, making it a better choice.
461 *
462 * Results are restricted to the range of the table given to the constructor
463 * NewLen5.
464 */
465
466 }, {
467 key: 'zero',
468 value: function zero(strong) {
469 var f = void 0;
470 if (strong) {
471 // (3.11), p. 29
472 var M = this.k / 24;
473 var N = (this.h + this.j) / 12;
474 var P = this.f / 2 - M;
475 var Q = (this.b + this.c) / 2 - N;
476 var numCoeff = [this.y3, Q, P, N, M];
477 var denCoeff = [Q, 2 * P, 3 * N, 4 * M];
478 f = function f(n0) {
479 return n0 - _base2.default.horner.apply(_base2.default, [n0].concat(numCoeff)) / _base2.default.horner.apply(_base2.default, [n0].concat(denCoeff));
480 };
481 } else {
482 // (3.10), p. 29
483 var _numCoeff = [-24 * this.y3, 0, this.k - 12 * this.f, -2 * (this.h + this.j), -this.k];
484 var den = 12 * (this.b + this.c) - 2 * (this.h + this.j);
485 f = function f(n0) {
486 return _base2.default.horner.apply(_base2.default, [n0].concat(_numCoeff)) / den;
487 };
488 }
489
490 var _iterate5 = iterate(0, f),
491 _iterate6 = _slicedToArray(_iterate5, 2),
492 n0 = _iterate6[0],
493 ok = _iterate6[1];
494
495 if (!ok) {
496 throw errorNoConverge;
497 }
498 if (n0 > 2 || n0 < -2) {
499 throw errorZeroOutside;
500 }
501 var x = 0.5 * this.xSum + 0.25 * this.xDiff * n0;
502 return x;
503 }
504 }]);
505
506 return Len5;
507}();
508
509/**
510 * Lagrange performs interpolation with unequally-spaced abscissae.
511 *
512 * Given a table of X and Y values, interpolate a new y value for argument x.
513 *
514 * X values in the table do not have to be equally spaced; they do not even
515 * have to be in order. They must however, be distinct.
516 *
517 * @param {Number} x - x-value of interpolation
518 * @param {Array} table - `[[x0, y0], ... [xN, yN]]` of x, y values
519 * @returns {Number} interpolation result `y` of `x`
520 */
521
522
523function lagrange(x, table) {
524 // method of BASIC program, p. 33.0
525 var sum = 0;
526 table.forEach(function (ti, i) {
527 var xi = ti[0];
528 var prod = 1.0;
529 table.forEach(function (tj, j) {
530 if (i !== j) {
531 var xj = tj[0];
532 prod *= (x - xj) / (xi - xj);
533 }
534 });
535 sum += ti[1] * prod;
536 });
537 return sum;
538}
539
540/**
541 * LagrangePoly uses the formula of Lagrange to produce an interpolating
542 * polynomial.
543 *
544 * X values in the table do not have to be equally spaced; they do not even
545 * have to be in order. They must however, be distinct.
546 *
547 * The returned polynomial will be of degree n-1 where n is the number of rows
548 * in the table. It can be evaluated for x using base.horner.
549 *
550 * @param {Array} table - `[[x0, y0], ... [xN, yN]]`
551 * @returns {Array} - polynomial array
552 */
553function lagrangePoly(table) {
554 // Method not fully described by Meeus, but needed for (numerical solution
555 // to Example 3.g.
556 var sum = new Array(table.length).fill(0);
557 var prod = new Array(table.length).fill(0);
558 var last = table.length - 1;
559
560 var _loop = function _loop(i) {
561 var xi = table[i][0] || table[i].x || 0;
562 var yi = table[i][1] || table[i].y || 0;
563 prod[last] = 1;
564 var den = 1.0;
565 var n = last;
566 for (var j = 0; j < table.length; j++) {
567 if (i !== j) {
568 var xj = table[j][0] || table[j].x || 0;
569 prod[n - 1] = prod[n] * -xj;
570 for (var k = n; k < last; k++) {
571 prod[k] -= prod[k + 1] * xj;
572 }
573 n--;
574 den *= xi - xj;
575 }
576 }
577 prod.forEach(function (pj, j) {
578 sum[j] += yi * pj / den;
579 });
580 };
581
582 for (var i = 0; i < table.length; i++) {
583 _loop(i);
584 }
585 return sum;
586}
587
588/**
589 * Linear Interpolation of x
590 */
591function linear(x, x1, xN, y) {
592 var interval = (xN - x1) / (y.length - 1);
593 if (interval === 0) {
594 throw errorNoXRange;
595 }
596 var nearestX = Math.floor((x - x1) / interval);
597 if (nearestX < 0) {
598 nearestX = 0;
599 } else if (nearestX > y.length - 2) {
600 nearestX = y.length - 2;
601 }
602 var y2 = y.slice(nearestX, nearestX + 2);
603 var x01 = x1 + nearestX * interval;
604 return y2[0] + (y[1] - y[0]) * (x - x01) / interval;
605}
606
607exports.default = {
608 errorNot3: errorNot3,
609 errorNot4: errorNot4,
610 errorNot5: errorNot5,
611 errorNoXRange: errorNoXRange,
612 errorNOutOfRange: errorNOutOfRange,
613 errorNoExtremum: errorNoExtremum,
614 errorExtremumOutside: errorExtremumOutside,
615 errorZeroOutside: errorZeroOutside,
616 errorNoConverge: errorNoConverge,
617 Len3: Len3,
618 len3ForInterpolateX: len3ForInterpolateX,
619 iterate: iterate,
620 len4Half: len4Half,
621 Len5: Len5,
622 lagrange: lagrange,
623 lagrangePoly: lagrangePoly,
624 linear: linear
625};
\No newline at end of file