| 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. |
| 4 | module mt19937 |
| 5 | |
| 6 | import rand.buffer |
| 7 | import rand.seed |
| 8 | |
| 9 | /* |
| 10 | C++ 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 | */ |
| 50 | pub const seed_len = 2 |
| 51 | |
| 52 | const nn = 312 |
| 53 | const mm = 156 |
| 54 | const matrix_a = 0xB5026F5AA96619E9 |
| 55 | const um = 0xFFFFFFFF80000000 |
| 56 | const lm = 0x7FFFFFFF |
| 57 | const 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. |
| 61 | pub struct MT19937RNG { |
| 62 | buffer.PRNGBuffer |
| 63 | mut: |
| 64 | state []u64 = get_first_state(seed.time_seed_array(2)) |
| 65 | mti int = nn |
| 66 | } |
| 67 | |
| 68 | fn 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] |
| 76 | fn 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]. |
| 87 | pub 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] |
| 100 | pub 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] |
| 116 | pub 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] |
| 131 | pub 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 | |
| 145 | const 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] |
| 149 | pub 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] |
| 177 | pub 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] |
| 183 | pub fn (mut rng MT19937RNG) free() { |
| 184 | unsafe { rng.state.free() } |
| 185 | } |
| 186 | |