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     atan: function (x) {
1247         if (this.isEmpty(x)) {
1248             return this.EMPTY.clone();
1249         }
1250         return new MatInterval(this.atanLo(x.lo), this.atanHi(x.hi));
1251     },
1252 
1253     /**
1254      * @param {JXG.Math.Interval} x
1255      * @returns JXG.Math.Interval
1256      */
1257     sinh: function (x) {
1258         if (this.isEmpty(x)) {
1259             return this.EMPTY.clone();
1260         }
1261         return new MatInterval(this.sinhLo(x.lo), this.sinhHi(x.hi));
1262     },
1263 
1264     /**
1265      * @param {JXG.Math.Interval} x
1266      * @returns JXG.Math.Interval
1267      */
1268     cosh: function (x) {
1269         if (this.isEmpty(x)) {
1270             return this.EMPTY.clone();
1271         }
1272         if (x.hi < 0) {
1273             return new MatInterval(this.coshLo(x.hi), this.coshHi(x.lo));
1274         }
1275         if (x.lo >= 0) {
1276             return new MatInterval(this.coshLo(x.lo), this.coshHi(x.hi));
1277         }
1278         return new MatInterval(1, this.coshHi(-x.lo > x.hi ? x.lo : x.hi));
1279     },
1280 
1281     /**
1282      * @param {JXG.Math.Interval} x
1283      * @returns JXG.Math.Interval
1284      */
1285     tanh: function (x) {
1286         if (this.isEmpty(x)) {
1287             return this.EMPTY.clone();
1288         }
1289         return new MatInterval(this.tanhLo(x.lo), this.tanhHi(x.hi));
1290     },
1291 
1292     /*
1293      * Relational
1294      */
1295 
1296     /**
1297      * @param {JXG.Math.Interval} x
1298      * @param {JXG.Math.Interval} y
1299      * @returns Boolean
1300      */
1301     equal: function (x, y) {
1302         if (this.isEmpty(x)) {
1303             return this.isEmpty(y);
1304         }
1305         return !this.isEmpty(y) && x.lo === y.lo && x.hi === y.hi;
1306     },
1307 
1308     // almostEqual: function(x, y): void {
1309     //     x = Array.isArray(x) ? x : x.toArray();
1310     //     y = Array.isArray(y) ? y : y.toArray();
1311     //     assertEps(x[0], y[0])
1312     //     assertEps(x[1], y[1])
1313     // },
1314 
1315     /**
1316      * @param {JXG.Math.Interval} x
1317      * @param {JXG.Math.Interval} y
1318      * @returns Boolean
1319      */
1320     notEqual: function (x, y) {
1321         if (this.isEmpty(x)) {
1322             return !this.isEmpty(y);
1323         }
1324         return this.isEmpty(y) || x.hi < y.lo || x.lo > y.hi;
1325     },
1326 
1327     /**
1328      * @param {JXG.Math.Interval} x
1329      * @param {JXG.Math.Interval} y
1330      * @returns Boolean
1331      */
1332     lt: function (x, y) {
1333         if (Type.isNumber(x)) {
1334             x = this.Interval(x);
1335         }
1336         if (Type.isNumber(y)) {
1337             y = this.Interval(y);
1338         }
1339         if (this.isEmpty(x) || this.isEmpty(y)) {
1340             return false;
1341         }
1342         return x.hi < y.lo;
1343     },
1344 
1345     /**
1346      * @param {JXG.Math.Interval} x
1347      * @param {JXG.Math.Interval} y
1348      * @returns Boolean
1349      */
1350     gt: function (x, y) {
1351         if (Type.isNumber(x)) {
1352             x = this.Interval(x);
1353         }
1354         if (Type.isNumber(y)) {
1355             y = this.Interval(y);
1356         }
1357         if (this.isEmpty(x) || this.isEmpty(y)) {
1358             return false;
1359         }
1360         return x.lo > y.hi;
1361     },
1362 
1363     /**
1364      * @param {JXG.Math.Interval} x
1365      * @param {JXG.Math.Interval} y
1366      * @returns Boolean
1367      */
1368     leq: function (x, y) {
1369         if (Type.isNumber(x)) {
1370             x = this.Interval(x);
1371         }
1372         if (Type.isNumber(y)) {
1373             y = this.Interval(y);
1374         }
1375         if (this.isEmpty(x) || this.isEmpty(y)) {
1376             return false;
1377         }
1378         return x.hi <= y.lo;
1379     },
1380 
1381     /**
1382      * @param {JXG.Math.Interval} x
1383      * @param {JXG.Math.Interval} y
1384      * @returns Boolean
1385      */
1386     geq: function (x, y) {
1387         if (Type.isNumber(x)) {
1388             x = this.Interval(x);
1389         }
1390         if (Type.isNumber(y)) {
1391             y = this.Interval(y);
1392         }
1393         if (this.isEmpty(x) || this.isEmpty(y)) {
1394             return false;
1395         }
1396         return x.lo >= y.hi;
1397     },
1398 
1399     /*
1400      * Constants
1401      */
1402     piLow: (3373259426.0 + 273688.0 / (1 << 21)) / (1 << 30),
1403     piHigh: (3373259426.0 + 273689.0 / (1 << 21)) / (1 << 30),
1404     piHalfLow: ((3373259426.0 + 273688.0 / (1 << 21)) / (1 << 30)) * 0.5,
1405     piHalfHigh: ((3373259426.0 + 273689.0 / (1 << 21)) / (1 << 30)) * 0.5,
1406     piTwiceLow: ((3373259426.0 + 273688.0 / (1 << 21)) / (1 << 30)) * 2,
1407     piTwiceHigh: ((3373259426.0 + 273689.0 / (1 << 21)) / (1 << 30)) * 2,
1408 
1409     /*
1410      * Round
1411      * Rounding functions for numbers
1412      */
1413     identity: function (v) {
1414         return v;
1415     },
1416 
1417     _prev: function (v) {
1418         if (v === Infinity) {
1419             return v;
1420         }
1421         return this.nextafter(v, -Infinity);
1422     },
1423 
1424     _next: function (v) {
1425         if (v === -Infinity) {
1426             return v;
1427         }
1428         return this.nextafter(v, Infinity);
1429     },
1430 
1431     prev: function (v) {
1432         return this._prev(v);
1433     },
1434 
1435     next: function (v) {
1436         return this._next(v);
1437     },
1438 
1439     toInteger: function (x) {
1440         return x < 0 ? Math.ceil(x) : Math.floor(x);
1441     },
1442 
1443     addLo: function (x, y) {
1444         return this.prev(x + y);
1445     },
1446     addHi: function (x, y) {
1447         return this.next(x + y);
1448     },
1449     subLo: function (x, y) {
1450         return this.prev(x - y);
1451     },
1452     subHi: function (x, y) {
1453         return this.next(x - y);
1454     },
1455     mulLo: function (x, y) {
1456         return this.prev(x * y);
1457     },
1458     mulHi: function (x, y) {
1459         return this.next(x * y);
1460     },
1461     divLo: function (x, y) {
1462         return this.prev(x / y);
1463     },
1464     divHi: function (x, y) {
1465         return this.next(x / y);
1466     },
1467     intLo: function (x) {
1468         return this.toInteger(this.prev(x));
1469     },
1470     intHi: function (x) {
1471         return this.toInteger(this.next(x));
1472     },
1473     logLo: function (x) {
1474         return this.prev(Math.log(x));
1475     },
1476     logHi: function (x) {
1477         return this.next(Math.log(x));
1478     },
1479     expLo: function (x) {
1480         return this.prev(Math.exp(x));
1481     },
1482     expHi: function (x) {
1483         return this.next(Math.exp(x));
1484     },
1485     sinLo: function (x) {
1486         return this.prev(Math.sin(x));
1487     },
1488     sinHi: function (x) {
1489         return this.next(Math.sin(x));
1490     },
1491     cosLo: function (x) {
1492         return this.prev(Math.cos(x));
1493     },
1494     cosHi: function (x) {
1495         return this.next(Math.cos(x));
1496     },
1497     tanLo: function (x) {
1498         return this.prev(Math.tan(x));
1499     },
1500     tanHi: function (x) {
1501         return this.next(Math.tan(x));
1502     },
1503     asinLo: function (x) {
1504         return this.prev(Math.asin(x));
1505     },
1506     asinHi: function (x) {
1507         return this.next(Math.asin(x));
1508     },
1509     acosLo: function (x) {
1510         return this.prev(Math.acos(x));
1511     },
1512     acosHi: function (x) {
1513         return this.next(Math.acos(x));
1514     },
1515     atanLo: function (x) {
1516         return this.prev(Math.atan(x));
1517     },
1518     atanHi: function (x) {
1519         return this.next(Math.atan(x));
1520     },
1521     sinhLo: function (x) {
1522         return this.prev(Mat.sinh(x));
1523     },
1524     sinhHi: function (x) {
1525         return this.next(Mat.sinh(x));
1526     },
1527     coshLo: function (x) {
1528         return this.prev(Mat.cosh(x));
1529     },
1530     coshHi: function (x) {
1531         return this.next(Mat.cosh(x));
1532     },
1533     tanhLo: function (x) {
1534         return this.prev(Mat.tanh(x));
1535     },
1536     tanhHi: function (x) {
1537         return this.next(Mat.tanh(x));
1538     },
1539     sqrtLo: function (x) {
1540         return this.prev(Math.sqrt(x));
1541     },
1542     sqrtHi: function (x) {
1543         return this.next(Math.sqrt(x));
1544     },
1545 
1546     powLo: function (x, power) {
1547         var y;
1548         if (power % 1 !== 0) {
1549             // power has decimals
1550             return this.prev(Math.pow(x, power));
1551         }
1552 
1553         y = (power & 1) === 1 ? x : 1;
1554         power >>= 1;
1555         while (power > 0) {
1556             x = this.mulLo(x, x);
1557             if ((power & 1) === 1) {
1558                 y = this.mulLo(x, y);
1559             }
1560             power >>= 1;
1561         }
1562         return y;
1563     },
1564 
1565     powHi: function (x, power) {
1566         var y;
1567         if (power % 1 !== 0) {
1568             // power has decimals
1569             return this.next(Math.pow(x, power));
1570         }
1571 
1572         y = (power & 1) === 1 ? x : 1;
1573         power >>= 1;
1574         while (power > 0) {
1575             x = this.mulHi(x, x);
1576             if ((power & 1) === 1) {
1577                 y = this.mulHi(x, y);
1578             }
1579             power >>= 1;
1580         }
1581         return y;
1582     },
1583 
1584     /**
1585      * @ignore
1586      * @private
1587      */
1588     disable: function () {
1589         this.next = this.prev = this.identity;
1590     },
1591 
1592     /**
1593      * @ignore
1594      * @private
1595      */
1596     enable: function () {
1597         this.prev = function (v) {
1598             return this._prev(v);
1599         };
1600 
1601         this.next = function (v) {
1602             return this._next(v);
1603         };
1604     },
1605 
1606     /*
1607      * nextafter
1608      */
1609     SMALLEST_DENORM: Math.pow(2, -1074),
1610     UINT_MAX: -1 >>> 0,
1611 
1612     nextafter: function (x, y) {
1613         var lo, hi;
1614 
1615         if (isNaN(x) || isNaN(y)) {
1616             return NaN;
1617         }
1618         if (x === y) {
1619             return x;
1620         }
1621         if (x === 0) {
1622             if (y < 0) {
1623                 return -this.SMALLEST_DENORM;
1624             }
1625             return this.SMALLEST_DENORM;
1626         }
1627         hi = doubleBits.hi(x);
1628         lo = doubleBits.lo(x);
1629         if (y > x === x > 0) {
1630             if (lo === this.UINT_MAX) {
1631                 hi += 1;
1632                 lo = 0;
1633             } else {
1634                 lo += 1;
1635             }
1636         } else {
1637             if (lo === 0) {
1638                 lo = this.UINT_MAX;
1639                 hi -= 1;
1640             } else {
1641                 lo -= 1;
1642             }
1643         }
1644         return doubleBits.pack(lo, hi);
1645     }
1646 };
1647 
1648 JXG.Math.IntervalArithmetic.PI = new MatInterval(
1649     Mat.IntervalArithmetic.piLow,
1650     Mat.IntervalArithmetic.piHigh
1651 );
1652 JXG.Math.IntervalArithmetic.PI_HALF = new MatInterval(
1653     Mat.IntervalArithmetic.piHalfLow,
1654     Mat.IntervalArithmetic.piHalfHigh
1655 );
1656 JXG.Math.IntervalArithmetic.PI_TWICE = new MatInterval(
1657     Mat.IntervalArithmetic.piTwiceLow,
1658     Mat.IntervalArithmetic.piTwiceHigh
1659 );
1660 JXG.Math.IntervalArithmetic.ZERO = new MatInterval(0);
1661 JXG.Math.IntervalArithmetic.ONE = new MatInterval(1);
1662 JXG.Math.IntervalArithmetic.WHOLE = new MatInterval().setWhole();
1663 JXG.Math.IntervalArithmetic.EMPTY = new MatInterval().setEmpty();
1664 
1665 export default JXG.Math.IntervalArithmetic;
1666