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