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}