#[cfg(not(feature = "std"))] use core::time::Duration;
#[cfg(feature = "std")] use std::time::Duration;
use core::ops::{Range, RangeInclusive};
use crate::distributions::float::IntoFloat;
use crate::distributions::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, WideningMultiply};
use crate::distributions::Distribution;
use crate::{Rng, RngCore};
#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use crate::distributions::utils::Float;
#[cfg(feature = "simd_support")] use packed_simd::*;
#[cfg(feature = "serde1")]
use serde::{Serialize, Deserialize};
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Uniform<X: SampleUniform>(X::Sampler);
impl<X: SampleUniform> Uniform<X> {
pub fn new<B1, B2>(low: B1, high: B2) -> Uniform<X>
where
B1: SampleBorrow<X> + Sized,
B2: SampleBorrow<X> + Sized,
{
Uniform(X::Sampler::new(low, high))
}
pub fn new_inclusive<B1, B2>(low: B1, high: B2) -> Uniform<X>
where
B1: SampleBorrow<X> + Sized,
B2: SampleBorrow<X> + Sized,
{
Uniform(X::Sampler::new_inclusive(low, high))
}
}
impl<X: SampleUniform> Distribution<X> for Uniform<X> {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> X {
self.0.sample(rng)
}
}
pub trait SampleUniform: Sized {
type Sampler: UniformSampler<X = Self>;
}
pub trait UniformSampler: Sized {
type X;
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized;
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized;
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X;
fn sample_single<R: Rng + ?Sized, B1, B2>(low: B1, high: B2, rng: &mut R) -> Self::X
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let uniform: Self = UniformSampler::new(low, high);
uniform.sample(rng)
}
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(low: B1, high: B2, rng: &mut R)
-> Self::X
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized
{
let uniform: Self = UniformSampler::new_inclusive(low, high);
uniform.sample(rng)
}
}
impl<X: SampleUniform> From<Range<X>> for Uniform<X> {
fn from(r: ::core::ops::Range<X>) -> Uniform<X> {
Uniform::new(r.start, r.end)
}
}
impl<X: SampleUniform> From<RangeInclusive<X>> for Uniform<X> {
fn from(r: ::core::ops::RangeInclusive<X>) -> Uniform<X> {
Uniform::new_inclusive(r.start(), r.end())
}
}
pub trait SampleBorrow<Borrowed> {
fn borrow(&self) -> &Borrowed;
}
impl<Borrowed> SampleBorrow<Borrowed> for Borrowed
where Borrowed: SampleUniform
{
#[inline(always)]
fn borrow(&self) -> &Borrowed {
self
}
}
impl<'a, Borrowed> SampleBorrow<Borrowed> for &'a Borrowed
where Borrowed: SampleUniform
{
#[inline(always)]
fn borrow(&self) -> &Borrowed {
*self
}
}
pub trait SampleRange<T> {
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T;
fn is_empty(&self) -> bool;
}
impl<T: SampleUniform + PartialOrd> SampleRange<T> for Range<T> {
#[inline]
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T {
T::Sampler::sample_single(self.start, self.end, rng)
}
#[inline]
fn is_empty(&self) -> bool {
!(self.start < self.end)
}
}
impl<T: SampleUniform + PartialOrd> SampleRange<T> for RangeInclusive<T> {
#[inline]
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T {
T::Sampler::sample_single_inclusive(self.start(), self.end(), rng)
}
#[inline]
fn is_empty(&self) -> bool {
!(self.start() <= self.end())
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformInt<X> {
low: X,
range: X,
z: X,
}
macro_rules! uniform_int_impl {
($ty:ty, $unsigned:ident, $u_large:ident) => {
impl SampleUniform for $ty {
type Sampler = UniformInt<$ty>;
}
impl UniformSampler for UniformInt<$ty> {
type X = $ty;
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low < high, "Uniform::new called with `low >= high`");
UniformSampler::new_inclusive(low, high - 1)
}
#[inline]
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(
low <= high,
"Uniform::new_inclusive called with `low > high`"
);
let unsigned_max = ::core::$u_large::MAX;
let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned;
let ints_to_reject = if range > 0 {
let range = $u_large::from(range);
(unsigned_max - range + 1) % range
} else {
0
};
UniformInt {
low,
range: range as $ty,
z: ints_to_reject as $unsigned as $ty,
}
}
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let range = self.range as $unsigned as $u_large;
if range > 0 {
let unsigned_max = ::core::$u_large::MAX;
let zone = unsigned_max - (self.z as $unsigned as $u_large);
loop {
let v: $u_large = rng.gen();
let (hi, lo) = v.wmul(range);
if lo <= zone {
return self.low.wrapping_add(hi as $ty);
}
}
} else {
rng.gen()
}
}
#[inline]
fn sample_single<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Self::X
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low < high, "UniformSampler::sample_single: low >= high");
Self::sample_single_inclusive(low, high - 1, rng)
}
#[inline]
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Self::X
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low <= high, "UniformSampler::sample_single_inclusive: low > high");
let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned as $u_large;
if range == 0 {
return rng.gen();
}
let zone = if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned {
let unsigned_max: $u_large = ::core::$u_large::MAX;
let ints_to_reject = (unsigned_max - range + 1) % range;
unsigned_max - ints_to_reject
} else {
(range << range.leading_zeros()).wrapping_sub(1)
};
loop {
let v: $u_large = rng.gen();
let (hi, lo) = v.wmul(range);
if lo <= zone {
return low.wrapping_add(hi as $ty);
}
}
}
}
};
}
uniform_int_impl! { i8, u8, u32 }
uniform_int_impl! { i16, u16, u32 }
uniform_int_impl! { i32, u32, u32 }
uniform_int_impl! { i64, u64, u64 }
#[cfg(not(target_os = "emscripten"))]
uniform_int_impl! { i128, u128, u128 }
uniform_int_impl! { isize, usize, usize }
uniform_int_impl! { u8, u8, u32 }
uniform_int_impl! { u16, u16, u32 }
uniform_int_impl! { u32, u32, u32 }
uniform_int_impl! { u64, u64, u64 }
uniform_int_impl! { usize, usize, usize }
#[cfg(not(target_os = "emscripten"))]
uniform_int_impl! { u128, u128, u128 }
#[cfg(feature = "simd_support")]
macro_rules! uniform_simd_int_impl {
($ty:ident, $unsigned:ident, $u_scalar:ident) => {
impl SampleUniform for $ty {
type Sampler = UniformInt<$ty>;
}
impl UniformSampler for UniformInt<$ty> {
type X = $ty;
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low.lt(high).all(), "Uniform::new called with `low >= high`");
UniformSampler::new_inclusive(low, high - 1)
}
#[inline]
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low.le(high).all(),
"Uniform::new_inclusive called with `low > high`");
let unsigned_max = ::core::$u_scalar::MAX;
let range: $unsigned = ((high - low) + 1).cast();
let not_full_range = range.gt($unsigned::splat(0));
let modulo = not_full_range.select(range, $unsigned::splat(unsigned_max));
let ints_to_reject = (unsigned_max - range + 1) % modulo;
let zone = unsigned_max - ints_to_reject;
UniformInt {
low,
range: range.cast(),
z: zone.cast(),
}
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let range: $unsigned = self.range.cast();
let zone: $unsigned = self.z.cast();
let mut v: $unsigned = rng.gen();
loop {
let (hi, lo) = v.wmul(range);
let mask = lo.le(zone);
if mask.all() {
let hi: $ty = hi.cast();
let result = self.low + hi;
let v: $ty = v.cast();
return range.gt($unsigned::splat(0)).select(result, v);
}
v = mask.select(v, rng.gen());
}
}
}
};
($(($unsigned:ident, $signed:ident),)+ $u_scalar:ident) => {
$(
uniform_simd_int_impl!($unsigned, $unsigned, $u_scalar);
uniform_simd_int_impl!($signed, $unsigned, $u_scalar);
)+
};
}
#[cfg(feature = "simd_support")]
uniform_simd_int_impl! {
(u64x2, i64x2),
(u64x4, i64x4),
(u64x8, i64x8),
u64
}
#[cfg(feature = "simd_support")]
uniform_simd_int_impl! {
(u32x2, i32x2),
(u32x4, i32x4),
(u32x8, i32x8),
(u32x16, i32x16),
u32
}
#[cfg(feature = "simd_support")]
uniform_simd_int_impl! {
(u16x2, i16x2),
(u16x4, i16x4),
(u16x8, i16x8),
(u16x16, i16x16),
(u16x32, i16x32),
u16
}
#[cfg(feature = "simd_support")]
uniform_simd_int_impl! {
(u8x2, i8x2),
(u8x4, i8x4),
(u8x8, i8x8),
(u8x16, i8x16),
(u8x32, i8x32),
(u8x64, i8x64),
u8
}
impl SampleUniform for char {
type Sampler = UniformChar;
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformChar {
sampler: UniformInt<u32>,
}
const CHAR_SURROGATE_START: u32 = 0xD800;
const CHAR_SURROGATE_LEN: u32 = 0xE000 - CHAR_SURROGATE_START;
fn char_to_comp_u32(c: char) -> u32 {
match c as u32 {
c if c >= CHAR_SURROGATE_START => c - CHAR_SURROGATE_LEN,
c => c,
}
}
impl UniformSampler for UniformChar {
type X = char;
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = char_to_comp_u32(*low_b.borrow());
let high = char_to_comp_u32(*high_b.borrow());
let sampler = UniformInt::<u32>::new(low, high);
UniformChar { sampler }
}
#[inline]
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = char_to_comp_u32(*low_b.borrow());
let high = char_to_comp_u32(*high_b.borrow());
let sampler = UniformInt::<u32>::new_inclusive(low, high);
UniformChar { sampler }
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let mut x = self.sampler.sample(rng);
if x >= CHAR_SURROGATE_START {
x += CHAR_SURROGATE_LEN;
}
unsafe { core::char::from_u32_unchecked(x) }
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformFloat<X> {
low: X,
scale: X,
}
macro_rules! uniform_float_impl {
($ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => {
impl SampleUniform for $ty {
type Sampler = UniformFloat<$ty>;
}
impl UniformSampler for UniformFloat<$ty> {
type X = $ty;
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low.all_lt(high), "Uniform::new called with `low >= high`");
assert!(
low.all_finite() && high.all_finite(),
"Uniform::new called with non-finite boundaries"
);
let max_rand = <$ty>::splat(
(::core::$u_scalar::MAX >> $bits_to_discard).into_float_with_exponent(0) - 1.0,
);
let mut scale = high - low;
loop {
let mask = (scale * max_rand + low).ge_mask(high);
if mask.none() {
break;
}
scale = scale.decrease_masked(mask);
}
debug_assert!(<$ty>::splat(0.0).all_le(scale));
UniformFloat { low, scale }
}
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(
low.all_le(high),
"Uniform::new_inclusive called with `low > high`"
);
assert!(
low.all_finite() && high.all_finite(),
"Uniform::new_inclusive called with non-finite boundaries"
);
let max_rand = <$ty>::splat(
(::core::$u_scalar::MAX >> $bits_to_discard).into_float_with_exponent(0) - 1.0,
);
let mut scale = (high - low) / max_rand;
loop {
let mask = (scale * max_rand + low).gt_mask(high);
if mask.none() {
break;
}
scale = scale.decrease_masked(mask);
}
debug_assert!(<$ty>::splat(0.0).all_le(scale));
UniformFloat { low, scale }
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let value1_2 = (rng.gen::<$uty>() >> $bits_to_discard).into_float_with_exponent(0);
let value0_1 = value1_2 - 1.0;
value0_1 * self.scale + self.low
}
#[inline]
fn sample_single<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Self::X
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(
low.all_lt(high),
"UniformSampler::sample_single: low >= high"
);
let mut scale = high - low;
loop {
let value1_2 =
(rng.gen::<$uty>() >> $bits_to_discard).into_float_with_exponent(0);
let value0_1 = value1_2 - 1.0;
let res = value0_1 * scale + low;
debug_assert!(low.all_le(res) || !scale.all_finite());
if res.all_lt(high) {
return res;
}
let mask = !scale.finite_mask();
if mask.any() {
assert!(
low.all_finite() && high.all_finite(),
"Uniform::sample_single: low and high must be finite"
);
scale = scale.decrease_masked(mask);
}
}
}
}
};
}
uniform_float_impl! { f32, u32, f32, u32, 32 - 23 }
uniform_float_impl! { f64, u64, f64, u64, 64 - 52 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f32x2, u32x2, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f32x4, u32x4, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f32x8, u32x8, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f32x16, u32x16, f32, u32, 32 - 23 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f64x2, u64x2, f64, u64, 64 - 52 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f64x4, u64x4, f64, u64, 64 - 52 }
#[cfg(feature = "simd_support")]
uniform_float_impl! { f64x8, u64x8, f64, u64, 64 - 52 }
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct UniformDuration {
mode: UniformDurationMode,
offset: u32,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
enum UniformDurationMode {
Small {
secs: u64,
nanos: Uniform<u32>,
},
Medium {
nanos: Uniform<u64>,
},
Large {
max_secs: u64,
max_nanos: u32,
secs: Uniform<u64>,
},
}
impl SampleUniform for Duration {
type Sampler = UniformDuration;
}
impl UniformSampler for UniformDuration {
type X = Duration;
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(low < high, "Uniform::new called with `low >= high`");
UniformDuration::new_inclusive(low, high - Duration::new(0, 1))
}
#[inline]
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
assert!(
low <= high,
"Uniform::new_inclusive called with `low > high`"
);
let low_s = low.as_secs();
let low_n = low.subsec_nanos();
let mut high_s = high.as_secs();
let mut high_n = high.subsec_nanos();
if high_n < low_n {
high_s -= 1;
high_n += 1_000_000_000;
}
let mode = if low_s == high_s {
UniformDurationMode::Small {
secs: low_s,
nanos: Uniform::new_inclusive(low_n, high_n),
}
} else {
let max = high_s
.checked_mul(1_000_000_000)
.and_then(|n| n.checked_add(u64::from(high_n)));
if let Some(higher_bound) = max {
let lower_bound = low_s * 1_000_000_000 + u64::from(low_n);
UniformDurationMode::Medium {
nanos: Uniform::new_inclusive(lower_bound, higher_bound),
}
} else {
let max_nanos = high_n - low_n;
UniformDurationMode::Large {
max_secs: high_s,
max_nanos,
secs: Uniform::new_inclusive(low_s, high_s),
}
}
};
UniformDuration {
mode,
offset: low_n,
}
}
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
match self.mode {
UniformDurationMode::Small { secs, nanos } => {
let n = nanos.sample(rng);
Duration::new(secs, n)
}
UniformDurationMode::Medium { nanos } => {
let nanos = nanos.sample(rng);
Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
}
UniformDurationMode::Large {
max_secs,
max_nanos,
secs,
} => {
let nano_range = Uniform::new(0, 1_000_000_000);
loop {
let s = secs.sample(rng);
let n = nano_range.sample(rng);
if !(s == max_secs && n > max_nanos) {
let sum = n + self.offset;
break Duration::new(s, sum);
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rngs::mock::StepRng;
#[test]
#[cfg(feature = "serde1")]
fn test_serialization_uniform_duration() {
let distr = UniformDuration::new(std::time::Duration::from_secs(10), std::time::Duration::from_secs(60));
let de_distr: UniformDuration = bincode::deserialize(&bincode::serialize(&distr).unwrap()).unwrap();
assert_eq!(
distr.offset, de_distr.offset
);
match (distr.mode, de_distr.mode) {
(UniformDurationMode::Small {secs: a_secs, nanos: a_nanos}, UniformDurationMode::Small {secs, nanos}) => {
assert_eq!(a_secs, secs);
assert_eq!(a_nanos.0.low, nanos.0.low);
assert_eq!(a_nanos.0.range, nanos.0.range);
assert_eq!(a_nanos.0.z, nanos.0.z);
}
(UniformDurationMode::Medium {nanos: a_nanos} , UniformDurationMode::Medium {nanos}) => {
assert_eq!(a_nanos.0.low, nanos.0.low);
assert_eq!(a_nanos.0.range, nanos.0.range);
assert_eq!(a_nanos.0.z, nanos.0.z);
}
(UniformDurationMode::Large {max_secs:a_max_secs, max_nanos:a_max_nanos, secs:a_secs}, UniformDurationMode::Large {max_secs, max_nanos, secs} ) => {
assert_eq!(a_max_secs, max_secs);
assert_eq!(a_max_nanos, max_nanos);
assert_eq!(a_secs.0.low, secs.0.low);
assert_eq!(a_secs.0.range, secs.0.range);
assert_eq!(a_secs.0.z, secs.0.z);
}
_ => panic!("`UniformDurationMode` was not serialized/deserialized correctly")
}
}
#[test]
#[cfg(feature = "serde1")]
fn test_uniform_serialization() {
let unit_box: Uniform<i32> = Uniform::new(-1, 1);
let de_unit_box: Uniform<i32> = bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap();
assert_eq!(unit_box.0.low, de_unit_box.0.low);
assert_eq!(unit_box.0.range, de_unit_box.0.range);
assert_eq!(unit_box.0.z, de_unit_box.0.z);
let unit_box: Uniform<f32> = Uniform::new(-1., 1.);
let de_unit_box: Uniform<f32> = bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap();
assert_eq!(unit_box.0.low, de_unit_box.0.low);
assert_eq!(unit_box.0.scale, de_unit_box.0.scale);
}
#[should_panic]
#[test]
fn test_uniform_bad_limits_equal_int() {
Uniform::new(10, 10);
}
#[test]
fn test_uniform_good_limits_equal_int() {
let mut rng = crate::test::rng(804);
let dist = Uniform::new_inclusive(10, 10);
for _ in 0..20 {
assert_eq!(rng.sample(dist), 10);
}
}
#[should_panic]
#[test]
fn test_uniform_bad_limits_flipped_int() {
Uniform::new(10, 5);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_integers() {
#[cfg(not(target_os = "emscripten"))] use core::{i128, u128};
use core::{i16, i32, i64, i8, isize};
use core::{u16, u32, u64, u8, usize};
let mut rng = crate::test::rng(251);
macro_rules! t {
($ty:ident, $v:expr, $le:expr, $lt:expr) => {{
for &(low, high) in $v.iter() {
let my_uniform = Uniform::new(low, high);
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $lt(v, high));
}
let my_uniform = Uniform::new_inclusive(low, high);
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $le(v, high));
}
let my_uniform = Uniform::new(&low, high);
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $lt(v, high));
}
let my_uniform = Uniform::new_inclusive(&low, &high);
for _ in 0..1000 {
let v: $ty = rng.sample(my_uniform);
assert!($le(low, v) && $le(v, high));
}
for _ in 0..1000 {
let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng);
assert!($le(low, v) && $lt(v, high));
}
for _ in 0..1000 {
let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng);
assert!($le(low, v) && $le(v, high));
}
}
}};
($($ty:ident),*) => {{
$(t!(
$ty,
[(0, 10), (10, 127), ($ty::MIN, $ty::MAX)],
|x, y| x <= y,
|x, y| x < y
);)*
}};
($($ty:ident),* => $scalar:ident) => {{
$(t!(
$ty,
[
($ty::splat(0), $ty::splat(10)),
($ty::splat(10), $ty::splat(127)),
($ty::splat($scalar::MIN), $ty::splat($scalar::MAX)),
],
|x: $ty, y| x.le(y).all(),
|x: $ty, y| x.lt(y).all()
);)*
}};
}
t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(not(target_os = "emscripten"))]
t!(i128, u128);
#[cfg(feature = "simd_support")]
{
t!(u8x2, u8x4, u8x8, u8x16, u8x32, u8x64 => u8);
t!(i8x2, i8x4, i8x8, i8x16, i8x32, i8x64 => i8);
t!(u16x2, u16x4, u16x8, u16x16, u16x32 => u16);
t!(i16x2, i16x4, i16x8, i16x16, i16x32 => i16);
t!(u32x2, u32x4, u32x8, u32x16 => u32);
t!(i32x2, i32x4, i32x8, i32x16 => i32);
t!(u64x2, u64x4, u64x8 => u64);
t!(i64x2, i64x4, i64x8 => i64);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_char() {
let mut rng = crate::test::rng(891);
let mut max = core::char::from_u32(0).unwrap();
for _ in 0..100 {
let c = rng.gen_range('A'..='Z');
assert!('A' <= c && c <= 'Z');
max = max.max(c);
}
assert_eq!(max, 'Z');
let d = Uniform::new(
core::char::from_u32(0xD7F0).unwrap(),
core::char::from_u32(0xE010).unwrap(),
);
for _ in 0..100 {
let c = d.sample(&mut rng);
assert!((c as u32) < 0xD800 || (c as u32) > 0xDFFF);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_floats() {
let mut rng = crate::test::rng(252);
let mut zero_rng = StepRng::new(0, 0);
let mut max_rng = StepRng::new(0xffff_ffff_ffff_ffff, 0);
macro_rules! t {
($ty:ty, $f_scalar:ident, $bits_shifted:expr) => {{
let v: &[($f_scalar, $f_scalar)] = &[
(0.0, 100.0),
(-1e35, -1e25),
(1e-35, 1e-25),
(-1e35, 1e35),
(<$f_scalar>::from_bits(0), <$f_scalar>::from_bits(3)),
(-<$f_scalar>::from_bits(10), -<$f_scalar>::from_bits(1)),
(-<$f_scalar>::from_bits(5), 0.0),
(-<$f_scalar>::from_bits(7), -0.0),
(10.0, ::core::$f_scalar::MAX),
(-100.0, ::core::$f_scalar::MAX),
(-::core::$f_scalar::MAX / 5.0, ::core::$f_scalar::MAX),
(-::core::$f_scalar::MAX, ::core::$f_scalar::MAX / 5.0),
(-::core::$f_scalar::MAX * 0.8, ::core::$f_scalar::MAX * 0.7),
(-::core::$f_scalar::MAX, ::core::$f_scalar::MAX),
];
for &(low_scalar, high_scalar) in v.iter() {
for lane in 0..<$ty>::lanes() {
let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar);
let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar);
let my_uniform = Uniform::new(low, high);
let my_incl_uniform = Uniform::new_inclusive(low, high);
for _ in 0..100 {
let v = rng.sample(my_uniform).extract(lane);
assert!(low_scalar <= v && v < high_scalar);
let v = rng.sample(my_incl_uniform).extract(lane);
assert!(low_scalar <= v && v <= high_scalar);
let v = <$ty as SampleUniform>::Sampler
::sample_single(low, high, &mut rng).extract(lane);
assert!(low_scalar <= v && v < high_scalar);
}
assert_eq!(
rng.sample(Uniform::new_inclusive(low, low)).extract(lane),
low_scalar
);
assert_eq!(zero_rng.sample(my_uniform).extract(lane), low_scalar);
assert_eq!(zero_rng.sample(my_incl_uniform).extract(lane), low_scalar);
assert_eq!(<$ty as SampleUniform>::Sampler
::sample_single(low, high, &mut zero_rng)
.extract(lane), low_scalar);
assert!(max_rng.sample(my_uniform).extract(lane) < high_scalar);
assert!(max_rng.sample(my_incl_uniform).extract(lane) <= high_scalar);
if (high_scalar - low_scalar) > 0.0001 {
let mut lowering_max_rng = StepRng::new(
0xffff_ffff_ffff_ffff,
(-1i64 << $bits_shifted) as u64,
);
assert!(
<$ty as SampleUniform>::Sampler
::sample_single(low, high, &mut lowering_max_rng)
.extract(lane) < high_scalar
);
}
}
}
assert_eq!(
rng.sample(Uniform::new_inclusive(
::core::$f_scalar::MAX,
::core::$f_scalar::MAX
)),
::core::$f_scalar::MAX
);
assert_eq!(
rng.sample(Uniform::new_inclusive(
-::core::$f_scalar::MAX,
-::core::$f_scalar::MAX
)),
-::core::$f_scalar::MAX
);
}};
}
t!(f32, f32, 32 - 23);
t!(f64, f64, 64 - 52);
#[cfg(feature = "simd_support")]
{
t!(f32x2, f32, 32 - 23);
t!(f32x4, f32, 32 - 23);
t!(f32x8, f32, 32 - 23);
t!(f32x16, f32, 32 - 23);
t!(f64x2, f64, 64 - 52);
t!(f64x4, f64, 64 - 52);
t!(f64x8, f64, 64 - 52);
}
}
#[test]
#[cfg(all(
feature = "std",
not(target_arch = "wasm32"),
not(target_arch = "asmjs")
))]
fn test_float_assertions() {
use super::SampleUniform;
use std::panic::catch_unwind;
fn range<T: SampleUniform>(low: T, high: T) {
let mut rng = crate::test::rng(253);
T::Sampler::sample_single(low, high, &mut rng);
}
macro_rules! t {
($ty:ident, $f_scalar:ident) => {{
let v: &[($f_scalar, $f_scalar)] = &[
(::std::$f_scalar::NAN, 0.0),
(1.0, ::std::$f_scalar::NAN),
(::std::$f_scalar::NAN, ::std::$f_scalar::NAN),
(1.0, 0.5),
(::std::$f_scalar::MAX, -::std::$f_scalar::MAX),
(::std::$f_scalar::INFINITY, ::std::$f_scalar::INFINITY),
(
::std::$f_scalar::NEG_INFINITY,
::std::$f_scalar::NEG_INFINITY,
),
(::std::$f_scalar::NEG_INFINITY, 5.0),
(5.0, ::std::$f_scalar::INFINITY),
(::std::$f_scalar::NAN, ::std::$f_scalar::INFINITY),
(::std::$f_scalar::NEG_INFINITY, ::std::$f_scalar::NAN),
(::std::$f_scalar::NEG_INFINITY, ::std::$f_scalar::INFINITY),
];
for &(low_scalar, high_scalar) in v.iter() {
for lane in 0..<$ty>::lanes() {
let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar);
let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar);
assert!(catch_unwind(|| range(low, high)).is_err());
assert!(catch_unwind(|| Uniform::new(low, high)).is_err());
assert!(catch_unwind(|| Uniform::new_inclusive(low, high)).is_err());
assert!(catch_unwind(|| range(low, low)).is_err());
assert!(catch_unwind(|| Uniform::new(low, low)).is_err());
}
}
}};
}
t!(f32, f32);
t!(f64, f64);
#[cfg(feature = "simd_support")]
{
t!(f32x2, f32);
t!(f32x4, f32);
t!(f32x8, f32);
t!(f32x16, f32);
t!(f64x2, f64);
t!(f64x4, f64);
t!(f64x8, f64);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_durations() {
#[cfg(not(feature = "std"))] use core::time::Duration;
#[cfg(feature = "std")] use std::time::Duration;
let mut rng = crate::test::rng(253);
let v = &[
(Duration::new(10, 50000), Duration::new(100, 1234)),
(Duration::new(0, 100), Duration::new(1, 50)),
(
Duration::new(0, 0),
Duration::new(u64::max_value(), 999_999_999),
),
];
for &(low, high) in v.iter() {
let my_uniform = Uniform::new(low, high);
for _ in 0..1000 {
let v = rng.sample(my_uniform);
assert!(low <= v && v < high);
}
}
}
#[test]
fn test_custom_uniform() {
use crate::distributions::uniform::{
SampleBorrow, SampleUniform, UniformFloat, UniformSampler,
};
#[derive(Clone, Copy, PartialEq, PartialOrd)]
struct MyF32 {
x: f32,
}
#[derive(Clone, Copy, Debug)]
struct UniformMyF32(UniformFloat<f32>);
impl UniformSampler for UniformMyF32 {
type X = MyF32;
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformMyF32(UniformFloat::<f32>::new(low.borrow().x, high.borrow().x))
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformSampler::new(low, high)
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
MyF32 {
x: self.0.sample(rng),
}
}
}
impl SampleUniform for MyF32 {
type Sampler = UniformMyF32;
}
let (low, high) = (MyF32 { x: 17.0f32 }, MyF32 { x: 22.0f32 });
let uniform = Uniform::new(low, high);
let mut rng = crate::test::rng(804);
for _ in 0..100 {
let x: MyF32 = rng.sample(uniform);
assert!(low <= x && x < high);
}
}
#[test]
fn test_uniform_from_std_range() {
let r = Uniform::from(2u32..7);
assert_eq!(r.0.low, 2);
assert_eq!(r.0.range, 5);
let r = Uniform::from(2.0f64..7.0);
assert_eq!(r.0.low, 2.0);
assert_eq!(r.0.scale, 5.0);
}
#[test]
fn test_uniform_from_std_range_inclusive() {
let r = Uniform::from(2u32..=6);
assert_eq!(r.0.low, 2);
assert_eq!(r.0.range, 5);
let r = Uniform::from(2.0f64..=7.0);
assert_eq!(r.0.low, 2.0);
assert!(r.0.scale > 5.0);
assert!(r.0.scale < 5.0 + 1e-14);
}
#[test]
fn value_stability() {
fn test_samples<T: SampleUniform + Copy + core::fmt::Debug + PartialEq>(
lb: T, ub: T, expected_single: &[T], expected_multiple: &[T],
) where Uniform<T>: Distribution<T> {
let mut rng = crate::test::rng(897);
let mut buf = [lb; 3];
for x in &mut buf {
*x = T::Sampler::sample_single(lb, ub, &mut rng);
}
assert_eq!(&buf, expected_single);
let distr = Uniform::new(lb, ub);
for x in &mut buf {
*x = rng.sample(&distr);
}
assert_eq!(&buf, expected_multiple);
}
test_samples(11u8, 219, &[17, 66, 214], &[181, 93, 165]);
test_samples(11u32, 219, &[17, 66, 214], &[181, 93, 165]);
test_samples(0f32, 1e-2f32, &[0.0003070104, 0.0026630748, 0.00979833], &[
0.008194133,
0.00398172,
0.007428536,
]);
test_samples(
-1e10f64,
1e10f64,
&[-4673848682.871551, 6388267422.932352, 4857075081.198343],
&[1173375212.1808167, 1917642852.109581, 2365076174.3153973],
);
test_samples(
Duration::new(2, 0),
Duration::new(4, 0),
&[
Duration::new(2, 532615131),
Duration::new(3, 638826742),
Duration::new(3, 485707508),
],
&[
Duration::new(3, 117337521),
Duration::new(3, 191764285),
Duration::new(3, 236507617),
],
);
}
}