use wrapn::{wrap, wu64, wusize};
use crate::_internal::impl_ring_rng64;
use crate::prng::b64::SplitMix64;
#[repr(C, align(64))]
pub struct Philox64 {
pub(crate) c: [wu64; 2],
pub(crate) k: [wu64; 2],
pub(crate) buf: [wu64; 2],
pub(crate) pos: wusize,
}
impl Philox64 {
pub const fn new(seed: u64) -> Self {
let mut seedgen = SplitMix64::new(seed);
Self {
c: wrap![1, 0],
k: wrap![seedgen.nextu_const(), seedgen.nextu_const()],
buf: wrap![0; 2],
pos: wrap!(2),
}
}
#[inline]
pub(crate) fn compute(mut c: [wu64; 2], k: [wu64; 2]) -> [wu64; 2] {
let mut key = k[0];
const M0: u128 = 0xD2B74407B1CE6E93;
const W0: u64 = 0x9E3779B97F4A7C15;
macro_rules! step {
() => {
step!(fin);
key += W0;
};
(fin) => {
let prod = c[0].cast::<u128>() * M0;
let hi = (prod >> 64).cast::<u64>();
let lo = prod.cast::<u64>();
c[0] = hi ^ c[1] ^ key;
c[1] = lo;
};
}
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!(fin);
c
}
#[inline]
pub fn next_raw(&mut self) -> [wu64; 2] {
let out = Self::compute(self.c, self.k);
self.c[0] += 1;
if self.c[0] == 0 {
self.c[1] += 1;
}
out
}
}
impl_ring_rng64!(Philox64, 2, next_raw);
#[repr(C, align(64))]
pub struct Philox4x64 {
c: [wu64; 4],
k: [wu64; 2],
buf: [wu64; 4],
pos: wusize,
}
impl Philox4x64 {
pub const fn new(seed: u64) -> Self {
let mut seedgen = SplitMix64::new(seed);
Self {
c: wrap![3, 2, 1, 0],
k: wrap![seedgen.nextu_const(), seedgen.nextu_const()],
buf: wrap![0; 4],
pos: wrap!(2),
}
}
#[inline]
pub(crate) fn compute(mut c: [wu64; 4], mut k: [wu64; 2]) -> [wu64; 4] {
const M0: u128 = 0xD2E7470EE14C6C93;
const M1: u128 = 0xCA5A826395121157;
const W0: u64 = 0x9E3779B97F4A7C15;
const W1: u64 = 0xBB67AE8584CAA73B;
macro_rules! step {
() => {
step!(fin);
k[0] += W0;
k[1] += W1;
};
(fin) => {
let prod0 = c[0].cast::<u128>() * M0;
let hi0 = (prod0 >> 64).cast::<u64>();
let lo0 = prod0.cast::<u64>();
let prod1 = c[2].cast::<u128>() * M1;
let hi1 = (prod1 >> 64).cast::<u64>();
let lo1 = prod1.cast::<u64>();
let c0 = hi1 ^ c[1] ^ k[0];
let c1 = lo1;
let c2 = hi0 ^ c[3] ^ k[1];
let c3 = lo0;
c[0] = c0;
c[1] = c1;
c[2] = c2;
c[3] = c3;
};
}
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!();
step!(fin);
c
}
#[inline]
pub fn next_raw(&mut self) -> [wu64; 4] {
let out = Self::compute(self.c, self.k);
self.c[0] += 1;
if self.c[0] == 0 {
self.c[1] += 1;
if self.c[1] == 0 {
self.c[2] += 1;
if self.c[2] == 0 {
self.c[3] += 1;
}
}
}
out
}
}
impl_ring_rng64!(Philox4x64, 4, next_raw);
#[cfg(test)]
mod tests {
use super::*;
use crate::rng::Rng;
crate::safe_test! {
Philox64,
Philox4x64
}
}