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