How to write and call std::hash? - for gmp's mpz_class and mpz_t

1

I think most of the work is done here, just a little detail is missing at the end. Read on.

I'm trying to write the glue code for using MurmurHash3 to hash big integers (mpz_t and mpz_class) of the GMP library in C++. I do this in order to later use them in a std::unordered_map<mpz_class, int>.

I want the code to compile in a useful way for 32 bit and for 64 bit systems and to be easily extensible when 128 bit systems are required. Therefor I've written the MurmurHash3_size_t() function which calls the right hash function of MurmurHash3 and then converts the result to size_t. I assume that size_t is of the correct bit size in terms of 32/64/128 bit systems. (I don't know if this assumption is useful.) This part of the code compiles nicely.

The problem arises when I want to define the std::hash function. I get a compiler error for my code (see comment in code). How to write these std::hash functions correctly and how to call them?

(click to view MurmurHash3.h)

File hash_mpz.cpp:

#include "hash_mpz.h"
#include <gmpxx.h>
#include "MurmurHash3.h"

size_t MurmurHash3_size_t(const void *key, int len, uint32_t seed) {

#if SIZE_MAX==0xffffffff
    size_t result;
    MurmurHash3_x86_32(key, len, seed, &result);
    return result;

#elif SIZE_MAX==0xffffffffffffffff
    size_t result[2];
    MurmurHash3_x64_128(key, len, seed, &result);
    return result[0] ^ result[1];

#else
#error cannot determine correct version of MurmurHash3, because SIZE_MAX is neither 0xffffffff nor 0xffffffffffffffff
#endif

}

namespace std {

size_t hash<mpz_t>::operator()(const mpz_t &x) const {
    // found 1846872219 by randomly hitting digits on my keyboard
    return MurmurHash3_size_t(x->_mp_d, x->_mp_size * sizeof(mp_limb_t), 1846872219);
}

size_t hash<mpz_class>::operator()(const mpz_class &x) const {
    // compiler error in next statement
    // error: no matching function for call to ‘std::hash<__mpz_struct [1]>::operator()(mpz_srcptr)’
    return hash<mpz_t>::operator()(x.get_mpz_t());
}

}
c++
hash
unordered-map
gmp
asked on Stack Overflow Jun 18, 2020 by Daniel S. • edited Jun 22, 2020 by Daniel S.

1 Answer

2

Found a solution which works for me:

namespace std {

size_t hash<mpz_srcptr>::operator()(const mpz_srcptr x) const {
    // found 1846872219 by randomly typing digits on my keyboard
    return MurmurHash3_size_t(x->_mp_d, x->_mp_size * sizeof(mp_limb_t),
            1846872219);
}

size_t hash<mpz_t>::operator()(const mpz_t &x) const {
    return hash<mpz_srcptr> { }((mpz_srcptr) x);
}

size_t hash<mpz_class>::operator()(const mpz_class &x) const {
    return hash<mpz_srcptr> { }(x.get_mpz_t());
}

}

Then you can use the hash function as follows:

#include <iostream>
#include <gmpxx.h>
#include <unordered_map>

#include "hash_mpz.h"

using namespace std;

int main() {
    mpz_class a;

    mpz_ui_pow_ui(a.get_mpz_t(), 168, 16);

    cout << "a      : " << a << endl;
    cout << "hash(a): " << (hash<mpz_class> { }(a)) << endl;

    unordered_map<mpz_class, int> map;
    map[a] = 2;
    cout << "map[a] : " << map[a] << endl;

    return 0;
}

Output:

a      : 402669288768856477614113920779288576
hash(a): 11740158581999522595
map[a] : 2

Comments are appreciated.

answered on Stack Overflow Jun 18, 2020 by Daniel S.

User contributions licensed under CC BY-SA 3.0