Skip to main content

rand/distr/
uniform_int.rs

1// Copyright 2018-2020 Developers of the Rand project.
2// Copyright 2017 The Rust Project Developers.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! `UniformInt` implementation
11
12use super::{Error, SampleBorrow, SampleUniform, UniformSampler};
13use crate::distr::utils::WideningMultiply;
14#[cfg(feature = "simd_support")]
15use crate::distr::{Distribution, StandardUniform};
16use crate::{Rng, RngExt};
17
18#[cfg(feature = "simd_support")]
19use core::simd::{Select, prelude::*};
20
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24/// The back-end implementing [`UniformSampler`] for integer types.
25///
26/// Unless you are implementing [`UniformSampler`] for your own type, this type
27/// should not be used directly, use [`Uniform`] instead.
28///
29/// # Implementation notes
30///
31/// For simplicity, we use the same generic struct `UniformInt<X>` for all
32/// integer types `X`. This gives us only one field type, `X`; to store unsigned
33/// values of this size, we take use the fact that these conversions are no-ops.
34///
35/// For a closed range, the number of possible numbers we should generate is
36/// `range = (high - low + 1)`. To avoid bias, we must ensure that the size of
37/// our sample space, `zone`, is a multiple of `range`; other values must be
38/// rejected (by replacing with a new random sample).
39///
40/// As a special case, we use `range = 0` to represent the full range of the
41/// result type (i.e. for `new_inclusive($ty::MIN, $ty::MAX)`).
42///
43/// The optimum `zone` is the largest product of `range` which fits in our
44/// (unsigned) target type. We calculate this by calculating how many numbers we
45/// must reject: `reject = (MAX + 1) % range = (MAX - range + 1) % range`. Any (large)
46/// product of `range` will suffice, thus in `sample_single` we multiply by a
47/// power of 2 via bit-shifting (faster but may cause more rejections).
48///
49/// The smallest integer PRNGs generate is `u32`. For 8- and 16-bit outputs we
50/// use `u32` for our `zone` and samples (because it's not slower and because
51/// it reduces the chance of having to reject a sample). In this case we cannot
52/// store `zone` in the target type since it is too large, however we know
53/// `ints_to_reject < range <= $uty::MAX`.
54///
55/// An alternative to using a modulus is widening multiply: After a widening
56/// multiply by `range`, the result is in the high word. Then comparing the low
57/// word against `zone` makes sure our distribution is uniform.
58///
59/// # Bias
60///
61/// Unless the `unbiased` feature flag is used, outputs may have a small bias.
62/// In the worst case, bias affects 1 in `2^n` samples where n is
63/// 56 (`i8` and `u8`), 48 (`i16` and `u16`), 96 (`i32` and `u32`), 64 (`i64`
64/// and `u64`), 128 (`i128` and `u128`).
65///
66/// [`Uniform`]: super::Uniform
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69pub struct UniformInt<X> {
70    pub(super) low: X,
71    pub(super) range: X,
72    thresh: X, // effectively 2.pow(max(64, uty_bits)) % range
73}
74
75macro_rules! uniform_int_impl {
76    ($ty:ty, $uty:ty, $sample_ty:ident) => {
77        impl UniformInt<$ty> {
78            /// Get the maximum possible value
79            #[allow(unused)]
80            #[inline]
81            pub(crate) fn max(&self) -> $ty {
82                if self.range == 0 {
83                    return <$ty>::MAX;
84                } else {
85                    // Wrapping through <$ty>::MIN is possible with a valid
86                    // sampler over signed types. Wrapping through <$ty>::MAX is
87                    // possible with a bad sampler (constructible using serde).
88                    let max = self.low.wrapping_add(self.range.wrapping_sub(1));
89                    if max < self.low { <$ty>::MAX } else { max }
90                }
91            }
92        }
93
94        impl SampleUniform for $ty {
95            type Sampler = UniformInt<$ty>;
96        }
97
98        impl UniformSampler for UniformInt<$ty> {
99            // We play free and fast with unsigned vs signed here
100            // (when $ty is signed), but that's fine, since the
101            // contract of this macro is for $ty and $uty to be
102            // "bit-equal", so casting between them is a no-op.
103
104            type X = $ty;
105
106            #[inline] // if the range is constant, this helps LLVM to do the
107            // calculations at compile-time.
108            fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
109            where
110                B1: SampleBorrow<Self::X> + Sized,
111                B2: SampleBorrow<Self::X> + Sized,
112            {
113                let low = *low_b.borrow();
114                let high = *high_b.borrow();
115                if !(low < high) {
116                    return Err(Error::EmptyRange);
117                }
118                UniformSampler::new_inclusive(low, high - 1)
119            }
120
121            #[inline] // if the range is constant, this helps LLVM to do the
122            // calculations at compile-time.
123            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
124            where
125                B1: SampleBorrow<Self::X> + Sized,
126                B2: SampleBorrow<Self::X> + Sized,
127            {
128                let low = *low_b.borrow();
129                let high = *high_b.borrow();
130                if !(low <= high) {
131                    return Err(Error::EmptyRange);
132                }
133
134                let range = high.wrapping_sub(low).wrapping_add(1) as $uty;
135                let thresh = if range > 0 {
136                    let range = $sample_ty::from(range);
137                    (range.wrapping_neg() % range)
138                } else {
139                    0
140                };
141
142                Ok(UniformInt {
143                    low,
144                    range: range as $ty,           // type: $uty
145                    thresh: thresh as $uty as $ty, // type: $sample_ty
146                })
147            }
148
149            /// Sample from distribution, Lemire's method, unbiased
150            #[inline]
151            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
152                let range = self.range as $uty as $sample_ty;
153                if range == 0 {
154                    return rng.random();
155                }
156
157                let thresh = self.thresh as $uty as $sample_ty;
158                let hi = loop {
159                    let (hi, lo) = rng.random::<$sample_ty>().wmul(range);
160                    if lo >= thresh {
161                        break hi;
162                    }
163                };
164                self.low.wrapping_add(hi as $ty)
165            }
166
167            #[inline]
168            fn sample_single<R: Rng + ?Sized, B1, B2>(
169                low_b: B1,
170                high_b: B2,
171                rng: &mut R,
172            ) -> Result<Self::X, Error>
173            where
174                B1: SampleBorrow<Self::X> + Sized,
175                B2: SampleBorrow<Self::X> + Sized,
176            {
177                let low = *low_b.borrow();
178                let high = *high_b.borrow();
179                if !(low < high) {
180                    return Err(Error::EmptyRange);
181                }
182                Self::sample_single_inclusive(low, high - 1, rng)
183            }
184
185            /// Sample single value, Canon's method, biased
186            ///
187            /// In the worst case, bias affects 1 in `2^n` samples where n is
188            /// 56 (`i8`), 48 (`i16`), 96 (`i32`), 64 (`i64`), 128 (`i128`).
189            #[cfg(not(feature = "unbiased"))]
190            #[inline]
191            fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
192                low_b: B1,
193                high_b: B2,
194                rng: &mut R,
195            ) -> Result<Self::X, Error>
196            where
197                B1: SampleBorrow<Self::X> + Sized,
198                B2: SampleBorrow<Self::X> + Sized,
199            {
200                let low = *low_b.borrow();
201                let high = *high_b.borrow();
202                if !(low <= high) {
203                    return Err(Error::EmptyRange);
204                }
205                let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty;
206                if range == 0 {
207                    // Range is MAX+1 (unrepresentable), so we need a special case
208                    return Ok(rng.random());
209                }
210
211                // generate a sample using a sensible integer type
212                let (mut result, lo_order) = rng.random::<$sample_ty>().wmul(range);
213
214                // if the sample is biased...
215                if lo_order > range.wrapping_neg() {
216                    // ...generate a new sample to reduce bias...
217                    let (new_hi_order, _) = (rng.random::<$sample_ty>()).wmul(range as $sample_ty);
218                    // ... incrementing result on overflow
219                    let is_overflow = lo_order.checked_add(new_hi_order as $sample_ty).is_none();
220                    result += is_overflow as $sample_ty;
221                }
222
223                Ok(low.wrapping_add(result as $ty))
224            }
225
226            /// Sample single value, Canon's method, unbiased
227            #[cfg(feature = "unbiased")]
228            #[inline]
229            fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
230                low_b: B1,
231                high_b: B2,
232                rng: &mut R,
233            ) -> Result<Self::X, Error>
234            where
235                B1: SampleBorrow<$ty> + Sized,
236                B2: SampleBorrow<$ty> + Sized,
237            {
238                let low = *low_b.borrow();
239                let high = *high_b.borrow();
240                if !(low <= high) {
241                    return Err(Error::EmptyRange);
242                }
243                let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty;
244                if range == 0 {
245                    // Range is MAX+1 (unrepresentable), so we need a special case
246                    return Ok(rng.random());
247                }
248
249                let (mut result, mut lo) = rng.random::<$sample_ty>().wmul(range);
250
251                // In contrast to the biased sampler, we use a loop:
252                while lo > range.wrapping_neg() {
253                    let (new_hi, new_lo) = (rng.random::<$sample_ty>()).wmul(range);
254                    match lo.checked_add(new_hi) {
255                        Some(x) if x < $sample_ty::MAX => {
256                            // Anything less than MAX: last term is 0
257                            break;
258                        }
259                        None => {
260                            // Overflow: last term is 1
261                            result += 1;
262                            break;
263                        }
264                        _ => {
265                            // Unlikely case: must check next sample
266                            lo = new_lo;
267                            continue;
268                        }
269                    }
270                }
271
272                Ok(low.wrapping_add(result as $ty))
273            }
274        }
275    };
276}
277
278uniform_int_impl! { i8, u8, u32 }
279uniform_int_impl! { i16, u16, u32 }
280uniform_int_impl! { i32, u32, u32 }
281uniform_int_impl! { i64, u64, u64 }
282uniform_int_impl! { i128, u128, u128 }
283uniform_int_impl! { u8, u8, u32 }
284uniform_int_impl! { u16, u16, u32 }
285uniform_int_impl! { u32, u32, u32 }
286uniform_int_impl! { u64, u64, u64 }
287uniform_int_impl! { u128, u128, u128 }
288
289#[cfg(feature = "simd_support")]
290macro_rules! uniform_simd_int_impl {
291    ($ty:ident, $unsigned:ident) => {
292        // The "pick the largest zone that can fit in an `u32`" optimization
293        // is less useful here. Multiple lanes complicate things, we don't
294        // know the PRNG's minimal output size, and casting to a larger vector
295        // is generally a bad idea for SIMD performance. The user can still
296        // implement it manually.
297
298        #[cfg(feature = "simd_support")]
299        impl<const LANES: usize> SampleUniform for Simd<$ty, LANES>
300        where
301            Simd<$unsigned, LANES>:
302                WideningMultiply<Output = (Simd<$unsigned, LANES>, Simd<$unsigned, LANES>)>,
303            StandardUniform: Distribution<Simd<$unsigned, LANES>>,
304        {
305            type Sampler = UniformInt<Simd<$ty, LANES>>;
306        }
307
308        #[cfg(feature = "simd_support")]
309        impl<const LANES: usize> UniformSampler for UniformInt<Simd<$ty, LANES>>
310        where
311            Simd<$unsigned, LANES>:
312                WideningMultiply<Output = (Simd<$unsigned, LANES>, Simd<$unsigned, LANES>)>,
313            StandardUniform: Distribution<Simd<$unsigned, LANES>>,
314        {
315            type X = Simd<$ty, LANES>;
316
317            #[inline] // if the range is constant, this helps LLVM to do the
318                      // calculations at compile-time.
319            fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
320                where B1: SampleBorrow<Self::X> + Sized,
321                      B2: SampleBorrow<Self::X> + Sized
322            {
323                let low = *low_b.borrow();
324                let high = *high_b.borrow();
325                if !(low.simd_lt(high).all()) {
326                    return Err(Error::EmptyRange);
327                }
328                UniformSampler::new_inclusive(low, high - Simd::splat(1))
329            }
330
331            #[inline] // if the range is constant, this helps LLVM to do the
332                      // calculations at compile-time.
333            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
334                where B1: SampleBorrow<Self::X> + Sized,
335                      B2: SampleBorrow<Self::X> + Sized
336            {
337                let low = *low_b.borrow();
338                let high = *high_b.borrow();
339                if !(low.simd_le(high).all()) {
340                    return Err(Error::EmptyRange);
341                }
342
343                // NOTE: all `Simd` operations are inherently wrapping,
344                //       see https://doc.rust-lang.org/std/simd/struct.Simd.html
345                let range: Simd<$unsigned, LANES> = ((high - low) + Simd::splat(1)).cast();
346
347                // We must avoid divide-by-zero by using 0 % 1 == 0.
348                let not_full_range = range.simd_gt(Simd::splat(0));
349                let modulo = not_full_range.select(range, Simd::splat(1));
350                let ints_to_reject = range.wrapping_neg() % modulo;
351
352                Ok(UniformInt {
353                    low,
354                    // These are really $unsigned values, but store as $ty:
355                    range: range.cast(),
356                    thresh: ints_to_reject.cast(),
357                })
358            }
359
360            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
361                let range: Simd<$unsigned, LANES> = self.range.cast();
362                let thresh: Simd<$unsigned, LANES> = self.thresh.cast();
363
364                // This might seem very slow, generating a whole new
365                // SIMD vector for every sample rejection. For most uses
366                // though, the chance of rejection is small and provides good
367                // general performance. With multiple lanes, that chance is
368                // multiplied. To mitigate this, we replace only the lanes of
369                // the vector which fail, iteratively reducing the chance of
370                // rejection. The replacement method does however add a little
371                // overhead. Benchmarking or calculating probabilities might
372                // reveal contexts where this replacement method is slower.
373                let mut v: Simd<$unsigned, LANES> = rng.random();
374                loop {
375                    let (hi, lo) = v.wmul(range);
376                    let mask = lo.simd_ge(thresh);
377                    if mask.all() {
378                        let hi: Simd<$ty, LANES> = hi.cast();
379                        // wrapping addition
380                        let result = self.low + hi;
381                        // `select` here compiles to a blend operation
382                        // When `range.eq(0).none()` the compare and blend
383                        // operations are avoided.
384                        let v: Simd<$ty, LANES> = v.cast();
385                        return range.simd_gt(Simd::splat(0)).select(result, v);
386                    }
387                    // Replace only the failing lanes
388                    v = mask.select(v, rng.random());
389                }
390            }
391        }
392    };
393
394    // bulk implementation
395    ($(($unsigned:ident, $signed:ident)),+) => {
396        $(
397            uniform_simd_int_impl!($unsigned, $unsigned);
398            uniform_simd_int_impl!($signed, $unsigned);
399        )+
400    };
401}
402
403#[cfg(feature = "simd_support")]
404uniform_simd_int_impl! { (u8, i8), (u16, i16), (u32, i32), (u64, i64) }
405
406/// The back-end implementing [`UniformSampler`] for `usize`.
407///
408/// # Implementation notes
409///
410/// Sampling a `usize` value is usually used in relation to the length of an
411/// array or other memory structure, thus it is reasonable to assume that the
412/// vast majority of use-cases will have a maximum size under [`u32::MAX`].
413/// In part to optimise for this use-case, but mostly to ensure that results
414/// are portable across 32-bit and 64-bit architectures (as far as is possible),
415/// this implementation will use 32-bit sampling when possible.
416#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
417#[derive(Clone, Copy, Debug, PartialEq, Eq)]
418#[cfg_attr(all(feature = "serde"), derive(Serialize))]
419// To be able to deserialize on 32-bit we need to replace this with a custom
420// implementation of the Deserialize trait, to be able to:
421// - panic when `mode64` is `true` on 32-bit,
422// - assign the default value to `mode64` when it's missing on 64-bit,
423// - panic when the `usize` fields are greater than `u32::MAX` on 32-bit.
424#[cfg_attr(
425    all(feature = "serde", target_pointer_width = "64"),
426    derive(Deserialize)
427)]
428pub struct UniformUsize {
429    /// The lowest possible value.
430    low: usize,
431    /// The number of possible values. `0` has a special meaning: all.
432    range: usize,
433    /// Threshold used when sampling to obtain a uniform distribution.
434    thresh: usize,
435    /// Whether the largest possible value is greater than `u32::MAX`.
436    #[cfg(target_pointer_width = "64")]
437    // Handle missing field when deserializing on 64-bit an object serialized
438    // on 32-bit. Can be removed when switching to a custom deserializer.
439    #[cfg_attr(feature = "serde", serde(default))]
440    mode64: bool,
441}
442
443#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
444impl SampleUniform for usize {
445    type Sampler = UniformUsize;
446}
447
448#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
449impl UniformSampler for UniformUsize {
450    type X = usize;
451
452    #[inline] // if the range is constant, this helps LLVM to do the
453    // calculations at compile-time.
454    fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
455    where
456        B1: SampleBorrow<Self::X> + Sized,
457        B2: SampleBorrow<Self::X> + Sized,
458    {
459        let low = *low_b.borrow();
460        let high = *high_b.borrow();
461        if !(low < high) {
462            return Err(Error::EmptyRange);
463        }
464
465        UniformSampler::new_inclusive(low, high - 1)
466    }
467
468    #[inline] // if the range is constant, this helps LLVM to do the
469    // calculations at compile-time.
470    fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
471    where
472        B1: SampleBorrow<Self::X> + Sized,
473        B2: SampleBorrow<Self::X> + Sized,
474    {
475        let low = *low_b.borrow();
476        let high = *high_b.borrow();
477        if !(low <= high) {
478            return Err(Error::EmptyRange);
479        }
480
481        #[cfg(target_pointer_width = "64")]
482        let mode64 = high > (u32::MAX as usize);
483        #[cfg(target_pointer_width = "32")]
484        let mode64 = false;
485
486        let (range, thresh);
487        if cfg!(target_pointer_width = "64") && !mode64 {
488            let range32 = (high as u32).wrapping_sub(low as u32).wrapping_add(1);
489            range = range32 as usize;
490            thresh = if range32 > 0 {
491                (range32.wrapping_neg() % range32) as usize
492            } else {
493                0
494            };
495        } else {
496            range = high.wrapping_sub(low).wrapping_add(1);
497            thresh = if range > 0 {
498                range.wrapping_neg() % range
499            } else {
500                0
501            };
502        }
503
504        Ok(UniformUsize {
505            low,
506            range,
507            thresh,
508            #[cfg(target_pointer_width = "64")]
509            mode64,
510        })
511    }
512
513    #[inline]
514    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> usize {
515        #[cfg(target_pointer_width = "32")]
516        let mode32 = true;
517        #[cfg(target_pointer_width = "64")]
518        let mode32 = !self.mode64;
519
520        if mode32 {
521            let range = self.range as u32;
522            if range == 0 {
523                return rng.random::<u32>() as usize;
524            }
525
526            let thresh = self.thresh as u32;
527            let hi = loop {
528                let (hi, lo) = rng.random::<u32>().wmul(range);
529                if lo >= thresh {
530                    break hi;
531                }
532            };
533            self.low.wrapping_add(hi as usize)
534        } else {
535            let range = self.range as u64;
536            if range == 0 {
537                return rng.random::<u64>() as usize;
538            }
539
540            let thresh = self.thresh as u64;
541            let hi = loop {
542                let (hi, lo) = rng.random::<u64>().wmul(range);
543                if lo >= thresh {
544                    break hi;
545                }
546            };
547            self.low.wrapping_add(hi as usize)
548        }
549    }
550
551    #[inline]
552    fn sample_single<R: Rng + ?Sized, B1, B2>(
553        low_b: B1,
554        high_b: B2,
555        rng: &mut R,
556    ) -> Result<Self::X, Error>
557    where
558        B1: SampleBorrow<Self::X> + Sized,
559        B2: SampleBorrow<Self::X> + Sized,
560    {
561        let low = *low_b.borrow();
562        let high = *high_b.borrow();
563        if !(low < high) {
564            return Err(Error::EmptyRange);
565        }
566
567        if cfg!(target_pointer_width = "64") && high > (u32::MAX as usize) {
568            return UniformInt::<u64>::sample_single(low as u64, high as u64, rng)
569                .map(|x| x as usize);
570        }
571
572        UniformInt::<u32>::sample_single(low as u32, high as u32, rng).map(|x| x as usize)
573    }
574
575    #[inline]
576    fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
577        low_b: B1,
578        high_b: B2,
579        rng: &mut R,
580    ) -> Result<Self::X, Error>
581    where
582        B1: SampleBorrow<Self::X> + Sized,
583        B2: SampleBorrow<Self::X> + Sized,
584    {
585        let low = *low_b.borrow();
586        let high = *high_b.borrow();
587        if !(low <= high) {
588            return Err(Error::EmptyRange);
589        }
590
591        if cfg!(target_pointer_width = "64") && high > (u32::MAX as usize) {
592            return UniformInt::<u64>::sample_single_inclusive(low as u64, high as u64, rng)
593                .map(|x| x as usize);
594        }
595
596        UniformInt::<u32>::sample_single_inclusive(low as u32, high as u32, rng).map(|x| x as usize)
597    }
598}
599
600#[cfg(test)]
601mod tests {
602    use super::*;
603    use crate::distr::{Distribution, Uniform};
604    use core::fmt::Debug;
605    use core::ops::Add;
606
607    #[test]
608    fn test_uniform_bad_limits_equal_int() {
609        assert_eq!(Uniform::new(10, 10), Err(Error::EmptyRange));
610    }
611
612    #[test]
613    fn test_uniform_good_limits_equal_int() {
614        let mut rng = crate::test::rng(804);
615        let dist = Uniform::new_inclusive(10, 10).unwrap();
616        for _ in 0..20 {
617            assert_eq!(rng.sample(dist), 10);
618        }
619    }
620
621    #[test]
622    fn test_uniform_bad_limits_flipped_int() {
623        assert_eq!(Uniform::new(10, 5), Err(Error::EmptyRange));
624    }
625
626    #[test]
627    #[cfg_attr(miri, ignore)] // Miri is too slow
628    fn test_integers() {
629        let mut rng = crate::test::rng(251);
630        macro_rules! t {
631            ($ty:ident, $v:expr, $le:expr, $lt:expr) => {{
632                for &(low, high) in $v.iter() {
633                    let my_uniform = Uniform::new(low, high).unwrap();
634                    for _ in 0..1000 {
635                        let v: $ty = rng.sample(my_uniform);
636                        assert!($le(low, v) && $lt(v, high));
637                    }
638
639                    let my_uniform = Uniform::new_inclusive(low, high).unwrap();
640                    for _ in 0..1000 {
641                        let v: $ty = rng.sample(my_uniform);
642                        assert!($le(low, v) && $le(v, high));
643                    }
644
645                    let my_uniform = Uniform::new(&low, high).unwrap();
646                    for _ in 0..1000 {
647                        let v: $ty = rng.sample(my_uniform);
648                        assert!($le(low, v) && $lt(v, high));
649                    }
650
651                    let my_uniform = Uniform::new_inclusive(&low, &high).unwrap();
652                    for _ in 0..1000 {
653                        let v: $ty = rng.sample(my_uniform);
654                        assert!($le(low, v) && $le(v, high));
655                    }
656
657                    for _ in 0..1000 {
658                        let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng).unwrap();
659                        assert!($le(low, v) && $lt(v, high));
660                    }
661
662                    for _ in 0..1000 {
663                        let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng).unwrap();
664                        assert!($le(low, v) && $le(v, high));
665                    }
666                }
667            }};
668
669            // scalar bulk
670            ($($ty:ident),*) => {{
671                $(t!(
672                    $ty,
673                    [(0, 10), (10, 127), ($ty::MIN, $ty::MAX)],
674                    |x, y| x <= y,
675                    |x, y| x < y
676                );)*
677            }};
678
679            // simd bulk
680            ($($ty:ident),* => $scalar:ident) => {{
681                $(t!(
682                    $ty,
683                    [
684                        ($ty::splat(0), $ty::splat(10)),
685                        ($ty::splat(10), $ty::splat(127)),
686                        ($ty::splat($scalar::MIN), $ty::splat($scalar::MAX)),
687                    ],
688                    |x: $ty, y| x.simd_le(y).all(),
689                    |x: $ty, y| x.simd_lt(y).all()
690                );)*
691            }};
692        }
693        t!(i8, i16, i32, i64, i128, u8, u16, u32, u64, usize, u128);
694
695        #[cfg(feature = "simd_support")]
696        {
697            t!(u8x4, u8x8, u8x16, u8x32, u8x64 => u8);
698            t!(i8x4, i8x8, i8x16, i8x32, i8x64 => i8);
699            t!(u16x2, u16x4, u16x8, u16x16, u16x32 => u16);
700            t!(i16x2, i16x4, i16x8, i16x16, i16x32 => i16);
701            t!(u32x2, u32x4, u32x8, u32x16 => u32);
702            t!(i32x2, i32x4, i32x8, i32x16 => i32);
703            t!(u64x2, u64x4, u64x8 => u64);
704            t!(i64x2, i64x4, i64x8 => i64);
705        }
706    }
707
708    #[test]
709    fn test_uniform_from_std_range() {
710        let r = Uniform::try_from(2u32..7).unwrap();
711        assert_eq!(r.0.low, 2);
712        assert_eq!(r.0.range, 5);
713        assert_eq!(r.0.max(), 6);
714    }
715
716    #[test]
717    fn test_uniform_from_std_range_bad_limits() {
718        #![allow(clippy::reversed_empty_ranges)]
719        assert!(Uniform::try_from(100..10).is_err());
720        assert!(Uniform::try_from(100..100).is_err());
721    }
722
723    #[test]
724    fn test_uniform_from_std_range_inclusive() {
725        let r = Uniform::try_from(2u32..=6).unwrap();
726        assert_eq!(r.0.low, 2);
727        assert_eq!(r.0.range, 5);
728        assert_eq!(r.0.max(), 6);
729    }
730
731    #[test]
732    fn test_uniform_from_std_range_inclusive_bad_limits() {
733        #![allow(clippy::reversed_empty_ranges)]
734        assert!(Uniform::try_from(100..=10).is_err());
735        assert!(Uniform::try_from(100..=99).is_err());
736    }
737
738    #[test]
739    fn value_stability() {
740        fn test_samples<T: SampleUniform + Copy + Debug + PartialEq + Add<T>>(
741            lb: T,
742            ub: T,
743            ub_excl: T,
744            expected: &[T],
745        ) where
746            Uniform<T>: Distribution<T>,
747        {
748            let mut rng = crate::test::rng(897);
749            let mut buf = [lb; 6];
750
751            for x in &mut buf[0..3] {
752                *x = T::Sampler::sample_single_inclusive(lb, ub, &mut rng).unwrap();
753            }
754
755            let distr = Uniform::new_inclusive(lb, ub).unwrap();
756            for x in &mut buf[3..6] {
757                *x = rng.sample(&distr);
758            }
759            assert_eq!(&buf, expected);
760
761            let mut rng = crate::test::rng(897);
762
763            for x in &mut buf[0..3] {
764                *x = T::Sampler::sample_single(lb, ub_excl, &mut rng).unwrap();
765            }
766
767            let distr = Uniform::new(lb, ub_excl).unwrap();
768            for x in &mut buf[3..6] {
769                *x = rng.sample(&distr);
770            }
771            assert_eq!(&buf, expected);
772        }
773
774        test_samples(-105i8, 111, 112, &[-99, -48, 107, 72, -19, 56]);
775        test_samples(2i16, 1352, 1353, &[43, 361, 1325, 1109, 539, 1005]);
776        test_samples(
777            -313853i32,
778            13513,
779            13514,
780            &[-303803, -226673, 6912, -45605, -183505, -70668],
781        );
782        test_samples(
783            131521i64,
784            6542165,
785            6542166,
786            &[1838724, 5384489, 4893692, 3712948, 3951509, 4094926],
787        );
788        test_samples(
789            -0x8000_0000_0000_0000_0000_0000_0000_0000i128,
790            -1,
791            0,
792            &[
793                -30725222750250982319765550926688025855,
794                -75088619368053423329503924805178012357,
795                -64950748766625548510467638647674468829,
796                -41794017901603587121582892414659436495,
797                -63623852319608406524605295913876414006,
798                -17404679390297612013597359206379189023,
799            ],
800        );
801        test_samples(11u8, 218, 219, &[17, 66, 214, 181, 93, 165]);
802        test_samples(11u16, 218, 219, &[17, 66, 214, 181, 93, 165]);
803        test_samples(11u32, 218, 219, &[17, 66, 214, 181, 93, 165]);
804        test_samples(11u64, 218, 219, &[66, 181, 165, 127, 134, 139]);
805        test_samples(11u128, 218, 219, &[181, 127, 139, 167, 141, 197]);
806        test_samples(11usize, 218, 219, &[17, 66, 214, 181, 93, 165]);
807
808        #[cfg(feature = "simd_support")]
809        {
810            let lb = Simd::from([11u8, 0, 128, 127]);
811            let ub = Simd::from([218, 254, 254, 254]);
812            let ub_excl = ub + Simd::splat(1);
813            test_samples(
814                lb,
815                ub,
816                ub_excl,
817                &[
818                    Simd::from([13, 5, 237, 130]),
819                    Simd::from([126, 186, 149, 161]),
820                    Simd::from([103, 86, 234, 252]),
821                    Simd::from([35, 18, 225, 231]),
822                    Simd::from([106, 153, 246, 177]),
823                    Simd::from([195, 168, 149, 222]),
824                ],
825            );
826        }
827    }
828
829    #[test]
830    fn test_uniform_usize_empty_range() {
831        assert_eq!(UniformUsize::new(10, 10), Err(Error::EmptyRange));
832        assert!(UniformUsize::new(10, 11).is_ok());
833
834        assert_eq!(UniformUsize::new_inclusive(10, 9), Err(Error::EmptyRange));
835        assert!(UniformUsize::new_inclusive(10, 10).is_ok());
836    }
837
838    #[test]
839    fn test_uniform_usize_constructors() {
840        assert_eq!(
841            UniformUsize::new_inclusive(u32::MAX as usize, u32::MAX as usize),
842            Ok(UniformUsize {
843                low: u32::MAX as usize,
844                range: 1,
845                thresh: 0,
846                #[cfg(target_pointer_width = "64")]
847                mode64: false
848            })
849        );
850        assert_eq!(
851            UniformUsize::new_inclusive(0, u32::MAX as usize),
852            Ok(UniformUsize {
853                low: 0,
854                range: 0,
855                thresh: 0,
856                #[cfg(target_pointer_width = "64")]
857                mode64: false
858            })
859        );
860        #[cfg(target_pointer_width = "64")]
861        assert_eq!(
862            UniformUsize::new_inclusive(0, u32::MAX as usize + 1),
863            Ok(UniformUsize {
864                low: 0,
865                range: u32::MAX as usize + 2,
866                thresh: 1,
867                mode64: true
868            })
869        );
870        #[cfg(target_pointer_width = "64")]
871        assert_eq!(
872            UniformUsize::new_inclusive(u32::MAX as usize, u64::MAX as usize),
873            Ok(UniformUsize {
874                low: u32::MAX as usize,
875                range: u64::MAX as usize - u32::MAX as usize + 1,
876                thresh: u32::MAX as usize,
877                mode64: true
878            })
879        );
880    }
881
882    // This could be run also on 32-bit when deserialization is implemented.
883    #[cfg(all(feature = "serde", target_pointer_width = "64"))]
884    #[test]
885    fn test_uniform_usize_deserialization() {
886        use serde_json;
887        let original = UniformUsize::new_inclusive(10, 100).expect("creation");
888        let serialized = serde_json::to_string(&original).expect("serialization");
889        let deserialized: UniformUsize =
890            serde_json::from_str(&serialized).expect("deserialization");
891        assert_eq!(deserialized, original);
892    }
893
894    #[cfg(all(feature = "serde", target_pointer_width = "64"))]
895    #[test]
896    fn test_uniform_usize_deserialization_from_32bit() {
897        use serde_json;
898        let serialized_on_32bit = r#"{"low":10,"range":91,"thresh":74}"#;
899        let deserialized: UniformUsize =
900            serde_json::from_str(serialized_on_32bit).expect("deserialization");
901        assert_eq!(
902            deserialized,
903            UniformUsize::new_inclusive(10, 100).expect("creation")
904        );
905    }
906
907    #[cfg(all(feature = "serde", target_pointer_width = "64"))]
908    #[test]
909    fn test_uniform_usize_deserialization_64bit() {
910        use serde_json;
911        let original = UniformUsize::new_inclusive(1, u64::MAX as usize - 1).expect("creation");
912        assert!(original.mode64);
913        let serialized = serde_json::to_string(&original).expect("serialization");
914        let deserialized: UniformUsize =
915            serde_json::from_str(&serialized).expect("deserialization");
916        assert_eq!(deserialized, original);
917    }
918}