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}
99
100impl Unary {
101 pub fn name(self) -> &'static str {
102 match self {
103 Unary::Sin => "sin",
104 Unary::Cos => "cos",
105 Unary::Exp => "exp",
106 Unary::Tanh => "tanh",
107 Unary::Sat => "sat",
108 Unary::Abs => "abs",
109 Unary::Log => "log",
110 Unary::Sqrt => "sqrt",
111 }
112 }
113
114 pub const ALL: &'static [Unary] = &[
115 Unary::Sin,
116 Unary::Cos,
117 Unary::Exp,
118 Unary::Tanh,
119 Unary::Sat,
120 Unary::Abs,
121 Unary::Log,
122 Unary::Sqrt,
123 ];
124
125 pub fn from_name(name: &str) -> Option<Unary> {
126 Unary::ALL.iter().copied().find(|u| u.name() == name)
127 }
128
129 pub fn is_closed(self) -> bool {
131 matches!(self, Unary::Sin | Unary::Cos | Unary::Exp)
132 }
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq)]
136pub enum Fold {
137 Max,
138 Min,
139 Mod,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
143pub struct IndexId(pub u32);
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub enum Bound {
147 Finite(i64),
148 Infinite,
149}
150
151#[derive(Clone, Debug, PartialEq)]
153pub struct Series {
154 pub index: IndexId,
155 pub lo: i64,
156 pub hi: Bound,
157 pub term: Part,
158}
159
160#[derive(Clone, Copy, Debug, PartialEq)]
161pub struct Mode {
162 pub omega: f64,
163 pub tau: f64,
164 pub amp: f64,
165 pub phase: f64,
166}
167
168#[derive(Clone, Copy, Debug, PartialEq)]
169pub enum Excitation {
170 HammerPulse { f0: f64, t0: f64, contact: f64 },
171 Impulse { t0: f64 },
172}
173
174#[derive(Clone, Debug, PartialEq)]
175pub struct ModalBank {
176 pub modes: Vec<Mode>,
177 pub excite: Excitation,
178}
179
180#[derive(Clone, Debug, PartialEq)]
182pub struct Rational {
183 pub zeros: Vec<C64>,
184 pub poles: Vec<C64>,
185 pub gain: C64,
186}
187
188#[derive(Clone, Debug, PartialEq)]
189pub enum Body {
190 Const(C64),
191 Line,
192 Index(IndexId),
193 Keyed {
195 seed: u64,
196 of: Part,
197 },
198 Param(ParamId),
199 Node(NodeId),
200 Add(Vec<Part>),
201 Mul(Vec<Part>),
202 Div(Part, Part),
203 Pow(Part, i32),
204 Apply(Unary, Part),
205 Fold(Fold, Vec<Part>),
206 Delta {
207 at: Part,
208 order: u16,
209 },
210 Pv(Part),
211 Shift {
212 by: f64,
213 of: Part,
214 },
215 Warp {
218 at: Part,
219 of: Part,
220 },
221 Deriv {
222 order: u16,
223 of: Part,
224 },
225 Crop {
227 of: Part,
228 l: Edge,
229 r: Edge,
230 rise: f64,
231 fall: f64,
232 },
233 Join(Vec<Part>),
234 Channel(Part, u8),
235 Rational(Rational),
236 Series(Box<Series>),
237 Modal(ModalBank),
238}
239
240pub fn children(f: &Body) -> Vec<&Part> {
243 match f {
244 Body::Const(_)
245 | Body::Line
246 | Body::Index(_)
247 | Body::Param(_)
248 | Body::Node(_)
249 | Body::Rational(_)
250 | Body::Modal(_) => Vec::new(),
251 Body::Keyed { of, .. } => vec![of],
252 Body::Add(parts) | Body::Mul(parts) | Body::Join(parts) | Body::Fold(_, parts) => {
253 parts.iter().collect()
254 }
255 Body::Div(a, b) | Body::Warp { at: a, of: b } => vec![a, b],
256 Body::Pow(of, _)
257 | Body::Apply(_, of)
258 | Body::Pv(of)
259 | Body::Channel(of, _)
260 | Body::Delta { at: of, .. }
261 | Body::Shift { of, .. }
262 | Body::Deriv { of, .. }
263 | Body::Crop { of, .. } => vec![of],
264 Body::Series(s) => vec![&s.term],
265 }
266}
267
268pub fn map_children(f: &Body, mut g: impl FnMut(&Part) -> Part) -> Body {
269 let one = |p: &Part, g: &mut dyn FnMut(&Part) -> Part| g(p);
270 match f {
271 Body::Const(_)
272 | Body::Line
273 | Body::Index(_)
274 | Body::Param(_)
275 | Body::Node(_)
276 | Body::Rational(_)
277 | Body::Modal(_) => f.clone(),
278 Body::Keyed { seed, of } => Body::Keyed {
279 seed: *seed,
280 of: one(of, &mut g),
281 },
282 Body::Add(parts) => Body::Add(parts.iter().map(g).collect()),
283 Body::Mul(parts) => Body::Mul(parts.iter().map(g).collect()),
284 Body::Join(parts) => Body::Join(parts.iter().map(g).collect()),
285 Body::Fold(op, parts) => Body::Fold(*op, parts.iter().map(g).collect()),
286 Body::Div(a, b) => Body::Div(g(a), g(b)),
287 Body::Warp { at, of } => Body::Warp {
288 at: g(at),
289 of: g(of),
290 },
291 Body::Pow(of, n) => Body::Pow(g(of), *n),
292 Body::Apply(op, of) => Body::Apply(*op, g(of)),
293 Body::Pv(of) => Body::Pv(g(of)),
294 Body::Channel(of, k) => Body::Channel(g(of), *k),
295 Body::Delta { at, order } => Body::Delta {
296 at: g(at),
297 order: *order,
298 },
299 Body::Shift { by, of } => Body::Shift { by: *by, of: g(of) },
300 Body::Deriv { order, of } => Body::Deriv {
301 order: *order,
302 of: g(of),
303 },
304 Body::Crop {
305 of,
306 l,
307 r,
308 rise,
309 fall,
310 } => Body::Crop {
311 of: g(of),
312 l: *l,
313 r: *r,
314 rise: *rise,
315 fall: *fall,
316 },
317 Body::Series(s) => Body::Series(Box::new(Series {
318 term: g(&s.term),
319 ..(**s).clone()
320 })),
321 }
322}
323
324pub fn shift_line(f: &Body, by: f64) -> Body {
327 let walk = |p: &Part| Part::new(p.origin, shift_line(&p.body, by));
328 match f {
329 Body::Line => Body::Add(vec![
330 Part::bare(Body::Line),
331 Part::bare(Body::Const(crate::complex::C64::real(-by))),
332 ]),
333 Body::Crop {
334 of,
335 l,
336 r,
337 rise,
338 fall,
339 } => Body::Crop {
340 of: walk(of),
341 l: Edge::at(l.value() + by),
342 r: Edge::at(r.value() + by),
343 rise: *rise,
344 fall: *fall,
345 },
346 Body::Warp { at, of } => Body::Warp {
347 at: walk(at),
348 of: of.clone(),
349 },
350 other => map_children(other, walk),
351 }
352}
353
354pub fn read_at(f: &Body, at: &Body) -> Body {
356 match f {
357 Body::Line => at.clone(),
358 Body::Crop { .. } => Body::Warp {
359 at: Part::bare(at.clone()),
360 of: Part::bare(f.clone()),
361 },
362 Body::Warp { at: inner, of } => Body::Warp {
363 at: Part::new(inner.origin, read_at(&inner.body, at)),
364 of: of.clone(),
365 },
366 other => map_children(other, |p| Part::new(p.origin, read_at(&p.body, at))),
367 }
368}
369
370pub fn shifts_opaquely(f: &Body) -> bool {
372 matches!(
373 f,
374 Body::Node(_) | Body::Param(_) | Body::Rational(_) | Body::Modal(_)
375 ) || children(f).iter().any(|p| shifts_opaquely(&p.body))
376}