518 lines
15 KiB
Rust
518 lines
15 KiB
Rust
use crate::population_seed_fn;
|
||
|
||
use super::{
|
||
RandomDeriver, RandomDeriverImpl, RandomGenerator, RandomImpl, gaussian::GaussianGenerator,
|
||
hash_block_pos,
|
||
};
|
||
|
||
/// Xoroshiro128+ random number generator implementation.
|
||
///
|
||
/// This is a modern, fast random number generator used in newer Minecraft versions.
|
||
/// It implements the Xoroshiro128+ algorithm, which has better statistical properties
|
||
/// and performance than the legacy LCG implementation.
|
||
///
|
||
/// The generator maintains 128 bits of state split into two 64-bit words (`lo` and `hi`)
|
||
/// and uses XOR, rotation, and shift operations to produce high-quality random numbers.
|
||
pub struct Xoroshiro {
|
||
/// Lower 64 bits of the generator state.
|
||
lo: u64,
|
||
/// Higher 64 bits of the generator state.
|
||
hi: u64,
|
||
/// Stored Gaussian value for the next Gaussian generation.
|
||
internal_next_gaussian: Option<f64>,
|
||
}
|
||
|
||
impl Xoroshiro {
|
||
population_seed_fn!();
|
||
|
||
/// Creates a new Xoroshiro generator from the given seed.
|
||
///
|
||
/// The seed is mixed using the Stafford 13 mixing function to ensure
|
||
/// good distribution of the initial state.
|
||
///
|
||
/// # Arguments
|
||
/// - `seed` – The initial seed value.
|
||
///
|
||
/// # Returns
|
||
/// A new `Xoroshiro` instance.
|
||
#[must_use]
|
||
pub const fn from_seed(seed: u64) -> Self {
|
||
let (lo, hi) = Self::mix_u64(seed);
|
||
let lo = mix_stafford_13(lo);
|
||
let hi = mix_stafford_13(hi);
|
||
Self::new(lo, hi)
|
||
}
|
||
|
||
/// Creates a new Xoroshiro generator with the given state words.
|
||
///
|
||
/// If both state words are zero, they are replaced with default values
|
||
/// to avoid the all-zero state which is invalid for the algorithm.
|
||
///
|
||
/// # Arguments
|
||
/// - `lo` – The lower 64-bit state word.
|
||
/// - `hi` – The higher 64-bit state word.
|
||
///
|
||
/// # Returns
|
||
/// A new `Xoroshiro` instance.
|
||
const fn new(lo: u64, hi: u64) -> Self {
|
||
let (lo, hi) = if (lo | hi) == 0 {
|
||
(0x9E3779B97F4A7C15, 0x6A09E667F3BCC909)
|
||
} else {
|
||
(lo, hi)
|
||
};
|
||
Self {
|
||
lo,
|
||
hi,
|
||
internal_next_gaussian: None,
|
||
}
|
||
}
|
||
|
||
/// Mixes a 64-bit seed into two 64-bit state words.
|
||
///
|
||
/// This uses the golden ratio constants to produce two distinct
|
||
/// starting values from a single seed.
|
||
///
|
||
/// # Arguments
|
||
/// - `seed` – The seed value.
|
||
///
|
||
/// # Returns
|
||
/// A tuple of two 64-bit values for initializing the state.
|
||
const fn mix_u64(seed: u64) -> (u64, u64) {
|
||
let l = seed ^ 0x6A09E667F3BCC909;
|
||
let m = l.wrapping_add(0x9E3779B97F4A7C15);
|
||
(l, m)
|
||
}
|
||
|
||
/// Creates a new Xoroshiro generator without mixing the seed.
|
||
///
|
||
/// This is used for testing and when the seed is already properly mixed.
|
||
///
|
||
/// # Arguments
|
||
/// - `seed` – The seed value (will be mixed using the standard mix function).
|
||
///
|
||
/// # Returns
|
||
/// A new `Xoroshiro` instance.
|
||
#[must_use]
|
||
pub const fn from_seed_unmixed(seed: u64) -> Self {
|
||
let (lo, hi) = Self::mix_u64(seed);
|
||
Self::new(lo, hi)
|
||
}
|
||
|
||
/// Generates a random value with the specified number of bits.
|
||
///
|
||
/// # Arguments
|
||
/// - `bits` – The number of bits to extract (0-64).
|
||
///
|
||
/// # Returns
|
||
/// A random value with the given number of bits.
|
||
const fn next(&mut self, bits: u64) -> u64 {
|
||
self.next_random() >> (64 - bits)
|
||
}
|
||
|
||
/// Generates the next random value and advances the generator state.
|
||
///
|
||
/// This implements the core Xoroshiro128+ algorithm:
|
||
/// `result = state.lo + state.hi`
|
||
/// Then updates the state using XOR, rotation, and shift operations.
|
||
///
|
||
/// # Returns
|
||
/// A 64-bit random value.
|
||
const fn next_random(&mut self) -> u64 {
|
||
let l = self.lo;
|
||
let m = self.hi;
|
||
let n = l.wrapping_add(m).rotate_left(17).wrapping_add(l);
|
||
let m = m ^ l;
|
||
self.lo = l.rotate_left(49) ^ m ^ (m << 21);
|
||
self.hi = m.rotate_left(28);
|
||
n
|
||
}
|
||
|
||
pub const fn next_splitter(&mut self) -> XoroshiroSplitter {
|
||
XoroshiroSplitter {
|
||
lo: self.next_random(),
|
||
hi: self.next_random(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl GaussianGenerator for Xoroshiro {
|
||
fn stored_next_gaussian(&self) -> Option<f64> {
|
||
self.internal_next_gaussian
|
||
}
|
||
|
||
fn set_stored_next_gaussian(&mut self, value: Option<f64>) {
|
||
self.internal_next_gaussian = value;
|
||
}
|
||
}
|
||
|
||
/// Stafford 13 mixing function for seed initialization.
|
||
///
|
||
/// This is a high-quality integer hash function used to mix the initial seed
|
||
/// into the generator state for better distribution.
|
||
///
|
||
/// # Arguments
|
||
/// - `z` – The value to mix.
|
||
///
|
||
/// # Returns
|
||
/// The mixed value.
|
||
const fn mix_stafford_13(z: u64) -> u64 {
|
||
let z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
|
||
let z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
|
||
z ^ (z >> 31)
|
||
}
|
||
|
||
impl RandomImpl for Xoroshiro {
|
||
fn split(&mut self) -> Self {
|
||
Self::new(self.next_random(), self.next_random())
|
||
}
|
||
|
||
fn next_splitter(&mut self) -> RandomDeriver {
|
||
RandomDeriver::Xoroshiro(self.next_splitter())
|
||
}
|
||
|
||
fn next_i32(&mut self) -> i32 {
|
||
self.next_random() as i32
|
||
}
|
||
|
||
fn next_bounded_i32(&mut self, bound: i32) -> i32 {
|
||
let mut l = (self.next_i32() as u64) & 0xFFFF_FFFF;
|
||
let mut m = l.wrapping_mul(bound as u64);
|
||
let mut n = m & 0xFFFF_FFFF;
|
||
if n < bound as u64 {
|
||
let i = ((!bound).wrapping_add(1) as u64) % (bound as u64);
|
||
while n < i {
|
||
l = (self.next_i32() as u64) & 0xFFFF_FFFF;
|
||
m = l.wrapping_mul(bound as u64);
|
||
n = m & 0xFFFF_FFFF;
|
||
}
|
||
}
|
||
let o = m >> 32;
|
||
o as i32
|
||
}
|
||
|
||
fn next_i64(&mut self) -> i64 {
|
||
self.next_random() as i64
|
||
}
|
||
|
||
fn next_bool(&mut self) -> bool {
|
||
(self.next_random() & 1) != 0
|
||
}
|
||
|
||
fn next_f32(&mut self) -> f32 {
|
||
self.next(24) as f32 * 5.960_464_5E-8f32
|
||
}
|
||
|
||
fn next_f64(&mut self) -> f64 {
|
||
self.next(53) as f64 * f64::from(1.110_223E-16f32)
|
||
}
|
||
|
||
fn next_gaussian(&mut self) -> f64 {
|
||
self.calculate_gaussian()
|
||
}
|
||
}
|
||
|
||
/// A splitter for creating derived Xoroshiro random generators.
|
||
///
|
||
/// This struct allows for deterministic creation of multiple independent
|
||
/// random generators from a single seed, which is essential for
|
||
/// parallelizable world generation.
|
||
pub struct XoroshiroSplitter {
|
||
/// Lower 64 bits of the splitter state.
|
||
lo: u64,
|
||
/// Higher 64 bits of the splitter state.
|
||
hi: u64,
|
||
}
|
||
|
||
impl XoroshiroSplitter {
|
||
#[must_use]
|
||
pub fn split_string(&self, seed: &str) -> Xoroshiro {
|
||
let bytes = md5::compute(seed.as_bytes());
|
||
let l = u64::from_be_bytes(bytes[0..8].try_into().unwrap_or([0; 8]));
|
||
let m = u64::from_be_bytes(bytes[8..16].try_into().unwrap_or([0; 8]));
|
||
|
||
Xoroshiro::new(l ^ self.lo, m ^ self.hi)
|
||
}
|
||
|
||
#[must_use]
|
||
pub const fn from_lo_and_hi(&self, lo: u64, hi: u64) -> Xoroshiro {
|
||
Xoroshiro::new(lo ^ self.lo, hi ^ self.hi)
|
||
}
|
||
|
||
#[must_use]
|
||
pub const fn split_pos(&self, x: i32, y: i32, z: i32) -> Xoroshiro {
|
||
let l = hash_block_pos(x, y, z) as u64;
|
||
let m = l ^ self.lo;
|
||
|
||
Xoroshiro::new(m, self.hi)
|
||
}
|
||
}
|
||
|
||
impl RandomDeriverImpl for XoroshiroSplitter {
|
||
fn split_string(&self, seed: &str) -> RandomGenerator {
|
||
RandomGenerator::Xoroshiro(self.split_string(seed))
|
||
}
|
||
|
||
fn split_u64(&self, seed: u64) -> RandomGenerator {
|
||
RandomGenerator::Xoroshiro(Xoroshiro::new(seed ^ self.lo, seed ^ self.hi))
|
||
}
|
||
|
||
fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator {
|
||
RandomGenerator::Xoroshiro(self.split_pos(x, y, z))
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use crate::random::{RandomDeriverImpl, RandomImpl};
|
||
|
||
use super::{Xoroshiro, mix_stafford_13};
|
||
|
||
// Values checked against results from the equivalent Java source
|
||
|
||
#[test]
|
||
fn mix_stafford_13_test() {
|
||
let values: [(u64, i64); 31] = [
|
||
(0, 0),
|
||
(1, 6238072747940578789),
|
||
(64, -8456553050427055661),
|
||
(4096, -1125827887270283392),
|
||
(262144, -120227641678947436),
|
||
(16777216, 6406066033425044679),
|
||
(1073741824, 3143522559155490559),
|
||
(16, -2773008118984693571),
|
||
(1024, 8101005175654470197),
|
||
(65536, -3551754741763842827),
|
||
(4194304, -2737109459693184599),
|
||
(2, -2606959012126976886),
|
||
(128, -5825874238589581082),
|
||
(8192, 1111983794319025228),
|
||
(524288, -7964047577924347155),
|
||
(33554432, -5634612006859462257),
|
||
(2147483648, -1436547171018572641),
|
||
(137438953472, -4514638798598940860),
|
||
(8796093022208, -610572083552328405),
|
||
(562949953421312, -263574021372026223),
|
||
(36028797018963968, 7868130499179604987),
|
||
(253, -4045451768301188906),
|
||
(127, -6873224393826578139),
|
||
(8447, 6670985465942597767),
|
||
(524543, -6228499289678716485),
|
||
(33554687, 2630391896919662492),
|
||
(2147483903, -6879633228472053040),
|
||
(137438953727, -5817997684975131823),
|
||
(8796093022463, 2384436581894988729),
|
||
(562949953421567, -5076179956679497213),
|
||
(36028797018964223, -5993365784811617721),
|
||
];
|
||
for (input, output) in values {
|
||
assert_eq!(mix_stafford_13(input), output as u64);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_i32() {
|
||
let values = [
|
||
-160476802,
|
||
781697906,
|
||
653572596,
|
||
1337520923,
|
||
-505875771,
|
||
-47281585,
|
||
342195906,
|
||
1417498593,
|
||
-1478887443,
|
||
1560080270,
|
||
];
|
||
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_i32(), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_bounded_i32() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values = [9, 1, 1, 3, 8, 9, 0, 3, 6, 3];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_bounded_i32(10), value);
|
||
}
|
||
|
||
let values = [
|
||
9784805, 470346, 13560642, 7320226, 14949645, 13460529, 2824352, 10938308, 14146127,
|
||
4549185,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_bounded_i32(0xFFFFFF), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_between_i32() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values = [99, 59, 57, 65, 94, 100, 54, 66, 83, 68];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_inbetween_i32(50, 100), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_inbetween_exclusive() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values = [98, 59, 57, 65, 94, 99, 53, 66, 82, 68];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_inbetween_i32_exclusive(50, 100), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_f64() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values: [f64; 10] = [
|
||
0.16474369376959186,
|
||
0.7997457290026366,
|
||
0.2511961888876212,
|
||
0.11712489470639631,
|
||
0.0997124786680137,
|
||
0.7566797430601416,
|
||
0.7723285712021574,
|
||
0.9420469457586381,
|
||
0.48056202536813664,
|
||
0.6099690583914598,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_f64(), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_f32() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values: [f32; 10] = [
|
||
0.16474366,
|
||
0.7997457,
|
||
0.25119615,
|
||
0.117124856,
|
||
0.09971243,
|
||
0.7566797,
|
||
0.77232856,
|
||
0.94204694,
|
||
0.48056197,
|
||
0.609969,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_f32(), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_i64() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values: [i64; 10] = [
|
||
3038984756725240190,
|
||
-3694039286755638414,
|
||
4633751808701151732,
|
||
2160572957309072155,
|
||
1839370574944072389,
|
||
-4488466507718817201,
|
||
-4199796579929588030,
|
||
-1069045159880208415,
|
||
8864804693509535725,
|
||
-7194800960680693874,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_i64(), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_bool() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values: [bool; 10] = [
|
||
false, false, false, true, true, true, false, true, true, false,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_bool(), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_gaussian() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values: [f64; 10] = [
|
||
-0.48540690699780015,
|
||
0.43399227545320296,
|
||
-0.3283265251019599,
|
||
-0.5052497078202575,
|
||
-0.3772512828630807,
|
||
0.2419080215945433,
|
||
-0.42622066207565135,
|
||
2.411315261138953,
|
||
-1.1419147030553274,
|
||
-0.05849758093810378,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_gaussian(), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn next_triangular() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let values: [f64; 10] = [
|
||
6.824989823834776,
|
||
10.670356470906125,
|
||
6.71516367803936,
|
||
9.151408127217596,
|
||
9.352964834883384,
|
||
8.291618967842293,
|
||
8.954549938640508,
|
||
11.833001837470519,
|
||
10.65851306020791,
|
||
11.684676364031647,
|
||
];
|
||
for value in values {
|
||
assert_eq!(xoroshiro.next_triangular(10f64, 5f64), value);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn split() {
|
||
let mut xoroshiro = Xoroshiro::from_seed(0);
|
||
|
||
let mut new_generator = xoroshiro.split();
|
||
assert_eq!(new_generator.next_i32(), 542195535);
|
||
|
||
{
|
||
// Drop splitter out of scope, so we can `mut` call new_generator again
|
||
let splitter = new_generator.next_splitter();
|
||
let mut rand_1 = splitter.split_string("TEST STRING");
|
||
assert_eq!(rand_1.next_i32(), -641435713);
|
||
|
||
let mut rand_2 = splitter.split_u64(42069);
|
||
assert_eq!(rand_2.next_i32(), -340700677);
|
||
|
||
let mut rand_3 = splitter.split_pos(1337, 80085, -69420);
|
||
assert_eq!(rand_3.next_i32(), 790449132);
|
||
};
|
||
// Verify we didn't mutate the originals
|
||
assert_eq!(xoroshiro.next_i32(), 653572596);
|
||
assert_eq!(new_generator.next_i32(), 435917842);
|
||
}
|
||
|
||
#[test]
|
||
fn intersection() {
|
||
let mut xoroshiro = Xoroshiro::new(0, 0);
|
||
assert_eq!(xoroshiro.next_i64(), 6807859099481836695);
|
||
}
|
||
}
|