// -*- mode: c++ -*-
#ifndef __IMP_MURMURHASH3__42357806
#define __IMP_MURMURHASH3__42357806

#include <cstdint>

namespace imp {
  namespace hashing {
    namespace detail {
      template <class T>
      constexpr T rotl(const T x, const int r) noexcept
      {
          return (x << r) | (x >> ((sizeof(T) * 8) - r));
      }

      constexpr uint32_t get_block(const char *x) noexcept
      {
          auto block = 0u;
          block |= x[0];
          block |= x[1] << 8;
          block |= x[2] << 16;
          block |= x[3] << 24;
          return block;
      }

      constexpr uint32_t get_block(const char16_t *x) noexcept
      {
          auto block = 0u;
          block |= x[0];
          block |= x[2] << 16;
          return block;
      }

      constexpr uint32_t get_block(const char32_t *x) noexcept
      {
          return x[0];
      }

      constexpr uint32_t get_block(const wchar_t *x) noexcept
      {
          return x[0];
      }

      constexpr std::uint32_t fmix32(std::uint32_t h) noexcept
      {
          h ^= h >> 16;
          h *= 0x85ebca6bu;
          h ^= h >> 13;
          h *= 0xc2b2ae35u;
          h ^= h >> 16;

          return h;
      }

      constexpr std::uint64_t fmix64(std::uint64_t k) noexcept
      {
          k ^= k >> 33;
          k *= 0xff51afd7ed558ccdull;
          k ^= k >> 33;
          k *= 0xc4ceb9fe1a85ec53ull;
          k ^= k >> 33;

          return k;
      }
    }

    constexpr auto murmur3_default_seed = 42;

    /* Original name: MurmurHash3_x86_32 */
    template <class T>
    constexpr int murmur3_32(const T *key, int len, const uint32_t seed = murmur3_default_seed) noexcept
    {
        constexpr auto c1 = 0xcc9e2d51u;
        constexpr auto c2 = 0x1b873593u;


        len *= sizeof(T);
        auto nblocks = len / 4;

        auto h1 = seed;

        //----------
        // body
        {
            auto begin = key;
            auto end = begin + nblocks;

            for (; begin != end; begin += sizeof(T))
            {
                auto k1 = detail::get_block(begin);

                k1 *= c1;
                k1 = detail::rotl(k1, 15);
                k1 *= c2;

                h1 ^= k1;
                h1 = detail::rotl(h1, 13);
                h1 = h1 * 5 + 0xe6546b64u;
            }
        }

        //----------
        // tail
        {
            auto tail = key + nblocks * 4;
            auto k1 = 0u;

            switch (len & 3)
            {
            case 3:
                k1 ^= tail[2] << 16;
//                [[fallthrough]]

            case 2:
                k1 ^= tail[1] << 8;
//                [[fallthrough]]

            case 1:
                k1 ^= tail[0];
                k1 *= c1;
                k1 = detail::rotl(k1, 15);
                k1 *= c2;
                h1 ^= k1;
                break;

            default:
                break;
            }
        }

        //----------
        // finalization

        h1 ^= len;

        h1 = detail::fmix32(h1);

        return h1;
    }
  }
}

#endif //__IMP_MURMURHASH3__42357806
