Skip to content

Commit

Permalink
Add Xoshiro256++ random number generator (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
ekzhang committed Aug 1, 2021
1 parent 64f3755 commit dc2fabd
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/graph/connectivity.rs
Expand Up @@ -45,7 +45,7 @@ impl ConnectivityData {
///
/// Multiple-edges and self-loops are correctly handled.
pub struct ConnectivityGraph<'a> {
// Immutable graph, frozen for the lifetime of the ConnectivityGraph object.
/// Immutable graph, frozen for the lifetime of the ConnectivityGraph object.
pub graph: &'a Graph,
/// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc.
pub cc: Vec<usize>,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
@@ -1,8 +1,10 @@
//! Algorithms Cookbook in Rust.

pub mod caching;
pub mod graph;
pub mod math;
pub mod order;
pub mod range_query;
pub mod rng;
pub mod scanner;
pub mod string_proc;
104 changes: 104 additions & 0 deletions src/rng.rs
@@ -0,0 +1,104 @@
//! Pseudorandom number generators (PRNGs).

/// A simple and efficient random number generator.
pub type SmallRng = Xoshiro256PlusPlus;

/// A xoshiro256++ random number generator.
///
/// This is a simplified version of the `SmallRng` implementation from the
/// excellent `rand` crate, keeping only essential features.
///
/// The xoshiro256++ algorithm is not suitable for cryptographic purposes, but
/// is very fast and has excellent statistical properties.
///
/// * Source: [Docs.rs](https://docs.rs/rand/0.8.4/src/rand/rngs/xoshiro256plusplus.rs.html)
/// * Theory: [Xorshift - Wikipedia](https://en.wikipedia.org/wiki/Xorshift)
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Xoshiro256PlusPlus {
s: [u64; 4],
}

impl Xoshiro256PlusPlus {
/// Construct a new RNG from a 64-bit seed.
pub fn new(mut state: u64) -> Self {
const PHI: u64 = 0x9e3779b97f4a7c15;
let mut seed = <[u64; 4]>::default();
for chunk in &mut seed {
state = state.wrapping_add(PHI);
let mut z = state;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
z = z ^ (z >> 31);
*chunk = z;
}
Self { s: seed }
}

/// Generate a random `u32`.
#[inline]
pub fn next_u32(&mut self) -> u32 {
(self.next_u64() >> 32) as u32
}

/// Generate a random `u64`.
#[inline]
pub fn next_u64(&mut self) -> u64 {
let result_plusplus = self.s[0]
.wrapping_add(self.s[3])
.rotate_left(23)
.wrapping_add(self.s[0]);

let t = self.s[1] << 17;

self.s[2] ^= self.s[0];
self.s[3] ^= self.s[1];
self.s[1] ^= self.s[2];
self.s[0] ^= self.s[3];

self.s[2] ^= t;

self.s[3] = self.s[3].rotate_left(45);

result_plusplus
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_xoshiro256plusplus() {
let mut rng = Xoshiro256PlusPlus::new(42);
assert_eq!(rng.next_u64(), 15021278609987233951);
assert_eq!(rng.next_u64(), 5881210131331364753);
assert_eq!(rng.next_u64(), 18149643915985481100);
assert_eq!(rng.next_u64(), 12933668939759105464);
assert_eq!(rng.next_u64(), 14637574242682825331);
assert_eq!(rng.next_u64(), 10848501901068131965);
assert_eq!(rng.next_u64(), 2312344417745909078);
assert_eq!(rng.next_u64(), 11162538943635311430);
}

#[test]
fn reference() {
let mut rng = Xoshiro256PlusPlus { s: [1, 2, 3, 4] };
// These values were produced with the reference implementation:
// http://xoshiro.di.unimi.it/xoshiro256plusplus.c
let expected = [
41943041,
58720359,
3588806011781223,
3591011842654386,
9228616714210784205,
9973669472204895162,
14011001112246962877,
12406186145184390807,
15849039046786891736,
10450023813501588000,
];
for &e in &expected {
assert_eq!(rng.next_u64(), e);
}
}
}

0 comments on commit dc2fabd

Please sign in to comment.