Skip to main content

sva_formula/
hash.rs

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