├── .gitignore ├── Makefile ├── README.md ├── UNLICENSE ├── genetic.c ├── hillclimb.c ├── hp16.c ├── prospector.c └── tests ├── degski64.c ├── h2hash32.c ├── hash32shift.c ├── murmurhash3_finalizer32.c └── splitmix64.c /.gitignore: -------------------------------------------------------------------------------- 1 | prospector 2 | *.so 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -std=c99 -Wall -Wextra -march=native -O3 -ggdb3 -fopenmp 3 | LDFLAGS = 4 | LDLIBS = -lm -ldl 5 | 6 | compile: prospector genetic hillclimb hp16 7 | 8 | prospector: prospector.c 9 | $(CC) $(LDFLAGS) $(CFLAGS) -o $@ prospector.c $(LDLIBS) 10 | 11 | genetic: genetic.c 12 | $(CC) $(LDFLAGS) $(CFLAGS) -o $@ genetic.c $(LDLIBS) 13 | 14 | hillclimb: hillclimb.c 15 | $(CC) $(LDFLAGS) $(CFLAGS) -o $@ hillclimb.c $(LDLIBS) 16 | 17 | hp16: hp16.c 18 | $(CC) $(LDFLAGS) $(CFLAGS) -o $@ hp16.c $(LDLIBS) 19 | 20 | tests/degski64.so: tests/degski64.c 21 | tests/h2hash32.so: tests/h2hash32.c 22 | tests/hash32shift.so: tests/hash32shift.c 23 | tests/splitmix64.so: tests/splitmix64.c 24 | 25 | hashes = \ 26 | tests/degski64.so \ 27 | tests/h2hash32.so \ 28 | tests/hash32shift.so \ 29 | tests/murmurhash3_finalizer32.so \ 30 | tests/splitmix64.so 31 | 32 | check: prospector $(hashes) 33 | ./prospector -E -8 -l tests/degski64.so 34 | ./prospector -E -4 -l tests/h2hash32.so 35 | ./prospector -E -4 -l tests/hash32shift.so 36 | ./prospector -E -4 -l tests/murmurhash3_finalizer32.so 37 | ./prospector -E -8 -l tests/splitmix64.so 38 | 39 | clean: 40 | rm -f prospector genetic hillclimb hp16 $(hashes) 41 | 42 | .SUFFIXES: .so .c 43 | .c.so: 44 | $(CC) -shared $(LDFLAGS) -fPIC $(CFLAGS) -o $@ $< 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hash Function Prospector 2 | 3 | This is a little tool for automated [integer hash function][wang] 4 | discovery. It generates billions of [integer hash functions][jenkins] at 5 | random from a selection of [nine reversible operations][rev] ([also][]). 6 | The generated functions are JIT compiled and their avalanche behavior is 7 | evaluated. The current best function is printed out in C syntax. 8 | 9 | The *avalanche score* is the number of output bits that remain "fixed" 10 | on average when a single input bit is flipped. Lower scores are better. 11 | Ideally the score is 0 — e.g. every output bit flips with a 50% chance 12 | when a single input bit is flipped. 13 | 14 | Prospector can generate both 32-bit and 64-bit integer hash functions. 15 | Check the usage (`-h`) for the full selection of options. Due to the JIT 16 | compiler, only x86-64 is supported, though the functions it discovers 17 | can, of course, be used anywhere. 18 | 19 | Article: [Prospecting for Hash Functions][article] 20 | 21 | ## Discovered Hash Functions 22 | 23 | There are two useful classes of hash functions discovered by the 24 | prospector and the other helper utilities here. Both use an 25 | *xorshift-multiply-xorshift* construction, but with a different number 26 | of rounds. 27 | 28 | ### Two round functions 29 | 30 | **Update**: [TheIronBorn has used combinatorial optimization][best] to 31 | discover the best known parameters for this construction: 32 | 33 | [16 21f0aaad 15 d35a2d97 15] = 0.10760229515479501 34 | 35 | * * * 36 | 37 | This 32-bit, two-round permutation has a particularly low bias and even 38 | beats the venerable MurmurHash3 32-bit finalizer by a tiny margin. The 39 | hash function construction was discovered by the prospector, then the 40 | parameters were tuned using hill climbing and a genetic algorithm. 41 | 42 | ```c 43 | // exact bias: 0.17353355999581582 44 | uint32_t 45 | lowbias32(uint32_t x) 46 | { 47 | x ^= x >> 16; 48 | x *= 0x7feb352d; 49 | x ^= x >> 15; 50 | x *= 0x846ca68b; 51 | x ^= x >> 16; 52 | return x; 53 | } 54 | 55 | // inverse 56 | uint32_t 57 | lowbias32_r(uint32_t x) 58 | { 59 | x ^= x >> 16; 60 | x *= 0x43021123; 61 | x ^= x >> 15 ^ x >> 30; 62 | x *= 0x1d69e2a5; 63 | x ^= x >> 16; 64 | return x; 65 | } 66 | ``` 67 | 68 | More 2-round constants with low bias, some even better than `lowbias32`: 69 | 70 | [15 d168aaad 15 af723597 15] = 0.15983776156606694 71 | [17 9e485565 16 ef1d6b47 16] = 0.16143129787074881 72 | [16 604baa5d 15 43d6ce97 15] = 0.16491052655811722 73 | [16 a812d533 15 b278e4ad 17] = 0.16540778981744320 74 | [16 9c8f2d35 15 5d1346b5 17] = 0.16835348823718840 75 | [16 88c0a94b 14 9d06da59 17] = 0.16898511658356749 76 | [16 a52fb2cd 15 551e4d49 16] = 0.17162579707098322 77 | [16 b237694b 15 eb5b4593 15] = 0.17274184020173433 78 | [16 7feb352d 15 846ca68b 16] = 0.17353355999581582 79 | [16 4bdc9aa5 15 2729b469 16] = 0.17355424787865850 80 | [16 dc63b4d3 15 2c32b9a9 15] = 0.17368589564800074 81 | [16 e02bd533 15 0364c8ad 17] = 0.17447893149410759 82 | [16 603a32a7 15 5a522677 15] = 0.17514135907753242 83 | [16 ac10d4eb 15 9d51b169 16] = 0.17676510450127819 84 | [15 f15f5959 14 7db29359 16] = 0.18103205436627479 85 | [16 83747333 14 aa256573 16] = 0.18105722344231542 86 | [16 be8b6ca7 14 6dd624b5 16] = 0.18223928664971270 87 | [17 7186cd35 15 fe6bba73 15] = 0.18312741727971640 88 | [16 93f2552b 15 959b4a4d 15] = 0.18360629205797341 89 | [16 df892d4b 15 3c2da6b3 16] = 0.18368195486921446 90 | [15 49c34cd3 13 e7418ca7 16] = 0.18400092964673831 91 | [15 4811acab 15 5591acd7 16] = 0.18522661033580071 92 | [16 dc85aaa7 15 6658a5cb 15] = 0.18577280285788791 93 | [16 1ec9b4db 15 3224d38d 17] = 0.18631684392389897 94 | [16 8ee0d535 15 5dc6b5af 15] = 0.18664478683752250 95 | [16 462daaad 15 0a36c95d 16] = 0.18674876992866513 96 | [16 17cdd657 15 a426cb25 15] = 0.18995262675473334 97 | [16 ab39aacb 15 a1b5d19b 15] = 0.19045785238099658 98 | [17 cd8512ad 15 b95c5a73 15] = 0.19050717016846502 99 | [16 aecc96b5 15 f64dcd47 15] = 0.19077817816874504 100 | [15 2548acd5 15 0b39d397 16] = 0.19121161052714156 101 | [15 7f19c559 15 b356358d 16] = 0.19198007174447981 102 | [16 4ffcab35 15 e98db28b 16] = 0.19423994132339928 103 | [15 1216ccb5 15 3abcdca9 15] = 0.19426091938816648 104 | [16 97219aad 15 ab46b735 15] = 0.19536391240344408 105 | [16 c845a997 15 f214db9b 17] = 0.19553179377831409 106 | [15 3a7ba96b 13 5e919299 16] = 0.19563436462680908 107 | [16 c3d9a965 16 362e4b47 15] = 0.19575424692659107 108 | [17 179cd515 15 4c495d47 15] = 0.19608530402798924 109 | [16 5dce3553 15 a655d8e9 15] = 0.19621753012889542 110 | [17 88a5ad35 16 96338b27 16] = 0.19653922266398804 111 | [17 0364d657 15 ac2a34c5 15] = 0.19665754791333651 112 | [16 3c9aa9ab 16 051369d7 16] = 0.19687211117412906 113 | [17 0ee6d967 15 9c8a4a33 16] = 0.19722490309575344 114 | [16 b921a6cb 14 30b5a6d1 16] = 0.19745192295417058 115 | [18 a136aaad 16 9f6d62d7 17] = 0.19768193144773874 116 | [16 0ae84d3b 15 3b9d4e5b 17] = 0.19776257374279985 117 | [17 24f4d2cd 15 1ba3b969 16] = 0.19789489706453650 118 | [16 418fb5b3 15 8cf3539b 16] = 0.19817117175199098 119 | [16 f0ae2ad7 15 8965d939 16] = 0.19881758420284917 120 | [17 9bde596b 16 1c9e9647 16] = 0.19882570872036193 121 | [16 bd10754b 14 35a29b0d 16] = 0.19885203058591913 122 | [17 78d31553 15 c547ac65 15] = 0.19918133404528665 123 | [15 81aab34d 15 18e746a3 15] = 0.19938572052445763 124 | [16 054335ab 15 146da68b 16] = 0.19943843016872725 125 | [17 a1c76a55 16 5ca46b97 16] = 0.19959562213253398 126 | [15 c62f4d53 14 62b8a46b 16] = 0.19973996656987172 127 | [16 6872cd2d 15 f4a0d975 17] = 0.19992260539370590 128 | 129 | This next function was discovered using only the prospector. It has a bit more 130 | bias than the previous function. 131 | 132 | ```c 133 | // exact bias: 0.34968228323361017 134 | uint32_t 135 | prospector32(uint32_t x) 136 | { 137 | x ^= x >> 15; 138 | x *= 0x2c1b3c6d; 139 | x ^= x >> 12; 140 | x *= 0x297a2d39; 141 | x ^= x >> 15; 142 | return x; 143 | } 144 | ``` 145 | 146 | To use the prospector search randomly for alternative multiplication constants, 147 | run it like so: 148 | 149 | $ ./prospector -p xorr:15,mul,xorr:12,mul,xorr:15 150 | 151 | ### Three round functions 152 | 153 | Another round of multiply-xorshift in this construction allows functions 154 | with carefully chosen parameters to reach the theoretical bias limit 155 | (bias = ~0.021). For example, this hash function is indistinguishable 156 | from a perfect PRF (e.g. a random permutation of all 32-bit integers): 157 | 158 | ```c 159 | // exact bias: 0.020888578919738908 160 | uint32_t 161 | triple32(uint32_t x) 162 | { 163 | x ^= x >> 17; 164 | x *= 0xed5ad4bb; 165 | x ^= x >> 11; 166 | x *= 0xac4c1b51; 167 | x ^= x >> 15; 168 | x *= 0x31848bab; 169 | x ^= x >> 14; 170 | return x; 171 | } 172 | 173 | // inverse 174 | uint32_t 175 | triple32_r(uint32_t x) 176 | { 177 | x ^= x >> 14 ^ x >> 28; 178 | x *= 0x32b21703; 179 | x ^= x >> 15 ^ x >> 30; 180 | x *= 0x469e0db1; 181 | x ^= x >> 11 ^ x >> 22; 182 | x *= 0x79a85073; 183 | x ^= x >> 17; 184 | return x; 185 | } 186 | ``` 187 | 188 | More 3-round constants with low bias: 189 | 190 | [17 ed5ad4bb 11 ac4c1b51 15 31848bab 14] = 0.020888578919738908 191 | [16 aeccedab 14 ac613e37 16 19c89935 17] = 0.021246568167078764 192 | [16 236f7153 12 33cd8663 15 3e06b66b 16] = 0.021280991798512679 193 | [18 4260bb47 13 27e8e1ed 15 9d48a33b 15] = 0.021576730651802156 194 | [17 3f6cde45 12 51d608ef 16 6e93639d 17] = 0.021772288363808408 195 | [15 5dfa224b 14 4bee7e4b 17 930ee371 15] = 0.02184521628884813 196 | [17 3964f363 14 9ac3751d 16 4e8772cb 17] = 0.021883292578109576 197 | [16 66046c65 14 d3f0865b 16 f9999193 16] = 0.0219446068365007 198 | [16 b1a89b33 14 09136aaf 16 5f2a44a7 15] = 0.021998624107282542 199 | [16 24767aad 12 daa18229 16 e9e53beb 16] = 0.022043911220395354 200 | [15 42f91d8d 14 61355a85 15 dcf2a949 14] = 0.022052539152635078 201 | [15 4df8395b 15 466b428b 16 b4b2868b 16] = 0.022140187420461286 202 | [16 2bbed51b 14 cd09896b 16 38d4c587 15] = 0.022159936298777144 203 | [16 0ab694cd 14 4c139e47 16 11a42c3b 16] = 0.02220928191220355 204 | [17 7f1e072b 12 8750a507 16 ecbb5b5f 16] = 0.022283743052847804 205 | [16 f1be7bad 14 73a54099 15 3b85b963 15] = 0.022316544125749647 206 | [16 66e756d5 14 b5f5a9cd 16 84e56b11 16] = 0.022372957847491555 207 | [15 233354bb 15 ce1247bd 16 855089bb 17] = 0.022406591070966285 208 | [16 eb6805ab 15 d2c7b7a7 16 7645a32b 16] = 0.022427060650927547 209 | [16 8288ab57 14 0d1bfe57 16 131631e5 16] = 0.022431656871313443 210 | [16 45109e55 14 3b94759d 16 adf31ea5 17] = 0.022436433678417977 211 | [15 26cd1933 14 e3da1d59 16 5a17445d 16] = 0.022460520416491526 212 | [16 7001e6eb 14 bb8e7313 16 3aa8c523 15] = 0.022491767264054854 213 | [16 49ed0a13 14 83588f29 15 658f258d 15] = 0.022500668856510898 214 | [16 6cdb9705 14 4d58d2ed 14 c8642b37 16] = 0.022504626537729222 215 | [16 a986846b 14 bdd5372d 15 ad44de6b 17] = 0.022528238323120016 216 | [16 c9575725 15 9448f4c5 16 3b7a5443 16] = 0.022586511310042686 217 | [15 fc54c453 13 08213789 15 669f96eb 16] = 0.022591114646032095 218 | [16 d47ef17b 14 642fa58f 16 a8b65b9b 16] = 0.022600633971701509 219 | [15 00bfaa73 14 8799c69b 16 731985b1 16] = 0.022645866629596379 220 | [16 953a55e9 15 8523822b 17 56e7aa63 15] = 0.022667180032713324 221 | [16 a3d7345b 15 7f41c9c7 16 308bd62d 17] = 0.022688845770122031 222 | [16 195565c7 14 16064d6f 16 0f9ec575 15] = 0.022697810688752193 223 | [16 13566dbb 14 59369a03 15 990f9d1b 16] = 0.022712430070797596 224 | [16 8430cc4b 15 a7831cbd 15 c6ccbd33 15] = 0.022734765033419774 225 | [16 699f272b 14 09c01023 16 39bd48c3 15] = 0.022854175321846512 226 | [15 336536c3 13 4f0e38b1 16 15d229f7 16] = 0.022884125170795171 227 | [16 221f686d 12 d8948a07 16 ed8a8345 16] = 0.022902500408830236 228 | [16 d7ca8cbb 13 eb4e259f 15 34ab1143 16] = 0.022905955538176669 229 | [16 7cb04f65 14 9b96da73 16 83625687 15] = 0.022906573700088178 230 | [15 5156196b 14 940d8869 15 0086f473 17] = 0.022984943828687553 231 | 232 | Prepending an increment to `triple32` breaks the `hash(0) = 0` issue while 233 | also lowering the bias a tiny bit further: 234 | 235 | ```c 236 | // exact bias: 0.020829410544597495 237 | uint32_t 238 | triple32inc(uint32_t x) 239 | { 240 | x++; 241 | x ^= x >> 17; 242 | x *= 0xed5ad4bb; 243 | x ^= x >> 11; 244 | x *= 0xac4c1b51; 245 | x ^= x >> 15; 246 | x *= 0x31848bab; 247 | x ^= x >> 14; 248 | return x; 249 | } 250 | 251 | // inverse 252 | uint32_t 253 | triple32inc_r(uint32_t x) 254 | { 255 | x ^= x >> 14 ^ x >> 28; 256 | x *= 0x32b21703; 257 | x ^= x >> 15 ^ x >> 30; 258 | x *= 0x469e0db1; 259 | x ^= x >> 11 ^ x >> 22; 260 | x *= 0x79a85073; 261 | x ^= x >> 17; 262 | x--; 263 | return x; 264 | } 265 | ``` 266 | 267 | ## Measuring exact bias 268 | 269 | The `-E` mode evaluates the bias of a given hash function (`-p` or `-l`). By 270 | default the prospector uses an estimate to quickly evaluate a function's bias, 271 | but it's non-deterministic and there's a lot of noise in the result. To 272 | exhaustively measure the exact bias, use the `-e` option. 273 | 274 | The function to be checked can be defined using `-p` and a pattern or 275 | `-l` and a shared library containing a function named `hash()`. For 276 | example, to measure the exact bias of the best hash function above: 277 | 278 | $ ./prospector -Eep xorr:16,mul:e2d0d4cb,xorr:15,mul:3c6ad939,xorr:15 279 | 280 | Or drop the function in a C file named hash.c, and name the function 281 | `hash()`. This lets you test hash functions that can't be represented 282 | using the prospector's limited notion of hash functions. 283 | 284 | $ cc -O3 -shared -fPIC -l hash.so hash.c 285 | $ ./prospector -Eel ./hash.so 286 | 287 | By default it treats its input as a 32-bit hash function. Use the `-8` 288 | switch to test (by estimation) 64-bit functions. There is no exact, 289 | exhaustive test for 64-bit hash functions since that would take far too 290 | long. 291 | 292 | ## Reversible operation selection 293 | 294 | ```c 295 | x = ~x; 296 | x ^= constant; 297 | x *= constant | 1; // e.g. only odd constants 298 | x += constant; 299 | x ^= x >> constant; 300 | x ^= x << constant; 301 | x += x << constant; 302 | x -= x << constant; 303 | x <<<= constant; // left rotation 304 | x = bswap(x) // swap high and low bytes. 305 | ``` 306 | 307 | Technically `x = ~x` is covered by `x ^= constant`. However, `~x` is 308 | uniquely special and particularly useful. The generator is very unlikely 309 | to generate the one correct constant for the XOR operator that achieves 310 | the same effect. 311 | 312 | ## 16-bit hashes 313 | 314 | Because the constraints are different for 16-bit hashes there's a separate 315 | tool for generating these hashes: `hp16`. Unlike the 32-bit / 64-bit 316 | prospector, this implementation is fully portable and will run on just 317 | about any system. It's also capable of generating and evaluating 128KiB 318 | s-boxes. 319 | 320 | Since 16-bit hashes are more likely to be needed on machines that, say, 321 | lack fast multiplication instructions, certain operations can be omitted 322 | during exploration (`-m`, `-r`). 323 | 324 | Some interesting results so far: 325 | 326 | ```c 327 | // 2-round xorshift-multiply (-Xn2) 328 | // bias = 0.0085905051336723701 329 | uint16_t hash16_xm2(uint16_t x) 330 | { 331 | x ^= x >> 8; x *= 0x88b5U; 332 | x ^= x >> 7; x *= 0xdb2dU; 333 | x ^= x >> 9; 334 | return x; 335 | } 336 | 337 | // 3-round xorshift-multiply (-Xn3) 338 | // bias = 0.0045976709018820602 339 | uint16_t hash16_xm3(uint16_t x) 340 | { 341 | x ^= x >> 7; x *= 0x2993U; 342 | x ^= x >> 5; x *= 0xe877U; 343 | x ^= x >> 9; x *= 0x0235U; 344 | x ^= x >> 10; 345 | return x; 346 | } 347 | 348 | // No multiplication (-Imn6) 349 | // bias = 0.023840118344741465 350 | uint16_t hash16_s6(uint16_t x) 351 | { 352 | x += x << 7; x ^= x >> 8; 353 | x += x << 3; x ^= x >> 2; 354 | x += x << 4; x ^= x >> 8; 355 | return x; 356 | } 357 | 358 | // Which is identical to this xorshift-multiply 359 | uint16_t hash16_s6(uint16_t x) 360 | { 361 | x *= 0x0081U; x ^= x >> 8; 362 | x *= 0x0009U; x ^= x >> 2; 363 | x *= 0x0011U; x ^= x >> 8; 364 | return x; 365 | } 366 | ``` 367 | 368 | A good 3-round xorshift hash (a short search via `hp16 -Xn3`) is a close 369 | approximation of a good s-box (i.e. `hp16 -S`). 370 | 371 | Be mindful of C integer promotion rules when doing 16-bit operations. For 372 | instance, on 32-bit implementations unsigned 16-bit operands will be 373 | promoted to signed 32-bit integers, leading to incorrect results in 374 | certain cases. The C programs printed by this program are careful to 375 | promote 16-bit operations to "unsigned int" where needed. 376 | 377 | 378 | [also]: https://marc-b-reynolds.github.io/math/2017/10/13/IntegerBijections.html 379 | [article]: https://nullprogram.com/blog/2018/07/31/ 380 | [best]: https://github.com/skeeto/hash-prospector/issues/19 381 | [jenkins]: http://burtleburtle.net/bob/hash/integer.html 382 | [rev]: http://papa.bretmulvey.com/post/124027987928/hash-functions 383 | [wang]: https://gist.github.com/badboy/6267743 384 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /genetic.c: -------------------------------------------------------------------------------- 1 | /* Genetic algorithm to explore xorshift-multiply-xorshift hashes. 2 | */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define POOL 40 10 | #define THRESHOLD 2.0 // Use exact when estimate is below this 11 | #define DONTCARE 0.3 // Only print tuples with bias below this threshold 12 | #define QUALITY 18 // 2^N iterations of estimate samples 13 | #define RESETMINS 90 // Reset pool after this many minutes of no progress 14 | 15 | static uint64_t 16 | rand64(uint64_t s[4]) 17 | { 18 | uint64_t x = s[1] * 5; 19 | uint64_t r = ((x << 7) | (x >> 57)) * 9; 20 | uint64_t t = s[1] << 17; 21 | s[2] ^= s[0]; 22 | s[3] ^= s[1]; 23 | s[1] ^= s[2]; 24 | s[0] ^= s[3]; 25 | s[2] ^= t; 26 | s[3] = (s[3] << 45) | (s[3] >> 19); 27 | return r; 28 | } 29 | 30 | 31 | #define FLAG_SCORED (1u << 0) 32 | #define FLAG_EXACT (1u << 1) 33 | #define FLAG_PRINTED (1u << 2) 34 | 35 | struct gene { 36 | double score; 37 | short s[3]; 38 | uint32_t c[2]; 39 | unsigned flags; 40 | }; 41 | 42 | static uint32_t 43 | hash(const struct gene *g, uint32_t x) 44 | { 45 | x ^= x >> g->s[0]; 46 | x *= g->c[0]; 47 | x ^= x >> g->s[1]; 48 | x *= g->c[1]; 49 | x ^= x >> g->s[2]; 50 | return x; 51 | } 52 | 53 | static double 54 | estimate_bias32(const struct gene *g, uint64_t rng[4]) 55 | { 56 | long n = 1L << QUALITY; 57 | long bins[32][32] = {{0}}; 58 | for (long i = 0; i < n; i++) { 59 | uint32_t x = rand64(rng); 60 | uint32_t h0 = hash(g, x); 61 | for (int j = 0; j < 32; j++) { 62 | uint32_t bit = UINT32_C(1) << j; 63 | uint32_t h1 = hash(g, x ^ bit); 64 | uint32_t set = h0 ^ h1; 65 | for (int k = 0; k < 32; k++) 66 | bins[j][k] += (set >> k) & 1; 67 | } 68 | } 69 | double mean = 0; 70 | for (int j = 0; j < 32; j++) { 71 | for (int k = 0; k < 32; k++) { 72 | double diff = (bins[j][k] - n / 2) / (n / 2.0); 73 | mean += (diff * diff) / (32 * 32); 74 | } 75 | } 76 | return sqrt(mean) * 1000.0; 77 | } 78 | 79 | #define EXACT_SPLIT 32 // must be power of two 80 | static double 81 | exact_bias32(const struct gene *g) 82 | { 83 | long long bins[32][32] = {{0}}; 84 | static const uint64_t range = (UINT64_C(1) << 32) / EXACT_SPLIT; 85 | #pragma omp parallel for 86 | for (int i = 0; i < EXACT_SPLIT; i++) { 87 | long long b[32][32] = {{0}}; 88 | for (uint64_t x = i * range; x < (i + 1) * range; x++) { 89 | uint32_t h0 = hash(g, x); 90 | for (int j = 0; j < 32; j++) { 91 | uint32_t bit = UINT32_C(1) << j; 92 | uint32_t h1 = hash(g, x ^ bit); 93 | uint32_t set = h0 ^ h1; 94 | for (int k = 0; k < 32; k++) 95 | b[j][k] += (set >> k) & 1; 96 | } 97 | } 98 | #pragma omp critical 99 | for (int j = 0; j < 32; j++) 100 | for (int k = 0; k < 32; k++) 101 | bins[j][k] += b[j][k]; 102 | } 103 | double mean = 0.0; 104 | for (int j = 0; j < 32; j++) { 105 | for (int k = 0; k < 32; k++) { 106 | double diff = (bins[j][k] - 2147483648L) / 2147483648.0; 107 | mean += (diff * diff) / (32 * 32); 108 | } 109 | } 110 | return sqrt(mean) * 1000.0; 111 | } 112 | 113 | static void 114 | gene_gen(struct gene *g, uint64_t rng[4]) 115 | { 116 | uint64_t s = rand64(rng); 117 | uint64_t c = rand64(rng); 118 | g->s[0] = 10 + (s >> 0) % 10; 119 | g->s[1] = 10 + (s >> 24) % 10; 120 | g->s[2] = 10 + (s >> 48) % 10; 121 | g->c[0] = c | 1u; 122 | g->c[1] = (c >> 32) | 1u; 123 | g->flags = 0; 124 | } 125 | 126 | static void 127 | gene_print(const struct gene *g, FILE *f) 128 | { 129 | fprintf(f, "[%2d %08lx %2d %08lx %2d]", 130 | g->s[0], (unsigned long)g->c[0], 131 | g->s[1], (unsigned long)g->c[1], g->s[2]); 132 | } 133 | 134 | static int 135 | small(uint64_t r) 136 | { 137 | static const int v[] = {-3, -2, -1, +1, +2, +3}; 138 | return v[r % 6]; 139 | } 140 | 141 | static void 142 | gene_mutate(struct gene *g, uint64_t rng[4]) 143 | { 144 | uint64_t r = rand64(rng); 145 | int s = r % 5; 146 | r >>= 3; 147 | switch (s) { 148 | case 0: 149 | g->s[0] += small(r); 150 | break; 151 | case 1: 152 | g->s[1] += small(r); 153 | break; 154 | case 2: 155 | g->s[2] += small(r); 156 | break; 157 | case 3: 158 | g->c[0] += (int)(r & 0xffff) - 32768; 159 | break; 160 | case 4: 161 | g->c[1] += (int)(r & 0xffff) - 32768; 162 | break; 163 | } 164 | g->flags = 0; 165 | } 166 | 167 | static void 168 | gene_cross(struct gene *g, 169 | const struct gene *a, 170 | const struct gene *b, 171 | uint64_t rng[4]) 172 | { 173 | uint64_t r = rand64(rng); 174 | *g = *a; 175 | switch (r & 2) { 176 | case 0: g->c[0] = b->c[0]; /* FALLTHROUGH */ 177 | case 1: g->s[1] = b->s[1]; /* FALLTHROUGH */ 178 | case 2: g->c[1] = b->c[1]; /* FALLTHROUGH */ 179 | case 3: g->s[2] = b->s[2]; 180 | } 181 | g->flags = 0; 182 | } 183 | 184 | static int 185 | gene_same(const struct gene *a, const struct gene *b) 186 | { 187 | return a->s[0] == b->s[0] && 188 | a->s[1] == b->s[1] && 189 | a->s[2] == b->s[2] && 190 | a->c[0] == b->c[0] && 191 | a->c[1] == b->c[1]; 192 | } 193 | 194 | static void 195 | rng_init(void *p, size_t len) 196 | { 197 | FILE *f = fopen("/dev/urandom", "rb"); 198 | if (!f) 199 | abort(); 200 | if (!fread(p, 1, len, f)) 201 | abort(); 202 | fclose(f); 203 | } 204 | 205 | static int 206 | cmp(const void *pa, const void *pb) 207 | { 208 | double a = *(double *)pa; 209 | double b = *(double *)pb; 210 | if (a < b) 211 | return -1; 212 | if (b < a) 213 | return 1; 214 | return 0; 215 | } 216 | 217 | static void 218 | undup(struct gene *pool, uint64_t rng[4]) 219 | { 220 | for (int i = 0; i < POOL; i++) 221 | for (int j = i + 1; j < POOL; j++) 222 | if (gene_same(pool + i, pool + j)) 223 | gene_mutate(pool + j, rng); 224 | } 225 | 226 | int 227 | main(void) 228 | { 229 | int verbose = 1; 230 | double best = 1000.0; 231 | time_t best_time = time(0); 232 | uint64_t rng[POOL][4]; 233 | struct gene pool[POOL]; 234 | 235 | rng_init(rng, sizeof(rng)); 236 | for (int i = 0; i < POOL; i++) 237 | gene_gen(pool + i, rng[0]); 238 | 239 | for (;;) { 240 | #pragma omp parallel for schedule(dynamic) 241 | for (int i = 0; i < POOL; i++) { 242 | if (!(pool[i].flags & FLAG_SCORED)) { 243 | pool[i].score = estimate_bias32(pool + i, rng[i]); 244 | pool[i].flags |= FLAG_SCORED; 245 | } 246 | } 247 | for (int i = 0; i < POOL; i++) { 248 | if (!(pool[i].flags & FLAG_EXACT) && pool[i].score < THRESHOLD) { 249 | pool[i].score = exact_bias32(pool + i); 250 | pool[i].flags |= FLAG_EXACT; 251 | } 252 | } 253 | 254 | qsort(pool, POOL, sizeof(*pool), cmp); 255 | if (verbose) { 256 | for (int i = 0; i < POOL; i++) { 257 | if (!(pool[i].flags & FLAG_PRINTED) && 258 | pool[i].score < DONTCARE) { 259 | gene_print(pool + i, stdout); 260 | printf(" = %.17g\n", pool[i].score); 261 | pool[i].flags |= FLAG_PRINTED; 262 | } 263 | } 264 | } 265 | 266 | time_t now = time(0); 267 | if (pool[0].score < best) { 268 | best = pool[0].score; 269 | best_time = now; 270 | } else if (now - best_time > RESETMINS * 60) { 271 | best = 1000.0; 272 | best_time = now; 273 | for (int i = 0; i < POOL; i++) 274 | gene_gen(pool + i, rng[0]); 275 | } 276 | 277 | int c = POOL / 4; 278 | for (int a = 0; c < POOL && a < POOL / 4; a++) 279 | for (int b = a + 1; c < POOL && b < POOL / 4; b++) 280 | gene_cross(pool + c++, pool + a, pool + b, rng[0]); 281 | undup(pool, rng[0]); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /hillclimb.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define HASHN 3 // number of multiplies in hash 11 | #define SHIFT_RANGE 1 // radius of shift search 12 | #define CONST_RANGE 2 // radius of const search 13 | #define QUALITY 18 // 2^N iterations of estimate samples 14 | #define THRESHOLD 1.95 // regenerate anything lower than this estimate 15 | 16 | static int optind = 1; 17 | static int opterr = 1; 18 | static int optopt; 19 | static char *optarg; 20 | static int 21 | getopt(int argc, char * const argv[], const char *optstring) 22 | { 23 | static int optpos = 1; 24 | const char *arg; 25 | (void)argc; 26 | /* Reset? */ 27 | if (optind == 0) { 28 | optind = 1; 29 | optpos = 1; 30 | } 31 | arg = argv[optind]; 32 | if (arg && strcmp(arg, "--") == 0) { 33 | optind++; 34 | return -1; 35 | } else if (!arg || arg[0] != '-' || !isalnum(arg[1])) { 36 | return -1; 37 | } else { 38 | const char *opt = strchr(optstring, arg[optpos]); 39 | optopt = arg[optpos]; 40 | if (!opt) { 41 | if (opterr && *optstring != ':') 42 | fprintf(stderr, "%s: illegal option: %c\n", argv[0], optopt); 43 | return '?'; 44 | } else if (opt[1] == ':') { 45 | if (arg[optpos + 1]) { 46 | optarg = (char *)arg + optpos + 1; 47 | optind++; 48 | optpos = 1; 49 | return optopt; 50 | } else if (argv[optind + 1]) { 51 | optarg = (char *)argv[optind + 1]; 52 | optind += 2; 53 | optpos = 1; 54 | return optopt; 55 | } else { 56 | if (opterr && *optstring != ':') 57 | fprintf(stderr, 58 | "%s: option requires an argument: %c\n", 59 | argv[0], optopt); 60 | return *optstring == ':' ? ':' : '?'; 61 | } 62 | } else { 63 | if (!arg[++optpos]) { 64 | optind++; 65 | optpos = 1; 66 | } 67 | return optopt; 68 | } 69 | } 70 | } 71 | 72 | #if defined(__unix__) 73 | #include 74 | uint64_t 75 | uepoch(void) 76 | { 77 | struct timeval tv; 78 | gettimeofday(&tv, NULL); 79 | return 1000000LL * tv.tv_sec + tv.tv_usec; 80 | } 81 | #elif defined(_WIN32) 82 | #include 83 | uint64_t 84 | uepoch(void) 85 | { 86 | FILETIME ft; 87 | GetSystemTimeAsFileTime(&ft); 88 | uint64_t tt = ft.dwHighDateTime; 89 | tt <<= 32; 90 | tt |= ft.dwLowDateTime; 91 | tt /=10; 92 | tt -= UINT64_C(11644473600000000); 93 | return tt; 94 | } 95 | #endif 96 | 97 | static uint64_t 98 | rand64(uint64_t s[4]) 99 | { 100 | uint64_t x = s[1] * 5; 101 | uint64_t r = ((x << 7) | (x >> 57)) * 9; 102 | uint64_t t = s[1] << 17; 103 | s[2] ^= s[0]; 104 | s[3] ^= s[1]; 105 | s[1] ^= s[2]; 106 | s[0] ^= s[3]; 107 | s[2] ^= t; 108 | s[3] = (s[3] << 45) | (s[3] >> 19); 109 | return r; 110 | } 111 | 112 | struct hash { 113 | uint32_t c[HASHN]; 114 | char s[HASHN + 1]; 115 | }; 116 | 117 | static void 118 | hash_gen(struct hash *h, uint64_t rng[4]) 119 | { 120 | for (int i = 0; i < HASHN; i++) 121 | h->c[i] = (rand64(rng) >> 32) | 1u; 122 | for (int i = 0; i <= HASHN; i++) 123 | h->s[i] = 16; 124 | } 125 | 126 | static int 127 | hash_equal(const struct hash *a, const struct hash *b) 128 | { 129 | for (int i = 0; i < HASHN; i++) { 130 | if (a->c[i] != b->c[i]) 131 | return 0; 132 | if (a->s[i] != b->s[i]) 133 | return 0; 134 | } 135 | return a->s[HASHN] == b->s[HASHN]; 136 | } 137 | 138 | static void 139 | hash_print(const struct hash *h) 140 | { 141 | putchar('['); 142 | for (int i = 0; i < HASHN; i++) 143 | printf("%2d %08lx ", h->s[i], (unsigned long)h->c[i]); 144 | printf("%2d]", h->s[HASHN]); 145 | fflush(stdout); 146 | } 147 | 148 | static int 149 | hash_parse(struct hash *h, char *str) 150 | { 151 | long s; 152 | unsigned long c; 153 | char *end, *tok; 154 | if (*str != '[') 155 | return 0; 156 | str++; 157 | for (int i = 0; i < HASHN; i++) { 158 | tok = strtok(i ? 0 : str, " "); 159 | s = strtol(tok, &end, 10); 160 | if (s < 1 || s > 31 || !(*end == 0 || *end == ' ')) 161 | return 0; 162 | h->s[i] = s; 163 | tok = strtok(0, " "); 164 | c = strtoul(tok, &end, 16); 165 | if (c > 0xffffffffUL || !(*end == 0 || *end == ' ')) 166 | return 0; 167 | h->c[i] = c; 168 | } 169 | tok = strtok(0, "]"); 170 | s = strtol(tok, &end, 10); 171 | if (s < 1 || s > 31 || *end) 172 | return 0; 173 | h->s[HASHN] = s; 174 | return 1; 175 | } 176 | 177 | static uint32_t 178 | hash(const struct hash *h, uint32_t x) 179 | { 180 | for (int i = 0; i < HASHN; i++) { 181 | x ^= x >> h->s[i]; 182 | x *= h->c[i]; 183 | } 184 | x ^= x >> h->s[HASHN]; 185 | return x; 186 | } 187 | 188 | static double 189 | estimate_bias32(const struct hash *f, uint64_t rng[4]) 190 | { 191 | long n = 1L << QUALITY; 192 | long bins[32][32] = {{0}}; 193 | for (long i = 0; i < n; i++) { 194 | uint32_t x = rand64(rng); 195 | uint32_t h0 = hash(f, x); 196 | for (int j = 0; j < 32; j++) { 197 | uint32_t bit = UINT32_C(1) << j; 198 | uint32_t h1 = hash(f, x ^ bit); 199 | uint32_t set = h0 ^ h1; 200 | for (int k = 0; k < 32; k++) 201 | bins[j][k] += (set >> k) & 1; 202 | } 203 | } 204 | double mean = 0; 205 | for (int j = 0; j < 32; j++) { 206 | for (int k = 0; k < 32; k++) { 207 | double diff = (bins[j][k] - n / 2) / (n / 2.0); 208 | mean += (diff * diff) / (32 * 32); 209 | } 210 | } 211 | return sqrt(mean) * 1000.0; 212 | } 213 | 214 | #define EXACT_SPLIT 32 // must be power of two 215 | static double 216 | exact_bias32(const struct hash *f) 217 | { 218 | int i; // declare here to work around Visual Studio issue 219 | long long bins[32][32] = {{0}}; 220 | static const uint64_t range = (UINT64_C(1) << 32) / EXACT_SPLIT; 221 | #pragma omp parallel for 222 | for (i = 0; i < EXACT_SPLIT; i++) { 223 | long long b[32][32] = {{0}}; 224 | for (uint64_t x = i * range; x < (i + 1) * range; x++) { 225 | uint32_t h0 = hash(f, x); 226 | for (int j = 0; j < 32; j++) { 227 | uint32_t bit = UINT32_C(1) << j; 228 | uint32_t h1 = hash(f, x ^ bit); 229 | uint32_t set = h0 ^ h1; 230 | for (int k = 0; k < 32; k++) 231 | b[j][k] += (set >> k) & 1; 232 | } 233 | } 234 | #pragma omp critical 235 | for (int j = 0; j < 32; j++) 236 | for (int k = 0; k < 32; k++) 237 | bins[j][k] += b[j][k]; 238 | } 239 | double mean = 0.0; 240 | for (int j = 0; j < 32; j++) { 241 | for (int k = 0; k < 32; k++) { 242 | double diff = (bins[j][k] - 2147483648L) / 2147483648.0; 243 | mean += (diff * diff) / (32 * 32); 244 | } 245 | } 246 | return sqrt(mean) * 1000.0; 247 | } 248 | 249 | static void 250 | hash_gen_strict(struct hash *h, uint64_t rng[4]) 251 | { 252 | do 253 | hash_gen(h, rng); 254 | while (estimate_bias32(h, rng) > THRESHOLD); 255 | } 256 | 257 | static uint64_t 258 | load64(const void *buf) 259 | { 260 | const unsigned char *p = buf; 261 | return (uint64_t)p[0] << 0 | 262 | (uint64_t)p[1] << 8 | 263 | (uint64_t)p[2] << 16 | 264 | (uint64_t)p[3] << 24 | 265 | (uint64_t)p[4] << 32 | 266 | (uint64_t)p[5] << 40 | 267 | (uint64_t)p[6] << 48 | 268 | (uint64_t)p[7] << 56; 269 | } 270 | 271 | static uint64_t 272 | mix64(uint64_t x, uint64_t y) 273 | { 274 | uint64_t r = 0x2b8a130976726633 * x - 0xb28cbd28446adb17 * y; 275 | r ^= r >> 32; 276 | return r; 277 | } 278 | 279 | static uint64_t 280 | hash64(uint64_t x, uint64_t m) 281 | { 282 | x *= m; 283 | x ^= x >> 32; 284 | return x; 285 | } 286 | 287 | static void 288 | mix64x4(uint64_t x[4]) 289 | { 290 | uint64_t i = 0xf81db9ba6dabee4e; 291 | uint64_t m = 0xb1d9e3fbc08321db; 292 | x[0] = hash64(x[0] + 0x347534cdcf0982b6, m); 293 | x[1] = hash64(x[1] + 0x975e2ee8f0f23aa8, m += i); 294 | x[2] = hash64(x[2] + 0x7baf736c6c769a0b, m += i); 295 | x[3] = hash64(x[3] + 0x884afc96accb90d9, m += i); 296 | #define ROUND64(a, b, c, d) \ 297 | x[b] = mix64(hash64(x[a], m += i), x[b]); \ 298 | x[c] = mix64(hash64(x[a], m += i), x[c]); \ 299 | x[d] = mix64(hash64(x[a], m += i), x[d]) 300 | ROUND64(0, 1, 2, 3); 301 | ROUND64(1, 0, 2, 3); 302 | ROUND64(2, 0, 1, 3); 303 | ROUND64(3, 0, 1, 3); 304 | #undef ROUND64 305 | } 306 | 307 | static void 308 | rng_init(uint64_t rng[4]) 309 | { 310 | void *p = malloc(1024L * 1024); 311 | rng[0] = uepoch(); 312 | rng[1] = (uint64_t)rng_init; 313 | rng[2] = (uint64_t)rng; 314 | rng[3] = (uint64_t)p; 315 | free(p); 316 | mix64x4(rng); 317 | } 318 | 319 | /* Modular multiplicative inverse (32-bit) */ 320 | static uint32_t 321 | modinv32(uint32_t x) 322 | { 323 | uint32_t a = x; 324 | x += x - a * x * x; 325 | x += x - a * x * x; 326 | x += x - a * x * x; 327 | x += x - a * x * x; 328 | x += x - a * x * x; 329 | return x; 330 | } 331 | 332 | static void 333 | usage(FILE *f) 334 | { 335 | fprintf(f, "usage: hillclimb [-EhIqs] [-p INIT] [-x SEED]\n"); 336 | fprintf(f, " -E Evaluate given pattern (-p)\n"); 337 | fprintf(f, " -h Print this message and exit\n"); 338 | fprintf(f, " -I Invert given pattern (-p) an quit\n"); 339 | fprintf(f, " -p INIT Provide an initial hash function\n"); 340 | fprintf(f, " -q Print less information (quiet)\n"); 341 | fprintf(f, " -s Quit after finding a local minima\n"); 342 | fprintf(f, " -x SEED Seed PRNG from a string (up to 32 bytes)\n"); 343 | } 344 | 345 | int 346 | main(int argc, char **argv) 347 | { 348 | int seeded = 0; 349 | uint64_t rng[4]; 350 | struct hash cur, last = {0}; 351 | int generate = 1; 352 | int one_shot = 0; 353 | int quiet = 0; 354 | int invert = 0; 355 | int evaluate = 0; 356 | double cur_score = -1; 357 | 358 | int option; 359 | while ((option = getopt(argc, argv, "EhIp:qsx:")) != -1) { 360 | switch (option) { 361 | case 'E': { 362 | evaluate = 1; 363 | } break; 364 | case 'h': { 365 | usage(stdout); 366 | exit(EXIT_SUCCESS); 367 | } break; 368 | case 'I': { 369 | invert = 1; 370 | } break; 371 | case 'p': { 372 | if (!hash_parse(&cur, optarg)) { 373 | fprintf(stderr, "hillclimb: invalid pattern: %s\n", optarg); 374 | exit(EXIT_FAILURE); 375 | } 376 | generate = 0; 377 | } break; 378 | case 'q': { 379 | quiet++; 380 | } break; 381 | case 's': { 382 | one_shot = 1; 383 | } break; 384 | case 'x': { 385 | unsigned char buf[32] = {0}; 386 | size_t len = strlen(optarg); 387 | if (len > sizeof(buf)) { 388 | fprintf(stderr, "hillclimb: seed too long (> 32 bytes)\n"); 389 | exit(EXIT_FAILURE); 390 | } 391 | memcpy(buf, optarg, len); 392 | rng[0] = load64(buf + 0); 393 | rng[1] = load64(buf + 8); 394 | rng[2] = load64(buf + 16); 395 | rng[3] = load64(buf + 24); 396 | mix64x4(rng); 397 | seeded = 1; 398 | } break; 399 | default: 400 | usage(stderr); 401 | exit(EXIT_FAILURE); 402 | } 403 | } 404 | 405 | if (invert) { 406 | if (generate) { 407 | fprintf(stderr, "hillclimb: -I requires -p\n"); 408 | exit(EXIT_FAILURE); 409 | } 410 | printf("uint32_t\nhash_r(uint32_t x)\n{\n"); 411 | for (int i = 0; i < HASHN * 2 + 1; i++) { 412 | switch (i & 1) { 413 | case 0: { 414 | int s = HASHN - i / 2; 415 | printf(" x ^="); 416 | for (int i = cur.s[s]; i < 32; i += cur.s[s]) 417 | printf(" %sx >> %d", i == cur.s[s] ? "" : "^ ", i); 418 | printf(";\n"); 419 | } break; 420 | case 1: { 421 | int c = HASHN - (i + 1) / 2; 422 | unsigned long inv = modinv32(cur.c[c]); 423 | printf(" x *= 0x%08lx;\n", inv); 424 | } break; 425 | } 426 | } 427 | printf(" return x;\n}\n"); 428 | exit(EXIT_SUCCESS); 429 | } 430 | 431 | if (evaluate) { 432 | if (generate) { 433 | fprintf(stderr, "hillclimb: -E requires -p\n"); 434 | exit(EXIT_FAILURE); 435 | } 436 | hash_print(&cur); 437 | printf(" = %.17g\n", exact_bias32(&cur)); 438 | exit(EXIT_SUCCESS); 439 | } 440 | 441 | if (!seeded) 442 | rng_init(rng); 443 | 444 | if (generate) 445 | hash_gen_strict(&cur, rng); 446 | 447 | for (;;) { 448 | int found = 0; 449 | struct hash best; 450 | double best_score; 451 | 452 | if (quiet < 2) 453 | hash_print(&cur); 454 | if (cur_score < 0) 455 | cur_score = exact_bias32(&cur); 456 | if (quiet < 2) 457 | printf(" = %.17g\n", cur_score); 458 | 459 | best = cur; 460 | best_score = cur_score; 461 | 462 | /* Explore around shifts */ 463 | for (int i = 0; i <= HASHN; i++) { 464 | /* In theory the shift could drift above 31 or below 1, but 465 | * in practice it would never get this far since these would 466 | * be terrible hashes. 467 | */ 468 | for (int d = -SHIFT_RANGE; d <= +SHIFT_RANGE; d++) { 469 | if (d == 0) continue; 470 | struct hash tmp = cur; 471 | tmp.s[i] += d; 472 | if (hash_equal(&tmp, &last)) continue; 473 | if (quiet <= 0) { 474 | printf(" "); 475 | hash_print(&tmp); 476 | } 477 | double score = exact_bias32(&tmp); 478 | if (quiet <= 0) 479 | printf(" = %.17g\n", score); 480 | if (score < best_score) { 481 | best_score = score; 482 | best = tmp; 483 | found = 1; 484 | } 485 | } 486 | } 487 | 488 | /* Explore around constants */ 489 | for (int i = 0; i < HASHN; i++) { 490 | for (int d = -CONST_RANGE; d <= +CONST_RANGE; d += 2) { 491 | if (d == 0) continue; 492 | struct hash tmp = cur; 493 | tmp.c[i] += d; 494 | if (hash_equal(&tmp, &last)) continue; 495 | if (quiet <= 0) { 496 | printf(" "); 497 | hash_print(&tmp); 498 | } 499 | double score = exact_bias32(&tmp); 500 | if (quiet <= 0) 501 | printf(" = %.17g\n", score); 502 | if (score < best_score) { 503 | best_score = score; 504 | best = tmp; 505 | found = 1; 506 | } 507 | } 508 | } 509 | 510 | if (found) { 511 | /* Move to the lowest item found */ 512 | if (quiet < 1) 513 | puts("CLIMB"); 514 | last = cur; 515 | cur = best; 516 | cur_score = best_score; 517 | } else if (one_shot) { 518 | /* Hit local minima, exit */ 519 | if (quiet < 1) 520 | puts("DONE"); 521 | hash_print(&cur); 522 | printf(" = %.17g\n", cur_score); 523 | break; 524 | } else { 525 | /* Hit local minima, reset */ 526 | if (quiet < 1) 527 | puts("RESET"); 528 | hash_print(&cur); 529 | printf(" = %.17g\n", cur_score); 530 | last.s[0] = 0; // set to invalid 531 | hash_gen_strict(&cur, rng); 532 | cur_score = -1; 533 | } 534 | } 535 | } 536 | -------------------------------------------------------------------------------- /hp16.c: -------------------------------------------------------------------------------- 1 | /* 16-bit hash prospector 2 | * 3 | * Unlike the 32-bit / 64-bit prospector, this implementation is fully 4 | * portable and will run on just about any system. It's also capable of 5 | * generating and evaluating 128kB s-boxes. 6 | * 7 | * Be mindful of C integer promotion rules when doing 16-bit operations. 8 | * For instance, on 32-bit implementations unsigned 16-bit operands will 9 | * be promoted to signed 32-bit integers, leading to incorrect results in 10 | * certain cases. The C programs printed by this program are careful to 11 | * promote 16-bit operations to "unsigned int" where needed. 12 | * 13 | * Since 16-bit hashes are likely to be needed on machines that do not 14 | * have efficient hardware multiplication or whose ISAs lack rotation 15 | * instructions, these operations may be optionally omitted during 16 | * exploration (-m, -r). 17 | * 18 | * This is free and unencumbered software released into the public domain. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define OPS_MAX 32 28 | 29 | enum hf_type { 30 | HF16_XOR, // x ^= imm 31 | HF16_MUL, // x *= imm (odd) 32 | HF16_ADD, // x += imm 33 | HF16_ROT, // x = (x << imm) | (x >> (16 - imm)) 34 | HF16_NOT, // x = ~x 35 | HF16_XORL, // x ^= x << imm 36 | HF16_XORR, // x ^= x >> imm 37 | HF16_ADDL, // x += x << imm 38 | HF16_SUBL, // x -= x << imm 39 | HF16_SBOX, // x = sbox[x] 40 | }; 41 | 42 | struct hf_op { 43 | enum hf_type type; 44 | unsigned imm; 45 | }; 46 | 47 | static unsigned short sbox[1L<<16]; 48 | 49 | static unsigned long long 50 | hash64(unsigned long long x) 51 | { 52 | x ^= x >> 32; 53 | x *= 0x25b751109e05be63; 54 | x &= 0xffffffffffffffff; 55 | x ^= x >> 32; 56 | x *= 0x2330e1453ed4b9b9; 57 | x &= 0xffffffffffffffff; 58 | x ^= x >> 32; 59 | return x; 60 | } 61 | 62 | static unsigned long 63 | u32(unsigned long long *s) 64 | { 65 | unsigned long r = *s >> 32; 66 | *s = *s*0x7c3c3267d015ceb5 + 1; 67 | r &= 0xffffffff; 68 | r ^= r >> 16; 69 | r *= 0x60857ba9; 70 | return r & 0xffffffff; 71 | } 72 | 73 | static unsigned long 74 | randint(unsigned long r, unsigned long long s[1]) 75 | { 76 | unsigned long long x = u32(s); 77 | unsigned long long m = x * r; 78 | unsigned long y = m & 0xffffffff; 79 | if (y < r) { 80 | unsigned long t = -r % r; 81 | while (y < t) { 82 | x = u32(s); 83 | m = x * r; 84 | y = m & 0xffffffff; 85 | } 86 | } 87 | return m >> 32; 88 | } 89 | 90 | static struct hf_op 91 | hf_gen(enum hf_type type, unsigned long long s[1]) 92 | { 93 | struct hf_op op; 94 | op.type = type; 95 | switch (op.type) { 96 | case HF16_NOT: 97 | case HF16_SBOX: op.imm = 0; break; 98 | case HF16_XOR: 99 | case HF16_ADD: op.imm = u32(s)>>16; break; 100 | case HF16_MUL: op.imm = u32(s)>>16 | 1; break; 101 | case HF16_ROT: 102 | case HF16_XORL: 103 | case HF16_XORR: 104 | case HF16_ADDL: 105 | case HF16_SUBL: op.imm = 1 + u32(s)%15; break; 106 | } 107 | return op; 108 | } 109 | 110 | /* May these operations be adjacent? */ 111 | static int 112 | hf_type_valid(enum hf_type a, enum hf_type b) 113 | { 114 | switch (a) { 115 | case HF16_NOT: 116 | case HF16_XOR: 117 | case HF16_MUL: 118 | case HF16_ADD: 119 | case HF16_ROT: 120 | case HF16_SBOX: return a != b; 121 | case HF16_XORL: 122 | case HF16_XORR: 123 | case HF16_ADDL: 124 | case HF16_SUBL: return 1; 125 | } 126 | return 0; 127 | } 128 | 129 | static void 130 | hf_genfunc(struct hf_op *ops, int n, unsigned long long s[1]) 131 | { 132 | for (int i = 0; i < n; i++) { 133 | do { 134 | enum hf_type type = u32(s) % HF16_SBOX; // (exclude sbox) 135 | ops[i] = hf_gen(type, s); 136 | } while (i > 0 && !hf_type_valid(ops[i-1].type, ops[i].type)); 137 | } 138 | } 139 | 140 | /* Indicate operation diffusion direction (+1 left, 0 none, -1 right). */ 141 | static int 142 | opdir(struct hf_op op) 143 | { 144 | switch (op.type) { 145 | case HF16_NOT: 146 | case HF16_XOR: 147 | case HF16_ADD: 148 | case HF16_SBOX: return 0; 149 | case HF16_MUL: 150 | case HF16_XORL: 151 | case HF16_ADDL: 152 | case HF16_SUBL: return +1; 153 | case HF16_XORR: return -1; 154 | case HF16_ROT: if (op.imm < 8) return +1; 155 | if (op.imm > 8) return -1; 156 | return 0; 157 | } 158 | abort(); 159 | } 160 | 161 | /* Prefer to alternate bit diffusion directions. */ 162 | static void 163 | hf_gensmart(struct hf_op *ops, int n, unsigned long long s[1]) 164 | { 165 | int dir = 0; 166 | for (int i = 0; i < n; i++) { 167 | int newdir; 168 | do { 169 | ops[i] = hf_gen(u32(s)%HF16_SBOX, s); 170 | newdir = opdir(ops[i]); 171 | } while (dir && newdir == dir); 172 | dir = newdir ? newdir : dir; 173 | } 174 | } 175 | 176 | static int 177 | popcount(int v) 178 | { 179 | // both GCC and Clang recognize this function as popcnt 180 | int c = 0; 181 | for (; v; c++) v &= v - 1; 182 | return c; 183 | } 184 | 185 | static void 186 | hf_genxormul(struct hf_op *ops, int n, unsigned long long s[1]) 187 | { 188 | ops[0].type = HF16_XORR; 189 | ops[0].imm = 1 + popcount(u32(s) >> 18); 190 | for (int i = 0; i < n; i++) { 191 | ops[2*i+1].type = HF16_MUL; 192 | ops[2*i+1].imm = u32(s)>>16 | 1; 193 | ops[2*i+2].type = HF16_XORR; 194 | ops[2*i+2].imm = 1 + popcount(u32(s) >> 18); 195 | } 196 | } 197 | 198 | /* An Add-Xor-Shift (AXS) hash alternates between diffusion leftward and 199 | * rightward where one direction is always xorshift and the other direction is 200 | * always add/sub-shift. 201 | * 202 | * x ^= x >> A; x += x << B; 203 | * x ^= x >> C; x -= x << D; 204 | * x ^= x >> E; x += x << F; 205 | * 206 | * This function generates all permutations of this construction in order. 207 | */ 208 | #define AXS_COUNT 182250000 209 | #define AXS_SIZE 6 210 | static void 211 | hf_genaxs(struct hf_op *ops, long i) 212 | { 213 | int shifts[] = { 214 | 1 + (i / 1) % 15, 215 | 1 + (i / 15) % 15, 216 | 1 + (i / 225) % 15, 217 | 1 + (i / 3375) % 15, 218 | 1 + (i / 50625) % 15, 219 | 1 + (i / 759375) % 15, 220 | }; 221 | int types[] = { 222 | (i / 11390625) % 2, 223 | (i / 22781250) % 2, 224 | (i / 45562500) % 2, 225 | }; 226 | int swap = (i / 91125000) % 2; 227 | for (int j = 0; j < 6; j += 2) { 228 | ops[j+ swap].type = types[j/2] ? HF16_ADDL : HF16_SUBL; 229 | ops[j+ swap].imm = shifts[j+0]; 230 | ops[j+!swap].type = HF16_XORR; 231 | ops[j+!swap].imm = shifts[j+1]; 232 | } 233 | } 234 | 235 | static unsigned 236 | hf_apply(const struct hf_op *ops, int n, unsigned x) 237 | { 238 | for (int i = 0; i < n; i++) { 239 | switch (ops[i].type) { 240 | case HF16_XOR: x ^= ops[i].imm; break; 241 | case HF16_MUL: x *= ops[i].imm; break; 242 | case HF16_ADD: x += ops[i].imm; break; 243 | case HF16_ROT: x = x<>(16 - ops[i].imm); break; 244 | case HF16_NOT: x = ~x; break; 245 | case HF16_XORL: x ^= x << ops[i].imm; break; 246 | case HF16_XORR: x ^= x >> ops[i].imm; break; 247 | case HF16_ADDL: x += x << ops[i].imm; break; 248 | case HF16_SUBL: x -= x << ops[i].imm; break; 249 | case HF16_SBOX: x = sbox[x]; break; 250 | } 251 | x &= 0xffff; 252 | } 253 | return x; 254 | } 255 | 256 | static void 257 | hf_print(const struct hf_op *ops, int n, FILE *f) 258 | { 259 | fprintf(f, "uint16_t hash(uint16_t x)\n"); 260 | fprintf(f, "{\n"); 261 | for (int i = 0; i < n; i++) { 262 | fputs(" ", f); 263 | switch (ops[i].type) { 264 | case HF16_XOR: 265 | fprintf(f, "x ^= 0x%04x;\n", ops[i].imm); 266 | break; 267 | case HF16_MUL: 268 | fprintf(f, "x *= 0x%04xU;\n", ops[i].imm); 269 | break; 270 | case HF16_ADD: 271 | fprintf(f, "x += 0x%04xU;\n", ops[i].imm); 272 | break; 273 | case HF16_ROT: 274 | fprintf(f, "x = (unsigned)x<<%d | x >>%d;\n", 275 | ops[i].imm, 16-ops[i].imm); 276 | break; 277 | case HF16_NOT: 278 | fprintf(f, "x = ~x;\n"); 279 | break; 280 | case HF16_XORL: 281 | fprintf(f, "x ^= (unsigned)x << %d;\n", ops[i].imm); 282 | break; 283 | case HF16_XORR: 284 | fprintf(f, "x ^= x >> %d;\n", ops[i].imm); 285 | break; 286 | case HF16_ADDL: 287 | fprintf(f, "x += (unsigned)x << %d;\n", ops[i].imm); 288 | break; 289 | case HF16_SUBL: 290 | fprintf(f, "x -= (unsigned)x << %d;\n", ops[i].imm); 291 | break; 292 | case HF16_SBOX: 293 | fprintf(f, "x = sbox[x];\n"); 294 | break; 295 | } 296 | } 297 | fprintf(f, " return x;\n"); 298 | fprintf(f, "}\n"); 299 | } 300 | 301 | static void 302 | sbox_init(void) 303 | { 304 | for (long i = 0; i < 1L<<16; i++) { 305 | sbox[i] = i; 306 | } 307 | } 308 | 309 | static void 310 | sbox_shuffle(unsigned long long s[1]) 311 | { 312 | for (long i = 0xffff; i > 0; i--) { 313 | long j = randint(i + 1, s); 314 | unsigned swap = sbox[i]; 315 | sbox[i] = sbox[j]; 316 | sbox[j] = swap; 317 | } 318 | } 319 | 320 | static void 321 | sbox_print(FILE *f) 322 | { 323 | for (long i = 0; i < 1L<<16; i++) { 324 | fprintf(f, "%04x%c", sbox[i], i % 16 == 15 ? '\n' : ' '); 325 | } 326 | } 327 | 328 | static double 329 | score(const struct hf_op *ops, int n) 330 | { 331 | long bins[32][32] = {{0}}; 332 | for (long x = 0; x < 1L<<16; x++) { 333 | unsigned h0 = hf_apply(ops, n, x); 334 | for (int j = 0; j < 16; j++) { 335 | unsigned bit = 1U << j; 336 | unsigned h1 = hf_apply(ops, n, x^bit); 337 | unsigned set = h0 ^ h1; 338 | for (int k = 0; k < 16; k++) 339 | bins[j][k] += (set >> k) & 1; 340 | } 341 | } 342 | 343 | double mean = 0.0; 344 | for (int j = 0; j < 16; j++) { 345 | for (int k = 0; k < 16; k++) { 346 | double diff = (bins[j][k] - (1<<15)) / (double)(1<<15); 347 | mean += (diff * diff) / (16 * 16); 348 | } 349 | } 350 | return sqrt(mean); 351 | } 352 | 353 | static int 354 | match(const struct hf_op *ops, int n, int types) 355 | { 356 | for (int i = 0; i < n; i++) { 357 | if (1< OPS_MAX) { 471 | fprintf(stderr, "fatal: invalid n, %s\n", xoptarg); 472 | usage(stderr); 473 | return 1; 474 | } 475 | n = tmp; 476 | break; 477 | case 'r': 478 | exclude |= 1< 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define ABI __attribute__((sysv_abi)) 16 | 17 | #define countof(a) ((int)(sizeof(a) / sizeof(0[a]))) 18 | 19 | static uint64_t 20 | xoroshiro128plus(uint64_t s[2]) 21 | { 22 | uint64_t s0 = s[0]; 23 | uint64_t s1 = s[1]; 24 | uint64_t result = s0 + s1; 25 | s1 ^= s0; 26 | s[0] = ((s0 << 24) | (s0 >> 40)) ^ s1 ^ (s1 << 16); 27 | s[1] = (s1 << 37) | (s1 >> 27); 28 | return result; 29 | } 30 | 31 | enum hf_type { 32 | /* 32 bits */ 33 | HF32_XOR, // x ^= const32 34 | HF32_MUL, // x *= const32 (odd) 35 | HF32_ADD, // x += const32 36 | HF32_ROT, // x = (x << const5) | (x >> (32 - const5)) 37 | HF32_NOT, // x = ~x 38 | HF32_BSWAP,// x = bswap32(x) 39 | HF32_XORL, // x ^= x << const5 40 | HF32_XORR, // x ^= x >> const5 41 | HF32_ADDL, // x += x << const5 42 | HF32_SUBL, // x -= x << const5 43 | /* 64 bits */ 44 | HF64_XOR, 45 | HF64_MUL, 46 | HF64_ADD, 47 | HF64_ROT, 48 | HF64_NOT, 49 | HF64_BSWAP, 50 | HF64_XORL, 51 | HF64_XORR, 52 | HF64_ADDL, 53 | HF64_SUBL, 54 | }; 55 | 56 | static const char hf_names[][8] = { 57 | [HF32_XOR] = "32xor", 58 | [HF32_MUL] = "32mul", 59 | [HF32_ADD] = "32add", 60 | [HF32_ROT] = "32rot", 61 | [HF32_NOT] = "32not", 62 | [HF32_BSWAP]= "32bswap", 63 | [HF32_XORL] = "32xorl", 64 | [HF32_XORR] = "32xorr", 65 | [HF32_ADDL] = "32addl", 66 | [HF32_SUBL] = "32subl", 67 | [HF64_XOR] = "64xor", 68 | [HF64_MUL] = "64mul", 69 | [HF64_ADD] = "64add", 70 | [HF64_ROT] = "64rot", 71 | [HF64_NOT] = "64not", 72 | [HF64_BSWAP]= "64bswap", 73 | [HF64_XORL] = "64xorl", 74 | [HF64_XORR] = "64xorr", 75 | [HF64_ADDL] = "64addl", 76 | [HF64_SUBL] = "64subl", 77 | }; 78 | 79 | #define FOP_LOCKED (1 << 0) 80 | struct hf_op { 81 | enum hf_type type; 82 | uint64_t constant; 83 | int flags; 84 | }; 85 | 86 | /* Randomize the constants of the given hash operation. 87 | */ 88 | static void 89 | hf_randomize(struct hf_op *op, uint64_t s[2]) 90 | { 91 | uint64_t r = xoroshiro128plus(s); 92 | switch (op->type) { 93 | case HF32_NOT: 94 | case HF64_NOT: 95 | case HF32_BSWAP: 96 | case HF64_BSWAP: 97 | op->constant = 0; 98 | break; 99 | case HF32_XOR: 100 | case HF32_ADD: 101 | op->constant = (uint32_t)r; 102 | break; 103 | case HF32_MUL: 104 | op->constant = (uint32_t)r | 1; 105 | break; 106 | case HF32_ROT: 107 | case HF32_XORL: 108 | case HF32_XORR: 109 | case HF32_ADDL: 110 | case HF32_SUBL: 111 | op->constant = 1 + r % 31; 112 | break; 113 | case HF64_XOR: 114 | case HF64_ADD: 115 | op->constant = r; 116 | break; 117 | case HF64_MUL: 118 | op->constant = r | 1; 119 | break; 120 | case HF64_ROT: 121 | case HF64_XORL: 122 | case HF64_XORR: 123 | case HF64_ADDL: 124 | case HF64_SUBL: 125 | op->constant = 1 + r % 63; 126 | break; 127 | } 128 | } 129 | 130 | #define F_U64 (1 << 0) 131 | #define F_TINY (1 << 1) // don't use big constants 132 | 133 | static void 134 | hf_gen(struct hf_op *op, uint64_t s[2], int flags) 135 | { 136 | uint64_t r = xoroshiro128plus(s); 137 | int min = flags & F_TINY ? 3 : 0; 138 | op->type = (r % (9 - min)) + min + (flags & F_U64 ? 9 : 0); 139 | hf_randomize(op, s); 140 | } 141 | 142 | /* Return 1 if these operations may be adjacent 143 | */ 144 | static int 145 | hf_type_valid(enum hf_type a, enum hf_type b) 146 | { 147 | switch (a) { 148 | case HF32_NOT: 149 | case HF32_BSWAP: 150 | case HF32_XOR: 151 | case HF32_MUL: 152 | case HF32_ADD: 153 | case HF32_ROT: 154 | case HF64_NOT: 155 | case HF64_BSWAP: 156 | case HF64_XOR: 157 | case HF64_MUL: 158 | case HF64_ADD: 159 | case HF64_ROT: 160 | return a != b; 161 | case HF32_XORL: 162 | case HF32_XORR: 163 | case HF32_ADDL: 164 | case HF32_SUBL: 165 | case HF64_XORL: 166 | case HF64_XORR: 167 | case HF64_ADDL: 168 | case HF64_SUBL: 169 | return 1; 170 | } 171 | abort(); 172 | } 173 | 174 | static void 175 | hf_genfunc(struct hf_op *ops, int n, int flags, uint64_t s[2]) 176 | { 177 | hf_gen(ops, s, flags); 178 | for (int i = 1; i < n; i++) { 179 | do { 180 | hf_gen(ops + i, s, flags); 181 | } while (!hf_type_valid(ops[i - 1].type, ops[i].type)); 182 | } 183 | } 184 | 185 | /* Randomize the parameters of the given functoin. 186 | */ 187 | static void 188 | hf_randfunc(struct hf_op *ops, int n, uint64_t s[2]) 189 | { 190 | for (int i = 0; i < n; i++) 191 | if (!(ops[i].flags & FOP_LOCKED)) 192 | hf_randomize(ops + i, s); 193 | } 194 | 195 | static void 196 | hf_print(const struct hf_op *op, char *buf) 197 | { 198 | unsigned long long c = op->constant; 199 | switch (op->type) { 200 | case HF32_NOT: 201 | case HF64_NOT: 202 | sprintf(buf, "x = ~x;"); 203 | break; 204 | case HF32_BSWAP: 205 | sprintf(buf, "x = __builtin_bswap32(x);"); 206 | break; 207 | case HF64_BSWAP: 208 | sprintf(buf, "x = __builtin_bswap64(x);"); 209 | break; 210 | case HF32_XOR: 211 | sprintf(buf, "x ^= 0x%08llx;", c); 212 | break; 213 | case HF32_MUL: 214 | sprintf(buf, "x *= 0x%08llx;", c); 215 | break; 216 | case HF32_ADD: 217 | sprintf(buf, "x += 0x%08llx;", c); 218 | break; 219 | case HF32_ROT: 220 | sprintf(buf, "x = (x << %llu) | (x >> %lld);", c, 32 - c); 221 | break; 222 | case HF32_XORL: 223 | sprintf(buf, "x ^= x << %llu;", c); 224 | break; 225 | case HF32_XORR: 226 | sprintf(buf, "x ^= x >> %llu;", c); 227 | break; 228 | case HF32_ADDL: 229 | sprintf(buf, "x += x << %llu;", c); 230 | break; 231 | case HF32_SUBL: 232 | sprintf(buf, "x -= x << %llu;", c); 233 | break; 234 | case HF64_XOR: 235 | sprintf(buf, "x ^= 0x%016llx;", c); 236 | break; 237 | case HF64_MUL: 238 | sprintf(buf, "x *= 0x%016llx;", c); 239 | break; 240 | case HF64_ADD: 241 | sprintf(buf, "x += 0x%016llx;", c); 242 | break; 243 | case HF64_ROT: 244 | sprintf(buf, "x = (x << %llu) | (x >> %lld);", c, 64 - c); 245 | break; 246 | case HF64_XORL: 247 | sprintf(buf, "x ^= x << %llu;", c); 248 | break; 249 | case HF64_XORR: 250 | sprintf(buf, "x ^= x >> %llu;", c); 251 | break; 252 | case HF64_ADDL: 253 | sprintf(buf, "x += x << %llu;", c); 254 | break; 255 | case HF64_SUBL: 256 | sprintf(buf, "x -= x << %llu;", c); 257 | break; 258 | } 259 | } 260 | 261 | static void 262 | hf_printfunc(const struct hf_op *ops, int n, FILE *f) 263 | { 264 | if (ops[0].type <= HF32_SUBL) 265 | fprintf(f, "uint32_t\nhash(uint32_t x)\n{\n"); 266 | else 267 | fprintf(f, "uint64_t\nhash(uint64_t x)\n{\n"); 268 | for (int i = 0; i < n; i++) { 269 | char buf[64]; 270 | hf_print(ops + i, buf); 271 | fprintf(f, " %s\n", buf); 272 | } 273 | fprintf(f, " return x;\n}\n"); 274 | } 275 | 276 | static unsigned char * 277 | hf_compile(const struct hf_op *ops, int n, unsigned char *buf) 278 | { 279 | if (ops[0].type <= HF32_SUBL) { 280 | /* mov eax, edi*/ 281 | *buf++ = 0x89; 282 | *buf++ = 0xf8; 283 | } else { 284 | /* mov rax, rdi*/ 285 | *buf++ = 0x48; 286 | *buf++ = 0x89; 287 | *buf++ = 0xf8; 288 | } 289 | 290 | for (int i = 0; i < n; i++) { 291 | switch (ops[i].type) { 292 | case HF32_NOT: 293 | /* not eax */ 294 | *buf++ = 0xf7; 295 | *buf++ = 0xd0; 296 | break; 297 | case HF32_BSWAP: 298 | /* bswap eax */ 299 | *buf++ = 0x0f; 300 | *buf++ = 0xc8; 301 | break; 302 | case HF32_XOR: 303 | /* xor eax, imm32 */ 304 | *buf++ = 0x35; 305 | *buf++ = ops[i].constant >> 0; 306 | *buf++ = ops[i].constant >> 8; 307 | *buf++ = ops[i].constant >> 16; 308 | *buf++ = ops[i].constant >> 24; 309 | break; 310 | case HF32_MUL: 311 | /* imul eax, eax, imm32 */ 312 | *buf++ = 0x69; 313 | *buf++ = 0xc0; 314 | *buf++ = ops[i].constant >> 0; 315 | *buf++ = ops[i].constant >> 8; 316 | *buf++ = ops[i].constant >> 16; 317 | *buf++ = ops[i].constant >> 24; 318 | break; 319 | case HF32_ADD: 320 | /* add eax, imm32 */ 321 | *buf++ = 0x05; 322 | *buf++ = ops[i].constant >> 0; 323 | *buf++ = ops[i].constant >> 8; 324 | *buf++ = ops[i].constant >> 16; 325 | *buf++ = ops[i].constant >> 24; 326 | break; 327 | case HF32_ROT: 328 | /* rol eax, imm8 */ 329 | *buf++ = 0xc1; 330 | *buf++ = 0xc0; 331 | *buf++ = ops[i].constant; 332 | break; 333 | case HF32_XORL: 334 | /* mov edi, eax */ 335 | *buf++ = 0x89; 336 | *buf++ = 0xc7; 337 | /* shl edi, imm8 */ 338 | *buf++ = 0xc1; 339 | *buf++ = 0xe7; 340 | *buf++ = ops[i].constant; 341 | /* xor eax, edi */ 342 | *buf++ = 0x31; 343 | *buf++ = 0xf8; 344 | break; 345 | case HF32_XORR: 346 | /* mov edi, eax */ 347 | *buf++ = 0x89; 348 | *buf++ = 0xc7; 349 | /* shr edi, imm8 */ 350 | *buf++ = 0xc1; 351 | *buf++ = 0xef; 352 | *buf++ = ops[i].constant; 353 | /* xor eax, edi */ 354 | *buf++ = 0x31; 355 | *buf++ = 0xf8; 356 | break; 357 | case HF32_ADDL: 358 | /* mov edi, eax */ 359 | *buf++ = 0x89; 360 | *buf++ = 0xc7; 361 | /* shl edi, imm8 */ 362 | *buf++ = 0xc1; 363 | *buf++ = 0xe7; 364 | *buf++ = ops[i].constant; 365 | /* add eax, edi */ 366 | *buf++ = 0x01; 367 | *buf++ = 0xf8; 368 | break; 369 | case HF32_SUBL: 370 | /* mov edi, eax */ 371 | *buf++ = 0x89; 372 | *buf++ = 0xc7; 373 | /* shl edi, imm8 */ 374 | *buf++ = 0xc1; 375 | *buf++ = 0xe7; 376 | *buf++ = ops[i].constant; 377 | /* sub eax, edi */ 378 | *buf++ = 0x29; 379 | *buf++ = 0xf8; 380 | break; 381 | case HF64_NOT: 382 | /* not rax */ 383 | *buf++ = 0x48; 384 | *buf++ = 0xf7; 385 | *buf++ = 0xd0; 386 | break; 387 | case HF64_BSWAP: 388 | /* bswap rax */ 389 | *buf++ = 0x48; 390 | *buf++ = 0x0f; 391 | *buf++ = 0xc8; 392 | break; 393 | case HF64_XOR: 394 | /* mov rdi, imm64 */ 395 | *buf++ = 0x48; 396 | *buf++ = 0xbf; 397 | *buf++ = ops[i].constant >> 0; 398 | *buf++ = ops[i].constant >> 8; 399 | *buf++ = ops[i].constant >> 16; 400 | *buf++ = ops[i].constant >> 24; 401 | *buf++ = ops[i].constant >> 32; 402 | *buf++ = ops[i].constant >> 40; 403 | *buf++ = ops[i].constant >> 48; 404 | *buf++ = ops[i].constant >> 56; 405 | /* xor rax, rdi */ 406 | *buf++ = 0x48; 407 | *buf++ = 0x31; 408 | *buf++ = 0xf8; 409 | break; 410 | case HF64_MUL: 411 | /* mov rdi, imm64 */ 412 | *buf++ = 0x48; 413 | *buf++ = 0xbf; 414 | *buf++ = ops[i].constant >> 0; 415 | *buf++ = ops[i].constant >> 8; 416 | *buf++ = ops[i].constant >> 16; 417 | *buf++ = ops[i].constant >> 24; 418 | *buf++ = ops[i].constant >> 32; 419 | *buf++ = ops[i].constant >> 40; 420 | *buf++ = ops[i].constant >> 48; 421 | *buf++ = ops[i].constant >> 56; 422 | /* imul rax, rdi */ 423 | *buf++ = 0x48; 424 | *buf++ = 0x0f; 425 | *buf++ = 0xaf; 426 | *buf++ = 0xc7; 427 | break; 428 | case HF64_ADD: 429 | /* mov rdi, imm64 */ 430 | *buf++ = 0x48; 431 | *buf++ = 0xbf; 432 | *buf++ = ops[i].constant >> 0; 433 | *buf++ = ops[i].constant >> 8; 434 | *buf++ = ops[i].constant >> 16; 435 | *buf++ = ops[i].constant >> 24; 436 | *buf++ = ops[i].constant >> 32; 437 | *buf++ = ops[i].constant >> 40; 438 | *buf++ = ops[i].constant >> 48; 439 | *buf++ = ops[i].constant >> 56; 440 | /* add rax, rdi */ 441 | *buf++ = 0x48; 442 | *buf++ = 0x01; 443 | *buf++ = 0xf8; 444 | break; 445 | case HF64_ROT: 446 | /* rol rax, imm8 */ 447 | *buf++ = 0x48; 448 | *buf++ = 0xc1; 449 | *buf++ = 0xc0; 450 | *buf++ = ops[i].constant; 451 | break; 452 | case HF64_XORL: 453 | /* mov edi, eax */ 454 | *buf++ = 0x48; 455 | *buf++ = 0x89; 456 | *buf++ = 0xc7; 457 | /* shl rdi, imm8 */ 458 | *buf++ = 0x48; 459 | *buf++ = 0xc1; 460 | *buf++ = 0xe7; 461 | *buf++ = ops[i].constant; 462 | /* xor rax, rdi */ 463 | *buf++ = 0x48; 464 | *buf++ = 0x31; 465 | *buf++ = 0xf8; 466 | break; 467 | case HF64_XORR: 468 | /* mov rdi, rax */ 469 | *buf++ = 0x48; 470 | *buf++ = 0x89; 471 | *buf++ = 0xc7; 472 | /* shr rdi, imm8 */ 473 | *buf++ = 0x48; 474 | *buf++ = 0xc1; 475 | *buf++ = 0xef; 476 | *buf++ = ops[i].constant; 477 | /* xor rax, rdi */ 478 | *buf++ = 0x48; 479 | *buf++ = 0x31; 480 | *buf++ = 0xf8; 481 | break; 482 | case HF64_ADDL: 483 | /* mov rdi, rax */ 484 | *buf++ = 0x48; 485 | *buf++ = 0x89; 486 | *buf++ = 0xc7; 487 | /* shl rdi, imm8 */ 488 | *buf++ = 0x48; 489 | *buf++ = 0xc1; 490 | *buf++ = 0xe7; 491 | *buf++ = ops[i].constant; 492 | /* add rax, rdi */ 493 | *buf++ = 0x48; 494 | *buf++ = 0x01; 495 | *buf++ = 0xf8; 496 | break; 497 | case HF64_SUBL: 498 | /* mov rdi, rax */ 499 | *buf++ = 0x48; 500 | *buf++ = 0x89; 501 | *buf++ = 0xc7; 502 | /* shl rdi, imm8 */ 503 | *buf++ = 0x48; 504 | *buf++ = 0xc1; 505 | *buf++ = 0xe7; 506 | *buf++ = ops[i].constant; 507 | /* sub rax, rdi */ 508 | *buf++ = 0x48; 509 | *buf++ = 0x29; 510 | *buf++ = 0xf8; 511 | break; 512 | } 513 | } 514 | 515 | /* ret */ 516 | *buf++ = 0xc3; 517 | return buf; 518 | } 519 | 520 | static void * 521 | execbuf_alloc(void) 522 | { 523 | int prot = PROT_READ | PROT_WRITE; 524 | int flags = MAP_PRIVATE | MAP_ANONYMOUS; 525 | void *p = mmap(NULL, 4096, prot, flags, -1, 0); 526 | if (p == MAP_FAILED) { 527 | fprintf(stderr, "prospector: %s\n", strerror(errno)); 528 | exit(EXIT_FAILURE); 529 | } 530 | return p; 531 | } 532 | 533 | static enum { 534 | WXR_UNKNOWN, WXR_ENABLED, WXR_DISABLED 535 | } wxr_enabled = WXR_UNKNOWN; 536 | 537 | static void 538 | execbuf_lock(void *buf) 539 | { 540 | switch (wxr_enabled) { 541 | case WXR_UNKNOWN: 542 | if (!mprotect(buf, 4096, PROT_READ | PROT_WRITE | PROT_EXEC)) { 543 | wxr_enabled = WXR_DISABLED; 544 | return; 545 | } 546 | wxr_enabled = WXR_ENABLED; 547 | /* FALLTHROUGH */ 548 | case WXR_ENABLED: 549 | if (mprotect(buf, 4096, PROT_READ | PROT_EXEC)) { 550 | fprintf(stderr, 551 | "prospector: mprotect(PROT_EXEC) failed: %s\n", 552 | strerror(errno)); 553 | exit(EXIT_FAILURE); 554 | } 555 | break; 556 | case WXR_DISABLED: 557 | break; 558 | } 559 | } 560 | 561 | static void 562 | execbuf_unlock(void *buf) 563 | { 564 | switch (wxr_enabled) { 565 | case WXR_UNKNOWN: 566 | abort(); 567 | case WXR_ENABLED: 568 | mprotect(buf, 4096, PROT_READ | PROT_WRITE); 569 | break; 570 | case WXR_DISABLED: 571 | break; 572 | } 573 | } 574 | 575 | /* Higher quality is slower but has more consistent results. */ 576 | static int score_quality = 18; 577 | 578 | /* Measures how each input bit affects each output bit. This measures 579 | * both bias and avalanche. 580 | */ 581 | static double 582 | estimate_bias32(uint32_t ABI (*f)(uint32_t), uint64_t rng[2]) 583 | { 584 | long n = 1L << score_quality; 585 | long bins[32][32] = {{0}}; 586 | for (long i = 0; i < n; i++) { 587 | uint32_t x = xoroshiro128plus(rng); 588 | uint32_t h0 = f(x); 589 | for (int j = 0; j < 32; j++) { 590 | uint32_t bit = UINT32_C(1) << j; 591 | uint32_t h1 = f(x ^ bit); 592 | uint32_t set = h0 ^ h1; 593 | for (int k = 0; k < 32; k++) 594 | bins[j][k] += (set >> k) & 1; 595 | } 596 | } 597 | double mean = 0; 598 | for (int j = 0; j < 32; j++) { 599 | for (int k = 0; k < 32; k++) { 600 | /* FIXME: normalize this somehow */ 601 | double diff = (bins[j][k] - n / 2) / (n / 2.0); 602 | mean += (diff * diff) / (32 * 32); 603 | } 604 | } 605 | return sqrt(mean) * 1000.0; 606 | } 607 | 608 | static double 609 | estimate_bias64(uint64_t ABI (*f)(uint64_t), uint64_t rng[2]) 610 | { 611 | long n = 1L << score_quality; 612 | long bins[64][64] = {{0}}; 613 | for (long i = 0; i < n; i++) { 614 | uint64_t x = xoroshiro128plus(rng); 615 | uint64_t h0 = f(x); 616 | for (int j = 0; j < 64; j++) { 617 | uint64_t bit = UINT64_C(1) << j; 618 | uint64_t h1 = f(x ^ bit); 619 | uint64_t set = h0 ^ h1; 620 | for (int k = 0; k < 64; k++) 621 | bins[j][k] += (set >> k) & 1; 622 | } 623 | } 624 | double mean = 0; 625 | for (int j = 0; j < 64; j++) { 626 | for (int k = 0; k < 64; k++) { 627 | /* FIXME: normalize this somehow */ 628 | double diff = (bins[j][k] - n / 2) / (n / 2.0); 629 | mean += (diff * diff) / (64 * 64); 630 | } 631 | } 632 | return sqrt(mean) * 1000.0; 633 | } 634 | 635 | #define EXACT_SPLIT 32 // must be power of two 636 | static double 637 | exact_bias32(uint32_t ABI (*f)(uint32_t)) 638 | { 639 | long long bins[32][32] = {{0}}; 640 | static const uint64_t range = (UINT64_C(1) << 32) / EXACT_SPLIT; 641 | #pragma omp parallel for 642 | for (int i = 0; i < EXACT_SPLIT; i++) { 643 | long long b[32][32] = {{0}}; 644 | for (uint64_t x = i * range; x < (i + 1) * range; x++) { 645 | uint32_t h0 = f(x); 646 | for (int j = 0; j < 32; j++) { 647 | uint32_t bit = UINT32_C(1) << j; 648 | uint32_t h1 = f(x ^ bit); 649 | uint32_t set = h0 ^ h1; 650 | for (int k = 0; k < 32; k++) 651 | b[j][k] += (set >> k) & 1; 652 | } 653 | } 654 | #pragma omp critical 655 | for (int j = 0; j < 32; j++) 656 | for (int k = 0; k < 32; k++) 657 | bins[j][k] += b[j][k]; 658 | } 659 | double mean = 0.0; 660 | for (int j = 0; j < 32; j++) { 661 | for (int k = 0; k < 32; k++) { 662 | double diff = (bins[j][k] - 2147483648L) / 2147483648.0; 663 | mean += (diff * diff) / (32 * 32); 664 | } 665 | } 666 | return sqrt(mean) * 1000.0; 667 | } 668 | 669 | static void 670 | usage(FILE *f) 671 | { 672 | fprintf(f, "usage: prospector " 673 | "[-E|L|S] [-4|-8] [-ehs] [-l lib] [-p pattern] [-r n:m] [-t x]\n"); 674 | fprintf(f, " -4 Generate 32-bit hash functions (default)\n"); 675 | fprintf(f, " -8 Generate 64-bit hash functions\n"); 676 | fprintf(f, " -e Measure bias exactly (requires -E)\n"); 677 | fprintf(f, " -h Print this help message\n"); 678 | fprintf(f, " -l ./lib.so Load hash() from a shared object\n"); 679 | fprintf(f, " -p pattern Search only a given pattern\n"); 680 | fprintf(f, " -q n Score quality knob (12-30, default: 18)\n"); 681 | fprintf(f, " -r n:m Use between n and m operations [3:6]\n"); 682 | fprintf(f, " -s Don't use large constants\n"); 683 | fprintf(f, " -t x Initial score threshold [10.0]\n"); 684 | fprintf(f, " -E Single evaluation mode (requires -p or -l)\n"); 685 | fprintf(f, " -S Hash function search mode (default)\n"); 686 | fprintf(f, " -L Enumerate output mode (requires -p or -l)\n"); 687 | } 688 | 689 | static int 690 | parse_operand(struct hf_op *op, char *buf) 691 | { 692 | op->flags |= FOP_LOCKED; 693 | switch (op->type) { 694 | case HF32_NOT: 695 | case HF64_NOT: 696 | case HF32_BSWAP: 697 | case HF64_BSWAP: 698 | return 0; 699 | case HF32_XOR: 700 | case HF32_MUL: 701 | case HF32_ADD: 702 | case HF64_XOR: 703 | case HF64_MUL: 704 | case HF64_ADD: 705 | op->constant = strtoull(buf, 0, 16); 706 | return 1; 707 | case HF32_ROT: 708 | case HF32_XORL: 709 | case HF32_XORR: 710 | case HF32_ADDL: 711 | case HF32_SUBL: 712 | case HF64_ROT: 713 | case HF64_XORL: 714 | case HF64_XORR: 715 | case HF64_ADDL: 716 | case HF64_SUBL: 717 | op->constant = atoi(buf); 718 | return 1; 719 | } 720 | return 0; 721 | } 722 | 723 | static int 724 | parse_template(struct hf_op *ops, int n, char *template, int flags) 725 | { 726 | int c = 0; 727 | int offset = flags & F_U64 ? HF64_XOR : 0; 728 | 729 | for (char *tok = strtok(template, ","); tok; tok = strtok(0, ",")) { 730 | if (c == n) return 0; 731 | int found = 0; 732 | size_t operand = strcspn(tok, ":"); 733 | int sep = tok[operand]; 734 | tok[operand] = 0; 735 | ops[c].flags = 0; 736 | for (int i = 0; i < countof(hf_names); i++) { 737 | if (!strcmp(hf_names[i] + 2, tok)) { 738 | found = 1; 739 | ops[c].type = i + offset; 740 | break; 741 | } 742 | } 743 | if (!found) 744 | return 0; 745 | if (sep == ':' && !parse_operand(ops + c, tok + operand + 1)) 746 | return 0; 747 | c++; 748 | } 749 | return c; 750 | } 751 | 752 | static void * 753 | load_function(const char *so) 754 | { 755 | void *handle = dlopen(so, RTLD_NOW); 756 | if (!handle) { 757 | fprintf(stderr, "prospector: could not load %s\n", so); 758 | exit(EXIT_FAILURE); 759 | } 760 | void *f = dlsym(handle, "hash"); 761 | if (!f) { 762 | fprintf(stderr, "prospector: could not find 'hash' in %s\n", so); 763 | exit(EXIT_FAILURE); 764 | } 765 | return f; 766 | } 767 | 768 | static uint64_t 769 | uepoch(void) 770 | { 771 | struct timeval tv; 772 | gettimeofday(&tv, NULL); 773 | return 1000000LL * tv.tv_sec + tv.tv_usec; 774 | } 775 | 776 | int 777 | main(int argc, char **argv) 778 | { 779 | int nops = 0; 780 | int min = 3; 781 | int max = 6; 782 | int flags = 0; 783 | int use_exact = 0; 784 | double best = 100.0; 785 | char *dynamic = 0; 786 | char *template = 0; 787 | struct hf_op ops[32]; 788 | void *buf = execbuf_alloc(); 789 | uint64_t rng[2] = {0x2a2bc037b59ff989, 0x6d7db86fa2f632ca}; 790 | 791 | enum {MODE_SEARCH, MODE_EVAL, MODE_LIST} mode = MODE_SEARCH; 792 | 793 | int option; 794 | while ((option = getopt(argc, argv, "48EehLl:q:r:st:p:")) != -1) { 795 | switch (option) { 796 | case '4': 797 | flags &= ~F_U64; 798 | break; 799 | case '8': 800 | flags |= F_U64; 801 | break; 802 | case 'E': 803 | mode = MODE_EVAL; 804 | break; 805 | case 'e': 806 | use_exact = 1; 807 | break; 808 | case 'h': usage(stdout); 809 | exit(EXIT_SUCCESS); 810 | break; 811 | case 'L': 812 | mode = MODE_LIST; 813 | break; 814 | case 'l': 815 | dynamic = optarg; 816 | break; 817 | case 'p': 818 | template = optarg; 819 | break; 820 | case 'r': 821 | if (sscanf(optarg, "%d:%d", &min, &max) != 2 || 822 | min < 1 || max > countof(ops) || min > max) { 823 | fprintf(stderr, "prospector: invalid range (-r): %s\n", 824 | optarg); 825 | exit(EXIT_FAILURE); 826 | } 827 | break; 828 | case 'q': 829 | score_quality = atoi(optarg); 830 | if (score_quality < 12 || score_quality > 30) { 831 | fprintf(stderr, "prospector: invalid quality: %s\n", 832 | optarg); 833 | exit(EXIT_FAILURE); 834 | } 835 | break; 836 | case 'S': 837 | mode = MODE_SEARCH; 838 | break; 839 | case 's': 840 | flags |= F_TINY; 841 | break; 842 | case 't': 843 | best = strtod(optarg, 0); 844 | break; 845 | default: 846 | usage(stderr); 847 | exit(EXIT_FAILURE); 848 | } 849 | } 850 | 851 | /* Get a unique seed */ 852 | FILE *urandom = fopen("/dev/urandom", "rb"); 853 | if (urandom) { 854 | if (!fread(rng, sizeof(rng), 1, urandom)) { 855 | fputs("prospector: failed to read /dev/urandom\n", stderr); 856 | exit(EXIT_FAILURE); 857 | } 858 | fclose(urandom); 859 | } 860 | 861 | if (template) { 862 | nops = parse_template(ops, countof(ops), template, flags); 863 | if (!nops) { 864 | fprintf(stderr, "prospector: invalid template\n"); 865 | exit(EXIT_FAILURE); 866 | } 867 | } 868 | 869 | if (mode == MODE_EVAL) { 870 | double bias; 871 | void *hashptr = 0; 872 | if (template) { 873 | hf_randfunc(ops, nops, rng); 874 | hf_compile(ops, nops, buf); 875 | execbuf_lock(buf); 876 | hashptr = buf; 877 | } else if (dynamic) { 878 | hashptr = load_function(dynamic); 879 | } else { 880 | fprintf(stderr, "prospector: must supply -p or -l\n"); 881 | exit(EXIT_FAILURE); 882 | } 883 | 884 | uint64_t nhash; 885 | uint64_t beg = uepoch(); 886 | if (flags & F_U64) { 887 | uint64_t ABI (*hash)(uint64_t) = hashptr; 888 | if (use_exact) 889 | fputs("warning: no exact bias for 64-bit\n", stderr); 890 | bias = estimate_bias64(hash, rng); 891 | nhash = (1L << score_quality) * 33; 892 | } else { 893 | uint32_t ABI (*hash)(uint32_t) = hashptr; 894 | if (use_exact) { 895 | bias = exact_bias32(hash); 896 | nhash = (1LL << 32) * 33; 897 | } else { 898 | bias = estimate_bias32(hash, rng); 899 | nhash = (1L << score_quality) * 65; 900 | } 901 | } 902 | uint64_t end = uepoch(); 903 | printf("bias = %.17g\n", bias); 904 | printf("speed = %.3f nsec / hash\n", (end - beg) * 1000.0 / nhash); 905 | return 0; 906 | } 907 | 908 | if (mode == MODE_LIST) { 909 | void *hashptr = 0; 910 | if (template) { 911 | hf_randfunc(ops, nops, rng); 912 | hf_compile(ops, nops, buf); 913 | execbuf_lock(buf); 914 | hashptr = buf; 915 | } else if (dynamic) { 916 | hashptr = load_function(dynamic); 917 | } else { 918 | fprintf(stderr, "prospector: must supply -p or -l\n"); 919 | exit(EXIT_FAILURE); 920 | } 921 | 922 | if (flags & F_U64) { 923 | uint64_t ABI (*hash)(uint64_t) = hashptr; 924 | uint64_t i = 0; 925 | do 926 | printf("%016llx %016llx\n", 927 | (unsigned long long)i, 928 | (unsigned long long)hash(i)); 929 | while (++i); 930 | } else { 931 | uint32_t ABI (*hash)(uint32_t) = hashptr; 932 | uint32_t i = 0; 933 | do 934 | printf("%08lx %08lx\n", 935 | (unsigned long)i, 936 | (unsigned long)hash(i)); 937 | while (++i); 938 | } 939 | return 0; 940 | } 941 | 942 | for (;;) { 943 | /* Generate */ 944 | if (template) { 945 | hf_randfunc(ops, nops, rng); 946 | } else { 947 | nops = min + xoroshiro128plus(rng) % (max - min + 1); 948 | hf_genfunc(ops, nops, flags, rng); 949 | } 950 | 951 | /* Evaluate */ 952 | double score; 953 | hf_compile(ops, nops, buf); 954 | execbuf_lock(buf); 955 | if (flags & F_U64) { 956 | uint64_t ABI (*hash)(uint64_t) = (void *)buf; 957 | score = estimate_bias64(hash, rng); 958 | } else { 959 | uint32_t ABI (*hash)(uint32_t) = (void *)buf; 960 | score = estimate_bias32(hash, rng); 961 | } 962 | execbuf_unlock(buf); 963 | 964 | /* Compare */ 965 | if (score < best) { 966 | printf("// score = %.17g\n", score); 967 | hf_printfunc(ops, nops, stdout); 968 | fflush(stdout); 969 | best = score; 970 | } 971 | } 972 | } 973 | -------------------------------------------------------------------------------- /tests/degski64.c: -------------------------------------------------------------------------------- 1 | /* H2 32-bit hash 2 | * https://github.com/h2database/h2database 3 | * src/test/org/h2/test/store/CalculateHashConstant.java 4 | */ 5 | #include 6 | 7 | __attribute__((sysv_abi)) 8 | uint64_t 9 | hash(uint64_t x) 10 | { 11 | x ^= x >> 32; 12 | x *= 0xd6e8feb86659fd93; 13 | x ^= x >> 32; 14 | x *= 0xd6e8feb86659fd93; 15 | x ^= x >> 32; 16 | return x; 17 | } 18 | 19 | __attribute__((sysv_abi)) 20 | uint64_t 21 | unhash(uint64_t x) 22 | { 23 | x ^= x >> 32; 24 | x *= 0xcfee444d8b59a89b; 25 | x ^= x >> 32; 26 | x *= 0xcfee444d8b59a89b; 27 | x ^= x >> 32; 28 | return x; 29 | } 30 | -------------------------------------------------------------------------------- /tests/h2hash32.c: -------------------------------------------------------------------------------- 1 | /* H2 32-bit hash 2 | * https://github.com/h2database/h2database 3 | * src/test/org/h2/test/store/CalculateHashConstant.java 4 | */ 5 | #include 6 | 7 | // exact bias: 1.4249702882580686 8 | __attribute__((sysv_abi)) 9 | uint32_t 10 | hash(uint32_t x) 11 | { 12 | x ^= x >> 16; 13 | x *= 0x45d9f3b; 14 | x ^= x >> 16; 15 | x *= 0x45d9f3b; 16 | x ^= x >> 16; 17 | return x; 18 | } 19 | 20 | __attribute__((sysv_abi)) 21 | uint32_t 22 | unhash(uint32_t x) 23 | { 24 | x ^= x >> 16; 25 | x *= 0x119de1f3; 26 | x ^= x >> 16; 27 | x *= 0x119de1f3; 28 | x ^= x >> 16; 29 | return x; 30 | } 31 | -------------------------------------------------------------------------------- /tests/hash32shift.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // exact bias: 44.000700486813841 4 | __attribute__((sysv_abi)) 5 | uint32_t 6 | hash(uint32_t x) 7 | { 8 | x = ~x + (x << 15); 9 | x ^= x >> 12; 10 | x += x << 2; 11 | x ^= x >> 4; 12 | x *= 2057; 13 | x ^= x >> 16; 14 | return x; 15 | } 16 | -------------------------------------------------------------------------------- /tests/murmurhash3_finalizer32.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // exact bias: 0.26398543281818287 4 | __attribute__((sysv_abi)) 5 | uint32_t 6 | hash(uint32_t x) 7 | { 8 | x ^= x >> 16; 9 | x *= 0x85ebca6b; 10 | x ^= x >> 13; 11 | x *= 0xc2b2ae35; 12 | x ^= x >> 16; 13 | return x; 14 | } 15 | -------------------------------------------------------------------------------- /tests/splitmix64.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((sysv_abi)) 4 | uint64_t 5 | hash(uint64_t x) 6 | { 7 | x += 0x9e3779b97f4a7c15; 8 | x ^= (x >> 30); 9 | x *= 0xbf58476d1ce4e5b9; 10 | x ^= (x >> 27); 11 | x *= 0x94d049bb133111eb; 12 | x ^= (x >> 31); 13 | return x; 14 | } 15 | --------------------------------------------------------------------------------