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