constexpr size_t _StringLengthLoop(const char * const str, const size_t size)
    noexcept {
  return (str[size] == '\0') ? size : _StringLengthLoop(str, size + 1);
}

constexpr inline size_t _StringLength(const char * const str) noexcept {
  return _StringLengthLoop(str, 0);
}

constexpr uint32_t _Murmur3Rotate_32(const uint32_t target,
    const uint8_t rotation) noexcept {
  return (target << rotation) | (target >> (32 - rotation));
}

constexpr uint32_t _Murmur3ShiftXor_32(const uint32_t hash, const size_t shift)
    noexcept {
  return hash ^ (hash >> shift);
}

constexpr uint32_t _Murmur3Last_32(const size_t len, const uint32_t hash)
    noexcept {
  return _Murmur3ShiftXor_32(0xc2b2ae35 * _Murmur3ShiftXor_32(
                        0x85ebca6b * _Murmur3ShiftXor_32(
                        hash ^ len, 16), 13), 16);
}

constexpr uint32_t _Murmur3Tail1_32(const char data, const size_t len,
    const uint32_t hash, const uint32_t constant) noexcept {
  return _Murmur3Last_32(len, hash ^ (0x1b873593 * _Murmur3Rotate_32(
    0xcc9e2d51 * (constant ^ static_cast<uint8_t>(data)), 15)));
}

constexpr uint32_t _Murmur3Tail2_32(const char data[2], const size_t len,
    const uint32_t hash, const uint32_t constant) noexcept {
  return _Murmur3Tail1_32(data[0], len, hash,
    constant ^ (static_cast<uint8_t>(data[1]) << 8));
}

constexpr uint32_t _Murmur3Tail3_32(const char data[3], const size_t len,
    const uint32_t hash) noexcept {
  return _Murmur3Tail2_32(data, len, hash,
    0 ^ (static_cast<uint8_t>(data[2]) << 16));
}

constexpr uint32_t _Murmur3Rest_32(const char *const data,
    const size_t len, const uint32_t hash) noexcept {
  return ((len & 3) == 3) ? _Murmur3Tail3_32(&data[len - 3], len, hash) :
         ((len & 3) == 2) ? _Murmur3Tail2_32(&data[len - 2], len, hash, 0) :
         ((len & 3) == 1) ? _Murmur3Tail1_32(data[len - 1], len, hash, 0) :
                            _Murmur3Last_32(len, hash);
}

constexpr inline uint32_t _Murmur3Load_32(const char *const data,
    const size_t i) noexcept {
  return static_cast<uint32_t>(data[(i * sizeof(uint32_t)) + 3]) << 24 |
         static_cast<uint32_t>(data[(i * sizeof(uint32_t)) + 2]) << 16 |
         static_cast<uint32_t>(data[(i * sizeof(uint32_t)) + 1]) <<  8 |
         static_cast<uint32_t>(data[(i * sizeof(uint32_t)) + 0]) <<  0;
}

constexpr inline uint32_t _Murmur3Update_32(const uint32_t hash,
    const uint32_t update) noexcept {
  return 0xe6546b64 + (5 * _Murmur3Rotate_32(hash ^ (
    0x1b873593 * _Murmur3Rotate_32(0xcc9e2d51 * update, 15)), 13));
}

constexpr uint32_t _Murmur3Loop_32(const char *const data,
    const size_t len, const uint32_t hash, const size_t i = 0) noexcept {
  return (i  < (len / 4)) ?
          _Murmur3Loop_32(data, len,
            _Murmur3Update_32(hash, _Murmur3Load_32(data, i)), i + 1) :
          _Murmur3Rest_32(data, len, hash);
}

constexpr inline uint32_t Murmur3_32(const char *const key, const size_t length,
    const uint32_t seed) noexcept {
  return _Murmur3Loop_32(key, length, seed);
}

constexpr inline uint32_t Murmur3_32(const char *const str,
    const uint32_t seed) noexcept {
  return Murmur3_32(str, _StringLength(str), seed);
}