use crate::rng::Rng64;
use crate::wrap;
use pollster::FutureExt;
use std::f64::consts::{E, PI};
use std::num::Wrapping;
use std::slice::from_raw_parts_mut;
use wgpu;
#[repr(C)]
pub struct Mt1993764 {
mt: [u64; N],
mti: usize,
}
const N: usize = 312;
const M: usize = 156;
const MATRIX_A: u64 = 0xB5026F5AA96619E9;
const UPPER_MASK: u64 = 0xFFFFFFFF80000000;
const LOWER_MASK: u64 = 0x7FFFFFFF;
impl Mt1993764 {
pub fn new(seed: u64, warm: usize) -> Self {
let mut mt = [0u64; N];
mt[0] = seed;
for i in 1..N {
let prev = mt[i - 1];
mt[i] = 6364136223846793005u64
.wrapping_mul(prev ^ (prev >> 62))
.wrapping_add(i as u64);
}
let mut rng = Self { mt, mti: N };
(0..warm).into_iter().for_each(|_| {
let _ = rng.nextu();
});
rng
}
#[inline]
pub fn nextu(&mut self) -> u64 {
if self.mti >= N {
self.twist();
}
let mut y = self.mt[self.mti];
self.mti += 1;
y ^= (y >> 29) & 0x5555555555555555;
y ^= (y << 17) & 0x71D67FFFEDA60000;
y ^= (y << 37) & 0xFFF7EEE000000000;
y ^= y >> 43;
y
}
fn twist(&mut self) {
for i in 0..N - M {
let x = (self.mt[i] & UPPER_MASK) | (self.mt[i + 1] & LOWER_MASK);
let mut x_a = x >> 1;
if x & 1 != 0 {
x_a ^= MATRIX_A;
}
self.mt[i] = self.mt[i + M] ^ x_a;
}
for i in N - M..N - 1 {
let x = (self.mt[i] & UPPER_MASK) | (self.mt[i + 1] & LOWER_MASK);
let mut x_a = x >> 1;
if x & 1 != 0 {
x_a ^= MATRIX_A;
}
self.mt[i] = self.mt[i + M - N] ^ x_a;
}
let x = (self.mt[N - 1] & UPPER_MASK) | (self.mt[0] & LOWER_MASK);
let mut x_a = x >> 1;
if x & 1 != 0 {
x_a ^= MATRIX_A;
}
self.mt[N - 1] = self.mt[M - 1] ^ x_a;
self.mti = 0;
}
#[inline]
pub fn nextf(&mut self) -> f64 {
self.nextu() as f64 * (1.0 / (u64::MAX as f64 + 1.0))
}
#[inline]
pub fn randi(&mut self, min: i64, max: i64) -> i64 {
let range = (max as i128 - min as i128 + 1) as u128;
let x = self.nextu();
((x as u128 * range) >> 64) as i64 + min
}
#[inline]
pub fn randf(&mut self, min: f64, max: f64) -> f64 {
let range = max - min;
let scale = range * (1.0 / (u64::MAX as f64 + 1.0));
(self.nextu() as f64 * scale) + min
}
#[inline]
pub fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
let index = self.randi(0, choices.len() as i64 - 1);
&choices[index as usize]
}
}
impl Rng64 for Mt1993764 {
#[inline]
fn randi(&mut self, min: i64, max: i64) -> i64 {
self.randi(min, max)
}
#[inline]
fn randf(&mut self, min: f64, max: f64) -> f64 {
self.randf(min, max)
}
#[inline]
fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
self.choice(choices)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mt1993764_new(seed: u64, warm: usize) -> *mut Mt1993764 {
Box::into_raw(Box::new(Mt1993764::new(seed, warm)))
}
#[unsafe(no_mangle)]
pub extern "C" fn mt1993764_free(ptr: *mut Mt1993764) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mt1993764_next_u64s(ptr: *mut Mt1993764, out: *mut u64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextu();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mt1993764_next_f64s(ptr: *mut Mt1993764, out: *mut f64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextf();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mt1993764_rand_i64s(
ptr: *mut Mt1993764,
out: *mut i64,
count: usize,
min: i64,
max: i64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randi(min, max);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mt1993764_rand_f64s(
ptr: *mut Mt1993764,
out: *mut f64,
count: usize,
min: f64,
max: f64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randf(min, max);
}
}
}
#[repr(C)]
struct GPUParams {
c_lo: u64,
c_hi: u64,
k_base: u64,
_pad: u64,
}
#[allow(unused)]
pub struct WgpuResources {
storage_buffer: wgpu::Buffer,
staging_buffer: wgpu::Buffer,
param_buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
}
#[repr(C)]
pub struct Philox64 {
c: [u64; 2],
k: [u64; 2],
device: wgpu::Device,
queue: wgpu::Queue,
pipeline: wgpu::ComputePipeline,
gpu_buffer: Vec<u64>,
gpu_ptr: usize,
wgpu_resources: Option<WgpuResources>,
}
const GPU_BUFFER_SIZE: usize = 65536;
impl Philox64 {
const fn chunk_size() -> usize {
2
}
const fn m0() -> u128 {
0xD2B74407B1CE6E93
}
async fn init_wgpu() -> (wgpu::Device, wgpu::Queue) {
let instance = wgpu::Instance::default();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
})
.await
.expect("GPU not found");
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: Some("UniloxDevice"),
required_features: wgpu::Features::SHADER_INT64,
required_limits: wgpu::Limits::default(),
..Default::default()
})
.await
.expect("GPU not found");
(device, queue)
}
pub fn new(seed: [u64; 2]) -> Self {
let (device, queue) = Self::init_wgpu().block_on();
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Philox Shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("./shaders/philox.wgsl").into()),
});
let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Philox Compute Pipeline"),
layout: None,
module: &shader,
entry_point: Some("main"),
cache: None,
compilation_options: Default::default(),
});
let size = GPU_BUFFER_SIZE as u64 * 8;
let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Storage Buffer"),
size,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Staging Buffer"),
size,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let param_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Params Buffer"),
size: std::mem::size_of::<GPUParams>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group_layout = compute_pipeline.get_bind_group_layout(0);
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: storage_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: param_buffer.as_entire_binding(),
},
],
});
Self {
c: [1, 0],
k: seed,
device,
queue,
pipeline: compute_pipeline,
gpu_buffer: vec![0; GPU_BUFFER_SIZE],
gpu_ptr: GPU_BUFFER_SIZE,
wgpu_resources: Some(WgpuResources {
storage_buffer,
staging_buffer,
param_buffer,
bind_group,
}),
}
}
pub fn warm(&mut self, count: usize) {
for _i in 0..count {
let _ = self.nextu();
}
}
#[inline]
pub fn nextu(&mut self) -> [u64; 2] {
let mut v0 = self.c[0];
let mut v1 = self.c[1];
let mut k = self.k[0];
let w0: u64 = 0x9E3779B97F4A7C15;
for _ in 0..10 {
let prod = (v0 as u128).wrapping_mul(Self::m0());
let hi = (prod >> 64) as u64;
let lo = prod as u64;
let next_v0 = hi ^ v1 ^ k;
let next_v1 = lo;
v0 = next_v0;
v1 = next_v1;
k = k.wrapping_add(w0);
}
self.c[0] = self.c[0].wrapping_add(1);
if self.c[0] == 0 {
self.c[1] = self.c[1].wrapping_add(1);
}
[v0, v1]
}
#[inline]
pub fn nextf(&mut self) -> f64 {
self.nextu()[0] as f64 * (1.0 / (u64::MAX as f64 + 1.0))
}
#[inline]
pub fn randi(&mut self, min: i64, max: i64) -> i64 {
let range = (max as i128 - min as i128 + 1) as u128;
let x = self.nextu()[0];
((x as u128 * range) >> 64) as i64 + min
}
#[inline]
pub fn randf(&mut self, min: f64, max: f64) -> f64 {
self.nextf() * (max - min) + min
}
pub fn next_u64s_gpu(&mut self, out: &mut [u64]) {
const CHUNK_SIZE: usize = 8_000_000;
for chunk in out.chunks_mut(CHUNK_SIZE) {
self.process_gpu_chunk(chunk);
}
}
fn process_gpu_chunk(&mut self, out: &mut [u64]) {
let count = out.len();
if count == 0 {
return;
}
let size = (count * 8) as u64;
let params = GPUParams {
c_lo: self.c[0],
c_hi: self.c[1],
k_base: self.k[0],
_pad: 0,
};
let storage_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Chunk Storage Buffer"),
size,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let staging_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Chunk Staging Buffer"),
size,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let param_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Chunk Params Buffer"),
size: std::mem::size_of::<GPUParams>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let params_bytes = unsafe {
std::slice::from_raw_parts(
¶ms as *const GPUParams as *const u8,
std::mem::size_of::<GPUParams>(),
)
};
self.queue.write_buffer(¶m_buffer, 0, params_bytes);
let bind_group_layout = self.pipeline.get_bind_group_layout(0);
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: storage_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: param_buffer.as_entire_binding(),
},
],
});
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: None,
timestamp_writes: None,
});
cpass.set_pipeline(&self.pipeline);
cpass.set_bind_group(0, &bind_group, &[]);
let groups = (count as u32 / 2 + 63) / 64;
cpass.dispatch_workgroups(groups, 1, 1);
}
encoder.copy_buffer_to_buffer(&storage_buffer, 0, &staging_buffer, 0, size);
self.queue.submit(Some(encoder.finish()));
let buffer_slice = staging_buffer.slice(..);
let (sender, receiver) = std::sync::mpsc::channel();
buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
self.device
.poll(wgpu::PollType::Wait {
submission_index: None,
timeout: None,
})
.expect("GPU poll failed");
if let Ok(Ok(())) = receiver.recv() {
let data = buffer_slice.get_mapped_range();
unsafe {
let result: &[u64] = std::slice::from_raw_parts(data.as_ptr() as *const u64, count);
out.copy_from_slice(result);
}
drop(data);
staging_buffer.unmap();
let num_increments = (count as u64 + 1) / 2;
let (new_c0, overflow) = self.c[0].overflowing_add(num_increments);
self.c[0] = new_c0;
if overflow {
self.c[1] = self.c[1].wrapping_add(1);
}
}
}
#[inline]
pub fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
let index = self.randi(0, choices.len() as i64 - 1) as usize;
&choices[index]
}
}
impl Rng64 for Philox64 {
#[inline]
fn randi(&mut self, min: i64, max: i64) -> i64 {
self.randi(min, max)
}
#[inline]
fn randf(&mut self, min: f64, max: f64) -> f64 {
self.randf(min, max)
}
#[inline]
fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
self.choice(choices)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_new(seed1: u64, seed2: u64) -> *mut Philox64 {
Box::into_raw(Box::new(Philox64::new([seed1, seed2])))
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_warm(ptr: *mut Philox64, count: usize) {
unsafe {
let rng = &mut *ptr;
rng.warm(count);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_free(ptr: *mut Philox64) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) }
}
}
#[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);
let mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox64::chunk_size());
buffer[i..i + take].copy_from_slice(&chunk[..take]);
i += take;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn philox64_next_u64s_gpu(ptr: *mut Philox64, out: *mut u64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
rng.next_u64s_gpu(buffer);
}
}
#[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);
let mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox64::chunk_size());
for j in 0..take {
buffer[i + j] = chunk[j] as f64 * (1.0 / (u64::MAX as f64 + 1.0));
}
i += take;
}
}
}
#[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 mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox64::chunk_size());
for j in 0..take {
let range = (max as i128 - min as i128 + 1) as u128;
buffer[i + j] = ((chunk[j] as u128 * range) >> 64) as i64 + min;
}
i += take;
}
}
}
#[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 mut i = 0;
while i < count {
let chunk = rng.nextu();
let take = (count - i).min(Philox64::chunk_size());
for j in 0..take {
let val_01 = chunk[j] as f64 * (1.0 / (u64::MAX as f64 + 1.0));
buffer[i + j] = val_01 * (max - min) + min;
}
i += take;
}
}
}
#[repr(C)]
pub struct Sfc64 {
a: u64,
b: u64,
c: u64,
counter: u64,
}
impl Sfc64 {
pub fn new(seed: u64) -> Self {
Self {
a: seed,
b: seed.wrapping_add(E.to_bits()),
c: seed.wrapping_add(PI.to_bits()),
counter: 0,
}
}
#[inline]
pub fn nextu(&mut self) -> u64 {
let res = self.a.wrapping_add(self.b).wrapping_add(self.counter);
self.a = self.b ^ (self.b >> 11);
self.b = self.c.wrapping_add(self.c << 3);
self.c = res.rotate_left(24);
self.counter = self.counter.wrapping_add(1);
res
}
#[inline]
pub fn nextf(&mut self) -> f64 {
self.nextu() as f64 * (1.0 / (u64::MAX as f64 + 1.0))
}
#[inline]
pub fn randi(&mut self, min: i64, max: i64) -> i64 {
let range = (max as i128 - min as i128 + 1) as u128;
let x = self.nextu();
((x as u128 * range) >> 64) as i64 + min
}
#[inline]
pub fn randf(&mut self, min: f64, max: f64) -> f64 {
let range = max - min;
let scale = range * (1.0 / (u64::MAX as f64 + 1.0));
(self.nextu() as f64 * scale) + min
}
#[inline]
pub fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
let index = self.randi(0, choices.len() as i64 - 1);
&choices[index as usize]
}
}
impl Rng64 for Sfc64 {
#[inline]
fn randi(&mut self, min: i64, max: i64) -> i64 {
self.randi(min, max)
}
#[inline]
fn randf(&mut self, min: f64, max: f64) -> f64 {
self.randf(min, max)
}
#[inline]
fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
self.choice(choices)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn sfc64_new(seed: u64) -> *mut Sfc64 {
Box::into_raw(Box::new(Sfc64::new(seed)))
}
#[unsafe(no_mangle)]
pub extern "C" fn sfc64_free(ptr: *mut Sfc64) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn sfc64_next_u64s(ptr: *mut Sfc64, out: *mut u64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextu();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn sfc64_next_f64s(ptr: *mut Sfc64, out: *mut f64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextf();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn sfc64_rand_i64s(
ptr: *mut Sfc64,
out: *mut i64,
count: usize,
min: i64,
max: i64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randi(min, max);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn sfc64_rand_f64s(
ptr: *mut Sfc64,
out: *mut f64,
count: usize,
min: f64,
max: f64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randf(min, max);
}
}
}
#[repr(C)]
pub struct Xorshift64 {
a: u64,
}
impl Xorshift64 {
pub fn new(seed: u64) -> Self {
Self { a: seed }
}
#[inline]
pub fn nextu(&mut self) -> u64 {
let mut x = self.a;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.a = x;
x
}
#[inline]
pub fn nextf(&mut self) -> f64 {
self.nextu() as f64 * (1.0 / (u64::MAX as f64 + 1.0))
}
#[inline]
pub fn randi(&mut self, min: i64, max: i64) -> i64 {
let range = (max as i128 - min as i128 + 1) as u128;
let x = self.nextu();
((x as u128 * range) >> 64) as i64 + min
}
#[inline]
pub fn randf(&mut self, min: f64, max: f64) -> f64 {
let range = max - min;
let scale = range * (1.0 / (u64::MAX as f64 + 1.0));
(self.nextu() as f64 * scale) + min
}
#[inline]
pub fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
let index = self.randi(0, choices.len() as i64 - 1);
&choices[index as usize]
}
}
impl Rng64 for Xorshift64 {
#[inline]
fn randi(&mut self, min: i64, max: i64) -> i64 {
self.randi(min, max)
}
#[inline]
fn randf(&mut self, min: f64, max: f64) -> f64 {
self.randf(min, max)
}
#[inline]
fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
self.choice(choices)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn xorshift64_new(seed: u64) -> *mut Xorshift64 {
Box::into_raw(Box::new(Xorshift64::new(seed)))
}
#[unsafe(no_mangle)]
pub extern "C" fn xorshift64_free(ptr: *mut Xorshift64) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn xorshift64_next_u64s(ptr: *mut Xorshift64, out: *mut u64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextu();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn xorshift64_next_f64s(ptr: *mut Xorshift64, out: *mut f64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextf();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn xorshift64_rand_i64s(
ptr: *mut Xorshift64,
out: *mut i64,
count: usize,
min: i64,
max: i64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randi(min, max);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn xorshift64_rand_f64s(
ptr: *mut Xorshift64,
out: *mut f64,
count: usize,
min: f64,
max: f64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randf(min, max);
}
}
}
#[repr(C)]
pub struct Cet64 {
k: Wrapping<u64>,
v: [Wrapping<u64>; 4],
c: Wrapping<u64>,
}
impl Cet64 {
pub fn new(seed: u64) -> Self {
let k = seed | 1;
Self {
k: wrap!(k),
v: [
wrap!(seed),
wrap!(k),
wrap!(k.reverse_bits() | 1),
wrap!(seed.rotate_left(64)),
],
c: wrap!(1327),
}
}
#[inline]
pub fn nextu(&mut self) -> u64 {
let [mut a, mut b, mut c, mut d] = self.v;
a += a.0.rotate_left(13).wrapping_mul(self.k.0);
c += c.0.rotate_left(27) ^ self.c.0;
b ^= a.0.rotate_left(32);
d ^= c.0.rotate_left(32);
self.c += 1327;
self.v = [a, b, c, d];
(((a ^ b) ^ (c ^ d)) ^ wrap!(182)).0
}
#[inline]
pub fn nextf(&mut self) -> f64 {
self.nextu() as f64 * (1.0 / (u64::MAX as f64 + 1.0))
}
#[inline]
pub fn randi(&mut self, min: i64, max: i64) -> i64 {
let range = (max as i128 - min as i128 + 1) as u128;
let x = self.nextu();
((x as u128 * range) >> 64) as i64 + min
}
#[inline]
pub fn randf(&mut self, min: f64, max: f64) -> f64 {
let range = max - min;
let scale = range * (1.0 / (u64::MAX as f64 + 1.0));
(self.nextu() as f64 * scale) + min
}
#[inline]
pub fn choice<'a, T>(&mut self, choices: &'a [T]) -> &'a T {
let index = self.randi(0, choices.len() as i64 - 1);
&choices[index as usize]
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cet64_new(seed: u64) -> *mut Cet64 {
Box::into_raw(Box::new(Cet64::new(seed)))
}
#[unsafe(no_mangle)]
pub extern "C" fn cet64_free(ptr: *mut Cet64) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cet64_next_u64s(ptr: *mut Cet64, out: *mut u64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextu();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cet64_next_f64s(ptr: *mut Cet64, out: *mut f64, count: usize) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.nextf();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cet64_rand_i64s(
ptr: *mut Cet64,
out: *mut i64,
count: usize,
min: i64,
max: i64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randi(min, max);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cet64_rand_f64s(
ptr: *mut Cet64,
out: *mut f64,
count: usize,
min: f64,
max: f64,
) {
unsafe {
let rng = &mut *ptr;
let buffer = from_raw_parts_mut(out, count);
for v in buffer {
*v = rng.randf(min, max);
}
}
}
#[repr(C)]
pub struct Cet364 {
k: Wrapping<u64>,
v: [Wrapping<u64>; 3],
c: Wrapping<u64>,
}
impl Cet364 {
pub fn new(seed: u64) -> Self {
let k = seed | 1;
Self {
k: wrap!(k),
v: [wrap!(seed), wrap!(k), wrap!(k.reverse_bits() | 1)],
c: wrap!(1327),
}
}
#[inline]
pub fn nextu(&mut self) -> u64 {
let [mut a, mut b, mut c] = self.v;
a = (a + self.c) * self.k;
b ^= c.0.rotate_left(32);
c += 1327;
self.c += 1327;
self.v = [a, b, c];
a.0 ^ b.0 ^ c.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mt1993764_works() {
let mut rng = Mt1993764::new(1, 1024);
assert_eq!(rng.nextu(), 4804558718269354394);
assert_eq!(rng.nextf(), 0.6894973013138909);
}
#[test]
fn philox64_works() {
let mut rng = Philox64::new([1, 2]);
assert_eq!(rng.nextu(), [15183679468541472402, 0]);
assert_eq!(rng.nextf(), 0.6462178266116214);
}
#[test]
fn xorshift64_works() {
let mut rng = Xorshift64::new(1);
assert_eq!(rng.nextu(), 1082269761);
assert_eq!(rng.nextf(), 0.06250387570981203);
}
#[test]
fn sfc64_works() {
let mut rng = Sfc64::new(1);
let val1 = rng.nextu();
let val2 = rng.nextf();
assert_eq!(val1, 4613303445314885483);
assert_eq!(val2, 0.5014639839943512);
}
#[test]
fn cet64_works() {
let mut rng = Cet64::new(1);
let val1 = rng.nextu();
assert_eq!(val1, 5981625010123571202);
}
}