TrieMap-2.0.0: Data/TrieMap/RadixTrie/Edge.hs
{-# LANGUAGE MagicHash, BangPatterns, UnboxedTuples, PatternGuards, CPP #-}
{-# OPTIONS -funbox-strict-fields #-}
module Data.TrieMap.RadixTrie.Edge where
import Data.TrieMap.Sized
import Data.TrieMap.TrieKey
import Data.TrieMap.RadixTrie.Slice
import Data.TrieMap.IntMap ()
import Data.TrieMap.Applicative ()
import Control.Applicative
import Control.Monad
import Data.Word
import Data.Traversable
import Data.Foldable (foldr, foldl)
import Data.Vector.Generic hiding (indexM, cmp, foldr, foldl)
import qualified Data.Vector
import qualified Data.Vector.Storable
import Prelude hiding (length, foldr, foldl, zip, take)
import GHC.Exts
#define V(f) f (Data.Vector.Vector) (k)
#define U(f) f (Data.Vector.Storable.Vector) (Word)
type Branch v k a = TrieMap k (Edge v k a)
data Edge v k a =
Edge Int# !(Slice v k) !(Maybe a) (Branch v k a)
data EdgeLoc v k a = Loc !(Slice v k) (Branch v k a) (Path v k a)
data Path v k a = Root
| Deep (Path v k a) !(Slice v k) !(Maybe a) (Hole k (Edge v k a))
type MEdge v k a = Maybe (Edge v k a)
instance Sized (Edge v k a) where
getSize# (Edge s# _ _ _) = s#
{-# SPECIALIZE singleLoc :: U(Slice) -> U(EdgeLoc) a #-}
singleLoc :: TrieKey k => Slice v k -> EdgeLoc v k a
singleLoc ks = Loc ks emptyM Root
{-# SPECIALIZE singletonEdge :: Sized a => U(Slice) -> a -> U(Edge) a #-}
singletonEdge :: (TrieKey k, Sized a) => Slice v k -> a -> Edge v k a
singletonEdge ks a = edge ks (Just a) emptyM
{-# SPECIALIZE getSimpleEdge :: U(Edge) a -> Simple a #-}
getSimpleEdge :: TrieKey k => Edge v k a -> Simple a
getSimpleEdge (Edge _ _ v ts)
| nullM ts = maybe Null Singleton v
| otherwise = NonSimple
{-# SPECIALIZE edge :: Sized a => U(Slice) -> Maybe a -> U(Branch) a -> U(Edge) a #-}
edge :: (TrieKey k, Sized a) => Slice v k -> Maybe a -> Branch v k a -> Edge v k a
edge ks v ts = Edge (getSize# v +# sizeM ts) ks v ts
{-# INLINE compact #-}
-- TODO: figure out a way to GC dead keys
compact :: TrieKey k => Edge v k a -> MEdge v k a
compact e@(Edge _ ks Nothing ts) = case getSimpleM ts of
Null -> Nothing
Singleton e' -> Just (unDropEdge (len ks + 1) e')
_ -> Just e
compact e = Just e
dropEdge :: Int -> Edge v k a -> Edge v k a
dropEdge n (Edge s# ks v ts) = Edge s# (dropSlice n ks) v ts
unDropEdge :: Int -> Edge v k a -> Edge v k a
unDropEdge n (Edge s# ks v ts) = Edge s# (unDropSlice n ks) v ts
{-# SPECIALIZE lookupEdge :: TrieKey k => V() -> V(Edge) a -> Maybe a #-}
{-# SPECIALIZE lookupEdge :: U() -> U(Edge) a -> Maybe a #-}
lookupEdge :: (TrieKey k, Vector v k) => v k -> Edge v k a -> Maybe a
lookupEdge = lookupE where
lookupE !ks (Edge _ ls v ts) = if kLen < lLen then Nothing else matchSliceV matcher matches ks ls where
!kLen = length ks
!lLen = len ls
matcher k l z
| k =? l = z
| otherwise = Nothing
matches _ _
| kLen == lLen = v
| otherwise = do e' <- lookupM (ks `unsafeIndex` lLen) ts
lookupE (unsafeDrop (lLen + 1) ks) e'
{-# SPECIALIZE searchEdge :: TrieKey k => V(Slice) -> V(Edge) a -> V(Path) a -> (Maybe a, V(EdgeLoc) a) #-}
{-# SPECIALIZE searchEdge :: U(Slice) -> U(Edge) a -> U(Path) a -> (Maybe a, U(EdgeLoc) a) #-}
searchEdge :: (TrieKey k, Vector v k) => Slice v k -> Edge v k a -> Path v k a -> (Maybe a, EdgeLoc v k a)
searchEdge = searchE where
searchE !ks e@(Edge _ ls v ts) path = iMatchSlice matcher matches ks ls where
matcher i k l z
| k =? l = z
| (# _, tHole #) <- searchM k (singletonM l (dropEdge (i+1) e))
= (Nothing, Loc (dropSlice (i+1) ks) emptyM (Deep path (takeSlice i ls) Nothing tHole))
matches kLen lLen = case compare kLen lLen of
EQ -> (v, Loc ls ts path)
LT -> let (lPre, !l, _) = splitSlice kLen ls in
(Nothing, Loc lPre (singletonM l (dropEdge (kLen + 1) e)) path)
GT -> let (_, !k, ks') = splitSlice lLen ks in case searchM k ts of
(# Nothing, tHole #) -> (Nothing, Loc ks' emptyM (Deep path ls v tHole))
(# Just e', tHole #) -> searchE ks' e' (Deep path ls v tHole)
{-# SPECIALIZE mapEdge :: Sized b => (a -> b) -> U(Edge) a -> U(Edge) b #-}
mapEdge :: (TrieKey k, Sized b) => (a -> b) -> Edge v k a -> Edge v k b
mapEdge f = mapE where
mapE (Edge _ ks v ts) = edge ks (f <$> v) (fmapM mapE ts)
{-# SPECIALIZE mapMaybeEdge :: Sized b => (a -> Maybe b) -> U(Edge) a -> U(MEdge) b #-}
mapMaybeEdge :: (TrieKey k, Sized b) => (a -> Maybe b) -> Edge v k a -> MEdge v k b
mapMaybeEdge f = mapMaybeE where
mapMaybeE (Edge _ ks v ts) = compact (edge ks (v >>= f) (mapMaybeM mapMaybeE ts))
{-# SPECIALIZE mapEitherEdge :: (Sized b, Sized c) =>
(a -> (# Maybe b, Maybe c #)) -> U(Edge) a -> (# U(MEdge) b, U(MEdge) c #) #-}
mapEitherEdge :: (TrieKey k, Sized b, Sized c) =>
(a -> (# Maybe b, Maybe c #)) -> Edge v k a -> (# MEdge v k b, MEdge v k c #)
mapEitherEdge f = mapEitherE where
mapEitherE (Edge _ ks v ts) = (# compact (edge ks vL tsL), compact (edge ks vR tsR) #)
where !(# vL, vR #) = mapEitherMaybe f v
!(# tsL, tsR #) = mapEitherM mapEitherE ts
{-# SPECIALIZE traverseEdge :: (Applicative f, Sized b) =>
(a -> f b) -> U(Edge) a -> f (U(Edge) b) #-}
traverseEdge :: (TrieKey k, Applicative f, Sized b) =>
(a -> f b) -> Edge v k a -> f (Edge v k b)
traverseEdge f = traverseE where
traverseE (Edge _ ks v ts) = edge ks <$> traverse f v <*> traverseM traverseE ts
{-# SPECIALIZE foldrEdge :: (a -> b -> b) -> U(Edge) a -> b -> b #-}
foldrEdge :: TrieKey k => (a -> b -> b) -> Edge v k a -> b -> b
foldrEdge f = foldrE where
foldrE (Edge _ _ v ts) z = foldr f (foldrM foldrE ts z) v
foldlEdge :: TrieKey k => (b -> a -> b) -> b -> Edge v k a -> b
foldlEdge f = foldlE where
foldlE z (Edge _ _ v ts) = foldlM foldlE ts (foldl f z v)
{-# SPECIALIZE rebuild :: Sized a => U(MEdge) a -> U(Path) a -> U(MEdge) a #-}
rebuild :: (TrieKey k, Sized a) => MEdge v k a -> Path v k a -> MEdge v k a
rebuild e Root = e
rebuild e (Deep path ks v tHole) = rebuild (compact $ edge ks v $ assignM e tHole) path
{-# SPECIALIZE fillHoleEdge :: Sized a => Maybe a -> U(EdgeLoc) a -> U(MEdge) a #-}
fillHoleEdge :: (TrieKey k, Sized a) => Maybe a -> EdgeLoc v k a -> MEdge v k a
fillHoleEdge v (Loc ks ts path) = rebuild (compact (edge ks v ts)) path
{-# SPECIALIZE unionEdge :: (TrieKey k, Sized a) =>
(a -> a -> Maybe a) -> V(Edge) a -> V(Edge) a -> V(MEdge) a #-}
{-# SPECIALIZE unionEdge :: Sized a =>
(a -> a -> Maybe a) -> U(Edge) a -> U(Edge) a -> U(MEdge) a #-}
unionEdge :: (TrieKey k, Vector v k, Sized a) =>
(a -> a -> Maybe a) -> Edge v k a -> Edge v k a -> MEdge v k a
unionEdge f = unionE where
eK@(Edge _ ks0 vK tsK) `unionE` eL@(Edge _ ls0 vL tsL) = iMatchSlice matcher matches ks0 ls0 where
matcher i k l z = case unifyM k eK' l eL' of
Left{} -> z
Right ts -> Just (edge (takeSlice i ks0) Nothing ts)
where eK' = dropEdge (i+1) eK
eL' = dropEdge (i+1) eL
matches kLen lLen = case compare kLen lLen of
EQ -> compact $ edge ks0 (unionMaybe f vK vL) $ unionM unionE tsK tsL
LT -> let eL' = dropEdge (kLen + 1) eL; l = ls0 !$ kLen; !(# eK', holeKT #) = searchM l tsK
in compact $ edge ks0 vK $ assignM (maybe (Just eL') (`unionE` eL') eK') holeKT
GT -> let eK' = dropEdge (lLen + 1) eK; k = ks0 !$ lLen; !(# eL', holeLT #) = searchM k tsL
in compact $ edge ls0 vL $ assignM (maybe (Just eK') (eK' `unionE`) eL') holeLT
{-# SPECIALIZE isectEdge :: (TrieKey k, Sized c) =>
(a -> b -> Maybe c) -> V(Edge) a -> V(Edge) b -> V(MEdge) c #-}
{-# SPECIALIZE isectEdge :: Sized c =>
(a -> b -> Maybe c) -> U(Edge) a -> U(Edge) b -> U(MEdge) c #-}
isectEdge :: (TrieKey k, Vector v k, Sized c) =>
(a -> b -> Maybe c) -> Edge v k a -> Edge v k b -> MEdge v k c
isectEdge f = isectE where
eK@(Edge _ ks0 vK tsK) `isectE` eL@(Edge _ ls0 vL tsL) = matchSlice matcher matches ks0 ls0 where
matcher k l z = guard (k =? l) >> z
matches kLen lLen = case compare kLen lLen of
EQ -> compact $ edge ks0 (isectMaybe f vK vL) $ isectM isectE tsK tsL
LT -> let l = ls0 !$ kLen in do
eK' <- lookupM l tsK
let eL' = dropEdge (kLen + 1) eL
unDropEdge (kLen + 1) <$> eK' `isectE` eL'
GT -> let k = ks0 !$ lLen in do
eL' <- lookupM k tsL
let eK' = dropEdge (lLen + 1) eK
unDropEdge (lLen + 1) <$> eK' `isectE` eL'
{-# SPECIALIZE diffEdge :: (TrieKey k, Sized a) =>
(a -> b -> Maybe a) -> V(Edge) a -> V(Edge) b -> V(MEdge) a #-}
{-# SPECIALIZE diffEdge :: Sized a =>
(a -> b -> Maybe a) -> U(Edge) a -> U(Edge) b -> U(MEdge) a #-}
diffEdge :: (TrieKey k, Vector v k, Sized a) =>
(a -> b -> Maybe a) -> Edge v k a -> Edge v k b -> MEdge v k a
diffEdge f = diffE where
eK@(Edge _ ks0 vK tsK) `diffE` eL@(Edge _ ls0 vL tsL) = matchSlice matcher matches ks0 ls0 where
matcher k l z
| k =? l = z
| otherwise = Just eK
matches kLen lLen = case compare kLen lLen of
EQ -> compact $ edge ks0 (diffMaybe f vK vL) $ diffM diffE tsK tsL
LT -> let l = ls0 !$ kLen; eL' = dropEdge (kLen + 1) eL in case searchM l tsK of
(# Nothing, _ #) -> Just eK
(# Just eK', holeKT #) -> compact $ edge ks0 vK $ assignM (eK' `diffE` eL') holeKT
GT -> let k = ks0 !$ lLen; eK' = dropEdge (lLen + 1) eK in case lookupM k tsL of
Nothing -> Just eK
Just eL' -> fmap (unDropEdge (lLen + 1)) (eK' `diffE` eL')
{-# SPECIALIZE isSubEdge :: TrieKey k => LEq a b -> LEq (V(Edge) a) (V(Edge) b) #-}
{-# SPECIALIZE isSubEdge :: LEq a b -> LEq (U(Edge) a) (U(Edge) b) #-}
isSubEdge :: (TrieKey k, Vector v k) => LEq a b -> LEq (Edge v k a) (Edge v k b)
isSubEdge (<=) = isSubE where
eK@(Edge _ ks0 vK tsK) `isSubE` (Edge _ ls0 vL tsL) = matchSlice matcher matches ks0 ls0 where
matcher k l z = k =? l && z
matches kLen lLen = case compare kLen lLen of
LT -> False
EQ -> subMaybe (<=) vK vL && isSubmapM isSubE tsK tsL
GT -> let k = ks0 !$ lLen in case lookupM k tsL of
Nothing -> False
Just eL' -> isSubE (dropEdge (lLen + 1) eK) eL'
{-# SPECIALIZE beforeEdge :: Sized a => Maybe a -> U(EdgeLoc) a -> U(MEdge) a #-}
beforeEdge :: (TrieKey k, Sized a) => Maybe a -> EdgeLoc v k a -> MEdge v k a
beforeEdge v (Loc ks ts path) = buildBefore (compact (edge ks v ts)) path where
buildBefore !e Root
= e
buildBefore e (Deep path ks v tHole)
= buildBefore (compact $ edge ks v $ beforeM e tHole) path
{-# SPECIALIZE afterEdge :: Sized a => Maybe a -> U(EdgeLoc) a -> U(MEdge) a #-}
afterEdge :: (TrieKey k, Sized a) => Maybe a -> EdgeLoc v k a -> MEdge v k a
afterEdge v (Loc ks ts path) = buildAfter (compact (edge ks v ts)) path where
buildAfter !e Root
= e
buildAfter e (Deep path ks v tHole)
= buildAfter (compact $ edge ks v $ afterM e tHole) path
{-# SPECIALIZE extractEdgeLoc :: MonadPlus m => U(Edge) a -> U(Path) a -> m (a, U(EdgeLoc) a) #-}
extractEdgeLoc :: (TrieKey k, MonadPlus m) => Edge v k a -> Path v k a -> m (a, EdgeLoc v k a)
extractEdgeLoc (Edge _ ks v ts) path = case v of
Nothing -> extractTS
Just a -> return (a, Loc ks ts path) `mplus` extractTS
where extractTS = do (e', tHole) <- extractHoleM ts
extractEdgeLoc e' (Deep path ks v tHole)
{-# SPECIALIZE indexEdge :: Sized a => Int# -> U(Edge) a -> U(Path) a -> (# Int#, a, U(EdgeLoc) a #) #-}
indexEdge :: (TrieKey k, Sized a) => Int# -> Edge v k a -> Path v k a -> (# Int#, a, EdgeLoc v k a #)
indexEdge = indexE where
indexE i# (Edge _ ks v@(Just a) ts) path
| i# <# sv# = (# i#, a, Loc ks ts path #)
| (# i'#, e', tHole #) <- indexM (i# -# sv#) ts
= indexE i'# e' (Deep path ks v tHole)
where !sv# = getSize# a
indexE i# (Edge _ ks Nothing ts) path
= indexE i'# e' (Deep path ks Nothing tHole)
where !(# i'#, e', tHole #) = indexM i# ts
{-# SPECIALIZE unifyEdge :: (TrieKey k, Sized a) => V(Slice) -> a -> V(Slice) -> a -> Either (V(EdgeLoc) a) (V(Edge) a) #-}
{-# SPECIALIZE unifyEdge :: Sized a => U(Slice) -> a -> U(Slice) -> a -> Either (U(EdgeLoc) a) (U(Edge) a) #-}
unifyEdge :: (Vector v k, TrieKey k, Sized a) => Slice v k -> a -> Slice v k -> a -> Either (EdgeLoc v k a) (Edge v k a)
unifyEdge ks1 a1 ks2 a2 = iMatchSlice matcher matches ks1 ks2 where
matcher !i k1 k2 z =
case unifyM k1 (singletonEdge (dropSlice (i+1) ks1) a1) k2 (singletonEdge (dropSlice (i+1) ks2) a2) of
Left{} -> z
Right ts -> Right (edge (takeSlice i ks1) Nothing ts)
matches len1 len2 = case compare len1 len2 of
LT -> let (_,k2,ks2') = splitSlice len1 ks2 in
Right (edge ks1 (Just a1) (singletonM k2 (singletonEdge ks2' a2)))
GT -> let (_,k1,ks1') = splitSlice len2 ks1 in
Right (edge ks2 (Just a2) (singletonM k1 (singletonEdge ks1' a1)))
_ -> Left (singleLoc ks1)