From 3bf4f424691ef9d24dd4f47b1c2badd6329573f2 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 16 Jul 2024 10:12:34 +0100 Subject: [PATCH 1/8] cfg-gate normally unused constant --- src/distributions/utils.rs | 2 ++ 1 file changed, 2 insertions(+) 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 From 54d6e3d38054efeec2483211df1bbe33540933fd Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 16 Jul 2024 11:47:01 +0100 Subject: [PATCH 2/8] Use Iterator::all over map --- rand_distr/src/dirichlet.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) 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] From 91b90ba63ac25ba481f74b7d0b5a9a88834b6048 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 16 Jul 2024 11:55:19 +0100 Subject: [PATCH 3/8] Make choose_multiple_array portable and add value-stability test --- src/seq/index.rs | 3 ++- src/seq/slice.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) 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..033707d5426 100644 --- a/src/seq/slice.rs +++ b/src/seq/slice.rs @@ -504,10 +504,15 @@ mod test { &['f', 'i', 'd', 'b', 'c', 'm', 'j', 'k'] ); + assert_eq!( + &chars.choose_multiple_array(&mut r), + &Some(['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); From 8e070ac8952d2e063bf8cfba97d451b7bbe05928 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 16 Jul 2024 12:38:31 +0100 Subject: [PATCH 4/8] Make rand::distributions::Slice portable and add value stability test Also assert that pointer width is an expected value. --- src/distributions/slice.rs | 47 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/distributions/slice.rs b/src/distributions/slice.rs index 10f830b7bb3..adc829c0752 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")))] +const _: () = assert!(false, "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,16 @@ impl<'a> super::DistString for Slice<'a, char> { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[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!(std::iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b)); + } +} From 42509010b00dfcd01b4c55229a8e0708315026f9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 17 Jul 2024 08:47:22 +0100 Subject: [PATCH 5/8] SmallRng: make intended platform support explicit --- src/rngs/mod.rs | 5 ++++- src/rngs/small.rs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) 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 /// From 4da448fc142598ccb590fa03e9867b8e1b14ae4e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 18 Jul 2024 09:05:02 +0100 Subject: [PATCH 6/8] Update CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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) From 2019db15b4923cae7787e6d47276bcb913a006e8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 18 Jul 2024 09:09:21 +0100 Subject: [PATCH 7/8] Fix tests for no-std builds --- src/distributions/slice.rs | 3 ++- src/seq/slice.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/distributions/slice.rs b/src/distributions/slice.rs index adc829c0752..3bf3118078b 100644 --- a/src/distributions/slice.rs +++ b/src/distributions/slice.rs @@ -195,12 +195,13 @@ 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!(std::iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b)); + assert!(iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b)); } } diff --git a/src/seq/slice.rs b/src/seq/slice.rs index 033707d5426..c82998fd358 100644 --- a/src/seq/slice.rs +++ b/src/seq/slice.rs @@ -495,18 +495,18 @@ 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'] - ); - - assert_eq!( - &chars.choose_multiple_array(&mut r), - &Some(['h', 'm', 'd', 'b', 'c', 'e', 'n', 'f']) + &['h', 'm', 'd', 'b', 'c', 'e', 'n', 'f'] ); #[cfg(feature = "alloc")] From 89ad30d84f360c1526227133b519d004d060291d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 19 Jul 2024 14:30:11 +0100 Subject: [PATCH 8/8] Use compile_error! --- src/distributions/slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distributions/slice.rs b/src/distributions/slice.rs index 3bf3118078b..8b8f9662595 100644 --- a/src/distributions/slice.rs +++ b/src/distributions/slice.rs @@ -14,7 +14,7 @@ use crate::Rng; use alloc::string::String; #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] -const _: () = assert!(false, "unsupported pointer width"); +compile_error!("unsupported pointer width"); #[derive(Debug, Clone, Copy)] enum UniformUsize {