From ef052ec539ed13dfbe5938559ca78f92071873f4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 9 Sep 2024 08:33:43 +0100 Subject: [PATCH] No usize except uniform (#1487) - Add UniformUsize - Support ..end and ..=ub range syntax for unsigned ints --- CHANGELOG.md | 2 + benches/benches/standard.rs | 2 +- rand_distr/src/weighted_alias.rs | 1 - src/distr/integer.rs | 36 +--- src/distr/slice.rs | 35 +--- src/distr/uniform.rs | 45 ++++- src/distr/uniform_int.rs | 276 ++++++++++++++++++++++++++++++- src/lib.rs | 4 +- src/rng.rs | 13 +- src/seq/index.rs | 134 +++++++++------ src/seq/iterator.rs | 9 +- src/seq/mod.rs | 17 +- src/seq/slice.rs | 10 +- 13 files changed, 416 insertions(+), 168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36aaa16782..d071e09391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. - Rename `rand::distributions` to `rand::distr` (#1470) - The `serde1` feature has been renamed `serde` (#1477) - Mark `WeightError`, `PoissonError`, `BinomialError` as `#[non_exhaustive]` (#1480). +- Add `UniformUsize` and use to make `Uniform` for `usize` portable (#1487) +- Remove support for generating `isize` and `usize` values with `Standard`, `Uniform` and `Fill` and usage as a `WeightedAliasIndex` weight (#1487) - Require `Clone` and `AsRef` bound for `SeedableRng::Seed`. (#1491) ## [0.9.0-alpha.1] - 2024-03-18 diff --git a/benches/benches/standard.rs b/benches/benches/standard.rs index 1e7fadc77a..cd88ea66a2 100644 --- a/benches/benches/standard.rs +++ b/benches/benches/standard.rs @@ -49,7 +49,7 @@ pub fn bench(c: &mut Criterion) { }; } - do_ty!(i8, i16, i32, i64, i128, isize); + do_ty!(i8, i16, i32, i64, i128); do_ty!(f32, f64); do_ty!(char); diff --git a/rand_distr/src/weighted_alias.rs b/rand_distr/src/weighted_alias.rs index 8826b5b76c..593219cafd 100644 --- a/rand_distr/src/weighted_alias.rs +++ b/rand_distr/src/weighted_alias.rs @@ -365,7 +365,6 @@ impl_weight_for_int!(u64); impl_weight_for_int!(u32); impl_weight_for_int!(u16); impl_weight_for_int!(u8); -impl_weight_for_int!(isize); impl_weight_for_int!(i128); impl_weight_for_int!(i64); impl_weight_for_int!(i32); diff --git a/src/distr/integer.rs b/src/distr/integer.rs index 49546a3941..8cf9ffc688 100644 --- a/src/distr/integer.rs +++ b/src/distr/integer.rs @@ -19,8 +19,8 @@ use core::arch::x86_64::__m512i; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::{__m128i, __m256i}; use core::num::{ - NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, - NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, + NonZeroU32, NonZeroU64, NonZeroU8, }; #[cfg(feature = "simd_support")] use core::simd::*; @@ -63,20 +63,6 @@ impl Distribution for Standard { } } -impl Distribution for Standard { - #[inline] - #[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] - fn sample(&self, rng: &mut R) -> usize { - rng.next_u32() as usize - } - - #[inline] - #[cfg(target_pointer_width = "64")] - fn sample(&self, rng: &mut R) -> usize { - rng.next_u64() as usize - } -} - macro_rules! impl_int_from_uint { ($ty:ty, $uty:ty) => { impl Distribution<$ty> for Standard { @@ -93,7 +79,6 @@ impl_int_from_uint! { i16, u16 } impl_int_from_uint! { i32, u32 } impl_int_from_uint! { i64, u64 } impl_int_from_uint! { i128, u128 } -impl_int_from_uint! { isize, usize } macro_rules! impl_nzint { ($ty:ty, $new:path) => { @@ -114,14 +99,12 @@ impl_nzint!(NonZeroU16, NonZeroU16::new); impl_nzint!(NonZeroU32, NonZeroU32::new); impl_nzint!(NonZeroU64, NonZeroU64::new); impl_nzint!(NonZeroU128, NonZeroU128::new); -impl_nzint!(NonZeroUsize, NonZeroUsize::new); impl_nzint!(NonZeroI8, NonZeroI8::new); impl_nzint!(NonZeroI16, NonZeroI16::new); impl_nzint!(NonZeroI32, NonZeroI32::new); impl_nzint!(NonZeroI64, NonZeroI64::new); impl_nzint!(NonZeroI128, NonZeroI128::new); -impl_nzint!(NonZeroIsize, NonZeroIsize::new); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] macro_rules! x86_intrinsic_impl { @@ -163,7 +146,7 @@ macro_rules! simd_impl { } #[cfg(feature = "simd_support")] -simd_impl!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize); +simd_impl!(u8, i8, u16, i16, u32, i32, u64, i64); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] x86_intrinsic_impl!( @@ -191,14 +174,12 @@ mod tests { fn test_integers() { let mut rng = crate::test::rng(806); - rng.sample::(Standard); rng.sample::(Standard); rng.sample::(Standard); rng.sample::(Standard); rng.sample::(Standard); rng.sample::(Standard); - rng.sample::(Standard); rng.sample::(Standard); rng.sample::(Standard); rng.sample::(Standard); @@ -239,17 +220,6 @@ mod tests { 111087889832015897993126088499035356354, ], ); - #[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] - test_samples(0usize, &[2220326409, 2575017975, 2018088303]); - #[cfg(target_pointer_width = "64")] - test_samples( - 0usize, - &[ - 11059617991457472009, - 16096616328739788143, - 1487364411147516184, - ], - ); test_samples(0i8, &[9, -9, 111]); // Skip further i* types: they are simple reinterpretation of u* samples diff --git a/src/distr/slice.rs b/src/distr/slice.rs index 4677b4e89e..d201caa246 100644 --- a/src/distr/slice.rs +++ b/src/distr/slice.rs @@ -8,40 +8,11 @@ use core::num::NonZeroUsize; -use crate::distr::{Distribution, Uniform}; -use crate::Rng; +use crate::distr::uniform::{UniformSampler, UniformUsize}; +use crate::distr::Distribution; #[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 @@ -110,7 +81,7 @@ impl<'a, T> Slice<'a, T> { Ok(Self { slice, - range: UniformUsize::new(num_choices.get()).unwrap(), + range: UniformUsize::new(0, num_choices.get()).unwrap(), num_choices, }) } diff --git a/src/distr/uniform.rs b/src/distr/uniform.rs index 4f07aaf26e..86a08fdc59 100644 --- a/src/distr/uniform.rs +++ b/src/distr/uniform.rs @@ -112,7 +112,7 @@ pub use float::UniformFloat; #[path = "uniform_int.rs"] mod int; #[doc(inline)] -pub use int::UniformInt; +pub use int::{UniformInt, UniformUsize}; #[path = "uniform_other.rs"] mod other; @@ -120,7 +120,7 @@ mod other; pub use other::{UniformChar, UniformDuration}; use core::fmt; -use core::ops::{Range, RangeInclusive}; +use core::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive}; use crate::distr::Distribution; use crate::{Rng, RngCore}; @@ -439,6 +439,41 @@ impl SampleRange for RangeInclusive { } } +macro_rules! impl_sample_range_u { + ($t:ty) => { + impl SampleRange<$t> for RangeTo<$t> { + #[inline] + fn sample_single(self, rng: &mut R) -> Result<$t, Error> { + <$t as SampleUniform>::Sampler::sample_single(0, self.end, rng) + } + + #[inline] + fn is_empty(&self) -> bool { + 0 == self.end + } + } + + impl SampleRange<$t> for RangeToInclusive<$t> { + #[inline] + fn sample_single(self, rng: &mut R) -> Result<$t, Error> { + <$t as SampleUniform>::Sampler::sample_single_inclusive(0, self.end, rng) + } + + #[inline] + fn is_empty(&self) -> bool { + false + } + } + }; +} + +impl_sample_range_u!(u8); +impl_sample_range_u!(u16); +impl_sample_range_u!(u32); +impl_sample_range_u!(u64); +impl_sample_range_u!(u128); +impl_sample_range_u!(usize); + #[cfg(test)] mod tests { use super::*; @@ -530,12 +565,6 @@ mod tests { assert_eq!(&buf, expected_multiple); } - // We test on a sub-set of types; possibly we should do more. - // TODO: SIMD types - - test_samples(11u8, 219, &[17, 66, 214], &[181, 93, 165]); - test_samples(11u32, 219, &[17, 66, 214], &[181, 93, 165]); - test_samples( 0f32, 1e-2f32, diff --git a/src/distr/uniform_int.rs b/src/distr/uniform_int.rs index 4a5bb31991..b53ca367b9 100644 --- a/src/distr/uniform_int.rs +++ b/src/distr/uniform_int.rs @@ -258,12 +258,10 @@ uniform_int_impl! { i16, u16, u32 } uniform_int_impl! { i32, u32, u32 } uniform_int_impl! { i64, u64, u64 } uniform_int_impl! { i128, u128, u128 } -uniform_int_impl! { isize, usize, usize } uniform_int_impl! { u8, u8, u32 } uniform_int_impl! { u16, u16, u32 } uniform_int_impl! { u32, u32, u32 } uniform_int_impl! { u64, u64, u64 } -uniform_int_impl! { usize, usize, usize } uniform_int_impl! { u128, u128, u128 } #[cfg(feature = "simd_support")] @@ -385,10 +383,189 @@ macro_rules! uniform_simd_int_impl { #[cfg(feature = "simd_support")] uniform_simd_int_impl! { (u8, i8), (u16, i16), (u32, i32), (u64, i64) } +/// The back-end implementing [`UniformSampler`] for `usize`. +/// +/// # Implementation notes +/// +/// Sampling a `usize` value is usually used in relation to the length of an +/// array or other memory structure, thus it is reasonable to assume that the +/// vast majority of use-cases will have a maximum size under [`u32::MAX`]. +/// In part to optimise for this use-case, but mostly to ensure that results +/// are portable across 32-bit and 64-bit architectures (as far as is possible), +/// this implementation will use 32-bit sampling when possible. +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct UniformUsize { + low: usize, + range: usize, + thresh: usize, + #[cfg(target_pointer_width = "64")] + mode64: bool, +} + +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +impl SampleUniform for usize { + type Sampler = UniformUsize; +} + +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +impl UniformSampler for UniformUsize { + type X = usize; + + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. + fn new(low_b: B1, high_b: B2) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low < high) { + return Err(Error::EmptyRange); + } + + UniformSampler::new_inclusive(low, high - 1) + } + + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. + fn new_inclusive(low_b: B1, high_b: B2) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low <= high) { + return Err(Error::EmptyRange); + } + + #[cfg(target_pointer_width = "64")] + let mode64 = high > (u32::MAX as usize); + #[cfg(target_pointer_width = "32")] + let mode64 = false; + + let (range, thresh); + if cfg!(target_pointer_width = "64") && !mode64 { + let range32 = (high as u32).wrapping_sub(low as u32).wrapping_add(1); + range = range32 as usize; + thresh = if range32 > 0 { + (range32.wrapping_neg() % range32) as usize + } else { + 0 + }; + } else { + range = high.wrapping_sub(low).wrapping_add(1); + thresh = if range > 0 { + range.wrapping_neg() % range + } else { + 0 + }; + } + + Ok(UniformUsize { + low, + range, + thresh, + #[cfg(target_pointer_width = "64")] + mode64, + }) + } + + #[inline] + fn sample(&self, rng: &mut R) -> usize { + #[cfg(target_pointer_width = "32")] + let mode32 = true; + #[cfg(target_pointer_width = "64")] + let mode32 = !self.mode64; + + if mode32 { + let range = self.range as u32; + if range == 0 { + return rng.random::() as usize; + } + + let thresh = self.thresh as u32; + let hi = loop { + let (hi, lo) = rng.random::().wmul(range); + if lo >= thresh { + break hi; + } + }; + self.low.wrapping_add(hi as usize) + } else { + let range = self.range as u64; + if range == 0 { + return rng.random::() as usize; + } + + let thresh = self.thresh as u64; + let hi = loop { + let (hi, lo) = rng.random::().wmul(range); + if lo >= thresh { + break hi; + } + }; + self.low.wrapping_add(hi as usize) + } + } + + #[inline] + fn sample_single( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low < high) { + return Err(Error::EmptyRange); + } + + if cfg!(target_pointer_width = "64") && high > (u32::MAX as usize) { + return UniformInt::::sample_single(low as u64, high as u64, rng) + .map(|x| x as usize); + } + + UniformInt::::sample_single(low as u32, high as u32, rng).map(|x| x as usize) + } + + #[inline] + fn sample_single_inclusive( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low <= high) { + return Err(Error::EmptyRange); + } + + if cfg!(target_pointer_width = "64") && high > (u32::MAX as usize) { + return UniformInt::::sample_single_inclusive(low as u64, high as u64, rng) + .map(|x| x as usize); + } + + UniformInt::::sample_single_inclusive(low as u32, high as u32, rng).map(|x| x as usize) + } +} + #[cfg(test)] mod tests { use super::*; - use crate::distr::Uniform; + use crate::distr::{Distribution, Uniform}; + use core::fmt::Debug; + use core::ops::Add; #[test] fn test_uniform_bad_limits_equal_int() { @@ -476,7 +653,7 @@ mod tests { );)* }}; } - t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, i128, u128); + t!(i8, i16, i32, i64, i128, u8, u16, u32, u64, usize, u128); #[cfg(feature = "simd_support")] { @@ -518,4 +695,95 @@ mod tests { assert!(Uniform::try_from(100..=10).is_err()); assert!(Uniform::try_from(100..=99).is_err()); } + + #[test] + fn value_stability() { + fn test_samples>( + lb: T, + ub: T, + ub_excl: T, + expected: &[T], + ) where + Uniform: Distribution, + { + let mut rng = crate::test::rng(897); + let mut buf = [lb; 6]; + + for x in &mut buf[0..3] { + *x = T::Sampler::sample_single_inclusive(lb, ub, &mut rng).unwrap(); + } + + let distr = Uniform::new_inclusive(lb, ub).unwrap(); + for x in &mut buf[3..6] { + *x = rng.sample(&distr); + } + assert_eq!(&buf, expected); + + let mut rng = crate::test::rng(897); + + for x in &mut buf[0..3] { + *x = T::Sampler::sample_single(lb, ub_excl, &mut rng).unwrap(); + } + + let distr = Uniform::new(lb, ub_excl).unwrap(); + for x in &mut buf[3..6] { + *x = rng.sample(&distr); + } + assert_eq!(&buf, expected); + } + + test_samples(-105i8, 111, 112, &[-99, -48, 107, 72, -19, 56]); + test_samples(2i16, 1352, 1353, &[43, 361, 1325, 1109, 539, 1005]); + test_samples( + -313853i32, + 13513, + 13514, + &[-303803, -226673, 6912, -45605, -183505, -70668], + ); + test_samples( + 131521i64, + 6542165, + 6542166, + &[1838724, 5384489, 4893692, 3712948, 3951509, 4094926], + ); + test_samples( + -0x8000_0000_0000_0000_0000_0000_0000_0000i128, + -1, + 0, + &[ + -30725222750250982319765550926688025855, + -75088619368053423329503924805178012357, + -64950748766625548510467638647674468829, + -41794017901603587121582892414659436495, + -63623852319608406524605295913876414006, + -17404679390297612013597359206379189023, + ], + ); + test_samples(11u8, 218, 219, &[17, 66, 214, 181, 93, 165]); + test_samples(11u16, 218, 219, &[17, 66, 214, 181, 93, 165]); + test_samples(11u32, 218, 219, &[17, 66, 214, 181, 93, 165]); + test_samples(11u64, 218, 219, &[66, 181, 165, 127, 134, 139]); + test_samples(11u128, 218, 219, &[181, 127, 139, 167, 141, 197]); + test_samples(11usize, 218, 219, &[17, 66, 214, 181, 93, 165]); + + #[cfg(feature = "simd_support")] + { + let lb = Simd::from([11u8, 0, 128, 127]); + let ub = Simd::from([218, 254, 254, 254]); + let ub_excl = ub + Simd::splat(1); + test_samples( + lb, + ub, + ub_excl, + &[ + Simd::from([13, 5, 237, 130]), + Simd::from([126, 186, 149, 161]), + Simd::from([103, 86, 234, 252]), + Simd::from([35, 18, 225, 231]), + Simd::from([106, 153, 246, 177]), + Simd::from([195, 168, 149, 222]), + ], + ); + } + } } diff --git a/src/lib.rs b/src/lib.rs index ce0206db09..3abbc5a266 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,13 +179,13 @@ mod test { #[test] #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] fn test_random() { - let _n: usize = random(); + let _n: u64 = random(); let _f: f32 = random(); let _o: Option> = random(); #[allow(clippy::type_complexity)] let _many: ( (), - (usize, isize, Option<(u32, (bool,))>), + Option<(u32, (bool,))>, (u8, i8, u16, i16, u32, i32, u64, i64), (f32, (f64, (f64,))), ) = random(); diff --git a/src/rng.rs b/src/rng.rs index 9cea5fb20c..9190747a29 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -102,7 +102,8 @@ pub trait Rng: RngCore { /// made from the given range. See also the [`Uniform`] distribution /// type which may be faster if sampling from the same range repeatedly. /// - /// Only `gen_range(low..high)` and `gen_range(low..=high)` are supported. + /// All types support `low..high_exclusive` and `low..=high` range syntax. + /// Unsigned integer types also support `..high_exclusive` and `..=high` syntax. /// /// # Panics /// @@ -116,13 +117,13 @@ pub trait Rng: RngCore { /// let mut rng = thread_rng(); /// /// // Exclusive range - /// let n: u32 = rng.gen_range(0..10); + /// let n: u32 = rng.gen_range(..10); /// println!("{}", n); /// let m: f64 = rng.gen_range(-40.0..1.3e5); /// println!("{}", m); /// /// // Inclusive range - /// let n: u32 = rng.gen_range(0..=10); + /// let n: u32 = rng.gen_range(..=10); /// println!("{}", n); /// ``` /// @@ -407,8 +408,8 @@ macro_rules! impl_fill { } } -impl_fill!(u16, u32, u64, usize, u128,); -impl_fill!(i8, i16, i32, i64, isize, i128,); +impl_fill!(u16, u32, u64, u128,); +impl_fill!(i8, i16, i32, i64, i128,); impl Fill for [T; N] where @@ -500,7 +501,7 @@ mod test { let a: u32 = r.gen_range(12..=24); assert!((12..=24).contains(&a)); - assert_eq!(r.gen_range(0u32..1), 0u32); + assert_eq!(r.gen_range(..1u32), 0u32); assert_eq!(r.gen_range(-12i64..-11), -12i64); assert_eq!(r.gen_range(3_000_000..3_000_001), 3_000_000); } diff --git a/src/seq/index.rs b/src/seq/index.rs index 53bd622e1c..5bb1a7597f 100644 --- a/src/seq/index.rs +++ b/src/seq/index.rs @@ -23,6 +23,9 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::collections::HashSet; +#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] +compile_error!("unsupported pointer width"); + /// A vector of indices. /// /// Multiple internal representations are possible. @@ -31,26 +34,29 @@ use std::collections::HashSet; pub enum IndexVec { #[doc(hidden)] U32(Vec), + #[cfg(target_pointer_width = "64")] #[doc(hidden)] - USize(Vec), + U64(Vec), } impl IndexVec { /// Returns the number of indices #[inline] pub fn len(&self) -> usize { - match *self { - IndexVec::U32(ref v) => v.len(), - IndexVec::USize(ref v) => v.len(), + match self { + IndexVec::U32(v) => v.len(), + #[cfg(target_pointer_width = "64")] + IndexVec::U64(v) => v.len(), } } /// Returns `true` if the length is 0. #[inline] pub fn is_empty(&self) -> bool { - match *self { - IndexVec::U32(ref v) => v.is_empty(), - IndexVec::USize(ref v) => v.is_empty(), + match self { + IndexVec::U32(v) => v.is_empty(), + #[cfg(target_pointer_width = "64")] + IndexVec::U64(v) => v.is_empty(), } } @@ -60,9 +66,10 @@ impl IndexVec { /// restrictions.) #[inline] pub fn index(&self, index: usize) -> usize { - match *self { - IndexVec::U32(ref v) => v[index] as usize, - IndexVec::USize(ref v) => v[index], + match self { + IndexVec::U32(v) => v[index] as usize, + #[cfg(target_pointer_width = "64")] + IndexVec::U64(v) => v[index] as usize, } } @@ -71,16 +78,18 @@ impl IndexVec { pub fn into_vec(self) -> Vec { match self { IndexVec::U32(v) => v.into_iter().map(|i| i as usize).collect(), - IndexVec::USize(v) => v, + #[cfg(target_pointer_width = "64")] + IndexVec::U64(v) => v.into_iter().map(|i| i as usize).collect(), } } /// Iterate over the indices as a sequence of `usize` values #[inline] pub fn iter(&self) -> IndexVecIter<'_> { - match *self { - IndexVec::U32(ref v) => IndexVecIter::U32(v.iter()), - IndexVec::USize(ref v) => IndexVecIter::USize(v.iter()), + match self { + IndexVec::U32(v) => IndexVecIter::U32(v.iter()), + #[cfg(target_pointer_width = "64")] + IndexVec::U64(v) => IndexVecIter::U64(v.iter()), } } } @@ -94,7 +103,8 @@ impl IntoIterator for IndexVec { fn into_iter(self) -> IndexVecIntoIter { match self { IndexVec::U32(v) => IndexVecIntoIter::U32(v.into_iter()), - IndexVec::USize(v) => IndexVecIntoIter::USize(v.into_iter()), + #[cfg(target_pointer_width = "64")] + IndexVec::U64(v) => IndexVecIntoIter::U64(v.into_iter()), } } } @@ -104,12 +114,15 @@ impl PartialEq for IndexVec { use self::IndexVec::*; match (self, other) { (U32(v1), U32(v2)) => v1 == v2, - (USize(v1), USize(v2)) => v1 == v2, - (U32(v1), USize(v2)) => { - (v1.len() == v2.len()) && (v1.iter().zip(v2.iter()).all(|(x, y)| *x as usize == *y)) + #[cfg(target_pointer_width = "64")] + (U64(v1), U64(v2)) => v1 == v2, + #[cfg(target_pointer_width = "64")] + (U32(v1), U64(v2)) => { + (v1.len() == v2.len()) && (v1.iter().zip(v2.iter()).all(|(x, y)| *x as u64 == *y)) } - (USize(v1), U32(v2)) => { - (v1.len() == v2.len()) && (v1.iter().zip(v2.iter()).all(|(x, y)| *x == *y as usize)) + #[cfg(target_pointer_width = "64")] + (U64(v1), U32(v2)) => { + (v1.len() == v2.len()) && (v1.iter().zip(v2.iter()).all(|(x, y)| *x == *y as u64)) } } } @@ -122,10 +135,11 @@ impl From> for IndexVec { } } -impl From> for IndexVec { +#[cfg(target_pointer_width = "64")] +impl From> for IndexVec { #[inline] - fn from(v: Vec) -> Self { - IndexVec::USize(v) + fn from(v: Vec) -> Self { + IndexVec::U64(v) } } @@ -134,8 +148,9 @@ impl From> for IndexVec { pub enum IndexVecIter<'a> { #[doc(hidden)] U32(slice::Iter<'a, u32>), + #[cfg(target_pointer_width = "64")] #[doc(hidden)] - USize(slice::Iter<'a, usize>), + U64(slice::Iter<'a, u64>), } impl<'a> Iterator for IndexVecIter<'a> { @@ -144,17 +159,19 @@ impl<'a> Iterator for IndexVecIter<'a> { #[inline] fn next(&mut self) -> Option { use self::IndexVecIter::*; - match *self { - U32(ref mut iter) => iter.next().map(|i| *i as usize), - USize(ref mut iter) => iter.next().cloned(), + match self { + U32(iter) => iter.next().map(|i| *i as usize), + #[cfg(target_pointer_width = "64")] + U64(iter) => iter.next().map(|i| *i as usize), } } #[inline] fn size_hint(&self) -> (usize, Option) { - match *self { - IndexVecIter::U32(ref v) => v.size_hint(), - IndexVecIter::USize(ref v) => v.size_hint(), + match self { + IndexVecIter::U32(v) => v.size_hint(), + #[cfg(target_pointer_width = "64")] + IndexVecIter::U64(v) => v.size_hint(), } } } @@ -166,8 +183,9 @@ impl<'a> ExactSizeIterator for IndexVecIter<'a> {} pub enum IndexVecIntoIter { #[doc(hidden)] U32(vec::IntoIter), + #[cfg(target_pointer_width = "64")] #[doc(hidden)] - USize(vec::IntoIter), + U64(vec::IntoIter), } impl Iterator for IndexVecIntoIter { @@ -176,18 +194,20 @@ impl Iterator for IndexVecIntoIter { #[inline] fn next(&mut self) -> Option { use self::IndexVecIntoIter::*; - match *self { - U32(ref mut v) => v.next().map(|i| i as usize), - USize(ref mut v) => v.next(), + match self { + U32(v) => v.next().map(|i| i as usize), + #[cfg(target_pointer_width = "64")] + U64(v) => v.next().map(|i| i as usize), } } #[inline] fn size_hint(&self) -> (usize, Option) { use self::IndexVecIntoIter::*; - match *self { - U32(ref v) => v.size_hint(), - USize(ref v) => v.size_hint(), + match self { + U32(v) => v.size_hint(), + #[cfg(target_pointer_width = "64")] + U64(v) => v.size_hint(), } } } @@ -225,9 +245,13 @@ where panic!("`amount` of samples must be less than or equal to `length`"); } if length > (u32::MAX as usize) { + #[cfg(target_pointer_width = "32")] + unreachable!(); + // We never want to use inplace here, but could use floyd's alg // Lazy version: always use the cache alg. - return sample_rejection(rng, length, amount); + #[cfg(target_pointer_width = "64")] + return sample_rejection(rng, length as u64, amount as u64); } let amount = amount as u32; let length = length as u32; @@ -285,7 +309,15 @@ where X: Into, { if length > (u32::MAX as usize) { - sample_efraimidis_spirakis(rng, length, weight, amount) + #[cfg(target_pointer_width = "32")] + unreachable!(); + + #[cfg(target_pointer_width = "64")] + { + let amount = amount as u64; + let length = length as u64; + sample_efraimidis_spirakis(rng, length, weight, amount) + } } else { assert!(amount <= u32::MAX as usize); let amount = amount as u32; @@ -402,7 +434,7 @@ where debug_assert!(amount <= length); let mut indices = Vec::with_capacity(amount as usize); for j in length - amount..length { - let t = rng.gen_range(0..=j); + let t = rng.gen_range(..=j); if let Some(pos) = indices.iter().position(|&x| x == t) { indices[pos] = j; } @@ -463,7 +495,8 @@ impl UInt for u32 { } } -impl UInt for usize { +#[cfg(target_pointer_width = "64")] +impl UInt for u64 { #[inline] fn zero() -> Self { 0 @@ -476,7 +509,7 @@ impl UInt for usize { #[inline] fn as_usize(self) -> usize { - self + self as usize } } @@ -521,20 +554,10 @@ mod test { #[test] #[cfg(feature = "serde")] fn test_serialization_index_vec() { - let some_index_vec = IndexVec::from(vec![254_usize, 234, 2, 1]); + let some_index_vec = IndexVec::from(vec![254_u32, 234, 2, 1]); let de_some_index_vec: IndexVec = bincode::deserialize(&bincode::serialize(&some_index_vec).unwrap()).unwrap(); - match (some_index_vec, de_some_index_vec) { - (IndexVec::U32(a), IndexVec::U32(b)) => { - assert_eq!(a, b); - } - (IndexVec::USize(a), IndexVec::USize(b)) => { - assert_eq!(a, b); - } - _ => { - panic!("failed to seralize/deserialize `IndexVec`") - } - } + assert_eq!(some_index_vec, de_some_index_vec); } #[test] @@ -610,7 +633,8 @@ mod test { assert!((i as usize) < len); } } - IndexVec::USize(_) => panic!("expected `IndexVec::U32`"), + #[cfg(target_pointer_width = "64")] + _ => panic!("expected `IndexVec::U32`"), } } diff --git a/src/seq/iterator.rs b/src/seq/iterator.rs index 46ecdfeac8..148093157d 100644 --- a/src/seq/iterator.rs +++ b/src/seq/iterator.rs @@ -9,7 +9,6 @@ //! `IteratorRandom` use super::coin_flipper::CoinFlipper; -use super::gen_index; #[allow(unused)] use super::IndexedRandom; use crate::Rng; @@ -71,7 +70,7 @@ pub trait IteratorRandom: Iterator + Sized { return match lower { 0 => None, 1 => self.next(), - _ => self.nth(gen_index(rng, lower)), + _ => self.nth(rng.gen_range(..lower)), }; } @@ -81,7 +80,7 @@ pub trait IteratorRandom: Iterator + Sized { // Continue until the iterator is exhausted loop { if lower > 1 { - let ix = gen_index(coin_flipper.rng, lower + consumed); + let ix = coin_flipper.rng.gen_range(..lower + consumed); let skip = if ix < lower { result = self.nth(ix); lower - (ix + 1) @@ -204,7 +203,7 @@ pub trait IteratorRandom: Iterator + Sized { // Continue, since the iterator was not exhausted for (i, elem) in self.enumerate() { - let k = gen_index(rng, i + 1 + amount); + let k = rng.gen_range(..i + 1 + amount); if let Some(slot) = buf.get_mut(k) { *slot = elem; } @@ -240,7 +239,7 @@ pub trait IteratorRandom: Iterator + Sized { // If the iterator stops once, then so do we. if reservoir.len() == amount { for (i, elem) in self.enumerate() { - let k = gen_index(rng, i + 1 + amount); + let k = rng.gen_range(..i + 1 + amount); if let Some(slot) = reservoir.get_mut(k) { *slot = elem; } diff --git a/src/seq/mod.rs b/src/seq/mod.rs index 0015517907..66459fe174 100644 --- a/src/seq/mod.rs +++ b/src/seq/mod.rs @@ -43,23 +43,8 @@ pub use iterator::IteratorRandom; pub use slice::SliceChooseIter; pub use slice::{IndexedMutRandom, IndexedRandom, SliceRandom}; -use crate::Rng; - -// Sample a number uniformly between 0 and `ubound`. Uses 32-bit sampling where -// possible, primarily in order to produce the same output on 32-bit and 64-bit -// platforms. -#[inline] -fn gen_index(rng: &mut R, ubound: usize) -> usize { - if ubound <= (u32::MAX as usize) { - rng.gen_range(0..ubound as u32) as usize - } else { - rng.gen_range(0..ubound) - } -} - /// Low-level API for sampling indices pub mod index { - use super::gen_index; use crate::Rng; #[cfg(feature = "alloc")] @@ -84,7 +69,7 @@ pub mod index { // Floyd's algorithm let mut indices = [0; N]; for (i, j) in (len - N..len).enumerate() { - let t = gen_index(rng, j + 1); + let t = rng.gen_range(..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 75f304c922..930e545094 100644 --- a/src/seq/slice.rs +++ b/src/seq/slice.rs @@ -9,7 +9,7 @@ //! `IndexedRandom`, `IndexedMutRandom`, `SliceRandom` use super::increasing_uniform::IncreasingUniform; -use super::{gen_index, index}; +use super::index; #[cfg(feature = "alloc")] use crate::distr::uniform::{SampleBorrow, SampleUniform}; #[cfg(feature = "alloc")] @@ -57,7 +57,7 @@ pub trait IndexedRandom: Index { if self.is_empty() { None } else { - Some(&self[gen_index(rng, self.len())]) + Some(&self[rng.gen_range(..self.len())]) } } @@ -259,7 +259,7 @@ pub trait IndexedMutRandom: IndexedRandom + IndexMut { None } else { let len = self.len(); - Some(&mut self[gen_index(rng, len)]) + Some(&mut self[rng.gen_range(..len)]) } } @@ -399,7 +399,7 @@ impl SliceRandom for [T] { // It ensures that the last `amount` elements of the slice // are randomly selected from the whole slice. - // `IncreasingUniform::next_index()` is faster than `gen_index` + // `IncreasingUniform::next_index()` is faster than `Rng::gen_range` // but only works for 32 bit integers // So we must use the slow method if the slice is longer than that. if self.len() < (u32::MAX as usize) { @@ -410,7 +410,7 @@ impl SliceRandom for [T] { } } else { for i in m..self.len() { - let index = gen_index(rng, i + 1); + let index = rng.gen_range(..i + 1); self.swap(i, index); } }