#include <cstdio>
#include <inttypes.h>
#include "discohash.h"
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#else
#define FORCE_INLINE inline __attribute__((always_inline))
#endif
const int STATE = 32;
const int STATE64 = STATE >> 3;
const int STATEM = STATE-1;
const int HSTATE64M = (STATE64 >> 1)-1;
const int STATE64M = STATE64-1;
uint8_t disco_buf[STATE] = {0};
uint64_t P = 0xFFFFFFFFFFFFFFFF - 58;
uint64_t Q = 13166748625691186689U;
uint8_t *ds8 = (uint8_t *)disco_buf;
uint32_t *ds32 = (uint32_t *)disco_buf;
uint64_t *ds = (uint64_t *)disco_buf;
FORCE_INLINE uint64_t rot( uint64_t v, int n)
{
n = n & 63U;
if (n)
v = (v >> n) | (v << (64-n));
return v;
}
FORCE_INLINE uint64_t rot32( uint64_t v, int n)
{
n = n & 31U;
if (n)
v = (v >> n) | (v << (64-n));
return v;
}
FORCE_INLINE uint8_t rot8( uint8_t v, int n)
{
n = n & 7U;
if (n)
v = (v >> n) | (v << (8-n));
return v;
}
FORCE_INLINE void mixA()
{
int i = ds32[0] & 1;
int j = ds32[3] & 3;
ds[0] = rot(ds[0], ds[i]);
ds[i] *= (P % (ds32[j] + 1) + 1);
ds[1] += ds32[j];
}
FORCE_INLINE void mix(const int A)
{
const int B = A+1;
ds[A] *= P;
ds[A] = rot(ds[A], 23);
ds[A] *= Q;
ds[B] ^= ds[A];
ds[B] *= P;
ds[B] = rot(ds[B], 23);
ds[B] *= Q;
}
FORCE_INLINE void round( const uint64_t * m64, const uint8_t * m8, int len )
{
int index = 0;
int sindex = 0;
uint64_t counter = 0xfaccadaccad09997;
uint8_t counter8 = 137;
for( int Len = len >> 3; index < Len; index++) {
ds[sindex] += rot(m64[index] + index + counter + 1, 23);
counter += ~m64[index] + 1;
if ( sindex == HSTATE64M ) {
mix(0);
} else if ( sindex == STATE64M ) {
mix(2);
sindex = -1;
}
sindex++;
}
mix(1);
index <<= 3;
sindex = index&(STATEM);
for( ; index < len; index++) {
ds8[sindex] += rot8(m8[index] + index + counter8 + 1, 23);
counter8 += ~m8[sindex] + 1;
mix(index%STATE64M);
if ( sindex >= STATEM ) {
sindex = -1;
}
sindex++;
}
mix(0);
mix(1);
mix(2);
}
void BEBB4185_64 ( const void * key, int len, unsigned seed, void * out )
{
const uint8_t *key8Arr = (uint8_t *)key;
const uint64_t *key64Arr = (uint64_t *)key;
const uint8_t seedbuf[16] = {0};
const uint8_t *seed8Arr = (uint8_t *)seedbuf;
const uint64_t *seed64Arr = (uint64_t *)seedbuf;
uint32_t *seed32Arr = (uint32_t *)seedbuf;
seed32Arr[0] = 0xc5550690;
seed32Arr[0] -= seed;
seed32Arr[1] = 1 + seed;
seed32Arr[2] = ~(1 - seed);
seed32Arr[3] = (1+seed) * 0xf00dacca;
ds[0] = 0x123456789abcdef0;
ds[1] = 0x0fedcba987654321;
ds[2] = 0xaccadacca80081e5;
ds[3] = 0xf00baaf00f00baaa;
round( key64Arr, key8Arr, len );
round( seed64Arr, seed8Arr, 16 );
round( ds, ds8, STATE );
const uint8_t output[STATE] = {0};
uint64_t *h = (uint64_t *)output;
h[0] = ds[2];
h[1] = ds[3];
h[0] += h[1];
((uint64_t *)out)[0] = h[0];
}