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 moved(self) -> Option<i64> {
90 (self.a == 1 && self.d == 1).then(|| i64::try_from(self.b).ok())?
91 }
92
93 pub fn at(self, n: i64) -> i64 {
94 let clamp = |k: i128| k.clamp(i128::from(i64::MIN), i128::from(i64::MAX)) as i64;
95 self.narrow_at(n)
96 .unwrap_or_else(|| clamp(self.index_at(i128::from(n))))
97 }
98
99 fn narrow_at(self, n: i64) -> Option<i64> {
101 if let Some(k) = self.whole_at(n) {
102 return Some(k);
103 }
104 let [a, b, d] = [self.a, self.b, self.d].map(|v| i64::try_from(v).ok());
105 let num = a?.checked_mul(n)?.checked_add(b?)?;
106 let d = d?;
107 let (floor, rem) = (num.div_euclid(d), num.rem_euclid(d));
108 Some(match self.between {
109 Between::Exact | Between::Floor => floor,
110 Between::Even => match rem.cmp(&(d - rem)) {
111 std::cmp::Ordering::Less => floor,
112 std::cmp::Ordering::Greater => floor + 1,
113 std::cmp::Ordering::Equal => floor + floor.rem_euclid(2),
114 },
115 })
116 }
117
118 fn whole_at(self, n: i64) -> Option<i64> {
119 if self.d != 1 {
120 return None;
121 }
122 let (a, b) = (i64::try_from(self.a).ok()?, i64::try_from(self.b).ok()?);
123 a.checked_mul(n)?.checked_add(b)
124 }
125
126 pub fn ahead(self) -> bool {
127 self.a != self.d || self.lead() > 0
128 }
129
130 pub fn lead(self) -> i64 {
131 let most = match self.between {
132 Between::Exact | Between::Floor => self.b.div_euclid(self.d),
133 Between::Even => (2 * self.b + self.d).div_euclid(2 * self.d),
134 };
135 most.clamp(i128::from(i64::MIN / 2), i128::from(i64::MAX / 2)) as i64
136 }
137
138 pub fn least(self) -> i64 {
139 let least = match self.between {
140 Between::Exact | Between::Floor => self.b.div_euclid(self.d),
141 Between::Even => (2 * self.b + self.d - 1).div_euclid(2 * self.d),
142 };
143 least.clamp(i128::from(i64::MIN / 2), i128::from(i64::MAX / 2)) as i64
144 }
145
146 pub fn image(self, over: Extent) -> Extent {
147 if over.is_empty() {
148 return over;
149 }
150 if self.a == 0 {
151 let at = self.at(0);
152 return Extent::new(at, at.saturating_add(1));
153 }
154 let ends = (first(over), last(over));
155 let at = |n: Option<i128>| n.map(|n| self.index_at(n));
156 let (lo, hi) = match self.a > 0 {
157 true => (at(ends.0), at(ends.1)),
158 false => (at(ends.1), at(ends.0)),
159 };
160 extent(lo, hi.map(|h| h + 1))
161 }
162
163 pub fn preimage(self, into: Extent) -> Extent {
166 if into.is_empty() {
167 return into;
168 }
169 if self.between == Between::Even {
170 let up = Map {
171 a: 2 * self.a,
172 b: 2 * self.b + self.d,
173 d: 2 * self.d,
174 between: Between::Floor,
175 };
176 let end = match into.end {
177 i64::MAX => i64::MAX,
178 e => e.saturating_add(1),
179 };
180 return up.preimage(Extent::new(into.start, end));
181 }
182 if self.a == 0 {
183 return match into.contains(self.at(0)) {
184 true => Extent::EVERYWHERE,
185 false => Extent::NOWHERE,
186 };
187 }
188 let (a, b, d) = (self.a, self.b, self.d);
189 let (first, last) = (first(into), last(into));
190 let lowest = |m: i128| ceil_div(m * d - b, a);
191 let highest = |m: i128| floor_div((m + 1) * d - 1 - b, a);
192 let (lo, hi) = match self.a > 0 {
193 true => (first.map(lowest), last.map(highest)),
194 false => (
195 last.map(|m| ceil_div((m + 1) * d - 1 - b, a)),
196 first.map(|m| floor_div(m * d - b, a)),
197 ),
198 };
199 extent(lo, hi.map(|h| h + 1))
200 }
201
202 fn index_at(self, n: i128) -> i128 {
203 #[cfg(test)]
204 WIDE.with(|w| w.set(w.get() + 1));
205 let num = self.a.saturating_mul(n).saturating_add(self.b);
206 let (floor, rem) = (num.div_euclid(self.d), num.rem_euclid(self.d));
207 match self.between {
208 Between::Exact | Between::Floor => floor,
209 Between::Even => match (2 * rem).cmp(&self.d) {
210 std::cmp::Ordering::Less => floor,
211 std::cmp::Ordering::Greater => floor + 1,
212 std::cmp::Ordering::Equal => floor + floor.rem_euclid(2),
213 },
214 }
215 }
216}
217
218fn gcd(a: i128, b: i128) -> i128 {
219 match b {
220 0 => a,
221 b => gcd(b, a % b),
222 }
223}
224
225fn first(e: Extent) -> Option<i128> {
226 (e.start != i64::MIN).then(|| i128::from(e.start))
227}
228
229fn last(e: Extent) -> Option<i128> {
230 (e.end != i64::MAX).then(|| i128::from(e.end) - 1)
231}
232
233fn floor_div(num: i128, den: i128) -> i128 {
234 match den < 0 {
235 true => (-num).div_euclid(-den),
236 false => num.div_euclid(den),
237 }
238}
239
240fn ceil_div(num: i128, den: i128) -> i128 {
241 -floor_div(-num, den)
242}
243
244fn extent(lo: Option<i128>, hi: Option<i128>) -> Extent {
245 let clamp = |n: i128| n.clamp(i128::from(i64::MIN + 1), i128::from(i64::MAX - 1)) as i64;
246 let (start, end) = (lo.map_or(i64::MIN, clamp), hi.map_or(i64::MAX, clamp));
247 match start < end {
248 true => Extent::new(start, end),
249 false => Extent::NOWHERE,
250 }
251}
252
253#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
256pub struct Grid {
257 pub rate: u32,
258 pub a: i128,
259 pub d: i128,
260}
261
262impl Grid {
263 pub const fn of(rate: u32) -> Grid {
264 Grid { rate, a: 1, d: 1 }
265 }
266
267 pub fn finer(rate: u32, scale: usize) -> Grid {
269 Grid {
270 rate,
271 a: 1,
272 d: scale as i128,
273 }
274 }
275
276 pub fn is_rate(&self) -> bool {
277 (self.a, self.d) == (1, 1)
278 }
279
280 fn exact(&self, n: i64) -> Option<(i128, i128)> {
281 let num = self.a.checked_mul(i128::from(n))?;
282 Some((num, self.d.checked_mul(i128::from(self.rate))?))
283 }
284
285 pub fn instant(&self, n: i64) -> f64 {
288 if self.is_rate() {
289 return n as f64 / f64::from(self.rate);
290 }
291 let num = self.a.saturating_mul(i128::from(n));
292 num as f64 / self.d.saturating_mul(i128::from(self.rate)) as f64
293 }
294
295 pub fn position(&self, n: i64) -> f64 {
296 self.a.saturating_mul(i128::from(n)) as f64 / self.d as f64
297 }
298
299 pub fn sr(&self) -> f64 {
300 f64::from(self.rate) * self.d as f64 / self.a as f64
301 }
302
303 pub fn count(&self, t: f64) -> f64 {
304 t * f64::from(self.rate) * self.d as f64 / self.a as f64
305 }
306
307 pub fn step_at(&self, t: f64, round: Round) -> Option<i64> {
310 match self.is_rate() {
311 true => rated(t, self.rate, round).or_else(|| self.wide(t, round)),
312 false => self.wide(t, round),
313 }
314 }
315
316 fn wide(&self, t: f64, round: Round) -> Option<i64> {
317 #[cfg(test)]
318 WIDE.with(|w| w.set(w.get() + 1));
319 if !t.is_finite() {
320 return None;
321 }
322 let bits = t.abs().to_bits();
323 let (exp, frac) = ((bits >> 52) as i32, (bits & ((1 << 52) - 1)) as i128);
324 let (mantissa, shift) = match exp {
325 0 => (frac, 1074),
326 e => (frac | (1 << 52), 1075 - e),
327 };
328 let zeros = mantissa.trailing_zeros().min(127) as i32;
329 let (mantissa, shift) = match mantissa {
330 0 => (0, 0),
331 m => (m >> zeros, shift - zeros),
332 };
333 let mantissa = if t < 0.0 { -mantissa } else { mantissa };
334 let (num, den) = match shift {
335 s if s <= 0 => (mantissa.checked_mul(1i128.checked_shl((-s) as u32)?)?, 1),
336 s if s < 127 => (mantissa, 1i128 << s),
337 _ => return None,
338 };
339 let top = num.checked_mul(self.d.checked_mul(i128::from(self.rate))?)?;
340 let bottom = self.a.checked_mul(den)?;
341 let (floor, rem) = (top.div_euclid(bottom), top.rem_euclid(bottom));
342 let k = match round {
343 Round::Floor => floor,
344 Round::Ceil => floor + i128::from(rem != 0),
345 Round::Even => match (2 * rem).cmp(&bottom) {
346 std::cmp::Ordering::Less => floor,
347 std::cmp::Ordering::Greater => floor + 1,
348 std::cmp::Ordering::Equal => floor + floor.rem_euclid(2),
349 },
350 };
351 i64::try_from(k).ok()
352 }
353}
354
355fn rated(t: f64, rate: u32, round: Round) -> Option<i64> {
359 let held = t == 0.0 || (2f64.powi(-74)..2f64.powi(51)).contains(&t.abs());
360 if !held || rate >= 1 << 26 {
361 return None;
362 }
363 let r = f64::from(rate);
364 let split = 134_217_729.0 * t;
365 let hi = split - (split - t);
366 let (x, y) = (hi * r, (t - hi) * r);
367 let p = x + y;
368 let back = p - x;
369 let e = (x - (p - back)) + (y - back);
370 if p.abs() >= 2f64.powi(51) {
371 return None;
372 }
373 let f = p.floor();
374 let k = match round {
375 Round::Floor if p == f && e < 0.0 => f - 1.0,
376 Round::Floor => f,
377 Round::Ceil if p == p.ceil() && e > 0.0 => p + 1.0,
378 Round::Ceil => p.ceil(),
379 Round::Even => match (p - f).total_cmp(&0.5) {
380 std::cmp::Ordering::Less => f,
381 std::cmp::Ordering::Greater => f + 1.0,
382 std::cmp::Ordering::Equal if e > 0.0 => f + 1.0,
383 std::cmp::Ordering::Equal if e < 0.0 => f,
384 std::cmp::Ordering::Equal => f + f.rem_euclid(2.0),
385 },
386 };
387 Some(k as i64)
388}
389
390#[cfg(test)]
391thread_local! {
392 pub(super) static WIDE: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
393}
394
395#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
400pub struct Wrap {
401 pub scale: (i128, i128),
402 pub shift: (i128, i128),
403 pub gain: (i128, i128),
404 pub inner: [(i128, i128); 2],
405 pub period: (i128, i128),
406}
407
408impl Wrap {
409 pub fn on(self, grid: Grid) -> Option<Stepped> {
411 let (_, rate) = grid.exact(0)?;
412 let [(s, sd), (o, od)] = self.inner;
413 let over = sd.checked_mul(rate)?;
414 let den = lcm(lcm(over, od)?, self.period.1)?;
415 let line = self.scale.1.checked_mul(rate)?;
416 let wrapped = self.gain.1.checked_mul(den)?;
417 let whole = lcm(lcm(line, self.shift.1)?, wrapped)?;
418 Some(Stepped {
419 step: grid.a,
420 inner: s,
421 over: den / over,
422 offset: o.checked_mul(den / od)?,
423 period: self.period.0.checked_mul(den / self.period.1)?,
424 scale: self.scale.0,
425 line: whole / line,
426 shift: self.shift.0.checked_mul(whole / self.shift.1)?,
427 gain: self.gain.0,
428 wrapped: whole / wrapped,
429 whole,
430 })
431 }
432
433 pub fn most(self, t: f64) -> f64 {
435 let q = |(num, den): (i128, i128)| num as f64 / den as f64;
436 q(self.scale).abs() * t.abs() + q(self.shift).abs() + q(self.gain).abs() * q(self.period)
437 }
438}
439
440#[derive(Clone, Copy, Debug, PartialEq, Eq)]
441pub struct Stepped {
442 step: i128,
443 inner: i128,
444 over: i128,
445 offset: i128,
446 period: i128,
447 scale: i128,
448 line: i128,
449 shift: i128,
450 gain: i128,
451 wrapped: i128,
452 whole: i128,
453}
454
455impl Stepped {
456 pub fn at(self, n: i64) -> Option<f64> {
458 let n = self.step.checked_mul(i128::from(n))?;
459 let x = self
460 .inner
461 .checked_mul(n)?
462 .checked_mul(self.over)?
463 .checked_add(self.offset)?;
464 let rem = x.rem_euclid(self.period);
465 let sum = self
466 .scale
467 .checked_mul(n)?
468 .checked_mul(self.line)?
469 .checked_add(self.shift)?
470 .checked_add(self.gain.checked_mul(rem)?.checked_mul(self.wrapped)?)?;
471 Some(sum as f64 / self.whole as f64)
472 }
473}
474
475fn lcm(a: i128, b: i128) -> Option<i128> {
476 (a / gcd(a, b)).checked_mul(b)
477}
478
479#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
480pub enum Round {
481 Even,
482 Floor,
483 Ceil,
484}
485
486#[derive(Clone, Debug, PartialEq)]
488pub enum Index<T = Box<NodeRenderer>> {
489 At(Map),
490 Step(T, Round),
491 Add(Vec<Index<T>>),
492 Neg(Box<Index<T>>),
493 Mul(Vec<Index<T>>),
494}
495
496impl<T> Index<T> {
497 pub fn times(&self) -> Vec<&T> {
498 let mut out = Vec::new();
499 self.each(&mut |t| out.push(t));
500 out
501 }
502
503 fn each<'a>(&'a self, f: &mut impl FnMut(&'a T)) {
504 match self {
505 Index::At(_) => {}
506 Index::Step(t, _) => f(t),
507 Index::Add(parts) | Index::Mul(parts) => parts.iter().for_each(|p| p.each(f)),
508 Index::Neg(p) => p.each(f),
509 }
510 }
511
512 pub fn mapped<U, E>(&self, f: &mut impl FnMut(&T) -> Result<U, E>) -> Result<Index<U>, E> {
513 let mut each = |parts: &[Index<T>]| -> Result<Vec<Index<U>>, E> {
514 parts.iter().map(|p| p.mapped(f)).collect()
515 };
516 Ok(match self {
517 Index::At(map) => Index::At(*map),
518 Index::Step(t, round) => Index::Step(f(t)?, *round),
519 Index::Add(parts) => Index::Add(each(parts)?),
520 Index::Mul(parts) => Index::Mul(each(parts)?),
521 Index::Neg(p) => Index::Neg(Box::new(p.mapped(f)?)),
522 })
523 }
524
525 pub fn at(&self, n: i64, grid: Grid, time: &impl Fn(&T) -> f64) -> Option<i64> {
526 match self {
527 Index::At(map) => map
528 .narrow_at(n)
529 .or_else(|| i64::try_from(map.index_at(i128::from(n))).ok()),
530 Index::Step(t, round) => grid.step_at(time(t), *round),
531 Index::Add(parts) => parts
532 .iter()
533 .try_fold(0i64, |held, p| held.checked_add(p.at(n, grid, time)?)),
534 Index::Mul(parts) => parts
535 .iter()
536 .try_fold(1i64, |held, p| held.checked_mul(p.at(n, grid, time)?)),
537 Index::Neg(p) => p.at(n, grid, time)?.checked_neg(),
538 }
539 }
540}
541
542#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
544pub enum Slot {
545 Read(BufId),
546 Own,
547}
548
549#[derive(Clone, Copy, Debug, PartialEq, Eq)]
550pub enum Unary {
551 Sin,
552 Cos,
553 Exp,
554 Sqrt,
555 Abs,
556 Tanh,
557 Log,
558 Sat,
559 Step,
560}
561
562#[derive(Clone, Copy, Debug, PartialEq, Eq)]
563pub enum Binary {
564 Max,
565 Min,
566 Mod,
567}
568
569#[derive(Clone, Debug, PartialEq)]
572pub enum Formula {
573 Sum(Box<SpectralSum>),
574 Written(Box<Written>),
575 Drawn { seed: u64, rate: u32 },
576}
577
578#[derive(Clone, Debug, PartialEq)]
580pub struct Written {
581 pub body: Body,
582 pub refs: Vec<Body>,
583}
584
585impl Formula {
586 pub fn at(&self, component: usize, t: f64) -> Result<f64, CollapseError> {
587 let value: C64 =
588 match self {
589 Formula::Drawn { seed, rate } => {
590 let step = Grid::of(*rate).step_at(t, Round::Even).ok_or(
591 CollapseError::NotEvaluable("a draw at an instant past any step"),
592 )?;
593 return Ok(sva_formula::draw(*seed, step));
594 }
595 Formula::Sum(sum) => crate::collapse::eval_spectral_sum_at(sum, component, t)?,
596 Formula::Written(written) => crate::collapse::eval_written_at(
597 &written.body,
598 component,
599 t,
600 &crate::collapse::Shared::new(&written.refs),
601 )?,
602 };
603 Ok(value.re)
604 }
605
606 pub fn ops(&self) -> usize {
608 fn terms(body: &Body, refs: &[usize]) -> usize {
609 match body {
610 Body::Node(id) => refs[id.0 as usize],
611 Body::Banded(b) => (b.widest.max(0) as usize)
612 .saturating_mul(terms(&b.series.term.body, refs))
613 .saturating_add(2),
614 _ => sva_formula::closed_form::children(body)
615 .iter()
616 .fold(1usize, |held, p| held.saturating_add(terms(&p.body, refs))),
617 }
618 }
619 match self {
620 Formula::Sum(sum) => sum.atoms().count().max(1),
621 Formula::Written(written) => {
622 let mut counted = Vec::with_capacity(written.refs.len());
623 for body in &written.refs {
624 counted.push(terms(body, &counted));
625 }
626 terms(&written.body, &counted)
627 }
628 Formula::Drawn { .. } => 1,
629 }
630 }
631}
632
633#[derive(Clone, Debug, PartialEq)]
636pub enum NodeRenderer {
637 Const(f64),
638 Time,
639 Wrap(Wrap),
640 Read {
641 slot: Slot,
642 map: Map,
643 },
644 Formula {
646 formula: Formula,
647 width: usize,
648 time: Box<NodeRenderer>,
649 },
650 Noise(u64),
651 Indexed {
654 slot: Slot,
655 index: Index,
656 reach: Option<(i64, i64)>,
657 },
658 Instant(Index),
660 Add(Vec<NodeRenderer>),
661 Mul(Vec<NodeRenderer>),
662 Sub(Box<NodeRenderer>, Box<NodeRenderer>),
663 Div(Box<NodeRenderer>, Box<NodeRenderer>),
664 Pow(Box<NodeRenderer>, Box<NodeRenderer>),
665 Map(Unary, Box<NodeRenderer>),
666 Zip(Binary, Box<NodeRenderer>, Box<NodeRenderer>),
667 Crop {
670 x: Box<NodeRenderer>,
671 window: (i64, i64),
672 a: f64,
673 b: f64,
674 rise: f64,
675 fall: f64,
676 },
677 Join(Vec<NodeRenderer>),
678 Channel {
679 x: Box<NodeRenderer>,
680 k: usize,
681 },
682 Filter {
684 site: SiteId,
685 from: i64,
686 x: Box<NodeRenderer>,
687 cutoff: Box<NodeRenderer>,
688 q: Box<NodeRenderer>,
689 gain: Box<NodeRenderer>,
690 },
691 Physics {
692 site: SiteId,
693 from: i64,
694 args: Vec<NodeRenderer>,
695 },
696}
697
698impl NodeRenderer {
699 pub fn stateless(&self) -> bool {
702 !self.holds_state() && self.operands().into_iter().all(NodeRenderer::stateless)
703 }
704
705 pub(crate) fn holds_state(&self) -> bool {
706 matches!(
707 self,
708 NodeRenderer::Filter { .. }
709 | NodeRenderer::Physics { .. }
710 | NodeRenderer::Read {
711 slot: Slot::Own,
712 ..
713 }
714 | NodeRenderer::Indexed {
715 slot: Slot::Own,
716 ..
717 }
718 )
719 }
720
721 pub(crate) fn operands(&self) -> Vec<&NodeRenderer> {
723 match self {
724 NodeRenderer::Add(set) | NodeRenderer::Mul(set) | NodeRenderer::Join(set) => {
725 set.iter().collect()
726 }
727 NodeRenderer::Sub(a, b)
728 | NodeRenderer::Div(a, b)
729 | NodeRenderer::Pow(a, b)
730 | NodeRenderer::Zip(_, a, b) => vec![a, b],
731 NodeRenderer::Map(_, x)
732 | NodeRenderer::Crop { x, .. }
733 | NodeRenderer::Channel { x, .. } => vec![x],
734 NodeRenderer::Filter {
735 x, cutoff, q, gain, ..
736 } => vec![x, cutoff, q, gain],
737 NodeRenderer::Physics { args, .. } => args.iter().collect(),
738 NodeRenderer::Formula { time, .. } => vec![time],
739 NodeRenderer::Indexed { index, .. } | NodeRenderer::Instant(index) => {
740 index.times().into_iter().map(|t| &**t).collect()
741 }
742 NodeRenderer::Const(_)
743 | NodeRenderer::Time
744 | NodeRenderer::Wrap(_)
745 | NodeRenderer::Noise(_)
746 | NodeRenderer::Read { .. } => Vec::new(),
747 }
748 }
749}
750
751#[derive(Clone, Debug, PartialEq)]
752pub enum Site {
753 Filter(Shape),
754 Physics(Box<Params>),
755}
756
757impl From<sva_formula::Unary> for Unary {
758 fn from(written: sva_formula::Unary) -> Unary {
759 match written {
760 sva_formula::Unary::Sin => Unary::Sin,
761 sva_formula::Unary::Cos => Unary::Cos,
762 sva_formula::Unary::Exp => Unary::Exp,
763 sva_formula::Unary::Sqrt => Unary::Sqrt,
764 sva_formula::Unary::Abs => Unary::Abs,
765 sva_formula::Unary::Tanh => Unary::Tanh,
766 sva_formula::Unary::Log => Unary::Log,
767 sva_formula::Unary::Sat => Unary::Sat,
768 sva_formula::Unary::Step => Unary::Step,
769 }
770 }
771}
772
773impl Unary {
774 pub fn apply(self, x: f64) -> f64 {
775 match self {
776 Unary::Sin => x.sin(),
777 Unary::Cos => x.cos(),
778 Unary::Exp => x.exp(),
779 Unary::Sqrt => x.sqrt(),
780 Unary::Abs => x.abs(),
781 Unary::Tanh => x.tanh(),
782 Unary::Log => x.ln(),
783 Unary::Sat => x.clamp(-1.0, 1.0),
784 Unary::Step => sva_formula::affine::step(x),
785 }
786 }
787}
788
789impl Binary {
790 pub fn apply(self, a: f64, b: f64) -> f64 {
791 match self {
792 Binary::Max => a.max(b),
793 Binary::Min => a.min(b),
794 Binary::Mod => a.rem_euclid(b),
795 }
796 }
797}
798
799#[cfg(test)]
800mod tests {
801 use super::{Grid, Map, Round, rated};
802
803 fn instants(rate: u32) -> Vec<f64> {
805 let r = f64::from(rate);
806 let mut out = vec![0.0, -0.0, 1e-300, -1e-300, 1e300, f64::MIN_POSITIVE];
807 let mut seed = 0x9e37_79b9_7f4a_7c15u64;
808 for k in -2_000i64..2_000 {
809 for half in [0.0, 0.5] {
810 let at = (k as f64 + half) / r;
811 let mut near = at;
812 for _ in 0..3 {
813 near = near.next_up();
814 out.push(near);
815 }
816 near = at;
817 for _ in 0..3 {
818 near = near.next_down();
819 out.push(near);
820 }
821 out.push(at);
822 out.push(at * 1e9);
823 }
824 seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
825 out.push(f64::from_bits(seed >> 2) * if seed & 1 == 0 { 1.0 } else { -1.0 });
826 }
827 out
828 }
829
830 #[test]
831 fn a_step_rounds_alike_in_doubles_and_in_wide_integers() {
832 for rate in [1, 8_000, 44_100, 48_000, 88_200, 96_000, 192_000] {
833 let grid = Grid::of(rate);
834 for t in instants(rate) {
835 for round in [Round::Floor, Round::Ceil, Round::Even] {
836 if let Some(k) = rated(t, rate, round) {
837 assert_eq!(Some(k), grid.wide(t, round), "{t:e} at {rate} {round:?}");
838 }
839 }
840 }
841 }
842 }
843
844 #[test]
845 fn a_rounded_map_reads_alike_narrow_and_wide() {
846 for (a, b, d) in [(1, 0, 2), (3, -7, 4), (-5, 11, 6), (2, 1, 4), (7, 3, 2)] {
847 for between in [super::Between::Floor, super::Between::Even] {
848 let Some(map) = Map::rounded(a, b, d, between) else {
849 continue;
850 };
851 for n in -50..50 {
852 let wide = i64::try_from(map.index_at(i128::from(n))).ok();
853 assert_eq!(map.narrow_at(n), wide, "{map:?} at {n}");
854 }
855 }
856 }
857 }
858}