pangraph (empty) → 0.1.1.5
raw patch · 16 files changed
+585/−0 lines, 16 filesdep +HUnitdep +algebraic-graphsdep +basesetup-changed
Dependencies added: HUnit, algebraic-graphs, base, bytestring, containers, hexml, pangraph
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- pangraph.cabal +48/−0
- src/Pangraph.hs +142/−0
- src/Pangraph/Containers.hs +39/−0
- src/Pangraph/Examples/Reading.hs +13/−0
- src/Pangraph/Examples/SampleGraph.hs +21/−0
- src/Pangraph/Examples/ToContainersGraph.hs +9/−0
- src/Pangraph/Examples/Writing.hs +8/−0
- src/Pangraph/GraphML/Parser.hs +20/−0
- src/Pangraph/GraphML/Writer.hs +54/−0
- src/Pangraph/Internal/XMLTemplate.hs +87/−0
- test/Containers.hs +13/−0
- test/GraphML.hs +55/−0
- test/Main.hs +10/−0
- test/Show.hs +34/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ pangraph.cabal view
@@ -0,0 +1,48 @@+name: pangraph+version: 0.1.1.5+synopsis: A set of parsers for graph languages.+description: A package allowing parsing of graph files into graph library datatypes. With aim the cope with large networks and provide translations between graph libraries. Like a pandoc but for graphs. This is my first library so any feedback and help is appreicated. For example use please see the homepage.+homepage: https://github.com/tuura/pangraph#readme+license: BSD3+license-file: LICENSE+author: Joe Scott+maintainer: joseph-scott@hotmail.co.uk+copyright: 2017 Joe Scott+category: graphs, library, data-structures, parser, xml+build-type: Simple+cabal-version: >=1.10+tested-with: GHC==8.0.2++library+ hs-source-dirs: src+ exposed-modules: Pangraph+ , Pangraph.Containers+ , Pangraph.GraphML.Parser+ , Pangraph.GraphML.Writer+ , Pangraph.Internal.XMLTemplate+ , Pangraph.Examples.Reading+ , Pangraph.Examples.Writing+ , Pangraph.Examples.ToContainersGraph+ , Pangraph.Examples.SampleGraph+ build-depends: base >= 4.8 && < 5+ , bytestring+ , hexml+ , containers+ , algebraic-graphs+ default-language: Haskell2010+ GHC-options: -Wall -fwarn-tabs++test-suite pangraph-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ other-modules: Show+ , GraphML+ , Containers+ build-depends: base >= 4.8 && < 5+ , pangraph+ , bytestring+ , HUnit+ , containers+ default-language: Haskell2010+ GHC-options: -Wall -fwarn-tabs -fbreak-on-exception
+ src/Pangraph.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}++module Pangraph (+ -- * Abstract Types+ Pangraph, Edge, Vertex, Attribute,+ Key, Value, VertexID, EdgeID,++ -- * Constructors+ makePangraph, makeEdge, makeVertex,++ -- * Pangraph Getters+ edgeList, vertexList, lookupVertex, lookupEdge,++ -- * Getters on Vertex and Edge+ edgeAttributes, vertexAttributes,+ edgeEndpoints, edgeID, vertexID++) where++import Data.Maybe (mapMaybe)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import qualified Data.ByteString as BS+import qualified Algebra.Graph.Class as Alga++-- | The 'Pangraph' type is the core intermediate type between abstract representations of graphs.+data Pangraph = Pangraph+ { vertices' :: Map VertexID Vertex+ , edges' :: Map EdgeID Edge+ } deriving (Eq)+-- | A Vertex holds ['Attribute'] and must have a unique 'VertexID' to be constructed with 'makeVertex'.+data Vertex = Vertex+ { vertexID' :: VertexID+ , vertexAttributes' :: [Attribute]+ } deriving (Eq)+-- | Edges also reqiure ['Attribute'] and a tuple of 'Vertex' passed as connections to be constructed with 'makeEdge'+data Edge = Edge+ { edgeID' :: Maybe EdgeID+ , edgeAttributes' :: [Attribute]+ , endpoints' :: (Vertex, Vertex)+ } deriving (Eq)++-- | A type exposed for lookup in the resulting lists.+type EdgeID = Int+-- | A field that is Maybe internally is exposed for lookup.+type VertexID = BS.ByteString+-- | The type alias for storage of fields.+type Attribute = (Key, Value)+-- | The 'Key' in the tuple that makes up 'Attribute'.+type Key = BS.ByteString+-- | The 'Value' in the tuple that makes up 'Attribute'.+type Value = BS.ByteString++type MalformedEdge = (Edge, (Maybe Vertex, Maybe Vertex))++instance Show Pangraph where+ show p = "makePangraph " ++ show (Map.elems (vertices' p)) ++ " " ++ show (Map.elems (edges' p))++instance Show Vertex where+ show (Vertex i as) = unwords ["makeVertex", show i, show as]++instance Show Edge where+ show (Edge _ as e) = unwords ["makeEdge", show as, show e]++instance Alga.ToGraph Pangraph where+ type ToVertex Pangraph = Vertex+ toGraph p = Alga.graph (vertexList p) (map edgeEndpoints $ edgeList p)++-- * List based constructors++-- | Takes lists of 'Vertex' and 'Edge' to produce 'Just Pangraph' if the graph is correctly formed.+makePangraph :: [Vertex] -> [Edge] -> Maybe Pangraph+makePangraph vs es = case verifyGraph vertexMap es of+ [] -> Just $ Pangraph vertexMap edgeMap+ _ -> Nothing+ where+ vertexMap :: Map VertexID Vertex+ vertexMap = Map.fromList $ zip (map vertexID vs) vs+ edgeMap :: Map EdgeID Edge+ edgeMap = Map.fromList indexEdges+ indexEdges :: [(EdgeID, Edge)]+ indexEdges = map (\ (i, Edge _ as a) -> (i, Edge (Just i) as a )) $ zip [0..] es++verifyGraph :: Map VertexID Vertex -> [Edge] -> [MalformedEdge]+verifyGraph vs = mapMaybe (\e -> lookupEndpoints (e, edgeEndpoints e))+ where+ lookupEndpoints :: (Edge, (Vertex, Vertex)) -> Maybe MalformedEdge+ lookupEndpoints (e, (v1,v2)) =+ case (Map.lookup (vertexID v1) vs, Map.lookup (vertexID v2) vs) of+ (Just _ , Just _) -> Nothing+ (Nothing, Just _) -> Just (e, (Just v1, Nothing))+ (Just _ , Nothing) -> Just (e, (Nothing, Just v2))+ (Nothing, Nothing) -> Just (e, (Just v1, Just v2))++-- | Edge constructor+makeEdge :: [Attribute] -> (Vertex, Vertex) -> Edge+makeEdge = Edge Nothing++-- | Vertex constructor+makeVertex :: VertexID -> [Attribute] -> Vertex+makeVertex = Vertex++-- * Pangraph Getters++-- | Returns the ['Edge'] from a 'Pangraph' instance+edgeList :: Pangraph -> [Edge]+edgeList p = Map.elems $ edges' p++-- | Returns the ['Vertex'] from a 'Pangraph' instance+vertexList :: Pangraph -> [Vertex]+vertexList p = Map.elems $ vertices' p++-- | Lookup of the 'EdgeID' in a 'Pangraph'. Complexity: /O(log n)/+lookupEdge :: EdgeID -> Pangraph -> Maybe Edge+lookupEdge key p = Map.lookup key $ edges' p++-- | Lookup of the 'VertexID' in a 'Pangraph'. Complexity: /O(log n)/+lookupVertex :: VertexID -> Pangraph -> Maybe Vertex+lookupVertex key p = Map.lookup key $ vertices' p++-- * Getters on 'Edge' and 'Vertex'++-- | Returns the ['Attribute'] of an 'Edge'+edgeAttributes :: Edge -> [Attribute]+edgeAttributes = edgeAttributes'++-- | Returns the ['Attribute'] list of an 'Edge'+vertexAttributes :: Vertex -> [Attribute]+vertexAttributes = vertexAttributes'++-- | Returns the endpoint of tupled 'Vertex' of an 'Edge'+edgeEndpoints :: Edge -> (Vertex, Vertex)+edgeEndpoints = endpoints'++-- | Returns the EdgeID if it has one. 'Edge's are given a new 'EdgeID' when they are passed and retrived from a 'Pangraph'+edgeID :: Edge -> Maybe EdgeID+edgeID = edgeID'++-- | Returns a 'VertexID'+vertexID :: Vertex -> VertexID+vertexID = vertexID'
+ src/Pangraph/Containers.hs view
@@ -0,0 +1,39 @@+module Pangraph.Containers+ ( convert+ ) where++import Pangraph+import qualified Data.Graph as CGraph++import Data.Maybe (fromMaybe)+import Data.List (groupBy, sort)+import Control.Arrow ((***))++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map++-- | Transforms a 'Pangraph' in a 'CGraph.Graph'.+convert :: Pangraph -> (CGraph.Graph, CGraph.Vertex -> (Vertex, VertexID, [VertexID]), VertexID -> Maybe CGraph.Vertex)+convert p = CGraph.graphFromEdges getVertices+ where+ -- A helper function for getting the IDs of endpoints.+ edgeEndpointIDs :: Edge -> (VertexID, VertexID)+ edgeEndpointIDs e = (vertexID *** vertexID) $ edgeEndpoints e++ -- Create an Edge Map using VertexID grouping edge sources together.+ edgeMap :: Map VertexID [VertexID]+ edgeMap = (Map.fromList . groupIDs) $ map edgeEndpointIDs $ edgeList p++ -- Lookup the edges for this vertex. Returning empty list on Nothing.+ vertexConnections :: Vertex -> [VertexID]+ vertexConnections v = fromMaybe [] (Map.lookup (vertexID v) edgeMap)++ -- Convert Pangraph Vertex into a form ready to collect Edges from the Pangraph+ getVertices :: [(Vertex, VertexID, [VertexID])]+ getVertices = map (\v ->(v, vertexID v, vertexConnections v)) $ vertexList p++groupIDs :: [(VertexID, VertexID)] -> [(VertexID, [VertexID])]+groupIDs vs = map (\ts -> (fst $ head ts, map snd ts)) groupedEdges+ where+ groupedEdges :: [[(VertexID, VertexID)]]+ groupedEdges = groupBy (\a b -> fst a == fst b) $ sort vs
+ src/Pangraph/Examples/Reading.hs view
@@ -0,0 +1,13 @@+module Pangraph.Examples.Reading where++import Prelude hiding (readFile)++import Data.ByteString (readFile)++import qualified Pangraph.GraphML.Parser as GraphML++main :: IO ()+main = do+ fileName <- getLine+ file <- readFile fileName+ print (GraphML.parse file)
+ src/Pangraph/Examples/SampleGraph.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE OverloadedStrings #-}++module Pangraph.Examples.SampleGraph+ ( smallGraph+ ) where++import Pangraph++smallGraph :: Pangraph+smallGraph = case graph of+ Just p -> p+ Nothing -> error "Small graph literal failed to construct."+ where+ graph =+ makePangraph+ [makeVertex "n0" [("id","n0")]+ ,makeVertex "n1" [("id","n1")]+ ,makeVertex "n2" [("id","n2")]]+ [makeEdge [("source","n0"),("target","n2")]+ (makeVertex "n0" [("id","n0")]+ ,makeVertex "n2" [("id","n2")])]
+ src/Pangraph/Examples/ToContainersGraph.hs view
@@ -0,0 +1,9 @@+module Pangraph.Examples.ToContainersGraph where++import Pangraph.Containers(convert)+import Pangraph.Examples.SampleGraph(smallGraph)++main :: IO ()+main =+ -- Transform to Containers Data.Graph and print.+ print $ ((\(a,_,_) -> a) . convert) smallGraph
+ src/Pangraph/Examples/Writing.hs view
@@ -0,0 +1,8 @@+module Pangraph.Examples.Writing where++import Pangraph.GraphML.Writer(write)+import Pangraph.Examples.SampleGraph(smallGraph)++main :: IO ()+main =+ print $ write smallGraph
+ src/Pangraph/GraphML/Parser.hs view
@@ -0,0 +1,20 @@+module Pangraph.GraphML.Parser (+parse,+unsafeParse+) where++import Data.Maybe+import qualified Data.ByteString as BS+import qualified Pangraph as P+import qualified Text.XML.Hexml as H+import qualified Pangraph.Internal.XMLTemplate as PT++-- * Parsing++-- | Returns 'Pangraph' if it can be parsed from a raw GraphML file.+parse :: BS.ByteString -> Maybe P.Pangraph+parse file = either (const Nothing) (PT.hexmlToPangraph PT.graphMLTemplate) (H.parse file)++-- | Like 'parse' except it throws an error on Nothing, which is when parsing fails.+unsafeParse :: BS.ByteString -> P.Pangraph+unsafeParse file = fromMaybe (error "Parse failed") (parse file)
+ src/Pangraph/GraphML/Writer.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE OverloadedStrings #-}++module Pangraph.GraphML.Writer+( write+) where++import Pangraph+import Prelude hiding(concat)+import Data.ByteString(ByteString, append, concat)+import Data.ByteString.Char8(pack)+++-- | Serialise a 'Pangraph' into a GraphML file producing a 'ByteString'.+write :: Pangraph -> ByteString+write p = concat $+ writeHeader 0 :+ writeGraphML 0 :+ writeGraphTag 1 :+ [] +++ map (writeNode 2) (vertexList p) +++ map (writeEdge 2) (edgeList p) +++ [closingTags]++writeHeader :: Int -> ByteString+writeHeader _ = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"++writeGraphML :: Int -> ByteString+writeGraphML _ = "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" \+ \xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"++writeGraphTag :: Int -> ByteString+writeGraphTag i = getIndentBS i `append` "<graph id=\"G\" edgedefault=\"undirected\">\n"++writeNode :: Int -> Vertex -> ByteString+writeNode i v = concat [getIndentBS i, "<node", nodesAtts, "/>\n"]+ where+ nodesAtts :: ByteString+ nodesAtts = concat $ map writeAttribute (vertexAttributes v)++writeEdge :: Int -> Edge -> ByteString+writeEdge i e = concat [getIndentBS i, "<edge", edgeAtts, "/>\n"]+ where+ edgeAtts :: ByteString+ edgeAtts = concat $ map writeAttribute (edgeAttributes e)++writeAttribute :: Attribute -> ByteString+writeAttribute (a,b) = concat [" ", a, "=\"", b, "\""]++closingTags :: ByteString+closingTags = append (getIndentBS 1) $ append "</graph>\n" "</graphml>"++-- Where i is the level of indentation.+getIndentBS :: Int -> ByteString+getIndentBS i = pack $ replicate (2*i) ' '
+ src/Pangraph/Internal/XMLTemplate.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE OverloadedStrings #-}++module Pangraph.Internal.XMLTemplate+( Template,+ graphMLTemplate,+ hexmlToPangraph+) where++import Data.Maybe++import qualified Pangraph as P++import qualified Text.XML.Hexml as H+import qualified Data.ByteString as BS++-- A list of places to find vertices and extractEdges.+data Template = XML [VertexRule] [EdgeRule]+-- A list of the locations of tags and which elements to take from them.+newtype EdgeRule = EdgeRule [(Path, Element)]+newtype VertexRule = VertexRule [(Path, Element)]++type Path = [BS.ByteString]+type Element = [BS.ByteString]+type HexmlVertex = H.Node++-- A template for graphML, it extracts the vertices and edges.+graphMLTemplate :: Template+graphMLTemplate = XML+ [VertexRule [( ["graphml", "graph", "node"], ["id"])]]+ [EdgeRule [( ["graphml", "graph", "edge"], ["source", "target"] )]]++hexmlToPangraph :: Template -> HexmlVertex -> Maybe P.Pangraph+hexmlToPangraph (XML nt et) root = P.makePangraph n e+ where+ -- Map all the given rules over the XML tree for vertices+ n = concatMap (extractVertices root "id") nt+ -- Map all the given rules over the XML tree for edges+ e = concatMap (extractEdges (assocVertices n) root ) et+ assocVertices :: [P.Vertex] -> [(P.VertexID, P.Vertex)]+ assocVertices vs = zip (map P.vertexID vs) vs++-- Applies the Vertex rule to the Hexml root node, returning a list of pangraph vertices found.+extractVertices :: HexmlVertex -> BS.ByteString -> VertexRule -> [P.Vertex]+extractVertices hexml idElement (VertexRule pe) = concatMap (makeVertex hexml idElement) pe++makeVertex :: HexmlVertex -> BS.ByteString -> (Path, Element) -> [P.Vertex]+makeVertex hexml idElement (path, element) = map (\as -> P.makeVertex (idElem as) as) attList+ where+ idElem :: [P.Attribute] -> P.VertexID+ idElem list = fromMaybe (error $ "Fatal: node missing id value: " ++ show list)+ (lookup idElement list)+++ attList :: [[P.Attribute]]+ attList = map (getAttributePairs element) $ resolvePath path hexml++-- Applies the edge rule to the Hexml root edge, returning a list of pangraph extractEdges found.+extractEdges:: [(P.VertexID, P.Vertex)] -> HexmlVertex -> EdgeRule -> [P.Edge]+extractEdges verticesAssoc hexml (EdgeRule pe) = concatMap (makeEdge verticesAssoc hexml) pe++makeEdge :: [(P.VertexID, P.Vertex)] -> HexmlVertex -> (Path, Element) -> [P.Edge]+makeEdge verticesAssoc hexml (path, element) = map (\as -> P.makeEdge as (getPrimitives as)) attList+ where+ getPrimitives :: [P.Attribute] -> (P.Vertex, P.Vertex)+ getPrimitives list = case (lookup src list, lookup dst list) of+ (Just srcID, Just dstID) ->+ case (lookup srcID verticesAssoc, lookup dstID verticesAssoc) of+ (Just vertexSrc, Just vertexDst) -> (vertexSrc, vertexDst)+ _ -> error $ "Fatal: Edge endpoints are not vertices: " ++ show list+ _ -> error $ "Fatal: Edge endpoints not found in attribute list: " ++ show list+ attList :: [[P.Attribute]]+ attList = map (getAttributePairs element) $ resolvePath path hexml+ src = "source"+ dst = "target"++resolvePath:: Path -> HexmlVertex -> [HexmlVertex]+resolvePath [] h = [h]+resolvePath bs h = concatMap (resolvePath (tail bs)) children+ where+ children :: [HexmlVertex]+ children = H.childrenBy h $ head bs++getAttributePairs:: Element -> HexmlVertex -> [P.Attribute]+getAttributePairs e h = map toAttribute $ mapMaybe (H.attributeBy h) e+ where+ toAttribute :: H.Attribute -> P.Attribute+ toAttribute a = (H.attributeName a, H.attributeValue a)
+ test/Containers.hs view
@@ -0,0 +1,13 @@+module Containers+ ( containersTests+ ) where++import Test.HUnit+import Pangraph.Examples.SampleGraph(smallGraph)+import Pangraph.Containers(convert)++containersTests :: [Test]+containersTests = [case1]++case1 :: Test+case1 = TestCase $ assertEqual "Containers Convert case 1" ("array (0,2) [(0,[2]),(1,[]),(2,[])]") $ (show . (\(a,_,_) -> a) . convert) smallGraph
+ test/GraphML.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}++module GraphML (+graphmlTests+) where++import Test.HUnit++import Data.Maybe++import Pangraph+import Pangraph.GraphML.Parser+import Pangraph.GraphML.Writer++graphmlTests :: [Test]+graphmlTests = [case1, case2]++case1 :: Test+case1 =+ let+ file = parse "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \+ \<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\"\+ \ xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\+ \ xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns\+ \ http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\+ \ <graph id=\"G\" edgedefault=\"undirected\">\+ \ <node id=\"n0\"/>\+ \ <node id=\"n1\"/>\+ \ <node id=\"n2\"/>\+ \ <edge id=\"e1\" source=\"n0\" target=\"n2\"/>\+ \ </graph>\+ \</graphml>"+ sampleVertices = [+ makeVertex "n0" [ ("id","n0")],+ makeVertex "n1" [ ("id","n1")],+ makeVertex "n2" [ ("id","n2")]]+ graph = makePangraph sampleVertices+ [makeEdge+ [("source","n0"),("target","n2")]+ (head sampleVertices, sampleVertices !! 2)]+ in TestCase $ assertEqual "GraphML Parse case 1" (graph :: Maybe Pangraph) file++case2 :: Test+case2 =+ let+ sampleVertices = [+ makeVertex "n0" [ ("id","n0")],+ makeVertex "n1" [ ("id","n1")],+ makeVertex "n2" [ ("id","n2")]]+ graph = makePangraph sampleVertices+ [makeEdge+ [("source","n0"),("target","n2")]+ (head sampleVertices, sampleVertices !! 2)]+ justGraph = fromMaybe (error "Sample graph failed to compile") graph+ in TestCase $ assertEqual "GraphML Write case 1" (graph :: Maybe Pangraph) (parse $ write justGraph)
+ test/Main.hs view
@@ -0,0 +1,10 @@+module Main where++import GraphML+import Show+import Containers++import Test.HUnit++main :: IO Counts+main = runTestTT $ TestList $ concat [graphmlTests, showTests, containersTests]
+ test/Show.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}++module Show (+showTests+) where++import Data.Maybe+import Test.HUnit+import Pangraph+++showTests :: [Test]+showTests = [case1]++case1 :: Test+case1 =+ let+ literal = "makePangraph [makeVertex \"0\" \+ \[(\"id\",\"0\")],makeVertex \"1\" \+ \[(\"id\",\"1\")]] [makeEdge \+ \[(\"source\",\"0\"),(\"target\",\"1\")] \+ \(makeVertex \"0\" \+ \[(\"id\",\"0\")],makeVertex \"1\" [(\"id\",\"1\")])]"+ sampleVertices = [makeVertex "0" [("id","0")]+ ,makeVertex "1" [ ("id","1")]]+ graph = show $ fromMaybe+ (error "Sample graph failed to build") $+ makePangraph+ sampleVertices [+ makeEdge+ [("source","0"), ("target","1")]+ (head sampleVertices, sampleVertices !! 1)+ ]+ in TestCase $ assertEqual "Show instance case 1" literal (graph :: String)