mirror of https://github.com/smol-rs/fastrand
246 lines
5.2 KiB
Rust
246 lines
5.2 KiB
Rust
//! A simple random number generator.
|
|
//!
|
|
//! Easy to use but not cryptographically secure.
|
|
//!
|
|
//! # Examples
|
|
//!
|
|
//! Flip a coin:
|
|
//!
|
|
//! ```
|
|
//! if fastrand::bool() {
|
|
//! println!("heads");
|
|
//! } else {
|
|
//! println!("tails");
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! Generate a random `i32`:
|
|
//!
|
|
//! ```
|
|
//! let num = fastrand::i32(..);
|
|
//! ```
|
|
//!
|
|
//! Choose a random element in an array:
|
|
//!
|
|
//! ```
|
|
//! let v = vec![1, 2, 3, 4, 5];
|
|
//! let i = fastrand::usize(..v.len());
|
|
//! let elem = v[i];
|
|
//! ```
|
|
//!
|
|
//! Shuffle an array:
|
|
//!
|
|
//! ```
|
|
//! let mut v = vec![1, 2, 3, 4, 5];
|
|
//! fastrand::shuffle(&mut v);
|
|
//! ```
|
|
|
|
use std::cell::Cell;
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::ops::{Bound, RangeBounds};
|
|
use std::thread;
|
|
use std::time::Instant;
|
|
|
|
/// Generates a random `u64`.
|
|
fn gen_u64() -> u64 {
|
|
thread_local! {
|
|
static RNG: Cell<(u64, u64)> = Cell::new((seed(), seed()));
|
|
}
|
|
|
|
#[cold]
|
|
fn seed() -> u64 {
|
|
let mut hasher = DefaultHasher::new();
|
|
thread::current().id().hash(&mut hasher);
|
|
Instant::now().hash(&mut hasher);
|
|
hasher.finish()
|
|
}
|
|
|
|
// xorshift+: https://en.wikipedia.org/wiki/Xorshift
|
|
RNG.try_with(|rng| {
|
|
let (mut a, mut b) = rng.get();
|
|
let (mut t, s) = (a, b);
|
|
a = s;
|
|
t ^= t << 23;
|
|
t ^= t >> 17;
|
|
t ^= s ^ (s >> 26);
|
|
b = t;
|
|
rng.set((a, b));
|
|
t.wrapping_add(s)
|
|
})
|
|
.unwrap_or_else(|_| seed())
|
|
}
|
|
|
|
/// Generates a random `u128`.
|
|
fn gen_u128() -> u128 {
|
|
((gen_u64() as u128) << 64) | (gen_u64() as u128)
|
|
}
|
|
|
|
/// Computes `(a * b) >> 32`.
|
|
fn mul_high_u32(a: u32, b: u32) -> u32 {
|
|
(((a as u64) * (b as u64)) >> 32) as u32
|
|
}
|
|
|
|
/// Computes `(a * b) >> 64`.
|
|
fn mul_high_u64(a: u64, b: u64) -> u64 {
|
|
(((a as u128) * (b as u128)) >> 64) as u64
|
|
}
|
|
|
|
/// Computes `(a * b) >> 128`.
|
|
fn mul_high_u128(a: u128, b: u128) -> u128 {
|
|
let a_lo = a as u64 as u128;
|
|
let a_hi = (a >> 64) as u64 as u128;
|
|
|
|
let b_lo = b as u64 as u128;
|
|
let b_hi = (b >> 64) as u64 as u128;
|
|
|
|
let carry = (a_lo * b_lo) >> 64;
|
|
let carry = (a_hi * b_lo + a_lo * b_hi + carry) >> 64;
|
|
a_hi * b_hi + carry
|
|
}
|
|
|
|
/// Generates a random `u32` in `0..n`.
|
|
fn gen_mod_u32(n: u32) -> u32 {
|
|
mul_high_u32(gen_u64() as u32, n)
|
|
}
|
|
|
|
/// Generates a random `u64` in `0..n`.
|
|
fn gen_mod_u64(n: u64) -> u64 {
|
|
mul_high_u64(gen_u64(), n)
|
|
}
|
|
|
|
/// Generates a random `u128` in `0..n`.
|
|
fn gen_mod_u128(n: u128) -> u128 {
|
|
mul_high_u128(gen_u128(), n)
|
|
}
|
|
|
|
macro_rules! code {
|
|
($t:tt, $gen:expr, $mod:ident, $doc:tt) => {
|
|
#[doc = $doc]
|
|
///
|
|
/// Panics if the range is empty.
|
|
pub fn $t(range: impl RangeBounds<$t>) -> $t {
|
|
let low = match range.start_bound() {
|
|
Bound::Unbounded => $t::MIN,
|
|
Bound::Included(&x) => x,
|
|
Bound::Excluded(&x) => x
|
|
.checked_add(1)
|
|
.unwrap_or_else(|| panic!("invalid start bound: {:?}", range.start_bound())),
|
|
};
|
|
|
|
let high = match range.end_bound() {
|
|
Bound::Unbounded => $t::MAX,
|
|
Bound::Included(&x) => x,
|
|
Bound::Excluded(&x) => x
|
|
.checked_sub(1)
|
|
.unwrap_or_else(|| panic!("invalid end bound: {:?}", range.end_bound())),
|
|
};
|
|
|
|
if low > high {
|
|
panic!(
|
|
"empty range: {:?}..{:?}",
|
|
range.start_bound(),
|
|
range.end_bound()
|
|
);
|
|
}
|
|
|
|
if low == $t::MIN && high == $t::MAX {
|
|
$gen() as $t
|
|
} else {
|
|
let len = high.wrapping_sub(low).wrapping_add(1);
|
|
low.wrapping_add($mod(len as _) as $t)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
code!(
|
|
u8,
|
|
gen_u64,
|
|
gen_mod_u32,
|
|
"Generates a random `u8` in the given range."
|
|
);
|
|
code!(
|
|
i8,
|
|
gen_u64,
|
|
gen_mod_u32,
|
|
"Generates a random `i8` in the given range."
|
|
);
|
|
|
|
code!(
|
|
u16,
|
|
gen_u64,
|
|
gen_mod_u32,
|
|
"Generates a random `u16` in the given range."
|
|
);
|
|
code!(
|
|
i16,
|
|
gen_u64,
|
|
gen_mod_u32,
|
|
"Generates a random `i16` in the given range."
|
|
);
|
|
|
|
code!(
|
|
u32,
|
|
gen_u64,
|
|
gen_mod_u32,
|
|
"Generates a random `u32` in the given range."
|
|
);
|
|
code!(
|
|
i32,
|
|
gen_u64,
|
|
gen_mod_u32,
|
|
"Generates a random `i32` in the given range."
|
|
);
|
|
|
|
code!(
|
|
u64,
|
|
gen_u64,
|
|
gen_mod_u64,
|
|
"Generates a random `u64` in the given range."
|
|
);
|
|
code!(
|
|
i64,
|
|
gen_u64,
|
|
gen_mod_u64,
|
|
"Generates a random `i64` in the given range."
|
|
);
|
|
|
|
code!(
|
|
usize,
|
|
gen_u64,
|
|
gen_mod_u64,
|
|
"Generates a random `usize` in the given range."
|
|
);
|
|
code!(
|
|
isize,
|
|
gen_u64,
|
|
gen_mod_u64,
|
|
"Generates a random `isize` in the given range."
|
|
);
|
|
|
|
code!(
|
|
u128,
|
|
gen_u128,
|
|
gen_mod_u128,
|
|
"Generates a random `u128` in the given range."
|
|
);
|
|
code!(
|
|
i128,
|
|
gen_u128,
|
|
gen_mod_u128,
|
|
"Generates a random `i128` in the given range."
|
|
);
|
|
|
|
/// Generates a random `bool`.
|
|
pub fn bool() -> bool {
|
|
self::u8(..) % 2 == 0
|
|
}
|
|
|
|
/// Shuffles a slice randomly.
|
|
pub fn shuffle<T>(slice: &mut [T]) {
|
|
for i in 1..slice.len() {
|
|
slice.swap(i, self::usize(..=i));
|
|
}
|
|
}
|