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 work(&self, from: usize, to: usize) -> (u128, u128) {
74 (0..self.width).fold((0, 0), |held, c| {
75 let (priced, waves) = worked(&self.row, c, from, to);
76 (held.0 + priced, held.1 + waves)
77 })
78 }
79
80 pub fn extend(&self, to: usize, tape: &mut Tape) -> Result<(), CollapseError> {
82 let step = 1.0 / f64::from(self.rate);
83 for i in tape.end()..to {
84 for c in 0..self.width {
85 tape.push(c, value(&self.row, c, i, self.rate, step)?);
86 }
87 }
88 Ok(())
89 }
90}
91
92fn worked(row: &Row, c: usize, from: usize, to: usize) -> (u128, u128) {
95 let n = (to - from) as u128;
96 let times = |(priced, waves): (usize, usize), n: u128| (priced as u128 * n, waves as u128 * n);
97 match row {
98 Row::Lines(lanes) => lanes[c]
99 .as_ref()
100 .map_or((0, 0), |d| times(d.lines_priced_and_turned(), n)),
101 Row::Sweep { sum, spans } => {
102 let inside = spans[c].as_ref().map_or(n, |spans| {
103 let held = spans
104 .iter()
105 .map(|(a, b)| to.min(*b).saturating_sub(from.max(*a)));
106 held.sum::<usize>() as u128
107 });
108 let atoms = sum.lanes[c].atoms.len();
109 times((atoms, atoms), inside)
110 }
111 Row::Point { written, .. } => times(plan::point_work(&written.body, c), n),
112 Row::Added(parts) => {
113 parts
114 .iter()
115 .fold((0, 0), |held, (part, width)| match lane(*width, c) {
116 Some(lane) => {
117 let (priced, waves) = worked(part, lane, from, to);
118 (held.0 + priced, held.1 + waves)
119 }
120 None => held,
121 })
122 }
123 }
124}
125
126fn of_sum(sum: &SpectralSum, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
128 if sum.var == Var::F {
129 return Err(CollapseError::NoBlockRow);
130 }
131 if let Some(found) = plan::kept_lines(sum, profile, profile.ceiling(rate))? {
132 return Ok(Row::Lines(
133 found.kept.iter().map(|kept| Direct::of(kept)).collect(),
134 ));
135 }
136 let truncated = truncate::spectral_sum(sum, Audible::of(profile, rate))?;
137 let spans = truncated
138 .lanes
139 .iter()
140 .map(|lane| span::windows(lane, 0.0, rate, f64::INFINITY))
141 .collect();
142 Ok(Row::Sweep {
143 sum: Box::new(truncated),
144 spans,
145 })
146}
147
148fn of_written(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
150 let Some(addends) = plan::addends(form) else {
151 return point(form, rate, profile);
152 };
153 let mut parts = Vec::with_capacity(addends.len());
154 for addend in &addends {
155 match of_term(addend, rate, profile)? {
156 Row::Added(inner) => parts.extend(inner),
157 part => {
158 let held = width(&part);
159 parts.push((part, held));
160 }
161 }
162 }
163 match parts
164 .iter()
165 .all(|(part, _)| matches!(part, Row::Point { .. }))
166 {
167 true => point(form, rate, profile),
168 false => Ok(Row::Added(parts)),
169 }
170}
171
172fn of_term(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
173 let Ok(sum) = normalize_closed_form(form) else {
174 return of_written(form, rate, profile);
175 };
176 match of_sum(&sum, rate, profile) {
177 Err(nested @ CollapseError::NestedSeries { .. }) => Err(nested),
178 Err(_) => of_written(form, rate, profile),
179 held => held,
180 }
181}
182
183fn point(form: &ClosedForm, rate: u32, profile: &Profile) -> Result<Row, CollapseError> {
184 let written = ClosedForm {
185 body: truncate::written(&form.body, Audible::of(profile, rate))?,
186 ..form.clone()
187 };
188 let width = point::width_of(&written.body, &point::NoRefs).max(1);
189 Ok(Row::Point {
190 written: Box::new(written),
191 width,
192 })
193}
194
195fn width(row: &Row) -> usize {
196 match row {
197 Row::Lines(lanes) => lanes.len(),
198 Row::Sweep { sum, .. } => sum.lanes.len(),
199 Row::Point { width, .. } => *width,
200 Row::Added(parts) => parts.iter().map(|(_, w)| *w).max().unwrap_or(1),
201 }
202}
203
204fn value(row: &Row, c: usize, i: usize, rate: u32, step: f64) -> Result<f64, CollapseError> {
206 Ok(match row {
207 Row::Lines(lanes) => {
208 let mut held = 0.0;
209 if let Some(direct) = &lanes[c] {
210 held += direct.at(0.0 + i as f64 / f64::from(rate));
211 }
212 held
213 }
214 Row::Sweep { sum, spans } => {
215 let inside = spans[c]
216 .as_ref()
217 .is_none_or(|spans| spans.iter().any(|(from, to)| (*from..*to).contains(&i)));
218 match inside {
219 true => point::eval_lane(&sum.lanes[c], 0.0 + i as f64 * step)?.re,
220 false => 0.0,
221 }
222 }
223 Row::Point { written, .. } => {
224 point::eval_body(&written.body, c, 0.0 + i as f64 * step, &point::NoRefs)?.re
225 }
226 Row::Added(parts) => {
227 let mut sum = 0.0;
228 for (part, held) in parts {
229 if let Some(lane) = lane(*held, c) {
230 sum += value(part, lane, i, rate, step)?;
231 }
232 }
233 sum
234 }
235 })
236}
237
238fn lane(width: usize, c: usize) -> Option<usize> {
240 let lane = if width == 1 { 0 } else { c };
241 (lane < width).then_some(lane)
242}