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