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