#ifdef DOC_BUILD_INTERNAL
#endif
#include <math.h>
#include "ohash_functions.h"
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
     __BYTE_ORDER == __LITTLE_ENDIAN) || \
    (defined(i386) || defined(__i386__) || defined(__i486__) || \
     defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
#define HASH_LITTLE_ENDIAN 1
#define HASH_BIG_ENDIAN 0
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
       __BYTE_ORDER == __BIG_ENDIAN) || \
      (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
#define HASH_LITTLE_ENDIAN 0
#define HASH_BIG_ENDIAN 1
#else
#define HASH_LITTLE_ENDIAN 0
#define HASH_BIG_ENDIAN 0
#endif
#define ohash_L3Rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
#define ohash_L3Mix(a,b,c) \
{ \
  a -= c; a ^= ohash_L3Rot(c, 4); c += b; \
  b -= a; b ^= ohash_L3Rot(a, 6); a += c; \
  c -= b; c ^= ohash_L3Rot(b, 8); b += a; \
  a -= c; a ^= ohash_L3Rot(c,16); c += b; \
  b -= a; b ^= ohash_L3Rot(a,19); a += c; \
  c -= b; c ^= ohash_L3Rot(b, 4); b += a; \
}
#define ohash_L3Final(a,b,c) \
{ \
  c ^= b; c -= ohash_L3Rot(b,14); \
  a ^= c; a -= ohash_L3Rot(c,11); \
  b ^= a; b -= ohash_L3Rot(a,25); \
  c ^= b; c -= ohash_L3Rot(b,16); \
  a ^= c; a -= ohash_L3Rot(c,4); \
  b ^= a; b -= ohash_L3Rot(a,14); \
  c ^= b; c -= ohash_L3Rot(b,24); \
}
uint32_t ohash_L3HashTriple(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t Seed)
{
  uint32_t x, y, s;
  s = Seed;
  x = 0x9e3779b9 + A;
  y = 0x9e3779b9 + B;
  ohash_L3Mix(x, y, s);
  x += C;
  ohash_L3Final(x, y, s);
  return s;
}
uint32_t ohash_L3Hashword(const uint32_t *Key, size_t Length, uint32_t Initval)
{
  uint32_t a,b,c;
  a = b = c = 0xdeadbeef + (((uint32_t)Length)<<2) + Initval;
  while (Length > 3)
  {
    a += Key[0];
    b += Key[1];
    c += Key[2];
    ohash_L3Mix(a,b,c);
    Length -= 3;
    Key += 3;
  }
  switch(Length)
  {
  case 3 : c+=Key[2];
  case 2 : b+=Key[1];
  case 1 : a+=Key[0];
    ohash_L3Final(a,b,c);
  case 0:
    break;
  }
  return c;
}
uint32_t ohash_L3Hashlittle( const void *Key, size_t Length, uint32_t Initval)
{
  uint32_t a,b,c;
  union { const void *ptr; size_t i; } u;
  a = b = c = 0xdeadbeef + ((uint32_t)Length) + Initval;
  u.ptr = Key;
  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
    const uint32_t *k = (const uint32_t *)Key;
    while (Length > 12)
    {
      a += k[0];
      b += k[1];
      c += k[2];
      ohash_L3Mix(a,b,c);
      Length -= 12;
      k += 3;
    }
#ifndef VALGRIND
    switch(Length)
    {
    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
    case 8 : b+=k[1]; a+=k[0]; break;
    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
    case 6 : b+=k[1]&0xffff; a+=k[0]; break;
    case 5 : b+=k[1]&0xff; a+=k[0]; break;
    case 4 : a+=k[0]; break;
    case 3 : a+=k[0]&0xffffff; break;
    case 2 : a+=k[0]&0xffff; break;
    case 1 : a+=k[0]&0xff; break;
    case 0 : return c;
    }
#else
    const uint8_t *k8;
    k8 = (const uint8_t *)k;
    switch(Length)
    {
    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
    case 11: c+=((uint32_t)k8[10])<<16;
    case 10: c+=((uint32_t)k8[9])<<8;
    case 9 : c+=k8[8];
    case 8 : b+=k[1]; a+=k[0]; break;
    case 7 : b+=((uint32_t)k8[6])<<16;
    case 6 : b+=((uint32_t)k8[5])<<8;
    case 5 : b+=k8[4];
    case 4 : a+=k[0]; break;
    case 3 : a+=((uint32_t)k8[2])<<16;
    case 2 : a+=((uint32_t)k8[1])<<8;
    case 1 : a+=k8[0]; break;
    case 0 : return c;
    }
#endif
  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
    const uint16_t *k = (const uint16_t *)Key;
    const uint8_t *k8;
    while (Length > 12)
    {
      a += k[0] + (((uint32_t)k[1])<<16);
      b += k[2] + (((uint32_t)k[3])<<16);
      c += k[4] + (((uint32_t)k[5])<<16);
      ohash_L3Mix(a,b,c);
      Length -= 12;
      k += 6;
    }
    k8 = (const uint8_t *)k;
    switch(Length)
    {
    case 12: c+=k[4]+(((uint32_t)k[5])<<16);
             b+=k[2]+(((uint32_t)k[3])<<16);
             a+=k[0]+(((uint32_t)k[1])<<16);
             break;
    case 11: c+=((uint32_t)k8[10])<<16;
    case 10: c+=k[4];
             b+=k[2]+(((uint32_t)k[3])<<16);
             a+=k[0]+(((uint32_t)k[1])<<16);
             break;
    case 9 : c+=k8[8];
    case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
             a+=k[0]+(((uint32_t)k[1])<<16);
             break;
    case 7 : b+=((uint32_t)k8[6])<<16;
    case 6 : b+=k[2];
             a+=k[0]+(((uint32_t)k[1])<<16);
             break;
    case 5 : b+=k8[4];
    case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
             break;
    case 3 : a+=((uint32_t)k8[2])<<16;
    case 2 : a+=k[0];
             break;
    case 1 : a+=k8[0];
             break;
    case 0 : return c;
    }
  } else {
    const uint8_t *k = (const uint8_t *)Key;
    while (Length > 12)
    {
      a += k[0];
      a += ((uint32_t)k[1])<<8;
      a += ((uint32_t)k[2])<<16;
      a += ((uint32_t)k[3])<<24;
      b += k[4];
      b += ((uint32_t)k[5])<<8;
      b += ((uint32_t)k[6])<<16;
      b += ((uint32_t)k[7])<<24;
      c += k[8];
      c += ((uint32_t)k[9])<<8;
      c += ((uint32_t)k[10])<<16;
      c += ((uint32_t)k[11])<<24;
      ohash_L3Mix(a,b,c);
      Length -= 12;
      k += 12;
    }
    switch(Length)
    {
    case 12: c+=((uint32_t)k[11])<<24;
    case 11: c+=((uint32_t)k[10])<<16;
    case 10: c+=((uint32_t)k[9])<<8;
    case 9 : c+=k[8];
    case 8 : b+=((uint32_t)k[7])<<24;
    case 7 : b+=((uint32_t)k[6])<<16;
    case 6 : b+=((uint32_t)k[5])<<8;
    case 5 : b+=k[4];
    case 4 : a+=((uint32_t)k[3])<<24;
    case 3 : a+=((uint32_t)k[2])<<16;
    case 2 : a+=((uint32_t)k[1])<<8;
    case 1 : a+=k[0];
             break;
    case 0 : return c;
    }
  }
  ohash_L3Final(a,b,c);
  return c;
}
