(function() {
  
  var Random
  
  if( typeof module !== 'undefined' ) {
    module.exports = MersenneTwister
    if( typeof require === 'function' ) {
      var Random = require( '../' )
    }
  } else {
    Random = this.Random
    Random.MT = MersenneTwister
  }
  
  /**
   * Mersenne Twister PRNG constructor
   * @param {Number} seed
   */
  function MersenneTwister( seed ) {
    
    Random.call( this )
    
    this._index = 0
    this._state = new Array( 624 )
    this._state[0] = seed != null ?
      seed : ( Math.random() * 0xFFFFFFFF ) | 0
    this._seed = this._state[0]
    
    var i, MT = this._state
    for( i = 1; i < 624; i++ ) {
      MT[i] = MT[i-1] ^ ( MT[i-1] >>> 30 )
      MT[i] = 0x6C078965 * MT[i] + i // 1812433253
      MT[i] = MT[i] & ( ( MT[i] << 32 ) - 1 )
    }
    
  }
  
  // Inherit from Random
  var $ = MersenneTwister.prototype =
    Object.create( Random.prototype )
  
  /**
   * Prototype's constructor
   * @type {Function}
   */
  $.constructor = MersenneTwister
  
  /**
   * Generate an array of 624 untempered numbers
   * @return {Undefined}
   */
  $._generateNumbers = function() {
    var i, y, MT = this._state
    for( i = 0; i < 624; i++ ) {
      // Bit 31 (32nd bit) of MT[i]
      y = ( MT[i] & 0x80000000 )
      // Bits 0-30 (first 31 bits) of MT[...]
      y = y + ( MT[(i+1) % 624] & 0x7FFFFFFF )
      // The new randomness
      MT[i] = MT[(i+397) % 624] ^ ( y >>> 1 )
      // In case y is odd
      if( ( y % 2 ) !== 0 ) {
        MT[i] = MT[i] ^ 0x9908B0DF // 2567483615
      }
    }
  }
  
  /**
   * Extract a tempered pseudorandom number [0,1]
   * based on the index-th value, calling
   * #_generateNumbers() every 624 numbers
   * @return {Number}
   */
  $.uniform = function() {
    
    if( this._index === 0 )
      this._generateNumbers()
    
    var y = this._state[ this._index ]
    
    y = y ^ ( y >>> 11 )
    y = y ^ (( y << 7 ) & 0x9D2C5680 ) // 2636928640
    y = y ^ (( y << 15 ) & 0xEFC60000 ) // 4022730752
    y = y ^ ( y >>> 18 )

    this._index = ( this._index + 1 ) % 624
    
    return ( y >>> 0 ) * ( 1.0 / 4294967296.0 )
    
  }
  
})()