diff --git a/CHANGELOG.md b/CHANGELOG.md index 39e3c5c7271..9667aebfa13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,13 +10,14 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. ## [Unreleased] - Add `rand::distributions::WeightedIndex::{weight, weights, total_weight}` (#1420) -- Add `IndexedRandom::choose_multiple_array`, `index::sample_array` (#1453) +- Add `IndexedRandom::choose_multiple_array`, `index::sample_array` (#1453, #1469) - Bump the MSRV to 1.61.0 - Rename `Rng::gen` to `Rng::random` to avoid conflict with the new `gen` keyword in Rust 2024 (#1435) - Move all benchmarks to new `benches` crate (#1439) - Annotate panicking methods with `#[track_caller]` (#1442, #1447) - Enable feature `small_rng` by default (#1455) - Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462) +- Fix portability of `rand::distributions::Slice` (#1469) ## [0.9.0-alpha.1] - 2024-03-18 - Add the `Slice::num_choices` method to the Slice distribution (#1402) diff --git a/rand_distr/src/dirichlet.rs b/rand_distr/src/dirichlet.rs index aae1e0750ff..3cd4c09eea7 100644 --- a/rand_distr/src/dirichlet.rs +++ b/rand_distr/src/dirichlet.rs @@ -333,20 +333,13 @@ where #[cfg(test)] mod test { use super::*; - use alloc::vec::Vec; #[test] fn test_dirichlet() { let d = Dirichlet::new([1.0, 2.0, 3.0]).unwrap(); let mut rng = crate::test::rng(221); let samples = d.sample(&mut rng); - let _: Vec = samples - .into_iter() - .map(|x| { - assert!(x > 0.0); - x - }) - .collect(); + assert!(samples.into_iter().all(|x: f64| x > 0.0)); } #[test] diff --git a/src/distributions/slice.rs b/src/distributions/slice.rs index 10f830b7bb3..8b8f9662595 100644 --- a/src/distributions/slice.rs +++ b/src/distributions/slice.rs @@ -9,9 +9,39 @@ use core::num::NonZeroUsize; use crate::distributions::{Distribution, Uniform}; +use crate::Rng; #[cfg(feature = "alloc")] use alloc::string::String; +#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] +compile_error!("unsupported pointer width"); + +#[derive(Debug, Clone, Copy)] +enum UniformUsize { + U32(Uniform), + #[cfg(target_pointer_width = "64")] + U64(Uniform), +} + +impl UniformUsize { + pub fn new(ubound: usize) -> Result { + #[cfg(target_pointer_width = "64")] + if ubound > (u32::MAX as usize) { + return Uniform::new(0, ubound as u64).map(UniformUsize::U64); + } + + Uniform::new(0, ubound as u32).map(UniformUsize::U32) + } + + pub fn sample(&self, rng: &mut R) -> usize { + match self { + UniformUsize::U32(uu) => uu.sample(rng) as usize, + #[cfg(target_pointer_width = "64")] + UniformUsize::U64(uu) => uu.sample(rng) as usize, + } + } +} + /// A distribution to sample items uniformly from a slice. /// /// [`Slice::new`] constructs a distribution referencing a slice and uniformly @@ -68,7 +98,7 @@ use alloc::string::String; #[derive(Debug, Clone, Copy)] pub struct Slice<'a, T> { slice: &'a [T], - range: Uniform, + range: UniformUsize, num_choices: NonZeroUsize, } @@ -80,7 +110,7 @@ impl<'a, T> Slice<'a, T> { Ok(Self { slice, - range: Uniform::new(0, num_choices.get()).unwrap(), + range: UniformUsize::new(num_choices.get()).unwrap(), num_choices, }) } @@ -161,3 +191,17 @@ impl<'a> super::DistString for Slice<'a, char> { } } } + +#[cfg(test)] +mod test { + use super::*; + use core::iter; + + #[test] + fn value_stability() { + let rng = crate::test::rng(651); + let slice = Slice::new(b"escaped emus explore extensively").unwrap(); + let expected = b"eaxee"; + assert!(iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b)); + } +} diff --git a/src/distributions/utils.rs b/src/distributions/utils.rs index 7e84665ec42..b54dc6d6c4e 100644 --- a/src/distributions/utils.rs +++ b/src/distributions/utils.rs @@ -241,7 +241,9 @@ pub(crate) trait FloatSIMDScalarUtils: FloatSIMDUtils { /// Implement functions on f32/f64 to give them APIs similar to SIMD types pub(crate) trait FloatAsSIMD: Sized { + #[cfg(test)] const LEN: usize = 1; + #[inline(always)] fn splat(scalar: Self) -> Self { scalar diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index afd9246d4ab..1aa65149a14 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -86,7 +86,10 @@ pub mod mock; // Public so we don't export `StepRng` directly, making it a bit #[cfg(feature = "small_rng")] mod small; -#[cfg(all(feature = "small_rng", not(target_pointer_width = "64")))] +#[cfg(all( + feature = "small_rng", + any(target_pointer_width = "32", target_pointer_width = "16") +))] mod xoshiro128plusplus; #[cfg(all(feature = "small_rng", target_pointer_width = "64"))] mod xoshiro256plusplus; diff --git a/src/rngs/small.rs b/src/rngs/small.rs index 835eadc0bca..ea7df062842 100644 --- a/src/rngs/small.rs +++ b/src/rngs/small.rs @@ -10,10 +10,10 @@ use rand_core::{RngCore, SeedableRng}; +#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] +type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus; #[cfg(target_pointer_width = "64")] type Rng = super::xoshiro256plusplus::Xoshiro256PlusPlus; -#[cfg(not(target_pointer_width = "64"))] -type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus; /// A small-state, fast, non-crypto, non-portable PRNG /// diff --git a/src/seq/index.rs b/src/seq/index.rs index 471e87c1e2c..8afe6d26364 100644 --- a/src/seq/index.rs +++ b/src/seq/index.rs @@ -7,6 +7,7 @@ // except according to those terms. //! Low-level API for sampling indices +use super::gen_index; #[cfg(feature = "alloc")] use alloc::vec::{self, Vec}; use core::slice; @@ -288,7 +289,7 @@ where // Floyd's algorithm let mut indices = [0; N]; for (i, j) in (len - N..len).enumerate() { - let t = rng.gen_range(0..=j); + let t = gen_index(rng, j + 1); if let Some(pos) = indices[0..i].iter().position(|&x| x == t) { indices[pos] = j; } diff --git a/src/seq/slice.rs b/src/seq/slice.rs index 60a0b1e7e40..c82998fd358 100644 --- a/src/seq/slice.rs +++ b/src/seq/slice.rs @@ -495,19 +495,24 @@ mod test { assert_eq!(chars.choose(&mut r), Some(&'l')); assert_eq!(nums.choose_mut(&mut r), Some(&mut 3)); + assert_eq!( + &chars.choose_multiple_array(&mut r), + &Some(['f', 'i', 'd', 'b', 'c', 'm', 'j', 'k']) + ); + #[cfg(feature = "alloc")] assert_eq!( &chars .choose_multiple(&mut r, 8) .cloned() .collect::>(), - &['f', 'i', 'd', 'b', 'c', 'm', 'j', 'k'] + &['h', 'm', 'd', 'b', 'c', 'e', 'n', 'f'] ); #[cfg(feature = "alloc")] - assert_eq!(chars.choose_weighted(&mut r, |_| 1), Ok(&'l')); + assert_eq!(chars.choose_weighted(&mut r, |_| 1), Ok(&'i')); #[cfg(feature = "alloc")] - assert_eq!(nums.choose_weighted_mut(&mut r, |_| 1), Ok(&mut 8)); + assert_eq!(nums.choose_weighted_mut(&mut r, |_| 1), Ok(&mut 2)); let mut r = crate::test::rng(414); nums.shuffle(&mut r);