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