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