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