1use sva_formula::spectral_sum::atom::SpectralAtom;
4use sva_formula::{ClosedForm, Lane, SpectralSum, Var, normalize_closed_form};
5
6use super::active::{self, Window};
7use super::lines::{self, Direct};
8use super::truncate::{self, Audible};
9use super::{atoms, plan, point, reaches_no_atom, reading, span, tail};
10use crate::Grid;
11use crate::error::CollapseError;
12use crate::label::{Detail, Label, Rule, Source};
13use crate::machine::tape::Tape;
14use crate::profile::Profile;
15
16pub struct Rows {
19 row: Row,
20 rate: u32,
21 width: usize,
22 label: (Source, Detail),
23}
24
25enum Row {
26 Lines(Vec<Option<Direct>>),
28 Grouped(Vec<Vec<Group>>),
30 Sweep {
31 sum: Box<SpectralSum>,
32 spans: Vec<Option<Vec<(i64, i64)>>>,
33 windows: Vec<Vec<Window>>,
34 },
35 Point {
37 written: Box<ClosedForm>,
38 width: usize,
39 windows: Vec<Window>,
40 },
41 Added(Vec<(Row, usize, Window)>),
43}
44
45impl Rows {
46 pub fn of(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Rows, CollapseError> {
48 match normalize_closed_form(form) {
49 Ok(sum) => Rows::of_spectral_sum_or_point(&sum, Some(form), rate, profile),
50 Err(_) if form.var == Var::T => Ok(Rows::held(of_written(form, rate, profile)?, rate)),
51 Err(left) => Err(CollapseError::LeftAlgebra(left.reason.clause())),
52 }
53 }
54
55 pub fn of_spectral_sum_or_point(
56 sum: &SpectralSum,
57 written: Option<&ClosedForm>,
58 rate: u32,
59 profile: &Profile,
60 ) -> Result<Rows, CollapseError> {
61 let row = match of_sum(sum, rate, profile) {
62 Err(e) if reaches_no_atom(&e) => match written.filter(|t| t.var == Var::T) {
63 Some(form) => of_written(form, rate, profile),
64 None => Err(e),
65 },
66 other => other,
67 }?;
68 Ok(Rows::held(row, rate))
69 }
70
71 fn held((row, label): (Row, (Source, Detail)), rate: u32) -> Rows {
72 Rows {
73 width: width(&row),
74 row,
75 rate,
76 label,
77 }
78 }
79
80 pub fn width(&self) -> usize {
81 self.width
82 }
83
84 pub fn label(&self, profile: &Profile) -> Label {
86 let (source, detail) = self.label.clone();
87 Label::new(source, profile.name, self.rate, detail)
88 }
89
90 pub fn lines(&self) -> Option<Vec<f64>> {
92 let Row::Lines(lanes) = &self.row else {
93 return None;
94 };
95 Some(lanes.iter().flatten().flat_map(Direct::hz).collect())
96 }
97
98 pub fn planes(&self, from: i64, to: i64) -> Result<Vec<Vec<f64>>, CollapseError> {
100 (0..self.width)
101 .map(|c| values(&self.row, c, (from, to), self.rate))
102 .collect()
103 }
104
105 pub fn work(&self, from: i64, to: i64) -> (u128, u128) {
107 (0..self.width).fold((0, 0), |held, c| {
108 let (priced, waves) = worked(&self.row, c, from, to, Grid::of(self.rate));
109 (held.0 + priced, held.1 + waves)
110 })
111 }
112
113 pub fn extend(&self, to: i64, tape: &mut Tape) -> Result<(), CollapseError> {
115 let from = tape.end();
116 if to <= from {
117 return Ok(());
118 }
119 let planes = (0..self.width)
120 .map(|c| values(&self.row, c, (from, to), self.rate))
121 .collect::<Result<Vec<_>, _>>()?;
122 for i in 0..(to - from) as usize {
123 for (c, plane) in planes.iter().enumerate() {
124 tape.push(c, plane[i]);
125 }
126 }
127 Ok(())
128 }
129}
130
131fn worked(row: &Row, c: usize, from: i64, to: i64, grid: Grid) -> (u128, u128) {
134 let n = (to - from) as u128;
135 let times = |(priced, waves): (usize, usize), n: u128| (priced as u128 * n, waves as u128 * n);
136 match row {
137 Row::Lines(lanes) => lanes[c]
138 .as_ref()
139 .map_or((0, 0), |d| times(d.lines_priced_and_turned(), n)),
140 Row::Grouped(lanes) => lanes[c].iter().fold((0, 0), |held, g| {
141 let (from, to) = active::meet((from, to), g.live);
142 let (priced, waves) = g
143 .direct
144 .as_ref()
145 .map_or((0, 0), Direct::lines_priced_and_turned);
146 let (priced, waves) = times((priced + 1, waves), (to - from).max(0) as u128);
147 (held.0 + priced, held.1 + waves)
148 }),
149 Row::Sweep { spans, windows, .. } => {
150 let evaluated =
151 active::evaluated(&windows[c], &inside(spans[c].as_deref(), (from, to)));
152 (evaluated, evaluated)
153 }
154 Row::Point { written, .. } => plan::point_work(&written.body, c, grid, (from, to)),
155 Row::Added(parts) => parts.iter().fold((0, 0), |held, (part, width, reach)| {
156 match (lane(*width, c), active::meet((from, to), *reach)) {
157 (Some(lane), (a, b)) if a < b => {
158 let (priced, waves) = worked(part, lane, a, b, grid);
159 (held.0 + priced, held.1 + waves)
160 }
161 _ => held,
162 }
163 }),
164 }
165}
166
167type Labelled = (Row, (Source, Detail));
168
169struct Group {
171 factor: SpectralAtom,
172 live: Window,
173 direct: Option<Direct>,
174}
175
176fn grouped(sum: &SpectralSum, band: Audible, grid: Grid) -> Option<Vec<Vec<Group>>> {
179 let lane = |lane: &Lane| {
180 let groups = lines::line_groups(lane, band)?.into_iter();
181 Some(
182 groups
183 .map(|(factor, held)| Group {
184 live: plan::group_window(&factor, &held, 1, grid),
185 direct: Direct::of(&held),
186 factor,
187 })
188 .collect(),
189 )
190 };
191 sum.lanes.iter().map(lane).collect()
192}
193
194fn of_sum(sum: &SpectralSum, rate: u32, profile: &Profile) -> Result<Labelled, CollapseError> {
196 if sum.var == Var::F {
197 return Err(CollapseError::NoBlockRow);
198 }
199 let ceiling = profile.ceiling(rate);
200 if let Some(found) = plan::kept_lines(sum, profile, ceiling)? {
201 let (dropped, dropped_more) = lines::dropped_list(found.dropped());
202 let summed = lines::distinct(&found.kept);
203 let detail = Detail::Lines {
204 rule: Rule::LineSpectrumSummed,
205 placed: 0,
206 summed,
207 dropped,
208 dropped_more,
209 terms: Some(summed),
210 tail_db: found.tail(),
211 };
212 let row = Row::Lines(found.kept.iter().map(|kept| Direct::of(kept)).collect());
213 return Ok((row, (Source::Exact, detail)));
214 }
215 let truncated = truncate::spectral_sum(sum, Audible::of(profile, rate))?;
216 let label = match () {
217 () if atoms::band_limited(&truncated, ceiling, profile) => (
218 Source::Exact,
219 Detail::Continuous {
220 rule: Rule::BandLimited,
221 },
222 ),
223 () if atoms::windowed(&truncated) => (
224 Source::Measured,
225 Detail::Cropped {
226 rule: Rule::CroppedPair,
227 tail_db: tail::tail_db(&truncated, ceiling),
228 },
229 ),
230 () => (
231 Source::Measured,
232 Detail::Point {
233 rule: Rule::PointSampled,
234 alias_db: None,
235 },
236 ),
237 };
238 let grid = Grid::of(rate);
239 if let Some(lanes) = grouped(sum, Audible::of(profile, rate), grid) {
240 return Ok((Row::Grouped(lanes), label));
241 }
242 let spans = truncated
243 .lanes
244 .iter()
245 .map(|lane| span::windows(lane, rate))
246 .collect();
247 let windows = truncated
248 .lanes
249 .iter()
250 .map(|lane| active::windows(lane, grid))
251 .collect();
252 let row = Row::Sweep {
253 sum: Box::new(truncated),
254 spans,
255 windows,
256 };
257 Ok((row, label))
258}
259
260fn of_written(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Labelled, CollapseError> {
262 let Some(addends) = plan::addends(form) else {
263 return point(form, rate, profile);
264 };
265 let mut parts = Vec::with_capacity(addends.len());
266 let mut labels: Vec<(Source, Detail)> = Vec::new();
267 for addend in &addends {
268 match of_term(addend, rate, profile)? {
269 (Row::Added(inner), (source, Detail::Added { parts: details })) => {
270 parts.extend(inner);
271 labels.extend(details.into_iter().map(|d| (source, d)));
272 }
273 (part, (source, detail)) => {
274 let (held, reach) = (width(&part), reach(&part, rate));
275 parts.push((part, held, reach));
276 labels.push((source, detail));
277 }
278 }
279 }
280 if parts
281 .iter()
282 .all(|(part, ..)| matches!(part, Row::Point { .. }))
283 {
284 return point(form, rate, profile);
285 }
286 let source = match labels.iter().all(|(s, _)| *s == Source::Exact) {
287 true => Source::Exact,
288 false => Source::Measured,
289 };
290 let detail = Detail::Added {
291 parts: labels.into_iter().map(|(_, d)| d).collect(),
292 };
293 Ok((Row::Added(parts), (source, detail)))
294}
295
296fn of_term(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Labelled, CollapseError> {
297 let Ok(sum) = normalize_closed_form(form) else {
298 return of_written(form, rate, profile);
299 };
300 match of_sum(&sum, rate, profile) {
301 Err(nested @ CollapseError::NestedSeries { .. }) => Err(nested),
302 Err(_) => of_written(form, rate, profile),
303 held => held,
304 }
305}
306
307fn point(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Labelled, CollapseError> {
308 let written = ClosedForm {
309 body: truncate::written(&form.body, Audible::of(profile, rate))?,
310 ..form.clone()
311 };
312 let width = point::width_of(&written.body, &point::NoRefs).max(1);
313 let windows = plan::summed(&written.body).map_or_else(Vec::new, |parts| {
314 plan::addend_windows(&parts, Grid::of(rate))
315 });
316 let row = Row::Point {
317 written: Box::new(written),
318 width,
319 windows,
320 };
321 let detail = Detail::Point {
322 rule: Rule::PointSampled,
323 alias_db: None,
324 };
325 Ok((row, (Source::Measured, detail)))
326}
327
328fn width(row: &Row) -> usize {
329 match row {
330 Row::Lines(lanes) => lanes.len(),
331 Row::Grouped(lanes) => lanes.len(),
332 Row::Sweep { sum, .. } => sum.lanes.len(),
333 Row::Point { width, .. } => *width,
334 Row::Added(parts) => parts.iter().map(|(_, w, _)| *w).max().unwrap_or(1),
335 }
336}
337
338fn reach(row: &Row, rate: u32) -> Window {
341 let hull = |spans: &mut dyn Iterator<Item = Window>| {
342 spans
343 .filter(|(a, b)| a < b)
344 .reduce(|x, y| (x.0.min(y.0), x.1.max(y.1)))
345 .unwrap_or((0, 0))
346 };
347 match row {
348 Row::Lines(_) => active::OPEN,
349 Row::Grouped(lanes) => hull(&mut lanes.iter().flatten().map(|g| g.live)),
350 Row::Sweep { spans, .. } if spans.iter().all(Option::is_some) => {
351 hull(&mut spans.iter().flatten().flatten().copied())
352 }
353 Row::Sweep { .. } => active::OPEN,
354 Row::Point {
355 written, windows, ..
356 } => match plan::summed(&written.body) {
357 Some(_) => hull(&mut windows.iter().copied()),
358 None => plan::live_window(&written.body, Grid::of(rate)),
359 },
360 Row::Added(parts) => hull(&mut parts.iter().map(|(.., reach)| *reach)),
361 }
362}
363
364fn values(row: &Row, c: usize, (from, to): Window, rate: u32) -> Result<Vec<f64>, CollapseError> {
366 let grid = Grid::of(rate);
367 let n = (to - from) as usize;
368 Ok(match row {
369 Row::Lines(lanes) => (from..to)
370 .map(|i| {
371 let mut held = 0.0;
372 if let Some(direct) = &lanes[c] {
373 held += direct.at(i as f64 / f64::from(rate));
374 }
375 held
376 })
377 .collect(),
378 Row::Grouped(lanes) => {
380 let mut out = vec![0.0; n];
381 for g in &lanes[c] {
382 let (a, b) = active::meet((from, to), g.live);
383 for m in a..b {
384 let summed = g
385 .direct
386 .as_ref()
387 .map_or(0.0, |d| 0.0 + d.at(m as f64 / f64::from(rate)));
388 out[(m - from) as usize] +=
389 summed * point::eval_atom(&g.factor, grid.instant(m))?.re;
390 }
391 }
392 out
393 }
394 Row::Sweep {
395 sum,
396 spans,
397 windows,
398 } => {
399 let mut out = vec![0.0; n];
400 for (a, b) in inside(spans[c].as_deref(), (from, to)) {
401 let at = (a - from) as usize..(b - from) as usize;
402 active::sweep(&sum.lanes[c], &windows[c], (a, b), grid, &mut out[at])?;
403 }
404 out
405 }
406 Row::Point {
407 written, windows, ..
408 } => reading::written(&written.body, windows, c, (from, to), grid)?,
409 Row::Added(parts) => {
410 let mut sum = vec![0.0; n];
411 for (part, held, reach) in parts {
412 if let (Some(lane), (a, b)) = (lane(*held, c), active::meet((from, to), *reach))
413 && a < b
414 {
415 let at = (a - from) as usize;
416 for (out, v) in sum[at..].iter_mut().zip(values(part, lane, (a, b), rate)?) {
417 *out += v;
418 }
419 }
420 }
421 sum
422 }
423 })
424}
425
426fn inside(spans: Option<&[(i64, i64)]>, (from, to): Window) -> Vec<Window> {
428 match spans {
429 None => vec![(from, to)],
430 Some(spans) => spans
431 .iter()
432 .map(|(a, b)| (from.max(*a), to.min(*b)))
433 .filter(|(a, b)| a < b)
434 .collect(),
435 }
436}
437
438fn lane(width: usize, c: usize) -> Option<usize> {
440 let lane = if width == 1 { 0 } else { c };
441 (lane < width).then_some(lane)
442}