{-# LANGUAGE BangPatterns #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} -------------------------------------------------------------------- -- | -- Copyright : (c) Edward Kmett 2013 -- License : BSD3 -- Maintainer: Edward Kmett -- Stability : experimental -- Portability: non-portable -- -------------------------------------------------------------------- module Data.Hash.Double ( Hash(..) , sip , pepper ) where import Control.Applicative import Control.Lens import Data.Data import Data.Hashable import Generics.Deriving -- $setup -- >>> :load Data.Hash.Double -- >>> import Control.Lens -- | \"Less Hashing, Same Performance: Building a Better Bloom Filter\" by -- Kirsch and Mitzenmacher demonstrated that for many use-cases, especially -- involving Bloom filters, we can use pairwise independent hashes to -- generate a family of related hash functions with good characteristics. -- -- -- -- This stores a pair of hashes. -- -- >>> sip (42 :: Int)^..taking 4 each -- [-2574874314062730062,-9186383815474761572,2648850756822758536,-3962658744589272970] -- -- >>> sip (42 :: Int)^.ix 3 -- -3962658744589272970 data Hash = Hash {-# UNPACK #-} !Int {-# UNPACK #-} !Int deriving (Eq,Ord,Show,Read,Data,Typeable,Generic) sip :: Hashable a => a -> Hash sip a = Hash (hash a) -- hash with the salt taken from Data.Hash (hashWithSalt pepper a) -- chosen by fair die roll {-# INLINE sip #-} pepper :: Int pepper = 0x53dffa872f4d7341 type instance Index Hash = Int type instance IxValue Hash = Int instance Gettable f => Contains f Hash where contains i f _ = coerce $ indexed f (i :: Int) True {-# INLINE contains #-} instance Gettable f => Ixed f Hash where ix i f (Hash a b) = coerce $ indexed f i (a + i * (b + i)) {-# INLINE ix #-} instance (Gettable f, Applicative f) => Each f Hash Hash Int Int where each f (Hash a b) = go 0 where go !i = indexed f i (a + i*(b+i)) *> go (i + 1) {-# INLINE each #-}