use crate::{_internal::FSCALE64, rng::Rng64, rng64::SplitMix64};
#[repr(C, align(64))]
pub struct Philox64 {
pub(crate) c: [u64; 2],
pub(crate) k: [u64; 2],
}
impl Philox64 {
pub fn new(seed: u64) -> Self {
let mut seedgen = SplitMix64::new(seed);
Self {
c: [1, 0],
k: [seedgen.nextu(), seedgen.nextu()],
}
}
#[inline]
pub(crate) fn compute(c: [u64; 2], k: [u64; 2]) -> [u64; 2] {
let mut v0 = c[0];
let mut v1 = c[1];
let mut key = k[0];
const M0: u128 = 0xD2B74407B1CE6E93;
const W0: u64 = 0x9E3779B97F4A7C15;
macro_rules! step {
() => {
step!(fin);
key = key.wrapping_add(W0);
};
(fin) => {
let prod = (v0 as u128).wrapping_mul(M0);
let hi = (prod >> 64) as u64;
let lo = prod as u64;
let next_v0 = hi ^ v1 ^ key;
let next_v1 = lo;
v0 = next_v0;
v1 = next_v1;
};
}
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!(fin);
[v0, v1]
}
#[inline]
pub fn nextu(&mut self) -> [u64; 2] {
let out = Self::compute(self.c, self.k);
self.c[0] = self.c[0].wrapping_add(1);
if self.c[0] == 0 {
self.c[1] = self.c[1].wrapping_add(1);
}
out
}
#[inline]
pub fn nextf(&mut self) -> [f64; 2] {
self.nextu().map(|x| (x as f64) * FSCALE64)
}
#[inline]
pub fn randi(&mut self, min: i64, max: i64) -> [i64; 2] {
let range = (max as i128 - min as i128 + 1) as u128;
self.nextu()
.map(|x| ((x as u128 * range) >> 64) as i64 + min)
}
#[inline]
pub fn randf(&mut self, min: f64, max: f64) -> [f64; 2] {
let scale = (max - min) * FSCALE64;
self.nextu().map(|x| (x as f64 * scale) + min)
}
}
#[cfg(test)]
mod tests {
use super::*;
crate::safe_test!(Philox64);
}