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