1 /** Various hash functions, including integer ones.
2  */
3 module nxt.hash_functions;
4 
5 @safe pure nothrow:
6 
7 /** See_Also: http://forum.dlang.org/post/o1igoc$21ma$1@digitalmars.com
8  *
9  * Doesn't work: integers are returned as is.
10  */
11 size_t typeidHashOf(T)(in T x) @trusted
12 {
13     pragma(inline, true);
14     return typeid(T).getHash(&x); // TODO why not pure @nogc?
15 }
16 
17 ///
18 @safe pure nothrow unittest
19 {
20     // TODO auto x = typeidHashOf(cast(int)17);
21 }
22 
23 hash_t hashOfTypeInfoPtr(TypeInfo_Class typeinfo) @trusted pure nothrow @nogc
24 {
25     pragma(inline, true);
26     assert(typeof(typeinfo).alignof == 8);
27     return (cast(hash_t)(cast(void*)typeinfo) >> 3);
28 }
29 
30 /** Hash that incorporates the hash of `typeid` bit-xored with `hashOf(a)`.
31  *
32  * See_Also: https://forum.dlang.org/post/lxqoknwuujbymolnlyfw@forum.dlang.org
33  */
34 hash_t hashOfPolymorphic(Class)(Class a) @trusted pure nothrow @nogc
35 if (is(Class == class))
36 {
37     pragma(inline, true);
38     static assert(typeid(Class).alignof == 8);
39     // const class_typeid_hash = (cast(hash_t)(cast(void*)typeid(Class)) >> 3)
40     import core.internal.hash : hashOf;
41     return fibonacci_hash(hashOf(cast(void*)typeid(a))) ^ hashOf(a);
42 }
43 
44 size_t fibonacci_hash(hash_t hash) @safe pure nothrow @nogc
45 {
46     pragma(inline, true);
47     return (hash * 11400714819323198485LU);
48 }
49 
50 version(unittest)
51 {
52     private static:
53 
54     class Thing
55     {
56     }
57 
58     class Expr : Thing
59     {
60         @safe pure nothrow @nogc:
61         alias Data = string;
62         this(Data data)
63         {
64             this.data = data;
65         }
66         @property override hash_t toHash() const @safe pure nothrow @nogc
67         {
68             return hashOf(data);
69         }
70         Data data;
71     }
72 
73     class NounExpr : Expr
74     {
75         @safe pure nothrow @nogc:
76         this(Data data)
77         {
78             super(data);
79         }
80         @property override hash_t toHash() const @safe pure nothrow @nogc
81         {
82             return hashOf(data);
83         }
84     }
85 
86     class Year : Thing
87     {
88         @safe pure nothrow @nogc:
89         alias Data = long;
90         @property override hash_t toHash() const @safe pure nothrow @nogc
91         {
92             return hashOf(data);
93         }
94         Data data;
95     }
96 }
97 
98 ///
99 @safe pure nothrow @nogc unittest
100 {
101     scope car1 = new Expr("car");
102     scope car2 = new Expr("car");
103     scope bar1 = new Expr("bar");
104     scope ncar = new NounExpr("car");
105 
106     void testEqual() @safe pure nothrow @nogc
107     {
108         assert(hashOf(car1) == hashOf(car2));
109         assert(hashOfPolymorphic(car1) == hashOfPolymorphic(car2));
110     }
111 
112     void testDifferent1() @safe pure nothrow @nogc
113     {
114         assert(hashOf(car1) != hashOf(bar1));
115         assert(hashOfPolymorphic(car1) != hashOfPolymorphic(bar1));
116     }
117 
118     void testDifferent2() @safe pure nothrow @nogc
119     {
120         assert(hashOf(car1) == hashOf(ncar));
121         assert(hashOfPolymorphic(car1) != hashOfPolymorphic(ncar));
122     }
123 
124     testEqual();
125     testDifferent1();
126     testDifferent2();
127 }
128 
129 @nogc:
130 
131 /** Dummy-hash for benchmarking performance of HashSet. */
132 ulong identityHash64Of(in ulong x)
133 {
134     pragma(inline, true);
135     return x;               // maps -1 to ulong.max
136 }
137 
138 ///
139 @safe pure nothrow @nogc unittest
140 {
141     assert(identityHash64Of(-1) == ulong.max);
142     assert(identityHash64Of(int.max) == int.max);
143     assert(identityHash64Of(ulong.max) == ulong.max);
144 }
145 
146 /** Mueller integer hash function (bit mixer) A (32-bit).
147  *
148  * See_Also: https://stackoverflow.com/a/12996028/683710
149  * See_Also: http://zimbry.blogspot.se/2011/09/better-bit-mixing-improving-on.html
150  */
151 uint muellerHash32(uint x)
152 {
153     pragma(inline, true);
154     x = ((x >> 16) ^ x) * 0x45d9f3b;
155     x = ((x >> 16) ^ x) * 0x45d9f3b;
156     x = (x >> 16) ^ x;
157     return x;
158 }
159 
160 /** Mueller integer hash function (bit mixer) A (64-bit).
161  *
162  * Based on splitmix64, which seems to be based on the blog article "Better Bit
163  * Mixing" (mix 13).
164  *
165  * See_Also: https://stackoverflow.com/a/12996028/683710
166  * See_Also: http://zimbry.blogspot.se/2011/09/better-bit-mixing-improving-on.html
167  * See_Also: http://xorshift.di.unimi.it/splitmix64.c
168  */
169 ulong muellerHash64(in ulong x)
170 {
171     pragma(inline, true);
172     typeof(return) y = x;
173     y = (y ^ (y >> 30)) * 0xbf58476d1ce4e5b9UL;
174     y = (y ^ (y >> 27)) * 0x94d049bb133111ebUL;
175     y = y ^ (y >> 31);
176     return y;
177 }
178 
179 /** Thomas Wang 64-bit mix integer hash function.
180  *
181  * See_Also: https://gist.github.com/badboy/6267743#64-bit-mix-functions
182  */
183 public ulong wangMixHash64(ulong x)
184 {
185     pragma(inline, true);
186     x = (~x) + (x << 21); // x = (x << 21) - x - 1;
187     x = x ^ (x >>> 24);
188     x = (x + (x << 3)) + (x << 8); // x * 265
189     x = x ^ (x >>> 14);
190     x = (x + (x << 2)) + (x << 4); // x * 21
191     x = x ^ (x >>> 28);
192     x = x + (x << 31);
193     return x;
194 }
195 
196 ///
197 @safe pure nothrow @nogc unittest
198 {
199     assert(wangMixHash64(0) == 8633297058295171728UL);
200     assert(wangMixHash64(1) == 6614235796240398542UL);
201 }
202 
203 /** Inspired by lemire's strongly universal hashing.
204  *
205  * See_Also: https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/
206  *
207  * Instead of shifts, we use rotations so we don't lose any bits.
208  *
209  * Added a final multiplcation with a constant for more mixing. It is most important that the
210  * lower bits are well mixed.
211  */
212 ulong lemireHash64(in ulong x)
213 {
214     pragma(inline, true);
215     import core.bitop : ror;
216     const ulong h1 = x * 0xA24BAED4963EE407UL;
217     const ulong h2 = ror(x, 32U) * 0x9FB21C651E98DF25UL;
218     const ulong h = ror(h1 + h2, 32U);
219     return h;
220 }
221 
222 ///
223 @safe pure nothrow @nogc unittest
224 {
225     assert(lemireHash64(0) == 0UL);
226     assert(lemireHash64(1) == 10826341276197359097UL);
227     assert(lemireHash64(2) == 3205938474390199283UL);
228 }
229 
230 /** Inspired by lemire's strongly universal hashing.
231  *
232  * See_Also: https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/
233  *
234  * Instead of shifts, we use rotations so we don't lose any bits.
235  *
236  * Added a final multiplcation with a constant for more mixing. It is most important that the
237  * lower bits are well mixed.
238  */
239 ulong lemireHash64(in double x) @trusted
240 {
241     pragma(inline, true);
242     import core.bitop : ror;
243     const ulong h1 = *(cast(ulong*)&x) * 0xA24BAED4963EE407UL;
244     const ulong h2 = ror(*(cast(ulong*)&x), 32U) * 0x9FB21C651E98DF25UL;
245     const ulong h = ror(h1 + h2, 32U);
246     return h;
247 }
248 
249 ///
250 @safe pure nothrow @nogc unittest
251 {
252     assert(lemireHash64(0f) == 0UL);
253     assert(lemireHash64(1f) == 5597974336836488763);
254     assert(lemireHash64(2f) == 4611686018555721673UL);
255 }