dawg 0.8.2 → 0.11
raw patch · 12 files changed
Files
- README.md +0/−0
- dawg.cabal +51/−50
- src/Data/DAWG.hs +0/−41
- src/Data/DAWG/Dynamic.hs +275/−0
- src/Data/DAWG/Dynamic/Internal.hs +27/−0
- src/Data/DAWG/Dynamic/Node.hs +80/−0
- src/Data/DAWG/Graph.hs +7/−2
- src/Data/DAWG/HashMap.hs +1/−1
- src/Data/DAWG/Internal.hs +0/−269
- src/Data/DAWG/Node.hs +0/−114
- src/Data/DAWG/Static.hs +195/−117
- src/Data/DAWG/Static/Node.hs +98/−0
− README.md
dawg.cabal view
@@ -1,54 +1,55 @@-cabal-version: 1.12+name: dawg+version: 0.11+synopsis: Directed acyclic word graphs+description:+ 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+ which can be used to build the automaton on-the-fly. The automaton from+ the "Data.DAWG.Static" module has lower memory footprint and provides+ static hashing functionality.+license: BSD3+license-file: LICENSE+cabal-version: >= 1.6+copyright: Copyright (c) 2012 IPI PAN+author: Jakub Waszczuk+maintainer: waszczuk.kuba@gmail.com+stability: experimental+category: Data, Data Structures+homepage: https://github.com/kawu/dawg+build-type: Simple --- This file has been generated from package.yaml by hpack version 0.31.1.------ see: https://github.com/sol/hpack------ hash: 5573d3f5ed5f3dad8d4c64b5f23df1efff78ab4e890fccbe657bd46c4cb550e8+library+ hs-source-dirs: src+ build-depends:+ base >= 4 && < 5+ , containers >= 0.4.1 && < 0.6+ , binary+ , vector+ , vector-binary+ , mtl+ , transformers -name: dawg-version: 0.8.2-synopsis: Directed acyclic word graphs-description: Please see the README on GitHub at <https://github.com/kawu/dawg#readme>-category: Data, Data Structures-homepage: https://github.com/kawu/dawg#readme-bug-reports: https://github.com/kawu/dawg/issues-author: Jakub Waszczuk-maintainer: waszczuk.kuba@gmail.com-copyright: 2012-2019 IPI PAN, Jakub Waszczuk-license: BSD3-license-file: LICENSE-build-type: Simple-extra-source-files:- README.md+ exposed-modules:+ Data.DAWG.Dynamic+ , Data.DAWG.Static -source-repository head- type: git- location: https://github.com/kawu/dawg+ other-modules:+ Data.DAWG.Types+ , Data.DAWG.Static.Node+ , Data.DAWG.Dynamic.Internal+ , Data.DAWG.Dynamic.Node+ , Data.DAWG.Graph+ , Data.DAWG.Trans+ , Data.DAWG.Trans.Vector+ , Data.DAWG.Trans.Map+ , Data.DAWG.Trans.Hashed+ , Data.DAWG.HashMap+ , Data.DAWG.Util -library- exposed-modules:- Data.DAWG- Data.DAWG.Graph- Data.DAWG.HashMap- Data.DAWG.Internal- Data.DAWG.Node- Data.DAWG.Static- Data.DAWG.Trans- Data.DAWG.Trans.Hashed- Data.DAWG.Trans.Map- Data.DAWG.Trans.Vector- Data.DAWG.Types- Data.DAWG.Util- other-modules:- Paths_dawg- hs-source-dirs:- src- build-depends:- base >=4 && <5- , binary- , containers >=0.5 && <0.7- , mtl- , vector- , vector-binary-instances- default-language: Haskell2010+ ghc-options: -Wall -O2++source-repository head+ type: git+ location: https://github.com/kawu/dawg.git
− src/Data/DAWG.hs
@@ -1,41 +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.------ Transition backend has to be specified by a type signature. You can import--- the desired transition type and define your own dictionary construction--- function.------ > import Data.DAWG--- > import Data.DAWG.Trans.Map (Trans)--- >--- > mkDict :: (Enum a, Ord b) => [([a], b)] -> DAWG Trans a b--- > mkDict = fromList--module Data.DAWG-(--- * DAWG type- DAWG-, MkNode--- * Query-, numStates-, lookup--- * Construction-, empty-, fromList-, fromListWith-, fromLang--- ** Insertion-, insert-, insertWith--- ** Deletion-, delete--- * Conversion-, assocs-, keys-, elems-) where--import Prelude hiding (lookup)-import Data.DAWG.Internal
+ 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
@@ -9,6 +9,7 @@ ( Graph (..) , empty , size+, nodes , nodeBy , insert , delete@@ -17,7 +18,7 @@ import Control.Applicative ((<$>), (<*>)) import Data.Binary (Binary, put, get) import qualified Data.IntSet as S-import qualified Data.IntMap.Strict as M+import qualified Data.IntMap as M import Data.DAWG.HashMap (Hash) import qualified Data.DAWG.HashMap as H@@ -68,6 +69,10 @@ 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@@ -105,7 +110,7 @@ -- | Increment the number of ingoing paths. incIngo :: ID -> Graph n -> Graph n-incIngo i g = g { ingoMap = M.insertWith (+) i 1 (ingoMap g) }+incIngo i g = g { ingoMap = M.insertWith' (+) i 1 (ingoMap g) } -- | Decrement the number of ingoing paths and return -- the resulting number.
src/Data/DAWG/HashMap.hs view
@@ -51,7 +51,7 @@ -- | Assumption: element is a member of the 'Value'. findUnsafe :: Ord a => a -> Value a b -> Maybe b-findUnsafe _ (Single _ y) = Just y -- unsafe+findUnsafe _ (Single _ y) = Just y -- unsafe findUnsafe x (Multi m) = M.lookup x m -- | Convert map into a 'Single' form if possible.
− src/Data/DAWG/Internal.hs
@@ -1,269 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}---- | 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.Internal-(--- * DAWG type- DAWG (..)-, MkNode--- * Query-, numStates-, 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 Data.Binary (Binary, put, get)-import qualified Data.Vector.Unboxed as U-import qualified Control.Monad.State.Strict as S--import Data.DAWG.Types-import Data.DAWG.Graph (Graph)-import Data.DAWG.Trans (Trans)-import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Node as N-import qualified Data.DAWG.Graph as G--type Node t a = N.Node t () a---- | Is /t/ a valid transition map within the context of--- /a/-valued automata nodes? All transition implementations--- provided by the library are instances of this class.-class (Ord (Node t a), Trans t) => MkNode t a where-instance (Ord (Node t a), Trans t) => MkNode t a where--type GraphM t a b = S.State (Graph (Node t a)) b--mkState :: (Graph a -> Graph a) -> Graph a -> ((), Graph a)-mkState f g = ((), f g)---- | Leaf node with no children and 'Nothing' value.-insertLeaf :: MkNode t a => GraphM t a ID-insertLeaf = do- i <- insertNode (N.Leaf Nothing)- insertNode (N.Branch i T.empty U.empty)---- | Return node with the given identifier.-nodeBy :: ID -> GraphM t a (Node t a)-nodeBy i = G.nodeBy i <$> S.get---- Evaluate the 'G.insert' function within the monad.-insertNode :: MkNode t a => Node t a -> GraphM t a ID-insertNode = S.state . G.insert---- Evaluate the 'G.delete' function within the monad.-deleteNode :: MkNode t a => Node t a -> GraphM t a ()-deleteNode = S.state . mkState . G.delete---- | Invariant: the identifier points to the 'Branch' node.-insertM :: MkNode t a => [Sym] -> a -> ID -> GraphM t 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- :: MkNode t a => (a -> a -> a)- -> [Sym] -> a -> ID -> GraphM t 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 :: MkNode t a => [Sym] -> ID -> GraphM t 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 :: Trans t => [Sym] -> ID -> GraphM t 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 :: Trans t => Graph (Node t 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)---- | A directed acyclic word graph with phantom type @a@ representing--- type of alphabet elements.-data DAWG t a b = DAWG- { graph :: !(Graph (Node t b))- , root :: !ID }- deriving (Show)--instance (MkNode t b, Binary t, Binary b) => Binary (DAWG t a b) where- put d = do- put (graph d)- put (root d)- get = DAWG <$> get <*> get---- | Empty DAWG.-empty :: (MkNode t b) => DAWG t a b-empty = - let (i, g) = S.runState insertLeaf G.empty- in DAWG g i---- | Number of states in the underlying graph.-numStates :: DAWG t a b -> Int-numStates = G.size . graph---- | Insert the (key, value) pair into the DAWG.-insert :: (Enum a, MkNode t b) => [a] -> b -> DAWG t a b -> DAWG t 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- :: (MkNode t b) => String -> b- -> DAWG t Char b -> DAWG t 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, MkNode t b) => (b -> b -> b)- -> [a] -> b -> DAWG t a b -> DAWG t 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- :: MkNode t b => (b -> b -> b) -> String -> b- -> DAWG t Char b -> DAWG t Char b #-}---- | Delete the key from the DAWG.-delete :: (Enum a, MkNode t b) => [a] -> DAWG t a b -> DAWG t 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- :: MkNode t b => String- -> DAWG t Char b -> DAWG t Char b #-}---- | Find value associated with the key.-lookup :: (Enum a, MkNode t b) => [a] -> DAWG t a b -> Maybe b-lookup xs' d =- let xs = map fromEnum xs'- in S.evalState (lookupM xs $ root d) (graph d)-{-# SPECIALIZE lookup- :: MkNode t b => String- -> DAWG t Char b -> Maybe b #-}---- | Return all key/value pairs in the DAWG in ascending key order.-assocs :: (Enum a, MkNode t b) => DAWG t a b -> [([a], b)]-assocs- = map (first (map toEnum))- . (assocsAcc <$> graph <*> root)-{-# SPECIALIZE assocs :: MkNode t b => DAWG t Char b -> [(String, b)] #-}---- | Return all keys of the DAWG in ascending order.-keys :: (Enum a, MkNode t b) => DAWG t a b -> [[a]]-keys = map fst . assocs-{-# SPECIALIZE keys :: MkNode t b => DAWG t Char b -> [String] #-}---- | Return all elements of the DAWG in the ascending order of their keys.-elems :: MkNode t b => DAWG t a b -> [b]-elems = map snd . (assocsAcc <$> graph <*> root)---- | Construct DAWG from the list of (word, value) pairs.-fromList :: (Enum a, MkNode t b) => [([a], b)] -> DAWG t a b-fromList xs =- let update t (x, v) = insert x v t- in foldl' update empty xs-{-# INLINE fromList #-}-{-# SPECIALIZE fromList- :: MkNode t b => [(String, b)] -> DAWG t Char b #-}---- | Construct DAWG from the list of (word, value) pairs--- with a combining function. The combining function is--- applied strictly.-fromListWith- :: (Enum a, MkNode t b) => (b -> b -> b)- -> [([a], b)] -> DAWG t a b-fromListWith f xs =- let update t (x, v) = insertWith f x v t- in foldl' update empty xs-{-# SPECIALIZE fromListWith- :: MkNode t b => (b -> b -> b)- -> [(String, b)] -> DAWG t Char b #-}---- | Make DAWG from the list of words. Annotate each word with--- the @()@ value.-fromLang :: (Enum a, MkNode t ()) => [[a]] -> DAWG t a ()-fromLang xs = fromList [(x, ()) | x <- xs]-{-# SPECIALIZE fromLang :: MkNode t () => [String] -> DAWG t Char () #-}
− src/Data/DAWG/Node.hs
@@ -1,114 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE StandaloneDeriving #-}---- | Internal representation of automata nodes.--module Data.DAWG.Node-( Node (..)-, onSym-, onSym'-, edges-, children-, insert-, reID-) where--import Control.Applicative ((<$>), (<*>))-import Control.Arrow (second)-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.Util (combine)-import Data.DAWG.Trans (Trans)-import Data.DAWG.HashMap (Hash, hash)-import qualified Data.DAWG.Trans.Hashed as H-import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Trans.Vector as TV-import qualified Data.DAWG.Trans.Map as TM---- | 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 t a b- = Branch {- -- | Epsilon transition.- eps :: {-# UNPACK #-} !ID- -- | Transition map (outgoing edges).- , transMap :: !(H.Hashed t)- -- | Labels corresponding to individual edges.- , labelVect :: !(U.Vector a) }- | Leaf { value :: !(Maybe b) }- deriving (Show)--deriving instance (Eq a, Eq b, U.Unbox a) => Eq (Node TV.Trans a b)-deriving instance (Ord a, Ord b, U.Unbox a) => Ord (Node TV.Trans a b)-deriving instance (Eq a, Eq b, U.Unbox a) => Eq (Node TM.Trans a b)-deriving instance (Ord a, Ord b, U.Unbox a) => Ord (Node TM.Trans a b)--instance (Trans t, Ord (Node t a b)) => Hash (Node t a b) where- hash Branch{..} = combine eps (H.hash transMap)- hash Leaf{..} = case value of- Just _ -> (-1)- Nothing -> (-2)--instance (U.Unbox a, Binary t, Binary a, Binary b) => Binary (Node t 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 :: Trans t => Sym -> Node t a b -> Maybe ID-onSym x (Branch _ t _) = T.lookup x t-onSym _ (Leaf _) = Nothing-{-# INLINE onSym #-}---- | Transition function.-onSym' :: (Trans t, U.Unbox a) => Sym -> Node t 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 :: Trans t => Node t a b -> [(Sym, ID)]-edges (Branch _ t _) = T.toList t-edges (Leaf _) = []-{-# INLINE edges #-}---- | List of children identifiers.-children :: Trans t => Node t a b -> [ID]-children = map snd . edges-{-# INLINE children #-}---- | Substitue edge determined by a given symbol.-insert :: Trans t => Sym -> ID -> Node t a b -> Node t a b-insert x i (Branch w t ls) = Branch w (T.insert x i t) ls-insert _ _ l = l-{-# INLINE insert #-}---- | Assign new identifiers.-reID :: Trans t => (ID -> ID) -> Node t a b -> Node t a b-reID _ (Leaf x) = Leaf x-reID f (Branch e t ls) =- let reTrans = T.fromList . map (second f) . T.toList- in Branch (f e) (reTrans t) ls
src/Data/DAWG/Static.hs view
@@ -1,63 +1,60 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-} + -- | The module implements /directed acyclic word graphs/ (DAWGs) internaly -- represented as /minimal acyclic deterministic finite-state automata/. ----- In comparison to "Data.DAWG" module the automaton implemented here:+-- In comparison to "Data.DAWG.Dynamic" module the automaton implemented here: ----- * Keeps all nodes in one array and therefore uses much less memory,+-- * 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,+-- 'index' and 'byIndex' functions, -- -- * Doesn't provide insert/delete family of operations.------ Transition backend has to be specified by a type signature. You can import--- the desired transition type and define your own dictionary construction--- function.------ > import Data.DAWG.Static--- > import Data.DAWG.Trans.Map (Trans)--- >--- > mkDict :: (Enum a, Ord b) => [([a], b)] -> DAWG Trans a Weight b--- > mkDict = weigh . fromList + module Data.DAWG.Static ( -- * DAWG type DAWG++-- * ID+, ID+, rootID+, byID+ -- * Query , lookup+, edges+, submap , numStates--- * Index+, numEdges++-- * Weight+, Weight+, weigh+, size , index , byIndex--- * Hash-, hash-, unHash+ -- * Construction , empty , fromList , fromListWith , fromLang-, freeze--- * Weight-, Weight-, weigh+ -- * Conversion , assocs , keys , elems+, freeze -- , thaw ) where + import Prelude hiding (lookup)-import Control.Applicative ((<$), (<$>), (<|>))+import Control.Applicative ((<$), (<$>), (<*>), (<|>)) import Control.Arrow (first) import Data.Binary (Binary, put, get) import Data.Vector.Binary ()@@ -67,127 +64,191 @@ import qualified Data.Vector.Unboxed as U import Data.DAWG.Types-import Data.DAWG.Trans (Trans)-import Data.DAWG.Node (Node)+import qualified Data.DAWG.Util as Util import qualified Data.DAWG.Trans as T-import qualified Data.DAWG.Trans.Vector as VT-import qualified Data.DAWG.Node as N+import qualified Data.DAWG.Static.Node as N import qualified Data.DAWG.Graph as G-import qualified Data.DAWG.Internal as D-import qualified Data.DAWG.Util as Util+import qualified Data.DAWG.Dynamic as D+import qualified Data.DAWG.Dynamic.Internal as D --- | @DAWG t a b c@ constitutes an automaton with alphabet symbols of type /a/,--- transition labels of type /b/ and node values of type /Maybe c/, implemented--- on top of the 'T.Trans' /t/ backend. All nodes are stored in a 'V.Vector'--- with positions of nodes corresponding to their 'ID's.-newtype DAWG t a b c = DAWG { unDAWG :: V.Vector (Node t b c) }- deriving (Show) -deriving instance (Eq b, Eq c, Unbox b) => Eq (DAWG VT.Trans a b c)-deriving instance (Ord b, Ord c, Unbox b) => Ord (DAWG VT.Trans a b c)+-- | @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 t, Binary b, Binary c, Unbox b) => Binary (DAWG t a b c) where- put = put . unDAWG- get = DAWG <$> get+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 :: (Trans t, Unbox b) => DAWG t a b c-empty = DAWG $ V.fromList+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.-numStates :: DAWG t a b c -> Int-numStates = V.length . unDAWG+-- 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 t a b c -> Node t b c-nodeBy i d = unDAWG d V.! i+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 :: Node t b c -> DAWG t a b c -> Maybe c+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, Trans t, Unbox b) => [a] -> DAWG t a b c -> Maybe c-lookup xs' =- let xs = map fromEnum xs'- in lookup'I xs 0-{-# SPECIALIZE lookup- :: (Trans t, Unbox b) => String- -> DAWG t Char b c -> Maybe c #-}+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 :: (Trans t, Unbox b) => [Sym] -> ID -> DAWG t 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, Trans t, Unbox b) => DAWG t a b c -> [([a], c)]-assocs d = map (first (map toEnum)) (assocs'I 0 d)-{-# SPECIALIZE assocs- :: (Trans t, Unbox b)- => DAWG t Char b c -> [(String, 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 -assocs'I :: (Trans t, Unbox b) => ID -> DAWG t a b c -> [([Sym], c)]-assocs'I i 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:)) (assocs'I j d)+ 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, Trans t, Unbox b) => DAWG t a b c -> [[a]]+keys :: (Enum a, Unbox b) => DAWG a b c -> [[a]] keys = map fst . assocs-{-# SPECIALIZE keys :: (Trans t, Unbox b) => DAWG t Char b c -> [String] #-}+{-# SPECIALIZE keys :: Unbox b => DAWG Char b c -> [String] #-} + -- | Return all elements of the DAWG in the ascending order of their keys.-elems :: (Trans t, Unbox b) => DAWG t a b c -> [c]-elems = map snd . assocs'I 0+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, D.MkNode t b)- => [([a], b)] -> DAWG t a () b+fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a () b fromList = freeze . D.fromList-{-# SPECIALIZE fromList- :: D.MkNode t b => [(String, b)] -> DAWG t Char () b #-}+{-# 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, D.MkNode t b)- => (b -> b -> b) -> [([a], b)] -> DAWG t a () b+fromListWith :: (Enum a, Ord b) => (b -> b -> b) -> [([a], b)] -> DAWG a () b fromListWith f = freeze . D.fromListWith f {-# SPECIALIZE fromListWith- :: D.MkNode t b => (b -> b -> b)- -> [(String, b)] -> DAWG t Char () b #-}+ :: 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, D.MkNode t ())- => [[a]] -> DAWG t a () ()+fromLang :: Enum a => [[a]] -> DAWG a () () fromLang = freeze . D.fromLang-{-# SPECIALIZE fromLang :: D.MkNode t () => [String] -> DAWG t Char () () #-}+{-# 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 :: Trans t => DAWG t a b c -> DAWG t a Weight c-weigh d = (DAWG . V.fromList)+-- 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@@ -207,13 +268,14 @@ -- Plain children and epsilon child. allChildren n = N.eps n : N.children n + -- | Construct immutable version of the automaton.-freeze :: Trans t => D.DAWG t a b -> DAWG t a () b-freeze d = DAWG . V.fromList $- map (N.reID newID . oldBy)+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.+ -- 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.@@ -221,12 +283,14 @@ -- 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 =@@ -242,7 +306,7 @@ -- -- New identifiers for value nodes. -- valIDs = foldl' updID GM.empty (values d) -- -- Values in the automaton.--- values = map value . V.toList . unDAWG+-- values = map value . V.toList . nodes -- -- Update ID map. -- updID m v = case GM.lookup v m of -- Just i -> m@@ -250,14 +314,38 @@ -- 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, Trans t) => [a] -> DAWG t a Weight c -> Maybe Int-index xs = index'I (map fromEnum xs) 0-{-# SPECIALIZE index- :: Trans t => String -> DAWG t Char Weight c -> Maybe Int #-}+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 :: Trans t => [Sym] -> ID -> DAWG t a 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@@ -266,20 +354,15 @@ 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, Trans t) => [a] -> DAWG t a Weight c -> Maybe Int-hash = index-{-# INLINE hash #-} -- | Find dictionary entry given its index with respect to the -- lexicographic order.-byIndex :: (Enum a, Trans t) => Int -> DAWG t a Weight c -> Maybe [a]-byIndex ix d = map toEnum <$> byIndex'I ix 0 d-{-# SPECIALIZE byIndex- :: Trans t => Int -> DAWG t Char Weight c -> Maybe String #-}+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 :: Trans t => Int -> ID -> DAWG t a Weight c -> Maybe [Sym]++byIndex'I :: Int -> ID -> DAWG a Weight c -> Maybe [Sym] byIndex'I ix i d | ix < 0 = Nothing | otherwise = here <|> there@@ -295,8 +378,3 @@ 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, Trans t) => Int -> DAWG t a Weight c -> Maybe [a]-unHash = byIndex-{-# INLINE unHash #-}
+ 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