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]
133 fn pick_true_first_returns_first_value() {
134 let node = Pick::new(2, polydat::ast::PortType::U64);
135 let v = run(
136 &node,
137 vec![
138 Value::Bool(true),
139 Value::Bool(false),
140 Value::Str("a".into()),
141 Value::Str("b".into()),
142 ],
143 );
144 assert_eq!(v.as_str(), "a");
145 }
146
147 #[test]
148 fn pick_true_second_returns_second_value() {
149 let node = Pick::new(2, polydat::ast::PortType::U64);
150 let v = run(
151 &node,
152 vec![
153 Value::Bool(false),
154 Value::Bool(true),
155 Value::Str("a".into()),
156 Value::Str("b".into()),
157 ],
158 );
159 assert_eq!(v.as_str(), "b");
160 }
161
162 #[test]
163 #[should_panic(expected = "pick: no selector matched")]
164 fn pick_zero_selectors_panics() {
165 let node = Pick::new(2, polydat::ast::PortType::U64);
166 run(
167 &node,
168 vec![
169 Value::Bool(false),
170 Value::Bool(false),
171 Value::Str("a".into()),
172 Value::Str("b".into()),
173 ],
174 );
175 }
176
177 #[test]
178 #[should_panic(expected = "pick: multiple selectors matched")]
179 fn pick_multiple_selectors_panics() {
180 let node = Pick::new(2, polydat::ast::PortType::U64);
181 run(
182 &node,
183 vec![
184 Value::Bool(true),
185 Value::Bool(true),
186 Value::Str("a".into()),
187 Value::Str("b".into()),
188 ],
189 );
190 }
191
192 #[test]
193 #[should_panic(expected = "pick: value v1 has type")]
194 fn pick_mixed_value_types_panics_at_eval() {
195 let node = Pick::new(2, polydat::ast::PortType::U64);
196 run(
197 &node,
198 vec![
199 Value::Bool(true),
200 Value::Bool(false),
201 Value::U64(1),
202 Value::Str("b".into()),
203 ],
204 );
205 }
206
207 #[test]
208 fn pick_variadic_n_works_for_2_3_4() {
209 let node = Pick::new(3, polydat::ast::PortType::U64);
211 let v = run(
212 &node,
213 vec![
214 Value::Bool(false),
215 Value::Bool(false),
216 Value::Bool(true),
217 Value::Str("x".into()),
218 Value::Str("y".into()),
219 Value::Str("z".into()),
220 ],
221 );
222 assert_eq!(v.as_str(), "z");
223
224 let node = Pick::new(4, polydat::ast::PortType::U64);
226 let v = run(
227 &node,
228 vec![
229 Value::Bool(false),
230 Value::Bool(true),
231 Value::Bool(false),
232 Value::Bool(false),
233 Value::U64(10),
234 Value::U64(20),
235 Value::U64(30),
236 Value::U64(40),
237 ],
238 );
239 assert_eq!(v.as_u64(), 20);
240 }
241
242 #[test]
243 fn pick_meta_has_correct_slot_count() {
244 use polydat::ast::{PortType, Slot};
245 let node = Pick::new(3, polydat::ast::PortType::U64);
247 assert_eq!(node.meta().ins.len(), 6);
248 for i in 0..3 {
251 match &node.meta().ins[i] {
252 Slot::Wire(p) => assert_eq!(p.typ, PortType::Bool, "selector {i} should be Bool"),
253 _ => panic!("expected wire slot at {i}"),
254 }
255 }
256 }
257}