use wrapn::{wrap, wu32};
use crate::{Rng, SplitMix32};
#[repr(C, align(64))]
#[derive(Debug, Clone, Copy)]
pub struct Jsf32 {
pub(crate) a: wu32,
pub(crate) b: wu32,
pub(crate) c: wu32,
pub(crate) d: wu32,
}
impl Jsf32 {
pub const fn new(seed: u32) -> Self {
let mut seedgen = SplitMix32::new(seed);
Self {
a: wrap!(0xf1ea5eed),
b: wrap!(seedgen.nextu_const()),
c: wrap!(seedgen.nextu_const()),
d: wrap!(seedgen.nextu_const()),
}
}
}
impl Rng for Jsf32 {
type Word = u32;
#[inline(always)]
fn nextu(&mut self) -> Self::Word {
let e = self.a - self.b.rotate_left(27);
self.a = self.b ^ self.c.rotate_left(17);
self.b = self.c + self.d;
self.c = self.d + e;
self.d = e + self.a;
*self.d
}
}
#[cfg(feature = "simd")]
pub use simd::*;
#[cfg(feature = "simd")]
pub mod simd {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use crate::{RngV, SplitMix32};
#[repr(C, align(64))]
pub struct Jsf32x8 {
pub(crate) a: __m256i,
pub(crate) b: __m256i,
pub(crate) c: __m256i,
pub(crate) d: __m256i,
}
pub(crate) const JSF32X8: usize = 8;
impl Jsf32x8 {
#[target_feature(enable = "avx2")]
pub fn new(seed: u32) -> Self {
let mut seedgen = SplitMix32::new(seed);
let mut sv = [[0u32; JSF32X8]; 3];
for vals in sv.iter_mut() {
for v in vals.iter_mut() {
*v = seedgen.nextu_const();
}
}
unsafe {
Self {
a: _mm256_set1_epi32(0xf1ea5eed_u32 as i32),
b: _mm256_loadu_si256(sv[0].as_ptr() as *const __m256i),
c: _mm256_loadu_si256(sv[1].as_ptr() as *const __m256i),
d: _mm256_loadu_si256(sv[2].as_ptr() as *const __m256i),
}
}
}
}
impl RngV for Jsf32x8 {
type Word = __m256i;
#[inline]
fn nextuv(&mut self) -> __m256i {
unsafe {
let e = _mm256_sub_epi32(self.a, _mm256_rol_epi32(self.b, 27));
self.a = _mm256_xor_si256(self.b, _mm256_rol_epi32(self.c, 17));
self.b = _mm256_add_epi32(self.c, self.d);
self.c = _mm256_add_epi32(self.d, e);
self.d = _mm256_add_epi32(e, self.a);
self.d
}
}
}
#[repr(C, align(64))]
pub struct Jsf32x16 {
pub(crate) a: __m512i,
pub(crate) b: __m512i,
pub(crate) c: __m512i,
pub(crate) d: __m512i,
}
pub(crate) const JSF32X16: usize = 16;
impl Jsf32x16 {
#[target_feature(enable = "avx512f")]
pub fn new(seed: u32) -> Self {
let mut seedgen = SplitMix32::new(seed);
let mut sv = [[0u32; JSF32X16]; 3];
for vals in sv.iter_mut() {
for v in vals.iter_mut() {
*v = seedgen.nextu_const();
}
}
const A: [u32; JSF32X16] = [0xf1ea5eedu32; JSF32X16];
unsafe {
Self {
a: _mm512_loadu_si512(A.as_ptr() as _),
b: _mm512_loadu_si512(sv[0].as_ptr() as _),
c: _mm512_loadu_si512(sv[1].as_ptr() as _),
d: _mm512_loadu_si512(sv[2].as_ptr() as _),
}
}
}
}
impl RngV for Jsf32x16 {
type Word = __m512i;
fn nextuv(&mut self) -> __m512i {
unsafe {
let e = _mm512_sub_epi32(self.a, _mm512_rol_epi32(self.b, 27));
self.a = _mm512_xor_si512(self.b, _mm512_rol_epi32(self.c, 17));
self.b = _mm512_add_epi32(self.c, self.d);
self.c = _mm512_add_epi32(self.d, e);
self.d = _mm512_add_epi32(e, self.a);
self.d
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
crate::safe_test! { Jsf32 }
#[cfg(all(feature = "simd", target_feature = "avx2"))]
crate::unsafe_test! { Jsf32x8 }
#[cfg(all(feature = "simd", target_feature = "avx512f"))]
crate::unsafe_test! { Jsf32x16 }
}