use rand::rngs::StdRng;
use rand::SeedableRng;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SeedPurpose {
Init = 0,
Selection = 1,
Crossover = 2,
Mutation = 3,
Replacement = 4,
Trial = 5,
Other = 6,
}
impl SeedPurpose {
const fn constant(self) -> u64 {
match self {
SeedPurpose::Init => 0xA5A5_A5A5_A5A5_A5A5,
SeedPurpose::Selection => 0x1234_5678_9ABC_DEF0,
SeedPurpose::Crossover => 0xDEAD_BEEF_CAFE_F00D,
SeedPurpose::Mutation => 0xFEED_FACE_0BAD_F00D,
SeedPurpose::Replacement => 0x0123_4567_89AB_CDEF,
SeedPurpose::Trial => 0xBAAD_F00D_DEAD_C0DE,
SeedPurpose::Other => 0x9E37_79B9_7F4A_7C15,
}
}
}
#[must_use]
pub fn seed_stream(base: u64, generation: u64, purpose: SeedPurpose) -> StdRng {
let mut x = base
.wrapping_add(generation.wrapping_mul(0x9E37_79B9_7F4A_7C15))
.wrapping_add(purpose.constant());
x = splitmix64(x);
x = splitmix64(x);
StdRng::seed_from_u64(x)
}
const fn splitmix64(mut x: u64) -> u64 {
x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
x ^ (x >> 31)
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{Rng, RngExt};
#[test]
fn seed_stream_is_deterministic() {
let mut a = seed_stream(42, 0, SeedPurpose::Init);
let mut b = seed_stream(42, 0, SeedPurpose::Init);
for _ in 0..8 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn different_purposes_produce_different_streams() {
let a = seed_stream(42, 0, SeedPurpose::Init).next_u64();
let b = seed_stream(42, 0, SeedPurpose::Selection).next_u64();
let c = seed_stream(42, 0, SeedPurpose::Mutation).next_u64();
assert_ne!(a, b);
assert_ne!(a, c);
assert_ne!(b, c);
}
#[test]
fn different_generations_produce_different_streams() {
let a = seed_stream(42, 0, SeedPurpose::Mutation).next_u64();
let b = seed_stream(42, 1, SeedPurpose::Mutation).next_u64();
assert_ne!(a, b);
}
#[test]
fn different_bases_produce_different_streams() {
let a = seed_stream(1, 0, SeedPurpose::Init).next_u64();
let b = seed_stream(2, 0, SeedPurpose::Init).next_u64();
assert_ne!(a, b);
}
#[test]
fn rng_generates_bounded_values() {
let mut rng = seed_stream(7, 0, SeedPurpose::Init);
let x: u32 = rng.random_range(0..100);
assert!(x < 100);
}
}