packages feed

dawg 0.5.0 → 0.6.0

raw patch · 6 files changed

+574/−273 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.DAWG: instance (Ord a, Binary a) => Binary (DAWG a)
- Data.DAWG: instance Eq a => Eq (DAWG a)
- Data.DAWG: instance Ord a => Ord (DAWG a)
- Data.DAWG: instance Show a => Show (DAWG a)
- Data.DAWG.Graph: Branch :: {-# UNPACK #-} !Id -> !(VMap Id) -> Node a
- Data.DAWG.Graph: Graph :: !(Map (Node a) Id) -> !IntSet -> !(IntMap (Node a)) -> !(IntMap Int) -> Graph a
- Data.DAWG.Graph: Value :: !a -> Node a
- Data.DAWG.Graph: data Graph a
- Data.DAWG.Graph: data Node a
- Data.DAWG.Graph: delete :: Ord a => Node a -> Graph a -> Graph a
- Data.DAWG.Graph: edgeMap :: Node a -> !(VMap Id)
- Data.DAWG.Graph: edges :: Node a -> [(Char, Id)]
- Data.DAWG.Graph: empty :: Graph a
- Data.DAWG.Graph: eps :: Node a -> {-# UNPACK #-} !Id
- Data.DAWG.Graph: freeIDs :: Graph a -> !IntSet
- Data.DAWG.Graph: idMap :: Graph a -> !(Map (Node a) Id)
- Data.DAWG.Graph: ingoMap :: Graph a -> !(IntMap Int)
- Data.DAWG.Graph: insert :: Ord a => Node a -> Graph a -> (Id, Graph a)
- Data.DAWG.Graph: instance (Ord a, Binary a) => Binary (Graph a)
- Data.DAWG.Graph: instance Binary a => Binary (Node a)
- Data.DAWG.Graph: instance Eq a => Eq (Graph a)
- Data.DAWG.Graph: instance Eq a => Eq (Node a)
- Data.DAWG.Graph: instance Functor Node
- Data.DAWG.Graph: instance Ord a => Ord (Graph a)
- Data.DAWG.Graph: instance Ord a => Ord (Node a)
- Data.DAWG.Graph: instance Show a => Show (Graph a)
- Data.DAWG.Graph: instance Show a => Show (Node a)
- Data.DAWG.Graph: nodeBy :: Id -> Graph a -> Node a
- Data.DAWG.Graph: nodeID :: Ord a => Node a -> Graph a -> Id
- Data.DAWG.Graph: nodeMap :: Graph a -> !(IntMap (Node a))
- Data.DAWG.Graph: onChar :: Char -> Node a -> Maybe Id
- Data.DAWG.Graph: size :: Graph a -> Int
- Data.DAWG.Graph: subst :: Char -> Id -> Node a -> Node a
- Data.DAWG.Graph: type Id = Int
- Data.DAWG.Graph: unValue :: Node a -> !a
- Data.DAWG.VMap: instance (Binary a, Unbox a) => Binary (VMap a)
- Data.DAWG.VMap: instance (Eq a, Unbox a) => Eq (VMap a)
- Data.DAWG.VMap: instance (Ord a, Unbox a) => Ord (VMap a)
- Data.DAWG.VMap: instance (Show a, Unbox a) => Show (VMap a)
+ Data.DAWG: instance (Ord b, Binary b) => Binary (DAWG a b)
+ Data.DAWG: instance Eq b => Eq (DAWG a b)
+ Data.DAWG: instance Ord b => Ord (DAWG a b)
+ Data.DAWG: instance Show b => Show (DAWG a b)
+ Data.DAWG.Frozen: assocs :: Enum a => DAWG a b -> [([a], b)]
+ Data.DAWG.Frozen: byIndex :: Enum a => Int -> DAWG a b -> Maybe [a]
+ Data.DAWG.Frozen: elems :: DAWG a b -> [b]
+ Data.DAWG.Frozen: empty :: DAWG a b
+ Data.DAWG.Frozen: freeze :: DAWG a b -> DAWG a b
+ Data.DAWG.Frozen: fromLang :: Enum a => [[a]] -> DAWG a ()
+ Data.DAWG.Frozen: fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a b
+ Data.DAWG.Frozen: fromListWith :: (Enum a, Ord b) => (b -> b -> b) -> [([a], b)] -> DAWG a b
+ Data.DAWG.Frozen: hash :: Enum a => [a] -> DAWG a b -> Maybe Int
+ Data.DAWG.Frozen: index :: Enum a => [a] -> DAWG a b -> Maybe Int
+ Data.DAWG.Frozen: instance Binary a => Binary (Node a)
+ Data.DAWG.Frozen: instance Eq a => Eq (Node a)
+ Data.DAWG.Frozen: instance Ord a => Ord (Node a)
+ Data.DAWG.Frozen: instance Show a => Show (Node a)
+ Data.DAWG.Frozen: keys :: Enum a => DAWG a b -> [[a]]
+ Data.DAWG.Frozen: lookup :: Enum a => [a] -> DAWG a b -> Maybe b
+ Data.DAWG.Frozen: numStates :: DAWG a b -> Int
+ Data.DAWG.Frozen: type DAWG a b = Vector (Node (Maybe b))
+ Data.DAWG.Frozen: unHash :: Enum a => Int -> DAWG a b -> Maybe [a]
+ Data.DAWG.Internal: Branch :: {-# UNPACK #-} !Id -> !VMap -> Node a
+ Data.DAWG.Internal: Graph :: !(Map (Node a) Id) -> !IntSet -> !(IntMap (Node a)) -> !(IntMap Int) -> Graph a
+ Data.DAWG.Internal: Value :: !a -> Node a
+ Data.DAWG.Internal: data Graph a
+ Data.DAWG.Internal: data Node a
+ Data.DAWG.Internal: delete :: Ord a => Node a -> Graph a -> Graph a
+ Data.DAWG.Internal: edgeMap :: Node a -> !VMap
+ Data.DAWG.Internal: edges :: Node a -> [(Int, Id)]
+ Data.DAWG.Internal: empty :: Graph a
+ Data.DAWG.Internal: eps :: Node a -> {-# UNPACK #-} !Id
+ Data.DAWG.Internal: freeIDs :: Graph a -> !IntSet
+ Data.DAWG.Internal: idMap :: Graph a -> !(Map (Node a) Id)
+ Data.DAWG.Internal: ingoMap :: Graph a -> !(IntMap Int)
+ Data.DAWG.Internal: insert :: Ord a => Node a -> Graph a -> (Id, Graph a)
+ Data.DAWG.Internal: instance (Ord a, Binary a) => Binary (Graph a)
+ Data.DAWG.Internal: instance Binary a => Binary (Node a)
+ Data.DAWG.Internal: instance Eq a => Eq (Graph a)
+ Data.DAWG.Internal: instance Eq a => Eq (Node a)
+ Data.DAWG.Internal: instance Functor Node
+ Data.DAWG.Internal: instance Ord a => Ord (Graph a)
+ Data.DAWG.Internal: instance Ord a => Ord (Node a)
+ Data.DAWG.Internal: instance Show a => Show (Graph a)
+ Data.DAWG.Internal: instance Show a => Show (Node a)
+ Data.DAWG.Internal: nodeBy :: Id -> Graph a -> Node a
+ Data.DAWG.Internal: nodeID :: Ord a => Node a -> Graph a -> Id
+ Data.DAWG.Internal: nodeMap :: Graph a -> !(IntMap (Node a))
+ Data.DAWG.Internal: onSym :: Int -> Node a -> Maybe Id
+ Data.DAWG.Internal: size :: Graph a -> Int
+ Data.DAWG.Internal: subst :: Int -> Id -> Node a -> Node a
+ Data.DAWG.Internal: type Id = Int
+ Data.DAWG.Internal: unValue :: Node a -> !a
+ Data.DAWG.VMap: instance Binary VMap
+ Data.DAWG.VMap: instance Eq VMap
+ Data.DAWG.VMap: instance Ord VMap
+ Data.DAWG.VMap: instance Show VMap
- Data.DAWG: DAWG :: !(Graph (Maybe a)) -> !Id -> DAWG a
+ Data.DAWG: DAWG :: !(Graph (Maybe b)) -> !Id -> DAWG a b
- Data.DAWG: assocs :: DAWG a -> [(String, a)]
+ Data.DAWG: assocs :: Enum a => DAWG a b -> [([a], b)]
- Data.DAWG: data DAWG a
+ Data.DAWG: data DAWG a b
- Data.DAWG: delete :: Ord a => String -> DAWG a -> DAWG a
+ Data.DAWG: delete :: (Enum a, Ord b) => [a] -> DAWG a b -> DAWG a b
- Data.DAWG: elems :: DAWG a -> [a]
+ Data.DAWG: elems :: DAWG a b -> [b]
- Data.DAWG: empty :: Ord a => DAWG a
+ Data.DAWG: empty :: Ord b => DAWG a b
- Data.DAWG: fromLang :: [String] -> DAWG ()
+ Data.DAWG: fromLang :: Enum a => [[a]] -> DAWG a ()
- Data.DAWG: fromList :: Ord a => [(String, a)] -> DAWG a
+ Data.DAWG: fromList :: (Enum a, Ord b) => [([a], b)] -> DAWG a b
- Data.DAWG: fromListWith :: Ord a => (a -> a -> a) -> [(String, a)] -> DAWG a
+ Data.DAWG: fromListWith :: (Enum a, Ord b) => (b -> b -> b) -> [([a], b)] -> DAWG a b
- Data.DAWG: graph :: DAWG a -> !(Graph (Maybe a))
+ Data.DAWG: graph :: DAWG a b -> !(Graph (Maybe b))
- Data.DAWG: insert :: Ord a => String -> a -> DAWG a -> DAWG a
+ Data.DAWG: insert :: (Enum a, Ord b) => [a] -> b -> DAWG a b -> DAWG a b
- Data.DAWG: insertWith :: Ord a => (a -> a -> a) -> String -> a -> DAWG a -> DAWG a
+ Data.DAWG: insertWith :: (Enum a, Ord b) => (b -> b -> b) -> [a] -> b -> DAWG a b -> DAWG a b
- Data.DAWG: keys :: DAWG a -> [String]
+ Data.DAWG: keys :: Enum a => DAWG a b -> [[a]]
- Data.DAWG: lookup :: String -> DAWG a -> Maybe a
+ Data.DAWG: lookup :: Enum a => [a] -> DAWG a b -> Maybe b
- Data.DAWG: numStates :: DAWG a -> Int
+ Data.DAWG: numStates :: DAWG a b -> Int
- Data.DAWG: root :: DAWG a -> !Id
+ Data.DAWG: root :: DAWG a b -> !Id
- Data.DAWG.VMap: data VMap a
+ Data.DAWG.VMap: data VMap
- Data.DAWG.VMap: empty :: Unbox a => VMap a
+ Data.DAWG.VMap: empty :: VMap
- Data.DAWG.VMap: fromList :: Unbox a => [(Char, a)] -> VMap a
+ Data.DAWG.VMap: fromList :: [(Int, Int)] -> VMap
- Data.DAWG.VMap: insert :: Unbox a => Char -> a -> VMap a -> VMap a
+ Data.DAWG.VMap: insert :: Int -> Int -> VMap -> VMap
- Data.DAWG.VMap: lookup :: Unbox a => Char -> VMap a -> Maybe a
+ Data.DAWG.VMap: lookup :: Int -> VMap -> Maybe Int
- Data.DAWG.VMap: toList :: Unbox a => VMap a -> [(Char, a)]
+ Data.DAWG.VMap: toList :: VMap -> [(Int, Int)]

Files

Data/DAWG.hs view
@@ -1,5 +1,5 @@--- | The module provides implementation of /directed acyclic word graphs/--- (DAWGs) also known as /minimal acyclic finite-state automata/.+-- | 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. @@ -12,18 +12,18 @@ , lookup -- * Construction , empty+, fromList+, fromListWith+, fromLang -- ** Insertion , insert , insertWith -- ** Deletion , delete -- * Conversion-, elems-, keys , assocs-, fromList-, fromListWith-, fromLang+, keys+, elems ) where  import Prelude hiding (lookup)@@ -33,11 +33,11 @@ import Data.Binary (Binary, put, get) import qualified Control.Monad.State.Strict as S -import Data.DAWG.Graph (Id, Node, Graph)-import qualified Data.DAWG.Graph as G+import Data.DAWG.Internal (Id, Node, Graph)+import qualified Data.DAWG.Internal as I import qualified Data.DAWG.VMap as V -type GraphM a b = S.State  (Graph (Maybe a)) b+type GraphM a b = S.State (Graph (Maybe a)) b  mkState :: (Graph a -> Graph a) -> Graph a -> ((), Graph a) mkState f g = ((), f g)@@ -45,172 +45,194 @@ -- | Leaf node with no children and 'Nothing' value. insertLeaf :: Ord a => GraphM a Id  insertLeaf = do-    i <- insertNode (G.Value Nothing)-    insertNode (G.Branch i V.empty)+    i <- insertNode (I.Value Nothing)+    insertNode (I.Branch i V.empty)  -- | Return node with the given identifier. nodeBy :: Id -> GraphM a (Node (Maybe a))-nodeBy i = G.nodeBy i <$> S.get+nodeBy i = I.nodeBy i <$> S.get --- Evaluate the 'G.insert' function within the monad.+-- Evaluate the 'I.insert' function within the monad. insertNode :: Ord a => Node (Maybe a) -> GraphM a Id-insertNode = S.state . G.insert+insertNode = S.state . I.insert --- Evaluate the 'G.delete' function within the monad.+-- Evaluate the 'I.delete' function within the monad. deleteNode :: Ord a => Node (Maybe a) -> GraphM a ()-deleteNode = S.state . mkState . G.delete+deleteNode = S.state . mkState . I.delete  -- | Invariant: the identifier points to the 'Branch' node.-insertM :: Ord a => String -> a -> Id -> GraphM a Id+insertM :: Ord a => [Int] -> a -> Id -> GraphM a Id insertM (x:xs) y i = do     n <- nodeBy i-    j <- case G.onChar x n of+    j <- case I.onSym x n of         Just j  -> return j         Nothing -> insertLeaf     k <- insertM xs y j     deleteNode n-    insertNode (G.subst x k n)+    insertNode (I.subst x k n) insertM [] y i = do     n <- nodeBy i-    w <- nodeBy (G.eps n)+    w <- nodeBy (I.eps n)     deleteNode w     deleteNode n-    j <- insertNode (G.Value $ Just y)-    insertNode (n { G.eps = j })+    j <- insertNode (I.Value $ Just y)+    insertNode (n { I.eps = j }) -insertWithM :: Ord a => (a -> a -> a) -> String -> a -> Id -> GraphM a Id+insertWithM :: Ord a => (a -> a -> a) -> [Int] -> a -> Id -> GraphM a Id insertWithM f (x:xs) y i = do     n <- nodeBy i-    j <- case G.onChar x n of+    j <- case I.onSym x n of         Just j  -> return j         Nothing -> insertLeaf     k <- insertWithM f xs y j     deleteNode n-    insertNode (G.subst x k n)+    insertNode (I.subst x k n) insertWithM f [] y i = do     n <- nodeBy i-    w <- nodeBy (G.eps n)+    w <- nodeBy (I.eps n)     deleteNode w     deleteNode n-    let y'new = case G.unValue w of+    let y'new = case I.unValue w of             Just y' -> f y y'             Nothing -> y-    j <- insertNode (G.Value $ Just y'new)-    insertNode (n { G.eps = j })+    j <- insertNode (I.Value $ Just y'new)+    insertNode (n { I.eps = j }) -deleteM :: Ord a => String -> Id -> GraphM a Id+deleteM :: Ord a => [Int] -> Id -> GraphM a Id deleteM (x:xs) i = do     n <- nodeBy i-    case G.onChar x n of+    case I.onSym x n of         Nothing -> return i         Just j  -> do             k <- deleteM xs j             deleteNode n-            insertNode (G.subst x k n)+            insertNode (I.subst x k n) deleteM [] i = do     n <- nodeBy i-    w <- nodeBy (G.eps n)+    w <- nodeBy (I.eps n)     deleteNode w     deleteNode n     j <- insertLeaf-    insertNode (n { G.eps = j })+    insertNode (n { I.eps = j })     -lookupM :: String -> Id -> GraphM a (Maybe a)+lookupM :: [Int] -> Id -> GraphM a (Maybe a) lookupM [] i = do-    j <- G.eps <$> nodeBy i-    G.unValue <$> nodeBy j+    j <- I.eps <$> nodeBy i+    I.unValue <$> nodeBy j lookupM (x:xs) i = do     n <- nodeBy i-    case G.onChar x n of+    case I.onSym x n of         Just j  -> lookupM xs j         Nothing -> return Nothing -assocsAcc :: Graph (Maybe a) -> Id -> [(String, a)]+assocsAcc :: Graph (Maybe a) -> Id -> [([Int], a)] assocsAcc g i =-    here w ++ concatMap there (G.edges n)+    here w ++ concatMap there (I.edges n)   where-    n = G.nodeBy i g-    w = G.nodeBy (G.eps n) g-    here v = case G.unValue v of-        Just x  -> [("", x)]+    n = I.nodeBy i g+    w = I.nodeBy (I.eps n) g+    here v = case I.unValue v of+        Just x  -> [([], x)]         Nothing -> []-    there (char, j) = map (first (char:)) (assocsAcc g j)+    there (sym, j) = map (first (sym:)) (assocsAcc g j) --- | A 'G.Graph' with one root from which all other graph nodes should--- be accesible.-data DAWG a = DAWG-    { graph :: !(Graph (Maybe a))+-- | A 'I.Graph' with one root from which all other graph nodes should+-- be accesible.  Parameter @a@ is a phantom parameter which represents+-- symbol type.+data DAWG a b = DAWG+    { graph :: !(Graph (Maybe b))     , root  :: !Id }     deriving (Show, Eq, Ord) -instance (Ord a, Binary a) => Binary (DAWG a) where+instance (Ord b, Binary b) => Binary (DAWG a b) where     put d = do         put (graph d)         put (root d)     get = DAWG <$> get <*> get  -- | Empty DAWG.-empty :: Ord a => DAWG a+empty :: Ord b => DAWG a b empty = -    let (i, g) = S.runState insertLeaf G.empty+    let (i, g) = S.runState insertLeaf I.empty     in  DAWG g i  -- | Number of states in the underlying graph.-numStates :: DAWG a -> Int-numStates = G.size . graph+numStates :: DAWG a b -> Int+numStates = I.size . graph  -- | Insert the (key, value) pair into the DAWG.-insert :: Ord a => String -> a -> DAWG a -> DAWG a-insert xs y d =-    let (i, g) = S.runState (insertM xs y $ root d) (graph d)+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+{-# 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 :: Ord a => (a -> a -> a) -> String -> a -> DAWG a -> DAWG a-insertWith f xs y d =-    let (i, g) = S.runState (insertWithM f xs y $ root d) (graph d)+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 :: Ord a => String -> DAWG a -> DAWG a-delete xs d =-    let (i, g) = S.runState (deleteM xs $ root d) (graph d)+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 :: String -> DAWG a -> Maybe a-lookup xs d = S.evalState (lookupM xs $ root d) (graph d)+lookup :: Enum a => [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 :: String -> DAWG Char b -> Maybe b #-} +-- | Return all key/value pairs in the DAWG in ascending key order.+assocs :: Enum a => DAWG a b -> [([a], b)]+assocs+    = map (first (map toEnum))+    . (assocsAcc <$> graph <*> root)+{-# SPECIALIZE assocs :: DAWG Char b -> [(String, b)] #-}+ -- | Return all keys of the DAWG in ascending order.-keys :: DAWG a -> [String]+keys :: Enum a => DAWG a b -> [[a]] keys = map fst . assocs+{-# SPECIALIZE keys :: DAWG Char b -> [String] #-}  -- | Return all elements of the DAWG in the ascending order of their keys.-elems :: DAWG a -> [a]-elems = map snd . assocs---- | Return all key/value pairs in the DAWG in ascending key order.-assocs :: DAWG a -> [(String, a)]-assocs d = assocsAcc (graph d) (root d)+elems :: DAWG a b -> [b]+elems = map snd . (assocsAcc <$> graph <*> root)  -- | Construct DAWG from the list of (word, value) pairs.-fromList :: Ord a => [(String, a)] -> DAWG a+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+{-# 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 :: Ord a => (a -> a -> a) -> [(String, a)] -> DAWG a+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 :: [String] -> DAWG ()+fromLang :: Enum a => [[a]] -> DAWG a () fromLang xs = fromList [(x, ()) | x <- xs]+{-# SPECIALIZE fromLang :: [String] -> DAWG Char () #-}
+ Data/DAWG/Frozen.hs view
@@ -0,0 +1,267 @@+{-# LANGUAGE RecordWildCards #-}++-- | 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:+--+--   * Keeps all nodes in one array and therefore uses much less memory,+--+--   * Constitutes a /perfect hash automaton/ with 'hash' and+--     'unHash' functions,+--+--   * Doesn't provide insert/delete family of operations.++module Data.DAWG.Frozen+(+-- * DAWG type+  DAWG+-- * Query+, lookup+, numStates+-- * Index+, index+, byIndex+-- ** Hashing+, hash+, unHash+-- * Construction+, empty+, fromList+, fromListWith+, fromLang+-- * Conversion+, assocs+, keys+, elems+, freeze+) where++import Prelude hiding (lookup)+import Control.Applicative (pure, (<$), (<$>), (<*>))+import Control.Arrow (first, second)+import Data.Binary (Binary, put, get)+import Data.Vector.Binary ()+import qualified Data.IntMap as M+import qualified Data.Vector as V++import qualified Data.DAWG.VMap as VM+import qualified Data.DAWG.Internal as I+import qualified Data.DAWG as D++-- | Node identifier.+type Id = Int++-- | State (node) of the automaton.+data Node a = Node {+    -- | Value kept in the node.+      value :: !a+    -- | Number of accepting states reachable from the node.+    , size  :: {-# UNPACK #-} !Int+    -- | Edges outgoing from the node.+    , edges :: !VM.VMap }+    deriving (Show, Eq, Ord)++instance Binary a => Binary (Node a) where+    put Node{..} = put value >> put size >> put edges+    get = Node <$> get <*> get <*> get++-- | Identifier of the child determined by the given symbol.+onSym :: Int -> Node a -> Maybe Id+onSym x (Node _ _ es) = VM.lookup x es++-- List of edges from the node.+edgeList :: Node a -> [(Int, Id)]+edgeList = VM.toList . edges++-- | List children identifiers.+children :: Node a -> [Id]+children = map snd . edgeList++-- | Root is stored on the first position of the array.+type DAWG a b = V.Vector (Node (Maybe b))++-- | Empty DAWG.+empty :: DAWG a b+empty = V.singleton (Node Nothing 0 VM.empty)++-- | Number of states in the automaton.+numStates :: DAWG a b -> Int+numStates = V.length++-- | Node with the given identifier.+nodeBy :: Id -> DAWG a b -> Node (Maybe b)+nodeBy i d = d V.! i++-- | Find value associated with the key.+lookup :: Enum a => [a] -> DAWG a b -> Maybe b+lookup xs' =+    let xs = map fromEnum xs'+    in  lookup'I xs 0+{-# SPECIALIZE lookup :: String -> DAWG Char b -> Maybe b #-}++lookup'I :: [Int] -> Id -> DAWG a b -> Maybe b+lookup'I []     i d = value (nodeBy i d)+lookup'I (x:xs) i d = case 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 => DAWG a b -> [([a], b)]+assocs d = map (first (map toEnum)) (assocs'I 0 d)+{-# SPECIALIZE assocs :: DAWG Char b -> [(String, b)] #-}++assocs'I :: Id -> DAWG a b -> [([Int], b)]+assocs'I i d =+    here ++ concatMap there (VM.toList (edges n))+  where+    n = nodeBy i d+    here = case value n of+        Just x  -> [([], x)]+        Nothing -> []+    there (sym, j) = map (first (sym:)) (assocs'I j d)++-- | Return all keys of the DAWG in ascending order.+keys :: Enum a => DAWG a b -> [[a]]+keys = map fst . assocs+{-# SPECIALIZE keys :: DAWG Char b -> [String] #-}++-- | Return all elements of the DAWG in the ascending order of their keys.+elems :: DAWG a b -> [b]+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 () #-}++-- | Recursively compute sizes of nodes. +detSize :: DAWG a b -> DAWG a b+detSize d = V.fromList+    [ (nodeBy i d) { size = mem i }+    | i <- [0 .. numStates d - 1] ]+  where+    add w x = maybe 0 (const 1) w + sum x+    mem     = ((V.!) . V.fromList) (map det [0 .. numStates d - 1])+    det i   =+        let n = nodeBy i d+            js = children n+        in  add (value n) (map mem js)++-- | Yield immutable version of the automaton.+freeze :: D.DAWG a b -> DAWG a b+freeze d = detSize . V.fromList $+    map (stop . oldBy) (M.elems (inverse old2new))+  where+    -- Map from old to new identifiers.+    old2new = M.fromList $ (D.root d, 0) : zip (nodeIDs d) [1..]+    -- List of non-frozen branches' IDs without the root ID.+    nodeIDs = filter (/= D.root d) . branchIDs+    -- Make frozen node with new IDs from non-frozen node.+    stop    = Node <$> onEps <*> pure 0 <*> mkEdges . I.edgeMap+    -- Extract value following the epsilon transition.+    onEps   = I.unValue . oldBy . I.eps+    -- List of edges with new IDs.+    mkEdges = VM.fromList . map (second (old2new M.!)) . VM.toList +    -- Non-frozen node by given identifier.+    oldBy i = I.nodeBy i (D.graph d)++-- | Branch IDs in the non-frozen DAWG.+branchIDs :: D.DAWG a b -> [I.Id]+branchIDs+    = map fst . filter (isBranch . snd)+    . M.assocs . I.nodeMap . D.graph+  where+    isBranch (I.Branch _ _) = True+    isBranch _              = False+        +-- | 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 a 'D.DAWG' version of the automaton.+-- thaw :: DAWG a b -> D.DAWG a b+-- thaw d =+--     D.DAWG graph 0+--   where+--     graph = I.Graph+--         (Map.fromList $ zip nodes [0..])+--         IS.empty+--         (M.fromList   $ zip [0..] nodes)+--         (++-- | Position in a set of all dictionary entries with respect+-- to the lexicographic order.+index :: Enum a => [a] -> DAWG a b -> Maybe Int+index xs = index'I (map fromEnum xs) 0+{-# SPECIALIZE index :: String -> DAWG Char b -> Maybe Int #-}++index'I :: [Int] -> Id -> DAWG a b -> Maybe Int+index'I []     i d = 0 <$ value (nodeBy i d)+index'I (x:xs) i d = case onSym x n of+    Just j  -> do+        x0 <- index'I xs j d+        let x1 = maybe 0 (const 1) (value n)+               + (sum . map sizeBy) (before (x, j))+        return $ x0 + x1+    Nothing -> Nothing+  where+    -- Current node.+    n = nodeBy i d+    -- Size of node by ID.+    sizeBy = size . flip nodeBy d+    -- All childresn IDs before the (x, j) edge.+    before e = map snd . fst $ span (/=e) (edgeList n)++-- | Perfect hashing function for dictionary entries.+-- A synonym for the 'index' function.+hash :: Enum a => [a] -> DAWG a b -> Maybe Int+hash = index+{-# INLINE hash #-}++-- | Find dictionary entry given its index with respect to the+-- lexicographic order.+byIndex :: Enum a => Int -> DAWG a b -> Maybe [a]+byIndex i d = map toEnum <$> byIndex'I i 0 d+{-# SPECIALIZE byIndex :: Int -> DAWG Char b -> Maybe String #-}++byIndex'I :: Int -> Id -> DAWG a b -> Maybe [Int]+byIndex'I ix i d = do+    (acc, x, j) <- findChild 0 (edgeList n)+    xs <- byIndex'I (ix - acc) j d+    return (x:xs)+  where+    -- Current node.+    n   = nodeBy i d+    -- Size of node by ID.+    sizeBy = size . flip nodeBy d+    -- Sum node size values and find the appropriate one.+    findChild acc ((x, j) : js)+        | acc < ix  = findChild (acc + sizeBy j) js+        | otherwise = Just (acc, x, j)+    findChild _ []  = Nothing++-- | Inverse of the 'hash' function and a synonym for the 'byIndex' function.+unHash :: Enum a => Int -> DAWG a b -> Maybe [a]+unHash = byIndex+{-# INLINE unHash #-}
− Data/DAWG/Graph.hs
@@ -1,186 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | The module provides a representation of a tree where all equivalent nodes--- (i.e. trees equal with respect to the '==' function) are compressed to one--- /directed acyclic graph/ (DAG) node with unique identifier.  Alternatively,--- it can be thought of as a /minimal acyclic finite-state automata/.--module Data.DAWG.Graph-( --- * Node-  Node (..)-, Id-, edges-, onChar-, subst--- * Graph-, Graph (..)-, empty-, size-, nodeBy-, nodeID-, insert-, delete-) where--import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, Get, put, get)-import qualified Data.Map as M-import qualified Data.IntSet as IS-import qualified Data.IntMap as IM--import qualified Data.DAWG.VMap as V---- | Node identifier.-type Id = Int---- | 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.------ Invariant: the 'value' identifier always points to the 'Value' node.--- Edges in the 'edgeMap', on the other hand, point to 'Branch' nodes.-data Node a-    = Branch-        { eps       :: {-# UNPACK #-} !Id-        , edgeMap   :: !(V.VMap Id) }-    | Value-        { unValue :: !a }-    deriving (Show, Eq, Ord)--instance Functor Node where-    fmap f (Value x) = Value (f x)-    fmap _ (Branch x y) = Branch x y--instance Binary a => Binary (Node a) where-    put Branch{..} = put (1 :: Int) >> put eps >> put edgeMap-    put Value{..}  = put (2 :: Int) >> put unValue-    get = do-        x <- get :: Get Int-        case x of-            1 -> Branch <$> get <*> get-            _ -> Value <$> get---- | List of non-epsilon edges outgoing from the 'Branch' node.-edges :: Node a -> [(Char, Id)]-edges (Branch _ es)     = V.toList es-edges (Value _)         = error "edges: value node"---- | Identifier of the child determined by the given character.-onChar :: Char -> Node a -> Maybe Id-onChar x (Branch _ es)  = V.lookup x es-onChar _ (Value _)      = error "onChar: value node"---- | Substitue the identifier of the child determined by the given--- character.-subst :: Char -> Id -> Node a -> Node a-subst x i (Branch w es) = Branch w (V.insert x i es)-subst _ _ (Value _)     = error "subst: value node"---- | 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 save some memory?-data Graph a = Graph {-    -- | Map from nodes to IDs.-      idMap     :: !(M.Map (Node a) Id)-    -- | Set of free IDs.-    , freeIDs   :: !IS.IntSet-    -- | Map from IDs to nodes. -    , nodeMap   :: !(IM.IntMap (Node a))-    -- | 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   :: !(IM.IntMap Int) }-    deriving (Show, Eq, Ord)--instance (Ord a, Binary a) => Binary (Graph a) where-    put Graph{..} = do-    	put idMap-	put freeIDs-	put nodeMap-	put ingoMap-    get = Graph <$> get <*> get <*> get <*> get---- | Empty graph.-empty :: Graph a-empty = Graph M.empty IS.empty IM.empty IM.empty---- | Size of the graph (number of nodes).-size :: Graph a -> Int-size = M.size . idMap---- | Node with the given identifier.-nodeBy :: Id -> Graph a -> Node a-nodeBy i g = nodeMap g IM.! i---- | Retrieve the node identifier.-nodeID :: Ord a => Node a -> Graph a -> Id-nodeID n g = idMap g M.! n---- | Add new graph node.-newNode :: Ord a => Node a -> Graph a -> (Id, Graph a)-newNode n Graph{..} =-    (i, Graph idMap' freeIDs' nodeMap' ingoMap')-  where-    idMap'      = M.insert  n i idMap-    nodeMap'    = IM.insert i n nodeMap-    ingoMap'    = IM.insert i 1 ingoMap-    (i, freeIDs') = if IS.null freeIDs-        then (M.size idMap, freeIDs)-        else IS.deleteFindMin freeIDs---- | Remove node from the graph.-remNode :: Ord a => Id -> Graph a -> Graph a-remNode i Graph{..} =-    Graph idMap' freeIDs' nodeMap' ingoMap'-  where-    idMap'      = M.delete  n idMap-    nodeMap'    = IM.delete i nodeMap-    ingoMap'    = IM.delete i ingoMap-    freeIDs'    = IS.insert i freeIDs-    n           = nodeMap IM.! i---- | Increment the number of ingoing paths.-incIngo :: Id -> Graph a -> Graph a-incIngo i g = g { ingoMap = IM.insertWith' (+) i 1 (ingoMap g) }---- | Descrement the number of ingoing paths and return--- the resulting number.-decIngo :: Id -> Graph a -> (Int, Graph a)-decIngo i g =-    let k = (ingoMap g IM.! i) - 1-    in  (k, g { ingoMap = IM.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--- ancestors of the node, so the operation alone will not ensure--- that properties of the graph are preserved.-insert :: Ord a => Node a -> Graph a -> (Id, Graph a)-insert n g = case M.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.--- NOTE: The function does not delete descendant nodes which may become--- inaccesible nor does it change the number of ingoing paths for any--- ancestor of the node.-delete :: Ord a => Node a -> Graph a -> Graph a-delete n g = if num == 0-    then remNode i g'-    else g'-  where-    i = nodeID n g-    (num, g') = decIngo i g
+ Data/DAWG/Internal.hs view
@@ -0,0 +1,192 @@+{-# LANGUAGE RecordWildCards #-}++-- | 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.Internal+( +-- * Node+  Node (..)+, Id+, edges+, onSym+, subst+-- * Graph+, Graph (..)+, empty+, size+, nodeBy+, nodeID+, insert+, delete+) where++import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, Get, put, get)+import qualified Data.Map as M+import qualified Data.IntSet as IS+import qualified Data.IntMap as IM++import qualified Data.DAWG.VMap as V++-- | Node identifier.+type Id = Int++-- | 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 'Value' nodes are distinguished from 'Branch' nodes, two values+-- equal with respect to '==' function are always kept in one 'Value'+-- 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 'value' identifier always points to the 'Value' node.+-- Edges in the 'edgeMap', on the other hand, point to 'Branch' nodes.+data Node a+    = Branch {+        -- | Epsilon transition.+          eps       :: {-# UNPACK #-} !Id+        -- | Map from alphabet symbols to 'Branch' node identifiers.+        , edgeMap   :: !V.VMap }+    | Value+        { unValue :: !a }+    deriving (Show, Eq, Ord)++instance Functor Node where+    fmap f (Value x) = Value (f x)+    fmap _ (Branch x y) = Branch x y++instance Binary a => Binary (Node a) where+    put Branch{..} = put (1 :: Int) >> put eps >> put edgeMap+    put Value{..}  = put (2 :: Int) >> put unValue+    get = do+        x <- get :: Get Int+        case x of+            1 -> Branch <$> get <*> get+            _ -> Value <$> get++-- | List of non-epsilon edges outgoing from the 'Branch' node.+edges :: Node a -> [(Int, Id)]+edges (Branch _ es)     = V.toList es+edges (Value _)         = error "edges: value node"++-- | Identifier of the child determined by the given symbol.+onSym :: Int -> Node a -> Maybe Id+onSym x (Branch _ es) = V.lookup x es+onSym _ (Value _)     = error "onSym: value node"++-- | Substitue the identifier of the child determined by the given symbol.+subst :: Int -> Id -> Node a -> Node a+subst x i (Branch w es) = Branch w (V.insert x i es)+subst _ _ (Value _)     = error "subst: value node"++-- | 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 a = Graph {+    -- | Map from nodes to IDs.+      idMap     :: !(M.Map (Node a) Id)+    -- | Set of free IDs.+    , freeIDs   :: !IS.IntSet+    -- | Map from IDs to nodes. +    , nodeMap   :: !(IM.IntMap (Node a))+    -- | 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   :: !(IM.IntMap Int) }+    deriving (Show, Eq, Ord)++instance (Ord a, Binary a) => Binary (Graph a) where+    put Graph{..} = do+    	put idMap+	put freeIDs+	put nodeMap+	put ingoMap+    get = Graph <$> get <*> get <*> get <*> get++-- | Empty graph.+empty :: Graph a+empty = Graph M.empty IS.empty IM.empty IM.empty++-- | Size of the graph (number of nodes).+size :: Graph a -> Int+size = M.size . idMap++-- | Node with the given identifier.+nodeBy :: Id -> Graph a -> Node a+nodeBy i g = nodeMap g IM.! i++-- | Retrieve the node identifier.+nodeID :: Ord a => Node a -> Graph a -> Id+nodeID n g = idMap g M.! n++-- | Add new graph node.+newNode :: Ord a => Node a -> Graph a -> (Id, Graph a)+newNode n Graph{..} =+    (i, Graph idMap' freeIDs' nodeMap' ingoMap')+  where+    idMap'      = M.insert  n i idMap+    nodeMap'    = IM.insert i n nodeMap+    ingoMap'    = IM.insert i 1 ingoMap+    (i, freeIDs') = if IS.null freeIDs+        then (M.size idMap, freeIDs)+        else IS.deleteFindMin freeIDs++-- | Remove node from the graph.+remNode :: Ord a => Id -> Graph a -> Graph a+remNode i Graph{..} =+    Graph idMap' freeIDs' nodeMap' ingoMap'+  where+    idMap'      = M.delete  n idMap+    nodeMap'    = IM.delete i nodeMap+    ingoMap'    = IM.delete i ingoMap+    freeIDs'    = IS.insert i freeIDs+    n           = nodeMap IM.! i++-- | Increment the number of ingoing paths.+incIngo :: Id -> Graph a -> Graph a+incIngo i g = g { ingoMap = IM.insertWith' (+) i 1 (ingoMap g) }++-- | Decrement the number of ingoing paths and return+-- the resulting number.+decIngo :: Id -> Graph a -> (Int, Graph a)+decIngo i g =+    let k = (ingoMap g IM.! i) - 1+    in  (k, g { ingoMap = IM.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 :: Ord a => Node a -> Graph a -> (Id, Graph a)+insert n g = case M.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.+-- 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 :: Ord a => Node a -> Graph a -> Graph a+delete n g = if num == 0+    then remNode i g'+    else g'+  where+    i = nodeID n g+    (num, g') = decIngo i g
Data/DAWG/VMap.hs view
@@ -18,27 +18,27 @@  -- | A strictly ascending vector of distinct elements with respect -- to 'fst' values.-newtype VMap a = VMap { unVMap :: U.Vector (Char, a) }+newtype VMap = VMap { unVMap :: U.Vector (Int, Int) }     deriving (Show, Eq, Ord) -instance (Binary a, U.Unbox a) => Binary (VMap a) where+instance Binary VMap where     put v = put (unVMap v)     get = VMap <$> get  -- | Empty map.-empty :: U.Unbox a => VMap a+empty :: VMap empty = VMap U.empty {-# INLINE empty #-}  -- | Lookup the character in the map.-lookup :: U.Unbox a => Char -> VMap a -> Maybe a+lookup :: Int -> VMap -> Maybe Int lookup x = fmap snd . U.find ((==x) . fst) . unVMap {-# INLINE lookup #-}  -- | Insert the character/value pair into the map. -- TODO: Optimize!  Use the invariant, that VMap is -- kept in an ascending vector.-insert :: U.Unbox a => Char -> a -> VMap a -> VMap a+insert :: Int -> Int -> VMap -> VMap insert x y     = VMap . U.fromList . M.toAscList     . M.insert x y@@ -47,11 +47,11 @@  -- | Smart 'VMap' constructor which ensures that the underlying vector is -- strictly ascending with respect to 'fst' values.-fromList :: U.Unbox a => [(Char, a)] -> VMap a+fromList :: [(Int, Int)] -> VMap fromList = VMap . U.fromList . M.toAscList  . M.fromList  {-# INLINE fromList #-}  -- | Convert the 'VMap' to a list of ascending character/value pairs.-toList :: U.Unbox a => VMap a -> [(Char, a)]+toList :: VMap -> [(Int, Int)] toList = U.toList . unVMap {-# INLINE toList #-}
dawg.cabal view
@@ -1,9 +1,14 @@ name:               dawg-version:            0.5.0+version:            0.6.0 synopsis:           Directed acyclic word graphs description:-    The library implements /directed acyclic word graphs/ (DAWGs), which can-    be also interpreted as /minimal acyclic finite-state automata/.+    The library implements /directed acyclic word graphs/ (DAWGs) internaly+    represented as /minimal acyclic deterministic finite-state automata/.+    .+    The "Data.DAWG" module provides fast insert and delete operations which+    can be used to build the automaton on-the-fly.+    Automaton from the "Data.DAWG.Frozen" module is ,,immutable'', but it+    has lower memory footprint and provides perfect hashing functionality. license:            BSD3 license-file:       LICENSE cabal-version:      >= 1.6@@ -26,7 +31,8 @@      exposed-modules:         Data.DAWG-      , Data.DAWG.Graph+      , Data.DAWG.Frozen+      , Data.DAWG.Internal       , Data.DAWG.VMap      ghc-options: -Wall -O2