urandom 0.2.2

Produce and consume randomness, to convert them to useful types and distributions, and some randomness-related algorithms.
Documentation
use core::{array, char, num};
use super::*;

/// Generic random value distribution, implemented for many primitive types.
///
/// Usually generates values with a numerically uniform distribution, and with a range appropriate to the type.
///
/// # Provided implementations
///
/// Assuming the provided `Rng` is well-behaved, these implementations generate values with the following ranges and distributions:
///
/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed over all values of the type.
/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all code points in the range `0..0x11_0000`,
///   except for the range `0xD800..0xE000` (the surrogate code points). This includes unassigned/reserved code points.
/// * `bool`: Generates `true` or `false`, each with equal probability.
/// * Floating point types (`f32` and `f64`): Uniformly distributed in the half-open interval `[1.0, 2.0)`. See notes below.
/// * Wrapping integers (`Wrapping<T>`), besides the type identical to their normal integer variants.
/// * Non-zero integers (`NonZeroU32`, `NonZeroU64`, etc.): Uniformly distributed over all non-zero values of the type.
///
/// The `StandardUniform` distribution also supports generation of the following compound types where all component types are supported:
///
/// * Tuples (up to 12 elements): each element is generated sequentially.
/// * Arrays: each element is generated sequentially; see also [`Random::fill_bytes`] which is faster for u32 and smaller types.
///
/// # Examples
///
/// ```
/// use urandom::distr::StandardUniform;
///
/// let value: f32 = urandom::new().sample(&StandardUniform);
/// assert!(value >= 1.0 && value < 2.0, "f32 from [1.0, 2.0): {value}");
/// ```
///
/// # Custom implementations
///
/// The `StandardUniform` distribution may be implemented for user types as follows:
///
/// ```
/// use urandom::{Random, Rng, Distribution, distr::StandardUniform};
///
/// struct MyF32(f32);
///
/// impl Distribution<MyF32> for StandardUniform {
/// 	fn sample<R: Rng + ?Sized>(&self, rand: &mut Random<R>) -> MyF32 {
/// 		MyF32(rand.next())
/// 	}
/// }
/// ```
///
/// # Floating point implementation
///
/// The floating point implementations for `StandardUniform` generate a random value in the half-open interval `[1.0, 2.0)`, i.e. including `1.0` but not `2.0`.
///
/// The random value is generated by transmuting a random mantissa with a fixed exponent which is very fast and convenient.
/// The underlying `Rng` can optimize generating random floating point values as they don't need every random bit of the underlying `u32` or `u64`.
///
/// This is equivalent to calling [`Random::next_f32`](Random::next_f32) and [`Random::next_f64`](Random::next_f64) directly.
///
/// Subtracting `1.0` is an easy way to get a random floating point value in the half-open interval `[0.0, 1.0)` but has a small bias
/// in the lower bits of the floating point mantissa. This is equivalent to `rand.range(0.0..1.0)`.
///
/// See also: [`Float01`](super::Float01) which samples from `(0.0, 1.0)` and does not suffer from this bias.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StandardUniform;

// Implement StandardUniform distribution for integers as simple casts.
// For 8 bit or 16 bit integers this simply throws away bits from the Rng.
macro_rules! impl_standard_dist {
	($ty:ty, $rand:ident => $e:expr) => {
		impl Distribution<$ty> for StandardUniform {
			#[inline]
			fn sample<R: Rng + ?Sized>(&self, $rand: &mut Random<R>) -> $ty { $e }
		}
	};
}
impl_standard_dist! { bool, rand => (rand.next_u32() as i32) < 0 }
impl_standard_dist! { i8, rand => rand.next_u32() as i8 }
impl_standard_dist! { u8, rand => rand.next_u32() as u8 }
impl_standard_dist! { i16, rand => rand.next_u32() as i16 }
impl_standard_dist! { u16, rand => rand.next_u32() as u16 }
impl_standard_dist! { i32, rand => rand.next_u32() as i32 }
impl_standard_dist! { u32, rand => rand.next_u32() as u32 }
impl_standard_dist! { i64, rand => rand.next_u64() as i64 }
impl_standard_dist! { u64, rand => rand.next_u64() as u64 }
impl_standard_dist! { i128, rand => { let low = rand.next_u64() as i128; let high = rand.next_u64() as i128; low | high << 64 } }
impl_standard_dist! { u128, rand => { let low = rand.next_u64() as u128; let high = rand.next_u64() as u128; low | high << 64 } }
#[cfg(target_pointer_width = "32")]
impl_standard_dist! { isize, rand => rand.next_u32() as isize }
#[cfg(target_pointer_width = "32")]
impl_standard_dist! { usize, rand => rand.next_u32() as usize }
#[cfg(target_pointer_width = "64")]
impl_standard_dist! { isize, rand => rand.next_u64() as isize }
#[cfg(target_pointer_width = "64")]
impl_standard_dist! { usize, rand => rand.next_u64() as usize }
impl_standard_dist! { f32, rand => rand.next_f32() }
impl_standard_dist! { f64, rand => rand.next_f64() }


impl Distribution<char> for StandardUniform {
	#[inline]
	fn sample<R: Rng + ?Sized>(&self, rand: &mut Random<R>) -> char {
		// A valid `char` is either in the interval `[0, 0xD800)` or `[0xE000, 0x11_0000)`.
		// All `char`s must therefore be in `[0, 0x11_0000)` but not in the "gap" `[0xD800, 0xE000)` which is
		// reserved for surrogates. This is the size of that gap.
		const GAP_SIZE: u32 = 0xE000 - 0xD800;

		// Uniform::new(0, 0x11_0000 - GAP_SIZE) can also be used, but it seemed slower.
		let range = Uniform::new(GAP_SIZE, 0x11_0000);

		let mut n = range.sample(rand);
		if n < 0xE000 {
			n -= GAP_SIZE;
		}

		// Let's be safe and provide a checked implementation for debugging
		#[cfg(debug_assertions)]
		return char::from_u32(n).unwrap();
		// Safety depends on proper implementation of `UniformInt`
		#[cfg(not(debug_assertions))]
		#[allow(unsafe_code)]
		return unsafe { char::from_u32_unchecked(n) };
	}
}

impl<T> Distribution<num::Wrapping<T>> for num::Wrapping<T> where StandardUniform: Distribution<T> {
	#[inline]
	fn sample<R: Rng + ?Sized>(&self, rand: &mut Random<R>) -> num::Wrapping<T> {
		num::Wrapping(StandardUniform.sample(rand))
	}
}

macro_rules! impl_nzint {
	($name:ident) => {
		impl Distribution<num::$name> for StandardUniform {
			#[inline]
			fn sample<R: Rng + ?Sized>(&self, rand: &mut Random<R>) -> num::$name {
				loop {
					if let Some(nz) = num::$name::new(rand.next()) {
						break nz;
					}
				}
			}
		}
	};
}
impl_nzint!(NonZeroU8);
impl_nzint!(NonZeroU16);
impl_nzint!(NonZeroU32);
impl_nzint!(NonZeroU64);
impl_nzint!(NonZeroU128);
impl_nzint!(NonZeroUsize);

macro_rules! impl_standard_dist_tuple {
	($($T:ident),*) => {
		impl<$($T),*> Distribution<($($T,)*)> for StandardUniform where $(StandardUniform: Distribution<$T>),* {
			#[inline]
			fn sample<R: Rng + ?Sized>(&self, _rng: &mut Random<R>) -> ($($T,)*) {
				($(<StandardUniform as Distribution<$T>>::sample(&StandardUniform, _rng),)*)
			}
		}
	};
}
impl_standard_dist_tuple!();
impl_standard_dist_tuple!(A);
impl_standard_dist_tuple!(A, B);
impl_standard_dist_tuple!(A, B, C);
impl_standard_dist_tuple!(A, B, C, D);
impl_standard_dist_tuple!(A, B, C, D, E);
impl_standard_dist_tuple!(A, B, C, D, E, F);
impl_standard_dist_tuple!(A, B, C, D, E, F, G);
impl_standard_dist_tuple!(A, B, C, D, E, F, G, H);
impl_standard_dist_tuple!(A, B, C, D, E, F, G, H, I);
impl_standard_dist_tuple!(A, B, C, D, E, F, G, H, I, J);
impl_standard_dist_tuple!(A, B, C, D, E, F, G, H, I, J, K);
impl_standard_dist_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);

impl<T, const N: usize> Distribution<[T; N]> for StandardUniform where StandardUniform: Distribution<T> {
	#[inline]
	fn sample<R: Rng + ?Sized>(&self, rand: &mut Random<R>) -> [T; N] {
		array::from_fn(move |_| <StandardUniform as Distribution<T>>::sample(&StandardUniform, rand))
	}
}

//----------------------------------------------------------------

#[test]
fn test_arrays() {
	let mut rand = crate::new();
	let _: [i8; 0] = rand.sample(&StandardUniform);
	let _: [u8; 1] = rand.sample(&StandardUniform);
	let _: [i16; 13] = rand.sample(&StandardUniform);
	let _: [u16; 14] = rand.sample(&StandardUniform);
	let _: [i32; 20] = rand.sample(&StandardUniform);
	let _: [u32; 21] = rand.sample(&StandardUniform);
	let _: [u64; 31] = rand.sample(&StandardUniform);
	let _: [i64; 78] = rand.sample(&StandardUniform);
	let _: [isize; 8] = rand.sample(&StandardUniform);
	let _: [usize; 9] = rand.sample(&StandardUniform);
}

#[test]
fn test_nzint() {
	let mut rand = crate::new();

	// Any failures manifest as an infinite loop
	for _ in 0..9000 {
		let _: num::NonZeroU32 = rand.sample(&StandardUniform);
	}
}

#[test]
fn test_char() {
	let mut rand = crate::new();

	// Any failures manifest as a panic when debug assertions are enabled
	for _ in 0..9000 {
		let _: char = rand.sample(&StandardUniform);
	}
}

#[test]
fn test_bool() {
	let mut rand = crate::new();
	const N: usize = 10000;

	let mut results = [0i32; 2];
	for _ in 0..N {
		results[rand.coin_flip() as usize] += 1;
	}

	let [tails, heads] = results;
	assert!((tails - heads).abs() < 1000, "Unbalanced coin flips!! heads = {heads}, tails = {tails} out of {N} trails ");
}