blob: e9299154b101bcbcd2737a00a2b3da8ce9572142 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
//! Generating numbers between two others.
// this is surprisingly complicated to be both generic & correct
use std::num::Wrapping as w;
use crate::Rng;
use crate::distributions::{Sample, IndependentSample};
/// Sample values uniformly between two bounds.
///
/// This gives a uniform distribution (assuming the RNG used to sample
/// it is itself uniform & the `SampleRange` implementation for the
/// given type is correct), even for edge cases like `low = 0u8`,
/// `high = 170u8`, for which a naive modulo operation would return
/// numbers less than 85 with double the probability to those greater
/// than 85.
///
/// Types should attempt to sample in `[low, high)`, i.e., not
/// including `high`, but this may be very difficult. All the
/// primitive integer types satisfy this property, and the float types
/// normally satisfy it, but rounding may mean `high` can occur.
///
/// # Example
///
/// ```rust
/// use sgx_rand::distributions::{IndependentSample, Range};
///
/// fn main() {
/// let between = Range::new(10, 10000);
/// let mut rng = sgx_rand::thread_rng();
/// let mut sum = 0;
/// for _ in 0..1000 {
/// sum += between.ind_sample(&mut rng);
/// }
/// println!("{}", sum);
/// }
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Range<X> {
low: X,
range: X,
accept_zone: X
}
impl<X: SampleRange + PartialOrd> Range<X> {
/// Create a new `Range` instance that samples uniformly from
/// `[low, high)`. Panics if `low >= high`.
pub fn new(low: X, high: X) -> Range<X> {
assert!(low < high, "Range::new called with `low >= high`");
SampleRange::construct_range(low, high)
}
}
impl<Sup: SampleRange> Sample<Sup> for Range<Sup> {
#[inline]
fn sample<R: Rng>(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) }
}
impl<Sup: SampleRange> IndependentSample<Sup> for Range<Sup> {
fn ind_sample<R: Rng>(&self, rng: &mut R) -> Sup {
SampleRange::sample_range(self, rng)
}
}
/// The helper trait for types that have a sensible way to sample
/// uniformly between two values. This should not be used directly,
/// and is only to facilitate `Range`.
pub trait SampleRange : Sized {
/// Construct the `Range` object that `sample_range`
/// requires. This should not ever be called directly, only via
/// `Range::new`, which will check that `low < high`, so this
/// function doesn't have to repeat the check.
fn construct_range(low: Self, high: Self) -> Range<Self>;
/// Sample a value from the given `Range` with the given `Rng` as
/// a source of randomness.
fn sample_range<R: Rng>(r: &Range<Self>, rng: &mut R) -> Self;
}
macro_rules! integer_impl {
($ty:ty, $unsigned:ident) => {
impl SampleRange for $ty {
// we play free and fast with unsigned vs signed here
// (when $ty is signed), but that's fine, since the
// contract of this macro is for $ty and $unsigned to be
// "bit-equal", so casting between them is a no-op & a
// bijection.
#[inline]
fn construct_range(low: $ty, high: $ty) -> Range<$ty> {
let range = (w(high as $unsigned) - w(low as $unsigned)).0;
let unsigned_max: $unsigned = ::std::$unsigned::MAX;
// this is the largest number that fits into $unsigned
// that `range` divides evenly, so, if we've sampled
// `n` uniformly from this region, then `n % range` is
// uniform in [0, range)
let zone = unsigned_max - unsigned_max % range;
Range {
low: low,
range: range as $ty,
accept_zone: zone as $ty
}
}
#[inline]
fn sample_range<R: Rng>(r: &Range<$ty>, rng: &mut R) -> $ty {
loop {
// rejection sample
let v = rng.gen::<$unsigned>();
// until we find something that fits into the
// region which r.range evenly divides (this will
// be uniformly distributed)
if v < r.accept_zone as $unsigned {
// and return it, with some adjustments
return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0;
}
}
}
}
}
}
integer_impl! { i8, u8 }
integer_impl! { i16, u16 }
integer_impl! { i32, u32 }
integer_impl! { i64, u64 }
integer_impl! { isize, usize }
integer_impl! { u8, u8 }
integer_impl! { u16, u16 }
integer_impl! { u32, u32 }
integer_impl! { u64, u64 }
integer_impl! { usize, usize }
macro_rules! float_impl {
($ty:ty) => {
impl SampleRange for $ty {
fn construct_range(low: $ty, high: $ty) -> Range<$ty> {
Range {
low: low,
range: high - low,
accept_zone: 0.0 // unused
}
}
fn sample_range<R: Rng>(r: &Range<$ty>, rng: &mut R) -> $ty {
r.low + r.range * rng.gen::<$ty>()
}
}
}
}
float_impl! { f32 }
float_impl! { f64 }