#ifndef tabulation_included
#define tabulation_included
#include <stdint.h>
#include <assert.h>
static inline uint8_t take08(const uint8_t *p){ uint8_t v; memcpy(&v, p, 1); return v; }
static inline uint16_t take16(const uint8_t *p){ uint16_t v; memcpy(&v, p, 2); return v; }
static inline uint32_t take32(const uint8_t *p){ uint32_t v; memcpy(&v, p, 4); return v; }
static inline uint64_t take64(const uint8_t *p){ uint64_t v; memcpy(&v, p, 8); return v; }
const static uint64_t MERSENNE_31 = (1ull << 31) - 1;
const static int CHAR_SIZE = 8;
const static int BLOCK_SIZE_32 = 1<<8;
static uint64_t multiply_shift_random_64[BLOCK_SIZE_32];
static uint32_t multiply_shift_a_64;
static uint64_t multiply_shift_b_64;
static int32_t tabulation_32[32/CHAR_SIZE][1<<CHAR_SIZE];
static int have_broken_rand = 0;
static uint32_t combine31(uint32_t h, uint32_t x, uint32_t a) {
uint64_t temp = (uint64_t)h * x + a;
return ((uint32_t)temp & MERSENNE_31) + (uint32_t)(temp >> 31);
}
static uint32_t finalize_tabulation_32(uint32_t h) {
uint32_t tab = 0;
for (int i = 0; i < 32/CHAR_SIZE; i++, h >>= CHAR_SIZE)
tab ^= tabulation_32[i][h & ((1<<CHAR_SIZE)-1)];
return tab;
}
static uint32_t tabulation_32_hash(const void * key, int len_bytes, uint32_t seed) {
const uint8_t* buf = (const uint8_t*) key;
int len_words_32 = len_bytes/4;
int len_blocks_32 = len_words_32/BLOCK_SIZE_32;
uint32_t h = len_bytes ^ seed;
for (int b = 0; b < len_blocks_32; b++) {
uint32_t block_hash = 0;
for (int i = 0; i < BLOCK_SIZE_32; i++, buf += 4)
block_hash ^= multiply_shift_random_64[i] * take32(buf) >> 32;
h = combine31(h, multiply_shift_a_64, block_hash >> 2);
}
int remaining_words = len_words_32 % BLOCK_SIZE_32;
for (int i = 0; i < remaining_words; i++, buf += 4)
h ^= multiply_shift_random_64[i] * take32(buf) >> 32;
int remaining_bytes = len_bytes % 4;
if (remaining_bytes) {
uint32_t last = 0;
if (remaining_bytes & 2) {last = take16(buf); buf += 2;}
if (remaining_bytes & 1) {last = (last << 8) | take08(buf);}
h ^= multiply_shift_b_64 * last >> 32;
}
return finalize_tabulation_32(h);
}
static uint64_t tab_rand64() {
uint64_t r = 0;
for (int i = 0; i < 4; i++) {
r <<= 16;
r ^= rand();
}
return r;
}
static void tabulation_32_seed_init(size_t &seed) {
srand(seed);
multiply_shift_a_64 = tab_rand64() & ((1ull<<30)-1);
if (!multiply_shift_a_64) {
multiply_shift_a_64 = tab_rand64() & ((1ull<<30)-1);
}
if (!multiply_shift_a_64) {
have_broken_rand = 1;
multiply_shift_a_64 = 0xababababbeafcafeULL & ((1ull<<30)-1);
}
multiply_shift_b_64 = tab_rand64();
if (!multiply_shift_b_64) {
multiply_shift_b_64 = have_broken_rand ? 0xdeadbeef : tab_rand64();
}
for (int i = 0; i < BLOCK_SIZE_32; i++) {
multiply_shift_random_64[i] = tab_rand64();
if (!multiply_shift_random_64[i]) {
multiply_shift_random_64[i] = have_broken_rand ? 0xdeadbeef : tab_rand64();
}
}
for (int i = 0; i < 32/CHAR_SIZE; i++)
for (int j = 0; j < 1<<CHAR_SIZE; j++)
tabulation_32[i][j] = tab_rand64();
}
#ifdef __SIZEOF_INT128__
const static uint64_t TAB_MERSENNE_61 = (1ull << 61) - 1;
const static int TAB_BLOCK_SIZE = 1<<8;
static __uint128_t tab_multiply_shift_random[TAB_BLOCK_SIZE];
static __uint128_t tab_multiply_shift_a;
static __uint128_t tab_multiply_shift_b;
static int64_t tabulation[64/CHAR_SIZE][1<<CHAR_SIZE];
static uint64_t combine61(uint64_t h, uint64_t x, uint64_t a) {
__uint128_t temp = (__uint128_t)h * x + a;
return ((uint64_t)temp & TAB_MERSENNE_61) + (uint64_t)(temp >> 61);
}
static uint64_t finalize_tabulation(uint64_t h) {
uint64_t tab = 0;
for (int i = 0; i < 64/CHAR_SIZE; i++, h >>= CHAR_SIZE)
tab ^= tabulation[i][h % (1<<CHAR_SIZE)];
return tab;
}
static uint64_t tabulation_hash(const void * key, int len_bytes, uint32_t seed) {
const uint8_t* buf = (const uint8_t*) key;
uint64_t h = len_bytes ^ seed ^ (seed << 8);
if (len_bytes >= 8) {
const int len_words = len_bytes/8;
if (len_words >= TAB_BLOCK_SIZE) {
const int len_blocks = len_words/TAB_BLOCK_SIZE;
for (int b = 0; b < len_blocks; b++) {
uint64_t block_hash = 0;
for (int i = 0; i < TAB_BLOCK_SIZE; i++, buf += 8) {
block_hash ^= (tab_multiply_shift_random[i] * take64(buf)) >> 64;
}
h = combine61(h, tab_multiply_shift_a, block_hash >> 4);
}
}
const int remaining_words = len_words % TAB_BLOCK_SIZE;
for (int i = 0; i < remaining_words; i++, buf += 8)
h ^= tab_multiply_shift_random[i] * take64(buf) >> 64;
}
const int remaining_bytes = len_bytes % 8;
if (remaining_bytes) {
uint64_t last = 0;
if (remaining_bytes & 4) {last = take32(buf); buf += 4;}
if (remaining_bytes & 2) {last = (last << 16) | take16(buf); buf += 2;}
if (remaining_bytes & 1) {last = (last << 8) | take08(buf);}
h ^= tab_multiply_shift_b * last >> 64;
}
return finalize_tabulation(h);
}
static __uint128_t tab_rand128() {
return (__uint128_t)tab_rand64() << 64 | tab_rand64();
}
static void tabulation_seed_init(size_t &seed) {
srand(seed);
tab_multiply_shift_a = tab_rand128() & ((1ull<<60)-1);
tab_multiply_shift_b = tab_rand128();
if (!tab_multiply_shift_a) tab_multiply_shift_a = tab_rand128() & ((1ull<<60)-1);
if (!tab_multiply_shift_a) {
have_broken_rand = 1;
tab_multiply_shift_a = 0xababababbeafcafeULL & ((1ull<<60)-1);
}
if (!tab_multiply_shift_b) tab_multiply_shift_b = tab_rand128();
if (!tab_multiply_shift_b) {
have_broken_rand = 1;
tab_multiply_shift_b++;
}
for (int i = 0; i < TAB_BLOCK_SIZE; i++) {
tab_multiply_shift_random[i] = tab_rand128();
if (!tab_multiply_shift_random[i])
tab_multiply_shift_random[i] = 0x12345678;
}
if (have_broken_rand)
assert(TAB_BLOCK_SIZE >= 64/CHAR_SIZE);
for (int i = 0; i < 64/CHAR_SIZE; i++)
for (int j = 0; j < 1<<CHAR_SIZE; j++)
tabulation[i][j] = have_broken_rand ? tab_multiply_shift_random[i] : tab_rand128();
}
#endif
#endif