1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
#pragma once


#include <limits><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <concepts><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <cstdint><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.


#include "internal/dev_env.hpp"
#include "internal/types.hpp"

#include "numeric/bit.hpp"


namespace uni {


template<std::unsigned_integral R, std::integral T>
constexpr R shrink(T x) noexcept(NO_EXCEPT) {
    constexpr int DIGITS_R = std::numeric_limits<R>::digits;<--- 'DIGITS_R' is assigned value 'std::numeric_limits::digits' here.
    constexpr int DIGITS_T = std::numeric_limits<R>::digits;<--- 'DIGITS_T' is assigned value 'std::numeric_limits::digits' here.

    REPD(digits, DIGITS_R, DIGITS_T, DIGITS_R) {
        x = (x >> digits) ^ uni::lower_bits(x, digits);
    }

    return x;
}


// Thanks to: https://gist.github.com/badboy/6267743
template<class T>
constexpr u32 hash32(T x) {
    if constexpr(std::integral<T>) {
        if constexpr(std::signed_integral<T>) return hash32(to_unsigned(x));

        constexpr int DIGITS_T = std::numeric_limits<T>::digits;

        if constexpr(DIGITS_T <= 32) {
            auto h = static_cast<u32>(x);

            h = ~h + (h << 15);
            h ^= (h >> 12);
            h += (h << 2);
            h ^= (h >> 4);
            h *= 2057;
            h ^= (h >> 16);

            return h;
        }
        else if constexpr(DIGITS_T <= 64) {
            auto h = static_cast<u64>(x);

            h = (~h) + (h << 18);
            h ^= (h >> 31);
            h *= 21;
            h ^= (h >> 11);
            h += (h << 6);
            h ^= (h >> 22);

            return static_cast<u32>(h);
        }
        else {
            return hash32(hash64(x));
        }
    }
    else {
        return hash32(std::hash<T>{}(x));
    }
}


template<class T>
constexpr u64 hash64(T x) {
    if constexpr(std::integral<T>) {
        if constexpr(std::signed_integral<T>) return hash64(to_unsigned(x));

        constexpr int DIGITS_T = std::numeric_limits<T>::digits;

        if constexpr(DIGITS_T <= 64) {
            auto h = static_cast<u64>(x);

            h = (~h) + (h << 21);
            h ^= (h >> 24);
            h *= 265;
            h ^= (h >> 14);
            h *= 21;
            h ^= (h >> 28);
            h += h << 31;

            return h;
        }
        else {
            return hash64(shrink<u64>(x));
        }
    }
    else {
        return hash64(std::hash<T>{}(x));
    }
}


template<class T>
struct hash {
    inline constexpr auto operator()(const T& key) const noexcept(NO_EXCEPT) {
        return static_cast<std::size_t>(uni::hash64(key));
    }
};


} // namespace uni