k98kurz icon

XORShiftPlus.js

k98kurz | PRO | 07/14/21 11:01:54 PM UTC (Edited) | 0 ⭐ | 2383 👁️ | Never ⏰ | []
JavaScript |

2.85 KB

|

None

|

0 👍

/

0 👎

/**
 *  XORShiftPlus "non-linear" PRNG
 *  Author: k98kurz (@gmail)
 *  License: MIT
 *  Date: 2020-09-07
 *  Description: Based upon the xorshift128+ generator, one of the fastest generators passing BigCrush.
 *  Methods:
 *      next(n)         returns Uint32Array of n random numbers
 *      nextHex(n)      returns Array of n hexidecimal numbers
 *      bytes(n)        returns Uint8Array of n random bytes
 *      bytesHex(n)     returns Array of n random bytes in hexidecimal
 *      hex(n)          returns hexidecimal string of n bytes
 *  Note: This generates with higher periodicity and better random distribution than the linear XORShift.
 */
class XORShiftPlus {
  #booted = false;
  #x;
  #y;
  #z;
  #w;
 
  constructor (seed) {
    this.#x = new Uint32Array(1);
    this.#y = new Uint32Array(1);
    this.#z = new Uint32Array(1);
    this.#w = new Uint32Array(1);
    this.#x[0] = seed ? seed|0 : 317973455;
    this.#y[0] = this.#x[0]<<362436069;
    this.#z[0] = this.#y[0]+this.#x[0];
    this.#w[0] = this.#z[0]^this.#x[0]+this.#y[0];
  }
 
  #next () {
    let t = new Uint32Array(1);
    t[0] = this.#x[0]^(this.#x[0]<<11);
    this.#x[0] = this.#y[0];
    this.#y[0] = this.#z[0];
    this.#z[0] = this.#w[0];
    this.#w[0] = this.#w[0]^(this.#w[0]>>19)^(t[0]^(t[0]>>8));
    return this.#w[0]+this.#y[0];
  }
 
  #boot () {
    // if not booted, discard the first numbers as they are somewhat predictable
    if (this.#booted)
      return;
    for (let i=0, n=this.#next(), j=this.#next()%256; i<=j; this.#next(), ++i);
    this.#booted = true;
  }
 
  next (n, option) {
    this.#boot();
    n = (n === undefined || typeof n !== 'number') ? 1 : n;
    let t; n |= 0; n = n>0 ? n : 1;
    option = (option === undefined || typeof n !== 'number') ? 0 : option;
 
    // redundant code, but improved performance
    switch (option) {
      // nextHex
      case 1:
        t = [];
        for (let i=0, il=n; i<il; t[i++] = this.#next().toString(16));
        break;
      // bytes
      case 2:
        t = new Uint8Array(n);
        for (let i=0, il=n; i<il; t[i++] = this.#next());
        break;
      // bytesHex
      case 3:
        t = [];
        for (let i=0, il=n; i<il; t[i] = (this.#next()%256).toString(16), t[i] = t[i].length%2 ? '0'+t[i] : t[i], ++i);
        break;
      // hex
      case 4:
        t = [];
        for (let i=0, il=n; i<il; t[i] = (this.#next()%256).toString(16), t[i] = t[i].length%2 ? '0'+t[i] : t[i], ++i);
        t = t.join('');
        break;
      // next
      default:
        t = new Uint32Array(n);
        for (let i=0, il=n; i<il; t[i++] = this.#next());
    }
    return t;
  };
 
  nextHex (n) { return this.next(n, 1); };
 
  bytes (n) { return this.next(n, 2); };
 
  bytesHex (n) { return this.next(n, 3); };
 
  hex (n) { return this.next(n, 4); };
}

Comments