-- | Haskell98 polymorphic Hash interface
module Data.Generics.Fixplate.Util.Hash.Class where

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

import Data.Char
import Data.Word
import Data.Int
import Data.Bits
import Data.List

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

-- | A type class for hashes.
-- Minimal complete definition: 'emptyHash', 'hashWord8', 'hashHash' and 'showHex'.
class (Eq hash, Ord hash, Hashable hash) => HashValue hash where

  hashWord8  :: Word8  -> hash -> hash
  hashWord16 :: Word16 -> hash -> hash
  hashWord32 :: Word32 -> hash -> hash
  hashWord64 :: Word64 -> hash -> hash

  emptyHash  :: hash
  hashHash   :: hash -> hash -> hash
  showHex    :: hash -> String

  hashWord32 w = hashWord8 a . hashWord8 b . hashWord8 c . hashWord8 d where
    a = fromIntegral (255 .&. (       w   ))
    b = fromIntegral (255 .&. (shiftR w  8))
    c = fromIntegral (255 .&. (shiftR w 16))
    d = fromIntegral (255 .&. (shiftR w 24))
 
  hashWord16 w = hashWord8 a . hashWord8 b where
    a = fromIntegral (255 .&. (       w   ))
    b = fromIntegral (255 .&. (shiftR w  8))

  hashWord64 w = hashWord32 a . hashWord32 b where
    a = fromIntegral (0xffffffff .&. (       w   ))
    b = fromIntegral (0xffffffff .&. (shiftR w 32))

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

-- | A type class of hashable objects. An instance has to compute the hash for
-- /any/ hash function, using the \"base\" types (eg. Word32).
--
-- Minimal complete definition: 'hashDigest'. The default for 'computeHash' is
--
-- > computeHash x = hashDigest x emptyHash
-- 
class Hashable a where
  hashDigest  :: HashValue hash => a -> hash -> hash
  computeHash :: HashValue hash => a -> hash

  computeHash x = hashDigest x emptyHash

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

instance Hashable Word8  where hashDigest = hashWord8
instance Hashable Word16 where hashDigest = hashWord16
instance Hashable Word32 where hashDigest = hashWord32
instance Hashable Word64 where hashDigest = hashWord64

instance Hashable Int  where hashDigest = hashInt
instance Hashable Word where hashDigest = hashWord
instance Hashable Bool where hashDigest = hashBool
instance Hashable Char where hashDigest = hashChar

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

instance Hashable a => Hashable [a] where
  hashDigest xs h = foldl' (flip hashDigest) h xs

instance (Hashable a, Hashable b) => Hashable (a,b) where
  hashDigest (x,y) = hashDigest y . hashDigest x

instance (Hashable a, Hashable b, Hashable c) => Hashable (a,b,c) where
  hashDigest (x,y,z) = hashDigest z . hashDigest y . hashDigest x

instance (Hashable a, Hashable b, Hashable c, Hashable d) => Hashable (a,b,c,d) where
  hashDigest (x,y,z,w) = hashDigest w . hashDigest z . hashDigest y . hashDigest x

instance (Hashable a, Hashable b, Hashable c, Hashable d, Hashable e) => Hashable (a,b,c,d,e) where
  hashDigest (x,y,z,w,u) = hashDigest u . hashDigest w . hashDigest z . hashDigest y . hashDigest x

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

hashInt  :: HashValue hash => Int  -> hash -> hash
hashWord :: HashValue hash => Word -> hash -> hash
hashBool :: HashValue hash => Bool -> hash -> hash
hashChar :: HashValue hash => Char -> hash -> hash

hashInt  k = hashWord64 (fromIntegral k)
hashWord k = hashWord64 (fromIntegral k)
hashBool b = hashWord8  (if b then 255 else 0)
hashChar c = hashWord16 (fromIntegral (ord c)) 

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