Permalink
Browse files

Add inverses for jenkins_rev_mix32, twang_mix64

Test Plan: test added

Reviewed By: soren@fb.com

FB internal diff: D538703
  • Loading branch information...
1 parent 0061634 commit 66415838e0a96f2947fd990cec20e237c5408248 @tudor tudor committed Aug 3, 2012
Showing with 99 additions and 11 deletions.
  1. +59 −7 folly/Hash.h
  2. +40 −4 folly/test/HashTest.cpp
View
@@ -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;
}
@@ -98,18 +117,51 @@ 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/
*/
View
@@ -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) {
@@ -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) {

0 comments on commit 6641583

Please sign in to comment.