use crate::formulas::{lcg32_step, lcg64_jump, lcg64_step, xsh_rr_u64_to_u32, PCG_MUL_64};
#[derive(Debug, Clone)]
pub struct PCG32 {
pub state: u64,
pub inc: u64,
}
impl PCG32 {
#[inline]
#[must_use]
pub const fn new(state: u64, inc: u64) -> Self {
Self { state, inc }
}
#[inline]
pub const fn seed(seed: u64, inc: u64) -> Self {
let seed = (seed << 1) | 1;
let inc = (inc << 1) | 1;
let state = lcg64_step(PCG_MUL_64, inc, seed);
Self { state, inc }
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn from_getrandom() -> Result<Self, getrandom::Error> {
use bytemuck::bytes_of_mut;
let mut buf = [0_u64; 2];
getrandom::getrandom(bytes_of_mut(&mut buf))?;
Ok(Self::new(buf[0], buf[1] | 1))
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
let new_state = lcg64_step(PCG_MUL_64, self.inc, self.state);
let out = xsh_rr_u64_to_u32(self.state);
self.state = new_state;
out
}
#[inline]
pub fn jump(&mut self, delta: u64) {
self.state = lcg64_jump(PCG_MUL_64, self.inc, self.state, delta);
}
}
#[derive(Debug, Clone)]
pub struct PCG32K<const K: usize> {
pub state: u64,
pub ext: [u32; K],
}
impl<const K: usize> PCG32K<K> {
#[inline]
#[must_use]
pub const fn new(state: u64, ext: [u32; K]) -> Self {
Self { state, ext }
}
#[inline]
pub const fn seed(seed: u64, mut ext: [u32; K]) -> Self {
let seed = (seed << 1) | 1;
let state = lcg64_step(PCG_MUL_64, 1, seed);
let mut i = 0;
while i < K {
ext[i] = lcg32_step(PCG_MUL_64 as u32, 1, ext[i]);
i += 1;
}
Self { state, ext }
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn from_getrandom() -> Result<Self, getrandom::Error> {
use bytemuck::bytes_of_mut;
let mut state = 0_u64;
getrandom::getrandom(bytes_of_mut(&mut state))?;
let mut out = Self::new(state, [0_u32; K]);
out.scramble_ext_array()?;
Ok(out)
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn scramble_ext_array(&mut self) -> Result<(), getrandom::Error> {
use bytemuck::bytes_of_mut;
getrandom::getrandom(bytes_of_mut(&mut self.ext))
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
let new_state = lcg64_step(PCG_MUL_64, 1, self.state);
let out = if K > 0 {
let ext_index: usize = self.state as usize % K;
let out = xsh_rr_u64_to_u32(self.state) ^ self.ext[ext_index];
if self.state == 0 {
self.ext_add(1)
}
out
} else {
xsh_rr_u64_to_u32(self.state)
};
self.state = new_state;
out
}
#[inline(never)]
fn ext_add(&mut self, delta: u32) {
if K == 0 {
return;
}
let (new_ext, carry) = self.ext[0].overflowing_add(delta);
self.ext[0] = new_ext;
if carry {
for ext in &mut self.ext[1..] {
let (new_ext, carry) = ext.overflowing_add(1);
*ext = new_ext;
if carry {
continue;
} else {
break;
}
}
}
}
}
#[test]
fn test_ext_add() {
let mut x = PCG32K::<2> { state: 0, ext: [u32::MAX, 0] };
x.ext_add(1);
assert_eq!(x.ext[0], 0);
assert_eq!(x.ext[1], 1);
let mut x = PCG32K::<3> { state: 0, ext: [u32::MAX, u32::MAX, 0] };
x.ext_add(1);
assert_eq!(x.ext[0], 0);
assert_eq!(x.ext[1], 0);
assert_eq!(x.ext[2], 1);
}