1mod active;
4mod atoms;
5mod blocks;
6mod inverse;
7mod lines;
8pub mod plan;
9mod point;
10mod reading;
11pub mod run;
12mod span;
13mod sum;
14mod tail;
15mod truncate;
16
17use sva_formula::{Body, C64, ClosedForm, SpectralSum, Var, normalize_closed_form};
18
19use crate::buffer::Buffer;
20use crate::error::CollapseError;
21use crate::label::{Detail, Label, Rule, Source};
22use crate::profile::Profile;
23
24pub use blocks::Rows;
25pub use plan::transform_flops;
26
27use plan::Plan;
28pub(crate) use point::NoRefs;
29pub use point::{Refs, crop_gain, lane_of, shoulders, unary};
30pub use truncate::{Audible, spectral_sum as truncate_spectral_sum, written as truncate_written};
31
32pub fn eval_spectral_sum_at(
34 sum: &SpectralSum,
35 component: usize,
36 t: f64,
37) -> Result<C64, CollapseError> {
38 point::eval_spectral_sum(sum, component, t)
39}
40
41pub fn eval_written_at(
44 body: &Body,
45 component: usize,
46 t: f64,
47 refs: &dyn Refs,
48) -> Result<C64, CollapseError> {
49 point::eval_body(body, component, t, refs)
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
54pub struct Extent {
55 pub start: i64,
56 pub end: i64,
57}
58
59impl Extent {
60 pub const EVERYWHERE: Extent = Extent {
62 start: i64::MIN,
63 end: i64::MAX,
64 };
65
66 pub const NOWHERE: Extent = Extent { start: 0, end: 0 };
67
68 pub fn new(start: i64, end: i64) -> Extent {
69 assert!(start <= end, "an extent [{start}, {end}) runs backwards");
70 Extent { start, end }
71 }
72
73 pub fn from(start: i64) -> Extent {
74 Extent::new(start, i64::MAX)
75 }
76
77 pub fn is_bounded(&self) -> bool {
78 self.start != i64::MIN && self.end != i64::MAX
79 }
80
81 pub fn contains(&self, n: i64) -> bool {
82 self.start <= n && n < self.end
83 }
84
85 pub fn intersect(self, other: Extent) -> Extent {
86 let start = self.start.max(other.start);
87 let end = self.end.min(other.end);
88 match start < end {
89 true => Extent { start, end },
90 false => Extent::NOWHERE,
91 }
92 }
93
94 pub fn hull(self, other: Extent) -> Extent {
95 match (self.is_empty(), other.is_empty()) {
96 (true, _) => other,
97 (_, true) => self,
98 _ => Extent {
99 start: self.start.min(other.start),
100 end: self.end.max(other.end),
101 },
102 }
103 }
104
105 pub fn shifted(self, by: i64) -> Extent {
107 if self.is_empty() {
108 return self;
109 }
110 let edge = |n: i64| match n {
111 i64::MIN | i64::MAX => n,
112 n => n.saturating_add(by).clamp(i64::MIN + 1, i64::MAX - 1),
113 };
114 Extent {
115 start: edge(self.start),
116 end: edge(self.end),
117 }
118 }
119
120 pub fn secs(rate: u32, start_secs: f64, end_secs: f64) -> Extent {
121 let at = |secs: f64| (secs * f64::from(rate)).round() as i64;
122 Extent::new(at(start_secs), at(end_secs))
123 }
124
125 pub fn len(&self) -> usize {
126 debug_assert!(self.is_bounded(), "an unbounded extent has no length");
127 (self.end - self.start) as usize
128 }
129
130 pub fn is_empty(&self) -> bool {
131 self.end == self.start
132 }
133
134 pub fn start_secs(&self, rate: u32) -> f64 {
135 self.start as f64 / f64::from(rate)
136 }
137
138 pub fn span_secs(&self, rate: u32) -> f64 {
139 self.len() as f64 / f64::from(rate)
140 }
141
142 pub fn instant(&self, i: usize, scale: usize, step: f64) -> f64 {
145 (self.start * scale as i64 + i as i64) as f64 * step
146 }
147}
148
149pub const ALIAS_OVERSAMPLE: usize = 4;
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155pub enum AliasScore {
156 Asked,
157 NotAsked,
158}
159
160pub fn render(
162 form: &ClosedForm,
163 rate: u32,
164 extent: Extent,
165 profile: &Profile,
166 score: AliasScore,
167) -> Result<(Buffer, Label), CollapseError> {
168 match normalize_closed_form(form) {
169 Ok(sum) => of_spectral_sum_or_point(&sum, Some(form), rate, extent, profile, score),
170 Err(_) if form.var == Var::T => render_written(form, rate, extent, profile, score),
171 Err(left) => Err(CollapseError::LeftAlgebra(left.reason.clause())),
172 }
173}
174
175pub fn render_written(
177 form: &ClosedForm,
178 rate: u32,
179 extent: Extent,
180 profile: &Profile,
181 score: AliasScore,
182) -> Result<(Buffer, Label), CollapseError> {
183 let len = extent.len();
184 run(
185 plan::of_written(form, rate, extent, profile, len)?,
186 rate,
187 extent,
188 profile,
189 len,
190 score,
191 )
192}
193
194fn point_row(
196 written: &ClosedForm,
197 width: usize,
198 rate: u32,
199 extent: Extent,
200 profile: &Profile,
201 len: usize,
202 score: AliasScore,
203) -> Result<(Buffer, Label), CollapseError> {
204 let planes = (0..width)
205 .map(|c| reading::sampled_body(written, c, rate, extent, len, 1))
206 .collect::<Result<Vec<_>, _>>()?;
207 let alias_db = match score {
208 AliasScore::Asked => Some(formula_alias(written, rate, extent, len, &planes)?),
209 AliasScore::NotAsked => None,
210 };
211 Ok(measured(
212 planes,
213 rate,
214 extent,
215 profile,
216 Detail::Point {
217 rule: Rule::PointSampled,
218 alias_db,
219 },
220 ))
221}
222
223pub fn of_spectral_sum_or_point(
226 sum: &SpectralSum,
227 written: Option<&ClosedForm>,
228 rate: u32,
229 extent: Extent,
230 profile: &Profile,
231 score: AliasScore,
232) -> Result<(Buffer, Label), CollapseError> {
233 match of_spectral_sum(sum, rate, extent, profile, score) {
234 Err(e) if reaches_no_atom(&e) => match written.filter(|t| t.var == Var::T) {
235 Some(form) => render_written(form, rate, extent, profile, score),
236 None => Err(e),
237 },
238 other => other,
239 }
240}
241
242pub struct Planned(Plan);
245
246pub fn planned(
247 sum: Option<&SpectralSum>,
248 written: Option<&ClosedForm>,
249 rate: u32,
250 extent: Extent,
251 profile: &Profile,
252) -> Result<Planned, CollapseError> {
253 let len = extent.len();
254 let normalized;
255 let sum = match (sum, written) {
256 (Some(sum), _) => sum,
257 (None, Some(form)) => match normalize_closed_form(form) {
258 Ok(held) => {
259 normalized = held;
260 &normalized
261 }
262 Err(_) if form.var == Var::T => {
263 return plan::of_written(form, rate, extent, profile, len).map(Planned);
264 }
265 Err(left) => return Err(CollapseError::LeftAlgebra(left.reason.clause())),
266 },
267 (None, None) => return Err(CollapseError::NotEvaluable("a form with neither view")),
268 };
269 match plan::of(sum, rate, extent, profile, len) {
270 Err(e) if reaches_no_atom(&e) => match written.filter(|t| t.var == Var::T) {
271 Some(form) => plan::of_written(form, rate, extent, profile, len).map(Planned),
272 None => Err(e),
273 },
274 other => other.map(Planned),
275 }
276}
277
278impl Planned {
279 pub fn nonzero(&self, rate: u32, extent: Extent) -> Extent {
280 self.0.nonzero(rate, extent)
281 }
282
283 pub fn route(&self) -> Vec<u64> {
284 self.0.route()
285 }
286
287 pub fn run(
288 self,
289 rate: u32,
290 extent: Extent,
291 profile: &Profile,
292 score: AliasScore,
293 ) -> Result<(Buffer, Label), CollapseError> {
294 run(self.0, rate, extent, profile, extent.len(), score)
295 }
296}
297
298pub(crate) fn reaches_no_atom(e: &CollapseError) -> bool {
301 matches!(e, CollapseError::NotEvaluable(_))
302}
303
304pub fn of_spectral_sum(
306 sum: &SpectralSum,
307 rate: u32,
308 extent: Extent,
309 profile: &Profile,
310 score: AliasScore,
311) -> Result<(Buffer, Label), CollapseError> {
312 let len = extent.len();
313 of_sum(sum, rate, extent, profile, len, score)
314}
315
316fn of_sum(
318 sum: &SpectralSum,
319 rate: u32,
320 extent: Extent,
321 profile: &Profile,
322 len: usize,
323 score: AliasScore,
324) -> Result<(Buffer, Label), CollapseError> {
325 run(
326 plan::of(sum, rate, extent, profile, len)?,
327 rate,
328 extent,
329 profile,
330 len,
331 score,
332 )
333}
334
335fn run(
336 plan: Plan,
337 rate: u32,
338 extent: Extent,
339 profile: &Profile,
340 len: usize,
341 score: AliasScore,
342) -> Result<(Buffer, Label), CollapseError> {
343 match plan {
344 Plan::Spectrum(sum) => spectrum_row(&sum, rate, extent, profile, len),
345 Plan::Lines(found) => Ok(line_row(&found, rate, extent, profile, len)),
346 Plan::Sampled(held) => sampled_row(&held, rate, extent, profile, len, score),
347 Plan::Point { written, width, .. } => {
348 point_row(&written, width, rate, extent, profile, len, score)
349 }
350 Plan::Added(parts) => sum::added(parts, rate, extent, profile, len, score),
351 }
352}
353
354fn line_row(
355 found: &plan::LinePlan,
356 rate: u32,
357 extent: Extent,
358 profile: &Profile,
359 len: usize,
360) -> (Buffer, Label) {
361 let planes: Vec<Vec<f64>> = found
362 .placed
363 .iter()
364 .zip(&found.summed)
365 .map(|(placed, summed)| {
366 let mut plane = lines::transformed(placed, extent, found.bins, rate, len);
367 lines::add_direct(&mut plane, summed, extent, rate);
368 plane
369 })
370 .collect();
371 let (list, more) = lines::dropped_list(&found.dropped);
372 let (placed, summed) = (
373 lines::distinct(&found.placed),
374 lines::distinct(&found.summed),
375 );
376 let detail = Detail::Lines {
377 rule: found.rule(),
378 placed,
379 summed,
380 dropped: list,
381 dropped_more: more,
382 terms: Some(placed + summed),
383 tail_db: found.tail_db,
384 };
385 exact(planes, rate, extent, profile, detail)
386}
387
388fn sampled_row(
389 held: &plan::Sampled,
390 rate: u32,
391 extent: Extent,
392 profile: &Profile,
393 len: usize,
394 score: AliasScore,
395) -> Result<(Buffer, Label), CollapseError> {
396 let (sum, rule) = (&held.sum, held.rule);
397 let planes = reading::sampled_spectral_sum(sum, &held.lanes, rate, extent, len)?;
398 match rule {
399 Rule::BandLimited => Ok(exact(
400 planes,
401 rate,
402 extent,
403 profile,
404 Detail::Continuous { rule },
405 )),
406 Rule::CroppedPair => Ok(measured(
407 planes,
408 rate,
409 extent,
410 profile,
411 Detail::Cropped {
412 rule,
413 tail_db: tail::tail_db(sum, profile.ceiling(rate)),
414 },
415 )),
416 _ => {
417 let alias_db = match score {
418 AliasScore::Asked => Some(spectral_sum_alias(sum, rate, extent, len, &planes)?),
419 AliasScore::NotAsked => None,
420 };
421 Ok(measured(
422 planes,
423 rate,
424 extent,
425 profile,
426 Detail::Point { rule, alias_db },
427 ))
428 }
429 }
430}
431
432fn spectrum_row(
433 sum: &SpectralSum,
434 rate: u32,
435 extent: Extent,
436 profile: &Profile,
437 len: usize,
438) -> Result<(Buffer, Label), CollapseError> {
439 let mut planes = Vec::with_capacity(sum.lanes.len());
440 for c in 0..sum.lanes.len() {
441 planes.push(inverse::collapse_lane(
442 sum,
443 c,
444 extent.start_secs(rate),
445 extent.span_secs(rate),
446 rate,
447 len,
448 )?);
449 }
450 let wrap_db = inverse::wrap_db(sum, extent.span_secs(rate), rate)?;
451 Ok(measured(
452 planes,
453 rate,
454 extent,
455 profile,
456 Detail::Spectrum {
457 rule: Rule::InverseSpectrum,
458 wrap_db,
459 },
460 ))
461}
462
463fn formula_alias(
465 form: &ClosedForm,
466 rate: u32,
467 extent: Extent,
468 len: usize,
469 planes: &[Vec<f64>],
470) -> Result<f64, CollapseError> {
471 let mut scored = Vec::with_capacity(planes.len());
472 for (c, base) in planes.iter().enumerate() {
473 let high = reading::sampled_body(
474 form,
475 c,
476 rate,
477 extent,
478 len * ALIAS_OVERSAMPLE,
479 ALIAS_OVERSAMPLE,
480 )?;
481 scored.push(one_component(base, &high, rate, extent));
482 }
483 Ok(worst_of(scored))
484}
485
486fn spectral_sum_alias(
487 sum: &SpectralSum,
488 rate: u32,
489 extent: Extent,
490 len: usize,
491 planes: &[Vec<f64>],
492) -> Result<f64, CollapseError> {
493 let step = 1.0 / (f64::from(rate) * ALIAS_OVERSAMPLE as f64);
494 let mut scored = Vec::with_capacity(planes.len());
495 for (c, base) in planes.iter().enumerate() {
496 let high: Vec<f64> = (0..len * ALIAS_OVERSAMPLE)
497 .map(|i| {
498 point::eval_spectral_sum(sum, c, extent.instant(i, ALIAS_OVERSAMPLE, step))
499 .map(|v| v.re)
500 })
501 .collect::<Result<_, _>>()?;
502 scored.push(one_component(base, &high, rate, extent));
503 }
504 Ok(worst_of(scored))
505}
506
507fn one_component(
508 base: &[f64],
509 high: &[f64],
510 rate: u32,
511 extent: Extent,
512) -> crate::measure::alias::Alias {
513 crate::measure::alias::measure_alias(
514 base,
515 high,
516 ALIAS_OVERSAMPLE,
517 f64::from(rate),
518 extent.start_secs(rate),
519 )
520}
521
522fn worst_of(scored: Vec<crate::measure::alias::Alias>) -> f64 {
524 crate::measure::alias::worst(scored).map_or(f64::NEG_INFINITY, |held| held.asr_db)
525}
526
527fn exact(
528 planes: Vec<Vec<f64>>,
529 rate: u32,
530 extent: Extent,
531 profile: &Profile,
532 detail: Detail,
533) -> (Buffer, Label) {
534 labelled(Source::Exact, planes, rate, extent, profile, detail)
535}
536
537fn measured(
538 planes: Vec<Vec<f64>>,
539 rate: u32,
540 extent: Extent,
541 profile: &Profile,
542 detail: Detail,
543) -> (Buffer, Label) {
544 labelled(Source::Measured, planes, rate, extent, profile, detail)
545}
546
547fn labelled(
548 source: Source,
549 planes: Vec<Vec<f64>>,
550 rate: u32,
551 extent: Extent,
552 profile: &Profile,
553 detail: Detail,
554) -> (Buffer, Label) {
555 let mut buffer = Buffer::of_planes(rate, planes);
556 buffer.start = extent.start;
557 (buffer, Label::new(source, profile.name, rate, detail))
558}