1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use rand::prelude::*;

/// Provides the way to use randomized values in generic way.
pub trait Random {
    /// Produces integral random value, uniformly distributed on the closed interval [min, max]
    fn uniform_int(&self, min: i32, max: i32) -> i32 {
        if min == max {
            return min;
        }

        assert!(min < max);
        self.get_rng().gen_range(min, max + 1)
    }

    /// Produces real random value, uniformly distributed on the closed interval [min, max)
    fn uniform_real(&self, min: f64, max: f64) -> f64 {
        if (min - max).abs() < std::f64::EPSILON {
            return min;
        }

        assert!(min < max);
        self.get_rng().gen_range(min, max)
    }

    /// Flips a coin and returns true if it is "heads", false otherwise.
    fn is_head_not_tails(&self) -> bool {
        self.uniform_int(1, 2) == 1
    }

    /// Returns an index from collected with probability weight.
    /// Uses exponential distribution where the weights are the rate of the distribution (lambda)
    /// and selects the smallest sampled value.
    fn weighted(&self, weights: &[usize]) -> usize {
        weights
            .iter()
            .zip(0_usize..)
            .map(|(&weight, index)| (-self.uniform_real(0., 1.).ln() / weight as f64, index))
            .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
            .unwrap()
            .1
    }

    /// Returns RNG.
    fn get_rng(&self) -> StdRng;
}

/// A default random implementation.
pub struct DefaultRandom {
    seed: Option<u64>,
}

impl DefaultRandom {
    /// Creates a new instance `DefaultRandom` with seed.
    pub fn new_with_seed(seed: u64) -> Self {
        Self { seed: Some(seed) }
    }
}

impl Random for DefaultRandom {
    fn get_rng(&self) -> StdRng {
        if let Some(ref seed) = self.seed {
            StdRng::seed_from_u64(*seed)
        } else {
            StdRng::from_rng(thread_rng()).expect("cannot get RNG")
        }
    }
}

impl Default for DefaultRandom {
    fn default() -> Self {
        Self { seed: None }
    }
}