      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, Float32Array: true */
     33 /*jslint nomen: true, plusplus: true, bitwise: true*/
     34
     35 /**
     36  * @fileoverview In this file the namespace JXG.Math is defined, which is the base namespace
     37  * for namespaces like JXG.Math.Numerics, JXG.Math.Plot, JXG.Math.Statistics, JXG.Math.Clip etc.
     38  */
     39 import JXG from "../jxg.js";
     40 import Type from "../utils/type.js";
     41
     42 var undef,
     43     /*
     44      * Dynamic programming approach for recursive functions.
     45      * From "Speed up your JavaScript, Part 3" by Nicholas C. Zakas.
     46      * @see JXG.Math.factorial
     47      * @see JXG.Math.binomial
     48      * http://blog.thejit.org/2008/09/05/memoization-in-javascript/
     49      *
     50      * This method is hidden, because it is only used in JXG.Math. If someone wants
     51      * to use it in JSXGraph outside of JXG.Math, it should be moved to jsxgraph.js
     52      */
     53     memoizer = function (f) {
     54         var cache, join;
     55
     56         if (f.memo) {
     57             return f.memo;
     58         }
     59
     60         cache = {};
     61         join = Array.prototype.join;
     62
     63         f.memo = function () {
     64             var key = join.call(arguments);
     65
     66             // Seems to be a bit faster than "if (a in b)"
     67             return cache[key] !== undef ? cache[key] : (cache[key] = f.apply(this, arguments));
     68         };
     69
     70         return f.memo;
     71     };
     72
     73 /**
     74  * Math namespace. Contains mathematics related methods which are
     75  * specific to JSXGraph or which extend the JavaScript Math class.
     76  * @namespace
     77  */
     78 JXG.Math = {
     79     /**
     80      * eps defines the closeness to zero. If the absolute value of a given number is smaller
     81      * than eps, it is considered to be equal to zero.
     82      * @type Number
     83      */
     84     eps: 0.000001,
     85
     86     /**
     87      * Determine the relative difference between two numbers.
     88      * @param  {Number} a First number
     89      * @param  {Number} b Second number
     90      * @returns {Number}  Relative difference between a and b: |a-b| / max(|a|, |b|)
     91      */
     92     relDif: function (a, b) {
     93         var c = Math.abs(a),
     94             d = Math.abs(b);
     95
     96         d = Math.max(c, d);
     97
     98         return d === 0.0 ? 0.0 : Math.abs(a - b) / d;
     99     },
    100
    101     /**
    102      * The JavaScript implementation of the % operator returns the symmetric modulo.
    103      * mod and "%" are both identical if a >= 0 and m >= 0 but the results differ if a or m < 0.
    104      * @param {Number} a
    105      * @param {Number} m
    106      * @returns {Number} Mathematical modulo <tt>a mod m</tt>
    107      */
    108     mod: function (a, m) {
    109         return a - Math.floor(a / m) * m;
    110     },
    111
    112     /**
    113      * Translate <code>x</code> into the interval <code>[a, b)</code> by adding
    114      * a multiple of <code>b - a</code>.
    115      * @param {Number} x
    116      * @param {Number} a
    117      * @param {Number} b
    118      */
    119     wrap: function (x, a, b) {
    120         return a + this.mod(x - a, b - a);
    121     },
    122
    123     /**
    124      * Clamp <code>x</code> within the interval <code>[a, b]</code>. If
    125      * <code>x</code> is below <code>a</code>, increase it to <code>a</code>. If
    126      * it's above <code>b</code>, decrease it to <code>b</code>.
    127      */
    128     clamp: function (x, a, b) {
    129         return Math.min(Math.max(x, a), b);
    130     },
    131
    132     /**
    133      * A way of clamping a periodic variable. If <code>x</code> is congruent mod
    134      * <code>period</code> to a point in <code>[a, b]</code>, return that point.
    135      * Otherwise, wrap it into <code>[mid - period/2, mid + period/2]</code>,
    136      * where <code>mid</code> is the mean of <code>a</code> and <code>b</code>,
    137      * and then clamp it to <code>[a, b]</code> from there.
    138      */
    139     wrapAndClamp: function (x, a, b, period) {
    140         var mid = 0.5 * (a + b),
    141             half_period = 0.5 * period;
    142
    143         return this.clamp(
    144             this.wrap(
    145                 x,
    146                 mid - half_period,
    147                 mid + half_period
    148             ),
    149             a,
    150             b
    151         );
    152     },
    153
    154     /**
    155      * Initializes a vector of size <tt>n</tt> wih coefficients set to the init value (default 0)
    156      * @param {Number} n Length of the vector
    157      * @param {Number} [init=0] Initial value for each coefficient
    158      * @returns {Array} An array of length <tt>n</tt>
    159      */
    160     vector: function (n, init) {
    161         var r, i;
    162
    163         init = init || 0;
    164         r = [];
    165
    166         for (i = 0; i < n; i++) {
    167             r[i] = init;
    168         }
    169
    170         return r;
    171     },
    172
    173     /**
    174      * Initializes a matrix as an array of rows with the given value.
    175      * @param {Number} n Number of rows
    176      * @param {Number} [m=n] Number of columns
    177      * @param {Number} [init=0] Initial value for each coefficient
    178      * @returns {Array} A <tt>n</tt> times <tt>m</tt>-matrix represented by a
    179      * two-dimensional array. The inner arrays hold the columns, the outer array holds the rows.
    180      */
    181     matrix: function (n, m, init) {
    182         var r, i, j;
    183
    184         init = init || 0;
    185         m = m || n;
    186         r = [];
    187
    188         for (i = 0; i < n; i++) {
    189             r[i] = [];
    190
    191             for (j = 0; j < m; j++) {
    192                 r[i][j] = init;
    193             }
    194         }
    195
    196         return r;
    197     },
    198
    199     /**
    200      * Generates an identity matrix. If n is a number and m is undefined or not a number, a square matrix is generated,
    201      * if n and m are both numbers, an nxm matrix is generated.
    202      * @param {Number} n Number of rows
    203      * @param {Number} [m=n] Number of columns
    204      * @returns {Array} A square matrix of length <tt>n</tt> with all coefficients equal to 0 except a_(i,i), i out of (1, ..., n), if <tt>m</tt> is undefined or not a number
    205      * or a <tt>n</tt> times <tt>m</tt>-matrix with a_(i,j) = 0 and a_(i,i) = 1 if m is a number.
    206      */
    207     identity: function (n, m) {
    208         var r, i;
    209
    210         if (m === undef && typeof m !== "number") {
    211             m = n;
    212         }
    213
    214         r = this.matrix(n, m);
    215
    216         for (i = 0; i < Math.min(n, m); i++) {
    217             r[i][i] = 1;
    218         }
    219
    220         return r;
    221     },
    222
    223     /**
    224      * Generates a 4x4 matrix for 3D to 2D projections.
    225      * @param {Number} l Left
    226      * @param {Number} r Right
    227      * @param {Number} t Top
    228      * @param {Number} b Bottom
    229      * @param {Number} n Near
    230      * @param {Number} f Far
    231      * @returns {Array} 4x4 Matrix
    232      */
    233     frustum: function (l, r, b, t, n, f) {
    234         var ret = this.matrix(4, 4);
    235
    236         ret[0][0] = (n * 2) / (r - l);
    237         ret[0][1] = 0;
    238         ret[0][2] = (r + l) / (r - l);
    239         ret[0][3] = 0;
    240
    241         ret[1][0] = 0;
    242         ret[1][1] = (n * 2) / (t - b);
    243         ret[1][2] = (t + b) / (t - b);
    244         ret[1][3] = 0;
    245
    246         ret[2][0] = 0;
    247         ret[2][1] = 0;
    248         ret[2][2] = -(f + n) / (f - n);
    249         ret[2][3] = -(f * n * 2) / (f - n);
    250
    251         ret[3][0] = 0;
    252         ret[3][1] = 0;
    253         ret[3][2] = -1;
    254         ret[3][3] = 0;
    255
    256         return ret;
    257     },
    258
    259     /**
    260      * Generates a 4x4 matrix for 3D to 2D projections.
    261      * @param {Number} fov Field of view in vertical direction, given in rad.
    262      * @param {Number} ratio Aspect ratio of the projection plane.
    263      * @param {Number} n Near
    264      * @param {Number} f Far
    265      * @returns {Array} 4x4 Projection Matrix
    266      */
    267     projection: function (fov, ratio, n, f) {
    268         var t = n * Math.tan(fov / 2),
    269             r = t * ratio;
    270
    271         return this.frustum(-r, r, -t, t, n, f);
    272     },
    273
    274     /**
    275      * Multiplies a vector vec to a matrix mat: mat * vec. The matrix is interpreted by this function as an array of rows.
    276      * Please note: This
    277      * function does not check if the dimensions match.
    278      * @param {Array} mat Two-dimensional array of numbers. The inner arrays describe the columns, the outer ones the matrix' rows.
    279      * @param {Array} vec Array of numbers
    280      * @returns {Array} Array of numbers containing the result
    281      * @example
    282      * var A = [[2, 1],
    283      *          [2, 3]],
    284      *     b = [4, 5],
    285      *     c;
    286      * c = JXG.Math.matVecMult(A, b);
    287      * // c === [13, 23];
    288      */
    289     matVecMult: function (mat, vec) {
    290         var i, k, s,
    291             m = mat.length,
    292             n = vec.length,
    293             res = [];
    294
    295         if (n === 3) {
    296             for (i = 0; i < m; i++) {
    297                 res[i] = mat[i][0] * vec[0] + mat[i][1] * vec[1] + mat[i][2] * vec[2];
    298             }
    299         } else {
    300             for (i = 0; i < m; i++) {
    301                 s = 0;
    302                 for (k = 0; k < n; k++) {
    303                     s += mat[i][k] * vec[k];
    304                 }
    305                 res[i] = s;
    306             }
    307         }
    308         return res;
    309     },
    310
    311     /**
    312      * Multiplies a vector vec to a matrix mat from the left: vec * mat.
    313      * The matrix is interpreted by this function as an array of rows.
    314      * Please note: This function does not check if the dimensions match.
    315      * @param {Array} vec Array of numbers
    316      * @param {Array} mat Two-dimensional array of numbers. The inner arrays describe the columns,
    317      *  the outer ones the matrix' rows.
    318      * @returns {Array} Array of numbers containing the result
    319      * @example
    320      * var A = [[2, 1],
    321      *          [2, 3]],
    322      *     b = [4, 5],
    323      *     c;
    324      * c = JXG.Math.vecMatMult(b, A);
    325      * // c === [18, 16];
    326      */
    327     vecMatMult: function (vec, mat) {
    328         var i, k, s,
    329             m = mat.length,
    330             n = vec.length,
    331             res = [];
    332
    333         if (n === 3) {
    334             for (i = 0; i < m; i++) {
    335                 res[i] = vec[0] * mat[0][i] + vec[1] * mat[1][i] + vec[2] * mat[2][i];
    336             }
    337         } else {
    338             for (i = 0; i < n; i++) {
    339                 s = 0;
    340                 for (k = 0; k < m; k++) {
    341                     s += vec[k] * mat[k][i];
    342                 }
    343                 res[i] = s;
    344             }
    345         }
    346         return res;
    347     },
    348
    349     /**
    350      * Computes the product of the two matrices: mat1 * mat2.
    351      * Returns a new matrix array.
    352      *
    353      * @param {Array} mat1 Two-dimensional array of numbers
    354      * @param {Array} mat2 Two-dimensional array of numbers
    355      * @returns {Array} Two-dimensional Array of numbers containing result
    356      */
    357     matMatMult: function (mat1, mat2) {
    358         var i, j, s, k,
    359             m = mat1.length,
    360             n = m > 0 ? mat2[0].length : 0,
    361             m2 = mat2.length,
    362             res = this.matrix(m, n);
    363
    364         for (i = 0; i < m; i++) {
    365             for (j = 0; j < n; j++) {
    366                 s = 0;
    367                 for (k = 0; k < m2; k++) {
    368                     s += mat1[i][k] * mat2[k][j];
    369                 }
    370                 res[i][j] = s;
    371             }
    372         }
    373         return res;
    374     },
    375
    376     /**
    377      * Multiply a matrix mat by a scalar alpha: mat * scalar
    378      *
    379      * @param {Array} mat Two-dimensional array of numbers
    380      * @param {Number} alpha Scalar
    381      * @returns {Array} Two-dimensional Array of numbers containing result
    382      */
    383     matNumberMult: function (mat, alpha) {
    384         var i, j,
    385             m = mat.length,
    386             n = m > 0 ? mat[0].length : 0,
    387             res = this.matrix(m, n);
    388
    389         for (i = 0; i < m; i++) {
    390             for (j = 0; j < n; j++) {
    391                 res[i][j] = mat[i][j] * alpha;
    392             }
    393         }
    394         return res;
    395     },
    396
    397     /**
    398      * Compute the sum of two matrices: mat1 + mat2.
    399      * Returns a new matrix object.
    400      *
    401      * @param {Array} mat1 Two-dimensional array of numbers
    402      * @param {Array} mat2 Two-dimensional array of numbers
    403      * @returns {Array} Two-dimensional Array of numbers containing result
    404      */
    405     matMatAdd: function (mat1, mat2) {
    406         var i, j,
    407             m = mat1.length,
    408             n = m > 0 ? mat1[0].length : 0,
    409             res = this.matrix(m, n);
    410
    411         for (i = 0; i < m; i++) {
    412             for (j = 0; j < n; j++) {
    413                 res[i][j] = mat1[i][j] + mat2[i][j];
    414             }
    415         }
    416         return res;
    417     },
    418
    419     /**
    420      * Transposes a matrix given as a two-dimensional array.
    421      * @param {Array} M The matrix to be transposed
    422      * @returns {Array} The transpose of M
    423      */
    424     transpose: function (M) {
    425         var MT, i, j, m, n;
    426
    427         // number of rows of M
    428         m = M.length;
    429         // number of columns of M
    430         n = M.length > 0 ? M[0].length : 0;
    431         MT = this.matrix(n, m);
    432
    433         for (i = 0; i < n; i++) {
    434             for (j = 0; j < m; j++) {
    435                 MT[i][j] = M[j][i];
    436             }
    437         }
    438
    439         return MT;
    440     },
    441
    442     /**
    443      * Compute the inverse of an <i>(n x n)</i>-matrix by Gauss elimination.
    444      *
    445      * @param {Array} A matrix
    446      * @returns {Array} Inverse matrix of A or empty array (i.e. []) in case A is singular.
    447      */
    448     inverse: function (Ain) {
    449         var i, j, k, r, s,
    450             eps = this.eps * this.eps,
    451             ma, swp,
    452             n = Ain.length,
    453             A = [],
    454             p = [],
    455             hv = [];
    456
    457         for (i = 0; i < n; i++) {
    458             A[i] = [];
    459             for (j = 0; j < n; j++) {
    460                 A[i][j] = Ain[i][j];
    461             }
    462             p[i] = i;
    463         }
    464
    465         for (j = 0; j < n; j++) {
    466             // Pivot search
    467             ma = Math.abs(A[j][j]);
    468             r = j;
    469
    470             for (i = j + 1; i < n; i++) {
    471                 if (Math.abs(A[i][j]) > ma) {
    472                     ma = Math.abs(A[i][j]);
    473                     r = i;
    474                 }
    475             }
    476
    477             // Singular matrix
    478             if (ma <= eps) {
    479                 JXG.warn('JXG.Math.inverse: singular matrix');
    480                 return [];
    481             }
    482
    483             // swap rows:
    484             if (r > j) {
    485                 for (k = 0; k < n; k++) {
    486                     swp = A[j][k];
    487                     A[j][k] = A[r][k];
    488                     A[r][k] = swp;
    489                 }
    490
    491                 swp = p[j];
    492                 p[j] = p[r];
    493                 p[r] = swp;
    494             }
    495
    496             // transformation:
    497             s = 1.0 / A[j][j];
    498             for (i = 0; i < n; i++) {
    499                 A[i][j] *= s;
    500             }
    501             A[j][j] = s;
    502
    503             for (k = 0; k < n; k++) {
    504                 if (k !== j) {
    505                     for (i = 0; i < n; i++) {
    506                         if (i !== j) {
    507                             A[i][k] -= A[i][j] * A[j][k];
    508                         }
    509                     }
    510                     A[j][k] = -s * A[j][k];
    511                 }
    512             }
    513         }
    514
    515         // swap columns:
    516         for (i = 0; i < n; i++) {
    517             for (k = 0; k < n; k++) {
    518                 hv[p[k]] = A[i][k];
    519             }
    520             for (k = 0; k < n; k++) {
    521                 A[i][k] = hv[k];
    522             }
    523         }
    524
    525         return A;
    526     },
    527
    528     /**
    529      * Trace of a square matrix, given as a two-dimensional array.
    530      * @param {Array} M Square matrix
    531      * @returns {Number} The trace of M, NaN if M is not square.
    532      */
    533     trace: function (M) {
    534         var i, m, n,
    535             t = 0.0;
    536
    537         // number of rows of M
    538         m = M.length;
    539         // number of columns of M
    540         n = M.length > 0 ? M[0].length : 0;
    541         if (m !== n) {
    542             return NaN;
    543         }
    544         for (i = 0; i < n; i++) {
    545             t += M[i][i];
    546         }
    547
    548         return t;
    549     },
    550
    551     /**
    552      * Inner product of two vectors a and b. n is the length of the vectors.
    553      * @param {Array} a Vector
    554      * @param {Array} b Vector
    555      * @param {Number} [n] Length of the Vectors. If not given the length of the first vector is taken.
    556      * @returns {Number} The inner product of a and b.
    557      */
    558     innerProduct: function (a, b, n) {
    559         var i,
    560             s = 0;
    561
    562         if (n === undef || !Type.isNumber(n)) {
    563             n = a.length;
    564         }
    565
    566         for (i = 0; i < n; i++) {
    567             s += a[i] * b[i];
    568         }
    569
    570         return s;
    571     },
    572
    573     /**
    574      * Calculates the cross product of two vectors both of length three.
    575      * In case of homogeneous coordinates this is either
    576      * <ul>
    577      * <li>the intersection of two lines</li>
    578      * <li>the line through two points</li>
    579      * </ul>
    580      * @param {Array} c1 Homogeneous coordinates of line or point 1
    581      * @param {Array} c2 Homogeneous coordinates of line or point 2
    582      * @returns {Array} vector of length 3: homogeneous coordinates of the resulting point / line.
    583      */
    584     crossProduct: function (c1, c2) {
    585         return [
    586             c1[1] * c2[2] - c1[2] * c2[1],
    587             c1[2] * c2[0] - c1[0] * c2[2],
    588             c1[0] * c2[1] - c1[1] * c2[0]
    589         ];
    590     },
    591
    592     /**
    593      * Euclidean norm of a vector.
    594      *
    595      * @param {Array} a Array containing a vector.
    596      * @param {Number} n (Optional) length of the array.
    597      * @returns {Number} Euclidean norm of the vector.
    598      */
    599     norm: function (a, n) {
    600         var i,
    601             sum = 0.0;
    602
    603         if (n === undef || !Type.isNumber(n)) {
    604             n = a.length;
    605         }
    606
    607         for (i = 0; i < n; i++) {
    608             sum += a[i] * a[i];
    609         }
    610
    611         return Math.sqrt(sum);
    612     },
    613
    614     /**
    615      * Compute a * x + y for a scalar a and vectors x and y.
    616      *
    617      * @param {Number} a
    618      * @param {Array} x
    619      * @param {Array} y
    620      * @returns {Array}
    621      */
    622     axpy: function (a, x, y) {
    623         var i,
    624             le = x.length,
    625             p = [];
    626         for (i = 0; i < le; i++) {
    627             p[i] = a * x[i] + y[i];
    628         }
    629         return p;
    630     },
    631
    632     /**
    633      * Compute the factorial of a positive integer. If a non-integer value
    634      * is given, the fraction will be ignored.
    635      * @function
    636      * @param {Number} n
    637      * @returns {Number} n! = n*(n-1)*...*2*1
    638      */
    639     factorial: memoizer(function (n) {
    640         if (n < 0) {
    641             return NaN;
    642         }
    643
    644         n = Math.floor(n);
    645
    646         if (n === 0 || n === 1) {
    647             return 1;
    648         }
    649
    650         return n * this.factorial(n - 1);
    651     }),
    652
    653     /**
    654      * Computes the binomial coefficient n over k.
    655      * @function
    656      * @param {Number} n Fraction will be ignored
    657      * @param {Number} k Fraction will be ignored
    658      * @returns {Number} The binomial coefficient n over k
    659      */
    660     binomial: memoizer(function (n, k) {
    661         var b, i;
    662
    663         if (k > n || k < 0) {
    664             return NaN;
    665         }
    666
    667         k = Math.round(k);
    668         n = Math.round(n);
    669
    670         if (k === 0 || k === n) {
    671             return 1;
    672         }
    673
    674         b = 1;
    675
    676         for (i = 0; i < k; i++) {
    677             b *= n - i;
    678             b /= i + 1;
    679         }
    680
    681         return b;
    682     }),
    683
    684     /**
    685      * Calculates the cosine hyperbolicus of x.
    686      * @function
    687      * @param {Number} x The number the cosine hyperbolicus will be calculated of.
    688      * @returns {Number} Cosine hyperbolicus of the given value.
    689      */
    690     cosh:
    691         Math.cosh ||
    692         function (x) {
    693             return (Math.exp(x) + Math.exp(-x)) * 0.5;
    694         },
    695
    696     /**
    697      * Sine hyperbolicus of x.
    698      * @function
    699      * @param {Number} x The number the sine hyperbolicus will be calculated of.
    700      * @returns {Number} Sine hyperbolicus of the given value.
    701      */
    702     sinh:
    703         Math.sinh ||
    704         function (x) {
    705             return (Math.exp(x) - Math.exp(-x)) * 0.5;
    706         },
    707
    708     /**
    709      * Hyperbolic arc-cosine of a number.
    710      * @function
    711      * @param {Number} x
    712      * @returns {Number}
    713      */
    714     acosh:
    715         Math.acosh ||
    716         function (x) {
    717             return Math.log(x + Math.sqrt(x * x - 1));
    718         },
    719
    720     /**
    721      * Hyperbolic arcsine of a number
    722      * @function
    723      * @param {Number} x
    724      * @returns {Number}
    725      */
    726     asinh:
    727         Math.asinh ||
    728         function (x) {
    729             if (x === -Infinity) {
    730                 return x;
    731             }
    732             return Math.log(x + Math.sqrt(x * x + 1));
    733         },
    734
    735     /**
    736      * Computes the cotangent of x.
    737      * @function
    738      * @param {Number} x The number the cotangent will be calculated of.
    739      * @returns {Number} Cotangent of the given value.
    740      */
    741     cot: function (x) {
    742         return 1 / Math.tan(x);
    743     },
    744
    745     /**
    746      * Computes the inverse cotangent of x.
    747      * @param {Number} x The number the inverse cotangent will be calculated of.
    748      * @returns {Number} Inverse cotangent of the given value.
    749      */
    750     acot: function (x) {
    751         return (x >= 0 ? 0.5 : -0.5) * Math.PI - Math.atan(x);
    752     },
    753
    754     /**
    755      * Compute n-th real root of a real number. n must be strictly positive integer.
    756      * If n is odd, the real n-th root exists and is negative.
    757      * For n even, for negative valuees of x NaN is returned
    758      * @param  {Number} x radicand. Must be non-negative, if n even.
    759      * @param  {Number} n index of the root. must be strictly positive integer.
    760      * @returns {Number} returns real root or NaN
    761      *
    762      * @example
    763      * nthroot(16, 4): 2
    764      * nthroot(-27, 3): -3
    765      * nthroot(-4, 2): NaN
    766      */
    767     nthroot: function (x, n) {
    768         var inv = 1 / n;
    769
    770         if (n <= 0 || Math.floor(n) !== n) {
    771             return NaN;
    772         }
    773
    774         if (x === 0.0) {
    775             return 0.0;
    776         }
    777
    778         if (x > 0) {
    779             return Math.exp(inv * Math.log(x));
    780         }
    781
    782         // From here on, x is negative
    783         if (n % 2 === 1) {
    784             return -Math.exp(inv * Math.log(-x));
    785         }
    786
    787         // x negative, even root
    788         return NaN;
    789     },
    790
    791     /**
    792      * Computes cube root of real number
    793      * Polyfill for Math.cbrt().
    794      *
    795      * @function
    796      * @param  {Number} x Radicand
    797      * @returns {Number} Cube root of x.
    798      */
    799     cbrt:
    800         Math.cbrt ||
    801         function (x) {
    802             return this.nthroot(x, 3);
    803         },
    804
    805     /**
    806      * Compute base to the power of exponent.
    807      * @param {Number} base
    808      * @param {Number} exponent
    809      * @returns {Number} base to the power of exponent.
    810      */
    811     pow: function (base, exponent) {
    812         if (base === 0) {
    813             if (exponent === 0) {
    814                 return 1;
    815             }
    816             return 0;
    817         }
    818
    819         // exponent is an integer
    820         if (Math.floor(exponent) === exponent) {
    821             return Math.pow(base, exponent);
    822         }
    823
    824         // exponent is not an integer
    825         if (base > 0) {
    826             return Math.exp(exponent * Math.log(base));
    827         }
    828
    829         return NaN;
    830     },
    831
    832     /**
    833      * Compute base to the power of the rational exponent m / n.
    834      * This function first reduces the fraction m/n and then computes
    835      * JXG.Math.pow(base, m/n).
    836      *
    837      * This function is necessary to have the same results for e.g.
    838      * (-8)^(1/3) = (-8)^(2/6) = -2
    839      * @param {Number} base
    840      * @param {Number} m numerator of exponent
    841      * @param {Number} n denominator of exponent
    842      * @returns {Number} base to the power of exponent.
    843      */
    844     ratpow: function (base, m, n) {
    845         var g;
    846         if (m === 0) {
    847             return 1;
    848         }
    849         if (n === 0) {
    850             return NaN;
    851         }
    852
    853         g = this.gcd(m, n);
    854         return this.nthroot(this.pow(base, m / g), n / g);
    855     },
    856
    857     /**
    858      * Logarithm to base 10.
    859      * @param {Number} x
    860      * @returns {Number} log10(x) Logarithm of x to base 10.
    861      */
    862     log10: function (x) {
    863         return Math.log(x) / Math.log(10.0);
    864     },
    865
    866     /**
    867      * Logarithm to base 2.
    868      * @param {Number} x
    869      * @returns {Number} log2(x) Logarithm of x to base 2.
    870      */
    871     log2: function (x) {
    872         return Math.log(x) / Math.log(2.0);
    873     },
    874
    875     /**
    876      * Logarithm to arbitrary base b. If b is not given, natural log is taken, i.e. b = e.
    877      * @param {Number} x
    878      * @param {Number} b base
    879      * @returns {Number} log(x, b) Logarithm of x to base b, that is log(x)/log(b).
    880      */
    881     log: function (x, b) {
    882         if (b !== undefined && Type.isNumber(b)) {
    883             return Math.log(x) / Math.log(b);
    884         }
    885
    886         return Math.log(x);
    887     },
    888
    889     /**
    890      * The sign() function returns the sign of a number, indicating whether the number is positive, negative or zero.
    891      *
    892      * @function
    893      * @param  {Number} x A Number
    894      * @returns {Number}  This function has 5 kinds of return values,
    895      *    1, -1, 0, -0, NaN, which represent "positive number", "negative number", "positive zero", "negative zero"
    896      *    and NaN respectively.
    897      */
    898     sign:
    899         Math.sign ||
    900         function (x) {
    901             x = +x; // convert to a number
    902             if (x === 0 || isNaN(x)) {
    903                 return x;
    904             }
    905             return x > 0 ? 1 : -1;
    906         },
    907
    908     /**
    909      * A square & multiply algorithm to compute base to the power of exponent.
    910      * Implementated by Wolfgang Riedl.
    911      *
    912      * @param {Number} base
    913      * @param {Number} exponent
    914      * @returns {Number} Base to the power of exponent
    915      */
    916     squampow: function (base, exponent) {
    917         var result;
    918
    919         if (Math.floor(exponent) === exponent) {
    920             // exponent is integer (could be zero)
    921             result = 1;
    922
    923             if (exponent < 0) {
    924                 // invert: base
    925                 base = 1.0 / base;
    926                 exponent *= -1;
    927             }
    928
    929             while (exponent !== 0) {
    930                 if (exponent & 1) {
    931                     result *= base;
    932                 }
    933
    934                 exponent >>= 1;
    935                 base *= base;
    936             }
    937             return result;
    938         }
    939
    940         return this.pow(base, exponent);
    941     },
    942
    943     /**
    944      * Greatest common divisor (gcd) of two numbers.
    945      * See {@link <a href="https://rosettacode.org/wiki/Greatest_common_divisor#JavaScript">rosettacode.org</a>}.
    946      *
    947      * @param  {Number} a First number
    948      * @param  {Number} b Second number
    949      * @returns {Number}   gcd(a, b) if a and b are numbers, NaN else.
    950      */
    951     gcd: function (a, b) {
    952         var tmp,
    953             endless = true;
    954
    955         a = Math.abs(a);
    956         b = Math.abs(b);
    957
    958         if (!(Type.isNumber(a) && Type.isNumber(b))) {
    959             return NaN;
    960         }
    961         if (b > a) {
    962             tmp = a;
    963             a = b;
    964             b = tmp;
    965         }
    966
    967         while (endless) {
    968             a %= b;
    969             if (a === 0) {
    970                 return b;
    971             }
    972             b %= a;
    973             if (b === 0) {
    974                 return a;
    975             }
    976         }
    977     },
    978
    979     /**
    980      * Least common multiple (lcm) of two numbers.
    981      *
    982      * @param  {Number} a First number
    983      * @param  {Number} b Second number
    984      * @returns {Number}   lcm(a, b) if a and b are numbers, NaN else.
    985      */
    986     lcm: function (a, b) {
    987         var ret;
    988
    989         if (!(Type.isNumber(a) && Type.isNumber(b))) {
    990             return NaN;
    991         }
    992
    993         ret = a * b;
    994         if (ret !== 0) {
    995             return ret / this.gcd(a, b);
    996         }
    997
    998         return 0;
    999     },
    1000
    1001     /**
    1002      * Special use of Math.round function to round not only to integers but also to chosen decimal values.
    1003      *
    1004      * @param {Number} value Value to be rounded.
    1005      * @param {Number} step Distance between the values to be rounded to. (default: 1.0)
    1006      * @param {Number} [min] If set, it will be returned the maximum of value and min.
    1007      * @param {Number} [max] If set, it will be returned the minimum of value and max.
    1008      * @returns {Number} Fitted value.
    1009      */
    1010     roundToStep: function (value, step, min, max) {
    1011         var n = value,
    1012             tmp, minOr0;
    1013
    1014         // for performance
    1015         if (!Type.exists(step) && !Type.exists(min) && !Type.exists(max)) {
    1016             return n;
    1017         }
    1018
    1019         if (JXG.exists(max)) {
    1020             n = Math.min(n, max);
    1021         }
    1022         if (JXG.exists(min)) {
    1023             n = Math.max(n, min);
    1024         }
    1025
    1026         minOr0 = min || 0;
    1027
    1028         if (JXG.exists(step)) {
    1029             tmp = (n - minOr0) / step;
    1030             if (Number.isInteger(tmp)) {
    1031                 return n;
    1032             }
    1033
    1034             tmp = Math.round(tmp);
    1035             n = minOr0 + tmp * step;
    1036         }
    1037
    1038         if (JXG.exists(max)) {
    1039             n = Math.min(n, max);
    1040         }
    1041         if (JXG.exists(min)) {
    1042             n = Math.max(n, min);
    1043         }
    1044
    1045         return n;
    1046     },
    1047
    1048     /**
    1049      *  Error function, see {@link https://en.wikipedia.org/wiki/Error_function}.
    1050      *
    1051      * @see JXG.Math.ProbFuncs.erf
    1052      * @param  {Number} x
    1053      * @returns {Number}
    1054      */
    1055     erf: function (x) {
    1056         return this.ProbFuncs.erf(x);
    1057     },
    1058
    1059     /**
    1060      * Complementary error function, i.e. 1 - erf(x).
    1061      *
    1062      * @see JXG.Math.erf
    1063      * @see JXG.Math.ProbFuncs.erfc
    1064      * @param  {Number} x
    1065      * @returns {Number}
    1066      */
    1067     erfc: function (x) {
    1068         return this.ProbFuncs.erfc(x);
    1069     },
    1070
    1071     /**
    1072      * Inverse of error function
    1073      *
    1074      * @see JXG.Math.erf
    1075      * @see JXG.Math.ProbFuncs.erfi
    1076      * @param  {Number} x
    1077      * @returns {Number}
    1078      */
    1079     erfi: function (x) {
    1080         return this.ProbFuncs.erfi(x);
    1081     },
    1082
    1083     /**
    1084      * Normal distribution function
    1085      *
    1086      * @see JXG.Math.ProbFuncs.ndtr
    1087      * @param  {Number} x
    1088      * @returns {Number}
    1089      */
    1090     ndtr: function (x) {
    1091         return this.ProbFuncs.ndtr(x);
    1092     },
    1093
    1094     /**
    1095      * Inverse of normal distribution function
    1096      *
    1097      * @see JXG.Math.ndtr
    1098      * @see JXG.Math.ProbFuncs.ndtri
    1099      * @param  {Number} x
    1100      * @returns {Number}
    1101      */
    1102     ndtri: function (x) {
    1103         return this.ProbFuncs.ndtri(x);
    1104     },
    1105
    1106     /**
    1107      * Returns sqrt(a * a + b * b) for a variable number of arguments.
    1108      * This is a naive implementation which might be faster than Math.hypot.
    1109      * The latter is numerically more stable.
    1110      *
    1111      * @param {Number} a Variable number of arguments.
    1112      * @returns Number
    1113      */
    1114     hypot: function () {
    1115         var i, le, a, sum;
    1116
    1117         le = arguments.length;
    1118         for (i = 0, sum = 0.0; i < le; i++) {
    1119             a = arguments[i];
    1120             sum += a * a;
    1121         }
    1122         return Math.sqrt(sum);
    1123     },
    1124
    1125     /**
    1126      * Heaviside unit step function. Returns 0 for x <, 1 for x > 0, and 0.5 for x == 0.
    1127      *
    1128      * @param {Number} x
    1129      * @returns Number
    1130      */
    1131     hstep: function (x) {
    1132         return (x > 0.0) ? 1 :
    1133             ((x < 0.0) ? 0.0 : 0.5);
    1134     },
    1135
    1136     /**
    1137      * Gamma function for real parameters by Lanczos approximation.
    1138      * Implementation straight from {@link https://en.wikipedia.org/wiki/Lanczos_approximation}.
    1139      *
    1140      * @param {Number} z
    1141      * @returns Number
    1142      */
    1143     gamma: function (z) {
    1144         var x, y, t, i, le,
    1145             g = 7,
    1146             // n = 9,
    1147             p = [
    1148                 1.0,
    1149                 676.5203681218851,
    1150                 -1259.1392167224028,
    1151                 771.32342877765313,
    1152                 -176.61502916214059,
    1153                 12.507343278686905,
    1154                 -0.13857109526572012,
    1155                 9.9843695780195716e-6,
    1156                 1.5056327351493116e-7
    1157             ];
    1158
    1159         if (z < 0.5) {
    1160             y = Math.PI / (Math.sin(Math.PI * z) * this.gamma(1 - z));  // Reflection formula
    1161         } else {
    1162             z -= 1;
    1163             x = p[0];
    1164             le = p.length;
    1165             for (i = 1; i < le; i++) {
    1166                 x += p[i] / (z + i);
    1167             }
    1168             t = z + g + 0.5;
    1169             y = Math.sqrt(2 * Math.PI) * Math.pow(t, z + 0.5) * Math.exp(-t) * x;
    1170         }
    1171         return y;
    1172     },
    1173
    1174     /* ********************  Comparisons and logical operators ************** */
    1175
    1176     /**
    1177      * Logical test: a < b?
    1178      *
    1179      * @param {Number} a
    1180      * @param {Number} b
    1181      * @returns {Boolean}
    1182      */
    1183     lt: function (a, b) {
    1184         return a < b;
    1185     },
    1186
    1187     /**
    1188      * Logical test: a <= b?
    1189      *
    1190      * @param {Number} a
    1191      * @param {Number} b
    1192      * @returns {Boolean}
    1193      */
    1194     leq: function (a, b) {
    1195         return a <= b;
    1196     },
    1197
    1198     /**
    1199      * Logical test: a > b?
    1200      *
    1201      * @param {Number} a
    1202      * @param {Number} b
    1203      * @returns {Boolean}
    1204      */
    1205     gt: function (a, b) {
    1206         return a > b;
    1207     },
    1208
    1209     /**
    1210      * Logical test: a >= b?
    1211      *
    1212      * @param {Number} a
    1213      * @param {Number} b
    1214      * @returns {Boolean}
    1215      */
    1216     geq: function (a, b) {
    1217         return a >= b;
    1218     },
    1219
    1220     /**
    1221      * Logical test: a === b?
    1222      *
    1223      * @param {Number} a
    1224      * @param {Number} b
    1225      * @returns {Boolean}
    1226      */
    1227     eq: function (a, b) {
    1228         return a === b;
    1229     },
    1230
    1231     /**
    1232      * Logical test: a !== b?
    1233      *
    1234      * @param {Number} a
    1235      * @param {Number} b
    1236      * @returns {Boolean}
    1237      */
    1238     neq: function (a, b) {
    1239         return a !== b;
    1240     },
    1241
    1242     /**
    1243      * Logical operator: a && b?
    1244      *
    1245      * @param {Boolean} a
    1246      * @param {Boolean} b
    1247      * @returns {Boolean}
    1248      */
    1249     and: function (a, b) {
    1250         return a && b;
    1251     },
    1252
    1253     /**
    1254      * Logical operator: !a?
    1255      *
    1256      * @param {Boolean} a
    1257      * @returns {Boolean}
    1258      */
    1259     not: function (a) {
    1260         return !a;
    1261     },
    1262
    1263     /**
    1264      * Logical operator: a || b?
    1265      *
    1266      * @param {Boolean} a
    1267      * @param {Boolean} b
    1268      * @returns {Boolean}
    1269      */
    1270     or: function (a, b) {
    1271         return a || b;
    1272     },
    1273
    1274     /**
    1275      * Logical operator: either a or b?
    1276      *
    1277      * @param {Boolean} a
    1278      * @param {Boolean} b
    1279      * @returns {Boolean}
    1280      */
    1281     xor: function (a, b) {
    1282         return (a || b) && !(a && b);
    1283     },
    1284
    1285     /**
    1286      *
    1287      * Convert a floating point number to sign + integer + fraction.
    1288      * fraction is given as nominator and denominator.
    1289      * <p>
    1290      * Algorithm: approximate the floating point number
    1291      * by a continued fraction and simultaneously keep track
    1292      * of its convergents.
    1293      * Inspired by {@link https://kevinboone.me/rationalize.html}.
    1294      *
    1295      * @param {Number} x Number which is to be converted
    1296      * @param {Number} [order=0.001] Small number determining the approximation precision.
    1297      * @returns {Array} [sign, leading, nominator, denominator] where sign is 1 or -1.
    1298      * @see JXG.toFraction
    1299      *
    1300      * @example
    1301      * JXG.Math.decToFraction(0.33333333);
    1302      * // Result: [ 1, 0, 1, 3 ]
    1303      *
    1304      * JXG.Math.decToFraction(0);
    1305      * // Result: [ 1, 0, 0, 1 ]
    1306      *
    1307      * JXG.Math.decToFraction(-10.66666666666667);
    1308      * // Result: [-1, 10, 2, 3 ]
    1309     */
    1310     decToFraction: function (x, order) {
    1311         var lead, sign, a,
    1312             n, n1, n2,
    1313             d, d1, d2,
    1314             it = 0,
    1315             maxit = 20;
    1316
    1317         order = Type.def(order, 0.001);
    1318
    1319         // Round the number.
    1320         // Otherwise, 0.999999999 would result in [0, 1, 1].
    1321         x = Math.round(x * 1.e12) * 1.e-12;
    1322
    1323         // Negative numbers:
    1324         // The minus sign is handled in sign.
    1325         sign = (x < 0) ? -1 : 1;
    1326         x = Math.abs(x);
    1327
    1328         // From now on we consider x to be nonnegative.
    1329         lead = Math.floor(x);
    1330         x -= Math.floor(x);
    1331         a = 0.0;
    1332         n2 = 1.0;
    1333         n = n1 = a;
    1334         d2 = 0.0;
    1335         d = d1 = 1.0;
    1336
    1337         while (x - Math.floor(x) > order && it < maxit) {
    1338             x = 1 / (x - a);
    1339             a = Math.floor(x);
    1340             n = n2 + a * n1;
    1341             d = d2 + a * d1;
    1342             n2 = n1;
    1343             d2 = d1;
    1344             n1 = n;
    1345             d1 = d;
    1346             it++;
    1347         }
    1348         return [sign, lead, n, d];
    1349     },
    1350
    1351     /* *************************** Normalize *************************** */
    1352
    1353     /**
    1354      * Normalize the standard form [c, b0, b1, a, k, r, q0, q1].
    1355      * @private
    1356      * @param {Array} stdform The standard form to be normalized.
    1357      * @returns {Array} The normalized standard form.
    1358      */
    1359     normalize: function (stdform) {
    1360         var n,
    1361             signr,
    1362             a2 = 2 * stdform[3],
    1363             r = stdform[4] / a2;
    1364
    1365         stdform[5] = r;
    1366         stdform[6] = -stdform[1] / a2;
    1367         stdform[7] = -stdform[2] / a2;
    1368
    1369         if (!isFinite(r)) {
    1370             n = this.hypot(stdform[1], stdform[2]);
    1371
    1372             stdform[0] /= n;
    1373             stdform[1] /= n;
    1374             stdform[2] /= n;
    1375             stdform[3] = 0;
    1376             stdform[4] = 1;
    1377         } else if (Math.abs(r) >= 1) {
    1378             stdform[0] = (stdform[6] * stdform[6] + stdform[7] * stdform[7] - r * r) / (2 * r);
    1379             stdform[1] = -stdform[6] / r;
    1380             stdform[2] = -stdform[7] / r;
    1381             stdform[3] = 1 / (2 * r);
    1382             stdform[4] = 1;
    1383         } else {
    1384             signr = r <= 0 ? -1 : 1;
    1385             stdform[0] =
    1386                 signr * (stdform[6] * stdform[6] + stdform[7] * stdform[7] - r * r) * 0.5;
    1387             stdform[1] = -signr * stdform[6];
    1388             stdform[2] = -signr * stdform[7];
    1389             stdform[3] = signr / 2;
    1390             stdform[4] = signr * r;
    1391         }
    1392
    1393         return stdform;
    1394     },
    1395
    1396     /**
    1397      * Converts a two-dimensional array to a one-dimensional Float32Array that can be processed by WebGL.
    1398      * @param {Array} m A matrix in a two-dimensional array.
    1399      * @returns {Float32Array} A one-dimensional array containing the matrix in column wise notation. Provides a fall
    1400      * back to the default JavaScript Array if Float32Array is not available.
    1401      */
    1402     toGL: function (m) {
    1403         var v, i, j;
    1404
    1405         if (typeof Float32Array === "function") {
    1406             v = new Float32Array(16);
    1407         } else {
    1408             v = new Array(16);
    1409         }
    1410
    1411         if (m.length !== 4 && m[0].length !== 4) {
    1412             return v;
    1413         }
    1414
    1415         for (i = 0; i < 4; i++) {
    1416             for (j = 0; j < 4; j++) {
    1417                 v[i + 4 * j] = m[i][j];
    1418             }
    1419         }
    1420
    1421         return v;
    1422     },
    1423
    1424     /**
    1425      * Theorem of Vieta: Given a set of simple zeroes x_0, ..., x_n
    1426      * of a polynomial f, compute the coefficients s_k, (k=0,...,n-1)
    1427      * of the polynomial of the form. See {@link https://de.wikipedia.org/wiki/Elementarsymmetrisches_Polynom}.
    1428      * <p>
    1429      *  f(x) = (x-x_0)*...*(x-x_n) =
    1430      *  x^n + sum_{k=1}^{n} (-1)^(k) s_{k-1} x^(n-k)
    1431      * </p>
    1432      * @param {Array} x Simple zeroes of the polynomial.
    1433      * @returns {Array} Coefficients of the polynomial.
    1434      *
    1435      */
    1436     Vieta: function (x) {
    1437         var n = x.length,
    1438             s = [],
    1439             m,
    1440             k,
    1441             y;
    1442
    1443         s = x.slice();
    1444         for (m = 1; m < n; ++m) {
    1445             y = s[m];
    1446             s[m] *= s[m - 1];
    1447             for (k = m - 1; k >= 1; --k) {
    1448                 s[k] += s[k - 1] * y;
    1449             }
    1450             s[0] += y;
    1451         }
    1452         return s;
    1453     }
    1454 };
    1455
    1456 export default JXG.Math;
    1457