      1 /*
      2     Copyright 2008-2024
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Alfred Wassermann
      7
      8     This file is part of JSXGraph.
      9
     10     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     11
     12     You can redistribute it and/or modify it under the terms of the
     13
     14       * GNU Lesser General Public License as published by
     15         the Free Software Foundation, either version 3 of the License, or
     16         (at your option) any later version
     17       OR
     18       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     19
     20     JSXGraph is distributed in the hope that it will be useful,
     21     but WITHOUT ANY WARRANTY; without even the implied warranty of
     22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     23     GNU Lesser General Public License for more details.
     24
     25     You should have received a copy of the GNU Lesser General Public License and
     26     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
     27     and <https://opensource.org/licenses/MIT/>.
     28  */
     29
     30 /*global JXG: true, define: true*/
     31 /*jslint nomen: true, plusplus: true*/
     32
     33 import JXG from "../jxg.js";
     34 import Mat from "./math.js";
     35 import Type from "../utils/type.js";
     36
     37 JXG.Math.DoubleBits = function () {
     38     var DOUBLE_VIEW = new Float64Array(1),
     39         UINT_VIEW = new Uint32Array(DOUBLE_VIEW.buffer),
     40         doubleBitsLE,
     41         toDoubleLE,
     42         lowUintLE,
     43         highUintLE,
     44         // doubleBits,
     45         // toDouble,
     46         // lowUint,
     47         // highUint,
     48         // hasTypedArrays = false,
     49         doubleBitsBE,
     50         toDoubleBE,
     51         lowUintBE,
     52         highUintBE;
     53
     54     if (Float64Array !== undefined) {
     55         DOUBLE_VIEW[0] = 1.0;
     56         // hasTypedArrays = true;
     57         if (UINT_VIEW[1] === 0x3ff00000) {
     58             // Use little endian
     59             doubleBitsLE = function (n) {
     60                 DOUBLE_VIEW[0] = n;
     61                 return [UINT_VIEW[0], UINT_VIEW[1]];
     62             };
     63             toDoubleLE = function (lo, hi) {
     64                 UINT_VIEW[0] = lo;
     65                 UINT_VIEW[1] = hi;
     66                 return DOUBLE_VIEW[0];
     67             };
     68
     69             lowUintLE = function (n) {
     70                 DOUBLE_VIEW[0] = n;
     71                 return UINT_VIEW[0];
     72             };
     73
     74             highUintLE = function (n) {
     75                 DOUBLE_VIEW[0] = n;
     76                 return UINT_VIEW[1];
     77             };
     78
     79             this.doubleBits = doubleBitsLE;
     80             this.pack = toDoubleLE;
     81             this.lo = lowUintLE;
     82             this.hi = highUintLE;
     83         } else if (UINT_VIEW[0] === 0x3ff00000) {
     84             // Use big endian
     85             doubleBitsBE = function (n) {
     86                 DOUBLE_VIEW[0] = n;
     87                 return [UINT_VIEW[1], UINT_VIEW[0]];
     88             };
     89
     90             toDoubleBE = function (lo, hi) {
     91                 UINT_VIEW[1] = lo;
     92                 UINT_VIEW[0] = hi;
     93                 return DOUBLE_VIEW[0];
     94             };
     95
     96             lowUintBE = function (n) {
     97                 DOUBLE_VIEW[0] = n;
     98                 return UINT_VIEW[1];
     99             };
    100
    101             highUintBE = function (n) {
    102                 DOUBLE_VIEW[0] = n;
    103                 return UINT_VIEW[0];
    104             };
    105
    106             this.doubleBits = doubleBitsBE;
    107             this.pack = toDoubleBE;
    108             this.lo = lowUintBE;
    109             this.hi = highUintBE;
    110         // } else {
    111         //     hasTypedArrays = false;
    112         }
    113     }
    114
    115     // if (!hasTypedArrays) {
    116     //     var buffer = new Buffer(8)
    117     //     doubleBits = function(n) {
    118     //         buffer.writeDoubleLE(n, 0, true);
    119     //         return [buffer.readUInt32LE(0, true), buffer.readUInt32LE(4, true)];
    120     //     };
    121
    122     //     toDouble = function(lo, hi) {
    123     //         buffer.writeUInt32LE(lo, 0, true);
    124     //         buffer.writeUInt32LE(hi, 4, true);
    125     //         return buffer.readDoubleLE(0, true);
    126     //     };
    127     //     lowUint = function(n) {
    128     //         buffer.writeDoubleLE(n, 0, true);
    129     //         return buffer.readUInt32LE(0, true);
    130     //     };
    131
    132     //     highUint = function(n) {
    133     //         buffer.writeDoubleLE(n, 0, true);
    134     //         return buffer.readUInt32LE(4, true);
    135     //     };
    136
    137     //     this.doubleBits = doubleBits;
    138     //     this.pack = toDouble;
    139     //     this.lo = lowUint;
    140     //     this.hi = highUint;
    141     // }
    142 };
    143
    144 JXG.extend(
    145     JXG.Math.DoubleBits.prototype,
    146     /** @lends JXG.Math.DoubleBits.prototype */ {
    147         sign: function (n) {
    148             return this.hi(n) >>> 31;
    149         },
    150
    151         exponent: function (n) {
    152             var b = this.hi(n);
    153             return ((b << 1) >>> 21) - 1023;
    154         },
    155
    156         fraction: function (n) {
    157             var lo = this.lo(n),
    158                 hi = this.hi(n),
    159                 b = hi & ((1 << 20) - 1);
    160
    161             if (hi & 0x7ff00000) {
    162                 b += 1 << 20;
    163             }
    164             return [lo, b];
    165         },
    166
    167         denormalized: function (n) {
    168             var hi = this.hi(n);
    169             return !(hi & 0x7ff00000);
    170         }
    171     }
    172 );
    173
    174 var doubleBits = new JXG.Math.DoubleBits(),
    175     /**
    176      * Interval for interval arithmetics. Consists of the properties
    177      * <ul>
    178      *  <li>lo
    179      *  <li>hi
    180      * </ul>
    181      * @name JXG.Math.Interval
    182      * @type Object
    183      */
    184     MatInterval = function (lo, hi) {
    185         if (lo !== undefined && hi !== undefined) {
    186             // possible cases:
    187             // - Interval(1, 2)
    188             // - Interval(Interval(1, 1), Interval(2, 2))     // singletons are required
    189             if (Mat.IntervalArithmetic.isInterval(lo)) {
    190                 if (!Mat.IntervalArithmetic.isSingleton(lo)) {
    191                     throw new TypeError(
    192                         "JXG.Math.IntervalArithmetic: interval `lo` must be a singleton"
    193                     );
    194                 }
    195                 this.lo = lo.lo;
    196             } else {
    197                 this.lo = lo;
    198             }
    199             if (Mat.IntervalArithmetic.isInterval(hi)) {
    200                 if (!Mat.IntervalArithmetic.isSingleton(hi)) {
    201                     throw new TypeError(
    202                         "JXG.Math.IntervalArithmetic: interval `hi` must be a singleton"
    203                     );
    204                 }
    205                 this.hi = hi.hi;
    206             } else {
    207                 this.hi = hi;
    208             }
    209         } else if (lo !== undefined) {
    210             // possible cases:
    211             // - Interval([1, 2])
    212             // - Interval([Interval(1, 1), Interval(2, 2)])
    213             if (Array.isArray(lo)) {
    214                 return new MatInterval(lo[0], lo[1]);
    215             }
    216             // - Interval(1)
    217             return new MatInterval(lo, lo);
    218         } else {
    219             // This else is necessary even if jslint declares it as redundant
    220             // possible cases:
    221             // - Interval()
    222             this.lo = this.hi = 0;
    223         }
    224     };
    225
    226 JXG.extend(MatInterval.prototype, {
    227     print: function () {
    228         console.log("[", this.lo, this.hi, "]");
    229     },
    230
    231     set: function (lo, hi) {
    232         this.lo = lo;
    233         this.hi = hi;
    234         return this;
    235     },
    236
    237     bounded: function (lo, hi) {
    238         return this.set(Mat.IntervalArithmetic.prev(lo), Mat.IntervalArithmetic.next(hi));
    239     },
    240
    241     boundedSingleton: function (v) {
    242         return this.bounded(v, v);
    243     },
    244
    245     assign: function (lo, hi) {
    246         if (typeof lo !== "number" || typeof hi !== "number") {
    247             throw new TypeError("JXG.Math.Interval#assign: arguments must be numbers");
    248         }
    249         if (isNaN(lo) || isNaN(hi) || lo > hi) {
    250             return this.setEmpty();
    251         }
    252         return this.set(lo, hi);
    253     },
    254
    255     setEmpty: function () {
    256         return this.set(Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY);
    257     },
    258
    259     setWhole: function () {
    260         return this.set(Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY);
    261     },
    262
    263     open: function (lo, hi) {
    264         return this.assign(Mat.IntervalArithmetic.next(lo), Mat.IntervalArithmetic.prev(hi));
    265     },
    266
    267     halfOpenLeft: function (lo, hi) {
    268         return this.assign(Mat.IntervalArithmetic.next(lo), hi);
    269     },
    270
    271     halfOpenRight: function (lo, hi) {
    272         return this.assign(lo, Mat.IntervalArithmetic.prev(hi));
    273     },
    274
    275     toArray: function () {
    276         return [this.lo, this.hi];
    277     },
    278
    279     clone: function () {
    280         return new MatInterval().set(this.lo, this.hi);
    281     }
    282 });
    283
    284 /**
    285  * Object for interval arithmetics.
    286  * @name JXG.Math.IntervalArithmetic
    287  * @namespace
    288  */
    289 JXG.Math.IntervalArithmetic = {
    290     Interval: function (lo, hi) {
    291         return new MatInterval(lo, hi);
    292     },
    293
    294     isInterval: function (i) {
    295         return (
    296             i !== null &&
    297             typeof i === "object" &&
    298             typeof i.lo === "number" &&
    299             typeof i.hi === "number"
    300         );
    301     },
    302
    303     isSingleton: function (i) {
    304         return i.lo === i.hi;
    305     },
    306
    307     /*
    308      * Arithmetics
    309      */
    310
    311     /**
    312      * Addition
    313      *
    314      * @param {JXG.Math.Interval|Number} x
    315      * @param {JXG.Math.Interval|Number} y
    316      * @returns JXG.Math.Interval
    317      */
    318     add: function (x, y) {
    319         if (Type.isNumber(x)) {
    320             x = this.Interval(x);
    321         }
    322         if (Type.isNumber(y)) {
    323             y = this.Interval(y);
    324         }
    325         return new MatInterval(this.addLo(x.lo, y.lo), this.addHi(x.hi, y.hi));
    326     },
    327
    328     /**
    329      * Subtraction
    330      *
    331      * @param {JXG.Math.Interval|Number} x
    332      * @param {JXG.Math.Interval|Number} y
    333      * @returns JXG.Math.Interval
    334      */
    335     sub: function (x, y) {
    336         if (Type.isNumber(x)) {
    337             x = this.Interval(x);
    338         }
    339         if (Type.isNumber(y)) {
    340             y = this.Interval(y);
    341         }
    342         return new MatInterval(this.subLo(x.lo, y.hi), this.subHi(x.hi, y.lo));
    343     },
    344
    345     /**
    346      * Multiplication
    347      *
    348      * @param {JXG.Math.Interval|Number} x
    349      * @param {JXG.Math.Interval|Number} y
    350      * @returns JXG.Math.Interval
    351      */
    352     mul: function (x, y) {
    353         var xl, xh, yl, yh, out;
    354
    355         if (Type.isNumber(x)) {
    356             x = this.Interval(x);
    357         }
    358         if (Type.isNumber(y)) {
    359             y = this.Interval(y);
    360         }
    361
    362         if (this.isEmpty(x) || this.isEmpty(y)) {
    363             return this.EMPTY.clone();
    364         }
    365         xl = x.lo;
    366         xh = x.hi;
    367         yl = y.lo;
    368         yh = y.hi;
    369         out = new MatInterval();
    370
    371         if (xl < 0) {
    372             if (xh > 0) {
    373                 if (yl < 0) {
    374                     if (yh > 0) {
    375                         // mixed * mixed
    376                         out.lo = Math.min(this.mulLo(xl, yh), this.mulLo(xh, yl));
    377                         out.hi = Math.max(this.mulHi(xl, yl), this.mulHi(xh, yh));
    378                     } else {
    379                         // mixed * negative
    380                         out.lo = this.mulLo(xh, yl);
    381                         out.hi = this.mulHi(xl, yl);
    382                     }
    383                 } else {
    384                     if (yh > 0) {
    385                         // mixed * positive
    386                         out.lo = this.mulLo(xl, yh);
    387                         out.hi = this.mulHi(xh, yh);
    388                     } else {
    389                         // mixed * zero
    390                         out.lo = 0;
    391                         out.hi = 0;
    392                     }
    393                 }
    394             } else {
    395                 if (yl < 0) {
    396                     if (yh > 0) {
    397                         // negative * mixed
    398                         out.lo = this.mulLo(xl, yh);
    399                         out.hi = this.mulHi(xl, yl);
    400                     } else {
    401                         // negative * negative
    402                         out.lo = this.mulLo(xh, yh);
    403                         out.hi = this.mulHi(xl, yl);
    404                     }
    405                 } else {
    406                     if (yh > 0) {
    407                         // negative * positive
    408                         out.lo = this.mulLo(xl, yh);
    409                         out.hi = this.mulHi(xh, yl);
    410                     } else {
    411                         // negative * zero
    412                         out.lo = 0;
    413                         out.hi = 0;
    414                     }
    415                 }
    416             }
    417         } else {
    418             if (xh > 0) {
    419                 if (yl < 0) {
    420                     if (yh > 0) {
    421                         // positive * mixed
    422                         out.lo = this.mulLo(xh, yl);
    423                         out.hi = this.mulHi(xh, yh);
    424                     } else {
    425                         // positive * negative
    426                         out.lo = this.mulLo(xh, yl);
    427                         out.hi = this.mulHi(xl, yh);
    428                     }
    429                 } else {
    430                     if (yh > 0) {
    431                         // positive * positive
    432                         out.lo = this.mulLo(xl, yl);
    433                         out.hi = this.mulHi(xh, yh);
    434                     } else {
    435                         // positive * zero
    436                         out.lo = 0;
    437                         out.hi = 0;
    438                     }
    439                 }
    440             } else {
    441                 // zero * any other value
    442                 out.lo = 0;
    443                 out.hi = 0;
    444             }
    445         }
    446         return out;
    447     },
    448
    449     /**
    450      * Division
    451      *
    452      * @param {JXG.Math.Interval|Number} x
    453      * @param {JXG.Math.Interval|Number} y
    454      * @returns JXG.Math.Interval
    455      */
    456     div: function (x, y) {
    457         if (Type.isNumber(x)) {
    458             x = this.Interval(x);
    459         }
    460         if (Type.isNumber(y)) {
    461             y = this.Interval(y);
    462         }
    463
    464         if (this.isEmpty(x) || this.isEmpty(y)) {
    465             return this.EMPTY.clone();
    466         }
    467         if (this.zeroIn(y)) {
    468             if (y.lo !== 0) {
    469                 if (y.hi !== 0) {
    470                     return this.divZero(x);
    471                 }
    472                 return this.divNegative(x, y.lo);
    473             }
    474             if (y.hi !== 0) {
    475                 return this.divPositive(x, y.hi);
    476             }
    477             return this.EMPTY.clone();
    478         }
    479         return this.divNonZero(x, y);
    480     },
    481
    482     /**
    483      * Return +x (i.e. identity)
    484      *
    485      * @param {JXG.Math.Interval} x
    486      * @returns JXG.Math.Interval
    487      */
    488     positive: function (x) {
    489         return new MatInterval(x.lo, x.hi);
    490     },
    491
    492     /**
    493      * Return -x
    494      *
    495      * @param {JXG.Math.Interval} x
    496      * @returns JXG.Math.Interval
    497      */
    498     negative: function (x) {
    499         if (Type.isNumber(x)) {
    500             return new MatInterval(-x);
    501         }
    502         return new MatInterval(-x.hi, -x.lo);
    503     },
    504
    505     /*
    506      * Utils
    507      */
    508
    509     /**
    510      * Test if interval is empty set.
    511      * @param {JXG.Math.Interval} i
    512      * @returns Boolean
    513      */
    514     isEmpty: function (i) {
    515         return i.lo > i.hi;
    516     },
    517
    518     /**
    519      * Test if interval is (-Infinity, Infinity).
    520      * @param {JXG.Math.Interval} i
    521      * @returns Boolean
    522      */
    523     isWhole: function (i) {
    524         return i.lo === -Infinity && i.hi === Infinity;
    525     },
    526
    527     /**
    528      * Test if interval contains 0.
    529      * @param {JXG.Math.Interval} i
    530      * @returns Boolean
    531      */
    532     zeroIn: function (i) {
    533         return this.hasValue(i, 0);
    534     },
    535
    536     /**
    537      * Test if interval contains a specific value.
    538      * @param {JXG.Math.Interval} i
    539      * @param {Number} value
    540      * @returns Boolean
    541      */
    542     hasValue: function (i, value) {
    543         if (this.isEmpty(i)) {
    544             return false;
    545         }
    546         return i.lo <= value && value <= i.hi;
    547     },
    548
    549     /**
    550      * Test if interval x contains interval y.
    551      * @param {JXG.Math.Interval} x
    552      * @param {JXG.Math.Interval} y
    553      * @returns Boolean
    554      */
    555     hasInterval: function (x, y) {
    556         if (this.isEmpty(x)) {
    557             return true;
    558         }
    559         return !this.isEmpty(y) && y.lo <= x.lo && x.hi <= y.hi;
    560     },
    561
    562     /**
    563      * Test if intervals x and y have non-zero intersection.
    564      * @param {JXG.Math.Interval} x
    565      * @param {JXG.Math.Interval} y
    566      * @returns Boolean
    567      */
    568     intervalsOverlap: function (x, y) {
    569         if (this.isEmpty(x) || this.isEmpty(y)) {
    570             return false;
    571         }
    572         return (x.lo <= y.lo && y.lo <= x.hi) || (y.lo <= x.lo && x.lo <= y.hi);
    573     },
    574
    575     /*
    576      * Division
    577      */
    578     /**
    579      * @private
    580      * @param {JXG.Math.Interval} x
    581      * @param {JXG.Math.Interval} y
    582      * @returns JXG.Math.Interval
    583      */
    584     divNonZero: function (x, y) {
    585         var xl = x.lo,
    586             xh = x.hi,
    587             yl = y.lo,
    588             yh = y.hi,
    589             out = new MatInterval();
    590
    591         if (xh < 0) {
    592             if (yh < 0) {
    593                 out.lo = this.divLo(xh, yl);
    594                 out.hi = this.divHi(xl, yh);
    595             } else {
    596                 out.lo = this.divLo(xl, yl);
    597                 out.hi = this.divHi(xh, yh);
    598             }
    599         } else if (xl < 0) {
    600             if (yh < 0) {
    601                 out.lo = this.divLo(xh, yh);
    602                 out.hi = this.divHi(xl, yh);
    603             } else {
    604                 out.lo = this.divLo(xl, yl);
    605                 out.hi = this.divHi(xh, yl);
    606             }
    607         } else {
    608             if (yh < 0) {
    609                 out.lo = this.divLo(xh, yh);
    610                 out.hi = this.divHi(xl, yl);
    611             } else {
    612                 out.lo = this.divLo(xl, yh);
    613                 out.hi = this.divHi(xh, yl);
    614             }
    615         }
    616         return out;
    617     },
    618
    619     /**
    620      * @private
    621      * @param {JXG.Math.Interval} x
    622      * @param {JXG.Math.Interval} y
    623      * @returns JXG.Math.Interval
    624      */
    625     divPositive: function (x, v) {
    626         if (x.lo === 0 && x.hi === 0) {
    627             return x;
    628         }
    629
    630         if (this.zeroIn(x)) {
    631             // mixed considering zero in both ends
    632             return this.WHOLE;
    633         }
    634
    635         if (x.hi < 0) {
    636             // negative / v
    637             return new MatInterval(Number.NEGATIVE_INFINITY, this.divHi(x.hi, v));
    638         }
    639         // positive / v
    640         return new MatInterval(this.divLo(x.lo, v), Number.POSITIVE_INFINITY);
    641     },
    642
    643     /**
    644      * @private
    645      * @param {JXG.Math.Interval} x
    646      * @param {JXG.Math.Interval} y
    647      * @returns JXG.Math.Interval
    648      */
    649     divNegative: function (x, v) {
    650         if (x.lo === 0 && x.hi === 0) {
    651             return x;
    652         }
    653
    654         if (this.zeroIn(x)) {
    655             // mixed considering zero in both ends
    656             return this.WHOLE;
    657         }
    658
    659         if (x.hi < 0) {
    660             // negative / v
    661             return new MatInterval(this.divLo(x.hi, v), Number.POSITIVE_INFINITY);
    662         }
    663         // positive / v
    664         return new MatInterval(Number.NEGATIVE_INFINITY, this.divHi(x.lo, v));
    665     },
    666
    667     /**
    668      * @private
    669      * @param {JXG.Math.Interval} x
    670      * @returns JXG.Math.Interval
    671      */
    672     divZero: function (x) {
    673         if (x.lo === 0 && x.hi === 0) {
    674             return x;
    675         }
    676         return this.WHOLE;
    677     },
    678
    679     /*
    680      * Algebra
    681      */
    682     /**
    683      * x mod y:  x - n * y
    684      * @param {JXG.Math.Interval|Number} x
    685      * @param {JXG.Math.Interval|Number} y
    686      * @returns JXG.Math.Interval
    687      */
    688     fmod: function (x, y) {
    689         var yb, n;
    690         if (Type.isNumber(x)) {
    691             x = this.Interval(x);
    692         }
    693         if (Type.isNumber(y)) {
    694             y = this.Interval(y);
    695         }
    696         if (this.isEmpty(x) || this.isEmpty(y)) {
    697             return this.EMPTY.clone();
    698         }
    699         yb = x.lo < 0 ? y.lo : y.hi;
    700         n = x.lo / yb;
    701         if (n < 0) {
    702             n = Math.ceil(n);
    703         } else {
    704             n = Math.floor(n);
    705         }
    706         // x mod y = x - n * y
    707         return this.sub(x, this.mul(y, new MatInterval(n)));
    708     },
    709
    710     /**
    711      * 1 / x
    712      * @param {JXG.Math.Interval|Number} x
    713      * @returns JXG.Math.Interval
    714      */
    715     multiplicativeInverse: function (x) {
    716         if (Type.isNumber(x)) {
    717             x = this.Interval(x);
    718         }
    719         if (this.isEmpty(x)) {
    720             return this.EMPTY.clone();
    721         }
    722         if (this.zeroIn(x)) {
    723             if (x.lo !== 0) {
    724                 if (x.hi !== 0) {
    725                     // [negative, positive]
    726                     return this.WHOLE;
    727                 }
    728                 // [negative, zero]
    729                 return new MatInterval(Number.NEGATIVE_INFINITY, this.divHi(1, x.lo));
    730             }
    731             if (x.hi !== 0) {
    732                 // [zero, positive]
    733                 return new MatInterval(this.divLo(1, x.hi), Number.POSITIVE_INFINITY);
    734             }
    735             // [zero, zero]
    736             return this.EMPTY.clone();
    737         }
    738         // [positive, positive]
    739         return new MatInterval(this.divLo(1, x.hi), this.divHi(1, x.lo));
    740     },
    741
    742     /**
    743      * x<sup>power</sup>
    744      * @param {JXG.Math.Interval|Number} x
    745      * @param {JXG.Math.Interval|Number} power
    746      * @returns JXG.Math.Interval
    747      */
    748     pow: function (x, power) {
    749         var yl, yh;
    750
    751         if (Type.isNumber(x)) {
    752             x = this.Interval(x);
    753         }
    754         if (this.isEmpty(x)) {
    755             return this.EMPTY.clone();
    756         }
    757         if (this.isInterval(power)) {
    758             if (!this.isSingleton(power)) {
    759                 return this.EMPTY.clone();
    760             }
    761             power = power.lo;
    762         }
    763
    764         if (power === 0) {
    765             if (x.lo === 0 && x.hi === 0) {
    766                 // 0^0
    767                 return this.EMPTY.clone();
    768             }
    769             // x^0
    770             return this.ONE.clone();
    771         }
    772         if (power < 0) {
    773             // compute [1 / x]^-power if power is negative
    774             return this.pow(this.multiplicativeInverse(x), -power);
    775         }
    776
    777         // power > 0
    778         if (power % 1 === 0) {
    779             // isSafeInteger(power) as boolean) {
    780             // power is integer
    781             if (x.hi < 0) {
    782                 // [negative, negative]
    783                 // assume that power is even so the operation will yield a positive interval
    784                 // if not then just switch the sign and order of the interval bounds
    785                 yl = this.powLo(-x.hi, power);
    786                 yh = this.powHi(-x.lo, power);
    787                 if ((power & 1) === 1) {
    788                     // odd power
    789                     return new MatInterval(-yh, -yl);
    790                 }
    791                 // even power
    792                 return new MatInterval(yl, yh);
    793             }
    794             if (x.lo < 0) {
    795                 // [negative, positive]
    796                 if ((power & 1) === 1) {
    797                     return new MatInterval(-this.powLo(-x.lo, power), this.powHi(x.hi, power));
    798                 }
    799                 // even power means that any negative number will be zero (min value = 0)
    800                 // and the max value will be the max of x.lo^power, x.hi^power
    801                 return new MatInterval(0, this.powHi(Math.max(-x.lo, x.hi), power));
    802             }
    803             // [positive, positive]
    804             return new MatInterval(this.powLo(x.lo, power), this.powHi(x.hi, power));
    805         }
    806         console.warn(
    807             "power is not an integer, you should use nth-root instead, returning an empty interval"
    808         );
    809         return this.EMPTY.clone();
    810     },
    811
    812     /**
    813      * sqrt(x)
    814      * @param {JXG.Math.Interval|Number} x
    815      * @returns JXG.Math.Interval
    816      */
    817     sqrt: function (x) {
    818         if (Type.isNumber(x)) {
    819             x = this.Interval(x);
    820         }
    821         return this.nthRoot(x, 2);
    822     },
    823
    824     /**
    825      * x<sup>1/n</sup>
    826      * @param {JXG.Math.Interval|Number} x
    827      * @param {Number} n
    828      * @returns JXG.Math.Interval
    829      */
    830     nthRoot: function (x, n) {
    831         var power, yl, yh, yp, yn;
    832
    833         if (Type.isNumber(x)) {
    834             x = this.Interval(x);
    835         }
    836         if (this.isEmpty(x) || n < 0) {
    837             // compute 1 / x^-power if power is negative
    838             return this.EMPTY.clone();
    839         }
    840
    841         // singleton interval check
    842         if (this.isInterval(n)) {
    843             if (!this.isSingleton(n)) {
    844                 return this.EMPTY.clone();
    845             }
    846             n = n.lo;
    847         }
    848
    849         power = 1 / n;
    850         if (x.hi < 0) {
    851             // [negative, negative]
    852             //if ((isSafeInteger(n) as boolean) && (n & 1) === 1) {
    853             if (n % 1 === 0 && (n & 1) === 1) {
    854                 // when n is odd we can always take the nth root
    855                 yl = this.powHi(-x.lo, power);
    856                 yh = this.powLo(-x.hi, power);
    857                 return new MatInterval(-yl, -yh);
    858             }
    859
    860             // n is not odd therefore there's no nth root
    861             return this.EMPTY.clone();
    862         }
    863         if (x.lo < 0) {
    864             // [negative, positive]
    865             yp = this.powHi(x.hi, power);
    866             // if ((isSafeInteger(n) as boolean) && (n & 1) === 1) {
    867             if (n % 1 === 0 && (n & 1) === 1) {
    868                 // nth root of x.lo is possible (n is odd)
    869                 yn = -this.powHi(-x.lo, power);
    870                 return new MatInterval(yn, yp);
    871             }
    872             return new MatInterval(0, yp);
    873         }
    874         // [positive, positive]
    875         return new MatInterval(this.powLo(x.lo, power), this.powHi(x.hi, power));
    876     },
    877
    878     /*
    879      * Misc
    880      */
    881     /**
    882      *
    883      * @param {JXG.Math.Interval|Number} x
    884      * @returns JXG.Math.Interval
    885      */
    886     exp: function (x) {
    887         if (Type.isNumber(x)) {
    888             x = this.Interval(x);
    889         }
    890         if (this.isEmpty(x)) {
    891             return this.EMPTY.clone();
    892         }
    893         return new MatInterval(this.expLo(x.lo), this.expHi(x.hi));
    894     },
    895
    896     /**
    897      * Natural log
    898      * @param {JXG.Math.Interval|Number} x
    899      * @returns JXG.Math.Interval
    900      */
    901     log: function (x) {
    902         var l;
    903         if (Type.isNumber(x)) {
    904             x = this.Interval(x);
    905         }
    906         if (this.isEmpty(x)) {
    907             return this.EMPTY.clone();
    908         }
    909         l = x.lo <= 0 ? Number.NEGATIVE_INFINITY : this.logLo(x.lo);
    910         return new MatInterval(l, this.logHi(x.hi));
    911     },
    912
    913     /**
    914      * Natural log, alias for {@link JXG.Math.IntervalArithmetic#log}.
    915      * @param {JXG.Math.Interval|Number} x
    916      * @returns JXG.Math.Interval
    917      */
    918     ln: function (x) {
    919         return this.log(x);
    920     },
    921
    922     // export const LOG_EXP_10 = this.log(new MatInterval(10, 10))
    923     // export const LOG_EXP_2 = log(new MatInterval(2, 2))
    924     /**
    925      * Logarithm to base 10.
    926      * @param {JXG.Math.Interval|Number} x
    927      * @returns JXG.Math.Interval
    928      */
    929     log10: function (x) {
    930         if (this.isEmpty(x)) {
    931             return this.EMPTY.clone();
    932         }
    933         return this.div(this.log(x), this.log(new MatInterval(10, 10)));
    934     },
    935
    936     /**
    937      * Logarithm to base 2.
    938      * @param {JXG.Math.Interval|Number} x
    939      * @returns JXG.Math.Interval
    940      */
    941     log2: function (x) {
    942         if (this.isEmpty(x)) {
    943             return this.EMPTY.clone();
    944         }
    945         return this.div(this.log(x), this.log(new MatInterval(2, 2)));
    946     },
    947
    948     /**
    949      * Hull of intervals x and y
    950      * @param {JXG.Math.Interval} x
    951      * @param {JXG.Math.Interval} y
    952      * @returns JXG.Math.Interval
    953      */
    954     hull: function (x, y) {
    955         var badX = this.isEmpty(x),
    956             badY = this.isEmpty(y);
    957         if (badX && badY) {
    958             return this.EMPTY.clone();
    959         }
    960         if (badX) {
    961             return y.clone();
    962         }
    963         if (badY) {
    964             return x.clone();
    965         }
    966         return new MatInterval(Math.min(x.lo, y.lo), Math.max(x.hi, y.hi));
    967     },
    968
    969     /**
    970      * Intersection of intervals x and y
    971      * @param {JXG.Math.Interval} x
    972      * @param {JXG.Math.Interval} y
    973      * @returns JXG.Math.Interval
    974      */
    975     intersection: function (x, y) {
    976         var lo, hi;
    977         if (this.isEmpty(x) || this.isEmpty(y)) {
    978             return this.EMPTY.clone();
    979         }
    980         lo = Math.max(x.lo, y.lo);
    981         hi = Math.min(x.hi, y.hi);
    982         if (lo <= hi) {
    983             return new MatInterval(lo, hi);
    984         }
    985         return this.EMPTY.clone();
    986     },
    987
    988     /**
    989      * Union of overlapping intervals x and y
    990      * @param {JXG.Math.Interval} x
    991      * @param {JXG.Math.Interval} y
    992      * @returns JXG.Math.Interval
    993      */
    994     union: function (x, y) {
    995         if (!this.intervalsOverlap(x, y)) {
    996             throw new Error("Interval#unions do not overlap");
    997         }
    998         return new MatInterval(Math.min(x.lo, y.lo), Math.max(x.hi, y.hi));
    999     },
    1000
    1001     /**
    1002      * Difference of overlapping intervals x and y
    1003      * @param {JXG.Math.Interval} x
    1004      * @param {JXG.Math.Interval} y
    1005      * @returns JXG.Math.Interval
    1006      */
    1007     difference: function (x, y) {
    1008         if (this.isEmpty(x) || this.isWhole(y)) {
    1009             return this.EMPTY.clone();
    1010         }
    1011         if (this.intervalsOverlap(x, y)) {
    1012             if (x.lo < y.lo && y.hi < x.hi) {
    1013                 // difference creates multiple subsets
    1014                 throw new Error("Interval.difference: difference creates multiple intervals");
    1015             }
    1016
    1017             // handle corner cases first
    1018             if ((y.lo <= x.lo && y.hi === Infinity) || (y.hi >= x.hi && y.lo === -Infinity)) {
    1019                 return this.EMPTY.clone();
    1020             }
    1021
    1022             // NOTE: empty interval is handled automatically
    1023             // e.g.
    1024             //
    1025             //    n = difference([0,1], [0,1]) // n = Interval(next(1), 1) = EMPTY
    1026             //    isEmpty(n) === true
    1027             //
    1028             if (y.lo <= x.lo) {
    1029                 return new MatInterval().halfOpenLeft(y.hi, x.hi);
    1030             }
    1031
    1032             // y.hi >= x.hi
    1033             return new MatInterval().halfOpenRight(x.lo, y.lo);
    1034         }
    1035         return x.clone();
    1036     },
    1037
    1038     /**
    1039      * @param {JXG.Math.Interval} x
    1040      * @returns JXG.Math.Interval
    1041      */
    1042     width: function (x) {
    1043         if (this.isEmpty(x)) {
    1044             return 0;
    1045         }
    1046         return this.subHi(x.hi, x.lo);
    1047     },
    1048
    1049     /**
    1050      * @param {JXG.Math.Interval} x
    1051      * @returns JXG.Math.Interval
    1052      */
    1053     abs: function (x) {
    1054         if (Type.isNumber(x)) {
    1055             x = this.Interval(x);
    1056         }
    1057         if (this.isEmpty(x)) {
    1058             return this.EMPTY.clone();
    1059         }
    1060         if (x.lo >= 0) {
    1061             return x.clone();
    1062         }
    1063         if (x.hi <= 0) {
    1064             return this.negative(x);
    1065         }
    1066         return new MatInterval(0, Math.max(-x.lo, x.hi));
    1067     },
    1068
    1069     /**
    1070      * @param {JXG.Math.Interval} x
    1071      * @param {JXG.Math.Interval} y
    1072      * @returns JXG.Math.Interval
    1073      */
    1074     max: function (x, y) {
    1075         var badX = this.isEmpty(x),
    1076             badY = this.isEmpty(y);
    1077         if (badX && badY) {
    1078             return this.EMPTY.clone();
    1079         }
    1080         if (badX) {
    1081             return y.clone();
    1082         }
    1083         if (badY) {
    1084             return x.clone();
    1085         }
    1086         return new MatInterval(Math.max(x.lo, y.lo), Math.max(x.hi, y.hi));
    1087     },
    1088
    1089     /**
    1090      * @param {JXG.Math.Interval} x
    1091      * @param {JXG.Math.Interval} y
    1092      * @returns JXG.Math.Interval
    1093      */
    1094     min: function (x, y) {
    1095         var badX = this.isEmpty(x),
    1096             badY = this.isEmpty(y);
    1097         if (badX && badY) {
    1098             return this.EMPTY.clone();
    1099         }
    1100         if (badX) {
    1101             return y.clone();
    1102         }
    1103         if (badY) {
    1104             return x.clone();
    1105         }
    1106         return new MatInterval(Math.min(x.lo, y.lo), Math.min(x.hi, y.hi));
    1107     },
    1108
    1109     /*
    1110      * Trigonometric
    1111      */
    1112     onlyInfinity: function (x) {
    1113         return !isFinite(x.lo) && x.lo === x.hi;
    1114     },
    1115
    1116     _handleNegative: function (interval) {
    1117         var n;
    1118         if (interval.lo < 0) {
    1119             if (interval.lo === -Infinity) {
    1120                 interval.lo = 0;
    1121                 interval.hi = Infinity;
    1122             } else {
    1123                 n = Math.ceil(-interval.lo / this.piTwiceLow);
    1124                 interval.lo += this.piTwiceLow * n;
    1125                 interval.hi += this.piTwiceLow * n;
    1126             }
    1127         }
    1128         return interval;
    1129     },
    1130
    1131     /**
    1132      * @param {JXG.Math.Interval} x
    1133      * @returns JXG.Math.Interval
    1134      */
    1135     cos: function (x) {
    1136         var cache, pi2, t, cosv, lo, hi, rlo, rhi;
    1137
    1138         if (this.isEmpty(x) || this.onlyInfinity(x)) {
    1139             return this.EMPTY.clone();
    1140         }
    1141
    1142         // create a clone of `x` because the clone is going to be modified
    1143         cache = new MatInterval().set(x.lo, x.hi);
    1144         this._handleNegative(cache);
    1145
    1146         pi2 = this.PI_TWICE;
    1147         t = this.fmod(cache, pi2);
    1148         if (this.width(t) >= pi2.lo) {
    1149             return new MatInterval(-1, 1);
    1150         }
    1151
    1152         // when t.lo > pi it's the same as
    1153         // -cos(t - pi)
    1154         if (t.lo >= this.piHigh) {
    1155             cosv = this.cos(this.sub(t, this.PI));
    1156             return this.negative(cosv);
    1157         }
    1158
    1159         lo = t.lo;
    1160         hi = t.hi;
    1161         rlo = this.cosLo(hi);
    1162         rhi = this.cosHi(lo);
    1163         // it's ensured that t.lo < pi and that t.lo >= 0
    1164         if (hi <= this.piLow) {
    1165             // when t.hi < pi
    1166             // [cos(t.lo), cos(t.hi)]
    1167             return new MatInterval(rlo, rhi);
    1168         }
    1169         if (hi <= pi2.lo) {
    1170             // when t.hi < 2pi
    1171             // [-1, max(cos(t.lo), cos(t.hi))]
    1172             return new MatInterval(-1, Math.max(rlo, rhi));
    1173         }
    1174         // t.lo < pi and t.hi > 2pi
    1175         return new MatInterval(-1, 1);
    1176     },
    1177
    1178     /**
    1179      * @param {JXG.Math.Interval} x
    1180      * @returns JXG.Math.Interval
    1181      */
    1182     sin: function (x) {
    1183         if (this.isEmpty(x) || this.onlyInfinity(x)) {
    1184             return this.EMPTY.clone();
    1185         }
    1186         return this.cos(this.sub(x, this.PI_HALF));
    1187     },
    1188
    1189     /**
    1190      * @param {JXG.Math.Interval} x
    1191      * @returns JXG.Math.Interval
    1192      */
    1193     tan: function (x) {
    1194         var cache, t, pi;
    1195         if (this.isEmpty(x) || this.onlyInfinity(x)) {
    1196             return this.EMPTY.clone();
    1197         }
    1198
    1199         // create a clone of `x` because the clone is going to be modified
    1200         cache = new MatInterval().set(x.lo, x.hi);
    1201         this._handleNegative(cache);
    1202
    1203         pi = this.PI;
    1204         t = this.fmod(cache, pi);
    1205         if (t.lo >= this.piHalfLow) {
    1206             t = this.sub(t, pi);
    1207         }
    1208         if (t.lo <= -this.piHalfLow || t.hi >= this.piHalfLow) {
    1209             return this.WHOLE.clone();
    1210         }
    1211         return new MatInterval(this.tanLo(t.lo), this.tanHi(t.hi));
    1212     },
    1213
    1214     /**
    1215      * @param {JXG.Math.Interval} x
    1216      * @returns JXG.Math.Interval
    1217      */
    1218     asin: function (x) {
    1219         var lo, hi;
    1220         if (this.isEmpty(x) || x.hi < -1 || x.lo > 1) {
    1221             return this.EMPTY.clone();
    1222         }
    1223         lo = x.lo <= -1 ? -this.piHalfHigh : this.asinLo(x.lo);
    1224         hi = x.hi >= 1 ? this.piHalfHigh : this.asinHi(x.hi);
    1225         return new MatInterval(lo, hi);
    1226     },
    1227
    1228     /**
    1229      * @param {JXG.Math.Interval} x
    1230      * @returns JXG.Math.Interval
    1231      */
    1232     acos: function (x) {
    1233         var lo, hi;
    1234         if (this.isEmpty(x) || x.hi < -1 || x.lo > 1) {
    1235             return this.EMPTY.clone();
    1236         }
    1237         lo = x.hi >= 1 ? 0 : this.acosLo(x.hi);
    1238         hi = x.lo <= -1 ? this.piHigh : this.acosHi(x.lo);
    1239         return new MatInterval(lo, hi);
    1240     },
    1241
    1242     /**
    1243      * @param {JXG.Math.Interval} x
    1244      * @returns JXG.Math.Interval
    1245      */
    1246     acot: function (x) {
    1247         if (this.isEmpty(x)) {
    1248             return this.EMPTY.clone();
    1249         }
    1250         return new MatInterval(this.acotLo(x.lo), this.acotHi(x.hi));
    1251     },
    1252
    1253     /**
    1254      * @param {JXG.Math.Interval} x
    1255      * @returns JXG.Math.Interval
    1256      */
    1257     atan: function (x) {
    1258         if (this.isEmpty(x)) {
    1259             return this.EMPTY.clone();
    1260         }
    1261         return new MatInterval(this.atanLo(x.lo), this.atanHi(x.hi));
    1262     },
    1263
    1264     /**
    1265      * @param {JXG.Math.Interval} x
    1266      * @returns JXG.Math.Interval
    1267      */
    1268     sinh: function (x) {
    1269         if (this.isEmpty(x)) {
    1270             return this.EMPTY.clone();
    1271         }
    1272         return new MatInterval(this.sinhLo(x.lo), this.sinhHi(x.hi));
    1273     },
    1274
    1275     /**
    1276      * @param {JXG.Math.Interval} x
    1277      * @returns JXG.Math.Interval
    1278      */
    1279     cosh: function (x) {
    1280         if (this.isEmpty(x)) {
    1281             return this.EMPTY.clone();
    1282         }
    1283         if (x.hi < 0) {
    1284             return new MatInterval(this.coshLo(x.hi), this.coshHi(x.lo));
    1285         }
    1286         if (x.lo >= 0) {
    1287             return new MatInterval(this.coshLo(x.lo), this.coshHi(x.hi));
    1288         }
    1289         return new MatInterval(1, this.coshHi(-x.lo > x.hi ? x.lo : x.hi));
    1290     },
    1291
    1292     /**
    1293      * @param {JXG.Math.Interval} x
    1294      * @returns JXG.Math.Interval
    1295      */
    1296     tanh: function (x) {
    1297         if (this.isEmpty(x)) {
    1298             return this.EMPTY.clone();
    1299         }
    1300         return new MatInterval(this.tanhLo(x.lo), this.tanhHi(x.hi));
    1301     },
    1302
    1303     /*
    1304      * Relational
    1305      */
    1306
    1307     /**
    1308      * @param {JXG.Math.Interval} x
    1309      * @param {JXG.Math.Interval} y
    1310      * @returns Boolean
    1311      */
    1312     equal: function (x, y) {
    1313         if (this.isEmpty(x)) {
    1314             return this.isEmpty(y);
    1315         }
    1316         return !this.isEmpty(y) && x.lo === y.lo && x.hi === y.hi;
    1317     },
    1318
    1319     // almostEqual: function(x, y): void {
    1320     //     x = Array.isArray(x) ? x : x.toArray();
    1321     //     y = Array.isArray(y) ? y : y.toArray();
    1322     //     assertEps(x[0], y[0])
    1323     //     assertEps(x[1], y[1])
    1324     // },
    1325
    1326     /**
    1327      * @param {JXG.Math.Interval} x
    1328      * @param {JXG.Math.Interval} y
    1329      * @returns Boolean
    1330      */
    1331     notEqual: function (x, y) {
    1332         if (this.isEmpty(x)) {
    1333             return !this.isEmpty(y);
    1334         }
    1335         return this.isEmpty(y) || x.hi < y.lo || x.lo > y.hi;
    1336     },
    1337
    1338     /**
    1339      * @param {JXG.Math.Interval} x
    1340      * @param {JXG.Math.Interval} y
    1341      * @returns Boolean
    1342      */
    1343     lt: function (x, y) {
    1344         if (Type.isNumber(x)) {
    1345             x = this.Interval(x);
    1346         }
    1347         if (Type.isNumber(y)) {
    1348             y = this.Interval(y);
    1349         }
    1350         if (this.isEmpty(x) || this.isEmpty(y)) {
    1351             return false;
    1352         }
    1353         return x.hi < y.lo;
    1354     },
    1355
    1356     /**
    1357      * @param {JXG.Math.Interval} x
    1358      * @param {JXG.Math.Interval} y
    1359      * @returns Boolean
    1360      */
    1361     gt: function (x, y) {
    1362         if (Type.isNumber(x)) {
    1363             x = this.Interval(x);
    1364         }
    1365         if (Type.isNumber(y)) {
    1366             y = this.Interval(y);
    1367         }
    1368         if (this.isEmpty(x) || this.isEmpty(y)) {
    1369             return false;
    1370         }
    1371         return x.lo > y.hi;
    1372     },
    1373
    1374     /**
    1375      * @param {JXG.Math.Interval} x
    1376      * @param {JXG.Math.Interval} y
    1377      * @returns Boolean
    1378      */
    1379     leq: function (x, y) {
    1380         if (Type.isNumber(x)) {
    1381             x = this.Interval(x);
    1382         }
    1383         if (Type.isNumber(y)) {
    1384             y = this.Interval(y);
    1385         }
    1386         if (this.isEmpty(x) || this.isEmpty(y)) {
    1387             return false;
    1388         }
    1389         return x.hi <= y.lo;
    1390     },
    1391
    1392     /**
    1393      * @param {JXG.Math.Interval} x
    1394      * @param {JXG.Math.Interval} y
    1395      * @returns Boolean
    1396      */
    1397     geq: function (x, y) {
    1398         if (Type.isNumber(x)) {
    1399             x = this.Interval(x);
    1400         }
    1401         if (Type.isNumber(y)) {
    1402             y = this.Interval(y);
    1403         }
    1404         if (this.isEmpty(x) || this.isEmpty(y)) {
    1405             return false;
    1406         }
    1407         return x.lo >= y.hi;
    1408     },
    1409
    1410     /*
    1411      * Constants
    1412      */
    1413     piLow: (3373259426.0 + 273688.0 / (1 << 21)) / (1 << 30),
    1414     piHigh: (3373259426.0 + 273689.0 / (1 << 21)) / (1 << 30),
    1415     piHalfLow: ((3373259426.0 + 273688.0 / (1 << 21)) / (1 << 30)) * 0.5,
    1416     piHalfHigh: ((3373259426.0 + 273689.0 / (1 << 21)) / (1 << 30)) * 0.5,
    1417     piTwiceLow: ((3373259426.0 + 273688.0 / (1 << 21)) / (1 << 30)) * 2,
    1418     piTwiceHigh: ((3373259426.0 + 273689.0 / (1 << 21)) / (1 << 30)) * 2,
    1419
    1420     /*
    1421      * Round
    1422      * Rounding functions for numbers
    1423      */
    1424     identity: function (v) {
    1425         return v;
    1426     },
    1427
    1428     _prev: function (v) {
    1429         if (v === Infinity) {
    1430             return v;
    1431         }
    1432         return this.nextafter(v, -Infinity);
    1433     },
    1434
    1435     _next: function (v) {
    1436         if (v === -Infinity) {
    1437             return v;
    1438         }
    1439         return this.nextafter(v, Infinity);
    1440     },
    1441
    1442     prev: function (v) {
    1443         return this._prev(v);
    1444     },
    1445
    1446     next: function (v) {
    1447         return this._next(v);
    1448     },
    1449
    1450     toInteger: function (x) {
    1451         return x < 0 ? Math.ceil(x) : Math.floor(x);
    1452     },
    1453
    1454     addLo: function (x, y) {
    1455         return this.prev(x + y);
    1456     },
    1457     addHi: function (x, y) {
    1458         return this.next(x + y);
    1459     },
    1460     subLo: function (x, y) {
    1461         return this.prev(x - y);
    1462     },
    1463     subHi: function (x, y) {
    1464         return this.next(x - y);
    1465     },
    1466     mulLo: function (x, y) {
    1467         return this.prev(x * y);
    1468     },
    1469     mulHi: function (x, y) {
    1470         return this.next(x * y);
    1471     },
    1472     divLo: function (x, y) {
    1473         return this.prev(x / y);
    1474     },
    1475     divHi: function (x, y) {
    1476         return this.next(x / y);
    1477     },
    1478     intLo: function (x) {
    1479         return this.toInteger(this.prev(x));
    1480     },
    1481     intHi: function (x) {
    1482         return this.toInteger(this.next(x));
    1483     },
    1484     logLo: function (x) {
    1485         return this.prev(Math.log(x));
    1486     },
    1487     logHi: function (x) {
    1488         return this.next(Math.log(x));
    1489     },
    1490     expLo: function (x) {
    1491         return this.prev(Math.exp(x));
    1492     },
    1493     expHi: function (x) {
    1494         return this.next(Math.exp(x));
    1495     },
    1496     sinLo: function (x) {
    1497         return this.prev(Math.sin(x));
    1498     },
    1499     sinHi: function (x) {
    1500         return this.next(Math.sin(x));
    1501     },
    1502     cosLo: function (x) {
    1503         return this.prev(Math.cos(x));
    1504     },
    1505     cosHi: function (x) {
    1506         return this.next(Math.cos(x));
    1507     },
    1508     tanLo: function (x) {
    1509         return this.prev(Math.tan(x));
    1510     },
    1511     tanHi: function (x) {
    1512         return this.next(Math.tan(x));
    1513     },
    1514     asinLo: function (x) {
    1515         return this.prev(Math.asin(x));
    1516     },
    1517     asinHi: function (x) {
    1518         return this.next(Math.asin(x));
    1519     },
    1520     acosLo: function (x) {
    1521         return this.prev(Math.acos(x));
    1522     },
    1523     acosHi: function (x) {
    1524         return this.next(Math.acos(x));
    1525     },
    1526     acotLo: function (x) {
    1527         return this.prev(Mat.acot(x));
    1528     },
    1529     acotHi: function (x) {
    1530         return this.next(Mat.acot(x));
    1531     },
    1532     atanLo: function (x) {
    1533         return this.prev(Math.atan(x));
    1534     },
    1535     atanHi: function (x) {
    1536         return this.next(Math.atan(x));
    1537     },
    1538     sinhLo: function (x) {
    1539         return this.prev(Mat.sinh(x));
    1540     },
    1541     sinhHi: function (x) {
    1542         return this.next(Mat.sinh(x));
    1543     },
    1544     coshLo: function (x) {
    1545         return this.prev(Mat.cosh(x));
    1546     },
    1547     coshHi: function (x) {
    1548         return this.next(Mat.cosh(x));
    1549     },
    1550     tanhLo: function (x) {
    1551         return this.prev(Mat.tanh(x));
    1552     },
    1553     tanhHi: function (x) {
    1554         return this.next(Mat.tanh(x));
    1555     },
    1556     sqrtLo: function (x) {
    1557         return this.prev(Math.sqrt(x));
    1558     },
    1559     sqrtHi: function (x) {
    1560         return this.next(Math.sqrt(x));
    1561     },
    1562
    1563     powLo: function (x, power) {
    1564         var y;
    1565         if (power % 1 !== 0) {
    1566             // power has decimals
    1567             return this.prev(Math.pow(x, power));
    1568         }
    1569
    1570         y = (power & 1) === 1 ? x : 1;
    1571         power >>= 1;
    1572         while (power > 0) {
    1573             x = this.mulLo(x, x);
    1574             if ((power & 1) === 1) {
    1575                 y = this.mulLo(x, y);
    1576             }
    1577             power >>= 1;
    1578         }
    1579         return y;
    1580     },
    1581
    1582     powHi: function (x, power) {
    1583         var y;
    1584         if (power % 1 !== 0) {
    1585             // power has decimals
    1586             return this.next(Math.pow(x, power));
    1587         }
    1588
    1589         y = (power & 1) === 1 ? x : 1;
    1590         power >>= 1;
    1591         while (power > 0) {
    1592             x = this.mulHi(x, x);
    1593             if ((power & 1) === 1) {
    1594                 y = this.mulHi(x, y);
    1595             }
    1596             power >>= 1;
    1597         }
    1598         return y;
    1599     },
    1600
    1601     /**
    1602      * @ignore
    1603      * @private
    1604      */
    1605     disable: function () {
    1606         this.next = this.prev = this.identity;
    1607     },
    1608
    1609     /**
    1610      * @ignore
    1611      * @private
    1612      */
    1613     enable: function () {
    1614         this.prev = function (v) {
    1615             return this._prev(v);
    1616         };
    1617
    1618         this.next = function (v) {
    1619             return this._next(v);
    1620         };
    1621     },
    1622
    1623     /*
    1624      * nextafter
    1625      */
    1626     SMALLEST_DENORM: Math.pow(2, -1074),
    1627     UINT_MAX: -1 >>> 0,
    1628
    1629     nextafter: function (x, y) {
    1630         var lo, hi;
    1631
    1632         if (isNaN(x) || isNaN(y)) {
    1633             return NaN;
    1634         }
    1635         if (x === y) {
    1636             return x;
    1637         }
    1638         if (x === 0) {
    1639             if (y < 0) {
    1640                 return -this.SMALLEST_DENORM;
    1641             }
    1642             return this.SMALLEST_DENORM;
    1643         }
    1644         hi = doubleBits.hi(x);
    1645         lo = doubleBits.lo(x);
    1646         if (y > x === x > 0) {
    1647             if (lo === this.UINT_MAX) {
    1648                 hi += 1;
    1649                 lo = 0;
    1650             } else {
    1651                 lo += 1;
    1652             }
    1653         } else {
    1654             if (lo === 0) {
    1655                 lo = this.UINT_MAX;
    1656                 hi -= 1;
    1657             } else {
    1658                 lo -= 1;
    1659             }
    1660         }
    1661         return doubleBits.pack(lo, hi);
    1662     }
    1663 };
    1664
    1665 JXG.Math.IntervalArithmetic.PI = new MatInterval(
    1666     Mat.IntervalArithmetic.piLow,
    1667     Mat.IntervalArithmetic.piHigh
    1668 );
    1669 JXG.Math.IntervalArithmetic.PI_HALF = new MatInterval(
    1670     Mat.IntervalArithmetic.piHalfLow,
    1671     Mat.IntervalArithmetic.piHalfHigh
    1672 );
    1673 JXG.Math.IntervalArithmetic.PI_TWICE = new MatInterval(
    1674     Mat.IntervalArithmetic.piTwiceLow,
    1675     Mat.IntervalArithmetic.piTwiceHigh
    1676 );
    1677 JXG.Math.IntervalArithmetic.ZERO = new MatInterval(0);
    1678 JXG.Math.IntervalArithmetic.ONE = new MatInterval(1);
    1679 JXG.Math.IntervalArithmetic.WHOLE = new MatInterval().setWhole();
    1680 JXG.Math.IntervalArithmetic.EMPTY = new MatInterval().setEmpty();
    1681
    1682 export default JXG.Math.IntervalArithmetic;
    1683