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