dao-0.1.0.1: src/Dao/HashMap.hs
-- "src/Dao/HashMap.hs" a simple hash map using Data.IntMap
--
-- Copyright (C) 2008-2014 Ramin Honary.
-- This file is part of the Dao System.
--
-- The Dao System is free software: you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- The Dao System is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program (see the file called "LICENSE"). If not, see
-- <http://www.gnu.org/licenses/agpl.html>.
module Dao.HashMap
( Hash128, Int128Hashable(hash128),
hash128_md5, deriveHash128_Show, deriveHash128_UStr,
deriveHash128_DataBinary, deriveHash128_DaoBinary,
Index, indexToPair, hashNewIndex, newIndex, indexHash, indexKey,
HashMap, hashLookup, hashAlter, hashInsert, hashDelete, hashAdjust, hashModify,
Dao.HashMap.lookup, alter, Dao.HashMap.insert, Dao.HashMap.delete, adjust, modify,
unionWith, unionsWith, Dao.HashMap.union, unions, differenceWith, difference, intersectionWith, intersection,
assocs, elems, keys, fromListWith, fromList, size, empty
) where
import Dao.String
import Dao.PPrint
import Dao.Binary
import Control.Applicative hiding (empty)
import Control.Monad
import Data.Typeable
import Data.Monoid
import Data.List
import Data.Char
import Data.Word
import qualified Data.Binary as B
import qualified Data.Binary.Get as B
import qualified Data.Binary.Put as B
import qualified Data.ByteString.Lazy as B
import qualified Data.Map as M
import qualified Data.IntMap as I
import qualified Data.Digest.MD5 as MD5
----------------------------------------------------------------------------------------------------
type Hash128 = (Word64, Word64)
type Int128Map a = I.IntMap (I.IntMap a)
_lookup :: Hash128 -> Int128Map a -> Maybe a
_lookup (h1, h2) = I.lookup (fromIntegral h1) >=> I.lookup (fromIntegral h2)
_alter :: (Maybe a -> Maybe a) -> Hash128 -> Int128Map a -> Int128Map a
_alter alt (h1, h2) =
I.alter
((\m -> if I.null m then Nothing else Just m) . I.alter alt (fromIntegral h2) . maybe mempty id)
(fromIntegral h1)
_unionWith :: (a -> a -> a) -> Int128Map a -> Int128Map a -> Int128Map a
_unionWith f = I.unionWith (I.unionWith f)
_differenceWith :: (a -> b -> Maybe a) -> Int128Map a -> Int128Map b -> Int128Map a
_differenceWith f = I.differenceWith (\a b -> let m = I.differenceWith f a b in guard (not $ I.null m) >> return m)
_intersectionWith :: (a -> b -> c) -> Int128Map a -> Int128Map b -> Int128Map c
_intersectionWith f = I.intersectionWith (I.intersectionWith f)
_assocs :: Int128Map a -> [(Hash128, a)]
_assocs m = I.assocs m >>= \ (h1, m) -> I.assocs m >>= \ (h2, a) -> [((fromIntegral h1, fromIntegral h2), a)]
_fromListWith :: (a -> a -> a) -> [(Hash128, a)] -> Int128Map a
_fromListWith f = fmap (I.fromListWith f) . I.fromListWith (++) . fmap (\ ((h1, h2), a) -> (fromIntegral h1, [(fromIntegral h2, a)]))
----------------------------------------------------------------------------------------------------
class Ord key => Int128Hashable key where { hash128 :: key -> Hash128 }
hash128_md5 :: [Word8] -> Hash128
hash128_md5 = B.runGet (pure (,) <*> B.getWord64be <*> B.getWord64be) . B.pack . MD5.hash
deriveHash128_Show :: Show a => a -> Hash128
deriveHash128_Show = deriveHash128_UStr . show
deriveHash128_UStr :: UStrType a => a -> Hash128
deriveHash128_UStr = hash128_md5 . utf8bytes
deriveHash128_DataBinary :: B.Binary a => a -> Hash128
deriveHash128_DataBinary = hash128_md5 . B.unpack . B.encode
deriveHash128_DaoBinary :: (HasCoderTable mtab, Binary a mtab) => mtab -> a -> Hash128
deriveHash128_DaoBinary mtab = hash128_md5 . B.unpack . encode mtab
----------------------------------------------------------------------------------------------------
data Index key = Index { indexHash :: Hash128, indexKey :: key } deriving (Eq, Ord, Typeable)
indexToPair :: Index key -> (B.ByteString, key)
indexToPair (Index{ indexHash=(h1, h2), indexKey=key }) =
(B.runPut (B.putWord64be h1 >> B.putWord64be h2), key)
instance Show key => Show (Index key) where
show (Index{ indexKey=key }) = concat ["Index (", show key, ")"]
instance (Read key, Int128Hashable key) => Read (Index key) where
readsPrec p s = do
s <- maybe [] return $ stripPrefix "Index" $ dropWhile isSpace s
(key, s) <- case dropWhile isSpace s of { '(':s -> readsPrec p s; _ -> [] }
case dropWhile isSpace s of
')':s -> [(Index{ indexHash=hash128 key, indexKey=key }, s)]
_ -> []
instance Binary key mtab => Binary (Index key) mtab where
put (Index{ indexHash=(h1, h2), indexKey=key }) = putWord64be h1 >> putWord64be h2 >> put key
get = do
h1 <- getWord64be
h2 <- getWord64be
key <- get
return $ Index{ indexHash=(h1, h2), indexKey=key }
instance PPrintable key => PPrintable (Index key) where { pPrint i = pPrint (indexKey i) }
hashNewIndex :: (key -> Hash128) -> key -> Index key
hashNewIndex hash key = Index{ indexHash = hash key, indexKey = key }
newIndex :: Int128Hashable key => key -> Index key
newIndex = hashNewIndex hash128
----------------------------------------------------------------------------------------------------
newtype HashMap key a = HashMap (Int128Map (M.Map key a)) deriving (Eq, Ord, Typeable)
instance (Show key, Show a) => Show (HashMap key a) where
show = ("fromList "++) . show . assocs
instance (Read key, Int128Hashable key, Read a) => Read (HashMap key a) where
readsPrec p s = maybe [] return (stripPrefix "fromList" $ dropWhile isSpace s) >>=
readsPrec p . dropWhile isSpace >>= \ (hmap, s) -> return (fromList hmap, s)
instance Functor (HashMap key) where
fmap f (HashMap m) = HashMap $ fmap (fmap (fmap f)) m
instance (Ord key, Monoid a) => Monoid (HashMap key a) where
mempty = HashMap mempty
mappend a b = unionWith mappend a b
instance HasNullValue (HashMap key a) where
nullValue = HashMap mempty
testNull = Dao.HashMap.null
instance (Ord key, Binary key mtab, Binary a mtab) => Binary (HashMap key a) mtab where
put = put . assocs
get = fromList <$> get
instance (PPrintable key, PPrintable a) => PPrintable (HashMap key a) where
pPrint = pList (pString "HashMap") "{" ", " "}" .
map (\ (a, b) -> pInline [pPrint a, pString " = ", pPrint b]) . assocs
null :: HashMap key a -> Bool
null (HashMap a) = I.null a
hashLookup :: Ord key => Index key -> HashMap key a -> Maybe a
hashLookup key (HashMap intmap) = _lookup (indexHash key) intmap >>= M.lookup (indexKey key)
lookup :: Int128Hashable key => key -> HashMap key a -> (Index key, Maybe a)
lookup key hmap = let i = Index{ indexHash=hash128 key, indexKey=key } in (i, hashLookup i hmap)
hashAlter :: Ord key => (Maybe a -> Maybe a) -> Index key -> HashMap key a -> HashMap key a
hashAlter alt key (HashMap intmap) = HashMap $
_alter
((\m -> guard (not $ M.null m) >> Just m) . M.alter alt (indexKey key) . maybe mempty id)
(indexHash key)
intmap
alter :: Int128Hashable key => (Maybe a -> Maybe a) -> key -> HashMap key a -> HashMap key a
alter f key = hashAlter f (Index{ indexHash=hash128 key, indexKey=key })
hashInsert :: Ord key => Index key -> a -> HashMap key a -> HashMap key a
hashInsert key a = hashAlter (const $ Just a) key
insert :: Int128Hashable key => key -> a -> HashMap key a -> HashMap key a
insert key = hashInsert (Index{ indexHash=hash128 key, indexKey=key})
hashDelete :: Ord key => Index key -> HashMap key a -> HashMap key a
hashDelete = hashAlter (const Nothing)
delete :: Int128Hashable key => key -> HashMap key a -> HashMap key a
delete key = hashDelete (Index{ indexHash=hash128 key, indexKey=key })
hashAdjust :: Ord key => (a -> a) -> Index key -> HashMap key a -> HashMap key a
hashAdjust f = hashAlter (fmap f)
adjust :: Int128Hashable key => (a -> a) -> key -> HashMap key a -> HashMap key a
adjust f key = hashAdjust f (Index{ indexHash=hash128 key, indexKey=key })
hashModify :: Ord key => (a -> Maybe a) -> Index key -> HashMap key a -> HashMap key a
hashModify f = hashAlter (>>=f)
modify :: Int128Hashable key => (a -> Maybe a) -> key -> HashMap key a -> HashMap key a
modify f key = hashModify f (Index{ indexHash=hash128 key, indexKey=key })
unionWith :: Ord key => (a -> a -> a) -> HashMap key a -> HashMap key a -> HashMap key a
unionWith f (HashMap a) (HashMap b) = HashMap $ _unionWith (M.unionWith f) a b
union :: Ord key => HashMap key a -> HashMap key a -> HashMap key a
union = unionWith const
unionsWith :: Ord key => (a -> a -> a) -> [HashMap key a] -> HashMap key a
unionsWith f = foldl (unionWith f) empty
unions :: Ord key => [HashMap key a] -> HashMap key a
unions = unionsWith const
differenceWith :: Ord key => (a -> b -> Maybe a) -> HashMap key a -> HashMap key b -> HashMap key a
differenceWith f (HashMap a) (HashMap b) = HashMap $
_differenceWith (\a b -> let m = M.differenceWith f a b in guard (not $ M.null m) >> return m) a b
difference :: Ord key => HashMap key a -> HashMap key b -> HashMap key a
difference = differenceWith (\ _ _ -> Nothing)
intersectionWith :: Ord key => (a -> b -> c) -> HashMap key a -> HashMap key b -> HashMap key c
intersectionWith f (HashMap a) (HashMap b) = HashMap $ _intersectionWith (M.intersectionWith f) a b
intersection :: Ord key => HashMap key a -> HashMap key a -> HashMap key a
intersection = intersectionWith const
assocs :: HashMap key a -> [(Index key, a)]
assocs (HashMap m) = do
(hash, m) <- _assocs m
(key , a) <- M.assocs m
[(Index{ indexHash=hash, indexKey=key }, a)]
elems :: HashMap key a -> [a]
elems = fmap snd . assocs
keys :: HashMap key a -> [Index key]
keys = fmap fst . assocs
fromListWith :: Ord key => (a -> a -> a) -> [(Index key, a)] -> HashMap key a
fromListWith f = HashMap . fmap (fmap $ M.fromListWith f) . _fromListWith (++) .
fmap (\ (Index{ indexHash=hash, indexKey=key }, a) -> (hash, [(key, a)]))
fromList :: Ord key => [(Index key, a)] -> HashMap key a
fromList = fromListWith (flip const)
size :: HashMap key a -> Integer
size (HashMap m) = I.foldl (I.foldl (\i -> (i+) . toInteger . M.size)) 0 m
empty :: HashMap key a
empty = HashMap I.empty