// seedrandom.js
//
// Defines a new method Math.seedrandom() that substitutes an RC4-based
// algorithm for Math.random() in a way that allows explicit seeding.
// RC4 is a high-quality random number generator, but the javascript
// implementation is about 3-10x slower than the built-in Math.random().
//
// Usage:
//
//   Math.seedrandom()         sets Math.random to an ARC4-based PRNG seeded
//                             using the current time and other entropy.
//   Math.seedrandom(string)   sets Math.random using the given seed.
//   Math.seedrandom(function) sets Math.random to the given function.
//
// Examples:
//
//   var rng1 = Math.seedrandom("hello"); // Use "hello" as the seed.
//   document.write(Math.random());       // Always 0.5463663768140734
//   document.write(Math.random());       // Always 0.43973793770592234
//
//   Math.seedrandom();                   // Seed based on time and DOM state.
//   document.write(Math.random());       // Unpredictable.
//
//   Math.seedrandom(rng1);               // Continue "hello" sequence.
//   document.write(Math.random());       // Always 0.554769432473455

Math.seedrandom = function seedrandom(arg) {
  if (typeof(arg) == 'undefined') {       // Generate a seed
    arg = '' + new Date().getTime() + Math.random() + window.history.length;
    for (var a in window) try { arg += '|' + window[a]; } catch (e) {}
    for (var a in document) try { arg += '|' + document[a]; } catch (e) {}
  }
  var arc4 = function ARC4(key) {                    // An ARC4 implementation
    if (key.length == 0) key = [0];
    this.i = 0;
    this.j = 0;
    this.S = [];
    this.c = [];
    this.m = 0;
    var i, j, t, u;
    for (i = 0; i < 256; ++i) {
      this.S[i] = i;
    }
    j = 0;
    for (i = 0; i < 256; ++i) {
      t = this.S[i];
      j = (j + t + key[i % key.length]) & 255;
      u = this.S[j];
      this.S[i] = u;
      this.S[j] = t;
    }
    this.i = 0;
    this.j = 0;
    this.next = function next(bytes) {    // Makes an n-byte-wide pseudorandom
      var s = this.S;
      var i = (this.i + 1) & 255; var t = s[i];
      var j = (this.j + t) & 255; var u = s[j];
      s[i] = u;
      s[j] = t;
      var r = s[(t + u) & 255];
      while (--bytes) {
        i = (i + 1) & 255; t = s[i];
        j = (j + t) & 255; u = s[j];
        s[i] = u;
        s[j] = t;
        r = r * 256 + s[(t + u) & 255];
      }
      this.i = i;
      this.j = j;
      return r;
    }
    for (i = 0; i < 256; ++i) {           // discard the initial 256 values
      this.next();
    }
  }
  var randclosure = arg;
  if (typeof(randclosure) != 'function') {
    arg = '' + arg;
    var key = [];
    for (var j = 0; j < arg.length; ++j) {
      if (key.length < 256) key.push(arg.charCodeAt(j));
      else key[j & 255] = ((key[j & 255] * 167) + arg.charCodeAt(j)) & 255;
    }
    var random_state = new arc4(key);
    randclosure = function random() {
      var n = random_state.next(7);
      var d = 72057594037927936;     // (2 ^ (7 * 8))
      while (n < 9007199254740992) { // (2 ^ 53) - fill a 52 bit mantissa
        d *= 256;
        n = n * 256 + random_state.next(1);
      }
      return n / d;
    }
  }
  Math.random = randclosure;
  return randclosure;
}
