TrieMap-0.5.3: Data/TrieMap/RadixTrie.hs
{-# LANGUAGE TemplateHaskell, FlexibleContexts, TypeFamilies, MultiParamTypeClasses, PatternGuards #-}
module Data.TrieMap.RadixTrie () where
import Data.TrieMap.TrieKey
import Data.TrieMap.Sized
import Data.TrieMap.Applicative
import Data.TrieMap.CPair
import Data.TrieMap.Regular.Class
-- import Data.TrieMap.Regular.TH
import Control.Applicative
import Control.Arrow
import Control.Monad
import Data.Maybe
import Data.Monoid
import Data.Foldable
import Data.Traversable
import Prelude hiding (lookup, foldr, foldl)
data Edge k m a = Edge {-# UNPACK #-} !Int [k] (Maybe a) (m (Edge k m a))
type Edge' k a = Edge k (TrieMap k) a
type MEdge k m a = Maybe (Edge k m a)
type MEdge' k a = Maybe (Edge' k a)
newtype RadixTrie k a = Radix (MEdge' k a)
type instance TrieMapT [] = RadixTrie
type instance TrieMap [k] = RadixTrie k
edgeSize :: Edge k m a -> Int
edgeSize (Edge sz _ _ _) = sz
instance TrieKey k (TrieMap k) => TrieKey [k] (RadixTrie k) where
emptyM = emptyT
nullM = nullT
lookupM = lookupT
lookupIxM = lookupIxT
assocAtM = assocAtT
alterM = alterT
alterLookupM = alterLookupT
traverseWithKeyM = traverseWithKeyT
foldWithKeyM = foldWithKeyT
foldlWithKeyM = foldlWithKeyT
mapEitherM = mapEitherT
splitLookupM = splitLookupT
unionM = unionT
isectM = isectT
diffM = diffT
extractM = extractT
isSubmapM = isSubmapT
fromListM = fromListT
fromAscListM = fromAscListT
fromDistAscListM = fromDistAscListT
instance TrieKeyT [] RadixTrie where
emptyT = Radix Nothing
nullT (Radix m) = isNothing m
sizeT _ (Radix m) = maybe 0 edgeSize m
lookupT ks (Radix m) = m >>= lookup ks
alterT s f ks (Radix m) = Radix (alter s f ks m)
alterLookupT s f ks (Radix m) = Radix <$> alterLookupE s f ks m
traverseWithKeyT s f (Radix m) = Radix <$> traverse (traverseE s f) m
extractT s f (Radix m) = maybe empty (fmap Radix <.> extractE s f) m
foldWithKeyT f (Radix m) z = foldr (foldE f) z m
foldlWithKeyT f (Radix m) z = foldl (foldlE f) z m
mapEitherT s1 s2 f (Radix m) = (Radix *** Radix) (maybe (Nothing, Nothing) (mapEitherE s1 s2 f) m)
unionT s f (Radix m1) (Radix m2) = Radix (unionMaybe (unionE s f) m1 m2)
isectT s f (Radix m1) (Radix m2) = Radix (isectMaybe (isectE s f) m1 m2)
diffT s f (Radix m1) (Radix m2) = Radix (diffMaybe (diffE s f) m1 m2)
lookupIxT s ks (Radix m) = maybe (empty, empty, empty) (lookupIxE s 0 ks) m
isSubmapT (<=) (Radix m1) (Radix m2) = subMaybe (isSubmapE (<=)) m1 m2
splitLookupT s f ks (Radix m) = Radix `sides` maybe (Nothing, Nothing, Nothing) (splitLookupE s f ks) m
assocAtT s i (Radix m) = maybe (empty, empty, empty) (assocAtE s 0 i) m
cat :: [k] -> Edge' k a -> Edge' k a
ks `cat` Edge sz ls v ts = Edge sz (ks ++ ls) v ts
cons :: k -> Edge' k a -> Edge' k a
k `cons` Edge sz ks v ts = Edge sz (k:ks) v ts
edge :: TrieKey k (TrieMap k) => Sized a -> [k] -> Maybe a -> TrieMap k (Edge' k a) -> Edge' k a
edge s ks v ts = Edge (maybe 0 s v + sizeM edgeSize ts) ks v ts
singleMaybe :: TrieKey k (TrieMap k) => Sized a -> [k] -> Maybe a -> MEdge' k a
singleMaybe s ks v = do v <- v
return (edge s ks (Just v) emptyM)
compact :: TrieKey k (TrieMap k) => Edge' k a -> MEdge' k a
compact e@(Edge sz ks Nothing ts) = case assocsM ts of
[] -> Nothing
[(l, e')] -> compact (ks `cat` (l `cons` e'))
_ -> Just e
compact e = Just e
lookup :: (Eq k, TrieKey k (TrieMap k)) => [k] -> Edge' k a -> Maybe a
lookup ks (Edge _ ls v ts) = match ks ls where
match (k:ks) (l:ls)
| k == l = match ks ls
match (k:ks) [] = lookupM k ts >>= lookup ks
match [] [] = v
match _ _ = Nothing
alter :: (TrieKey k (TrieMap k)) => Sized a -> (Maybe a -> Maybe a) -> [k] -> MEdge' k a -> MEdge' k a
alter s f ks0 Nothing = singleMaybe s ks0 (f Nothing)
alter s f ks0 (Just e@(Edge sz ls0 v ts)) = match 0 ks0 ls0 where
match i _ _ | i `seq` False = undefined
match i (k:ks) (l:ls) = case compare k l of
LT | Just v' <- f Nothing
-> Just $ let sv = s v' in Edge (sv + sz) (take i ls0) Nothing (fromDistAscListM edgeSize
[(k, Edge sv ks (Just v') emptyM), (l, Edge sz ls v ts)])
EQ -> match (i+1) ks ls
GT | Just v' <- f Nothing
-> Just $ let sv = s v' in Edge (sv + sz) (take i ls0) Nothing (fromDistAscListM edgeSize
[(l, Edge sz ls v ts), (k, Edge sv ks (Just v') emptyM)])
_ -> Just e
match _ (k:ks) [] = compact $ edge s ls0 v (alterM edgeSize g k ts) where
g = alter s f ks
match _ [] (l:ls)
| Just v' <- f Nothing
= Just (Edge (s v' + sz) ks0 (Just v') (singletonM edgeSize l (Edge sz ls v ts)))
match _ [] []
= compact (edge s ls0 (f v) ts)
match _ _ _ = Just e
alterLookupE :: TrieKey k (TrieMap k) => Sized a -> (Maybe a -> CPair z (Maybe a)) -> [k] -> MEdge' k a -> CPair z (MEdge' k a)
alterLookupE s f ks Nothing = singleMaybe s ks <$> f Nothing
alterLookupE s f ks0 (Just e@(Edge sz ls0 v0 ts0)) = match 0 ks0 ls0 where
match i _ _ | i `seq` False = undefined
match i (k:ks) (l:ls) = case compare k l of
LT -> fmap (Just . maybe e (\ v' -> let sv = s v' in Edge (sz + sv) (take i ls0) Nothing $
fromDistAscListM edgeSize [(k, Edge sv ks (Just v') emptyM), (l, Edge sz ls v0 ts0)]))
(f Nothing)
GT -> fmap (Just . maybe e (\ v' -> let sv = s v' in Edge (sz + sv) (take i ls0) Nothing $
fromDistAscListM edgeSize [(l, Edge sz ls v0 ts0), (k, Edge sv ks (Just v') emptyM)]))
(f Nothing)
EQ -> match (i+1) ks ls
match _ (k:ks) [] = fmap (compact . edge s ls0 v0) (alterLookupM edgeSize g k ts0) where
g = alterLookupE s f ks
match _ [] (l:ls) = fmap (Just . maybe e (\ v' -> let sv = s v' in Edge (sv + sz) ks0 (Just v') (singletonM edgeSize l (Edge sz ls v0 ts0))))
(f Nothing)
match _ [] [] = fmap (\ v' -> compact $ edge s ls0 v' ts0) (f v0)
traverseE :: (Applicative f, TrieKey k (TrieMap k)) => Sized b -> ([k] -> a -> f b) -> Edge' k a -> f (Edge' k b)
traverseE s f (Edge _ ks v ts)
= edge s ks <$> traverse (f ks) v <*> traverseWithKeyM edgeSize g ts
where g l = traverseE s (\ ls -> f (ks ++ l:ls))
extractE :: (Alternative f, TrieKey k (TrieMap k)) => Sized a -> ([k] -> a -> f (CPair x (Maybe a))) -> Edge' k a -> f (CPair x (MEdge' k a))
extractE s f (Edge _ ks v ts) = case v of
Nothing -> rest
Just v -> fmap (\ v' -> compact (edge s ks v' ts)) <$> f ks v <|> rest
where rest = fmap (compact . edge s ks v) <$> extractM edgeSize g ts
g l = extractE s (\ ls -> f (ks ++ l:ls))
aboutE :: (Alternative f, TrieKey k (TrieMap k)) => ([k] -> a -> f x) -> Edge' k a -> f x
aboutE f = cpFst <.> extractE (const 0) (\ k a -> fmap (flip cP Nothing) (f k a))
foldE :: TrieKey k (TrieMap k) => ([k] -> a -> b -> b) -> Edge' k a -> b -> b
foldE f (Edge _ ks v ts) z = foldr (f ks) (foldWithKeyM g ts z) v where
g l = foldE (\ ls -> f (ks ++ l:ls))
foldlE :: TrieKey k (TrieMap k) => ([k] -> b -> a -> b) -> b -> Edge' k a -> b
foldlE f z (Edge _ ks v ts) = foldlWithKeyM g ts (foldl (f ks) z v) where
g l = foldlE (\ ls -> f (ks ++ l:ls))
mapEitherE :: TrieKey k (TrieMap k) => Sized b -> Sized c -> ([k] -> a -> (Maybe b, Maybe c)) -> Edge' k a ->
(MEdge' k b, MEdge' k c)
mapEitherE s1 s2 f (Edge _ ks v ts) = (compact *** compact) (edge s1 ks vL tsL, edge s2 ks vR tsR)
where (vL, vR) = maybe (Nothing, Nothing) (f ks) v
(tsL, tsR) = mapEitherM edgeSize edgeSize (\ l -> mapEitherE s1 s2 (\ ls -> f (ks ++ l:ls))) ts
unionE :: TrieKey k (TrieMap k) => Sized a -> ([k] -> a -> a -> Maybe a) -> Edge' k a -> Edge' k a -> MEdge' k a
unionE s f eK@(Edge szK ks0 vK tsK) eL@(Edge szL ls0 vL tsL) = match 0 ks0 ls0 where
match i _ _ | i `seq` False = undefined
match i (k:ks) (l:ls) = case compare k l of
EQ -> match (i+1) ks ls
LT -> Just $ Edge (szK + szL) (take i ks0) Nothing (fromDistAscListM edgeSize
[(k, Edge szK ks vK tsK), (l, Edge szL ls vL tsL)])
GT -> Just $ Edge (szK + szL) (take i ks0) Nothing (fromDistAscListM edgeSize
[(l, Edge szL ls vL tsL), (k, Edge szK ks vK tsK)])
match _ [] (l:ls) = compact (edge s ks0 vK (alterM edgeSize g l tsK)) where
g (Just eK') = unionE s (\ ls' -> f (ks0 ++ l:ls')) eK' (Edge szL ls vL tsL)
g Nothing = Just (Edge szL ls vL tsL)
match _ (k:ks) [] = compact (edge s ls0 vL (alterM edgeSize g k tsL)) where
g Nothing = Just (Edge szK ks vK tsK)
g (Just eL') = unionE s (\ ks' -> f (ls0 ++ k:ks')) (Edge szK ks vK tsK) eL'
match _ [] [] = compact (edge s ls0 (unionMaybe (f ls0) vK vL) (unionM edgeSize g tsK tsL)) where
g x = unionE s (\ xs -> f (ls0 ++ x:xs))
isectE :: TrieKey k (TrieMap k) => Sized c -> ([k] -> a -> b -> Maybe c) -> Edge' k a -> Edge' k b -> MEdge' k c
isectE s f eK@(Edge szK ks0 vK tsK) eL@(Edge szL ls0 vL tsL) = match ks0 ls0 where
match (k:ks) (l:ls)
| k == l = match ks ls
match (k:ks) [] = do eL' <- lookupM k tsL
cat ls0 <$> cons k <$> isectE s (\ ks' -> f (ls0 ++ k:ks')) (Edge szK ks vK tsK) eL'
match [] (l:ls) = do eK' <- lookupM l tsK
cat ks0 <$> cons l <$> isectE s (\ ls' -> f (ks0 ++ l:ls')) eK' (Edge szL ls vL tsL)
match [] [] = compact (edge s ks0 (isectMaybe (f ks0) vK vL) (isectM edgeSize g tsK tsL)) where
g x = isectE s (\ xs -> f (ks0 ++ x:xs))
match _ _ = Nothing
diffE :: TrieKey k (TrieMap k) => Sized a -> ([k] -> a -> b -> Maybe a) -> Edge' k a -> Edge' k b -> MEdge' k a
diffE s f eK@(Edge szK ks0 vK tsK) eL@(Edge szL ls0 vL tsL) = match ks0 ls0 where
match (k:ks) (l:ls)
| k == l = match ks ls
match (k:ks) []
| Just eL' <- lookupM k tsL
= cat ls0 . cons k <$> diffE s (\ ks' -> f (ls0 ++ k:ks')) (Edge szK ks vK tsK) eL'
match [] (l:ls)
= compact (edge s ks0 vK (alterM edgeSize (>>= g) l tsK))
where g eK' = diffE s (\ ls' -> f (ks0 ++ l:ls')) eK' (Edge szL ls vL tsL)
match [] [] = compact (edge s ks0 (diffMaybe (f ks0) vK vL) (diffM edgeSize g tsK tsL)) where
g x = diffE s (\ xs -> f (ks0 ++ x:xs))
match _ _ = Just eK
lookupIxE :: TrieKey k (TrieMap k) => Sized a -> Int -> [k] -> Edge' k a -> IndexPos [k] a
lookupIxE s i ks e@(Edge sz ls v ts) = match ks ls where
match (k:ks) (l:ls) = case compare k l of
LT -> (empty, empty, aboutE (return .: Asc i) e)
EQ -> match ks ls
GT -> (aboutE (\ k a -> return (Asc (i + sz - s a) k a)) e, empty, empty)
match (k:ks) [] = let sv = maybe 0 s v in case onIndex (i + sv +) (lookupIxM edgeSize k ts) of
(lb, x, ub) -> let lookupX = do Asc i' k' e' <- x
return $ onKey (\ ks' -> ls ++ k':ks') $
lookupIxE s i' ks e'
in ((do v <- Last v
return (Asc i ls v)) <|>
(do Asc iL kL eL <- lb
aboutE (\ ksL vL -> return $ Asc (iL + edgeSize eL - s vL) (ls ++ kL:ksL) vL) eL) <|>
(do (lb', _, _) <- Last lookupX
lb'),
(do (_, x', _) <- lookupX
x'),
(do (_, _, ub') <- First lookupX
ub') <|>
(do Asc iU kU eU <- ub
aboutE (\ ksU -> return . Asc iU (ls ++ kU:ksU)) eU))
match [] (l:ls) = (empty, empty, aboutE (return .: Asc i) e)
match [] [] = (empty, Asc i ls <$> v, aboutM (\ x -> aboutE (\ xs -> return . Asc (i + maybe 0 s v) (ls ++ x:xs))) ts)
isSubmapE :: TrieKey k (TrieMap k) => LEq a b -> LEq (Edge' k a) (Edge' k b)
isSubmapE (<=) (Edge szK ks vK tsK) (Edge szL ls vL tsL) = match ks ls where
match (k:ks) (l:ls)
| k == l = match ks ls
match (k:ks) []
| Just eL' <- lookupM k tsL
= isSubmapE (<=) (Edge szK ks vK tsK) eL'
match [] [] = subMaybe (<=) vK vL && isSubmapM (isSubmapE (<=)) tsK tsL
match _ _ = False
splitLookupE :: TrieKey k (TrieMap k) => Sized a -> (a -> (Maybe a, Maybe x, Maybe a)) -> [k] -> Edge' k a ->
(MEdge' k a, Maybe x, MEdge' k a)
splitLookupE s f ks e@(Edge _ ls v ts) = match ks ls where
match (k:ks) (l:ls) = case compare k l of
LT -> (Nothing, Nothing, Just e)
GT -> (Just e, Nothing, Nothing)
EQ -> match ks ls
match (k:ks) [] = case splitLookupM edgeSize g k ts of
(tsL, x, tsR) -> (compact (edge s ls v tsL), x, compact (edge s ls Nothing tsR))
where g = splitLookupE s f ks
match [] (l:ls) = (Nothing, Nothing, Just e)
match [] [] = (singleMaybe s ls vL, x, compact (edge s ls vR ts))
where (vL, x, vR) = maybe (Nothing, Nothing, Nothing) f v
assocAtE :: TrieKey k (TrieMap k) => Sized a -> Int -> Int -> Edge' k a -> IndexPos [k] a
assocAtE _ i0 i _ | i0 `seq` i `seq` False = undefined
assocAtE s i0 i (Edge sz ks v ts) = let sv = maybe 0 s v in case assocAtM edgeSize (i - sv) ts of
(lb, x, ub) -> let lookupX = do Asc i' l e' <- x
return (onKey (\ ls -> ks ++ l:ls) (assocAtE s (i0 + sv + i') (i - i') e'))
in ((do v <- Last v
guard (i >= sv)
return (Asc i0 ks v)) <|>
(do Asc iL lL eL <- lb
aboutE (\ ls vL -> return (Asc (i0 + iL + sv + edgeSize eL - s vL) (ks ++ lL:ls) vL)) eL) <|>
(do (lb', _, _) <- Last lookupX
lb'),
(do v <- v
guard (i >= 0 && i < sv)
return (Asc i0 ks v)) <|>
(do (_, x', _) <- lookupX
x'),
(do (_, _, ub') <- First lookupX
ub') <|>
(do v <- First v
guard (i < 0)
return (Asc i0 ks v)))