1use sva_formula::spectral_sum::atom::SpectralAtom;
4use sva_formula::{ClosedForm, Lane, Opaque, Reads, SpectralSum, Var, normalize_closed_form};
5
6use super::active::{self, SampleInterval};
7use super::column::{self, Program};
8use super::lines::{self, Direct};
9use super::truncate::{self, Audible};
10use super::{addends, atoms, point, tail};
11use crate::error::CollapseError;
12use crate::grid::{Grid, Round};
13use crate::label::{Detail, Label, Rule, Source};
14use crate::profile::Profile;
15
16pub struct Rows {
19 row: Row,
20 grid: Grid,
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 intervals: Vec<Vec<SampleInterval>>,
34 },
35 Point {
38 written: Box<ClosedForm>,
39 width: usize,
40 intervals: Vec<SampleInterval>,
41 programs: Vec<Program>,
42 },
43 Added(Vec<(Row, usize, SampleInterval)>),
45}
46
47impl std::fmt::Debug for Rows {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("Rows")
50 .field("grid", &self.grid)
51 .field("width", &self.width)
52 .field("label", &self.label)
53 .finish_non_exhaustive()
54 }
55}
56
57impl Rows {
58 pub fn of(form: &ClosedForm, grid: Grid, profile: &Profile) -> Result<Rows, CollapseError> {
59 match normalize_closed_form(form) {
60 Ok(sum) => Rows::of_spectral_sum_or_point(&sum, Some(form), (grid, profile), &Opaque),
61 Err(_) if form.var == Var::T => Ok(Rows::held(of_written(form, grid, profile)?, grid)),
62 Err(left) => Err(CollapseError::LeftAlgebra(left.reason.clause())),
63 }
64 }
65
66 pub fn of_spectral_sum_or_point(
68 sum: &SpectralSum,
69 written: Option<&ClosedForm>,
70 (grid, profile): (Grid, &Profile),
71 reads: &dyn Reads,
72 ) -> Result<Rows, CollapseError> {
73 let row = match of_sum(sum, grid, profile, reads) {
74 Err(e) if reaches_no_atom(&e) => match written.filter(|t| t.var == Var::T) {
75 Some(form) => of_written(form, grid, profile),
76 None => Err(e),
77 },
78 other => other,
79 }?;
80 Ok(Rows::held(row, grid))
81 }
82
83 fn held((row, label): (Row, (Source, Detail)), grid: Grid) -> Rows {
84 Rows {
85 width: width(&row),
86 row,
87 grid,
88 label,
89 }
90 }
91
92 pub fn width(&self) -> usize {
93 self.width
94 }
95
96 pub fn label(&self, profile: &Profile) -> Label {
98 let (source, detail) = self.label.clone();
99 Label::new(source, profile.name, self.grid.rate, detail)
100 }
101
102 pub fn lines(&self) -> Option<Vec<f64>> {
104 let Row::Lines(lanes) = &self.row else {
105 return None;
106 };
107 Some(lanes.iter().flatten().flat_map(Direct::hz).collect())
108 }
109
110 pub fn planes(&self, from: i64, to: i64) -> Result<Vec<Vec<f64>>, CollapseError> {
112 (0..self.width)
113 .map(|c| values(&self.row, c, (from, to), self.grid))
114 .collect()
115 }
116
117 pub fn at(&self, c: usize, t: f64) -> Result<f64, CollapseError> {
119 let c =
120 lane(self.width, c).ok_or(CollapseError::NotEvaluable("a component past the width"))?;
121 let landed = self.grid.step_at(t, Round::Even);
122 match landed.filter(|n| self.grid.instant(*n) == t) {
123 Some(n) => Ok(values(&self.row, c, (n, n + 1), self.grid)?[0]),
124 None => between(&self.row, c, t),
125 }
126 }
127}
128
129type Labelled = (Row, (Source, Detail));
130
131struct Group {
133 factor: SpectralAtom,
134 live: SampleInterval,
135 direct: Option<Direct>,
136}
137
138fn grouped(
141 sum: &SpectralSum,
142 (band, grid): (Audible, Grid),
143 reads: &dyn Reads,
144) -> Option<Vec<Vec<Group>>> {
145 let lane = |lane: &Lane| {
146 let groups = lines::line_groups(lane, band, reads)?.into_iter();
147 Some(
148 groups
149 .map(|(factor, held)| Group {
150 live: lines::group_interval(&factor, &held, grid),
151 direct: Direct::of(&held),
152 factor,
153 })
154 .collect(),
155 )
156 };
157 sum.lanes.iter().map(lane).collect()
158}
159
160fn of_sum(
162 sum: &SpectralSum,
163 grid: Grid,
164 profile: &Profile,
165 reads: &dyn Reads,
166) -> Result<Labelled, CollapseError> {
167 if sum.var == Var::F {
168 return Err(CollapseError::NoBlockRow);
169 }
170 let band = Audible::on(profile, grid);
171 let ceiling = band.ceiling();
172 if let Some(found) = lines::kept_lines(sum, profile, ceiling, reads)? {
173 let (dropped, dropped_more) = lines::dropped_list(found.dropped());
174 let summed = lines::distinct(&found.kept);
175 let detail = Detail::Lines {
176 rule: Rule::LineSpectrumSummed,
177 summed,
178 dropped,
179 dropped_more,
180 tail_db: found.tail(),
181 };
182 let row = Row::Lines(found.kept.iter().map(|kept| Direct::of(kept)).collect());
183 return Ok((row, (Source::Exact, detail)));
184 }
185 let truncated = truncate::spectral_sum_read(sum, band, reads)?;
186 let label = match () {
187 () if atoms::band_limited(&truncated, ceiling, profile) => (
188 Source::Exact,
189 Detail::Continuous {
190 rule: Rule::BandLimited,
191 },
192 ),
193 () if atoms::windowed(&truncated) => (
194 Source::Measured,
195 Detail::Cropped {
196 rule: Rule::CroppedPair,
197 tail_db: tail::tail_db(&truncated, ceiling),
198 },
199 ),
200 () => (
201 Source::Measured,
202 Detail::Point {
203 rule: Rule::PointSampled,
204 alias_db: None,
205 tail_db: None,
206 },
207 ),
208 };
209 if let Some(lanes) = grouped(sum, (band, grid), reads) {
210 return Ok((Row::Grouped(lanes), label));
211 }
212 let spans = truncated
213 .lanes
214 .iter()
215 .map(|lane| active::spans(lane, grid))
216 .collect();
217 let intervals = truncated
218 .lanes
219 .iter()
220 .map(|lane| active::nonzero_intervals(lane, grid))
221 .collect();
222 let row = Row::Sweep {
223 sum: Box::new(truncated),
224 spans,
225 intervals,
226 };
227 Ok((row, label))
228}
229
230fn of_written(form: &ClosedForm, grid: Grid, profile: &Profile) -> Result<Labelled, CollapseError> {
232 let Some(addends) = addends::addends(form) else {
233 return point(form, grid, profile);
234 };
235 let mut parts = Vec::with_capacity(addends.len());
236 let mut labels: Vec<(Source, Detail)> = Vec::new();
237 for addend in &addends {
238 match of_term(addend, grid, profile)? {
239 (Row::Added(inner), (source, Detail::Added { parts: details })) => {
240 parts.extend(inner);
241 labels.extend(details.into_iter().map(|d| (source, d)));
242 }
243 (part, (source, detail)) => {
244 let (held, reach) = (width(&part), reach(&part, grid));
245 parts.push((part, held, reach));
246 labels.push((source, detail));
247 }
248 }
249 }
250 if parts
251 .iter()
252 .all(|(part, ..)| matches!(part, Row::Point { .. }))
253 {
254 return point(form, grid, profile);
255 }
256 let source = match labels.iter().all(|(s, _)| *s == Source::Exact) {
257 true => Source::Exact,
258 false => Source::Measured,
259 };
260 let detail = Detail::Added {
261 parts: labels.into_iter().map(|(_, d)| d).collect(),
262 };
263 Ok((Row::Added(parts), (source, detail)))
264}
265
266fn of_term(form: &ClosedForm, grid: Grid, profile: &Profile) -> Result<Labelled, CollapseError> {
267 let Ok(sum) = normalize_closed_form(form) else {
268 return of_written(form, grid, profile);
269 };
270 match of_sum(&sum, grid, profile, &Opaque) {
271 Err(nested @ CollapseError::NestedSeries { .. }) => Err(nested),
272 Err(_) => of_written(form, grid, profile),
273 held => held,
274 }
275}
276
277fn point(form: &ClosedForm, grid: Grid, profile: &Profile) -> Result<Labelled, CollapseError> {
278 let written = ClosedForm {
279 body: truncate::written(&form.body, Audible::on(profile, grid))?,
280 ..form.clone()
281 };
282 let tail_db = truncate::dropped_db(&written.body);
283 let width = column::width_of(&written.body, &[]).max(1);
284 let parts = addends::summed(&written.body).unwrap_or_default();
285 let intervals = addends::addend_intervals(&parts, grid);
286 let roots: Vec<&sva_formula::Body> = std::iter::once(&written.body)
287 .chain(parts.iter().map(|p| &*p.body))
288 .collect();
289 let programs = (0..width).map(|c| Program::of(&roots, &[], c)).collect();
290 let row = Row::Point {
291 written: Box::new(written),
292 width,
293 intervals,
294 programs,
295 };
296 let detail = Detail::Point {
297 rule: Rule::PointSampled,
298 alias_db: None,
299 tail_db,
300 };
301 Ok((row, (Source::Measured, detail)))
302}
303
304fn width(row: &Row) -> usize {
305 match row {
306 Row::Lines(lanes) => lanes.len(),
307 Row::Grouped(lanes) => lanes.len(),
308 Row::Sweep { sum, .. } => sum.lanes.len(),
309 Row::Point { width, .. } => *width,
310 Row::Added(parts) => parts.iter().map(|(_, w, _)| *w).max().unwrap_or(1),
311 }
312}
313
314fn reach(row: &Row, grid: Grid) -> SampleInterval {
317 let hull = |spans: &mut dyn Iterator<Item = SampleInterval>| {
318 spans
319 .filter(|(a, b)| a < b)
320 .reduce(|x, y| (x.0.min(y.0), x.1.max(y.1)))
321 .unwrap_or((0, 0))
322 };
323 match row {
324 Row::Lines(_) => active::OPEN,
325 Row::Grouped(lanes) => hull(&mut lanes.iter().flatten().map(|g| g.live)),
326 Row::Sweep { spans, .. } if spans.iter().all(Option::is_some) => {
327 hull(&mut spans.iter().flatten().flatten().copied())
328 }
329 Row::Sweep { .. } => active::OPEN,
330 Row::Point {
331 written, intervals, ..
332 } => match addends::summed(&written.body) {
333 Some(_) => hull(&mut intervals.iter().copied()),
334 None => addends::live_interval(&written.body, grid),
335 },
336 Row::Added(parts) => hull(&mut parts.iter().map(|(.., reach)| *reach)),
337 }
338}
339
340fn values(
341 row: &Row,
342 c: usize,
343 (from, to): SampleInterval,
344 grid: Grid,
345) -> Result<Vec<f64>, CollapseError> {
346 let n = (to - from) as usize;
347 Ok(match row {
348 Row::Lines(lanes) => (from..to)
349 .map(|i| {
350 let mut held = 0.0;
351 if let Some(direct) = &lanes[c] {
352 held += direct.at(grid.instant(i));
353 }
354 held
355 })
356 .collect(),
357 Row::Grouped(lanes) => {
359 let mut out = vec![0.0; n];
360 for g in &lanes[c] {
361 let (a, b) = active::meet((from, to), g.live);
362 for m in a..b {
363 let summed = g
364 .direct
365 .as_ref()
366 .map_or(0.0, |d| 0.0 + d.at(grid.instant(m)));
367 out[(m - from) as usize] += summed
368 * point::eval_atom_on(&g.factor, grid.instant(m), Some((grid, m)))?.re;
369 }
370 }
371 out
372 }
373 Row::Sweep {
374 sum,
375 spans,
376 intervals,
377 } => {
378 let mut out = vec![0.0; n];
379 for (a, b) in inside(spans[c].as_deref(), (from, to)) {
380 let at = (a - from) as usize..(b - from) as usize;
381 active::sweep(&sum.lanes[c], &intervals[c], (a, b), grid, &mut out[at])?;
382 }
383 out
384 }
385 Row::Point {
386 written,
387 intervals,
388 programs,
389 ..
390 } => {
391 let summed = addends::summed(&written.body).is_some();
392 point_values(&programs[c], summed.then_some(intervals), (from, to), grid)?
393 }
394 Row::Added(parts) => {
395 let mut sum = vec![0.0; n];
396 for (part, held, reach) in parts {
397 if let (Some(lane), (a, b)) = (lane(*held, c), active::meet((from, to), *reach))
398 && a < b
399 {
400 let at = (a - from) as usize;
401 for (out, v) in sum[at..].iter_mut().zip(values(part, lane, (a, b), grid)?) {
402 *out += v;
403 }
404 }
405 }
406 sum
407 }
408 })
409}
410
411const CHUNK: i64 = 256;
413
414fn point_values(
418 program: &Program,
419 intervals: Option<&Vec<SampleInterval>>,
420 (from, to): SampleInterval,
421 grid: Grid,
422) -> Result<Vec<f64>, CollapseError> {
423 let mut out = vec![0.0; (to - from).max(0) as usize];
424 let mut columns = program.columns(CHUNK.min(to - from).max(0) as usize);
425 columns.grid = grid;
426 let mut at = from;
427 while at < to {
428 let end = (at + CHUNK).min(to);
429 let lanes = (end - at) as usize;
430 columns.again();
431 for (i, n) in (at..end).enumerate() {
432 columns.t[i] = grid.instant(n);
433 columns.on[i] = Some(n);
434 }
435 let written = &mut out[(at - from) as usize..][..lanes];
436 match intervals {
437 None => {
438 let whole = columns.root(0, (0, lanes));
439 for (i, v) in written.iter_mut().enumerate() {
440 *v = whole.get(i).map_err(Clone::clone)?.re;
441 }
442 }
443 Some(intervals) => {
444 let mut sums: Vec<Result<sva_formula::C64, CollapseError>> =
445 vec![Ok(sva_formula::C64::ZERO); lanes];
446 for (k, live) in intervals.iter().enumerate() {
447 let (lo, hi) = active::meet((at, end), *live);
448 let (lo, hi) = ((lo - at).max(0) as usize, (hi - at).max(0) as usize);
449 if lo >= hi {
450 continue;
451 }
452 let addend = columns.root(k + 1, (lo, hi));
453 for (i, sum) in sums.iter_mut().enumerate().take(hi).skip(lo) {
454 if let Some(held) = sum.as_ref().ok().copied() {
455 *sum = addend.get(i).map(|v| held + v).map_err(Clone::clone);
456 }
457 }
458 }
459 for (v, sum) in written.iter_mut().zip(sums) {
460 *v = match sum? {
461 sum if sum.is_finite() => sum.re,
462 _ => return Err(column::INFINITE),
463 };
464 }
465 }
466 }
467 at = end;
468 }
469 Ok(out)
470}
471
472fn between(row: &Row, c: usize, t: f64) -> Result<f64, CollapseError> {
474 Ok(match row {
475 Row::Lines(lanes) => {
476 let mut held = 0.0;
477 if let Some(direct) = &lanes[c] {
478 held += direct.at(t);
479 }
480 held
481 }
482 Row::Grouped(lanes) => {
483 let mut out = 0.0;
484 for g in &lanes[c] {
485 let summed = g.direct.as_ref().map_or(0.0, |d| 0.0 + d.at(t));
486 out += summed * point::eval_atom_on(&g.factor, t, None)?.re;
487 }
488 out
489 }
490 Row::Sweep { sum, .. } => point::eval_spectral_sum(sum, c, t)?.re,
491 Row::Point { programs, .. } => {
492 let mut columns = programs[c].columns(1);
493 columns.t[0] = t;
494 columns.root(0, (0, 1)).get(0).map_err(Clone::clone)?.re
495 }
496 Row::Added(parts) => {
497 let mut sum = 0.0;
498 for (part, held, _) in parts {
499 if let Some(lane) = lane(*held, c) {
500 sum += between(part, lane, t)?;
501 }
502 }
503 sum
504 }
505 })
506}
507
508fn inside(spans: Option<&[SampleInterval]>, (from, to): SampleInterval) -> Vec<SampleInterval> {
510 match spans {
511 None => vec![(from, to)],
512 Some(spans) => spans
513 .iter()
514 .map(|(a, b)| (from.max(*a), to.min(*b)))
515 .filter(|(a, b)| a < b)
516 .collect(),
517 }
518}
519
520fn lane(width: usize, c: usize) -> Option<usize> {
522 let lane = if width == 1 { 0 } else { c };
523 (lane < width).then_some(lane)
524}
525
526fn reaches_no_atom(e: &CollapseError) -> bool {
529 matches!(e, CollapseError::NotEvaluable(_))
530}