Skip to content

Commit

Permalink
Add inverses for jenkins_rev_mix32, twang_mix64
Browse files Browse the repository at this point in the history
Test Plan: test added

Reviewed By: soren@fb.com

FB internal diff: D538703
  • Loading branch information
tudor committed Aug 5, 2012
1 parent 0061634 commit 6641583
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 11 deletions.
66 changes: 59 additions & 7 deletions folly/Hash.h
Expand Up @@ -69,13 +69,32 @@ size_t hash_combine(const T& t, const Ts&... ts) {
*/

inline uint64_t twang_mix64(uint64_t key) {
key = (~key) + (key << 21);
key = (~key) + (key << 21); // key *= (1 << 21) - 1; key -= 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8);
key = key + (key << 3) + (key << 8); // key *= 1 + (1 << 3) + (1 << 8)
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4);
key = key + (key << 2) + (key << 4); // key *= 1 + (1 << 2) + (1 << 4)
key = key ^ (key >> 28);
key = key + (key << 31);
key = key + (key << 31); // key *= 1 + (1 << 31)
return key;
}

/*
* Inverse of twang_mix64
*
* Note that twang_unmix64 is significantly slower than twang_mix64.
*/

inline uint64_t twang_unmix64(uint64_t key) {
// See the comments in jenkins_rev_unmix32 for an explanation as to how this
// was generated
key *= 4611686016279904257U;
key ^= (key >> 28) ^ (key >> 56);
key *= 14933078535860113213U;
key ^= (key >> 14) ^ (key >> 28) ^ (key >> 42) ^ (key >> 56);
key *= 15244667743933553977U;
key ^= (key >> 24) ^ (key >> 48);
key = (key + 1) * 9223367638806167551U;
return key;
}

Expand All @@ -98,17 +117,50 @@ inline uint32_t twang_32from64(uint64_t key) {
*/

inline uint32_t jenkins_rev_mix32(uint32_t key) {
key += (key << 12);
key += (key << 12); // key *= (1 + (1 << 12))
key ^= (key >> 22);
key += (key << 4);
key += (key << 4); // key *= (1 + (1 << 4))
key ^= (key >> 9);
key += (key << 10);
key += (key << 10); // key *= (1 + (1 << 10))
key ^= (key >> 2);
// key *= (1 + (1 << 7)) * (1 + (1 << 12))
key += (key << 7);
key += (key << 12);
return key;
}

/*
* Inverse of jenkins_rev_mix32
*
* Note that jenkinks_rev_unmix32 is significantly slower than
* jenkins_rev_mix32.
*/

inline uint32_t jenkins_rev_unmix32(uint32_t key) {
// These are the modular multiplicative inverses (in Z_2^32) of the
// multiplication factors in jenkins_rev_mix32, in reverse order. They were
// computed using the Extended Euclidean algorithm, see
// http://en.wikipedia.org/wiki/Modular_multiplicative_inverse
key *= 2364026753U;

// The inverse of a ^= (a >> n) is
// b = a
// for (int i = n; i < 32; i += n) {
// b ^= (a >> i);
// }
key ^=
(key >> 2) ^ (key >> 4) ^ (key >> 6) ^ (key >> 8) ^
(key >> 10) ^ (key >> 12) ^ (key >> 14) ^ (key >> 16) ^
(key >> 18) ^ (key >> 20) ^ (key >> 22) ^ (key >> 24) ^
(key >> 26) ^ (key >> 28) ^ (key >> 30);
key *= 3222273025U;
key ^= (key >> 9) ^ (key >> 18) ^ (key >> 27);
key *= 4042322161U;
key ^= (key >> 22);
key *= 16773121U;
return key;
}

/*
* Fowler / Noll / Vo (FNV) Hash
* http://www.isthe.com/chongo/tech/comp/fnv/
Expand Down
44 changes: 40 additions & 4 deletions folly/test/HashTest.cpp
Expand Up @@ -102,11 +102,29 @@ TEST(Hash, Hsieh32) {
TEST(Hash, TWang_Mix64) {
uint64_t i1 = 0x78a87873e2d31dafULL;
uint64_t i1_res = 3389151152926383528ULL;
EXPECT_EQ(twang_mix64(i1), i1_res);
EXPECT_EQ(i1_res, twang_mix64(i1));
EXPECT_EQ(i1, twang_unmix64(i1_res));

uint64_t i2 = 0x0123456789abcdefULL;
uint64_t i2_res = 3061460455458984563ull;
EXPECT_EQ(twang_mix64(i2), i2_res);
EXPECT_EQ(i2_res, twang_mix64(i2));
EXPECT_EQ(i2, twang_unmix64(i2_res));
}

namespace {
void checkTWang(uint64_t r) {
uint64_t result = twang_mix64(r);
EXPECT_EQ(r, twang_unmix64(result));
}
} // namespace

TEST(Hash, TWang_Unmix64) {
// We'll try (1 << i), (1 << i) + 1, (1 << i) - 1
for (int i = 1; i < 64; i++) {
checkTWang((1U << i) - 1);
checkTWang(1U << i);
checkTWang((1U << i) + 1);
}
}

TEST(Hash, TWang_32From64) {
Expand All @@ -122,11 +140,29 @@ TEST(Hash, TWang_32From64) {
TEST(Hash, Jenkins_Rev_Mix32) {
uint32_t i1 = 3805486511ul;
uint32_t i1_res = 381808021ul;
EXPECT_EQ(jenkins_rev_mix32(i1), i1_res);
EXPECT_EQ(i1_res, jenkins_rev_mix32(i1));
EXPECT_EQ(i1, jenkins_rev_unmix32(i1_res));

uint32_t i2 = 2309737967ul;
uint32_t i2_res = 1834777923ul;
EXPECT_EQ(jenkins_rev_mix32(i2), i2_res);
EXPECT_EQ(i2_res, jenkins_rev_mix32(i2));
EXPECT_EQ(i2, jenkins_rev_unmix32(i2_res));
}

namespace {
void checkJenkins(uint32_t r) {
uint32_t result = jenkins_rev_mix32(r);
EXPECT_EQ(r, jenkins_rev_unmix32(result));
}
} // namespace

TEST(Hash, Jenkins_Rev_Unmix32) {
// We'll try (1 << i), (1 << i) + 1, (1 << i) - 1
for (int i = 1; i < 32; i++) {
checkJenkins((1U << i) - 1);
checkJenkins(1U << i);
checkJenkins((1U << i) + 1);
}
}

TEST(Hash, hasher) {
Expand Down

0 comments on commit 6641583

Please sign in to comment.