{-# LANGUAGE BangPatterns, DeriveDataTypeable, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MagicHash, MultiParamTypeClasses, OverlappingInstances, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-} ----------------------------------------------------------------------------- -- | -- Module : Data.EnumMapMap.Base -- Copyright : (c) Daan Leijen 2002 -- (c) Andriy Palamarchuk 2008 -- (c) Matthew West 2012 -- License : BSD-style -- Stability : experimental -- Portability : Uses GHC extensions -- -- Based on Data.IntMap.Base. -- -- This defines the @'EnumMapMap' (k ':&' t) v@ instance, and the Key data types. The -- terminating key type is K, and the @'EnumMapMap' (K k) v@ instances are defined -- in EnumMapMap.Lazy and EnumMapMap.Strict. ----------------------------------------------------------------------------- module Data.EnumMapMap.Base( -- * Key types (:&)(..), N(..), Z(..), d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, -- * Split/Join Keys IsSplit(..), Plus, SubKey(..), -- * Internal MkNestedPair(..), -- ** IsEMM EMM(..), IsKey(..), EnumMapMap(..), -- ** SKey HasSKey(..), SubKeyS(..), -- ** EMM internals mergeWithKey', mapWithKey_, mapMaybeWithKey_, traverseWithKey_, foldrWithKey_, foldlStrict, -- ** IntMap internals Prefix, Mask, Nat, Key, intFromNat, natFromInt, shiftRL, shiftLL, branchMask, mask, bin, tip, shorter, nomatch, match, join, zero ) where import Prelude hiding (lookup, map, filter, foldr, foldl, null, init, head, tail) import Control.Applicative (Applicative(pure,(<*>)),(<$>)) import Control.DeepSeq (NFData(rnf)) import Data.Bits import Data.Default import qualified Data.Foldable as FOLD import Data.Maybe (fromMaybe) import Data.SafeCopy import Data.Semigroup import Data.Traversable (Traversable(traverse)) import Data.Typeable import GHC.Exts (Word(..), Int(..), uncheckedShiftRL#, uncheckedShiftL#) data EMM k v = Bin {-# UNPACK #-} !Prefix {-# UNPACK #-} !Mask !(EMM k v) !(EMM k v) | Tip {-# UNPACK #-} !Int v | Nil deriving (Show) type Nat = Word type Key = Int type Prefix = Int type Mask = Int infixr 3 :& -- | Multiple keys are joined by the (':&') constructor. -- -- > multiKey :: Int :& Int :& K Int -- > multiKey = 5 :& 6 :& K 5 -- data k :& t = !k :& !t deriving (Show, Eq) data Z = Z data N n = N !n -- | Split after 1 key. -- -- > emm :: EnumMapMap (T1 :& T2 :& K T3) v -- > splitKey d1 emm :: EnumMapMap (T1 :& K T2) (EnumMapMap (K T3) v) d1 :: Z d1 = Z -- | Split after 2 keys. -- -- > emm :: EnumMapMap (T1 :& T2 :& K T3) v -- > splitKey d1 emm :: EnumMapMap (K T1) (EnumMapMap (T2 :& K T3) v) d2 :: N Z d2 = N d1 d3 :: N(N Z) d3 = N d2 d4 :: N(N(N Z)) d4 = N d3 d5 :: N(N(N(N Z))) d5 = N d4 d6 :: N(N(N(N(N Z)))) d6 = N d5 d7 :: N(N(N(N(N(N Z))))) d7 = N d6 d8 :: N(N(N(N(N(N(N Z)))))) d8 = N d7 d9 :: N(N(N(N(N(N(N(N Z))))))) d9 = N d8 d10 :: N(N(N(N(N(N(N(N(N Z)))))))) d10 = N d9 class IsSplit k z where type Head k z :: * type Tail k z :: * -- | Split a key so that an 'EnumMapMap' becomes an 'EnumMapMap' of -- 'EnumMapMap's. -- -- > newtype ID = ID Int deriving Enum -- > emm = empty :: EnumMapMap (Int :& K ID) Bool -- > res :: EnumMapMap (K ID) Bool -- > res = lookup (K 5) $ splitKey d1 emm -- -- If the level is too high then the compilation will fail with an error -- -- > emm = empty :: EnumMapMap (Int :& Int :& K Int) Bool -- 3 levels -- > res1 = splitKey d4 emm -- ERROR! Instance not found... -- > res2 = splitKey d3 emm -- ERROR! Instance not found... -- > res3 = splitKey d2 emm -- Good -- splitKey :: z -> EnumMapMap k v -> EnumMapMap (Head k z) (EnumMapMap (Tail k z) v) instance (IsSplit t n, Enum k) => IsSplit (k :& t) (N n) where type Head (k :& t) (N n) = k :& Head t n type Tail (k :& t) (N n) = Tail t n splitKey (N n) (KCC emm) = KCC $ mapWithKey_ (\_ -> splitKey n) emm type family Plus k1 k2 :: * type instance Plus (k1 :& t) k2 = k1 :& Plus t k2 -- | This is used by the SafeCopy instance. It strips the constructors and Enum -- from the key so that -- > NestedPair (T1 :& T2 :& K T3) v ~ (Int, (Int, (Int, v))) class MkNestedPair k v where type NestedPair k v :: * nestedPair :: k -> v -> NestedPair k v unNestedPair :: NestedPair k v -> (k ,v) instance (MkNestedPair t v, Enum k) => MkNestedPair (k :& t) v where type NestedPair (k :& t) v = (Int, NestedPair t v) nestedPair (k :& t) v = (fromEnum k, nestedPair t v) unNestedPair (k, x) = (toEnum k :& t, v) where (t, v) = unNestedPair x class SubKey k1 k2 v where -- | k1 should be a prefix of k2. If @k1 ~ k2@ then the 'Result' will be -- @v@. -- -- > Result (K ID1) (ID1 :& K ID2) v ~ EnumMapMap (K ID2) v -- > Result (ID1 :& K ID2) (ID1 :& K ID2) v ~ v -- > Result (ID1 :& K ID2) (K ID1) v -- ERROR -- > Result (ID2 :& K ID1) (ID1 :& K ID2) -- ERROR type Result k1 k2 v :: * -- | Is the key present in the 'EnumMapMap'? member :: k1 -> EnumMapMap k2 v -> Bool -- | An 'EnumMapMap' with one element -- -- > singleton (5 :& K 3) "a" == fromList [(5 :& K 3, "a")] -- > singleton (K 5) $ singleton (K 2) "a" == fromList [(5 :& K 3, "a")] singleton :: k1 -> Result k1 k2 v -> EnumMapMap k2 v -- | Lookup up the value at a key in the 'EnumMapMap'. -- -- > emm = fromList [(3 :& K 1, "a")] -- > lookup (3 :& K 1) emm == Just "a" -- > lookup (2 :& K 1) emm == Nothing -- -- If the given key has less dimensions then the 'EnumMapMap' then a submap -- is returned. -- -- > emm2 = fromList [(3 :& 2 :& K 1, "a"), (3 :& 2 :& K 4, "a")] -- > lookup (3 :& K 2) emm2 == Just $ fromList [(K 1, "a"), (K 4, "a")] -- lookup :: (IsKey k1, IsKey k2) => k1 -> EnumMapMap k2 v -> Maybe (Result k1 k2 v) -- | Insert a new key\/value pair into the 'EnumMapMap'. Can also insert submaps. insert :: (IsKey k1, IsKey k2) => k1 -> Result k1 k2 v -> EnumMapMap k2 v -> EnumMapMap k2 v -- | Insert with a combining function. Can also insert submaps. insertWith :: (IsKey k1, IsKey k2) => (Result k1 k2 v -> Result k1 k2 v -> Result k1 k2 v) -> k1 -> Result k1 k2 v -> EnumMapMap k2 v -> EnumMapMap k2 v insertWith f = insertWithKey (const f) -- | Insert with a combining function. Can also insert submaps. insertWithKey :: (IsKey k1, IsKey k2) => (k1 -> Result k1 k2 v -> Result k1 k2 v -> Result k1 k2 v) -> k1 -> Result k1 k2 v -> EnumMapMap k2 v -> EnumMapMap k2 v -- | Remove a key and it's value from the 'EnumMapMap'. If the key is not -- present the original 'EnumMapMap' is returned. delete :: (IsKey k1, IsKey k2) => k1 -> EnumMapMap k2 v -> EnumMapMap k2 v class SubKeyS k s where -- | The intersection of an 'EnumMapMap' and an 'EnumMapSet'. If a key is -- present in the EnumMapSet then it will be present in the resulting -- 'EnumMapMap'. Works with 'EnumMapSet's that are submaps of the -- 'EnumMapMap'. intersectSet :: (IsKey k, IsKey s) => EnumMapMap k v -> EnumMapMap s () -> EnumMapMap k v -- | The difference between an 'EnumMapMap' and an 'EnumMapSet'. If a key -- is present in the 'EnumMapSet' it will not be present in the result. differenceSet :: (IsKey k, IsKey s) => EnumMapMap k v -> EnumMapMap s () -> EnumMapMap k v class HasSKey k where type Skey k :: * -- | Convert a key terminated with 'K' into one terminated with 'S'. -- -- > k = 1 :& 2 :& 'K' 3 -- > toS k == 1 :& 2 :& 'S' 3 -- toS :: k -> Skey k -- | Convert a key terminated with 'S' into one terminated with 'K'. -- -- > s = 1 :& 2 :& S 3 -- > toK s == 1 :& 2 :& K 3 toK :: Skey k -> k instance (HasSKey t) => HasSKey (k :& t) where type Skey (k :& t) = k :& Skey t toS (k :& t) = (:&) k $! toS t toK (k :& t) = (:&) k $! toK t class (Eq k) => IsKey k where -- | A map of keys to values. The keys are 'Enum' types but are stored as 'Int's -- so any keys with the same 'Int' value are treated as the same. The aim is to -- provide typesafe indexing. data EnumMapMap k :: * -> * -- | No subtrees should be empty. Returns 'True' if one is. emptySubTrees :: EnumMapMap k v -> Bool emptySubTrees_ :: EnumMapMap k v -> Bool -- | Remove empty subtrees. removeEmpties :: EnumMapMap k v -> EnumMapMap k v -- | Join a key so that an 'EnumMapMap' of 'EnumMapMap's becomes an -- 'EnumMapMap'. -- -- > newtype ID = ID Int deriving Enum -- > emm :: EnumMapMap (K Int) (EnumMapMap (K ID) Bool) -- > res :: EnumMapMap (Int :& K ID) Bool -- > res = joinKey emm -- -- 'joinKey' is the opposite of 'splitKey'. -- -- > emm = empty :: EnumMapMap (Int :& Int :& K ID) Bool) -- > emm == joinKey $ splitKey d2 emm -- joinKey :: (IsKey (Plus k k2)) => EnumMapMap k (EnumMapMap k2 v) -> EnumMapMap (Plus k k2) v joinKey = removeEmpties . unsafeJoinKey -- | Join a key so that an 'EnumMapMap' of 'EnumMapMap's becomes an -- 'EnumMapMap'. The unsafe version does not check for empty subtrees, so -- it is faster. -- -- > newtype ID = ID Int deriving Enum -- > emm :: EnumMapMap (K Int) (EnumMapMap (K ID) Bool) -- > res :: EnumMapMap (Int :& K ID) Bool -- > res = unsafeJoinKey emm -- unsafeJoinKey :: EnumMapMap k (EnumMapMap k2 v) -> EnumMapMap (Plus k k2) v -- | The empty 'EnumMapMap'. empty :: EnumMapMap k v -- | Is the 'EnumMapMap' empty? -- -- Submaps can never be empty, so the following should always hold true: -- -- > emm :: EnumMapMap (Int :& Int :& K ID) Bool) -- > null $ splitKey x emm == False null :: EnumMapMap k v -> Bool -- | Number of elements in the 'EnumMapMap'. size :: EnumMapMap k v -> Int -- | The expression (@'alter' f k emm@) alters the value at @k@, or absence thereof. -- 'alter' can be used to insert, delete, or update a value in an 'EnumMapMap'. alter :: (Maybe v -> Maybe v) -> k -> EnumMapMap k v -> EnumMapMap k v -- | Map a function over all values in the 'EnumMapMap'. map :: (v -> t) -> EnumMapMap k v -> EnumMapMap k t map f = mapWithKey (const f) -- | Map values and collect the 'Just' results. mapMaybe :: (v -> Maybe t) -> EnumMapMap k v -> EnumMapMap k t mapMaybe f = mapMaybeWithKey (\_ x -> f x) -- | Map keys\/values and collect the 'Just' results. mapMaybeWithKey :: (k -> v -> Maybe t) -> EnumMapMap k v -> EnumMapMap k t -- | Map a function over all key\/value pairs in the 'EnumMapMap'. mapWithKey :: (k -> v -> t) -> EnumMapMap k v -> EnumMapMap k t -- | @TraverseWithKey@ behaves exactly like a regular 'traverse' except that -- the traversing function also has access to the key associated with a -- value. traverseWithKey :: (Applicative t) => (k -> a -> t b) -> EnumMapMap k a -> t (EnumMapMap k b) -- | Fold the values in the 'EnumMapMap' using the given -- right-associative binary operator foldr :: (v -> t -> t) -> t -> EnumMapMap k v -> t -- | Fold the keys and values in the 'EnumMapMap' using the given right-associative -- binary operator. foldrWithKey :: (k -> v -> t -> t) -> t -> EnumMapMap k v -> t -- | Convert the 'EnumMapMap' to a list of key\/value pairs. toList :: SubKey k k v => EnumMapMap k v -> [(k, v)] toList = foldrWithKey (\k x xs -> (k, x):xs) [] -- | Convert the 'EnumMapMap' to a list of nested tuples toNestedPairList :: (SubKey k k v, MkNestedPair k v) => EnumMapMap k v -> [NestedPair k v] toNestedPairList = foldrWithKey (\k x xs -> (nestedPair k x):xs) [] -- | Create an 'EnumMapMap' from a list of key\/value pairs. fromList :: (SubKey k k v, Result k k v ~ v) => [(k, v)] -> EnumMapMap k v fromList = foldlStrict (\t (k, x) -> insert k x t) empty -- | Create an 'EnumMapMap' from a list of nested tuples fromNestedPairList :: (SubKey k k v, Result k k v ~ v, MkNestedPair k v) => [NestedPair k v] -> EnumMapMap k v fromNestedPairList = foldlStrict f empty where f :: (IsKey k, SubKey k k v, Result k k v ~ v, MkNestedPair k v) => EnumMapMap k v -> NestedPair k v -> EnumMapMap k v f emm = g emm . unNestedPair g :: (IsKey k, SubKey k k v, Result k k v ~ v) => EnumMapMap k v -> (k, v) -> EnumMapMap k v g emm (k, v) = insert k v emm -- | List of elements in ascending order of keys elems :: EnumMapMap k v -> [v] elems = foldr (:) [] -- | List of keys keys :: EnumMapMap k v -> [k] keys = foldrWithKey (\k _ ks -> k:ks) [] -- | The 'Data.EnumMapSet' of the keys. 'EnumMapMap' keys can be converted into -- 'Data.EnumMapSet' keys using 'toS', and back again using 'toK'. keysSet :: (HasSKey k) => EnumMapMap k v -> EnumMapMap (Skey k) () -- | Build an 'EnumMapMap' from an 'EnumMapSet' and a function which for each -- key computes it's value fromSet :: HasSKey k => (k -> v) -> EnumMapMap (Skey k) () -> EnumMapMap k v -- | The minimal key and value of the 'EnumMapMap'. -- -- > findMin empty -- ERROR, no minimal key -- > findMin $ fromList [(K 1, "a", K 3, "b")] == (K 1, a) findMin :: EnumMapMap k v -> (k, v) -- | Retrieves the minimal (key,value) pair of the EnumMapMap, and the -- EnumMapMap stripped of that element, or 'Nothing' if passed an empty map. minViewWithKey :: EnumMapMap k v -> Maybe ((k, v), EnumMapMap k v) deleteFindMin :: EnumMapMap k v -> ((k, v), EnumMapMap k v) deleteFindMin = fromMaybe(error "deleteFindMin: empty EnumMapMap has no minimal\ \ element") . minViewWithKey -- | The (left-biased) union of two 'EnumMapMap's. -- It prefers the first 'EnumMapMap' when duplicate keys are encountered. union :: EnumMapMap k v -> EnumMapMap k v -> EnumMapMap k v -- | The union of a list of maps. unions :: [EnumMapMap k v] -> EnumMapMap k v unions = foldlStrict union empty -- | The union of a list of maps with a combining function unionsWith :: (v -> v -> v) -> [EnumMapMap k v] -> EnumMapMap k v unionsWith f = foldlStrict (unionWith f) empty -- | The union with a combining function. unionWith :: (v -> v -> v) -> EnumMapMap k v -> EnumMapMap k v -> EnumMapMap k v unionWith f = unionWithKey (const f) -- | The union with a combining function. unionWithKey :: (k -> v -> v -> v) -> EnumMapMap k v -> EnumMapMap k v -> EnumMapMap k v -- | Difference between two 'EnumMapMap's (based on keys). difference :: EnumMapMap k v1 -> EnumMapMap k v2 -> EnumMapMap k v1 -- | Difference with a combining function. differenceWith :: (v1 -> v2 -> Maybe v1) -> EnumMapMap k v1 -> EnumMapMap k v2 -> EnumMapMap k v1 differenceWith f = differenceWithKey (const f) -- | Difference with a combining function. differenceWithKey :: (k -> v1 -> v2 -> Maybe v1) -> EnumMapMap k v1 -> EnumMapMap k v2 -> EnumMapMap k v1 -- | The (left-biased) intersection of two 'EnumMapMap' (based on keys). intersection :: EnumMapMap k v1 -> EnumMapMap k v2 -> EnumMapMap k v1 -- | The intersection with a combining function. intersectionWith :: (v1 -> v2 -> v3) -> EnumMapMap k v1 -> EnumMapMap k v2 -> EnumMapMap k v3 intersectionWith f = intersectionWithKey (const f) -- | The intersection with a combining function. intersectionWithKey :: (k -> v1 -> v2 -> v3) -> EnumMapMap k v1 -> EnumMapMap k v2 -> EnumMapMap k v3 equal :: Eq v => EnumMapMap k v -> EnumMapMap k v -> Bool nequal :: Eq v => EnumMapMap k v -> EnumMapMap k v -> Bool instance (Enum k, IsKey t1, IsKey t2, SubKey t1 t2 v) => SubKey (k :& t1) (k :& t2) v where type Result (k :& t1) (k :& t2) v = Result t1 t2 v member !(key' :& nxt) (KCC emm) = key `seq` go emm where go t = case t of Bin _ m l r -> case zero key m of True -> go l False -> go r Tip kx x -> case key == kx of True -> member nxt x False -> False Nil -> False key = fromEnum key' singleton (key :& nxt) = KCC . Tip (fromEnum key) . singleton nxt lookup !(key' :& nxt) (KCC emm) = key `seq` go emm where go (Bin _ m l r) | zero key m = go l | otherwise = go r go (Tip kx x) = case kx == key of True -> lookup nxt x False -> Nothing go Nil = Nothing key = fromEnum key' insert (key :& nxt) val (KCC emm) = KCC $ insertWith_ (insert nxt val) key (singleton nxt val) emm insertWithKey f k@(key :& nxt) val (KCC emm) = KCC $ insertWith_ go key (singleton nxt val) emm where go = insertWithKey (\_ -> f k) nxt val delete !(key :& nxt) (KCC emm) = KCC $ alter_ (delete nxt) (fromEnum key) emm instance (Enum k, IsKey t1, IsKey t2, SubKeyS t1 t2) => SubKeyS (k :& t1) (k :& t2) where intersectSet (KCC emm) (KCC ems) = KCC $ mergeWithKey' binD go (const Nil) (const Nil) emm ems where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 $ intersectSet x1 x2 differenceSet (KCC emm) (KCC ems) = KCC $ mergeWithKey' binD go id (const Nil) emm ems where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 $ differenceSet x1 x2 instance (Eq k, Enum k, IsKey t, HasSKey t) => IsKey (k :& t) where newtype EnumMapMap (k :& t) v = KCC (EMM k (EnumMapMap t v)) emptySubTrees e@(KCC emm) = case emm of Nil -> False _ -> emptySubTrees_ e emptySubTrees_ (KCC emm) = go emm where go t = case t of Bin _ _ l r -> go l || go r Tip _ v -> emptySubTrees_ v Nil -> True removeEmpties (KCC emm) = KCC $ go emm where go t = case t of Bin p m l r -> bin p m (go l) (go r) Tip k v -> tip k (removeEmpties v) Nil -> Nil unsafeJoinKey (KCC emm) = KCC $ mapWithKey_ (const unsafeJoinKey) emm empty = KCC Nil null (KCC t) = case t of Nil -> True _ -> False size (KCC t) = go t where go (Bin _ _ l r) = go l + go r go (Tip _ y) = size y go Nil = 0 alter f !(key :& nxt) (KCC emm) = KCC $ alter_ (alter f nxt) (fromEnum key) emm mapWithKey f (KCC emm) = KCC $ mapWithKey_ go emm where go k = mapWithKey (\nxt -> f $! k :& nxt) mapMaybeWithKey f (KCC emm) = KCC $ mapMaybeWithKey_ go emm where go k = mapMaybeWithKey (\nxt -> f $! k :& nxt) traverseWithKey f (KCC emm) = KCC <$> traverseWithKey_ go emm where go k = traverseWithKey (\nxt -> f $! k :& nxt) foldr f init (KCC emm) = foldrWithKey_ (\_ val z -> foldr f z val) init emm foldrWithKey f init (KCC emm) = foldrWithKey_ go init emm where go k val z = foldrWithKey (\nxt -> f $! k :& nxt) z val keysSet (KCC emm) = KCC $ mapWithKey_ (const keysSet) emm fromSet f (KCC ems) = KCC $ mapWithKey_ go ems where go k = fromSet (\nxt -> f $! k :& nxt) findMin (KCC emm) = case emm of Nil -> error "findMin: no minimal element" Tip k v -> (toEnum k :& t, v') where (t, v') = findMin v Bin _ m l r | m < 0 -> go r | otherwise -> go l where go (Tip k v) = (toEnum k :& t, v') where (t, v') = findMin v go (Bin _ _ l' _) = go l' go Nil = error "findMin: Nil" minViewWithKey (KCC emm) = goat emm >>= \(r, emm') -> return (r, KCC emm') where goat t = case t of Nil -> Nothing Bin p m l r | m < 0 -> case go r of (result, r') -> Just (result, binD p m l r') _ -> Just (go t) go (Bin p m l r) = case go l of (result, l') -> (result, binD p m l' r) go (Tip k y) = case minViewWithKey y of Just ((t, v), y') -> ((toEnum k :& t, v), tip k y') Nothing -> error "minViewWithKey: Nothing" go Nil = error "minViewWithKey Nil" union (KCC emm1) (KCC emm2) = KCC $ mergeWithKey' binD go id id emm1 emm2 where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 $ union x1 x2 unionWithKey f (KCC emm1) (KCC emm2) = KCC $ mergeWithKey' binD go id id emm1 emm2 where go = \(Tip k1 x1) (Tip _ x2) -> Tip k1 $ unionWithKey (g k1) x1 x2 g k1 nxt = f $! toEnum k1 :& nxt difference (KCC emm1) (KCC emm2) = KCC $ mergeWithKey' binD go id (const Nil) emm1 emm2 where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 (difference x1 x2) differenceWithKey f (KCC emm1) (KCC emm2) = KCC $ mergeWithKey' binD go id (const Nil) emm1 emm2 where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 $ differenceWithKey (\nxt -> f $! toEnum k1 :& nxt) x1 x2 intersection (KCC emm1) (KCC emm2) = KCC $ mergeWithKey' binD go (const Nil) (const Nil) emm1 emm2 where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 $ intersection x1 x2 intersectionWithKey f (KCC emm1) (KCC emm2) = KCC $ mergeWithKey' binD go (const Nil) (const Nil) emm1 emm2 where go = \(Tip k1 x1) (Tip _ x2) -> tip k1 $ intersectionWithKey (\nxt -> f $! toEnum k1 :& nxt) x1 x2 equal (KCC emm1) (KCC emm2) = emm1 == emm2 nequal (KCC emm1) (KCC emm2) = emm1 /= emm2 {-------------------------------------------------------------------- Helpers --------------------------------------------------------------------} insertWith_ :: Enum k => (v -> v) -> k -> v -> EMM k v -> EMM k v insertWith_ f !key' val emm = key `seq` go emm where go t = case t of Bin p m l r | nomatch key p m -> join key (Tip key val) p t | zero key m -> Bin p m (go l) r | otherwise -> Bin p m l (go r) Tip ky y | key == ky -> Tip key (f y) | otherwise -> join key (Tip key val) ky t Nil -> Tip key val key = fromEnum key' {-# INLINE insertWith_ #-} -- | 'alter_' is used to walk down the tree to find the 'EnumMapMap' to actually -- change. If the new 'EnumMapMap' is null then it's removed from the containing -- 'EMM'. alter_ :: (IsKey b) => (EnumMapMap b v -> EnumMapMap b v) -> Key -> EMM a (EnumMapMap b v) -> EMM a (EnumMapMap b v) alter_ f k = go where go t = case t of Bin p m l r | nomatch k p m -> joinD k (tip k $ f empty) p t | zero k m -> binD p m (go l) r | otherwise -> binD p m l (go r) Tip ky y | k == ky -> tip k $ f y | otherwise -> joinD k (tip k $ f empty) ky t Nil -> tip k $ f empty {-# INLINE alter_ #-} mapWithKey_ :: Enum k => (k -> v -> t) -> EMM k v -> EMM k t mapWithKey_ f = go where go (Bin p m l r) = Bin p m (go l) (go r) go (Tip k x) = Tip k (f (toEnum k) x) go Nil = Nil {-# INLINE mapWithKey_ #-} mapMaybeWithKey_ :: (IsKey b, Enum key) => (key -> EnumMapMap b v -> EnumMapMap b t) -> EMM a (EnumMapMap b v) -> EMM a (EnumMapMap b t) mapMaybeWithKey_ f = go where go (Bin p m l r) = binD p m (go l) (go r) go (Tip k x) = tip k $ f (toEnum k) x go Nil = Nil {-# INLINE mapMaybeWithKey_ #-} traverseWithKey_ :: (Enum k, Applicative t) => (k -> a -> t b) -> EMM k a -> t (EMM k b) traverseWithKey_ f = go where go Nil = pure Nil go (Tip k v) = Tip k <$> f (toEnum k) v go (Bin p m l r) = Bin p m <$> go l <*> go r foldrWithKey_ :: (Enum k) => (k -> v -> t -> t) -> t -> EMM k v -> t foldrWithKey_ f z = \emm -> case emm of Bin _ m l r | m < 0 -> go (go z l) r | otherwise -> go (go z r) l _ -> go z emm where go z' Nil = z' go z' (Tip kx tx) = f (toEnum kx) tx z' go z' (Bin _ _ l r) = go (go z' r) l {-# INLINE foldrWithKey_ #-} -- | See 'IntMap' documentation for an explanation of 'mergeWithKey''. mergeWithKey' :: (Prefix -> Mask -> EMM a v3 -> EMM a v3 -> EMM a v3) -> (EMM a v1 -> EMM a v2 -> EMM a v3) -> (EMM a v1 -> EMM a v3) -> (EMM a v2 -> EMM a v3) -> EMM a v1 -> EMM a v2 -> EMM a v3 mergeWithKey' bin' f g1 g2 = go where go t1@(Bin p1 m1 l1 r1) t2@(Bin p2 m2 l2 r2) | shorter m1 m2 = merge1 | shorter m2 m1 = merge2 | p1 == p2 = bin' p1 m1 (go l1 l2) (go r1 r2) | otherwise = maybe_join p1 (g1 t1) p2 (g2 t2) where merge1 | nomatch p2 p1 m1 = maybe_join p1 (g1 t1) p2 (g2 t2) | zero p2 m1 = bin' p1 m1 (go l1 t2) (g1 r1) | otherwise = bin' p1 m1 (g1 l1) (go r1 t2) merge2 | nomatch p1 p2 m2 = maybe_join p1 (g1 t1) p2 (g2 t2) | zero p1 m2 = bin' p2 m2 (go t1 l2) (g2 r2) | otherwise = bin' p2 m2 (g2 l2) (go t1 r2) go t1'@(Bin {}) t2'@(Tip k2' _) = merge t2' k2' t1' where merge t2 k2 t1@(Bin p1 m1 l1 r1) | nomatch k2 p1 m1 = maybe_join p1 (g1 t1) k2 (g2 t2) | zero k2 m1 = bin' p1 m1 (merge t2 k2 l1) (g1 r1) | otherwise = bin' p1 m1 (g1 l1) (merge t2 k2 r1) merge t2 k2 t1@(Tip k1 _) | k1 == k2 = f t1 t2 | otherwise = maybe_join k1 (g1 t1) k2 (g2 t2) merge t2 _ Nil = g2 t2 go t1@(Bin {}) Nil = g1 t1 go t1'@(Tip k1' _) t2' = merge t1' k1' t2' where merge t1 k1 t2@(Bin p2 m2 l2 r2) | nomatch k1 p2 m2 = maybe_join k1 (g1 t1) p2 (g2 t2) | zero k1 m2 = bin' p2 m2 (merge t1 k1 l2) (g2 r2) | otherwise = bin' p2 m2 (g2 l2) (merge t1 k1 r2) merge t1 k1 t2@(Tip k2 _) | k1 == k2 = f t1 t2 | otherwise = maybe_join k1 (g1 t1) k2 (g2 t2) merge t1 _ Nil = g1 t1 go Nil t2 = g2 t2 maybe_join _ Nil _ t2 = t2 maybe_join _ t1 _ Nil = t1 maybe_join p1 t1 p2 t2 = join p1 t1 p2 t2 {-# INLINE maybe_join #-} {-# INLINE mergeWithKey' #-} {--------------------------------------------------------------------- Instances ---------------------------------------------------------------------} -- Eq instance (Eq v, IsKey k) => Eq (EnumMapMap k v) where t1 == t2 = equal t1 t2 t1 /= t2 = nequal t1 t2 instance Eq v => Eq (EMM k v) where t1 == t2 = equalE t1 t2 t1 /= t2 = nequalE t1 t2 equalE :: Eq v => EMM k v -> EMM k v -> Bool equalE (Bin p1 m1 l1 r1) (Bin p2 m2 l2 r2) = (m1 == m2) && (p1 == p2) && equalE l1 l2 && equalE r1 r2 equalE (Tip kx x) (Tip ky y) = (kx == ky) && (x==y) equalE Nil Nil = True equalE _ _ = False nequalE :: Eq v => EMM k v -> EMM k v -> Bool nequalE (Bin p1 m1 l1 r1) (Bin p2 m2 l2 r2) = (m1 /= m2) || (p1 /= p2) || nequalE l1 l2 || nequalE r1 r2 nequalE (Tip kx x) (Tip ky y) = (kx /= ky) || (x/=y) nequalE Nil Nil = False nequalE _ _ = True instance (IsKey k) => Functor (EnumMapMap k) where fmap = map -- | This instance differs from the 'Monoid' instance in 'IntMap'. Where the keys -- are the same the values are combined using 'mappend'. instance (IsKey k, Semigroup v) => Monoid (EnumMapMap k v) where mempty = empty mappend = unionWith (<>) mconcat = unionsWith (<>) instance (IsKey k, Semigroup v) => Semigroup (EnumMapMap k v) where (<>) = unionWith (<>) times1p _ a = a instance (Show v, Show (EnumMapMap t v)) => Show (EnumMapMap (k :& t) v) where show (KCC emm) = show emm instance (NFData v, NFData (EnumMapMap t v)) => NFData (EnumMapMap (k :& t) v) where rnf (KCC emm) = go emm where go Nil = () go (Tip _ v) = rnf v go (Bin _ _ l r) = go l `seq` go r instance (NFData k, NFData t) => NFData (k :& t) where rnf (k :& t) = rnf k `seq` rnf t -- Foldable instance (FOLD.Foldable (EnumMapMap t), Enum k, Eq k, IsKey t, HasSKey t) => FOLD.Foldable (EnumMapMap (k :& t)) where fold (KCC emm) = go emm where go Nil = mempty go (Tip _ v) = FOLD.fold v go (Bin _ _ l r) = go l `mappend` go r foldr = foldr foldMap f (KCC emm) = go emm where go Nil = mempty go (Tip _ v) = FOLD.foldMap f v go (Bin _ _ l r) = go l `mappend` go r instance (IsKey k, FOLD.Foldable (EnumMapMap k)) => Traversable (EnumMapMap k) where traverse f = traverseWithKey (\_ -> f) -- Default instance (IsKey k) => Default (EnumMapMap k v) where def = empty -- Typeable deriving instance Typeable2 (:&) deriving instance Typeable2 EnumMapMap -- SafeCopy instance (Enum a, SafeCopy b) => SafeCopy (a :& b) where getCopy = contain $ do a <- safeGet b <- safeGet return (toEnum a :& b) putCopy (a :& b) = contain $ do safePut $ fromEnum a safePut b errorTypeName _ = "(:&)" -- We only define this for (k :& t) here because the more general version -- causes overlaps between EnumMapMap and EnumMapSet. instance (SafeCopy k, SafeCopy (NestedPair k v), IsKey k, Result k k v ~ v, SubKey k k v, MkNestedPair k v) => SafeCopy (EnumMapMap k v) where getCopy = contain $ fmap fromNestedPairList safeGet putCopy = contain . safePut . toNestedPairList errorTypeName _ = "EnumMapMap" {-------------------------------------------------------------------- Nat conversion --------------------------------------------------------------------} natFromInt :: Int -> Nat natFromInt = fromIntegral {-# INLINE natFromInt #-} intFromNat :: Nat -> Int intFromNat = fromIntegral {-# INLINE intFromNat #-} shiftRL, shiftLL :: Nat -> Int -> Nat shiftRL (W# x) (I# i) = W# (uncheckedShiftRL# x i) shiftLL (W# x) (I# i) = W# (uncheckedShiftL# x i) {-# INLINE shiftRL #-} {-# INLINE shiftLL #-} {-------------------------------------------------------------------- Join --------------------------------------------------------------------} join :: Prefix -> EMM a v -> Prefix -> EMM a v -> EMM a v join p1 t1 p2 t2 | zero p1 m = Bin p m t1 t2 | otherwise = Bin p m t2 t1 where m = branchMask p1 p2 p = mask p1 m {-# INLINE join #-} joinD :: (IsKey b) => Prefix -> EMM a (EnumMapMap b v) -> Prefix -> EMM a (EnumMapMap b v) -> EMM a (EnumMapMap b v) joinD p1 t1 p2 t2 | zero p1 m = binD p m t1 t2 | otherwise = binD p m t2 t1 where m = branchMask p1 p2 p = mask p1 m {-# INLINE joinD #-} {-------------------------------------------------------------------- @bin@ assures that we never have empty trees within a tree. --------------------------------------------------------------------} bin :: Prefix -> Mask -> EMM k v -> EMM k v -> EMM k v bin _ _ l Nil = l bin _ _ Nil r = r bin p m l r = Bin p m l r {-# INLINE bin #-} {-------------------------------------------------------------------- @binD@ assures that we never have empty trees in the next level --------------------------------------------------------------------} binD :: (IsKey b) => Prefix -> Mask -> EMM a (EnumMapMap b v) -> EMM a (EnumMapMap b v) -> EMM a (EnumMapMap b v) binD _ _ l Nil = l binD _ _ Nil r = r binD p m l r@(Tip _ y) | null y = l | otherwise = Bin p m l r binD p m l@(Tip _ y) r | null y = r | otherwise = Bin p m l r binD p m l r = Bin p m l r {-# INLINE binD #-} tip :: (IsKey b) => Key -> EnumMapMap b v -> EMM a (EnumMapMap b v) tip k val | null val = Nil | otherwise = Tip k val {-# INLINE tip #-} {-------------------------------------------------------------------- Endian independent bit twiddling --------------------------------------------------------------------} zero :: Key -> Mask -> Bool zero i m = natFromInt i .&. natFromInt m == 0 {-# INLINE zero #-} nomatch,match :: Key -> Prefix -> Mask -> Bool nomatch i p m = mask i m /= p {-# INLINE nomatch #-} match i p m = mask i m == p {-# INLINE match #-} mask :: Key -> Mask -> Prefix mask i m = maskW (natFromInt i) (natFromInt m) {-# INLINE mask #-} {-------------------------------------------------------------------- Big endian operations --------------------------------------------------------------------} maskW :: Nat -> Nat -> Prefix maskW i m = intFromNat (i .&. (complement (m-1) `xor` m)) {-# INLINE maskW #-} shorter :: Mask -> Mask -> Bool shorter m1 m2 = natFromInt m1 > natFromInt m2 {-# INLINE shorter #-} branchMask :: Prefix -> Prefix -> Mask branchMask p1 p2 = intFromNat (highestBitMask (natFromInt p1 `xor` natFromInt p2)) {-# INLINE branchMask #-} {---------------------------------------------------------------------- [highestBitMask] returns a word where only the highest bit is set. It is found by first setting all bits in lower positions than the highest bit and than taking an exclusive or with the original value. Allthough the function may look expensive, GHC compiles this into excellent C code that subsequently compiled into highly efficient machine code. The algorithm is derived from Jorg Arndt's FXT library. ----------------------------------------------------------------------} highestBitMask :: Nat -> Nat highestBitMask x0 = case (x0 .|. shiftRL x0 1) of x1 -> case (x1 .|. shiftRL x1 2) of x2 -> case (x2 .|. shiftRL x2 4) of x3 -> case (x3 .|. shiftRL x3 8) of x4 -> case (x4 .|. shiftRL x4 16) of x5 -> case (x5 .|. shiftRL x5 32) of -- for 64 bit platforms x6 -> (x6 `xor` shiftRL x6 1) {-# INLINE highestBitMask #-} {-------------------------------------------------------------------- Utilities --------------------------------------------------------------------} foldlStrict :: (a -> b -> a) -> a -> [b] -> a foldlStrict f = go where go z [] = z go z (x:xs) = let z' = f z x in z' `seq` go z' xs {-# INLINE foldlStrict #-}