1use std::collections::{BTreeSet, HashMap};
4
5use sva_formula::{Held, NodeId, Var};
6
7use crate::cast::Cast;
8use crate::error::EngineError;
9use crate::instantiate::Instances;
10use crate::query::Ask;
11use crate::typing::{Typing, Value, When};
12
13pub struct Order<'i> {
15 pub groups: Vec<Vec<String>>,
16 inst: &'i Instances,
17}
18
19impl<'i> Order<'i> {
20 pub fn deps(&self, path: &str) -> &'i [String] {
21 self.inst.deps(path)
22 }
23
24 pub fn is_loop(&self, group: &[String]) -> bool {
26 is_loop(self.inst, group)
27 }
28}
29
30pub(crate) fn is_loop(inst: &Instances, group: &[String]) -> bool {
31 match group {
32 [only] => inst.deps(only).iter().any(|d| d == only),
33 _ => true,
34 }
35}
36
37struct Frame {
38 node: String,
39 refs: Vec<String>,
40 idx: usize,
41}
42
43pub fn schedule_from<'i>(inst: &'i Instances, roots: &[String]) -> Result<Order<'i>, EngineError> {
45 let mut walk = Walk {
46 inst,
47 within: &|_| true,
48 index: HashMap::new(),
49 low: HashMap::new(),
50 open: Vec::new(),
51 next: 0,
52 groups: Vec::new(),
53 };
54 for root in roots {
55 if !inst.holds(root) {
56 return Err(EngineError::UnknownNode(root.to_string()));
57 }
58 walk.from(root);
59 }
60 Ok(Order {
61 groups: walk.groups,
62 inst,
63 })
64}
65
66pub(crate) fn grouped(
68 inst: &Instances,
69 starts: &[String],
70 region: &dyn Fn(&str) -> bool,
71) -> Vec<Vec<String>> {
72 let mut walk = Walk {
73 inst,
74 within: region,
75 index: HashMap::new(),
76 low: HashMap::new(),
77 open: Vec::new(),
78 next: 0,
79 groups: Vec::new(),
80 };
81 for start in starts.iter().filter(|s| region(s)) {
82 walk.from(start);
83 }
84 walk.groups
85}
86
87struct Walk<'a> {
88 inst: &'a Instances,
89 within: &'a dyn Fn(&str) -> bool,
90 index: HashMap<String, usize>,
91 low: HashMap<String, usize>,
92 open: Vec<String>,
93 next: usize,
94 groups: Vec<Vec<String>>,
95}
96
97impl Walk<'_> {
98 fn reads(&self, node: &str) -> Vec<String> {
99 let reads = self.inst.deps(node).iter();
100 reads.filter(|read| (self.within)(read)).cloned().collect()
101 }
102
103 fn from(&mut self, root: &str) {
104 if self.index.contains_key(root) {
105 return;
106 }
107 self.index.insert(root.to_string(), self.next);
108 self.low.insert(root.to_string(), self.next);
109 self.next += 1;
110 self.open.push(root.to_string());
111 let seed = self.reads(root);
112 let mut stack = vec![Frame {
113 node: root.to_string(),
114 refs: seed,
115 idx: 0,
116 }];
117
118 while let Some(frame) = stack.last_mut() {
119 if frame.idx < frame.refs.len() {
120 let target = frame.refs[frame.idx].clone();
121 let node = frame.node.clone();
122 frame.idx += 1;
123 match self.index.get(&target).copied() {
124 None => {
125 self.index.insert(target.clone(), self.next);
126 self.low.insert(target.clone(), self.next);
127 self.next += 1;
128 self.open.push(target.clone());
129 let refs = self.reads(&target);
130 stack.push(Frame {
131 node: target,
132 refs,
133 idx: 0,
134 });
135 }
136 Some(at) if self.open.contains(&target) => {
137 let mine = self.low[&node];
138 self.low.insert(node, mine.min(at));
139 }
140 Some(_) => {}
141 }
142 continue;
143 }
144
145 let node = frame.node.clone();
146 let mine = self.low[&node];
147 stack.pop();
148 if let Some(parent) = stack.last() {
149 let above = self.low[&parent.node];
150 self.low.insert(parent.node.clone(), above.min(mine));
151 }
152 if mine == self.index[&node] {
153 let at = self
154 .open
155 .iter()
156 .rposition(|n| *n == node)
157 .expect("a root of its group is still open");
158 let mut group = self.open.split_off(at);
159 group.sort();
160 self.groups.push(group);
161 }
162 }
163 }
164}
165
166#[derive(Clone, Debug, Default, PartialEq)]
168pub struct Schedule {
169 pub wanted: Vec<NodeId>,
171 pub compose: Vec<NodeId>,
173 pub tallied: Vec<NodeId>,
176}
177
178pub fn plan(typing: &Typing, root: NodeId, asks: &[Ask]) -> Schedule {
180 let mut wanted: BTreeSet<NodeId> = BTreeSet::new();
181 let mut rows: BTreeSet<NodeId> = BTreeSet::new();
182 let mut compose: Vec<NodeId> = Vec::new();
183 let audio = asks.is_empty();
184 for ask in asks {
185 let Some(id) = typing.id(&ask.node) else {
186 continue;
187 };
188 if ask.representation.reading().reads == crate::query::Reads::Structure {
189 continue;
190 }
191 match ask.representation.consumes(typing.ty(id).is_closed_form()) {
192 sva_samples::Consumes::ClosedForm if !compose.contains(&id) => compose.push(id),
193 sva_samples::Consumes::ClosedForm => {}
194 _ => {
195 wanted.insert(id);
196 }
197 }
198 if let crate::query::Representation::Ledger { depth } = ask.representation {
199 attributed(typing, id, depth, &mut rows);
200 }
201 }
202 if audio || !wanted.is_empty() || !rows.is_empty() {
203 wanted.insert(root);
204 }
205 compose.retain(|id| !wanted.contains(id) && !rows.contains(id));
206 Schedule {
207 tallied: rows.difference(&wanted).copied().collect(),
208 wanted: wanted.into_iter().collect(),
209 compose,
210 }
211}
212
213fn attributed(typing: &Typing, id: NodeId, depth: usize, rows: &mut BTreeSet<NodeId>) {
215 let mut seen = BTreeSet::from([id]);
217 let mut level = vec![id];
218 for _ in 0..depth {
219 let mut next = Vec::new();
220 while let Some(held) = level.pop() {
221 for operand in read_operands(typing, held) {
222 if !seen.insert(operand) {
223 continue;
224 }
225 rows.insert(operand);
226 match typing.name(operand) == typing.name(held) {
227 true => level.push(operand),
228 false => next.push(operand),
229 }
230 }
231 }
232 level = next;
233 }
234}
235
236pub(crate) fn holds_self(typing: &Typing, id: NodeId, seen: &mut BTreeSet<NodeId>) -> bool {
239 if !seen.insert(id) {
240 return false;
241 }
242 match typing.value(id) {
243 Value::SelfAt { .. } => true,
244 Value::Cast(Cast::Sample, _) | Value::Read { .. } => false,
245 Value::Cast(_, source) => holds_self(typing, *source, seen),
246 Value::Op { args, .. } => args.iter().any(|a| holds_self(typing, *a, seen)),
247 Value::Filter {
248 x, cutoff, q, gain, ..
249 } => [x, cutoff, q, gain]
250 .into_iter()
251 .any(|operand| holds_self(typing, *operand, seen)),
252 Value::Solver { varying, .. } => varying.iter().any(|(_, a)| holds_self(typing, *a, seen)),
253 Value::ClosedForm(_) | Value::Noise(_) => false,
254 }
255}
256
257pub(crate) fn materialized_operands(typing: &Typing, id: NodeId) -> Vec<NodeId> {
260 let sampled = |set: Vec<NodeId>| -> Vec<NodeId> {
261 let mut out = Vec::new();
262 for op in set {
263 if typing.ty(op).is_closed_form() {
264 continue;
265 }
266 let inlined = matches!(
267 typing.value(op),
268 Value::Op { .. } | Value::Filter { .. } | Value::Read { .. }
269 );
270 match inlined || holds_self(typing, op, &mut BTreeSet::new()) {
271 true => out.extend(materialized_operands(typing, op)),
272 false => out.push(op),
273 }
274 }
275 out
276 };
277 match typing.value(id) {
278 Value::ClosedForm(_) | Value::Noise(_) => Vec::new(),
279 Value::SelfAt { at, .. } => sampled(at.moving()),
280 Value::Solver { varying, .. } => sampled(varying.iter().map(|(_, a)| *a).collect()),
281 Value::Cast(Cast::Sample, source) => vec![*source],
282 Value::Read { source, at, .. } => {
283 let mut out = match (at, anywhere(typing, *source)) {
284 (When::Moving(_) | When::Step(_), true) => Vec::new(),
285 _ => vec![*source],
286 };
287 out.extend(sampled(at.moving()));
288 out
289 }
290 Value::Cast(_, source) => sampled(vec![*source]),
291 Value::Op { args, .. } => sampled(args.clone()),
292 Value::Filter {
293 x, cutoff, q, gain, ..
294 } => sampled(vec![*x, *cutoff, *q, *gain]),
295 }
296}
297
298pub(crate) fn anywhere(typing: &Typing, id: NodeId) -> bool {
300 match typing.value(id) {
301 Value::Noise(_) => true,
302 Value::Cast(Cast::Sample, of) => typing.ty(*of).held == Held::Form(Var::T),
303 Value::ClosedForm(form) => form.var == Var::T,
304 _ => false,
305 }
306}
307
308pub(crate) fn read_operands(typing: &Typing, id: NodeId) -> Vec<NodeId> {
310 match typing.value(id) {
311 Value::ClosedForm(form) => crate::refs::nodes_in(&form.body),
312 _ if typing.ty(id).is_closed_form() => {
313 let mut out = Vec::new();
314 reads_under(typing, id, &mut BTreeSet::new(), &mut out);
315 out.dedup();
316 out
317 }
318 _ => materialized_operands(typing, id),
319 }
320}
321
322fn reads_under(typing: &Typing, id: NodeId, seen: &mut BTreeSet<NodeId>, out: &mut Vec<NodeId>) {
324 if !seen.insert(id) {
325 return;
326 }
327 match typing.value(id) {
328 Value::ClosedForm(form) => out.extend(crate::refs::nodes_in(&form.body)),
329 Value::Read { source, .. } => out.push(*source),
330 Value::Cast(_, source) => read_through(typing, *source, seen, out),
331 Value::Op { args, .. } => {
332 for arg in args {
333 read_through(typing, *arg, seen, out);
334 }
335 }
336 Value::Filter {
337 x, cutoff, q, gain, ..
338 } => {
339 for operand in [x, cutoff, q, gain] {
340 read_through(typing, *operand, seen, out);
341 }
342 }
343 Value::Solver { varying, .. } => {
344 for (_, arg) in varying {
345 read_through(typing, *arg, seen, out);
346 }
347 }
348 Value::SelfAt { .. } | Value::Noise(_) => {}
349 }
350}
351
352fn read_through(typing: &Typing, id: NodeId, seen: &mut BTreeSet<NodeId>, out: &mut Vec<NodeId>) {
354 let Value::ClosedForm(_) = typing.value(id) else {
355 return reads_under(typing, id, seen, out);
356 };
357 if seen.insert(id) {
358 out.push(id);
359 }
360}