1 /*
  2     Copyright 2008-2023
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Alfred Wassermann
  7 
  8     This file is part of JSXGraph.
  9 
 10     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 11 
 12     You can redistribute it and/or modify it under the terms of the
 13 
 14       * GNU Lesser General Public License as published by
 15         the Free Software Foundation, either version 3 of the License, or
 16         (at your option) any later version
 17       OR
 18       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 19 
 20     JSXGraph is distributed in the hope that it will be useful,
 21     but WITHOUT ANY WARRANTY; without even the implied warranty of
 22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 23     GNU Lesser General Public License for more details.
 24 
 25     You should have received a copy of the GNU Lesser General Public License and
 26     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
 27     and <https://opensource.org/licenses/MIT/>.
 28  */
 29 
 30 /*global JXG: true, define: true*/
 31 /*jslint nomen: true, plusplus: true*/
 32 
 33 import Mat from "./math";
 34 
 35 /**
 36  * Functions for extrapolation of sequences. Used for finding limits of sequences which is used for curve plotting.
 37  * @name JXG.Math.Extrapolate
 38  * @exports Mat.Extrapolate as JXG.Math.Extrapolate
 39  * @namespace
 40  */
 41 Mat.Extrapolate = {
 42     upper: 15,
 43     infty: 1e4,
 44 
 45     /**
 46      * Wynn's epsilon algorithm. Ported from the FORTRAN version in
 47      * Ernst Joachim Weniger, "Nonlinear sequence transformations for the acceleration of convergence
 48      * and the summation of divergent series", Computer Physics Reports Vol. 10, 189-371 (1989).
 49      *
 50      * @param {Number} s_n next value of sequence, i.e. n-th element of sequence
 51      * @param {Number} n index of s_n in the sequence
 52      * @param {Array} e One-dimensional array containing the extrapolation data. Has to be supplied by the calling routine.
 53      * @returns {Number} New estimate of the limit of the sequence.
 54      *
 55      * @memberof JXG.Math.Extrapolate
 56      */
 57     wynnEps: function (s_n, n, e) {
 58         var HUGE = 1e20,
 59             TINY = 1e-15,
 60             f0 = 1, // f0 may be changed to other values, see vanden Broeck, Schwartz (1979)
 61             f,
 62             j,
 63             aux1,
 64             aux2,
 65             diff,
 66             estlim;
 67 
 68         e[n] = s_n;
 69         if (n === 0) {
 70             estlim = s_n;
 71         } else {
 72             aux2 = 0.0;
 73             for (j = n; j > 0; j--) {
 74                 aux1 = aux2;
 75                 aux2 = e[j - 1];
 76                 diff = e[j] - aux2;
 77                 if (Math.abs(diff) <= TINY) {
 78                     e[j - 1] = HUGE;
 79                 } else {
 80                     f = (n - j + 1) % 2 === 1 ? f0 : 1;
 81                     e[j - 1] = aux1 * f + 1 / diff;
 82                 }
 83             }
 84             estlim = e[n % 2];
 85         }
 86 
 87         return estlim;
 88     },
 89 
 90     // wynnRho: function(s_n, n, e) {
 91     //     var HUGE = 1.e+20,
 92     //         TINY = 1.e-15,
 93     //         j, f,
 94     //         aux1, aux2, diff, estlim;
 95 
 96     //     e[n] = s_n;
 97     //     if (n === 0) {
 98     //         estlim = s_n;
 99     //     } else {
100     //         aux2 = 0.0;
101     //         for (j = n; j >= 1; j--) {
102     //             aux1 = aux2;
103     //             aux2 = e[j - 1];
104     //             diff = e[j] - aux2;
105     //             if (Math.abs(diff) <= TINY) {
106     //                 e[j - 1] = HUGE;
107     //             } else {
108     //                 f = ((n - j + 1) % 2 === 1) ? n - j + 1  : 1;
109     //                 e[j - 1] = aux1 + f / diff;
110     //             }
111     //         }
112     //         estlim = e[n % 2];
113     //     }
114 
115     //     return estlim;
116     // },
117 
118     /**
119      * Aitken transformation. Ported from the FORTRAN version in
120      * Ernst Joachim Weniger, "Nonlinear sequence transformations for the acceleration of convergence
121      * and the summation of divergent series", Computer Physics Reports Vol. 10, 189-371 (1989).
122      *
123      * @param {Number} s_n next value of sequence, i.e. n-th element of sequence
124      * @param {Number} n index of s_n in the sequence
125      * @param {Array} a One-dimensional array containing the extrapolation data. Has to be supplied by the calling routine.
126      * @returns {Number} New estimate of the limit of the sequence.
127      *
128      * @memberof JXG.Math.Extrapolate
129      */
130     aitken: function (s_n, n, a) {
131         var estlim,
132             HUGE = 1e20,
133             TINY = 1e-15,
134             denom,
135             v,
136             lowmax,
137             j,
138             m;
139 
140         a[n] = s_n;
141         if (n < 2) {
142             estlim = s_n;
143         } else {
144             lowmax = n / 2;
145             for (j = 1; j <= lowmax; j++) {
146                 m = n - 2 * j;
147                 denom = a[m + 2] - 2 * a[m + 1] + a[m];
148                 if (Math.abs(denom) < TINY) {
149                     a[m] = HUGE;
150                 } else {
151                     v = a[m] - a[m + 1];
152                     a[m] -= (v * v) / denom;
153                 }
154             }
155             estlim = a[n % 2];
156         }
157         return estlim;
158     },
159 
160     /**
161      * Iterated Brezinski transformation. Ported from the FORTRAN version in
162      * Ernst Joachim Weniger, "Nonlinear sequence transformations for the acceleration of convergence
163      * and the summation of divergent series", Computer Physics Reports Vol. 10, 189-371 (1989).
164      *
165      * @param {Number} s_n next value of sequence, i.e. n-th element of sequence
166      * @param {Number} n index of s_n in the sequence
167      * @param {Array} a One-dimensional array containing the extrapolation data. Has to be supplied by the calling routine.
168      * @returns {Number} New estimate of the limit of the sequence.
169      *
170      * @memberof JXG.Math.Extrapolate
171      */
172     brezinski: function (s_n, n, a) {
173         var estlim,
174             HUGE = 1e20,
175             TINY = 1e-15,
176             denom,
177             d0,
178             d1,
179             d2,
180             lowmax,
181             j,
182             m;
183 
184         a[n] = s_n;
185         if (n < 3) {
186             estlim = s_n;
187         } else {
188             lowmax = n / 3;
189             m = n;
190             for (j = 1; j <= lowmax; j++) {
191                 m -= 3;
192                 d0 = a[m + 1] - a[m];
193                 d1 = a[m + 2] - a[m + 1];
194                 d2 = a[m + 3] - a[m + 2];
195                 denom = d2 * (d1 - d0) - d0 * (d2 - d1);
196                 if (Math.abs(denom) < TINY) {
197                     a[m] = HUGE;
198                 } else {
199                     a[m] = a[m + 1] - (d0 * d1 * (d2 - d1)) / denom;
200                 }
201             }
202             estlim = a[n % 3];
203         }
204         return estlim;
205     },
206 
207     /**
208      * Extrapolated iteration to approximate the value f(x_0).
209      *
210      * @param {Number} x0 Value for which the limit of f is to be determined. f(x0) may or may not exist.
211      * @param {Number} h0 Initial (signed) distance from x0.
212      * @param {Function} f Function for which the limit at x0 is to be determined
213      * @param {String} method String to choose the method. Available values: "wynnEps", "aitken", "brezinski"
214      * @param {Number} step_type Approximation method. step_type = 0 uses the sequence x0 + h0/n; step_type = 1 uses the sequence x0 + h0 * 2^(-n)
215      *
216      * @returns {Array} Array of length 3. Position 0: estimated value for f(x0), position 1: 'finite', 'infinite', or 'NaN'.
217      * Position 2: value between 0 and 1 judging the reliability of the result (1: high, 0: not successful).
218      *
219      * @memberof JXG.Math.Extrapolate
220      * @see JXG.Math.Extrapolate.limit
221      * @see JXG.Math.Extrapolate.wynnEps
222      * @see JXG.Math.Extrapolate.aitken
223      * @see JXG.Math.Extrapolate.brezinski
224      */
225     iteration: function (x0, h0, f, method, step_type) {
226         var n,
227             v,
228             w,
229             estlim = NaN,
230             diff,
231             r = 0.5,
232             E = [],
233             result = "finite",
234             h = h0;
235 
236         step_type = step_type || 0;
237 
238         for (n = 1; n <= this.upper; n++) {
239             h = step_type === 0 ? h0 / (n + 1) : h * r;
240             v = f(x0 + h, true);
241 
242             w = this[method](v, n - 1, E);
243             //console.log(n, x0 + h, v, w);
244             if (isNaN(w)) {
245                 result = "NaN";
246                 break;
247             }
248             if (v !== 0 && w / v > this.infty) {
249                 estlim = w;
250                 result = "infinite";
251                 break;
252             }
253             diff = w - estlim;
254             if (Math.abs(diff) < 1e-7) {
255                 break;
256             }
257             estlim = w;
258         }
259         return [estlim, result, 1 - (n - 1) / this.upper];
260     },
261 
262     /**
263      * Levin transformation. See Numerical Recipes, ed. 3.
264      * Not yet ready for use.
265      *
266      * @param {Number} s_n next value of sequence, i.e. n-th element of sequence
267      * @param {Number} n index of s_n in the sequence
268      * @param {Array} numer One-dimensional array containing the extrapolation data for the numerator. Has to be supplied by the calling routine.
269      * @param {Array} denom One-dimensional array containing the extrapolation data for the denominator. Has to be supplied by the calling routine.
270      *
271      * @memberof JXG.Math.Extrapolate
272      */
273     levin: function (s_n, n, omega, beta, numer, denom) {
274         var HUGE = 1e20,
275             TINY = 1e-15,
276             j,
277             fact,
278             ratio,
279             term,
280             estlim;
281 
282         term = 1.0 / (beta + n);
283         numer[n] = s_n / omega;
284         denom[n] = 1 / omega;
285         if (n > 0) {
286             numer[n - 1] = numer[n] - numer[n - 1];
287             denom[n - 1] = denom[n] - denom[n - 1];
288             if (n > 1) {
289                 ratio = (beta + n - 1) * term;
290                 for (j = 2; j <= n; j++) {
291                     fact = (beta + n - j) * Math.pow(ratio, j - 2) * term;
292                     numer[n - j] = numer[n - j + 1] - fact * numer[n - j];
293                     denom[n - j] = denom[n - j + 1] - fact * denom[n - j];
294                     term *= ratio;
295                 }
296             }
297         }
298         if (Math.abs(denom[0]) < TINY) {
299             estlim = HUGE;
300         } else {
301             estlim = numer[0] / denom[0];
302         }
303         return estlim;
304     },
305 
306     iteration_levin: function (x0, h0, f, step_type) {
307         var n,
308             v,
309             w,
310             estlim = NaN,
311             v_prev,
312             delta,
313             diff,
314             omega,
315             beta = 1,
316             r = 0.5,
317             numer = [],
318             denom = [],
319             result = "finite",
320             h = h0,
321             transform = "u";
322 
323         step_type = step_type || 0;
324 
325         v_prev = f(x0 + h0, true);
326         for (n = 1; n <= this.upper; n++) {
327             h = step_type === 0 ? h0 / (n + 1) : h * r;
328             v = f(x0 + h, true);
329             delta = v - v_prev;
330             if (Math.abs(delta) < 1) {
331                 transform = "u";
332             } else {
333                 transform = "t";
334             }
335             if (transform === "u") {
336                 omega = (beta + n) * delta; // u transformation
337             } else {
338                 omega = delta; // t transformation
339             }
340 
341             v_prev = v;
342             w = this.levin(v, n - 1, omega, beta, numer, denom);
343             diff = w - estlim;
344             // console.log(n, delta, transform, x0 + h, v, w, diff);
345 
346             if (isNaN(w)) {
347                 result = "NaN";
348                 break;
349             }
350             if (v !== 0 && w / v > this.infty) {
351                 estlim = w;
352                 result = "infinite";
353                 break;
354             }
355             if (Math.abs(diff) < 1e-7) {
356                 break;
357             }
358             estlim = w;
359         }
360         return [estlim, result, 1 - (n - 1) / this.upper];
361     },
362 
363     /**
364      *
365      * @param {Number} x0 Value for which the limit of f is to be determined. f(x0) may or may not exist.
366      * @param {Number} h0 Initial (signed) distance from x0.
367      * @param {Function} f Function for which the limit at x0 is to be determined
368      *
369      * @returns {Array} Array of length 3. Position 0: estimated value for f(x0), position 1: 'finite', 'infinite', or 'NaN'.
370      * Position 2: value between 0 and 1 judging the reliability of the result (1: high, 0: not successful).
371      * In case that the extrapolation fails, position 1 and 2 contain 'direct' and 0.
372      *
373      * @example
374      * var f1 = (x) => Math.log(x),
375      *     f2 = (x) => Math.tan(x - Math.PI * 0.5),
376      *     f3 = (x) => 4 / x;
377      *
378      * var x0 = 0.0000001;
379      * var h = 0.1;
380      * for (let f of [f1, f2, f3]) {
381      *     console.log("x0=", x0, f.toString());
382      *     console.log(JXG.Math.Extrapolate.limit(x0, h, f));
383      *  }
384      *
385      * </pre><div id="JXG5e8c6a7e-eeae-43fb-a669-26b5c9e40cab" class="jxgbox" style="width: 300px; height: 300px;"></div>
386      * <script type="text/javascript">
387      *     (function() {
388      *         var board = JXG.JSXGraph.initBoard('JXG5e8c6a7e-eeae-43fb-a669-26b5c9e40cab',
389      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
390      *     var f1 = (x) => Math.log(x),
391      *         f2 = (x) => Math.tan(x - Math.PI * 0.5),
392      *         f3 = (x) => 4 / x;
393      *
394      *     var x0 = 0.0000001;
395      *     var h = 0.1;
396      *     for (let f of [f1, f2, f3]) {
397      *         console.log("x0=", x0, f.toString());
398      *         console.log(JXG.Math.Extrapolate.limit(x0, h, f));
399      *      }
400      *
401      *     })();
402      *
403      * </script><pre>
404      *
405      *
406      * @see JXG.Math.Extrapolate.iteration
407      * @memberof JXG.Math.Extrapolate
408      */
409     limit: function (x0, h0, f) {
410         return this.iteration_levin(x0, h0, f, 0);
411         //return this.iteration(x0, h0, f, 'wynnEps', 1);
412 
413         // var algs = ['wynnEps', 'levin'], //, 'wynnEps', 'levin', 'aitken', 'brezinski'],
414         //     le = algs.length,
415         //     i, t, res;
416         // for (i = 0; i < le; i++) {
417         //     for (t = 0; t < 1; t++) {
418         //         if (algs[i] === 'levin') {
419         //             res = this.iteration_levin(x0, h0, f, t);
420         //         } else {
421         //             res = this.iteration(x0, h0, f, algs[i], t);
422         //         }
423         //         if (res[2] > 0.6) {
424         //             return res;
425         //         }
426         //         console.log(algs[i], t, res)
427         //     }
428         // }
429         // return [f(x0 + Math.sign(h0) * Math.sqrt(Mat.eps)), 'direct', 0];
430     }
431 };
432 
433 export default Mat.Extrapolate;
434