use std::slice::from_raw_parts_mut;
use rayon::prelude::*;
use wrapn::{wrap, wu64};
use crate::_internal::{fill_chunk_auto, prefer_nt};
use crate::cbrng::b64::Philox64;
use crate::{i2f_bits, u2f_01};
#[unsafe(no_mangle)]
pub extern "C" fn philox64_new(seed: u64) -> *mut Philox64 {
Box::into_raw(Box::new(Philox64::new(seed)))
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_free(ptr: *mut Philox64) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) }
}
}
const PHILOX64_PAR_CHUNK: usize = 0x20000;
#[inline(always)]
fn philox64_fill<T, M>(buffer: &mut [T], c0: [wu64; 2], k: [wu64; 2], map: M)
where
T: Copy + Default + Send,
M: Fn(u64) -> T + Sync,
{
let nt = prefer_nt::<T>(buffer.len());
buffer
.par_chunks_mut(PHILOX64_PAR_CHUNK)
.enumerate()
.for_each(|(chunk_idx, chunk)| {
let mut block = ((chunk_idx * PHILOX64_PAR_CHUNK) / 2) as u64;
unsafe {
fill_chunk_auto(chunk, nt, || {
let mut out = [T::default(); 8];
for blk in out.chunks_exact_mut(2) {
let mut c = c0;
let (new_c0, overflow) = c[0].overflowing_add(block);
c[0] = wrap!(new_c0);
if overflow {
c[1] += 1;
}
let r = Philox64::compute(c, k);
blk[0] = map(*r[0]);
blk[1] = map(*r[1]);
block = block.wrapping_add(1);
}
out
});
}
});
}
#[inline(always)]
fn philox64_advance(rng: &mut Philox64, count: usize) {
let num_blocks = (count.div_ceil(8) * 4) as u64;
let (new_c0, carry) = rng.c[0].overflowing_add(num_blocks);
rng.c[0] = new_c0.into();
if carry {
rng.c[1] += 1;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_next_u64s(ptr: *mut Philox64, out: *mut u64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
philox64_fill(buffer, rng.c, rng.k, |x| x);
philox64_advance(rng, count);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_next_f64s(ptr: *mut Philox64, out: *mut f64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
philox64_fill(buffer, rng.c, rng.k, |x| u2f_01!(f64, 64, x));
philox64_advance(rng, count);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_rand_i64s(
ptr: *mut Philox64,
out: *mut i64,
count: usize,
min: i64,
max: i64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
let range = (max as i128 - min as i128 + 1) as u128;
philox64_fill(buffer, rng.c, rng.k, |x| {
((x as u128 * range) >> 64) as i64 + min
});
philox64_advance(rng, count);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_rand_f64s(
ptr: *mut Philox64,
out: *mut f64,
count: usize,
min: f64,
max: f64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
let mult = max - min;
philox64_fill(buffer, rng.c, rng.k, |x| u2f_01!(f64, 64, x) * mult + min);
philox64_advance(rng, count);
}
}