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