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