1#![doc(
2 html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
3 html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
4)]
5use anyhow::Result;
6use graphix_compiler::{
7 expr::ExprId, Apply, BuiltIn, Event, ExecCtx, Node, Rt, Scope, UserEvent,
8};
9use graphix_package_core::{deftype, CachedVals};
10use netidx::subscriber::Value;
11use netidx_value::ValArray;
12use rand::{rng, seq::SliceRandom, RngExt};
13use smallvec::{smallvec, SmallVec};
14
15#[derive(Debug)]
16struct Rand {
17 args: CachedVals,
18}
19
20impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Rand {
21 const NAME: &str = "rand";
22 deftype!("fn<'a: [Int, Float]>(?#start:'a, ?#end:'a, #clock:Any) -> 'a");
23
24 fn init<'a, 'b, 'c>(
25 _ctx: &'a mut ExecCtx<R, E>,
26 _typ: &'a graphix_compiler::typ::FnType,
27 _scope: &'b Scope,
28 from: &'c [Node<R, E>],
29 _top_id: ExprId,
30 ) -> Result<Box<dyn Apply<R, E>>> {
31 Ok(Box::new(Rand { args: CachedVals::new(from) }))
32 }
33}
34
35impl<R: Rt, E: UserEvent> Apply<R, E> for Rand {
36 fn update(
37 &mut self,
38 ctx: &mut ExecCtx<R, E>,
39 from: &mut [Node<R, E>],
40 event: &mut Event<E>,
41 ) -> Option<Value> {
42 macro_rules! gen_cases {
43 ($start:expr, $end:expr, $($typ:ident),+) => {
44 match ($start, $end) {
45 $(
46 (Value::$typ(start), Value::$typ(end)) if start < end => {
47 Some(Value::$typ(rng().random_range(*start..*end)))
48 }
49 ),+
50 _ => None
51 }
52 };
53 }
54 let up = self.args.update(ctx, from, event);
55 if up {
56 match &self.args.0[..] {
57 [Some(start), Some(end), Some(_)] => gen_cases!(
58 start, end, F32, F64, I32, I64, Z32, Z64, U32, U64, V32, V64
59 ),
60 _ => None,
61 }
62 } else {
63 None
64 }
65 }
66
67 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
68 self.args.clear()
69 }
70}
71
72#[derive(Debug)]
73struct Pick;
74
75impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Pick {
76 const NAME: &str = "rand_pick";
77 deftype!("fn(Array<'a>) -> 'a");
78
79 fn init<'a, 'b, 'c>(
80 _ctx: &'a mut ExecCtx<R, E>,
81 _typ: &'a graphix_compiler::typ::FnType,
82 _scope: &'b Scope,
83 _from: &'c [Node<R, E>],
84 _top_id: ExprId,
85 ) -> Result<Box<dyn Apply<R, E>>> {
86 Ok(Box::new(Pick))
87 }
88}
89
90impl<R: Rt, E: UserEvent> Apply<R, E> for Pick {
91 fn update(
92 &mut self,
93 ctx: &mut ExecCtx<R, E>,
94 from: &mut [Node<R, E>],
95 event: &mut Event<E>,
96 ) -> Option<Value> {
97 from[0].update(ctx, event).and_then(|a| match a {
98 Value::Array(a) if a.len() > 0 => {
99 Some(a[rng().random_range(0..a.len())].clone())
100 }
101 _ => None,
102 })
103 }
104
105 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
106}
107
108#[derive(Debug)]
109struct Shuffle(SmallVec<[Value; 32]>);
110
111impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Shuffle {
112 const NAME: &str = "rand_shuffle";
113 deftype!("fn(Array<'a>) -> Array<'a>");
114
115 fn init<'a, 'b, 'c>(
116 _ctx: &'a mut ExecCtx<R, E>,
117 _typ: &'a graphix_compiler::typ::FnType,
118 _scope: &'b Scope,
119 _from: &'c [Node<R, E>],
120 _top_id: ExprId,
121 ) -> Result<Box<dyn Apply<R, E>>> {
122 Ok(Box::new(Shuffle(smallvec![])))
123 }
124}
125
126impl<R: Rt, E: UserEvent> Apply<R, E> for Shuffle {
127 fn update(
128 &mut self,
129 ctx: &mut ExecCtx<R, E>,
130 from: &mut [Node<R, E>],
131 event: &mut Event<E>,
132 ) -> Option<Value> {
133 from[0].update(ctx, event).and_then(|a| match a {
134 Value::Array(a) => {
135 self.0.extend(a.iter().cloned());
136 self.0.shuffle(&mut rng());
137 Some(Value::Array(ValArray::from_iter_exact(self.0.drain(..))))
138 }
139 _ => None,
140 })
141 }
142
143 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
144 self.0.clear()
145 }
146}
147
148#[cfg(test)]
149mod test;
150
151graphix_derive::defpackage! {
152 builtins => [
153 Rand,
154 Pick,
155 Shuffle,
156 ],
157}