[m-rev.] for review: reimpelement the hash_pred for ints in Mercury

Julien Fischer jfischer at opturion.com
Fri Sep 1 10:30:21 AEST 2017


For review by anyone.


---------------------------

Reimplement the hash_pred for ints in Mercury.

library/hash_pred.m:
     As above: implementing this in Mercury means that we now have a working
     implementation for the non-C backends as well.   (I have kept Ralph's
     original C implementation about for now.)  Also use this implementation
     for the uint type.

     Provide a link to the archived version of Thomas Wang's website.

Julien.

diff --git a/library/hash_table.m b/library/hash_table.m
index 204082a99..4e97f37ec 100644
--- a/library/hash_table.m
+++ b/library/hash_table.m
@@ -266,6 +266,7 @@
      % Default hash_preds for ints and strings and everything (buwahahaha!)
      %
  :- pred int_hash(int::in, int::out) is det.
+:- pred uint_hash(uint::in, int::out) is det.
  :- pred float_hash(float::in, int::out) is det.
  :- pred char_hash(char::in, int::out) is det.
  :- pred string_hash(string::in, int::out) is det.
@@ -274,6 +275,14 @@
  %---------------------------------------------------------------------------%
  %---------------------------------------------------------------------------%

+:- implementation.
+:- interface.
+
+% For the purposes of comparison I've left Ralph's foreign_proc version
+% of the integer hash functions intact (for now).
+
+:- pred cint_hash(int::in, int::out) is det.
+
  :- implementation.

  :- import_module bool.
@@ -284,8 +293,11 @@
  :- import_module list.
  :- import_module pair.
  :- import_module string.
+:- import_module uint.
  :- import_module univ.

+
+
  %---------------------------------------------------------------------------%

  :- interface.
@@ -789,15 +801,24 @@ fold3_p(P, List, !A, !B, !C) :-

  %---------------------------------------------------------------------------%

-    % From http://www.concentric.net/~Ttwang/tech/inthash.htm
-    %   public int hash32shift(int key)
-    %   public long hash64shift(long key)
-    %
+% The integer hash functions below are originally from:
+%
+%  http://www.concentric.net/~Ttwang/tech/inthash.htm
+%
+% The above link is now dead; the last version can be found at:
+%
+%  https://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm
+%
+% The algorithms from that page that we use are:
+%
+%   public int hash32shiftmult(int key)
+%   public long hash64shift(long key)
+
  :- pragma foreign_proc("C",
-    int_hash(N::in, H::out),
-    [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+    cint_hash(N::in, H::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
  "
-    const int c2 = 0x27d4eb2d; /* a prime or an odd constant */
+    const int c2 = 0x27d4eb2d; // a prime or an odd constant
      MR_Unsigned key;

      key = N;
@@ -809,11 +830,11 @@ fold3_p(P, List, !A, !B, !C) :-
          key = key * c2;
          key = key ^ (key >> 15);
      } else {
-        key = (~key) + (key << 21); /* key = (key << 21) - key - 1; */
+        key = (~key) + (key << 21); // key = (key << 21) - key - 1;
          key = key ^ (key >> 24);
-        key = (key + (key << 3)) + (key << 8); /* key * 265 */
+        key = (key + (key << 3)) + (key << 8);  //  key * 265
          key = key ^ (key >> 14);
-        key = (key + (key << 2)) + (key << 4); /* key * 21 */
+        key = (key + (key << 2)) + (key << 4);  // key * 21
          key = key ^ (key >> 28);
          key = key + (key << 31);
      }
@@ -821,9 +842,28 @@ fold3_p(P, List, !A, !B, !C) :-
      H = key;
  ").

-int_hash(N, N).
-    % For use on non-C backends.
-    % There are almost certainly better ones out there...
+int_hash(Key, Hash) :-
+    UKey = uint.cast_from_int(Key),
+    uint_hash(UKey, Hash).
+
+uint_hash(!.Key, Hash) :-
+    C2 = 0x_27d4_eb2d_u,
+    ( if bits_per_uint = 4 then
+        !:Key = (!.Key `xor` 61_u) `xor` (!.Key >> 16),
+        !:Key = !.Key + (!.Key << 3),
+        !:Key = !.Key `xor` (!.Key >> 4),
+        !:Key = !.Key * C2,
+        !:Key = !.Key `xor` (!.Key >> 15)
+    else
+        !:Key = (\ !.Key) + (!.Key << 21),
+        !:Key = !.Key `xor` (!.Key >> 24),
+        !:Key = (!.Key + (!.Key << 3)) + (!.Key << 8),
+        !:Key = !.Key `xor` (!.Key >> 14),
+        !:Key = (!.Key + (!.Key << 2)) + (!.Key << 4),
+        !:Key = !.Key `xor` (!.Key >> 28),
+        !:Key = !.Key + (!.Key << 31)
+    ),
+    Hash = uint.cast_to_int(!.Key).

  float_hash(F, float.hash(F)).
      % There are almost certainly better ones out there...


More information about the reviews mailing list