1use 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
12pub struct Rows {
15 row: Row,
16 rate: u32,
17 width: usize,
18}
19
20enum Row {
21 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 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 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
84fn 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
106fn 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
162fn 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}