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