class Random
  # Supplement the MT19937 class with methods to do
  # conversions the same way as MRI.
  # No argument checking is done here either.

  class MT19937
    FLOAT_FACTOR = 1.0/9007199254740992.0
    # generates a random number on [0,1) with 53-bit resolution
    def random_float
      ((random_32_bits >> 5) * 67108864.0 + (random_32_bits >> 6)) * FLOAT_FACTOR;
    end

    # Returns an integer within 0...upto
    def random_integer(upto)
      n = upto - 1
      nb_full_32 = 0
      while n > PAD_32_BITS
        n >>= 32
        nb_full_32 += 1
      end
      mask = mask_32_bits(n)
      begin
        rand = random_32_bits & mask
        nb_full_32.times do
          rand <<= 32
          rand |= random_32_bits
        end
      end until rand < upto
      rand
    end

    def random_bytes(nb)
      nb_32_bits = (nb + 3) / 4
      random = nb_32_bits.times.map { random_32_bits }
      random.pack("L" * nb_32_bits)[0, nb]
    end

    def state_as_bignum
      b = 0
      @state.each_with_index do |val, i|
        b |= val << (32 * i)
      end
      b
    end

    def left # It's actually the number of words left + 1, as per MRI...
      MT19937::STATE_SIZE - @last_read
    end

    def marshal_dump
      [state_as_bignum, left]
    end

    def marshal_load(ary)
      b, left = ary
      @last_read = MT19937::STATE_SIZE - left
      @state = Array.new(STATE_SIZE)
      STATE_SIZE.times do |i|
        @state[i] = b & PAD_32_BITS
        b >>= 32
      end
    end

    # Convert an Integer seed of arbitrary size to either a single 32 bit integer, or an Array of 32 bit integers
    def self.convert_seed(seed)
      seed = seed.abs
      long_values = []
      begin
        long_values << (seed & PAD_32_BITS)
        seed >>= 32
      end until seed == 0

      long_values.pop if long_values[-1] == 1 && long_values.size > 1 # Done to allow any kind of sequence of integers

      long_values.size > 1 ? long_values : long_values.first
    end

    def self.[](seed)
      new(convert_seed(seed))
    end

  private
    MASK_BY = [1,2,4,8,16]
    def mask_32_bits(n)
      MASK_BY.each do |shift|
        n |= n >> shift
      end
      n
    end
  end
end
