1use crate::complex::C64;
4use crate::env::{NodeId, ParamId};
5use crate::origin::Origin;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8pub enum Var {
9 T,
10 F,
11}
12
13impl Var {
14 pub fn as_str(self) -> &'static str {
15 match self {
16 Var::T => "t",
17 Var::F => "f",
18 }
19 }
20}
21
22#[derive(Clone, Debug, PartialEq)]
24pub struct ClosedForm {
25 pub var: Var,
26 pub body: Body,
27 pub origin: Origin,
28}
29
30#[derive(Clone, Debug, PartialEq)]
31pub struct Part {
32 pub origin: Origin,
33 pub body: Box<Body>,
34}
35
36impl Part {
37 pub fn new(origin: Origin, body: Body) -> Part {
38 Part {
39 origin,
40 body: Box::new(body),
41 }
42 }
43
44 pub fn bare(body: Body) -> Part {
45 Part::new(Origin::UNKNOWN, body)
46 }
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51pub enum Edge {
52 NegInf,
53 At(u64),
54 PosInf,
55}
56
57impl Ord for Edge {
59 fn cmp(&self, other: &Edge) -> std::cmp::Ordering {
60 self.value().total_cmp(&other.value())
61 }
62}
63
64impl PartialOrd for Edge {
65 fn partial_cmp(&self, other: &Edge) -> Option<std::cmp::Ordering> {
66 Some(self.cmp(other))
67 }
68}
69
70impl Edge {
71 pub fn at(x: f64) -> Edge {
72 match x {
73 x if x == f64::NEG_INFINITY => Edge::NegInf,
74 x if x == f64::INFINITY => Edge::PosInf,
75 x => Edge::At(crate::complex::canonical(x)),
76 }
77 }
78
79 pub fn value(self) -> f64 {
80 match self {
81 Edge::NegInf => f64::NEG_INFINITY,
82 Edge::At(bits) => f64::from_bits(bits),
83 Edge::PosInf => f64::INFINITY,
84 }
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum Unary {
90 Sin,
91 Cos,
92 Exp,
93 Tanh,
94 Sat,
95 Abs,
96 Log,
97 Sqrt,
98 Step,
100}
101
102impl Unary {
103 pub fn name(self) -> &'static str {
104 match self {
105 Unary::Sin => "sin",
106 Unary::Cos => "cos",
107 Unary::Exp => "exp",
108 Unary::Tanh => "tanh",
109 Unary::Sat => "sat",
110 Unary::Abs => "abs",
111 Unary::Log => "log",
112 Unary::Sqrt => "sqrt",
113 Unary::Step => "step",
114 }
115 }
116
117 pub const ALL: &'static [Unary] = &[
118 Unary::Sin,
119 Unary::Cos,
120 Unary::Exp,
121 Unary::Tanh,
122 Unary::Sat,
123 Unary::Abs,
124 Unary::Log,
125 Unary::Sqrt,
126 Unary::Step,
127 ];
128
129 pub fn from_name(name: &str) -> Option<Unary> {
130 Unary::ALL.iter().copied().find(|u| u.name() == name)
131 }
132
133 pub fn is_closed(self) -> bool {
135 matches!(self, Unary::Sin | Unary::Cos | Unary::Exp)
136 }
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum Fold {
141 Max,
142 Min,
143 Mod,
144}
145
146#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
147pub struct IndexId(pub u32);
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150pub enum Bound {
151 Finite(i64),
152 Infinite,
153}
154
155#[derive(Clone, Debug, PartialEq)]
157pub struct Series {
158 pub index: IndexId,
159 pub lo: i64,
160 pub hi: Bound,
161 pub term: Part,
162}
163
164#[derive(Clone, Copy, Debug, PartialEq)]
165pub struct Mode {
166 pub omega: f64,
167 pub tau: f64,
168 pub amp: f64,
169 pub phase: f64,
170}
171
172#[derive(Clone, Copy, Debug, PartialEq)]
173pub enum Excitation {
174 HammerPulse { f0: f64, t0: f64, contact: f64 },
175 Impulse { t0: f64 },
176}
177
178#[derive(Clone, Debug, PartialEq)]
179pub struct ModalBank {
180 pub modes: Vec<Mode>,
181 pub excite: Excitation,
182}
183
184#[derive(Clone, Debug, PartialEq)]
186pub struct Rational {
187 pub zeros: Vec<C64>,
188 pub poles: Vec<C64>,
189 pub gain: C64,
190}
191
192#[derive(Clone, Debug, PartialEq)]
193pub enum Body {
194 Const(C64),
195 Line,
196 Index(IndexId),
197 Keyed {
199 seed: u64,
200 of: Part,
201 },
202 Param(ParamId),
203 Node(NodeId),
204 Add(Vec<Part>),
205 Mul(Vec<Part>),
206 Div(Part, Part),
207 Pow(Part, i32),
208 Apply(Unary, Part),
209 Fold(Fold, Vec<Part>),
210 Delta {
211 at: Part,
212 order: u16,
213 },
214 Pv(Part),
215 Shift {
216 by: f64,
217 of: Part,
218 },
219 Warp {
222 at: Part,
223 of: Part,
224 },
225 Deriv {
226 order: u16,
227 of: Part,
228 },
229 Crop {
231 of: Part,
232 l: Edge,
233 r: Edge,
234 rise: f64,
235 fall: f64,
236 },
237 Join(Vec<Part>),
238 Channel(Part, u8),
239 Rational(Rational),
240 Series(Box<Series>),
241 Modal(ModalBank),
242 Run(Box<crate::run::Run>),
243 Banded(Box<crate::banded::Banded>),
244}
245
246pub fn children(f: &Body) -> Vec<&Part> {
249 match f {
250 Body::Const(_)
251 | Body::Line
252 | Body::Index(_)
253 | Body::Param(_)
254 | Body::Node(_)
255 | Body::Rational(_)
256 | Body::Modal(_)
257 | Body::Run(_) => Vec::new(),
258 Body::Keyed { of, .. } => vec![of],
259 Body::Add(parts) | Body::Mul(parts) | Body::Join(parts) | Body::Fold(_, parts) => {
260 parts.iter().collect()
261 }
262 Body::Div(a, b) | Body::Warp { at: a, of: b } => vec![a, b],
263 Body::Pow(of, _)
264 | Body::Apply(_, of)
265 | Body::Pv(of)
266 | Body::Channel(of, _)
267 | Body::Delta { at: of, .. }
268 | Body::Shift { of, .. }
269 | Body::Deriv { of, .. }
270 | Body::Crop { of, .. } => vec![of],
271 Body::Series(s) => vec![&s.term],
272 Body::Banded(b) => vec![&b.series.term],
273 }
274}
275
276pub fn map_children(f: &Body, mut g: impl FnMut(&Part) -> Part) -> Body {
277 let one = |p: &Part, g: &mut dyn FnMut(&Part) -> Part| g(p);
278 match f {
279 Body::Const(_)
280 | Body::Line
281 | Body::Index(_)
282 | Body::Param(_)
283 | Body::Node(_)
284 | Body::Rational(_)
285 | Body::Modal(_)
286 | Body::Run(_) => f.clone(),
287 Body::Keyed { seed, of } => Body::Keyed {
288 seed: *seed,
289 of: one(of, &mut g),
290 },
291 Body::Add(parts) => Body::Add(parts.iter().map(g).collect()),
292 Body::Mul(parts) => Body::Mul(parts.iter().map(g).collect()),
293 Body::Join(parts) => Body::Join(parts.iter().map(g).collect()),
294 Body::Fold(op, parts) => Body::Fold(*op, parts.iter().map(g).collect()),
295 Body::Div(a, b) => Body::Div(g(a), g(b)),
296 Body::Warp { at, of } => Body::Warp {
297 at: g(at),
298 of: g(of),
299 },
300 Body::Pow(of, n) => Body::Pow(g(of), *n),
301 Body::Apply(op, of) => Body::Apply(*op, g(of)),
302 Body::Pv(of) => Body::Pv(g(of)),
303 Body::Channel(of, k) => Body::Channel(g(of), *k),
304 Body::Delta { at, order } => Body::Delta {
305 at: g(at),
306 order: *order,
307 },
308 Body::Shift { by, of } => Body::Shift { by: *by, of: g(of) },
309 Body::Deriv { order, of } => Body::Deriv {
310 order: *order,
311 of: g(of),
312 },
313 Body::Crop {
314 of,
315 l,
316 r,
317 rise,
318 fall,
319 } => Body::Crop {
320 of: g(of),
321 l: *l,
322 r: *r,
323 rise: *rise,
324 fall: *fall,
325 },
326 Body::Series(s) => Body::Series(Box::new(Series {
327 term: g(&s.term),
328 ..(**s).clone()
329 })),
330 Body::Banded(b) => {
331 let mut held = (**b).clone();
332 held.series.term = g(&b.series.term);
333 Body::Banded(Box::new(held))
334 }
335 }
336}
337
338pub fn shift_line(f: &Body, by: f64) -> Body {
341 let walk = |p: &Part| Part::new(p.origin, shift_line(&p.body, by));
342 match f {
343 Body::Line => Body::Add(vec![
344 Part::bare(Body::Line),
345 Part::bare(Body::Const(crate::complex::C64::real(-by))),
346 ]),
347 Body::Crop {
348 of,
349 l,
350 r,
351 rise,
352 fall,
353 } => Body::Crop {
354 of: walk(of),
355 l: Edge::at(l.value() + by),
356 r: Edge::at(r.value() + by),
357 rise: *rise,
358 fall: *fall,
359 },
360 Body::Warp { at, of } => Body::Warp {
361 at: walk(at),
362 of: of.clone(),
363 },
364 Body::Node(_) => Body::Shift {
365 by,
366 of: Part::bare(f.clone()),
367 },
368 other => map_children(other, walk),
369 }
370}
371
372pub fn read_at(f: &Body, at: &Body) -> Body {
374 read_at_with(f, at, &|_| None)
375}
376
377pub fn read_at_with(f: &Body, at: &Body, node: &dyn Fn(NodeId) -> Option<NodeId>) -> Body {
378 match f {
379 Body::Line => at.clone(),
380 Body::Node(id) if let Some(moved) = node(*id) => Body::Node(moved),
381 Body::Crop { .. } | Body::Node(_) => Body::Warp {
382 at: Part::bare(at.clone()),
383 of: Part::bare(f.clone()),
384 },
385 Body::Warp { at: inner, of } => Body::Warp {
386 at: Part::new(inner.origin, read_at_with(&inner.body, at, node)),
387 of: of.clone(),
388 },
389 other => map_children(other, |p| {
390 Part::new(p.origin, read_at_with(&p.body, at, node))
391 }),
392 }
393}
394
395pub fn shifts_opaquely(f: &Body) -> bool {
397 matches!(
398 f,
399 Body::Param(_) | Body::Rational(_) | Body::Modal(_) | Body::Run(_) | Body::Banded(_)
400 ) || children(f).iter().any(|p| shifts_opaquely(&p.body))
401}