v / vlib / rand / mt19937 / mt19937.v
185 lines · 163 sloc · 5.66 KB · 55e8faaedf64ec9e77581af873479891baf249d1
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module mt19937
5
6import rand.buffer
7import rand.seed
8
9/*
10C++ functions for MT19937, with initialization improved 2002/2/10.
11 Coded by Takuji Nishimura and Makoto Matsumoto.
12 This is a faster version by taking Shawn Cokus's optimization,
13 Matthe Bellew's simplification, Isaku Wada's real version.
14
15 Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
16 All rights reserved.
17
18 Redistribution and use in source and binary forms, with or without
19 modification, are permitted provided that the following conditions
20 are met:
21
22 1. Redistributions of source code must retain the above copyright
23 notice, this list of conditions and the following disclaimer.
24
25 2. Redistributions in binary form must reproduce the above copyright
26 notice, this list of conditions and the following disclaimer in the
27 documentation and/or other materials provided with the distribution.
28
29 3. The names of its contributors may not be used to endorse or promote
30 products derived from this software without specific prior written
31 permission.
32
33 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
34 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
35 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
36 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
45
46 Any feedback is very welcome.
47 http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
48 email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
49*/
50pub const seed_len = 2
51
52const nn = 312
53const mm = 156
54const matrix_a = 0xB5026F5AA96619E9
55const um = 0xFFFFFFFF80000000
56const lm = 0x7FFFFFFF
57const inv_f64_limit = 1.0 / 9007199254740992.0
58
59// MT19937RNG is generator that uses the Mersenne Twister algorithm with period 2^19937.
60// **NOTE**: The RNG is not seeded when instantiated so remember to seed it before use.
61pub struct MT19937RNG {
62 buffer.PRNGBuffer
63mut:
64 state []u64 = get_first_state(seed.time_seed_array(2))
65 mti int = nn
66}
67
68fn get_first_state(seed_data []u32) []u64 {
69 mut state := []u64{len: nn}
70 calculate_state(seed_data, mut state)
71 return state
72}
73
74// calculate_state returns a random state array calculated from the `seed_data`.
75@[ignore_overflow]
76fn calculate_state(seed_data []u32, mut state []u64) {
77 lo := u64(seed_data[0])
78 hi := u64(seed_data[1])
79 state[0] = u64((hi << 32) | lo)
80 for j := 1; j < nn; j++ {
81 state[j] = u64(6364136223846793005) * (state[j - 1] ^ (state[j - 1] >> 62)) + u64(j)
82 }
83}
84
85// seed sets the current random state based on `seed_data`.
86// seed expects `seed_data` to be only two `u32`s in little-endian format as [lower, higher].
87pub fn (mut rng MT19937RNG) seed(seed_data []u32) {
88 if seed_data.len != 2 {
89 eprintln('mt19937 needs only two 32bit integers as seed: [lower, higher]')
90 exit(1)
91 }
92 calculate_state(seed_data, mut rng.state)
93 rng.mti = nn
94 rng.bytes_left = 0
95 rng.buffer = 0
96}
97
98// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
99@[inline]
100pub fn (mut rng MT19937RNG) u8() u8 {
101 if rng.bytes_left >= 1 {
102 rng.bytes_left -= 1
103 value := u8(rng.buffer)
104 rng.buffer >>= 8
105 return value
106 }
107 rng.buffer = rng.u64()
108 rng.bytes_left = 7
109 value := u8(rng.buffer)
110 rng.buffer >>= 8
111 return value
112}
113
114// u16 returns a pseudorandom 16bit int in range `[0, 2¹⁶)`.
115@[inline]
116pub fn (mut rng MT19937RNG) u16() u16 {
117 if rng.bytes_left >= 2 {
118 rng.bytes_left -= 2
119 value := u16(rng.buffer)
120 rng.buffer >>= 16
121 return value
122 }
123 ans := rng.u64()
124 rng.buffer = ans >> 16
125 rng.bytes_left = 6
126 return u16(ans)
127}
128
129// u32 returns a pseudorandom 32bit int in range `[0, 2³²)`.
130@[inline]
131pub fn (mut rng MT19937RNG) u32() u32 {
132 // Can we take a whole u32 out of the buffer?
133 if rng.bytes_left >= 4 {
134 rng.bytes_left -= 4
135 value := u32(rng.buffer)
136 rng.buffer >>= 32
137 return value
138 }
139 ans := rng.u64()
140 rng.buffer = ans >> 32
141 rng.bytes_left = 4
142 return u32(ans)
143}
144
145const mag01 = [u64(0), u64(matrix_a)]
146
147// u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`.
148@[direct_array_access; ignore_overflow; inline]
149pub fn (mut rng MT19937RNG) u64() u64 {
150 mut x := u64(0)
151 mut i := int(0)
152 if rng.mti >= nn {
153 for i = 0; i < nn - mm; i++ {
154 x = (rng.state[i] & um) | (rng.state[i + 1] & lm)
155 rng.state[i] = rng.state[i + mm] ^ (x >> 1) ^ mag01[int(x & 1)]
156 }
157 for i < nn - 1 {
158 x = (rng.state[i] & um) | (rng.state[i + 1] & lm)
159 rng.state[i] = rng.state[i + (mm - nn)] ^ (x >> 1) ^ mag01[int(x & 1)]
160 i++
161 }
162 x = (rng.state[nn - 1] & um) | (rng.state[0] & lm)
163 rng.state[nn - 1] = rng.state[mm - 1] ^ (x >> 1) ^ mag01[int(x & 1)]
164 rng.mti = 0
165 }
166 x = rng.state[rng.mti]
167 rng.mti++
168 x ^= (x >> 29) & 0x5555555555555555
169 x ^= (x << 17) & 0x71D67FFFEDA60000
170 x ^= (x << 37) & 0xFFF7EEE000000000
171 x ^= (x >> 43)
172 return x
173}
174
175// block_size returns the number of bits that the RNG can produce in a single iteration.
176@[inline]
177pub fn (mut rng MT19937RNG) block_size() int {
178 return 64
179}
180
181// free should be called when the generator is no longer needed.
182@[unsafe]
183pub fn (mut rng MT19937RNG) free() {
184 unsafe { rng.state.free() }
185}
186