random/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3#[macro_use]
4extern crate log;
5
6
7#[cfg(feature = "bag")]
8#[cfg_attr(docsrs, doc(cfg(feature = "bag")))]
9pub mod weighted_bag;
10
11#[cfg(feature = "bag")]
12#[cfg_attr(docsrs, doc(cfg(feature = "bag")))]
13pub use weighted_bag::WeightedBag;
14
15struct Storage {
16    seed: u64,
17    rng: rand::rngs::SmallRng,
18}
19
20std::thread_local! {
21    static STORAGE: std::cell::RefCell<Storage> = std::cell::RefCell::new({
22    use {
23        rand::{rngs::SmallRng, Rng, SeedableRng as _},
24    };
25
26    // This is ugly, but i need the seed
27    let seed = SmallRng::from_entropy().gen::<u64>();
28
29    trace!("Initializing with seed: {seed}");
30
31    Storage {
32        seed,
33        // This is the fastest way to make multithreading i found
34        rng: SmallRng::seed_from_u64(seed),
35    }
36
37
38    })
39}
40
41/// Sets the seed for the future queries
42/// This is mostly usefull to make deterministic tests for games, or even bug hunts
43pub fn set_seed(seed: u64) {
44    use rand::{rngs::SmallRng, SeedableRng};
45    STORAGE.with_borrow_mut(|storage| {
46        storage.seed = seed;
47
48        storage.rng = SmallRng::seed_from_u64(seed);
49    });
50}
51
52/// Retrieves the seed
53pub fn seed() -> u64 {
54    STORAGE.with_borrow(|storage| storage.seed)
55}
56
57/// Samples a number from the range x..y
58///
59/// x has to be smaller than or equal to y
60pub fn get<T>(x: T, y: T) -> T
61where
62    T: rand::distributions::uniform::SampleUniform
63        + std::cmp::PartialEq
64        + std::cmp::PartialOrd
65        + std::fmt::Debug,
66{
67    use rand::Rng as _;
68
69    if x == y {
70        // warn!("Can't sample empty range: {x:?} !");
71        return x;
72    };
73
74    STORAGE.with_borrow_mut(|storage| storage.rng.gen_range(x..y))
75}
76
77/// Samples a number from the range x..=y
78///
79/// x has to be smaller than or equal to y
80pub fn get_inc<T>(x: T, y: T) -> T
81where
82    // R: rand::distributions::uniform::SampleRange<T> + std::fmt::Debug,
83    T: rand::distributions::uniform::SampleUniform
84        + std::cmp::PartialEq
85        + std::fmt::Debug
86        + std::cmp::PartialOrd,
87{
88    use rand::Rng as _;
89
90    if x == y {
91        // warn!("Can't sample empty range: {x:?} !");
92        return x;
93    };
94
95    STORAGE.with_borrow_mut(|storage| storage.rng.gen_range(x..=y))
96}
97
98/// Returns true 50% of the time
99///
100/// Technically not realistic as it cannot land on it's side :)
101pub fn conflip() -> bool {
102    use rand::Rng as _;
103
104    STORAGE.with_borrow_mut(|storage| storage.rng.gen_bool(0.5))
105}
106
107/// Samples a random String with a given lengh
108pub fn str(len: usize) -> String {
109    use rand::distributions::{Alphanumeric, DistString};
110
111    STORAGE.with_borrow_mut(|storage| Alphanumeric.sample_string(&mut storage.rng, len))
112}
113
114/// Select a random element from the given slice
115///
116/// Panics if the input slice is empty
117pub fn pick<T: std::fmt::Debug>(input: &[T]) -> &T {
118    use rand::seq::SliceRandom;
119
120    if input.is_empty() {
121        panic!("Can't sample empty slice ")
122    }
123
124    STORAGE.with_borrow_mut(|storage| input.choose(&mut storage.rng).unwrap())
125}