use std::mem::transmute;
pub struct PRNG {
seed: u64,
}
impl PRNG {
#[inline(always)]
pub fn init(s: u64) -> PRNG {
PRNG { seed: s }
}
#[allow(dead_code)]
pub fn rand(&mut self) -> u64 {
self.rand_change()
}
pub fn sparse_rand(&mut self) -> u64 {
let mut s = self.rand_change();
s &= self.rand_change();
s &= self.rand_change();
s
}
pub fn singular_bit(&mut self) -> u64 {
let arr: [u8; 8] = unsafe {transmute(self.rand() ^ self.rand())};
let byte: u8 = arr.iter().fold(0, |acc, &x| acc ^ x);
(1u64).wrapping_shl(((byte) >> 2) as u32)
}
fn rand_change(&mut self) -> u64 {
self.seed ^= self.seed >> 12;
self.seed ^= self.seed << 25;
self.seed ^= self.seed >> 27;
self.seed.wrapping_mul(2685_8216_5773_6338_717)
}
}
#[cfg(test)]
mod test {
use super::PRNG;
const ROUNDS: u32 = 4;
const MUTS: u32 = 32;
#[test]
fn check_bit_displacement() {
let mut seeder = PRNG::init(10300014);
let mut acc = [0u32; 64];
for _ in 0..ROUNDS {
let mut prng = PRNG::init(seeder.rand());
for _ in 0..MUTS {
add_to_bit_counts(prng.singular_bit(), &mut acc);
}
}
let max = *acc.iter().max().unwrap();
for (_i, m) in acc.iter_mut().enumerate() {
*m *= 100;
*m /= max;
}
let _sum: u32 = acc.iter().sum();
}
fn add_to_bit_counts(mut num: u64, acc: &mut [u32; 64]) {
while num != 0 {
let i = num.trailing_zeros();
acc[i as usize] += 1;
num &= !((1u64) << i);
}
}
}