ferray_random/bitgen/
pcg64.rs1#![allow(clippy::unreadable_literal)]
10
11use super::BitGenerator;
12
13pub struct Pcg64 {
27 state: u128,
28 inc: u128,
29}
30
31const PCG_DEFAULT_MULTIPLIER: u128 = 0x2360_ED05_1FC6_5DA4_4385_DF64_9FCC_F645;
33
34impl Pcg64 {
35 #[inline]
37 const fn step(&mut self) {
38 self.state = self
39 .state
40 .wrapping_mul(PCG_DEFAULT_MULTIPLIER)
41 .wrapping_add(self.inc);
42 }
43
44 #[inline]
46 const fn output(state: u128) -> u64 {
47 let xsl = ((state >> 64) ^ state) as u64;
48 let rot = (state >> 122) as u32;
49 xsl.rotate_right(rot)
50 }
51}
52
53impl BitGenerator for Pcg64 {
54 fn next_u64(&mut self) -> u64 {
55 let old_state = self.state;
56 self.step();
57 Self::output(old_state)
58 }
59
60 fn seed_from_u64(seed: u64) -> Self {
61 let seed128 = {
63 let mut s = seed;
64 let a = super::splitmix64(&mut s);
65 let b = super::splitmix64(&mut s);
66 ((a as u128) << 64) | (b as u128)
67 };
68 let inc = {
70 let mut s = seed.wrapping_add(0xda3e39cb94b95bdb);
71 let a = super::splitmix64(&mut s);
72 let b = super::splitmix64(&mut s);
73 (((a as u128) << 64) | (b as u128)) | 1
74 };
75
76 let mut rng = Self { state: 0, inc };
77 rng.step();
78 rng.state = rng.state.wrapping_add(seed128);
79 rng.step();
80 rng
81 }
82
83 fn jump(&mut self) -> Option<()> {
84 None
86 }
87
88 fn stream(_seed: u64, _stream_id: u64) -> Option<Self> {
89 None
91 }
92}
93
94impl Clone for Pcg64 {
95 fn clone(&self) -> Self {
96 Self {
97 state: self.state,
98 inc: self.inc,
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn deterministic_output() {
109 let mut rng1 = Pcg64::seed_from_u64(42);
110 let mut rng2 = Pcg64::seed_from_u64(42);
111 for _ in 0..1000 {
112 assert_eq!(rng1.next_u64(), rng2.next_u64());
113 }
114 }
115
116 #[test]
117 fn different_seeds_differ() {
118 let mut rng1 = Pcg64::seed_from_u64(42);
119 let mut rng2 = Pcg64::seed_from_u64(43);
120 let mut same = true;
121 for _ in 0..100 {
122 if rng1.next_u64() != rng2.next_u64() {
123 same = false;
124 break;
125 }
126 }
127 assert!(!same);
128 }
129
130 #[test]
131 fn jump_not_supported() {
132 let mut rng = Pcg64::seed_from_u64(42);
133 assert!(rng.jump().is_none());
134 }
135
136 #[test]
137 fn stream_not_supported() {
138 assert!(Pcg64::stream(42, 0).is_none());
139 }
140
141 #[test]
142 fn output_covers_full_range() {
143 let mut rng = Pcg64::seed_from_u64(12345);
144 let mut seen_high = false;
145 let mut seen_low = false;
146 for _ in 0..10_000 {
147 let v = rng.next_u64();
148 if v > (u64::MAX / 2) {
149 seen_high = true;
150 } else {
151 seen_low = true;
152 }
153 if seen_high && seen_low {
154 break;
155 }
156 }
157 assert!(seen_high && seen_low);
158 }
159}