1use rand::rngs::StdRng;
9use rand::SeedableRng;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18#[repr(u8)]
19pub enum SeedPurpose {
20 Init = 0,
22 Selection = 1,
24 Crossover = 2,
26 Mutation = 3,
28 Replacement = 4,
30 Trial = 5,
32 Other = 6,
34}
35
36impl SeedPurpose {
37 const fn constant(self) -> u64 {
38 match self {
39 SeedPurpose::Init => 0xA5A5_A5A5_A5A5_A5A5,
40 SeedPurpose::Selection => 0x1234_5678_9ABC_DEF0,
41 SeedPurpose::Crossover => 0xDEAD_BEEF_CAFE_F00D,
42 SeedPurpose::Mutation => 0xFEED_FACE_0BAD_F00D,
43 SeedPurpose::Replacement => 0x0123_4567_89AB_CDEF,
44 SeedPurpose::Trial => 0xBAAD_F00D_DEAD_C0DE,
45 SeedPurpose::Other => 0x9E37_79B9_7F4A_7C15,
46 }
47 }
48}
49
50#[must_use]
55pub fn seed_stream(base: u64, generation: u64, purpose: SeedPurpose) -> StdRng {
56 let mut x = base
57 .wrapping_add(generation.wrapping_mul(0x9E37_79B9_7F4A_7C15))
58 .wrapping_add(purpose.constant());
59 x = splitmix64(x);
60 x = splitmix64(x);
61 StdRng::seed_from_u64(x)
62}
63
64const fn splitmix64(mut x: u64) -> u64 {
65 x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
66 x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
67 x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
68 x ^ (x >> 31)
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use rand::{Rng, RngExt};
75
76 #[test]
77 fn seed_stream_is_deterministic() {
78 let mut a = seed_stream(42, 0, SeedPurpose::Init);
79 let mut b = seed_stream(42, 0, SeedPurpose::Init);
80 for _ in 0..8 {
81 assert_eq!(a.next_u64(), b.next_u64());
82 }
83 }
84
85 #[test]
86 fn different_purposes_produce_different_streams() {
87 let a = seed_stream(42, 0, SeedPurpose::Init).next_u64();
88 let b = seed_stream(42, 0, SeedPurpose::Selection).next_u64();
89 let c = seed_stream(42, 0, SeedPurpose::Mutation).next_u64();
90 assert_ne!(a, b);
91 assert_ne!(a, c);
92 assert_ne!(b, c);
93 }
94
95 #[test]
96 fn different_generations_produce_different_streams() {
97 let a = seed_stream(42, 0, SeedPurpose::Mutation).next_u64();
98 let b = seed_stream(42, 1, SeedPurpose::Mutation).next_u64();
99 assert_ne!(a, b);
100 }
101
102 #[test]
103 fn different_bases_produce_different_streams() {
104 let a = seed_stream(1, 0, SeedPurpose::Init).next_u64();
105 let b = seed_stream(2, 0, SeedPurpose::Init).next_u64();
106 assert_ne!(a, b);
107 }
108
109 #[test]
110 fn rng_generates_bounded_values() {
111 let mut rng = seed_stream(7, 0, SeedPurpose::Init);
112 let x: u32 = rng.random_range(0..100);
113 assert!(x < 100);
114 }
115}