mscore/simulation/
noise_rng.rs1use rand::rngs::StdRng;
13use rand::SeedableRng;
14
15const MIX: u64 = 0x9E37_79B9_7F4A_7C15;
17
18#[inline]
23pub fn stream_key(seed: u64, keys: &[u64]) -> u64 {
24 let mut h = seed ^ MIX;
25 for (i, &k) in keys.iter().enumerate() {
26 let mut x = k.wrapping_add(MIX.wrapping_mul(i as u64 + 1));
28 x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
29 x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
30 x ^= x >> 31;
31 h = (h ^ x).wrapping_mul(MIX);
32 }
33 h
34}
35
36#[inline]
38pub fn noise_rng(seed: u64, keys: &[u64]) -> StdRng {
39 StdRng::seed_from_u64(stream_key(seed, keys))
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use rand::Rng;
46
47 fn draw(seed: u64, keys: &[u64]) -> u64 {
48 noise_rng(seed, keys).gen()
49 }
50
51 #[test]
52 fn same_inputs_give_the_same_stream() {
53 assert_eq!(draw(41, &[7, 3, 1]), draw(41, &[7, 3, 1]));
54 }
55
56 #[test]
57 fn the_seed_matters() {
58 assert_ne!(draw(41, &[7, 3, 1]), draw(42, &[7, 3, 1]));
59 }
60
61 #[test]
62 fn each_key_component_matters() {
63 let base = draw(41, &[7, 3, 1]);
64 assert_ne!(base, draw(41, &[8, 3, 1]));
65 assert_ne!(base, draw(41, &[7, 4, 1]));
66 assert_ne!(base, draw(41, &[7, 3, 2]));
67 }
68
69 #[test]
70 fn key_order_matters() {
71 assert_ne!(draw(41, &[1, 2]), draw(41, &[2, 1]));
72 }
73
74 #[test]
75 fn neighbouring_keys_do_not_collide() {
76 let mut seen = std::collections::HashSet::new();
78 for frame in 0..200u64 {
79 for scan in 0..50u64 {
80 assert!(seen.insert(stream_key(41, &[frame, scan])), "collision at {frame}/{scan}");
81 }
82 }
83 }
84}