mirror of
https://github.com/Magnus167/rustframe.git
synced 2025-11-19 10:06:11 +00:00
107 lines
3.0 KiB
Rust
107 lines
3.0 KiB
Rust
//! Core traits for random number generators and sampling ranges.
|
|
//!
|
|
//! ```
|
|
//! use rustframe::random::{rng, Rng};
|
|
//! let mut r = rng();
|
|
//! let value: f64 = r.random_range(0.0..1.0);
|
|
//! assert!(value >= 0.0 && value < 1.0);
|
|
//! ```
|
|
use std::f64::consts::PI;
|
|
use std::ops::Range;
|
|
|
|
/// Trait implemented by random number generators.
|
|
pub trait Rng {
|
|
/// Generate the next random `u64` value.
|
|
fn next_u64(&mut self) -> u64;
|
|
|
|
/// Generate a value uniformly in the given range.
|
|
fn random_range<T>(&mut self, range: Range<T>) -> T
|
|
where
|
|
T: RangeSample,
|
|
{
|
|
T::from_u64(self.next_u64(), &range)
|
|
}
|
|
|
|
/// Generate a boolean with probability 0.5 of being `true`.
|
|
fn gen_bool(&mut self) -> bool {
|
|
self.random_range(0..2usize) == 1
|
|
}
|
|
|
|
/// Sample from a normal distribution using the Box-Muller transform.
|
|
fn normal(&mut self, mean: f64, sd: f64) -> f64 {
|
|
let u1 = self.random_range(0.0..1.0);
|
|
let u2 = self.random_range(0.0..1.0);
|
|
mean + sd * (-2.0 * u1.ln()).sqrt() * (2.0 * PI * u2).cos()
|
|
}
|
|
}
|
|
|
|
/// Conversion from a raw `u64` into a type within a range.
|
|
pub trait RangeSample: Sized {
|
|
fn from_u64(value: u64, range: &Range<Self>) -> Self;
|
|
}
|
|
|
|
impl RangeSample for usize {
|
|
fn from_u64(value: u64, range: &Range<Self>) -> Self {
|
|
let span = range.end - range.start;
|
|
(value as usize % span) + range.start
|
|
}
|
|
}
|
|
|
|
impl RangeSample for f64 {
|
|
fn from_u64(value: u64, range: &Range<Self>) -> Self {
|
|
let span = range.end - range.start;
|
|
range.start + (value as f64 / u64::MAX as f64) * span
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_range_sample_usize_boundary() {
|
|
assert_eq!(<usize as RangeSample>::from_u64(0, &(0..1)), 0);
|
|
assert_eq!(<usize as RangeSample>::from_u64(u64::MAX, &(0..1)), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_range_sample_f64_boundary() {
|
|
let v0 = <f64 as RangeSample>::from_u64(0, &(0.0..1.0));
|
|
let vmax = <f64 as RangeSample>::from_u64(u64::MAX, &(0.0..1.0));
|
|
assert!(v0 >= 0.0 && v0 < 1.0);
|
|
assert!(vmax > 0.999999999999 && vmax <= 1.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_range_sample_usize_varied() {
|
|
for i in 0..5 {
|
|
let v = <usize as RangeSample>::from_u64(i, &(10..15));
|
|
assert!(v >= 10 && v < 15);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_range_sample_f64_span() {
|
|
for val in [0, u64::MAX / 2, u64::MAX] {
|
|
let f = <f64 as RangeSample>::from_u64(val, &(2.0..4.0));
|
|
assert!(f >= 2.0 && f <= 4.0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_range_sample_usize_single_value() {
|
|
for val in [0, 1, u64::MAX] {
|
|
let n = <usize as RangeSample>::from_u64(val, &(5..6));
|
|
assert_eq!(n, 5);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_range_sample_f64_negative_range() {
|
|
for val in [0, u64::MAX / 3, u64::MAX] {
|
|
let f = <f64 as RangeSample>::from_u64(val, &(-2.0..2.0));
|
|
assert!(f >= -2.0 && f <= 2.0);
|
|
}
|
|
}
|
|
}
|