random_utils/
lib.rs

1// Copyright 2016 by Szymon LipiƄski.
2// 
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7//! A couple of random number generation utilities.
8//!
9//! # Usage
10//!
11//! ```toml
12//! [dependencies]
13//! random_utils = "0.1"
14//! ```
15//!
16
17extern crate rand;
18extern crate num;
19
20use rand::{Rng};
21use std::ops::Add;
22use rand::distributions::range::SampleRange;
23use std::fmt::Display;
24
25use num::traits::{One};
26
27/// Returns random number from the range [low, high].
28///
29/// The numbers are uniformly distributed over [low, high].
30#[allow(dead_code)]
31pub fn random_range<T>(low: T, high: T) -> T
32    where T: PartialOrd + Add<Output=T> + SampleRange + Display + One
33{
34    
35    if low > high {
36        panic!("Low({}) is higher than high({}).", low, high);
37    }
38
39    let mut rng = rand::thread_rng();
40    let higher = high + T::one();
41    rng.gen_range::<T>(low, higher)
42}
43
44
45#[cfg(test)]
46mod tests {
47
48    use super::random_range;
49    use std::collections::HashMap;
50    use std::cmp::{max, min};
51
52    #[test]
53    #[should_panic(expected = "Low(10) is higher than high(5).")]
54    fn random_range_bad_arguments() {
55        random_range(10, 5);
56    }
57
58    #[test]
59    fn random_range_equal_arguments() {
60        for _ in 1..1000 {
61            assert_eq!(random_range(10, 10), 10);
62        }
63    }
64
65    #[test]
66    /// A very simple test for randomness.
67    ///
68    /// Runs the rng function huge number of times.
69    /// Checks the average (which should be (high+low)/2).
70    ///
71    /// Checks the number each generated number appeared,
72    /// which should be 5% lower/higher than the average.
73    ///
74    /// Checks if each of the number has been generated.
75    ///
76    fn random_range_arguments() {
77        let low = 1;
78        let high = 1_000;
79        let each_number_times = 10_000;
80        let rand_numbers_count = (high-low+1) * each_number_times;
81        let mut results: HashMap<i32, i32> = HashMap::new();
82
83        for _ in 1..rand_numbers_count {
84            let r = random_range(low, high);
85            let counter = results.entry(r).or_insert(1);
86            *counter += 1;
87        }
88
89        // calculate the average
90        let mut sum: i64 = 0;
91        let mut max_count = 0;
92        let mut min_count = rand_numbers_count * 10;
93        for (k ,v) in &results {
94            sum = sum + (*v as i64) * (*k as i64);
95            min_count = min(min_count, *v);
96            max_count = max(max_count, *v);
97        }
98        assert_eq!(results.len(), (high-low+1) as usize);
99        let average = sum as f32 / rand_numbers_count as f32;
100        let expected_average = (high + low) as f32 / 2.0;
101
102        assert!( expected_average <= average * 1.001);
103        assert!( expected_average >= average * 0.999);
104        
105        assert!( (min_count as f32) > each_number_times as f32 * 0.95);
106        assert!( (min_count as f32) < each_number_times as f32 * 1.05);
107    }
108
109}