Skip to main content

sva_formula/
hash.rs

1// Concern: content-addresses a SpectralSum under the table version, and draws a seed at a step | Non-concern: what a hash keys (sva-engine) | IO: (&SpectralSum) -> Hash
2
3use std::fmt;
4
5use crate::closed_form::{
6    Body, Bound, ClosedForm, Edge, Excitation, Fold, ModalBank, Mode, Part, Rational, Series,
7    Unary, Var,
8};
9use crate::complex::{C64, canonical};
10use crate::env::NodeId;
11use crate::lanes::Lanes;
12use crate::run::Mirror;
13use crate::spectral_sum::atom::{Singular, SpectralAtom};
14use crate::spectral_sum::{Lane, SpectralSum};
15use crate::table::TABLE_VERSION;
16
17/// Two independently primed FNV-1a lanes: serving one closed form's samples for another is silent
18/// corruption, so the width is set against birthday collisions rather than speed.
19#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
20pub struct Hash(pub u64, pub u64);
21
22impl fmt::Display for Hash {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        write!(f, "{:016x}{:016x}", self.0, self.1)
25    }
26}
27
28pub fn hash_spectral_sum(n: &SpectralSum) -> Hash {
29    hash_spectral_sum_under(n, TABLE_VERSION)
30}
31
32pub fn hash_closed_form(t: &ClosedForm) -> Hash {
33    hash_closed_form_under(t, TABLE_VERSION)
34}
35
36/// The version is the first field after the tag, so a table bump retires every entry keyed
37/// by one of these.
38pub fn hash_spectral_sum_under(n: &SpectralSum, table_version: u64) -> Hash {
39    let mut s = Sink::new(0x01, table_version);
40    s.var(n.var);
41    s.u64(n.lanes.len() as u64);
42    for lane in &n.lanes {
43        s.lane(lane);
44    }
45    s.finish()
46}
47
48pub fn hash_closed_form_under(t: &ClosedForm, table_version: u64) -> Hash {
49    let mut s = Sink::new(0x02, table_version);
50    s.var(t.var);
51    s.formula(&t.body);
52    s.finish()
53}
54
55/// A closed form that reads other nodes, each ref hashed as what `node` names it: the node's
56/// own content, never the number a graph gave it.
57pub fn hash_closed_form_with(t: &ClosedForm, node: &mut dyn FnMut(NodeId) -> Hash) -> Hash {
58    let mut s = Sink::new(0x04, TABLE_VERSION);
59    s.node = Some(node);
60    s.var(t.var);
61    s.formula(&t.body);
62    s.finish()
63}
64
65/// The unit-interval value one seed draws at one whole step, wherever the pair is written.
66pub fn draw(seed: u64, step: i64) -> f64 {
67    mix(mix(seed ^ DRAWN) ^ step as u64) as f64 / u64::MAX as f64
68}
69
70const DRAWN: u64 = 0x9e37_79b9_7f4a_7c15;
71
72/// The draw at the whole step nearest `key`, ties to even; `None` past any `i64` step.
73pub fn draw_nearest(seed: u64, key: f64) -> Option<f64> {
74    let step = key.round_ties_even();
75    let held = step >= i64::MIN as f64 && step < i64::MAX as f64;
76    held.then(|| draw(seed, step as i64))
77}
78
79struct Sink<'a> {
80    lanes: Lanes<0>,
81    node: Option<&'a mut dyn FnMut(NodeId) -> Hash>,
82}
83
84impl<'a> Sink<'a> {
85    fn new(tag: u8, table_version: u64) -> Sink<'a> {
86        let mut s = Sink {
87            lanes: Lanes::default(),
88            node: None,
89        };
90        s.byte(tag);
91        s.u64(table_version);
92        s
93    }
94
95    fn byte(&mut self, b: u8) {
96        self.lanes.word(u64::from(b));
97    }
98
99    fn u64(&mut self, v: u64) {
100        for b in v.to_le_bytes() {
101            self.byte(b);
102        }
103    }
104
105    fn i64(&mut self, v: i64) {
106        self.u64(v as u64);
107    }
108
109    fn f64(&mut self, v: f64) {
110        self.u64(canonical(v));
111    }
112
113    fn c64(&mut self, v: C64) {
114        let (re, im) = v.bits();
115        self.u64(re);
116        self.u64(im);
117    }
118
119    fn var(&mut self, v: Var) {
120        self.byte(match v {
121            Var::T => 0,
122            Var::F => 1,
123        });
124    }
125
126    fn edge(&mut self, e: Edge) {
127        match e {
128            Edge::NegInf => self.byte(0),
129            Edge::At(bits) => {
130                self.byte(1);
131                self.u64(bits);
132            }
133            Edge::PosInf => self.byte(2),
134        }
135    }
136
137    fn lane(&mut self, lane: &Lane) {
138        self.u64(lane.atoms.len() as u64);
139        for a in &lane.atoms {
140            self.atom(a);
141        }
142        self.u64(lane.series.len() as u64);
143        for s in &lane.series {
144            self.series(s);
145        }
146        self.u64(lane.modal.len() as u64);
147        for m in &lane.modal {
148            self.modal(m);
149        }
150    }
151
152    /// `Origin` is excluded: two spellings from different files must hash alike.
153    fn atom(&mut self, a: &SpectralAtom) {
154        self.c64(a.c);
155        self.u64(u64::from(a.poly.degree));
156        self.f64(a.poly.at);
157        match a.exp {
158            None => self.byte(0),
159            Some(e) => {
160                self.byte(1);
161                self.f64(e.sigma);
162                self.f64(e.omega);
163            }
164        }
165        match a.gauss {
166            None => self.byte(0),
167            Some(g) => {
168                self.byte(1);
169                self.f64(g.a);
170                self.f64(g.mu);
171            }
172        }
173        match a.ind {
174            None => self.byte(0),
175            Some(i) => {
176                self.byte(1);
177                self.edge(i.l);
178                self.edge(i.r);
179            }
180        }
181        match a.pole {
182            None => self.byte(0),
183            Some(p) => {
184                self.byte(1);
185                self.c64(p.at);
186                self.u64(u64::from(p.order));
187                self.byte(u8::from(p.pv));
188            }
189        }
190        match a.sing {
191            Singular::Regular => self.byte(0),
192            Singular::Delta { at, order } => {
193                self.byte(1);
194                self.f64(at);
195                self.u64(u64::from(order));
196            }
197        }
198    }
199
200    fn series(&mut self, s: &Series) {
201        self.u64(u64::from(s.index.0));
202        self.i64(s.lo);
203        match s.hi {
204            Bound::Finite(n) => {
205                self.byte(0);
206                self.i64(n);
207            }
208            Bound::Infinite => self.byte(1),
209        }
210        self.formula(&s.term.body);
211    }
212
213    fn amps(&mut self, amps: &[C64]) {
214        self.u64(amps.len() as u64);
215        for a in amps {
216            self.c64(*a);
217        }
218    }
219
220    fn modal(&mut self, m: &ModalBank) {
221        self.u64(m.modes.len() as u64);
222        for Mode {
223            omega,
224            tau,
225            amp,
226            phase,
227        } in &m.modes
228        {
229            self.f64(*omega);
230            self.f64(*tau);
231            self.f64(*amp);
232            self.f64(*phase);
233        }
234        match m.excite {
235            Excitation::HammerPulse { f0, t0, contact } => {
236                self.byte(0);
237                self.f64(f0);
238                self.f64(t0);
239                self.f64(contact);
240            }
241            Excitation::Impulse { t0 } => {
242                self.byte(1);
243                self.f64(t0);
244            }
245        }
246    }
247
248    fn parts(&mut self, parts: &[Part]) {
249        self.u64(parts.len() as u64);
250        for p in parts {
251            self.formula(&p.body);
252        }
253    }
254
255    fn rational(&mut self, r: &Rational) {
256        self.u64(r.zeros.len() as u64);
257        for z in &r.zeros {
258            self.c64(*z);
259        }
260        self.u64(r.poles.len() as u64);
261        for p in &r.poles {
262            self.c64(*p);
263        }
264        self.c64(r.gain);
265    }
266
267    fn formula(&mut self, f: &Body) {
268        match f {
269            Body::Const(c) => {
270                self.byte(0x10);
271                self.c64(*c);
272            }
273            Body::Line => self.byte(0x11),
274            Body::Index(i) => {
275                self.byte(0x12);
276                self.u64(u64::from(i.0));
277            }
278            Body::Param(p) => {
279                self.byte(0x13);
280                self.u64(u64::from(p.0));
281            }
282            Body::Node(n) => {
283                self.byte(0x14);
284                match self.node.as_mut().map(|named| named(*n)) {
285                    Some(held) => {
286                        self.u64(held.0);
287                        self.u64(held.1);
288                    }
289                    None => self.u64(u64::from(n.0)),
290                }
291            }
292            Body::Add(parts) => {
293                self.byte(0x15);
294                self.parts(parts);
295            }
296            Body::Mul(parts) => {
297                self.byte(0x16);
298                self.parts(parts);
299            }
300            Body::Div(a, b) => {
301                self.byte(0x17);
302                self.formula(&a.body);
303                self.formula(&b.body);
304            }
305            Body::Pow(base, n) => {
306                self.byte(0x18);
307                self.formula(&base.body);
308                self.i64(i64::from(*n));
309            }
310            Body::Apply(op, arg) => {
311                self.byte(0x19);
312                self.byte(unary_tag(*op));
313                self.formula(&arg.body);
314            }
315            Body::Fold(op, args) => {
316                self.byte(0x1a);
317                self.byte(match op {
318                    Fold::Max => 0,
319                    Fold::Min => 1,
320                    Fold::Mod => 2,
321                });
322                self.parts(args);
323            }
324            Body::Delta { at, order } => {
325                self.byte(0x1b);
326                self.formula(&at.body);
327                self.u64(u64::from(*order));
328            }
329            Body::Pv(at) => {
330                self.byte(0x1c);
331                self.formula(&at.body);
332            }
333            Body::Warp { at, of } => {
334                self.byte(0x27);
335                self.formula(&at.body);
336                self.formula(&of.body);
337            }
338            Body::Shift { by, of } => {
339                self.byte(0x1d);
340                self.f64(*by);
341                self.formula(&of.body);
342            }
343            Body::Deriv { order, of } => {
344                self.byte(0x1e);
345                self.u64(u64::from(*order));
346                self.formula(&of.body);
347            }
348            Body::Crop {
349                of,
350                l,
351                r,
352                rise,
353                fall,
354            } => {
355                self.byte(0x1f);
356                self.formula(&of.body);
357                self.edge(*l);
358                self.edge(*r);
359                self.f64(*rise);
360                self.f64(*fall);
361            }
362            Body::Join(parts) => {
363                self.byte(0x21);
364                self.parts(parts);
365            }
366            Body::Channel(of, k) => {
367                self.byte(0x22);
368                self.formula(&of.body);
369                self.byte(*k);
370            }
371            Body::Rational(r) => {
372                self.byte(0x23);
373                self.rational(r);
374            }
375            Body::Series(s) => {
376                self.byte(0x24);
377                self.series(s);
378            }
379            Body::Modal(m) => {
380                self.byte(0x25);
381                self.modal(m);
382            }
383            Body::Run(run) => {
384                self.byte(0x28);
385                self.f64(run.offset);
386                self.f64(run.step);
387                self.i64(run.first);
388                self.amps(&run.amps);
389                match &run.mirror {
390                    Mirror::None => self.byte(0),
391                    Mirror::Conjugate => self.byte(1),
392                    Mirror::Held(amps) => {
393                        self.byte(2);
394                        self.amps(amps);
395                    }
396                }
397            }
398            Body::Keyed { seed, of } => {
399                self.byte(0x26);
400                self.u64(*seed);
401                self.formula(&of.body);
402            }
403        }
404    }
405
406    /// One avalanche past the lanes, so a short input still fills both words.
407    fn finish(&self) -> Hash {
408        let Hash(a, b) = self.lanes.finish();
409        Hash(mix(a), mix(b))
410    }
411}
412
413fn unary_tag(op: Unary) -> u8 {
414    match op {
415        Unary::Sin => 0,
416        Unary::Cos => 1,
417        Unary::Exp => 2,
418        Unary::Tanh => 3,
419        Unary::Sat => 4,
420        Unary::Abs => 5,
421        Unary::Log => 6,
422        Unary::Sqrt => 7,
423        Unary::Step => 8,
424    }
425}
426
427fn mix(mut z: u64) -> u64 {
428    z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
429    z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
430    z ^ (z >> 31)
431}