simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

xor4096.js (4559B)


      1 // A Javascript implementaion of Richard Brent's Xorgens xor4096 algorithm.
      2 //
      3 // This fast non-cryptographic random number generator is designed for
      4 // use in Monte-Carlo algorithms. It combines a long-period xorshift
      5 // generator with a Weyl generator, and it passes all common batteries
      6 // of stasticial tests for randomness while consuming only a few nanoseconds
      7 // for each prng generated.  For background on the generator, see Brent's
      8 // paper: "Some long-period random number generators using shifts and xors."
      9 // http://arxiv.org/pdf/1004.3115v1.pdf
     10 //
     11 // Usage:
     12 //
     13 // var xor4096 = require('xor4096');
     14 // random = xor4096(1);                        // Seed with int32 or string.
     15 // assert.equal(random(), 0.1520436450538547); // (0, 1) range, 53 bits.
     16 // assert.equal(random.int32(), 1806534897);   // signed int32, 32 bits.
     17 //
     18 // For nonzero numeric keys, this impelementation provides a sequence
     19 // identical to that by Brent's xorgens 3 implementaion in C.  This
     20 // implementation also provides for initalizing the generator with
     21 // string seeds, or for saving and restoring the state of the generator.
     22 //
     23 // On Chrome, this prng benchmarks about 2.1 times slower than
     24 // Javascript's built-in Math.random().
     25 
     26 (function(global, module, define) {
     27 
     28 function XorGen(seed) {
     29   var me = this;
     30 
     31   // Set up generator function.
     32   me.next = function() {
     33     var w = me.w,
     34         X = me.X, i = me.i, t, v;
     35     // Update Weyl generator.
     36     me.w = w = (w + 0x61c88647) | 0;
     37     // Update xor generator.
     38     v = X[(i + 34) & 127];
     39     t = X[i = ((i + 1) & 127)];
     40     v ^= v << 13;
     41     t ^= t << 17;
     42     v ^= v >>> 15;
     43     t ^= t >>> 12;
     44     // Update Xor generator array state.
     45     v = X[i] = v ^ t;
     46     me.i = i;
     47     // Result is the combination.
     48     return (v + (w ^ (w >>> 16))) | 0;
     49   };
     50 
     51   function init(me, seed) {
     52     var t, v, i, j, w, X = [], limit = 128;
     53     if (seed === (seed | 0)) {
     54       // Numeric seeds initialize v, which is used to generates X.
     55       v = seed;
     56       seed = null;
     57     } else {
     58       // String seeds are mixed into v and X one character at a time.
     59       seed = seed + '\0';
     60       v = 0;
     61       limit = Math.max(limit, seed.length);
     62     }
     63     // Initialize circular array and weyl value.
     64     for (i = 0, j = -32; j < limit; ++j) {
     65       // Put the unicode characters into the array, and shuffle them.
     66       if (seed) v ^= seed.charCodeAt((j + 32) % seed.length);
     67       // After 32 shuffles, take v as the starting w value.
     68       if (j === 0) w = v;
     69       v ^= v << 10;
     70       v ^= v >>> 15;
     71       v ^= v << 4;
     72       v ^= v >>> 13;
     73       if (j >= 0) {
     74         w = (w + 0x61c88647) | 0;     // Weyl.
     75         t = (X[j & 127] ^= (v + w));  // Combine xor and weyl to init array.
     76         i = (0 == t) ? i + 1 : 0;     // Count zeroes.
     77       }
     78     }
     79     // We have detected all zeroes; make the key nonzero.
     80     if (i >= 128) {
     81       X[(seed && seed.length || 0) & 127] = -1;
     82     }
     83     // Run the generator 512 times to further mix the state before using it.
     84     // Factoring this as a function slows the main generator, so it is just
     85     // unrolled here.  The weyl generator is not advanced while warming up.
     86     i = 127;
     87     for (j = 4 * 128; j > 0; --j) {
     88       v = X[(i + 34) & 127];
     89       t = X[i = ((i + 1) & 127)];
     90       v ^= v << 13;
     91       t ^= t << 17;
     92       v ^= v >>> 15;
     93       t ^= t >>> 12;
     94       X[i] = v ^ t;
     95     }
     96     // Storing state as object members is faster than using closure variables.
     97     me.w = w;
     98     me.X = X;
     99     me.i = i;
    100   }
    101 
    102   init(me, seed);
    103 }
    104 
    105 function copy(f, t) {
    106   t.i = f.i;
    107   t.w = f.w;
    108   t.X = f.X.slice();
    109   return t;
    110 };
    111 
    112 function impl(seed, opts) {
    113   if (seed == null) seed = +(new Date);
    114   var xg = new XorGen(seed),
    115       state = opts && opts.state,
    116       prng = function() { return (xg.next() >>> 0) / 0x100000000; };
    117   prng.double = function() {
    118     do {
    119       var top = xg.next() >>> 11,
    120           bot = (xg.next() >>> 0) / 0x100000000,
    121           result = (top + bot) / (1 << 21);
    122     } while (result === 0);
    123     return result;
    124   };
    125   prng.int32 = xg.next;
    126   prng.quick = prng;
    127   if (state) {
    128     if (state.X) copy(state, xg);
    129     prng.state = function() { return copy(xg, {}); }
    130   }
    131   return prng;
    132 }
    133 
    134 if (module && module.exports) {
    135   module.exports = impl;
    136 } else if (define && define.amd) {
    137   define(function() { return impl; });
    138 } else {
    139   this.xor4096 = impl;
    140 }
    141 
    142 })(
    143   this,                                     // window object or global
    144   (typeof module) == 'object' && module,    // present in node.js
    145   (typeof define) == 'function' && define   // present with an AMD loader
    146 );