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