ferray_random/bitgen/
sfc64.rs1#![allow(clippy::unreadable_literal)]
8
9use super::BitGenerator;
10
11pub struct Sfc64 {
17 a: u64,
18 b: u64,
19 c: u64,
20 counter: u64,
21}
22
23impl Sfc64 {
24 #[inline]
25 fn advance(&mut self) -> u64 {
26 let tmp = self.a.wrapping_add(self.b).wrapping_add(self.counter);
27 self.counter = self.counter.wrapping_add(1);
28 self.a = self.b ^ (self.b >> 11);
29 self.b = self.c.wrapping_add(self.c << 3);
30 self.c = self.c.rotate_left(24).wrapping_add(tmp);
31 tmp
32 }
33}
34
35impl BitGenerator for Sfc64 {
36 fn next_u64(&mut self) -> u64 {
37 self.advance()
38 }
39
40 fn seed_from_u64(seed: u64) -> Self {
41 let mut rng = Self {
45 a: seed,
46 b: seed,
47 c: seed,
48 counter: 1,
49 };
50 for _ in 0..12 {
51 let _ = rng.advance();
52 }
53 rng
54 }
55
56 fn jump(&mut self) -> Option<()> {
57 None
58 }
59
60 fn stream(_seed: u64, _stream_id: u64) -> Option<Self> {
61 None
62 }
63}
64
65impl Clone for Sfc64 {
66 fn clone(&self) -> Self {
67 Self {
68 a: self.a,
69 b: self.b,
70 c: self.c,
71 counter: self.counter,
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn deterministic_output() {
82 let mut a = Sfc64::seed_from_u64(0xc0ffee);
83 let mut b = Sfc64::seed_from_u64(0xc0ffee);
84 for _ in 0..1000 {
85 assert_eq!(a.next_u64(), b.next_u64());
86 }
87 }
88
89 #[test]
90 fn different_seeds_differ() {
91 let mut a = Sfc64::seed_from_u64(1);
92 let mut b = Sfc64::seed_from_u64(2);
93 let mut diff = false;
94 for _ in 0..100 {
95 if a.next_u64() != b.next_u64() {
96 diff = true;
97 break;
98 }
99 }
100 assert!(diff);
101 }
102
103 #[test]
104 fn full_range() {
105 let mut rng = Sfc64::seed_from_u64(0x1234_5678_9abc_def0);
106 let mut hi = false;
107 let mut lo = false;
108 for _ in 0..10_000 {
109 let v = rng.next_u64();
110 if v > u64::MAX / 2 {
111 hi = true;
112 } else {
113 lo = true;
114 }
115 if hi && lo {
116 break;
117 }
118 }
119 assert!(hi && lo);
120 }
121}