From eff66f8056b01a732df9523cb3a3d06b2d69c750 Mon Sep 17 00:00:00 2001 From: pommicket Date: Wed, 14 Dec 2022 16:16:57 -0500 Subject: nicer GenRandom --- gen_random/src/lib.rs | 207 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 175 insertions(+), 32 deletions(-) (limited to 'gen_random/src') diff --git a/gen_random/src/lib.rs b/gen_random/src/lib.rs index b9cdb4f..c21a1b1 100644 --- a/gen_random/src/lib.rs +++ b/gen_random/src/lib.rs @@ -4,11 +4,61 @@ use std::rc::Rc; use std::sync::Arc; use std::cell::{Cell, RefCell}; +/// Generate random structs and enums! +/// +/// You shouldn't implement this trait yourself — instead, use the `derive` macro: +/// ``` +/// use gen_random_proc_macro::GenRandom; +/// use gen_random::GenRandom; +/// +/// #[derive(GenRandom, Debug)] +/// enum MyType { +/// // this variant will be chosen 7 / 10.5 = 2/3 of the time +/// #[prob = 7] +/// Variant1(f64), +/// // this variant will be chosen 3.5 / 10.5 = 1/3 of the time +/// #[prob = 3.5] +/// Variant2 { +/// // bias & scale attributes can be used for fields of type f32/f64. +/// // this makes `a` range from 2 to 6 (as opposed to the default 0 to 1). +/// #[bias = 2.0] +/// #[scale = 4.0] +/// a: f64, +/// // we can even include a randomly-generated MyType inside this MyType! +/// // be careful when doing this or else you might try to generate an infinite struct! +/// b: Box +/// } +/// } +/// +/// fn main() { +/// let my_value = MyType::gen_thread_random(); +/// println!("{my_value:?}"); +/// } +/// ``` + pub trait GenRandom: Sized { - fn gen_random(rng: &mut impl Rng) -> Self; - fn gen_thread_random() -> Self { + /// To allow recursive structs like binary trees, + /// we provide a `max_depth` functionality. + /// If your struct isn't recursive, you can use [GenRandom::gen_random] instead. + /// If `max_depth <= 0` the **first** variant of an `enum` is always chosen + /// (so make sure `Empty` or whatever comes first). + /// For `Option`, if `max_depth <= 0`, `None` is always chosen. + fn gen_random_max_depth(rng: &mut impl Rng, max_depth: isize) -> Self; + + /// Generate a random instance of this struct using the given random number generator. + fn gen_random(rng: &mut impl Rng) -> Self { + Self::gen_random_max_depth(rng, isize::MAX) + } + + /// Generate a random instance of this struct using `rand::thread_rng()` with a maximum depth. + fn gen_thread_random_max_depth(max_depth: isize) -> Self { let mut thread_rng = rand::thread_rng(); - Self::gen_random(&mut thread_rng) + Self::gen_random_max_depth(&mut thread_rng, max_depth) + } + + /// Generate a random instance of this struct using `rand::thread_rng()`. + fn gen_thread_random() -> Self { + Self::gen_thread_random_max_depth(isize::MAX) } } @@ -21,96 +71,189 @@ pub fn gen_thread_random_vec(len: usize) -> Vec { } impl GenRandom for f32 { - fn gen_random(rng: &mut impl Rng) -> Self { + fn gen_random_max_depth(rng: &mut impl Rng, _depth: isize) -> Self { rng.gen_range(0.0..1.0) } } impl GenRandom for f64 { - fn gen_random(rng: &mut impl Rng) -> Self { + fn gen_random_max_depth(rng: &mut impl Rng, _depth: isize) -> Self { rng.gen_range(0.0..1.0) } } impl GenRandom for Box { - fn gen_random(rng: &mut impl Rng) -> Self { - Box::new(T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + Box::new(T::gen_random_max_depth(rng, depth)) } } impl GenRandom for [T; 1] { - fn gen_random(rng: &mut impl Rng) -> Self { - [T::gen_random(rng)] + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + [T::gen_random_max_depth(rng, depth)] } } impl GenRandom for [T; 2] { - fn gen_random(rng: &mut impl Rng) -> Self { - [T::gen_random(rng), T::gen_random(rng)] + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + [T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)] } } impl GenRandom for [T; 3] { - fn gen_random(rng: &mut impl Rng) -> Self { - [T::gen_random(rng), T::gen_random(rng), T::gen_random(rng)] + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + [T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)] } } impl GenRandom for [T; 4] { - fn gen_random(rng: &mut impl Rng) -> Self { - [T::gen_random(rng), T::gen_random(rng), T::gen_random(rng), T::gen_random(rng)] + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + [T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)] } } impl GenRandom for (T, T) { - fn gen_random(rng: &mut impl Rng) -> Self { - (T::gen_random(rng), T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + (T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)) } } impl GenRandom for (T, T, T) { - fn gen_random(rng: &mut impl Rng) -> Self { - (T::gen_random(rng), T::gen_random(rng), T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + (T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)) } } impl GenRandom for (T, T, T, T) { - fn gen_random(rng: &mut impl Rng) -> Self { - (T::gen_random(rng), T::gen_random(rng), T::gen_random(rng), T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + (T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)) } } impl GenRandom for Rc { - fn gen_random(rng: &mut impl Rng) -> Self { - Self::new(T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + Self::new(T::gen_random_max_depth(rng, depth)) } } impl GenRandom for Arc { - fn gen_random(rng: &mut impl Rng) -> Self { - Self::new(T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + Self::new(T::gen_random_max_depth(rng, depth)) } } impl GenRandom for Cell { - fn gen_random(rng: &mut impl Rng) -> Self { - Self::new(T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + Self::new(T::gen_random_max_depth(rng, depth)) } } impl GenRandom for RefCell { - fn gen_random(rng: &mut impl Rng) -> Self { - Self::new(T::gen_random(rng)) + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + Self::new(T::gen_random_max_depth(rng, depth)) } } impl GenRandom for Option { - fn gen_random(rng: &mut impl Rng) -> Self { - if rng.gen_range(0..2) == 0 { + fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { + if depth <= 0 { + None + } else if rng.gen_range(0..2) == 0 { None } else { - Some(T::gen_random(rng)) + Some(T::gen_random_max_depth(rng, depth)) } } } + +#[cfg(test)] +mod tests { + extern crate gen_random_proc_macro; + extern crate rand; + use super::{gen_thread_random_vec, GenRandom}; + use gen_random_proc_macro::GenRandom; + + #[derive(GenRandom, Debug)] + enum Test1 { + #[prob = 0.2] + A(f32), + #[prob = 0.8] + B(Option), + } + + #[derive(GenRandom, Debug)] + #[allow(dead_code)] + enum Test2 { + #[prob = 0.1] + Variant1, + #[prob = 0.7] + Variant2 { x: f32, y: f64, z: Test1 }, + #[prob = 0.2] + Variant3(f32, Box), + } + + #[derive(GenRandom, Debug)] + enum LinkedList { + #[prob = 10] + Empty, + #[prob = 90] + Cons(f32, Box), + } + + #[derive(GenRandom, Debug)] + enum BinaryTree { + #[prob = 1] + Empty, + #[prob = 99] + Node(f64, Box, Box) + } + + #[derive(GenRandom, Debug)] + struct ScaleBias { + #[bias = 1.0] + #[scale = 10.0] + a: f32, + #[bias = 2.0] + #[scale = 0.0] + b: f32, + } + + #[test] + fn basic() { + let tests1: Vec = gen_thread_random_vec(10); + println!("{tests1:?}"); + } + + #[test] + fn many_types_of_variants() { + let tests2: Vec = gen_thread_random_vec(10); + println!("{tests2:?}"); + } + + #[test] + fn linked_list() { + let ll = LinkedList::gen_thread_random(); + println!("{ll:?}"); + } + + #[test] + fn scale_bias() { + let sb: Vec = gen_thread_random_vec(10); + println!("{sb:?}"); + for x in sb.iter() { + if x.a < 1.0 || x.a > 11.0 { + panic!("a field should be between 1 and 11; got {}", x.a); + } + if x.b != 2.0 { + panic!("b field should be exactly 2; got {}", x.b); + } + } + } + + #[test] + fn binary_tree_max_depth() { + let bintree = BinaryTree::gen_thread_random_max_depth(5); + println!("{bintree:?}"); + } +} -- cgit v1.2.3