{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} module Data.Trie.Rooted where import Control.Applicative import Data.List.NonEmpty hiding (head, tail) import qualified Data.List.NonEmpty as NE import Data.Monoid import Data.Trie.Pseudo as P data Rooted t a = Rooted { root :: (Maybe a) , children :: [PseudoTrie t a] } deriving (Show, Eq, Functor) -- | Intersection instance instance (Eq t) => Applicative (Rooted t) where pure x = Rooted (Just x) [] (<*>) (Rooted mf fs) (Rooted mx xs) = Rooted (mf <*> mx) [intersectionWith ($) f x | f <- fs, x <- xs] -- | Union instance instance (Eq t, Monoid a) => Monoid (Rooted t a) where mempty = Rooted Nothing [] mappend = unionWith mappend newtype MergeRooted t a = MergeRooted { unMergeRooted :: Rooted t a } deriving (Functor) instance (Eq t) => Monoid (MergeRooted t a) where mempty = MergeRooted $ Rooted Nothing [] (MergeRooted x) `mappend` (MergeRooted y) = MergeRooted $ x `Data.Trie.Rooted.merge` y -- | Strictly constructive form of @Data.Trie.Pseudo.assign@ assign :: (Eq t) => [t] -> Maybe a -> Rooted t a -> Rooted t a assign [] mx (Rooted _ ys) = Rooted mx ys assign tss@(t:ts) mx (Rooted my ys) | any (`beginsWith` t) ys = Rooted my $ fmap (P.assign (NE.fromList tss) mx) ys | otherwise = case mx of Nothing -> Rooted my ys -- nowhere to remove Just x -> Rooted my $ Rest (NE.fromList tss) x : ys lookup :: (Eq t) => [t] -> Rooted t a -> Maybe a lookup [] (Rooted mx _) = mx lookup ts (Rooted _ xs) = foldr (go ts) Nothing xs where go ts x Nothing = P.lookup (NE.fromList ts) x go ts x ma@(Just a) = ma lookupNearestParent :: (Eq t) => [t] -> Rooted t a -> Maybe a lookupNearestParent ts (Rooted mx xs) = getFirst $ First mx <> First (foldr (go ts) Nothing xs) where go ts x Nothing = P.lookupNearestParent ts x go ts x ma@(Just a) = ma merge :: (Eq t) => Rooted t a -> Rooted t a -> Rooted t a merge (Rooted mx xs) (Rooted my ys) = Rooted (getLast $ Last mx <> Last my) $ foldr go [] $ xs ++ ys where go q [] = [q] go q (z:zs) | areDisjoint q z = q : z : zs | otherwise = P.merge q z : zs -- | Prepend root with a tag push :: Rooted t a -> NonEmpty t -> PseudoTrie t a push (Rooted (Just x) []) ts = Rest ts x push (Rooted Nothing []) _ = Nil push r@(Rooted mx xs) (t:|ts) = case ts of [] -> More (t,mx) $ NE.fromList xs _ -> More (t,Nothing) $ NE.fromList [push r $ NE.fromList ts] -- | Disjoint cases just pull children to common root unionWith :: (Eq t) => (a -> a -> a) -> Rooted t a -> Rooted t a -> Rooted t a unionWith f (Rooted mx xs) (Rooted my ys) = Rooted (f <$> mx <*> my) $ Prelude.concat [process f x y | x <- xs, y <- ys] where process f x y | areDisjoint x y = [unionWith' f x y] | otherwise = x : [y] -- partial function, neglecting disjoint cases unionWith' _ Nil Nil = Nil unionWith' _ Nil y = y unionWith' _ x Nil = x unionWith' f (Rest tss@(t:|ts) x) (Rest pss@(p:|ps) y) | tss == pss = Rest pss $ f x y | t == p = case (ts,ps) of ([], p':ps') -> More (t, Just x) $ Rest (fromList ps) y :| [] (t':ts', []) -> More (p, Just y) $ Rest (fromList ts) x :| [] (_,_) -> More (t,Nothing) $ fromList [unionWith' f (Rest (NE.fromList ts) x) (Rest (NE.fromList ps) y)] unionWith' f (More (t,mx) xs) (More (p,my) ys) | t == p = let zs = NE.toList xs ++ NE.toList ys in More (p,f <$> mx <*> my) $ NE.fromList $ foldr (\q (z':zs') -> unionWith' f z' q : zs') [head zs] (tail zs) unionWith' f (More (t,mx) xs) (Rest pss@(p:|ps) y) | t == p = case ps of [] -> More (p,f <$> mx <*> Just y) xs _ -> More (t,mx) $ fmap (flip (unionWith' f) $ Rest (fromList ps) y) xs unionWith' f (Rest tss@(t:|ts) x) (More (p,my) ys) | t == p = case ts of [] -> More (t,f <$> Just x <*> my) ys _ -> More (p,my) $ fmap (unionWith' f $ Rest (fromList ts) x) ys