      1 /*
      2     Copyright 2008-2025
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Alfred Wassermann,
      8         Peter Wilfahrt
      9
     10     This file is part of JSXGraph.
     11
     12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     13
     14     You can redistribute it and/or modify it under the terms of the
     15
     16       * GNU Lesser General Public License as published by
     17         the Free Software Foundation, either version 3 of the License, or
     18         (at your option) any later version
     19       OR
     20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     21
     22     JSXGraph is distributed in the hope that it will be useful,
     23     but WITHOUT ANY WARRANTY; without even the implied warranty of
     24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     25     GNU Lesser General Public License for more details.
     26
     27     You should have received a copy of the GNU Lesser General Public License and
     28     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
     29     and <https://opensource.org/licenses/MIT/>.
     30  */
     31
     32 /*global JXG: true, define: true*/
     33 /*jslint nomen: true, plusplus: true*/
     34 /*eslint no-loss-of-precision: off */
     35
     36 /**
     37  * @fileoverview In this file the namespace Math.Numerics is defined, which holds numerical
     38  * algorithms for solving linear equations etc.
     39  */
     40
     41 import JXG from "../jxg.js";
     42 import Type from "../utils/type.js";
     43 import Env from "../utils/env.js";
     44 import Mat from "./math.js";
     45
     46 // Predefined butcher tableaus for the common Runge-Kutta method (fourth order), Heun method (second order), and Euler method (first order).
     47 var predefinedButcher = {
     48     rk4: {
     49         s: 4,
     50         A: [
     51             [0, 0, 0, 0],
     52             [0.5, 0, 0, 0],
     53             [0, 0.5, 0, 0],
     54             [0, 0, 1, 0]
     55         ],
     56         b: [1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0],
     57         c: [0, 0.5, 0.5, 1]
     58     },
     59     heun: {
     60         s: 2,
     61         A: [
     62             [0, 0],
     63             [1, 0]
     64         ],
     65         b: [0.5, 0.5],
     66         c: [0, 1]
     67     },
     68     euler: {
     69         s: 1,
     70         A: [[0]],
     71         b: [1],
     72         c: [0]
     73     }
     74 };
     75
     76 /**
     77  * The JXG.Math.Numerics namespace holds numerical algorithms, constants, and variables.
     78  * @name JXG.Math.Numerics
     79  * @exports Mat.Numerics as JXG.Math.Numerics
     80  * @namespace
     81  */
     82 Mat.Numerics = {
     83     //JXG.extend(Mat.Numerics, /** @lends JXG.Math.Numerics */ {
     84     /**
     85      * Solves a system of linear equations given by A and b using the Gauss-Jordan-elimination.
     86      * The algorithm runs in-place. I.e. the entries of A and b are changed.
     87      * @param {Array} A Square matrix represented by an array of rows, containing the coefficients of the lineare equation system.
     88      * @param {Array} b A vector containing the linear equation system's right hand side.
     89      * @throws {Error} If a non-square-matrix is given or if b has not the right length or A's rank is not full.
     90      * @returns {Array} A vector that solves the linear equation system.
     91      * @memberof JXG.Math.Numerics
     92      */
     93     Gauss: function (A, b) {
     94         var i,
     95             j,
     96             k,
     97             // copy the matrix to prevent changes in the original
     98             Acopy,
     99             // solution vector, to prevent changing b
    100             x,
    101             eps = Mat.eps,
    102             // number of columns of A
    103             n = A.length > 0 ? A[0].length : 0;
    104
    105         if (n !== b.length || n !== A.length) {
    106             throw new Error(
    107                 "JXG.Math.Numerics.Gauss: Dimensions don't match. A must be a square matrix and b must be of the same length as A."
    108             );
    109         }
    110
    111         // initialize solution vector
    112         Acopy = [];
    113         x = b.slice(0, n);
    114
    115         for (i = 0; i < n; i++) {
    116             Acopy[i] = A[i].slice(0, n);
    117         }
    118
    119         // Gauss-Jordan-elimination
    120         for (j = 0; j < n; j++) {
    121             for (i = n - 1; i > j; i--) {
    122                 // Is the element which is to eliminate greater than zero?
    123                 if (Math.abs(Acopy[i][j]) > eps) {
    124                     // Equals pivot element zero?
    125                     if (Math.abs(Acopy[j][j]) < eps) {
    126                         // At least numerically, so we have to exchange the rows
    127                         Type.swap(Acopy, i, j);
    128                         Type.swap(x, i, j);
    129                     } else {
    130                         // Saves the L matrix of the LR-decomposition. unnecessary.
    131                         Acopy[i][j] /= Acopy[j][j];
    132                         // Transform right-hand-side b
    133                         x[i] -= Acopy[i][j] * x[j];
    134
    135                         // subtract the multiple of A[i][j] / A[j][j] of the j-th row from the i-th.
    136                         for (k = j + 1; k < n; k++) {
    137                             Acopy[i][k] -= Acopy[i][j] * Acopy[j][k];
    138                         }
    139                     }
    140                 }
    141             }
    142
    143             // The absolute values of all coefficients below the j-th row in the j-th column are smaller than JXG.Math.eps.
    144             if (Math.abs(Acopy[j][j]) < eps) {
    145                 throw new Error(
    146                     "JXG.Math.Numerics.Gauss(): The given matrix seems to be singular."
    147                 );
    148             }
    149         }
    150
    151         this.backwardSolve(Acopy, x, true);
    152
    153         return x;
    154     },
    155
    156     /**
    157      * Solves a system of linear equations given by the right triangular matrix R and vector b.
    158      * @param {Array} R Right triangular matrix represented by an array of rows. All entries a_(i,j) with i < j are ignored.
    159      * @param {Array} b Right hand side of the linear equation system.
    160      * @param {Boolean} [canModify=false] If true, the right hand side vector is allowed to be changed by this method.
    161      * @returns {Array} An array representing a vector that solves the system of linear equations.
    162      * @memberof JXG.Math.Numerics
    163      */
    164     backwardSolve: function (R, b, canModify) {
    165         var x, m, n, i, j;
    166
    167         if (canModify) {
    168             x = b;
    169         } else {
    170             x = b.slice(0, b.length);
    171         }
    172
    173         // m: number of rows of R
    174         // n: number of columns of R
    175         m = R.length;
    176         n = R.length > 0 ? R[0].length : 0;
    177
    178         for (i = m - 1; i >= 0; i--) {
    179             for (j = n - 1; j > i; j--) {
    180                 x[i] -= R[i][j] * x[j];
    181             }
    182             x[i] /= R[i][i];
    183         }
    184
    185         return x;
    186     },
    187
    188     /**
    189      *  Gauss-Bareiss algorithm to compute the
    190      *  determinant of matrix without fractions.
    191      *  See Henri Cohen, "A Course in Computational
    192      *  Algebraic Number Theory (Graduate texts
    193      *  in mathematics; 138)", Springer-Verlag,
    194      *  ISBN 3-540-55640-0 / 0-387-55640-0
    195      *  Third, Corrected Printing 1996
    196      *  "Algorithm 2.2.6", pg. 52-53
    197      *
    198      * @param {Array} mat Matrix
    199      * @returns Number
    200      * @private
    201      * @memberof JXG.Math.Numerics
    202      */
    203     gaussBareiss: function (mat) {
    204         var k, c, s,
    205             i, j, p,
    206             n, M, t,
    207             eps = Mat.eps;
    208
    209         n = mat.length;
    210
    211         if (n <= 0) {
    212             return 0;
    213         }
    214
    215         if (mat[0].length < n) {
    216             n = mat[0].length;
    217         }
    218
    219         // Copy the input matrix to M
    220         M = [];
    221
    222         for (i = 0; i < n; i++) {
    223             M[i] = mat[i].slice(0, n);
    224         }
    225
    226         c = 1;
    227         s = 1;
    228
    229         for (k = 0; k < n - 1; k++) {
    230             p = M[k][k];
    231
    232             // Pivot step
    233             if (Math.abs(p) < eps) {
    234                 for (i = k + 1; i < n; i++) {
    235                     if (Math.abs(M[i][k]) >= eps) {
    236                         break;
    237                     }
    238                 }
    239
    240                 // No nonzero entry found in column k -> det(M) = 0
    241                 if (i === n) {
    242                     return 0.0;
    243                 }
    244
    245                 // swap row i and k partially
    246                 for (j = k; j < n; j++) {
    247                     t = M[i][j];
    248                     M[i][j] = M[k][j];
    249                     M[k][j] = t;
    250                 }
    251                 s = -s;
    252                 p = M[k][k];
    253             }
    254
    255             // Main step
    256             for (i = k + 1; i < n; i++) {
    257                 for (j = k + 1; j < n; j++) {
    258                     t = p * M[i][j] - M[i][k] * M[k][j];
    259                     M[i][j] = t / c;
    260                 }
    261             }
    262
    263             c = p;
    264         }
    265
    266         return s * M[n - 1][n - 1];
    267     },
    268
    269     /**
    270      * Computes the determinant of a square nxn matrix with the
    271      * Gauss-Bareiss algorithm.
    272      * @param {Array} mat Matrix.
    273      * @returns {Number} The determinant pf the matrix mat.
    274      *                   The empty matrix returns 0.
    275      * @memberof JXG.Math.Numerics
    276      */
    277     det: function (mat) {
    278         var n = mat.length;
    279
    280         if (n === 2 && mat[0].length === 2) {
    281             return mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1];
    282         }
    283
    284         return this.gaussBareiss(mat);
    285     },
    286
    287     /**
    288      * Compute the Eigenvalues and Eigenvectors of a symmetric 3x3 matrix with the Jacobi method
    289      * Adaption of a FORTRAN program by Ed Wilson, Dec. 25, 1990
    290      * @param {Array} Ain A symmetric 3x3 matrix.
    291      * @returns {Array} [A,V] the matrices A and V. The diagonal of A contains the Eigenvalues, V contains the Eigenvectors.
    292      * @memberof JXG.Math.Numerics
    293      */
    294     Jacobi: function (Ain) {
    295         var i,
    296             j,
    297             k,
    298             aa,
    299             si,
    300             co,
    301             tt,
    302             ssum,
    303             amax,
    304             eps = Mat.eps * Mat.eps,
    305             sum = 0.0,
    306             n = Ain.length,
    307             V = [
    308                 [0, 0, 0],
    309                 [0, 0, 0],
    310                 [0, 0, 0]
    311             ],
    312             A = [
    313                 [0, 0, 0],
    314                 [0, 0, 0],
    315                 [0, 0, 0]
    316             ],
    317             nloops = 0;
    318
    319         // Initialization. Set initial Eigenvectors.
    320         for (i = 0; i < n; i++) {
    321             for (j = 0; j < n; j++) {
    322                 V[i][j] = 0.0;
    323                 A[i][j] = Ain[i][j];
    324                 sum += Math.abs(A[i][j]);
    325             }
    326             V[i][i] = 1.0;
    327         }
    328
    329         // Trivial problems
    330         if (n === 1) {
    331             return [A, V];
    332         }
    333
    334         if (sum <= 0.0) {
    335             return [A, V];
    336         }
    337
    338         sum /= n * n;
    339
    340         // Reduce matrix to diagonal
    341         do {
    342             ssum = 0.0;
    343             amax = 0.0;
    344             for (j = 1; j < n; j++) {
    345                 for (i = 0; i < j; i++) {
    346                     // Check if A[i][j] is to be reduced
    347                     aa = Math.abs(A[i][j]);
    348
    349                     if (aa > amax) {
    350                         amax = aa;
    351                     }
    352
    353                     ssum += aa;
    354
    355                     if (aa >= eps) {
    356                         // calculate rotation angle
    357                         aa = Math.atan2(2.0 * A[i][j], A[i][i] - A[j][j]) * 0.5;
    358                         si = Math.sin(aa);
    359                         co = Math.cos(aa);
    360
    361                         // Modify 'i' and 'j' columns
    362                         for (k = 0; k < n; k++) {
    363                             tt = A[k][i];
    364                             A[k][i] = co * tt + si * A[k][j];
    365                             A[k][j] = -si * tt + co * A[k][j];
    366                             tt = V[k][i];
    367                             V[k][i] = co * tt + si * V[k][j];
    368                             V[k][j] = -si * tt + co * V[k][j];
    369                         }
    370
    371                         // Modify diagonal terms
    372                         A[i][i] = co * A[i][i] + si * A[j][i];
    373                         A[j][j] = -si * A[i][j] + co * A[j][j];
    374                         A[i][j] = 0.0;
    375
    376                         // Make 'A' matrix symmetrical
    377                         for (k = 0; k < n; k++) {
    378                             A[i][k] = A[k][i];
    379                             A[j][k] = A[k][j];
    380                         }
    381                         // A[i][j] made zero by rotation
    382                     }
    383                 }
    384             }
    385             nloops += 1;
    386         } while (Math.abs(ssum) / sum > eps && nloops < 2000);
    387
    388         return [A, V];
    389     },
    390
    391     /**
    392      * Calculates the integral of function f over interval using Newton-Cotes-algorithm.
    393      * @param {Array} interval The integration interval, e.g. [0, 3].
    394      * @param {function} f A function which takes one argument of type number and returns a number.
    395      * @param {Object} [config] The algorithm setup. Accepted properties are number_of_nodes of type number and integration_type
    396      * with value being either 'trapez', 'simpson', or 'milne'.
    397      * @param {Number} [config.number_of_nodes=28]
    398      * @param {String} [config.integration_type='milne'] Possible values are 'milne', 'simpson', 'trapez'
    399      * @returns {Number} Integral value of f over interval
    400      * @throws {Error} If config.number_of_nodes doesn't match config.integration_type an exception is thrown. If you want to use
    401      * simpson rule respectively milne rule config.number_of_nodes must be dividable by 2 respectively 4.
    402      * @example
    403      * function f(x) {
    404      *   return x*x;
    405      * }
    406      *
    407      * // calculates integral of <tt>f</tt> from 0 to 2.
    408      * var area1 = JXG.Math.Numerics.NewtonCotes([0, 2], f);
    409      *
    410      * // the same with an anonymous function
    411      * var area2 = JXG.Math.Numerics.NewtonCotes([0, 2], function (x) { return x*x; });
    412      *
    413      * // use trapez rule with 16 nodes
    414      * var area3 = JXG.Math.Numerics.NewtonCotes([0, 2], f,
    415      *                                   {number_of_nodes: 16, integration_type: 'trapez'});
    416      * @memberof JXG.Math.Numerics
    417      */
    418     NewtonCotes: function (interval, f, config) {
    419         var evaluation_point,
    420             i,
    421             number_of_intervals,
    422             integral_value = 0.0,
    423             number_of_nodes =
    424                 config && Type.isNumber(config.number_of_nodes) ? config.number_of_nodes : 28,
    425             available_types = { trapez: true, simpson: true, milne: true },
    426             integration_type =
    427                 config &&
    428                     config.integration_type &&
    429                     available_types.hasOwnProperty(config.integration_type) &&
    430                     available_types[config.integration_type]
    431                     ? config.integration_type
    432                     : "milne",
    433             step_size = (interval[1] - interval[0]) / number_of_nodes;
    434
    435         switch (integration_type) {
    436             case "trapez":
    437                 integral_value = (f(interval[0]) + f(interval[1])) * 0.5;
    438                 evaluation_point = interval[0];
    439
    440                 for (i = 0; i < number_of_nodes - 1; i++) {
    441                     evaluation_point += step_size;
    442                     integral_value += f(evaluation_point);
    443                 }
    444
    445                 integral_value *= step_size;
    446                 break;
    447             case "simpson":
    448                 if (number_of_nodes % 2 > 0) {
    449                     throw new Error(
    450                         "JSXGraph:  INT_SIMPSON requires config.number_of_nodes dividable by 2."
    451                     );
    452                 }
    453
    454                 number_of_intervals = number_of_nodes / 2.0;
    455                 integral_value = f(interval[0]) + f(interval[1]);
    456                 evaluation_point = interval[0];
    457
    458                 for (i = 0; i < number_of_intervals - 1; i++) {
    459                     evaluation_point += 2.0 * step_size;
    460                     integral_value += 2.0 * f(evaluation_point);
    461                 }
    462
    463                 evaluation_point = interval[0] - step_size;
    464
    465                 for (i = 0; i < number_of_intervals; i++) {
    466                     evaluation_point += 2.0 * step_size;
    467                     integral_value += 4.0 * f(evaluation_point);
    468                 }
    469
    470                 integral_value *= step_size / 3.0;
    471                 break;
    472             default:
    473                 if (number_of_nodes % 4 > 0) {
    474                     throw new Error(
    475                         "JSXGraph: Error in INT_MILNE: config.number_of_nodes must be a multiple of 4"
    476                     );
    477                 }
    478
    479                 number_of_intervals = number_of_nodes * 0.25;
    480                 integral_value = 7.0 * (f(interval[0]) + f(interval[1]));
    481                 evaluation_point = interval[0];
    482
    483                 for (i = 0; i < number_of_intervals - 1; i++) {
    484                     evaluation_point += 4.0 * step_size;
    485                     integral_value += 14.0 * f(evaluation_point);
    486                 }
    487
    488                 evaluation_point = interval[0] - 3.0 * step_size;
    489
    490                 for (i = 0; i < number_of_intervals; i++) {
    491                     evaluation_point += 4.0 * step_size;
    492                     integral_value +=
    493                         32.0 * (f(evaluation_point) + f(evaluation_point + 2 * step_size));
    494                 }
    495
    496                 evaluation_point = interval[0] - 2.0 * step_size;
    497
    498                 for (i = 0; i < number_of_intervals; i++) {
    499                     evaluation_point += 4.0 * step_size;
    500                     integral_value += 12.0 * f(evaluation_point);
    501                 }
    502
    503                 integral_value *= (2.0 * step_size) / 45.0;
    504         }
    505         return integral_value;
    506     },
    507
    508     /**
    509      * Calculates the integral of function f over interval using Romberg iteration.
    510      * @param {Array} interval The integration interval, e.g. [0, 3].
    511      * @param {function} f A function which takes one argument of type number and returns a number.
    512      * @param {Object} [config] The algorithm setup. Accepted properties are max_iterations of type number and precision eps.
    513      * @param {Number} [config.max_iterations=20]
    514      * @param {Number} [config.eps=0.0000001]
    515      * @returns {Number} Integral value of f over interval
    516      * @example
    517      * function f(x) {
    518      *   return x*x;
    519      * }
    520      *
    521      * // calculates integral of <tt>f</tt> from 0 to 2.
    522      * var area1 = JXG.Math.Numerics.Romberg([0, 2], f);
    523      *
    524      * // the same with an anonymous function
    525      * var area2 = JXG.Math.Numerics.Romberg([0, 2], function (x) { return x*x; });
    526      *
    527      * // use trapez rule with maximum of 16 iterations or stop if the precision 0.0001 has been reached.
    528      * var area3 = JXG.Math.Numerics.Romberg([0, 2], f,
    529      *                                   {max_iterations: 16, eps: 0.0001});
    530      * @memberof JXG.Math.Numerics
    531      */
    532     Romberg: function (interval, f, config) {
    533         var a,
    534             b,
    535             h,
    536             s,
    537             n,
    538             k,
    539             i,
    540             q,
    541             p = [],
    542             integral = 0.0,
    543             last = Infinity,
    544             m = config && Type.isNumber(config.max_iterations) ? config.max_iterations : 20,
    545             eps = config && Type.isNumber(config.eps) ? config.eps : config.eps || 0.0000001;
    546
    547         a = interval[0];
    548         b = interval[1];
    549         h = b - a;
    550         n = 1;
    551
    552         p[0] = 0.5 * h * (f(a) + f(b));
    553
    554         for (k = 0; k < m; ++k) {
    555             s = 0;
    556             h *= 0.5;
    557             n *= 2;
    558             q = 1;
    559
    560             for (i = 1; i < n; i += 2) {
    561                 s += f(a + i * h);
    562             }
    563
    564             p[k + 1] = 0.5 * p[k] + s * h;
    565
    566             integral = p[k + 1];
    567             for (i = k - 1; i >= 0; --i) {
    568                 q *= 4;
    569                 p[i] = p[i + 1] + (p[i + 1] - p[i]) / (q - 1.0);
    570                 integral = p[i];
    571             }
    572
    573             if (Math.abs(integral - last) < eps * Math.abs(integral)) {
    574                 break;
    575             }
    576             last = integral;
    577         }
    578
    579         return integral;
    580     },
    581
    582     /**
    583      * Calculates the integral of function f over interval using Gauss-Legendre quadrature.
    584      * @param {Array} interval The integration interval, e.g. [0, 3].
    585      * @param {function} f A function which takes one argument of type number and returns a number.
    586      * @param {Object} [config] The algorithm setup. Accepted property is the order n of type number. n is allowed to take
    587      * values between 2 and 18, default value is 12.
    588      * @param {Number} [config.n=16]
    589      * @returns {Number} Integral value of f over interval
    590      * @example
    591      * function f(x) {
    592      *   return x*x;
    593      * }
    594      *
    595      * // calculates integral of <tt>f</tt> from 0 to 2.
    596      * var area1 = JXG.Math.Numerics.GaussLegendre([0, 2], f);
    597      *
    598      * // the same with an anonymous function
    599      * var area2 = JXG.Math.Numerics.GaussLegendre([0, 2], function (x) { return x*x; });
    600      *
    601      * // use 16 point Gauss-Legendre rule.
    602      * var area3 = JXG.Math.Numerics.GaussLegendre([0, 2], f,
    603      *                                   {n: 16});
    604      * @memberof JXG.Math.Numerics
    605      */
    606     GaussLegendre: function (interval, f, config) {
    607         var a,
    608             b,
    609             i,
    610             m,
    611             xp,
    612             xm,
    613             result = 0.0,
    614             table_xi = [],
    615             table_w = [],
    616             xi,
    617             w,
    618             n = config && Type.isNumber(config.n) ? config.n : 12;
    619
    620         if (n > 18) {
    621             n = 18;
    622         }
    623
    624         /* n = 2 */
    625         table_xi[2] = [0.5773502691896257645091488];
    626         table_w[2] = [1.0];
    627
    628         /* n = 4 */
    629         table_xi[4] = [0.3399810435848562648026658, 0.8611363115940525752239465];
    630         table_w[4] = [0.6521451548625461426269361, 0.3478548451374538573730639];
    631
    632         /* n = 6 */
    633         table_xi[6] = [
    634             0.2386191860831969086305017, 0.6612093864662645136613996,
    635             0.9324695142031520278123016
    636         ];
    637         table_w[6] = [
    638             0.4679139345726910473898703, 0.3607615730481386075698335,
    639             0.1713244923791703450402961
    640         ];
    641
    642         /* n = 8 */
    643         table_xi[8] = [
    644             0.1834346424956498049394761, 0.525532409916328985817739,
    645             0.7966664774136267395915539, 0.9602898564975362316835609
    646         ];
    647         table_w[8] = [
    648             0.3626837833783619829651504, 0.3137066458778872873379622,
    649             0.222381034453374470544356, 0.1012285362903762591525314
    650         ];
    651
    652         /* n = 10 */
    653         table_xi[10] = [
    654             0.148874338981631210884826, 0.4333953941292471907992659,
    655             0.6794095682990244062343274, 0.8650633666889845107320967, 0.973906528517171720077964
    656         ];
    657         table_w[10] = [
    658             0.295524224714752870173893, 0.2692667193099963550912269,
    659             0.2190863625159820439955349, 0.1494513491505805931457763,
    660             0.0666713443086881375935688
    661         ];
    662
    663         /* n = 12 */
    664         table_xi[12] = [
    665             0.1252334085114689154724414, 0.3678314989981801937526915,
    666             0.5873179542866174472967024, 0.7699026741943046870368938,
    667             0.9041172563704748566784659, 0.9815606342467192506905491
    668         ];
    669         table_w[12] = [
    670             0.2491470458134027850005624, 0.2334925365383548087608499,
    671             0.2031674267230659217490645, 0.1600783285433462263346525,
    672             0.1069393259953184309602547, 0.047175336386511827194616
    673         ];
    674
    675         /* n = 14 */
    676         table_xi[14] = [
    677             0.1080549487073436620662447, 0.3191123689278897604356718,
    678             0.5152486363581540919652907, 0.6872929048116854701480198,
    679             0.8272013150697649931897947, 0.9284348836635735173363911,
    680             0.9862838086968123388415973
    681         ];
    682         table_w[14] = [
    683             0.2152638534631577901958764, 0.2051984637212956039659241,
    684             0.1855383974779378137417166, 0.1572031671581935345696019,
    685             0.1215185706879031846894148, 0.0801580871597602098056333,
    686             0.0351194603317518630318329
    687         ];
    688
    689         /* n = 16 */
    690         table_xi[16] = [
    691             0.0950125098376374401853193, 0.2816035507792589132304605,
    692             0.4580167776572273863424194, 0.6178762444026437484466718,
    693             0.7554044083550030338951012, 0.8656312023878317438804679,
    694             0.9445750230732325760779884, 0.9894009349916499325961542
    695         ];
    696         table_w[16] = [
    697             0.1894506104550684962853967, 0.1826034150449235888667637,
    698             0.1691565193950025381893121, 0.1495959888165767320815017,
    699             0.1246289712555338720524763, 0.0951585116824927848099251,
    700             0.0622535239386478928628438, 0.0271524594117540948517806
    701         ];
    702
    703         /* n = 18 */
    704         table_xi[18] = [
    705             0.0847750130417353012422619, 0.2518862256915055095889729,
    706             0.4117511614628426460359318, 0.5597708310739475346078715,
    707             0.6916870430603532078748911, 0.8037049589725231156824175,
    708             0.8926024664975557392060606, 0.9558239495713977551811959, 0.991565168420930946730016
    709         ];
    710         table_w[18] = [
    711             0.1691423829631435918406565, 0.1642764837458327229860538,
    712             0.154684675126265244925418, 0.1406429146706506512047313,
    713             0.1225552067114784601845191, 0.100942044106287165562814,
    714             0.0764257302548890565291297, 0.0497145488949697964533349,
    715             0.0216160135264833103133427
    716         ];
    717
    718         /* n = 3 */
    719         table_xi[3] = [0.0, 0.7745966692414833770358531];
    720         table_w[3] = [0.8888888888888888888888889, 0.5555555555555555555555556];
    721
    722         /* n = 5 */
    723         table_xi[5] = [0.0, 0.5384693101056830910363144, 0.9061798459386639927976269];
    724         table_w[5] = [
    725             0.5688888888888888888888889, 0.4786286704993664680412915, 0.236926885056189087514264
    726         ];
    727
    728         /* n = 7 */
    729         table_xi[7] = [
    730             0.0, 0.4058451513773971669066064, 0.7415311855993944398638648,
    731             0.9491079123427585245261897
    732         ];
    733         table_w[7] = [
    734             0.417959183673469387755102, 0.3818300505051189449503698,
    735             0.2797053914892766679014678, 0.1294849661688696932706114
    736         ];
    737
    738         /* n = 9 */
    739         table_xi[9] = [
    740             0.0, 0.324253423403808929038538, 0.613371432700590397308702,
    741             0.8360311073266357942994298, 0.9681602395076260898355762
    742         ];
    743         table_w[9] = [
    744             0.3302393550012597631645251, 0.3123470770400028400686304,
    745             0.2606106964029354623187429, 0.180648160694857404058472, 0.0812743883615744119718922
    746         ];
    747
    748         /* n = 11 */
    749         table_xi[11] = [
    750             0.0, 0.269543155952344972331532, 0.5190961292068118159257257,
    751             0.7301520055740493240934163, 0.8870625997680952990751578, 0.978228658146056992803938
    752         ];
    753         table_w[11] = [
    754             0.2729250867779006307144835, 0.2628045445102466621806889,
    755             0.2331937645919904799185237, 0.1862902109277342514260976,
    756             0.1255803694649046246346943, 0.0556685671161736664827537
    757         ];
    758
    759         /* n = 13 */
    760         table_xi[13] = [
    761             0.0, 0.2304583159551347940655281, 0.4484927510364468528779129,
    762             0.6423493394403402206439846, 0.8015780907333099127942065,
    763             0.9175983992229779652065478, 0.9841830547185881494728294
    764         ];
    765         table_w[13] = [
    766             0.2325515532308739101945895, 0.2262831802628972384120902,
    767             0.2078160475368885023125232, 0.1781459807619457382800467,
    768             0.1388735102197872384636018, 0.0921214998377284479144218,
    769             0.0404840047653158795200216
    770         ];
    771
    772         /* n = 15 */
    773         table_xi[15] = [
    774             0.0, 0.2011940939974345223006283, 0.3941513470775633698972074,
    775             0.5709721726085388475372267, 0.7244177313601700474161861,
    776             0.8482065834104272162006483, 0.9372733924007059043077589,
    777             0.9879925180204854284895657
    778         ];
    779         table_w[15] = [
    780             0.2025782419255612728806202, 0.1984314853271115764561183,
    781             0.1861610000155622110268006, 0.1662692058169939335532009,
    782             0.1395706779261543144478048, 0.1071592204671719350118695,
    783             0.0703660474881081247092674, 0.0307532419961172683546284
    784         ];
    785
    786         /* n = 17 */
    787         table_xi[17] = [
    788             0.0, 0.1784841814958478558506775, 0.3512317634538763152971855,
    789             0.5126905370864769678862466, 0.6576711592166907658503022,
    790             0.7815140038968014069252301, 0.8802391537269859021229557,
    791             0.950675521768767761222717, 0.990575475314417335675434
    792         ];
    793         table_w[17] = [
    794             0.1794464703562065254582656, 0.176562705366992646325271,
    795             0.1680041021564500445099707, 0.1540457610768102880814316, 0.13513636846852547328632,
    796             0.1118838471934039710947884, 0.0850361483171791808835354,
    797             0.0554595293739872011294402, 0.02414830286854793196011
    798         ];
    799
    800         a = interval[0];
    801         b = interval[1];
    802
    803         //m = Math.ceil(n * 0.5);
    804         m = (n + 1) >> 1;
    805
    806         xi = table_xi[n];
    807         w = table_w[n];
    808
    809         xm = 0.5 * (b - a);
    810         xp = 0.5 * (b + a);
    811
    812         if (n & (1 === 1)) {
    813             // n odd
    814             result = w[0] * f(xp);
    815             for (i = 1; i < m; ++i) {
    816                 result += w[i] * (f(xp + xm * xi[i]) + f(xp - xm * xi[i]));
    817             }
    818         } else {
    819             // n even
    820             result = 0.0;
    821             for (i = 0; i < m; ++i) {
    822                 result += w[i] * (f(xp + xm * xi[i]) + f(xp - xm * xi[i]));
    823             }
    824         }
    825
    826         return xm * result;
    827     },
    828
    829     /**
    830      * Scale error in Gauss Kronrod quadrature.
    831      * Internal method used in {@link JXG.Math.Numerics._gaussKronrod}.
    832      * @private
    833      */
    834     _rescale_error: function (err, result_abs, result_asc) {
    835         var scale,
    836             min_err,
    837             DBL_MIN = 2.2250738585072014e-308,
    838             DBL_EPS = 2.2204460492503131e-16;
    839
    840         err = Math.abs(err);
    841         if (result_asc !== 0 && err !== 0) {
    842             scale = Math.pow((200 * err) / result_asc, 1.5);
    843
    844             if (scale < 1.0) {
    845                 err = result_asc * scale;
    846             } else {
    847                 err = result_asc;
    848             }
    849         }
    850         if (result_abs > DBL_MIN / (50 * DBL_EPS)) {
    851             min_err = 50 * DBL_EPS * result_abs;
    852
    853             if (min_err > err) {
    854                 err = min_err;
    855             }
    856         }
    857
    858         return err;
    859     },
    860
    861     /**
    862      * Generic Gauss-Kronrod quadrature algorithm.
    863      * Internal method used in {@link JXG.Math.Numerics.GaussKronrod15},
    864      * {@link JXG.Math.Numerics.GaussKronrod21},
    865      * {@link JXG.Math.Numerics.GaussKronrod31}.
    866      * Taken from QUADPACK.
    867      *
    868      * @param {Array} interval The integration interval, e.g. [0, 3].
    869      * @param {function} f A function which takes one argument of type number and returns a number.
    870      * @param {Number} n order of approximation. Actually, n is the length of the array xgk. For example, for the 15-point Kronrod rule, n is equal to 8.
    871      * @param {Array} xgk Kronrod quadrature abscissae
    872      * @param {Array} wg Weights of the Gauss rule
    873      * @param {Array} wgk Weights of the Kronrod rule
    874      * @param {Object} resultObj Object returning resultObj.abserr, resultObj.resabs, resultObj.resasc.
    875      * See the library QUADPACK for an explanation.
    876      *
    877      * @returns {Number} Integral value of f over interval
    878      *
    879      * @private
    880      */
    881     _gaussKronrod: function (interval, f, n, xgk, wg, wgk, resultObj) {
    882         var a = interval[0],
    883             b = interval[1],
    884             up,
    885             result,
    886             center = 0.5 * (a + b),
    887             half_length = 0.5 * (b - a),
    888             abs_half_length = Math.abs(half_length),
    889             f_center = f(center),
    890             result_gauss = 0.0,
    891             result_kronrod = f_center * wgk[n - 1],
    892             result_abs = Math.abs(result_kronrod),
    893             result_asc = 0.0,
    894             mean = 0.0,
    895             err = 0.0,
    896             j, jtw, jtwm1,
    897             abscissa,
    898             fval1, fval2, fsum,
    899             fv1 = [],
    900             fv2 = [];
    901
    902         if (n % 2 === 0) {
    903             result_gauss = f_center * wg[n / 2 - 1];
    904         }
    905
    906         up = Math.floor((n - 1) / 2);
    907         for (j = 0; j < up; j++) {
    908             jtw = j * 2 + 1; // in original fortran j=1,2,3 jtw=2,4,6
    909             abscissa = half_length * xgk[jtw];
    910             fval1 = f(center - abscissa);
    911             fval2 = f(center + abscissa);
    912             fsum = fval1 + fval2;
    913             fv1[jtw] = fval1;
    914             fv2[jtw] = fval2;
    915             result_gauss += wg[j] * fsum;
    916             result_kronrod += wgk[jtw] * fsum;
    917             result_abs += wgk[jtw] * (Math.abs(fval1) + Math.abs(fval2));
    918         }
    919
    920         up = Math.floor(n / 2);
    921         for (j = 0; j < up; j++) {
    922             jtwm1 = j * 2;
    923             abscissa = half_length * xgk[jtwm1];
    924             fval1 = f(center - abscissa);
    925             fval2 = f(center + abscissa);
    926             fv1[jtwm1] = fval1;
    927             fv2[jtwm1] = fval2;
    928             result_kronrod += wgk[jtwm1] * (fval1 + fval2);
    929             result_abs += wgk[jtwm1] * (Math.abs(fval1) + Math.abs(fval2));
    930         }
    931
    932         mean = result_kronrod * 0.5;
    933         result_asc = wgk[n - 1] * Math.abs(f_center - mean);
    934
    935         for (j = 0; j < n - 1; j++) {
    936             result_asc += wgk[j] * (Math.abs(fv1[j] - mean) + Math.abs(fv2[j] - mean));
    937         }
    938
    939         // scale by the width of the integration region
    940         err = (result_kronrod - result_gauss) * half_length;
    941
    942         result_kronrod *= half_length;
    943         result_abs *= abs_half_length;
    944         result_asc *= abs_half_length;
    945         result = result_kronrod;
    946
    947         resultObj.abserr = this._rescale_error(err, result_abs, result_asc);
    948         resultObj.resabs = result_abs;
    949         resultObj.resasc = result_asc;
    950
    951         return result;
    952     },
    953
    954     /**
    955      * 15-point Gauss-Kronrod quadrature algorithm, see the library QUADPACK
    956      * @param {Array} interval The integration interval, e.g. [0, 3].
    957      * @param {function} f A function which takes one argument of type number and returns a number.
    958      * @param {Object} resultObj Object returning resultObj.abserr, resultObj.resabs, resultObj.resasc. See the library
    959      *  QUADPACK for an explanation.
    960      *
    961      * @returns {Number} Integral value of f over interval
    962      *
    963      * @memberof JXG.Math.Numerics
    964      */
    965     GaussKronrod15: function (interval, f, resultObj) {
    966         /* Gauss quadrature weights and kronrod quadrature abscissae and
    967                 weights as evaluated with 80 decimal digit arithmetic by
    968                 L. W. Fullerton, Bell Labs, Nov. 1981. */
    969
    970         var xgk =
    971             /* abscissae of the 15-point kronrod rule */
    972             [
    973                 0.991455371120812639206854697526329, 0.949107912342758524526189684047851,
    974                 0.864864423359769072789712788640926, 0.741531185599394439863864773280788,
    975                 0.58608723546769113029414483825873, 0.405845151377397166906606412076961,
    976                 0.207784955007898467600689403773245, 0.0
    977             ],
    978             /* xgk[1], xgk[3], ... abscissae of the 7-point gauss rule.
    979                 xgk[0], xgk[2], ... abscissae to optimally extend the 7-point gauss rule */
    980
    981             wg =
    982                 /* weights of the 7-point gauss rule */
    983                 [
    984                     0.129484966168869693270611432679082, 0.27970539148927666790146777142378,
    985                     0.381830050505118944950369775488975, 0.417959183673469387755102040816327
    986                 ],
    987             wgk =
    988                 /* weights of the 15-point kronrod rule */
    989                 [
    990                     0.02293532201052922496373200805897, 0.063092092629978553290700663189204,
    991                     0.104790010322250183839876322541518, 0.140653259715525918745189590510238,
    992                     0.16900472663926790282658342659855, 0.190350578064785409913256402421014,
    993                     0.204432940075298892414161999234649, 0.209482141084727828012999174891714
    994                 ];
    995
    996         return this._gaussKronrod(interval, f, 8, xgk, wg, wgk, resultObj);
    997     },
    998
    999     /**
    1000      * 21 point Gauss-Kronrod quadrature algorithm, see the library QUADPACK
    1001      * @param {Array} interval The integration interval, e.g. [0, 3].
    1002      * @param {function} f A function which takes one argument of type number and returns a number.
    1003      * @param {Object} resultObj Object returning resultObj.abserr, resultObj.resabs, resultObj.resasc. See the library
    1004      *  QUADPACK for an explanation.
    1005      *
    1006      * @returns {Number} Integral value of f over interval
    1007      *
    1008      * @memberof JXG.Math.Numerics
    1009      */
    1010     GaussKronrod21: function (interval, f, resultObj) {
    1011         /* Gauss quadrature weights and kronrod quadrature abscissae and
    1012                 weights as evaluated with 80 decimal digit arithmetic by
    1013                 L. W. Fullerton, Bell Labs, Nov. 1981. */
    1014
    1015         var xgk =
    1016             /* abscissae of the 21-point kronrod rule */
    1017             [
    1018                 0.995657163025808080735527280689003, 0.973906528517171720077964012084452,
    1019                 0.930157491355708226001207180059508, 0.865063366688984510732096688423493,
    1020                 0.780817726586416897063717578345042, 0.679409568299024406234327365114874,
    1021                 0.562757134668604683339000099272694, 0.433395394129247190799265943165784,
    1022                 0.294392862701460198131126603103866, 0.14887433898163121088482600112972, 0.0
    1023             ],
    1024             /* xgk[1], xgk[3], ... abscissae of the 10-point gauss rule.
    1025                 xgk[0], xgk[2], ... abscissae to optimally extend the 10-point gauss rule */
    1026             wg =
    1027                 /* weights of the 10-point gauss rule */
    1028                 [
    1029                     0.066671344308688137593568809893332, 0.149451349150580593145776339657697,
    1030                     0.219086362515982043995534934228163, 0.269266719309996355091226921569469,
    1031                     0.295524224714752870173892994651338
    1032                 ],
    1033             wgk =
    1034                 /* weights of the 21-point kronrod rule */
    1035                 [
    1036                     0.011694638867371874278064396062192, 0.03255816230796472747881897245939,
    1037                     0.05475589657435199603138130024458, 0.07503967481091995276704314091619,
    1038                     0.093125454583697605535065465083366, 0.109387158802297641899210590325805,
    1039                     0.123491976262065851077958109831074, 0.134709217311473325928054001771707,
    1040                     0.142775938577060080797094273138717, 0.147739104901338491374841515972068,
    1041                     0.149445554002916905664936468389821
    1042                 ];
    1043
    1044         return this._gaussKronrod(interval, f, 11, xgk, wg, wgk, resultObj);
    1045     },
    1046
    1047     /**
    1048      * 31 point Gauss-Kronrod quadrature algorithm, see the library QUADPACK
    1049      * @param {Array} interval The integration interval, e.g. [0, 3].
    1050      * @param {function} f A function which takes one argument of type number and returns a number.
    1051      * @param {Object} resultObj Object returning resultObj.abserr, resultObj.resabs, resultObj.resasc. See the library
    1052      *  QUADPACK for an explanation.
    1053      *
    1054      * @returns {Number} Integral value of f over interval
    1055      *
    1056      * @memberof JXG.Math.Numerics
    1057      */
    1058     GaussKronrod31: function (interval, f, resultObj) {
    1059         /* Gauss quadrature weights and kronrod quadrature abscissae and
    1060                 weights as evaluated with 80 decimal digit arithmetic by
    1061                 L. W. Fullerton, Bell Labs, Nov. 1981. */
    1062
    1063         var xgk =
    1064             /* abscissae of the 21-point kronrod rule */
    1065             [
    1066                 0.998002298693397060285172840152271, 0.987992518020485428489565718586613,
    1067                 0.967739075679139134257347978784337, 0.937273392400705904307758947710209,
    1068                 0.897264532344081900882509656454496, 0.848206583410427216200648320774217,
    1069                 0.790418501442465932967649294817947, 0.724417731360170047416186054613938,
    1070                 0.650996741297416970533735895313275, 0.570972172608538847537226737253911,
    1071                 0.485081863640239680693655740232351, 0.394151347077563369897207370981045,
    1072                 0.299180007153168812166780024266389, 0.201194093997434522300628303394596,
    1073                 0.101142066918717499027074231447392, 0.0
    1074             ],
    1075             /* xgk[1], xgk[3], ... abscissae of the 10-point gauss rule.
    1076                 xgk[0], xgk[2], ... abscissae to optimally extend the 10-point gauss rule */
    1077             wg =
    1078                 /* weights of the 10-point gauss rule */
    1079                 [
    1080                     0.030753241996117268354628393577204, 0.070366047488108124709267416450667,
    1081                     0.107159220467171935011869546685869, 0.139570677926154314447804794511028,
    1082                     0.166269205816993933553200860481209, 0.186161000015562211026800561866423,
    1083                     0.198431485327111576456118326443839, 0.202578241925561272880620199967519
    1084                 ],
    1085             wgk =
    1086                 /* weights of the 21-point kronrod rule */
    1087                 [
    1088                     0.005377479872923348987792051430128, 0.015007947329316122538374763075807,
    1089                     0.025460847326715320186874001019653, 0.03534636079137584622203794847836,
    1090                     0.04458975132476487660822729937328, 0.05348152469092808726534314723943,
    1091                     0.062009567800670640285139230960803, 0.069854121318728258709520077099147,
    1092                     0.076849680757720378894432777482659, 0.083080502823133021038289247286104,
    1093                     0.088564443056211770647275443693774, 0.093126598170825321225486872747346,
    1094                     0.096642726983623678505179907627589, 0.099173598721791959332393173484603,
    1095                     0.10076984552387559504494666261757, 0.101330007014791549017374792767493
    1096                 ];
    1097
    1098         return this._gaussKronrod(interval, f, 16, xgk, wg, wgk, resultObj);
    1099     },
    1100
    1101     /**
    1102      * Generate workspace object for {@link JXG.Math.Numerics.Qag}.
    1103      * @param {Array} interval The integration interval, e.g. [0, 3].
    1104      * @param {Number} n Max. limit
    1105      * @returns {Object} Workspace object
    1106      *
    1107      * @private
    1108      * @memberof JXG.Math.Numerics
    1109      */
    1110     _workspace: function (interval, n) {
    1111         return {
    1112             limit: n,
    1113             size: 0,
    1114             nrmax: 0,
    1115             i: 0,
    1116             alist: [interval[0]],
    1117             blist: [interval[1]],
    1118             rlist: [0.0],
    1119             elist: [0.0],
    1120             order: [0],
    1121             level: [0],
    1122
    1123             qpsrt: function () {
    1124                 var last = this.size - 1,
    1125                     limit = this.limit,
    1126                     errmax,
    1127                     errmin,
    1128                     i,
    1129                     k,
    1130                     top,
    1131                     i_nrmax = this.nrmax,
    1132                     i_maxerr = this.order[i_nrmax];
    1133
    1134                 /* Check whether the list contains more than two error estimates */
    1135                 if (last < 2) {
    1136                     this.order[0] = 0;
    1137                     this.order[1] = 1;
    1138                     this.i = i_maxerr;
    1139                     return;
    1140                 }
    1141
    1142                 errmax = this.elist[i_maxerr];
    1143
    1144                 /* This part of the routine is only executed if, due to a difficult
    1145                         integrand, subdivision increased the error estimate. In the normal
    1146                         case the insert procedure should start after the nrmax-th largest
    1147                         error estimate. */
    1148                 while (i_nrmax > 0 && errmax > this.elist[this.order[i_nrmax - 1]]) {
    1149                     this.order[i_nrmax] = this.order[i_nrmax - 1];
    1150                     i_nrmax--;
    1151                 }
    1152
    1153                 /* Compute the number of elements in the list to be maintained in
    1154                         descending order. This number depends on the number of
    1155                         subdivisions still allowed. */
    1156                 if (last < limit / 2 + 2) {
    1157                     top = last;
    1158                 } else {
    1159                     top = limit - last + 1;
    1160                 }
    1161
    1162                 /* Insert errmax by traversing the list top-down, starting
    1163                         comparison from the element elist(order(i_nrmax+1)). */
    1164                 i = i_nrmax + 1;
    1165
    1166                 /* The order of the tests in the following line is important to
    1167                         prevent a segmentation fault */
    1168                 while (i < top && errmax < this.elist[this.order[i]]) {
    1169                     this.order[i - 1] = this.order[i];
    1170                     i++;
    1171                 }
    1172
    1173                 this.order[i - 1] = i_maxerr;
    1174
    1175                 /* Insert errmin by traversing the list bottom-up */
    1176                 errmin = this.elist[last];
    1177                 k = top - 1;
    1178
    1179                 while (k > i - 2 && errmin >= this.elist[this.order[k]]) {
    1180                     this.order[k + 1] = this.order[k];
    1181                     k--;
    1182                 }
    1183
    1184                 this.order[k + 1] = last;
    1185
    1186                 /* Set i_max and e_max */
    1187                 i_maxerr = this.order[i_nrmax];
    1188                 this.i = i_maxerr;
    1189                 this.nrmax = i_nrmax;
    1190             },
    1191
    1192             set_initial_result: function (result, error) {
    1193                 this.size = 1;
    1194                 this.rlist[0] = result;
    1195                 this.elist[0] = error;
    1196             },
    1197
    1198             update: function (a1, b1, area1, error1, a2, b2, area2, error2) {
    1199                 var i_max = this.i,
    1200                     i_new = this.size,
    1201                     new_level = this.level[this.i] + 1;
    1202
    1203                 /* append the newly-created intervals to the list */
    1204
    1205                 if (error2 > error1) {
    1206                     this.alist[i_max] = a2; /* blist[maxerr] is already == b2 */
    1207                     this.rlist[i_max] = area2;
    1208                     this.elist[i_max] = error2;
    1209                     this.level[i_max] = new_level;
    1210
    1211                     this.alist[i_new] = a1;
    1212                     this.blist[i_new] = b1;
    1213                     this.rlist[i_new] = area1;
    1214                     this.elist[i_new] = error1;
    1215                     this.level[i_new] = new_level;
    1216                 } else {
    1217                     this.blist[i_max] = b1; /* alist[maxerr] is already == a1 */
    1218                     this.rlist[i_max] = area1;
    1219                     this.elist[i_max] = error1;
    1220                     this.level[i_max] = new_level;
    1221
    1222                     this.alist[i_new] = a2;
    1223                     this.blist[i_new] = b2;
    1224                     this.rlist[i_new] = area2;
    1225                     this.elist[i_new] = error2;
    1226                     this.level[i_new] = new_level;
    1227                 }
    1228
    1229                 this.size++;
    1230
    1231                 if (new_level > this.maximum_level) {
    1232                     this.maximum_level = new_level;
    1233                 }
    1234
    1235                 this.qpsrt();
    1236             },
    1237
    1238             retrieve: function () {
    1239                 var i = this.i;
    1240                 return {
    1241                     a: this.alist[i],
    1242                     b: this.blist[i],
    1243                     r: this.rlist[i],
    1244                     e: this.elist[i]
    1245                 };
    1246             },
    1247
    1248             sum_results: function () {
    1249                 var nn = this.size,
    1250                     k,
    1251                     result_sum = 0.0;
    1252
    1253                 for (k = 0; k < nn; k++) {
    1254                     result_sum += this.rlist[k];
    1255                 }
    1256
    1257                 return result_sum;
    1258             },
    1259
    1260             subinterval_too_small: function (a1, a2, b2) {
    1261                 var e = 2.2204460492503131e-16,
    1262                     u = 2.2250738585072014e-308,
    1263                     tmp = (1 + 100 * e) * (Math.abs(a2) + 1000 * u);
    1264
    1265                 return Math.abs(a1) <= tmp && Math.abs(b2) <= tmp;
    1266             }
    1267         };
    1268     },
    1269
    1270     /**
    1271      * Quadrature algorithm qag from QUADPACK.
    1272      * Internal method used in {@link JXG.Math.Numerics.GaussKronrod15},
    1273      * {@link JXG.Math.Numerics.GaussKronrod21},
    1274      * {@link JXG.Math.Numerics.GaussKronrod31}.
    1275      *
    1276      * @param {Array} interval The integration interval, e.g. [0, 3].
    1277      * @param {function} f A function which takes one argument of type number and returns a number.
    1278      * @param {Object} [config] The algorithm setup. Accepted propert are max. recursion limit of type number,
    1279      * and epsrel and epsabs, the relative and absolute required precision of type number. Further,
    1280      * q the internal quadrature sub-algorithm of type function.
    1281      * @param {Number} [config.limit=15]
    1282      * @param {Number} [config.epsrel=0.0000001]
    1283      * @param {Number} [config.epsabs=0.0000001]
    1284      * @param {Number} [config.q=JXG.Math.Numerics.GaussKronrod15]
    1285      * @returns {Number} Integral value of f over interval
    1286      *
    1287      * @example
    1288      * function f(x) {
    1289      *   return x*x;
    1290      * }
    1291      *
    1292      * // calculates integral of <tt>f</tt> from 0 to 2.
    1293      * var area1 = JXG.Math.Numerics.Qag([0, 2], f);
    1294      *
    1295      * // the same with an anonymous function
    1296      * var area2 = JXG.Math.Numerics.Qag([0, 2], function (x) { return x*x; });
    1297      *
    1298      * // use JXG.Math.Numerics.GaussKronrod31 rule as sub-algorithm.
    1299      * var area3 = JXG.Math.Numerics.Quag([0, 2], f,
    1300      *                                   {q: JXG.Math.Numerics.GaussKronrod31});
    1301      * @memberof JXG.Math.Numerics
    1302      */
    1303     Qag: function (interval, f, config) {
    1304         var DBL_EPS = 2.2204460492503131e-16,
    1305             ws = this._workspace(interval, 1000),
    1306             limit = config && Type.isNumber(config.limit) ? config.limit : 15,
    1307             epsrel = config && Type.isNumber(config.epsrel) ? config.epsrel : 0.0000001,
    1308             epsabs = config && Type.isNumber(config.epsabs) ? config.epsabs : 0.0000001,
    1309             q = config && Type.isFunction(config.q) ? config.q : this.GaussKronrod15,
    1310             resultObj = {},
    1311             area,
    1312             errsum,
    1313             result0,
    1314             abserr0,
    1315             resabs0,
    1316             resasc0,
    1317             result,
    1318             tolerance,
    1319             iteration = 0,
    1320             roundoff_type1 = 0,
    1321             roundoff_type2 = 0,
    1322             error_type = 0,
    1323             round_off,
    1324             a1,
    1325             b1,
    1326             a2,
    1327             b2,
    1328             a_i,
    1329             b_i,
    1330             r_i,
    1331             e_i,
    1332             area1 = 0,
    1333             area2 = 0,
    1334             area12 = 0,
    1335             error1 = 0,
    1336             error2 = 0,
    1337             error12 = 0,
    1338             resasc1,
    1339             resasc2,
    1340             // resabs1, resabs2,
    1341             wsObj,
    1342             delta;
    1343
    1344         if (limit > ws.limit) {
    1345             JXG.warn("iteration limit exceeds available workspace");
    1346         }
    1347         if (epsabs <= 0 && (epsrel < 50 * Mat.eps || epsrel < 0.5e-28)) {
    1348             JXG.warn("tolerance cannot be acheived with given epsabs and epsrel");
    1349         }
    1350
    1351         result0 = q.apply(this, [interval, f, resultObj]);
    1352         abserr0 = resultObj.abserr;
    1353         resabs0 = resultObj.resabs;
    1354         resasc0 = resultObj.resasc;
    1355
    1356         ws.set_initial_result(result0, abserr0);
    1357         tolerance = Math.max(epsabs, epsrel * Math.abs(result0));
    1358         round_off = 50 * DBL_EPS * resabs0;
    1359
    1360         if (abserr0 <= round_off && abserr0 > tolerance) {
    1361             result = result0;
    1362             // abserr = abserr0;
    1363
    1364             JXG.warn("cannot reach tolerance because of roundoff error on first attempt");
    1365             return -Infinity;
    1366         }
    1367
    1368         if ((abserr0 <= tolerance && abserr0 !== resasc0) || abserr0 === 0.0) {
    1369             result = result0;
    1370             // abserr = abserr0;
    1371
    1372             return result;
    1373         }
    1374
    1375         if (limit === 1) {
    1376             result = result0;
    1377             // abserr = abserr0;
    1378
    1379             JXG.warn("a maximum of one iteration was insufficient");
    1380             return -Infinity;
    1381         }
    1382
    1383         area = result0;
    1384         errsum = abserr0;
    1385         iteration = 1;
    1386
    1387         do {
    1388             area1 = 0;
    1389             area2 = 0;
    1390             area12 = 0;
    1391             error1 = 0;
    1392             error2 = 0;
    1393             error12 = 0;
    1394
    1395             /* Bisect the subinterval with the largest error estimate */
    1396             wsObj = ws.retrieve();
    1397             a_i = wsObj.a;
    1398             b_i = wsObj.b;
    1399             r_i = wsObj.r;
    1400             e_i = wsObj.e;
    1401
    1402             a1 = a_i;
    1403             b1 = 0.5 * (a_i + b_i);
    1404             a2 = b1;
    1405             b2 = b_i;
    1406
    1407             area1 = q.apply(this, [[a1, b1], f, resultObj]);
    1408             error1 = resultObj.abserr;
    1409             // resabs1 = resultObj.resabs;
    1410             resasc1 = resultObj.resasc;
    1411
    1412             area2 = q.apply(this, [[a2, b2], f, resultObj]);
    1413             error2 = resultObj.abserr;
    1414             // resabs2 = resultObj.resabs;
    1415             resasc2 = resultObj.resasc;
    1416
    1417             area12 = area1 + area2;
    1418             error12 = error1 + error2;
    1419
    1420             errsum += error12 - e_i;
    1421             area += area12 - r_i;
    1422
    1423             if (resasc1 !== error1 && resasc2 !== error2) {
    1424                 delta = r_i - area12;
    1425                 if (Math.abs(delta) <= 1.0e-5 * Math.abs(area12) && error12 >= 0.99 * e_i) {
    1426                     roundoff_type1++;
    1427                 }
    1428                 if (iteration >= 10 && error12 > e_i) {
    1429                     roundoff_type2++;
    1430                 }
    1431             }
    1432
    1433             tolerance = Math.max(epsabs, epsrel * Math.abs(area));
    1434
    1435             if (errsum > tolerance) {
    1436                 if (roundoff_type1 >= 6 || roundoff_type2 >= 20) {
    1437                     error_type = 2; /* round off error */
    1438                 }
    1439
    1440                 /* set error flag in the case of bad integrand behaviour at
    1441                     a point of the integration range */
    1442
    1443                 if (ws.subinterval_too_small(a1, a2, b2)) {
    1444                     error_type = 3;
    1445                 }
    1446             }
    1447
    1448             ws.update(a1, b1, area1, error1, a2, b2, area2, error2);
    1449             wsObj = ws.retrieve();
    1450             a_i = wsObj.a_i;
    1451             b_i = wsObj.b_i;
    1452             r_i = wsObj.r_i;
    1453             e_i = wsObj.e_i;
    1454
    1455             iteration++;
    1456         } while (iteration < limit && !error_type && errsum > tolerance);
    1457
    1458         result = ws.sum_results();
    1459         // abserr = errsum;
    1460         /*
    1461   if (errsum <= tolerance)
    1462     {
    1463       return GSL_SUCCESS;
    1464     }
    1465   else if (error_type == 2)
    1466     {
    1467       GSL_ERROR ("roundoff error prevents tolerance from being achieved",
    1468                  GSL_EROUND);
    1469     }
    1470   else if (error_type == 3)
    1471     {
    1472       GSL_ERROR ("bad integrand behavior found in the integration interval",
    1473                  GSL_ESING);
    1474     }
    1475   else if (iteration == limit)
    1476     {
    1477       GSL_ERROR ("maximum number of subdivisions reached", GSL_EMAXITER);
    1478     }
    1479   else
    1480     {
    1481       GSL_ERROR ("could not integrate function", GSL_EFAILED);
    1482     }
    1483 */
    1484
    1485         return result;
    1486     },
    1487
    1488     /**
    1489      * Integral of function f over interval.
    1490      * @param {Array} interval The integration interval, e.g. [0, 3].
    1491      * @param {function} f A function which takes one argument of type number and returns a number.
    1492      * @returns {Number} The value of the integral of f over interval
    1493      * @see JXG.Math.Numerics.NewtonCotes
    1494      * @see JXG.Math.Numerics.Romberg
    1495      * @see JXG.Math.Numerics.Qag
    1496      * @memberof JXG.Math.Numerics
    1497      */
    1498     I: function (interval, f) {
    1499         // return this.NewtonCotes(interval, f, {number_of_nodes: 16, integration_type: 'milne'});
    1500         // return this.Romberg(interval, f, {max_iterations: 20, eps: 0.0000001});
    1501         return this.Qag(interval, f, {
    1502             q: this.GaussKronrod15,
    1503             limit: 15,
    1504             epsrel: 0.0000001,
    1505             epsabs: 0.0000001
    1506         });
    1507     },
    1508
    1509     /**
    1510      * Newton's method to find roots of a funtion in one variable.
    1511      * @param {function} f We search for a solution of f(x)=0.
    1512      * @param {Number} x initial guess for the root, i.e. start value.
    1513      * @param {Object} context optional object that is treated as "this" in the function body. This is useful if
    1514      * the function is a method of an object and contains a reference to its parent object via "this".
    1515      * @returns {Number} A root of the function f.
    1516      * @memberof JXG.Math.Numerics
    1517      */
    1518     Newton: function (f, x, context) {
    1519         var df,
    1520             i = 0,
    1521             h = Mat.eps,
    1522             newf = f.apply(context, [x]);
    1523         // nfev = 1;
    1524
    1525         // For compatibility
    1526         if (Type.isArray(x)) {
    1527             x = x[0];
    1528         }
    1529
    1530         while (i < 50 && Math.abs(newf) > h) {
    1531             df = this.D(f, context)(x);
    1532             // nfev += 2;
    1533
    1534             if (Math.abs(df) > h) {
    1535                 x -= newf / df;
    1536             } else {
    1537                 x += Math.random() * 0.2 - 1.0;
    1538             }
    1539
    1540             newf = f.apply(context, [x]);
    1541             // nfev += 1;
    1542             i += 1;
    1543         }
    1544
    1545         return x;
    1546     },
    1547
    1548     /**
    1549      * Abstract method to find roots of univariate functions, which - for the time being -
    1550      * is an alias for {@link JXG.Math.Numerics.chandrupatla}.
    1551      * @param {function} f We search for a solution of f(x)=0.
    1552      * @param {Number|Array} x initial guess for the root, i.e. starting value, or start interval enclosing the root.
    1553      * If x is an interval [a,b], it is required that f(a)f(b) <= 0, otherwise the minimum of f in [a, b] will be returned.
    1554      * If x is a number, the algorithms tries to enclose the root by an interval [a, b] containing x and the root and
    1555      * f(a)f(b) <= 0. If this fails, the algorithm falls back to Newton's method.
    1556      *
    1557      * @param {Object} context optional object that is treated as "this" in the function body. This is useful if
    1558      * the function is a method of an object and contains a reference to its parent object via "this".
    1559      * @returns {Number} A root of the function f.
    1560      *
    1561      * @see JXG.Math.Numerics.chandrupatla
    1562      * @see JXG.Math.Numerics.fzero
    1563      * @see JXG.Math.Numerics.polzeros
    1564      * @see JXG.Math.Numerics.Newton
    1565      * @memberof JXG.Math.Numerics
    1566      */
    1567     root: function (f, x, context) {
    1568         //return this.fzero(f, x, context);
    1569         return this.chandrupatla(f, x, context);
    1570     },
    1571
    1572     /**
    1573      * Compute an intersection of the curves c1 and c2
    1574      * with a generalized Newton method.
    1575      * We want to find values t1, t2 such that
    1576      * c1(t1) = c2(t2), i.e.
    1577      * <br>
    1578      * (c1_x(t1)-c2_x(t2),c1_y(t1)-c2_y(t2)) = (0,0).
    1579      * <p>
    1580      * We set
    1581      * (e,f) := (c1_x(t1)-c2_x(t2),c1_y(t1)-c2_y(t2))
    1582      * <p>
    1583      * The Jacobian J is defined by
    1584      * <pre>
    1585      * J = (a, b)
    1586      *     (c, d)
    1587      * </pre>
    1588      * where
    1589      * <ul>
    1590      * <li> a = c1_x'(t1)
    1591      * <li> b = -c2_x'(t2)
    1592      * <li> c = c1_y'(t1)
    1593      * <li> d = -c2_y'(t2)
    1594      * </ul>
    1595      * The inverse J^(-1) of J is equal to
    1596      * <pre>
    1597      *  (d, -b)/ (ad-bc)
    1598      *  (-c, a) / (ad-bc)
    1599      * </pre>
    1600      *
    1601      * Then, (t1new, t2new) := (t1,t2) - J^(-1)*(e,f).
    1602      * <p>
    1603      * If the function meetCurveCurve has the properties
    1604      * t1memo and t2memo then these are taken as start values
    1605      * for the Newton algorithm.
    1606      * After stopping of the Newton algorithm the values of t1 and t2 are stored in
    1607      * t1memo and t2memo.
    1608      *
    1609      * @param {JXG.Curve} c1 Curve, Line or Circle
    1610      * @param {JXG.Curve} c2 Curve, Line or Circle
    1611      * @param {Number} t1ini start value for t1
    1612      * @param {Number} t2ini start value for t2
    1613      * @returns {JXG.Coords} intersection point
    1614      * @memberof JXG.Math.Numerics
    1615      */
    1616     generalizedNewton: function (c1, c2, t1ini, t2ini) {
    1617         var t1, t2,
    1618             a, b, c, d, e, f,
    1619             disc,
    1620             F,
    1621             D00, D01, D10, D11,
    1622             count = 0;
    1623
    1624         if (this.generalizedNewton.t1memo) {
    1625             t1 = this.generalizedNewton.t1memo;
    1626             t2 = this.generalizedNewton.t2memo;
    1627         } else {
    1628             t1 = t1ini;
    1629             t2 = t2ini;
    1630         }
    1631
    1632         e = c1.X(t1) - c2.X(t2);
    1633         f = c1.Y(t1) - c2.Y(t2);
    1634         F = e * e + f * f;
    1635
    1636         D00 = this.D(c1.X, c1);
    1637         D01 = this.D(c2.X, c2);
    1638         D10 = this.D(c1.Y, c1);
    1639         D11 = this.D(c2.Y, c2);
    1640
    1641         while (F > Mat.eps && count < 10) {
    1642             a = D00(t1);
    1643             b = -D01(t2);
    1644             c = D10(t1);
    1645             d = -D11(t2);
    1646             disc = a * d - b * c;
    1647             t1 -= (d * e - b * f) / disc;
    1648             t2 -= (a * f - c * e) / disc;
    1649             e = c1.X(t1) - c2.X(t2);
    1650             f = c1.Y(t1) - c2.Y(t2);
    1651             F = e * e + f * f;
    1652             count += 1;
    1653         }
    1654
    1655         this.generalizedNewton.t1memo = t1;
    1656         this.generalizedNewton.t2memo = t2;
    1657
    1658         if (Math.abs(t1) < Math.abs(t2)) {
    1659             return [c1.X(t1), c1.Y(t1)];
    1660         }
    1661
    1662         return [c2.X(t2), c2.Y(t2)];
    1663     },
    1664
    1665     /**
    1666      * Returns the Lagrange polynomials for curves with equidistant nodes, see
    1667      * Jean-Paul Berrut, Lloyd N. Trefethen: Barycentric Lagrange Interpolation,
    1668      * SIAM Review, Vol 46, No 3, (2004) 501-517.
    1669      * The graph of the parametric curve [x(t),y(t)] runs through the given points.
    1670      * @param {Array} p Array of JXG.Points
    1671      * @returns {Array} An array consisting of two functions x(t), y(t) which define a parametric curve
    1672      * f(t) = (x(t), y(t)), a number x1 (which equals 0) and a function x2 defining the curve's domain.
    1673      * That means the curve is defined between x1 and x2(). x2 returns the (length of array p minus one).
    1674      * @memberof JXG.Math.Numerics
    1675      *
    1676      * @example
    1677      * var p = [];
    1678      *
    1679      * p[0] = board.create('point', [0, -2], {size:2, name: 'C(a)'});
    1680      * p[1] = board.create('point', [-1.5, 5], {size:2, name: ''});
    1681      * p[2] = board.create('point', [1, 4], {size:2, name: ''});
    1682      * p[3] = board.create('point', [3, 3], {size:2, name: 'C(b)'});
    1683      *
    1684      * // Curve
    1685      * var fg = JXG.Math.Numerics.Neville(p);
    1686      * var graph = board.create('curve', fg, {strokeWidth:3, strokeOpacity:0.5});
    1687      *
    1688      * </pre><div id="JXG88a8b3a8-6561-44f5-a678-76bca13fd484" class="jxgbox" style="width: 300px; height: 300px;"></div>
    1689      * <script type="text/javascript">
    1690      *     (function() {
    1691      *         var board = JXG.JSXGraph.initBoard('JXG88a8b3a8-6561-44f5-a678-76bca13fd484',
    1692      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1693      *     var p = [];
    1694      *
    1695      *     p[0] = board.create('point', [0, -2], {size:2, name: 'C(a)'});
    1696      *     p[1] = board.create('point', [-1.5, 5], {size:2, name: ''});
    1697      *     p[2] = board.create('point', [1, 4], {size:2, name: ''});
    1698      *     p[3] = board.create('point', [3, 3], {size:2, name: 'C(b)'});
    1699      *
    1700      *     // Curve
    1701      *     var fg = JXG.Math.Numerics.Neville(p);
    1702      *     var graph = board.create('curve', fg, {strokeWidth:3, strokeOpacity:0.5});
    1703      *
    1704      *     })();
    1705      *
    1706      * </script><pre>
    1707      *
    1708      */
    1709     Neville: function (p) {
    1710         var w = [],
    1711             /** @ignore */
    1712             makeFct = function (fun) {
    1713                 return function (t, suspendedUpdate) {
    1714                     var i,
    1715                         d,
    1716                         s,
    1717                         bin = Mat.binomial,
    1718                         len = p.length,
    1719                         len1 = len - 1,
    1720                         num = 0.0,
    1721                         denom = 0.0;
    1722
    1723                     if (!suspendedUpdate) {
    1724                         s = 1;
    1725                         for (i = 0; i < len; i++) {
    1726                             w[i] = bin(len1, i) * s;
    1727                             s *= -1;
    1728                         }
    1729                     }
    1730
    1731                     d = t;
    1732
    1733                     for (i = 0; i < len; i++) {
    1734                         if (d === 0) {
    1735                             return p[i][fun]();
    1736                         }
    1737                         s = w[i] / d;
    1738                         d -= 1;
    1739                         num += p[i][fun]() * s;
    1740                         denom += s;
    1741                     }
    1742                     return num / denom;
    1743                 };
    1744             },
    1745             xfct = makeFct("X"),
    1746             yfct = makeFct("Y");
    1747
    1748         return [
    1749             xfct,
    1750             yfct,
    1751             0,
    1752             function () {
    1753                 return p.length - 1;
    1754             }
    1755         ];
    1756     },
    1757
    1758     /**
    1759      * Calculates second derivatives at the given knots.
    1760      * @param {Array} x x values of knots
    1761      * @param {Array} y y values of knots
    1762      * @returns {Array} Second derivatives of the interpolated function at the knots.
    1763      * @see JXG.Math.Numerics.splineEval
    1764      * @memberof JXG.Math.Numerics
    1765      */
    1766     splineDef: function (x, y) {
    1767         var pair,
    1768             i,
    1769             l,
    1770             n = Math.min(x.length, y.length),
    1771             diag = [],
    1772             z = [],
    1773             data = [],
    1774             dx = [],
    1775             delta = [],
    1776             F = [];
    1777
    1778         if (n === 2) {
    1779             return [0, 0];
    1780         }
    1781
    1782         for (i = 0; i < n; i++) {
    1783             pair = { X: x[i], Y: y[i] };
    1784             data.push(pair);
    1785         }
    1786         data.sort(function (a, b) {
    1787             return a.X - b.X;
    1788         });
    1789         for (i = 0; i < n; i++) {
    1790             x[i] = data[i].X;
    1791             y[i] = data[i].Y;
    1792         }
    1793
    1794         for (i = 0; i < n - 1; i++) {
    1795             dx.push(x[i + 1] - x[i]);
    1796         }
    1797         for (i = 0; i < n - 2; i++) {
    1798             delta.push(
    1799                 (6 * (y[i + 2] - y[i + 1])) / dx[i + 1] - (6 * (y[i + 1] - y[i])) / dx[i]
    1800             );
    1801         }
    1802
    1803         // ForwardSolve
    1804         diag.push(2 * (dx[0] + dx[1]));
    1805         z.push(delta[0]);
    1806
    1807         for (i = 0; i < n - 3; i++) {
    1808             l = dx[i + 1] / diag[i];
    1809             diag.push(2 * (dx[i + 1] + dx[i + 2]) - l * dx[i + 1]);
    1810             z.push(delta[i + 1] - l * z[i]);
    1811         }
    1812
    1813         // BackwardSolve
    1814         F[n - 3] = z[n - 3] / diag[n - 3];
    1815         for (i = n - 4; i >= 0; i--) {
    1816             F[i] = (z[i] - dx[i + 1] * F[i + 1]) / diag[i];
    1817         }
    1818
    1819         // Generate f''-Vector
    1820         for (i = n - 3; i >= 0; i--) {
    1821             F[i + 1] = F[i];
    1822         }
    1823
    1824         // natural cubic spline
    1825         F[0] = 0;
    1826         F[n - 1] = 0;
    1827
    1828         return F;
    1829     },
    1830
    1831     /**
    1832      * Evaluate points on spline.
    1833      * @param {Number|Array} x0 A single float value or an array of values to evaluate
    1834      * @param {Array} x x values of knots
    1835      * @param {Array} y y values of knots
    1836      * @param {Array} F Second derivatives at knots, calculated by {@link JXG.Math.Numerics.splineDef}
    1837      * @see JXG.Math.Numerics.splineDef
    1838      * @returns {Number|Array} A single value or an array, depending on what is given as x0.
    1839      * @memberof JXG.Math.Numerics
    1840      */
    1841     splineEval: function (x0, x, y, F) {
    1842         var i,
    1843             j,
    1844             a,
    1845             b,
    1846             c,
    1847             d,
    1848             x_,
    1849             n = Math.min(x.length, y.length),
    1850             l = 1,
    1851             asArray = false,
    1852             y0 = [];
    1853
    1854         // number of points to be evaluated
    1855         if (Type.isArray(x0)) {
    1856             l = x0.length;
    1857             asArray = true;
    1858         } else {
    1859             x0 = [x0];
    1860         }
    1861
    1862         for (i = 0; i < l; i++) {
    1863             // is x0 in defining interval?
    1864             if (x0[i] < x[0] || x[i] > x[n - 1]) {
    1865                 return NaN;
    1866             }
    1867
    1868             // determine part of spline in which x0 lies
    1869             for (j = 1; j < n; j++) {
    1870                 if (x0[i] <= x[j]) {
    1871                     break;
    1872                 }
    1873             }
    1874
    1875             j -= 1;
    1876
    1877             // we're now in the j-th partial interval, i.e. x[j] < x0[i] <= x[j+1];
    1878             // determine the coefficients of the polynomial in this interval
    1879             a = y[j];
    1880             b =
    1881                 (y[j + 1] - y[j]) / (x[j + 1] - x[j]) -
    1882                 ((x[j + 1] - x[j]) / 6) * (F[j + 1] + 2 * F[j]);
    1883             c = F[j] / 2;
    1884             d = (F[j + 1] - F[j]) / (6 * (x[j + 1] - x[j]));
    1885             // evaluate x0[i]
    1886             x_ = x0[i] - x[j];
    1887             //y0.push(a + b*x_ + c*x_*x_ + d*x_*x_*x_);
    1888             y0.push(a + (b + (c + d * x_) * x_) * x_);
    1889         }
    1890
    1891         if (asArray) {
    1892             return y0;
    1893         }
    1894
    1895         return y0[0];
    1896     },
    1897
    1898     /**
    1899      * Generate a string containing the function term of a polynomial.
    1900      * @param {Array} coeffs Coefficients of the polynomial. The position i belongs to x^i.
    1901      * @param {Number} deg Degree of the polynomial
    1902      * @param {String} varname Name of the variable (usually 'x')
    1903      * @param {Number} prec Precision
    1904      * @returns {String} A string containing the function term of the polynomial.
    1905      * @memberof JXG.Math.Numerics
    1906      */
    1907     generatePolynomialTerm: function (coeffs, deg, varname, prec) {
    1908         var i,
    1909             t = [];
    1910
    1911         for (i = deg; i >= 0; i--) {
    1912             Type.concat(t, ["(", coeffs[i].toPrecision(prec), ")"]);
    1913
    1914             if (i > 1) {
    1915                 Type.concat(t, ["*", varname, "<sup>", i, "<", "/sup> + "]);
    1916             } else if (i === 1) {
    1917                 Type.concat(t, ["*", varname, " + "]);
    1918             }
    1919         }
    1920
    1921         return t.join("");
    1922     },
    1923
    1924     /**
    1925      * Computes the polynomial through a given set of coordinates in Lagrange form.
    1926      * Returns the Lagrange polynomials, see
    1927      * Jean-Paul Berrut, Lloyd N. Trefethen: Barycentric Lagrange Interpolation,
    1928      * SIAM Review, Vol 46, No 3, (2004) 501-517.
    1929      * <p>
    1930      * It possesses the method getTerm() which returns the string containing the function term of the polynomial and
    1931      * the method getCoefficients() which returns an array containing the coefficients of the polynomial.
    1932      * @param {Array} p Array of JXG.Points
    1933      * @returns {function} A function of one parameter which returns the value of the polynomial, whose graph runs through the given points.
    1934      * @memberof JXG.Math.Numerics
    1935      *
    1936      * @example
    1937      * var p = [];
    1938      * p[0] = board.create('point', [-1,2], {size:4});
    1939      * p[1] = board.create('point', [0,3], {size:4});
    1940      * p[2] = board.create('point', [1,1], {size:4});
    1941      * p[3] = board.create('point', [3,-1], {size:4});
    1942      * var f = JXG.Math.Numerics.lagrangePolynomial(p);
    1943      * var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    1944      *
    1945      * </pre><div id="JXGc058aa6b-74d4-41e1-af94-df06169a2d89" class="jxgbox" style="width: 300px; height: 300px;"></div>
    1946      * <script type="text/javascript">
    1947      *     (function() {
    1948      *         var board = JXG.JSXGraph.initBoard('JXGc058aa6b-74d4-41e1-af94-df06169a2d89',
    1949      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1950      *     var p = [];
    1951      *     p[0] = board.create('point', [-1,2], {size:4});
    1952      *     p[1] = board.create('point', [0,3], {size:4});
    1953      *     p[2] = board.create('point', [1,1], {size:4});
    1954      *     p[3] = board.create('point', [3,-1], {size:4});
    1955      *     var f = JXG.Math.Numerics.lagrangePolynomial(p);
    1956      *     var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    1957      *
    1958      *     })();
    1959      *
    1960      * </script><pre>
    1961      *
    1962      * @example
    1963      * var points = [];
    1964      * points[0] = board.create('point', [-1,2], {size:4});
    1965      * points[1] = board.create('point', [0, 0], {size:4});
    1966      * points[2] = board.create('point', [2, 1], {size:4});
    1967      *
    1968      * var f = JXG.Math.Numerics.lagrangePolynomial(points);
    1969      * var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    1970      * var txt = board.create('text', [-3, -4,  () => f.getTerm(2, 't', ' * ')], {fontSize: 16});
    1971      * var txt2 = board.create('text', [-3, -6,  () => f.getCoefficients()], {fontSize: 12});
    1972      *
    1973      * </pre><div id="JXG73fdaf12-e257-4374-b488-ae063e4eecbb" class="jxgbox" style="width: 300px; height: 300px;"></div>
    1974      * <script type="text/javascript">
    1975      *     (function() {
    1976      *         var board = JXG.JSXGraph.initBoard('JXG73fdaf12-e257-4374-b488-ae063e4eecbb',
    1977      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1978      *     var points = [];
    1979      *     points[0] = board.create('point', [-1,2], {size:4});
    1980      *     points[1] = board.create('point', [0, 0], {size:4});
    1981      *     points[2] = board.create('point', [2, 1], {size:4});
    1982      *
    1983      *     var f = JXG.Math.Numerics.lagrangePolynomial(points);
    1984      *     var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    1985      *     var txt = board.create('text', [-3, -4,  () => f.getTerm(2, 't', ' * ')], {fontSize: 16});
    1986      *     var txt2 = board.create('text', [-3, -6,  () => f.getCoefficients()], {fontSize: 12});
    1987      *
    1988      *     })();
    1989      *
    1990      * </script><pre>
    1991      *
    1992      */
    1993     lagrangePolynomial: function (p) {
    1994         var w = [],
    1995             that = this,
    1996             /** @ignore */
    1997             fct = function (x, suspendedUpdate) {
    1998                 var i, // j,
    1999                     k,
    2000                     xi,
    2001                     s, //M,
    2002                     len = p.length,
    2003                     num = 0,
    2004                     denom = 0;
    2005
    2006                 if (!suspendedUpdate) {
    2007                     for (i = 0; i < len; i++) {
    2008                         w[i] = 1.0;
    2009                         xi = p[i].X();
    2010
    2011                         for (k = 0; k < len; k++) {
    2012                             if (k !== i) {
    2013                                 w[i] *= xi - p[k].X();
    2014                             }
    2015                         }
    2016
    2017                         w[i] = 1 / w[i];
    2018                     }
    2019
    2020                     // M = [];
    2021                     // for (k = 0; k < len; k++) {
    2022                     //     M.push([1]);
    2023                     // }
    2024                 }
    2025
    2026                 for (i = 0; i < len; i++) {
    2027                     xi = p[i].X();
    2028
    2029                     if (x === xi) {
    2030                         return p[i].Y();
    2031                     }
    2032
    2033                     s = w[i] / (x - xi);
    2034                     denom += s;
    2035                     num += s * p[i].Y();
    2036                 }
    2037
    2038                 return num / denom;
    2039             };
    2040
    2041         /**
    2042          * Get the term of the Lagrange polynomial as string.
    2043          * Calls {@link JXG.Math.Numerics#lagrangePolynomialTerm}.
    2044          *
    2045          * @name JXG.Math.Numerics.lagrangePolynomial#getTerm
    2046          * @param {Number} digits Number of digits of the coefficients
    2047          * @param {String} param Variable name
    2048          * @param {String} dot Dot symbol
    2049          * @returns {String} containing the term of Lagrange polynomial as string.
    2050          * @see JXG.Math.Numerics.lagrangePolynomialTerm
    2051          * @example
    2052          * var points = [];
    2053          * points[0] = board.create('point', [-1,2], {size:4});
    2054          * points[1] = board.create('point', [0, 0], {size:4});
    2055          * points[2] = board.create('point', [2, 1], {size:4});
    2056          *
    2057          * var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2058          * var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2059          * var txt = board.create('text', [-3, -4,  () => f.getTerm(2, 't', ' * ')], {fontSize: 16});
    2060          *
    2061          * </pre><div id="JXG73fdaf12-e257-4374-b488-ae063e4eeccf" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2062          * <script type="text/javascript">
    2063          *     (function() {
    2064          *         var board = JXG.JSXGraph.initBoard('JXG73fdaf12-e257-4374-b488-ae063e4eeccf',
    2065          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2066          *     var points = [];
    2067          *     points[0] = board.create('point', [-1,2], {size:4});
    2068          *     points[1] = board.create('point', [0, 0], {size:4});
    2069          *     points[2] = board.create('point', [2, 1], {size:4});
    2070          *
    2071          *     var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2072          *     var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2073          *     var txt = board.create('text', [-3, -4,  () => f.getTerm(2, 't', ' * ')], {fontSize: 16});
    2074          *
    2075          *     })();
    2076          *
    2077          * </script><pre>
    2078          *
    2079          */
    2080         fct.getTerm = function (digits, param, dot) {
    2081             return that.lagrangePolynomialTerm(p, digits, param, dot)();
    2082         };
    2083
    2084         /**
    2085          * Get the coefficients of the Lagrange polynomial as array. The leading
    2086          * coefficient is at position 0.
    2087          * Calls {@link JXG.Math.Numerics#lagrangePolynomialCoefficients}.
    2088          *
    2089          * @name JXG.Math.Numerics.lagrangePolynomial#getCoefficients
    2090          * @returns {Array} containing the coefficients of the Lagrange polynomial.
    2091          * @see JXG.Math.Numerics.lagrangePolynomial.getTerm
    2092          * @see JXG.Math.Numerics.lagrangePolynomialTerm
    2093          * @see JXG.Math.Numerics.lagrangePolynomialCoefficients
    2094          * @example
    2095          * var points = [];
    2096          * points[0] = board.create('point', [-1,2], {size:4});
    2097          * points[1] = board.create('point', [0, 0], {size:4});
    2098          * points[2] = board.create('point', [2, 1], {size:4});
    2099          *
    2100          * var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2101          * var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2102          * var txt = board.create('text', [1, -4,  () => f.getCoefficients()], {fontSize: 10});
    2103          *
    2104          * </pre><div id="JXG52a883a5-2e0c-4caf-8f84-8650c173c365" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2105          * <script type="text/javascript">
    2106          *     (function() {
    2107          *         var board = JXG.JSXGraph.initBoard('JXG52a883a5-2e0c-4caf-8f84-8650c173c365',
    2108          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2109          *     var points = [];
    2110          *     points[0] = board.create('point', [-1,2], {size:4});
    2111          *     points[1] = board.create('point', [0, 0], {size:4});
    2112          *     points[2] = board.create('point', [2, 1], {size:4});
    2113          *
    2114          *     var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2115          *     var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2116          *     var txt = board.create('text', [1, -4,  () => f.getCoefficients()], {fontSize: 10});
    2117          *
    2118          *     })();
    2119          *
    2120          * </script><pre>
    2121          *
    2122          */
    2123         fct.getCoefficients = function () {
    2124             return that.lagrangePolynomialCoefficients(p)();
    2125         };
    2126
    2127         return fct;
    2128     },
    2129
    2130     /**
    2131      * Determine the Lagrange polynomial through an array of points and
    2132      * return the term of the polynomial as string.
    2133      *
    2134      * @param {Array} points Array of JXG.Points
    2135      * @param {Number} digits Number of decimal digits of the coefficients
    2136      * @param {String} param Name of the parameter. Default: 'x'.
    2137      * @param {String} dot Multiplication symbol. Default: ' * '.
    2138      * @returns {Function} returning the Lagrange polynomial term through
    2139      *    the supplied points as string
    2140      * @memberof JXG.Math.Numerics
    2141      *
    2142      * @example
    2143      * var points = [];
    2144      * points[0] = board.create('point', [-1,2], {size:4});
    2145      * points[1] = board.create('point', [0, 0], {size:4});
    2146      * points[2] = board.create('point', [2, 1], {size:4});
    2147      *
    2148      * var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2149      * var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2150      *
    2151      * var f_txt = JXG.Math.Numerics.lagrangePolynomialTerm(points, 2, 't', ' * ');
    2152      * var txt = board.create('text', [-3, -4, f_txt], {fontSize: 16});
    2153      *
    2154      * </pre><div id="JXGd45e9e96-7526-486d-aa43-e1178d5f2baa" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2155      * <script type="text/javascript">
    2156      *     (function() {
    2157      *         var board = JXG.JSXGraph.initBoard('JXGd45e9e96-7526-486d-aa43-e1178d5f2baa',
    2158      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2159      *     var points = [];
    2160      *     points[0] = board.create('point', [-1,2], {size:4});
    2161      *     points[1] = board.create('point', [0, 0], {size:4});
    2162      *     points[2] = board.create('point', [2, 1], {size:4});
    2163      *
    2164      *     var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2165      *     var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2166      *
    2167      *     var f_txt = JXG.Math.Numerics.lagrangePolynomialTerm(points, 2, 't', ' * ');
    2168      *     var txt = board.create('text', [-3, -4, f_txt], {fontSize: 16});
    2169      *
    2170      *     })();
    2171      *
    2172      * </script><pre>
    2173      *
    2174      */
    2175     lagrangePolynomialTerm: function (points, digits, param, dot) {
    2176         var that = this;
    2177
    2178         return function () {
    2179             var len = points.length,
    2180                 coeffs = [],
    2181                 isLeading = true,
    2182                 n, t, j, c;
    2183
    2184             param = param || "x";
    2185             if (dot === undefined) {
    2186                 dot = " * ";
    2187             }
    2188
    2189             n = len - 1; // (Max) degree of the polynomial
    2190             coeffs = that.lagrangePolynomialCoefficients(points)();
    2191
    2192             t = "";
    2193             for (j = 0; j < coeffs.length; j++) {
    2194                 c = coeffs[j];
    2195                 if (Math.abs(c) < Mat.eps) {
    2196                     continue;
    2197                 }
    2198                 if (JXG.exists(digits)) {
    2199                     c = Env._round10(c, -digits);
    2200                 }
    2201                 if (isLeading) {
    2202                     t += c > 0 ? c : "-" + -c;
    2203                     isLeading = false;
    2204                 } else {
    2205                     t += c > 0 ? " + " + c : " - " + -c;
    2206                 }
    2207
    2208                 if (n - j > 1) {
    2209                     t += dot + param + "^" + (n - j);
    2210                 } else if (n - j === 1) {
    2211                     t += dot + param;
    2212                 }
    2213             }
    2214             return t; // board.jc.manipulate('f = map(x) -> ' + t + ';');
    2215         };
    2216     },
    2217
    2218     /**
    2219      * Determine the Lagrange polynomial through an array of points and
    2220      * return the coefficients of the polynomial as array.
    2221      * The leading coefficient is at position 0.
    2222      *
    2223      * @param {Array} points Array of JXG.Points
    2224      * @returns {Function} returning the coefficients of the Lagrange polynomial through
    2225      *    the supplied points.
    2226      * @memberof JXG.Math.Numerics
    2227      *
    2228      * @example
    2229      * var points = [];
    2230      * points[0] = board.create('point', [-1,2], {size:4});
    2231      * points[1] = board.create('point', [0, 0], {size:4});
    2232      * points[2] = board.create('point', [2, 1], {size:4});
    2233      *
    2234      * var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2235      * var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2236      *
    2237      * var f_arr = JXG.Math.Numerics.lagrangePolynomialCoefficients(points);
    2238      * var txt = board.create('text', [1, -4, f_arr], {fontSize: 10});
    2239      *
    2240      * </pre><div id="JXG1778f0d1-a420-473f-99e8-1755ef4be97e" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2241      * <script type="text/javascript">
    2242      *     (function() {
    2243      *         var board = JXG.JSXGraph.initBoard('JXG1778f0d1-a420-473f-99e8-1755ef4be97e',
    2244      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2245      *     var points = [];
    2246      *     points[0] = board.create('point', [-1,2], {size:4});
    2247      *     points[1] = board.create('point', [0, 0], {size:4});
    2248      *     points[2] = board.create('point', [2, 1], {size:4});
    2249      *
    2250      *     var f = JXG.Math.Numerics.lagrangePolynomial(points);
    2251      *     var graph = board.create('functiongraph', [f,-10, 10], {strokeWidth:3});
    2252      *
    2253      *     var f_arr = JXG.Math.Numerics.lagrangePolynomialCoefficients(points);
    2254      *     var txt = board.create('text', [1, -4, f_arr], {fontSize: 10});
    2255      *
    2256      *     })();
    2257      *
    2258      * </script><pre>
    2259      *
    2260      */
    2261     lagrangePolynomialCoefficients: function (points) {
    2262         return function () {
    2263             var len = points.length,
    2264                 zeroes = [],
    2265                 coeffs = [],
    2266                 coeffs_sum = [],
    2267                 i, j, c, p;
    2268
    2269             // n = len - 1; // (Max) degree of the polynomial
    2270             for (j = 0; j < len; j++) {
    2271                 coeffs_sum[j] = 0;
    2272             }
    2273
    2274             for (i = 0; i < len; i++) {
    2275                 c = points[i].Y();
    2276                 p = points[i].X();
    2277                 zeroes = [];
    2278                 for (j = 0; j < len; j++) {
    2279                     if (j !== i) {
    2280                         c /= p - points[j].X();
    2281                         zeroes.push(points[j].X());
    2282                     }
    2283                 }
    2284                 coeffs = [1].concat(Mat.Vieta(zeroes));
    2285                 for (j = 0; j < coeffs.length; j++) {
    2286                     coeffs_sum[j] += (j % 2 === 1 ? -1 : 1) * coeffs[j] * c;
    2287                 }
    2288             }
    2289
    2290             return coeffs_sum;
    2291         };
    2292     },
    2293
    2294     /**
    2295      * Determine the coefficients of a cardinal spline polynom, See
    2296      * https://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections
    2297      * @param  {Number} x1 point 1
    2298      * @param  {Number} x2 point 2
    2299      * @param  {Number} t1 tangent slope 1
    2300      * @param  {Number} t2 tangent slope 2
    2301      * @return {Array}    coefficents array c for the polynomial t maps to
    2302      * c[0] + c[1]*t + c[2]*t*t + c[3]*t*t*t
    2303      */
    2304     _initCubicPoly: function (x1, x2, t1, t2) {
    2305         return [x1, t1, -3 * x1 + 3 * x2 - 2 * t1 - t2, 2 * x1 - 2 * x2 + t1 + t2];
    2306     },
    2307
    2308     /**
    2309      * Computes the cubic cardinal spline curve through a given set of points. The curve
    2310      * is uniformly parametrized.
    2311      * Two artificial control points at the beginning and the end are added.
    2312      *
    2313      * The implementation (especially the centripetal parametrization) is from
    2314      * https://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections .
    2315      * @param {Array} points Array consisting of JXG.Points.
    2316      * @param {Number|Function} tau The tension parameter, either a constant number or a function returning a number. This number is between 0 and 1.
    2317      * tau=1/2 give Catmull-Rom splines.
    2318      * @param {String} type (Optional) parameter which allows to choose between "uniform" (default) and
    2319      * "centripetal" parameterization. Thus the two possible values are "uniform" or "centripetal".
    2320      * @returns {Array} An Array consisting of four components: Two functions each of one parameter t
    2321      * which return the x resp. y coordinates of the Catmull-Rom-spline curve in t, a zero value,
    2322      * and a function simply returning the length of the points array
    2323      * minus three.
    2324      * @memberof JXG.Math.Numerics
    2325      */
    2326     CardinalSpline: function (points, tau_param, type) {
    2327         var p,
    2328             coeffs = [],
    2329             makeFct,
    2330             tau, _tau,
    2331             that = this;
    2332
    2333         if (Type.isFunction(tau_param)) {
    2334             _tau = tau_param;
    2335         } else {
    2336             _tau = function () {
    2337                 return tau_param;
    2338             };
    2339         }
    2340
    2341         if (type === undefined) {
    2342             type = "uniform";
    2343         }
    2344
    2345         /** @ignore */
    2346         makeFct = function (which) {
    2347             return function (t, suspendedUpdate) {
    2348                 var s,
    2349                     c,
    2350                     // control point at the beginning and at the end
    2351                     first,
    2352                     last,
    2353                     t1,
    2354                     t2,
    2355                     dt0,
    2356                     dt1,
    2357                     dt2,
    2358                     // dx, dy,
    2359                     len;
    2360
    2361                 if (points.length < 2) {
    2362                     return NaN;
    2363                 }
    2364
    2365                 if (!suspendedUpdate) {
    2366                     tau = _tau();
    2367
    2368                     // New point list p: [first, points ..., last]
    2369                     first = {
    2370                         X: function () {
    2371                             return 2 * points[0].X() - points[1].X();
    2372                         },
    2373                         Y: function () {
    2374                             return 2 * points[0].Y() - points[1].Y();
    2375                         },
    2376                         Dist: function (p) {
    2377                             var dx = this.X() - p.X(),
    2378                                 dy = this.Y() - p.Y();
    2379                             return Mat.hypot(dx, dy);
    2380                         }
    2381                     };
    2382
    2383                     last = {
    2384                         X: function () {
    2385                             return (
    2386                                 2 * points[points.length - 1].X() -
    2387                                 points[points.length - 2].X()
    2388                             );
    2389                         },
    2390                         Y: function () {
    2391                             return (
    2392                                 2 * points[points.length - 1].Y() -
    2393                                 points[points.length - 2].Y()
    2394                             );
    2395                         },
    2396                         Dist: function (p) {
    2397                             var dx = this.X() - p.X(),
    2398                                 dy = this.Y() - p.Y();
    2399                             return Mat.hypot(dx, dy);
    2400                         }
    2401                     };
    2402
    2403                     p = [first].concat(points, [last]);
    2404                     len = p.length;
    2405
    2406                     coeffs[which] = [];
    2407
    2408                     for (s = 0; s < len - 3; s++) {
    2409                         if (type === "centripetal") {
    2410                             // The order is important, since p[0].coords === undefined
    2411                             dt0 = p[s].Dist(p[s + 1]);
    2412                             dt1 = p[s + 2].Dist(p[s + 1]);
    2413                             dt2 = p[s + 3].Dist(p[s + 2]);
    2414
    2415                             dt0 = Math.sqrt(dt0);
    2416                             dt1 = Math.sqrt(dt1);
    2417                             dt2 = Math.sqrt(dt2);
    2418
    2419                             if (dt1 < Mat.eps) {
    2420                                 dt1 = 1.0;
    2421                             }
    2422                             if (dt0 < Mat.eps) {
    2423                                 dt0 = dt1;
    2424                             }
    2425                             if (dt2 < Mat.eps) {
    2426                                 dt2 = dt1;
    2427                             }
    2428
    2429                             t1 =
    2430                                 (p[s + 1][which]() - p[s][which]()) / dt0 -
    2431                                 (p[s + 2][which]() - p[s][which]()) / (dt1 + dt0) +
    2432                                 (p[s + 2][which]() - p[s + 1][which]()) / dt1;
    2433
    2434                             t2 =
    2435                                 (p[s + 2][which]() - p[s + 1][which]()) / dt1 -
    2436                                 (p[s + 3][which]() - p[s + 1][which]()) / (dt2 + dt1) +
    2437                                 (p[s + 3][which]() - p[s + 2][which]()) / dt2;
    2438
    2439                             t1 *= dt1;
    2440                             t2 *= dt1;
    2441
    2442                             coeffs[which][s] = that._initCubicPoly(
    2443                                 p[s + 1][which](),
    2444                                 p[s + 2][which](),
    2445                                 tau * t1,
    2446                                 tau * t2
    2447                             );
    2448                         } else {
    2449                             coeffs[which][s] = that._initCubicPoly(
    2450                                 p[s + 1][which](),
    2451                                 p[s + 2][which](),
    2452                                 tau * (p[s + 2][which]() - p[s][which]()),
    2453                                 tau * (p[s + 3][which]() - p[s + 1][which]())
    2454                             );
    2455                         }
    2456                     }
    2457                 }
    2458
    2459                 if (isNaN(t)) {
    2460                     return NaN;
    2461                 }
    2462
    2463                 len = points.length;
    2464                 // This is necessary for our advanced plotting algorithm:
    2465                 if (t <= 0.0) {
    2466                     return points[0][which]();
    2467                 }
    2468                 if (t >= len) {
    2469                     return points[len - 1][which]();
    2470                 }
    2471
    2472                 s = Math.floor(t);
    2473                 if (s === t) {
    2474                     return points[s][which]();
    2475                 }
    2476
    2477                 t -= s;
    2478                 c = coeffs[which][s];
    2479                 if (c === undefined) {
    2480                     return NaN;
    2481                 }
    2482
    2483                 return ((c[3] * t + c[2]) * t + c[1]) * t + c[0];
    2484             };
    2485         };
    2486
    2487         return [
    2488             makeFct("X"),
    2489             makeFct("Y"),
    2490             0,
    2491             function () {
    2492                 return points.length - 1;
    2493             }
    2494         ];
    2495     },
    2496
    2497     /**
    2498      * Computes the cubic Catmull-Rom spline curve through a given set of points. The curve
    2499      * is uniformly parametrized. The curve is the cardinal spline curve for tau=0.5.
    2500      * Two artificial control points at the beginning and the end are added.
    2501      * @param {Array} points Array consisting of JXG.Points.
    2502      * @param {String} type (Optional) parameter which allows to choose between "uniform" (default) and
    2503      * "centripetal" parameterization. Thus the two possible values are "uniform" or "centripetal".
    2504      * @returns {Array} An Array consisting of four components: Two functions each of one parameter t
    2505      * which return the x resp. y coordinates of the Catmull-Rom-spline curve in t, a zero value, and a function simply
    2506      * returning the length of the points array minus three.
    2507      * @memberof JXG.Math.Numerics
    2508      */
    2509     CatmullRomSpline: function (points, type) {
    2510         return this.CardinalSpline(points, 0.5, type);
    2511     },
    2512
    2513     /**
    2514      * Computes the regression polynomial of a given degree through a given set of coordinates.
    2515      * Returns the regression polynomial function.
    2516      * @param {Number|function|Slider} degree number, function or slider.
    2517      * Either
    2518      * @param {Array} dataX Array containing either the x-coordinates of the data set or both coordinates in
    2519      * an array of {@link JXG.Point}s or {@link JXG.Coords}.
    2520      * In the latter case, the <tt>dataY</tt> parameter will be ignored.
    2521      * @param {Array} dataY Array containing the y-coordinates of the data set,
    2522      * @returns {function} A function of one parameter which returns the value of the regression polynomial of the given degree.
    2523      * It possesses the method getTerm() which returns the string containing the function term of the polynomial.
    2524      * The function returned will throw an exception, if the data set is malformed.
    2525      * @memberof JXG.Math.Numerics
    2526      */
    2527     regressionPolynomial: function (degree, dataX, dataY) {
    2528         var coeffs, deg, dX, dY, inputType, fct,
    2529             term = "";
    2530
    2531         // Slider
    2532         if (Type.isPoint(degree) && Type.isFunction(degree.Value)) {
    2533             /** @ignore */
    2534             deg = function () {
    2535                 return degree.Value();
    2536             };
    2537             // function
    2538         } else if (Type.isFunction(degree)) {
    2539             deg = degree;
    2540             // number
    2541         } else if (Type.isNumber(degree)) {
    2542             /** @ignore */
    2543             deg = function () {
    2544                 return degree;
    2545             };
    2546         } else {
    2547             throw new Error(
    2548                 "JSXGraph: Can't create regressionPolynomial from degree of type'" +
    2549                 typeof degree +
    2550                 "'."
    2551             );
    2552         }
    2553
    2554         // Parameters degree, dataX, dataY
    2555         if (arguments.length === 3 && Type.isArray(dataX) && Type.isArray(dataY)) {
    2556             inputType = 0;
    2557             // Parameters degree, point array
    2558         } else if (
    2559             arguments.length === 2 &&
    2560             Type.isArray(dataX) &&
    2561             dataX.length > 0 &&
    2562             Type.isPoint(dataX[0])
    2563         ) {
    2564             inputType = 1;
    2565         } else if (
    2566             arguments.length === 2 &&
    2567             Type.isArray(dataX) &&
    2568             dataX.length > 0 &&
    2569             dataX[0].usrCoords &&
    2570             dataX[0].scrCoords
    2571         ) {
    2572             inputType = 2;
    2573         } else {
    2574             throw new Error("JSXGraph: Can't create regressionPolynomial. Wrong parameters.");
    2575         }
    2576
    2577         /** @ignore */
    2578         fct = function (x, suspendedUpdate) {
    2579             var i, j,
    2580                 M, MT, y, B, c, s, d,
    2581                 // input data
    2582                 len = dataX.length;
    2583
    2584             d = Math.floor(deg());
    2585
    2586             if (!suspendedUpdate) {
    2587                 // point list as input
    2588                 if (inputType === 1) {
    2589                     dX = [];
    2590                     dY = [];
    2591
    2592                     for (i = 0; i < len; i++) {
    2593                         dX[i] = dataX[i].X();
    2594                         dY[i] = dataX[i].Y();
    2595                     }
    2596                 }
    2597
    2598                 if (inputType === 2) {
    2599                     dX = [];
    2600                     dY = [];
    2601
    2602                     for (i = 0; i < len; i++) {
    2603                         dX[i] = dataX[i].usrCoords[1];
    2604                         dY[i] = dataX[i].usrCoords[2];
    2605                     }
    2606                 }
    2607
    2608                 // check for functions
    2609                 if (inputType === 0) {
    2610                     dX = [];
    2611                     dY = [];
    2612
    2613                     for (i = 0; i < len; i++) {
    2614                         if (Type.isFunction(dataX[i])) {
    2615                             dX.push(dataX[i]());
    2616                         } else {
    2617                             dX.push(dataX[i]);
    2618                         }
    2619
    2620                         if (Type.isFunction(dataY[i])) {
    2621                             dY.push(dataY[i]());
    2622                         } else {
    2623                             dY.push(dataY[i]);
    2624                         }
    2625                     }
    2626                 }
    2627
    2628                 M = [];
    2629                 for (j = 0; j < len; j++) {
    2630                     M.push([1]);
    2631                 }
    2632                 for (i = 1; i <= d; i++) {
    2633                     for (j = 0; j < len; j++) {
    2634                         M[j][i] = M[j][i - 1] * dX[j];
    2635                     }
    2636                 }
    2637
    2638                 y = dY;
    2639                 MT = Mat.transpose(M);
    2640                 B = Mat.matMatMult(MT, M);
    2641                 c = Mat.matVecMult(MT, y);
    2642                 coeffs = Mat.Numerics.Gauss(B, c);
    2643                 term = Mat.Numerics.generatePolynomialTerm(coeffs, d, "x", 3);
    2644             }
    2645
    2646             // Horner's scheme to evaluate polynomial
    2647             s = coeffs[d];
    2648             for (i = d - 1; i >= 0; i--) {
    2649                 s = s * x + coeffs[i];
    2650             }
    2651
    2652             return s;
    2653         };
    2654
    2655         /** @ignore */
    2656         fct.getTerm = function () {
    2657             return term;
    2658         };
    2659
    2660         return fct;
    2661     },
    2662
    2663     /**
    2664      * Computes the cubic Bezier curve through a given set of points.
    2665      * @param {Array} points Array consisting of 3*k+1 {@link JXG.Points}.
    2666      * The points at position k with k mod 3 = 0 are the data points,
    2667      * points at position k with k mod 3 = 1 or 2 are the control points.
    2668      * @returns {Array} An array consisting of two functions of one parameter t which return the
    2669      * x resp. y coordinates of the Bezier curve in t, one zero value, and a third function accepting
    2670      * no parameters and returning one third of the length of the points.
    2671      * @memberof JXG.Math.Numerics
    2672      */
    2673     bezier: function (points) {
    2674         var len,
    2675             flen,
    2676             /** @ignore */
    2677             makeFct = function (which) {
    2678                 return function (t, suspendedUpdate) {
    2679                     var z = Math.floor(t) * 3,
    2680                         t0 = t % 1,
    2681                         t1 = 1 - t0;
    2682
    2683                     if (!suspendedUpdate) {
    2684                         flen = 3 * Math.floor((points.length - 1) / 3);
    2685                         len = Math.floor(flen / 3);
    2686                     }
    2687
    2688                     if (t < 0) {
    2689                         return points[0][which]();
    2690                     }
    2691
    2692                     if (t >= len) {
    2693                         return points[flen][which]();
    2694                     }
    2695
    2696                     if (isNaN(t)) {
    2697                         return NaN;
    2698                     }
    2699
    2700                     return (
    2701                         t1 * t1 * (t1 * points[z][which]() + 3 * t0 * points[z + 1][which]()) +
    2702                         (3 * t1 * points[z + 2][which]() + t0 * points[z + 3][which]()) *
    2703                         t0 *
    2704                         t0
    2705                     );
    2706                 };
    2707             };
    2708
    2709         return [
    2710             makeFct("X"),
    2711             makeFct("Y"),
    2712             0,
    2713             function () {
    2714                 return Math.floor(points.length / 3);
    2715             }
    2716         ];
    2717     },
    2718
    2719     /**
    2720      * Computes the B-spline curve of order k (order = degree+1) through a given set of points.
    2721      * @param {Array} points Array consisting of JXG.Points.
    2722      * @param {Number} order Order of the B-spline curve.
    2723      * @returns {Array} An Array consisting of four components: Two functions each of one parameter t
    2724      * which return the x resp. y coordinates of the B-spline curve in t, a zero value, and a function simply
    2725      * returning the length of the points array minus one.
    2726      * @memberof JXG.Math.Numerics
    2727      */
    2728     bspline: function (points, order) {
    2729         var knots,
    2730             _knotVector = function (n, k) {
    2731                 var j,
    2732                     kn = [];
    2733
    2734                 for (j = 0; j < n + k + 1; j++) {
    2735                     if (j < k) {
    2736                         kn[j] = 0.0;
    2737                     } else if (j <= n) {
    2738                         kn[j] = j - k + 1;
    2739                     } else {
    2740                         kn[j] = n - k + 2;
    2741                     }
    2742                 }
    2743
    2744                 return kn;
    2745             },
    2746             _evalBasisFuncs = function (t, kn, k, s) {
    2747                 var i,
    2748                     j,
    2749                     a,
    2750                     b,
    2751                     den,
    2752                     N = [];
    2753
    2754                 if (kn[s] <= t && t < kn[s + 1]) {
    2755                     N[s] = 1;
    2756                 } else {
    2757                     N[s] = 0;
    2758                 }
    2759
    2760                 for (i = 2; i <= k; i++) {
    2761                     for (j = s - i + 1; j <= s; j++) {
    2762                         if (j <= s - i + 1 || j < 0) {
    2763                             a = 0.0;
    2764                         } else {
    2765                             a = N[j];
    2766                         }
    2767
    2768                         if (j >= s) {
    2769                             b = 0.0;
    2770                         } else {
    2771                             b = N[j + 1];
    2772                         }
    2773
    2774                         den = kn[j + i - 1] - kn[j];
    2775
    2776                         if (den === 0) {
    2777                             N[j] = 0;
    2778                         } else {
    2779                             N[j] = ((t - kn[j]) / den) * a;
    2780                         }
    2781
    2782                         den = kn[j + i] - kn[j + 1];
    2783
    2784                         if (den !== 0) {
    2785                             N[j] += ((kn[j + i] - t) / den) * b;
    2786                         }
    2787                     }
    2788                 }
    2789                 return N;
    2790             },
    2791             /** @ignore */
    2792             makeFct = function (which) {
    2793                 return function (t, suspendedUpdate) {
    2794                     var y,
    2795                         j,
    2796                         s,
    2797                         N = [],
    2798                         len = points.length,
    2799                         n = len - 1,
    2800                         k = order;
    2801
    2802                     if (n <= 0) {
    2803                         return NaN;
    2804                     }
    2805
    2806                     if (n + 2 <= k) {
    2807                         k = n + 1;
    2808                     }
    2809
    2810                     if (t <= 0) {
    2811                         return points[0][which]();
    2812                     }
    2813
    2814                     if (t >= n - k + 2) {
    2815                         return points[n][which]();
    2816                     }
    2817
    2818                     s = Math.floor(t) + k - 1;
    2819                     knots = _knotVector(n, k);
    2820                     N = _evalBasisFuncs(t, knots, k, s);
    2821
    2822                     y = 0.0;
    2823                     for (j = s - k + 1; j <= s; j++) {
    2824                         if (j < len && j >= 0) {
    2825                             y += points[j][which]() * N[j];
    2826                         }
    2827                     }
    2828
    2829                     return y;
    2830                 };
    2831             };
    2832
    2833         return [
    2834             makeFct("X"),
    2835             makeFct("Y"),
    2836             0,
    2837             function () {
    2838                 return points.length - 1;
    2839             }
    2840         ];
    2841     },
    2842
    2843     /**
    2844      * Numerical (symmetric) approximation of derivative. suspendUpdate is piped through,
    2845      * see {@link JXG.Curve#updateCurve}
    2846      * and {@link JXG.Curve#hasPoint}.
    2847      * @param {function} f Function in one variable to be differentiated.
    2848      * @param {object} [obj] Optional object that is treated as "this" in the function body. This is useful, if the function is a
    2849      * method of an object and contains a reference to its parent object via "this".
    2850      * @returns {function} Derivative function of a given function f.
    2851      * @memberof JXG.Math.Numerics
    2852      */
    2853     D: function (f, obj) {
    2854         if (!Type.exists(obj)) {
    2855             return function (x, suspendedUpdate) {
    2856                 var h = 0.00001,
    2857                     h2 = h * 2.0;
    2858
    2859                 // Experiments with Richardsons rule
    2860                 /*
    2861                     var phi = (f(x + h, suspendedUpdate) - f(x - h, suspendedUpdate)) / h2;
    2862                     var phi2;
    2863                     h *= 0.5;
    2864                     h2 *= 0.5;
    2865                     phi2 = (f(x + h, suspendedUpdate) - f(x - h, suspendedUpdate)) / h2;
    2866
    2867                     return phi2 + (phi2 - phi) / 3.0;
    2868                     */
    2869                 return (f(x + h, suspendedUpdate) - f(x - h, suspendedUpdate)) / h2;
    2870             };
    2871         }
    2872
    2873         return function (x, suspendedUpdate) {
    2874             var h = 0.00001,
    2875                 h2 = h * 2.0;
    2876
    2877             return (
    2878                 (f.apply(obj, [x + h, suspendedUpdate]) -
    2879                     f.apply(obj, [x - h, suspendedUpdate])) /
    2880                 h2
    2881             );
    2882         };
    2883     },
    2884
    2885     /**
    2886      * Evaluate the function term for {@link JXG.Math.Numerics.riemann}.
    2887      * @private
    2888      * @param {Number} x function argument
    2889      * @param {function} f JavaScript function returning a number
    2890      * @param {String} type Name of the Riemann sum type, e.g. 'lower'.
    2891      * @param {Number} delta Width of the bars in user coordinates
    2892      * @returns {Number} Upper (delta > 0) or lower (delta < 0) value of the bar containing x of the Riemann sum.
    2893      * @see JXG.Math.Numerics.riemann
    2894      * @private
    2895      * @memberof JXG.Math.Numerics
    2896      */
    2897     _riemannValue: function (x, f, type, delta) {
    2898         var y, y1, x1, delta1;
    2899
    2900         if (delta < 0) {
    2901             // delta is negative if the lower function term is evaluated
    2902             if (type !== "trapezoidal") {
    2903                 x = x + delta;
    2904             }
    2905             delta *= -1;
    2906             if (type === "lower") {
    2907                 type = "upper";
    2908             } else if (type === "upper") {
    2909                 type = "lower";
    2910             }
    2911         }
    2912
    2913         delta1 = delta * 0.01; // for 'lower' and 'upper'
    2914
    2915         if (type === "right") {
    2916             y = f(x + delta);
    2917         } else if (type === "middle") {
    2918             y = f(x + delta * 0.5);
    2919         } else if (type === "left" || type === "trapezoidal") {
    2920             y = f(x);
    2921         } else if (type === "lower") {
    2922             y = f(x);
    2923
    2924             for (x1 = x + delta1; x1 <= x + delta; x1 += delta1) {
    2925                 y1 = f(x1);
    2926
    2927                 if (y1 < y) {
    2928                     y = y1;
    2929                 }
    2930             }
    2931
    2932             y1 = f(x + delta);
    2933             if (y1 < y) {
    2934                 y = y1;
    2935             }
    2936         } else if (type === "upper") {
    2937             y = f(x);
    2938
    2939             for (x1 = x + delta1; x1 <= x + delta; x1 += delta1) {
    2940                 y1 = f(x1);
    2941                 if (y1 > y) {
    2942                     y = y1;
    2943                 }
    2944             }
    2945
    2946             y1 = f(x + delta);
    2947             if (y1 > y) {
    2948                 y = y1;
    2949             }
    2950         } else if (type === "random") {
    2951             y = f(x + delta * Math.random());
    2952         } else if (type === "simpson") {
    2953             y = (f(x) + 4 * f(x + delta * 0.5) + f(x + delta)) / 6.0;
    2954         } else {
    2955             y = f(x); // default is lower
    2956         }
    2957
    2958         return y;
    2959     },
    2960
    2961     /**
    2962      * Helper function to create curve which displays Riemann sums.
    2963      * Compute coordinates for the rectangles showing the Riemann sum.
    2964      * <p>
    2965      * In case of type "simpson" and "trapezoidal", the horizontal line approximating the function value
    2966      * is replaced by a parabola or a secant. IN case of "simpson",
    2967      * the parabola is approximated visually by a polygonal chain of fixed step width.
    2968      *
    2969      * @param {Function|Array} f Function or array of two functions.
    2970      * If f is a function the integral of this function is approximated by the Riemann sum.
    2971      * If f is an array consisting of two functions the area between the two functions is filled
    2972      * by the Riemann sum bars.
    2973      * @param {Number} n number of rectangles.
    2974      * @param {String} type Type of approximation. Possible values are: 'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezoidal'.
    2975      * "simpson" is Simpson's 1/3 rule.
    2976      * @param {Number} start Left border of the approximation interval
    2977      * @param {Number} end Right border of the approximation interval
    2978      * @returns {Array} An array of two arrays containing the x and y coordinates for the rectangles showing the Riemann sum. This
    2979      * array may be used as parent array of a {@link JXG.Curve}. The third parameteris the riemann sum, i.e. the sum of the volumes of all
    2980      * rectangles.
    2981      * @memberof JXG.Math.Numerics
    2982      */
    2983     riemann: function (gf, n, type, start, end) {
    2984         var i, delta,
    2985             k, a, b, c, f0, f1, f2, xx, h,
    2986             steps = 30, // Fixed step width for Simpson's rule
    2987             xarr = [],
    2988             yarr = [],
    2989             x = start,
    2990             sum = 0,
    2991             y, f, g;
    2992
    2993         if (Type.isArray(gf)) {
    2994             g = gf[0];
    2995             f = gf[1];
    2996         } else {
    2997             f = gf;
    2998         }
    2999
    3000         n = Math.floor(n);
    3001
    3002         if (n <= 0) {
    3003             return [xarr, yarr, sum];
    3004         }
    3005
    3006         delta = (end - start) / n;
    3007
    3008         // "Upper" horizontal line defined by function
    3009         for (i = 0; i < n; i++) {
    3010             if (type === "simpson") {
    3011                 sum += this._riemannValue(x, f, type, delta) * delta;
    3012
    3013                 h = delta * 0.5;
    3014                 f0 = f(x);
    3015                 f1 = f(x + h);
    3016                 f2 = f(x + 2 * h);
    3017
    3018                 a = (f2 + f0 - 2 * f1) / (h * h) * 0.5;
    3019                 b = (f2 - f0) / (2 * h);
    3020                 c = f1;
    3021                 for (k = 0; k < steps; k++) {
    3022                     xx = k * delta / steps - h;
    3023                     xarr.push(x + xx + h);
    3024                     yarr.push(a * xx * xx + b * xx + c);
    3025                 }
    3026                 x += delta;
    3027                 y = f2;
    3028             } else {
    3029                 y = this._riemannValue(x, f, type, delta);
    3030                 xarr.push(x);
    3031                 yarr.push(y);
    3032
    3033                 x += delta;
    3034                 if (type === "trapezoidal") {
    3035                     f2 = f(x);
    3036                     sum += (y + f2) * 0.5 * delta;
    3037                     y = f2;
    3038                 } else {
    3039                     sum += y * delta;
    3040                 }
    3041
    3042                 xarr.push(x);
    3043                 yarr.push(y);
    3044             }
    3045             xarr.push(x);
    3046             yarr.push(y);
    3047         }
    3048
    3049         // "Lower" horizontal line
    3050         // Go backwards
    3051         for (i = 0; i < n; i++) {
    3052             if (type === "simpson" && g) {
    3053                 sum -= this._riemannValue(x, g, type, -delta) * delta;
    3054
    3055                 h = delta * 0.5;
    3056                 f0 = g(x);
    3057                 f1 = g(x - h);
    3058                 f2 = g(x - 2 * h);
    3059
    3060                 a = (f2 + f0 - 2 * f1) / (h * h) * 0.5;
    3061                 b = (f2 - f0) / (2 * h);
    3062                 c = f1;
    3063                 for (k = 0; k < steps; k++) {
    3064                     xx = k * delta / steps - h;
    3065                     xarr.push(x - xx - h);
    3066                     yarr.push(a * xx * xx + b * xx + c);
    3067                 }
    3068                 x -= delta;
    3069                 y = f2;
    3070             } else {
    3071                 if (g) {
    3072                     y = this._riemannValue(x, g, type, -delta);
    3073                 } else {
    3074                     y = 0.0;
    3075                 }
    3076                 xarr.push(x);
    3077                 yarr.push(y);
    3078
    3079                 x -= delta;
    3080                 if (g) {
    3081                     if (type === "trapezoidal") {
    3082                         f2 = g(x);
    3083                         sum -= (y + f2) * 0.5 * delta;
    3084                         y = f2;
    3085                     } else {
    3086                         sum -= y * delta;
    3087                     }
    3088                 }
    3089             }
    3090             xarr.push(x);
    3091             yarr.push(y);
    3092
    3093             // Draw the vertical lines
    3094             xarr.push(x);
    3095             yarr.push(f(x));
    3096         }
    3097
    3098         return [xarr, yarr, sum];
    3099     },
    3100
    3101     /**
    3102      * Approximate the integral by Riemann sums.
    3103      * Compute the area described by the riemann sum rectangles.
    3104      *
    3105      * If there is an element of type {@link Riemannsum}, then it is more efficient
    3106      * to use the method JXG.Curve.Value() of this element instead.
    3107      *
    3108      * @param {Function_Array} f Function or array of two functions.
    3109      * If f is a function the integral of this function is approximated by the Riemann sum.
    3110      * If f is an array consisting of two functions the area between the two functions is approximated
    3111      * by the Riemann sum.
    3112      * @param {Number} n number of rectangles.
    3113      * @param {String} type Type of approximation. Possible values are: 'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson' or 'trapezoidal'.
    3114      *
    3115      * @param {Number} start Left border of the approximation interval
    3116      * @param {Number} end Right border of the approximation interval
    3117      * @returns {Number} The sum of the areas of the rectangles.
    3118      * @memberof JXG.Math.Numerics
    3119      */
    3120     riemannsum: function (f, n, type, start, end) {
    3121         JXG.deprecated("Numerics.riemannsum()", "Numerics.riemann()[2]");
    3122         return this.riemann(f, n, type, start, end)[2];
    3123     },
    3124
    3125     /**
    3126      * Solve initial value problems numerically using <i>explicit</i> Runge-Kutta methods.
    3127      * See {@link https://en.wikipedia.org/wiki/Runge-Kutta_methods} for more information on the algorithm.
    3128      * @param {object|String} butcher Butcher tableau describing the Runge-Kutta method to use. This can be either a string describing
    3129      * a Runge-Kutta method with a Butcher tableau predefined in JSXGraph like 'euler', 'heun', 'rk4' or an object providing the structure
    3130      * <pre>
    3131      * {
    3132      *     s: <Number>,
    3133      *     A: <matrix>,
    3134      *     b: <Array>,
    3135      *     c: <Array>
    3136      * }
    3137      * </pre>
    3138      * which corresponds to the Butcher tableau structure
    3139      * shown here: https://en.wikipedia.org/w/index.php?title=List_of_Runge%E2%80%93Kutta_methods&oldid=357796696 .
    3140      * <i>Default</i> is 'euler'.
    3141      * @param {Array} x0 Initial value vector. Even if the problem is one-dimensional, the initial value has to be given in an array.
    3142      * @param {Array} I Interval on which to integrate.
    3143      * @param {Number} N Number of integration intervals, i.e. there are <i>N+1</i> evaluation points.
    3144      * @param {function} f Function describing the right hand side of the first order ordinary differential equation, i.e. if the ode
    3145      * is given by the equation <pre>dx/dt = f(t, x(t))</pre>. So, f has to take two parameters, a number <tt>t</tt> and a
    3146      * vector <tt>x</tt>, and has to return a vector of the same length as <tt>x</tt> has.
    3147      * @returns {Array} An array of vectors describing the solution of the ode on the given interval I.
    3148      * @example
    3149      * // A very simple autonomous system dx(t)/dt = x(t);
    3150      * var f = function(t, x) {
    3151      *     return [x[0]];
    3152      * }
    3153      *
    3154      * // Solve it with initial value x(0) = 1 on the interval [0, 2]
    3155      * // with 20 evaluation points.
    3156      * var data = JXG.Math.Numerics.rungeKutta('heun', [1], [0, 2], 20, f);
    3157      *
    3158      * // Prepare data for plotting the solution of the ode using a curve.
    3159      * var dataX = [];
    3160      * var dataY = [];
    3161      * var h = 0.1;        // (I[1] - I[0])/N  = (2-0)/20
    3162      * var i;
    3163      * for(i=0; i<data.length; i++) {
    3164      *     dataX[i] = i*h;
    3165      *     dataY[i] = data[i][0];
    3166      * }
    3167      * var g = board.create('curve', [dataX, dataY], {strokeWidth:'2px'});
    3168      * </pre><div class="jxgbox" id="JXGd2432d04-4ef7-4159-a90b-a2eb8d38c4f6" style="width: 300px; height: 300px;"></div>
    3169      * <script type="text/javascript">
    3170      * var board = JXG.JSXGraph.initBoard('JXGd2432d04-4ef7-4159-a90b-a2eb8d38c4f6', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
    3171      * var f = function(t, x) {
    3172      *     // we have to copy the value.
    3173      *     // return x; would just return the reference.
    3174      *     return [x[0]];
    3175      * }
    3176      * var data = JXG.Math.Numerics.rungeKutta('heun', [1], [0, 2], 20, f);
    3177      * var dataX = [];
    3178      * var dataY = [];
    3179      * var h = 0.1;
    3180      * for(var i=0; i<data.length; i++) {
    3181      *     dataX[i] = i*h;
    3182      *     dataY[i] = data[i][0];
    3183      * }
    3184      * var g = board.create('curve', [dataX, dataY], {strokeColor:'red', strokeWidth:'2px'});
    3185      * </script><pre>
    3186      * @memberof JXG.Math.Numerics
    3187      */
    3188     rungeKutta: function (butcher, x0, I, N, f) {
    3189         var e,
    3190             i, j, k, l, s,
    3191             x = [],
    3192             y = [],
    3193             h = (I[1] - I[0]) / N,
    3194             t = I[0],
    3195             dim = x0.length,
    3196             result = [],
    3197             r = 0;
    3198
    3199         if (Type.isString(butcher)) {
    3200             butcher = predefinedButcher[butcher] || predefinedButcher.euler;
    3201         }
    3202         s = butcher.s;
    3203
    3204         // Don't change x0, so copy it
    3205         x = x0.slice();
    3206
    3207         for (i = 0; i <= N; i++) {
    3208             result[r] = x.slice();
    3209
    3210             r++;
    3211             k = [];
    3212
    3213             for (j = 0; j < s; j++) {
    3214                 // Init y = 0
    3215                 for (e = 0; e < dim; e++) {
    3216                     y[e] = 0.0;
    3217                 }
    3218
    3219                 // Calculate linear combination of former k's and save it in y
    3220                 for (l = 0; l < j; l++) {
    3221                     for (e = 0; e < dim; e++) {
    3222                         y[e] += butcher.A[j][l] * h * k[l][e];
    3223                     }
    3224                 }
    3225
    3226                 // Add x(t) to y
    3227                 for (e = 0; e < dim; e++) {
    3228                     y[e] += x[e];
    3229                 }
    3230
    3231                 // Calculate new k and add it to the k matrix
    3232                 k.push(f(t + butcher.c[j] * h, y));
    3233             }
    3234
    3235             // Init y = 0
    3236             for (e = 0; e < dim; e++) {
    3237                 y[e] = 0.0;
    3238             }
    3239
    3240             for (l = 0; l < s; l++) {
    3241                 for (e = 0; e < dim; e++) {
    3242                     y[e] += butcher.b[l] * k[l][e];
    3243                 }
    3244             }
    3245
    3246             for (e = 0; e < dim; e++) {
    3247                 x[e] = x[e] + h * y[e];
    3248             }
    3249
    3250             t += h;
    3251         }
    3252
    3253         return result;
    3254     },
    3255
    3256     /**
    3257      * Maximum number of iterations in {@link JXG.Math.Numerics.fzero} and
    3258      * {@link JXG.Math.Numerics.chandrupatla}
    3259      * @type Number
    3260      * @default 80
    3261      * @memberof JXG.Math.Numerics
    3262      */
    3263     maxIterationsRoot: 80,
    3264
    3265     /**
    3266      * Maximum number of iterations in {@link JXG.Math.Numerics.fminbr}
    3267      * @type Number
    3268      * @default 500
    3269      * @memberof JXG.Math.Numerics
    3270      */
    3271     maxIterationsMinimize: 500,
    3272
    3273     /**
    3274      * Given a number x_0, this function tries to find a second number x_1 such that
    3275      * the function f has opposite signs at x_0 and x_1.
    3276      * The return values have to be tested if the method succeeded.
    3277      *
    3278      * @param {Function} f Function, whose root is to be found
    3279      * @param {Number} x0 Start value
    3280      * @param {Object} [context] Parent object in case f is method of it
    3281      * @returns {Array} [x_0, f(x_0), x_1, f(x_1)] in case that x_0 <= x_1
    3282      *   or [x_1, f(x_1), x_0, f(x_0)] in case that x_1 < x_0.
    3283      *
    3284      * @see JXG.Math.Numerics.fzero
    3285      * @see JXG.Math.Numerics.chandrupatla
    3286      *
    3287      * @memberof JXG.Math.Numerics
    3288      */
    3289     findBracket: function (f, x0, context) {
    3290         var a, aa, fa, blist, b, fb, u, fu, i, len;
    3291
    3292         if (Type.isArray(x0)) {
    3293             return x0;
    3294         }
    3295
    3296         a = x0;
    3297         fa = f.call(context, a);
    3298         // nfev += 1;
    3299
    3300         // Try to get b, by trying several values related to a
    3301         aa = a === 0 ? 1 : a;
    3302         blist = [
    3303             a - 0.1 * aa,
    3304             a + 0.1 * aa,
    3305             a - 1,
    3306             a + 1,
    3307             a - 0.5 * aa,
    3308             a + 0.5 * aa,
    3309             a - 0.6 * aa,
    3310             a + 0.6 * aa,
    3311             a - 1 * aa,
    3312             a + 1 * aa,
    3313             a - 2 * aa,
    3314             a + 2 * aa,
    3315             a - 5 * aa,
    3316             a + 5 * aa,
    3317             a - 10 * aa,
    3318             a + 10 * aa,
    3319             a - 50 * aa,
    3320             a + 50 * aa,
    3321             a - 100 * aa,
    3322             a + 100 * aa
    3323         ];
    3324         len = blist.length;
    3325
    3326         for (i = 0; i < len; i++) {
    3327             b = blist[i];
    3328             fb = f.call(context, b);
    3329             // nfev += 1;
    3330
    3331             if (fa * fb <= 0) {
    3332                 break;
    3333             }
    3334         }
    3335         if (b < a) {
    3336             u = a;
    3337             a = b;
    3338             b = u;
    3339
    3340             fu = fa;
    3341             fa = fb;
    3342             fb = fu;
    3343         }
    3344         return [a, fa, b, fb];
    3345     },
    3346
    3347     /**
    3348      *
    3349      * Find zero of an univariate function f.
    3350      * @param {function} f Function, whose root is to be found
    3351      * @param {Array|Number} x0  Start value or start interval enclosing the root.
    3352      * If x0 is an interval [a,b], it is required that f(a)f(b) <= 0, otherwise the minimum of f in [a, b] will be returned.
    3353      * If x0 is a number, the algorithms tries to enclose the root by an interval [a, b] containing x0 and the root and
    3354      * f(a)f(b) <= 0. If this fails, the algorithm falls back to Newton's method.
    3355      * @param {Object} [context] Parent object in case f is method of it
    3356      * @returns {Number} the approximation of the root
    3357      * Algorithm:
    3358      *  Brent's root finder from
    3359      *  G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical
    3360      *  computations. M., Mir, 1980, p.180 of the Russian edition
    3361      *  https://www.netlib.org/c/brent.shar
    3362      *
    3363      * If x0 is an array containing lower and upper bound for the zero
    3364      * algorithm 748 is applied. Otherwise, if x0 is a number,
    3365      * the algorithm tries to bracket a zero of f starting from x0.
    3366      * If this fails, we fall back to Newton's method.
    3367      *
    3368      * @see JXG.Math.Numerics.chandrupatla
    3369      * @see JXG.Math.Numerics.root
    3370      * @see JXG.Math.Numerics.findBracket
    3371      * @see JXG.Math.Numerics.Newton
    3372      * @see JXG.Math.Numerics.fminbr
    3373      * @memberof JXG.Math.Numerics
    3374      */
    3375     fzero: function (f, x0, context) {
    3376         var a, b, c,
    3377             fa, fb, fc,
    3378             res, x00,
    3379             prev_step,
    3380             t1, t2,
    3381             cb,
    3382             tol_act,   // Actual tolerance
    3383             p,         // Interpolation step is calculated in the form p/q; division
    3384             q,         // operations is delayed until the last moment
    3385             new_step,  // Step at this iteration
    3386             eps = Mat.eps,
    3387             maxiter = this.maxIterationsRoot,
    3388             niter = 0;
    3389         // nfev = 0;
    3390
    3391         if (Type.isArray(x0)) {
    3392             if (x0.length < 2) {
    3393                 throw new Error(
    3394                     "JXG.Math.Numerics.fzero: length of array x0 has to be at least two."
    3395                 );
    3396             }
    3397
    3398             x00 = this.findDomain(f, x0, context);
    3399             a = x00[0];
    3400             b = x00[1];
    3401             // a = x0[0];
    3402             // b = x0[1];
    3403
    3404             fa = f.call(context, a);
    3405             // nfev += 1;
    3406             fb = f.call(context, b);
    3407             // nfev += 1;
    3408         } else {
    3409             res = this.findBracket(f, x0, context);
    3410             a = res[0];
    3411             fa = res[1];
    3412             b = res[2];
    3413             fb = res[3];
    3414         }
    3415
    3416         if (Math.abs(fa) <= eps) {
    3417             return a;
    3418         }
    3419         if (Math.abs(fb) <= eps) {
    3420             return b;
    3421         }
    3422
    3423         if (fa * fb > 0) {
    3424             // Bracketing not successful, fall back to Newton's method or to fminbr
    3425             if (Type.isArray(x0)) {
    3426                 return this.fminbr(f, [a, b], context);
    3427             }
    3428
    3429             return this.Newton(f, a, context);
    3430         }
    3431
    3432         // OK, we have enclosed a zero of f.
    3433         // Now we can start Brent's method
    3434         c = a;
    3435         fc = fa;
    3436
    3437         // Main iteration loop
    3438         while (niter < maxiter) {
    3439             // Distance from the last but one to the last approximation
    3440             prev_step = b - a;
    3441
    3442             // Swap data for b to be the best approximation
    3443             if (Math.abs(fc) < Math.abs(fb)) {
    3444                 a = b;
    3445                 b = c;
    3446                 c = a;
    3447
    3448                 fa = fb;
    3449                 fb = fc;
    3450                 fc = fa;
    3451             }
    3452             tol_act = 2 * eps * Math.abs(b) + eps * 0.5;
    3453             new_step = (c - b) * 0.5;
    3454
    3455             if (Math.abs(new_step) <= tol_act || Math.abs(fb) <= eps) {
    3456                 //  Acceptable approx. is found
    3457                 return b;
    3458             }
    3459
    3460             // Decide if the interpolation can be tried
    3461             // If prev_step was large enough and was in true direction Interpolatiom may be tried
    3462             if (Math.abs(prev_step) >= tol_act && Math.abs(fa) > Math.abs(fb)) {
    3463                 cb = c - b;
    3464
    3465                 // If we have only two distinct points linear interpolation can only be applied
    3466                 if (a === c) {
    3467                     t1 = fb / fa;
    3468                     p = cb * t1;
    3469                     q = 1.0 - t1;
    3470                     // Quadric inverse interpolation
    3471                 } else {
    3472                     q = fa / fc;
    3473                     t1 = fb / fc;
    3474                     t2 = fb / fa;
    3475
    3476                     p = t2 * (cb * q * (q - t1) - (b - a) * (t1 - 1.0));
    3477                     q = (q - 1.0) * (t1 - 1.0) * (t2 - 1.0);
    3478                 }
    3479
    3480                 // p was calculated with the opposite sign; make p positive
    3481                 if (p > 0) {
    3482                     q = -q;
    3483                     // and assign possible minus to q
    3484                 } else {
    3485                     p = -p;
    3486                 }
    3487
    3488                 // If b+p/q falls in [b,c] and isn't too large it is accepted
    3489                 // If p/q is too large then the bissection procedure can reduce [b,c] range to more extent
    3490                 if (
    3491                     p < 0.75 * cb * q - Math.abs(tol_act * q) * 0.5 &&
    3492                     p < Math.abs(prev_step * q * 0.5)
    3493                 ) {
    3494                     new_step = p / q;
    3495                 }
    3496             }
    3497
    3498             // Adjust the step to be not less than tolerance
    3499             if (Math.abs(new_step) < tol_act) {
    3500                 new_step = new_step > 0 ? tol_act : -tol_act;
    3501             }
    3502
    3503             // Save the previous approx.
    3504             a = b;
    3505             fa = fb;
    3506             b += new_step;
    3507             fb = f.call(context, b);
    3508             // Do step to a new approxim.
    3509             // nfev += 1;
    3510
    3511             // Adjust c for it to have a sign opposite to that of b
    3512             if ((fb > 0 && fc > 0) || (fb < 0 && fc < 0)) {
    3513                 c = a;
    3514                 fc = fa;
    3515             }
    3516             niter++;
    3517         } // End while
    3518
    3519         return b;
    3520     },
    3521
    3522     /**
    3523      * Find zero of an univariate function f.
    3524      * @param {function} f Function, whose root is to be found
    3525      * @param {Array|Number} x0  Start value or start interval enclosing the root.
    3526      * If x0 is an interval [a,b], it is required that f(a)f(b) <= 0, otherwise the minimum of f in [a, b] will be returned.
    3527      * If x0 is a number, the algorithms tries to enclose the root by an interval [a, b] containing x0 and the root and
    3528      * f(a)f(b) <= 0. If this fails, the algorithm falls back to Newton's method.
    3529      * @param {Object} [context] Parent object in case f is method of it
    3530      * @returns {Number} the approximation of the root
    3531      * Algorithm:
    3532      * Chandrupatla's method, see
    3533      * Tirupathi R. Chandrupatla,
    3534      * "A new hybrid quadratic/bisection algorithm for finding the zero of a nonlinear function without using derivatives",
    3535      * Advances in Engineering Software, Volume 28, Issue 3, April 1997, Pages 145-149.
    3536      *
    3537      * If x0 is an array containing lower and upper bound for the zero
    3538      * algorithm 748 is applied. Otherwise, if x0 is a number,
    3539      * the algorithm tries to bracket a zero of f starting from x0.
    3540      * If this fails, we fall back to Newton's method.
    3541      *
    3542      * @see JXG.Math.Numerics.root
    3543      * @see JXG.Math.Numerics.fzero
    3544      * @see JXG.Math.Numerics.findBracket
    3545      * @see JXG.Math.Numerics.Newton
    3546      * @see JXG.Math.Numerics.fminbr
    3547      * @memberof JXG.Math.Numerics
    3548      */
    3549     chandrupatla: function (f, x0, context) {
    3550         var a, b, fa, fb,
    3551             res,
    3552             niter = 0,
    3553             maxiter = this.maxIterationsRoot,
    3554             rand = 1 + Math.random() * 0.001,
    3555             t = 0.5 * rand,
    3556             eps = Mat.eps, // 1.e-10,
    3557             dlt = 0.00001,
    3558             x1, x2, x3, x,
    3559             f1, f2, f3, y,
    3560             xm, fm,
    3561             tol, tl,
    3562             xi, ph, fl, fh,
    3563             AL, A, B, C, D;
    3564
    3565         if (Type.isArray(x0)) {
    3566             if (x0.length < 2) {
    3567                 throw new Error(
    3568                     "JXG.Math.Numerics.fzero: length of array x0 has to be at least two."
    3569                 );
    3570             }
    3571
    3572             a = x0[0];
    3573             fa = f.call(context, a);
    3574             // nfev += 1;
    3575             b = x0[1];
    3576             fb = f.call(context, b);
    3577             // nfev += 1;
    3578         } else {
    3579             res = this.findBracket(f, x0, context);
    3580             a = res[0];
    3581             fa = res[1];
    3582             b = res[2];
    3583             fb = res[3];
    3584         }
    3585
    3586         if (fa * fb > 0) {
    3587             // Bracketing not successful, fall back to Newton's method or to fminbr
    3588             if (Type.isArray(x0)) {
    3589                 return this.fminbr(f, [a, b], context);
    3590             }
    3591
    3592             return this.Newton(f, a, context);
    3593         }
    3594
    3595         x1 = a;
    3596         x2 = b;
    3597         f1 = fa;
    3598         f2 = fb;
    3599         do {
    3600             x = x1 + t * (x2 - x1);
    3601             y = f.call(context, x);
    3602
    3603             // Arrange 2-1-3: 2-1 interval, 1 middle, 3 discarded point
    3604             if (Math.sign(y) === Math.sign(f1)) {
    3605                 x3 = x1;
    3606                 x1 = x;
    3607                 f3 = f1;
    3608                 f1 = y;
    3609             } else {
    3610                 x3 = x2;
    3611                 x2 = x1;
    3612                 f3 = f2;
    3613                 f2 = f1;
    3614             }
    3615             x1 = x;
    3616             f1 = y;
    3617
    3618             xm = x1;
    3619             fm = f1;
    3620             if (Math.abs(f2) < Math.abs(f1)) {
    3621                 xm = x2;
    3622                 fm = f2;
    3623             }
    3624             tol = 2 * eps * Math.abs(xm) + 0.5 * dlt;
    3625             tl = tol / Math.abs(x2 - x1);
    3626             if (tl > 0.5 || fm === 0) {
    3627                 break;
    3628             }
    3629             // If inverse quadratic interpolation holds, use it
    3630             xi = (x1 - x2) / (x3 - x2);
    3631             ph = (f1 - f2) / (f3 - f2);
    3632             fl = 1 - Math.sqrt(1 - xi);
    3633             fh = Math.sqrt(xi);
    3634             if (fl < ph && ph < fh) {
    3635                 AL = (x3 - x1) / (x2 - x1);
    3636                 A = f1 / (f2 - f1);
    3637                 B = f3 / (f2 - f3);
    3638                 C = f1 / (f3 - f1);
    3639                 D = f2 / (f3 - f2);
    3640                 t = A * B + C * D * AL;
    3641             } else {
    3642                 t = 0.5 * rand;
    3643             }
    3644             // Adjust t away from the interval boundary
    3645             if (t < tl) {
    3646                 t = tl;
    3647             }
    3648             if (t > 1 - tl) {
    3649                 t = 1 - tl;
    3650             }
    3651             niter++;
    3652         } while (niter <= maxiter);
    3653         // console.log(niter);
    3654
    3655         return xm;
    3656     },
    3657
    3658     /**
    3659      * Find a small enclosing interval of the domain of a function by
    3660      * tightening the input interval x0.
    3661      * <p>
    3662      * This is a helper function which is used in {@link JXG.Math.Numerics.fminbr}
    3663      * and {@link JXG.Math.Numerics.fzero}
    3664      * to avoid search in an interval where the function is mostly undefined.
    3665      *
    3666      * @param {function} f
    3667      * @param {Array} x0 Start interval
    3668      * @param {Object} context Parent object in case f is method of it
    3669      * @returns Array
    3670      *
    3671      * @example
    3672      * var f = (x) => Math.sqrt(x);
    3673      * console.log(JXG.Math.Numerics.findDomain(f, [-5, 5]));
    3674      *
    3675      * // Output: [ -0.00020428174445492973, 5 ]
    3676      */
    3677     findDomain: function (f, x0, context) {
    3678         var a, b, c, fc,
    3679             x,
    3680             gr = 1 - 1 / 1.61803398875,
    3681             eps = 0.001,
    3682             cnt,
    3683             max_cnt = 20;
    3684
    3685         if (!Type.isArray(x0) || x0.length < 2) {
    3686             throw new Error(
    3687                 "JXG.Math.Numerics.findDomain: length of array x0 has to be at least two."
    3688             );
    3689         }
    3690
    3691         x = x0.slice();
    3692         a = x[0];
    3693         b = x[1];
    3694         fc = f.call(context, a);
    3695         if (isNaN(fc)) {
    3696             // Divide the interval with the golden ratio
    3697             // and keep a such that f(a) = NaN
    3698             cnt = 0;
    3699             while (b - a > eps && cnt < max_cnt) {
    3700                 c = (b - a) * gr + a;
    3701                 fc = f.call(context, c);
    3702                 if (isNaN(fc)) {
    3703                     a = c;
    3704                 } else {
    3705                     b = c;
    3706                 }
    3707                 cnt++;
    3708             }
    3709             x[0] = a;
    3710         }
    3711
    3712         a = x[0];
    3713         b = x[1];
    3714         fc = f.call(context, b);
    3715         if (isNaN(fc)) {
    3716             // Divide the interval with the golden ratio
    3717             // and keep b such that f(b) = NaN
    3718             cnt = 0;
    3719             while (b - a > eps && cnt < max_cnt) {
    3720                 c = b - (b - a) * gr;
    3721                 fc = f.call(context, c);
    3722                 if (isNaN(fc)) {
    3723                     b = c;
    3724                 } else {
    3725                     a = c;
    3726                 }
    3727                 cnt++;
    3728             }
    3729             x[0] = b;
    3730         }
    3731         return x;
    3732     },
    3733
    3734     /**
    3735      *
    3736      * Find minimum of an univariate function f.
    3737      * <p>
    3738      * Algorithm:
    3739      *  G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical
    3740      *  computations. M., Mir, 1980, p.180 of the Russian edition
    3741      *
    3742      * @param {function} f Function, whose minimum is to be found
    3743      * @param {Array} x0  Start interval enclosing the minimum
    3744      * @param {Object} [context] Parent object in case f is method of it
    3745      * @returns {Number} the approximation of the minimum value position
    3746      * @memberof JXG.Math.Numerics
    3747      **/
    3748     fminbr: function (f, x0, context) {
    3749         var a, b, x, v, w,
    3750             fx, fv, fw,
    3751             x00,
    3752             range, middle_range, tol_act, new_step,
    3753             p, q, t, ft,
    3754             r = (3.0 - Math.sqrt(5.0)) * 0.5,      // Golden section ratio
    3755             tol = Mat.eps,
    3756             sqrteps = Mat.eps, // Math.sqrt(Mat.eps),
    3757             maxiter = this.maxIterationsMinimize,
    3758             niter = 0;
    3759         // nfev = 0;
    3760
    3761         if (!Type.isArray(x0) || x0.length < 2) {
    3762             throw new Error(
    3763                 "JXG.Math.Numerics.fminbr: length of array x0 has to be at least two."
    3764             );
    3765         }
    3766
    3767         x00 = this.findDomain(f, x0, context);
    3768         a = x00[0];
    3769         b = x00[1];
    3770         v = a + r * (b - a);
    3771         fv = f.call(context, v);
    3772
    3773         // First step - always gold section
    3774         // nfev += 1;
    3775         x = v;
    3776         w = v;
    3777         fx = fv;
    3778         fw = fv;
    3779
    3780         while (niter < maxiter) {
    3781             // Range over the interval in which we are looking for the minimum
    3782             range = b - a;
    3783             middle_range = (a + b) * 0.5;
    3784
    3785             // Actual tolerance
    3786             tol_act = sqrteps * Math.abs(x) + tol / 3.0;
    3787
    3788             if (Math.abs(x - middle_range) + range * 0.5 <= 2.0 * tol_act) {
    3789                 // Acceptable approx. is found
    3790                 return x;
    3791             }
    3792
    3793             // Obtain the golden section step
    3794             new_step = r * (x < middle_range ? b - x : a - x);
    3795
    3796             // Decide if the interpolation can be tried. If x and w are distinct, interpolatiom may be tried
    3797             if (Math.abs(x - w) >= tol_act) {
    3798                 // Interpolation step is calculated as p/q;
    3799                 // division operation is delayed until last moment
    3800                 t = (x - w) * (fx - fv);
    3801                 q = (x - v) * (fx - fw);
    3802                 p = (x - v) * q - (x - w) * t;
    3803                 q = 2 * (q - t);
    3804
    3805                 if (q > 0) {
    3806                     p = -p; // q was calculated with the opposite sign; make q positive
    3807                 } else {
    3808                     q = -q; // // and assign possible minus to p
    3809                 }
    3810                 if (
    3811                     Math.abs(p) < Math.abs(new_step * q) && // If x+p/q falls in [a,b]
    3812                     p > q * (a - x + 2 * tol_act) &&        //  not too close to a and
    3813                     p < q * (b - x - 2 * tol_act)
    3814                 ) {
    3815                     // b, and isn't too large
    3816                     new_step = p / q; // it is accepted
    3817                 }
    3818                 // If p / q is too large then the
    3819                 // golden section procedure can
    3820                 // reduce [a,b] range to more
    3821                 // extent
    3822             }
    3823
    3824             // Adjust the step to be not less than tolerance
    3825             if (Math.abs(new_step) < tol_act) {
    3826                 if (new_step > 0) {
    3827                     new_step = tol_act;
    3828                 } else {
    3829                     new_step = -tol_act;
    3830                 }
    3831             }
    3832
    3833             // Obtain the next approximation to min
    3834             // and reduce the enveloping range
    3835
    3836             // Tentative point for the min
    3837             t = x + new_step;
    3838             ft = f.call(context, t);
    3839             // nfev += 1;
    3840
    3841             // t is a better approximation
    3842             if (ft <= fx) {
    3843                 // Reduce the range so that t would fall within it
    3844                 if (t < x) {
    3845                     b = x;
    3846                 } else {
    3847                     a = x;
    3848                 }
    3849
    3850                 // Assign the best approx to x
    3851                 v = w;
    3852                 w = x;
    3853                 x = t;
    3854
    3855                 fv = fw;
    3856                 fw = fx;
    3857                 fx = ft;
    3858                 // x remains the better approx
    3859             } else {
    3860                 // Reduce the range enclosing x
    3861                 if (t < x) {
    3862                     a = t;
    3863                 } else {
    3864                     b = t;
    3865                 }
    3866
    3867                 if (ft <= fw || w === x) {
    3868                     v = w;
    3869                     w = t;
    3870                     fv = fw;
    3871                     fw = ft;
    3872                 } else if (ft <= fv || v === x || v === w) {
    3873                     v = t;
    3874                     fv = ft;
    3875                 }
    3876             }
    3877             niter += 1;
    3878         }
    3879
    3880         return x;
    3881     },
    3882
    3883     /**
    3884      * GLOMIN seeks a global minimum of a function F(X) in an interval [A,B]
    3885      * and is the adaption of the algorithm GLOMIN by Richard Brent.
    3886      *
    3887      * Here is the original documentation:
    3888      * <pre>
    3889      *
    3890      * Discussion:
    3891      *
    3892      * This function assumes that F(X) is twice continuously differentiable over [A,B]
    3893      * and that |F''(X)| <= M for all X in [A,B].
    3894      *
    3895      * Licensing:
    3896      *   This code is distributed under the GNU LGPL license.
    3897      *
    3898      * Modified:
    3899      *
    3900      *   17 April 2008
    3901      *
    3902      * Author:
    3903      *
    3904      *   Original FORTRAN77 version by Richard Brent.
    3905      *   C version by John Burkardt.
    3906      *   https://people.math.sc.edu/Burkardt/c_src/brent/brent.c
    3907      *
    3908      * Reference:
    3909      *
    3910      *   Richard Brent,
    3911      *  Algorithms for Minimization Without Derivatives,
    3912      *   Dover, 2002,
    3913      *  ISBN: 0-486-41998-3,
    3914      *   LC: QA402.5.B74.
    3915      *
    3916      * Parameters:
    3917      *
    3918      *   Input, double A, B, the endpoints of the interval.
    3919      *  It must be the case that A < B.
    3920      *
    3921      *   Input, double C, an initial guess for the global
    3922      *  minimizer.  If no good guess is known, C = A or B is acceptable.
    3923      *
    3924      *  Input, double M, the bound on the second derivative.
    3925      *
    3926      *   Input, double MACHEP, an estimate for the relative machine
    3927      *  precision.
    3928      *
    3929      *   Input, double E, a positive tolerance, a bound for the
    3930      *  absolute error in the evaluation of F(X) for any X in [A,B].
    3931      *
    3932      *   Input, double T, a positive error tolerance.
    3933      *
    3934      *    Input, double F (double x ), a user-supplied
    3935      *  function whose global minimum is being sought.
    3936      *
    3937      *   Output, double *X, the estimated value of the abscissa
    3938      *  for which F attains its global minimum value in [A,B].
    3939      *
    3940      *   Output, double GLOMIN, the value F(X).
    3941      * </pre>
    3942      *
    3943      * In JSXGraph, some parameters of the original algorithm are set to fixed values:
    3944      * <ul>
    3945      *  <li> M = 10000000.0
    3946      *  <li> C = A or B, depending if f(A) <= f(B)
    3947      *  <li> T = JXG.Math.eps
    3948      *  <li> E = JXG.Math.eps * JXG.Math.eps
    3949      *  <li> MACHEP = JXG.Math.eps * JXG.Math.eps * JXG.Math.eps
    3950      * </ul>
    3951      * @param {function} f Function, whose global minimum is to be found
    3952      * @param {Array} x0 Array of length 2 determining the interval [A, B] for which the global minimum is to be found
    3953      * @returns {Array} [x, y] x is the position of the global minimum and y = f(x).
    3954      */
    3955     glomin: function (f, x0) {
    3956         var a0, a2, a3, d0, d1, d2, h,
    3957             k, m2,
    3958             p, q, qs,
    3959             r, s, sc,
    3960             y, y0, y1, y2, y3, yb,
    3961             z0, z1, z2,
    3962             a, b, c, x,
    3963             m = 10000000.0,
    3964             t = Mat.eps, // * Mat.eps,
    3965             e = Mat.eps * Mat.eps,
    3966             machep = Mat.eps * Mat.eps * Mat.eps;
    3967
    3968         a = x0[0];
    3969         b = x0[1];
    3970         c = (f(a) < f(b)) ? a : b;
    3971
    3972         a0 = b;
    3973         x = a0;
    3974         a2 = a;
    3975         y0 = f(b);
    3976         yb = y0;
    3977         y2 = f(a);
    3978         y = y2;
    3979
    3980         if (y0 < y) {
    3981             y = y0;
    3982         } else {
    3983             x = a;
    3984         }
    3985
    3986         if (m <= 0.0 || b <= a) {
    3987             return y;
    3988         }
    3989
    3990         m2 = 0.5 * (1.0 + 16.0 * machep) * m;
    3991
    3992         if (c <= a || b <= c) {
    3993             sc = 0.5 * (a + b);
    3994         } else {
    3995             sc = c;
    3996         }
    3997
    3998         y1 = f(sc);
    3999         k = 3;
    4000         d0 = a2 - sc;
    4001         h = 9.0 / 11.0;
    4002
    4003         if (y1 < y) {
    4004             x = sc;
    4005             y = y1;
    4006         }
    4007
    4008         for (; ;) {
    4009             d1 = a2 - a0;
    4010             d2 = sc - a0;
    4011             z2 = b - a2;
    4012             z0 = y2 - y1;
    4013             z1 = y2 - y0;
    4014             r = d1 * d1 * z0 - d0 * d0 * z1;
    4015             p = r;
    4016             qs = 2.0 * (d0 * z1 - d1 * z0);
    4017             q = qs;
    4018
    4019             if (k < 1000000 || y2 <= y) {
    4020                 for (; ;) {
    4021                     if (q * (r * (yb - y2) + z2 * q * ((y2 - y) + t)) <
    4022                         z2 * m2 * r * (z2 * q - r)) {
    4023
    4024                         a3 = a2 + r / q;
    4025                         y3 = f(a3);
    4026
    4027                         if (y3 < y) {
    4028                             x = a3;
    4029                             y = y3;
    4030                         }
    4031                     }
    4032                     k = ((1611 * k) % 1048576);
    4033                     q = 1.0;
    4034                     r = (b - a) * 0.00001 * k;
    4035
    4036                     if (z2 <= r) {
    4037                         break;
    4038                     }
    4039                 }
    4040             } else {
    4041                 k = ((1611 * k) % 1048576);
    4042                 q = 1.0;
    4043                 r = (b - a) * 0.00001 * k;
    4044
    4045                 while (r < z2) {
    4046                     if (q * (r * (yb - y2) + z2 * q * ((y2 - y) + t)) <
    4047                         z2 * m2 * r * (z2 * q - r)) {
    4048
    4049                         a3 = a2 + r / q;
    4050                         y3 = f(a3);
    4051
    4052                         if (y3 < y) {
    4053                             x = a3;
    4054                             y = y3;
    4055                         }
    4056                     }
    4057                     k = ((1611 * k) % 1048576);
    4058                     q = 1.0;
    4059                     r = (b - a) * 0.00001 * k;
    4060                 }
    4061             }
    4062
    4063             r = m2 * d0 * d1 * d2;
    4064             s = Math.sqrt(((y2 - y) + t) / m2);
    4065             h = 0.5 * (1.0 + h);
    4066             p = h * (p + 2.0 * r * s);
    4067             q = q + 0.5 * qs;
    4068             r = - 0.5 * (d0 + (z0 + 2.01 * e) / (d0 * m2));
    4069
    4070             if (r < s || d0 < 0.0) {
    4071                 r = a2 + s;
    4072             } else {
    4073                 r = a2 + r;
    4074             }
    4075
    4076             if (0.0 < p * q) {
    4077                 a3 = a2 + p / q;
    4078             } else {
    4079                 a3 = r;
    4080             }
    4081
    4082             for (; ;) {
    4083                 a3 = Math.max(a3, r);
    4084
    4085                 if (b <= a3) {
    4086                     a3 = b;
    4087                     y3 = yb;
    4088                 } else {
    4089                     y3 = f(a3);
    4090                 }
    4091
    4092                 if (y3 < y) {
    4093                     x = a3;
    4094                     y = y3;
    4095                 }
    4096
    4097                 d0 = a3 - a2;
    4098
    4099                 if (a3 <= r) {
    4100                     break;
    4101                 }
    4102
    4103                 p = 2.0 * (y2 - y3) / (m * d0);
    4104
    4105                 if ((1.0 + 9.0 * machep) * d0 <= Math.abs(p)) {
    4106                     break;
    4107                 }
    4108
    4109                 if (0.5 * m2 * (d0 * d0 + p * p) <= (y2 - y) + (y3 - y) + 2.0 * t) {
    4110                     break;
    4111                 }
    4112                 a3 = 0.5 * (a2 + a3);
    4113                 h = 0.9 * h;
    4114             }
    4115
    4116             if (b <= a3) {
    4117                 break;
    4118             }
    4119
    4120             a0 = sc;
    4121             sc = a2;
    4122             a2 = a3;
    4123             y0 = y1;
    4124             y1 = y2;
    4125             y2 = y3;
    4126         }
    4127
    4128         return [x, y];
    4129     },
    4130
    4131     /**
    4132      * Determine all roots of a polynomial with real or complex coefficients by using the
    4133      * iterative method attributed to Weierstrass, Durand, Kerner, Aberth, and Ehrlich. In particular,
    4134      * the iteration method with cubic convergence is used that is usually attributed to Ehrlich-Aberth.
    4135      * <p>
    4136      * The returned roots are sorted with respect to their real values.
    4137      * <p> This method makes use of the JSXGraph classes {@link JXG.Complex} and {@link JXG.C} to handle
    4138      * complex numbers.
    4139      *
    4140      * @param {Array} a Array of coefficients of the polynomial a[0] + a[1]*x+ a[2]*x**2...
    4141      * The coefficients are of type Number or JXG.Complex.
    4142      * @param {Number} [deg] Optional degree of the polynomial. Otherwise all entries are taken, with
    4143      * leading zeros removed.
    4144      * @param {Number} [tol=Number.EPSILON] Approximation tolerance
    4145      * @param {Number} [max_it=30] Maximum number of iterations
    4146      * @param {Array} [initial_values=null] Array of initial values for the roots. If not given,
    4147      * starting values are determined by the method of Ozawa.
    4148      * @returns {Array} Array of complex numbers (of JXG.Complex) approximating the roots of the polynomial.
    4149      * @memberof JXG.Math.Numerics
    4150      * @see JXG.Complex
    4151      * @see JXG.C
    4152      *
    4153      * @example
    4154      * // Polynomial p(z) = -1 + 1z^2
    4155      * var i, roots,
    4156      *     p = [-1, 0, 1];
    4157      *
    4158      * roots = JXG.Math.Numerics.polzeros(p);
    4159      * for (i = 0; i < roots.length; i++) {
    4160      *     console.log(i, roots[i].toString());
    4161      * }
    4162      * // Output:
    4163      *   0 -1 + -3.308722450212111e-24i
    4164      *   1 1 + 0i
    4165      *
    4166      * @example
    4167      * // Polynomial p(z) = -1 + 3z - 9z^2 + z^3 - 8z^6 + 9z^7 - 9z^8 + z^9
    4168      * var i, roots,
    4169      *     p = [-1, 3, -9, 1, 0, 0, -8, 9, -9, 1];
    4170      *
    4171      * roots = JXG.Math.Numerics.polzeros(p);
    4172      * for (i = 0; i < roots.length; i++) {
    4173      *     console.log(i, roots[i].toString());
    4174      * }
    4175      * // Output:
    4176      * 0 -0.7424155888401961 + 0.4950476539211721i
    4177      * 1 -0.7424155888401961 + -0.4950476539211721i
    4178      * 2 0.16674869833354108 + 0.2980502714610669i
    4179      * 3 0.16674869833354108 + -0.29805027146106694i
    4180      * 4 0.21429002063640837 + 1.0682775088132996i
    4181      * 5 0.21429002063640842 + -1.0682775088132999i
    4182      * 6 0.861375497926218 + -0.6259177003583295i
    4183      * 7 0.8613754979262181 + 0.6259177003583295i
    4184      * 8 8.000002743888055 + -1.8367099231598242e-40i
    4185      *
    4186      */
    4187     polzeros: function (coeffs, deg, tol, max_it, initial_values) {
    4188         var i, le, off, it,
    4189             debug = false,
    4190             cc = [],
    4191             obvious = [],
    4192             roots = [],
    4193
    4194             /**
    4195              * Horner method to evaluate polynomial or the derivative thereof for complex numbers,
    4196              * i.e. coefficients and variable are complex.
    4197              * @function
    4198              * @param {Array} a Array of complex coefficients of the polynomial a[0] + a[1]*x+ a[2]*x**2...
    4199              * @param {JXG.Complex} z Value for which the polynomial will be evaluated.
    4200              * @param {Boolean} [derivative=false] If true the derivative will be evaluated.
    4201              * @ignore
    4202              */
    4203             hornerComplex = function (a, z, derivative) {
    4204                 var i, s,
    4205                     n = a.length - 1;
    4206
    4207                 derivative = derivative || false;
    4208                 if (derivative) {
    4209                     // s = n * a_n
    4210                     s = JXG.C.mult(n, a[n]);
    4211                     for (i = n - 1; i > 0; i--) {
    4212                         // s = s * z + i * a_i
    4213                         s.mult(z);
    4214                         s.add(JXG.C.mult(a[i], i));
    4215                     }
    4216                 } else {
    4217                     // s = a_n
    4218                     s = JXG.C.copy(a[n]);
    4219                     for (i = n - 1; i >= 0; i--) {
    4220                         // s = s * z + a_i
    4221                         s.mult(z);
    4222                         s.add(a[i]);
    4223                     }
    4224                 }
    4225                 return s;
    4226             },
    4227
    4228             /**
    4229              * Horner method to evaluate reciprocal polynomial or the derivative thereof for complex numbers,
    4230              * i.e. coefficients and variable are complex.
    4231              * @function
    4232              * @param {Array} a Array of complex coefficients of the polynomial a[0] + a[1]*x+ a[2]*x**2...
    4233              * @param {JXG.Complex} z Value for which the reciprocal polynomial will be evaluated.
    4234              * @param {Boolean} [derivative=false] If true the derivative will be evaluated.
    4235              * @ignore
    4236              */
    4237             hornerRec = function (a, x, derivative) {
    4238                 var i, s,
    4239                     n = a.length - 1;
    4240
    4241                 derivative = derivative || false;
    4242                 if (derivative) {
    4243                     // s = n * a_0
    4244                     s = JXG.C.mult(n, a[0]);
    4245                     for (i = n - 1; i > 0; i--) {
    4246                         // s = s * x + i * a_{n-i}
    4247                         s.mult(x);
    4248                         s.add(JXG.C.mult(a[n - i], i));
    4249                     }
    4250                 } else {
    4251                     // s = a_0
    4252                     s = JXG.C.copy(a[0]);
    4253                     for (i = n - 1; i >= 0; i--) {
    4254                         // s = s * x + a_{n-i}
    4255                         s.mult(x);
    4256                         s.add(a[n - i]);
    4257                     }
    4258                 }
    4259                 return s;
    4260             },
    4261
    4262             /**
    4263              * Horner method to evaluate real polynomial at a real value.
    4264              * @function
    4265              * @param {Array} a Array of real coefficients of the polynomial a[0] + a[1]*x+ a[2]*x**2...
    4266              * @param {Number} z Value for which the polynomial will be evaluated.
    4267              * @ignore
    4268              */
    4269             horner = function (a, x) {
    4270                 var i, s,
    4271                     n = a.length - 1;
    4272
    4273                 s = a[n];
    4274                 for (i = n - 1; i >= 0; i--) {
    4275                     s = s * x + a[i];
    4276                 }
    4277                 return s;
    4278             },
    4279
    4280             /**
    4281              * Determine start values for the Aberth iteration, see
    4282              * Ozawa, "An experimental study of the starting values
    4283              * of the Durand-Kerner-Aberth iteration" (1995).
    4284              *
    4285              * @function
    4286              * @param {Array} a Array of complex coefficients of the polynomial a[0] + a[1]*x+ a[2]*x**2...
    4287              * @returns {Array} Array Initial values for the roots.
    4288              * @ignore
    4289              */
    4290             initial_guess = function (a) {
    4291                 var i, r,
    4292                     n = a.length - 1, // degree
    4293                     alpha1 = Math.PI * 2 / n,
    4294                     alpha0 = Math.PI / n * 0.5,
    4295                     b, z,
    4296                     init = [];
    4297
    4298
    4299                 // From Ozawa, "An experimental study of the starting values
    4300                 // of the Durand-Kerner-Aberth iteration" (1995)
    4301
    4302                 // b is the arithmetic mean of the roots.
    4303                 // With is Vieta's formula <https://en.wikipedia.org/wiki/Vieta%27s_formulas>
    4304                 //   b = -a_{n-1} / (n * a_n)
    4305                 b = JXG.C.mult(-1, a[n - 1]);
    4306                 b.div(JXG.C.mult(n, a[n]));
    4307
    4308                 // r is the geometric mean of the deviations |b - root_i|.
    4309                 // Using
    4310                 //   p(z) = a_n prod(z - root_i)
    4311                 // and therefore
    4312                 //   |p(b)| = |a_n| prod(|b - root_i|)
    4313                 // we arrive at:
    4314                 //   r = |p(b)/a_n|^(1/n)
    4315                 z = JXG.C.div(hornerComplex(a, b), a[n]);
    4316                 r = Math.pow(JXG.C.abs(z), 1 / n);
    4317                 if (r === 0) { r = 1; }
    4318
    4319                 for (i = 0; i < n; i++) {
    4320                     a = new JXG.Complex(r * Math.cos(alpha1 * i + alpha0), r * Math.sin(alpha1 * i + alpha0));
    4321                     init[i] = JXG.C.add(b, a);
    4322                 }
    4323
    4324                 return init;
    4325             },
    4326
    4327             /**
    4328              * Ehrlich-Aberth iteration. The stopping criterion is from
    4329              * D.A. Bini, "Numerical computation of polynomial zeros
    4330              * by means of Aberths's method", Numerical Algorithms (1996).
    4331              *
    4332              * @function
    4333              * @param {Array} a Array of complex coefficients of the polynomial a[0] + a[1]*x+ a[2]*x**2...
    4334              * @param {Number} mu Machine precision
    4335              * @param {Number} max_it Maximum number of iterations
    4336              * @param {Array} z Initial guess for the roots. Will be changed in place.
    4337              * @returns {Number} Number of iterations
    4338              * @ignore
    4339              */
    4340             aberthIteration = function (cc, mu, max_it, z) {
    4341                 var k, i, j,
    4342                     done = [],
    4343                     cr = [],
    4344                     gamma, x,
    4345                     done_sum = 0,
    4346                     num, denom, s, pp,
    4347                     n = z.length;
    4348
    4349                 for (i = 0; i < n; i++) {
    4350                     done.push(false);
    4351                 }
    4352                 for (i = 0; i < cc.length; i++) {
    4353                     cr.push(JXG.C.abs(cc[i]) * (4 * i + 1));
    4354                 }
    4355                 for (k = 0; k < max_it && done_sum < n; k++) {
    4356                     for (i = 0; i < n; i++) {
    4357                         if (done[i]) {
    4358                             continue;
    4359                         }
    4360                         num = hornerComplex(cc, z[i]);
    4361                         x = JXG.C.abs(z[i]);
    4362
    4363                         // Stopping criterion by D.A. Bini
    4364                         // "Numerical computation of polynomial zeros
    4365                         // by means of Aberths's method", Numerical Algorithms (1996).
    4366                         //
    4367                         if (JXG.C.abs(num) < mu * horner(cr, x)) {
    4368                             done[i] = true;
    4369                             done_sum++;
    4370                             if (done_sum === n) {
    4371                                 break;
    4372                             }
    4373                             continue;
    4374                         }
    4375
    4376                         // num = P(z_i) / P'(z_i)
    4377                         if (x > 1) {
    4378                             gamma = JXG.C.div(1, z[i]);
    4379                             pp = hornerRec(cc, gamma, true);
    4380                             pp.div(hornerRec(cc, gamma));
    4381                             pp.mult(gamma);
    4382                             num = JXG.C.sub(n, pp);
    4383                             num = JXG.C.div(z[i], num);
    4384                         } else {
    4385                             num.div(hornerComplex(cc, z[i], true));
    4386                         }
    4387
    4388                         // denom = sum_{i\neq j} 1 / (z_i  - z_j)
    4389                         denom = new JXG.Complex(0);
    4390                         for (j = 0; j < n; j++) {
    4391                             if (j === i) {
    4392                                 continue;
    4393                             }
    4394                             s = JXG.C.sub(z[i], z[j]);
    4395                             s = JXG.C.div(1, s);
    4396                             denom.add(s);
    4397                         }
    4398
    4399                         // num = num / 1 - num * sum_{i\neq j} 1 / (z_i - z_j)
    4400                         denom.mult(num);
    4401                         denom = JXG.C.sub(1, denom);
    4402                         num.div(denom);
    4403                         // z_i = z_i - num
    4404                         z[i].sub(num);
    4405                     }
    4406                 }
    4407
    4408                 return k;
    4409             };
    4410
    4411
    4412         tol = tol || Number.EPSILON;
    4413         max_it = max_it || 30;
    4414
    4415         le = coeffs.length;
    4416         if (JXG.isNumber(deg) && deg >= 0 && deg < le - 1) {
    4417             le = deg + 1;
    4418         }
    4419
    4420         // Convert coefficient array to complex numbers
    4421         for (i = 0; i < le; i++) {
    4422             cc.push(new JXG.Complex(coeffs[i]));
    4423         }
    4424
    4425         // Search for (multiple) roots at x=0
    4426         for (i = 0; i < le; i++) {
    4427             if (cc[i].real !== 0 || cc[i].imaginary !== 0) {
    4428                 off = i;
    4429                 break;
    4430             }
    4431         }
    4432
    4433         // Deflate root x=0, store roots at x=0 in obvious
    4434         for (i = 0; i < off; i++) {
    4435             obvious.push(new JXG.Complex(0));
    4436         }
    4437         cc = cc.slice(off);
    4438         le = cc.length;
    4439
    4440         // Remove leading zeros from the coefficient array
    4441         for (i = le - 1; i >= 0; i--) {
    4442             if (cc[i].real !== 0 || cc[i].imaginary !== 0) {
    4443                 break;
    4444             }
    4445             cc.pop();
    4446         }
    4447         le = cc.length;
    4448         if (le === 0) {
    4449             return [];
    4450         }
    4451
    4452         // From now on we can assume that the
    4453         // constant coefficient and the leading coefficient
    4454         // are not zero.
    4455         if (initial_values) {
    4456             for (i = 0; i < le - 1; i++) {
    4457                 roots.push(new JXG.Complex(initial_values[i]));
    4458             }
    4459         } else {
    4460             roots = initial_guess(cc);
    4461         }
    4462         it = aberthIteration(cc, tol, max_it, roots);
    4463
    4464         // Append the roots at x=0
    4465         roots = obvious.concat(roots);
    4466
    4467         if (debug) {
    4468             console.log("Iterations:", it);
    4469             console.log('Roots:');
    4470             for (i = 0; i < roots.length; i++) {
    4471                 console.log(i, roots[i].toString(), JXG.C.abs(hornerComplex(cc, roots[i])));
    4472             }
    4473         }
    4474
    4475         // Sort roots according to their real part
    4476         roots.sort(function (a, b) {
    4477             if (a.real < b.real) {
    4478                 return -1;
    4479             }
    4480             if (a.real > b.real) {
    4481                 return 1;
    4482             }
    4483             return 0;
    4484         });
    4485
    4486         return roots;
    4487     },
    4488
    4489     /**
    4490      * Implements the Ramer-Douglas-Peucker algorithm.
    4491      * It discards points which are not necessary from the polygonal line defined by the point array
    4492      * pts. The computation is done in screen coordinates.
    4493      * Average runtime is O(nlog(n)), worst case runtime is O(n^2), where n is the number of points.
    4494      * @param {Array} pts Array of {@link JXG.Coords}
    4495      * @param {Number} eps If the absolute value of a given number <tt>x</tt> is smaller than <tt>eps</tt> it is considered to be equal <tt>0</tt>.
    4496      * @returns {Array} An array containing points which represent an apparently identical curve as the points of pts do, but contains fewer points.
    4497      * @memberof JXG.Math.Numerics
    4498      */
    4499     RamerDouglasPeucker: function (pts, eps) {
    4500         var allPts = [],
    4501             newPts = [],
    4502             i, k, len,
    4503             endless = true,
    4504
    4505             /**
    4506              * findSplit() is a subroutine of {@link JXG.Math.Numerics.RamerDouglasPeucker}.
    4507              * It searches for the point between index i and j which
    4508              * has the largest distance from the line between the points i and j.
    4509              * @param {Array} pts Array of {@link JXG.Coords}
    4510              * @param {Number} i Index of a point in pts
    4511              * @param {Number} j Index of a point in pts
    4512              * @ignore
    4513              * @private
    4514              */
    4515             findSplit = function (pts, i, j) {
    4516                 var d, k, ci, cj, ck,
    4517                     x0, y0, x1, y1,
    4518                     den, lbda,
    4519                     eps = Mat.eps * Mat.eps,
    4520                     huge = 10000,
    4521                     dist = 0,
    4522                     f = i;
    4523
    4524                 if (j - i < 2) {
    4525                     return [-1.0, 0];
    4526                 }
    4527
    4528                 ci = pts[i].scrCoords;
    4529                 cj = pts[j].scrCoords;
    4530
    4531                 if (isNaN(ci[1]) || isNaN(ci[2])) {
    4532                     return [NaN, i];
    4533                 }
    4534                 if (isNaN(cj[1]) || isNaN(cj[2])) {
    4535                     return [NaN, j];
    4536                 }
    4537
    4538                 for (k = i + 1; k < j; k++) {
    4539                     ck = pts[k].scrCoords;
    4540                     if (isNaN(ck[1]) || isNaN(ck[2])) {
    4541                         return [NaN, k];
    4542                     }
    4543
    4544                     x0 = ck[1] - ci[1];
    4545                     y0 = ck[2] - ci[2];
    4546                     x1 = cj[1] - ci[1];
    4547                     y1 = cj[2] - ci[2];
    4548                     x0 = x0 === Infinity ? huge : x0;
    4549                     y0 = y0 === Infinity ? huge : y0;
    4550                     x1 = x1 === Infinity ? huge : x1;
    4551                     y1 = y1 === Infinity ? huge : y1;
    4552                     x0 = x0 === -Infinity ? -huge : x0;
    4553                     y0 = y0 === -Infinity ? -huge : y0;
    4554                     x1 = x1 === -Infinity ? -huge : x1;
    4555                     y1 = y1 === -Infinity ? -huge : y1;
    4556                     den = x1 * x1 + y1 * y1;
    4557
    4558                     if (den > eps) {
    4559                         lbda = (x0 * x1 + y0 * y1) / den;
    4560
    4561                         if (lbda < 0.0) {
    4562                             lbda = 0.0;
    4563                         } else if (lbda > 1.0) {
    4564                             lbda = 1.0;
    4565                         }
    4566
    4567                         x0 = x0 - lbda * x1;
    4568                         y0 = y0 - lbda * y1;
    4569                         d = x0 * x0 + y0 * y0;
    4570                     } else {
    4571                         lbda = 0.0;
    4572                         d = x0 * x0 + y0 * y0;
    4573                     }
    4574
    4575                     if (d > dist) {
    4576                         dist = d;
    4577                         f = k;
    4578                     }
    4579                 }
    4580                 return [Math.sqrt(dist), f];
    4581             },
    4582             /**
    4583              * RDP() is a private subroutine of {@link JXG.Math.Numerics.RamerDouglasPeucker}.
    4584              * It runs recursively through the point set and searches the
    4585              * point which has the largest distance from the line between the first point and
    4586              * the last point. If the distance from the line is greater than eps, this point is
    4587              * included in our new point set otherwise it is discarded.
    4588              * If it is taken, we recursively apply the subroutine to the point set before
    4589              * and after the chosen point.
    4590              * @param {Array} pts Array of {@link JXG.Coords}
    4591              * @param {Number} i Index of an element of pts
    4592              * @param {Number} j Index of an element of pts
    4593              * @param {Number} eps If the absolute value of a given number <tt>x</tt> is smaller than <tt>eps</tt> it is considered to be equal <tt>0</tt>.
    4594              * @param {Array} newPts Array of {@link JXG.Coords}
    4595              * @ignore
    4596              * @private
    4597              */
    4598             RDP = function (pts, i, j, eps, newPts) {
    4599                 var result = findSplit(pts, i, j),
    4600                     k = result[1];
    4601
    4602                 if (isNaN(result[0])) {
    4603                     RDP(pts, i, k - 1, eps, newPts);
    4604                     newPts.push(pts[k]);
    4605                     do {
    4606                         ++k;
    4607                     } while (k <= j && isNaN(pts[k].scrCoords[1] + pts[k].scrCoords[2]));
    4608                     if (k <= j) {
    4609                         newPts.push(pts[k]);
    4610                     }
    4611                     RDP(pts, k + 1, j, eps, newPts);
    4612                 } else if (result[0] > eps) {
    4613                     RDP(pts, i, k, eps, newPts);
    4614                     RDP(pts, k, j, eps, newPts);
    4615                 } else {
    4616                     newPts.push(pts[j]);
    4617                 }
    4618             };
    4619
    4620         len = pts.length;
    4621
    4622         i = 0;
    4623         while (endless) {
    4624             // Search for the next point without NaN coordinates
    4625             while (i < len && isNaN(pts[i].scrCoords[1] + pts[i].scrCoords[2])) {
    4626                 i += 1;
    4627             }
    4628             // Search for the next position of a NaN point
    4629             k = i + 1;
    4630             while (k < len && !isNaN(pts[k].scrCoords[1] + pts[k].scrCoords[2])) {
    4631                 k += 1;
    4632             }
    4633             k--;
    4634
    4635             // Only proceed if something is left
    4636             if (i < len && k > i) {
    4637                 newPts = [];
    4638                 newPts[0] = pts[i];
    4639                 RDP(pts, i, k, eps, newPts);
    4640                 allPts = allPts.concat(newPts);
    4641             }
    4642             if (i >= len) {
    4643                 break;
    4644             }
    4645             // Push the NaN point
    4646             if (k < len - 1) {
    4647                 allPts.push(pts[k + 1]);
    4648             }
    4649             i = k + 1;
    4650         }
    4651
    4652         return allPts;
    4653     },
    4654
    4655     /**
    4656      * Old name for the implementation of the Ramer-Douglas-Peucker algorithm.
    4657      * @deprecated Use {@link JXG.Math.Numerics.RamerDouglasPeucker}
    4658      * @memberof JXG.Math.Numerics
    4659      */
    4660     RamerDouglasPeuker: function (pts, eps) {
    4661         JXG.deprecated("Numerics.RamerDouglasPeuker()", "Numerics.RamerDouglasPeucker()");
    4662         return this.RamerDouglasPeucker(pts, eps);
    4663     },
    4664
    4665     /**
    4666      * Implements the Visvalingam-Whyatt algorithm.
    4667      * See M. Visvalingam, J. D. Whyatt:
    4668      * "Line generalisation by repeated elimination of the smallest area", C.I.S.R.G Discussion paper 10, July 1992
    4669      *
    4670      * The algorithm discards points which are not necessary from the polygonal line defined by the point array
    4671      * pts (consisting of type JXG.Coords).
    4672      * @param {Array} pts Array of {@link JXG.Coords}
    4673      * @param {Number} numPoints Number of remaining intermediate points. The first and the last point of the original points will
    4674      *    be taken in any case.
    4675      * @returns {Array} An array containing points which approximates the curve defined by pts.
    4676      * @memberof JXG.Math.Numerics
    4677      *
    4678      * @example
    4679      *     var i, p = [];
    4680      *     for (i = 0; i < 5; ++i) {
    4681      *         p.push(board.create('point', [Math.random() * 12 - 6, Math.random() * 12 - 6]));
    4682      *     }
    4683      *
    4684      *     // Plot a cardinal spline curve
    4685      *     var splineArr = JXG.Math.Numerics.CardinalSpline(p, 0.5);
    4686      *     var cu1 = board.create('curve', splineArr, {strokeColor: 'green'});
    4687      *
    4688      *     var c = board.create('curve', [[0],[0]], {strokeWidth: 2, strokeColor: 'black'});
    4689      *     c.updateDataArray = function() {
    4690      *         var i, len, points;
    4691      *
    4692      *         // Reduce number of intermediate points with Visvakingam-Whyatt to 6
    4693      *         points = JXG.Math.Numerics.Visvalingam(cu1.points, 6);
    4694      *         // Plot the remaining points
    4695      *         len = points.length;
    4696      *         this.dataX = [];
    4697      *         this.dataY = [];
    4698      *         for (i = 0; i < len; i++) {
    4699      *             this.dataX.push(points[i].usrCoords[1]);
    4700      *             this.dataY.push(points[i].usrCoords[2]);
    4701      *         }
    4702      *     };
    4703      *     board.update();
    4704      *
    4705      * </pre><div id="JXGce0cc55c-b592-11e6-8270-104a7d3be7eb" class="jxgbox" style="width: 300px; height: 300px;"></div>
    4706      * <script type="text/javascript">
    4707      *     (function() {
    4708      *         var board = JXG.JSXGraph.initBoard('JXGce0cc55c-b592-11e6-8270-104a7d3be7eb',
    4709      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    4710      *
    4711      *         var i, p = [];
    4712      *         for (i = 0; i < 5; ++i) {
    4713      *             p.push(board.create('point', [Math.random() * 12 - 6, Math.random() * 12 - 6]));
    4714      *         }
    4715      *
    4716      *         // Plot a cardinal spline curve
    4717      *         var splineArr = JXG.Math.Numerics.CardinalSpline(p, 0.5);
    4718      *         var cu1 = board.create('curve', splineArr, {strokeColor: 'green'});
    4719      *
    4720      *         var c = board.create('curve', [[0],[0]], {strokeWidth: 2, strokeColor: 'black'});
    4721      *         c.updateDataArray = function() {
    4722      *             var i, len, points;
    4723      *
    4724      *             // Reduce number of intermediate points with Visvakingam-Whyatt to 6
    4725      *             points = JXG.Math.Numerics.Visvalingam(cu1.points, 6);
    4726      *             // Plot the remaining points
    4727      *             len = points.length;
    4728      *             this.dataX = [];
    4729      *             this.dataY = [];
    4730      *             for (i = 0; i < len; i++) {
    4731      *                 this.dataX.push(points[i].usrCoords[1]);
    4732      *                 this.dataY.push(points[i].usrCoords[2]);
    4733      *             }
    4734      *         };
    4735      *         board.update();
    4736      *
    4737      *     })();
    4738      *
    4739      * </script><pre>
    4740      *
    4741      */
    4742     Visvalingam: function (pts, numPoints) {
    4743         var i,
    4744             len,
    4745             vol,
    4746             lastVol,
    4747             linkedList = [],
    4748             heap = [],
    4749             points = [],
    4750             lft,
    4751             rt,
    4752             lft2,
    4753             rt2,
    4754             obj;
    4755
    4756         len = pts.length;
    4757
    4758         // At least one intermediate point is needed
    4759         if (len <= 2) {
    4760             return pts;
    4761         }
    4762
    4763         // Fill the linked list
    4764         // Add first point to the linked list
    4765         linkedList[0] = {
    4766             used: true,
    4767             lft: null,
    4768             node: null
    4769         };
    4770
    4771         // Add all intermediate points to the linked list,
    4772         // whose triangle area is nonzero.
    4773         lft = 0;
    4774         for (i = 1; i < len - 1; i++) {
    4775             vol = Math.abs(
    4776                 JXG.Math.Numerics.det([
    4777                     pts[i - 1].usrCoords,
    4778                     pts[i].usrCoords,
    4779                     pts[i + 1].usrCoords
    4780                 ])
    4781             );
    4782             if (!isNaN(vol)) {
    4783                 obj = {
    4784                     v: vol,
    4785                     idx: i
    4786                 };
    4787                 heap.push(obj);
    4788                 linkedList[i] = {
    4789                     used: true,
    4790                     lft: lft,
    4791                     node: obj
    4792                 };
    4793                 linkedList[lft].rt = i;
    4794                 lft = i;
    4795             }
    4796         }
    4797
    4798         // Add last point to the linked list
    4799         linkedList[len - 1] = {
    4800             used: true,
    4801             rt: null,
    4802             lft: lft,
    4803             node: null
    4804         };
    4805         linkedList[lft].rt = len - 1;
    4806
    4807         // Remove points until only numPoints intermediate points remain
    4808         lastVol = -Infinity;
    4809         while (heap.length > numPoints) {
    4810             // Sort the heap with the updated volume values
    4811             heap.sort(function (a, b) {
    4812                 // descending sort
    4813                 return b.v - a.v;
    4814             });
    4815
    4816             // Remove the point with the smallest triangle
    4817             i = heap.pop().idx;
    4818             linkedList[i].used = false;
    4819             lastVol = linkedList[i].node.v;
    4820
    4821             // Update the pointers of the linked list
    4822             lft = linkedList[i].lft;
    4823             rt = linkedList[i].rt;
    4824             linkedList[lft].rt = rt;
    4825             linkedList[rt].lft = lft;
    4826
    4827             // Update the values for the volumes in the linked list
    4828             lft2 = linkedList[lft].lft;
    4829             if (lft2 !== null) {
    4830                 vol = Math.abs(
    4831                     JXG.Math.Numerics.det([
    4832                         pts[lft2].usrCoords,
    4833                         pts[lft].usrCoords,
    4834                         pts[rt].usrCoords
    4835                     ])
    4836                 );
    4837
    4838                 linkedList[lft].node.v = vol >= lastVol ? vol : lastVol;
    4839             }
    4840             rt2 = linkedList[rt].rt;
    4841             if (rt2 !== null) {
    4842                 vol = Math.abs(
    4843                     JXG.Math.Numerics.det([
    4844                         pts[lft].usrCoords,
    4845                         pts[rt].usrCoords,
    4846                         pts[rt2].usrCoords
    4847                     ])
    4848                 );
    4849
    4850                 linkedList[rt].node.v = vol >= lastVol ? vol : lastVol;
    4851             }
    4852         }
    4853
    4854         // Return an array with the remaining points
    4855         i = 0;
    4856         points = [pts[i]];
    4857         do {
    4858             i = linkedList[i].rt;
    4859             points.push(pts[i]);
    4860         } while (linkedList[i].rt !== null);
    4861
    4862         return points;
    4863     }
    4864 };
    4865
    4866 export default Mat.Numerics;
    4867