├── rust-toolchain ├── .gitattributes ├── check ├── .gitignore ├── sample.cpp └── Makefile ├── .gitignore ├── CHANGELOG.md ├── tests ├── thread_rng.rs └── sfmt.rs ├── Cargo.toml ├── README.md ├── LICENSE ├── benches └── rand_gen.rs ├── src ├── packed.rs ├── thread_rng.rs ├── lib.rs └── sfmt.rs └── .github └── workflows └── rust.yml /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.53.0 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | check/*.txt binary 2 | -------------------------------------------------------------------------------- /check/.gitignore: -------------------------------------------------------------------------------- 1 | SFMT-src-*/ 2 | a.out 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | 6 | rusty-tags.* 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Unreleased 2 | ----------- 3 | 4 | Added 5 | ----- 6 | - Another Mersenne primes are supported https://github.com/rust-math/sfmt/pull/30 7 | - Test for comparing to the original SFMT implementation https://github.com/rust-math/sfmt/pull/34 8 | 9 | Maintenance 10 | ------------ 11 | - Switch CI from Azure Pipeline to GitHub Actions https://github.com/rust-math/sfmt/pull/33 12 | - Fix badges https://github.com/rust-math/sfmt/pull/31 13 | 14 | 0.7.0 - 2021/9/4 15 | ---------------- 16 | 17 | Updated dependencies 18 | --------------------- 19 | - rand 0.8 20 | - rand_core 0.6 21 | -------------------------------------------------------------------------------- /check/sample.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate u64 random integers using original SFMT implementation 3 | * 4 | * - Seed is fixed value (seed=1234) 5 | * - Generate 10000 integers 6 | * - `SFMT_MEXP` will be set as a compiler flag. See Makefile. 7 | */ 8 | #include "./SFMT-src-1.5.1/SFMT.h" 9 | #include 10 | 11 | int main(int argc, char *argv[]) { 12 | sfmt_t sfmt; 13 | sfmt_init_gen_rand(&sfmt, 1234); 14 | for (int i = 0; i < 10000; i++) { 15 | uint64_t x = sfmt_genrand_uint64(&sfmt); 16 | std::cout << x << "\n"; 17 | } 18 | std::cout << std::flush; 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /tests/thread_rng.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "thread_rng")] 2 | 3 | use rand::Rng; 4 | use std::thread; 5 | 6 | fn gen() -> Vec { 7 | let mut rng = sfmt::thread_rng(); 8 | let mut v: Vec = Vec::new(); 9 | for _ in 0..3 { 10 | v.push(rng.gen()); 11 | } 12 | v 13 | } 14 | 15 | /// Two different thread should returns different random numbers 16 | #[test] 17 | #[should_panic] 18 | fn thread_rng() { 19 | let th1 = thread::spawn(gen); 20 | let th2 = thread::spawn(gen); 21 | let v1 = th1.join().unwrap(); 22 | let v2 = th2.join().unwrap(); 23 | assert_eq!(v1, v2); 24 | } 25 | -------------------------------------------------------------------------------- /check/Makefile: -------------------------------------------------------------------------------- 1 | 2 | SFMT_VERSION := 1.5.1 3 | SFMT_DIR := SFMT-src-$(SFMT_VERSION) 4 | SFMT_ARCHIVE := $(SFMT_DIR).tar.gz 5 | 6 | CXX := g++ 7 | CXX_FLAGS := -O2 -msse2 -DHAVE_SSE2 8 | 9 | MEXPS := 607 1279 2281 4253 11213 19937 44497 86243 132049 216091 10 | U64_REFERENCES := $(foreach MEXP,$(MEXPS),u64_$(MEXP).txt) 11 | 12 | all: $(U64_REFERENCES) 13 | 14 | $(SFMT_DIR)/SFMT.c: 15 | wget http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/$(SFMT_ARCHIVE) 16 | tar xf $(SFMT_ARCHIVE) 17 | rm -f $(SFMT_ARCHIVE) 18 | 19 | clean: 20 | rm -rf $(SFMT_DIR) 21 | rm -f $(U64_REFERENCES) 22 | 23 | define generate_u64 24 | $(CXX) $(CXX_FLAGS) -DSFMT_MEXP=$(1) $^ 25 | ./a.out > u64_$(1).txt 26 | rm a.out 27 | 28 | endef 29 | 30 | $(U64_REFERENCES): sample.cpp $(SFMT_DIR)/SFMT.c 31 | $(foreach MEXP,$(MEXPS),$(call generate_u64,$(MEXP))) 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sfmt" 3 | version = "0.7.0" 4 | authors = ["Toshiki Teramura "] 5 | edition = "2018" 6 | 7 | description = "Rust implementation of SIMD-oriented Fast Mersenne Twister (SFMT)" 8 | documentation = "https://docs.rs/sfmt/" 9 | repository = "https://github.com/termoshtt/rust-sfmt" 10 | keywords = ["random", "rng"] 11 | categories = ["algorithms"] 12 | license = "MIT" 13 | 14 | [features] 15 | default = ["thread_rng"] 16 | 17 | # The thread_rng feature requires the rand dependency 18 | thread_rng = ["rand/getrandom"] 19 | 20 | [dependencies] 21 | rand = { version = "0.8.4", optional = true } 22 | rand_core = "0.6.3" 23 | 24 | [dev-dependencies] 25 | rand_xorshift = "0.3.0" 26 | rand_core = { version = "0.6", features=["getrandom"] } 27 | paste = "1.0.5" 28 | 29 | [package.metadata.release] 30 | no-dev-version = true 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sfmt 2 | ===== 3 | 4 | [![Crate](https://img.shields.io/crates/v/sfmt.svg)](https://crates.io/crates/sfmt) 5 | [![docs.rs](https://docs.rs/sfmt/badge.svg)](https://docs.rs/sfmt) 6 | [![DOI](https://zenodo.org/badge/118722822.svg)](https://zenodo.org/badge/latestdoi/118722822) 7 | 8 | Rust implementation of [SIMD-oriented Fast Mersenne Twister (SFMT)] interface using x86-SIMD in `std::arch`. 9 | This is pure rust re-implementation, and tested on Windows/macOS/Linux. 10 | This works with limited parameters (607, 1279, 2281, 4253, 11213, 19937, 44497, 86243, 132049, 216091). 11 | 12 | [SIMD-oriented Fast Mersenne Twister (SFMT)]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ 13 | 14 | Limitations 15 | ------------ 16 | 17 | - Supported only on x86 and x86_64 (due to original SFMT) 18 | - Require rustc >= 1.51 19 | 20 | License 21 | -------- 22 | MIT-License 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Toshiki Teramura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benches/rand_gen.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate rand; 4 | extern crate sfmt; 5 | extern crate test; 6 | 7 | use rand::*; 8 | use rand_core::SeedableRng; 9 | use rand_xorshift::*; 10 | use sfmt::SFMT; 11 | use test::Bencher; 12 | 13 | macro_rules! def_bench { 14 | ($name:ident, $t:ty, $rng:expr) => { 15 | #[bench] 16 | fn $name(b: &mut Bencher) { 17 | let mut rng = $rng; 18 | b.iter(|| { 19 | for _ in 0..100 { 20 | let _rng = rng.gen::<$t>(); 21 | } 22 | }); 23 | } 24 | }; 25 | } // def_bench! 26 | 27 | mod gen_f64 { 28 | use super::*; 29 | def_bench!(xorshift, f64, XorShiftRng::from_entropy()); 30 | def_bench!(sfmt, f64, SFMT::from_entropy()); 31 | } 32 | 33 | mod gen_f32 { 34 | use super::*; 35 | def_bench!(xorshift, f32, XorShiftRng::from_entropy()); 36 | def_bench!(sfmt, f32, SFMT::from_entropy()); 37 | } 38 | 39 | mod gen_u64 { 40 | use super::*; 41 | def_bench!(xorshift, u64, XorShiftRng::from_entropy()); 42 | def_bench!(sfmt, u64, SFMT::from_entropy()); 43 | } 44 | 45 | mod gen_u32 { 46 | use super::*; 47 | def_bench!(xorshift, u32, XorShiftRng::from_entropy()); 48 | def_bench!(sfmt, u32, SFMT::from_entropy()); 49 | } 50 | -------------------------------------------------------------------------------- /src/packed.rs: -------------------------------------------------------------------------------- 1 | //! packed_simd-like wrapper layer 2 | 3 | #[cfg(target_arch = "x86")] 4 | use std::arch::x86::*; 5 | #[cfg(target_arch = "x86_64")] 6 | use std::arch::x86_64::*; 7 | 8 | #[allow(non_camel_case_types)] 9 | pub(crate) type i32x4 = __m128i; 10 | 11 | pub(crate) fn new(e0: i32, e1: i32, e2: i32, e3: i32) -> i32x4 { 12 | unsafe { _mm_set_epi32(e3, e2, e1, e0) } 13 | } 14 | 15 | pub(crate) fn zero() -> i32x4 { 16 | unsafe { _mm_setzero_si128() } 17 | } 18 | 19 | pub(crate) fn extract(vals: i32x4, imm: usize) -> u32 { 20 | unsafe { 21 | match imm { 22 | 0 => _mm_extract_epi32(vals, 0) as u32, 23 | 1 => _mm_extract_epi32(vals, 1) as u32, 24 | 2 => _mm_extract_epi32(vals, 2) as u32, 25 | 3 => _mm_extract_epi32(vals, 3) as u32, 26 | _ => core::hint::unreachable_unchecked(), 27 | } 28 | } 29 | } 30 | 31 | pub(crate) fn insert(vals: &mut i32x4, val: i32, imm: usize) { 32 | let updated = unsafe { 33 | match imm { 34 | 0 => _mm_insert_epi32(*vals, val, 0), 35 | 1 => _mm_insert_epi32(*vals, val, 1), 36 | 2 => _mm_insert_epi32(*vals, val, 2), 37 | 3 => _mm_insert_epi32(*vals, val, 3), 38 | _ => core::hint::unreachable_unchecked(), 39 | } 40 | }; 41 | unsafe { 42 | ::std::ptr::write(vals, updated); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: {} 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: 1.53.0 17 | default: true 18 | components: clippy, rustfmt 19 | - uses: actions-rs/cargo@v1 20 | with: 21 | command: test 22 | 23 | check-format: 24 | runs-on: ubuntu-20.04 25 | steps: 26 | - uses: actions/checkout@v1 27 | - uses: actions-rs/toolchain@v1 28 | with: 29 | toolchain: 1.53.0 30 | default: true 31 | components: rustfmt 32 | - uses: actions-rs/cargo@v1 33 | with: 34 | command: fmt 35 | args: -- --check 36 | 37 | doc: 38 | runs-on: ubuntu-20.04 39 | steps: 40 | - uses: actions/checkout@v1 41 | - uses: actions-rs/toolchain@v1 42 | with: 43 | toolchain: 1.53.0 44 | default: true 45 | - uses: actions-rs/cargo@v1 46 | with: 47 | command: doc 48 | args: --no-deps 49 | 50 | clippy: 51 | runs-on: ubuntu-20.04 52 | steps: 53 | - uses: actions/checkout@v1 54 | - uses: actions-rs/toolchain@v1 55 | with: 56 | toolchain: 1.53.0 57 | default: true 58 | components: clippy 59 | - uses: actions-rs/cargo@v1 60 | with: 61 | command: clippy 62 | -------------------------------------------------------------------------------- /tests/sfmt.rs: -------------------------------------------------------------------------------- 1 | use paste::paste; 2 | use rand_core::{RngCore, SeedableRng}; 3 | use sfmt::*; 4 | use std::{fs, io, io::BufRead}; 5 | 6 | // Read random numbers generated by original SFMT implementation 7 | fn read_reference(filename: &str) -> Result, io::Error> { 8 | let f = io::BufReader::new(fs::File::open(filename)?); 9 | Ok(f.lines() 10 | .map(|line| { 11 | line.unwrap() 12 | .parse::() 13 | .expect("Failed to parse as u64") 14 | }) 15 | .collect()) 16 | } 17 | 18 | macro_rules! compare_to_original { 19 | ($mexp:expr) => { 20 | paste! { 21 | #[test] 22 | fn [< compare_to_original_ $mexp >]() { 23 | // This crate only works on x86/x86_64, which must be Little Endition 24 | let mut rng = paramed::SFMT::<$mexp, { $mexp / 128 + 1 }>::from_seed(1234_u32.to_le_bytes()); 25 | let answer = read_reference(&format!("check/u64_{}.txt", $mexp)).unwrap(); 26 | for ans in answer { 27 | let r = rng.next_u64(); 28 | assert_eq!(r, ans); 29 | } 30 | } 31 | } // paste 32 | }; 33 | } 34 | 35 | compare_to_original!(607); 36 | compare_to_original!(1279); 37 | compare_to_original!(2281); 38 | compare_to_original!(4253); 39 | compare_to_original!(11213); 40 | compare_to_original!(19937); 41 | compare_to_original!(44497); 42 | compare_to_original!(86243); 43 | compare_to_original!(132049); 44 | compare_to_original!(216091); 45 | -------------------------------------------------------------------------------- /src/thread_rng.rs: -------------------------------------------------------------------------------- 1 | //! Thread-local RNG based on SFMT 2 | 3 | use super::SFMT; 4 | 5 | use rand_core::{Error, RngCore, SeedableRng}; 6 | use std::cell::RefCell; 7 | use std::rc::Rc; 8 | 9 | thread_local!( 10 | static THREAD_RNG_KEY: Rc> = { 11 | Rc::new(RefCell::new(SFMT::from_entropy())) 12 | } 13 | ); 14 | 15 | /// Thread-local RNG based on SFMT. 16 | /// 17 | /// See the reference of the function [thread_rng](fn.thread_rng.html), which generates this struct. 18 | #[derive(Clone)] 19 | pub struct ThreadRng { 20 | rng: Rc>, 21 | } 22 | 23 | /// Create a thread local RNG. 24 | /// 25 | /// The seed of SFMT is generated by `rand::thread_rng()` on each thread. 26 | /// 27 | /// ``` 28 | /// # extern crate sfmt; 29 | /// # extern crate rand; 30 | /// # use rand::Rng; 31 | /// let mut rng = sfmt::thread_rng(); 32 | /// rng.gen::(); // random u32 33 | /// ``` 34 | pub fn thread_rng() -> ThreadRng { 35 | ThreadRng { 36 | rng: THREAD_RNG_KEY.with(|t| t.clone()), 37 | } 38 | } 39 | 40 | impl RngCore for ThreadRng { 41 | fn next_u32(&mut self) -> u32 { 42 | self.rng.borrow_mut().next_u32() 43 | } 44 | 45 | fn next_u64(&mut self) -> u64 { 46 | self.rng.borrow_mut().next_u64() 47 | } 48 | 49 | fn fill_bytes(&mut self, dest: &mut [u8]) { 50 | self.rng.borrow_mut().fill_bytes(dest) 51 | } 52 | 53 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { 54 | self.rng.borrow_mut().try_fill_bytes(dest) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Rust implementation of [SIMD-oriented Fast Mersenne Twister (SFMT)] using [stable SIMD] 2 | //! 3 | //! [SIMD-oriented Fast Mersenne Twister (SFMT)]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ 4 | //! [stable SIMD]: https://github.com/rust-lang/rfcs/blob/master/text/2325-stable-simd.md 5 | //! 6 | //! ``` 7 | //! use rand_core::{RngCore, SeedableRng}; 8 | //! let mut rng = sfmt::SFMT19937::seed_from_u64(42); 9 | //! let r = rng.next_u32(); 10 | //! println!("random u32 number = {}", r); 11 | //! ``` 12 | 13 | mod packed; 14 | mod sfmt; 15 | #[cfg(feature = "thread_rng")] 16 | mod thread_rng; 17 | 18 | #[cfg(feature = "thread_rng")] 19 | pub use self::thread_rng::{thread_rng, ThreadRng}; 20 | 21 | /// Fall back to [`SFMT19937`], not be a breaking change. 22 | pub type SFMT = SFMT19937; 23 | /// SFMT with a state length 607 24 | pub type SFMT607 = paramed::SFMT<607, { 607 / 128 + 1 }>; 25 | /// SFMT with a state length 1279 26 | pub type SFMT1279 = paramed::SFMT<1279, { 1279 / 128 + 1 }>; 27 | /// SFMT with a state length 2281 28 | pub type SFMT2281 = paramed::SFMT<2281, { 2281 / 128 + 1 }>; 29 | /// SFMT with a state length 4253 30 | pub type SFMT4253 = paramed::SFMT<4253, { 4253 / 128 + 1 }>; 31 | /// SFMT with a state length 11213 32 | pub type SFMT11213 = paramed::SFMT<11213, { 11213 / 128 + 1 }>; 33 | /// SFMT with a state length 19937 34 | pub type SFMT19937 = paramed::SFMT<19937, { 19937 / 128 + 1 }>; 35 | /// SFMT with a state length 44497 36 | pub type SFMT44497 = paramed::SFMT<44497, { 44497 / 128 + 1 }>; 37 | /// SFMT with a state length 86243. 38 | pub type SFMT86243 = paramed::SFMT<86243, { 86243 / 128 + 1 }>; 39 | /// SFMT with a state length 132049. 40 | pub type SFMT132049 = paramed::SFMT<132049, { 132049 / 128 + 1 }>; 41 | /// SFMT with a state length 216091. 42 | pub type SFMT216091 = paramed::SFMT<216091, { 216091 / 128 + 1 }>; 43 | 44 | /// Internal implemention of SFMT with `MEXP` parameter. 45 | pub mod paramed { 46 | use crate::{ 47 | packed::*, 48 | sfmt::{SfmtParams, SFMTMEXP}, 49 | }; 50 | use rand_core::{impls, Error, RngCore, SeedableRng}; 51 | 52 | /// State of SFMT 53 | /// 54 | /// This struct implements random number generation through `rand::Rng`. 55 | /// The MEXP is a parameter that defines a length of state. 56 | /// MEXP is limted to be a known value, and it is checked at compile time. 57 | /// MEXP can only be `607,1279,2281,4253,11213,19937,44497,86243,132049,216091`. 58 | /// Since there is a limitation to const generics, we also need the `MEXP_N = {MEXP / 128 + 1}` 59 | /// ``` 60 | /// # use rand_core::SeedableRng; 61 | /// let s = sfmt::SFMT19937::seed_from_u64(23); 62 | /// ``` 63 | #[derive(Clone)] 64 | pub struct SFMT { 65 | /// the 128-bit internal state array 66 | pub(crate) state: [i32x4; MEXP_N], 67 | /// index counter to the 32-bit internal state array 68 | pub(crate) idx: usize, 69 | } 70 | 71 | impl SFMT 72 | where 73 | SFMTMEXP: SfmtParams, 74 | { 75 | fn pop32(&mut self) -> u32 { 76 | let val = extract(self.state[self.idx / 4], self.idx % 4); 77 | self.idx += 1; 78 | val 79 | } 80 | 81 | fn pop64(&mut self) -> u64 { 82 | let p = self.state.as_ptr() as *const u32; 83 | let val = unsafe { 84 | let p = p.offset(self.idx as isize); 85 | *(p as *const u64) // reinterpret cast [u32; 2] -> u64 86 | }; 87 | self.idx += 2; 88 | val 89 | } 90 | 91 | fn gen_all(&mut self) { 92 | SFMTMEXP::::sfmt_gen_rand_all(self); 93 | self.idx = 0; 94 | } 95 | } 96 | 97 | impl SeedableRng for SFMT 98 | where 99 | SFMTMEXP: SfmtParams, 100 | { 101 | type Seed = [u8; 4]; 102 | 103 | fn from_seed(seed: [u8; 4]) -> Self { 104 | let mut sfmt = Self { 105 | state: [zero(); MEXP_N], 106 | idx: 0, 107 | }; 108 | let seed = unsafe { *(seed.as_ptr() as *const u32) }; 109 | SFMTMEXP::::sfmt_init_gen_rand(&mut sfmt, seed); 110 | sfmt 111 | } 112 | } 113 | 114 | impl RngCore for SFMT 115 | where 116 | SFMTMEXP: SfmtParams, 117 | { 118 | fn next_u32(&mut self) -> u32 { 119 | if self.idx >= SFMTMEXP::::SFMT_N32 { 120 | self.gen_all(); 121 | } 122 | self.pop32() 123 | } 124 | 125 | fn next_u64(&mut self) -> u64 { 126 | if self.idx >= SFMTMEXP::::SFMT_N32 - 1 { 127 | // drop last u32 if idx == N32-1 128 | self.gen_all(); 129 | } 130 | self.pop64() 131 | } 132 | 133 | fn fill_bytes(&mut self, dest: &mut [u8]) { 134 | impls::fill_bytes_via_next(self, dest) 135 | } 136 | 137 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { 138 | Ok(self.fill_bytes(dest)) 139 | } 140 | } 141 | } 142 | #[cfg(test)] 143 | mod tests { 144 | use super::*; 145 | use rand_core::{RngCore, SeedableRng}; 146 | 147 | #[test] 148 | fn random_607() { 149 | let mut rng = SFMT607::seed_from_u64(0); 150 | for _ in 0..607 * 20 { 151 | // Generate many random numbers to test the overwrap 152 | let r = rng.next_u64(); 153 | if r % 2 == 0 { 154 | let _r = rng.next_u32(); 155 | } // shift SFMT.idx randomly 156 | } 157 | } 158 | #[test] 159 | fn random_19937() { 160 | let mut rng = SFMT::seed_from_u64(0); 161 | for _ in 0..19937 * 20 { 162 | // Generate many random numbers to test the overwrap 163 | let r = rng.next_u64(); 164 | if r % 2 == 0 { 165 | let _r = rng.next_u32(); 166 | } // shift SFMT.idx randomly 167 | } 168 | } 169 | #[test] 170 | fn random_44497() { 171 | let mut rng = SFMT44497::seed_from_u64(0); 172 | for _ in 0..44497 * 20 { 173 | // Generate many random numbers to test the overwrap 174 | let r = rng.next_u64(); 175 | if r % 2 == 0 { 176 | let _r = rng.next_u32(); 177 | } // shift SFMT.idx randomly 178 | } 179 | } 180 | #[test] 181 | fn random_86243() { 182 | let mut rng = SFMT86243::seed_from_u64(0); 183 | for _ in 0..86243 * 20 { 184 | // Generate many random numbers to test the overwrap 185 | let r = rng.next_u64(); 186 | if r % 2 == 0 { 187 | let _r = rng.next_u32(); 188 | } // shift SFMT.idx randomly 189 | } 190 | } 191 | #[test] 192 | fn random_216091() { 193 | let mut rng = SFMT216091::seed_from_u64(0); 194 | for _ in 0..216091 * 20 { 195 | // Generate many random numbers to test the overwrap 196 | let r = rng.next_u64(); 197 | if r % 2 == 0 { 198 | let _r = rng.next_u32(); 199 | } // shift SFMT.idx randomly 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/sfmt.rs: -------------------------------------------------------------------------------- 1 | //! Rust re-implementation of SFMT 2 | 3 | use super::*; 4 | use crate::packed::*; 5 | 6 | /// Parameters used in sfmt. 7 | pub trait SfmtParams: Sized { 8 | const SFMT_MEXP: usize = MEXP; 9 | const SFMT_N: usize = MEXP_N; //Self::SFMT_MEXP / 128 + 1; 10 | const SFMT_N32: usize = Self::SFMT_N * 4; 11 | 12 | const SFMT_POS1: usize; 13 | const SFMT_SL1: i32; 14 | const SFMT_SL2: i32; 15 | const SFMT_SR1: i32; 16 | const SFMT_SR2: i32; 17 | const SFMT_MSK1: i32; 18 | const SFMT_MSK2: i32; 19 | const SFMT_MSK3: i32; 20 | const SFMT_MSK4: i32; 21 | const SFMT_PARITY1: u32; 22 | const SFMT_PARITY2: u32; 23 | const SFMT_PARITY3: u32; 24 | const SFMT_PARITY4: u32; 25 | 26 | fn mm_recursion(a: i32x4, b: i32x4, c: i32x4, d: i32x4) -> i32x4 { 27 | #[cfg(target_arch = "x86")] 28 | use std::arch::x86::*; 29 | #[cfg(target_arch = "x86_64")] 30 | use std::arch::x86_64::*; 31 | 32 | unsafe { 33 | let mask = new( 34 | Self::SFMT_MSK1, 35 | Self::SFMT_MSK2, 36 | Self::SFMT_MSK3, 37 | Self::SFMT_MSK4, 38 | ); 39 | let y = _mm_srli_epi32(b, Self::SFMT_SR1); 40 | let z = _mm_srli_si128(c, Self::SFMT_SR2); 41 | let v = _mm_slli_epi32(d, Self::SFMT_SL1); 42 | let z = _mm_xor_si128(z, a); 43 | let z = _mm_xor_si128(z, v); 44 | let x = _mm_slli_si128(a, Self::SFMT_SL2); 45 | let y = _mm_and_si128(y, mask); 46 | let z = _mm_xor_si128(z, x); 47 | _mm_xor_si128(z, y) 48 | } 49 | } 50 | 51 | fn sfmt_gen_rand_all(sfmt: &mut paramed::SFMT) { 52 | let st = &mut sfmt.state; 53 | let mut r1 = st[Self::SFMT_N - 2]; 54 | let mut r2 = st[Self::SFMT_N - 1]; 55 | for i in 0..(Self::SFMT_N - Self::SFMT_POS1) { 56 | st[i] = Self::mm_recursion(st[i], st[i + Self::SFMT_POS1], r1, r2); 57 | r1 = r2; 58 | r2 = st[i]; 59 | } 60 | for i in (Self::SFMT_N - Self::SFMT_POS1)..Self::SFMT_N { 61 | st[i] = Self::mm_recursion(st[i], st[i + Self::SFMT_POS1 - Self::SFMT_N], r1, r2); 62 | r1 = r2; 63 | r2 = st[i]; 64 | } 65 | } 66 | 67 | fn period_certification(sfmt: &mut paramed::SFMT) { 68 | let mut inner = 0_u32; 69 | let st = &mut sfmt.state[0]; 70 | let parity = [ 71 | Self::SFMT_PARITY1, 72 | Self::SFMT_PARITY2, 73 | Self::SFMT_PARITY3, 74 | Self::SFMT_PARITY4, 75 | ]; 76 | for i in 0..4 { 77 | inner ^= extract(*st, i) & parity[i]; 78 | } 79 | for i in [16, 8, 4, 2, 1].iter() { 80 | inner ^= inner >> i; 81 | } 82 | inner &= 1; 83 | if inner == 1 { 84 | return; 85 | } 86 | for i in 0..4 { 87 | let mut work = 1_u32; 88 | for _ in 0..32 { 89 | if (work & parity[i]) != 0 { 90 | let val = extract(*st, i) ^ work; 91 | insert(st, val as i32, i); 92 | return; 93 | } 94 | work = work << 1; 95 | } 96 | } 97 | } 98 | 99 | fn iterate(pre: i32, i: i32) -> i32 { 100 | use std::num::Wrapping; 101 | let pre = Wrapping(pre as u32); 102 | let i = Wrapping(i as u32); 103 | (Wrapping(1812433253) * (pre ^ (pre >> 30)) + i).0 as i32 104 | } 105 | 106 | fn map(a: i32, idx: i32) -> (i32x4, i32) { 107 | let b = Self::iterate(a, 4 * idx + 1); 108 | let c = Self::iterate(b, 4 * idx + 2); 109 | let d = Self::iterate(c, 4 * idx + 3); 110 | let a2 = Self::iterate(d, 4 * idx + 4); 111 | (new(a, b, c, d), a2) 112 | } 113 | 114 | fn sfmt_init_gen_rand(sfmt: &mut paramed::SFMT, seed: u32) { 115 | let mut pre = seed as i32; 116 | for (idx, v) in sfmt.state.iter_mut().enumerate() { 117 | let (v_, pre_) = Self::map(pre, idx as i32); 118 | *v = v_; 119 | pre = pre_; 120 | } 121 | sfmt.idx = Self::SFMT_N32; 122 | Self::period_certification(sfmt); 123 | } 124 | } 125 | /// Wrapper for `MEXP` parameter. 126 | pub struct SFMTMEXP; 127 | 128 | macro_rules! parms_impl { 129 | ($mexp : expr, $n : expr, $pos1 : expr, $sl1 : expr, $sl2 : expr, $sr1 : expr, $sr2 : expr, 130 | $msk1 : expr, $msk2 : expr, $msk3 : expr, $msk4 : expr, 131 | $parity1 : expr, $parity2 : expr, $parity3 : expr, $parity4 : expr) => { 132 | impl SfmtParams<$mexp, $n> for SFMTMEXP<$mexp, $n> { 133 | const SFMT_POS1: usize = $pos1; 134 | const SFMT_SL1: i32 = $sl1; 135 | const SFMT_SL2: i32 = $sl2; 136 | const SFMT_SR1: i32 = $sr1; 137 | const SFMT_SR2: i32 = $sr2; 138 | const SFMT_MSK1: i32 = $msk1 as i32; 139 | const SFMT_MSK2: i32 = $msk2 as i32; 140 | const SFMT_MSK3: i32 = $msk3 as i32; 141 | const SFMT_MSK4: i32 = $msk4 as i32; 142 | const SFMT_PARITY1: u32 = $parity1; 143 | const SFMT_PARITY2: u32 = $parity2; 144 | const SFMT_PARITY3: u32 = $parity3; 145 | const SFMT_PARITY4: u32 = $parity4; 146 | } 147 | }; 148 | } 149 | 150 | parms_impl!( 151 | 607, 152 | { 607 / 128 + 1 }, 153 | 2, 154 | 15, 155 | 3, 156 | 13, 157 | 3, 158 | 0xfdff_37ff_u32, 159 | 0xef7f_3f7d_u32, 160 | 0xff77_7b7d_u32, 161 | 0x7ff7_fb2f_u32, 162 | 0x0000_0001, 163 | 0x0000_0000, 164 | 0x0000_0000, 165 | 0x5986_f054 166 | ); 167 | parms_impl!( 168 | 1279, 169 | { 1279 / 128 + 1 }, 170 | 7, 171 | 14, 172 | 3, 173 | 5, 174 | 1, 175 | 0xf7fe_fffd_u32, 176 | 0x7fef_cfff_u32, 177 | 0xaff3_ef3f_u32, 178 | 0xb5ff_ff7f_u32, 179 | 0x0000_0001_u32, 180 | 0x0000_0000_u32, 181 | 0x0000_0000_u32, 182 | 0x2000_0000_u32 183 | ); 184 | parms_impl!( 185 | 2281, 186 | { 2281 / 128 + 1 }, 187 | 12, 188 | 19, 189 | 1, 190 | 5, 191 | 1, 192 | 0xbff7_ffbf_u32, 193 | 0xfdff_fffe_u32, 194 | 0xf7ff_ef7f_u32, 195 | 0xf2f7_cbbf_u32, 196 | 0x0000_0001_u32, 197 | 0x0000_0000_u32, 198 | 0x0000_0000_u32, 199 | 0x41df_a600_u32 200 | ); 201 | parms_impl!( 202 | 4253, 203 | { 4253 / 128 + 1 }, 204 | 17, 205 | 20, 206 | 1, 207 | 7, 208 | 1, 209 | 0x9f7b_ffff_u32, 210 | 0x9fff_ff5f_u32, 211 | 0x3eff_fffb_u32, 212 | 0xffff_f7bb_u32, 213 | 0xa800_0001_u32, 214 | 0xaf53_90a3_u32, 215 | 0xb740_b3f8_u32, 216 | 0x6c11_486d_u32 217 | ); 218 | parms_impl!( 219 | 11213, 220 | { 11213 / 128 + 1 }, 221 | 68, 222 | 14, 223 | 3, 224 | 7, 225 | 3, 226 | 0xefff_f7fb_u32, 227 | 0xffff_ffef_u32, 228 | 0xdfdf_bfff_u32, 229 | 0x7fff_dbfd_u32, 230 | 0x0000_0001_u32, 231 | 0x0000_0000_u32, 232 | 0xb740_b3f8_u32, 233 | 0x6c11_486d_u32 234 | ); 235 | parms_impl!( 236 | 19937, 237 | { 19937 / 128 + 1 }, 238 | 122, 239 | 18, 240 | 1, 241 | 11, 242 | 1, 243 | 0xdfff_ffef_u32, 244 | 0xddfe_cb7f_u32, 245 | 0xbffa_ffff_u32, 246 | 0xbfff_fff6_u32, 247 | 0x0000_0001_u32, 248 | 0x0000_0000_u32, 249 | 0x0000_0000_u32, 250 | 0x13c9_e684_u32 251 | ); 252 | parms_impl!( 253 | 44497, 254 | { 44497 / 128 + 1 }, 255 | 330, 256 | 5, 257 | 3, 258 | 9, 259 | 3, 260 | 0xefff_fffb_u32, 261 | 0xdfbe_bfff_u32, 262 | 0xbfbf_7bef_u32, 263 | 0x9ffd_7bff_u32, 264 | 0x0000_0001_u32, 265 | 0x0000_0000_u32, 266 | 0xa3ac_4000_u32, 267 | 0xecc1_327a_u32 268 | ); 269 | parms_impl!( 270 | 86243, 271 | { 86243 / 128 + 1 }, 272 | 366, 273 | 6, 274 | 7, 275 | 19, 276 | 1, 277 | 0xfdbf_fbff_u32, 278 | 0xbff7_ff3f_u32, 279 | 0xfd77_efff_u32, 280 | 0xbf9f_f3ff_u32, 281 | 0x0000_0001_u32, 282 | 0x0000_0000_u32, 283 | 0x0000_0000_u32, 284 | 0xe952_8d85_u32 285 | ); 286 | parms_impl!( 287 | 132049, 288 | { 132049 / 128 + 1 }, 289 | 110, 290 | 19, 291 | 1, 292 | 21, 293 | 1, 294 | 0xffff_bb5f_u32, 295 | 0xfb6e_bf95_u32, 296 | 0xfffe_fffa_u32, 297 | 0xcff7_7fff_u32, 298 | 0x0000_0001_u32, 299 | 0x0000_0000_u32, 300 | 0xcb52_0000_u32, 301 | 0xc7e9_1c7d_u32 302 | ); 303 | parms_impl!( 304 | 216091, 305 | { 216091 / 128 + 1 }, 306 | 627, 307 | 11, 308 | 3, 309 | 10, 310 | 1, 311 | 0xbff7_bff7_u32, 312 | 0xbfff_ffff_u32, 313 | 0xbfff_fa7f_u32, 314 | 0xffdd_fbfb_u32, 315 | 0xf800_0001_u32, 316 | 0x89e8_0709_u32, 317 | 0x3bd2_b64b_u32, 318 | 0x0c64_b1e4_u32 319 | ); 320 | 321 | #[cfg(test)] 322 | mod tests { 323 | use super::*; 324 | 325 | fn split(a: i32x4) -> [u32; 4] { 326 | [extract(a, 0), extract(a, 1), extract(a, 2), extract(a, 3)] 327 | } 328 | 329 | #[test] 330 | fn test_mm_recursion_19937() { 331 | let a = new(1, 2, 3, 4); 332 | let z = SFMTMEXP::<19937, { 19937 / 128 + 1 }>::mm_recursion(a, a, a, a); 333 | let zc = new(33816833, 50856450, 67896067, 1049604); // calculated by C code 334 | assert_eq!(split(z), split(zc)); 335 | 336 | let b = new(431, 232, 83, 14); 337 | let c = new(213, 22, 93, 234); 338 | let d = new(112, 882, 23, 124); 339 | let z = SFMTMEXP::<19937, { 19937 / 128 + 1 }>::mm_recursion(a, b, c, d); 340 | let zc = new(398459137, 1355284994, -363068669, 32506884); // calculated by C code 341 | assert_eq!(split(z), split(zc)); 342 | } 343 | } 344 | --------------------------------------------------------------------------------