Add inverses for jenkins_rev_mix32, twang_mix64
authorTudor Bosman <tudorb@fb.com>
Fri, 3 Aug 2012 01:23:23 +0000 (18:23 -0700)
committerTudor Bosman <tudorb@fb.com>
Sun, 5 Aug 2012 17:40:19 +0000 (10:40 -0700)
Test Plan: test added

Reviewed By: soren@fb.com

FB internal diff: D538703

folly/Hash.h
folly/test/HashTest.cpp

index a6e330465a9b323f859155e9527687c5a620d3ae..efe0f047194015b89e25b29fe544642662e28118 100644 (file)
@@ -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,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/
index f06f8b84495c52523330bed7a5274c720a074424..954b6b6d299a6ade605a04381f776a89e2717ba5 100644 (file)
@@ -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) {