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