polymur_hash/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(test), no_std)]
3#![forbid(unsafe_code)]
4
5extern crate alloc;
6
7use alloc::vec::Vec;
8use core::hash::Hasher;
9
10/// A fast, non-cryptographic hash function based on polynomial evaluation.
11///
12/// PolymurHash is a universal hash function that provides excellent performance
13/// and good distribution properties. It's particularly well-suited for hash tables
14/// and other non-cryptographic applications.
15///
16/// # Examples
17///
18/// Basic usage:
19/// ```
20/// use polymur_hash::PolymurHash;
21///
22/// let hasher = PolymurHash::new(0);
23/// let data = b"Hello, world!";
24/// let hash = hasher.hash(data);
25/// ```
26///
27/// Using with a custom seed:
28/// ```
29/// use polymur_hash::PolymurHash;
30///
31/// let seed = 0xDEADBEEFCAFEBABE_u64;
32/// let hasher = PolymurHash::from_u64_seed(seed);
33/// let hash = hasher.hash(b"Some data");
34/// ```
35///
36/// Hash with tweak for additional randomization:
37/// ```
38/// use polymur_hash::PolymurHash;
39///
40/// let hasher = PolymurHash::new(42);
41/// let tweak = 0x123456789ABCDEF0;
42/// let hash = hasher.hash_with_tweak(b"Data", tweak);
43/// ```
44#[derive(Clone, Debug)]
45pub struct PolymurHash {
46    k: u64,
47    k2: u64,
48    k7: u64,
49    s: u64,
50}
51
52impl PolymurHash {
53    /// Creates a new PolymurHash instance from a 128-bit seed.
54    ///
55    /// # Arguments
56    ///
57    /// * `seed` - A 128-bit seed value to initialize the hasher
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use polymur_hash::PolymurHash;
63    ///
64    /// let hasher = PolymurHash::new(0x123456789ABCDEF0123456789ABCDEF0);
65    /// ```
66    pub fn new(seed: u128) -> Self {
67        let k_seed = seed as u64;
68        let s_seed = (seed >> 64) as u64;
69        Self::from_u64x2_seed(k_seed, s_seed)
70    }
71
72    /// Creates a new PolymurHash instance from a 64-bit seed.
73    ///
74    /// This method expands the 64-bit seed into the required internal state.
75    ///
76    /// # Arguments
77    ///
78    /// * `seed` - A 64-bit seed value to initialize the hasher
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use polymur_hash::PolymurHash;
84    ///
85    /// let hasher = PolymurHash::from_u64_seed(0xDEADBEEF);
86    /// ```
87    pub fn from_u64_seed(seed: u64) -> Self {
88        let k_seed = Self::mix(seed.wrapping_add(POLYMUR_ARBITRARY3));
89        let s_seed = Self::mix(seed.wrapping_add(POLYMUR_ARBITRARY4));
90        Self::from_u64x2_seed(k_seed, s_seed)
91    }
92
93    /// Creates a new PolymurHash instance from two 64-bit seeds.
94    ///
95    /// This provides direct control over the key and state seeds.
96    ///
97    /// # Arguments
98    ///
99    /// * `k_seed` - Seed for the polynomial key generation
100    /// * `s_seed` - Seed for the final mixing state
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use polymur_hash::PolymurHash;
106    ///
107    /// let hasher = PolymurHash::from_u64x2_seed(0x12345678, 0x9ABCDEF0);
108    /// ```
109    pub fn from_u64x2_seed(mut k_seed: u64, s_seed: u64) -> Self {
110        let s = s_seed ^ POLYMUR_ARBITRARY1;
111        let mut pow37 = [0u64; 64];
112        pow37[0] = 37;
113        pow37[32] = 559096694736811184;
114        for i in 0..31 {
115            pow37[i + 1] = extrared611(red611(mul128(pow37[i], pow37[i])));
116            pow37[i + 33] = extrared611(red611(mul128(pow37[i + 32], pow37[i + 32])));
117        }
118
119        'retry: loop {
120            k_seed = k_seed.wrapping_add(POLYMUR_ARBITRARY2);
121            let mut e = (k_seed >> 3) | 1;
122            const PRIMES: [u64; 11] = [3, 5, 7, 11, 13, 31, 41, 61, 151, 331, 1321];
123            for &p in PRIMES.iter() {
124                if (e % p) == 0 {
125                    continue 'retry;
126                }
127            }
128            let (mut ka, mut kb): (u64, u64) = (1, 1);
129            let mut i: usize = 0;
130            while e > 0 {
131                if (e & 1) != 0 {
132                    ka = extrared611(red611(mul128(ka, pow37[i])));
133                }
134                if (e & 2) != 0 {
135                    kb = extrared611(red611(mul128(kb, pow37[i + 1])));
136                }
137                e >>= 2;
138                i += 2;
139            }
140            let k = extrared611(extrared611(red611(mul128(ka, kb))));
141            let k2 = extrared611(red611(mul128(k, k)));
142            let k3 = red611(mul128(k, k2));
143            let k4 = red611(mul128(k2, k2));
144            let k7 = extrared611(red611(mul128(k3, k4)));
145            if k7 < (1_u64 << 60) - (1_u64 << 56) {
146                return Self { k, k2, k7, s };
147            }
148        }
149    }
150
151    /// Computes the hash of the given data with an additional tweak value.
152    ///
153    /// The tweak allows for additional randomization without changing the key.
154    /// This is useful for applications that need multiple independent hash values
155    /// from the same key.
156    ///
157    /// # Arguments
158    ///
159    /// * `buf` - The data to hash
160    /// * `tweak` - An additional value to mix into the hash
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use polymur_hash::PolymurHash;
166    ///
167    /// let hasher = PolymurHash::new(0);
168    /// let data = b"Hello, world!";
169    /// let hash1 = hasher.hash_with_tweak(data, 1);
170    /// let hash2 = hasher.hash_with_tweak(data, 2);
171    /// assert_ne!(hash1, hash2); // Different tweaks produce different hashes
172    /// ```
173    pub fn hash_with_tweak(&self, buf: impl AsRef<[u8]>, tweak: u64) -> u64 {
174        let h = self.poly1611(buf, tweak);
175        Self::mix(h).wrapping_add(self.s)
176    }
177
178    /// Computes the hash of the given data.
179    ///
180    /// # Arguments
181    ///
182    /// * `buf` - The data to hash
183    ///
184    /// # Returns
185    ///
186    /// A 64-bit hash value
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use polymur_hash::PolymurHash;
192    ///
193    /// let hasher = PolymurHash::new(0);
194    /// let hash = hasher.hash(b"Hello, world!");
195    /// ```
196    pub fn hash(&self, buf: impl AsRef<[u8]>) -> u64 {
197        let h = self.poly1611(buf, 0);
198        Self::mix(h).wrapping_add(self.s)
199    }
200
201    fn poly1611(&self, buf: impl AsRef<[u8]>, tweak: u64) -> u64 {
202        let mut buf = buf.as_ref();
203        let mut m = [0u64; 7];
204        let mut poly_acc = tweak;
205        if buf.len() <= 7 {
206            m[0] = le_u64_0_8(buf);
207            return poly_acc.wrapping_add(red611(mul128(
208                self.k.wrapping_add(m[0]),
209                self.k2.wrapping_add(buf.len() as u64),
210            )));
211        }
212
213        let k3 = red611(mul128(self.k, self.k2));
214        let k4 = red611(mul128(self.k2, self.k2));
215        if buf.len() >= 50 {
216            let k5 = extrared611(red611(mul128(self.k, k4)));
217            let k6 = extrared611(red611(mul128(self.k2, k4)));
218            let k3 = extrared611(k3);
219            let k4 = extrared611(k4);
220            let mut h: u64 = 0;
221            loop {
222                for i in 0..7 {
223                    let mut tmp = [0u8; 8];
224                    tmp.copy_from_slice(&buf[7 * i..][..8]);
225                    m[i] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
226                }
227                let t0 = mul128(self.k + m[0], k6 + m[1]);
228                let t1 = mul128(self.k2 + m[2], k5 + m[3]);
229                let t2 = mul128(k3 + m[4], k4 + m[5]);
230                let t3 = mul128(h + m[6], self.k7);
231                let s = t0.wrapping_add(t1).wrapping_add(t2).wrapping_add(t3);
232                h = red611(s);
233                buf = &buf[49..];
234                if buf.len() < 50 {
235                    break;
236                }
237            }
238            let k14 = red611(mul128(self.k7, self.k7));
239            let hk14 = red611(mul128(extrared611(h), k14));
240            poly_acc += extrared611(hk14);
241        }
242
243        if buf.len() >= 8 {
244            let mut tmp = [0u8; 8];
245            tmp.copy_from_slice(&buf[..8]);
246            m[0] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
247            tmp.copy_from_slice(&buf[(buf.len() - 7) / 2..][0..8]);
248            m[1] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
249            tmp.copy_from_slice(&buf[buf.len() - 8..][0..8]);
250            m[2] = u64::from_le_bytes(tmp) >> 8;
251            let t0 = mul128(self.k2 + m[0], self.k7.wrapping_add(m[1]));
252            let t1 = mul128(self.k + m[2], k3.wrapping_add(buf.len() as _));
253            if buf.len() <= 21 {
254                return poly_acc + red611(t0.wrapping_add(t1));
255            }
256            tmp.copy_from_slice(&buf[7..][0..8]);
257            m[3] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
258            tmp.copy_from_slice(&buf[14..][0..8]);
259            m[4] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
260            tmp.copy_from_slice(&buf[buf.len() - 21..][0..8]);
261            m[5] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
262            tmp.copy_from_slice(&buf[buf.len() - 14..][0..8]);
263            m[6] = u64::from_le_bytes(tmp) & 0x00ffffffffffffff;
264            let t0r = red611(t0);
265            let t2 = mul128(self.k2 + m[3], self.k7 + m[4]);
266            let t3 = mul128(t0r + m[5], k4 + m[6]);
267            let s = t1.wrapping_add(t2).wrapping_add(t3);
268            return poly_acc.wrapping_add(red611(s));
269        }
270
271        m[0] = le_u64_0_8(buf);
272        poly_acc.wrapping_add(red611(mul128(
273            self.k.wrapping_add(m[0]),
274            self.k2.wrapping_add(buf.len() as _),
275        )))
276    }
277
278    #[inline(always)]
279    fn mix(mut x: u64) -> u64 {
280        x ^= x >> 32;
281        x = x.wrapping_mul(0xe9846af9b1a615d);
282        x ^= x >> 32;
283        x = x.wrapping_mul(0xe9846af9b1a615d);
284        x ^= x >> 28;
285        x
286    }
287}
288
289const POLYMUR_P611: u64 = 0x1fffffffffffffff;
290const POLYMUR_ARBITRARY1: u64 = 0x6a09e667f3bcc908;
291const POLYMUR_ARBITRARY2: u64 = 0xbb67ae8584caa73b;
292const POLYMUR_ARBITRARY3: u64 = 0x3c6ef372fe94f82b;
293const POLYMUR_ARBITRARY4: u64 = 0xa54ff53a5f1d36f1;
294
295/// A hasher that implements the core library's `Hasher` trait.
296///
297/// This allows PolymurHash to be used with HashMap and HashSet, even in no_std environments
298/// (requires `alloc` for the internal buffer).
299///
300/// # Examples
301///
302/// Using with HashMap:
303/// ```
304/// # #[cfg(feature = "std")] {
305/// use std::collections::HashMap;
306/// use std::hash::BuildHasherDefault;
307/// use polymur_hash::PolymurHasher;
308///
309/// type PolymurHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PolymurHasher>>;
310///
311/// let mut map: PolymurHashMap<String, i32> = PolymurHashMap::default();
312/// map.insert("hello".to_string(), 42);
313/// # }
314/// ```
315#[derive(Clone, Debug)]
316pub struct PolymurHasher {
317    hasher: PolymurHash,
318    buffer: Vec<u8>,
319}
320
321impl Default for PolymurHasher {
322    fn default() -> Self {
323        Self {
324            hasher: PolymurHash::new(0),
325            buffer: Vec::new(),
326        }
327    }
328}
329
330impl PolymurHasher {
331    /// Creates a new PolymurHasher with a specific seed.
332    pub fn with_seed(seed: u128) -> Self {
333        Self {
334            hasher: PolymurHash::new(seed),
335            buffer: Vec::new(),
336        }
337    }
338}
339
340impl Hasher for PolymurHasher {
341    fn finish(&self) -> u64 {
342        self.hasher.hash(&self.buffer)
343    }
344
345    fn write(&mut self, bytes: &[u8]) {
346        self.buffer.extend_from_slice(bytes);
347    }
348
349    fn write_u8(&mut self, i: u8) {
350        self.buffer.push(i);
351    }
352
353    fn write_u16(&mut self, i: u16) {
354        self.buffer.extend_from_slice(&i.to_le_bytes());
355    }
356
357    fn write_u32(&mut self, i: u32) {
358        self.buffer.extend_from_slice(&i.to_le_bytes());
359    }
360
361    fn write_u64(&mut self, i: u64) {
362        self.buffer.extend_from_slice(&i.to_le_bytes());
363    }
364
365    fn write_u128(&mut self, i: u128) {
366        self.buffer.extend_from_slice(&i.to_le_bytes());
367    }
368
369    fn write_usize(&mut self, i: usize) {
370        self.buffer.extend_from_slice(&i.to_le_bytes());
371    }
372
373    fn write_i8(&mut self, i: i8) {
374        self.write_u8(i as u8);
375    }
376
377    fn write_i16(&mut self, i: i16) {
378        self.write_u16(i as u16);
379    }
380
381    fn write_i32(&mut self, i: i32) {
382        self.write_u32(i as u32);
383    }
384
385    fn write_i64(&mut self, i: i64) {
386        self.write_u64(i as u64);
387    }
388
389    fn write_i128(&mut self, i: i128) {
390        self.write_u128(i as u128);
391    }
392
393    fn write_isize(&mut self, i: isize) {
394        self.write_usize(i as usize);
395    }
396}
397
398#[inline(always)]
399fn mul128(a: u64, b: u64) -> u128 {
400    (a as u128) * (b as u128)
401}
402
403#[inline(always)]
404fn red611(x: u128) -> u64 {
405    ((x as u64) & POLYMUR_P611) + ((x >> 61) as u64)
406}
407
408#[inline(always)]
409fn extrared611(x: u64) -> u64 {
410    (x & POLYMUR_P611) + (x >> 61)
411}
412
413#[inline(always)]
414fn le_u64_0_8(buf: &[u8]) -> u64 {
415    let len = buf.len();
416    if len < 4 {
417        if len == 0 {
418            return 0;
419        }
420        let mut v = buf[0] as u64;
421        v |= (buf[len / 2] as u64) << (8 * (len / 2));
422        v |= (buf[len - 1] as u64) << (8 * (len - 1));
423        return v;
424    }
425
426    let mut tmp = [0u8; 4];
427    tmp.copy_from_slice(&buf[0..4]);
428    let lo = u32::from_le_bytes(tmp) as u64;
429    tmp.copy_from_slice(&buf[len - 4..][..4]);
430    let hi = u32::from_le_bytes(tmp) as u64;
431
432    lo | (hi << (8 * (len - 4)))
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438
439    #[test]
440    fn test_edge_cases() {
441        let hasher = PolymurHash::new(0);
442
443        // Empty input
444        assert_eq!(hasher.hash(b""), hasher.hash([]));
445
446        // Single byte
447        assert_ne!(hasher.hash(b"a"), hasher.hash(b"b"));
448
449        // Same data with different seeds produces different hashes
450        let hasher2 = PolymurHash::new(0xDEADBEEFCAFEBABE);
451        assert_ne!(hasher.hash(b"test"), hasher2.hash(b"test"));
452
453        // Verify tweaks work correctly
454        let data = b"test data";
455        assert_ne!(
456            hasher.hash_with_tweak(data, 0),
457            hasher.hash_with_tweak(data, 1)
458        );
459        assert_eq!(hasher.hash(data), hasher.hash_with_tweak(data, 0));
460    }
461
462    #[test]
463    fn test_different_lengths() {
464        let hasher = PolymurHash::new(42);
465
466        // Test various lengths to hit different code paths
467        let lengths = [
468            0, 1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 31, 32, 48, 49, 50, 63, 64, 127, 128, 255, 256,
469            1023, 1024,
470        ];
471
472        for &len in &lengths {
473            let data = vec![0xABu8; len];
474            let hash = hasher.hash(&data);
475
476            // Verify that changing any byte changes the hash
477            if len > 0 {
478                let mut modified = data.clone();
479                modified[len / 2] = 0xCD;
480                assert_ne!(
481                    hash,
482                    hasher.hash(&modified),
483                    "Hash collision at length {}",
484                    len
485                );
486            }
487        }
488    }
489
490    #[test]
491    fn test_seed_variants() {
492        let data = b"test data for seed variants";
493
494        // Test that different seed creation methods with same values produce same results
495        let seed_high = 0x123456789ABCDEF0_u64;
496        let seed_low = 0x0123456789ABCDEF0_u64;
497        let seed_128 = ((seed_high as u128) << 64) | (seed_low as u128);
498        let h1 = PolymurHash::new(seed_128);
499        let h2 = PolymurHash::from_u64x2_seed(seed_low, seed_high);
500
501        // These should produce the same hash since they use the same seed values
502        assert_eq!(h1.hash(data), h2.hash(data));
503
504        // Different seeds should produce different hashes
505        let h3 = PolymurHash::from_u64_seed(0x123456789ABCDEF0);
506        assert_ne!(h1.hash(data), h3.hash(data));
507    }
508
509    #[test]
510    fn test_hasher_trait() {
511        use core::hash::{Hash, Hasher as _};
512
513        let mut hasher1 = PolymurHasher::default();
514        let mut hasher2 = PolymurHasher::default();
515
516        // Hash the same string using the Hash trait
517        "Hello, world!".hash(&mut hasher1);
518        "Hello, world!".hash(&mut hasher2);
519
520        assert_eq!(hasher1.finish(), hasher2.finish());
521
522        // Test integer hashing
523        let mut hasher3 = PolymurHasher::default();
524        let mut hasher4 = PolymurHasher::default();
525
526        42u32.hash(&mut hasher3);
527        hasher4.write_u32(42);
528
529        assert_eq!(hasher3.finish(), hasher4.finish());
530    }
531
532    #[test]
533    fn test_hashmap_integration() {
534        use core::hash::BuildHasherDefault;
535        use std::collections::HashMap;
536
537        type PolymurHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PolymurHasher>>;
538
539        let mut map: PolymurHashMap<String, i32> = PolymurHashMap::default();
540
541        map.insert("foo".to_string(), 42);
542        map.insert("bar".to_string(), 123);
543        map.insert("baz".to_string(), 456);
544
545        assert_eq!(map.get("foo"), Some(&42));
546        assert_eq!(map.get("bar"), Some(&123));
547        assert_eq!(map.get("baz"), Some(&456));
548        assert_eq!(map.get("qux"), None);
549    }
550
551    #[test]
552    fn test() {
553        let mut t = 0;
554
555        for i in 0..1000 {
556            let hasher = PolymurHash::new(i as u128 * 0x419a02900419a02900419a02900419);
557            let mut size = 1;
558            loop {
559                let mut m = vec![0u8; size];
560                m.iter_mut().for_each(|x| *x = i as u8);
561                let res = hasher.hash(&m);
562                t += res as u128;
563                debug_assert!(t != 0);
564                if size >= 65536 {
565                    break;
566                }
567                size *= 2;
568            }
569
570            let hasher = PolymurHash::from_u64_seed(i * 0x419a02900419a0);
571            let mut size = 1;
572            loop {
573                let mut m = vec![0u8; size];
574                m.iter_mut().for_each(|x| *x = i as u8);
575                let res = hasher.hash(&m);
576                t += res as u128;
577                debug_assert!(t != 0);
578                if size >= 65536 {
579                    break;
580                }
581                size *= 3;
582            }
583        }
584
585        assert_eq!(t, 0x38e01175b44e95e6e0f6);
586    }
587
588    #[test]
589    fn test_vectors() {
590        // Tests from https://github.com/orlp/polymur-hash/blob/a7cc6b00051b4b579d718a4f26428098580029ec/test.c#L7
591
592        const POLYMUR_TEST_STRINGS: [&str;100] = [
593    "",
594    "i",
595    "es",
596    "vca",
597    "bdxa",
598    "bbbmc",
599    "vn5719",
600    "lpvif62",
601    "1fcjgark",
602    "1jlz2nr6w",
603    "g4q6ebxvod",
604    "ehiybujo2n1",
605    "6u2990ulzi7m",
606    "c3xcb4ew8v678",
607    "bhcaqrm221pea1",
608    "oyl3iqxqr85eeve",
609    "b41kacwmnim8rup5",
610    "563ug64z3zdtlj438",
611    "3spvl57qfg4udw2l3s",
612    "297r1bqesqdhb3jd50g",
613    "kbc5btot9x1fqslddmha",
614    "r0vxw6kk8tc6pk0oxnr6m",
615    "wkgmmma9icgky3bnj5bjir",
616    "5eslfmq1w3i7wvd89ls7nvf",
617    "40ytv0ye8cq49no6ys1pdrot",
618    "p3mbto6bl36g3cx9sstyiugsd",
619    "m0ylpn0wh5krbebs0j5trzgveb",
620    "qsy8gpheo76vb8g0ivaojk1zgk4",
621    "dwqf8tpad4k3x69sah7pstrg8zxx",
622    "ls3zrsjf1o3cr5sjy7dzp98198i3y",
623    "xvhvx3wbzer9b7kr4jqg2ok9e3mv5d",
624    "yapzlwab361wvh0xf1rydn5ynqx8cz0",
625    "nj56v1p9dc7qdmcn2wksfg5kic1uegm2",
626    "hlebeoafjqtqxfwd9ge94z3ofk88c4a5x",
627    "6li8qyu0n8nwoggm4hqzqdamem5barzjyw",
628    "wj7sp7dhpfapsd8w2nzn8s7xtnro9g45x7t",
629    "ahio6so1x30oziw54ux5iojjdfvkwpw2v14d",
630    "wm6yacnl6k3kj3c6i1jeajuwmquv9yujms0wq",
631    "kzs6xfhmc4ifmstnekcze4y1l83ddvxust2r0o",
632    "ckamexupx7cmsuza9nssw6n45e7go4s3osr1903",
633    "nob5bj9tok346dg62jbfjfrhg5l6itsno2hkhfru",
634    "vgo0ko42n5jvrvnv3ddpwg8h7gkqoxbllv2fdy0no",
635    "dgs47djqzq3czo0i0v1u3d3x72vtvi3w2tsf9shx6k",
636    "8vjrw7jz90kf969txb5qrh0u5332zf5epsp8aes4aqh",
637    "3ni9vtqiq6vnxipfa2wag8vfwq2nyce1kgq5nj3razx9",
638    "u29xjkod6rtu5j5tlwkydt9khih6o2do84q6ukwlr00xf",
639    "yxxubvyxuusw827qctqr6tmm69rij5ex2zk1etps8qh61e",
640    "p7lh4mvadnp6uw0vt7bnzcbv1wjswuuc6gjmu684yznx8lp",
641    "8c27lotvnab6ra8pq9aon0w30ydyulesinew3akqrhhmm39e",
642    "ttipbm97gpk7tiog1doncalwgpb7alk16dapga2ekzjt59pv6",
643    "mbbtplseab2mgtgh8uwlhbmdrwxae3tc2mtf98bwuhmz4bfjnf",
644    "shnjeydnj8awrkz3rd69wqqd9srie4eo6gc6ylhz2ouv4t4qbar",
645    "lckl12agnpr6q5053h9v38lyk71emkvwdzrv0ic3a4a4pn3w3o4x",
646    "7927wqjo5jiecfk0bbtt6065j5jl7x0vv1mcxxxl0j1oatrom44zp",
647    "bajk3ff026vx0u7o5d7ry7w7n07sqdy4urv4psr79jp13e0mxsks1r",
648    "en6j5o90gmgj7ssbz6jv3kzdsbzczu518c3zmezkp02rtvo1s88n9pu",
649    "58fkwyf44tjnrytgplb5qfbvlwtav3zutxowoor2mklkr2up4nzpefos",
650    "cep02qfl6swv1j3mwy5kprm4p8drszchufrkyr5ejbtzgu5cti6fqab5c",
651    "lr5q0p1dljga8h4vruy1doa79hntwbdyolnh1fbe3phfk7f5rgs4815foj",
652    "hmnjq6h1sslivjzmbxbpqba29f6kvbea6n6c4sanm40nzmrxt8hm61ooq3e",
653    "ae43xxu1mqrbynmctit7m4wf02o0kf2vvw1l3y51n4cu5v5ba4dia67wf0bo",
654    "qz9ye2ur849obmm23d5tnfc3xdaeajil0gm2pz8z9psedj50h5hcwbcn8n2lo",
655    "w3xar1pzaff7fhyw6cshdgechm2pj1ebwrbkdct5xfbmxskr3937dodvky62i8",
656    "ypy5k197quc9ypqoj9kle2eky307jnnd7tu52hqhn6mo7jj1fvmi42kkgq40iy6",
657    "k1bp6qwiul8fnd6rfe42ge6gskk0jkr9fjgmuujey3kn8ie88h9qguw2gboo7i80",
658    "begb64jkzfujx7ch3ain1iixidnbhcbcglcuf7nys8eansnkewtiye9xv7s2ksuev",
659    "vf5d8vdjtwp5vo1ocb274nkl6h8vg97m4v5htfwv02tj9u68vdnteeim6q0zllxflj",
660    "dcg9osulcdw9sqaue4cfz6k990vpstoxmvwbxzhzichkhdujy36v556u7oxug51gdup",
661    "1rtgdtibcaos4ebzrbl1fkjahtbel6fyqipuu8lxfrwnggjr8wgoscfxp46wv9wjk315",
662    "r27qj342zj4anpkqpr9yqo7udnldwiqqpq667zzjgw33yia3wt2p6t221onq4pvfaywbj",
663    "2yzxskad06pt9zvjmiobfz12a3q6wqgpj4450rpxj0jvjk3cx39qo6cbpukxqsy6idqd40",
664    "813zultj26k3gn6gibolpuozgaxu8exfatf4iqqugelcf6k8dnzvsjb9s25g3gyess2uscc",
665    "i4p0jkxf3ajc02x330y3tg8l521fzootabn53ovru20ph3n17hfygaz1axs61jxipz6jac5z",
666    "5bk748kkvww7toeyeueukk2qyin2o5ohnvj7l1cqs9zgy92n6ujxg6sxdjw81hfd29nzrb4kh",
667    "uvhy62avo1wqms1rrtefth84xhnv1a59aez6r4xq0pla74036o3vznihxexwydnfjojmk6ipl6",
668    "0t0dlfopg27cqv1xp4qfgwdlivvgqz204hkh5ianbb4abgk0yjolcwhhitrcksha5s6otmps0hd",
669    "vrbhcwrmn5xbq8f518ntvmaeg89n7nh1uxebfsmd7smoog3k2w12zv0px32pf4b78er5f3pgy7b9",
670    "x5bmnefocbtxm8avt22ekuy5hcdyxh86is5fnns9ycfm7o25x9frwv9kfv2ohyd3txlc8zlg5rjjx",
671    "ttfrgnfvvj552vjymrqqd1yjlyff7vkffprnvu3co4vuah8y0s56tziih3yowm64ja810gb1sgk0um",
672    "a66t43i9vrr3cmg5qf52akuk8bxl4rm3i86rm7h5brjou9k2egrzy3h19hh8kqr2queyvrwb673qikj",
673    "mfuwhbvd88n21obpmwx273mmeqiz98qfmb04z0ute54kc1d9bbdyfbx2sc4em6t4pfektm05qs7bgc9z",
674    "x8wbm0kjpyua8wpgsejgxc06geitm1c0bxihvcwnxnif63dj7cygzk7led0z49ol6zf2xwcmf99n4osip",
675    "fvba43myr0ozab882crozdz0zx4lfl2h7xe2phfqte97g58fake2fzi87mpftz9qdmt45gm79xl43k1hji",
676    "wnr0pz08rm3j65b7pl116l59pxy6prnydf9xod1qdi3hp3lod2vuzy1v7gt2g72sejaomn5u53daxjrr9xk",
677    "bwo7nfqda6w56voyvg1nr7vkq61zi7gy0aggn6pic3gup7uy18zzsc7y5yz3ptvp5cd53i95dj521k4n6n7t",
678    "mromebynw459uydhhgcgrate6hnst5srng9knfjc02vtg1vywok3rdbw935pf1qwghnh0nibyb60l9elkmajg",
679    "59dcjawsd4kjjcceco3hphizua88l0qtrfd000iam3rnb4tmy6kzf5bhkc9ud1hsg3dd53tlsxarcl0n59081h",
680    "odgdgfkwcpz0zjcwsz9is5h4nhebzht7fqa1b4g8e2snb6bn5hu3ixyd2pk1ey5g3eab0m3aoknfi9ctkpxz07j",
681    "0ljqm7r10ns2pjo8x69oi0zuqss9y7301yd6rmex8djwrbqmvh2mbwscgj9pmrgul5ao0tvpefpe5a9cac5xbdwb",
682    "b449ak3ihp8tdrbteffru5vboeh1z63c55at3qz70p13d2fim50q8i06zjyb53i4gqzunx6rsl07jxjd9g77me1ww",
683    "oqzf6c40snvrjz4v0f4h8p0ozjfy1y4xihxwaz16vbxf3qsa805xodw8z5xq3hb7dag8fnxtlsc62150kk253i3buj",
684    "2eicp9a5aq2uycq55y7rsixlg3pfk7gyin65fghf03kks18dixbckxmbv5xnhyrir7qm8maz4rk2bi3zs9chidlhehf",
685    "7k1wyjs6fxss4e0ywqfurgop6f7y7e97f3mr5hnb0hlhqkqbqvi1e1z3qfyxc3te75r67fc4h9li06rl9zadg3v9zmz6",
686    "k3e403zdtia8i0gpodm00yaujr1w474bh3985o3csbfjp3dll4t98i5lesloo6rqjec2aycb3ttx1t6lg0cl9hrjkgheb",
687    "2fv8zdl1ljmpjbvaan0nt99tra48yjmc5pv91n1c5l8qp5pv77zwsx75ouay7bmgy2tjc1aazyu5zj7oimesavv9n2h7ky",
688    "ghxs7uejpzpbxjsdmc2w9fabrg4j4pwwbn0wjxux2luk1k0ciror4gcvww18e610u2wpczuwrcphy2xr1129vweqhhgitge",
689    "vk7wfi9hhi0j9n2grs8rxgq68kw54dbdviuxnvtwgz77h0qkbzqw7pgm7zgn21cxlxnyzigeyz2rzrj3awloq86tqe60e070",
690    "d1aot9216s547uk1rg651iscb1bjpgth5j4f6arx1902npcykk8niz3ffpbed47idgzvt4u59fyi5e0e2afpjb5gjk4rysn8j",
691    "2jef2xl4o9yub0z6jnxu8gm87g9iv9zdtu9yolvxtensjrtgplnmnuhz43nsxztk8s936k6eruckkiwc5hnch4qdzft093986x",
692    "oo70ed77jci4bgodhnyf37axrx4f8gf8qs94f4l9xi9h0jkdl2ozoi2p7q7qu1945l21dzj6rhvqearzrmblfo3ljjldj0m9fue",
693];
694
695        const POLYMUR_REFERENCE_VALUES: [u64; 100] = [
696            0x1a6ef9f9d6c576fb,
697            0xd16d059771c65e13,
698            0x5ee4e0c09f562f87,
699            0x535b5311db007b0b,
700            0xd17124f14bd16b5d,
701            0xe84c87105c5b5cad,
702            0xb16ce684b89df9c0,
703            0x656525cace200667,
704            0x92b460794885d16d,
705            0xe6cc0fd9725b46b9,
706            0xc875ade1929bc93d,
707            0x68a2686ced37268a,
708            0x1d1809fd7e7e14ef,
709            0x699b8f31fc40c137,
710            0xd10dca2605654d2d,
711            0xd6bc75cb729f18d7,
712            0xfe0c617e7cb1bffe,
713            0xf5f14c731c1b9a22,
714            0x7a0382228d248631,
715            0x6c3a5f49d8a48bc0,
716            0x3606ebe637bb4ebc,
717            0xeb4854d75431ad1d,
718            0xfa8ff1a34793ebb0,
719            0x7e46ad8e2338cc38,
720            0xf8ff088ada3154b4,
721            0x706669bf0925914f,
722            0x70fc5fbcd3485ace,
723            0x96fd279baed2f2ab,
724            0x6403a64c68d7bf68,
725            0x3f8f532e1df472e5,
726            0xbfc49c083515596f,
727            0xd678a4b338fbf03b,
728            0x127142a2f38b70a1,
729            0x8a1a56fbb85b71f6,
730            0x961d22b14e6f1932,
731            0xa166b0326c942c30,
732            0x0f3d837dddb86ae2,
733            0x0f8164504b4ea8b1,
734            0xe4f6475d5a739af4,
735            0xbf535ad625c0d51f,
736            0x47f10a5a13be50ad,
737            0x3dc5ce9c148969b3,
738            0x8dc071fb4df8e144,
739            0x9d0a83586cbed3b8,
740            0xc4379e22f2809b99,
741            0x42010c7dd7657650,
742            0xcc31a6fbcdab8be8,
743            0x7bad06c38400138a,
744            0x0178b41584eb483d,
745            0x78afc38d52514efc,
746            0x65a57c4e59288dc7,
747            0x86e7cc3e273e4e47,
748            0xeb99661fb41a6bd2,
749            0xea0979aa6cd70feb,
750            0xa64a347c0b8e007b,
751            0x3692969270fe8fa4,
752            0x17640c6052e26555,
753            0xdf9e0fd276291357,
754            0x64cca6ebf4580720,
755            0xf82b33f6399c3f49,
756            0xbe3ccb7526561379,
757            0x8c796fce8509c043,
758            0x9849fded8c92ce51,
759            0xa0e744d838dbc4ef,
760            0x8e4602d33a961a65,
761            0xda381d6727886a7e,
762            0xa503a344fc066833,
763            0xbf8ff5bc36d5dc7b,
764            0x795ae9ed95bca7e9,
765            0x19c80807dc900762,
766            0xea7d27083e6ca641,
767            0xeba7e4a637fe4fb5,
768            0x34ac9bde50ce9087,
769            0xe290dd0393f2586a,
770            0xbd7074e9843d9dca,
771            0x66c17140a05887e6,
772            0x4ad7b3e525e37f94,
773            0xde0d009c18880dd6,
774            0x1516bbb1caca46d3,
775            0xe9c907ec28f89499,
776            0xd677b655085e1e14,
777            0xac5f949b08f29553,
778            0xd353b06cb49b5503,
779            0x9c25eb30ffa8cc78,
780            0x6cf18c91658e0285,
781            0x99264d2b2cc86a77,
782            0x8b438cd1bb8fb65d,
783            0xdfd56cf20b217732,
784            0x71f4e35bf761bacf,
785            0x87d7c01f2b11659c,
786            0x95de608c3ad2653c,
787            0x51b50e6996b8de93,
788            0xd21e837b2121e8c9,
789            0x73d07c7cb3fa0ba7,
790            0x8113fab03cab6df3,
791            0x57cdddea972cc490,
792            0xc3df94778f1eec30,
793            0x7509771e4127701e,
794            0x28240c74c56f8f7c,
795            0x194fa4f68aab8e27,
796        ];
797
798        let hasher = PolymurHash::from_u64_seed(0xfedbca9876543210);
799        let tweak = 0xabcdef0123456789;
800        for (i, s) in POLYMUR_TEST_STRINGS.iter().enumerate() {
801            let hash = hasher.hash_with_tweak(s.as_bytes(), tweak);
802            assert_eq!(
803                POLYMUR_REFERENCE_VALUES[i], hash,
804                "i={}; expected={:x}; got={:x}",
805                i, POLYMUR_REFERENCE_VALUES[i], hash
806            );
807        }
808    }
809}