Skip to content

Commit 5bdcc3d

Browse files
committed
Implement UniformSampler for Wrapping<T>
1 parent b3fd8d0 commit 5bdcc3d

File tree

1 file changed

+199
-5
lines changed

1 file changed

+199
-5
lines changed

src/distributions/uniform.rs

Lines changed: 199 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
//! [`UniformFloat`]: struct.UniformFloat.html
100100
//! [`UniformDuration`]: struct.UniformDuration.html
101101
102+
use core::num::Wrapping;
102103
#[cfg(feature = "std")]
103104
use std::time::Duration;
104105

@@ -507,6 +508,139 @@ wmul_impl_usize! { u64 }
507508

508509

509510

511+
/// The back-end implementing [`UniformSampler`] for `Wrapping<T>`.
512+
///
513+
/// Unless you are implementing [`UniformSampler`] for your own types, this type
514+
/// should not be used directly, use [`Uniform`] instead.
515+
///
516+
/// The method used is the same as for [`UniformInt`], with one exception: it is
517+
/// not required that `low <= high`. If `low` is greater than `high`, the range
518+
/// to sample from contains both `low..MAX` and `MIN..high`, i.e. the range
519+
/// wraps around.
520+
///
521+
/// [`UniformSampler`]: trait.UniformSampler.html
522+
/// [`Uniform`]: struct.Uniform.html
523+
/// [`UniformInt`]: struct.UniformInt.html
524+
#[derive(Clone, Copy, Debug)]
525+
pub struct UniformWrapping<X> {
526+
low: X,
527+
range: X,
528+
zone: X,
529+
}
530+
531+
macro_rules! uniform_wrapping_int_impl {
532+
($ty:ty, $signed:ty, $unsigned:ident,
533+
$i_large:ident, $u_large:ident) => {
534+
impl SampleUniform for Wrapping<$ty> {
535+
type Sampler = UniformWrapping<$ty>;
536+
}
537+
538+
impl UniformSampler for UniformWrapping<$ty> {
539+
// We play free and fast with unsigned vs signed here
540+
// (when $ty is signed), but that's fine, since the
541+
// contract of this macro is for $ty and $unsigned to be
542+
// "bit-equal", so casting between them is a no-op.
543+
544+
type X = Wrapping<$ty>;
545+
546+
#[inline] // if the range is constant, this helps LLVM to do the
547+
// calculations at compile-time.
548+
fn new(low: Self::X, high: Self::X) -> Self {
549+
UniformSampler::new_inclusive(low, high - Wrapping(1))
550+
}
551+
552+
#[inline] // if the range is constant, this helps LLVM to do the
553+
// calculations at compile-time.
554+
fn new_inclusive(low: Self::X, high: Self::X) -> Self {
555+
let unsigned_max = ::core::$unsigned::MAX;
556+
557+
let range =
558+
high.0.wrapping_sub(low.0).wrapping_add(1) as $unsigned;
559+
let ints_to_reject =
560+
if range > 0 {
561+
(unsigned_max - range + 1) % range
562+
} else {
563+
0
564+
};
565+
let zone = unsigned_max - ints_to_reject;
566+
567+
UniformWrapping {
568+
low: low.0,
569+
// These are really $unsigned values, but store as $ty:
570+
range: range as $ty,
571+
zone: zone as $ty
572+
}
573+
}
574+
575+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
576+
let range = self.range as $unsigned as $u_large;
577+
if range > 0 {
578+
// Grow `zone` to fit a type of at least 32 bits, by
579+
// sign-extending it (the first bit is always 1, so are all
580+
// the preceding bits of the larger type).
581+
// For types that already have the right size, all the
582+
// casting is a no-op.
583+
let zone = self.zone as $signed as $i_large as $u_large;
584+
loop {
585+
let v: $u_large = rng.gen();
586+
let (hi, lo) = v.wmul(range);
587+
if lo <= zone {
588+
return Wrapping(self.low.wrapping_add(hi as $ty));
589+
}
590+
}
591+
} else {
592+
// Sample from the entire integer range.
593+
rng.gen()
594+
}
595+
}
596+
597+
fn sample_single<R: Rng + ?Sized>(low: Self::X,
598+
high: Self::X,
599+
rng: &mut R) -> Self::X
600+
{
601+
let range = high.0.wrapping_sub(low.0) as $unsigned as $u_large;
602+
let zone =
603+
if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned {
604+
// Using a modulus is faster than the approximation for
605+
// i8 and i16. I suppose we trade the cost of one
606+
// modulus for near-perfect branch prediction.
607+
let unsigned_max: $u_large = ::core::$u_large::MAX;
608+
let ints_to_reject = (unsigned_max - range + 1) % range;
609+
unsigned_max - ints_to_reject
610+
} else {
611+
// conservative but fast approximation
612+
range << range.leading_zeros()
613+
};
614+
615+
loop {
616+
let v: $u_large = rng.gen();
617+
let (hi, lo) = v.wmul(range);
618+
if lo <= zone {
619+
return Wrapping(low.0.wrapping_add(hi as $ty));
620+
}
621+
}
622+
}
623+
}
624+
}
625+
}
626+
627+
uniform_wrapping_int_impl! { i8, i8, u8, i32, u32 }
628+
uniform_wrapping_int_impl! { i16, i16, u16, i32, u32 }
629+
uniform_wrapping_int_impl! { i32, i32, u32, i32, u32 }
630+
uniform_wrapping_int_impl! { i64, i64, u64, i64, u64 }
631+
#[cfg(feature = "i128_support")]
632+
uniform_wrapping_int_impl! { i128, i128, u128, u128, u128 }
633+
uniform_wrapping_int_impl! { isize, isize, usize, isize, usize }
634+
uniform_wrapping_int_impl! { u8, i8, u8, i32, u32 }
635+
uniform_wrapping_int_impl! { u16, i16, u16, i32, u32 }
636+
uniform_wrapping_int_impl! { u32, i32, u32, i32, u32 }
637+
uniform_wrapping_int_impl! { u64, i64, u64, i64, u64 }
638+
uniform_wrapping_int_impl! { usize, isize, usize, isize, usize }
639+
#[cfg(feature = "i128_support")]
640+
uniform_wrapping_int_impl! { u128, u128, u128, i128, u128 }
641+
642+
643+
510644
/// The back-end implementing [`UniformSampler`] for floating-point types.
511645
///
512646
/// Unless you are implementing [`UniformSampler`] for your own type, this type
@@ -548,7 +682,7 @@ macro_rules! uniform_float_impl {
548682
type X = $ty;
549683

550684
fn new(low: Self::X, high: Self::X) -> Self {
551-
assert!(low < high, "Uniform::new called with `low >= high`");
685+
assert!(low <= high, "Uniform::new called with `low >= high`"); // FIXME
552686
let scale = high - low;
553687
let offset = low - scale;
554688
UniformFloat {
@@ -697,6 +831,7 @@ impl UniformSampler for UniformDuration {
697831
mod tests {
698832
use Rng;
699833
use distributions::uniform::{Uniform, UniformSampler, UniformFloat, SampleUniform};
834+
use core::num::Wrapping;
700835

701836
#[should_panic]
702837
#[test]
@@ -746,10 +881,10 @@ mod tests {
746881
macro_rules! t {
747882
($($ty:ident),*) => {{
748883
$(
749-
let v: &[($ty, $ty)] = &[(0, 10),
750-
(10, 127),
751-
(::core::$ty::MIN, ::core::$ty::MAX)];
752-
for &(low, high) in v.iter() {
884+
let v: &[($ty, $ty)] = &[(0, 10),
885+
(10, 127),
886+
(::core::$ty::MIN, ::core::$ty::MAX)];
887+
for &(low, high) in v.iter() {
753888
let my_uniform = Uniform::new(low, high);
754889
for _ in 0..1000 {
755890
let v: $ty = rng.sample(my_uniform);
@@ -776,6 +911,65 @@ mod tests {
776911
t!(i128, u128)
777912
}
778913

914+
#[test]
915+
fn test_wrapping() {
916+
let mut rng = ::test::rng(251);
917+
macro_rules! t {
918+
($($ty:ident),*) => {{
919+
$(
920+
let v: &[(Wrapping<$ty>, Wrapping<$ty>)] =
921+
&[(Wrapping(0), Wrapping(10)),
922+
(Wrapping(10), Wrapping(127)),
923+
(Wrapping(::core::$ty::MIN), Wrapping(::core::$ty::MAX))];
924+
for &(low, high) in v.iter() {
925+
let my_uniform = Uniform::new(low, high);
926+
for _ in 0..1000 {
927+
let v: Wrapping<$ty> = rng.sample(my_uniform);
928+
assert!(low <= v && v < high);
929+
}
930+
931+
let my_uniform = Uniform::new_inclusive(low, high);
932+
for _ in 0..1000 {
933+
let v: Wrapping<$ty> = rng.sample(my_uniform);
934+
assert!(low <= v && v <= high);
935+
}
936+
937+
for _ in 0..1000 {
938+
let v: Wrapping<$ty> =
939+
Uniform::sample_single(low, high, &mut rng);
940+
assert!(low <= v && v < high);
941+
}
942+
}
943+
944+
// Switch the bounds to test wrapping around
945+
for &(low, high) in v.iter() {
946+
let my_uniform = Uniform::new(high, low);
947+
for _ in 0..1000 {
948+
let v: Wrapping<$ty> = rng.sample(my_uniform);
949+
assert!(v >= high || v < low);
950+
}
951+
952+
let my_uniform = Uniform::new_inclusive(high, low);
953+
for _ in 0..1000 {
954+
let v: Wrapping<$ty> = rng.sample(my_uniform);
955+
assert!(v >= high || v <= low);
956+
}
957+
958+
for _ in 0..1000 {
959+
let v: Wrapping<$ty> =
960+
Uniform::sample_single(high, low, &mut rng);
961+
assert!(v >= high || v < low);
962+
}
963+
}
964+
)*
965+
}}
966+
}
967+
t!(i8, i16, i32, i64, isize,
968+
u8, u16, u32, u64, usize);
969+
#[cfg(feature = "i128_support")]
970+
t!(i128, u128)
971+
}
972+
779973
#[test]
780974
fn test_floats() {
781975
let mut rng = ::test::rng(252);

0 commit comments

Comments
 (0)