netidx_bscript/stdfn/
rand.rs

1use crate::{
2    deftype, expr::Expr, stdfn::CachedVals, Apply, BuiltIn, BuiltInInitFn, Ctx, Event,
3    ExecCtx, Node, UserEvent,
4};
5use netidx::subscriber::Value;
6use netidx_netproto::valarray::ValArray;
7use rand::{rng, seq::SliceRandom, Rng};
8use smallvec::{smallvec, SmallVec};
9use std::sync::Arc;
10
11#[derive(Debug)]
12struct Rand {
13    args: CachedVals,
14}
15
16impl<C: Ctx, E: UserEvent> BuiltIn<C, E> for Rand {
17    const NAME: &str = "rand";
18    deftype!("rand", "fn<'a: [Int, Float]>(?#start:'a, ?#end:'a, #trigger:Any) -> 'a");
19
20    fn init(_: &mut ExecCtx<C, E>) -> BuiltInInitFn<C, E> {
21        Arc::new(|_, _, _, from, _| Ok(Box::new(Rand { args: CachedVals::new(from) })))
22    }
23}
24
25impl<C: Ctx, E: UserEvent> Apply<C, E> for Rand {
26    fn update(
27        &mut self,
28        ctx: &mut ExecCtx<C, E>,
29        from: &mut [Node<C, E>],
30        event: &mut Event<E>,
31    ) -> Option<Value> {
32        macro_rules! gen_cases {
33            ($start:expr, $end:expr, $($typ:ident),+) => {
34                match ($start, $end) {
35                    $(
36                        (Value::$typ(start), Value::$typ(end)) if start < end => {
37                            Some(Value::$typ(rng().random_range(*start..*end)))
38                        }
39                    ),+
40                    _ => None
41                }
42            };
43        }
44        let up = self.args.update(ctx, from, event);
45        if up {
46            match &self.args.0[..] {
47                [Some(start), Some(end), Some(_)] => gen_cases!(
48                    start, end, F32, F64, I32, I64, Z32, Z64, U32, U64, V32, V64
49                ),
50                _ => None,
51            }
52        } else {
53            None
54        }
55    }
56}
57
58#[derive(Debug)]
59struct Pick;
60
61impl<C: Ctx, E: UserEvent> BuiltIn<C, E> for Pick {
62    const NAME: &str = "rand_pick";
63    deftype!("rand", "fn(Array<'a>) -> 'a");
64
65    fn init(_: &mut ExecCtx<C, E>) -> BuiltInInitFn<C, E> {
66        Arc::new(|_, _, _, _, _| Ok(Box::new(Pick)))
67    }
68}
69
70impl<C: Ctx, E: UserEvent> Apply<C, E> for Pick {
71    fn update(
72        &mut self,
73        ctx: &mut ExecCtx<C, E>,
74        from: &mut [Node<C, E>],
75        event: &mut Event<E>,
76    ) -> Option<Value> {
77        from[0].update(ctx, event).and_then(|a| match a {
78            Value::Array(a) if a.len() > 0 => {
79                Some(a[rng().random_range(0..a.len())].clone())
80            }
81            _ => None,
82        })
83    }
84}
85
86#[derive(Debug)]
87struct Shuffle(SmallVec<[Value; 32]>);
88
89impl<C: Ctx, E: UserEvent> BuiltIn<C, E> for Shuffle {
90    const NAME: &str = "rand_shuffle";
91    deftype!("rand", "fn(Array<'a>) -> Array<'a>");
92
93    fn init(_: &mut ExecCtx<C, E>) -> BuiltInInitFn<C, E> {
94        Arc::new(|_, _, _, _, _| Ok(Box::new(Shuffle(smallvec![]))))
95    }
96}
97
98impl<C: Ctx, E: UserEvent> Apply<C, E> for Shuffle {
99    fn update(
100        &mut self,
101        ctx: &mut ExecCtx<C, E>,
102        from: &mut [Node<C, E>],
103        event: &mut Event<E>,
104    ) -> Option<Value> {
105        from[0].update(ctx, event).and_then(|a| match a {
106            Value::Array(a) => {
107                self.0.extend(a.iter().cloned());
108                self.0.shuffle(&mut rng());
109                Some(Value::Array(ValArray::from_iter_exact(self.0.drain(..))))
110            }
111            _ => None,
112        })
113    }
114}
115
116const MOD: &str = r#"
117pub mod rand {
118    /// generate a random number between #start and #end (exclusive)
119    /// every time #trigger updates. If start and end are not specified,
120    /// they default to 0.0 and 1.0
121    pub let rand = |#start = 0.0, #end = 1.0, #trigger| 'rand;
122
123    /// pick a random element from the array and return it. Update
124    /// each time the array updates. If the array is empty return
125    /// nothing.
126    pub let pick = |a| 'rand_pick;
127
128    /// return a shuffled copy of a
129    pub let shuffle = |a| 'rand_shuffle
130}
131"#;
132
133pub fn register<C: Ctx, E: UserEvent>(ctx: &mut ExecCtx<C, E>) -> Expr {
134    ctx.register_builtin::<Rand>().unwrap();
135    ctx.register_builtin::<Pick>().unwrap();
136    ctx.register_builtin::<Shuffle>().unwrap();
137    MOD.parse().unwrap()
138}