Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rng::gen_index; no usize for Standard #1471

Closed
wants to merge 10 commits into from
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.
- Fix portability of `rand::distributions::Slice` (#1469)
- Rename `rand::distributions` to `rand::distr` (#1470)
- The `serde1` feature has been renamed `serde` (#1477)
- Add `Rng::gen_index` and `UniformUsize` (#1471)
- Remove support for generating `isize` and `usize` values with `Standard`, `Uniform` and `Fill` and usage as a `WeightedAliasIndex` weight (#1471)

## [0.9.0-alpha.1] - 2024-03-18
- Add the `Slice::num_choices` method to the Slice distribution (#1402)
Expand Down
5 changes: 0 additions & 5 deletions benches/benches/base_distributions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,6 @@ distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000).unwrap());
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000).unwrap());
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123).unwrap());
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789).unwrap());
distr_int!(distr_uniform_usize16, usize, Uniform::new(0usize, 0xb9d7).unwrap());
distr_int!(distr_uniform_usize32, usize, Uniform::new(0usize, 0x548c0f43).unwrap());
#[cfg(target_pointer_width = "64")]
distr_int!(distr_uniform_usize64, usize, Uniform::new(0usize, 0x3a42714f2bf927a8).unwrap());
distr_int!(distr_uniform_isize, isize, Uniform::new(-1060478432isize, 1858574057).unwrap());

distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319).unwrap());
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319).unwrap());
Expand Down
2 changes: 0 additions & 2 deletions rand_distr/src/weighted_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,13 +359,11 @@ macro_rules! impl_weight_for_int {

impl_weight_for_float!(f64);
impl_weight_for_float!(f32);
impl_weight_for_int!(usize);
impl_weight_for_int!(u128);
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);
Expand Down
36 changes: 3 additions & 33 deletions src/distr/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -63,20 +63,6 @@ impl Distribution<u128> for Standard {
}
}

impl Distribution<usize> for Standard {
#[inline]
#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> usize {
rng.next_u32() as usize
}

#[inline]
#[cfg(target_pointer_width = "64")]
fn sample<R: Rng + ?Sized>(&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 {
Expand All @@ -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) => {
Expand All @@ -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 {
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -191,14 +174,12 @@ mod tests {
fn test_integers() {
let mut rng = crate::test::rng(806);

rng.sample::<isize, _>(Standard);
rng.sample::<i8, _>(Standard);
rng.sample::<i16, _>(Standard);
rng.sample::<i32, _>(Standard);
rng.sample::<i64, _>(Standard);
rng.sample::<i128, _>(Standard);

rng.sample::<usize, _>(Standard);
rng.sample::<u8, _>(Standard);
rng.sample::<u16, _>(Standard);
rng.sample::<u32, _>(Standard);
Expand Down Expand Up @@ -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
Expand Down
35 changes: 3 additions & 32 deletions src/distr/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>),
#[cfg(target_pointer_width = "64")]
U64(Uniform<u64>),
}

impl UniformUsize {
pub fn new(ubound: usize) -> Result<Self, super::uniform::Error> {
#[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<R: Rng + ?Sized>(&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
Expand Down Expand Up @@ -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,
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/distr/uniform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
140 changes: 137 additions & 3 deletions src/distr/uniform_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -385,6 +383,142 @@ 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(UniformUsizeImpl);

#[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"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UniformUsizeImpl {
U32(UniformInt<u32>),
#[cfg(target_pointer_width = "64")]
U64(UniformInt<u64>),
}

#[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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low < high) {
return Err(Error::EmptyRange);
}

#[cfg(target_pointer_width = "64")]
if high > (u32::MAX as usize) {
return UniformInt::new(low as u64, high as u64)
.map(|ui| UniformUsize(UniformUsizeImpl::U64(ui)));
}

UniformInt::new(low as u32, high as u32).map(|ui| UniformUsize(UniformUsizeImpl::U32(ui)))
}

#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low <= high) {
return Err(Error::EmptyRange);
}

#[cfg(target_pointer_width = "64")]
if high > (u32::MAX as usize) {
return UniformInt::new_inclusive(low as u64, high as u64)
.map(|ui| UniformUsize(UniformUsizeImpl::U64(ui)));
}

UniformInt::new_inclusive(low as u32, high as u32)
.map(|ui| UniformUsize(UniformUsizeImpl::U32(ui)))
}

#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> usize {
match self.0 {
UniformUsizeImpl::U32(uu) => uu.sample(rng) as usize,
#[cfg(target_pointer_width = "64")]
UniformUsizeImpl::U64(uu) => uu.sample(rng) as usize,
}
}

#[inline]
fn sample_single<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low < high) {
return Err(Error::EmptyRange);
}

#[cfg(target_pointer_width = "64")]
if high > (u32::MAX as usize) {
return UniformInt::<u64>::sample_single(low as u64, high as u64, rng)
.map(|x| x as usize);
}

UniformInt::<u32>::sample_single(low as u32, high as u32, rng).map(|x| x as usize)
}

#[inline]
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();
if !(low <= high) {
return Err(Error::EmptyRange);
}

#[cfg(target_pointer_width = "64")]
if high > (u32::MAX as usize) {
return UniformInt::<u64>::sample_single_inclusive(low as u64, high as u64, rng)
.map(|x| x as usize);
}

UniformInt::<u32>::sample_single_inclusive(low as u32, high as u32, rng).map(|x| x as usize)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -476,7 +610,7 @@ mod tests {
);)*
}};
}
t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, i128, u128);
t!(i8, i16, i32, i64, u8, u16, u32, u64, i128, u128);

#[cfg(feature = "simd_support")]
{
Expand Down
20 changes: 0 additions & 20 deletions src/distr/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,26 +124,6 @@ macro_rules! wmul_impl_large {
}
wmul_impl_large! { u128, 64 }

macro_rules! wmul_impl_usize {
($ty:ty) => {
impl WideningMultiply for usize {
type Output = (usize, usize);

#[inline(always)]
fn wmul(self, x: usize) -> Self::Output {
let (high, low) = (self as $ty).wmul(x as $ty);
(high as usize, low as usize)
}
}
};
}
#[cfg(target_pointer_width = "16")]
wmul_impl_usize! { u16 }
#[cfg(target_pointer_width = "32")]
wmul_impl_usize! { u32 }
#[cfg(target_pointer_width = "64")]
wmul_impl_usize! { u64 }

#[cfg(feature = "simd_support")]
mod simd_wmul {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion src/distr/weighted_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ mod test {
#[test]
fn overflow() {
assert_eq!(
WeightedIndex::new([2, usize::MAX]),
WeightedIndex::new([2, u32::MAX]),
Err(WeightError::Overflow)
);
}
Expand Down
Loading