packages feed

comfort-graph 0.0.3.1 → 0.0.3.2

raw patch · 6 files changed

+376/−277 lines, 6 filesdep +doctest-exitcode-stdiodep ~transformersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: doctest-exitcode-stdio

Dependency ranges changed: transformers

API changes (from Hackage documentation)

- Data.Graph.Comfort: EDirEdge :: (DirEdge node) -> EitherEdge node
+ Data.Graph.Comfort: EDirEdge :: DirEdge node -> EitherEdge node
- Data.Graph.Comfort: EUndirEdge :: (UndirEdge node) -> EitherEdge node
+ Data.Graph.Comfort: EUndirEdge :: UndirEdge node -> EitherEdge node
- Data.Graph.Comfort: insertNode :: (Ord n) => n -> nl -> Graph e n el nl -> Graph e n el nl
+ Data.Graph.Comfort: insertNode :: Ord n => n -> nl -> Graph e n el nl -> Graph e n el nl
- Data.Graph.Comfort: lookupNode :: (Ord n) => n -> Graph e n el nl -> Maybe nl
+ Data.Graph.Comfort: lookupNode :: Ord n => n -> Graph e n el nl -> Maybe nl
- Data.Graph.Comfort: undirEdge :: (Ord node) => node -> node -> UndirEdge node
+ Data.Graph.Comfort: undirEdge :: Ord node => node -> node -> UndirEdge node

Files

+ Makefile view
@@ -0,0 +1,8 @@+run-test:	update-test+	runhaskell Setup configure --user --enable-tests+	runhaskell Setup build+	runhaskell Setup haddock+	runhaskell Setup test comfort-graph-test --show-details=streaming++update-test:+	doctest-extract -i src/ -o test/ --executable-main=Main.hs Data.Graph.Comfort
comfort-graph.cabal view
@@ -1,5 +1,5 @@ Name:                comfort-graph-Version:             0.0.3.1+Version:             0.0.3.2 Synopsis:            Graph structure with type parameters for nodes and edges Description:   This graph structure is based on "Data.Map"@@ -41,7 +41,7 @@   * @fgl@:       standard package for graph processing with many graph algorithms       but cumbersome data structure with Int numbered nodes-Homepage:            http://hub.darcs.net/thielema/comfort-graph+Homepage:            https://hub.darcs.net/thielema/comfort-graph License:             BSD3 License-File:        LICENSE Author:              Henning Thielemann@@ -49,15 +49,17 @@ Category:            Data Build-Type:          Simple Cabal-Version:       >=1.10+Extra-Source-Files:+  Makefile  Source-Repository this-  Tag:         0.0.3.1+  Tag:         0.0.3.2   Type:        darcs-  Location:    http://hub.darcs.net/thielema/comfort-graph+  Location:    https://hub.darcs.net/thielema/comfort-graph  Source-Repository head   Type:        darcs-  Location:    http://hub.darcs.net/thielema/comfort-graph+  Location:    https://hub.darcs.net/thielema/comfort-graph  Library   Exposed-Modules:@@ -79,12 +81,16 @@   If impl(ghc>=8.0)     GHC-Options: -fno-warn-redundant-constraints -Test-Suite test-comfort-graph+Test-Suite comfort-graph-test   Type:                exitcode-stdio-1.0   Hs-Source-Dirs:      test   Main-is:             Main.hs+  Other-Modules:+    Test.Base+    Test.Data.Graph.Comfort   Build-Depends:     comfort-graph,+    doctest-exitcode-stdio >=0.0 && <0.1,     QuickCheck >=2 && <3,     transformers,     containers,
src/Data/Graph/Comfort.hs view
@@ -88,6 +88,49 @@ import Prelude (error)  +{- $setup+>>> import Test.Base+>>>+>>> import qualified Data.Graph.Comfort as Graph+>>> import qualified Data.Map as Map+>>> import qualified Data.Set as Set+>>> import qualified Data.Char as Char+>>>+>>> import qualified Control.Monad.Trans.Class as MT+>>> import qualified Control.Monad.Trans.State as MS+>>> import Control.Applicative (pure)+>>> import Data.Functor.Identity (Identity(Identity), runIdentity)+>>>+>>> import qualified Test.QuickCheck as QC+>>> import Test.QuickCheck ((==>))+>>>+>>> deleteNodeIfExists :: Node -> MonoGraph -> MonoGraph+>>> deleteNodeIfExists n gr =+>>>    maybe gr (const $ Graph.deleteNode n gr) $ Graph.lookupNode n gr+>>>+>>> isolated :: (Graph.Edge e, Ord n) => Graph.Graph e n el nl -> n -> Bool+>>> isolated gr n = Set.null (Graph.adjacentEdgeSet gr n)+>>>+>>> nodeAction :: (Monad m) => NodeLabel -> MS.StateT NodeLabel m NodeLabel+>>> nodeAction x = do y <- MS.get; MS.put x; return y+>>>+>>> evalTraverseNode :: NodeLabel -> MonoGraph -> MonoGraph+>>> evalTraverseNode nl =+>>>    flip MS.evalState nl . Graph.traverseNode nodeAction+>>>+>>> edgeAction :: (Monad m) => EdgeLabel -> MS.StateT EdgeLabel m EdgeLabel+>>> edgeAction x = MS.modify (x+) >> MS.get+>>>+>>> evalTraverseEdge :: EdgeLabel -> MonoGraph -> MonoGraph+>>> evalTraverseEdge el =+>>>    flip MS.evalState el . Graph.traverseEdge edgeAction+>>>+>>> evalTraverse :: NodeLabel -> EdgeLabel -> MonoGraph -> MonoGraph+>>> evalTraverse nl el =+>>>    flip MS.evalState el . flip MS.evalStateT nl .+>>>    Graph.traverse nodeAction (MT.lift . edgeAction)+-}+ {- For all 'Graph's the 'isConsistent' predicate must be 'True'. -}@@ -311,6 +354,10 @@ edges = Map.keys . edgeLabels  +{- |+prop> \(TestGraph gr) -> Graph.isConsistent (Graph.reverse gr)+prop> \(TestGraph gr) -> Graph.reverse (Graph.reverse gr) == gr+-} reverse ::    (Reverse e, Ord n) =>    Graph e n el nl -> Graph e n el nl@@ -359,6 +406,10 @@           Map.mapKeys (wrap . g . unwrap) outs)) .    Map.mapKeysWith (error "Graph.mapKeys: node map is not injective") f +{- |+prop> Graph.isEmpty (Graph.empty :: MonoGraph)+prop> Graph.isConsistent (Graph.empty :: MonoGraph)+-} empty :: Graph edge node edgeLabel nodeLabel empty = Graph Map.empty @@ -410,6 +461,9 @@ nodeLabels :: (Edge e, Ord n) => Graph e n el nl -> Map n nl nodeLabels = fmap snd3 . graphMapWrap +{- |+prop> \(GraphAndEdge gr e) -> Graph.lookupEdge e gr == Map.lookup e (Graph.edgeLabels gr)+-} lookupEdge :: (Edge e, Ord n) => e n -> Graph e n el nl -> Maybe el lookupEdge e (Graph g) =    Map.lookup (wrap e) . thd3 =<< Map.lookup (from e) g@@ -425,6 +479,9 @@ isEmpty :: Graph e n el nl -> Bool isEmpty = Map.null . graphMapWrap +{- |+prop> \(TestGraph gr) n -> Graph.lookupNode n gr == Map.lookup n (Graph.nodeLabels gr)+-} lookupNode :: (Ord n) => n -> Graph e n el nl -> Maybe nl lookupNode n (Graph g) = fmap snd3 $ Map.lookup n g @@ -466,6 +523,10 @@  {- | Node to be deleted must be contained in the graph.++prop> \(TestGraph gr) n -> Graph.isConsistent $ deleteNodeIfExists n gr+prop> \(TestGraph gr) n nl -> Graph.deleteNode n (Graph.insertNode n nl gr) == deleteNodeIfExists n gr+prop> \(TestGraph gr) -> let isolatedNodes = filter (isolated gr) $ Graph.nodes gr in not (null isolatedNodes) ==> QC.forAll (QC.elements isolatedNodes) $ \n nl -> Graph.insertNode n nl gr == Graph.insertNode n nl (Graph.deleteNode n gr) -} deleteNode ::    (Edge e, Ord n) =>@@ -490,6 +551,11 @@    Set n -> Graph e n el nl -> Graph e n el nl deleteNodeSet delNs g = Set.foldl (flip deleteNode) g delNs +{- |+prop> \(GraphAndEdge gr e) -> Graph.isConsistent $ Graph.deleteEdge e gr+prop> \(GraphAndEdge gr e) el -> Graph.deleteEdge e (Graph.insertEdge e el gr) == Graph.deleteEdge e gr+prop> \(GraphAndEdge gr e) el -> Graph.insertEdge e el gr == Graph.insertEdge e el (Graph.deleteEdge e gr)+-} deleteEdge ::    (Edge e, Ord n) =>    e n -> Graph e n el nl -> Graph e n el nl@@ -498,6 +564,9 @@       Map.adjust (mapThd3 $ Map.delete $ wrap e) (from e) .       Map.adjust (mapFst3 $ Map.delete $ wrap e) (to e) +{- |+prop> \(GraphAndEdge gr e) -> Graph.filterEdgeWithKey (\ei _ -> e/=ei) gr == Graph.deleteEdge e gr+-} filterEdgeWithKey ::    (Edge e, Ord n) =>    (e n -> el -> Bool) ->@@ -548,6 +617,9 @@ and existing edges are maintained. However, I think we should better have an extra function for this purpose and you should not rely on this behavior.++prop> \(TestGraph gr) n nl -> Graph.isConsistent $ Graph.insertNode n nl gr+prop> \(TestGraph gr) n nl -> Graph.lookupNode n (Graph.insertNode n nl gr) == Just nl -} insertNode ::    (Ord n) => n -> nl -> Graph e n el nl -> Graph e n el nl@@ -558,6 +630,10 @@       n (Map.empty, nl, Map.empty) .    graphMapWrap +{- |+prop> \(GraphAndEdge gr e) el -> Graph.isConsistent $ Graph.insertEdge e el gr+prop> \(GraphAndEdge gr e) el -> Graph.lookupEdge e (Graph.insertEdge e el gr) == Just el+-} insertEdge ::    (Edge e, Ord n) =>    e n -> el -> Graph e n el nl -> Graph e n el nl@@ -589,6 +665,9 @@ fromList ns es =    fromMapWrap (Map.fromList ns) $ Map.fromList $ map (mapFst wrap) es +{- |+prop> \(TestGraph gr) -> gr == Graph.fromMap (Graph.nodeLabels gr) (Graph.edgeLabels gr)+-} fromMap ::    (Edge e, Ord n) =>    Map n nl -> Map (e n) el -> Graph e n el nl@@ -606,6 +685,9 @@           (TMap.cons Map.empty $ Map.mapKeysWith Map.union fromWrap ess) ns  +{- |+prop> \(TestGraph gr) -> Graph.mapNode id gr == gr+-} mapNode :: (nl0 -> nl1) -> Graph e n el nl0 -> Graph e n el nl1 mapNode f =    Graph . fmap (\(ins,n,outs) -> (ins, f n, outs)) . graphMapWrap@@ -616,6 +698,9 @@    Map.mapWithKey (\n (ins,nl,outs) -> (ins, f n nl, outs)) .    graphMapWrap +{- |+prop> \(TestGraph gr) -> Graph.mapEdge id gr == gr+-} mapEdge :: (el0 -> el1) -> Graph e n el0 nl -> Graph e n el1 nl mapEdge f =    Graph . fmap (\(ins,n,outs) -> (fmap f ins, n, fmap f outs)) . graphMapWrap@@ -649,6 +734,9 @@  {- | Same restrictions as in 'traverse'.++prop> \(TestGraph gr) nl -> Graph.isConsistent $ evalTraverseNode nl gr+prop> \(TestGraph gr) -> runIdentity (Graph.traverseNode (Identity . Char.toUpper) gr) == Graph.mapNode Char.toUpper gr -} traverseNode ::    (Applicative f, Edge e, Ord n) =>@@ -660,6 +748,9 @@  {- | Same restrictions as in 'traverse'.++prop> \(TestGraph gr) el -> Graph.isConsistent $ evalTraverseEdge el gr+prop> \(TestGraph gr) el -> runIdentity (Graph.traverseEdge (Identity . (el+)) gr) == Graph.mapEdge (el+) gr -} traverseEdge ::    (Applicative f, Edge e, Ord n) =>@@ -669,6 +760,12 @@  {- | Don't rely on a particular order of traversal!++prop> \(TestGraph gr) nl el -> Graph.isConsistent $ evalTraverse nl el gr+prop> \(TestGraph gr) nl el -> evalTraverse nl el gr == evalTraverseNode nl (evalTraverseEdge el gr)+prop> \(TestGraph gr) nl el -> evalTraverse nl el gr == evalTraverseEdge el (evalTraverseNode nl gr)+prop> \(TestGraph gr) nl -> flip MS.evalState nl (Graph.traverseNode nodeAction gr) == flip MS.evalState nl (Graph.traverse nodeAction pure gr)+prop> \(TestGraph gr) el -> flip MS.evalState el (Graph.traverseEdge edgeAction gr) == flip MS.evalState el (Graph.traverse pure edgeAction gr) -} traverse, _traverseNaive ::    (Applicative f, Edge e, Ord n) =>
test/Main.hs view
@@ -1,275 +1,10 @@-module Main (main) where--import qualified Data.Graph.Comfort as Graph-import Data.Graph.Comfort (Graph)--import qualified Data.Map as Map-import qualified Data.Set as Set-import qualified Data.Char as Char-import Data.Bool.HT (implies)--import qualified Control.Monad.Trans.Class as MT-import qualified Control.Monad.Trans.State as MS-import Control.Applicative (liftA2, pure)-import Data.Functor.Identity (Identity(Identity), runIdentity)--import qualified Test.QuickCheck as QC----type Edge = Graph.DirEdge-type Node = Int-type EdgeLabel = Integer-type NodeLabel = Char-type MonoGraph = Graph Edge Node EdgeLabel NodeLabel--newtype TestGraph = TestGraph {getTestGraph :: MonoGraph}-   deriving (Show)--instance QC.Arbitrary TestGraph where-   shrink (TestGraph g) =-      case Graph.nodeSet g of-         ns ->-            map (TestGraph . flip Graph.deleteNodeSet g .-                 Set.difference ns . Set.fromList) $-            QC.shrink $ Set.toList ns-   arbitrary = do-      nodes <- QC.arbitrary-      fmap TestGraph $-         if null nodes-            then return Graph.empty-            else do-               let genNode = QC.elements $ map fst nodes-               fmap (Graph.fromList nodes) $ QC.listOf $-                  liftA2 (,)-                     (liftA2 Graph.DirEdge genNode genNode) QC.arbitrary---data GraphAndEdge = GraphAndEdge MonoGraph (Edge Node)-   deriving (Show)--instance QC.Arbitrary GraphAndEdge where-   shrink (GraphAndEdge gr e) =-      map (\(TestGraph g) -> GraphAndEdge g e) $ QC.shrink $ TestGraph gr-   arbitrary =-      let makeGraph p consEdge = do-            TestGraph gr <- QC.suchThat QC.arbitrary $ p . getTestGraph-            fmap (GraphAndEdge gr) $ consEdge gr-      in  QC.oneof-            [makeGraph (not . null . Graph.edges) $ QC.elements . Graph.edges,-             makeGraph (not . null . Graph.nodes) $-                \gr ->-                   let selNode = QC.elements $ Graph.nodes gr-                   in  liftA2 Graph.DirEdge selNode selNode]---test :: (QC.Testable prop) => String -> prop -> IO ()-test msg prop =-   putStr (msg ++ ": ") >> QC.quickCheck prop---emptyIsEmpty :: Bool-emptyIsEmpty =-   Graph.isEmpty (Graph.empty :: MonoGraph)--emptyIsConsistent :: Bool-emptyIsConsistent =-   Graph.isEmpty (Graph.empty :: MonoGraph)--fromMapNodeEdgeLabels :: TestGraph -> Bool-fromMapNodeEdgeLabels (TestGraph gr) =-   Graph.fromMap (Graph.nodeLabels gr) (Graph.edgeLabels gr) == gr--reverseIsConsistent :: TestGraph -> Bool-reverseIsConsistent (TestGraph gr) =-   Graph.isConsistent (Graph.reverse gr)--reverseReverse :: TestGraph -> Bool-reverseReverse (TestGraph gr) =-   Graph.reverse (Graph.reverse gr) == gr--mapNodeId :: TestGraph -> Bool-mapNodeId (TestGraph gr) =-   Graph.mapNode id gr == gr--mapEdgeId :: TestGraph -> Bool-mapEdgeId (TestGraph gr) =-   Graph.mapEdge id gr == gr--insertNodeIsConsistent :: TestGraph -> Node -> NodeLabel -> Bool-insertNodeIsConsistent (TestGraph gr) n nl =-   Graph.isConsistent $ Graph.insertNode n nl gr--insertLookupNode :: TestGraph -> Node -> NodeLabel -> Bool-insertLookupNode (TestGraph gr) n nl =-   Graph.lookupNode n (Graph.insertNode n nl gr) == Just nl--lookupNodeLabels :: TestGraph -> Node -> Bool-lookupNodeLabels (TestGraph gr) n =-   Graph.lookupNode n gr == Map.lookup n (Graph.nodeLabels gr)--deleteNodeIfExists :: Node -> MonoGraph -> MonoGraph-deleteNodeIfExists n gr =-   maybe gr (const $ Graph.deleteNode n gr) $ Graph.lookupNode n gr--deleteNodeIsConsistent :: TestGraph -> Node -> Bool-deleteNodeIsConsistent (TestGraph gr) n =-   Graph.isConsistent $ deleteNodeIfExists n gr--deleteInsertNode :: TestGraph -> Node -> NodeLabel -> Bool-deleteInsertNode (TestGraph gr) n nl =-   Graph.deleteNode n (Graph.insertNode n nl gr)-   ==-   deleteNodeIfExists n gr--insertDeleteNode :: TestGraph -> Node -> NodeLabel -> Bool-insertDeleteNode (TestGraph gr) n nl =-   let isolated =-         maybe False (const True) (Graph.lookupNode n gr)-         &&-         Set.null (Graph.adjacentEdgeSet gr n)-   in  isolated `implies`-       Graph.insertNode n nl gr-       ==-       Graph.insertNode n nl (Graph.deleteNode n gr)---insertEdgeIsConsistent :: GraphAndEdge -> EdgeLabel -> Bool-insertEdgeIsConsistent (GraphAndEdge gr e) el =-   Graph.isConsistent $ Graph.insertEdge e el gr--insertLookupEdge :: GraphAndEdge -> EdgeLabel -> Bool-insertLookupEdge (GraphAndEdge gr e) el =-   Graph.lookupEdge e (Graph.insertEdge e el gr) == Just el--lookupEdgeLabels :: GraphAndEdge -> Bool-lookupEdgeLabels (GraphAndEdge gr e) =-   Graph.lookupEdge e gr == Map.lookup e (Graph.edgeLabels gr)--deleteEdgeIsConsistent :: GraphAndEdge -> Bool-deleteEdgeIsConsistent (GraphAndEdge gr e) =-   Graph.isConsistent $ Graph.deleteEdge e gr--deleteInsertEdge :: GraphAndEdge -> EdgeLabel -> Bool-deleteInsertEdge (GraphAndEdge gr e) el =-   Graph.deleteEdge e (Graph.insertEdge e el gr)-   ==-   Graph.deleteEdge e gr--insertDeleteEdge :: GraphAndEdge -> EdgeLabel -> Bool-insertDeleteEdge (GraphAndEdge gr e) el =-   Graph.insertEdge e el gr-   ==-   Graph.insertEdge e el (Graph.deleteEdge e gr)--filterDeleteEdge :: GraphAndEdge -> Bool-filterDeleteEdge (GraphAndEdge gr e) =-   Graph.filterEdgeWithKey (\ei _ -> e/=ei) gr == Graph.deleteEdge e gr---nodeAction :: (Monad m) => NodeLabel -> MS.StateT NodeLabel m NodeLabel-nodeAction x = do y <- MS.get; MS.put x; return y--evalTraverseNode :: NodeLabel -> MonoGraph -> MonoGraph-evalTraverseNode nl =-   flip MS.evalState nl . Graph.traverseNode nodeAction--traverseNodeIsConsistent :: TestGraph -> NodeLabel -> Bool-traverseNodeIsConsistent (TestGraph gr) nl =-   Graph.isConsistent $ evalTraverseNode nl gr---edgeAction :: (Monad m) => EdgeLabel -> MS.StateT EdgeLabel m EdgeLabel-edgeAction x = MS.modify (x+) >> MS.get--evalTraverseEdge :: EdgeLabel -> MonoGraph -> MonoGraph-evalTraverseEdge el =-   flip MS.evalState el . Graph.traverseEdge edgeAction--traverseEdgeIsConsistent :: TestGraph -> EdgeLabel -> Bool-traverseEdgeIsConsistent (TestGraph gr) el =-   Graph.isConsistent $ evalTraverseEdge el gr--evalTraverse :: NodeLabel -> EdgeLabel -> MonoGraph -> MonoGraph-evalTraverse nl el =-   flip MS.evalState el . flip MS.evalStateT nl .-   Graph.traverse nodeAction (MT.lift . edgeAction)--traverseIsConsistent :: TestGraph -> NodeLabel -> EdgeLabel -> Bool-traverseIsConsistent (TestGraph gr) nl el =-   Graph.isConsistent $ evalTraverse nl el gr---traverseNodeEdge :: TestGraph -> NodeLabel -> EdgeLabel -> Bool-traverseNodeEdge (TestGraph gr) nl el =-   evalTraverseNode nl (evalTraverseEdge el gr)-   ==-   evalTraverse nl el gr--traverseEdgeNode :: TestGraph -> NodeLabel -> EdgeLabel -> Bool-traverseEdgeNode (TestGraph gr) nl el =-   evalTraverseEdge el (evalTraverseNode nl gr)-   ==-   evalTraverse nl el gr---traverseNode :: TestGraph -> NodeLabel -> Bool-traverseNode (TestGraph gr) nl =-   flip MS.evalState nl (Graph.traverseNode nodeAction gr)-   ==-   flip MS.evalState nl (Graph.traverse nodeAction pure gr)--traverseEdge :: TestGraph -> EdgeLabel -> Bool-traverseEdge (TestGraph gr) el =-   flip MS.evalState el (Graph.traverseEdge edgeAction gr)-   ==-   flip MS.evalState el (Graph.traverse pure edgeAction gr)---traverseMapNode :: TestGraph -> Bool-traverseMapNode (TestGraph gr) =-   runIdentity (Graph.traverseNode (Identity . Char.toUpper) gr)-   ==-   Graph.mapNode Char.toUpper gr+-- Do not edit! Automatically created with doctest-extract.+module Main where -traverseMapEdge :: TestGraph -> EdgeLabel -> Bool-traverseMapEdge (TestGraph gr) el =-   runIdentity (Graph.traverseEdge (Identity . (el+)) gr)-   ==-   Graph.mapEdge (el+) gr+import qualified Test.Data.Graph.Comfort +import qualified Test.DocTest.Driver as DocTest  main :: IO ()-main = do-   test "emptyIsEmpty" $ emptyIsEmpty-   test "emptyIsConsistent" $ emptyIsConsistent-   test "fromMapNodeEdgeLabels" $ fromMapNodeEdgeLabels-   test "reverseIsConsistent" $ reverseIsConsistent-   test "reverseReverse" $ reverseReverse-   test "mapNodeId" $ mapNodeId-   test "mapEdgeId" $ mapEdgeId--   test "insertNodeIsConsistent" $ insertNodeIsConsistent-   test "insertLookupNode" $ insertLookupNode-   test "lookupNodeLabels" $ lookupNodeLabels-   test "deleteNodeIsConsistent" $ deleteNodeIsConsistent-   test "deleteInsertNode" $ deleteInsertNode-   test "insertDeleteNode" $ insertDeleteNode--   test "insertEdgeIsConsistent" $ insertEdgeIsConsistent-   test "insertLookupEdge" $ insertLookupEdge-   test "lookupEdgeLabels" $ lookupEdgeLabels-   test "deleteEdgeIsConsistent" $ deleteEdgeIsConsistent-   test "deleteInsertEdge" $ deleteInsertEdge-   test "insertDeleteEdge" $ insertDeleteEdge-   test "filterDeleteEdge" $ filterDeleteEdge--   test "traverseNodeIsConsistent" $ traverseNodeIsConsistent-   test "traverseEdgeIsConsistent" $ traverseEdgeIsConsistent-   test "traverseIsConsistent" $ traverseIsConsistent-   test "traverseNodeEdge" $ traverseNodeEdge-   test "traverseEdgeNode" $ traverseEdgeNode-   test "traverseNode" $ traverseNode-   test "traverseEdge" $ traverseEdge-   test "traverseMapNode" $ traverseMapNode-   test "traverseMapEdge" $ traverseMapEdge+main = DocTest.run $ do+    Test.Data.Graph.Comfort.test
+ test/Test/Base.hs view
@@ -0,0 +1,57 @@+module Test.Base where++import qualified Data.Graph.Comfort as Graph+import Data.Graph.Comfort (Graph)++import qualified Data.Set as Set++import Control.Applicative (liftA2)++import qualified Test.QuickCheck as QC++++type Edge = Graph.DirEdge+type Node = Int+type EdgeLabel = Integer+type NodeLabel = Char+type MonoGraph = Graph Edge Node EdgeLabel NodeLabel++newtype TestGraph = TestGraph {getTestGraph :: MonoGraph}+   deriving (Show)++instance QC.Arbitrary TestGraph where+   shrink (TestGraph g) =+      case Graph.nodeSet g of+         ns ->+            map (TestGraph . flip Graph.deleteNodeSet g .+                 Set.difference ns . Set.fromList) $+            QC.shrink $ Set.toList ns+   arbitrary = do+      nodes <- QC.arbitrary+      fmap TestGraph $+         if null nodes+            then return Graph.empty+            else do+               let genNode = QC.elements $ map fst nodes+               fmap (Graph.fromList nodes) $ QC.listOf $+                  liftA2 (,)+                     (liftA2 Graph.DirEdge genNode genNode) QC.arbitrary+++data GraphAndEdge = GraphAndEdge MonoGraph (Edge Node)+   deriving (Show)++instance QC.Arbitrary GraphAndEdge where+   shrink (GraphAndEdge gr e) =+      map (\(TestGraph g) -> GraphAndEdge g e) $ QC.shrink $ TestGraph gr+   arbitrary =+      let makeGraph p consEdge = do+            TestGraph gr <- QC.suchThat QC.arbitrary $ p . getTestGraph+            fmap (GraphAndEdge gr) $ consEdge gr+      in  QC.oneof+            [makeGraph (not . null . Graph.edges) $ QC.elements . Graph.edges,+             makeGraph (not . null . Graph.nodes) $+                \gr ->+                   let selNode = QC.elements $ Graph.nodes gr+                   in  liftA2 Graph.DirEdge selNode selNode]
+ test/Test/Data/Graph/Comfort.hs view
@@ -0,0 +1,196 @@+-- Do not edit! Automatically created with doctest-extract from src/Data/Graph/Comfort.hs+{-# LINE 91 "src/Data/Graph/Comfort.hs" #-}++module Test.Data.Graph.Comfort where++import qualified Test.DocTest.Driver as DocTest++{-# LINE 92 "src/Data/Graph/Comfort.hs" #-}+import     Test.Base++import     qualified Data.Graph.Comfort as Graph+import     qualified Data.Map as Map+import     qualified Data.Set as Set+import     qualified Data.Char as Char++import     qualified Control.Monad.Trans.Class as MT+import     qualified Control.Monad.Trans.State as MS+import     Control.Applicative (pure)+import     Data.Functor.Identity (Identity(Identity), runIdentity)++import     qualified Test.QuickCheck as QC+import     Test.QuickCheck ((==>))++deleteNodeIfExists     :: Node -> MonoGraph -> MonoGraph+deleteNodeIfExists     n gr =+       maybe gr (const $ Graph.deleteNode n gr) $ Graph.lookupNode n gr++isolated     :: (Graph.Edge e, Ord n) => Graph.Graph e n el nl -> n -> Bool+isolated     gr n = Set.null (Graph.adjacentEdgeSet gr n)++nodeAction     :: (Monad m) => NodeLabel -> MS.StateT NodeLabel m NodeLabel+nodeAction     x = do y <- MS.get; MS.put x; return y++evalTraverseNode     :: NodeLabel -> MonoGraph -> MonoGraph+evalTraverseNode     nl =+       flip MS.evalState nl . Graph.traverseNode nodeAction++edgeAction     :: (Monad m) => EdgeLabel -> MS.StateT EdgeLabel m EdgeLabel+edgeAction     x = MS.modify (x+) >> MS.get++evalTraverseEdge     :: EdgeLabel -> MonoGraph -> MonoGraph+evalTraverseEdge     el =+       flip MS.evalState el . Graph.traverseEdge edgeAction++evalTraverse     :: NodeLabel -> EdgeLabel -> MonoGraph -> MonoGraph+evalTraverse     nl el =+       flip MS.evalState el . flip MS.evalStateT nl .+       Graph.traverse nodeAction (MT.lift . edgeAction)++test :: DocTest.T ()+test = do+ DocTest.printPrefix "Data.Graph.Comfort:358: "+{-# LINE 358 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 358 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> Graph.isConsistent (Graph.reverse gr))+ DocTest.printPrefix "Data.Graph.Comfort:359: "+{-# LINE 359 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 359 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> Graph.reverse (Graph.reverse gr) == gr)+ DocTest.printPrefix "Data.Graph.Comfort:410: "+{-# LINE 410 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 410 "src/Data/Graph/Comfort.hs" #-}+     (Graph.isEmpty (Graph.empty :: MonoGraph))+ DocTest.printPrefix "Data.Graph.Comfort:411: "+{-# LINE 411 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 411 "src/Data/Graph/Comfort.hs" #-}+     (Graph.isConsistent (Graph.empty :: MonoGraph))+ DocTest.printPrefix "Data.Graph.Comfort:465: "+{-# LINE 465 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 465 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) -> Graph.lookupEdge e gr == Map.lookup e (Graph.edgeLabels gr))+ DocTest.printPrefix "Data.Graph.Comfort:483: "+{-# LINE 483 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 483 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) n -> Graph.lookupNode n gr == Map.lookup n (Graph.nodeLabels gr))+ DocTest.printPrefix "Data.Graph.Comfort:527: "+{-# LINE 527 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 527 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) n -> Graph.isConsistent $ deleteNodeIfExists n gr)+ DocTest.printPrefix "Data.Graph.Comfort:528: "+{-# LINE 528 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 528 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) n nl -> Graph.deleteNode n (Graph.insertNode n nl gr) == deleteNodeIfExists n gr)+ DocTest.printPrefix "Data.Graph.Comfort:529: "+{-# LINE 529 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 529 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> let isolatedNodes = filter (isolated gr) $ Graph.nodes gr in not (null isolatedNodes) ==> QC.forAll (QC.elements isolatedNodes) $ \n nl -> Graph.insertNode n nl gr == Graph.insertNode n nl (Graph.deleteNode n gr))+ DocTest.printPrefix "Data.Graph.Comfort:555: "+{-# LINE 555 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 555 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) -> Graph.isConsistent $ Graph.deleteEdge e gr)+ DocTest.printPrefix "Data.Graph.Comfort:556: "+{-# LINE 556 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 556 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) el -> Graph.deleteEdge e (Graph.insertEdge e el gr) == Graph.deleteEdge e gr)+ DocTest.printPrefix "Data.Graph.Comfort:557: "+{-# LINE 557 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 557 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) el -> Graph.insertEdge e el gr == Graph.insertEdge e el (Graph.deleteEdge e gr))+ DocTest.printPrefix "Data.Graph.Comfort:568: "+{-# LINE 568 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 568 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) -> Graph.filterEdgeWithKey (\ei _ -> e/=ei) gr == Graph.deleteEdge e gr)+ DocTest.printPrefix "Data.Graph.Comfort:621: "+{-# LINE 621 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 621 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) n nl -> Graph.isConsistent $ Graph.insertNode n nl gr)+ DocTest.printPrefix "Data.Graph.Comfort:622: "+{-# LINE 622 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 622 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) n nl -> Graph.lookupNode n (Graph.insertNode n nl gr) == Just nl)+ DocTest.printPrefix "Data.Graph.Comfort:634: "+{-# LINE 634 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 634 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) el -> Graph.isConsistent $ Graph.insertEdge e el gr)+ DocTest.printPrefix "Data.Graph.Comfort:635: "+{-# LINE 635 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 635 "src/Data/Graph/Comfort.hs" #-}+     (\(GraphAndEdge gr e) el -> Graph.lookupEdge e (Graph.insertEdge e el gr) == Just el)+ DocTest.printPrefix "Data.Graph.Comfort:669: "+{-# LINE 669 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 669 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> gr == Graph.fromMap (Graph.nodeLabels gr) (Graph.edgeLabels gr))+ DocTest.printPrefix "Data.Graph.Comfort:689: "+{-# LINE 689 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 689 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> Graph.mapNode id gr == gr)+ DocTest.printPrefix "Data.Graph.Comfort:702: "+{-# LINE 702 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 702 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> Graph.mapEdge id gr == gr)+ DocTest.printPrefix "Data.Graph.Comfort:738: "+{-# LINE 738 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 738 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) nl -> Graph.isConsistent $ evalTraverseNode nl gr)+ DocTest.printPrefix "Data.Graph.Comfort:739: "+{-# LINE 739 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 739 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) -> runIdentity (Graph.traverseNode (Identity . Char.toUpper) gr) == Graph.mapNode Char.toUpper gr)+ DocTest.printPrefix "Data.Graph.Comfort:752: "+{-# LINE 752 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 752 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) el -> Graph.isConsistent $ evalTraverseEdge el gr)+ DocTest.printPrefix "Data.Graph.Comfort:753: "+{-# LINE 753 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 753 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) el -> runIdentity (Graph.traverseEdge (Identity . (el+)) gr) == Graph.mapEdge (el+) gr)+ DocTest.printPrefix "Data.Graph.Comfort:764: "+{-# LINE 764 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 764 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) nl el -> Graph.isConsistent $ evalTraverse nl el gr)+ DocTest.printPrefix "Data.Graph.Comfort:765: "+{-# LINE 765 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 765 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) nl el -> evalTraverse nl el gr == evalTraverseNode nl (evalTraverseEdge el gr))+ DocTest.printPrefix "Data.Graph.Comfort:766: "+{-# LINE 766 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 766 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) nl el -> evalTraverse nl el gr == evalTraverseEdge el (evalTraverseNode nl gr))+ DocTest.printPrefix "Data.Graph.Comfort:767: "+{-# LINE 767 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 767 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) nl -> flip MS.evalState nl (Graph.traverseNode nodeAction gr) == flip MS.evalState nl (Graph.traverse nodeAction pure gr))+ DocTest.printPrefix "Data.Graph.Comfort:768: "+{-# LINE 768 "src/Data/Graph/Comfort.hs" #-}+ DocTest.property+{-# LINE 768 "src/Data/Graph/Comfort.hs" #-}+     (\(TestGraph gr) el -> flip MS.evalState el (Graph.traverseEdge edgeAction gr) == flip MS.evalState el (Graph.traverse pure edgeAction gr))