class Backports::Random::MT19937

An implementation of Mersenne Twister MT19937 in Ruby

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

Constants

FLOAT_FACTOR
LAST_31_BITS
LAST_STATE
MASK_BY
OFFSET
PAD_32_BITS
STATE_SIZE

Public Class Methods

[](seed) click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 79
def self.[](seed)
  new(convert_seed(seed))
end
convert_seed(seed) click to toggle source

Convert an Integer seed of arbitrary size to either a single 32 bit integer, or an Array of 32 bit integers

# File lib/backports/random/bits_and_bytes.rb, line 66
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
new(seed) click to toggle source

See seed=

# File lib/backports/random/MT19937.rb, line 10
def initialize(seed)
  self.seed = seed
end

Public Instance Methods

left() click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 47
def left # It's actually the number of words left + 1, as per MRI...
  MT19937::STATE_SIZE - @last_read
end
marshal_dump() click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 51
def marshal_dump
  [state_as_bignum, left]
end
marshal_load(ary) click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 55
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
next_state() click to toggle source

Generates a completely new state out of the previous one.

# File lib/backports/random/MT19937.rb, line 18
def next_state
  STATE_SIZE.times do |i|
    mix = @state[i] & 0x80000000 | @state[i+1 - STATE_SIZE] & 0x7fffffff
    @state[i] = @state[i+OFFSET - STATE_SIZE] ^ (mix >> 1)
    @state[i] ^= 0x9908b0df if mix.odd?
  end
  @last_read = -1
end
random_32_bits() click to toggle source

Returns a random Integer from the range 0 … (1 << 32)

# File lib/backports/random/MT19937.rb, line 66
def random_32_bits
  next_state if @last_read >= LAST_STATE
  @last_read += 1
  y = @state[@last_read]
  # Tempering
  y ^= (y >> 11)
  y ^= (y << 7) & 0x9d2c5680
  y ^= (y << 15) & 0xefc60000
  y ^= (y >> 18)
end
random_bytes(nb) click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 33
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
random_float() click to toggle source

generates a random number on [0,1) with 53-bit resolution

# File lib/backports/random/bits_and_bytes.rb, line 10
def random_float
  ((random_32_bits >> 5) * 67108864.0 + (random_32_bits >> 6)) * FLOAT_FACTOR;
end
random_integer(upto) click to toggle source

Returns an integer within 0…upto

# File lib/backports/random/bits_and_bytes.rb, line 15
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
seed=(seed) click to toggle source

Seed must be either an Integer (only the first 32 bits will be used) or an Array of Integers (of which only the first 32 bits will be used)

No conversion or type checking is done at this level

# File lib/backports/random/MT19937.rb, line 31
def seed=(seed)
  case seed
  when Integer
    @state = Array.new(STATE_SIZE)
    @state[0] = seed & PAD_32_BITS
    (1..LAST_STATE).each do |i|
      @state[i] = (1812433253 * (@state[i-1]  ^ @state[i-1]>>30) + i)& PAD_32_BITS
    end
    @last_read = LAST_STATE
  when Array
    self.seed = 19650218
    i=1
    j=0
    [STATE_SIZE, seed.size].max.times do
      @state[i] = (@state[i] ^ (@state[i-1] ^ @state[i-1]>>30) * 1664525) + j + seed[j] & PAD_32_BITS
      if (i+=1) >= STATE_SIZE
        @state[0] = @state[-1]
        i = 1
      end
      j = 0 if (j+=1) >= seed.size
    end
    (STATE_SIZE-1).times do
      @state[i] = (@state[i] ^ (@state[i-1] ^ @state[i-1]>>30) * 1566083941) - i & PAD_32_BITS
      if (i+=1) >= STATE_SIZE
        @state[0] = @state[-1]
        i = 1
      end
    end
    @state[0] = 0x80000000
  else
    raise ArgumentError, "Seed must be an Integer or an Array"
  end
end
state_as_bignum() click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 39
def state_as_bignum
  b = 0
  @state.each_with_index do |val, i|
    b |= val << (32 * i)
  end
  b
end

Private Instance Methods

mask_32_bits(n) click to toggle source
# File lib/backports/random/bits_and_bytes.rb, line 85
def mask_32_bits(n)
  MASK_BY.each do |shift|
    n |= n >> shift
  end
  n
end