Class: Backports::Random::MT19937

Inherits:
Object
  • Object
show all
Defined in:
lib/backports/random/MT19937.rb,
lib/backports/random/bits_and_bytes.rb

Overview

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

Constant Summary collapse

STATE_SIZE =
624
LAST_STATE =
STATE_SIZE - 1
PAD_32_BITS =
0xffffffff
LAST_31_BITS =
0x7fffffff
OFFSET =
397
FLOAT_FACTOR =
1.0/9007199254740992.0

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(seed) ⇒ MT19937

See seed=



10
11
12
# File 'lib/backports/random/MT19937.rb', line 10

def initialize(seed)
  self.seed = seed
end

Class Method Details

.[](seed) ⇒ Object



79
80
81
# File 'lib/backports/random/bits_and_bytes.rb', line 79

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

.convert_seed(seed) ⇒ Object

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



66
67
68
69
70
71
72
73
74
75
76
77
# 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

Instance Method Details

#leftObject

It’s actually the number of words left + 1, as per MRI…



47
48
49
# 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_dumpObject



51
52
53
# File 'lib/backports/random/bits_and_bytes.rb', line 51

def marshal_dump
  [state_as_bignum, left]
end

#marshal_load(ary) ⇒ Object



55
56
57
58
59
60
61
62
63
# 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_stateObject

Generates a completely new state out of the previous one.



18
19
20
21
22
23
24
25
# 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_bitsObject

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



66
67
68
69
70
71
72
73
74
75
# 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) ⇒ Object



33
34
35
36
37
# 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_floatObject

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



10
11
12
# 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) ⇒ Object

Returns an integer within 0…upto



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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) ⇒ Object

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



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 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_bignumObject



39
40
41
42
43
44
45
# 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