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