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