rlox_core/
seed.rs

1use rand::SeedableRng;
2use rand_chacha::ChaCha8Rng;
3
4/// Derive a per-environment seed from a master seed and an index.
5///
6/// Uses a simple but effective mixing strategy: XOR the master seed with
7/// a hash derived from the index, ensuring each env gets a unique but
8/// deterministic stream.
9pub fn derive_seed(master: u64, index: usize) -> u64 {
10    // Use a ChaCha8 round to mix master + index into a new seed.
11    // This avoids trivial correlations between adjacent indices.
12    let combined = master
13        .wrapping_add(index as u64)
14        .wrapping_mul(0x9E3779B97F4A7C15);
15    let mut rng = ChaCha8Rng::seed_from_u64(combined);
16    rand::Rng::random::<u64>(&mut rng)
17}
18
19/// Create a ChaCha8Rng from a seed value.
20pub fn rng_from_seed(seed: u64) -> ChaCha8Rng {
21    ChaCha8Rng::seed_from_u64(seed)
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    #[test]
29    fn derive_seed_is_deterministic() {
30        let s1 = derive_seed(42, 0);
31        let s2 = derive_seed(42, 0);
32        assert_eq!(s1, s2);
33    }
34
35    #[test]
36    fn derive_seed_differs_by_index() {
37        let s0 = derive_seed(42, 0);
38        let s1 = derive_seed(42, 1);
39        let s2 = derive_seed(42, 2);
40        assert_ne!(s0, s1);
41        assert_ne!(s1, s2);
42        assert_ne!(s0, s2);
43    }
44
45    #[test]
46    fn derive_seed_differs_by_master() {
47        let s1 = derive_seed(1, 0);
48        let s2 = derive_seed(2, 0);
49        assert_ne!(s1, s2);
50    }
51}