1use sva_formula::{Body, C64, Shape, SpectralSum};
4
5use crate::collapse::Extent;
6use crate::error::CollapseError;
7use crate::physics::Params;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
10pub struct BufId(pub u32);
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
13pub struct SiteId(pub u32);
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct Map {
19 pub a: i128,
20 pub b: i128,
21 pub d: i128,
22 pub between: Between,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub enum Between {
27 Exact,
28 Floor,
29 Even,
30}
31
32impl Map {
33 pub const fn shift(b: i64) -> Map {
34 Map::whole(1, b)
35 }
36
37 pub const fn whole(a: i128, b: i64) -> Map {
39 Map {
40 a,
41 b: b as i128,
42 d: 1,
43 between: Between::Exact,
44 }
45 }
46
47 pub fn new(a: i128, b: i128, d: i128) -> Option<Map> {
49 Map::rounded(a, b, d, Between::Exact)
50 }
51
52 pub fn rounded(a: i128, b: i128, d: i128, between: Between) -> Option<Map> {
54 let g = gcd(gcd(a.abs(), b.abs()), d.abs()).max(1);
55 let sign = d.signum();
56 let limit = 1i128 << 100;
57 let map = Map {
58 a: sign * a / g,
59 b: sign * b / g,
60 d: d.abs() / g,
61 between,
62 };
63 let held = map.d > 0 && map.a.abs() < limit && map.b.abs() < limit && map.d < limit;
64 held.then(|| map.settled())
65 .filter(|m| m.d == 1 || m.between != Between::Exact)
66 }
67
68 fn settled(self) -> Map {
69 let exact = match self.between {
70 _ if self.d == 1 => true,
71 Between::Exact => false,
72 Between::Floor => self.a % self.d == 0,
73 Between::Even => {
74 let tie = 2 * self.b.rem_euclid(self.d) == self.d;
75 self.a % self.d == 0 && (!tie || (self.a / self.d) % 2 == 0)
76 }
77 };
78 match exact {
79 true => Map {
80 a: self.a / self.d,
81 b: self.index_at(0),
82 d: 1,
83 between: Between::Exact,
84 },
85 false => self,
86 }
87 }
88
89 pub fn at(self, n: i64) -> i64 {
90 let clamp = |k: i128| k.clamp(i128::from(i64::MIN), i128::from(i64::MAX)) as i64;
91 self.whole_at(n)
92 .unwrap_or_else(|| clamp(self.index_at(i128::from(n))))
93 }
94
95 fn whole_at(self, n: i64) -> Option<i64> {
96 if self.d != 1 {
97 return None;
98 }
99 let (a, b) = (i64::try_from(self.a).ok()?, i64::try_from(self.b).ok()?);
100 a.checked_mul(n)?.checked_add(b)
101 }
102
103 pub fn ahead(self) -> bool {
104 self.a != self.d || self.lead() > 0
105 }
106
107 pub fn lead(self) -> i64 {
108 let most = match self.between {
109 Between::Exact | Between::Floor => self.b.div_euclid(self.d),
110 Between::Even => (2 * self.b + self.d).div_euclid(2 * self.d),
111 };
112 most.clamp(i128::from(i64::MIN / 2), i128::from(i64::MAX / 2)) as i64
113 }
114
115 pub fn least(self) -> i64 {
116 let least = match self.between {
117 Between::Exact | Between::Floor => self.b.div_euclid(self.d),
118 Between::Even => (2 * self.b + self.d - 1).div_euclid(2 * self.d),
119 };
120 least.clamp(i128::from(i64::MIN / 2), i128::from(i64::MAX / 2)) as i64
121 }
122
123 pub fn image(self, over: Extent) -> Extent {
124 if over.is_empty() {
125 return over;
126 }
127 if self.a == 0 {
128 let at = self.at(0);
129 return Extent::new(at, at.saturating_add(1));
130 }
131 let ends = (first(over), last(over));
132 let at = |n: Option<i128>| n.map(|n| self.index_at(n));
133 let (lo, hi) = match self.a > 0 {
134 true => (at(ends.0), at(ends.1)),
135 false => (at(ends.1), at(ends.0)),
136 };
137 extent(lo, hi.map(|h| h + 1))
138 }
139
140 pub fn preimage(self, into: Extent) -> Extent {
143 if into.is_empty() {
144 return into;
145 }
146 if self.between == Between::Even {
147 let up = Map {
148 a: 2 * self.a,
149 b: 2 * self.b + self.d,
150 d: 2 * self.d,
151 between: Between::Floor,
152 };
153 let end = match into.end {
154 i64::MAX => i64::MAX,
155 e => e.saturating_add(1),
156 };
157 return up.preimage(Extent::new(into.start, end));
158 }
159 if self.a == 0 {
160 return match into.contains(self.at(0)) {
161 true => Extent::EVERYWHERE,
162 false => Extent::NOWHERE,
163 };
164 }
165 let (a, b, d) = (self.a, self.b, self.d);
166 let (first, last) = (first(into), last(into));
167 let lowest = |m: i128| ceil_div(m * d - b, a);
168 let highest = |m: i128| floor_div((m + 1) * d - 1 - b, a);
169 let (lo, hi) = match self.a > 0 {
170 true => (first.map(lowest), last.map(highest)),
171 false => (
172 last.map(|m| ceil_div((m + 1) * d - 1 - b, a)),
173 first.map(|m| floor_div(m * d - b, a)),
174 ),
175 };
176 extent(lo, hi.map(|h| h + 1))
177 }
178
179 fn index_at(self, n: i128) -> i128 {
180 let num = self.a.saturating_mul(n).saturating_add(self.b);
181 let (floor, rem) = (num.div_euclid(self.d), num.rem_euclid(self.d));
182 match self.between {
183 Between::Exact | Between::Floor => floor,
184 Between::Even => match (2 * rem).cmp(&self.d) {
185 std::cmp::Ordering::Less => floor,
186 std::cmp::Ordering::Greater => floor + 1,
187 std::cmp::Ordering::Equal => floor + floor.rem_euclid(2),
188 },
189 }
190 }
191}
192
193fn gcd(a: i128, b: i128) -> i128 {
194 match b {
195 0 => a,
196 b => gcd(b, a % b),
197 }
198}
199
200fn first(e: Extent) -> Option<i128> {
201 (e.start != i64::MIN).then(|| i128::from(e.start))
202}
203
204fn last(e: Extent) -> Option<i128> {
205 (e.end != i64::MAX).then(|| i128::from(e.end) - 1)
206}
207
208fn floor_div(num: i128, den: i128) -> i128 {
209 match den < 0 {
210 true => (-num).div_euclid(-den),
211 false => num.div_euclid(den),
212 }
213}
214
215fn ceil_div(num: i128, den: i128) -> i128 {
216 -floor_div(-num, den)
217}
218
219fn extent(lo: Option<i128>, hi: Option<i128>) -> Extent {
220 let clamp = |n: i128| n.clamp(i128::from(i64::MIN + 1), i128::from(i64::MAX - 1)) as i64;
221 let (start, end) = (lo.map_or(i64::MIN, clamp), hi.map_or(i64::MAX, clamp));
222 match start < end {
223 true => Extent::new(start, end),
224 false => Extent::NOWHERE,
225 }
226}
227
228#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
231pub struct Grid {
232 pub rate: u32,
233 pub a: i128,
234 pub d: i128,
235}
236
237impl Grid {
238 pub const fn of(rate: u32) -> Grid {
239 Grid { rate, a: 1, d: 1 }
240 }
241
242 pub fn is_rate(&self) -> bool {
243 (self.a, self.d) == (1, 1)
244 }
245
246 fn exact(&self, n: i64) -> Option<(i128, i128)> {
247 let num = self.a.checked_mul(i128::from(n))?;
248 Some((num, self.d.checked_mul(i128::from(self.rate))?))
249 }
250
251 pub fn instant(&self, n: i64) -> f64 {
254 if self.is_rate() {
255 return n as f64 / f64::from(self.rate);
256 }
257 let num = self.a.saturating_mul(i128::from(n));
258 num as f64 / self.d.saturating_mul(i128::from(self.rate)) as f64
259 }
260
261 pub fn stepped(&self, n: i64) -> f64 {
262 self.position(n) * (1.0 / f64::from(self.rate))
263 }
264
265 pub fn position(&self, n: i64) -> f64 {
266 self.a.saturating_mul(i128::from(n)) as f64 / self.d as f64
267 }
268
269 pub fn sr(&self) -> f64 {
270 f64::from(self.rate) * self.d as f64 / self.a as f64
271 }
272
273 pub fn count(&self, t: f64) -> f64 {
274 t * f64::from(self.rate) * self.d as f64 / self.a as f64
275 }
276
277 pub fn step_at(&self, t: f64, round: Round) -> Option<i64> {
280 if !t.is_finite() {
281 return None;
282 }
283 let bits = t.abs().to_bits();
284 let (exp, frac) = ((bits >> 52) as i32, (bits & ((1 << 52) - 1)) as i128);
285 let (mantissa, shift) = match exp {
286 0 => (frac, 1074),
287 e => (frac | (1 << 52), 1075 - e),
288 };
289 let zeros = mantissa.trailing_zeros().min(127) as i32;
290 let (mantissa, shift) = match mantissa {
291 0 => (0, 0),
292 m => (m >> zeros, shift - zeros),
293 };
294 let mantissa = if t < 0.0 { -mantissa } else { mantissa };
295 let (num, den) = match shift {
296 s if s <= 0 => (mantissa.checked_mul(1i128.checked_shl((-s) as u32)?)?, 1),
297 s if s < 127 => (mantissa, 1i128 << s),
298 _ => return None,
299 };
300 let top = num.checked_mul(self.d.checked_mul(i128::from(self.rate))?)?;
301 let bottom = self.a.checked_mul(den)?;
302 let (floor, rem) = (top.div_euclid(bottom), top.rem_euclid(bottom));
303 let k = match round {
304 Round::Floor => floor,
305 Round::Ceil => floor + i128::from(rem != 0),
306 Round::Even => match (2 * rem).cmp(&bottom) {
307 std::cmp::Ordering::Less => floor,
308 std::cmp::Ordering::Greater => floor + 1,
309 std::cmp::Ordering::Equal => floor + floor.rem_euclid(2),
310 },
311 };
312 i64::try_from(k).ok()
313 }
314}
315
316#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
321pub struct Wrap {
322 pub scale: (i128, i128),
323 pub shift: (i128, i128),
324 pub gain: (i128, i128),
325 pub inner: [(i128, i128); 2],
326 pub period: (i128, i128),
327}
328
329impl Wrap {
330 pub fn on(self, grid: Grid) -> Option<Stepped> {
332 let (_, rate) = grid.exact(0)?;
333 let [(s, sd), (o, od)] = self.inner;
334 let over = sd.checked_mul(rate)?;
335 let den = lcm(lcm(over, od)?, self.period.1)?;
336 let line = self.scale.1.checked_mul(rate)?;
337 let wrapped = self.gain.1.checked_mul(den)?;
338 let whole = lcm(lcm(line, self.shift.1)?, wrapped)?;
339 Some(Stepped {
340 step: grid.a,
341 inner: s,
342 over: den / over,
343 offset: o.checked_mul(den / od)?,
344 period: self.period.0.checked_mul(den / self.period.1)?,
345 scale: self.scale.0,
346 line: whole / line,
347 shift: self.shift.0.checked_mul(whole / self.shift.1)?,
348 gain: self.gain.0,
349 wrapped: whole / wrapped,
350 whole,
351 })
352 }
353
354 pub fn most(self, t: f64) -> f64 {
356 let q = |(num, den): (i128, i128)| num as f64 / den as f64;
357 q(self.scale).abs() * t.abs() + q(self.shift).abs() + q(self.gain).abs() * q(self.period)
358 }
359}
360
361#[derive(Clone, Copy, Debug, PartialEq, Eq)]
362pub struct Stepped {
363 step: i128,
364 inner: i128,
365 over: i128,
366 offset: i128,
367 period: i128,
368 scale: i128,
369 line: i128,
370 shift: i128,
371 gain: i128,
372 wrapped: i128,
373 whole: i128,
374}
375
376impl Stepped {
377 pub fn at(self, n: i64) -> Option<f64> {
379 let n = self.step.checked_mul(i128::from(n))?;
380 let x = self
381 .inner
382 .checked_mul(n)?
383 .checked_mul(self.over)?
384 .checked_add(self.offset)?;
385 let rem = x.rem_euclid(self.period);
386 let sum = self
387 .scale
388 .checked_mul(n)?
389 .checked_mul(self.line)?
390 .checked_add(self.shift)?
391 .checked_add(self.gain.checked_mul(rem)?.checked_mul(self.wrapped)?)?;
392 Some(sum as f64 / self.whole as f64)
393 }
394}
395
396fn lcm(a: i128, b: i128) -> Option<i128> {
397 (a / gcd(a, b)).checked_mul(b)
398}
399
400#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
401pub enum Round {
402 Even,
403 Floor,
404 Ceil,
405}
406
407#[derive(Clone, Debug, PartialEq)]
409pub enum Index<T = Box<NodeRenderer>> {
410 At(Map),
411 Step(T, Round),
412 Add(Vec<Index<T>>),
413 Neg(Box<Index<T>>),
414 Mul(Vec<Index<T>>),
415}
416
417impl<T> Index<T> {
418 pub fn times(&self) -> Vec<&T> {
419 let mut out = Vec::new();
420 self.each(&mut |t| out.push(t));
421 out
422 }
423
424 fn each<'a>(&'a self, f: &mut impl FnMut(&'a T)) {
425 match self {
426 Index::At(_) => {}
427 Index::Step(t, _) => f(t),
428 Index::Add(parts) | Index::Mul(parts) => parts.iter().for_each(|p| p.each(f)),
429 Index::Neg(p) => p.each(f),
430 }
431 }
432
433 pub fn mapped<U, E>(&self, f: &mut impl FnMut(&T) -> Result<U, E>) -> Result<Index<U>, E> {
434 let mut each = |parts: &[Index<T>]| -> Result<Vec<Index<U>>, E> {
435 parts.iter().map(|p| p.mapped(f)).collect()
436 };
437 Ok(match self {
438 Index::At(map) => Index::At(*map),
439 Index::Step(t, round) => Index::Step(f(t)?, *round),
440 Index::Add(parts) => Index::Add(each(parts)?),
441 Index::Mul(parts) => Index::Mul(each(parts)?),
442 Index::Neg(p) => Index::Neg(Box::new(p.mapped(f)?)),
443 })
444 }
445
446 pub fn at(&self, n: i64, grid: Grid, time: &impl Fn(&T) -> f64) -> Option<i64> {
447 match self {
448 Index::At(map) => map
449 .whole_at(n)
450 .or_else(|| i64::try_from(map.index_at(i128::from(n))).ok()),
451 Index::Step(t, round) => grid.step_at(time(t), *round),
452 Index::Add(parts) => parts
453 .iter()
454 .try_fold(0i64, |held, p| held.checked_add(p.at(n, grid, time)?)),
455 Index::Mul(parts) => parts
456 .iter()
457 .try_fold(1i64, |held, p| held.checked_mul(p.at(n, grid, time)?)),
458 Index::Neg(p) => p.at(n, grid, time)?.checked_neg(),
459 }
460 }
461}
462
463#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
465pub enum Slot {
466 Read(BufId),
467 Own,
468}
469
470#[derive(Clone, Copy, Debug, PartialEq, Eq)]
471pub enum Unary {
472 Sin,
473 Cos,
474 Exp,
475 Sqrt,
476 Abs,
477 Tanh,
478 Log,
479 Sat,
480 Step,
481}
482
483#[derive(Clone, Copy, Debug, PartialEq, Eq)]
484pub enum Binary {
485 Max,
486 Min,
487 Mod,
488}
489
490#[derive(Clone, Debug, PartialEq)]
493pub enum Formula {
494 Sum(Box<SpectralSum>),
495 Written(Box<Body>),
496 Drawn { seed: u64, rate: u32 },
497}
498
499impl Formula {
500 pub fn at(&self, component: usize, t: f64) -> Result<f64, CollapseError> {
501 let value: C64 =
502 match self {
503 Formula::Drawn { seed, rate } => {
504 let step = Grid::of(*rate).step_at(t, Round::Even).ok_or(
505 CollapseError::NotEvaluable("a draw at an instant past any step"),
506 )?;
507 return Ok(sva_formula::draw(*seed, step));
508 }
509 Formula::Sum(sum) => crate::collapse::eval_spectral_sum_at(sum, component, t)?,
510 Formula::Written(body) => {
511 crate::collapse::eval_written_at(body, component, t, &crate::collapse::NoRefs)?
512 }
513 };
514 Ok(value.re)
515 }
516
517 pub fn ops(&self) -> usize {
519 fn terms(body: &Body) -> usize {
520 1 + sva_formula::closed_form::children(body)
521 .iter()
522 .map(|p| terms(&p.body))
523 .sum::<usize>()
524 }
525 match self {
526 Formula::Sum(sum) => sum.atoms().count().max(1),
527 Formula::Written(body) => terms(body),
528 Formula::Drawn { .. } => 1,
529 }
530 }
531}
532
533#[derive(Clone, Debug, PartialEq)]
536pub enum NodeRenderer {
537 Const(f64),
538 Time,
539 Wrap(Wrap),
540 Read {
541 slot: Slot,
542 map: Map,
543 },
544 Formula {
546 formula: Formula,
547 width: usize,
548 time: Box<NodeRenderer>,
549 },
550 Noise(u64),
551 Indexed {
554 slot: Slot,
555 index: Index,
556 reach: Option<(i64, i64)>,
557 },
558 Instant(Index),
560 Add(Vec<NodeRenderer>),
561 Mul(Vec<NodeRenderer>),
562 Sub(Box<NodeRenderer>, Box<NodeRenderer>),
563 Div(Box<NodeRenderer>, Box<NodeRenderer>),
564 Pow(Box<NodeRenderer>, Box<NodeRenderer>),
565 Map(Unary, Box<NodeRenderer>),
566 Zip(Binary, Box<NodeRenderer>, Box<NodeRenderer>),
567 Crop {
570 x: Box<NodeRenderer>,
571 window: (i64, i64),
572 a: f64,
573 b: f64,
574 rise: f64,
575 fall: f64,
576 },
577 Join(Vec<NodeRenderer>),
578 Channel {
579 x: Box<NodeRenderer>,
580 k: usize,
581 },
582 Filter {
584 site: SiteId,
585 from: i64,
586 x: Box<NodeRenderer>,
587 cutoff: Box<NodeRenderer>,
588 q: Box<NodeRenderer>,
589 gain: Box<NodeRenderer>,
590 },
591 Physics {
592 site: SiteId,
593 from: i64,
594 args: Vec<NodeRenderer>,
595 },
596}
597
598impl NodeRenderer {
599 pub fn stateless(&self) -> bool {
602 !self.holds_state() && self.operands().into_iter().all(NodeRenderer::stateless)
603 }
604
605 pub(crate) fn holds_state(&self) -> bool {
606 matches!(
607 self,
608 NodeRenderer::Filter { .. }
609 | NodeRenderer::Physics { .. }
610 | NodeRenderer::Read {
611 slot: Slot::Own,
612 ..
613 }
614 | NodeRenderer::Indexed {
615 slot: Slot::Own,
616 ..
617 }
618 )
619 }
620
621 pub(crate) fn operands(&self) -> Vec<&NodeRenderer> {
623 match self {
624 NodeRenderer::Add(set) | NodeRenderer::Mul(set) | NodeRenderer::Join(set) => {
625 set.iter().collect()
626 }
627 NodeRenderer::Sub(a, b)
628 | NodeRenderer::Div(a, b)
629 | NodeRenderer::Pow(a, b)
630 | NodeRenderer::Zip(_, a, b) => vec![a, b],
631 NodeRenderer::Map(_, x)
632 | NodeRenderer::Crop { x, .. }
633 | NodeRenderer::Channel { x, .. } => vec![x],
634 NodeRenderer::Filter {
635 x, cutoff, q, gain, ..
636 } => vec![x, cutoff, q, gain],
637 NodeRenderer::Physics { args, .. } => args.iter().collect(),
638 NodeRenderer::Formula { time, .. } => vec![time],
639 NodeRenderer::Indexed { index, .. } | NodeRenderer::Instant(index) => {
640 index.times().into_iter().map(|t| &**t).collect()
641 }
642 NodeRenderer::Const(_)
643 | NodeRenderer::Time
644 | NodeRenderer::Wrap(_)
645 | NodeRenderer::Noise(_)
646 | NodeRenderer::Read { .. } => Vec::new(),
647 }
648 }
649}
650
651#[derive(Clone, Debug, PartialEq)]
652pub enum Site {
653 Filter(Shape),
654 Physics(Box<Params>),
655}
656
657impl From<sva_formula::Unary> for Unary {
658 fn from(written: sva_formula::Unary) -> Unary {
659 match written {
660 sva_formula::Unary::Sin => Unary::Sin,
661 sva_formula::Unary::Cos => Unary::Cos,
662 sva_formula::Unary::Exp => Unary::Exp,
663 sva_formula::Unary::Sqrt => Unary::Sqrt,
664 sva_formula::Unary::Abs => Unary::Abs,
665 sva_formula::Unary::Tanh => Unary::Tanh,
666 sva_formula::Unary::Log => Unary::Log,
667 sva_formula::Unary::Sat => Unary::Sat,
668 sva_formula::Unary::Step => Unary::Step,
669 }
670 }
671}
672
673impl Unary {
674 pub fn apply(self, x: f64) -> f64 {
675 match self {
676 Unary::Sin => x.sin(),
677 Unary::Cos => x.cos(),
678 Unary::Exp => x.exp(),
679 Unary::Sqrt => x.sqrt(),
680 Unary::Abs => x.abs(),
681 Unary::Tanh => x.tanh(),
682 Unary::Log => x.ln(),
683 Unary::Sat => x.clamp(-1.0, 1.0),
684 Unary::Step => sva_formula::affine::step(x),
685 }
686 }
687}
688
689impl Binary {
690 pub fn apply(self, a: f64, b: f64) -> f64 {
691 match self {
692 Binary::Max => a.max(b),
693 Binary::Min => a.min(b),
694 Binary::Mod => a.rem_euclid(b),
695 }
696 }
697}