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