module Data.HashMap.Common
(
HashMap(..)
, join
, bin
, zero
, nomatch
, empty
, union
, toList
, filterMapWithKey
, traverseWithKey
, foldrWithKey
, shorter
, insertCollidingWith
) where
#include "MachDeps.h"
import Control.Applicative (Applicative((<*>), pure), (<$>))
import Control.DeepSeq (NFData(rnf))
import Data.Bits (Bits(..), (.&.), xor)
import qualified Data.Foldable as Foldable
import Data.Monoid (Monoid(mempty, mappend))
import Data.Traversable (Traversable(..))
import Data.Typeable (Typeable)
import Data.Word (Word)
import Prelude hiding (foldr, map)
#if defined(__GLASGOW_HASKELL__)
import GHC.Exts (build)
#endif
import qualified Data.FullList.Lazy as FL
data HashMap k v
= Bin !SuffixMask
!(HashMap k v)
!(HashMap k v)
| Tip !Hash
!(FL.FullList k v)
| Nil
deriving (Typeable)
type Suffix = Int
type Hash = Int
type SuffixMask = Int
instance (Eq k, Eq v) => Eq (HashMap k v) where
t1 == t2 = equal t1 t2
t1 /= t2 = nequal t1 t2
toList :: HashMap k v -> [(k, v)]
#if defined(__GLASGOW_HASKELL__)
toList t = build (\ c z -> foldrWithKey (curry c) z t)
#else
toList = foldrWithKey (\ k v xs -> (k, v) : xs) []
#endif
equal :: (Eq k, Eq v) => HashMap k v -> HashMap k v -> Bool
equal (Bin sm1 l1 r1) (Bin sm2 l2 r2) =
(sm1 == sm2) && (equal l1 l2) && (equal r1 r2)
equal (Tip h1 l1) (Tip h2 l2) = (h1 == h2) && (l1 == l2)
equal Nil Nil = True
equal _ _ = False
nequal :: (Eq k, Eq v) => HashMap k v -> HashMap k v -> Bool
nequal (Bin sm1 l1 r1) (Bin sm2 l2 r2) =
(sm1 /= sm2) || (nequal l1 l2) || (nequal r1 r2)
nequal (Tip h1 l1) (Tip h2 l2) = (h1 /= h2) || (l1 /= l2)
nequal Nil Nil = False
nequal _ _ = True
instance (NFData k, NFData v) => NFData (HashMap k v) where
rnf Nil = ()
rnf (Tip _ xs) = rnf xs
rnf (Bin _ l r) = rnf l `seq` rnf r
instance Functor (HashMap k) where
fmap = map
instance (Show k, Show v) => Show (HashMap k v) where
showsPrec d m = showParen (d > 10) $
showString "fromList " . shows (toList m)
map :: (v1 -> v2) -> HashMap k v1 -> HashMap k v2
map f = go
where
go (Bin sm l r) = Bin sm (go l) (go r)
go (Tip h l) = Tip h (FL.map f' l)
go Nil = Nil
f' k v = (k, f v)
instance Foldable.Foldable (HashMap k) where
foldr f = foldrWithKey (const f)
foldrWithKey :: (k -> v -> a -> a) -> a -> HashMap k v -> a
foldrWithKey f = go
where
go z (Bin _ l r) = go (go z r) l
go z (Tip _ l) = FL.foldrWithKey f z l
go z Nil = z
instance Eq k => Monoid (HashMap k v) where
mempty = empty
mappend = union
empty :: HashMap k v
empty = Nil
union :: Eq k => HashMap k v -> HashMap k v -> HashMap k v
union t1@(Bin sm1 l1 r1) t2@(Bin sm2 l2 r2)
| sm1 == sm2 = Bin sm1 (union l1 l2) (union r1 r2)
| shorter sm1 sm2 = union1
| shorter sm2 sm1 = union2
| otherwise = join sm1 t1 sm2 t2
where
union1 | nomatch sm2 sm1 = join sm1 t1 sm2 t2
| zero sm2 sm1 = Bin sm1 (union l1 t2) r1
| otherwise = Bin sm1 l1 (union r1 t2)
union2 | nomatch sm1 sm2 = join sm1 t1 sm2 t2
| zero sm1 sm2 = Bin sm2 (union t1 l2) r2
| otherwise = Bin sm2 l2 (union t1 r2)
union (Tip h l) t = insertCollidingL h l t
union t (Tip h l) = insertCollidingR h l t
union Nil t = t
union t Nil = t
#if __GLASGOW_HASKELL__ >= 700
#endif
insertCollidingL :: Eq k => Hash -> FL.FullList k v -> HashMap k v -> HashMap k v
insertCollidingL = insertCollidingWith FL.union
#if __GLASGOW_HASKELL__ >= 700
#endif
insertCollidingR :: Eq k => Hash -> FL.FullList k v -> HashMap k v -> HashMap k v
insertCollidingR = insertCollidingWith (flip FL.union)
#if __GLASGOW_HASKELL__ >= 700
#endif
insertCollidingWith :: Eq k
=> (FL.FullList k v -> FL.FullList k v -> FL.FullList k v)
-> Hash -> FL.FullList k v
-> HashMap k v -> HashMap k v
insertCollidingWith f h0 l0 t0 = go h0 l0 t0
where
go !h !xs t@(Bin sm l r)
| nomatch h sm = join h (Tip h xs) sm t
| zero h sm = Bin sm (go h xs l) r
| otherwise = Bin sm l (go h xs r)
go h xs t@(Tip h' l)
| h == h' = Tip h $ f xs l
| otherwise = join h (Tip h xs) h' t
go h xs Nil = Tip h xs
instance Traversable (HashMap k) where
traverse f = traverseWithKey (const f)
filterMapWithKey :: (k -> v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2
filterMapWithKey f = go
where
go (Bin sm l r) = bin sm (go l) (go r)
go (Tip h vs) =
case FL.foldrWithKey ff FL.Nil vs of
FL.Nil -> Nil
FL.Cons k v xs -> Tip h (FL.FL k v xs)
go Nil = Nil
ff k v xs =
case f k v of
Nothing -> xs
Just x -> FL.Cons k x xs
traverseWithKey :: Applicative f => (k -> v1 -> f v2) -> HashMap k v1
-> f (HashMap k v2)
traverseWithKey f = go
where
go (Bin sm l r) = Bin sm <$> go l <*> go r
go (Tip h l) = Tip h <$> FL.traverseWithKey f l
go Nil = pure Nil
join :: Suffix -> HashMap k v -> Suffix -> HashMap k v -> HashMap k v
join s1 t1 s2 t2
| zero s1 sm = Bin sm t1 t2
| otherwise = Bin sm t2 t1
where
sm = branchSuffixMask s1 s2
bin :: SuffixMask -> HashMap k v -> HashMap k v -> HashMap k v
bin _ l Nil = l
bin _ Nil r = r
bin sm l r = Bin sm l r
zero :: Hash -> SuffixMask -> Bool
zero i sm = (i .&. smi) /= smi
where smi = fromIntegral sm
nomatch :: Hash -> SuffixMask -> Bool
nomatch i sm = (cb + cb 1) < fromIntegral sm
where cb = differentBit i (fromIntegral sm)
differentBit :: Hash -> Hash -> Word
differentBit h1 h2 =
fromIntegral (critBit (fromIntegral h1 `xor` fromIntegral h2))
suffixW :: Word -> Word -> Word
suffixW i m = i .&. (m1)
branchSuffixMask :: Suffix -> Suffix -> SuffixMask
branchSuffixMask p1 p2 =
fromIntegral (m + suffixW w1 m)
where m = differentBit p1 p2
w1 = fromIntegral p1
shorter :: SuffixMask -> SuffixMask -> Bool
shorter sm1 sm2 = (fromIntegral sm1 :: Word) < (fromIntegral sm2 :: Word)
critBit :: Word -> Word
critBit w = w .&. (negate w)