1use polydat::ast::Value;
19
20const PICK_HINT: &str = "\n hint: did the probe phase that sets these booleans run before \
23this phase? Check scenario-tree DFS order or declare a `detect_*` \
24phase ahead of consumers.";
25
26#[polydat::polydat_node(category = Comparison, variadic_min = 1)]
34fn pick(selectors: &[bool], values: &[Value]) -> Value {
35 let n = selectors.len();
36 debug_assert_eq!(
37 n,
38 values.len(),
39 "pick arity mismatch at eval: selectors={} values={}",
40 n,
41 values.len()
42 );
43
44 if polydat::library::debug_nodes_enabled() {
45 let sels: Vec<String> = selectors
46 .iter()
47 .enumerate()
48 .map(|(i, s)| format!("b{i}={s}"))
49 .collect();
50 let vals: Vec<String> = values
51 .iter()
52 .enumerate()
53 .map(|(i, v)| format!("v{i}={}", v.to_display_string()))
54 .collect();
55 polydat::library::support::audit::debug(&format!(
56 "pick: selectors=[{}] values=[{}]",
57 sels.join(", "),
58 vals.join(", "),
59 ));
60 }
61
62 let mut matched: Vec<usize> = Vec::new();
63 for (i, &sel) in selectors.iter().enumerate() {
64 if sel {
65 matched.push(i);
66 }
67 }
68
69 if matched.is_empty() {
70 panic!(
71 "pick: no selector matched (all N={n} booleans false); \
72 workload author guarantees one of {{b0, …, bN-1}} is \
73 true at this point{PICK_HINT}"
74 );
75 }
76 if matched.len() > 1 {
77 let positions: Vec<String> = matched.iter().map(|i| format!("b{i}")).collect();
78 panic!(
79 "pick: multiple selectors matched (positions {}); \
80 selectors must be mutually exclusive{PICK_HINT}",
81 positions.join(", ")
82 );
83 }
84
85 let first_pt = values[0].port_type();
87 for (i, v) in values.iter().enumerate().skip(1) {
88 let vpt = v.port_type();
89 if vpt != first_pt {
90 panic!(
91 "pick: value v{i} has type {vpt:?} but v0 has type {first_pt:?}; \
92 all value inputs must share a common type{PICK_HINT}"
93 );
94 }
95 }
96
97 values[matched[0]].clone()
98}
99
100#[cfg(test)]
112mod tests {
113 use super::*;
114 use polydat::ast::PolydatNode;
115
116 fn run(node: &Pick, inputs: Vec<Value>) -> Value {
117 let mut out = [Value::None];
118 node.eval(&inputs, &mut out);
119 out.into_iter().next().unwrap()
120 }
121
122 #[test]
129 fn pick_true_first_returns_first_value() {
130 let node = Pick::new(2);
131 let v = run(
132 &node,
133 vec![
134 Value::Bool(true),
135 Value::Bool(false),
136 Value::Str("a".into()),
137 Value::Str("b".into()),
138 ],
139 );
140 assert_eq!(v.as_str(), "a");
141 }
142
143 #[test]
144 fn pick_true_second_returns_second_value() {
145 let node = Pick::new(2);
146 let v = run(
147 &node,
148 vec![
149 Value::Bool(false),
150 Value::Bool(true),
151 Value::Str("a".into()),
152 Value::Str("b".into()),
153 ],
154 );
155 assert_eq!(v.as_str(), "b");
156 }
157
158 #[test]
159 #[should_panic(expected = "pick: no selector matched")]
160 fn pick_zero_selectors_panics() {
161 let node = Pick::new(2);
162 run(
163 &node,
164 vec![
165 Value::Bool(false),
166 Value::Bool(false),
167 Value::Str("a".into()),
168 Value::Str("b".into()),
169 ],
170 );
171 }
172
173 #[test]
174 #[should_panic(expected = "pick: multiple selectors matched")]
175 fn pick_multiple_selectors_panics() {
176 let node = Pick::new(2);
177 run(
178 &node,
179 vec![
180 Value::Bool(true),
181 Value::Bool(true),
182 Value::Str("a".into()),
183 Value::Str("b".into()),
184 ],
185 );
186 }
187
188 #[test]
189 #[should_panic(expected = "pick: value v1 has type")]
190 fn pick_mixed_value_types_panics_at_eval() {
191 let node = Pick::new(2);
192 run(
193 &node,
194 vec![
195 Value::Bool(true),
196 Value::Bool(false),
197 Value::U64(1),
198 Value::Str("b".into()),
199 ],
200 );
201 }
202
203 #[test]
204 fn pick_variadic_n_works_for_2_3_4() {
205 let node = Pick::new(3);
207 let v = run(
208 &node,
209 vec![
210 Value::Bool(false),
211 Value::Bool(false),
212 Value::Bool(true),
213 Value::Str("x".into()),
214 Value::Str("y".into()),
215 Value::Str("z".into()),
216 ],
217 );
218 assert_eq!(v.as_str(), "z");
219
220 let node = Pick::new(4);
222 let v = run(
223 &node,
224 vec![
225 Value::Bool(false),
226 Value::Bool(true),
227 Value::Bool(false),
228 Value::Bool(false),
229 Value::U64(10),
230 Value::U64(20),
231 Value::U64(30),
232 Value::U64(40),
233 ],
234 );
235 assert_eq!(v.as_u64(), 20);
236 }
237
238 #[test]
239 fn pick_meta_has_correct_slot_count() {
240 use polydat::ast::{PortType, Slot};
241 let node = Pick::new(3);
243 assert_eq!(node.meta().ins.len(), 6);
244 for i in 0..3 {
247 match &node.meta().ins[i] {
248 Slot::Wire(p) => assert_eq!(p.typ, PortType::Bool, "selector {i} should be Bool"),
249 _ => panic!("expected wire slot at {i}"),
250 }
251 }
252 }
253}