----------------------------------------------------------------------------- -- | -- Module : Unstable.Org.Lochan.Trie -- Copyright : (c) Keith Wansbrough 2005 -- License : BSD-style -- -- Maintainer : keith.hlib at lochan.org -- : modified and extended by Ulf Norell -- Stability : experimental -- Portability : portable -- -- This module provides a very basic implementation of the Trie data type, -- with no great concern for efficiency, or for completeness of API. -- ----------------------------------------------------------------------------- module Agda.Utils.Trie ( -- * Data type Trie, -- * Constructors empty, singleton, union, unionWith, insert, insertWith, -- * Primitive accessors and mutators value, children, value_u, children_u, lookup, lookupPath, -- * Basic operations preOrder, upwards, downwards, -- * Derived operations takeWhile, takeWhile_V, fringe, ) where import Prelude hiding (takeWhile, lookup) import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe import Control.Monad -- |A Trie with key elements of type @k@ (keys of type @[k]@) and values of type @v@. data Trie k v = Trie { value :: Maybe v , children :: Map k (Trie k v) } deriving Show -- |Modify the 'children' field of a trie. value_u :: (Maybe v -> Maybe v) -> Trie k v -> Trie k v value_u f p = p { value = f (value p) } -- |Modify the 'children' field of a trie. children_u :: (Map k (Trie k v) -> Map k (Trie k v)) -> Trie k v -> Trie k v children_u f p = p { children = f (children p) } -- |The empty trie. empty :: Trie k v empty = Trie { value = Nothing, children = Map.empty } -- |The singleton trie. singleton :: Ord k => [k] -> v -> Trie k v singleton [] x = Trie { value = Just x, children = Map.empty } singleton (k:ks) x = Trie { value = Nothing, children = Map.singleton k (singleton ks x) } -- |Combining two tries. The first shadows the second. union :: Ord k => Trie k v -> Trie k v -> Trie k v union p1 p2 = Trie { value = mplus (value p1) (value p2), children = Map.unionWith union (children p1) (children p2) } -- |Combining two tries. If the two define the same key, the -- specified combining function is used. unionWith :: Ord k => (v -> v -> v) -> Trie k v -> Trie k v -> Trie k v unionWith f p1 p2 = Trie { value = lift f (value p1) (value p2) , children = Map.unionWith (unionWith f) (children p1) (children p2) } where lift _ Nothing y = y lift _ x Nothing = x lift _ (Just x) (Just y) = Just (f x y) -- |Insertion. insert :: Ord k => [k] -> v -> Trie k v -> Trie k v insert k v t = union (singleton k v) t insertWith :: Ord k => (v -> v -> v) -> [k] -> v -> Trie k v -> Trie k v insertWith f k v t = unionWith f (singleton k v) t -- |Lookup an element. lookup :: Ord k => [k] -> Trie k v -> Maybe v lookup [] t = value t lookup (k : ks) t = lookup ks =<< Map.lookup k (children t) -- |Lookup and return all values on the path. lookupPath :: Ord k => [k] -> Trie k v -> [v] lookupPath ks t = case ks of [] -> list $ value t k : ks -> concat $ list (value t) : list (fmap (lookupPath ks) (Map.lookup k $ children t)) where list = maybe [] (:[]) -- |Enumerate all (key,value) pairs, in preorder. preOrder :: Ord k => [k] -> Trie k v -> [([k],v)] preOrder ks p = getNode p ++ concatMap (\(k,p') -> preOrder (ks++[k]) p') (Map.toList (children p)) where getNode p = maybe [] (\ v -> [(ks,v)]) (value p) -- |An upwards accumulation on the trie. upwards :: Ord k => (Trie k v -> Trie k v) -> Trie k v -> Trie k v upwards f = f . children_u (Map.map (upwards f)) -- |A downwards accumulation on the trie. downwards :: Ord k => (Trie k v -> Trie k v) -> Trie k v -> Trie k v downwards f = children_u (Map.map (downwards f)) . f -- |Return the prefix of the trie satisfying @f@. takeWhile :: Ord k => (Trie k v -> Bool) -> Trie k v -> Trie k v takeWhile f = downwards (children_u (Map.filter f)) -- |Return the prefix of the trie satisfying @f@ on all values present. takeWhile_V :: Ord k => (v -> Bool) -> Trie k v -> Trie k v takeWhile_V f = takeWhile (maybe True f . value) -- |Return the fringe of the trie (the trie composed of only the leaf nodes). fringe :: Ord k => Trie k v -> Trie k v fringe = upwards (\ p -> if Map.null (children p) then p else value_u (const Nothing) p)