| 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 splitmix64 |
| 5 | |
| 6 | import rand.seed |
| 7 | import rand.buffer |
| 8 | |
| 9 | pub const seed_len = 2 |
| 10 | |
| 11 | // SplitMix64RNG is ported from http://xoshiro.di.unimi.it/splitmix64.c . |
| 12 | pub struct SplitMix64RNG { |
| 13 | buffer.PRNGBuffer |
| 14 | mut: |
| 15 | state u64 = seed.time_seed_64() |
| 16 | bytes_left int |
| 17 | buffer u64 |
| 18 | } |
| 19 | |
| 20 | // seed sets the seed of the accepting SplitMix64RNG to the given data in little-endian format (i.e. lower 32 bits are in [0] and higher 32 bits in [1]). |
| 21 | pub fn (mut rng SplitMix64RNG) seed(seed_data []u32) { |
| 22 | if seed_data.len != 2 { |
| 23 | eprintln('SplitMix64RNG needs 2 32-bit unsigned integers as the seed.') |
| 24 | exit(1) |
| 25 | } |
| 26 | rng.state = seed_data[0] | (u64(seed_data[1]) << 32) |
| 27 | rng.bytes_left = 0 |
| 28 | rng.buffer = 0 |
| 29 | } |
| 30 | |
| 31 | // byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`. |
| 32 | @[inline] |
| 33 | pub fn (mut rng SplitMix64RNG) u8() u8 { |
| 34 | if rng.bytes_left >= 1 { |
| 35 | rng.bytes_left -= 1 |
| 36 | value := u8(rng.buffer) |
| 37 | rng.buffer >>= 8 |
| 38 | return value |
| 39 | } |
| 40 | rng.buffer = rng.u64() |
| 41 | rng.bytes_left = 7 |
| 42 | value := u8(rng.buffer) |
| 43 | rng.buffer >>= 8 |
| 44 | return value |
| 45 | } |
| 46 | |
| 47 | // u16 returns a pseudorandom 16bit int in range `[0, 2¹⁶)`. |
| 48 | @[inline] |
| 49 | pub fn (mut rng SplitMix64RNG) u16() u16 { |
| 50 | if rng.bytes_left >= 2 { |
| 51 | rng.bytes_left -= 2 |
| 52 | value := u16(rng.buffer) |
| 53 | rng.buffer >>= 16 |
| 54 | return value |
| 55 | } |
| 56 | ans := rng.u64() |
| 57 | rng.buffer = ans >> 16 |
| 58 | rng.bytes_left = 6 |
| 59 | return u16(ans) |
| 60 | } |
| 61 | |
| 62 | // u32 returns a pseudorandom 32bit int in range `[0, 2³²)`. |
| 63 | @[inline] |
| 64 | pub fn (mut rng SplitMix64RNG) u32() u32 { |
| 65 | if rng.bytes_left >= 4 { |
| 66 | rng.bytes_left -= 4 |
| 67 | value := u32(rng.buffer) |
| 68 | rng.buffer >>= 32 |
| 69 | return value |
| 70 | } |
| 71 | ans := rng.u64() |
| 72 | rng.buffer = ans >> 32 |
| 73 | rng.bytes_left = 4 |
| 74 | return u32(ans) |
| 75 | } |
| 76 | |
| 77 | // u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`. |
| 78 | @[ignore_overflow; inline] |
| 79 | pub fn (mut rng SplitMix64RNG) u64() u64 { |
| 80 | rng.state += (0x9e3779b97f4a7c15) |
| 81 | mut z := rng.state |
| 82 | z = (z ^ (z >> u64(30))) * 0xbf58476d1ce4e5b9 |
| 83 | z = (z ^ (z >> u64(27))) * 0x94d049bb133111eb |
| 84 | return z ^ (z >> (31)) |
| 85 | } |
| 86 | |
| 87 | // block_size returns the number of bits that the RNG can produce in a single iteration. |
| 88 | @[inline] |
| 89 | pub fn (mut rng SplitMix64RNG) block_size() int { |
| 90 | return 64 |
| 91 | } |
| 92 | |
| 93 | // free should be called when the generator is no longer needed. |
| 94 | @[unsafe] |
| 95 | pub fn (mut rng SplitMix64RNG) free() { |
| 96 | } |
| 97 | |