nanorand/
gen.rs

1use crate::Rng;
2use core::ops::{Bound, RangeBounds};
3
4macro_rules! gen {
5	($($type:ty),+) => {
6		$(
7			impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for $type {
8				fn random(rng: &mut Generator) -> Self {
9					let mut bytes = [0u8; core::mem::size_of::<$type>()];
10					rng.fill_bytes(&mut bytes);
11					Self::from_ne_bytes(bytes)
12				}
13			}
14		)+
15	};
16}
17
18macro_rules! range {
19	($(($type:ty, $bigger:ty, $signed:ty)),+) => {
20		$(
21			impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomRange<Generator, OUTPUT> for $type {
22				fn random_range<Bounds: RangeBounds<Self>>(rng: &mut Generator, bounds: Bounds) -> Self {
23					const BITS: $bigger = core::mem::size_of::<$type>() as $bigger * 8;
24					let lower = match bounds.start_bound() {
25						Bound::Included(lower) => *lower,
26						Bound::Excluded(lower) => lower.saturating_add(1),
27						Bound::Unbounded => <$type>::MIN,
28					};
29					let upper = match bounds.end_bound() {
30						Bound::Included(upper) => upper.saturating_add(1),
31						Bound::Excluded(upper) => *upper,
32						Bound::Unbounded => <$type>::MAX,
33					};
34					assert!(upper >= lower, "{} >= {} (lower bound was bigger than upper bound)", upper, lower);
35					let upper = upper.saturating_sub(lower);
36					let mut value = Self::random(rng);
37					let mut m = (upper as $bigger).wrapping_mul(value as $bigger);
38					if (m as $type) < upper {
39						let t = (!upper + 1) % upper;
40						while (m as $type) < t {
41							value = Self::random(rng);
42							m = (upper as $bigger).wrapping_mul(value as $bigger);
43						}
44					}
45					(m >> BITS) as $type + lower
46				}
47			}
48
49			impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomRange<Generator, OUTPUT> for $signed {
50				fn random_range<Bounds: RangeBounds<Self>>(r: &mut Generator, bounds: Bounds) -> Self {
51					let lower = match bounds.start_bound() {
52						Bound::Included(lower) => *lower,
53						Bound::Excluded(lower) => lower.saturating_add(1),
54						Bound::Unbounded => <$signed>::MIN
55					};
56					let upper = match bounds.end_bound() {
57						Bound::Included(upper) => *upper,
58						Bound::Excluded(upper) => upper.saturating_sub(1),
59						Bound::Unbounded => <$signed>::MAX,
60					};
61					assert!(upper >= lower, "{} >= {} (lower bound was bigger than upper bound)", upper, lower);
62					let lower = lower.wrapping_sub(<$signed>::MIN) as $type;
63					let upper = upper.wrapping_sub(<$signed>::MIN) as $type;
64					<$type>::random_range(r, lower..=upper).wrapping_add(<$signed>::MAX as $type) as $signed
65				}
66			}
67		)+
68	}
69}
70
71/// A trait used for generating a random object with an RNG,
72pub trait RandomGen<Generator: Rng<OUTPUT>, const OUTPUT: usize> {
73	/// Return a random instance of the implementing type, from the specified RNG instance.
74	fn random(rng: &mut Generator) -> Self;
75}
76
77/// A trait used for generating a random number within a range, with an RNG,
78pub trait RandomRange<Generator: Rng<OUTPUT>, const OUTPUT: usize>:
79	RandomGen<Generator, OUTPUT>
80{
81	/// Return a ranged number of the implementing type, from the specified RNG instance.
82	///
83	/// # Panics
84	/// This function will panic if the lower bound of the range is greater than the upper bound.
85	fn random_range<Bounds: RangeBounds<Self>>(nng: &mut Generator, range: Bounds) -> Self;
86}
87
88impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for bool {
89	fn random(rng: &mut Generator) -> Self {
90		u8::random(rng) < 0b10000000
91	}
92}
93
94impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for f32 {
95	fn random(rng: &mut Generator) -> Self {
96		(u32::random(rng) as f32) / (u32::MAX as f32)
97	}
98}
99
100impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for f64 {
101	fn random(rng: &mut Generator) -> Self {
102		(u64::random(rng) as f64) / (u64::MAX as f64)
103	}
104}
105
106gen!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
107range!(
108	(u8, u16, i8),
109	(u16, u32, i16),
110	(u32, u64, i32),
111	(u64, u128, i64)
112);
113#[cfg(target_pointer_width = "16")]
114range!((usize, u32, isize));
115#[cfg(target_pointer_width = "32")]
116range!((usize, u64, isize));
117#[cfg(target_pointer_width = "64")]
118range!((usize, u128, isize));
119
120#[cfg(test)]
121mod tests {
122	use crate::{Rng, WyRand};
123	#[test]
124	fn ensure_unsigned_in_range() {
125		let mut rng = WyRand::new();
126		for _ in 0..1000 {
127			let number = rng.generate_range(10_u64..=20);
128			assert!(
129				(10..=20).contains(&number),
130				"{} was outside of 10..=20",
131				number
132			);
133
134			let number = rng.generate_range(10_u64..30);
135			assert!(
136				(10..30).contains(&number),
137				"{} was outside of 10..30",
138				number
139			);
140
141			let number = rng.generate_range(512_u64..);
142			assert!((512..).contains(&number), "{} was outside of 512..", number);
143
144			let number = rng.generate_range(..1024_u64);
145			assert!(
146				(..1024).contains(&number),
147				"{} was outside of ..1024",
148				number
149			);
150		}
151	}
152	#[test]
153	fn ensure_signed_in_range() {
154		let mut rng = WyRand::new();
155		for _ in 0..1000 {
156			let number = rng.generate_range(-50..);
157			assert!((-50..).contains(&number), "{} was outside of -50..", number);
158
159			let number = rng.generate_range(..512);
160			assert!((..512).contains(&number), "{} was outside of ..512", number);
161
162			let number = rng.generate_range(..-32);
163			assert!((..-32).contains(&number), "{} was outside of ..-32", number);
164		}
165	}
166
167	#[test]
168	fn ensure_floats_generate_properly() {
169		let mut rng = WyRand::new();
170		for _ in 0..1000 {
171			let number = rng.generate::<f32>();
172			assert!(1.0 >= number, "{} was bigger than 1.0", number);
173			assert!(number >= 0.0, "0 was bigger than {}", number);
174
175			let number = rng.generate::<f64>();
176			assert!(1.0 >= number, "{} was bigger than 1.0", number);
177			assert!(number >= 0.0, "0 was bigger than {}", number);
178		}
179	}
180
181	#[test]
182	#[should_panic]
183	fn ensure_invalid_range_panics() {
184		let mut rng = WyRand::new();
185		#[allow(clippy::reversed_empty_ranges)]
186		rng.generate_range(10..=5);
187	}
188}