-----------------------------------------------------------------------------
-- |
-- Module      :  Data.HashTable.Multiplicative
-- Copyright   :  (c) Jan-Willem Maessen 2006
--                (c) The University of Glasgow 2003
-- License     :  BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer  :  libraries@haskell.org
-- Stability   :  provisional
-- Portability :  portable
--
-- Multiplicative hashing.  Separates hashing technique from table
-- management technique.  Multiplicative hashing appears to be faster
-- in practice than the modulus-based hashing technique used in the
-- original Data.HashTable and found in Data.HashTable.Modulus.
--
-----------------------------------------------------------------------------
module Data.HashTable.Multiplicative (hashInt, hashString) where

import Data.Bits
import Data.Int  ( Int32, Int64 )
import Data.List ( foldl' )
import Data.Char ( ord )

-- -----------------------------------------------------------------------------
-- Sample hash functions

-- $hash_functions
--
-- This implementation of hash tables uses the low-order /n/ bits of the hash
-- value for a key, where /n/ varies as the hash table grows.  A good hash
-- function therefore will give an even distribution regardless of /n/.
--
-- If your keyspace is integrals such that the low-order bits between
-- keys are highly variable, then you could get away with using 'id'
-- as the hash function.
--
-- We provide some sample hash functions for 'Int' and 'String' below.

golden :: Int32
golden = -1640531527

-- | A sample (and useful) hash function for Int and Int32,
-- implemented by extracting the uppermost 32 bits of the 64-bit
-- result of multiplying by a 32-bit constant.  The constant is from
-- Knuth, derived from the golden ratio:
-- > golden = round ((sqrt 5 - 1) * 2^31) :: Int
hashInt :: Int -> Int32
hashInt x = mulHi (fromIntegral x) golden

-- hi 32 bits of a x-bit * 32 bit -> 64-bit multiply
mulHi :: Int32 -> Int32 -> Int32
mulHi a b = fromIntegral (r `shiftR` 32)
  where r :: Int64
        r = fromIntegral a * fromIntegral b :: Int64

-- | A sample hash function for Strings.  We keep multiplying by the
-- golden ratio and adding.  The implementation is:
--
-- > hashString = foldl' f 0
-- >   where f m c = fromIntegral (ord c) + mulHi m golden
--
-- Note that this has not been extensively tested for reasonability,
-- but Knuth argues that repeated multiplication by the golden ratio
-- will minimize gaps in the hash space.
hashString :: String -> Int32
hashString = foldl' f 0
  where f m c = fromIntegral (ord c) + mulHi m golden
