Skip to content

Commit e90e9eb

Browse files
committed
Merge changes from #69 (sample_single)
2 parents 9d236da + 4ebd21f commit e90e9eb

File tree

4 files changed

+64
-10
lines changed

4 files changed

+64
-10
lines changed

benches/distributions.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@ macro_rules! gen_range_int {
7575
#[bench]
7676
fn $fnn(b: &mut Bencher) {
7777
let mut rng = XorShiftRng::new().unwrap();
78+
let high = $high;
7879

7980
b.iter(|| {
8081
for _ in 0..::RAND_BENCH_N {
81-
let x: $ty = Range::new($low, $high).sample(&mut rng);
82+
let x: $ty = Range::sample_single($low, high, &mut rng);
8283
black_box(x);
84+
black_box(high);
8385
}
8486
});
8587
b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;

src/distributions/range.rs

+53-5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ impl Range<RangeInt<i32>> {
6565
assert!(low < high, "Range::new called with `low >= high`");
6666
Range { inner: RangeImpl::new_inclusive(low, high) }
6767
}
68+
69+
/// Sample a single value uniformly from `[low, high)`.
70+
/// Panics if `low >= high`.
71+
pub fn sample_single<X: SampleRange, R: Rng+?Sized>(low: X, high: X, rng: &mut R) -> X {
72+
assert!(low < high, "Range::sample_single called with low >= high");
73+
X::T::sample_single(low, high, rng)
74+
}
6875
}
6976

7077
impl<T: RangeImpl> Distribution<T::X> for Range<T> {
@@ -118,8 +125,8 @@ pub trait SampleRange: PartialOrd+Sized {
118125
/// let range = Range::new(low, high);
119126
/// let x = range.sample(&mut thread_rng());
120127
/// ```
121-
pub trait RangeImpl {
122-
/// The type sampled by this implementation (output type).
128+
pub trait RangeImpl: Sized {
129+
/// The type sampled by this implementation.
123130
type X: PartialOrd;
124131

125132
/// Construct self, with inclusive lower bound and exclusive upper bound
@@ -137,6 +144,16 @@ pub trait RangeImpl {
137144

138145
/// Sample a value.
139146
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> Self::X;
147+
148+
/// Sample a single value uniformly from a range with inclusive lower bound
149+
/// and exclusive upper bound `[low, high)`.
150+
/// Panics if `low >= high`.
151+
fn sample_single<R: Rng+?Sized>(low: Self::X, high: Self::X, rng: &mut R)
152+
-> Self::X
153+
{
154+
let range: Self = RangeImpl::new(low, high);
155+
range.sample(rng)
156+
}
140157
}
141158

142159
/// Implementation of `RangeImpl` for integer types.
@@ -246,9 +263,9 @@ macro_rules! range_int_impl {
246263
loop {
247264
let v: $u_large = Uniform.sample(rng);
248265
if $use_mult {
249-
let (high, low) = v.wmul(range);
250-
if low <= zone {
251-
return self.low.wrapping_add(high as $ty);
266+
let (hi, lo) = v.wmul(range);
267+
if lo <= zone {
268+
return self.low.wrapping_add(hi as $ty);
252269
}
253270
} else {
254271
if v <= zone {
@@ -261,6 +278,37 @@ macro_rules! range_int_impl {
261278
Uniform.sample(rng)
262279
}
263280
}
281+
282+
fn sample_single<R: Rng+?Sized>(low: Self::X, high: Self::X, rng: &mut R) -> Self::X {
283+
let range = (high as $u_large)
284+
.wrapping_sub(low as $u_large);
285+
let zone =
286+
if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned {
287+
// Using a modulus is faster than the approximation for
288+
// i8 and i16. I suppose we trade the cost of one
289+
// modulus for near-perfect branch prediction.
290+
let unsigned_max: $u_large = ::core::$u_large::MAX;
291+
let ints_to_reject = (unsigned_max - range + 1) % range;
292+
unsigned_max - ints_to_reject
293+
} else {
294+
// conservative but fast approximation
295+
range << range.leading_zeros()
296+
};
297+
298+
loop {
299+
let v: $u_large = Uniform.sample(rng);
300+
if $use_mult {
301+
let (hi, lo) = v.wmul(range);
302+
if lo <= zone {
303+
return low.wrapping_add(hi as $ty);
304+
}
305+
} else {
306+
if v <= zone {
307+
return low.wrapping_add((v % range) as $ty);
308+
}
309+
}
310+
}
311+
}
264312
}
265313
}
266314
}

src/distributions/uniform.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,17 @@ impl Distribution<char> for Codepoint {
218218

219219
impl Distribution<char> for AsciiWordChar {
220220
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> char {
221-
use sequences::Choose;
221+
const RANGE: u32 = 26 + 26 + 10;
222222
const GEN_ASCII_STR_CHARSET: &'static [u8] =
223223
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
224224
abcdefghijklmnopqrstuvwxyz\
225225
0123456789";
226-
*GEN_ASCII_STR_CHARSET.choose(rng).unwrap() as char
226+
loop {
227+
let var = rng.next_u32() & 0x3F;
228+
if var < RANGE {
229+
return GEN_ASCII_STR_CHARSET[var as usize] as char
230+
}
231+
}
227232
}
228233
}
229234

src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,7 @@ pub trait Sample: Rng {
426426
/// }
427427
/// ```
428428
fn gen_range<T: SampleRange>(&mut self, low: T, high: T) -> T {
429-
assert!(low < high, "Sample::gen_range called with low >= high");
430-
Range::new(low, high).sample(self)
429+
Range::sample_single(low, high, self)
431430
}
432431

433432
/// Construct an iterator on an `Rng`.

0 commit comments

Comments
 (0)