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#
singleLoc :: TrieKey k => Slice v k -> EdgeLoc v k a
singleLoc ks = Loc ks emptyM Root
singletonEdge :: (TrieKey k, Sized a) => Slice v k -> a -> Edge v k a
singletonEdge ks a = edge ks (Just a) emptyM
getSimpleEdge :: TrieKey k => Edge v k a -> Simple a
getSimpleEdge (Edge _ _ v ts)
| nullM ts = maybe Null Singleton v
| otherwise = NonSimple
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
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
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'
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)
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)
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))
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
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
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)
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
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
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
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'
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')
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'
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
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
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)
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
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)