Skip to main content

sva_samples/collapse/
blocks.rs

1// Concern: reads a closed form onto any span of the grid by rows no horizon chooses | Non-concern: the rows a whole render fits to its horizon (plan.rs) | IO: (form, rate) -> Rows; (Tape, to) -> samples
2
3use sva_formula::{ClosedForm, SpectralSum, Var, normalize_closed_form};
4
5use super::lines::Direct;
6use super::truncate::{self, Audible};
7use super::{plan, point, reaches_no_atom, span};
8use crate::error::CollapseError;
9use crate::machine::tape::Tape;
10use crate::profile::Profile;
11
12/// Rows chosen from the form, the rate and the profile alone: each sample is its own
13/// instant's value, so any span reads what one whole span reads there.
14pub struct Rows {
15    row: Row,
16    rate: u32,
17    width: usize,
18}
19
20enum Row {
21    /// Every kept line summed at each instant, as a whole render's direct route sums them.
22    Lines(Vec<Option<Direct>>),
23    Sweep {
24        sum: Box<SpectralSum>,
25        spans: Vec<Option<Vec<(usize, usize)>>>,
26    },
27    Point {
28        written: Box<ClosedForm>,
29        width: usize,
30    },
31    Added(Vec<(Row, usize)>),
32}
33
34impl Rows {
35    /// `collapse::render`'s dispatch.
36    pub fn of(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Rows, CollapseError> {
37        match normalize_closed_form(form) {
38            Ok(sum) => Rows::of_spectral_sum_or_point(&sum, Some(form), rate, profile),
39            Err(_) if form.var == Var::T => Ok(Rows::held(of_written(form, rate, profile)?, rate)),
40            Err(left) => Err(CollapseError::LeftAlgebra(left.reason.clause())),
41        }
42    }
43
44    pub fn of_spectral_sum_or_point(
45        sum: &SpectralSum,
46        written: Option<&ClosedForm>,
47        rate: u32,
48        profile: &Profile,
49    ) -> Result<Rows, CollapseError> {
50        let row = match of_sum(sum, rate, profile) {
51            Err(e) if reaches_no_atom(&e) => match written.filter(|t| t.var == Var::T) {
52                Some(form) => of_written(form, rate, profile),
53                None => Err(e),
54            },
55            other => other,
56        }?;
57        Ok(Rows::held(row, rate))
58    }
59
60    fn held(row: Row, rate: u32) -> Rows {
61        Rows {
62            width: width(&row),
63            row,
64            rate,
65        }
66    }
67
68    pub fn width(&self) -> usize {
69        self.width
70    }
71
72    /// Appends `[tape.end(), to)` of every component, counted from the grid's start.
73    pub fn extend(&self, to: usize, tape: &mut Tape) -> Result<(), CollapseError> {
74        let step = 1.0 / f64::from(self.rate);
75        for i in tape.end()..to {
76            for c in 0..self.width {
77                tape.push(c, value(&self.row, c, i, self.rate, step)?);
78            }
79        }
80        Ok(())
81    }
82}
83
84/// `plan::of` with each row a horizon picks by cost replaced by the one summed per instant.
85fn of_sum(sum: &SpectralSum, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
86    if sum.var == Var::F {
87        return Err(CollapseError::NoBlockRow);
88    }
89    if let Some(found) = plan::kept_lines(sum, profile, profile.ceiling(rate))? {
90        return Ok(Row::Lines(
91            found.kept.iter().map(|kept| Direct::of(kept)).collect(),
92        ));
93    }
94    let truncated = truncate::spectral_sum(sum, Audible::of(profile, rate))?;
95    let spans = truncated
96        .lanes
97        .iter()
98        .map(|lane| span::windows(lane, 0.0, rate, f64::INFINITY))
99        .collect();
100    Ok(Row::Sweep {
101        sum: Box::new(truncated),
102        spans,
103    })
104}
105
106/// `plan::of_written`'s split into addends.
107fn of_written(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
108    let Some(addends) = plan::addends(form) else {
109        return point(form, rate, profile);
110    };
111    let mut parts = Vec::with_capacity(addends.len());
112    for addend in &addends {
113        match of_term(addend, rate, profile)? {
114            Row::Added(inner) => parts.extend(inner),
115            part => {
116                let held = width(&part);
117                parts.push((part, held));
118            }
119        }
120    }
121    match parts
122        .iter()
123        .all(|(part, _)| matches!(part, Row::Point { .. }))
124    {
125        true => point(form, rate, profile),
126        false => Ok(Row::Added(parts)),
127    }
128}
129
130fn of_term(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
131    let Ok(sum) = normalize_closed_form(form) else {
132        return of_written(form, rate, profile);
133    };
134    match of_sum(&sum, rate, profile) {
135        Err(nested @ CollapseError::NestedSeries { .. }) => Err(nested),
136        Err(_) => of_written(form, rate, profile),
137        held => held,
138    }
139}
140
141fn point(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
142    let written = ClosedForm {
143        body: truncate::written(&form.body, Audible::of(profile, rate))?,
144        ..form.clone()
145    };
146    let width = point::width_of(&written.body, &point::NoRefs).max(1);
147    Ok(Row::Point {
148        written: Box::new(written),
149        width,
150    })
151}
152
153fn width(row: &Row) -> usize {
154    match row {
155        Row::Lines(lanes) => lanes.len(),
156        Row::Sweep { sum, .. } => sum.lanes.len(),
157        Row::Point { width, .. } => *width,
158        Row::Added(parts) => parts.iter().map(|(_, w)| *w).max().unwrap_or(1),
159    }
160}
161
162/// Each row's arithmetic, in its whole-render row's order, so the bits agree.
163fn value(row: &Row, c: usize, i: usize, rate: u32, step: f64) -> Result<f64, CollapseError> {
164    Ok(match row {
165        Row::Lines(lanes) => {
166            let mut held = 0.0;
167            if let Some(direct) = &lanes[c] {
168                held += direct.at(0.0 + i as f64 / f64::from(rate));
169            }
170            held
171        }
172        Row::Sweep { sum, spans } => {
173            let inside = spans[c]
174                .as_ref()
175                .is_none_or(|spans| spans.iter().any(|(from, to)| (*from..*to).contains(&i)));
176            match inside {
177                true => point::eval_lane(&sum.lanes[c], 0.0 + i as f64 * step)?.re,
178                false => 0.0,
179            }
180        }
181        Row::Point { written, .. } => {
182            point::eval_body(&written.body, c, 0.0 + i as f64 * step, &point::NoRefs)?.re
183        }
184        Row::Added(parts) => {
185            let mut sum = 0.0;
186            for (part, held) in parts {
187                let lane = match held {
188                    1 => 0,
189                    _ => c,
190                };
191                if lane < *held {
192                    sum += value(part, lane, i, rate, step)?;
193                }
194            }
195            sum
196        }
197    })
198}