packages feed

dawg 0.7.0 → 0.7.1

raw patch · 7 files changed

+444/−268 lines, 7 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.DAWG.Internal: Branch :: {-# UNPACK #-} !Id -> !(VMap Id) -> Node a
- Data.DAWG.Internal: Value :: !a -> Node a
- Data.DAWG.Internal: data Node a
- Data.DAWG.Internal: edgeMap :: Node a -> !(VMap Id)
- Data.DAWG.Internal: edges :: Node a -> [(Int, Id)]
- Data.DAWG.Internal: eps :: Node a -> {-# UNPACK #-} !Id
- Data.DAWG.Internal: instance Binary a => Binary (Node a)
- Data.DAWG.Internal: instance Eq a => Eq (Node a)
- Data.DAWG.Internal: instance Functor Node
- Data.DAWG.Internal: instance Ord a => Ord (Node a)
- Data.DAWG.Internal: instance Show a => Show (Node a)
- Data.DAWG.Internal: onSym :: Int -> Node a -> Maybe Id
- 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.Static: instance (Eq a, Eq b, Unbox b) => Eq (Node a b)
- Data.DAWG.Static: instance (Ord a, Ord b, Unbox b) => Ord (Node a b)
- Data.DAWG.Static: instance (Show a, Show b, Unbox b) => Show (Node a b)
- Data.DAWG.Static: instance (Unbox b, Binary a, Binary b) => Binary (Node a b)
+ Data.DAWG.Node: Branch :: {-# UNPACK #-} !ID -> !(VMap b) -> Node a b
+ Data.DAWG.Node: Leaf :: !a -> Node a b
+ Data.DAWG.Node: annotate :: a -> Edge b -> Edge a
+ Data.DAWG.Node: data Node a b
+ Data.DAWG.Node: edgeMap :: Node a b -> !(VMap b)
+ Data.DAWG.Node: edges :: Unbox b => Node a b -> [b]
+ Data.DAWG.Node: eps :: Node a b -> {-# UNPACK #-} !ID
+ Data.DAWG.Node: instance (Eq a, Eq b, Unbox b) => Eq (Node a b)
+ Data.DAWG.Node: instance (Ord a, Ord b, Unbox b) => Ord (Node a b)
+ Data.DAWG.Node: instance (Show a, Show b, Unbox b) => Show (Node a b)
+ Data.DAWG.Node: instance (Unbox b, Binary a, Binary b) => Binary (Node a b)
+ Data.DAWG.Node: label :: Edge a -> a
+ Data.DAWG.Node: labeled :: a -> ID -> Edge a
+ Data.DAWG.Node: onSym :: Unbox b => Sym -> Node a b -> Maybe b
+ Data.DAWG.Node: subst :: Unbox b => Sym -> b -> Node a b -> Node a b
+ Data.DAWG.Node: to :: Edge a -> ID
+ Data.DAWG.Node: toGeneric :: Node a -> Node a (Edge ())
+ Data.DAWG.Node: trans :: Unbox b => Node a b -> [(Sym, b)]
+ Data.DAWG.Node: type Edge a = (ID, a)
+ Data.DAWG.Node: type ID = Int
+ Data.DAWG.Node: type Sym = Int
+ Data.DAWG.Node: value :: Node a b -> !a
+ Data.DAWG.Node.Specialized: Branch :: {-# UNPACK #-} !ID -> !(VMap ID) -> Node a
+ Data.DAWG.Node.Specialized: Leaf :: !a -> Node a
+ Data.DAWG.Node.Specialized: data Node a
+ Data.DAWG.Node.Specialized: edgeMap :: Node a -> !(VMap ID)
+ Data.DAWG.Node.Specialized: edges :: Node a -> [ID]
+ Data.DAWG.Node.Specialized: eps :: Node a -> {-# UNPACK #-} !ID
+ Data.DAWG.Node.Specialized: instance Binary a => Binary (Node a)
+ Data.DAWG.Node.Specialized: instance Eq a => Eq (Node a)
+ Data.DAWG.Node.Specialized: instance Ord a => Ord (Node a)
+ Data.DAWG.Node.Specialized: instance Show a => Show (Node a)
+ Data.DAWG.Node.Specialized: onSym :: Sym -> Node a -> Maybe ID
+ Data.DAWG.Node.Specialized: reIdent :: (ID -> ID) -> Node a -> Node a
+ Data.DAWG.Node.Specialized: subst :: Sym -> ID -> Node a -> Node a
+ Data.DAWG.Node.Specialized: trans :: Node a -> [(Sym, ID)]
+ Data.DAWG.Node.Specialized: type ID = Int
+ Data.DAWG.Node.Specialized: type Sym = Int
+ Data.DAWG.Node.Specialized: value :: Node a -> !a
+ Data.DAWG.Static: instance (Binary b, Binary c, Unbox c) => Binary (DAWG a b c)
+ Data.DAWG.Static: instance (Eq b, Eq c, Unbox c) => Eq (DAWG a b c)
+ Data.DAWG.Static: instance (Ord b, Ord c, Unbox c) => Ord (DAWG a b c)
+ Data.DAWG.Static: instance (Show b, Show c, Unbox c) => Show (DAWG a b c)
- Data.DAWG: DAWG :: !(Graph (Maybe b)) -> !Id -> DAWG a b
+ Data.DAWG: DAWG :: !(Graph (Maybe b)) -> !ID -> DAWG a b
- Data.DAWG: root :: DAWG a b -> !Id
+ Data.DAWG: root :: DAWG a b -> !ID
- Data.DAWG.Internal: Graph :: !(Map (Node a) Id) -> !IntSet -> !(IntMap (Node a)) -> !(IntMap Int) -> Graph a
+ Data.DAWG.Internal: Graph :: !(Map (Node a) ID) -> !IntSet -> !(IntMap (Node a)) -> !(IntMap Int) -> Graph a
- Data.DAWG.Internal: idMap :: Graph a -> !(Map (Node a) Id)
+ Data.DAWG.Internal: idMap :: Graph a -> !(Map (Node a) ID)
- Data.DAWG.Internal: insert :: Ord a => Node a -> Graph a -> (Id, Graph a)
+ Data.DAWG.Internal: insert :: Ord a => Node a -> Graph a -> (ID, Graph a)
- Data.DAWG.Internal: nodeBy :: Id -> Graph a -> 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: nodeID :: Ord a => Node a -> Graph a -> ID
- Data.DAWG.Static: DAWG :: Vector (Node (Maybe b) c) -> DAWG a b c
+ Data.DAWG.Static: DAWG :: Vector (Node b c) -> DAWG a b c
- Data.DAWG.Static: unDAWG :: DAWG a b c -> Vector (Node (Maybe b) c)
+ Data.DAWG.Static: unDAWG :: DAWG a b c -> Vector (Node b c)

Files

Data/DAWG.hs view
@@ -33,105 +33,110 @@ import Data.Binary (Binary, put, get) import qualified Control.Monad.State.Strict as S -import Data.DAWG.Internal (Id, Node, Graph)+import Data.DAWG.Internal (Graph) import qualified Data.DAWG.Internal as I import qualified Data.DAWG.VMap as V +import Data.DAWG.Node.Specialized hiding (Node)+import qualified Data.DAWG.Node.Specialized as N++type Node a = N.Node (Maybe a)+ type GraphM a b = S.State (Graph (Maybe 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 :: Ord a => GraphM a Id +insertLeaf :: Ord a => GraphM a ID  insertLeaf = do-    i <- insertNode (I.Value Nothing)-    insertNode (I.Branch i V.empty)+    i <- insertNode (N.Leaf Nothing)+    insertNode (N.Branch i V.empty)  -- | Return node with the given identifier.-nodeBy :: Id -> GraphM a (Node (Maybe a))+nodeBy :: ID -> GraphM a (Node a) nodeBy i = I.nodeBy i <$> S.get  -- Evaluate the 'I.insert' function within the monad.-insertNode :: Ord a => Node (Maybe a) -> GraphM a Id+insertNode :: Ord a => Node a -> GraphM a ID insertNode = S.state . I.insert  -- Evaluate the 'I.delete' function within the monad.-deleteNode :: Ord a => Node (Maybe a) -> GraphM a ()+deleteNode :: Ord a => Node a -> GraphM a () deleteNode = S.state . mkState . I.delete  -- | Invariant: the identifier points to the 'Branch' node.-insertM :: Ord a => [Int] -> 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 I.onSym x n of+    j <- case onSym x n of         Just j  -> return j         Nothing -> insertLeaf     k <- insertM xs y j     deleteNode n-    insertNode (I.subst x k n)+    insertNode (subst x k n) insertM [] y i = do     n <- nodeBy i-    w <- nodeBy (I.eps n)+    w <- nodeBy (N.eps n)     deleteNode w     deleteNode n-    j <- insertNode (I.Value $ Just y)-    insertNode (n { I.eps = j })+    j <- insertNode (N.Leaf $ Just y)+    insertNode (n { N.eps = j }) -insertWithM :: Ord a => (a -> a -> a) -> [Int] -> 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 I.onSym x n of+    j <- case onSym x n of         Just j  -> return j         Nothing -> insertLeaf     k <- insertWithM f xs y j     deleteNode n-    insertNode (I.subst x k n)+    insertNode (subst x k n) insertWithM f [] y i = do     n <- nodeBy i-    w <- nodeBy (I.eps n)+    w <- nodeBy (N.eps n)     deleteNode w     deleteNode n-    let y'new = case I.unValue w of+    let y'new = case N.value w of             Just y' -> f y y'             Nothing -> y-    j <- insertNode (I.Value $ Just y'new)-    insertNode (n { I.eps = j })+    j <- insertNode (N.Leaf $ Just y'new)+    insertNode (n { N.eps = j }) -deleteM :: Ord a => [Int] -> Id -> GraphM a Id+deleteM :: Ord a => [Int] -> ID -> GraphM a ID deleteM (x:xs) i = do     n <- nodeBy i-    case I.onSym x n of+    case onSym x n of         Nothing -> return i         Just j  -> do             k <- deleteM xs j             deleteNode n-            insertNode (I.subst x k n)+            insertNode (subst x k n) deleteM [] i = do     n <- nodeBy i-    w <- nodeBy (I.eps n)+    w <- nodeBy (N.eps n)     deleteNode w     deleteNode n     j <- insertLeaf-    insertNode (n { I.eps = j })+    insertNode (n { N.eps = j })     -lookupM :: [Int] -> Id -> GraphM a (Maybe a)+lookupM :: [Int] -> ID -> GraphM a (Maybe a) lookupM [] i = do-    j <- I.eps <$> nodeBy i-    I.unValue <$> nodeBy j+    j <- N.eps <$> nodeBy i+    N.value <$> nodeBy j lookupM (x:xs) i = do     n <- nodeBy i-    case I.onSym x n of+    case onSym x n of         Just j  -> lookupM xs j         Nothing -> return Nothing -assocsAcc :: Graph (Maybe a) -> Id -> [([Int], a)]+assocsAcc :: Graph (Maybe a) -> ID -> [([Int], a)] assocsAcc g i =-    here w ++ concatMap there (I.edges n)+    here w ++ concatMap there (trans n)   where     n = I.nodeBy i g-    w = I.nodeBy (I.eps n) g-    here v = case I.unValue v of+    w = I.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)@@ -141,7 +146,7 @@ -- symbol type. data DAWG a b = DAWG     { graph :: !(Graph (Maybe b))-    , root  :: !Id }+    , root  :: !ID }     deriving (Show, Eq, Ord)  instance (Ord b, Binary b) => Binary (DAWG a b) where@@ -166,6 +171,7 @@     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.@@ -219,6 +225,7 @@ 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
Data/DAWG/Internal.hs view
@@ -1,19 +1,12 @@ {-# 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.Internal-( --- * Node-  Node (..)-, Id-, edges-, onSym-, subst--- * Graph-, Graph (..)+( Graph (..) , empty , size , nodeBy@@ -23,65 +16,18 @@ ) where  import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, Get, put, get)+-- import Data.List (foldl')+import Data.Binary (Binary, put, get) import qualified Data.Map as M+-- import qualified Data.Tree as T 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 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 -> [(Int, Id)]-edges (Branch _ es)     = V.toList es-edges (Value _)         = error "edges: value node"+-- import qualified Control.Monad.State.Strict as S --- | 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"+import Data.DAWG.Node.Specialized hiding (Node)+import qualified Data.DAWG.Node.Specialized as N --- | 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"+type Node a = N.Node a  -- | A set of nodes.  To every node a unique identifier is assigned. -- Invariants: @@ -97,7 +43,7 @@ -- the memory footprint? data Graph a = Graph {     -- | Map from nodes to IDs.-      idMap     :: !(M.Map (Node a) Id)+      idMap     :: !(M.Map (Node a) ID)     -- | Set of free IDs.     , freeIDs   :: !IS.IntSet     -- | Map from IDs to nodes. @@ -127,15 +73,15 @@ size = M.size . idMap  -- | Node with the given identifier.-nodeBy :: Id -> Graph a -> Node a+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 :: 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 :: Ord a => Node a -> Graph a -> (ID, Graph a) newNode n Graph{..} =     (i, Graph idMap' freeIDs' nodeMap' ingoMap')   where@@ -147,7 +93,7 @@         else IS.deleteFindMin freeIDs  -- | Remove node from the graph.-remNode :: Ord a => Id -> Graph a -> Graph a+remNode :: Ord a => ID -> Graph a -> Graph a remNode i Graph{..} =     Graph idMap' freeIDs' nodeMap' ingoMap'   where@@ -158,12 +104,12 @@     n           = nodeMap IM.! i  -- | Increment the number of ingoing paths.-incIngo :: Id -> Graph a -> Graph a+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 :: 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) })@@ -173,7 +119,7 @@ -- 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 :: 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@@ -190,3 +136,72 @@   where     i = nodeID 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/Node.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE RecordWildCards #-}++-- | Internal representation of automata nodes.++module Data.DAWG.Node+(+-- * Basic types+  ID+, Sym+-- * Node+, Node (..)+, onSym+, trans+, edges+, subst+, toGeneric+-- * Edge+, Edge+, to+, label+, annotate+, labeled+) where++import Control.Applicative ((<$>), (<*>))+import Control.Arrow (second)+import Data.Binary (Binary, Get, put, get)+import Data.Vector.Unboxed (Unbox)++import qualified Data.DAWG.VMap as M+import qualified Data.DAWG.Node.Specialized as N++-- | Node identifier.+type ID = Int++-- | Internal representation of the transition symbol.+type Sym = Int++-- | Edge with label.+type Edge a = (ID, a)++-- | Destination ID.+to :: Edge a -> ID+to = fst+{-# INLINE to #-}++-- | Edge label.+label :: Edge a -> a+label = snd+{-# INLINE label #-}++-- | Annotate edge wit a new label.+annotate :: a -> Edge b -> Edge a+annotate x (i, _) = (i, x)+{-# INLINE annotate #-}++-- | Construct edge annotated with a given label.+labeled :: a -> ID -> Edge a+labeled x i = (i, x)+{-# INLINE labeled #-}++-- | 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+        -- | Labeled edges outgoing from the node.+        , edgeMap   :: !(M.VMap b) }+    | Leaf { value  :: !a }+    deriving (Show, Eq, Ord)++instance (Unbox b, Binary a, Binary b) => Binary (Node a b) where+    put Branch{..} = put (1 :: Int) >> put eps >> put edgeMap+    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 :: Unbox b => Sym -> Node a b -> Maybe b+onSym x (Branch _ es)   = M.lookup x es+onSym _ (Leaf _)        = Nothing+{-# INLINE onSym #-}++-- | List of symbol/edge pairs outgoing from the node.+trans :: Unbox b => Node a b -> [(Sym, b)]+trans (Branch _ es)     = M.toList es+trans (Leaf _)          = []+{-# INLINE trans #-}++-- | List of outgoing edges.+edges :: Unbox b => Node a b -> [b]+edges = map snd . trans+{-# INLINE edges #-}++-- | Substitue edge determined by a given symbol.+subst :: Unbox b => Sym -> b -> Node a b -> Node a b+subst x e (Branch w es) = Branch w (M.insert x e es)+subst _ _ l             = l+{-# INLINE subst #-}++-- | Yield generic version of a specialized node.+toGeneric :: N.Node a -> Node a (Edge ())+toGeneric N.Leaf{..}    = Leaf value+toGeneric N.Branch{..}  = Branch eps (annEdges edgeMap) where+    annEdges = M.fromList . map annEdge . M.toList+    annEdge = second (labeled ())
+ Data/DAWG/Node/Specialized.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE RecordWildCards #-}++-- | Internal representation of automata nodes specialized to+-- a version with unlabeled edges.++module Data.DAWG.Node.Specialized+(+-- * Basic types+  ID+, Sym+-- * Node+, Node (..)+, onSym+, trans+, edges+, subst+, reIdent+) where++import Control.Applicative ((<$>), (<*>))+import Control.Arrow (second)+import Data.Binary (Binary, Get, put, get)++import qualified Data.DAWG.VMap as M++-- | Node identifier.+type ID = Int++-- | Internal representation of the transition symbol.+type Sym = 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 '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+        -- | Labeled edges outgoing from the node.+        , edgeMap   :: !(M.VMap ID) }+    | Leaf { value  :: !a }+    deriving (Show, Eq, Ord)++instance (Binary a) => Binary (Node a) where+    put Branch{..} = put (1 :: Int) >> put eps >> put edgeMap+    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 _ es)   = M.lookup x es+onSym _ (Leaf _)        = Nothing+{-# INLINE onSym #-}++-- | List of symbol/edge pairs outgoing from the node.+trans :: Node a -> [(Sym, ID)]+trans (Branch _ es)     = M.toList es+trans (Leaf _)          = []+{-# INLINE trans #-}++-- | List of outgoing edges.+edges :: Node a -> [ID]+edges = map snd . trans+{-# INLINE edges #-}++-- | Substitue edge determined by a given symbol.+subst :: Sym -> ID -> Node a -> Node a+subst x e (Branch w es) = Branch w (M.insert x e es)+subst _ _ l             = l+{-# INLINE subst #-}++-- | Assign new identifiers.+reIdent :: (ID -> ID) -> Node a -> Node a+reIdent _ (Leaf x)      = Leaf x+reIdent f (Branch e es) =+    let reEdges = M.fromList . map (second f) . M.toList+    in  Branch (f e) (reEdges es)
Data/DAWG/Static.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}  -- | The module implements /directed acyclic word graphs/ (DAWGs) internaly -- represented as /minimal acyclic deterministic finite-state automata/.@@ -38,95 +39,51 @@ , assocs , keys , elems+-- , thaw ) where  import Prelude hiding (lookup)-import Control.Applicative ((<$), (<$>), (<*>), (<|>))-import Control.Arrow (first, second)-import Data.Binary (Binary, put, get)+import Control.Applicative ((<$), (<$>), (<|>))+import Control.Arrow (first)+import Data.Binary (Binary) import Data.Vector.Binary () import Data.Vector.Unboxed (Unbox) import qualified Data.IntMap as M import qualified Data.Vector as V +import Data.DAWG.Node hiding (Node)+import qualified Data.DAWG.Node as N+import qualified Data.DAWG.Node.Specialized as NS 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---- | Internal representation of the transition symbol.-type Sym = Int---- | Edge with label.-type Edge a = (Id, a)--to :: Edge a -> Id-to = fst-{-# INLINE to #-}--label :: Edge a -> a-label = snd-{-# INLINE label #-}--annotate :: a -> Edge b -> Edge a-annotate x (i, _) = (i, x)-{-# INLINE annotate #-}--labeled :: a -> Id -> Edge a-labeled x i = (i, x)-{-# INLINE labeled #-}---- | State (node) of the automaton.-data Node a b = Node {-    -- | Value kept in the node.-      value     :: !a-    -- | Labeled edges outgoing from the node.-    , edgeMap   :: !(VM.VMap (Edge b)) }-    deriving (Show, Eq, Ord)--instance (Unbox b, Binary a, Binary b) => Binary (Node a b) where-    put Node{..} = put value >> put edgeMap-    get = Node <$> get <*> get---- | Transition function.-onSym :: Unbox b => Sym -> Node a b -> Maybe (Edge b)-onSym x (Node _ es) = VM.lookup x es-{-# INLINE onSym #-}---- List of symbol/edge pairs outgoing from the node.-trans :: Unbox b => Node a b -> [(Sym, Edge b)]-trans = VM.toList . edgeMap-{-# INLINE trans #-}---- | List of outgoing edges.-edges :: Unbox b => Node a b -> [Edge b]-edges = map snd . trans-{-# INLINE edges #-}---- | List children identifiers.-children :: Unbox b => Node a b -> [Id]-children = map to . edges-{-# INLINE children #-}+type Node a b = N.Node (Maybe a) (Edge b)  -- | @DAWG a b c@ constitutes an automaton with alphabet symbols of type /a/, -- node values of type /Maybe b/ and additional transition labels of type /c/. -- Root is stored on the first position of the array.-newtype DAWG a b c = DAWG { unDAWG :: V.Vector (Node (Maybe b) c) }+newtype DAWG a b c = DAWG { unDAWG :: V.Vector (Node b c) }+    deriving (Show, Eq, Ord, Binary)  -- | Empty DAWG. empty :: Unbox c => DAWG a b c-empty = DAWG $ V.singleton (Node Nothing VM.empty)+empty = DAWG $ V.fromList+    [ Branch 1 VM.empty+    , Leaf Nothing ]  -- | Number of states in the automaton. numStates :: DAWG a b c -> Int numStates = V.length . unDAWG  -- | Node with the given identifier.-nodeBy :: Id -> DAWG a b c -> Node (Maybe b) c+nodeBy :: ID -> DAWG a b c -> Node b c nodeBy i d = unDAWG d V.! i +-- | Value in leaf node with a given ID.+leafValue :: Node b c -> DAWG a b c -> Maybe b+leafValue n = value . nodeBy (eps n)+ -- | Find value associated with the key. lookup :: (Unbox c, Enum a) => [a] -> DAWG a b c -> Maybe b lookup xs' =@@ -134,8 +91,8 @@     in  lookup'I xs 0 {-# SPECIALIZE lookup :: Unbox c => String -> DAWG Char b c -> Maybe b #-} -lookup'I :: Unbox c => [Sym] -> Id -> DAWG a b c -> Maybe b-lookup'I []     i d = value (nodeBy i d)+lookup'I :: Unbox c => [Sym] -> ID -> DAWG a b c -> Maybe b+lookup'I []     i d = leafValue (nodeBy i d) d lookup'I (x:xs) i d = case onSym x (nodeBy i d) of     Just e  -> lookup'I xs (to e) d     Nothing -> Nothing@@ -145,12 +102,12 @@ assocs d = map (first (map toEnum)) (assocs'I 0 d) {-# SPECIALIZE assocs :: Unbox c => DAWG Char b c -> [(String, b)] #-} -assocs'I :: Unbox c => Id -> DAWG a b c -> [([Sym], b)]+assocs'I :: Unbox c => ID -> DAWG a b c -> [([Sym], b)] assocs'I i d =     here ++ concatMap there (trans n)   where     n = nodeBy i d-    here = case value n of+    here = case leafValue n d of         Just x  -> [([], x)]         Nothing -> []     there (x, e) = map (first (x:)) (assocs'I (to e) d)@@ -195,55 +152,44 @@ -- | Compute node weights and store corresponding values in transition labels. weigh :: Unbox c => DAWG a b c -> DAWG a b Weight weigh d = (DAWG . V.fromList)-    [ Node (value n) (apply ws (trans n))+    [ branch n (apply ws (trans n))     | i <- [0 .. numStates d - 1]     , let n  = nodeBy i d     , let ws = accum (children n) ]   where+    -- Branch with new edges.+    branch Branch{..} es    = Branch eps es+    branch Leaf{..}   _     = 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 =-        let n = nodeBy i d-            js = children n-        in  add (value n) (map nodeWeight js)-    add w x = maybe 0 (const 1) w + sum x+    detWeight i = case nodeBy i d of+        Leaf w  -> maybe 0 (const 1) w+        n       -> sum . map nodeWeight $ allChildren n     -- Weight for subsequent edges.     accum = init . scanl (+) 0 . map nodeWeight     -- Apply weight to edges.      apply ws ts = VM.fromList         [ (x, annotate w e)         | (w, (x, e)) <- zip ws ts ]+    -- Plain children and epsilon child. +    allChildren n = eps n : children n+    -- IDs of plain children.+    children = map to . edges  -- | Construct immutable version of the automaton. freeze :: D.DAWG a b -> DAWG a b () freeze d = DAWG . V.fromList $-    map (stop . oldBy) (M.elems (inverse old2new))+    map (N.toGeneric . NS.reIdent newID . oldBy)+        (M.elems (inverse old2new))   where     -- Map from old to new identifiers.-    old2new :: M.IntMap Int     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 <*> 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 mkEdge) . VM.toList -    -- Make edge from old ID.-    mkEdge = labeled () . (old2new M.!)+    newID   = (M.!) old2new+    -- List of node IDs without the root ID.+    nodeIDs = filter (/= D.root d) . map fst . M.assocs . I.nodeMap . D.graph     -- 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@@ -251,16 +197,28 @@     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+-- -- | Yield mutable version of the automaton.+-- thaw :: (Unbox c, Ord a) => DAWG a b c -> D.DAWG a b -- thaw d =---     D.DAWG graph 0+--     D.fromNodes nodes 0 --   where---     graph = I.Graph---         (Map.fromList $ zip nodes [0..])---         IS.empty---         (M.fromList   $ zip [0..] nodes)---         (+--     -- 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.@@ -268,11 +226,11 @@ index xs = index'I (map fromEnum xs) 0 {-# SPECIALIZE index :: String -> DAWG Char b Weight -> Maybe Int #-} -index'I :: [Sym] -> Id -> DAWG a b Weight -> Maybe Int-index'I []     i d = 0 <$ value (nodeBy i d)+index'I :: [Sym] -> ID -> DAWG a b Weight -> Maybe Int+index'I []     i d = 0 <$ leafValue (nodeBy i d) d index'I (x:xs) i d = do     let n = nodeBy i d-        v = maybe 0 (const 1) (value n)+        v = maybe 0 (const 1) (leafValue n d)     e <- onSym x n     w <- index'I xs (to e) d     return (v + w + label e)@@ -289,18 +247,17 @@ byIndex ix d = map toEnum <$> byIndex'I ix 0 d {-# SPECIALIZE byIndex :: Int -> DAWG Char b Weight -> Maybe String #-} -byIndex'I :: Int -> Id -> DAWG a b Weight -> Maybe [Sym]+byIndex'I :: Int -> ID -> DAWG a b Weight -> Maybe [Sym] byIndex'I ix i d     | ix < 0    = Nothing     | otherwise = here <|> there   where     n = nodeBy i d-    v = maybe 0 (const 1) (value n)+    v = maybe 0 (const 1) (leafValue n d)     here-        | ix == 0   = [] <$ value (nodeBy i d)+        | ix == 0   = [] <$ leafValue (nodeBy i d) d         | otherwise = Nothing     there = do-        -- (x, e) <- VM.firstLL label (ix - v) (edgeMap n)         (x, e) <- VM.findLastLE cmp (edgeMap n)         xs <- byIndex'I (ix - v - label e) (to e) d         return (x:xs)
Data/DAWG/VMap.hs view
@@ -6,8 +6,8 @@ ( VMap (unVMap) , empty , lookup-, findLastLE , insert+, findLastLE , fromList , toList ) where@@ -39,70 +39,55 @@  -- | Lookup the symbol in the map. lookup :: Unbox a => Int -> VMap a -> Maybe a-lookup x (VMap v)-    | U.null v  = Nothing-    | otherwise = ST.runST $ do-        w <- U.unsafeThaw v-        fmap snd <$> search w x-  where-    search vec e =-        loop 0 (UM.length vec - 1)-      where-        loop !l !u-            | u <= l    = do-                e' <- UM.unsafeRead vec k-                return $ if e == fst e'-                    then (Just e')-                    else Nothing-            | otherwise = do-                e' <- UM.unsafeRead vec k-                case compare (fst e') e of-                    LT -> loop (k+1) u-                    EQ -> return (Just e')-                    GT -> loop l (k-1)-          where-            k = (u + l) `shiftR` 1--- lookup x = fmap snd . U.find ((==x) . fst) . unVMap+lookup x (VMap v) =+    case binarySearch (flip compare x . fst) v of+        Left k  -> snd <$> v U.!? k+        Right _ -> Nothing {-# INLINE lookup #-} --- | Find last map element which is not GT with respect to the--- given ordering function.+-- | Insert the symbol/value pair into the map.+insert :: Unbox a => Int -> a -> VMap a -> VMap a+insert x y (VMap v) = VMap $+    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 #-}++-- | 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) -> VMap a -> Maybe (Int, a)-findLastLE cmp (VMap v) = ST.runST $ do+findLastLE cmp (VMap v) =+    let k = binarySearch (cmp . snd) v+    in  v U.!? either id (\x -> x-1) k+{-# INLINE findLastLE #-}++-- | 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-    k <- search w-    return (v U.!? (k - 1))+    search w   where-    search vec =-        loop 0 (UM.length vec)+    search w =+        loop 0 (UM.length w)       where         loop !l !u-            | u <= l    = return l+            | u <= l    = return (Right l)             | otherwise = do                 let k = (u + l) `shiftR` 1-                x <- UM.unsafeRead vec k-                case cmp (snd x) of+                x <- UM.unsafeRead w k+                case cmp x of                     LT -> loop (k+1) u-                    EQ -> return (k+1)+                    EQ -> return (Left k)                     GT -> loop l     k--- firstLL f x vm = do---     k <-  U.findIndex ((>x) . f . snd) v---       <|> if n > 0 then Just n else Nothing---     return (v U.! (k - 1))---   where---     v = unVMap vm---     n = U.length v-{-# INLINE findLastLE #-}---- | Insert the symbol/value pair into the map.--- TODO: Optimize! Use the invariant, that VMap is--- kept in an ascending vector.-insert :: Unbox a => Int -> a -> VMap a -> VMap a-insert x y-    = VMap . U.fromList . M.toAscList-    . M.insert x y-    . M.fromList . U.toList . unVMap-{-# INLINE insert #-}+{-# INLINE binarySearch #-}  -- | Smart 'VMap' constructor which ensures that the underlying vector is -- strictly ascending with respect to 'fst' values.
dawg.cabal view
@@ -1,5 +1,5 @@ name:               dawg-version:            0.7.0+version:            0.7.1 synopsis:           Directed acyclic word graphs description:     The library implements /directed acyclic word graphs/ (DAWGs) internaly@@ -31,6 +31,8 @@      exposed-modules:         Data.DAWG+      , Data.DAWG.Node+      , Data.DAWG.Node.Specialized       , Data.DAWG.Static       , Data.DAWG.Internal       , Data.DAWG.VMap