From 64a84e1fde349ead283d99b5740c733ac8bdb9b7 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 12:02:52 +0900 Subject: [PATCH 01/12] Add other params support. --- src/lib.rs | 44 ++++-- src/sfmt.rs | 389 ++++++++++++++++++++++++++++++++++------------ src/thread_rng.rs | 4 +- tests/sfmt.rs | 2 +- 4 files changed, 325 insertions(+), 114 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 69ea4a6..9a6cb90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! //! ``` //! use rand_core::{RngCore, SeedableRng}; -//! let mut rng = sfmt::SFMT::seed_from_u64(42); +//! let mut rng = sfmt::SFMT::<19937>::seed_from_u64(42); //! let r = rng.next_u32(); //! println!("random u32 number = {}", r); //! ``` @@ -14,25 +14,33 @@ mod packed; mod sfmt; #[cfg(feature = "thread_rng")] mod thread_rng; - +pub use crate::sfmt::SfmtParam; +use crate::sfmt::SfmtParams; use rand_core::{impls, Error, RngCore, SeedableRng}; use self::packed::*; #[cfg(feature = "thread_rng")] pub use self::thread_rng::{thread_rng, ThreadRng}; +/// State of SFMT +/// +/// This struct implements random number generation through `rand::Rng`. + /// State of SFMT /// /// This struct implements random number generation through `rand::Rng`. #[derive(Clone)] -pub struct SFMT { +pub struct SFMT { /// the 128-bit internal state array - state: [i32x4; sfmt::SFMT_N], + pub(crate) state: [i32x4; N], /// index counter to the 32-bit internal state array - idx: usize, + pub(crate) idx: usize, } -impl SFMT { +impl SFMT +where + SfmtParam: SfmtParams, +{ fn pop32(&mut self) -> u32 { let val = extract(self.state[self.idx / 4], self.idx % 4); self.idx += 1; @@ -50,35 +58,41 @@ impl SFMT { } fn gen_all(&mut self) { - sfmt::sfmt_gen_rand_all(self); + SfmtParam::::sfmt_gen_rand_all(self); self.idx = 0; } } -impl SeedableRng for SFMT { +impl SeedableRng for SFMT +where + SfmtParam: SfmtParams, +{ type Seed = [u8; 4]; fn from_seed(seed: [u8; 4]) -> Self { let mut sfmt = SFMT { - state: [zero(); sfmt::SFMT_N], + state: [zero(); N], idx: 0, }; let seed = unsafe { *(seed.as_ptr() as *const u32) }; - sfmt::sfmt_init_gen_rand(&mut sfmt, seed); + SfmtParam::::sfmt_init_gen_rand(&mut sfmt, seed); sfmt } } -impl RngCore for SFMT { +impl RngCore for SFMT +where + SfmtParam: SfmtParams, +{ fn next_u32(&mut self) -> u32 { - if self.idx >= sfmt::SFMT_N32 { + if self.idx >= SfmtParam::::SFMT_N32 { self.gen_all(); } self.pop32() } fn next_u64(&mut self) -> u64 { - if self.idx >= sfmt::SFMT_N32 - 1 { + if self.idx >= SfmtParam::::SFMT_N32 - 1 { // drop last u32 if idx == N32-1 self.gen_all(); } @@ -101,8 +115,8 @@ mod tests { #[test] fn random() { - let mut rng = SFMT::seed_from_u64(0); - for _ in 0..sfmt::SFMT_N * 20 { + let mut rng = SFMT::<19937>::seed_from_u64(0); + for _ in 0..19937 * 20 { // Generate many random numbers to test the overwrap let r = rng.next_u64(); if r % 2 == 0 { diff --git a/src/sfmt.rs b/src/sfmt.rs index 31aa45e..7d1086b 100644 --- a/src/sfmt.rs +++ b/src/sfmt.rs @@ -3,112 +3,309 @@ use super::*; use crate::packed::*; -const SFMT_MEXP: usize = 19937; -pub const SFMT_N: usize = SFMT_MEXP / 128 + 1; // = 156 -pub const SFMT_N32: usize = SFMT_N * 4; - -const SFMT_POS1: usize = 122; -const SFMT_SL1: i32 = 18; -const SFMT_SL2: i32 = 1; -const SFMT_SR1: i32 = 11; -const SFMT_SR2: i32 = 1; -const SFMT_MSK1: i32 = 0xdfffffef_u32 as i32; -const SFMT_MSK2: i32 = 0xddfecb7f_u32 as i32; -const SFMT_MSK3: i32 = 0xbffaffff_u32 as i32; -const SFMT_MSK4: i32 = 0xbffffff6_u32 as i32; -const SFMT_PARITY1: u32 = 0x00000001; -const SFMT_PARITY2: u32 = 0x00000000; -const SFMT_PARITY3: u32 = 0x00000000; -const SFMT_PARITY4: u32 = 0x13c9e684; - -fn mm_recursion(a: i32x4, b: i32x4, c: i32x4, d: i32x4) -> i32x4 { - #[cfg(target_arch = "x86")] - use std::arch::x86::*; - #[cfg(target_arch = "x86_64")] - use std::arch::x86_64::*; - - unsafe { - let mask = new(SFMT_MSK1, SFMT_MSK2, SFMT_MSK3, SFMT_MSK4); - let y = _mm_srli_epi32(b, SFMT_SR1); - let z = _mm_srli_si128(c, SFMT_SR2); - let v = _mm_slli_epi32(d, SFMT_SL1); - let z = _mm_xor_si128(z, a); - let z = _mm_xor_si128(z, v); - let x = _mm_slli_si128(a, SFMT_SL2); - let y = _mm_and_si128(y, mask); - let z = _mm_xor_si128(z, x); - _mm_xor_si128(z, y) - } -} +pub trait SfmtParams: Sized { + const SFMT_MEXP: usize = N; + const SFMT_N: usize = Self::SFMT_MEXP / 128 + 1; // = 156 + const SFMT_N32: usize = Self::SFMT_N * 4; + + const SFMT_POS1: usize; + const SFMT_SL1: i32; + const SFMT_SL2: i32; + const SFMT_SR1: i32; + const SFMT_SR2: i32; + const SFMT_MSK1: i32; + const SFMT_MSK2: i32; + const SFMT_MSK3: i32; + const SFMT_MSK4: i32; + const SFMT_PARITY1: u32; + const SFMT_PARITY2: u32; + const SFMT_PARITY3: u32; + const SFMT_PARITY4: u32; -pub fn sfmt_gen_rand_all(sfmt: &mut SFMT) { - let st = &mut sfmt.state; - let mut r1 = st[SFMT_N - 2]; - let mut r2 = st[SFMT_N - 1]; - for i in 0..(SFMT_N - SFMT_POS1) { - st[i] = mm_recursion(st[i], st[i + SFMT_POS1], r1, r2); - r1 = r2; - r2 = st[i]; + fn mm_recursion(a: i32x4, b: i32x4, c: i32x4, d: i32x4) -> i32x4 { + #[cfg(target_arch = "x86")] + use std::arch::x86::*; + #[cfg(target_arch = "x86_64")] + use std::arch::x86_64::*; + + unsafe { + let mask = new( + Self::SFMT_MSK1, + Self::SFMT_MSK2, + Self::SFMT_MSK3, + Self::SFMT_MSK4, + ); + let y = _mm_srli_epi32(b, Self::SFMT_SR1); + let z = _mm_srli_si128(c, Self::SFMT_SR2); + let v = _mm_slli_epi32(d, Self::SFMT_SL1); + let z = _mm_xor_si128(z, a); + let z = _mm_xor_si128(z, v); + let x = _mm_slli_si128(a, Self::SFMT_SL2); + let y = _mm_and_si128(y, mask); + let z = _mm_xor_si128(z, x); + _mm_xor_si128(z, y) + } } - for i in (SFMT_N - SFMT_POS1)..SFMT_N { - st[i] = mm_recursion(st[i], st[i + SFMT_POS1 - SFMT_N], r1, r2); - r1 = r2; - r2 = st[i]; + + fn sfmt_gen_rand_all(sfmt: &mut SFMT) { + let st = &mut sfmt.state; + let mut r1 = st[Self::SFMT_N - 2]; + let mut r2 = st[Self::SFMT_N - 1]; + for i in 0..(Self::SFMT_N - Self::SFMT_POS1) { + st[i] = Self::mm_recursion(st[i], st[i + Self::SFMT_POS1], r1, r2); + r1 = r2; + r2 = st[i]; + } + for i in (Self::SFMT_N - Self::SFMT_POS1)..Self::SFMT_N { + st[i] = Self::mm_recursion(st[i], st[i + Self::SFMT_POS1 - Self::SFMT_N], r1, r2); + r1 = r2; + r2 = st[i]; + } } -} -pub fn period_certification(sfmt: &mut SFMT) { - let mut inner = 0_u32; - let st = &mut sfmt.state[0]; - let parity = [SFMT_PARITY1, SFMT_PARITY2, SFMT_PARITY3, SFMT_PARITY4]; - for i in 0..4 { - inner ^= extract(*st, i) & parity[i]; + fn period_certification(sfmt: &mut SFMT) { + let mut inner = 0_u32; + let st = &mut sfmt.state[0]; + let parity = [ + Self::SFMT_PARITY1, + Self::SFMT_PARITY2, + Self::SFMT_PARITY3, + Self::SFMT_PARITY4, + ]; + for i in 0..4 { + inner ^= extract(*st, i) & parity[i]; + } + for i in [16, 8, 4, 2, 1].iter() { + inner ^= inner >> i; + } + inner &= 1; + if inner == 1 { + return; + } + for i in 0..4 { + let mut work = 1_u32; + for _ in 0..32 { + if (work & parity[i]) != 0 { + let val = extract(*st, i) ^ work; + insert(st, val as i32, i); + return; + } + work = work << 1; + } + } } - for i in [16, 8, 4, 2, 1].iter() { - inner ^= inner >> i; + + fn iterate(pre: i32, i: i32) -> i32 { + use std::num::Wrapping; + let pre = Wrapping(pre as u32); + let i = Wrapping(i as u32); + (Wrapping(1812433253) * (pre ^ (pre >> 30)) + i).0 as i32 } - inner &= 1; - if inner == 1 { - return; + + fn map(a: i32, idx: i32) -> (i32x4, i32) { + let b = Self::iterate(a, 4 * idx + 1); + let c = Self::iterate(b, 4 * idx + 2); + let d = Self::iterate(c, 4 * idx + 3); + let a2 = Self::iterate(d, 4 * idx + 4); + (new(a, b, c, d), a2) } - for i in 0..4 { - let mut work = 1_u32; - for _ in 0..32 { - if (work & parity[i]) != 0 { - let val = extract(*st, i) ^ work; - insert(st, val as i32, i); - return; - } - work = work << 1; + + fn sfmt_init_gen_rand(sfmt: &mut SFMT, seed: u32) { + let mut pre = seed as i32; + for (idx, v) in sfmt.state.iter_mut().enumerate() { + let (v_, pre_) = Self::map(pre, idx as i32); + *v = v_; + pre = pre_; } + sfmt.idx = Self::SFMT_N32; + Self::period_certification(sfmt); } } -fn iterate(pre: i32, i: i32) -> i32 { - use std::num::Wrapping; - let pre = Wrapping(pre as u32); - let i = Wrapping(i as u32); - (Wrapping(1812433253) * (pre ^ (pre >> 30)) + i).0 as i32 -} +pub struct SfmtParam; -fn map(a: i32, idx: i32) -> (i32x4, i32) { - let b = iterate(a, 4 * idx + 1); - let c = iterate(b, 4 * idx + 2); - let d = iterate(c, 4 * idx + 3); - let a2 = iterate(d, 4 * idx + 4); - (new(a, b, c, d), a2) +macro_rules! parms_impl { + ($n : expr, $pos1 : expr, $sl1 : expr, $sl2 : expr, $sr1 : expr, $sr2 : expr, + $msk1 : expr, $msk2 : expr, $msk3 : expr, $msk4 : expr, + $parity1 : expr, $parity2 : expr, $parity3 : expr, $parity4 : expr) => { + impl SfmtParams<$n> for SfmtParam<$n> { + const SFMT_POS1: usize = $pos1; + const SFMT_SL1: i32 = $sl1; + const SFMT_SL2: i32 = $sl2; + const SFMT_SR1: i32 = $sr1; + const SFMT_SR2: i32 = $sr2; + const SFMT_MSK1: i32 = $msk1 as i32; + const SFMT_MSK2: i32 = $msk2 as i32; + const SFMT_MSK3: i32 = $msk3 as i32; + const SFMT_MSK4: i32 = $msk4 as i32; + const SFMT_PARITY1: u32 = $parity1; + const SFMT_PARITY2: u32 = $parity2; + const SFMT_PARITY3: u32 = $parity3; + const SFMT_PARITY4: u32 = $parity4; + } + }; } -pub fn sfmt_init_gen_rand(sfmt: &mut SFMT, seed: u32) { - let mut pre = seed as i32; - for (idx, v) in sfmt.state.iter_mut().enumerate() { - let (v_, pre_) = map(pre, idx as i32); - *v = v_; - pre = pre_; - } - sfmt.idx = SFMT_N32; - period_certification(sfmt); -} +parms_impl!( + 607, + 2, + 15, + 3, + 13, + 3, + 0xfdff_37ff_u32, + 0xef7f_3f7d_u32, + 0xff77_7b7d_u32, + 0x7ff7_fb2f_u32, + 0x0000_0001, + 0x0000_0000, + 0x0000_0000, + 0x5986_f054 +); +parms_impl!( + 1279, + 7, + 14, + 3, + 5, + 1, + 0xf7fe_fffd_u32, + 0x7fef_cfff_u32, + 0xaff3_ef3f_u32, + 0xb5ff_ff7f_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0x0000_0000_u32, + 0x2000_0000_u32 +); +parms_impl!( + 2281, + 12, + 19, + 1, + 5, + 1, + 0xbff7_ffbf_u32, + 0xfdff_fffe_u32, + 0xf7ffe_f7f_u32, + 0xf2f7_cbbf_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0x0000_0000_u32, + 0x41df_a600_u32 +); +parms_impl!( + 4253, + 17, + 20, + 1, + 7, + 1, + 0x9f7b_ffff_u32, + 0x9fff_ff5f_u32, + 0x3eff_fffb_u32, + 0xffff_f7bb_u32, + 0xa800_0001_u32, + 0xaf53_90a3_u32, + 0xb740_b3f8_u32, + 0x6c11_486d_u32 +); +parms_impl!( + 11213, + 68, + 14, + 3, + 7, + 3, + 0xefff_f7fb_u32, + 0xffff_ffef_u32, + 0xdfdf_bfff_u32, + 0x7fff_dbfd_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0xb740_b3f8_u32, + 0x6c11_486d_u32 +); +parms_impl!( + 19937, + 122, + 18, + 1, + 11, + 1, + 0xdfff_ffef_u32, + 0xddfe_cb7f_u32, + 0xbffa_ffff_u32, + 0xbfff_fff6_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0x0000_0000_u32, + 0x13c9_e684_u32 +); +parms_impl!( + 44497, + 330, + 5, + 3, + 9, + 3, + 0xefff_fffb_u32, + 0xdfbe_bfff_u32, + 0xbfbf_7bef_u32, + 0x9ffd_7bff_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0xa3ac_4000_u32, + 0xecc1_327a_u32 +); +parms_impl!( + 86243, + 366, + 6, + 7, + 19, + 1, + 0xfdbf_bff7_u32, + 0xfd77_efff_u32, + 0xfd77_efff_u32, + 0xbf9f_f3ff_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0x0000_0000_u32, + 0x3952_8d85_u32 +); +parms_impl!( + 132049, + 110, + 19, + 1, + 21, + 1, + 0xffff_bb5f_u32, + 0xfb6e_bf95_u32, + 0xfffe_fffa_u32, + 0xcff7_7fff_u32, + 0x0000_0001_u32, + 0x0000_0000_u32, + 0xcb52_0000_u32, + 0xc7e9_1c7d_u32 +); +parms_impl!( + 216091, + 627, + 11, + 3, + 10, + 1, + 0xbff7_bff7_u32, + 0xbfff_ffff_u32, + 0xbfff_fa7f_u32, + 0xffdd_fbfb_u32, + 0xf800_0001_u32, + 0x89e8_0709_u32, + 0x3bd2_b64b_u32, + 0x0c64_b1e4_u32 +); #[cfg(test)] mod tests { @@ -138,7 +335,7 @@ mod tests { fn test_init() { let seed: u32 = 1234; let seed = unsafe { *(&seed as *const u32 as *const [u8; 4]) }; - let sfmt = SFMT::from_seed(seed); + let sfmt = SFMT::<19937>::from_seed(seed); let ans = read_answer("check/init1234.txt").unwrap(); for (v, a) in sfmt.state.iter().zip(ans.iter()) { assert_eq!(split(*v), split(*a)); @@ -146,16 +343,16 @@ mod tests { } #[test] - fn test_mm_recursion() { + fn test_mm_recursion_19937() { let a = new(1, 2, 3, 4); - let z = mm_recursion(a, a, a, a); + let z = SfmtParam::<19937>::mm_recursion(a, a, a, a); let zc = new(33816833, 50856450, 67896067, 1049604); // calculated by C code assert_eq!(split(z), split(zc)); let b = new(431, 232, 83, 14); let c = new(213, 22, 93, 234); let d = new(112, 882, 23, 124); - let z = mm_recursion(a, b, c, d); + let z = SfmtParam::<19937>::mm_recursion(a, b, c, d); let zc = new(398459137, 1355284994, -363068669, 32506884); // calculated by C code assert_eq!(split(z), split(zc)); } diff --git a/src/thread_rng.rs b/src/thread_rng.rs index 85f5b25..c509d98 100644 --- a/src/thread_rng.rs +++ b/src/thread_rng.rs @@ -7,7 +7,7 @@ use std::cell::RefCell; use std::rc::Rc; thread_local!( - static THREAD_RNG_KEY: Rc> = { + static THREAD_RNG_KEY: Rc>> = { Rc::new(RefCell::new(SFMT::from_entropy())) } ); @@ -17,7 +17,7 @@ thread_local!( /// See the reference of the function [thread_rng](fn.thread_rng.html), which generates this struct. #[derive(Clone)] pub struct ThreadRng { - rng: Rc>, + rng: Rc>>, } /// Create a thread local RNG. diff --git a/tests/sfmt.rs b/tests/sfmt.rs index 1589e6f..b3188a4 100644 --- a/tests/sfmt.rs +++ b/tests/sfmt.rs @@ -17,7 +17,7 @@ fn gen_u32() { let ans = read_answer().expect("Failed to load answers"); let seed: u32 = 1234; let seed = unsafe { *(&seed as *const u32 as *const [u8; 4]) }; - let mut sfmt = SFMT::from_seed(seed); // 1234 = 0x4D2 + let mut sfmt = SFMT::<19937>::from_seed(seed); // 1234 = 0x4D2 for (t, val) in ans.into_iter().enumerate() { let r = sfmt.next_u32(); println!("[{}] gen = {}, ans = {}", t, r, val); From 2a27d8fb2a9ee27e20df10b0e1008f14fb44f4c1 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 12:26:06 +0900 Subject: [PATCH 02/12] Fix docs and rename SfmtParam to SfmtN. --- src/lib.rs | 23 +++++++++++------------ src/sfmt.rs | 11 ++++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9a6cb90..c381bc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ mod packed; mod sfmt; #[cfg(feature = "thread_rng")] mod thread_rng; -pub use crate::sfmt::SfmtParam; +use crate::sfmt::SfmtN; use crate::sfmt::SfmtParams; use rand_core::{impls, Error, RngCore, SeedableRng}; @@ -25,10 +25,9 @@ pub use self::thread_rng::{thread_rng, ThreadRng}; /// State of SFMT /// /// This struct implements random number generation through `rand::Rng`. - -/// State of SFMT -/// -/// This struct implements random number generation through `rand::Rng`. +/// The N is a parameter that defines a length of state. +/// N is limted to be a known value, and it is checked at compile time. +/// N can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. #[derive(Clone)] pub struct SFMT { /// the 128-bit internal state array @@ -39,7 +38,7 @@ pub struct SFMT { impl SFMT where - SfmtParam: SfmtParams, + SfmtN: SfmtParams, { fn pop32(&mut self) -> u32 { let val = extract(self.state[self.idx / 4], self.idx % 4); @@ -58,14 +57,14 @@ where } fn gen_all(&mut self) { - SfmtParam::::sfmt_gen_rand_all(self); + SfmtN::::sfmt_gen_rand_all(self); self.idx = 0; } } impl SeedableRng for SFMT where - SfmtParam: SfmtParams, + SfmtN: SfmtParams, { type Seed = [u8; 4]; @@ -75,24 +74,24 @@ where idx: 0, }; let seed = unsafe { *(seed.as_ptr() as *const u32) }; - SfmtParam::::sfmt_init_gen_rand(&mut sfmt, seed); + SfmtN::::sfmt_init_gen_rand(&mut sfmt, seed); sfmt } } impl RngCore for SFMT where - SfmtParam: SfmtParams, + SfmtN: SfmtParams, { fn next_u32(&mut self) -> u32 { - if self.idx >= SfmtParam::::SFMT_N32 { + if self.idx >= SfmtN::::SFMT_N32 { self.gen_all(); } self.pop32() } fn next_u64(&mut self) -> u64 { - if self.idx >= SfmtParam::::SFMT_N32 - 1 { + if self.idx >= SfmtN::::SFMT_N32 - 1 { // drop last u32 if idx == N32-1 self.gen_all(); } diff --git a/src/sfmt.rs b/src/sfmt.rs index 7d1086b..9821951 100644 --- a/src/sfmt.rs +++ b/src/sfmt.rs @@ -3,6 +3,7 @@ use super::*; use crate::packed::*; +/// Parameters used in sfmt. pub trait SfmtParams: Sized { const SFMT_MEXP: usize = N; const SFMT_N: usize = Self::SFMT_MEXP / 128 + 1; // = 156 @@ -121,14 +122,14 @@ pub trait SfmtParams: Sized { Self::period_certification(sfmt); } } - -pub struct SfmtParam; +/// Wrapper for `N` parameter. +pub struct SfmtN; macro_rules! parms_impl { ($n : expr, $pos1 : expr, $sl1 : expr, $sl2 : expr, $sr1 : expr, $sr2 : expr, $msk1 : expr, $msk2 : expr, $msk3 : expr, $msk4 : expr, $parity1 : expr, $parity2 : expr, $parity3 : expr, $parity4 : expr) => { - impl SfmtParams<$n> for SfmtParam<$n> { + impl SfmtParams<$n> for SfmtN<$n> { const SFMT_POS1: usize = $pos1; const SFMT_SL1: i32 = $sl1; const SFMT_SL2: i32 = $sl2; @@ -345,14 +346,14 @@ mod tests { #[test] fn test_mm_recursion_19937() { let a = new(1, 2, 3, 4); - let z = SfmtParam::<19937>::mm_recursion(a, a, a, a); + let z = SfmtN::<19937>::mm_recursion(a, a, a, a); let zc = new(33816833, 50856450, 67896067, 1049604); // calculated by C code assert_eq!(split(z), split(zc)); let b = new(431, 232, 83, 14); let c = new(213, 22, 93, 234); let d = new(112, 882, 23, 124); - let z = SfmtParam::<19937>::mm_recursion(a, b, c, d); + let z = SfmtN::<19937>::mm_recursion(a, b, c, d); let zc = new(398459137, 1355284994, -363068669, 32506884); // calculated by C code assert_eq!(split(z), split(zc)); } From 2e92746ad694a7535467a1b2caf1d6aa2f4f57e8 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 12:36:32 +0900 Subject: [PATCH 03/12] update rand_core to 0.6 --- Cargo.toml | 10 +++++++--- benches/rand_gen.rs | 10 ++++++---- src/thread_rng.rs | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9d5321..6507995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,12 @@ license = "MIT" default = ["thread_rng"] # The thread_rng feature requires the rand dependency -thread_rng = ["rand"] +thread_rng = ["rand/getrandom"] [dependencies] -rand_core = "0.5" -rand = {version = "0.7", optional = true} +rand_core = "0.6" +rand = {version = "0.8", optional = true} + +[dev-dependencies] +rand_xorshift = "0.3.0" +rand_core = {version = "0.6", features=["getrandom"]} \ No newline at end of file diff --git a/benches/rand_gen.rs b/benches/rand_gen.rs index bcbb2cd..ef1e652 100644 --- a/benches/rand_gen.rs +++ b/benches/rand_gen.rs @@ -5,6 +5,8 @@ extern crate sfmt; extern crate test; use rand::*; +use rand_core::SeedableRng; +use rand_xorshift::*; use sfmt::SFMT; use test::Bencher; @@ -25,23 +27,23 @@ macro_rules! def_bench { mod gen_f64 { use super::*; def_bench!(xorshift, f64, XorShiftRng::from_entropy()); - def_bench!(sfmt, f64, SFMT::from_entropy()); + def_bench!(sfmt, f64, SFMT::<19937>::from_entropy()); } mod gen_f32 { use super::*; def_bench!(xorshift, f32, XorShiftRng::from_entropy()); - def_bench!(sfmt, f32, SFMT::from_entropy()); + def_bench!(sfmt, f32, SFMT::<19937>::from_entropy()); } mod gen_u64 { use super::*; def_bench!(xorshift, u64, XorShiftRng::from_entropy()); - def_bench!(sfmt, u64, SFMT::from_entropy()); + def_bench!(sfmt, u64, SFMT::<19937>::from_entropy()); } mod gen_u32 { use super::*; def_bench!(xorshift, u32, XorShiftRng::from_entropy()); - def_bench!(sfmt, u32, SFMT::from_entropy()); + def_bench!(sfmt, u32, SFMT::<19937>::from_entropy()); } diff --git a/src/thread_rng.rs b/src/thread_rng.rs index c509d98..9c28fbe 100644 --- a/src/thread_rng.rs +++ b/src/thread_rng.rs @@ -2,7 +2,7 @@ use super::SFMT; -use rand::{Error, RngCore, SeedableRng}; +use rand_core::{Error, RngCore, SeedableRng}; use std::cell::RefCell; use std::rc::Rc; From 4d195144f4f6bf6eea2be4b8bf33bdc6dc201fc4 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 15:20:22 +0900 Subject: [PATCH 04/12] Fix not to be breaking change. --- benches/rand_gen.rs | 8 +- src/lib.rs | 173 +++++++++++++++++++++++++------------------- src/sfmt.rs | 8 +- src/thread_rng.rs | 4 +- tests/sfmt.rs | 2 +- 5 files changed, 109 insertions(+), 86 deletions(-) diff --git a/benches/rand_gen.rs b/benches/rand_gen.rs index ef1e652..61e03b8 100644 --- a/benches/rand_gen.rs +++ b/benches/rand_gen.rs @@ -27,23 +27,23 @@ macro_rules! def_bench { mod gen_f64 { use super::*; def_bench!(xorshift, f64, XorShiftRng::from_entropy()); - def_bench!(sfmt, f64, SFMT::<19937>::from_entropy()); + def_bench!(sfmt, f64, SFMT::from_entropy()); } mod gen_f32 { use super::*; def_bench!(xorshift, f32, XorShiftRng::from_entropy()); - def_bench!(sfmt, f32, SFMT::<19937>::from_entropy()); + def_bench!(sfmt, f32, SFMT::from_entropy()); } mod gen_u64 { use super::*; def_bench!(xorshift, u64, XorShiftRng::from_entropy()); - def_bench!(sfmt, u64, SFMT::<19937>::from_entropy()); + def_bench!(sfmt, u64, SFMT::from_entropy()); } mod gen_u32 { use super::*; def_bench!(xorshift, u32, XorShiftRng::from_entropy()); - def_bench!(sfmt, u32, SFMT::<19937>::from_entropy()); + def_bench!(sfmt, u32, SFMT::from_entropy()); } diff --git a/src/lib.rs b/src/lib.rs index c381bc9..24b7fa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! //! ``` //! use rand_core::{RngCore, SeedableRng}; -//! let mut rng = sfmt::SFMT::<19937>::seed_from_u64(42); +//! let mut rng = sfmt::SFMT19937::seed_from_u64(42); //! let r = rng.next_u32(); //! println!("random u32 number = {}", r); //! ``` @@ -14,99 +14,122 @@ mod packed; mod sfmt; #[cfg(feature = "thread_rng")] mod thread_rng; -use crate::sfmt::SfmtN; -use crate::sfmt::SfmtParams; -use rand_core::{impls, Error, RngCore, SeedableRng}; -use self::packed::*; #[cfg(feature = "thread_rng")] pub use self::thread_rng::{thread_rng, ThreadRng}; -/// State of SFMT -/// -/// This struct implements random number generation through `rand::Rng`. -/// The N is a parameter that defines a length of state. -/// N is limted to be a known value, and it is checked at compile time. -/// N can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. -#[derive(Clone)] -pub struct SFMT { - /// the 128-bit internal state array - pub(crate) state: [i32x4; N], - /// index counter to the 32-bit internal state array - pub(crate) idx: usize, -} +pub type SFMT = SFMT19937; +pub type SFMT607 = paramed::SFMT<607>; +pub type SFMT1279 = paramed::SFMT<1279>; +pub type SFMT2281 = paramed::SFMT<2281>; +pub type SFMT4253 = paramed::SFMT<4253>; +pub type SFMT11213 = paramed::SFMT<11213>; +pub type SFMT19937 = paramed::SFMT<19937>; +pub type SFMT44497 = paramed::SFMT<44497>; +pub type SFMT86243 = paramed::SFMT<86243>; +pub type SFMT132049 = paramed::SFMT<132049>; +pub type SFMT216091 = paramed::SFMT<216091>; -impl SFMT -where - SfmtN: SfmtParams, -{ - fn pop32(&mut self) -> u32 { - let val = extract(self.state[self.idx / 4], self.idx % 4); - self.idx += 1; - val - } +pub mod paramed { + use crate::{ + packed::*, + sfmt::{SfmtN, SfmtParams}, + }; + use rand_core::{impls, Error, RngCore, SeedableRng}; - fn pop64(&mut self) -> u64 { - let p = self.state.as_ptr() as *const u32; - let val = unsafe { - let p = p.offset(self.idx as isize); - *(p as *const u64) // reinterpret cast [u32; 2] -> u64 - }; - self.idx += 2; - val + /// State of SFMT + /// + /// This struct implements random number generation through `rand::Rng`. + /// The N is a parameter that defines a length of state. + /// N is limted to be a known value, and it is checked at compile time. + /// N can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. + /// ``` + /// # use rand_core::SeedableRng; + /// let s = sfmt::paramed::SFMT::<19937>::seed_from_u64(23); + /// ``` + /// ```compile_fail + /// # use rand_core::SeedableRng; + /// let s = sfmt::paramed::SFMT::<2>::seed_from_u64(23); + /// ``` + #[derive(Clone)] + pub struct SFMT { + /// the 128-bit internal state array + pub(crate) state: [i32x4; N], + /// index counter to the 32-bit internal state array + pub(crate) idx: usize, } - fn gen_all(&mut self) { - SfmtN::::sfmt_gen_rand_all(self); - self.idx = 0; - } -} + impl SFMT + where + SfmtN: SfmtParams, + { + fn pop32(&mut self) -> u32 { + let val = extract(self.state[self.idx / 4], self.idx % 4); + self.idx += 1; + val + } -impl SeedableRng for SFMT -where - SfmtN: SfmtParams, -{ - type Seed = [u8; 4]; + fn pop64(&mut self) -> u64 { + let p = self.state.as_ptr() as *const u32; + let val = unsafe { + let p = p.offset(self.idx as isize); + *(p as *const u64) // reinterpret cast [u32; 2] -> u64 + }; + self.idx += 2; + val + } - fn from_seed(seed: [u8; 4]) -> Self { - let mut sfmt = SFMT { - state: [zero(); N], - idx: 0, - }; - let seed = unsafe { *(seed.as_ptr() as *const u32) }; - SfmtN::::sfmt_init_gen_rand(&mut sfmt, seed); - sfmt + fn gen_all(&mut self) { + SfmtN::::sfmt_gen_rand_all(self); + self.idx = 0; + } } -} -impl RngCore for SFMT -where - SfmtN: SfmtParams, -{ - fn next_u32(&mut self) -> u32 { - if self.idx >= SfmtN::::SFMT_N32 { - self.gen_all(); + impl SeedableRng for SFMT + where + SfmtN: SfmtParams, + { + type Seed = [u8; 4]; + + fn from_seed(seed: [u8; 4]) -> Self { + let mut sfmt = SFMT { + state: [zero(); N], + idx: 0, + }; + let seed = unsafe { *(seed.as_ptr() as *const u32) }; + SfmtN::::sfmt_init_gen_rand(&mut sfmt, seed); + sfmt } - self.pop32() } - fn next_u64(&mut self) -> u64 { - if self.idx >= SfmtN::::SFMT_N32 - 1 { - // drop last u32 if idx == N32-1 - self.gen_all(); + impl RngCore for SFMT + where + SfmtN: SfmtParams, + { + fn next_u32(&mut self) -> u32 { + if self.idx >= SfmtN::::SFMT_N32 { + self.gen_all(); + } + self.pop32() } - self.pop64() - } - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_next(self, dest) - } + fn next_u64(&mut self) -> u64 { + if self.idx >= SfmtN::::SFMT_N32 - 1 { + // drop last u32 if idx == N32-1 + self.gen_all(); + } + self.pop64() + } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } } - #[cfg(test)] mod tests { use super::*; @@ -114,7 +137,7 @@ mod tests { #[test] fn random() { - let mut rng = SFMT::<19937>::seed_from_u64(0); + let mut rng = SFMT::seed_from_u64(0); for _ in 0..19937 * 20 { // Generate many random numbers to test the overwrap let r = rng.next_u64(); diff --git a/src/sfmt.rs b/src/sfmt.rs index 9821951..86cf00d 100644 --- a/src/sfmt.rs +++ b/src/sfmt.rs @@ -48,7 +48,7 @@ pub trait SfmtParams: Sized { } } - fn sfmt_gen_rand_all(sfmt: &mut SFMT) { + fn sfmt_gen_rand_all(sfmt: &mut paramed::SFMT) { let st = &mut sfmt.state; let mut r1 = st[Self::SFMT_N - 2]; let mut r2 = st[Self::SFMT_N - 1]; @@ -64,7 +64,7 @@ pub trait SfmtParams: Sized { } } - fn period_certification(sfmt: &mut SFMT) { + fn period_certification(sfmt: &mut paramed::SFMT) { let mut inner = 0_u32; let st = &mut sfmt.state[0]; let parity = [ @@ -111,7 +111,7 @@ pub trait SfmtParams: Sized { (new(a, b, c, d), a2) } - fn sfmt_init_gen_rand(sfmt: &mut SFMT, seed: u32) { + fn sfmt_init_gen_rand(sfmt: &mut paramed::SFMT, seed: u32) { let mut pre = seed as i32; for (idx, v) in sfmt.state.iter_mut().enumerate() { let (v_, pre_) = Self::map(pre, idx as i32); @@ -336,7 +336,7 @@ mod tests { fn test_init() { let seed: u32 = 1234; let seed = unsafe { *(&seed as *const u32 as *const [u8; 4]) }; - let sfmt = SFMT::<19937>::from_seed(seed); + let sfmt = paramed::SFMT::<19937>::from_seed(seed); let ans = read_answer("check/init1234.txt").unwrap(); for (v, a) in sfmt.state.iter().zip(ans.iter()) { assert_eq!(split(*v), split(*a)); diff --git a/src/thread_rng.rs b/src/thread_rng.rs index 9c28fbe..cd0becb 100644 --- a/src/thread_rng.rs +++ b/src/thread_rng.rs @@ -7,7 +7,7 @@ use std::cell::RefCell; use std::rc::Rc; thread_local!( - static THREAD_RNG_KEY: Rc>> = { + static THREAD_RNG_KEY: Rc> = { Rc::new(RefCell::new(SFMT::from_entropy())) } ); @@ -17,7 +17,7 @@ thread_local!( /// See the reference of the function [thread_rng](fn.thread_rng.html), which generates this struct. #[derive(Clone)] pub struct ThreadRng { - rng: Rc>>, + rng: Rc>, } /// Create a thread local RNG. diff --git a/tests/sfmt.rs b/tests/sfmt.rs index b3188a4..1589e6f 100644 --- a/tests/sfmt.rs +++ b/tests/sfmt.rs @@ -17,7 +17,7 @@ fn gen_u32() { let ans = read_answer().expect("Failed to load answers"); let seed: u32 = 1234; let seed = unsafe { *(&seed as *const u32 as *const [u8; 4]) }; - let mut sfmt = SFMT::<19937>::from_seed(seed); // 1234 = 0x4D2 + let mut sfmt = SFMT::from_seed(seed); // 1234 = 0x4D2 for (t, val) in ans.into_iter().enumerate() { let r = sfmt.next_u32(); println!("[{}] gen = {}, ans = {}", t, r, val); From e4c3d20a6380a7c537b3eac9481b9b686d525922 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 15:31:14 +0900 Subject: [PATCH 05/12] small optimize --- src/packed.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/packed.rs b/src/packed.rs index 4e8e8b6..f4f7d3f 100644 --- a/src/packed.rs +++ b/src/packed.rs @@ -23,7 +23,7 @@ pub(crate) fn extract(vals: i32x4, imm: usize) -> u32 { 1 => _mm_extract_epi32(vals, 1) as u32, 2 => _mm_extract_epi32(vals, 2) as u32, 3 => _mm_extract_epi32(vals, 3) as u32, - _ => unreachable!(), + _ => core::hint::unreachable_unchecked(), } } } @@ -35,8 +35,10 @@ pub(crate) fn insert(vals: &mut i32x4, val: i32, imm: usize) { 1 => _mm_insert_epi32(*vals, val, 1), 2 => _mm_insert_epi32(*vals, val, 2), 3 => _mm_insert_epi32(*vals, val, 3), - _ => unreachable!(), + _ => core::hint::unreachable_unchecked(), } }; - ::std::mem::replace(vals, updated); + unsafe { + ::std::ptr::write(vals, updated); + } } From 606a1bead730013ddd4712790a894ab62295c3f0 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 17:09:02 +0900 Subject: [PATCH 06/12] update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4744cd4..b8b921e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ sfmt Rust implementation of [SIMD-oriented Fast Mersenne Twister (SFMT)] interface using x86-SIMD in `std::arch`. This is pure rust re-implementation, and tested on Windows/macOS/Linux. +This works with all parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091). [SIMD-oriented Fast Mersenne Twister (SFMT)]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ @@ -14,7 +15,7 @@ Limitations ------------ - Supported only on x86 and x86_64 (due to original SFMT) -- Algorithms other than MT19937 are not supported (may be fixed in future release) +- Require rustc >= 1.51 License -------- From 0935e35fc85c69cfb3d3bff1dd9f409a842e0f3a Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 17:17:49 +0900 Subject: [PATCH 07/12] Add docs to alias types. --- src/lib.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 24b7fa4..b0883af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,18 +18,30 @@ mod thread_rng; #[cfg(feature = "thread_rng")] pub use self::thread_rng::{thread_rng, ThreadRng}; +/// Fall back to [`SFMT19937`], not be a breaking change. pub type SFMT = SFMT19937; +/// SFMT with a state length 607 pub type SFMT607 = paramed::SFMT<607>; +/// SFMT with a state length 1279 pub type SFMT1279 = paramed::SFMT<1279>; +/// SFMT with a state length 2281 pub type SFMT2281 = paramed::SFMT<2281>; +/// SFMT with a state length 4253 pub type SFMT4253 = paramed::SFMT<4253>; +/// SFMT with a state length 11213 pub type SFMT11213 = paramed::SFMT<11213>; +/// SFMT with a state length 19937 pub type SFMT19937 = paramed::SFMT<19937>; +/// SFMT with a state length 44497 pub type SFMT44497 = paramed::SFMT<44497>; +/// SFMT with a state length 86243 pub type SFMT86243 = paramed::SFMT<86243>; +/// SFMT with a state length 132049 pub type SFMT132049 = paramed::SFMT<132049>; +/// SFMT with a state length 216091 pub type SFMT216091 = paramed::SFMT<216091>; +/// Internal implemention of SFMT with `N` parameter. pub mod paramed { use crate::{ packed::*, @@ -45,11 +57,7 @@ pub mod paramed { /// N can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. /// ``` /// # use rand_core::SeedableRng; - /// let s = sfmt::paramed::SFMT::<19937>::seed_from_u64(23); - /// ``` - /// ```compile_fail - /// # use rand_core::SeedableRng; - /// let s = sfmt::paramed::SFMT::<2>::seed_from_u64(23); + /// let s = sfmt::SFMT19937::seed_from_u64(23); /// ``` #[derive(Clone)] pub struct SFMT { From cdd9db7adeec27b8f3093cff94eca64851f33685 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 17:28:23 +0900 Subject: [PATCH 08/12] rename SfmtN to SfmtMEXP --- src/lib.rs | 26 +++++++++++++------------- src/sfmt.rs | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b0883af..7c079e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,31 +45,31 @@ pub type SFMT216091 = paramed::SFMT<216091>; pub mod paramed { use crate::{ packed::*, - sfmt::{SfmtN, SfmtParams}, + sfmt::{SfmtMEXP, SfmtParams}, }; use rand_core::{impls, Error, RngCore, SeedableRng}; /// State of SFMT /// /// This struct implements random number generation through `rand::Rng`. - /// The N is a parameter that defines a length of state. - /// N is limted to be a known value, and it is checked at compile time. - /// N can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. + /// The MEXP is a parameter that defines a length of state. + /// MEXP is limted to be a known value, and it is checked at compile time. + /// MEXP can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. /// ``` /// # use rand_core::SeedableRng; /// let s = sfmt::SFMT19937::seed_from_u64(23); /// ``` #[derive(Clone)] - pub struct SFMT { + pub struct SFMT { /// the 128-bit internal state array - pub(crate) state: [i32x4; N], + pub(crate) state: [i32x4; MEXP], /// index counter to the 32-bit internal state array pub(crate) idx: usize, } impl SFMT where - SfmtN: SfmtParams, + SfmtMEXP: SfmtParams, { fn pop32(&mut self) -> u32 { let val = extract(self.state[self.idx / 4], self.idx % 4); @@ -88,14 +88,14 @@ pub mod paramed { } fn gen_all(&mut self) { - SfmtN::::sfmt_gen_rand_all(self); + SfmtMEXP::::sfmt_gen_rand_all(self); self.idx = 0; } } impl SeedableRng for SFMT where - SfmtN: SfmtParams, + SfmtMEXP: SfmtParams, { type Seed = [u8; 4]; @@ -105,24 +105,24 @@ pub mod paramed { idx: 0, }; let seed = unsafe { *(seed.as_ptr() as *const u32) }; - SfmtN::::sfmt_init_gen_rand(&mut sfmt, seed); + SfmtMEXP::::sfmt_init_gen_rand(&mut sfmt, seed); sfmt } } impl RngCore for SFMT where - SfmtN: SfmtParams, + SfmtMEXP: SfmtParams, { fn next_u32(&mut self) -> u32 { - if self.idx >= SfmtN::::SFMT_N32 { + if self.idx >= SfmtMEXP::::SFMT_N32 { self.gen_all(); } self.pop32() } fn next_u64(&mut self) -> u64 { - if self.idx >= SfmtN::::SFMT_N32 - 1 { + if self.idx >= SfmtMEXP::::SFMT_N32 - 1 { // drop last u32 if idx == N32-1 self.gen_all(); } diff --git a/src/sfmt.rs b/src/sfmt.rs index 86cf00d..ce7d3c1 100644 --- a/src/sfmt.rs +++ b/src/sfmt.rs @@ -4,8 +4,8 @@ use super::*; use crate::packed::*; /// Parameters used in sfmt. -pub trait SfmtParams: Sized { - const SFMT_MEXP: usize = N; +pub trait SfmtParams: Sized { + const SFMT_MEXP: usize = MEXP; const SFMT_N: usize = Self::SFMT_MEXP / 128 + 1; // = 156 const SFMT_N32: usize = Self::SFMT_N * 4; @@ -48,7 +48,7 @@ pub trait SfmtParams: Sized { } } - fn sfmt_gen_rand_all(sfmt: &mut paramed::SFMT) { + fn sfmt_gen_rand_all(sfmt: &mut paramed::SFMT) { let st = &mut sfmt.state; let mut r1 = st[Self::SFMT_N - 2]; let mut r2 = st[Self::SFMT_N - 1]; @@ -64,7 +64,7 @@ pub trait SfmtParams: Sized { } } - fn period_certification(sfmt: &mut paramed::SFMT) { + fn period_certification(sfmt: &mut paramed::SFMT) { let mut inner = 0_u32; let st = &mut sfmt.state[0]; let parity = [ @@ -111,7 +111,7 @@ pub trait SfmtParams: Sized { (new(a, b, c, d), a2) } - fn sfmt_init_gen_rand(sfmt: &mut paramed::SFMT, seed: u32) { + fn sfmt_init_gen_rand(sfmt: &mut paramed::SFMT, seed: u32) { let mut pre = seed as i32; for (idx, v) in sfmt.state.iter_mut().enumerate() { let (v_, pre_) = Self::map(pre, idx as i32); @@ -122,14 +122,14 @@ pub trait SfmtParams: Sized { Self::period_certification(sfmt); } } -/// Wrapper for `N` parameter. -pub struct SfmtN; +/// Wrapper for `MEXP` parameter. +pub struct SfmtMEXP; macro_rules! parms_impl { ($n : expr, $pos1 : expr, $sl1 : expr, $sl2 : expr, $sr1 : expr, $sr2 : expr, $msk1 : expr, $msk2 : expr, $msk3 : expr, $msk4 : expr, $parity1 : expr, $parity2 : expr, $parity3 : expr, $parity4 : expr) => { - impl SfmtParams<$n> for SfmtN<$n> { + impl SfmtParams<$n> for SfmtMEXP<$n> { const SFMT_POS1: usize = $pos1; const SFMT_SL1: i32 = $sl1; const SFMT_SL2: i32 = $sl2; @@ -346,14 +346,14 @@ mod tests { #[test] fn test_mm_recursion_19937() { let a = new(1, 2, 3, 4); - let z = SfmtN::<19937>::mm_recursion(a, a, a, a); + let z = SfmtMEXP::<19937>::mm_recursion(a, a, a, a); let zc = new(33816833, 50856450, 67896067, 1049604); // calculated by C code assert_eq!(split(z), split(zc)); let b = new(431, 232, 83, 14); let c = new(213, 22, 93, 234); let d = new(112, 882, 23, 124); - let z = SfmtN::<19937>::mm_recursion(a, b, c, d); + let z = SfmtMEXP::<19937>::mm_recursion(a, b, c, d); let zc = new(398459137, 1355284994, -363068669, 32506884); // calculated by C code assert_eq!(split(z), split(zc)); } From 185ffbdd4dcde3c3c9caca78585ad5987ab6d9da Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 17:51:20 +0900 Subject: [PATCH 09/12] Fix incompleted rename --- src/lib.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7c079e5..13f5afc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ pub type SFMT132049 = paramed::SFMT<132049>; /// SFMT with a state length 216091 pub type SFMT216091 = paramed::SFMT<216091>; -/// Internal implemention of SFMT with `N` parameter. +/// Internal implemention of SFMT with `MEXP` parameter. pub mod paramed { use crate::{ packed::*, @@ -67,9 +67,9 @@ pub mod paramed { pub(crate) idx: usize, } - impl SFMT + impl SFMT where - SfmtMEXP: SfmtParams, + SfmtMEXP: SfmtParams, { fn pop32(&mut self) -> u32 { let val = extract(self.state[self.idx / 4], self.idx % 4); @@ -88,41 +88,41 @@ pub mod paramed { } fn gen_all(&mut self) { - SfmtMEXP::::sfmt_gen_rand_all(self); + SfmtMEXP::::sfmt_gen_rand_all(self); self.idx = 0; } } - impl SeedableRng for SFMT + impl SeedableRng for SFMT where - SfmtMEXP: SfmtParams, + SfmtMEXP: SfmtParams, { type Seed = [u8; 4]; fn from_seed(seed: [u8; 4]) -> Self { let mut sfmt = SFMT { - state: [zero(); N], + state: [zero(); MEXP], idx: 0, }; let seed = unsafe { *(seed.as_ptr() as *const u32) }; - SfmtMEXP::::sfmt_init_gen_rand(&mut sfmt, seed); + SfmtMEXP::::sfmt_init_gen_rand(&mut sfmt, seed); sfmt } } - impl RngCore for SFMT + impl RngCore for SFMT where - SfmtMEXP: SfmtParams, + SfmtMEXP: SfmtParams, { fn next_u32(&mut self) -> u32 { - if self.idx >= SfmtMEXP::::SFMT_N32 { + if self.idx >= SfmtMEXP::::SFMT_N32 { self.gen_all(); } self.pop32() } fn next_u64(&mut self) -> u64 { - if self.idx >= SfmtMEXP::::SFMT_N32 - 1 { + if self.idx >= SfmtMEXP::::SFMT_N32 - 1 { // drop last u32 if idx == N32-1 self.gen_all(); } From f6691f213a29b63fe1a180c923a52d93ddc036e3 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 18:12:39 +0900 Subject: [PATCH 10/12] Add tests for large parameter. --- README.md | 2 +- src/lib.rs | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b8b921e..cd49e0c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ sfmt Rust implementation of [SIMD-oriented Fast Mersenne Twister (SFMT)] interface using x86-SIMD in `std::arch`. This is pure rust re-implementation, and tested on Windows/macOS/Linux. -This works with all parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091). +This works with limited parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091), but it can cause stack overflow for large parameters for now. [SIMD-oriented Fast Mersenne Twister (SFMT)]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ diff --git a/src/lib.rs b/src/lib.rs index 13f5afc..c0e6872 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,11 +34,11 @@ pub type SFMT11213 = paramed::SFMT<11213>; pub type SFMT19937 = paramed::SFMT<19937>; /// SFMT with a state length 44497 pub type SFMT44497 = paramed::SFMT<44497>; -/// SFMT with a state length 86243 +/// SFMT with a state length 86243. Can cause stack overlow for now. pub type SFMT86243 = paramed::SFMT<86243>; -/// SFMT with a state length 132049 +/// SFMT with a state length 132049. Can cause stack overlow for now. pub type SFMT132049 = paramed::SFMT<132049>; -/// SFMT with a state length 216091 +/// SFMT with a state length 216091. Can cause stack overlow for now. pub type SFMT216091 = paramed::SFMT<216091>; /// Internal implemention of SFMT with `MEXP` parameter. @@ -154,4 +154,26 @@ mod tests { } // shift SFMT.idx randomly } } + #[test] + fn random_44497() { + let mut rng = SFMT44497::seed_from_u64(0); + for _ in 0..44497 * 20 { + // Generate many random numbers to test the overwrap + let r = rng.next_u64(); + if r % 2 == 0 { + let _r = rng.next_u32(); + } // shift SFMT.idx randomly + } + } + //#[test] + fn random_86243() { + let mut rng = SFMT86243::seed_from_u64(0); + for _ in 0..86243 * 20 { + // Generate many random numbers to test the overwrap + let r = rng.next_u64(); + if r % 2 == 0 { + let _r = rng.next_u32(); + } // shift SFMT.idx randomly + } + } } From f155c190d17cc779113f7b6e3056bfb148c602cb Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 18:58:54 +0900 Subject: [PATCH 11/12] Fix the state size to MEXP_N. --- src/lib.rs | 50 +++++++++++++++++++++++++------------------------- src/sfmt.rs | 32 +++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c0e6872..dba7a8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,31 +21,31 @@ pub use self::thread_rng::{thread_rng, ThreadRng}; /// Fall back to [`SFMT19937`], not be a breaking change. pub type SFMT = SFMT19937; /// SFMT with a state length 607 -pub type SFMT607 = paramed::SFMT<607>; +pub type SFMT607 = paramed::SFMT<607, { 607 / 128 + 1 }>; /// SFMT with a state length 1279 -pub type SFMT1279 = paramed::SFMT<1279>; +pub type SFMT1279 = paramed::SFMT<1279, { 1279 / 128 + 1 }>; /// SFMT with a state length 2281 -pub type SFMT2281 = paramed::SFMT<2281>; +pub type SFMT2281 = paramed::SFMT<2281, { 2281 / 128 + 1 }>; /// SFMT with a state length 4253 -pub type SFMT4253 = paramed::SFMT<4253>; +pub type SFMT4253 = paramed::SFMT<4253, { 4253 / 128 + 1 }>; /// SFMT with a state length 11213 -pub type SFMT11213 = paramed::SFMT<11213>; +pub type SFMT11213 = paramed::SFMT<11213, { 11213 / 128 + 1 }>; /// SFMT with a state length 19937 -pub type SFMT19937 = paramed::SFMT<19937>; +pub type SFMT19937 = paramed::SFMT<19937, { 19937 / 128 + 1 }>; /// SFMT with a state length 44497 -pub type SFMT44497 = paramed::SFMT<44497>; +pub type SFMT44497 = paramed::SFMT<44497, { 44497 / 128 + 1 }>; /// SFMT with a state length 86243. Can cause stack overlow for now. -pub type SFMT86243 = paramed::SFMT<86243>; +pub type SFMT86243 = paramed::SFMT<86243, { 86243 / 128 + 1 }>; /// SFMT with a state length 132049. Can cause stack overlow for now. -pub type SFMT132049 = paramed::SFMT<132049>; +pub type SFMT132049 = paramed::SFMT<132049, { 132049 / 128 + 1 }>; /// SFMT with a state length 216091. Can cause stack overlow for now. -pub type SFMT216091 = paramed::SFMT<216091>; +pub type SFMT216091 = paramed::SFMT<216091, { 216091 / 128 + 1 }>; /// Internal implemention of SFMT with `MEXP` parameter. pub mod paramed { use crate::{ packed::*, - sfmt::{SfmtMEXP, SfmtParams}, + sfmt::{SfmtParams, SFMTMEXP}, }; use rand_core::{impls, Error, RngCore, SeedableRng}; @@ -60,16 +60,16 @@ pub mod paramed { /// let s = sfmt::SFMT19937::seed_from_u64(23); /// ``` #[derive(Clone)] - pub struct SFMT { + pub struct SFMT { /// the 128-bit internal state array - pub(crate) state: [i32x4; MEXP], + pub(crate) state: [i32x4; MEXP_N], /// index counter to the 32-bit internal state array pub(crate) idx: usize, } - impl SFMT + impl SFMT where - SfmtMEXP: SfmtParams, + SFMTMEXP: SfmtParams, { fn pop32(&mut self) -> u32 { let val = extract(self.state[self.idx / 4], self.idx % 4); @@ -88,41 +88,41 @@ pub mod paramed { } fn gen_all(&mut self) { - SfmtMEXP::::sfmt_gen_rand_all(self); + SFMTMEXP::::sfmt_gen_rand_all(self); self.idx = 0; } } - impl SeedableRng for SFMT + impl SeedableRng for SFMT where - SfmtMEXP: SfmtParams, + SFMTMEXP: SfmtParams, { type Seed = [u8; 4]; fn from_seed(seed: [u8; 4]) -> Self { - let mut sfmt = SFMT { - state: [zero(); MEXP], + let mut sfmt = Self { + state: [zero(); MEXP_N], idx: 0, }; let seed = unsafe { *(seed.as_ptr() as *const u32) }; - SfmtMEXP::::sfmt_init_gen_rand(&mut sfmt, seed); + SFMTMEXP::::sfmt_init_gen_rand(&mut sfmt, seed); sfmt } } - impl RngCore for SFMT + impl RngCore for SFMT where - SfmtMEXP: SfmtParams, + SFMTMEXP: SfmtParams, { fn next_u32(&mut self) -> u32 { - if self.idx >= SfmtMEXP::::SFMT_N32 { + if self.idx >= SFMTMEXP::::SFMT_N32 { self.gen_all(); } self.pop32() } fn next_u64(&mut self) -> u64 { - if self.idx >= SfmtMEXP::::SFMT_N32 - 1 { + if self.idx >= SFMTMEXP::::SFMT_N32 - 1 { // drop last u32 if idx == N32-1 self.gen_all(); } diff --git a/src/sfmt.rs b/src/sfmt.rs index ce7d3c1..8bec625 100644 --- a/src/sfmt.rs +++ b/src/sfmt.rs @@ -4,9 +4,9 @@ use super::*; use crate::packed::*; /// Parameters used in sfmt. -pub trait SfmtParams: Sized { +pub trait SfmtParams: Sized { const SFMT_MEXP: usize = MEXP; - const SFMT_N: usize = Self::SFMT_MEXP / 128 + 1; // = 156 + const SFMT_N: usize = MEXP_N; //Self::SFMT_MEXP / 128 + 1; // = 156 const SFMT_N32: usize = Self::SFMT_N * 4; const SFMT_POS1: usize; @@ -48,7 +48,7 @@ pub trait SfmtParams: Sized { } } - fn sfmt_gen_rand_all(sfmt: &mut paramed::SFMT) { + fn sfmt_gen_rand_all(sfmt: &mut paramed::SFMT) { let st = &mut sfmt.state; let mut r1 = st[Self::SFMT_N - 2]; let mut r2 = st[Self::SFMT_N - 1]; @@ -64,7 +64,7 @@ pub trait SfmtParams: Sized { } } - fn period_certification(sfmt: &mut paramed::SFMT) { + fn period_certification(sfmt: &mut paramed::SFMT) { let mut inner = 0_u32; let st = &mut sfmt.state[0]; let parity = [ @@ -111,7 +111,7 @@ pub trait SfmtParams: Sized { (new(a, b, c, d), a2) } - fn sfmt_init_gen_rand(sfmt: &mut paramed::SFMT, seed: u32) { + fn sfmt_init_gen_rand(sfmt: &mut paramed::SFMT, seed: u32) { let mut pre = seed as i32; for (idx, v) in sfmt.state.iter_mut().enumerate() { let (v_, pre_) = Self::map(pre, idx as i32); @@ -123,13 +123,13 @@ pub trait SfmtParams: Sized { } } /// Wrapper for `MEXP` parameter. -pub struct SfmtMEXP; +pub struct SFMTMEXP; macro_rules! parms_impl { - ($n : expr, $pos1 : expr, $sl1 : expr, $sl2 : expr, $sr1 : expr, $sr2 : expr, + ($mexp : expr, $n : expr, $pos1 : expr, $sl1 : expr, $sl2 : expr, $sr1 : expr, $sr2 : expr, $msk1 : expr, $msk2 : expr, $msk3 : expr, $msk4 : expr, $parity1 : expr, $parity2 : expr, $parity3 : expr, $parity4 : expr) => { - impl SfmtParams<$n> for SfmtMEXP<$n> { + impl SfmtParams<$mexp, $n> for SFMTMEXP<$mexp, $n> { const SFMT_POS1: usize = $pos1; const SFMT_SL1: i32 = $sl1; const SFMT_SL2: i32 = $sl2; @@ -149,6 +149,7 @@ macro_rules! parms_impl { parms_impl!( 607, + { 607 / 128 + 1 }, 2, 15, 3, @@ -165,6 +166,7 @@ parms_impl!( ); parms_impl!( 1279, + { 1279 / 128 + 1 }, 7, 14, 3, @@ -181,6 +183,7 @@ parms_impl!( ); parms_impl!( 2281, + { 2281 / 128 + 1 }, 12, 19, 1, @@ -197,6 +200,7 @@ parms_impl!( ); parms_impl!( 4253, + { 4253 / 128 + 1 }, 17, 20, 1, @@ -213,6 +217,7 @@ parms_impl!( ); parms_impl!( 11213, + { 11213 / 128 + 1 }, 68, 14, 3, @@ -229,6 +234,7 @@ parms_impl!( ); parms_impl!( 19937, + { 19937 / 128 + 1 }, 122, 18, 1, @@ -245,6 +251,7 @@ parms_impl!( ); parms_impl!( 44497, + { 44497 / 128 + 1 }, 330, 5, 3, @@ -261,6 +268,7 @@ parms_impl!( ); parms_impl!( 86243, + { 86243 / 128 + 1 }, 366, 6, 7, @@ -277,6 +285,7 @@ parms_impl!( ); parms_impl!( 132049, + { 132049 / 128 + 1 }, 110, 19, 1, @@ -293,6 +302,7 @@ parms_impl!( ); parms_impl!( 216091, + { 216091 / 128 + 1 }, 627, 11, 3, @@ -336,7 +346,7 @@ mod tests { fn test_init() { let seed: u32 = 1234; let seed = unsafe { *(&seed as *const u32 as *const [u8; 4]) }; - let sfmt = paramed::SFMT::<19937>::from_seed(seed); + let sfmt = paramed::SFMT::<19937, { 19937 / 128 + 1 }>::from_seed(seed); let ans = read_answer("check/init1234.txt").unwrap(); for (v, a) in sfmt.state.iter().zip(ans.iter()) { assert_eq!(split(*v), split(*a)); @@ -346,14 +356,14 @@ mod tests { #[test] fn test_mm_recursion_19937() { let a = new(1, 2, 3, 4); - let z = SfmtMEXP::<19937>::mm_recursion(a, a, a, a); + let z = SFMTMEXP::<19937, { 19937 / 128 + 1 }>::mm_recursion(a, a, a, a); let zc = new(33816833, 50856450, 67896067, 1049604); // calculated by C code assert_eq!(split(z), split(zc)); let b = new(431, 232, 83, 14); let c = new(213, 22, 93, 234); let d = new(112, 882, 23, 124); - let z = SfmtMEXP::<19937>::mm_recursion(a, b, c, d); + let z = SFMTMEXP::<19937, { 19937 / 128 + 1 }>::mm_recursion(a, b, c, d); let zc = new(398459137, 1355284994, -363068669, 32506884); // calculated by C code assert_eq!(split(z), split(zc)); } From c44d2b08d6ada8571e7b26b184d11bfc1b6202a7 Mon Sep 17 00:00:00 2001 From: aobatact Date: Mon, 3 May 2021 19:05:27 +0900 Subject: [PATCH 12/12] Fix docs and add test. --- README.md | 2 +- src/lib.rs | 33 ++++++++++++++++++++++++++++----- src/sfmt.rs | 2 +- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cd49e0c..8520cd1 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ sfmt Rust implementation of [SIMD-oriented Fast Mersenne Twister (SFMT)] interface using x86-SIMD in `std::arch`. This is pure rust re-implementation, and tested on Windows/macOS/Linux. -This works with limited parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091), but it can cause stack overflow for large parameters for now. +This works with limited parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091). [SIMD-oriented Fast Mersenne Twister (SFMT)]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ diff --git a/src/lib.rs b/src/lib.rs index dba7a8b..7f08472 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,11 +34,11 @@ pub type SFMT11213 = paramed::SFMT<11213, { 11213 / 128 + 1 }>; pub type SFMT19937 = paramed::SFMT<19937, { 19937 / 128 + 1 }>; /// SFMT with a state length 44497 pub type SFMT44497 = paramed::SFMT<44497, { 44497 / 128 + 1 }>; -/// SFMT with a state length 86243. Can cause stack overlow for now. +/// SFMT with a state length 86243. pub type SFMT86243 = paramed::SFMT<86243, { 86243 / 128 + 1 }>; -/// SFMT with a state length 132049. Can cause stack overlow for now. +/// SFMT with a state length 132049. pub type SFMT132049 = paramed::SFMT<132049, { 132049 / 128 + 1 }>; -/// SFMT with a state length 216091. Can cause stack overlow for now. +/// SFMT with a state length 216091. pub type SFMT216091 = paramed::SFMT<216091, { 216091 / 128 + 1 }>; /// Internal implemention of SFMT with `MEXP` parameter. @@ -55,6 +55,7 @@ pub mod paramed { /// The MEXP is a parameter that defines a length of state. /// MEXP is limted to be a known value, and it is checked at compile time. /// MEXP can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. + /// Since there is a limitation to const generics, we also need the `MEXP_N = {MEXP / 128 + 1}` /// ``` /// # use rand_core::SeedableRng; /// let s = sfmt::SFMT19937::seed_from_u64(23); @@ -144,7 +145,18 @@ mod tests { use rand_core::{RngCore, SeedableRng}; #[test] - fn random() { + fn random_607() { + let mut rng = SFMT607::seed_from_u64(0); + for _ in 0..607 * 20 { + // Generate many random numbers to test the overwrap + let r = rng.next_u64(); + if r % 2 == 0 { + let _r = rng.next_u32(); + } // shift SFMT.idx randomly + } + } + #[test] + fn random_19937() { let mut rng = SFMT::seed_from_u64(0); for _ in 0..19937 * 20 { // Generate many random numbers to test the overwrap @@ -165,7 +177,7 @@ mod tests { } // shift SFMT.idx randomly } } - //#[test] + #[test] fn random_86243() { let mut rng = SFMT86243::seed_from_u64(0); for _ in 0..86243 * 20 { @@ -176,4 +188,15 @@ mod tests { } // shift SFMT.idx randomly } } + #[test] + fn random_216091() { + let mut rng = SFMT216091::seed_from_u64(0); + for _ in 0..216091 * 20 { + // Generate many random numbers to test the overwrap + let r = rng.next_u64(); + if r % 2 == 0 { + let _r = rng.next_u32(); + } // shift SFMT.idx randomly + } + } } diff --git a/src/sfmt.rs b/src/sfmt.rs index 8bec625..4d0810f 100644 --- a/src/sfmt.rs +++ b/src/sfmt.rs @@ -6,7 +6,7 @@ use crate::packed::*; /// Parameters used in sfmt. pub trait SfmtParams: Sized { const SFMT_MEXP: usize = MEXP; - const SFMT_N: usize = MEXP_N; //Self::SFMT_MEXP / 128 + 1; // = 156 + const SFMT_N: usize = MEXP_N; //Self::SFMT_MEXP / 128 + 1; const SFMT_N32: usize = Self::SFMT_N * 4; const SFMT_POS1: usize;