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

Small changes from experimental branch #273

Merged
merged 5 commits into from
Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/distributions/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ mod test {
let mut rng = ::test::rng(221);
for _ in 0..1000 {
assert!(exp.sample(&mut rng) >= 0.0);
assert!(exp.sample(&mut rng) >= 0.0);
}
}
#[test]
Expand Down
66 changes: 49 additions & 17 deletions src/distributions/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

//! Basic floating-point number distributions

use core::mem;
use Rng;
use distributions::{Distribution, Uniform};


/// A distribution to sample floating point numbers uniformly in the open
/// interval `(0, 1)` (not including either endpoint).
Expand Down Expand Up @@ -52,48 +56,76 @@ pub struct Open01;
pub struct Closed01;


// Return the next random f32 selected from the half-open
// interval `[0, 1)`.
//
// This uses a technique described by Saito and Matsumoto at
// MCQMC'08. Given that the IEEE floating point numbers are
// uniformly distributed over [1,2), we generate a number in
// this range and then offset it onto the range [0,1). Our
// choice of bits (masking v. shifting) is arbitrary and
// should be immaterial for high quality generators. For low
// quality generators (ex. LCG), prefer bitshifting due to
// correlation between sequential low order bits.
//
// See:
// A PRNG specialized in double precision floating point numbers using
// an affine transition
//
// * <http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf>
// * <http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf>
impl Distribution<f32> for Uniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f32 {
const UPPER_MASK: u32 = 0x3F800000;
const LOWER_MASK: u32 = 0x7FFFFF;
let tmp = UPPER_MASK | (rng.next_u32() & LOWER_MASK);
let result: f32 = unsafe { mem::transmute(tmp) };
result - 1.0
}
}
impl Distribution<f64> for Uniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
const UPPER_MASK: u64 = 0x3FF0000000000000;
const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF;
let tmp = UPPER_MASK | (rng.next_u64() & LOWER_MASK);
let result: f64 = unsafe { mem::transmute(tmp) };
result - 1.0
}
}

macro_rules! float_impls {
($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => {
($mod_name:ident, $ty:ty, $mantissa_bits:expr) => {
mod $mod_name {
use Rng;
use distributions::{Distribution, Uniform};
use distributions::{Distribution};
use super::{Open01, Closed01};

const SCALE: $ty = (1u64 << $mantissa_bits) as $ty;

impl Distribution<$ty> for Uniform {
/// Generate a floating point number in the half-open
/// interval `[0,1)`.
///
/// See `Closed01` for the closed interval `[0,1]`,
/// and `Open01` for the open interval `(0,1)`.
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
rng.$method_name()
}
}
impl Distribution<$ty> for Open01 {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
// add 0.5 * epsilon, so that smallest number is
// greater than 0, and largest number is still
// less than 1, specifically 1 - 0.5 * epsilon.
rng.$method_name() + 0.5 / SCALE
let x: $ty = rng.gen();
x + 0.5 / SCALE
}
}
impl Distribution<$ty> for Closed01 {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
// rescale so that 1.0 - epsilon becomes 1.0
// precisely.
rng.$method_name() * SCALE / (SCALE - 1.0)
let x: $ty = rng.gen();
x * SCALE / (SCALE - 1.0)
}
}
}
}
}
float_impls! { f64_rand_impls, f64, 52, next_f64 }
float_impls! { f32_rand_impls, f32, 23, next_f32 }
float_impls! { f64_rand_impls, f64, 52 }
float_impls! { f32_rand_impls, f32, 23 }


#[cfg(test)]
Expand Down
5 changes: 0 additions & 5 deletions src/distributions/gamma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ mod test {
let mut rng = ::test::rng(201);
for _ in 0..1000 {
chi.sample(&mut rng);
chi.sample(&mut rng);
}
}
#[test]
Expand All @@ -327,7 +326,6 @@ mod test {
let mut rng = ::test::rng(202);
for _ in 0..1000 {
chi.sample(&mut rng);
chi.sample(&mut rng);
}
}
#[test]
Expand All @@ -336,7 +334,6 @@ mod test {
let mut rng = ::test::rng(203);
for _ in 0..1000 {
chi.sample(&mut rng);
chi.sample(&mut rng);
}
}
#[test]
Expand All @@ -351,7 +348,6 @@ mod test {
let mut rng = ::test::rng(204);
for _ in 0..1000 {
f.sample(&mut rng);
f.sample(&mut rng);
}
}

Expand All @@ -361,7 +357,6 @@ mod test {
let mut rng = ::test::rng(205);
for _ in 0..1000 {
t.sample(&mut rng);
t.sample(&mut rng);
}
}
}
28 changes: 28 additions & 0 deletions src/distributions/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,31 @@ impl Distribution<u128> for Uniform {
((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128)
}
}


#[cfg(test)]
mod tests {
use Rng;
use distributions::{Uniform};

#[test]
fn test_integers() {
let mut rng = ::test::rng(806);

rng.sample::<isize, _>(Uniform);
rng.sample::<i8, _>(Uniform);
rng.sample::<i16, _>(Uniform);
rng.sample::<i32, _>(Uniform);
rng.sample::<i64, _>(Uniform);
#[cfg(feature = "i128_support")]
rng.sample::<i128, _>(Uniform);

rng.sample::<usize, _>(Uniform);
rng.sample::<u8, _>(Uniform);
rng.sample::<u16, _>(Uniform);
rng.sample::<u32, _>(Uniform);
rng.sample::<u64, _>(Uniform);
#[cfg(feature = "i128_support")]
rng.sample::<u128, _>(Uniform);
}
}
7 changes: 0 additions & 7 deletions src/distributions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,6 @@ fn ziggurat<R: Rng + ?Sized, P, Z>(
// creating a f64), so we might as well reuse some to save
// generating a whole extra random number. (Seems to be 15%
// faster.)
//
// This unfortunately misses out on the benefits of direct
// floating point generation if an RNG like dSMFT is
// used. (That is, such RNGs create floats directly, highly
// efficiently and overload next_f32/f64, so by not calling it
// this may be slower than it would be otherwise.)
// FIXME: investigate/optimise for the above.
let bits: u64 = rng.gen();
let i = (bits & 0xff) as usize;
let f = (bits >> 11) as f64 / SCALE;
Expand Down
2 changes: 0 additions & 2 deletions src/distributions/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ mod tests {
let mut rng = ::test::rng(210);
for _ in 0..1000 {
norm.sample(&mut rng);
norm.sample(&mut rng);
}
}
#[test]
Expand All @@ -183,7 +182,6 @@ mod tests {
let mut rng = ::test::rng(211);
for _ in 0..1000 {
lnorm.sample(&mut rng);
lnorm.sample(&mut rng);
}
}
#[test]
Expand Down
14 changes: 14 additions & 0 deletions src/distributions/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,17 @@ impl<T> Distribution<Option<T>> for Uniform where Uniform: Distribution<T> {
}
}
}


#[cfg(test)]
mod tests {
use {Rng, RngCore, Uniform};

#[test]
fn test_misc() {
let mut rng: &mut RngCore = &mut ::test::rng(820);

rng.sample::<char, _>(Uniform);
rng.sample::<bool, _>(Uniform);
}
}
141 changes: 141 additions & 0 deletions src/entropy_rng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Entropy generator, or wrapper around external generators

use {RngCore, Error, impls};
use {OsRng, JitterRng};

/// An RNG provided specifically for seeding PRNGs.
///
/// Where possible, `EntropyRng` retrieves random data from the operating
/// system's interface for random numbers ([`OsRng`]); if that fails it will
/// fall back to the [`JitterRng`] entropy collector. In the latter case it will
/// still try to use [`OsRng`] on the next usage.
///
/// This is either a little slow ([`OsRng`] requires a system call) or extremely
/// slow ([`JitterRng`] must use significant CPU time to generate sufficient
/// jitter). It is recommended to only use `EntropyRng` to seed a PRNG (as in
/// [`thread_rng`]) or to generate a small key.
///
/// [`OsRng`]: os/struct.OsRng.html
/// [`JitterRng`]: jitter/struct.JitterRng.html
/// [`thread_rng`]: fn.thread_rng.html
#[derive(Debug)]
pub struct EntropyRng {
rng: EntropySource,
}

#[derive(Debug)]
enum EntropySource {
Os(OsRng),
Jitter(JitterRng),
None,
}

impl EntropyRng {
/// Create a new `EntropyRng`.
///
/// This method will do no system calls or other initialization routines,
/// those are done on first use. This is done to make `new` infallible,
/// and `try_fill_bytes` the only place to report errors.
pub fn new() -> Self {
EntropyRng { rng: EntropySource::None }
}
}

impl RngCore for EntropyRng {
fn next_u32(&mut self) -> u32 {
impls::next_u32_via_fill(self)
}

fn next_u64(&mut self) -> u64 {
impls::next_u64_via_fill(self)
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
self.try_fill_bytes(dest).unwrap_or_else(|err|
panic!("all entropy sources failed; first error: {}", err))
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error>
{
let mut rng = OsRng::new()?;
rng.try_fill_bytes(dest)?;
Ok(rng)
}

fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error>
{
let mut rng = JitterRng::new()?;
rng.try_fill_bytes(dest)?;
Ok(rng)
}

let mut switch_rng = None;
match self.rng {
EntropySource::None => {
let os_rng_result = try_os_new(dest);
match os_rng_result {
Ok(os_rng) => {
debug!("EntropyRng: using OsRng");
switch_rng = Some(EntropySource::Os(os_rng));
}
Err(os_rng_error) => {
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
os_rng_error);
match try_jitter_new(dest) {
Ok(jitter_rng) => {
debug!("EntropyRng: using JitterRng");
switch_rng = Some(EntropySource::Jitter(jitter_rng));
}
Err(_jitter_error) => {
warn!("EntropyRng: JitterRng failed: {}",
_jitter_error);
return Err(os_rng_error);
}
}
}
}
}
EntropySource::Os(ref mut rng) => {
let os_rng_result = rng.try_fill_bytes(dest);
if let Err(os_rng_error) = os_rng_result {
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
os_rng_error);
match try_jitter_new(dest) {
Ok(jitter_rng) => {
debug!("EntropyRng: using JitterRng");
switch_rng = Some(EntropySource::Jitter(jitter_rng));
}
Err(_jitter_error) => {
warn!("EntropyRng: JitterRng failed: {}",
_jitter_error);
return Err(os_rng_error);
}
}
}
}
EntropySource::Jitter(ref mut rng) => {
if let Ok(os_rng) = try_os_new(dest) {
debug!("EntropyRng: using OsRng");
switch_rng = Some(EntropySource::Os(os_rng));
} else {
return rng.try_fill_bytes(dest); // use JitterRng
}
}
}
if let Some(rng) = switch_rng {
self.rng = rng;
}
Ok(())
}
}
4 changes: 3 additions & 1 deletion src/jitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Non-physical true random number generator based on timing jitter.

use {RngCore, Error, ErrorKind, impls};
use {RngCore, CryptoRng, Error, ErrorKind, impls};

use core::{fmt, mem, ptr};
#[cfg(feature="std")]
Expand Down Expand Up @@ -776,5 +776,7 @@ impl RngCore for JitterRng {
}
}

impl CryptoRng for JitterRng {}

// There are no tests included because (1) this is an "external" RNG, so output
// is not reproducible and (2) `test_timer` *will* fail on some platforms.
Loading