diff --git a/src/distributions/float.rs b/src/distributions/float.rs index 757746c3687..a18f3dbf241 100644 --- a/src/distributions/float.rs +++ b/src/distributions/float.rs @@ -98,73 +98,58 @@ float_impls! { f32_rand_impls, f32, 23, next_f32 } #[cfg(test)] mod tests { - use {Rng, RngCore, impls}; + use Rng; + use mock::StepRng; use distributions::{Open01, Closed01}; const EPSILON32: f32 = ::core::f32::EPSILON; const EPSILON64: f64 = ::core::f64::EPSILON; - struct ConstantRng(u64); - impl RngCore for ConstantRng { - fn next_u32(&mut self) -> u32 { - let ConstantRng(v) = *self; - v as u32 - } - fn next_u64(&mut self) -> u64 { - let ConstantRng(v) = *self; - v - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) - } - } - #[test] fn floating_point_edge_cases() { - let mut zeros = ConstantRng(0); + let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.gen::(), 0.0); assert_eq!(zeros.gen::(), 0.0); - let mut one = ConstantRng(1); + let mut one = StepRng::new(1, 0); assert_eq!(one.gen::(), EPSILON32); assert_eq!(one.gen::(), EPSILON64); - let mut max = ConstantRng(!0); + let mut max = StepRng::new(!0, 0); assert_eq!(max.gen::(), 1.0 - EPSILON32); assert_eq!(max.gen::(), 1.0 - EPSILON64); } #[test] fn fp_closed_edge_cases() { - let mut zeros = ConstantRng(0); + let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.sample::(Closed01), 0.0); assert_eq!(zeros.sample::(Closed01), 0.0); - let mut one = ConstantRng(1); + let mut one = StepRng::new(1, 0); let one32 = one.sample::(Closed01); let one64 = one.sample::(Closed01); assert!(EPSILON32 < one32 && one32 < EPSILON32 * 1.01); assert!(EPSILON64 < one64 && one64 < EPSILON64 * 1.01); - let mut max = ConstantRng(!0); + let mut max = StepRng::new(!0, 0); assert_eq!(max.sample::(Closed01), 1.0); assert_eq!(max.sample::(Closed01), 1.0); } #[test] fn fp_open_edge_cases() { - let mut zeros = ConstantRng(0); + let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.sample::(Open01), 0.0 + EPSILON32 / 2.0); assert_eq!(zeros.sample::(Open01), 0.0 + EPSILON64 / 2.0); - let mut one = ConstantRng(1); + let mut one = StepRng::new(1, 0); let one32 = one.sample::(Open01); let one64 = one.sample::(Open01); assert!(EPSILON32 < one32 && one32 < EPSILON32 * 2.0); assert!(EPSILON64 < one64 && one64 < EPSILON64 * 2.0); - let mut max = ConstantRng(!0); + let mut max = StepRng::new(!0, 0); assert_eq!(max.sample::(Open01), 1.0 - EPSILON32 / 2.0); assert_eq!(max.sample::(Open01), 1.0 - EPSILON64 / 2.0); } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 92f44f15704..85c78e18481 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -386,26 +386,10 @@ fn ziggurat( #[cfg(test)] mod tests { - use {Rng, RngCore}; - use impls; + use Rng; + use mock::StepRng; use super::{WeightedChoice, Weighted, Distribution}; - // 0, 1, 2, 3, ... - struct CountingRng { i: u32 } - impl RngCore for CountingRng { - fn next_u32(&mut self) -> u32 { - self.i += 1; - self.i - 1 - } - fn next_u64(&mut self) -> u64 { - self.next_u32() as u64 - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u32(self, dest) - } - } - #[test] fn test_weighted_choice() { // this makes assumptions about the internal implementation of @@ -419,7 +403,7 @@ mod tests { let wc = WeightedChoice::new(&mut items); let expected = $expected; - let mut rng = CountingRng { i: 0 }; + let mut rng = StepRng::new(0, 1); for &val in expected.iter() { assert_eq!(wc.sample(&mut rng), val) diff --git a/src/lib.rs b/src/lib.rs index d7080ae4a63..caedb4d842b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -297,6 +297,7 @@ use distributions::range::SampleRange; pub mod distributions; mod impls; pub mod jitter; +pub mod mock; #[cfg(feature="std")] pub mod os; #[cfg(feature="std")] pub mod read; pub mod reseeding; @@ -1380,7 +1381,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { - use impls; + use mock::StepRng; #[cfg(feature="std")] use super::{random, thread_rng, EntropyRng}; use super::{RngCore, Rng, SeedableRng, StdRng}; @@ -1419,16 +1420,6 @@ mod test { TestRng { inner: StdRng::from_seed(seed) } } - struct ConstRng { i: u64 } - impl RngCore for ConstRng { - fn next_u32(&mut self) -> u32 { self.i as u32 } - fn next_u64(&mut self) -> u64 { self.i } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) - } - } - #[test] #[cfg(feature="std")] fn test_entropy() { @@ -1438,7 +1429,7 @@ mod test { #[test] fn test_fill_bytes_default() { - let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 }; + let mut r = StepRng::new(0x11_22_33_44_55_66_77_88, 0); // check every remainder mod 8, both in small and big vectors. let lengths = [0, 1, 2, 3, 4, 5, 6, 7, @@ -1460,7 +1451,7 @@ mod test { #[test] fn test_fill() { let x = 9041086907909331047; // a random u64 - let mut rng = ConstRng { i: x }; + let mut rng = StepRng::new(x, 0); // Convert to byte sequence and back to u64; byte-swap twice if BE. let mut array = [0u64; 2]; diff --git a/src/mock.rs b/src/mock.rs new file mode 100644 index 00000000000..05702d66164 --- /dev/null +++ b/src/mock.rs @@ -0,0 +1,61 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Mock random number generator + +use {RngCore, Error, impls}; + +/// A simple implementation of `RngCore` for testing purposes. +/// +/// This generates an arithmetic sequence (i.e. adds a constant each step) +/// over a `u64` number, using wrapping arithmetic. If the increment is 0 +/// the generator yields a constant. +/// +/// ```rust +/// use rand::Rng; +/// use rand::mock::StepRng; +/// +/// let mut my_rng = StepRng::new(2, 1); +/// let sample: [u64; 3] = my_rng.gen(); +/// assert_eq!(sample, [2, 3, 4]); +/// ``` +#[derive(Debug, Clone)] +pub struct StepRng { + v: u64, + a: u64, +} + +impl StepRng { + /// Create a `StepRng`, yielding an arithmetic sequence starting with + /// `initial` and incremented by `increment` each time. + pub fn new(initial: u64, increment: u64) -> Self { + StepRng { v: initial, a: increment } + } +} + +impl RngCore for StepRng { + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + + fn next_u64(&mut self) -> u64 { + let result = self.v; + self.v = self.v.wrapping_add(self.a); + result + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} diff --git a/src/reseeding.rs b/src/reseeding.rs index 2beb3e5bd6c..38a6cec826a 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -182,57 +182,24 @@ impl RngCore for ReseedingRng #[cfg(test)] mod test { - use {impls, le}; - use super::{ReseedingRng}; - use {SeedableRng, RngCore, Error}; - - struct Counter { - i: u32 - } - - impl RngCore for Counter { - fn next_u32(&mut self) -> u32 { - self.i += 1; - // very random - self.i - 1 - } - fn next_u64(&mut self) -> u64 { - impls::next_u64_via_u32(self) - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) - } - } - impl SeedableRng for Counter { - type Seed = [u8; 4]; - fn from_seed(seed: Self::Seed) -> Self { - let mut seed_u32 = [0u32; 1]; - le::read_u32_into(&seed, &mut seed_u32); - Counter { i: seed_u32[0] } - } - } - - #[derive(Debug, Clone)] - struct ResetCounter; - impl RngCore for ResetCounter { - fn next_u32(&mut self) -> u32 { unimplemented!() } - fn next_u64(&mut self) -> u64 { unimplemented!() } - fn fill_bytes(&mut self, _dest: &mut [u8]) { unimplemented!() } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - for i in dest.iter_mut() { *i = 0; } - Ok(()) - } - } + use {Rng, SeedableRng, StdRng}; + use mock::StepRng; + use super::ReseedingRng; #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ResetCounter); - - let mut i = 0; - for _ in 0..1000 { - assert_eq!(rs.next_u32(), i % 100); - i += 1; + let mut zero = StepRng::new(0, 0); + let rng = StdRng::from_rng(&mut zero).unwrap(); + let mut reseeding = ReseedingRng::new(rng, 32, zero); + + // Currently we only support for arrays up to length 32. + // TODO: cannot generate seq via Rng::gen because it uses different alg + let mut buf = [0u8; 32]; + reseeding.fill(&mut buf); + let seq = buf; + for _ in 0..10 { + reseeding.fill(&mut buf); + assert_eq!(buf, seq); } } }