tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });