1use std::collections::HashMap;
4
5use crate::error::SampleError;
6use crate::grid::Grid;
7use crate::machine::renderer::{
8 Between, Binary, Formula, Index, Map, NodeRenderer, Site, SiteId, Slot, Stepped, Unary,
9};
10
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub(crate) enum Op {
13 Const(f64),
14 Time,
15 Wrap(Option<Stepped>),
17 Noise {
20 seed: u64,
21 at: Option<Map>,
22 },
23 Read {
24 slot: Slot,
25 at: Map,
26 },
27 ReadScaled {
29 slot: Slot,
30 at: Map,
31 by: f64,
32 },
33 Indexed {
35 slot: Slot,
36 at: usize,
37 arity: usize,
38 reach: Option<(i64, i64)>,
39 },
40 Instant {
41 at: usize,
42 arity: usize,
43 },
44 Formula {
46 at: usize,
47 },
48 Add(usize),
49 Mul(usize),
50 Sub,
51 Div,
52 Pow,
53 Map(Unary),
54 Zip(Binary),
55 Crop {
56 window: (i64, i64),
57 a: f64,
58 b: f64,
59 rise: f64,
60 fall: f64,
61 },
62 Join(usize),
63 Channel(usize),
64 Filter {
65 site: SiteId,
66 from: i64,
67 },
68 Physics {
69 site: SiteId,
70 from: i64,
71 arity: usize,
72 },
73}
74
75#[derive(Clone, Debug, PartialEq)]
78pub struct Layout {
79 pub grid: Grid,
80 pub width: usize,
81 pub read_widths: Vec<usize>,
82 pub sites: Vec<Site>,
83}
84
85fn meet(a: usize, b: usize) -> Result<usize, SampleError> {
87 match (a, b) {
88 (a, b) if a == b => Ok(a),
89 (1, b) => Ok(b),
90 (a, 1) => Ok(a),
91 (left, right) => Err(SampleError::WidthMismatch { left, right }),
92 }
93}
94
95#[derive(Default)]
97pub(crate) struct Lowered {
98 pub(crate) ops: Vec<Op>,
99 pub(crate) widths: Vec<usize>,
100 pub(crate) args: Vec<Vec<usize>>,
101 pub(crate) formulas: Vec<Formula>,
102 pub(crate) indices: Vec<Index<usize>>,
103 held: HashMap<String, usize>,
104}
105
106pub(crate) fn lowered(
109 renderer: &NodeRenderer,
110 layout: &Layout,
111) -> Result<(Lowered, usize), SampleError> {
112 let mut out = Lowered::default();
113 let root = lower(renderer, layout, &mut out)?;
114 debug_assert_eq!(root, out.ops.len() - 1, "an op is never its own operand");
115 let width = out.widths[root];
116 Ok((out, width))
117}
118
119impl Lowered {
120 fn push(&mut self, op: Op, width: usize, args: Vec<usize>) -> usize {
122 let key = self.key(&op, &args);
123 if let Some(slot) = key.as_ref().and_then(|key| self.held.get(key)) {
124 if matches!(op, Op::Indexed { .. } | Op::Instant { .. }) {
125 self.indices.pop();
126 }
127 if matches!(op, Op::Formula { .. }) {
128 self.formulas.pop();
129 }
130 return *slot;
131 }
132 self.ops.push(op);
133 self.widths.push(width);
134 self.args.push(args);
135 let slot = self.ops.len() - 1;
136 if let Some(key) = key {
137 self.held.insert(key, slot);
138 }
139 slot
140 }
141
142 fn key(&self, op: &Op, args: &[usize]) -> Option<String> {
145 let bits = |v: &[f64]| v.iter().map(|v| v.to_bits()).collect::<Vec<u64>>();
146 let what = match op {
147 Op::Const(v) => format!("const {:?}", bits(&[*v])),
148 Op::ReadScaled { slot, at, by } => format!("scaled {slot:?} {at:?} {:?}", bits(&[*by])),
149 Op::Crop {
150 window,
151 a,
152 b,
153 rise,
154 fall,
155 } => format!("crop {window:?} {:?}", bits(&[*a, *b, *rise, *fall])),
156 Op::Indexed {
157 slot, at, reach, ..
158 } => format!("indexed {slot:?} {reach:?} {:?}", self.indices[*at]),
159 Op::Instant { at, .. } => format!("instant {:?}", self.indices[*at]),
160 Op::Formula { at } => {
161 let first = self.formulas.iter().position(|f| *f == self.formulas[*at]);
162 format!("formula {}", first.expect("the formula itself"))
163 }
164 Op::Filter { .. } | Op::Physics { .. } => return None,
165 other => format!("{other:?}"),
166 };
167 Some(format!("{what} {args:?}"))
168 }
169}
170
171fn indexed(
172 index: &Index,
173 layout: &Layout,
174 out: &mut Lowered,
175) -> Result<(usize, Vec<usize>), SampleError> {
176 let mut operands = Vec::new();
177 let program = index.mapped(&mut |time| {
178 operands.push(lower(time, layout, out)?);
179 Ok::<usize, SampleError>(operands.len() - 1)
180 })?;
181 out.indices.push(program);
182 Ok((out.indices.len() - 1, operands))
183}
184
185fn lower(r: &NodeRenderer, layout: &Layout, out: &mut Lowered) -> Result<usize, SampleError> {
187 if let NodeRenderer::Mul(parts) = r
188 && let Some((slot, at, by)) = scaled_read(parts)
189 {
190 let width = slot_width(slot, layout);
191 return Ok(out.push(Op::ReadScaled { slot, at, by }, width, Vec::new()));
192 }
193 let (op, operands) = match r {
194 NodeRenderer::Formula { formula, time, .. } => {
195 let operands = vec![lower(time, layout, out)?];
196 out.formulas.push(formula.clone());
197 let at = out.formulas.len() - 1;
198 (Op::Formula { at }, operands)
199 }
200 NodeRenderer::Indexed { slot, index, reach } => {
201 let (at, operands) = indexed(index, layout, out)?;
202 let op = Op::Indexed {
203 slot: *slot,
204 at,
205 arity: operands.len(),
206 reach: *reach,
207 };
208 (op, operands)
209 }
210 NodeRenderer::Instant(index) => {
211 let (at, operands) = indexed(index, layout, out)?;
212 let arity = operands.len();
213 (Op::Instant { at, arity }, operands)
214 }
215 other => {
216 let operands = other
217 .operands()
218 .into_iter()
219 .map(|p| lower(p, layout, out))
220 .collect::<Result<Vec<_>, _>>()?;
221 (op_of(other, layout), operands)
222 }
223 };
224 let widths: Vec<usize> = operands.iter().map(|s| out.widths[*s]).collect();
225 let w = width(r, &widths, layout)?;
226 Ok(out.push(op, w, operands))
227}
228
229fn op_of(r: &NodeRenderer, layout: &Layout) -> Op {
230 match r {
231 NodeRenderer::Const(v) => Op::Const(*v),
232 NodeRenderer::Time => Op::Time,
233 NodeRenderer::Wrap(wrap) => Op::Wrap(wrap.on(layout.grid)),
234 NodeRenderer::Noise(seed) => Op::Noise {
235 seed: *seed,
236 at: Map::rounded(layout.grid.a, 0, layout.grid.d, Between::Even),
237 },
238 NodeRenderer::Read { slot, map } => Op::Read {
239 slot: *slot,
240 at: *map,
241 },
242 NodeRenderer::Add(parts) => Op::Add(parts.len()),
243 NodeRenderer::Mul(parts) => Op::Mul(parts.len()),
244 NodeRenderer::Join(parts) => Op::Join(parts.len()),
245 NodeRenderer::Sub(..) => Op::Sub,
246 NodeRenderer::Div(..) => Op::Div,
247 NodeRenderer::Pow(..) => Op::Pow,
248 NodeRenderer::Map(f, _) => Op::Map(*f),
249 NodeRenderer::Zip(f, ..) => Op::Zip(*f),
250 NodeRenderer::Crop {
251 window,
252 a,
253 b,
254 rise,
255 fall,
256 ..
257 } => Op::Crop {
258 window: *window,
259 a: *a,
260 b: *b,
261 rise: *rise,
262 fall: *fall,
263 },
264 NodeRenderer::Channel { k, .. } => Op::Channel(*k),
265 NodeRenderer::Filter { site, from, .. } => Op::Filter {
266 site: *site,
267 from: *from,
268 },
269 NodeRenderer::Physics { site, from, args } => Op::Physics {
270 site: *site,
271 from: *from,
272 arity: args.len(),
273 },
274 NodeRenderer::Formula { .. } | NodeRenderer::Indexed { .. } | NodeRenderer::Instant(_) => {
275 unreachable!("lowered with the program tables they name")
276 }
277 }
278}
279
280pub(crate) fn width(
282 r: &NodeRenderer,
283 operands: &[usize],
284 layout: &Layout,
285) -> Result<usize, SampleError> {
286 let met = |from: usize| operands.iter().try_fold(from, |w, &o| meet(w, o));
287 Ok(match r {
288 NodeRenderer::Const(_)
289 | NodeRenderer::Time
290 | NodeRenderer::Wrap(_)
291 | NodeRenderer::Noise(_) => 1,
292 NodeRenderer::Read { slot, .. } => slot_width(*slot, layout),
293 NodeRenderer::Formula { width, .. } => match operands {
294 [times] if *times != 1 && times != width => {
295 return Err(SampleError::WidthMismatch {
296 left: *times,
297 right: *width,
298 });
299 }
300 _ => *width,
301 },
302 NodeRenderer::Indexed { slot, .. } => slot_width(*slot, layout),
303 NodeRenderer::Instant(_) | NodeRenderer::Physics { .. } => 1,
304 NodeRenderer::Add(_) | NodeRenderer::Mul(_) => met(1)?,
305 NodeRenderer::Sub(..)
306 | NodeRenderer::Div(..)
307 | NodeRenderer::Pow(..)
308 | NodeRenderer::Zip(..)
309 | NodeRenderer::Filter { .. } => met(operands[0])?,
310 NodeRenderer::Map(..) | NodeRenderer::Crop { .. } => operands[0],
311 NodeRenderer::Join(_) => operands.iter().sum(),
312 NodeRenderer::Channel { k, .. } => match operands[0] {
313 w if *k >= w => return Err(SampleError::ChannelOutOfRange { k: *k, width: w }),
314 _ => 1,
315 },
316 })
317}
318
319fn slot_width(slot: Slot, layout: &Layout) -> usize {
320 match slot {
321 Slot::Read(id) => layout.read_widths[id.0 as usize],
322 Slot::Own => layout.width,
323 }
324}
325
326fn scaled_read(parts: &[NodeRenderer]) -> Option<(Slot, Map, f64)> {
327 match parts {
328 [NodeRenderer::Read { slot, map }, NodeRenderer::Const(by)]
329 | [NodeRenderer::Const(by), NodeRenderer::Read { slot, map }] => Some((*slot, *map, *by)),
330 _ => None,
331 }
332}