simple-squiggle

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

seedrandom.js (8602B)


      1 /*
      2 Copyright 2019 David Bau.
      3 
      4 Permission is hereby granted, free of charge, to any person obtaining
      5 a copy of this software and associated documentation files (the
      6 "Software"), to deal in the Software without restriction, including
      7 without limitation the rights to use, copy, modify, merge, publish,
      8 distribute, sublicense, and/or sell copies of the Software, and to
      9 permit persons to whom the Software is furnished to do so, subject to
     10 the following conditions:
     11 
     12 The above copyright notice and this permission notice shall be
     13 included in all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     18 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     19 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     20 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     21 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     22 
     23 */
     24 
     25 (function (global, pool, math) {
     26 //
     27 // The following constants are related to IEEE 754 limits.
     28 //
     29 
     30 var width = 256,        // each RC4 output is 0 <= x < 256
     31     chunks = 6,         // at least six RC4 outputs for each double
     32     digits = 52,        // there are 52 significant digits in a double
     33     rngname = 'random', // rngname: name for Math.random and Math.seedrandom
     34     startdenom = math.pow(width, chunks),
     35     significance = math.pow(2, digits),
     36     overflow = significance * 2,
     37     mask = width - 1,
     38     nodecrypto;         // node.js crypto module, initialized at the bottom.
     39 
     40 //
     41 // seedrandom()
     42 // This is the seedrandom function described above.
     43 //
     44 function seedrandom(seed, options, callback) {
     45   var key = [];
     46   options = (options == true) ? { entropy: true } : (options || {});
     47 
     48   // Flatten the seed string or build one from local entropy if needed.
     49   var shortseed = mixkey(flatten(
     50     options.entropy ? [seed, tostring(pool)] :
     51     (seed == null) ? autoseed() : seed, 3), key);
     52 
     53   // Use the seed to initialize an ARC4 generator.
     54   var arc4 = new ARC4(key);
     55 
     56   // This function returns a random double in [0, 1) that contains
     57   // randomness in every bit of the mantissa of the IEEE 754 value.
     58   var prng = function() {
     59     var n = arc4.g(chunks),             // Start with a numerator n < 2 ^ 48
     60         d = startdenom,                 //   and denominator d = 2 ^ 48.
     61         x = 0;                          //   and no 'extra last byte'.
     62     while (n < significance) {          // Fill up all significant digits by
     63       n = (n + x) * width;              //   shifting numerator and
     64       d *= width;                       //   denominator and generating a
     65       x = arc4.g(1);                    //   new least-significant-byte.
     66     }
     67     while (n >= overflow) {             // To avoid rounding up, before adding
     68       n /= 2;                           //   last byte, shift everything
     69       d /= 2;                           //   right using integer math until
     70       x >>>= 1;                         //   we have exactly the desired bits.
     71     }
     72     return (n + x) / d;                 // Form the number within [0, 1).
     73   };
     74 
     75   prng.int32 = function() { return arc4.g(4) | 0; }
     76   prng.quick = function() { return arc4.g(4) / 0x100000000; }
     77   prng.double = prng;
     78 
     79   // Mix the randomness into accumulated entropy.
     80   mixkey(tostring(arc4.S), pool);
     81 
     82   // Calling convention: what to return as a function of prng, seed, is_math.
     83   return (options.pass || callback ||
     84       function(prng, seed, is_math_call, state) {
     85         if (state) {
     86           // Load the arc4 state from the given state if it has an S array.
     87           if (state.S) { copy(state, arc4); }
     88           // Only provide the .state method if requested via options.state.
     89           prng.state = function() { return copy(arc4, {}); }
     90         }
     91 
     92         // If called as a method of Math (Math.seedrandom()), mutate
     93         // Math.random because that is how seedrandom.js has worked since v1.0.
     94         if (is_math_call) { math[rngname] = prng; return seed; }
     95 
     96         // Otherwise, it is a newer calling convention, so return the
     97         // prng directly.
     98         else return prng;
     99       })(
    100   prng,
    101   shortseed,
    102   'global' in options ? options.global : (this == math),
    103   options.state);
    104 }
    105 
    106 //
    107 // ARC4
    108 //
    109 // An ARC4 implementation.  The constructor takes a key in the form of
    110 // an array of at most (width) integers that should be 0 <= x < (width).
    111 //
    112 // The g(count) method returns a pseudorandom integer that concatenates
    113 // the next (count) outputs from ARC4.  Its return value is a number x
    114 // that is in the range 0 <= x < (width ^ count).
    115 //
    116 function ARC4(key) {
    117   var t, keylen = key.length,
    118       me = this, i = 0, j = me.i = me.j = 0, s = me.S = [];
    119 
    120   // The empty key [] is treated as [0].
    121   if (!keylen) { key = [keylen++]; }
    122 
    123   // Set up S using the standard key scheduling algorithm.
    124   while (i < width) {
    125     s[i] = i++;
    126   }
    127   for (i = 0; i < width; i++) {
    128     s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))];
    129     s[j] = t;
    130   }
    131 
    132   // The "g" method returns the next (count) outputs as one number.
    133   (me.g = function(count) {
    134     // Using instance members instead of closure state nearly doubles speed.
    135     var t, r = 0,
    136         i = me.i, j = me.j, s = me.S;
    137     while (count--) {
    138       t = s[i = mask & (i + 1)];
    139       r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))];
    140     }
    141     me.i = i; me.j = j;
    142     return r;
    143     // For robust unpredictability, the function call below automatically
    144     // discards an initial batch of values.  This is called RC4-drop[256].
    145     // See http://google.com/search?q=rsa+fluhrer+response&btnI
    146   })(width);
    147 }
    148 
    149 //
    150 // copy()
    151 // Copies internal state of ARC4 to or from a plain object.
    152 //
    153 function copy(f, t) {
    154   t.i = f.i;
    155   t.j = f.j;
    156   t.S = f.S.slice();
    157   return t;
    158 };
    159 
    160 //
    161 // flatten()
    162 // Converts an object tree to nested arrays of strings.
    163 //
    164 function flatten(obj, depth) {
    165   var result = [], typ = (typeof obj), prop;
    166   if (depth && typ == 'object') {
    167     for (prop in obj) {
    168       try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {}
    169     }
    170   }
    171   return (result.length ? result : typ == 'string' ? obj : obj + '\0');
    172 }
    173 
    174 //
    175 // mixkey()
    176 // Mixes a string seed into a key that is an array of integers, and
    177 // returns a shortened string seed that is equivalent to the result key.
    178 //
    179 function mixkey(seed, key) {
    180   var stringseed = seed + '', smear, j = 0;
    181   while (j < stringseed.length) {
    182     key[mask & j] =
    183       mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++));
    184   }
    185   return tostring(key);
    186 }
    187 
    188 //
    189 // autoseed()
    190 // Returns an object for autoseeding, using window.crypto and Node crypto
    191 // module if available.
    192 //
    193 function autoseed() {
    194   try {
    195     var out;
    196     if (nodecrypto && (out = nodecrypto.randomBytes)) {
    197       // The use of 'out' to remember randomBytes makes tight minified code.
    198       out = out(width);
    199     } else {
    200       out = new Uint8Array(width);
    201       (global.crypto || global.msCrypto).getRandomValues(out);
    202     }
    203     return tostring(out);
    204   } catch (e) {
    205     var browser = global.navigator,
    206         plugins = browser && browser.plugins;
    207     return [+new Date, global, plugins, global.screen, tostring(pool)];
    208   }
    209 }
    210 
    211 //
    212 // tostring()
    213 // Converts an array of charcodes to a string
    214 //
    215 function tostring(a) {
    216   return String.fromCharCode.apply(0, a);
    217 }
    218 
    219 //
    220 // When seedrandom.js is loaded, we immediately mix a few bits
    221 // from the built-in RNG into the entropy pool.  Because we do
    222 // not want to interfere with deterministic PRNG state later,
    223 // seedrandom will not call math.random on its own again after
    224 // initialization.
    225 //
    226 mixkey(math.random(), pool);
    227 
    228 //
    229 // Nodejs and AMD support: export the implementation as a module using
    230 // either convention.
    231 //
    232 if ((typeof module) == 'object' && module.exports) {
    233   module.exports = seedrandom;
    234   // When in node.js, try using crypto package for autoseeding.
    235   try {
    236     nodecrypto = require('crypto');
    237   } catch (ex) {}
    238 } else if ((typeof define) == 'function' && define.amd) {
    239   define(function() { return seedrandom; });
    240 } else {
    241   // When included as a plain script, set up Math.seedrandom global.
    242   math['seed' + rngname] = seedrandom;
    243 }
    244 
    245 
    246 // End anonymous scope, and pass initial values.
    247 })(
    248   // global: `self` in browsers (including strict mode and web workers),
    249   // otherwise `this` in Node and other environments
    250   (typeof self !== 'undefined') ? self : this,
    251   [],     // pool: entropy pool starts empty
    252   Math    // math: package containing random, pow, and seedrandom
    253 );