deMath.js (32248B)
1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES Utilities 3 * ------------------------------------------------ 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21 'use strict'; 22 goog.provide('framework.delibs.debase.deMath'); 23 24 /** @typedef { (Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array) } */ 25 goog.TypedArray; 26 27 /** @typedef { (Array<number>|Array<boolean>|goog.TypedArray) } */ 28 goog.NumberArray; 29 30 goog.scope(function() { 31 32 var deMath = framework.delibs.debase.deMath; 33 34 var DE_ASSERT = function(x) { 35 if (!x) 36 throw new Error('Assert failed'); 37 }; 38 39 /** @const */ deMath.INT32_SIZE = 4; 40 41 deMath.deInRange32 = function(a, mn, mx) { 42 return (a >= mn) && (a <= mx); 43 }; 44 45 deMath.deInBounds32 = function(a, mn, mx) { 46 return (a >= mn) && (a < mx); 47 }; 48 49 /** 50 * @param {number} a 51 * @return {number} 52 */ 53 deMath.deFloatFrac = function(a) { return a - Math.floor(a); }; 54 55 /** 56 * Transform a 64-bit float number into a 32-bit float number. 57 * Native dEQP uses 32-bit numbers, so sometimes 64-bit floating numbers in JS should be transformed into 32-bit ones to ensure the correctness of the result. 58 * @param {number} a 59 * @return {number} 60 */ 61 deMath.toFloat32 = (function() { 62 var FLOAT32ARRAY1 = new Float32Array(1); 63 return function(a) { 64 FLOAT32ARRAY1[0] = a; 65 return FLOAT32ARRAY1[0]; 66 }; 67 })(); 68 69 /** @const */ deMath.INV_LOG_2_FLOAT32 = deMath.toFloat32(1.44269504089); /** 1.0 / log_e(2.0) */ 70 71 /** 72 * Check if a value is a power-of-two. 73 * @param {number} a Input value. 74 * @return {boolean} return True if input is a power-of-two value, false otherwise. 75 * (Also returns true for zero). 76 */ 77 deMath.deIsPowerOfTwo32 = function(a) { 78 return ((a & (a - 1)) == 0); 79 }; 80 81 /** 82 * Align an integer to given power-of-two size. 83 * @param {number} val The number to align. 84 * @param {number} align The size to align to. 85 * @return {number} The aligned value 86 */ 87 deMath.deAlign32 = function(val, align) { 88 if (!deMath.deIsPowerOfTwo32(align)) 89 throw new Error('Not a power of 2: ' + align); 90 return ((val + align - 1) & ~(align - 1)) & 0xFFFFFFFF; //0xFFFFFFFF make sure it returns a 32 bit calculation in 64 bit browsers. 91 }; 92 93 /** 94 * Compute the bit population count of an integer. 95 * @param {number} a 96 * @return {number} The number of one bits in 97 */ 98 deMath.dePop32 = function(a) { 99 /** @type {number} */ var mask0 = 0x55555555; /* 1-bit values. */ 100 /** @type {number} */ var mask1 = 0x33333333; /* 2-bit values. */ 101 /** @type {number} */ var mask2 = 0x0f0f0f0f; /* 4-bit values. */ 102 /** @type {number} */ var mask3 = 0x00ff00ff; /* 8-bit values. */ 103 /** @type {number} */ var mask4 = 0x0000ffff; /* 16-bit values. */ 104 /** @type {number} */ var t = a & 0xFFFFFFFF; /* Crop to 32-bit value */ 105 t = (t & mask0) + ((t >> 1) & mask0); 106 t = (t & mask1) + ((t >> 2) & mask1); 107 t = (t & mask2) + ((t >> 4) & mask2); 108 t = (t & mask3) + ((t >> 8) & mask3); 109 t = (t & mask4) + (t >> 16); 110 return t; 111 }; 112 113 deMath.clamp = function(val, minParm, maxParm) { 114 return Math.min(Math.max(val, minParm), maxParm); 115 }; 116 117 /** 118 * @param {Array<number>} values 119 * @param {number} minParm 120 * @param {number} maxParm 121 * @return {Array<number>} 122 */ 123 deMath.clampVector = function(values, minParm, maxParm) { 124 var result = []; 125 for (var i = 0; i < values.length; i++) 126 result.push(deMath.clamp(values[i], minParm, maxParm)); 127 return result; 128 }; 129 130 deMath.imod = function(a, b) { 131 var m = a % b; 132 return m < 0 ? m + b : m; 133 }; 134 135 deMath.mirror = function(a) { 136 return a >= 0 ? a : -(1 + a); 137 }; 138 139 /** 140 * @param {goog.NumberArray} a Source array 141 * @param {goog.NumberArray} indices 142 * @return {Array<number>} Swizzled array 143 */ 144 deMath.swizzle = function(a, indices) { 145 if (!indices.length) 146 throw new Error('Argument must be an array'); 147 var dst = []; 148 for (var i = 0; i < indices.length; i++) 149 dst.push(a[indices[i]]); 150 return dst; 151 }; 152 153 /** 154 * Shift left elements of array a by elements of array b 155 * dst[n] a[n] << b[n] 156 * @param {goog.NumberArray} a 157 * @param {goog.NumberArray} b 158 * @return {Array<number>} Result array 159 */ 160 deMath.arrayShiftLeft = function(a, b) { 161 if (a.length != b.length) 162 throw new Error('Arrays must have the same size'); 163 var dst = []; 164 for (var i = 0; i < a.length; i++) 165 dst.push(a[i] << b[i]); 166 return dst; 167 }; 168 169 /** 170 * Multiply two vectors, element by element 171 * @param {goog.NumberArray} a 172 * @param {goog.NumberArray} b 173 * @return {Array<number>} Result array 174 */ 175 176 deMath.multiply = function(a, b) { 177 if (a.length != b.length) 178 throw new Error('Arrays must have the same size'); 179 var dst = []; 180 for (var i = 0; i < a.length; i++) 181 dst.push(a[i] * b[i]); 182 return dst; 183 }; 184 185 /** 186 * Divide two vectors, element by element 187 * @param {goog.NumberArray} a 188 * @param {goog.NumberArray} b 189 * @return {Array<number>} Result array 190 * @throws {Error} 191 */ 192 193 deMath.divide = function(a, b) { 194 if (a.length != b.length) 195 throw new Error('Arrays must have the same size'); 196 var dst = []; 197 for (var i = 0; i < a.length; i++) { 198 if (b[i] === 0) 199 throw new Error('Division by 0'); 200 dst.push(a[i] / b[i]); 201 } 202 return dst; 203 }; 204 205 /** 206 * Divide vector by a scalar 207 * @param {goog.NumberArray} a 208 * @param {number} b 209 * @return {Array<number>} Result array 210 */ 211 deMath.divideScale = function(a, b) { 212 var dst = []; 213 for (var i = 0; i < a.length; i++) 214 dst.push(a[i] / b); 215 return dst; 216 }; 217 218 /** 219 * @param {number} a 220 * @param {number} b 221 * @return {number} 222 */ 223 deMath.mod = function(a, b) { 224 return a - b * Math.floor(a / b); 225 }; 226 227 /** 228 * Modulus vector by a scalar 229 * @param {goog.NumberArray} a 230 * @param {number} b 231 * @return {Array<number>} Result array 232 */ 233 deMath.modScale = function(a, b) { 234 var dst = []; 235 for (var i = 0; i < a.length; i++) 236 dst.push(deMath.mod(a[i], b)); 237 return dst; 238 }; 239 240 /** 241 * Multiply vector by a scalar 242 * @param {goog.NumberArray} a 243 * @param {number} b 244 * @return {Array<number>} Result array 245 */ 246 deMath.scale = function(a, b) { 247 var dst = []; 248 for (var i = 0; i < a.length; i++) 249 dst.push(a[i] * b); 250 return dst; 251 }; 252 253 /** 254 * Add vector and scalar, element by element 255 * @param {goog.NumberArray} a 256 * @param {number} b 257 * @return {Array<number>} Result array 258 */ 259 deMath.addScalar = function(a, b) { 260 if (!Array.isArray(a)) 261 throw new Error('First argument must be an array.'); 262 if (typeof b !== 'number') 263 throw new Error('Second argument must be a number.'); 264 var dst = []; 265 for (var i = 0; i < a.length; i++) 266 dst.push(a[i] + b); 267 return dst; 268 }; 269 270 /** 271 * Add two vectors, element by element 272 * @param {goog.NumberArray} a 273 * @param {goog.NumberArray} b 274 * @return {Array<number>} Result array 275 */ 276 deMath.add = function(a, b) { 277 if (a.length != b.length) 278 throw new Error('Arrays must have the same size'); 279 var dst = []; 280 for (var i = 0; i < a.length; i++) 281 dst.push(a[i] + b[i]); 282 return dst; 283 }; 284 285 /** 286 * Subtract two vectors, element by element 287 * @param {goog.NumberArray} a 288 * @param {goog.NumberArray} b 289 * @return {Array<number>} Result array 290 */ 291 292 deMath.subtract = function(a, b) { 293 if (a.length != b.length) 294 throw new Error('Arrays must have the same size'); 295 var dst = []; 296 for (var i = 0; i < a.length; i++) 297 dst.push(a[i] - b[i]); 298 return dst; 299 }; 300 301 /** 302 * Subtract vector and scalar, element by element 303 * @param {goog.NumberArray} a 304 * @param {number} b 305 * @return {Array<number>} Result array 306 */ 307 deMath.subScalar = function(a, b) { 308 if (!Array.isArray(a)) 309 throw new Error('First argument must be an array.'); 310 if (typeof b !== 'number') 311 throw new Error('Second argument must be a number.'); 312 var dst = []; 313 for (var i = 0; i < a.length; i++) 314 dst.push(a[i] - b); 315 return dst; 316 }; 317 318 /** 319 * Calculate absolute difference between two vectors 320 * @param {goog.NumberArray} a 321 * @param {goog.NumberArray} b 322 * @return {Array<number>} abs(diff(a - b)) 323 */ 324 deMath.absDiff = function(a, b) { 325 if (a.length != b.length) 326 throw new Error('Arrays must have the same size'); 327 var dst = []; 328 for (var i = 0; i < a.length; i++) 329 dst.push(Math.abs(a[i] - b[i])); 330 return dst; 331 }; 332 333 /** 334 * Calculate absolute value of a vector 335 * @param {goog.NumberArray} a 336 * @return {Array<number>} abs(a) 337 */ 338 deMath.abs = function(a) { 339 var dst = []; 340 for (var i = 0; i < a.length; i++) 341 dst.push(Math.abs(a[i])); 342 return dst; 343 }; 344 345 /** 346 * Is a <= b (element by element)? 347 * @param {goog.NumberArray} a 348 * @param {goog.NumberArray} b 349 * @return {Array<boolean>} Result array of booleans 350 */ 351 deMath.lessThanEqual = function(a, b) { 352 if (a.length != b.length) 353 throw new Error('Arrays must have the same size'); 354 var dst = []; 355 for (var i = 0; i < a.length; i++) 356 dst.push(a[i] <= b[i]); 357 return dst; 358 }; 359 360 /** 361 * Is a === b (element by element)? 362 * @param {goog.NumberArray} a 363 * @param {goog.NumberArray} b 364 * @return {boolean} Result 365 */ 366 deMath.equal = function(a, b) { 367 if (a.length != b.length) 368 throw new Error('Arrays must have the same size'); 369 for (var i = 0; i < a.length; i++) { 370 if (a[i] !== b[i]) 371 return false; 372 } 373 return true; 374 }; 375 376 /** 377 * Are all values in the array true? 378 * @param {Array<boolean>} a 379 * @return {boolean} 380 */ 381 deMath.boolAll = function(a) { 382 for (var i = 0; i < a.length; i++) 383 if (a[i] == false) 384 return false; 385 return true; 386 }; 387 388 /** 389 * deMath.assign(a, b) element by element 390 * @param {goog.NumberArray} a 391 * @return {Array<number>} 392 */ 393 deMath.assign = function(a) { 394 var dst = []; 395 for (var i = 0; i < a.length; i++) 396 dst.push(a[i]); 397 return dst; 398 }; 399 400 /** 401 * deMath.max(a, b) element by element 402 * @param {goog.NumberArray} a 403 * @param {goog.NumberArray} b 404 * @return {Array<number>} 405 */ 406 deMath.max = function(a, b) { 407 if (a.length != b.length) 408 throw new Error('Arrays must have the same size'); 409 var dst = []; 410 for (var i = 0; i < a.length; i++) 411 dst.push(Math.max(a[i], b[i])); 412 return dst; 413 }; 414 415 /** 416 * deMath.min(a, b) element by element 417 * @param {goog.NumberArray} a 418 * @param {goog.NumberArray} b 419 * @return {Array<number>} 420 */ 421 deMath.min = function(a, b) { 422 if (a.length != b.length) 423 throw new Error('Arrays must have the same size'); 424 var dst = []; 425 for (var i = 0; i < a.length; i++) 426 dst.push(Math.min(a[i], b[i])); 427 return dst; 428 }; 429 430 // Nearest-even rounding in case of tie (fractional part 0.5), otherwise ordinary rounding. 431 deMath.rint = function(a) { 432 var floorVal = Math.floor(a); 433 var fracVal = a - floorVal; 434 435 if (fracVal != 0.5) 436 return Math.round(a); // Ordinary case. 437 438 var roundUp = (floorVal % 2) != 0; 439 440 return floorVal + (roundUp ? 1 : 0); 441 }; 442 443 /** 444 * wrap the number, so that it fits in the range [minValue, maxValue] 445 * @param {number} v 446 * @param {number} minValue 447 * @param {number} maxValue 448 * @return {number} 449 */ 450 deMath.wrap = function(v, minValue, maxValue) { 451 var range = maxValue - minValue + 1; 452 453 if (v < minValue) { 454 v += range * (Math.floor((minValue - v) / range) + 1); 455 } 456 return minValue + Math.floor((v - minValue) % range); 457 }; 458 459 /** 460 * Round number to int by dropping fractional part 461 * it is equivalent of GLSL int() constructor 462 * @param {number} a 463 * @return {number} 464 */ 465 deMath.intCast = function(a) { 466 var v; 467 if (a >= 0) 468 v = Math.floor(a); 469 else 470 v = Math.ceil(a); 471 return deMath.wrap(v, -0x80000000, 0x7FFFFFFF); 472 }; 473 474 /** 475 * Round number to uint by dropping fractional part 476 * it is equivalent of GLSL uint() constructor 477 * @param {number} a 478 * @return {number} 479 */ 480 deMath.uintCast = function(a) { 481 var v; 482 if (a >= 0) 483 v = Math.floor(a); 484 else 485 v = Math.ceil(a); 486 return deMath.wrap(v, 0, 0xFFFFFFFF); 487 }; 488 489 /** 490 * @param {number} a 491 * @return {number} 492 */ 493 deMath.logToFloor = function(a) { 494 assertMsgOptions(a > 0, 'Value is less or equal than zero', false, true); 495 return 31 - deMath.clz32(a); 496 }; 497 498 /** 499 * Find intersection of two rectangles 500 * @param {goog.NumberArray} a Array [x, y, width, height] 501 * @param {goog.NumberArray} b Array [x, y, width, height] 502 * @return {Array<number>} 503 */ 504 deMath.intersect = function(a, b) { 505 if (a.length != 4) 506 throw new Error('Array "a" must have length 4 but has length: ' + a.length); 507 if (b.length != 4) 508 throw new Error('Array "b" must have length 4 but has length: ' + b.length); 509 var x0 = Math.max(a[0], b[0]); 510 var y0 = Math.max(a[1], b[1]); 511 var x1 = Math.min(a[0] + a[2], b[0] + b[2]); 512 var y1 = Math.min(a[1] + a[3], b[1] + b[3]); 513 var w = Math.max(0, x1 - x0); 514 var h = Math.max(0, y1 - y0); 515 516 return [x0, y0, w, h]; 517 }; 518 519 /** deMath.deMathHash 520 * @param {number} a 521 * @return {number} 522 */ 523 deMath.deMathHash = function(a) { 524 var key = a; 525 key = (key ^ 61) ^ (key >> 16); 526 key = key + (key << 3); 527 key = key ^ (key >> 4); 528 key = key * 0x27d4eb2d; /* prime/odd constant */ 529 key = key ^ (key >> 15); 530 return key; 531 }; 532 533 /** 534 * Converts a byte array to a number. Cannot convert numbers larger than 52 bits. 535 * @param {Uint8Array} array 536 * @return {number} 537 */ 538 deMath.arrayToNumber = function(array) { 539 DE_ASSERT(array.length <= 6 || (array.length == 6 && array[5] <= 127)); 540 /** @type {number} */ var result = 0; 541 542 for (var ndx = 0; ndx < array.length; ndx++) { 543 result += array[ndx] * Math.pow(256, ndx); 544 } 545 546 return result; 547 }; 548 549 /** 550 * Fills a byte array with a number 551 * @param {Uint8Array} array Output array (already resized) 552 * @param {number} number 553 */ 554 deMath.numberToArray = function(array, number) { 555 DE_ASSERT(Number.isInteger(number)); 556 for (var byteNdx = 0; byteNdx < array.length; byteNdx++) { 557 /** @type {number} */ var acumzndx = !byteNdx ? number : Math.floor(number / Math.pow(256, byteNdx)); 558 array[byteNdx] = acumzndx & 0xFF; 559 } 560 }; 561 562 /** 563 * Obtains the bit fragment from a number 564 * @param {number} x 565 * @param {number} firstNdx 566 * @param {number} lastNdx 567 * @return {number} 568 */ 569 deMath.getBitRange = function(x, firstNdx, lastNdx) { 570 DE_ASSERT(lastNdx - firstNdx <= 52); 571 var shifted = deMath.shiftRight(x, firstNdx); 572 var bitSize = lastNdx - firstNdx; 573 var mask; 574 if (bitSize < 32) 575 mask = (1 << bitSize) - 1; 576 else 577 mask = Math.pow(2, bitSize) - 1; 578 var masked = deMath.binaryAnd(shifted, mask); 579 return masked; 580 }; 581 582 /** 583 * Obtains the bit fragment from a Uint32Array representing a number. 584 * (ArrayBuffer representations are used in tcuFloat.) 585 * 586 * Cannot return more than 52 bits ((lastNdx - firstNdx) <= 52). 587 * 588 * @param {Uint32Array} array 589 * @param {number} firstNdx 590 * @param {number} lastNdx 591 * @return {number} 592 */ 593 deMath.getArray32BitRange = function(array, firstNdx, lastNdx) { 594 DE_ASSERT(0 <= firstNdx && firstNdx < (array.length * 32)); 595 DE_ASSERT(0 < lastNdx && lastNdx <= (array.length * 32)); 596 DE_ASSERT((lastNdx - firstNdx) <= 52); 597 598 // Example of how this works for a 64-bit number (Uint32Array of length 2). 599 // 600 // * Note that the shift operators in the code << and >>> are pointing in 601 // the opposite direction of this diagram, since LSB is shown on the left. 602 // 603 // [array[0], array[1] ] 604 // [00000011111111111111111111111111, 11111111111100000000000000000000] 605 // ^LSB MSB^ ^LSB MSB^ 606 // 607 // [00000011111111111111111111111111, 11111111111100000000000000000000] 608 // \ \ 609 // firstNdx = 6 (inclusive) lastNdx = 44 (exclusive) 610 // blockIndexA = 0 blockIndexB = 1 611 // 612 // [00000011111111111111111111111111, 11111111111100000000000000000000] 613 // \-----\ \-------------------\ 614 // bitsToBeginningOfBlock = 6 bitsFromEndOfBlock = 20 615 // 616 // -------------blockA------------- -------------blockB------------- 617 // [00000011111111111111111111111111, 11111111111100000000000000000000] 618 // ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ 619 // blockATruncated blockBTruncated 620 // \--blockATruncatedLength--\ 621 // 622 // 11111111111111111111111111 111111111111 623 // ^^^^^^^^^^^^^^^^^^^^^^^^^^--^^^^^^^^^^^^ return value (38 bits) 624 625 /** @type {number} */ var blockIndexA = Math.floor(firstNdx / 32); 626 /** @type {number} */ var bitsToBeginningOfBlock = firstNdx % 32; 627 /** @type {number} */ var blockIndexB = Math.floor((lastNdx - 1) / 32); 628 /** @type {number} */ var bitsFromEndOfBlock = 31 - ((lastNdx - 1) % 32); 629 630 /** @type {number} */ var blockB = array[blockIndexB]; 631 // Chop off the most significant `bitsFromEndOfBlock` bits from blockB. 632 // Note: Initially this logic used a bitmask instead. But there are subtle 633 // corner cases in JS that caused results to sometimes come out negative. 634 // This truncation method is just used to avoid that complexity. 635 /** @type {number} */ var blockBTruncated = (blockB << bitsFromEndOfBlock) >>> bitsFromEndOfBlock; 636 637 if (blockIndexA == blockIndexB) { 638 // firstNdx and lastNdx are in the same block. 639 // Chop off the least significant `bitsToBeginningOfBlock` bits from blockBTruncated. 640 return blockBTruncated >>> bitsToBeginningOfBlock; 641 } else { 642 // firstNdx and lastNdx are in different blocks. 643 /** @type {number} */ var blockA = array[blockIndexA]; 644 // Chop off the least significant `bitsToBeginningOfBlock` bits from blockA. 645 /** @type {number} */ var blockATruncated = blockA >>> bitsToBeginningOfBlock; 646 /** @type {number} */ var blockATruncatedLength = 32 - bitsToBeginningOfBlock; 647 648 // Concatenate blockATruncated and blockBTruncated. 649 // Conceptually equivalent to: 650 // blockATruncated | (blockBTruncated << blockATruncatedLength) 651 // except that wouldn't work for numbers larger than 32 bits. 652 return blockATruncated + (blockBTruncated * Math.pow(2, blockATruncatedLength)); 653 } 654 }; 655 656 /** 657 * Split a large signed number into low and high 32bit dwords. 658 * @param {number} x 659 * @return {Array<number>} 660 */ 661 deMath.split32 = function(x) { 662 var ret = []; 663 ret[1] = Math.floor(x / 0x100000000); 664 ret[0] = x - ret[1] * 0x100000000; 665 return ret; 666 }; 667 668 /** 669 * Split a signed number's low 32bit dwords into low and high 16bit dwords. 670 * @param {number} x 671 * @return {Array<number>} 672 */ 673 deMath.split16 = function(x) { 674 var ret = []; 675 x = x & 0xffffffff; 676 ret[1] = Math.floor(x / 0x10000); 677 ret[0] = x - ret[1] * 0x10000; 678 return ret; 679 }; 680 681 /** 682 * Recontruct a number from high and low 32 bit dwords 683 * @param {Array<number>} x 684 * @return {number} 685 */ 686 deMath.join32 = function(x) { 687 var v0 = x[0] >= 0 ? x[0] : 0x100000000 + x[0]; 688 var v1 = x[1]; 689 var val = v1 * 0x100000000 + v0; 690 return val; 691 }; 692 693 //Bit operations with the help of arrays 694 695 /** 696 * @enum 697 */ 698 deMath.BinaryOp = { 699 AND: 0, 700 OR: 1, 701 XOR: 2 702 }; 703 704 /** 705 * Performs a normal (native) binary operation 706 * @param {number} valueA First operand 707 * @param {number} valueB Second operand 708 * @param {deMath.BinaryOp} operation The desired operation to perform 709 * @return {number} 710 */ 711 deMath.doNativeBinaryOp = function(valueA, valueB, operation) { 712 switch (operation) { 713 case deMath.BinaryOp.AND: 714 return valueA & valueB; 715 case deMath.BinaryOp.OR: 716 return valueA | valueB; 717 case deMath.BinaryOp.XOR: 718 return valueA ^ valueB; 719 default: 720 throw new Error('Unknown operation: ' + operation); 721 } 722 }; 723 724 /** 725 * Performs a binary operation between two operands 726 * with the help of arrays to avoid losing the internal binary representation. 727 * @param {number} valueA First operand 728 * @param {number} valueB Second operand 729 * @param {deMath.BinaryOp} binaryOpParm The desired operation to perform 730 * @return {number} 731 */ 732 deMath.binaryOp = function(valueA, valueB, binaryOpParm) { 733 //quick path if values fit in signed 32 bit range 734 if (deMath.deInRange32(valueA, -0x80000000, 0x7FFFFFFF) && deMath.deInRange32(valueB, -0x80000000, 0x7FFFFFFF)) 735 return deMath.doNativeBinaryOp(valueA, valueB, binaryOpParm); 736 737 var x = deMath.split32(valueA); 738 var y = deMath.split32(valueB); 739 var z = []; 740 for (var i = 0; i < 2; i++) 741 z[i] = deMath.doNativeBinaryOp(x[i], y[i], binaryOpParm); 742 var ret = deMath.join32(z); 743 return ret; 744 }; 745 746 /** 747 * @param {number} a 748 * @param {number} b 749 * @return {number} 750 */ 751 deMath.binaryAnd = function(a, b) { 752 return deMath.binaryOp(a, b, deMath.BinaryOp.AND); 753 }; 754 755 /** 756 * @param {goog.NumberArray} a 757 * @param {number} b 758 * @return {Array<number>} 759 */ 760 deMath.binaryAndVecScalar = function(a, b) { 761 if (!Array.isArray(a)) 762 throw new Error('First argument must be an array.'); 763 if (typeof b !== 'number') 764 throw new Error('Second argument must be a number.'); 765 var dst = []; 766 for (var i = 0; i < a.length; i++) 767 dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.AND)); 768 return dst; 769 }; 770 771 /** 772 * @param {number} a 773 * @param {number} b 774 * @return {number} 775 */ 776 deMath.binaryOr = function(a, b) { 777 return deMath.binaryOp(a, b, deMath.BinaryOp.OR); 778 }; 779 780 /** 781 * @param {goog.NumberArray} a 782 * @param {number} b 783 * @return {Array<number>} 784 */ 785 deMath.binaryOrVecScalar = function(a, b) { 786 if (!Array.isArray(a)) 787 throw new Error('First argument must be an array.'); 788 if (typeof b !== 'number') 789 throw new Error('Second argument must be a number.'); 790 var dst = []; 791 for (var i = 0; i < a.length; i++) 792 dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.OR)); 793 return dst; 794 }; 795 796 /** 797 * @param {number} a 798 * @param {number} b 799 * @return {number} 800 */ 801 deMath.binaryXor = function(a, b) { 802 return deMath.binaryOp(a, b, deMath.BinaryOp.XOR); 803 }; 804 805 /** 806 * @param {goog.NumberArray} a 807 * @param {number} b 808 * @return {Array<number>} 809 */ 810 deMath.binaryXorVecScalar = function(a, b) { 811 if (!Array.isArray(a)) 812 throw new Error('First argument must be an array.'); 813 if (typeof b !== 'number') 814 throw new Error('Second argument must be a number.'); 815 var dst = []; 816 for (var i = 0; i < a.length; i++) 817 dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.XOR)); 818 return dst; 819 }; 820 821 /** 822 * Performs a binary NOT operation on an operand 823 * @param {number} value Operand 824 * @return {number} 825 */ 826 deMath.binaryNot = function(value) { 827 //quick path if value fits in signed 32 bit range 828 if (deMath.deInRange32(value, -0x80000000, 0x7FFFFFFF)) 829 return ~value; 830 831 var x = deMath.split32(value); 832 x[0] = ~x[0]; 833 x[1] = ~x[1]; 834 var ret = deMath.join32(x); 835 return ret; 836 }; 837 838 /** 839 * Shifts the given value 'steps' bits to the left. Replaces << operator 840 * This function should be used if the expected value will be wider than 32-bits. 841 * @param {number} value 842 * @param {number} steps 843 * @return {number} 844 */ 845 deMath.shiftLeft = function(value, steps) { 846 //quick path 847 if (steps < 31) { 848 var v = value * (1 << steps); 849 if (deMath.deInRange32(v, -0x80000000, 0x7FFFFFFF)) 850 return v; 851 } 852 853 if (steps == 0) 854 return value; 855 else if (steps < 32) { 856 var mask = (1 << 32 - steps) - 1; 857 var x = deMath.split32(value); 858 var highBits = x[0] & (~mask); 859 var y = highBits >> (32 - steps); 860 if (highBits < 0) { 861 var m = (1 << steps) - 1; 862 y &= m; 863 } 864 var result = []; 865 result[0] = x[0] << steps; 866 result[1] = x[1] << steps; 867 result[1] |= y; 868 869 return deMath.join32(result); 870 } else { 871 var x = deMath.split32(value); 872 var result = []; 873 result[0] = 0; 874 result[1] = x[0] << steps - 32; 875 return deMath.join32(result); 876 } 877 }; 878 879 /** 880 * @param {Array<number>} a 881 * @param {number} b 882 */ 883 deMath.shiftLeftVecScalar = function(a, b) { 884 var dst = []; 885 for (var i = 0; i < a.length; i++) 886 dst.push(deMath.shiftLeft(a[i], b)); 887 return dst; 888 }; 889 890 /** 891 * Shifts the given value 'steps' bits to the right. Replaces >> operator 892 * This function should be used if the value is wider than 32-bits 893 * @param {number} value 894 * @param {number} steps 895 * @return {number} 896 */ 897 deMath.shiftRight = function(value, steps) { 898 //quick path 899 if (deMath.deInRange32(value, -0x80000000, 0x7FFFFFFF) && steps < 32) 900 return value >> steps; 901 902 if (steps == 0) 903 return value; 904 else if (steps < 32) { 905 if (steps == 0) 906 return value; 907 var mask = (1 << steps) - 1; 908 var x = deMath.split32(value); 909 var lowBits = x[1] & mask; 910 var result = []; 911 var m = (1 << 32 - steps) - 1; 912 result[0] = (x[0] >> steps) & m; 913 result[1] = x[1] >> steps; 914 result[0] |= lowBits << 32 - steps; 915 return deMath.join32(result); 916 } else { 917 var x = deMath.split32(value); 918 var result = []; 919 result[0] = x[1] >> steps - 32; 920 result[1] = value < 0 ? -1 : 0; 921 return deMath.join32(result); 922 } 923 }; 924 925 /** 926 * @param {Array<number>} a 927 * @param {number} b 928 */ 929 deMath.shiftRightVecScalar = function(a, b) { 930 var dst = []; 931 for (var i = 0; i < a.length; i++) 932 dst.push(deMath.shiftRight(a[i], b)); 933 return dst; 934 }; 935 936 /** deMath.logicalAndBool over two arrays of booleans 937 * @param {Array<boolean>} a 938 * @param {Array<boolean>} b 939 * @return {Array<boolean>} 940 */ 941 deMath.logicalAndBool = function(a, b) { 942 if (!Array.isArray(a)) 943 throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); 944 if (!Array.isArray(b)) 945 throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); 946 if (a.length != b.length) 947 throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); 948 949 /** @type {Array<boolean>} */ var result = []; 950 for (var i = 0; i < a.length; i++) { 951 if (a[i] & b[i]) 952 result.push(true); 953 else 954 result.push(false); 955 } 956 return result; 957 }; 958 959 /** deMath.logicalOrBool over two arrays of booleans 960 * @param {Array<boolean>} a 961 * @param {Array<boolean>} b 962 * @return {Array<boolean>} 963 */ 964 deMath.logicalOrBool = function(a, b) { 965 if (!Array.isArray(a)) 966 throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); 967 if (!Array.isArray(b)) 968 throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); 969 if (a.length != b.length) 970 throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); 971 972 /** @type {Array<boolean>} */ var result = []; 973 for (var i = 0; i < a.length; i++) { 974 if (a[i] | b[i]) 975 result.push(true); 976 else 977 result.push(false); 978 } 979 return result; 980 }; 981 982 /** deMath.logicalNotBool over an array of booleans 983 * @param {Array<boolean>} a 984 * @return {Array<boolean>} 985 */ 986 deMath.logicalNotBool = function(a) { 987 if (!Array.isArray(a)) 988 throw new Error('The passed value is not an array: (' + typeof(a) + ')' + a); 989 990 /** @type {Array<boolean>} */ var result = []; 991 for (var i = 0; i < a.length; i++) 992 result.push(!a[i]); 993 return result; 994 }; 995 996 /** deMath.greaterThan over two arrays of booleans 997 * @param {Array<number>} a 998 * @param {Array<number>} b 999 * @return {Array<boolean>} 1000 */ 1001 deMath.greaterThan = function(a, b) { 1002 if (!Array.isArray(a)) 1003 throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); 1004 if (!Array.isArray(b)) 1005 throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); 1006 if (a.length != b.length) 1007 throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); 1008 1009 /** @type {Array<boolean>} */ var result = []; 1010 for (var i = 0; i < a.length; i++) 1011 result.push(a[i] > b[i]); 1012 return result; 1013 }; 1014 1015 /** deMath.greaterThan over two arrays of booleans 1016 * @param {Array<number>} a 1017 * @param {Array<number>} b 1018 * @return {Array<boolean>} 1019 */ 1020 deMath.greaterThanEqual = function(a, b) { 1021 if (!Array.isArray(a)) 1022 throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); 1023 if (!Array.isArray(b)) 1024 throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); 1025 if (a.length != b.length) 1026 throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); 1027 1028 /** @type {Array<boolean>} */ var result = []; 1029 for (var i = 0; i < a.length; i++) 1030 result.push(a[i] >= b[i]); 1031 return result; 1032 }; 1033 1034 /** 1035 * Array of float to array of int (0, 255) 1036 * @param {Array<number>} a 1037 * @return {Array<number>} 1038 */ 1039 1040 deMath.toIVec = function(a) { 1041 /** @type {Array<number>} */ var res = []; 1042 for (var i = 0; i < a.length; i++) 1043 res.push(deMath.clamp(Math.floor(a[i] * 255), 0, 255)); 1044 return res; 1045 }; 1046 1047 /** 1048 * @param {number} a 1049 * @return {number} 1050 */ 1051 deMath.clz32 = function(a) { 1052 /** @type {number} */ var maxValue = 2147483648; // max 32 bit number 1053 /** @type {number} */ var leadingZeros = 0; 1054 while (a < maxValue) { 1055 maxValue = maxValue >>> 1; 1056 leadingZeros++; 1057 } 1058 return leadingZeros; 1059 }; 1060 1061 /** 1062 * @param {number} a 1063 * @param {number} exponent 1064 * @return {number} 1065 */ 1066 deMath.deLdExp = function(a, exponent) { 1067 return deMath.ldexp(a, exponent); 1068 }; 1069 1070 /** 1071 * @param {number} a 1072 * @param {number} exponent 1073 * @return {number} 1074 */ 1075 deMath.deFloatLdExp = function(a, exponent) { 1076 return deMath.ldexp(a, exponent); 1077 }; 1078 1079 /** 1080 * @param {number} value 1081 * @return {Array<number>} 1082 */ 1083 deMath.frexp = (function() { 1084 var data = new DataView(new ArrayBuffer(8)); 1085 1086 return function(value) { 1087 if (value === 0) return [value, 0]; 1088 data.setFloat64(0, value); 1089 var bits = (data.getUint32(0) >>> 20) & 0x7FF; 1090 if (bits === 0) { 1091 data.setFloat64(0, value * Math.pow(2, 64)); 1092 bits = ((data.getUint32(0) >>> 20) & 0x7FF) - 64; 1093 } 1094 var exponent = bits - 1022, 1095 mantissa = deMath.ldexp(value, -exponent); 1096 return [mantissa, exponent]; 1097 } 1098 })(); 1099 1100 /** 1101 * @param {number} mantissa 1102 * @param {number} exponent 1103 * @return {number} 1104 */ 1105 deMath.ldexp = function(mantissa, exponent) { 1106 return exponent > 1023 ? // avoid multiplying by infinity 1107 mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023) : 1108 exponent < -1074 ? // avoid multiplying by zero 1109 mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074) : 1110 mantissa * Math.pow(2, exponent); 1111 }; 1112 1113 /** 1114 * @param {number} a 1115 * @return {number} 1116 */ 1117 deMath.deCbrt = function(a) { 1118 return deMath.deSign(a) * Math.pow(Math.abs(a), 1.0 / 3.0); 1119 }; 1120 1121 /** 1122 * @param {number} x 1123 * @return {number} 1124 */ 1125 deMath.deSign = function(x) { 1126 return isNaN(x) ? x : ((x > 0.0) - (x < 0.0)); 1127 }; 1128 1129 deMath.deFractExp = function(x) { 1130 var result = { 1131 significand: x, 1132 exponent: 0 1133 }; 1134 1135 if (isFinite(x)) { 1136 var r = deMath.frexp(x); 1137 result.exponent = r[1] - 1; 1138 result.significand = r[0] * 2; 1139 } 1140 return result; 1141 }; 1142 1143 });