packages feed

dawg 0.9 → 0.11

raw patch · 27 files changed

Files

− Data/DAWG/Dynamic.hs
@@ -1,238 +0,0 @@--- | The module implements /directed acyclic word graphs/ (DAWGs) internaly--- represented as /minimal acyclic deterministic finite-state automata/.--- The implementation provides fast insert and delete operations--- which can be used to build the DAWG structure incrementaly.--module Data.DAWG.Dynamic-(--- * DAWG type-  DAWG--- * Query-, numStates-, numEdges-, lookup--- * Construction-, empty-, fromList-, fromListWith-, fromLang--- ** Insertion-, insert-, insertWith--- ** Deletion-, delete--- * Conversion-, assocs-, keys-, elems-) where--import Prelude hiding (lookup)-import Control.Applicative ((<$>), (<*>))-import Control.Arrow (first)-import Data.List (foldl')-import qualified Control.Monad.State.Strict as S--import Data.DAWG.Types-import Data.DAWG.Graph (Graph)-import Data.DAWG.Dynamic.Internal-import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Graph as G-import qualified Data.DAWG.Dynamic.Node as N--type GraphM a b = S.State (Graph (N.Node a)) b--mkState :: (Graph a -> Graph a) -> Graph a -> ((), Graph a)-mkState f g = ((), f g)---- | Return node with the given identifier.-nodeBy :: ID -> GraphM a (N.Node a)-nodeBy i = G.nodeBy i <$> S.get---- Evaluate the 'G.insert' function within the monad.-insertNode :: Ord a => N.Node a -> GraphM a ID-insertNode = S.state . G.insert---- | Leaf node with no children and 'Nothing' value.-insertLeaf :: Ord a => GraphM a ID-insertLeaf = do-    i <- insertNode (N.Leaf Nothing)-    insertNode (N.Branch i T.empty)---- Evaluate the 'G.delete' function within the monad.-deleteNode :: Ord a => N.Node a -> GraphM a ()-deleteNode = S.state . mkState . G.delete---- | Invariant: the identifier points to the 'Branch' node.-insertM :: Ord a => [Sym] -> a -> ID -> GraphM a ID-insertM (x:xs) y i = do-    n <- nodeBy i-    j <- case N.onSym x n of-        Just j  -> return j-        Nothing -> insertLeaf-    k <- insertM xs y j-    deleteNode n-    insertNode (N.insert x k n)-insertM [] y i = do-    n <- nodeBy i-    w <- nodeBy (N.eps n)-    deleteNode w-    deleteNode n-    j <- insertNode (N.Leaf $ Just y)-    insertNode (n { N.eps = j })--insertWithM-    :: Ord a => (a -> a -> a)-    -> [Sym] -> a -> ID -> GraphM a ID-insertWithM f (x:xs) y i = do-    n <- nodeBy i-    j <- case N.onSym x n of-        Just j  -> return j-        Nothing -> insertLeaf-    k <- insertWithM f xs y j-    deleteNode n-    insertNode (N.insert x k n)-insertWithM f [] y i = do-    n <- nodeBy i-    w <- nodeBy (N.eps n)-    deleteNode w-    deleteNode n-    let y'new = case N.value w of-            Just y' -> f y y'-            Nothing -> y-    j <- insertNode (N.Leaf $ Just y'new)-    insertNode (n { N.eps = j })--deleteM :: Ord a => [Sym] -> ID -> GraphM a ID-deleteM (x:xs) i = do-    n <- nodeBy i-    case N.onSym x n of-        Nothing -> return i-        Just j  -> do-            k <- deleteM xs j-            deleteNode n-            insertNode (N.insert x k n)-deleteM [] i = do-    n <- nodeBy i-    w <- nodeBy (N.eps n)-    deleteNode w-    deleteNode n-    j <- insertLeaf-    insertNode (n { N.eps = j })-    -lookupM :: [Sym] -> ID -> GraphM a (Maybe a)-lookupM [] i = do-    j <- N.eps <$> nodeBy i-    N.value <$> nodeBy j-lookupM (x:xs) i = do-    n <- nodeBy i-    case N.onSym x n of-        Just j  -> lookupM xs j-        Nothing -> return Nothing--assocsAcc :: Graph (N.Node a) -> ID -> [([Sym], a)]-assocsAcc g i =-    here w ++ concatMap there (N.edges n)-  where-    n = G.nodeBy i g-    w = G.nodeBy (N.eps n) g-    here v = case N.value v of-        Just x  -> [([], x)]-        Nothing -> []-    there (sym, j) = map (first (sym:)) (assocsAcc g j)---- | Empty DAWG.-empty :: Ord b => DAWG a b-empty = -    let (i, g) = S.runState insertLeaf G.empty-    in  DAWG g i---- | Number of states in the automaton.-numStates :: DAWG a b -> Int-numStates = G.size . graph---- | Number of edges in the automaton.-numEdges :: DAWG a b -> Int-numEdges = sum . map (length . N.edges) . G.nodes . graph---- | Insert the (key, value) pair into the DAWG.-insert :: (Enum a, Ord b) => [a] -> b -> DAWG a b -> DAWG a b-insert xs' y d =-    let xs = map fromEnum xs'-        (i, g) = S.runState (insertM xs y $ root d) (graph d)-    in  DAWG g i-{-# INLINE insert #-}-{-# SPECIALIZE insert :: Ord b => String -> b -> DAWG Char b -> DAWG Char b #-}---- | Insert with a function, combining new value and old value.--- 'insertWith' f key value d will insert the pair (key, value) into d if--- key does not exist in the DAWG. If the key does exist, the function--- will insert the pair (key, f new_value old_value).-insertWith-    :: (Enum a, Ord b) => (b -> b -> b)-    -> [a] -> b -> DAWG a b -> DAWG a b-insertWith f xs' y d =-    let xs = map fromEnum xs'-        (i, g) = S.runState (insertWithM f xs y $ root d) (graph d)-    in  DAWG g i-{-# SPECIALIZE insertWith-        :: Ord b => (b -> b -> b) -> String -> b-        -> DAWG Char b -> DAWG Char b #-}---- | Delete the key from the DAWG.-delete :: (Enum a, Ord b) => [a] -> DAWG a b -> DAWG a b-delete xs' d =-    let xs = map fromEnum xs'-        (i, g) = S.runState (deleteM xs $ root d) (graph d)-    in  DAWG g i-{-# SPECIALIZE delete :: Ord b => String -> DAWG Char b -> DAWG Char b #-}---- | Find value associated with the key.-lookup :: (Enum a, Ord b) => [a] -> DAWG a b -> Maybe b-lookup xs' d =-    let xs = map fromEnum xs'-    in  S.evalState (lookupM xs $ root d) (graph d)-{-# SPECIALIZE lookup :: Ord b => String -> DAWG Char b -> Maybe b #-}---- | Return all key/value pairs in the DAWG in ascending key order.-assocs :: (Enum a, Ord b) => DAWG a b -> [([a], b)]-assocs-    = map (first (map toEnum))-    . (assocsAcc <$> graph <*> root)-{-# SPECIALIZE assocs :: Ord b => DAWG Char b -> [(String, b)] #-}---- | Return all keys of the DAWG in ascending order.-keys :: (Enum a, Ord b) => DAWG a b -> [[a]]-keys = map fst . assocs-{-# SPECIALIZE keys :: Ord b => DAWG Char b -> [String] #-}---- | Return all elements of the DAWG in the ascending order of their keys.-elems :: Ord b => DAWG a b -> [b]-elems = map snd . (assocsAcc <$> graph <*> root)---- | Construct DAWG from the list of (word, value) pairs.-fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a b-fromList xs =-    let update t (x, v) = insert x v t-    in  foldl' update empty xs-{-# INLINE fromList #-}-{-# SPECIALIZE fromList :: Ord b => [(String, b)] -> DAWG Char b #-}---- | Construct DAWG from the list of (word, value) pairs--- with a combining function.  The combining function is--- applied strictly.-fromListWith-    :: (Enum a, Ord b) => (b -> b -> b)-    -> [([a], b)] -> DAWG a b-fromListWith f xs =-    let update t (x, v) = insertWith f x v t-    in  foldl' update empty xs-{-# SPECIALIZE fromListWith-        :: Ord b => (b -> b -> b)-        -> [(String, b)] -> DAWG Char b #-}---- | Make DAWG from the list of words.  Annotate each word with--- the @()@ value.-fromLang :: Enum a => [[a]] -> DAWG a ()-fromLang xs = fromList [(x, ()) | x <- xs]-{-# SPECIALIZE fromLang :: [String] -> DAWG Char () #-}
− Data/DAWG/Dynamic/Internal.hs
@@ -1,27 +0,0 @@--- | The module exports internal representation of dynamic DAWG.--module Data.DAWG.Dynamic.Internal-(--- * DAWG type-  DAWG (..)-) where--import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, put, get)--import Data.DAWG.Types-import Data.DAWG.Graph (Graph)-import qualified Data.DAWG.Dynamic.Node as N---- | A directed acyclic word graph with phantom type @a@ representing--- type of alphabet elements.-data DAWG a b = DAWG-    { graph :: !(Graph (N.Node b))-    , root  :: !ID }-    deriving (Show, Eq, Ord)--instance (Ord b, Binary b) => Binary (DAWG a b) where-    put d = do-        put (graph d)-        put (root d)-    get = DAWG <$> get <*> get
− Data/DAWG/Dynamic/Node.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | Internal representation of dynamic automata nodes.--module Data.DAWG.Dynamic.Node-( Node(..)-, onSym-, edges-, children-, insert-) where--import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, Get, put, get)--import Data.DAWG.Types-import Data.DAWG.Util (combine)-import Data.DAWG.HashMap (Hash, hash)-import Data.DAWG.Trans.Map (Trans)-import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Trans.Hashed as H---- | Two nodes (states) belong to the same equivalence class (and,--- consequently, they must be represented as one node in the graph)--- iff they are equal with respect to their values and outgoing--- edges.------ Since 'Leaf' nodes are distinguished from 'Branch' nodes, two values--- equal with respect to '==' function are always kept in one 'Leaf'--- node in the graph.  It doesn't change the fact that to all 'Branch'--- nodes one value is assigned through the epsilon transition.------ Invariant: the 'eps' identifier always points to the 'Leaf' node.--- Edges in the 'edgeMap', on the other hand, point to 'Branch' nodes.-data Node a-    = Branch {-        -- | Epsilon transition.-          eps       :: {-# UNPACK #-} !ID-        -- | Transition map (outgoing edges).-        , transMap  :: !(H.Hashed Trans) }-    | Leaf { value  :: !(Maybe a) }-    deriving (Show, Eq, Ord)--instance Ord a => Hash (Node a) where-    hash Branch{..} = combine eps (H.hash transMap)-    hash Leaf{..}   = case value of-    	Just _	-> (-1)-	Nothing	-> (-2)--instance Binary a => Binary (Node a) where-    put Branch{..} = put (1 :: Int) >> put eps >> put transMap-    put Leaf{..}   = put (2 :: Int) >> put value-    get = do-        x <- get :: Get Int-        case x of-            1 -> Branch <$> get <*> get-            _ -> Leaf <$> get---- | Transition function.-onSym :: Sym -> Node a -> Maybe ID-onSym x (Branch _ t)    = T.lookup x t-onSym _ (Leaf _)        = Nothing-{-# INLINE onSym #-}---- | List of symbol/edge pairs outgoing from the node.-edges :: Node a -> [(Sym, ID)]-edges (Branch _ t)  = T.toList t-edges (Leaf _)      = []-{-# INLINE edges #-}---- | List of children identifiers.-children :: Node a -> [ID]-children = map snd . edges-{-# INLINE children #-}---- | Substitue edge determined by a given symbol.-insert :: Sym -> ID -> Node a -> Node a-insert x i (Branch w t) = Branch w (T.insert x i t)-insert _ _ l            = l-{-# INLINE insert #-}
− Data/DAWG/Graph.hs
@@ -1,213 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE DoAndIfThenElse #-}---- | Internal representation of the "Data.DAWG" automaton.  Names in this--- module correspond to a graphical representation of automaton: nodes refer--- to states and edges refer to transitions.--module Data.DAWG.Graph-( Graph (..)-, empty-, size-, nodes-, nodeBy-, insert-, delete-) where--import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, put, get)-import qualified Data.IntSet as S-import qualified Data.IntMap as M--import Data.DAWG.HashMap (Hash)-import qualified Data.DAWG.HashMap as H--type ID = Int---- | A set of nodes.  To every node a unique identifier is assigned.--- Invariants: ------   * freeIDs \\intersection occupiedIDs = \\emptySet,------   * freeIDs \\sum occupiedIDs =---     {0, 1, ..., |freeIDs \\sum occupiedIDs| - 1},------ where occupiedIDs = elemSet idMap.------ TODO: Is it possible to merge 'freeIDs' with 'ingoMap' to reduce--- the memory footprint?-data Graph n = Graph {-    -- | Map from nodes to IDs with hash values interpreted-    -- as keys and (node, ID) pairs interpreted as map elements.-      idMap     :: !(H.HashMap n ID)-    -- | Set of free IDs.-    , freeIDs   :: !S.IntSet-    -- | Map from IDs to nodes. -    , nodeMap   :: !(M.IntMap n)-    -- | Number of ingoing paths (different paths from the root-    -- to the given node) for each node ID in the graph.-    -- The number of ingoing paths can be also interpreted as-    -- a number of occurences of the node in a tree representation-    -- of the graph.-    , ingoMap   :: !(M.IntMap Int) }-    deriving (Show, Eq, Ord)--instance (Ord n, Binary n) => Binary (Graph n) where-    put Graph{..} = do-        put idMap-        put freeIDs-        put nodeMap-        put ingoMap-    get = Graph <$> get <*> get <*> get <*> get---- | Empty graph.-empty :: Graph n-empty = Graph H.empty S.empty M.empty M.empty---- | Size of the graph (number of nodes).-size :: Graph n -> Int-size = H.size . idMap---- | List of graph nodes.-nodes :: Graph n -> [n]-nodes = M.elems . nodeMap---- | Node with the given identifier.-nodeBy :: ID -> Graph n -> n-nodeBy i g = nodeMap g M.! i---- | Retrieve identifier of a node assuming that the node--- is present in the graph.  If the assumption is not--- safisfied, the returned identifier may be incorrect.-nodeID'Unsafe :: Hash n => n -> Graph n -> ID-nodeID'Unsafe n g = H.lookupUnsafe n (idMap g)---- | Add new graph node (assuming that it is not already a member--- of the graph).-newNode :: Hash n => n -> Graph n -> (ID, Graph n)-newNode n Graph{..} =-    (i, Graph idMap' freeIDs' nodeMap' ingoMap')-  where-    idMap'      = H.insertUnsafe n i idMap-    nodeMap'    = M.insert i n nodeMap-    ingoMap'    = M.insert i 1 ingoMap-    (i, freeIDs') = if S.null freeIDs-        then (H.size idMap, freeIDs)-        else S.deleteFindMin freeIDs---- | Remove node from the graph (assuming that it is a member--- of the graph).-remNode :: Hash n => ID -> Graph n -> Graph n-remNode i Graph{..} =-    Graph idMap' freeIDs' nodeMap' ingoMap'-  where-    idMap'      = H.deleteUnsafe n idMap-    nodeMap'    = M.delete i nodeMap-    ingoMap'    = M.delete i ingoMap-    freeIDs'    = S.insert i freeIDs-    n           = nodeMap M.! i---- | Increment the number of ingoing paths.-incIngo :: ID -> Graph n -> Graph n-incIngo i g = g { ingoMap = M.insertWith' (+) i 1 (ingoMap g) }---- | Decrement the number of ingoing paths and return--- the resulting number.-decIngo :: ID -> Graph n -> (Int, Graph n)-decIngo i g =-    let k = (ingoMap g M.! i) - 1-    in  (k, g { ingoMap = M.insert i k (ingoMap g) })---- | Insert node into the graph.  If the node was already a member--- of the graph, just increase the number of ingoing paths.--- NOTE: Number of ingoing paths will not be changed for any descendants--- of the node, so the operation alone will not ensure that properties--- of the graph are preserved.-insert :: Hash n => n -> Graph n -> (ID, Graph n)-insert n g = case H.lookup n (idMap g) of-    Just i  -> (i, incIngo i g)-    Nothing -> newNode n g---- | Delete node from the graph.  If the node was present in the graph--- at multiple positions, just decrease the number of ingoing paths.--- Function crashes if the node is not a member of the graph. --- NOTE: The function does not delete descendant nodes which may become--- inaccesible nor does it change the number of ingoing paths for any--- descendant of the node.-delete :: Hash n => n -> Graph n -> Graph n-delete n g = if num == 0-    then remNode i g'-    else g'-  where-    i = nodeID'Unsafe n g-    (num, g') = decIngo i g---- -- | Construct a graph from a list of node/ID pairs and a root ID.--- -- Identifiers must be consistent with edges outgoing from--- -- individual nodes.--- fromNodes :: Ord a => [(Node a, ID)] -> ID -> Graph a--- fromNodes xs rootID = graph---   where---     graph = Graph---         (M.fromList xs)---         IS.empty---         (IM.fromList $ map swap xs)---         ( foldl' updIngo (IM.singleton rootID 1)---             $ topSort graph rootID )---     swap (x, y) = (y, x)---     updIngo m i =---         let n = nodeBy i graph---             ingo = m IM.! i---         in  foldl' (push ingo) m (edges n)---     push x m j = IM.adjust (+x) j m--- --- postorder :: T.Tree a -> [a] -> [a]--- postorder (T.Node a ts) = postorderF ts . (a :)--- --- postorderF :: T.Forest a -> [a] -> [a]--- postorderF ts = foldr (.) id $ map postorder ts--- --- postOrd :: Graph a -> ID -> [ID]--- postOrd g i = postorder (dfs g i) []--- --- -- | Topological sort given a root ID.--- topSort :: Graph a -> ID -> [ID]--- topSort g = reverse . postOrd g--- --- -- | Depth first search starting with given ID.--- dfs :: Graph a -> ID -> T.Tree ID--- dfs g = prune . generate g--- --- generate :: Graph a -> ID -> T.Tree ID--- generate g i = T.Node i---     ( T.Node (eps n) []---     : map (generate g) (edges n) )---   where---     n = nodeBy i g--- --- type SetM a = S.State IS.IntSet a--- --- run :: SetM a -> a--- run act = S.evalState act IS.empty--- --- contains :: ID -> SetM Bool--- contains i = IS.member i <$> S.get--- --- include :: ID -> SetM ()--- include i = S.modify (IS.insert i)--- --- prune :: T.Tree ID -> T.Tree ID--- prune t = head $ run (chop [t])--- --- chop :: T.Forest ID -> SetM (T.Forest ID)--- chop [] = return []--- chop (T.Node v ts : us) = do---     visited <- contains v---     if visited then---         chop us---     else do---         include v---         as <- chop ts---         bs <- chop us---         return (T.Node v as : bs)
− Data/DAWG/HashMap.hs
@@ -1,110 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | A map from hashable keys to values.--module Data.DAWG.HashMap-( Hash (..)-, HashMap (..)-, empty-, lookup-, insertUnsafe-, lookupUnsafe-, deleteUnsafe-) where--import Prelude hiding (lookup)-import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, Get, put, get)-import qualified Data.Map as M-import qualified Data.IntMap as I--fromJust :: Maybe a -> a-fromJust (Just x)   = x-fromJust Nothing    = error "fromJust: Nothing"-{-# INLINE fromJust #-}---- | Class for types which provide hash values.-class Ord a => Hash a where-    hash    :: a -> Int---- | Value in a HashMap.-data Value a b-    = Single !a !b-    | Multi  !(M.Map a b)-    deriving (Show, Eq, Ord)--instance (Ord a, Binary a, Binary b) => Binary (Value a b) where-    put (Single x y)    = put (1 :: Int) >> put x >> put y-    put (Multi m)       = put (2 :: Int) >> put m-    get = do-        x <- get :: Get Int-        case x of-            1   -> Single <$> get <*> get-            _   -> Multi <$> get---- | Find element associated to a value key.-find :: Ord a => a -> Value a b -> Maybe b-find x (Single x' y) = if x == x'-    then Just y-    else Nothing-find x (Multi m) = M.lookup x m---- | Assumption: element is a member of the 'Value'. -findUnsafe :: Ord a => a -> Value a b -> Maybe b-findUnsafe _ (Single _ y) = Just y	-- unsafe-findUnsafe x (Multi m) = M.lookup x m---- | Convert map into a 'Single' form if possible.-trySingle :: Ord a => M.Map a b -> Value a b-trySingle m = if M.size m == 1-    then (uncurry Single) (M.findMin m)-    else Multi m---- | Insert element into a value.-embed :: Ord a => a -> b -> Value a b -> Value a b-embed x y (Single x' y')    = Multi $ M.fromList [(x, y), (x', y')]-embed x y (Multi m)         = Multi $ M.insert x y m---- | Delete element from a value.  Return 'Nothing' if the resultant--- value is empty.-ejectUnsafe :: Ord a => a -> Value a b -> Maybe (Value a b)-ejectUnsafe _ (Single _ _)  = Nothing    -- unsafe-ejectUnsafe x (Multi m)     = (Just . trySingle) (M.delete x m)---- | A map from /a/ keys to /b/ elements where keys instantiate the--- 'Hash' type class.  Key/element pairs are kept in 'Value' objects--- which takes care of potential hash collisions.-data HashMap a b = HashMap-    { size      :: {-# UNPACK #-} !Int-    , hashMap   :: !(I.IntMap (Value a b)) }-    deriving (Show, Eq, Ord)--instance (Ord a, Binary a, Binary b) => Binary (HashMap a b) where-    put HashMap{..} = put size >> put hashMap-    get = HashMap <$> get <*> get---- | Empty map.-empty :: HashMap a b-empty = HashMap 0 I.empty---- | Lookup element in the map.-lookup :: Hash a => a -> HashMap a b -> Maybe b-lookup x (HashMap _ m) = I.lookup (hash x) m >>= find x---- | Assumption: element is present in the map.-lookupUnsafe :: Hash a => a -> HashMap a b -> b-lookupUnsafe x (HashMap _ m) = fromJust (I.lookup (hash x) m >>= findUnsafe x)---- | Insert a new element.  The function doesn't check--- if the element was already present in the map.-insertUnsafe :: Hash a => a -> b -> HashMap a b -> HashMap a b-insertUnsafe x y (HashMap n m) =-    let i = hash x-        f (Just v)  = embed x y v-        f Nothing   = Single x y-    in  HashMap (n + 1) $ I.alter (Just . f) i m---- | Assumption: element is present in the map.-deleteUnsafe :: Hash a => a -> HashMap a b -> HashMap a b-deleteUnsafe x (HashMap n m) =-    HashMap (n - 1) $ I.update (ejectUnsafe x) (hash x) m
− Data/DAWG/Static.hs
@@ -1,274 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | The module implements /directed acyclic word graphs/ (DAWGs) internaly--- represented as /minimal acyclic deterministic finite-state automata/.------ In comparison to "Data.DAWG.Dynamic" module the automaton implemented here:------   * Keeps all nodes in one array and therefore uses less memory,------   * When 'weigh'ed, it can be used to perform static hashing with---     'hash' and 'unHash' functions,------   * Doesn't provide insert/delete family of operations.--module Data.DAWG.Static-(--- * DAWG type-  DAWG--- * Query-, lookup-, numStates-, numEdges--- * Index-, index-, byIndex--- * Hash-, hash-, unHash--- * Construction-, empty-, fromList-, fromListWith-, fromLang-, freeze--- * Weight-, Weight-, weigh--- * Conversion-, assocs-, keys-, elems--- , thaw-) where--import Prelude hiding (lookup)-import Control.Applicative ((<$), (<$>), (<|>))-import Control.Arrow (first)-import Data.Binary (Binary, put, get)-import Data.Vector.Binary ()-import Data.Vector.Unboxed (Unbox)-import qualified Data.IntMap as M-import qualified Data.Vector as V-import qualified Data.Vector.Unboxed as U--import Data.DAWG.Types-import qualified Data.DAWG.Util as Util-import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Static.Node as N-import qualified Data.DAWG.Graph as G-import qualified Data.DAWG.Dynamic as D-import qualified Data.DAWG.Dynamic.Internal as D---- | @DAWG a b c@ constitutes an automaton with alphabet symbols of type /a/,--- transition labels of type /b/ and node values of type /Maybe c/.--- All nodes are stored in a 'V.Vector' with positions of nodes corresponding--- to their 'ID's.-newtype DAWG a b c = DAWG { unDAWG :: V.Vector (N.Node b c) }-    deriving (Show, Eq, Ord)--instance (Binary b, Binary c, Unbox b) => Binary (DAWG a b c) where-    put = put . unDAWG-    get = DAWG <$> get---- | Empty DAWG.-empty :: Unbox b => DAWG a b c-empty = DAWG $ V.fromList-    [ N.Branch 1 T.empty U.empty-    , N.Leaf Nothing ]---- | Number of states in the automaton.-numStates :: DAWG a b c -> Int-numStates = V.length . unDAWG---- | Number of edges in the automaton.-numEdges :: DAWG a b c -> Int-numEdges = sum . map (length . N.edges) . V.toList . unDAWG---- | Node with the given identifier.-nodeBy :: ID -> DAWG a b c -> N.Node b c-nodeBy i d = unDAWG d V.! i---- | Value in leaf node with a given ID.-leafValue :: N.Node b c -> DAWG a b c -> Maybe c-leafValue n = N.value . nodeBy (N.eps n)---- | Find value associated with the key.-lookup :: (Enum a, Unbox b) => [a] -> DAWG a b c -> Maybe c-lookup xs' =-    let xs = map fromEnum xs'-    in  lookup'I xs 0-{-# SPECIALIZE lookup :: Unbox b => String -> DAWG Char b c -> Maybe c #-}--lookup'I :: Unbox b => [Sym] -> ID -> DAWG a b c -> Maybe c-lookup'I []     i d = leafValue (nodeBy i d) d-lookup'I (x:xs) i d = case N.onSym x (nodeBy i d) of-    Just j  -> lookup'I xs j d-    Nothing -> Nothing---- | Return all key/value pairs in the DAWG in ascending key order.-assocs :: (Enum a, Unbox b) => DAWG a b c -> [([a], c)]-assocs d = map (first (map toEnum)) (assocs'I 0 d)-{-# SPECIALIZE assocs :: Unbox b => DAWG Char b c -> [(String, c)] #-}--assocs'I :: Unbox b => ID -> DAWG a b c -> [([Sym], c)]-assocs'I i d =-    here ++ concatMap there (N.edges n)-  where-    n = nodeBy i d-    here = case leafValue n d of-        Just x  -> [([], x)]-        Nothing -> []-    there (x, j) = map (first (x:)) (assocs'I j d)---- | Return all keys of the DAWG in ascending order.-keys :: (Enum a, Unbox b) => DAWG a b c -> [[a]]-keys = map fst . assocs-{-# SPECIALIZE keys :: Unbox b => DAWG Char b c -> [String] #-}---- | Return all elements of the DAWG in the ascending order of their keys.-elems :: Unbox b => DAWG a b c -> [c]-elems = map snd . assocs'I 0---- | Construct 'DAWG' from the list of (word, value) pairs.--- First a 'D.DAWG' is created and then it is frozen using--- the 'freeze' function.-fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a () b-fromList = freeze . D.fromList-{-# SPECIALIZE fromList :: Ord b => [(String, b)] -> DAWG Char () b #-}---- | Construct DAWG from the list of (word, value) pairs--- with a combining function.  The combining function is--- applied strictly. First a 'D.DAWG' is created and then--- it is frozen using the 'freeze' function.-fromListWith :: (Enum a, Ord b) => (b -> b -> b) -> [([a], b)] -> DAWG a () b-fromListWith f = freeze . D.fromListWith f-{-# SPECIALIZE fromListWith-        :: Ord b => (b -> b -> b)-        -> [(String, b)] -> DAWG Char () b #-}---- | Make DAWG from the list of words.  Annotate each word with--- the @()@ value.  First a 'D.DAWG' is created and then it is frozen--- using the 'freeze' function.-fromLang :: Enum a => [[a]] -> DAWG a () ()-fromLang = freeze . D.fromLang-{-# SPECIALIZE fromLang :: [String] -> DAWG Char () () #-}---- | Weight of a node corresponds to the number of final states--- reachable from the node.  Weight of an edge is a sum of weights--- of preceding nodes outgoing from the same parent node.-type Weight = Int---- | Compute node weights and store corresponding values in transition labels.-weigh :: DAWG a b c -> DAWG a Weight c-weigh d = (DAWG . V.fromList)-    [ branch n ws-    | i <- [0 .. numStates d - 1]-    , let n  = nodeBy i d-    , let ws = accum (N.children n) ]-  where-    -- Branch with new weights.-    branch N.Branch{..} ws  = N.Branch eps transMap ws-    branch N.Leaf{..} _     = N.Leaf value-    -- In nodeWeight node weights are memoized.-    nodeWeight = ((V.!) . V.fromList) (map detWeight [0 .. numStates d - 1])-    -- Determine weight of the node.-    detWeight i = case nodeBy i d of-        N.Leaf w    -> maybe 0 (const 1) w-        n           -> sum . map nodeWeight $ allChildren n-    -- Weights for subsequent edges.-    accum = U.fromList . init . scanl (+) 0 . map nodeWeight-    -- Plain children and epsilon child. -    allChildren n = N.eps n : N.children n---- | Construct immutable version of the automaton.-freeze :: D.DAWG a b -> DAWG a () b-freeze d = DAWG . V.fromList $-    map (N.fromDyn newID . oldBy)-        (M.elems (inverse old2new))-  where-    -- Map from old to new identifiers.-    old2new = M.fromList $ (D.root d, 0) : zip (nodeIDs d) [1..]-    newID   = (M.!) old2new-    -- List of node IDs without the root ID.-    nodeIDs = filter (/= D.root d) . map fst . M.assocs . G.nodeMap . D.graph-    -- Non-frozen node by given identifier.-    oldBy i = G.nodeBy i (D.graph d)-        --- | Inverse of the map.-inverse :: M.IntMap Int -> M.IntMap Int-inverse =-    let swap (x, y) = (y, x)-    in  M.fromList . map swap . M.toList---- -- | Yield mutable version of the automaton.--- thaw :: (Unbox b, Ord a) => DAWG a b c -> D.DAWG a b--- thaw d =---     D.fromNodes nodes 0---   where---     -- List of resulting nodes.---     nodes = branchNodes ++ leafNodes---     -- Branching nodes.---     branchNodes =---         [ ---     -- Number of states used to shift new value IDs.---     n = numStates d---     -- New identifiers for value nodes.---     valIDs = foldl' updID GM.empty (values d)---     -- Values in the automaton.---     values = map value . V.toList . unDAWG---     -- Update ID map.---     updID m v = case GM.lookup v m of---         Just i  -> m---         Nothing -> ---             let j = GM.size m + n---             in  j `seq` GM.insert v j---- | Position in a set of all dictionary entries with respect--- to the lexicographic order.-index :: Enum a => [a] -> DAWG a Weight c -> Maybe Int-index xs = index'I (map fromEnum xs) 0-{-# SPECIALIZE index :: String -> DAWG Char Weight c -> Maybe Int #-}--index'I :: [Sym] -> ID -> DAWG a Weight c -> Maybe Int-index'I []     i d = 0 <$ leafValue (nodeBy i d) d-index'I (x:xs) i d = do-    let n = nodeBy i d-        u = maybe 0 (const 1) (leafValue n d)-    (j, v) <- N.onSym' x n-    w <- index'I xs j d-    return (u + v + w)---- | Perfect hashing function for dictionary entries.--- A synonym for the 'index' function.-hash :: Enum a => [a] -> DAWG a Weight c -> Maybe Int-hash = index-{-# INLINE hash #-}---- | Find dictionary entry given its index with respect to the--- lexicographic order.-byIndex :: Enum a => Int -> DAWG a Weight c -> Maybe [a]-byIndex ix d = map toEnum <$> byIndex'I ix 0 d-{-# SPECIALIZE byIndex :: Int -> DAWG Char Weight c -> Maybe String #-}--byIndex'I :: Int -> ID -> DAWG a Weight c -> Maybe [Sym]-byIndex'I ix i d-    | ix < 0    = Nothing-    | otherwise = here <|> there-  where-    n = nodeBy i d-    u = maybe 0 (const 1) (leafValue n d)-    here-        | ix == 0   = [] <$ leafValue (nodeBy i d) d-        | otherwise = Nothing-    there = do-        (k, w) <- Util.findLastLE cmp (N.labelVect n)-        (x, j) <- T.byIndex k (N.transMap n)-        xs <- byIndex'I (ix - u - w) j d-        return (x:xs)-    cmp w = compare w (ix - u)---- | Inverse of the 'hash' function and a synonym for the 'byIndex' function.-unHash :: Enum a => Int -> DAWG a Weight c -> Maybe [a]-unHash = byIndex-{-# INLINE unHash #-}
− Data/DAWG/Static/Node.hs
@@ -1,98 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | Internal representation of static automata nodes.--module Data.DAWG.Static.Node-( Node(..)-, onSym-, onSym'-, edges-, children-, insert-, fromDyn-) where--import Control.Arrow (second)-import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, Get, put, get)-import Data.Vector.Binary ()-import qualified Data.Vector.Unboxed as U--import Data.DAWG.Types-import Data.DAWG.Trans.Vector (Trans)-import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Dynamic.Node as D---- | Two nodes (states) belong to the same equivalence class (and,--- consequently, they must be represented as one node in the graph)--- iff they are equal with respect to their values and outgoing--- edges.------ Since 'Leaf' nodes are distinguished from 'Branch' nodes, two values--- equal with respect to '==' function are always kept in one 'Leaf'--- node in the graph.  It doesn't change the fact that to all 'Branch'--- nodes one value is assigned through the epsilon transition.------ Invariant: the 'eps' identifier always points to the 'Leaf' node.--- Edges in the 'edgeMap', on the other hand, point to 'Branch' nodes.-data Node a b-    = Branch {-        -- | Epsilon transition.-          eps       :: {-# UNPACK #-} !ID-        -- | Transition map (outgoing edges).-        , transMap  :: !Trans-        -- | Labels corresponding to individual edges.-        , labelVect :: !(U.Vector a) }-    | Leaf { value  :: !(Maybe b) }-    deriving (Show, Eq, Ord)--instance (U.Unbox a, Binary a, Binary b) => Binary (Node a b) where-    put Branch{..} = put (1 :: Int) >> put eps >> put transMap >> put labelVect-    put Leaf{..}   = put (2 :: Int) >> put value-    get = do-        x <- get :: Get Int-        case x of-            1 -> Branch <$> get <*> get <*> get-            _ -> Leaf <$> get---- | Transition function.-onSym :: Sym -> Node a b -> Maybe ID-onSym x (Branch _ t _)  = T.lookup x t-onSym _ (Leaf _)        = Nothing-{-# INLINE onSym #-}---- | Transition function.-onSym' :: U.Unbox a => Sym -> Node a b -> Maybe (ID, a)-onSym' x (Branch _ t ls)   = do-    k <- T.index x t-    (,) <$> (snd <$> T.byIndex k t)-        <*> ls U.!? k-onSym' _ (Leaf _)           = Nothing-{-# INLINE onSym' #-}---- | List of symbol/edge pairs outgoing from the node.-edges :: Node a b -> [(Sym, ID)]-edges (Branch _ t _)    = T.toList t-edges (Leaf _)          = []-{-# INLINE edges #-}---- | List of children identifiers.-children :: Node a b -> [ID]-children = map snd . edges-{-# INLINE children #-}---- | Substitue edge determined by a given symbol.-insert :: Sym -> ID -> Node a b -> Node a b-insert x i (Branch w t ls)  = Branch w (T.insert x i t) ls-insert _ _ l                = l-{-# INLINE insert #-}---- | Make "static" node from a "dynamic" node.-fromDyn-    :: (ID -> ID)   -- ^ Assign new IDs -    -> D.Node b     -- ^ "Dynamic" node-    -> Node () b    -- ^ "Static" node-fromDyn _ (D.Leaf x)        = Leaf x-fromDyn f (D.Branch e t)    =-    let reTrans = T.fromList . map (second f) . T.toList-    in  Branch (f e) (reTrans t) U.empty
− Data/DAWG/Trans.hs
@@ -1,26 +0,0 @@--- | The module provides an abstraction over transition maps from--- alphabet symbols to node identifiers.--module Data.DAWG.Trans-( Trans (..)-) where--import Data.DAWG.Types---- | Abstraction over transition maps from alphabet symbols to--- node identifiers.-class Trans t where-    -- | Empty transition map.-    empty       :: t-    -- | Lookup sybol in the map.-    lookup      :: Sym -> t -> Maybe ID-    -- | Find index of the symbol.-    index       :: Sym -> t -> Maybe Int-    -- | Select a (symbol, ID) pair by index of its position in the map.-    byIndex     :: Int -> t -> Maybe (Sym, ID)-    -- | Insert element to the transition map.-    insert      :: Sym -> ID -> t -> t-    -- | Construct transition map from a list.-    fromList    :: [(Sym, ID)] -> t-    -- | Translate transition map into a list.-    toList      :: t -> [(Sym, ID)]
− Data/DAWG/Trans/Hashed.hs
@@ -1,63 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE FlexibleInstances #-}---- | Transition map with a hash.--module Data.DAWG.Trans.Hashed-( Hashed (..)-) where--import Prelude hiding (lookup)-import Control.Applicative ((<$>), (<*>))-import Data.DAWG.Util (combine)-import Data.Binary (Binary, put, get)-import Data.DAWG.Trans-import qualified Data.DAWG.Trans.Map as M-import qualified Data.DAWG.Trans.Vector as V---- | Hash of a transition map is a sum of element-wise hashes.--- Hash for a given element @(Sym, ID)@ is equal to @combine Sym ID@.-data Hashed t = Hashed-    { hash  :: {-# UNPACK #-} !Int-    , trans :: !t }-    deriving (Show)--instance Binary t => Binary (Hashed t) where-    put Hashed{..} = put hash >> put trans-    get = Hashed <$> get <*> get--instance Trans t => Trans (Hashed t) where-    empty       = Hashed 0 empty-    {-# INLINE empty #-} --    lookup x    = lookup x . trans-    {-# INLINE lookup #-} --    index x     = index x . trans-    {-# INLINE index #-} --    byIndex i   = byIndex i . trans-    {-# INLINE byIndex #-} --    insert x y (Hashed h t) = Hashed-        (h - h' + combine x y)-        (insert x y t)-      where-        h' = case lookup x t of-            Just y' -> combine x y'-            Nothing -> 0-    {-# INLINE insert #-}--    fromList xs = Hashed -        (sum $ map (uncurry combine) xs)-        (fromList xs)-    {-# INLINE fromList #-}--    toList  = toList . trans-    {-# INLINE toList #-}--deriving instance Eq  (Hashed M.Trans)-deriving instance Ord (Hashed M.Trans)-deriving instance Eq  (Hashed V.Trans)-deriving instance Ord (Hashed V.Trans)
− Data/DAWG/Trans/Map.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---- | Implementation of a transition map build on top of the "M.Map" container.--module Data.DAWG.Trans.Map-( Trans (unTrans)-) where--import Prelude hiding (lookup)-import Data.Binary (Binary)-import qualified Data.Map as M--import Data.DAWG.Types-import qualified Data.DAWG.Trans as C---- | A vector of distinct key/value pairs strictly ascending with respect--- to key values.-newtype Trans = Trans { unTrans :: M.Map Sym ID }-    deriving (Show, Eq, Ord, Binary)--instance C.Trans Trans where-    empty = Trans M.empty-    {-# INLINE empty #-}--    lookup x = M.lookup x . unTrans-    {-# INLINE lookup #-}--    index x = M.lookupIndex x . unTrans-    {-# INLINE index #-}--    byIndex i (Trans m) =-	let n = M.size m-        in  if i >= 0 && i < n-                then Just (M.elemAt i m)-                else Nothing-    {-# INLINE byIndex #-}--    insert x y (Trans m) = Trans (M.insert x y m)-    {-# INLINE insert #-}--    fromList = Trans . M.fromList-    {-# INLINE fromList #-}--    toList = M.toList . unTrans-    {-# INLINE toList #-}
− Data/DAWG/Trans/Vector.hs
@@ -1,58 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---- | A vector representation of a transition map.  Memory efficient, but the--- insert operation is /O(n)/ with respect to the number of transitions.--- In particular, complexity of the insert operation can make the construction--- of a large-alphabet dictionary intractable.--module Data.DAWG.Trans.Vector-( Trans (unTrans)-) where--import Prelude hiding (lookup)-import Control.Applicative ((<$>))-import Data.Binary (Binary)-import Data.Vector.Binary ()-import qualified Data.IntMap as M-import qualified Data.Vector.Unboxed as U-import qualified Data.Vector.Unboxed.Mutable as UM--import Data.DAWG.Types-import Data.DAWG.Util-import qualified Data.DAWG.Trans as C---- | A vector of distinct key/value pairs strictly ascending with respect--- to key values.-newtype Trans = Trans { unTrans :: U.Vector (Sym, ID) }-    deriving (Show, Eq, Ord, Binary)--instance C.Trans Trans where-    empty = Trans U.empty-    {-# INLINE empty #-}--    lookup x m = do-        k <- C.index x m-        snd <$> C.byIndex k m-    {-# INLINE lookup #-}--    index x (Trans v)-        = either Just (const Nothing) $-            binarySearch (flip compare x . fst) v-    {-# INLINE index #-}--    byIndex k (Trans v) = v U.!? k-    {-# INLINE byIndex #-}--    insert x y (Trans v) = Trans $-        case binarySearch (flip compare x . fst) v of-            Left k  -> U.modify (\w -> UM.write w k (x, y)) v-            Right k ->-                let (v'L, v'R) = U.splitAt k v-                in  U.concat [v'L, U.singleton (x, y), v'R]-    {-# INLINE insert #-}--    fromList = Trans . U.fromList . M.toAscList . M.fromList-    {-# INLINE fromList #-}--    toList = U.toList . unTrans-    {-# INLINE toList #-}
− Data/DAWG/Types.hs
@@ -1,12 +0,0 @@--- | Basic types used throughout the library.--module Data.DAWG.Types-( ID-, Sym-) where---- | Node identifier.-type ID = Int---- | Internal representation of an alphabet element.-type Sym = Int
− Data/DAWG/Util.hs
@@ -1,58 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE TupleSections #-}---- | Utility functions.--module Data.DAWG.Util-( binarySearch-, findLastLE-, combine-) where--import Control.Applicative ((<$>))-import Data.Bits (shiftR, xor)-import Data.Vector.Unboxed (Unbox)-import qualified Control.Monad.ST as ST-import qualified Data.Vector.Unboxed as U-import qualified Data.Vector.Unboxed.Mutable as UM---- | Given a vector of length @n@ strictly ascending with respect to a given--- comparison function, find an index at which the given element could be--- inserted while preserving sortedness.--- The 'Left' result indicates, that the 'EQ' element has been found,--- while the 'Right' result means otherwise.  Value of the 'Right'--- result is in the [0,n] range.-binarySearch :: Unbox a => (a -> Ordering) -> U.Vector a -> Either Int Int-binarySearch cmp v = ST.runST $ do-    w <- U.unsafeThaw v-    search w-  where-    search w =-        loop 0 (UM.length w)-      where-        loop !l !u-            | u <= l    = return (Right l)-            | otherwise = do-                let k = (u + l) `shiftR` 1-                x <- UM.unsafeRead w k-                case cmp x of-                    LT -> loop (k+1) u-                    EQ -> return (Left k)-                    GT -> loop l     k-{-# INLINE binarySearch #-}---- | Given a vector sorted with respect to some underlying comparison--- function, find last element which is not 'GT' with respect to the--- comparison function.-findLastLE :: Unbox a => (a -> Ordering) -> U.Vector a -> Maybe (Int, a)-findLastLE cmp v =-    let k' = binarySearch cmp v-    	k  = either id (\x -> x-1) k'-    in  (k,) <$> v U.!? k-{-# INLINE findLastLE #-}---- | Combine two given hash values.  'combine' has zero as a left--- identity.-combine :: Int -> Int -> Int-combine h1 h2 = (h1 * 16777619) `xor` h2-{-# INLINE combine #-}
dawg.cabal view
@@ -1,8 +1,8 @@ name:               dawg-version:            0.9+version:            0.11 synopsis:           Directed acyclic word graphs description:-    The library implements /directed acyclic word graphs/ (DAWGs) internaly+    The library implements /directed acyclic word graphs/ (DAWGs) internally     represented as /minimal acyclic deterministic finite-state automata/.     .     The "Data.DAWG.Dynamic" module provides fast insert and delete operations@@ -21,6 +21,7 @@ build-type:         Simple  library+    hs-source-dirs: src     build-depends:         base >= 4 && < 5       , containers >= 0.4.1 && < 0.6@@ -28,6 +29,7 @@       , vector       , vector-binary       , mtl+      , transformers      exposed-modules:         Data.DAWG.Dynamic
+ src/Data/DAWG/Dynamic.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE RecordWildCards #-}+++-- | The module implements /directed acyclic word graphs/ (DAWGs) internaly+-- represented as /minimal acyclic deterministic finite-state automata/.+-- The implementation provides fast insert and delete operations+-- which can be used to build the DAWG structure incrementaly.+++module Data.DAWG.Dynamic+(+-- * DAWG type+  DAWG++-- * Query+, lookup+, numStates+, numEdges++-- * Construction+, empty+, fromList+, fromListWith+, fromLang+-- ** Insertion+, insert+, insertWith+-- ** Deletion+, delete++-- * Conversion+, assocs+, keys+, elems+) where+++import Prelude hiding (lookup)+import Control.Applicative ((<$>), (<*>))+import Control.Arrow (first)+import Data.List (foldl')+import qualified Control.Monad.State.Strict as S+import           Control.Monad.Trans.Maybe+import           Control.Monad.Trans.Class++import Data.DAWG.Types+import Data.DAWG.Graph (Graph)+import Data.DAWG.Dynamic.Internal+import qualified Data.DAWG.Trans as T+import qualified Data.DAWG.Graph as G+import qualified Data.DAWG.Dynamic.Node as N+++type GraphM a = S.State (Graph (N.Node a))++mkState :: (Graph a -> Graph a) -> Graph a -> ((), Graph a)+mkState f g = ((), f g)++-- | Return node with the given identifier.+nodeBy :: ID -> GraphM a (N.Node a)+nodeBy i = G.nodeBy i <$> S.get++-- Evaluate the 'G.insert' function within the monad.+insertNode :: Ord a => N.Node a -> GraphM a ID+insertNode = S.state . G.insert++-- | Leaf node with no children and 'Nothing' value.+insertLeaf :: Ord a => GraphM a ID+insertLeaf = do+    i <- insertNode (N.Leaf Nothing)+    insertNode (N.Branch i T.empty)++-- Evaluate the 'G.delete' function within the monad.+deleteNode :: Ord a => N.Node a -> GraphM a ()+deleteNode = S.state . mkState . G.delete++-- | Invariant: the identifier points to the 'Branch' node.+insertM :: Ord a => [Sym] -> a -> ID -> GraphM a ID+insertM (x:xs) y i = do+    n <- nodeBy i+    j <- case N.onSym x n of+        Just j  -> return j+        Nothing -> insertLeaf+    k <- insertM xs y j+    deleteNode n+    insertNode (N.insert x k n)+insertM [] y i = do+    n <- nodeBy i+    w <- nodeBy (N.eps n)+    deleteNode w+    deleteNode n+    j <- insertNode (N.Leaf $ Just y)+    insertNode (n { N.eps = j })++insertWithM+    :: Ord a => (a -> a -> a)+    -> [Sym] -> a -> ID -> GraphM a ID+insertWithM f (x:xs) y i = do+    n <- nodeBy i+    j <- case N.onSym x n of+        Just j  -> return j+        Nothing -> insertLeaf+    k <- insertWithM f xs y j+    deleteNode n+    insertNode (N.insert x k n)+insertWithM f [] y i = do+    n <- nodeBy i+    w <- nodeBy (N.eps n)+    deleteNode w+    deleteNode n+    let y'new = case N.value w of+            Just y' -> f y y'+            Nothing -> y+    j <- insertNode (N.Leaf $ Just y'new)+    insertNode (n { N.eps = j })++deleteM :: Ord a => [Sym] -> ID -> GraphM a ID+deleteM (x:xs) i = do+    n <- nodeBy i+    case N.onSym x n of+        Nothing -> return i+        Just j  -> do+            k <- deleteM xs j+            deleteNode n+            insertNode (N.insert x k n)+deleteM [] i = do+    n <- nodeBy i+    w <- nodeBy (N.eps n)+    deleteNode w+    deleteNode n+    j <- insertLeaf+    insertNode (n { N.eps = j })++-- | Follow the path from the given identifier.+follow :: [Sym] -> ID -> MaybeT (GraphM a) ID+follow (x:xs) i = do+    n <- lift $ nodeBy i+    j <- liftMaybe $ N.onSym x n+    follow xs j+follow [] i = return i+    +lookupM :: [Sym] -> ID -> GraphM a (Maybe a)+lookupM xs i = runMaybeT $ do+    j <- follow xs i+    k <- lift $ N.eps <$> nodeBy j+    MaybeT $ N.value <$> nodeBy k++-- | Return all (key, value) pairs in ascending key order in the+-- sub-DAWG determined by the given node ID.+subPairs :: Graph (N.Node a) -> ID -> [([Sym], a)]+subPairs g i =+    here w ++ concatMap there (N.edges n)+  where+    n = G.nodeBy i g+    w = G.nodeBy (N.eps n) g+    here v = case N.value v of+        Just x  -> [([], x)]+        Nothing -> []+    there (sym, j) = map (first (sym:)) (subPairs g j)++-- | Empty DAWG.+empty :: Ord b => DAWG a b+empty = +    let (i, g) = S.runState insertLeaf G.empty+    in  DAWG g i++-- | Number of states in the automaton.+numStates :: DAWG a b -> Int+numStates = G.size . graph++-- | Number of edges in the automaton.+numEdges :: DAWG a b -> Int+numEdges = sum . map (length . N.edges) . G.nodes . graph++-- | Insert the (key, value) pair into the DAWG.+insert :: (Enum a, Ord b) => [a] -> b -> DAWG a b -> DAWG a b+insert xs' y d =+    let xs = map fromEnum xs'+        (i, g) = S.runState (insertM xs y $ root d) (graph d)+    in  DAWG g i+{-# INLINE insert #-}++-- | Insert with a function, combining new value and old value.+-- 'insertWith' f key value d will insert the pair (key, value) into d if+-- key does not exist in the DAWG. If the key does exist, the function+-- will insert the pair (key, f new_value old_value).+insertWith+    :: (Enum a, Ord b) => (b -> b -> b)+    -> [a] -> b -> DAWG a b -> DAWG a b+insertWith f xs' y d =+    let xs = map fromEnum xs'+        (i, g) = S.runState (insertWithM f xs y $ root d) (graph d)+    in  DAWG g i+{-# SPECIALIZE insertWith+        :: Ord b => (b -> b -> b) -> String -> b+        -> DAWG Char b -> DAWG Char b #-}++-- | Delete the key from the DAWG.+delete :: (Enum a, Ord b) => [a] -> DAWG a b -> DAWG a b+delete xs' d =+    let xs = map fromEnum xs'+        (i, g) = S.runState (deleteM xs $ root d) (graph d)+    in  DAWG g i+{-# SPECIALIZE delete :: Ord b => String -> DAWG Char b -> DAWG Char b #-}++-- | Find value associated with the key.+lookup :: (Enum a, Ord b) => [a] -> DAWG a b -> Maybe b+lookup xs' d =+    let xs = map fromEnum xs'+    in  S.evalState (lookupM xs $ root d) (graph d)+{-# SPECIALIZE lookup :: Ord b => String -> DAWG Char b -> Maybe b #-}++-- -- | Find all (key, value) pairs such that key is prefixed+-- -- with the given string.+-- withPrefix :: (Enum a, Ord b) => [a] -> DAWG a b -> [([a], b)]+-- withPrefix xs DAWG{..}+--     = map (first $ (xs ++) . map toEnum)+--     $ maybe [] (subPairs graph)+--     $ flip S.evalState graph $ runMaybeT+--     $ follow (map fromEnum xs) root+-- {-# SPECIALIZE withPrefix+--     :: Ord b => String -> DAWG Char b+--     -> [(String, b)] #-}++-- | Return all key/value pairs in the DAWG in ascending key order.+assocs :: (Enum a, Ord b) => DAWG a b -> [([a], b)]+assocs+    = map (first (map toEnum))+    . (subPairs <$> graph <*> root)+{-# SPECIALIZE assocs :: Ord b => DAWG Char b -> [(String, b)] #-}++-- | Return all keys of the DAWG in ascending order.+keys :: (Enum a, Ord b) => DAWG a b -> [[a]]+keys = map fst . assocs+{-# SPECIALIZE keys :: Ord b => DAWG Char b -> [String] #-}++-- | Return all elements of the DAWG in the ascending order of their keys.+elems :: Ord b => DAWG a b -> [b]+elems = map snd . (subPairs <$> graph <*> root)++-- | Construct DAWG from the list of (word, value) pairs.+fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a b+fromList xs =+    let update t (x, v) = insert x v t+    in  foldl' update empty xs+{-# INLINE fromList #-}++-- | Construct DAWG from the list of (word, value) pairs+-- with a combining function.  The combining function is+-- applied strictly.+fromListWith+    :: (Enum a, Ord b) => (b -> b -> b)+    -> [([a], b)] -> DAWG a b+fromListWith f xs =+    let update t (x, v) = insertWith f x v t+    in  foldl' update empty xs+{-# SPECIALIZE fromListWith+        :: Ord b => (b -> b -> b)+        -> [(String, b)] -> DAWG Char b #-}++-- | Make DAWG from the list of words.  Annotate each word with+-- the @()@ value.+fromLang :: Enum a => [[a]] -> DAWG a ()+fromLang xs = fromList [(x, ()) | x <- xs]+{-# SPECIALIZE fromLang :: [String] -> DAWG Char () #-}+++----------------+-- Misc+----------------+++liftMaybe :: Monad m => Maybe a -> MaybeT m a+liftMaybe = MaybeT . return+{-# INLINE liftMaybe #-}
+ src/Data/DAWG/Dynamic/Internal.hs view
@@ -0,0 +1,27 @@+-- | The module exports internal representation of dynamic DAWG.++module Data.DAWG.Dynamic.Internal+(+-- * DAWG type+  DAWG (..)+) where++import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, put, get)++import Data.DAWG.Types+import Data.DAWG.Graph (Graph)+import qualified Data.DAWG.Dynamic.Node as N++-- | A directed acyclic word graph with phantom type @a@ representing+-- type of alphabet elements.+data DAWG a b = DAWG+    { graph :: !(Graph (N.Node b))+    , root  :: !ID }+    deriving (Show, Eq, Ord)++instance (Ord b, Binary b) => Binary (DAWG a b) where+    put d = do+        put (graph d)+        put (root d)+    get = DAWG <$> get <*> get
+ src/Data/DAWG/Dynamic/Node.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE RecordWildCards #-}++-- | Internal representation of dynamic automata nodes.++module Data.DAWG.Dynamic.Node+( Node(..)+, onSym+, edges+, children+, insert+) where++import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, Get, put, get)++import Data.DAWG.Types+import Data.DAWG.Util (combine)+import Data.DAWG.HashMap (Hash, hash)+import Data.DAWG.Trans.Map (Trans)+import qualified Data.DAWG.Trans as T+import qualified Data.DAWG.Trans.Hashed as H++-- | Two nodes (states) belong to the same equivalence class (and,+-- consequently, they must be represented as one node in the graph)+-- iff they are equal with respect to their values and outgoing+-- edges.+--+-- Since 'Leaf' nodes are distinguished from 'Branch' nodes, two values+-- equal with respect to '==' function are always kept in one 'Leaf'+-- node in the graph.  It doesn't change the fact that to all 'Branch'+-- nodes one value is assigned through the epsilon transition.+--+-- Invariant: the 'eps' identifier always points to the 'Leaf' node.+-- Edges in the 'edgeMap', on the other hand, point to 'Branch' nodes.+data Node a+    = Branch {+        -- | Epsilon transition.+          eps       :: {-# UNPACK #-} !ID+        -- | Transition map (outgoing edges).+        , transMap  :: !(H.Hashed Trans) }+    | Leaf { value  :: !(Maybe a) }+    deriving (Show, Eq, Ord)++instance Ord a => Hash (Node a) where+    hash Branch{..} = combine eps (H.hash transMap)+    hash Leaf{..}   = case value of+    	Just _	-> (-1)+	Nothing	-> (-2)++instance Binary a => Binary (Node a) where+    put Branch{..} = put (1 :: Int) >> put eps >> put transMap+    put Leaf{..}   = put (2 :: Int) >> put value+    get = do+        x <- get :: Get Int+        case x of+            1 -> Branch <$> get <*> get+            _ -> Leaf <$> get++-- | Transition function.+onSym :: Sym -> Node a -> Maybe ID+onSym x (Branch _ t)    = T.lookup x t+onSym _ (Leaf _)        = Nothing+{-# INLINE onSym #-}++-- | List of symbol/edge pairs outgoing from the node.+edges :: Node a -> [(Sym, ID)]+edges (Branch _ t)  = T.toList t+edges (Leaf _)      = []+{-# INLINE edges #-}++-- | List of children identifiers.+children :: Node a -> [ID]+children = map snd . edges+{-# INLINE children #-}++-- | Substitue edge determined by a given symbol.+insert :: Sym -> ID -> Node a -> Node a+insert x i (Branch w t) = Branch w (T.insert x i t)+insert _ _ l            = l+{-# INLINE insert #-}
+ src/Data/DAWG/Graph.hs view
@@ -0,0 +1,213 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE DoAndIfThenElse #-}++-- | Internal representation of the "Data.DAWG" automaton.  Names in this+-- module correspond to a graphical representation of automaton: nodes refer+-- to states and edges refer to transitions.++module Data.DAWG.Graph+( Graph (..)+, empty+, size+, nodes+, nodeBy+, insert+, delete+) where++import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, put, get)+import qualified Data.IntSet as S+import qualified Data.IntMap as M++import Data.DAWG.HashMap (Hash)+import qualified Data.DAWG.HashMap as H++type ID = Int++-- | A set of nodes.  To every node a unique identifier is assigned.+-- Invariants: +--+--   * freeIDs \\intersection occupiedIDs = \\emptySet,+--+--   * freeIDs \\sum occupiedIDs =+--     {0, 1, ..., |freeIDs \\sum occupiedIDs| - 1},+--+-- where occupiedIDs = elemSet idMap.+--+-- TODO: Is it possible to merge 'freeIDs' with 'ingoMap' to reduce+-- the memory footprint?+data Graph n = Graph {+    -- | Map from nodes to IDs with hash values interpreted+    -- as keys and (node, ID) pairs interpreted as map elements.+      idMap     :: !(H.HashMap n ID)+    -- | Set of free IDs.+    , freeIDs   :: !S.IntSet+    -- | Map from IDs to nodes. +    , nodeMap   :: !(M.IntMap n)+    -- | Number of ingoing paths (different paths from the root+    -- to the given node) for each node ID in the graph.+    -- The number of ingoing paths can be also interpreted as+    -- a number of occurences of the node in a tree representation+    -- of the graph.+    , ingoMap   :: !(M.IntMap Int) }+    deriving (Show, Eq, Ord)++instance (Ord n, Binary n) => Binary (Graph n) where+    put Graph{..} = do+        put idMap+        put freeIDs+        put nodeMap+        put ingoMap+    get = Graph <$> get <*> get <*> get <*> get++-- | Empty graph.+empty :: Graph n+empty = Graph H.empty S.empty M.empty M.empty++-- | Size of the graph (number of nodes).+size :: Graph n -> Int+size = H.size . idMap++-- | List of graph nodes.+nodes :: Graph n -> [n]+nodes = M.elems . nodeMap++-- | Node with the given identifier.+nodeBy :: ID -> Graph n -> n+nodeBy i g = nodeMap g M.! i++-- | Retrieve identifier of a node assuming that the node+-- is present in the graph.  If the assumption is not+-- safisfied, the returned identifier may be incorrect.+nodeID'Unsafe :: Hash n => n -> Graph n -> ID+nodeID'Unsafe n g = H.lookupUnsafe n (idMap g)++-- | Add new graph node (assuming that it is not already a member+-- of the graph).+newNode :: Hash n => n -> Graph n -> (ID, Graph n)+newNode n Graph{..} =+    (i, Graph idMap' freeIDs' nodeMap' ingoMap')+  where+    idMap'      = H.insertUnsafe n i idMap+    nodeMap'    = M.insert i n nodeMap+    ingoMap'    = M.insert i 1 ingoMap+    (i, freeIDs') = if S.null freeIDs+        then (H.size idMap, freeIDs)+        else S.deleteFindMin freeIDs++-- | Remove node from the graph (assuming that it is a member+-- of the graph).+remNode :: Hash n => ID -> Graph n -> Graph n+remNode i Graph{..} =+    Graph idMap' freeIDs' nodeMap' ingoMap'+  where+    idMap'      = H.deleteUnsafe n idMap+    nodeMap'    = M.delete i nodeMap+    ingoMap'    = M.delete i ingoMap+    freeIDs'    = S.insert i freeIDs+    n           = nodeMap M.! i++-- | Increment the number of ingoing paths.+incIngo :: ID -> Graph n -> Graph n+incIngo i g = g { ingoMap = M.insertWith' (+) i 1 (ingoMap g) }++-- | Decrement the number of ingoing paths and return+-- the resulting number.+decIngo :: ID -> Graph n -> (Int, Graph n)+decIngo i g =+    let k = (ingoMap g M.! i) - 1+    in  (k, g { ingoMap = M.insert i k (ingoMap g) })++-- | Insert node into the graph.  If the node was already a member+-- of the graph, just increase the number of ingoing paths.+-- NOTE: Number of ingoing paths will not be changed for any descendants+-- of the node, so the operation alone will not ensure that properties+-- of the graph are preserved.+insert :: Hash n => n -> Graph n -> (ID, Graph n)+insert n g = case H.lookup n (idMap g) of+    Just i  -> (i, incIngo i g)+    Nothing -> newNode n g++-- | Delete node from the graph.  If the node was present in the graph+-- at multiple positions, just decrease the number of ingoing paths.+-- Function crashes if the node is not a member of the graph. +-- NOTE: The function does not delete descendant nodes which may become+-- inaccesible nor does it change the number of ingoing paths for any+-- descendant of the node.+delete :: Hash n => n -> Graph n -> Graph n+delete n g = if num == 0+    then remNode i g'+    else g'+  where+    i = nodeID'Unsafe n g+    (num, g') = decIngo i g++-- -- | Construct a graph from a list of node/ID pairs and a root ID.+-- -- Identifiers must be consistent with edges outgoing from+-- -- individual nodes.+-- fromNodes :: Ord a => [(Node a, ID)] -> ID -> Graph a+-- fromNodes xs rootID = graph+--   where+--     graph = Graph+--         (M.fromList xs)+--         IS.empty+--         (IM.fromList $ map swap xs)+--         ( foldl' updIngo (IM.singleton rootID 1)+--             $ topSort graph rootID )+--     swap (x, y) = (y, x)+--     updIngo m i =+--         let n = nodeBy i graph+--             ingo = m IM.! i+--         in  foldl' (push ingo) m (edges n)+--     push x m j = IM.adjust (+x) j m+-- +-- postorder :: T.Tree a -> [a] -> [a]+-- postorder (T.Node a ts) = postorderF ts . (a :)+-- +-- postorderF :: T.Forest a -> [a] -> [a]+-- postorderF ts = foldr (.) id $ map postorder ts+-- +-- postOrd :: Graph a -> ID -> [ID]+-- postOrd g i = postorder (dfs g i) []+-- +-- -- | Topological sort given a root ID.+-- topSort :: Graph a -> ID -> [ID]+-- topSort g = reverse . postOrd g+-- +-- -- | Depth first search starting with given ID.+-- dfs :: Graph a -> ID -> T.Tree ID+-- dfs g = prune . generate g+-- +-- generate :: Graph a -> ID -> T.Tree ID+-- generate g i = T.Node i+--     ( T.Node (eps n) []+--     : map (generate g) (edges n) )+--   where+--     n = nodeBy i g+-- +-- type SetM a = S.State IS.IntSet a+-- +-- run :: SetM a -> a+-- run act = S.evalState act IS.empty+-- +-- contains :: ID -> SetM Bool+-- contains i = IS.member i <$> S.get+-- +-- include :: ID -> SetM ()+-- include i = S.modify (IS.insert i)+-- +-- prune :: T.Tree ID -> T.Tree ID+-- prune t = head $ run (chop [t])+-- +-- chop :: T.Forest ID -> SetM (T.Forest ID)+-- chop [] = return []+-- chop (T.Node v ts : us) = do+--     visited <- contains v+--     if visited then+--         chop us+--     else do+--         include v+--         as <- chop ts+--         bs <- chop us+--         return (T.Node v as : bs)
+ src/Data/DAWG/HashMap.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE RecordWildCards #-}++-- | A map from hashable keys to values.++module Data.DAWG.HashMap+( Hash (..)+, HashMap (..)+, empty+, lookup+, insertUnsafe+, lookupUnsafe+, deleteUnsafe+) where++import Prelude hiding (lookup)+import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, Get, put, get)+import qualified Data.Map as M+import qualified Data.IntMap as I++fromJust :: Maybe a -> a+fromJust (Just x)   = x+fromJust Nothing    = error "fromJust: Nothing"+{-# INLINE fromJust #-}++-- | Class for types which provide hash values.+class Ord a => Hash a where+    hash    :: a -> Int++-- | Value in a HashMap.+data Value a b+    = Single !a !b+    | Multi  !(M.Map a b)+    deriving (Show, Eq, Ord)++instance (Ord a, Binary a, Binary b) => Binary (Value a b) where+    put (Single x y)    = put (1 :: Int) >> put x >> put y+    put (Multi m)       = put (2 :: Int) >> put m+    get = do+        x <- get :: Get Int+        case x of+            1   -> Single <$> get <*> get+            _   -> Multi <$> get++-- | Find element associated to a value key.+find :: Ord a => a -> Value a b -> Maybe b+find x (Single x' y) = if x == x'+    then Just y+    else Nothing+find x (Multi m) = M.lookup x m++-- | Assumption: element is a member of the 'Value'. +findUnsafe :: Ord a => a -> Value a b -> Maybe b+findUnsafe _ (Single _ y) = Just y	-- unsafe+findUnsafe x (Multi m) = M.lookup x m++-- | Convert map into a 'Single' form if possible.+trySingle :: Ord a => M.Map a b -> Value a b+trySingle m = if M.size m == 1+    then (uncurry Single) (M.findMin m)+    else Multi m++-- | Insert element into a value.+embed :: Ord a => a -> b -> Value a b -> Value a b+embed x y (Single x' y')    = Multi $ M.fromList [(x, y), (x', y')]+embed x y (Multi m)         = Multi $ M.insert x y m++-- | Delete element from a value.  Return 'Nothing' if the resultant+-- value is empty.+ejectUnsafe :: Ord a => a -> Value a b -> Maybe (Value a b)+ejectUnsafe _ (Single _ _)  = Nothing    -- unsafe+ejectUnsafe x (Multi m)     = (Just . trySingle) (M.delete x m)++-- | A map from /a/ keys to /b/ elements where keys instantiate the+-- 'Hash' type class.  Key/element pairs are kept in 'Value' objects+-- which takes care of potential hash collisions.+data HashMap a b = HashMap+    { size      :: {-# UNPACK #-} !Int+    , hashMap   :: !(I.IntMap (Value a b)) }+    deriving (Show, Eq, Ord)++instance (Ord a, Binary a, Binary b) => Binary (HashMap a b) where+    put HashMap{..} = put size >> put hashMap+    get = HashMap <$> get <*> get++-- | Empty map.+empty :: HashMap a b+empty = HashMap 0 I.empty++-- | Lookup element in the map.+lookup :: Hash a => a -> HashMap a b -> Maybe b+lookup x (HashMap _ m) = I.lookup (hash x) m >>= find x++-- | Assumption: element is present in the map.+lookupUnsafe :: Hash a => a -> HashMap a b -> b+lookupUnsafe x (HashMap _ m) = fromJust (I.lookup (hash x) m >>= findUnsafe x)++-- | Insert a new element.  The function doesn't check+-- if the element was already present in the map.+insertUnsafe :: Hash a => a -> b -> HashMap a b -> HashMap a b+insertUnsafe x y (HashMap n m) =+    let i = hash x+        f (Just v)  = embed x y v+        f Nothing   = Single x y+    in  HashMap (n + 1) $ I.alter (Just . f) i m++-- | Assumption: element is present in the map.+deleteUnsafe :: Hash a => a -> HashMap a b -> HashMap a b+deleteUnsafe x (HashMap n m) =+    HashMap (n - 1) $ I.update (ejectUnsafe x) (hash x) m
+ src/Data/DAWG/Static.hs view
@@ -0,0 +1,380 @@+{-# LANGUAGE RecordWildCards #-}+++-- | The module implements /directed acyclic word graphs/ (DAWGs) internaly+-- represented as /minimal acyclic deterministic finite-state automata/.+--+-- In comparison to "Data.DAWG.Dynamic" module the automaton implemented here:+--+--   * Keeps all nodes in one array and therefore uses less memory,+--+--   * When 'weigh'ed, it can be used to perform static hashing with+--     'index' and 'byIndex' functions,+--+--   * Doesn't provide insert/delete family of operations.+++module Data.DAWG.Static+(+-- * DAWG type+  DAWG++-- * ID+, ID+, rootID+, byID++-- * Query+, lookup+, edges+, submap+, numStates+, numEdges++-- * Weight+, Weight+, weigh+, size+, index+, byIndex++-- * Construction+, empty+, fromList+, fromListWith+, fromLang++-- * Conversion+, assocs+, keys+, elems+, freeze+-- , thaw+) where+++import Prelude hiding (lookup)+import Control.Applicative ((<$), (<$>), (<*>), (<|>))+import Control.Arrow (first)+import Data.Binary (Binary, put, get)+import Data.Vector.Binary ()+import Data.Vector.Unboxed (Unbox)+import qualified Data.IntMap as M+import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as U++import Data.DAWG.Types+import qualified Data.DAWG.Util as Util+import qualified Data.DAWG.Trans as T+import qualified Data.DAWG.Static.Node as N+import qualified Data.DAWG.Graph as G+import qualified Data.DAWG.Dynamic as D+import qualified Data.DAWG.Dynamic.Internal as D+++-- | @DAWG a b c@ constitutes an automaton with alphabet symbols of type /a/,+-- transition labels of type /b/ and node values of type /Maybe c/.+-- All nodes are stored in a 'V.Vector' with positions of nodes corresponding+-- to their 'ID's.+--+data DAWG a b c = DAWG+    { nodes  :: V.Vector (N.Node b c)+    -- | The actual DAWG root has the 0 ID.  Thanks to the 'rootID'+    -- attribute, we can represent a submap of a DAWG.+    , rootID :: ID+    } deriving (Show, Eq, Ord)++instance (Binary b, Binary c, Unbox b) => Binary (DAWG a b c) where+    put DAWG{..} = put nodes >> put rootID+    get = DAWG <$> get <*> get+++-- | Retrieve sub-DAWG with a given ID (or `Nothing`, if there's+-- no such DAWG).  This function can be used, together with the+-- `root` function, to store IDs rather than entire DAWGs in a+-- data structure.+byID :: ID -> DAWG a b c -> Maybe (DAWG a b c)+byID i d = if i >= 0 && i < V.length (nodes d)+    then Just (d { rootID = i })+    else Nothing+++-- | Empty DAWG.+empty :: Unbox b => DAWG a b c+empty = flip DAWG 0 $ V.fromList+    [ N.Branch 1 T.empty U.empty+    , N.Leaf Nothing ]+++-- | A list of outgoing edges.+edges :: Enum a => DAWG a b c -> [(a, DAWG a b c)]+edges d =+    [ (toEnum sym, d{ rootID = i })+    | (sym, i) <- N.edges n ]+  where+    n = nodeBy (rootID d) d+++-- | Return the sub-DAWG containing all keys beginning with a prefix.+-- The in-memory representation of the resultant DAWG is the same as of+-- the original one, only the pointer to the DAWG root will be different.+submap :: (Enum a, Unbox b) => [a] -> DAWG a b c -> DAWG a b c+submap xs d = case follow (map fromEnum xs) (rootID d) d of+    Just i  -> d { rootID = i }+    Nothing -> empty+{-# SPECIALIZE submap :: Unbox b => String -> DAWG Char b c -> DAWG Char b c #-}+++-- | Number of states in the automaton.+-- TODO: The function ignores the `rootID` value, it won't work properly+-- after using the `submap` function.+numStates :: DAWG a b c -> Int+numStates = V.length . nodes+++-- | Number of edges in the automaton.+-- TODO: The function ignores the `rootID` value, it won't work properly+-- after using the `submap` function.+numEdges :: DAWG a b c -> Int+numEdges = sum . map (length . N.edges) . V.toList . nodes+++-- | Node with the given identifier.+nodeBy :: ID -> DAWG a b c -> N.Node b c+nodeBy i d = nodes d V.! i+++-- | Value in leaf node with a given ID.+leafValue :: N.Node b c -> DAWG a b c -> Maybe c+leafValue n = N.value . nodeBy (N.eps n)+++-- | Follow the path from the given identifier.+follow :: Unbox b => [Sym] -> ID -> DAWG a b c -> Maybe ID+follow (x:xs) i d = do+    j <- N.onSym x (nodeBy i d)+    follow xs j d+follow [] i _ = Just i+++-- | Find value associated with the key.+lookup :: (Enum a, Unbox b) => [a] -> DAWG a b c -> Maybe c+lookup xs d = lookup'I (map fromEnum xs) (rootID d) d+{-# SPECIALIZE lookup :: Unbox b => String -> DAWG Char b c -> Maybe c #-}+++lookup'I :: Unbox b => [Sym] -> ID -> DAWG a b c -> Maybe c+lookup'I xs i d = do+    j <- follow xs i d+    leafValue (nodeBy j d) d+++-- -- | Find all (key, value) pairs such that key is prefixed+-- -- with the given string.+-- withPrefix :: (Enum a, Unbox b) => [a] -> DAWG a b c -> [([a], c)]+-- withPrefix xs d = maybe [] id $ do+--     i <- follow (map fromEnum xs) 0 d+--     let prepare = (xs ++) . map toEnum+--     return $ map (first prepare) (subPairs i d)+-- {-# SPECIALIZE withPrefix+--     :: Unbox b => String -> DAWG Char b c+--     -> [(String, c)] #-}+++-- | Return all (key, value) pairs in ascending key order in the+-- sub-DAWG determined by the given node ID.+subPairs :: Unbox b => ID -> DAWG a b c -> [([Sym], c)]+subPairs i d =+    here ++ concatMap there (N.edges n)+  where+    n = nodeBy i d+    here = case leafValue n d of+        Just x  -> [([], x)]+        Nothing -> []+    there (x, j) = map (first (x:)) (subPairs j d)+++-- | Return all (key, value) pairs in the DAWG in ascending key order.+assocs :: (Enum a, Unbox b) => DAWG a b c -> [([a], c)]+assocs d = map (first (map toEnum)) (subPairs (rootID d) d)+{-# SPECIALIZE assocs :: Unbox b => DAWG Char b c -> [(String, c)] #-}+++-- | Return all keys of the DAWG in ascending order.+keys :: (Enum a, Unbox b) => DAWG a b c -> [[a]]+keys = map fst . assocs+{-# SPECIALIZE keys :: Unbox b => DAWG Char b c -> [String] #-}+++-- | Return all elements of the DAWG in the ascending order of their keys.+elems :: Unbox b => DAWG a b c -> [c]+elems d = map snd $ subPairs (rootID d) d+++-- | Construct 'DAWG' from the list of (word, value) pairs.+-- First a 'D.DAWG' is created and then it is frozen using+-- the 'freeze' function.+fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a () b+fromList = freeze . D.fromList+{-# SPECIALIZE fromList :: Ord b => [(String, b)] -> DAWG Char () b #-}+++-- | Construct DAWG from the list of (word, value) pairs+-- with a combining function.  The combining function is+-- applied strictly. First a 'D.DAWG' is created and then+-- it is frozen using the 'freeze' function.+fromListWith :: (Enum a, Ord b) => (b -> b -> b) -> [([a], b)] -> DAWG a () b+fromListWith f = freeze . D.fromListWith f+{-# SPECIALIZE fromListWith+        :: Ord b => (b -> b -> b)+        -> [(String, b)] -> DAWG Char () b #-}+++-- | Make DAWG from the list of words.  Annotate each word with+-- the @()@ value.  First a 'D.DAWG' is created and then it is frozen+-- using the 'freeze' function.+fromLang :: Enum a => [[a]] -> DAWG a () ()+fromLang = freeze . D.fromLang+{-# SPECIALIZE fromLang :: [String] -> DAWG Char () () #-}+++-- | Weight of a node corresponds to the number of final states+-- reachable from the node.  Weight of an edge is a sum of weights+-- of preceding nodes outgoing from the same parent node.+type Weight = Int+++-- | Compute node weights and store corresponding values in transition labels.+-- Be aware, that the entire DAWG will be weighted, even when (because of the use of+-- the `submap` function) only a part of the DAWG is currently selected.+weigh :: DAWG a b c -> DAWG a Weight c+weigh d = flip DAWG (rootID d) $ V.fromList+    [ branch n ws+    | i <- [0 .. numStates d - 1]+    , let n  = nodeBy i d+    , let ws = accum (N.children n) ]+  where+    -- Branch with new weights.+    branch N.Branch{..} ws  = N.Branch eps transMap ws+    branch N.Leaf{..} _     = N.Leaf value+    -- In nodeWeight node weights are memoized.+    nodeWeight = ((V.!) . V.fromList) (map detWeight [0 .. numStates d - 1])+    -- Determine weight of the node.+    detWeight i = case nodeBy i d of+        N.Leaf w    -> maybe 0 (const 1) w+        n           -> sum . map nodeWeight $ allChildren n+    -- Weights for subsequent edges.+    accum = U.fromList . init . scanl (+) 0 . map nodeWeight+    -- Plain children and epsilon child. +    allChildren n = N.eps n : N.children n+++-- | Construct immutable version of the automaton.+freeze :: D.DAWG a b -> DAWG a () b+freeze d = flip DAWG 0 . V.fromList $+    map (N.fromDyn newID . oldBy)+        (M.elems (inverse old2new))+  where+    -- Map from old to new identifiers.  The root identifier is mapped to 0.+    old2new = M.fromList $ (D.root d, 0) : zip (nodeIDs d) [1..]+    newID   = (M.!) old2new+    -- List of node IDs without the root ID.+    nodeIDs = filter (/= D.root d) . map fst . M.assocs . G.nodeMap . D.graph+    -- Non-frozen node by given identifier.+    oldBy i = G.nodeBy i (D.graph d)+        ++-- | Inverse of the map.+inverse :: M.IntMap Int -> M.IntMap Int+inverse =+    let swap (x, y) = (y, x)+    in  M.fromList . map swap . M.toList+++-- -- | Yield mutable version of the automaton.+-- thaw :: (Unbox b, Ord a) => DAWG a b c -> D.DAWG a b+-- thaw d =+--     D.fromNodes nodes 0+--   where+--     -- List of resulting nodes.+--     nodes = branchNodes ++ leafNodes+--     -- Branching nodes.+--     branchNodes =+--         [ +--     -- Number of states used to shift new value IDs.+--     n = numStates d+--     -- New identifiers for value nodes.+--     valIDs = foldl' updID GM.empty (values d)+--     -- Values in the automaton.+--     values = map value . V.toList . nodes+--     -- Update ID map.+--     updID m v = case GM.lookup v m of+--         Just i  -> m+--         Nothing -> +--             let j = GM.size m + n+--             in  j `seq` GM.insert v j+++-- | A number of distinct (key, value) pairs in the weighted DAWG.+size :: DAWG a Weight c -> Int+size d = size'I (rootID d) d+++size'I :: ID -> DAWG a Weight c -> Int+size'I i d = add $ do+    x <- case N.edges n of+        [] -> Nothing+        xs -> Just (fst $ last xs)+    (j, v) <- N.onSym' x n+    return $ v + size'I j d+  where+    n = nodeBy i d+    u = maybe 0 (const 1) (leafValue n d)+    add m = u + maybe 0 id m+++-----------------------------------------+-- Index+-----------------------------------------+++-- | Position in a set of all dictionary entries with respect+-- to the lexicographic order.+index :: Enum a => [a] -> DAWG a Weight c -> Maybe Int+index xs d = index'I (map fromEnum xs) (rootID d) d+{-# SPECIALIZE index :: String -> DAWG Char Weight c -> Maybe Int #-}+++index'I :: [Sym] -> ID -> DAWG a Weight c -> Maybe Int+index'I []     i d = 0 <$ leafValue (nodeBy i d) d+index'I (x:xs) i d = do+    let n = nodeBy i d+        u = maybe 0 (const 1) (leafValue n d)+    (j, v) <- N.onSym' x n+    w <- index'I xs j d+    return (u + v + w)+++-- | Find dictionary entry given its index with respect to the+-- lexicographic order.+byIndex :: Enum a => Int -> DAWG a Weight c -> Maybe [a]+byIndex ix d = map toEnum <$> byIndex'I ix (rootID d) d+{-# SPECIALIZE byIndex :: Int -> DAWG Char Weight c -> Maybe String #-}+++byIndex'I :: Int -> ID -> DAWG a Weight c -> Maybe [Sym]+byIndex'I ix i d+    | ix < 0    = Nothing+    | otherwise = here <|> there+  where+    n = nodeBy i d+    u = maybe 0 (const 1) (leafValue n d)+    here+        | ix == 0   = [] <$ leafValue (nodeBy i d) d+        | otherwise = Nothing+    there = do+        (k, w) <- Util.findLastLE cmp (N.labelVect n)+        (x, j) <- T.byIndex k (N.transMap n)+        xs <- byIndex'I (ix - u - w) j d+        return (x:xs)+    cmp w = compare w (ix - u)
+ src/Data/DAWG/Static/Node.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE RecordWildCards #-}++-- | Internal representation of a static automata node.++module Data.DAWG.Static.Node+( Node(..)+, onSym+, onSym'+, edges+, children+, insert+, fromDyn+) where++import Control.Arrow (second)+import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, Get, put, get)+import Data.Vector.Binary ()+import qualified Data.Vector.Unboxed as U++import Data.DAWG.Types+import Data.DAWG.Trans.Vector (Trans)+import qualified Data.DAWG.Trans as T+import qualified Data.DAWG.Dynamic.Node as D++-- | Two nodes (states) belong to the same equivalence class (and,+-- consequently, they must be represented as one node in the graph)+-- iff they are equal with respect to their values and outgoing+-- edges.+--+-- Since 'Leaf' nodes are distinguished from 'Branch' nodes, two values+-- equal with respect to '==' function are always kept in one 'Leaf'+-- node in the graph.  It doesn't change the fact that to all 'Branch'+-- nodes one value is assigned through the epsilon transition.+--+-- Invariant: the 'eps' identifier always points to the 'Leaf' node.+-- Edges in the 'edgeMap', on the other hand, point to 'Branch' nodes.+data Node a b+    = Branch {+        -- | Epsilon transition.+          eps       :: {-# UNPACK #-} !ID+        -- | Transition map (outgoing edges).+        , transMap  :: !Trans+        -- | Labels corresponding to individual edges.+        , labelVect :: !(U.Vector a) }+    | Leaf { value  :: !(Maybe b) }+    deriving (Show, Eq, Ord)++instance (U.Unbox a, Binary a, Binary b) => Binary (Node a b) where+    put Branch{..} = put (1 :: Int) >> put eps >> put transMap >> put labelVect+    put Leaf{..}   = put (2 :: Int) >> put value+    get = do+        x <- get :: Get Int+        case x of+            1 -> Branch <$> get <*> get <*> get+            _ -> Leaf <$> get++-- | Transition function.+onSym :: Sym -> Node a b -> Maybe ID+onSym x (Branch _ t _)  = T.lookup x t+onSym _ (Leaf _)        = Nothing+{-# INLINE onSym #-}++-- | Transition function.+onSym' :: U.Unbox a => Sym -> Node a b -> Maybe (ID, a)+onSym' x (Branch _ t ls)   = do+    k <- T.index x t+    (,) <$> (snd <$> T.byIndex k t)+        <*> ls U.!? k+onSym' _ (Leaf _)           = Nothing+{-# INLINE onSym' #-}++-- | List of symbol/edge pairs outgoing from the node.+edges :: Node a b -> [(Sym, ID)]+edges (Branch _ t _)    = T.toList t+edges (Leaf _)          = []+{-# INLINE edges #-}++-- | List of children identifiers.+children :: Node a b -> [ID]+children = map snd . edges+{-# INLINE children #-}++-- | Substitue edge determined by a given symbol.+insert :: Sym -> ID -> Node a b -> Node a b+insert x i (Branch w t ls)  = Branch w (T.insert x i t) ls+insert _ _ l                = l+{-# INLINE insert #-}++-- | Make "static" node from a "dynamic" node.+fromDyn+    :: (ID -> ID)   -- ^ Assign new IDs +    -> D.Node b     -- ^ "Dynamic" node+    -> Node () b    -- ^ "Static" node+fromDyn _ (D.Leaf x)        = Leaf x+fromDyn f (D.Branch e t)    =+    let reTrans = T.fromList . map (second f) . T.toList+    in  Branch (f e) (reTrans t) U.empty
+ src/Data/DAWG/Trans.hs view
@@ -0,0 +1,26 @@+-- | The module provides an abstraction over transition maps from+-- alphabet symbols to node identifiers.++module Data.DAWG.Trans+( Trans (..)+) where++import Data.DAWG.Types++-- | Abstraction over transition maps from alphabet symbols to+-- node identifiers.+class Trans t where+    -- | Empty transition map.+    empty       :: t+    -- | Lookup sybol in the map.+    lookup      :: Sym -> t -> Maybe ID+    -- | Find index of the symbol.+    index       :: Sym -> t -> Maybe Int+    -- | Select a (symbol, ID) pair by index of its position in the map.+    byIndex     :: Int -> t -> Maybe (Sym, ID)+    -- | Insert element to the transition map.+    insert      :: Sym -> ID -> t -> t+    -- | Construct transition map from a list.+    fromList    :: [(Sym, ID)] -> t+    -- | Translate transition map into a list.+    toList      :: t -> [(Sym, ID)]
+ src/Data/DAWG/Trans/Hashed.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE FlexibleInstances #-}++-- | Transition map with a hash.++module Data.DAWG.Trans.Hashed+( Hashed (..)+) where++import Prelude hiding (lookup)+import Control.Applicative ((<$>), (<*>))+import Data.DAWG.Util (combine)+import Data.Binary (Binary, put, get)+import Data.DAWG.Trans+import qualified Data.DAWG.Trans.Map as M+import qualified Data.DAWG.Trans.Vector as V++-- | Hash of a transition map is a sum of element-wise hashes.+-- Hash for a given element @(Sym, ID)@ is equal to @combine Sym ID@.+data Hashed t = Hashed+    { hash  :: {-# UNPACK #-} !Int+    , trans :: !t }+    deriving (Show)++instance Binary t => Binary (Hashed t) where+    put Hashed{..} = put hash >> put trans+    get = Hashed <$> get <*> get++instance Trans t => Trans (Hashed t) where+    empty       = Hashed 0 empty+    {-# INLINE empty #-} ++    lookup x    = lookup x . trans+    {-# INLINE lookup #-} ++    index x     = index x . trans+    {-# INLINE index #-} ++    byIndex i   = byIndex i . trans+    {-# INLINE byIndex #-} ++    insert x y (Hashed h t) = Hashed+        (h - h' + combine x y)+        (insert x y t)+      where+        h' = case lookup x t of+            Just y' -> combine x y'+            Nothing -> 0+    {-# INLINE insert #-}++    fromList xs = Hashed +        (sum $ map (uncurry combine) xs)+        (fromList xs)+    {-# INLINE fromList #-}++    toList  = toList . trans+    {-# INLINE toList #-}++deriving instance Eq  (Hashed M.Trans)+deriving instance Ord (Hashed M.Trans)+deriving instance Eq  (Hashed V.Trans)+deriving instance Ord (Hashed V.Trans)
+ src/Data/DAWG/Trans/Map.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- | Implementation of a transition map build on top of the "M.Map" container.++module Data.DAWG.Trans.Map+( Trans (unTrans)+) where++import Prelude hiding (lookup)+import Data.Binary (Binary)+import qualified Data.Map as M++import Data.DAWG.Types+import qualified Data.DAWG.Trans as C++-- | A vector of distinct key/value pairs strictly ascending with respect+-- to key values.+newtype Trans = Trans { unTrans :: M.Map Sym ID }+    deriving (Show, Eq, Ord, Binary)++instance C.Trans Trans where+    empty = Trans M.empty+    {-# INLINE empty #-}++    lookup x = M.lookup x . unTrans+    {-# INLINE lookup #-}++    index x = M.lookupIndex x . unTrans+    {-# INLINE index #-}++    byIndex i (Trans m) =+	let n = M.size m+        in  if i >= 0 && i < n+                then Just (M.elemAt i m)+                else Nothing+    {-# INLINE byIndex #-}++    insert x y (Trans m) = Trans (M.insert x y m)+    {-# INLINE insert #-}++    fromList = Trans . M.fromList+    {-# INLINE fromList #-}++    toList = M.toList . unTrans+    {-# INLINE toList #-}
+ src/Data/DAWG/Trans/Vector.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- | A vector representation of a transition map.  Memory efficient, but the+-- insert operation is /O(n)/ with respect to the number of transitions.+-- In particular, complexity of the insert operation can make the construction+-- of a large-alphabet dictionary intractable.++module Data.DAWG.Trans.Vector+( Trans (unTrans)+) where++import Prelude hiding (lookup)+import Control.Applicative ((<$>))+import Data.Binary (Binary)+import Data.Vector.Binary ()+import qualified Data.IntMap as M+import qualified Data.Vector.Unboxed as U+import qualified Data.Vector.Unboxed.Mutable as UM++import Data.DAWG.Types+import Data.DAWG.Util+import qualified Data.DAWG.Trans as C++-- | A vector of distinct key/value pairs strictly ascending with respect+-- to key values.+newtype Trans = Trans { unTrans :: U.Vector (Sym, ID) }+    deriving (Show, Eq, Ord, Binary)++instance C.Trans Trans where+    empty = Trans U.empty+    {-# INLINE empty #-}++    lookup x m = do+        k <- C.index x m+        snd <$> C.byIndex k m+    {-# INLINE lookup #-}++    index x (Trans v)+        = either Just (const Nothing) $+            binarySearch (flip compare x . fst) v+    {-# INLINE index #-}++    byIndex k (Trans v) = v U.!? k+    {-# INLINE byIndex #-}++    insert x y (Trans v) = Trans $+        case binarySearch (flip compare x . fst) v of+            Left k  -> U.modify (\w -> UM.write w k (x, y)) v+            Right k ->+                let (v'L, v'R) = U.splitAt k v+                in  U.concat [v'L, U.singleton (x, y), v'R]+    {-# INLINE insert #-}++    fromList = Trans . U.fromList . M.toAscList . M.fromList+    {-# INLINE fromList #-}++    toList = U.toList . unTrans+    {-# INLINE toList #-}
+ src/Data/DAWG/Types.hs view
@@ -0,0 +1,12 @@+-- | Basic types used throughout the library.++module Data.DAWG.Types+( ID+, Sym+) where++-- | Node identifier.+type ID = Int++-- | Internal representation of an alphabet element.+type Sym = Int
+ src/Data/DAWG/Util.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TupleSections #-}++-- | Utility functions.++module Data.DAWG.Util+( binarySearch+, findLastLE+, combine+) where++import Control.Applicative ((<$>))+import Data.Bits (shiftR, xor)+import Data.Vector.Unboxed (Unbox)+import qualified Control.Monad.ST as ST+import qualified Data.Vector.Unboxed as U+import qualified Data.Vector.Unboxed.Mutable as UM++-- | Given a vector of length @n@ strictly ascending with respect to a given+-- comparison function, find an index at which the given element could be+-- inserted while preserving sortedness.+-- The 'Left' result indicates, that the 'EQ' element has been found,+-- while the 'Right' result means otherwise.  Value of the 'Right'+-- result is in the [0,n] range.+binarySearch :: Unbox a => (a -> Ordering) -> U.Vector a -> Either Int Int+binarySearch cmp v = ST.runST $ do+    w <- U.unsafeThaw v+    search w+  where+    search w =+        loop 0 (UM.length w)+      where+        loop !l !u+            | u <= l    = return (Right l)+            | otherwise = do+                let k = (u + l) `shiftR` 1+                x <- UM.unsafeRead w k+                case cmp x of+                    LT -> loop (k+1) u+                    EQ -> return (Left k)+                    GT -> loop l     k+{-# INLINE binarySearch #-}++-- | Given a vector sorted with respect to some underlying comparison+-- function, find last element which is not 'GT' with respect to the+-- comparison function.+findLastLE :: Unbox a => (a -> Ordering) -> U.Vector a -> Maybe (Int, a)+findLastLE cmp v =+    let k' = binarySearch cmp v+    	k  = either id (\x -> x-1) k'+    in  (k,) <$> v U.!? k+{-# INLINE findLastLE #-}++-- | Combine two given hash values.  'combine' has zero as a left+-- identity.+combine :: Int -> Int -> Int+combine h1 h2 = (h1 * 16777619) `xor` h2+{-# INLINE combine #-}