crazy_train/randomizer.rs
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
//! This module provides a Randomizer struct that manages random number generation
//! with support for seeded and non-seeded generation. It allows for generating
//! random numbers, booleans, strings, paths, and shuffling items.
//!
use crate::generator::{StringDef, StringDefBuilder};
use rand::{rngs::StdRng, seq::SliceRandom, Rng, RngCore, SeedableRng};
use std::{cell::RefCell, path::PathBuf};
/// Struct for managing random number generation, allowing seed control for reproducibility.
pub struct Randomizer {
pub rng: RefCell<Box<dyn RngCore + Send>>,
pub seed: u64,
}
/// Default implementation for [`Randomizer`], initializing RNG with a random seed.
impl Default for Randomizer {
fn default() -> Self {
let mut seed_rng = StdRng::from_entropy();
let seed = seed_rng.next_u64();
let rng = RefCell::new(Box::new(StdRng::seed_from_u64(seed)));
Self { rng, seed }
}
}
impl Randomizer {
/// Create a new [`Randomizer`] with a specified seed.
///
/// # Example:
///
/// ```rust
/// use crazy_train::Randomizer;
/// let randomizer = Randomizer::with_seed(42);
/// ```
#[must_use]
pub fn with_seed(seed: u64) -> Self {
let rng = RefCell::new(Box::new(StdRng::seed_from_u64(seed)));
Self { rng, seed }
}
/// Generate a random number between the specified minimum and maximum values (inclusive).
///
/// # Example:
///
/// ```rust
/// use crazy_train::Randomizer;
/// let randomizer = Randomizer::with_seed(42);
/// assert_eq!(randomizer.number_between(1,10), 7);
// assert_eq!(!randomizer.number_between(1,10), 2);
/// ```
pub fn number_between(&self, min: u32, max: u32) -> u32 {
let mut rng = self.rng.borrow_mut();
let random_number = rng.next_u32();
min + (random_number % (max - min + 1))
}
/// Generate a random boolean value (true or false).
///
/// # Example:
///
/// ```rust
/// use crazy_train::Randomizer;
/// let randomizer = Randomizer::with_seed(42);
/// assert!(randomizer.bool());
// assert!(!randomizer.bool());
/// ```
pub fn bool(&self) -> bool {
let mut rng = self.rng.borrow_mut();
let random_number = rng.next_u32();
random_number % 2 == 0
}
/// Create a [`StringDefBuilder`] based on a given [`StringDef`].
///
/// # Example:
///
/// ```rust
/// use crazy_train::{Randomizer, StringDef};
/// let string_def = StringDef::default();
/// let randomizer = Randomizer::with_seed(42);
/// assert_eq!(randomizer.string(string_def.clone()).to_string(), "noqkak");
/// assert_eq!(randomizer.string(string_def.clone()).include_capital_letters(true).to_string(), "TWdAyN");
/// assert_eq!(randomizer.string(string_def.clone()).include_unicode(true).to_string(), "kdnfa😩");
/// assert_eq!(randomizer.string(string_def.clone()).include_numbers(true).to_string(), "0684n0");
/// assert_eq!(randomizer.string(string_def.clone()).include_symbol(true).to_string(), "=wqf`g");
/// assert_eq!(randomizer.string(string_def.clone()).length(10).to_string(), "wgavmyyuzw");
/// ```
pub fn string(&self, def: StringDef) -> StringDefBuilder<'_> {
StringDefBuilder {
string_def: def,
rng: &self.rng,
}
}
/// Generate a random path of a specified length.
///
/// # Example:
///
/// ```rust
/// use crazy_train::Randomizer;
/// use std::path::PathBuf;
/// let randomizer = Randomizer::with_seed(42);
/// assert_eq!(randomizer.path(), PathBuf::from("gowqzkza"));
/// ```
pub fn path(&self) -> PathBuf {
let mut rng = self.rng.borrow_mut();
let path_length = rng.gen_range(5..=10);
let path_name: String = (0..path_length)
.map(|_| char::from(rng.gen_range(b'a'..=b'z')))
.collect();
PathBuf::from(path_name)
}
/// Shuffle a slice of items and return a new vector with the shuffled items.
///
/// # Example:
///
/// ```rust
/// use crazy_train::Randomizer;
/// let randomizer = Randomizer::with_seed(42);
/// let list = vec![1, 2, 3, 4, 5, 6];
/// assert_eq!(randomizer.shuffle(&list), vec![1, 5, 6, 3, 2, 4]);
/// ```
pub fn shuffle<T>(&self, items: &[T]) -> Vec<T>
where
T: Clone,
{
let mut rng = self.rng.borrow_mut();
let mut shuffled_items = items.to_vec();
shuffled_items.shuffle(&mut *rng);
shuffled_items
}
/// Pick a random selection of items from a given slice.
///
/// # Example:
///
/// ```rust
/// use crazy_train::Randomizer;
/// let randomizer = Randomizer::with_seed(42);
/// let list = vec![1, 2, 3, 4, 5, 6];
/// assert_eq!(randomizer.pick_random(&list), vec![2, 6]);
/// ```
pub fn pick_random<T>(&self, items: &[T]) -> Vec<T>
where
T: Clone,
{
let mut rng = self.rng.borrow_mut();
let count = rng.gen_range(1..=10);
(0..count)
.map(|_| {
let index = rng.gen_range(0..items.len());
items[index].clone()
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rand_number() {
let randomizer = Randomizer::with_seed(42);
assert_eq!(randomizer.number_between(1, 100), 27);
assert_eq!(randomizer.number_between(1, 100), 52);
assert_eq!(randomizer.number_between(1, 100), 98);
}
#[test]
fn rand_bool() {
let randomizer = Randomizer::with_seed(42);
assert!(randomizer.bool());
assert!(!randomizer.bool());
assert!(!randomizer.bool());
assert!(!randomizer.bool());
assert!(!randomizer.bool());
assert!(!randomizer.bool());
assert!(randomizer.bool());
}
#[test]
fn rand_string() {
let randomizer = Randomizer::with_seed(42);
assert_eq!(
randomizer.string(StringDef::default()).to_string(),
"noqkak".to_string()
);
assert_eq!(
randomizer.string(StringDef::default()).to_string(),
"twdayn".to_string()
);
assert_eq!(
randomizer
.string(StringDef {
include_symbol: true,
..Default::default()
})
.to_string(),
"kdnfa)".to_string()
);
assert_eq!(
randomizer
.string(StringDef {
length: 10,
..Default::default()
})
.to_string(),
"hdpnewfykq".to_string()
);
assert_eq!(
randomizer
.string(StringDef {
include_unicode: true,
..Default::default()
})
.to_string(),
"vjjp😓".to_string()
);
}
#[test]
fn rand_path() {
let randomizer = Randomizer::with_seed(42);
assert_eq!(randomizer.path(), PathBuf::from("gowqzkza"));
}
#[test]
fn shuffle() {
let randomizer = Randomizer::with_seed(42);
let list = vec![1, 2, 3, 4, 5, 6];
assert_eq!(randomizer.shuffle(&list), vec![1, 5, 6, 3, 2, 4]);
}
#[test]
fn pick_random() {
let randomizer = Randomizer::with_seed(42);
let list = vec![1, 2, 3, 4, 5, 6];
assert_eq!(randomizer.pick_random(&list), vec![2, 6]);
assert_eq!(randomizer.pick_random(&list), vec![3, 1, 3, 5, 6, 1, 6]);
}
}