use std::slice::from_raw_parts_mut;
#[repr(C)]
pub struct Philox32 {
c: [u32; 4],
k: [u32; 2],
}
impl Philox32 {
const fn chunk_size() -> usize {
4
}
const fn m0() -> u32 {
0xD2511F53
}
const fn m1() -> u32 {
0xCD9E8D57
}
pub fn new(seed: [u32; 2]) -> Self {
Self {
c: [1, 0, 0, 0],
k: seed,
}
}
pub fn warm(&mut self, count: usize) {
for _i in 0..count {
let _ = self.nextu();
}
}
#[inline]
pub fn nextu(&mut self) -> [u32; 4] {
let mut out = [0u32; 4];
let p0 = self.c[0].wrapping_mul(Self::m0());
let p1 = self.c[2].wrapping_mul(Self::m1());
out[0] = p0 ^ self.c[1] ^ self.k[0];
out[1] = p0;
out[2] = p1 ^ self.c[3] ^ self.k[1];
out[3] = p1;
self.c[0] = self.c[0].wrapping_add(1);
self.c[1] = self.c[1].wrapping_add(1);
self.c[2] = self.c[2].wrapping_add(1);
self.c[3] = self.c[3].wrapping_add(1);
out
}
#[inline]
pub fn nextf(&mut self) -> f32 {
self.nextu()[0] as f32 * (1.0 / (u32::MAX as f32 + 1.0))
}
#[inline]
pub fn randi(&mut self, min: i32, max: i32) -> i32 {
let range = (max as i64 - min as i64 + 1) as u64;
let x = self.nextu()[0];
((x as u64 * range) >> 32) as i32 + min
}
#[inline]
pub fn randf(&mut self, min: f32, max: f32) -> f32 {
let range = max - min;
let scale = range * (1.0 / (u32::MAX as f32 + 1.0));
(self.nextu()[0] as f32 * scale) + min
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_new(seed1: u32, seed2: u32) -> *mut Philox32 {
Box::into_raw(Box::new(Philox32::new([seed1, seed2])))
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_warm(ptr: *mut Philox32, count: usize) {
unsafe {
let rng = &mut *ptr;
rng.warm(count);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_free(ptr: *mut Philox32) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) }
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_next_u32s(ptr: *mut Philox32, out: *mut u32, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
let mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox32::chunk_size());
buffer[i..i + take].copy_from_slice(&chunk[..take]);
i += take;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_next_f32s(ptr: *mut Philox32, out: *mut f32, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
let mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox32::chunk_size());
for j in 0..take {
buffer[i + j] = chunk[j] as f32 * (1.0 / (u32::MAX as f32 + 1.0));
}
i += take;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_rand_i32s(
ptr: *mut Philox32,
out: *mut i32,
count: usize,
min: i32,
max: i32,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
let mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox32::chunk_size());
for j in 0..take {
let range = (max as i64 - min as i64 + 1) as u64;
buffer[i + j] = ((chunk[j] as u64 * range) >> 32) as i32 + min;
}
i += take;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox32_rand_f32s(
ptr: *mut Philox32,
out: *mut f32,
count: usize,
min: f32,
max: f32,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
let mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox32::chunk_size());
for j in 0..take {
let scale = (max - min) * (1.0 / (u32::MAX as f32 + 1.0));
buffer[i + j] = (chunk[j] as f32 * scale) + min;
}
i += take;
}
}
}
#[cfg(test)]
mod tests {
use super::Philox32;
#[test]
fn test_philox32() {
let rng = &mut Philox32::new([1, 2]);
assert_eq!(rng.nextu(), [3528531794, 3528531795, 2, 0]);
}
#[test]
fn test_philox32_methods() {
let mut rng = Philox32::new([1, 2]);
let _ = rng.nextu();
let f = rng.nextf();
assert!(f >= 0.0 && f < 1.0);
let i = rng.randi(10, 20);
assert!(i >= 10 && i <= 20);
let rf = rng.randf(10.0, 20.0);
assert!(rf >= 10.0 && rf < 20.0);
}
}