1 /*
  2     Copyright 2008-2023
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Alfred Wassermann
  7 
  8     This file is part of JSXGraph.
  9 
 10     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 11 
 12     You can redistribute it and/or modify it under the terms of the
 13 
 14       * GNU Lesser General Public License as published by
 15         the Free Software Foundation, either version 3 of the License, or
 16         (at your option) any later version
 17       OR
 18       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 19 
 20     JSXGraph is distributed in the hope that it will be useful,
 21     but WITHOUT ANY WARRANTY; without even the implied warranty of
 22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 23     GNU Lesser General Public License for more details.
 24 
 25     You should have received a copy of the GNU Lesser General Public License and
 26     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
 27     and <https://opensource.org/licenses/MIT/>.
 28  */
 29 
 30 /*global JXG: true, define: true*/
 31 /*jslint nomen: true, plusplus: true*/
 32 
 33 import JXG from "../jxg";
 34 import Mat from "./math";
 35 import Type from "../utils/type";
 36 
 37 JXG.Math.DoubleBits = function () {
 38     var hasTypedArrays = false,
 39         DOUBLE_VIEW = new Float64Array(1),
 40         UINT_VIEW = new Uint32Array(DOUBLE_VIEW.buffer),
 41         doubleBitsLE,
 42         toDoubleLE,
 43         lowUintLE,
 44         highUintLE,
 45         doubleBitsBE,
 46         toDoubleBE,
 47         lowUintBE,
 48         highUintBE,
 49         doubleBits,
 50         toDouble,
 51         lowUint,
 52         highUint;
 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