#![allow(clippy::unreadable_literal)]
use super::BitGenerator;
pub struct Pcg64Dxsm {
state: u128,
inc: u128,
}
const PCG_CHEAP_MULTIPLIER: u64 = 0xda94_2042_e4dd_58b5;
impl Pcg64Dxsm {
#[inline]
const fn step(&mut self) {
let mult128 = PCG_CHEAP_MULTIPLIER as u128;
self.state = self.state.wrapping_mul(mult128).wrapping_add(self.inc);
}
#[inline]
const fn output(state: u128) -> u64 {
let hi0 = (state >> 64) as u64;
let lo = (state as u64) | 1;
let mut hi = hi0;
hi ^= hi >> 32;
hi = hi.wrapping_mul(PCG_CHEAP_MULTIPLIER);
hi ^= hi >> 48;
hi = hi.wrapping_mul(lo);
hi
}
}
impl BitGenerator for Pcg64Dxsm {
fn next_u64(&mut self) -> u64 {
let old = self.state;
self.step();
Self::output(old)
}
fn seed_from_u64(seed: u64) -> Self {
let seed128 = {
let mut s = seed;
let a = super::splitmix64(&mut s);
let b = super::splitmix64(&mut s);
((a as u128) << 64) | (b as u128)
};
let inc = {
let mut s = seed.wrapping_add(0xda3e_39cb_94b9_5bdb);
let a = super::splitmix64(&mut s);
let b = super::splitmix64(&mut s);
(((a as u128) << 64) | (b as u128)) | 1
};
let mut rng = Self { state: 0, inc };
rng.step();
rng.state = rng.state.wrapping_add(seed128);
rng.step();
rng
}
fn jump(&mut self) -> Option<()> {
None
}
fn stream(_seed: u64, _stream_id: u64) -> Option<Self> {
None
}
}
impl Clone for Pcg64Dxsm {
fn clone(&self) -> Self {
Self {
state: self.state,
inc: self.inc,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic_output() {
let mut a = Pcg64Dxsm::seed_from_u64(7);
let mut b = Pcg64Dxsm::seed_from_u64(7);
for _ in 0..1000 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn different_seeds_differ() {
let mut a = Pcg64Dxsm::seed_from_u64(7);
let mut b = Pcg64Dxsm::seed_from_u64(8);
let mut diff = false;
for _ in 0..100 {
if a.next_u64() != b.next_u64() {
diff = true;
break;
}
}
assert!(diff);
}
#[test]
fn full_range() {
let mut rng = Pcg64Dxsm::seed_from_u64(0xfeed_face);
let mut hi = false;
let mut lo = false;
for _ in 0..10_000 {
let v = rng.next_u64();
if v > u64::MAX / 2 {
hi = true;
} else {
lo = true;
}
if hi && lo {
break;
}
}
assert!(hi && lo);
}
}