packages feed

algebraic-graphs 0.4 → 0.5

raw patch · 61 files changed

+9473/−5354 lines, 61 filesdep +transformersdep −base-compatdep −base-orphansdep −bifunctorsdep ~QuickCheckdep ~extradep ~inspection-testingPVP ok

version bump matches the API change (PVP)

Dependencies added: transformers

Dependencies removed: base-compat, base-orphans, bifunctors, semigroups

Dependency ranges changed: QuickCheck, extra, inspection-testing

API changes (from Hackage documentation)

- Algebra.Graph.AdjacencyIntMap.Internal: AM :: IntMap IntSet -> AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: [adjacencyIntMap] :: AdjacencyIntMap -> IntMap IntSet
- Algebra.Graph.AdjacencyIntMap.Internal: consistent :: AdjacencyIntMap -> Bool
- Algebra.Graph.AdjacencyIntMap.Internal: instance Control.DeepSeq.NFData Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Classes.Eq Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Classes.Ord Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Generics.Generic Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Num.Num Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Show.Show Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: newtype AdjacencyIntMap
- Algebra.Graph.AdjacencyMap.Internal: AM :: Map a (Set a) -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: [adjacencyMap] :: AdjacencyMap a -> Map a (Set a)
- Algebra.Graph.AdjacencyMap.Internal: consistent :: Ord a => AdjacencyMap a -> Bool
- Algebra.Graph.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: instance GHC.Generics.Generic (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: internalEdgeList :: Map a (Set a) -> [(a, a)]
- Algebra.Graph.AdjacencyMap.Internal: newtype AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: referredToVertexSet :: Ord a => Map a (Set a) -> Set a
- Algebra.Graph.Class: instance (Algebra.Graph.Label.Dioid e, GHC.Classes.Eq e, GHC.Classes.Ord a) => Algebra.Graph.Class.Graph (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Class: instance Algebra.Graph.Class.Graph (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Class: instance Algebra.Graph.Class.Graph Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Undirected (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Fold: adjacencyList :: Ord a => Fold a -> [(a, [a])]
- Algebra.Graph.Fold: biclique :: [a] -> [a] -> Fold a
- Algebra.Graph.Fold: circuit :: [a] -> Fold a
- Algebra.Graph.Fold: clique :: [a] -> Fold a
- Algebra.Graph.Fold: connect :: Fold a -> Fold a -> Fold a
- Algebra.Graph.Fold: connects :: [Fold a] -> Fold a
- Algebra.Graph.Fold: data Fold a
- Algebra.Graph.Fold: edge :: a -> a -> Fold a
- Algebra.Graph.Fold: edgeCount :: Ord a => Fold a -> Int
- Algebra.Graph.Fold: edgeList :: Ord a => Fold a -> [(a, a)]
- Algebra.Graph.Fold: edgeSet :: Ord a => Fold a -> Set (a, a)
- Algebra.Graph.Fold: edges :: [(a, a)] -> Fold a
- Algebra.Graph.Fold: empty :: Fold a
- Algebra.Graph.Fold: foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Fold a -> b
- Algebra.Graph.Fold: hasEdge :: Eq a => a -> a -> Fold a -> Bool
- Algebra.Graph.Fold: hasVertex :: Eq a => a -> Fold a -> Bool
- Algebra.Graph.Fold: induce :: (a -> Bool) -> Fold a -> Fold a
- Algebra.Graph.Fold: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance GHC.Base.Alternative Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance GHC.Base.Applicative Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance GHC.Base.Functor Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance GHC.Base.Monad Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance GHC.Base.MonadPlus Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance GHC.Num.Num a => GHC.Num.Num (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: isEmpty :: Fold a -> Bool
- Algebra.Graph.Fold: isSubgraphOf :: Ord a => Fold a -> Fold a -> Bool
- Algebra.Graph.Fold: overlay :: Fold a -> Fold a -> Fold a
- Algebra.Graph.Fold: overlays :: [Fold a] -> Fold a
- Algebra.Graph.Fold: path :: [a] -> Fold a
- Algebra.Graph.Fold: removeEdge :: Eq a => a -> a -> Fold a -> Fold a
- Algebra.Graph.Fold: removeVertex :: Eq a => a -> Fold a -> Fold a
- Algebra.Graph.Fold: simplify :: Ord a => Fold a -> Fold a
- Algebra.Graph.Fold: size :: Fold a -> Int
- Algebra.Graph.Fold: star :: a -> [a] -> Fold a
- Algebra.Graph.Fold: stars :: [(a, [a])] -> Fold a
- Algebra.Graph.Fold: transpose :: Fold a -> Fold a
- Algebra.Graph.Fold: vertex :: a -> Fold a
- Algebra.Graph.Fold: vertexCount :: Ord a => Fold a -> Int
- Algebra.Graph.Fold: vertexList :: Ord a => Fold a -> [a]
- Algebra.Graph.Fold: vertexSet :: Ord a => Fold a -> Set a
- Algebra.Graph.Fold: vertices :: [a] -> Fold a
- Algebra.Graph.HigherKinded.Class: instance Algebra.Graph.HigherKinded.Class.Graph Algebra.Graph.Fold.Fold
- Algebra.Graph.Internal: Edge :: Hit
- Algebra.Graph.Internal: Miss :: Hit
- Algebra.Graph.Internal: Tail :: Hit
- Algebra.Graph.Internal: data Hit
- Algebra.Graph.Internal: instance GHC.Classes.Eq Algebra.Graph.Internal.Hit
- Algebra.Graph.Internal: instance GHC.Classes.Ord Algebra.Graph.Internal.Hit
- Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.PowerSet a)
- Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Label.Minimum a)
- Algebra.Graph.Label: instance GHC.Num.Num a => GHC.Num.Num (Algebra.Graph.Label.Extended a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: AM :: Map a (Map a e) -> AdjacencyMap e a
- Algebra.Graph.Labelled.AdjacencyMap.Internal: [adjacencyMap] :: AdjacencyMap e a -> Map a (Map a e)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: consistent :: (Ord a, Eq e, Monoid e) => AdjacencyMap e a -> Bool
- Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (Control.DeepSeq.NFData a, Control.DeepSeq.NFData e) => Control.DeepSeq.NFData (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Eq a, GHC.Classes.Eq e) => GHC.Classes.Eq (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Eq e, Algebra.Graph.Label.Dioid e, GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a, GHC.Classes.Ord e, GHC.Show.Show e) => GHC.Show.Show (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Ord e, GHC.Base.Monoid e, GHC.Classes.Ord a) => GHC.Classes.Ord (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: instance GHC.Generics.Generic (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.Labelled.AdjacencyMap.Internal: newtype AdjacencyMap e a
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: NAM :: AdjacencyMap a -> AdjacencyMap a
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: [am] :: AdjacencyMap a -> AdjacencyMap a
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: consistent :: Ord a => AdjacencyMap a -> Bool
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance GHC.Generics.Generic (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.NonEmpty.AdjacencyMap.Internal: newtype AdjacencyMap a
- Algebra.Graph.Relation.Internal: Relation :: Set a -> Set (a, a) -> Relation a
- Algebra.Graph.Relation.Internal: [domain] :: Relation a -> Set a
- Algebra.Graph.Relation.Internal: [relation] :: Relation a -> Set (a, a)
- Algebra.Graph.Relation.Internal: connect :: Ord a => Relation a -> Relation a -> Relation a
- Algebra.Graph.Relation.Internal: consistent :: Ord a => Relation a -> Bool
- Algebra.Graph.Relation.Internal: data Relation a
- Algebra.Graph.Relation.Internal: empty :: Relation a
- Algebra.Graph.Relation.Internal: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.Internal: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.Internal: overlay :: Ord a => Relation a -> Relation a -> Relation a
- Algebra.Graph.Relation.Internal: referredToVertexSet :: Ord a => Set (a, a) -> Set a
- Algebra.Graph.Relation.Internal: setProduct :: Set a -> Set b -> Set (a, b)
- Algebra.Graph.Relation.Internal: vertex :: a -> Relation a
- Algebra.Graph.Relation.InternalDerived: PreorderRelation :: Relation a -> PreorderRelation a
- Algebra.Graph.Relation.InternalDerived: ReflexiveRelation :: Relation a -> ReflexiveRelation a
- Algebra.Graph.Relation.InternalDerived: TransitiveRelation :: Relation a -> TransitiveRelation a
- Algebra.Graph.Relation.InternalDerived: [fromPreorder] :: PreorderRelation a -> Relation a
- Algebra.Graph.Relation.InternalDerived: [fromReflexive] :: ReflexiveRelation a -> Relation a
- Algebra.Graph.Relation.InternalDerived: [fromTransitive] :: TransitiveRelation a -> Relation a
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Preorder (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Reflexive (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Reflexive (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Transitive (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Transitive (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
- Algebra.Graph.Relation.InternalDerived: newtype PreorderRelation a
- Algebra.Graph.Relation.InternalDerived: newtype ReflexiveRelation a
- Algebra.Graph.Relation.InternalDerived: newtype TransitiveRelation a
- Algebra.Graph.Relation.Symmetric.Internal: SR :: Relation a -> Relation a
- Algebra.Graph.Relation.Symmetric.Internal: connect :: Ord a => Relation a -> Relation a -> Relation a
- Algebra.Graph.Relation.Symmetric.Internal: consistent :: Ord a => Relation a -> Bool
- Algebra.Graph.Relation.Symmetric.Internal: edgeSet :: Ord a => Relation a -> Set (a, a)
- Algebra.Graph.Relation.Symmetric.Internal: empty :: Relation a
- Algebra.Graph.Relation.Symmetric.Internal: fromSymmetric :: Relation a -> Relation a
- Algebra.Graph.Relation.Symmetric.Internal: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Relation.Symmetric.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Relation.Symmetric.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Relation.Symmetric.Internal: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Relation.Symmetric.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Relation.Symmetric.Internal: newtype Relation a
- Algebra.Graph.Relation.Symmetric.Internal: overlay :: Ord a => Relation a -> Relation a -> Relation a
- Algebra.Graph.Relation.Symmetric.Internal: vertex :: a -> Relation a
- Algebra.Graph.ToGraph: instance (GHC.Classes.Eq e, GHC.Base.Monoid e, GHC.Classes.Ord a) => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
- Algebra.Graph.ToGraph: instance Algebra.Graph.ToGraph.ToGraph Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
- Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.ToGraph: size :: ToGraph t => t -> Int
+ Algebra.Graph: buildg :: (forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b) -> Graph a
+ Algebra.Graph: induceJust :: Graph (Maybe a) -> Graph a
+ Algebra.Graph: instance GHC.Generics.Generic (Algebra.Graph.Graph a)
+ Algebra.Graph.Acyclic.AdjacencyMap: adjacencyList :: AdjacencyMap a -> [(a, [a])]
+ Algebra.Graph.Acyclic.AdjacencyMap: box :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (a, b)
+ Algebra.Graph.Acyclic.AdjacencyMap: consistent :: Ord a => AdjacencyMap a -> Bool
+ Algebra.Graph.Acyclic.AdjacencyMap: data AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: edgeCount :: AdjacencyMap a -> Int
+ Algebra.Graph.Acyclic.AdjacencyMap: edgeList :: AdjacencyMap a -> [(a, a)]
+ Algebra.Graph.Acyclic.AdjacencyMap: edgeSet :: Eq a => AdjacencyMap a -> Set (a, a)
+ Algebra.Graph.Acyclic.AdjacencyMap: empty :: AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: fromAcyclic :: AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool
+ Algebra.Graph.Acyclic.AdjacencyMap: hasVertex :: Ord a => a -> AdjacencyMap a -> Bool
+ Algebra.Graph.Acyclic.AdjacencyMap: induce :: (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: induceJust :: Ord a => AdjacencyMap (Maybe a) -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Acyclic.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.Acyclic.AdjacencyMap: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Acyclic.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.Acyclic.AdjacencyMap: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Acyclic.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.Acyclic.AdjacencyMap: isEmpty :: AdjacencyMap a -> Bool
+ Algebra.Graph.Acyclic.AdjacencyMap: isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool
+ Algebra.Graph.Acyclic.AdjacencyMap: join :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (Either a b)
+ Algebra.Graph.Acyclic.AdjacencyMap: postSet :: Ord a => a -> AdjacencyMap a -> Set a
+ Algebra.Graph.Acyclic.AdjacencyMap: preSet :: Ord a => a -> AdjacencyMap a -> Set a
+ Algebra.Graph.Acyclic.AdjacencyMap: removeEdge :: Ord a => a -> a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: removeVertex :: Ord a => a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: scc :: Ord a => AdjacencyMap a -> AdjacencyMap (AdjacencyMap a)
+ Algebra.Graph.Acyclic.AdjacencyMap: shrink :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: toAcyclic :: Ord a => AdjacencyMap a -> Maybe (AdjacencyMap a)
+ Algebra.Graph.Acyclic.AdjacencyMap: toAcyclicOrd :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: topSort :: Ord a => AdjacencyMap a -> [a]
+ Algebra.Graph.Acyclic.AdjacencyMap: transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: union :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (Either a b)
+ Algebra.Graph.Acyclic.AdjacencyMap: vertex :: a -> AdjacencyMap a
+ Algebra.Graph.Acyclic.AdjacencyMap: vertexCount :: AdjacencyMap a -> Int
+ Algebra.Graph.Acyclic.AdjacencyMap: vertexList :: AdjacencyMap a -> [a]
+ Algebra.Graph.Acyclic.AdjacencyMap: vertexSet :: AdjacencyMap a -> Set a
+ Algebra.Graph.Acyclic.AdjacencyMap: vertices :: Ord a => [a] -> AdjacencyMap a
+ Algebra.Graph.AdjacencyIntMap: consistent :: AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: fromAdjacencyMap :: AdjacencyMap Int -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: instance Control.DeepSeq.NFData Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: instance GHC.Classes.Eq Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: instance GHC.Classes.Ord Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: instance GHC.Generics.Generic Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: instance GHC.Num.Num Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: instance GHC.Show.Show Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Algorithm: bfs :: [Int] -> AdjacencyIntMap -> [[Int]]
+ Algebra.Graph.AdjacencyIntMap.Algorithm: bfsForest :: [Int] -> AdjacencyIntMap -> Forest Int
+ Algebra.Graph.AdjacencyIntMap.Algorithm: type Cycle = NonEmpty
+ Algebra.Graph.AdjacencyMap: box :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (a, b)
+ Algebra.Graph.AdjacencyMap: consistent :: Ord a => AdjacencyMap a -> Bool
+ Algebra.Graph.AdjacencyMap: induceJust :: Ord a => AdjacencyMap (Maybe a) -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap: instance GHC.Generics.Generic (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap.Algorithm: bfs :: Ord a => [a] -> AdjacencyMap a -> [[a]]
+ Algebra.Graph.AdjacencyMap.Algorithm: bfsForest :: Ord a => [a] -> AdjacencyMap a -> Forest a
+ Algebra.Graph.AdjacencyMap.Algorithm: instance (GHC.Show.Show a, GHC.Classes.Ord a) => GHC.Show.Show (Algebra.Graph.AdjacencyMap.Algorithm.StateSCC a)
+ Algebra.Graph.AdjacencyMap.Algorithm: type Cycle = NonEmpty
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: biclique :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: circuit :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: connect :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: connects :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: consistent :: (Ord a, Ord b) => AdjacencyMap a b -> Bool
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: data AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: detectParts :: Ord a => AdjacencyMap a -> Either (OddCycle a) (AdjacencyMap a a)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: edge :: a -> b -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: edgeCount :: AdjacencyMap a b -> Int
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: edgeList :: AdjacencyMap a b -> [(a, b)]
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: edgeSet :: (Ord a, Ord b) => AdjacencyMap a b -> Set (a, b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: edges :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: empty :: AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: fromBipartite :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap (Either a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: fromBipartiteWith :: Ord c => (a -> c) -> (b -> c) -> AdjacencyMap a b -> AdjacencyMap c
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: hasEdge :: (Ord a, Ord b) => a -> b -> AdjacencyMap a b -> Bool
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: hasLeftVertex :: Ord a => a -> AdjacencyMap a b -> Bool
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: hasRightVertex :: Ord b => b -> AdjacencyMap a b -> Bool
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: hasVertex :: (Ord a, Ord b) => Either a b -> AdjacencyMap a b -> Bool
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Eq (Algebra.Graph.Bipartite.Undirected.AdjacencyMap.AdjacencyMap a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (Algebra.Graph.Bipartite.Undirected.AdjacencyMap.AdjacencyMap a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Classes.Ord b, GHC.Num.Num b) => GHC.Num.Num (Algebra.Graph.Bipartite.Undirected.AdjacencyMap.AdjacencyMap a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Classes.Ord b, GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Algebra.Graph.Bipartite.Undirected.AdjacencyMap.AdjacencyMap a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance GHC.Classes.Eq Algebra.Graph.Bipartite.Undirected.AdjacencyMap.Part
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance GHC.Generics.Generic (Algebra.Graph.Bipartite.Undirected.AdjacencyMap.AdjacencyMap a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: instance GHC.Show.Show Algebra.Graph.Bipartite.Undirected.AdjacencyMap.Part
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: isEmpty :: AdjacencyMap a b -> Bool
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: leftAdjacencyMap :: AdjacencyMap a b -> Map a (Set b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: leftVertex :: a -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: leftVertexCount :: AdjacencyMap a b -> Int
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: leftVertexList :: AdjacencyMap a b -> [a]
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: leftVertexSet :: AdjacencyMap a b -> Set a
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: overlay :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: overlays :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: rightAdjacencyMap :: AdjacencyMap a b -> Map b (Set a)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: rightVertex :: b -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: rightVertexCount :: AdjacencyMap a b -> Int
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: rightVertexList :: AdjacencyMap a b -> [b]
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: rightVertexSet :: AdjacencyMap a b -> Set b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: swap :: AdjacencyMap a b -> AdjacencyMap b a
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: toBipartite :: (Ord a, Ord b) => AdjacencyMap (Either a b) -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c) -> AdjacencyMap a -> AdjacencyMap b c
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: type OddCycle a = [a]
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: vertex :: Either a b -> AdjacencyMap a b
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: vertexCount :: AdjacencyMap a b -> Int
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: vertexList :: AdjacencyMap a b -> [Either a b]
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: vertexSet :: (Ord a, Ord b) => AdjacencyMap a b -> Set (Either a b)
+ Algebra.Graph.Bipartite.Undirected.AdjacencyMap: vertices :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b
+ Algebra.Graph.Class: instance (Algebra.Graph.Label.Dioid e, GHC.Classes.Eq e, GHC.Classes.Ord a) => Algebra.Graph.Class.Graph (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Graph (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Graph Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Undirected (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Undirected (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Internal: coerce00 :: Coercible f g => f x -> g x
+ Algebra.Graph.Internal: coerce01 :: (Coercible a b, Coercible f g) => (f x -> a) -> g x -> b
+ Algebra.Graph.Internal: coerce10 :: (Coercible a b, Coercible f g) => (a -> f x) -> b -> g x
+ Algebra.Graph.Internal: coerce11 :: (Coercible a b, Coercible c d, Coercible f g) => (a -> f x -> c) -> b -> g x -> d
+ Algebra.Graph.Internal: coerce20 :: (Coercible a b, Coercible c d, Coercible f g) => (a -> c -> f x) -> b -> d -> g x
+ Algebra.Graph.Internal: coerce21 :: (Coercible a b, Coercible c d, Coercible p q, Coercible f g) => (a -> c -> f x -> p) -> b -> d -> g x -> q
+ Algebra.Graph.Internal: forEach :: Applicative f => Set a -> (a -> f b) -> f ()
+ Algebra.Graph.Internal: forEachInt :: Applicative f => IntSet -> (Int -> f a) -> f ()
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Eq a) => GHC.Num.Num (Algebra.Graph.Label.Extended a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Labelled: induceJust :: Graph e (Maybe a) -> Graph e a
+ Algebra.Graph.Labelled: instance (Control.DeepSeq.NFData e, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.Labelled: instance GHC.Generics.Generic (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.Labelled.AdjacencyMap: consistent :: (Ord a, Eq e, Monoid e) => AdjacencyMap e a -> Bool
+ Algebra.Graph.Labelled.AdjacencyMap: induceJust :: Ord a => AdjacencyMap e (Maybe a) -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: instance (Control.DeepSeq.NFData a, Control.DeepSeq.NFData e) => Control.DeepSeq.NFData (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap: instance (GHC.Classes.Eq a, GHC.Classes.Eq e) => GHC.Classes.Eq (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap: instance (GHC.Classes.Eq e, Algebra.Graph.Label.Dioid e, GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Show.Show a, GHC.Classes.Ord e, GHC.Show.Show e) => GHC.Show.Show (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap: instance (GHC.Classes.Ord e, GHC.Base.Monoid e, GHC.Classes.Ord a) => GHC.Classes.Ord (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap: instance GHC.Generics.Generic (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.NonEmpty: induceJust1 :: Graph (Maybe a) -> Maybe (Graph a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: consistent :: Ord a => AdjacencyMap a -> Bool
+ Algebra.Graph.NonEmpty.AdjacencyMap: fromNonEmpty :: AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: induceJust1 :: Ord a => AdjacencyMap (Maybe a) -> Maybe (AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: instance GHC.Generics.Generic (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.Relation: consistent :: Ord a => Relation a -> Bool
+ Algebra.Graph.Relation: induceJust :: Ord a => Relation (Maybe a) -> Relation a
+ Algebra.Graph.Relation: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.Relation: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.Relation: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.Relation: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.Relation: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.Relation.Preorder: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance GHC.Classes.Ord a => Algebra.Graph.Class.Preorder (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance GHC.Classes.Ord a => Algebra.Graph.Class.Reflexive (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance GHC.Classes.Ord a => Algebra.Graph.Class.Transitive (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Preorder: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Preorder.PreorderRelation a)
+ Algebra.Graph.Relation.Reflexive: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Reflexive: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Reflexive: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Reflexive: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Reflexive: instance GHC.Classes.Ord a => Algebra.Graph.Class.Reflexive (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Reflexive: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Reflexive: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Reflexive.ReflexiveRelation a)
+ Algebra.Graph.Relation.Symmetric: consistent :: Ord a => Relation a -> Bool
+ Algebra.Graph.Relation.Symmetric: induceJust :: Ord a => Relation (Maybe a) -> Relation a
+ Algebra.Graph.Relation.Symmetric: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Relation.Symmetric: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Relation.Symmetric: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Relation.Symmetric: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Relation.Symmetric: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Relation.Transitive: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.Relation.Transitive: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.Relation.Transitive: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.Relation.Transitive: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.Relation.Transitive: instance GHC.Classes.Ord a => Algebra.Graph.Class.Transitive (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.Relation.Transitive: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.Relation.Transitive: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Transitive.TransitiveRelation a)
+ Algebra.Graph.ToGraph: instance (GHC.Classes.Eq e, GHC.Base.Monoid e, GHC.Classes.Ord a) => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Labelled.AdjacencyMap.AdjacencyMap e a)
+ Algebra.Graph.ToGraph: instance Algebra.Graph.ToGraph.ToGraph Algebra.Graph.AdjacencyIntMap.AdjacencyIntMap
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap a)
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Relation.Relation a)
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Relation.Symmetric.Relation a)
+ Algebra.Graph.Undirected: adjacencyList :: Ord a => Graph a -> [(a, [a])]
+ Algebra.Graph.Undirected: biclique :: [a] -> [a] -> Graph a
+ Algebra.Graph.Undirected: circuit :: [a] -> Graph a
+ Algebra.Graph.Undirected: clique :: [a] -> Graph a
+ Algebra.Graph.Undirected: complement :: Ord a => Graph a -> Graph a
+ Algebra.Graph.Undirected: connect :: Graph a -> Graph a -> Graph a
+ Algebra.Graph.Undirected: connects :: [Graph a] -> Graph a
+ Algebra.Graph.Undirected: data Graph a
+ Algebra.Graph.Undirected: edge :: a -> a -> Graph a
+ Algebra.Graph.Undirected: edgeCount :: Ord a => Graph a -> Int
+ Algebra.Graph.Undirected: edgeList :: Ord a => Graph a -> [(a, a)]
+ Algebra.Graph.Undirected: edgeSet :: Ord a => Graph a -> Set (a, a)
+ Algebra.Graph.Undirected: edges :: [(a, a)] -> Graph a
+ Algebra.Graph.Undirected: empty :: Graph a
+ Algebra.Graph.Undirected: foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b
+ Algebra.Graph.Undirected: forest :: Forest a -> Graph a
+ Algebra.Graph.Undirected: fromUndirected :: Ord a => Graph a -> Graph a
+ Algebra.Graph.Undirected: hasEdge :: Eq a => a -> a -> Graph a -> Bool
+ Algebra.Graph.Undirected: hasVertex :: Eq a => a -> Graph a -> Bool
+ Algebra.Graph.Undirected: induce :: (a -> Bool) -> Graph a -> Graph a
+ Algebra.Graph.Undirected: induceJust :: Graph (Maybe a) -> Graph a
+ Algebra.Graph.Undirected: instance (GHC.Show.Show a, GHC.Classes.Ord a) => GHC.Show.Show (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Undirected: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Undirected: instance GHC.Base.Alternative Algebra.Graph.Undirected.Graph
+ Algebra.Graph.Undirected: instance GHC.Base.Applicative Algebra.Graph.Undirected.Graph
+ Algebra.Graph.Undirected: instance GHC.Base.Functor Algebra.Graph.Undirected.Graph
+ Algebra.Graph.Undirected: instance GHC.Base.Monad Algebra.Graph.Undirected.Graph
+ Algebra.Graph.Undirected: instance GHC.Base.MonadPlus Algebra.Graph.Undirected.Graph
+ Algebra.Graph.Undirected: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Undirected: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Undirected: instance GHC.Generics.Generic (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Undirected: instance GHC.Num.Num a => GHC.Num.Num (Algebra.Graph.Undirected.Graph a)
+ Algebra.Graph.Undirected: isEmpty :: Graph a -> Bool
+ Algebra.Graph.Undirected: isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool
+ Algebra.Graph.Undirected: mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a
+ Algebra.Graph.Undirected: neighbours :: Ord a => a -> Graph a -> Set a
+ Algebra.Graph.Undirected: overlay :: Graph a -> Graph a -> Graph a
+ Algebra.Graph.Undirected: overlays :: [Graph a] -> Graph a
+ Algebra.Graph.Undirected: path :: [a] -> Graph a
+ Algebra.Graph.Undirected: removeEdge :: Eq a => a -> a -> Graph a -> Graph a
+ Algebra.Graph.Undirected: removeVertex :: Eq a => a -> Graph a -> Graph a
+ Algebra.Graph.Undirected: replaceVertex :: Eq a => a -> a -> Graph a -> Graph a
+ Algebra.Graph.Undirected: size :: Graph a -> Int
+ Algebra.Graph.Undirected: star :: a -> [a] -> Graph a
+ Algebra.Graph.Undirected: stars :: [(a, [a])] -> Graph a
+ Algebra.Graph.Undirected: toRelation :: Ord a => Graph a -> Relation a
+ Algebra.Graph.Undirected: toUndirected :: Graph a -> Graph a
+ Algebra.Graph.Undirected: tree :: Tree a -> Graph a
+ Algebra.Graph.Undirected: vertex :: a -> Graph a
+ Algebra.Graph.Undirected: vertexCount :: Ord a => Graph a -> Int
+ Algebra.Graph.Undirected: vertexList :: Ord a => Graph a -> [a]
+ Algebra.Graph.Undirected: vertexSet :: Ord a => Graph a -> Set a
+ Algebra.Graph.Undirected: vertices :: [a] -> Graph a
+ Data.Graph.Typed: scc :: Ord a => AdjacencyMap a -> AdjacencyMap (AdjacencyMap a)
- Algebra.Graph.AdjacencyIntMap.Algorithm: topSort :: AdjacencyIntMap -> Maybe [Int]
+ Algebra.Graph.AdjacencyIntMap.Algorithm: topSort :: AdjacencyIntMap -> Either (Cycle Int) [Int]
- Algebra.Graph.AdjacencyMap.Algorithm: topSort :: Ord a => AdjacencyMap a -> Maybe [a]
+ Algebra.Graph.AdjacencyMap.Algorithm: topSort :: Ord a => AdjacencyMap a -> Either (Cycle a) [a]
- Algebra.Graph.HigherKinded.Class: class (MonadPlus g) => Graph g
+ Algebra.Graph.HigherKinded.Class: class MonadPlus g => Graph g
- Algebra.Graph.Label: type CountShortestPaths e a = Optimum (Distance e) (Count Integer)
+ Algebra.Graph.Label: type CountShortestPaths e = Optimum (Distance e) (Count Integer)
- Algebra.Graph.Labelled.AdjacencyMap: skeleton :: AdjacencyMap e a -> AdjacencyMap a
+ Algebra.Graph.Labelled.AdjacencyMap: skeleton :: Ord a => AdjacencyMap e a -> AdjacencyMap a
- Algebra.Graph.ToGraph: adjacencyMap :: (ToGraph t, Ord (ToVertex t)) => t -> Map (ToVertex t) (Set (ToVertex t))
+ Algebra.Graph.ToGraph: adjacencyMap :: ToGraph t => Ord (ToVertex t) => t -> Map (ToVertex t) (Set (ToVertex t))
- Algebra.Graph.ToGraph: topSort :: (ToGraph t, Ord (ToVertex t)) => t -> Maybe [ToVertex t]
+ Algebra.Graph.ToGraph: topSort :: (ToGraph t, Ord (ToVertex t)) => t -> Either (Cycle (ToVertex t)) [ToVertex t]

Files

+ AUTHORS.md view
@@ -0,0 +1,23 @@+The Alga library was originally developed by++* [Andrey Mokhov](mailto:andrey.mokhov@gmail.com) [@snowleopard](https://github.com/snowleopard)++but over time many contributors helped make it much better, including (among others):++* [Vasily Alferov](mailto:vasily.v.alferov@gmail.com) [@vasalf](https://github.com/vasalf)+* [Piotr Gawryś](mailto:pgawrys2@gmail.com) [@Avasil](https://github.com/Avasil)+* [Alexandre Moine](mailto:alexandre@moine.me) [@nobrakal](https://github.com/nobrakal)+* [Joseph Novakovich](mailto:jrn@bluefarm.ca) [@jitwit](https://github.com/jitwit)+* [Adithya Obilisetty](mailto:adi.obilisetty@gmail.com) [@adithyaov](https://github.com/adithyaov)+* [Armando Santos](mailto:armandoifsantos@gmail.com) [@bolt12](https://github.com/bolt12)++If you are not on this list, it's not because your contributions are not+appreciated, but because I didn't want to add your name and contact details+without your consent. Please fix this by sending a PR, keeping the list+alphabetical (sorted by last and then first name).++Also see the autogenerated yet still possibly incomplete+[list of contributors](https://github.com/snowleopard/alga/graphs/contributors).++Thank you all for your help!+Andrey
CHANGES.md view
@@ -1,5 +1,28 @@ # Change log +## 0.5++* #217, #224, #227, #234, #235: Add new BFS, DFS, topological sort, and SCC+                                algorithms for adjacency maps.+* #228, #247, #254: Improve algebraic graph fusion.+* #207, #218, #255: Add `Bipartite.Undirected.AdjacencyMap`.+* #220, #237, #255: Add `Algebra.Graph.Undirected`.+* #203, #215, #223: Add `Acyclic.AdjacencyMap`.+* #202, #209, #211: Add `induceJust` and `induceJust1`.+* #172, #245: Stop supporting GHC 7.8.4 and GHC 7.10.3.+* #208: Add `fromNonEmpty` to `NonEmpty.AdjacencyMap`.+* #208: Add `fromAdjacencyMap` to `AdjacencyIntMap`.+* #208: Drop `Internal` modules for `AdjacencyIntMap`, `AdjacencyMap`,+        `Labelled.AdjacencyMap`, `NonEmpty.AdjacencyMap`, `Relation` and+        `Relation.Symmetric`.+* #206: Add `Algebra.Graph.AdjacencyMap.box`.+* #205: Drop dependencies on `base-compat` and `base-orphans`.+* #205: Remove `Algebra.Graph.Fold`.+* #151: Remove `ToGraph.size`. Demote `ToGraph.adjacencyMap`,+        `ToGraph.adjacencyIntMap`, `ToGraph.adjacencyMapTranspose` and+        `ToGraph.adjacencyIntMapTranspose` to functions.+* #204: Derive `Generic` and `NFData` for `Algebra.Graph` and `Algebra.Graph.Labelled`.+ ## 0.4  * #174: Add `Symmetric.Relation`.
LICENSE view
@@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016-2018 Andrey Mokhov+Copyright (c) 2016-2020 Andrey Mokhov  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
algebraic-graphs.cabal view
@@ -1,5 +1,6 @@+cabal-version: 2.2 name:          algebraic-graphs-version:       0.4+version:       0.5 synopsis:      A library for algebraic graph construction and transformation license:       MIT license-file:  LICENSE@@ -10,13 +11,11 @@ homepage:      https://github.com/snowleopard/alga category:      Algebra, Algorithms, Data Structures, Graphs build-type:    Simple-cabal-version: 1.18-tested-with:   GHC==7.8.4,-               GHC==7.10.3,-               GHC==8.0.2,-               GHC==8.2.2,-               GHC==8.4.3,-               GHC==8.6.4+tested-with:   GHC == 8.0.2,+               GHC == 8.2.2,+               GHC == 8.4.4,+               GHC == 8.6.5,+               GHC == 8.8.1 stability:     experimental description:     <https://github.com/snowleopard/alga Alga> is a library for algebraic construction and@@ -50,15 +49,14 @@     <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Class.html Algebra.Graph.Class>     and     <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-HigherKinded-Class.html Algebra.Graph.HigherKinded.Class>-    can be used for polymorphic construction and manipulation of graphs. Also see-    <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Fold.html Algebra.Graph.Fold>-    that defines the Boehm-Berarducci encoding of algebraic graphs.+    can be used for polymorphic construction and manipulation of graphs.     .     This is an experimental library and the API is expected to remain unstable until version 1.0.0.     Please consider contributing to the on-going     <https://github.com/snowleopard/alga/issues discussions on the library API>.  extra-doc-files:+    AUTHORS.md     CHANGES.md     README.md @@ -66,122 +64,97 @@     type:     git     location: https://github.com/snowleopard/alga.git +common common-settings+    build-depends:      array        >= 0.4     && < 0.6,+                        base         >= 4.7     && < 5,+                        containers   >= 0.5.5.1 && < 0.8,+                        deepseq      >= 1.3.0.1 && < 1.5,+                        mtl          >= 2.1     && < 2.3,+                        transformers >= 0.4     && < 0.6+    default-language:   Haskell2010+    default-extensions: FlexibleContexts+                        FlexibleInstances+                        GeneralizedNewtypeDeriving+                        ScopedTypeVariables+                        TupleSections+                        TypeApplications+                        TypeFamilies+    other-extensions:   CPP+                        DeriveFunctor+                        OverloadedStrings+                        RankNTypes+                        RecordWildCards+    GHC-options:        -Wall+                        -Wcompat+                        -Wincomplete-record-updates+                        -Wincomplete-uni-patterns+                        -Wredundant-constraints+                        -fno-warn-name-shadowing+                        -fspec-constr+ library+    import:             common-settings     hs-source-dirs:     src     exposed-modules:    Algebra.Graph,+                        Algebra.Graph.Undirected,+                        Algebra.Graph.Acyclic.AdjacencyMap,                         Algebra.Graph.AdjacencyIntMap,                         Algebra.Graph.AdjacencyIntMap.Algorithm,-                        Algebra.Graph.AdjacencyIntMap.Internal,                         Algebra.Graph.AdjacencyMap,                         Algebra.Graph.AdjacencyMap.Algorithm,-                        Algebra.Graph.AdjacencyMap.Internal,+                        Algebra.Graph.Bipartite.Undirected.AdjacencyMap,                         Algebra.Graph.Class,                         Algebra.Graph.Export,                         Algebra.Graph.Export.Dot,-                        Algebra.Graph.Fold,                         Algebra.Graph.HigherKinded.Class,                         Algebra.Graph.Internal,                         Algebra.Graph.Label,                         Algebra.Graph.Labelled,                         Algebra.Graph.Labelled.AdjacencyMap,-                        Algebra.Graph.Labelled.AdjacencyMap.Internal,                         Algebra.Graph.Labelled.Example.Automaton,                         Algebra.Graph.Labelled.Example.Network,                         Algebra.Graph.NonEmpty,                         Algebra.Graph.NonEmpty.AdjacencyMap,-                        Algebra.Graph.NonEmpty.AdjacencyMap.Internal,                         Algebra.Graph.Relation,-                        Algebra.Graph.Relation.Internal,-                        Algebra.Graph.Relation.InternalDerived,                         Algebra.Graph.Relation.Preorder,                         Algebra.Graph.Relation.Reflexive,                         Algebra.Graph.Relation.Symmetric,-                        Algebra.Graph.Relation.Symmetric.Internal,                         Algebra.Graph.Relation.Transitive,                         Algebra.Graph.ToGraph,                         Data.Graph.Typed-    build-depends:      array       >= 0.4     && < 0.6,-                        base        >= 4.7     && < 5,-                        base-compat >= 0.9.1   && < 0.11,-                        containers  >= 0.5.5.1 && < 0.8,-                        deepseq     >= 1.3.0.1 && < 1.5,-                        mtl         >= 2.1     && < 2.3-    if !impl(ghc >= 8.0)-        build-depends:  semigroups  >= 0.18.3  && < 0.18.4-    if !impl(ghc >= 7.10)-        build-depends:  bifunctors  >= 5       && < 5.6-    default-language:   Haskell2010-    default-extensions: FlexibleContexts-                        FlexibleInstances-                        GeneralizedNewtypeDeriving-                        ScopedTypeVariables-                        TupleSections-                        TypeFamilies-    other-extensions:   CPP-                        DeriveFunctor-                        OverloadedStrings-                        RecordWildCards-    GHC-options:        -Wall-                        -fno-warn-name-shadowing-                        -fspec-constr-    if impl(ghc >= 8.0)-        GHC-options:    -Wcompat-                        -Wincomplete-record-updates-                        -Wincomplete-uni-patterns-                        -Wredundant-constraints  test-suite test-alga+    import:             common-settings     hs-source-dirs:     test     type:               exitcode-stdio-1.0     main-is:            Main.hs     other-modules:      Algebra.Graph.Test,                         Algebra.Graph.Test.API,+                        Algebra.Graph.Test.Acyclic.AdjacencyMap,                         Algebra.Graph.Test.AdjacencyIntMap,                         Algebra.Graph.Test.AdjacencyMap,                         Algebra.Graph.Test.Arbitrary,+                        Algebra.Graph.Test.Bipartite.Undirected.AdjacencyMap,                         Algebra.Graph.Test.Export,-                        Algebra.Graph.Test.Fold,                         Algebra.Graph.Test.Generic,                         Algebra.Graph.Test.Graph,+                        Algebra.Graph.Test.Undirected,                         Algebra.Graph.Test.Internal,+                        Algebra.Graph.Test.Label,                         Algebra.Graph.Test.Labelled.AdjacencyMap,                         Algebra.Graph.Test.Labelled.Graph,                         Algebra.Graph.Test.NonEmpty.AdjacencyMap,                         Algebra.Graph.Test.NonEmpty.Graph,                         Algebra.Graph.Test.Relation,                         Algebra.Graph.Test.Relation.SymmetricRelation,+                        Algebra.Graph.Test.RewriteRules,                         Data.Graph.Test.Typed-    if impl(ghc >= 8.0.2)-        other-modules:  Algebra.Graph.Test.RewriteRules     build-depends:      algebraic-graphs,-                        array        >= 0.4     && < 0.6,-                        base         >= 4.7     && < 5,-                        base-compat  >= 0.9.1   && < 0.11,-                        base-orphans >= 0.5.4   && < 0.9,-                        containers   >= 0.5.5.1 && < 0.8,-                        extra        >= 1.5     && < 2,-                        QuickCheck   >= 2.9     && < 2.14-    if !impl(ghc >= 8.0)-        build-depends:  semigroups   >= 0.18.3  && < 0.18.4-    if impl(ghc >= 8.0.2)-        build-depends:  inspection-testing >= 0.4 && < 0.5--    default-language:   Haskell2010-    GHC-options:        -Wall-                        -fno-warn-name-shadowing-                        -fspec-constr-    if impl(ghc >= 8.0)-        GHC-options:    -Wcompat-                        -Wincomplete-record-updates-                        -Wincomplete-uni-patterns-                        -Wredundant-constraints-    default-extensions: FlexibleContexts-                        FlexibleInstances-                        GeneralizedNewtypeDeriving-                        ScopedTypeVariables-                        TupleSections-                        TypeFamilies+                        extra              >= 1.4     && < 2,+                        inspection-testing >= 0.4.2.2 && < 0.5,+                        QuickCheck         >= 2.10    && < 2.14     other-extensions:   ConstrainedClassMethods                         ConstraintKinds-                        RankNTypes+                        MultiParamTypeClasses+                        TemplateHaskell                         ViewPatterns
src/Algebra/Graph.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DeriveGeneric, RankNTypes #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -26,7 +26,7 @@     empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,      -- * Graph folding-    foldg,+    foldg, buildg,      -- * Relations on graphs     isSubgraphOf, (===),@@ -41,7 +41,7 @@      -- * Graph transformation     removeVertex, removeEdge, replaceVertex, mergeVertices, splitVertex,-    transpose, induce, simplify, sparsify, sparsifyKL,+    transpose, induce, induceJust, simplify, sparsify, sparsifyKL,      -- * Graph composition     compose, box,@@ -50,17 +50,15 @@     Context (..), context     ) where -import Prelude ()-import Prelude.Compat- import Control.Applicative (Alternative)-import Control.DeepSeq (NFData (..))-import Control.Monad.Compat+import Control.DeepSeq+import Control.Monad (MonadPlus (..)) import Control.Monad.State (runState, get, put) import Data.Foldable (toList) import Data.Maybe (fromMaybe) import Data.Semigroup ((<>)) import Data.Tree+import GHC.Generics  import Algebra.Graph.Internal @@ -175,12 +173,16 @@ @'empty' <= x x     <= x + y x + y <= x * y@++Deforestation (fusion) is implemented for some functions in this module. This means+that when a function tagged as a \"good producer\" is composed with a \"good consumer\",+the intermediate structure will not be built. -} data Graph a = Empty              | Vertex a              | Overlay (Graph a) (Graph a)              | Connect (Graph a) (Graph a)-             deriving (Show)+             deriving (Show, Generic)  {- Note [Functions for rewrite rules] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -211,12 +213,10 @@ creating our own intermediate functions for guiding rewrite rules when needed. -} +-- | 'fmap' is a good consumer and producer. instance Functor Graph where-    fmap = fmapR--fmapR :: (a -> b) -> Graph a -> Graph b-fmapR f g = bindR g (vertex . f)-{-# INLINE fmapR #-}+    fmap f g = g >>= (vertex . f)+    {-# INLINE fmap #-}  instance NFData a => NFData (Graph a) where     rnf Empty         = ()@@ -234,9 +234,11 @@     abs         = id     negate      = id +-- | `==` is a good consumer of both arguments. instance Ord a => Eq (Graph a) where     (==) = eqR +-- | 'compare' is a good consumer of both arguments. instance Ord a => Ord (Graph a) where     compare = ordR @@ -244,39 +246,40 @@ -- Check if two graphs are equal by converting them to their adjacency maps. eqR :: Ord a => Graph a -> Graph a -> Bool eqR x y = toAdjacencyMap x == toAdjacencyMap y-{-# NOINLINE [1] eqR #-}+{-# INLINE [2] eqR #-} {-# RULES "eqR/Int" eqR = eqIntR #-}  -- Like 'eqR' but specialised for graphs with vertices of type 'Int'. eqIntR :: Graph Int -> Graph Int -> Bool eqIntR x y = toAdjacencyIntMap x == toAdjacencyIntMap y+{-# INLINE eqIntR #-}  -- TODO: Find a more efficient comparison. -- Compare two graphs by converting them to their adjacency maps. ordR :: Ord a => Graph a -> Graph a -> Ordering ordR x y = compare (toAdjacencyMap x) (toAdjacencyMap y)-{-# NOINLINE [1] ordR #-}+{-# INLINE [2] ordR #-} {-# RULES "ordR/Int" ordR = ordIntR #-}  -- Like 'ordR' but specialised for graphs with vertices of type 'Int'. ordIntR :: Graph Int -> Graph Int -> Ordering ordIntR x y = compare (toAdjacencyIntMap x) (toAdjacencyIntMap y)+{-# INLINE ordIntR #-} +-- TODO: It should be a good consumer of its second argument too.+-- | `<*>` is a good consumer of its first agument and producer. instance Applicative Graph where-    pure  = Vertex-    (<*>) = apR--apR :: Graph (a -> b) -> Graph a -> Graph b-apR f x = bindR f (<$> x)-{-# INLINE apR #-}+    pure    = Vertex+    f <*> x = buildg $ \e v o c ->+      foldg e (\w -> foldg e (v . w) o c x) o c f+    {-# INLINE (<*>) #-} +-- | `>>=` is a good consumer and producer. instance Monad Graph where     return = pure-    (>>=)  = bindR--bindR :: Graph a -> (a -> Graph b) -> Graph b-bindR g f = foldg Empty f Overlay Connect g-{-# INLINE [0] bindR #-}+    g >>= f  = buildg $ \e v o c ->+      foldg e (composeR (foldg e v o c) f) o c g+    {-# INLINE (>>=) #-}  instance Alternative Graph where     empty = Empty@@ -306,7 +309,7 @@ -- -- @ -- 'isEmpty'     (vertex x) == False--- 'hasVertex' x (vertex x) == True+-- 'hasVertex' x (vertex y) == (x == y) -- 'vertexCount' (vertex x) == 1 -- 'edgeCount'   (vertex x) == 0 -- 'size'        (vertex x) == 1@@ -327,6 +330,7 @@ -- @ edge :: a -> a -> Graph a edge x y = connect (vertex x) (vertex y)+{-# INLINE edge #-}  -- | /Overlay/ two graphs. An alias for the constructor 'Overlay'. This is a -- commutative, associative and idempotent operation with the identity 'empty'.@@ -371,10 +375,15 @@ connect = Connect {-# INLINE connect #-} +-- TODO: Simplifiy the definition to `overlays . map vertex` while presreving+-- goodness properties (which is not trivial since overlays is only a good+-- consumer of lists and not of lists of graphs). -- | Construct the graph comprising a given list of isolated vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- vertices []            == 'empty' -- vertices [x]           == 'vertex' x@@ -383,25 +392,32 @@ -- 'vertexSet'   . vertices == Set.'Set.fromList' -- @ vertices :: [a] -> Graph a-vertices = overlays . map vertex+vertices xs = buildg $ \e v o _ -> combineR e o v xs {-# INLINE vertices #-}  -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- edges []          == 'empty' -- edges [(x,y)]     == 'edge' x y+-- edges             == 'overlays' . 'map' ('uncurry' 'edge') -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- @ edges :: [(a, a)] -> Graph a-edges = overlays . map (uncurry edge)+edges xs = buildg $ \e v o c ->+  combineR e o (\e -> c (v (fst e)) (v (snd e))) xs+{-# INLINE edges #-}  -- | Overlay a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length -- of the given list, and /S/ is the sum of sizes of the graphs in the list. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- overlays []        == 'empty' -- overlays [x]       == x@@ -410,13 +426,15 @@ -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: [Graph a] -> Graph a-overlays = fromMaybe empty . foldr1Safe overlay-{-# INLINE [1] overlays #-}+overlays xs = buildg $ \e v o c -> combineR e o (foldg e v o c) xs+{-# INLINE overlays #-}  -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length -- of the given list, and /S/ is the sum of sizes of the graphs in the list. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- connects []        == 'empty' -- connects [x]       == x@@ -425,15 +443,23 @@ -- 'isEmpty' . connects == 'all' 'isEmpty' -- @ connects :: [Graph a] -> Graph a-connects = fromMaybe empty . foldr1Safe connect-{-# INLINE [1] connects #-}+connects xs = buildg $ \e v o c -> combineR e c (foldg e v o c) xs+{-# INLINE connects #-} +-- Safe version of foldr with a map (the composition is optimized)+-- This is a good consumer of lists.+combineR :: c -> (c -> c -> c) -> (a -> c) -> [a] -> c+combineR e o f = fromMaybe e . foldr1Safe o . map f+{-# INLINE combineR #-}+ -- | Generalised 'Graph' folding: recursively collapse a 'Graph' by applying -- the provided functions to the leaves and internal nodes of the expression. -- The order of arguments is: empty, vertex, overlay and connect. -- Complexity: /O(s)/ applications of given functions. As an example, the -- complexity of 'size' is /O(s)/, since all functions have cost /O(1)/. --+-- Good consumer.+-- -- @ -- foldg 'empty' 'vertex'        'overlay' 'connect'        == id -- foldg 'empty' 'vertex'        'overlay' ('flip' 'connect') == 'transpose'@@ -459,18 +485,34 @@     foldg e v o c (Overlay x y) = o (foldg e v o c x) (foldg e v o c y) "foldg/Connect" forall e v o c x y.     foldg e v o c (Connect x y) = c (foldg e v o c x) (foldg e v o c y)--"foldg/overlays" forall e v o c xs.-    foldg e v o c (overlays xs) = fromMaybe e (foldr (maybeF o . foldg e v o c) Nothing xs)-"foldg/connects" forall e v o c xs.-    foldg e v o c (connects xs) = fromMaybe e (foldr (maybeF c . foldg e v o c) Nothing xs)  #-} +-- | Build a graph given an interpretation of the four graph construction primitives 'empty',+-- 'vertex', 'overlay' and 'connect', in this order. See examples for further clarification.+--+-- Functions expressed with 'buildg' are good producers.+--+-- @+-- buildg f                                                   == f 'empty' 'vertex' 'overlay' 'connect'+-- buildg (\\e _ _ _ -> e)                                     == 'empty'+-- buildg (\\_ v _ _ -> v x)                                   == 'vertex' x+-- buildg (\\e v o c -> o ('foldg' e v o c x) ('foldg' e v o c y)) == 'overlay' x y+-- buildg (\\e v o c -> c ('foldg' e v o c x) ('foldg' e v o c y)) == 'connect' x y+-- buildg (\\e v o _ -> 'foldr' o e ('map' v xs))                  == 'vertices' xs+-- buildg (\\e v o c -> 'foldg' e v o ('flip' c) g)                == 'transpose' g+-- 'foldg' e v o c (buildg f)                                   == f e v o c+-- @+buildg :: (forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b) -> Graph a+buildg f = f Empty Vertex Overlay Connect+{-# INLINE [1] buildg #-}+ -- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the -- first graph is a /subgraph/ of the second. -- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a -- graph can be quadratic with respect to the expression size /s/. --+-- Good consumer of both arguments.+-- -- @ -- isSubgraphOf 'empty'         x             ==  True -- isSubgraphOf ('vertex' x)    'empty'         ==  False@@ -481,12 +523,13 @@ -- @ isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool isSubgraphOf x y = AM.isSubgraphOf (toAdjacencyMap x) (toAdjacencyMap y)-{-# NOINLINE [1] isSubgraphOf #-}+{-# INLINE [2] isSubgraphOf #-} {-# RULES "isSubgraphOf/Int" isSubgraphOf = isSubgraphOfIntR #-}  -- Like 'isSubgraphOf' but specialised for graphs with vertices of type 'Int'. isSubgraphOfIntR :: Graph Int -> Graph Int -> Bool isSubgraphOfIntR x y = AIM.isSubgraphOf (toAdjacencyIntMap x) (toAdjacencyIntMap y)+{-# INLINE isSubgraphOfIntR #-}  -- | Structural equality on graph expressions. -- Complexity: /O(s)/ time.@@ -508,9 +551,11 @@  infix 4 === --- | Check if a graph is empty. A convenient alias for 'null'.+-- | Check if a graph is empty. -- Complexity: /O(s)/ time. --+-- Good consumer.+-- -- @ -- isEmpty 'empty'                       == True -- isEmpty ('overlay' 'empty' 'empty')       == True@@ -520,11 +565,14 @@ -- @ isEmpty :: Graph a -> Bool isEmpty = foldg True (const False) (&&) (&&)+{-# INLINE isEmpty #-}  -- | The /size/ of a graph, i.e. the number of leaves of the expression -- including 'empty' leaves. -- Complexity: /O(s)/ time. --+-- Good consumer.+-- -- @ -- size 'empty'         == 1 -- size ('vertex' x)    == 1@@ -535,23 +583,46 @@ -- @ size :: Graph a -> Int size = foldg 1 (const 1) (+) (+)+{-# INLINE size #-}  -- | Check if a graph contains a given vertex. -- Complexity: /O(s)/ time. --+-- Good consumer.+-- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Eq a => a -> Graph a -> Bool hasVertex x = foldg False (==x) (||) (||)+{-# INLINE hasVertex #-} {-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-} +{- Note [The implementation of hasEdge]++We fold a graph into a function of type Int -> Int where the Int stands for the+number of vertices of the specified edge that have been matched so far. The edge+belongs to the graph if we reach the number 2. Note that this algorithm can be+generalised to algebraic graphs of higher dimensions, e.g. we can similarly find+3-edges (triangles), 4-edges (tetrahedra) and k-edges in O(s) time.++The four graph constructors are interpreted as follows:++  * Empty       : the matching number is unchanged;+  * Vertex x    : if x matches the next vertex, the number is incremented;+  * Overlay x y : pick the best match in the two subexpressions;+  * Connect x y : match the subexpressions one after another.++Note that in the last two cases we can (and do) shortcircuit the computation as+soon as the edge is fully matched in one of the subexpressions.+-} -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time. --+-- Good consumer.+-- -- @ -- hasEdge x y 'empty'            == False -- hasEdge x y ('vertex' z)       == False@@ -560,23 +631,23 @@ -- hasEdge x y                  == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Eq a => a -> a -> Graph a -> Bool-hasEdge s t g = hit g == Edge+hasEdge s t g = foldg id v o c g 0 == 2   where-    hit Empty         = Miss-    hit (Vertex x   ) = if x == s then Tail else Miss-    hit (Overlay x y) = case hit x of-        Miss -> hit y-        Tail -> max Tail (hit y)-        Edge -> Edge-    hit (Connect x y) = case hit x of-        Miss -> hit y-        Tail -> if hasVertex t y then Edge else Tail-        Edge -> Edge+    v x 0   = if x == s then 1 else 0+    v x _   = if x == t then 2 else 1+    o x y a = case x a of+        0 -> y a+        1 -> if y a == 2 then 2 else 1+        _ -> 2 :: Int+    c x y a = case x a of { 2 -> 2; res -> y res }+{-# INLINE hasEdge #-} {-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-}  -- | The number of vertices in a graph. -- Complexity: /O(s * log(n))/ time. --+-- Good consumer.+-- -- @ -- vertexCount 'empty'             ==  0 -- vertexCount ('vertex' x)        ==  1@@ -585,17 +656,20 @@ -- @ vertexCount :: Ord a => Graph a -> Int vertexCount = Set.size . vertexSet-{-# INLINE [1] vertexCount #-}+{-# INLINE [2] vertexCount #-} {-# RULES "vertexCount/Int" vertexCount = vertexIntCountR #-}  -- Like 'vertexCount' but specialised for graphs with vertices of type 'Int'. vertexIntCountR :: Graph Int -> Int vertexIntCountR = IntSet.size . vertexIntSetR+{-# INLINE vertexIntCountR #-}  -- | The number of edges in a graph. -- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a -- graph can be quadratic with respect to the expression size /s/. --+-- Good consumer.+-- -- @ -- edgeCount 'empty'      == 0 -- edgeCount ('vertex' x) == 0@@ -604,16 +678,19 @@ -- @ edgeCount :: Ord a => Graph a -> Int edgeCount = AM.edgeCount . toAdjacencyMap-{-# INLINE [1] edgeCount #-}+{-# INLINE [2] edgeCount #-} {-# RULES "edgeCount/Int" edgeCount = edgeCountIntR #-}  -- Like 'edgeCount' but specialised for graphs with vertices of type 'Int'. edgeCountIntR :: Graph Int -> Int edgeCountIntR = AIM.edgeCount . toAdjacencyIntMap+{-# INLINE edgeCountIntR #-}  -- | The sorted list of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. --+-- Good consumer of graphs and producer of lists.+-- -- @ -- vertexList 'empty'      == [] -- vertexList ('vertex' x) == [x]@@ -621,17 +698,20 @@ -- @ vertexList :: Ord a => Graph a -> [a] vertexList = Set.toAscList . vertexSet-{-# INLINE [1] vertexList #-}+{-# INLINE [2] vertexList #-} {-# RULES "vertexList/Int" vertexList = vertexIntListR #-}  -- Like 'vertexList' but specialised for graphs with vertices of type 'Int'. vertexIntListR :: Graph Int -> [Int] vertexIntListR = IntSet.toList . vertexIntSetR+{-# INLINE vertexIntListR #-}  -- | The sorted list of edges of a graph. -- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of -- edges /m/ of a graph can be quadratic with respect to the expression size /s/. --+-- Good consumer of graphs and producer of lists.+-- -- @ -- edgeList 'empty'          == [] -- edgeList ('vertex' x)     == []@@ -642,16 +722,19 @@ -- @ edgeList :: Ord a => Graph a -> [(a, a)] edgeList = AM.edgeList . toAdjacencyMap-{-# INLINE [1] edgeList #-}+{-# INLINE [2] edgeList #-} {-# RULES "edgeList/Int" edgeList = edgeIntListR #-}  -- Like 'edgeList' but specialised for graphs with vertices of type 'Int'. edgeIntListR :: Graph Int -> [(Int, Int)] edgeIntListR = AIM.edgeList . toAdjacencyIntMap+{-# INLINE edgeIntListR #-}  -- | The set of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. --+-- Good consumer.+-- -- @ -- vertexSet 'empty'      == Set.'Set.empty' -- vertexSet . 'vertex'   == Set.'Set.singleton'@@ -659,14 +742,18 @@ -- @ vertexSet :: Ord a => Graph a -> Set.Set a vertexSet = foldg Set.empty Set.singleton Set.union Set.union+{-# INLINE vertexSet #-}  -- Like 'vertexSet' but specialised for graphs with vertices of type 'Int'. vertexIntSetR :: Graph Int -> IntSet.IntSet vertexIntSetR = foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union+{-# INLINE vertexIntSetR #-}  -- | The set of edges of a given graph. -- Complexity: /O(s * log(m))/ time and /O(m)/ memory. --+-- Good consumer.+-- -- @ -- edgeSet 'empty'      == Set.'Set.empty' -- edgeSet ('vertex' x) == Set.'Set.empty'@@ -675,16 +762,19 @@ -- @ edgeSet :: Ord a => Graph a -> Set.Set (a, a) edgeSet = AM.edgeSet . toAdjacencyMap-{-# INLINE [1] edgeSet #-}+{-# INLINE [2] edgeSet #-} {-# RULES "edgeSet/Int" edgeSet = edgeIntSetR #-}  -- Like 'edgeSet' but specialised for graphs with vertices of type 'Int'. edgeIntSetR :: Graph Int -> Set.Set (Int,Int) edgeIntSetR = AIM.edgeSet . toAdjacencyIntMap+{-# INLINE edgeIntSetR #-}  -- | The sorted /adjacency list/ of a graph. -- Complexity: /O(n + m)/ time and /O(m)/ memory. --+-- Good consumer.+-- -- @ -- adjacencyList 'empty'          == [] -- adjacencyList ('vertex' x)     == [(x, [])]@@ -694,6 +784,7 @@ -- @ adjacencyList :: Ord a => Graph a -> [(a, [a])] adjacencyList = AM.adjacencyList . toAdjacencyMap+{-# INLINE adjacencyList #-} {-# SPECIALISE adjacencyList :: Graph Int -> [(Int, [Int])] #-}  -- TODO: This is a very inefficient implementation. Find a way to construct an@@ -702,15 +793,21 @@ -- Convert a graph to 'AM.AdjacencyMap'. toAdjacencyMap :: Ord a => Graph a -> AM.AdjacencyMap a toAdjacencyMap = foldg AM.empty AM.vertex AM.overlay AM.connect+{-# INLINE toAdjacencyMap #-}  -- Like @toAdjacencyMap@ but specialised for graphs with vertices of type 'Int'. toAdjacencyIntMap :: Graph Int -> AIM.AdjacencyIntMap toAdjacencyIntMap = foldg AIM.empty AIM.vertex AIM.overlay AIM.connect+{-# INLINE toAdjacencyIntMap #-} +-- TODO: Make path a good consumer of lists, that is, express it with foldr.+-- This is not straightforward if we want to preserve efficiency. -- | The /path/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list. --+-- Good producer.+-- -- @ -- path []        == 'empty' -- path [x]       == 'vertex' x@@ -718,14 +815,21 @@ -- path . 'reverse' == 'transpose' . path -- @ path :: [a] -> Graph a-path xs = case xs of []     -> empty-                     [x]    -> vertex x-                     (_:ys) -> edges (zip xs ys)+path xs = buildg $ \e v o c ->+  case xs of+    []     -> e+    [x]    -> v x+    (_:ys) -> foldg e v o c $ edges (zip xs ys)+{-# INLINE path #-} +-- TODO: Make circuit a good consumer of lists, that is, express it with foldr.+-- This is not straightforward if we want to preserve efficiency. -- | The /circuit/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list. --+-- Good producer.+-- -- @ -- circuit []        == 'empty' -- circuit [x]       == 'edge' x x@@ -733,13 +837,18 @@ -- circuit . 'reverse' == 'transpose' . circuit -- @ circuit :: [a] -> Graph a-circuit []     = empty-circuit (x:xs) = path $ [x] ++ xs ++ [x]+circuit xs = buildg $ \e v o c ->+  case xs of+    [] -> e+    (x:xs) -> foldg e v o c $ path $ [x] ++ xs ++ [x]+{-# INLINE circuit #-}  -- | The /clique/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- clique []         == 'empty' -- clique [x]        == 'vertex' x@@ -749,13 +858,15 @@ -- clique . 'reverse'  == 'transpose' . clique -- @ clique :: [a] -> Graph a-clique = connects . map vertex-{-# INLINE [1] clique #-}+clique xs = buildg $ \e v _ c -> combineR e c v xs+{-# INLINE clique #-}  -- | The /biclique/ on two lists of vertices. -- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the -- lengths of the given lists. --+-- Good consumer of both arguments and producer of graphs.+-- -- @ -- biclique []      []      == 'empty' -- biclique [x]     []      == 'vertex' x@@ -764,14 +875,21 @@ -- biclique xs      ys      == 'connect' ('vertices' xs) ('vertices' ys) -- @ biclique :: [a] -> [a] -> Graph a-biclique xs [] = vertices xs-biclique [] ys = vertices ys-biclique xs ys = connect (vertices xs) (vertices ys)+biclique xs ys = buildg $ \e v o c ->+  case foldr1Safe o (map v xs) of+    Nothing -> foldg e v o c $ vertices ys+    Just xs ->+      case foldr1Safe o (map v ys) of+        Nothing -> xs+        Just ys -> c xs ys+{-# INLINE biclique #-}  -- | The /star/ formed by a centre vertex connected to a list of leaves. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list. --+-- Good consumer of lists and good producer of graphs.+-- -- @ -- star x []    == 'vertex' x -- star x [y]   == 'edge' x y@@ -779,8 +897,10 @@ -- star x ys    == 'connect' ('vertex' x) ('vertices' ys) -- @ star :: a -> [a] -> Graph a-star x [] = vertex x-star x ys = connect (vertex x) (vertices ys)+star x ys = buildg $ \_ v o c ->+  case foldr1Safe o (map v ys) of+    Nothing -> v x+    Just vertices  -> c (v x) vertices {-# INLINE star #-}  -- | The /stars/ formed by overlaying a list of 'star's. An inverse of@@ -788,6 +908,8 @@ -- Complexity: /O(L)/ time, memory and size, where /L/ is the total size of the -- input. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- stars []                      == 'empty' -- stars [(x, [])]               == 'vertex' x@@ -798,7 +920,8 @@ -- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys) -- @ stars :: [(a, [a])] -> Graph a-stars = overlays . map (uncurry star)+stars xs = buildg $ \e v o c ->+  combineR e o (foldg e v o c . uncurry star) xs {-# INLINE stars #-}  -- | The /tree graph/ constructed from a given 'Tree.Tree' data structure.@@ -901,6 +1024,8 @@ -- | Remove a vertex from a given graph. -- Complexity: /O(s)/ time, memory and size. --+-- Good consumer and producer.+-- -- @ -- removeVertex x ('vertex' x)       == 'empty' -- removeVertex 1 ('vertex' 2)       == 'vertex' 2@@ -940,6 +1065,8 @@ -- given 'Graph'. If @y@ already exists, @x@ and @y@ will be merged. -- Complexity: /O(s)/ time, memory and size. --+-- Good consumer and producer.+-- -- @ -- replaceVertex x x            == id -- replaceVertex x y ('vertex' x) == 'vertex' y@@ -947,12 +1074,15 @@ -- @ replaceVertex :: Eq a => a -> a -> Graph a -> Graph a replaceVertex u v = fmap $ \w -> if w == u then v else w+{-# INLINE replaceVertex #-} {-# SPECIALISE replaceVertex :: Int -> Int -> Graph Int -> Graph Int #-}  -- | Merge vertices satisfying a given predicate into a given vertex. -- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes -- /O(1)/ to be evaluated. --+-- Good consumer and producer.+-- -- @ -- mergeVertices ('const' False) x    == id -- mergeVertices (== x) y           == 'replaceVertex' x y@@ -961,12 +1091,15 @@ -- @ mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a mergeVertices p v = fmap $ \w -> if p w then v else w+{-# INLINE mergeVertices #-}  -- | Split a vertex into a list of vertices with the same connectivity. -- Complexity: /O(s + k * L)/ time, memory and size, where /k/ is the number of -- occurrences of the vertex in the expression and /L/ is the length of the -- given list. --+-- Good consumer of lists and producer of graphs.+-- -- @ -- splitVertex x []                  == 'removeVertex' x -- splitVertex x [x]                 == id@@ -974,12 +1107,17 @@ -- splitVertex 1 [0,1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3) -- @ splitVertex :: Eq a => a -> [a] -> Graph a -> Graph a-splitVertex v us g = g >>= \w -> if w == v then vertices us else vertex w+splitVertex x us g = buildg $ \e v o c ->+  let gus = foldg e v o c (vertices us) in+  foldg e (\w -> if w == x then gus else v w) o c g+{-# INLINE splitVertex #-} {-# SPECIALISE splitVertex :: Int -> [Int] -> Graph Int -> Graph Int #-}  -- | Transpose a given graph. -- Complexity: /O(s)/ time, memory and size. --+-- Good consumer and producer.+-- -- @ -- transpose 'empty'       == 'empty' -- transpose ('vertex' x)  == 'vertex' x@@ -989,14 +1127,17 @@ -- 'edgeList' . transpose  == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Graph a -> Graph a-transpose = foldg Empty Vertex Overlay (flip Connect)+transpose g = buildg $ \e v o c -> foldg e v o (flip c) g {-# INLINE transpose #-} +-- TODO: Implement via 'induceJust' to reduce code duplication. -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate. -- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes -- /O(1)/ to be evaluated. --+-- Good consumer and producer.+-- -- @ -- induce ('const' True ) x      == x -- induce ('const' False) x      == 'empty'@@ -1005,13 +1146,37 @@ -- 'isSubgraphOf' (induce p x) x == True -- @ induce :: (a -> Bool) -> Graph a -> Graph a-induce p = foldg Empty (\x -> if p x then Vertex x else Empty) (k Overlay) (k Connect)+induce p g = buildg $ \e v o c -> fromMaybe e $+  foldg Nothing (\x -> if p x then Just (v x) else Nothing) (k o) (k c) g   where-    k _ x     Empty = x -- Constant folding to get rid of Empty leaves-    k _ Empty y     = y-    k f x     y     = f x y-{-# INLINE [1] induce #-}+    k _ x        Nothing  = x -- Constant folding to get rid of Empty leaves+    k _ Nothing  y        = y+    k f (Just x) (Just y) = Just (f x y)+{-# INLINE induce #-} +-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(s)/ time, memory and size.+--+-- Good consumer and producer.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'fmap' 'Just'                                    == 'id'+-- induceJust . 'fmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Graph (Maybe a) -> Graph a+induceJust g = buildg $ \e v o c -> fromMaybe e $+  foldg Nothing (fmap v) (k o) (k c) g+  where+    k _ x        Nothing  = x -- Constant folding to get rid of Empty leaves+    k _ Nothing  y        = y+    k f (Just x) (Just y) = Just (f x y)+{-# INLINE induceJust #-}++-- NB: This is not a good producer since it requires an Eq instance on the+-- produced structure. -- | Simplify a graph expression. Semantically, this is the identity function, -- but it simplifies a given expression according to the laws of the algebra. -- The function does not compute the simplest possible expression,@@ -1019,6 +1184,8 @@ -- Complexity: the function performs /O(s)/ graph comparisons. It is guaranteed -- that the size of the result does not exceed the size of the given expression. --+-- Good consumer.+-- -- @ -- simplify              == id -- 'size' (simplify x)     <= 'size' x@@ -1030,6 +1197,7 @@ -- @ simplify :: Ord a => Graph a -> Graph a simplify = foldg Empty Vertex (simple Overlay) (simple Connect)+{-# INLINE simplify #-} {-# SPECIALISE simplify :: Graph Int -> Graph Int #-}  simple :: Eq g => (g -> g -> g) -> g -> g -> g@@ -1054,6 +1222,8 @@ -- quadratic, i.e. /m = O(m1 * m2)/, but the algebraic representation requires -- only /O(m1 + m2)/ operations to list them. --+-- Good consumer of both arguments and good producer.+-- -- @ -- compose 'empty'            x                == 'empty' -- compose x                'empty'            == 'empty'@@ -1068,14 +1238,16 @@ -- 'size' (compose x y)                        <= 'edgeCount' x + 'edgeCount' y + 1 -- @ compose :: Ord a => Graph a -> Graph a -> Graph a-compose x y = overlays-    [ biclique xs ys-    | v <- Set.toList (AM.vertexSet mx `Set.union` AM.vertexSet my)-    , let xs = Set.toList (AM.postSet v mx), not (null xs)-    , let ys = Set.toList (AM.postSet v my), not (null ys) ]+compose x y = buildg $ \e v o c -> fromMaybe e $+  foldr1Safe o+    [ foldg e v o c (biclique xs ys)+    | ve <- Set.toList (AM.vertexSet mx `Set.union` AM.vertexSet my)+    , let xs = Set.toList (AM.postSet ve mx), not (null xs)+    , let ys = Set.toList (AM.postSet ve my), not (null ys) ]   where     mx = toAdjacencyMap (transpose x)     my = toAdjacencyMap y+{-# INLINE compose #-}  -- | Compute the /Cartesian product/ of graphs. -- Complexity: /O(s1 * s2)/ time, memory and size, where /s1/ and /s2/ are the@@ -1170,81 +1342,44 @@ The rules for foldg work very similarly to GHC's mapFB rules; see a note below this line: http://hackage.haskell.org/package/base/docs/src/GHC.Base.html#mapFB. -* Up to (but not including) phase 1, we use the "buildR/f" rule to rewrite all-  saturated applications of f into its buildR/foldg form, hoping for fusion to-  happen (through the "foldg/buildR" rule).--  In phases 1 and 0, we switch off these rules, inline buildR, and switch on the-  "graph/f" rule, which rewrites "foldg/f" back into plain functions if needed.+* All concerned expressions are inlined to allow the compiler to apply the main+  rule: "foldg/buildg".+  This rule states that the composition of a good producer (expressed via buildg)+  and a good consumer (expressed via foldg) can be fused to remove the construction+  of the intermediate structure. -  It's important that these two rules aren't both active at once (along with-  build's unfolding) else we'd get an infinite loop in the rules. Hence the-  activation control below.+* If this inlining is made blindlessly, it can lead to unneeded operations. They+  are optimized via the "foldg/id" rule. -* composeR and matchR are here to remember the original function after applying-  a "buildR/f" rule. These functions are higher-order functions and therefore+* composeR is here to allow further optimization. As an high-order function, it   benefit from inlining in the final phase. -* The "bindR/bindR" rule optimises compositions of multiple bindR's.+* The "composeR/composeR" rule optimises compositions of multiple composeR's. -} -type Foldg a = forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b--buildR :: Foldg a -> Graph a-buildR g = g Empty Vertex Overlay Connect-{-# INLINE [1] buildR #-}- composeR :: (b -> c) -> (a -> b) -> a -> c composeR = (.)-{-# INLINE [0] composeR #-}--matchR :: b -> (a -> b) -> (a -> Bool) -> a -> b-matchR e v p = \x -> if p x then v x else e-{-# INLINE [0] matchR #-}---- These rules transform functions into their buildR equivalents.-{-# RULES-"buildR/bindR" forall f g.-    bindR g f = buildR (\e v o c -> foldg e (composeR (foldg e v o c) f) o c g)--"buildR/induce" [~1] forall p g.-    induce p g = buildR (\e v o c -> foldg e (matchR e v p) o c g)--"buildR/foldg(fc)" [~1] forall (f :: forall b. (b -> b -> b) -> (b -> b -> b)) g.-    foldg Empty Vertex Overlay (f Connect) g = buildR (\e v o c -> foldg e v o (f c) g)--"buildR/foldg(fo)" [~1] forall (f :: forall b. (b -> b -> b) -> (b -> b -> b)) g.-    foldg Empty Vertex (f Overlay) Connect g = buildR (\e v o c -> foldg e v (f o) c g)--"buildR/foldg(fo)(hc)" [~1] forall (f :: forall b. (b -> b -> b) -> (b -> b -> b)) (h :: forall b. (b -> b -> b) -> (b -> b -> b)) g.-    foldg Empty Vertex (f Overlay) (h Connect) g = buildR (\e v o c -> foldg e v (f o) (h c) g)- #-}+{-# INLINE [1] composeR #-}  -- Rewrite rules for fusion. {-# RULES--- Fuse a foldg followed by a buildR-"foldg/buildR" forall e v o c (g :: Foldg a).-    foldg e v o c (buildR g) = g e v o c+-- Fuse a foldg followed by a buildg.+"foldg/buildg" forall e v o c (g :: forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b).+    foldg e v o c (buildg g) = g e v o c --- Fuse composeR's. This occurs when two adjacent 'bindR' were rewritted into--- their buildR form.-"bindR/bindR" forall c f g.-    composeR (composeR c f) g = composeR c (f.g)+-- Fuse composeR's (from bind's definition).+"composeR/composeR" forall c f g.+    composeR (composeR c f) g = composeR c (f . g) --- Rewrite identity (which can appear in the rewriting of bindR) to a much efficient one+-- Rewrite identity (which can appear in the inlining of 'buildg') to a more efficient one. "foldg/id"     foldg Empty Vertex Overlay Connect = id  #-} --- Eliminate remaining rewrite-only functions.-{-# RULES-"graph/induce" [1] forall f.-    foldg Empty (matchR Empty Vertex f) Overlay Connect = induce f- #-}- -- 'Focus' on a specified subgraph. focus :: (a -> Bool) -> Graph a -> Focus a focus f = foldg emptyFocus (vertexFocus f) overlayFoci connectFoci+{-# INLINE focus #-}  -- | The 'Context' of a subgraph comprises its 'inputs' and 'outputs', i.e. all -- the vertices that are connected to the subgraph's vertices. Note that inputs@@ -1257,6 +1392,8 @@ -- | Extract the 'Context' of a subgraph specified by a given predicate. Returns -- @Nothing@ if the specified subgraph is empty. --+-- Good consumer.+-- -- @ -- context ('const' False) x                   == Nothing -- context (== 1)        ('edge' 1 2)          == Just ('Context' [   ] [2  ])@@ -1269,3 +1406,4 @@             | otherwise = Nothing   where     f = focus p g+{-# INLINE context #-}
+ src/Algebra/Graph/Acyclic/AdjacencyMap.hs view
@@ -0,0 +1,543 @@+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Acyclic.AdjacencyMap+-- Copyright  : (c) Andrey Mokhov 2016-2019+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for+-- the motivation behind the library, the underlying theory, and implementation+-- details.+--+-- This module defines the 'AdjacencyMap' data type and for acyclic graphs, as+-- well as associated operations and algorithms. To avoid name clashes with+-- "Algebra.Graph.AdjacencyMap", this module can be imported qualified:+--+-- @+-- import qualified Algebra.Graph.Acyclic.AdjacencyMap as Acyclic+-- @+-----------------------------------------------------------------------------+module Algebra.Graph.Acyclic.AdjacencyMap (+    -- * Data structure+    AdjacencyMap, fromAcyclic,++    -- * Basic graph construction primitives+    empty, vertex, vertices, union, join,++    -- * Relations on graphs+    isSubgraphOf,++    -- * Graph properties+    isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,+    adjacencyList, vertexSet, edgeSet, preSet, postSet,++    -- * Graph transformation+    removeVertex, removeEdge, transpose, induce, induceJust,++    -- * Graph composition+    box,++    -- * Relational operations+    transitiveClosure,++    -- * Algorithms+    topSort, scc,++    -- * Conversion to acyclic graphs+    toAcyclic, toAcyclicOrd, shrink,++    -- * Miscellaneous+    consistent+    ) where++import Data.Set (Set)+import Data.Coerce (coerce)++import qualified Algebra.Graph.AdjacencyMap           as AM+import qualified Algebra.Graph.AdjacencyMap.Algorithm as AM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap  as NAM+import qualified Data.List.NonEmpty                   as NonEmpty+import qualified Data.Map                             as Map+import qualified Data.Set                             as Set++{-| The 'AdjacencyMap' data type represents an acyclic graph by a map of+vertices to their adjacency sets. Although the internal representation allows+for cycles, the methods provided by this module cannot be used to construct a+graph with cycles.++The 'Show' instance is defined using basic graph construction primitives where+possible, falling back to 'toAcyclic' and "Algebra.Graph.AdjacencyMap"+otherwise:++@+show empty                == "empty"+show (shrink 1)           == "vertex 1"+show (shrink $ 1 + 2)     == "vertices [1,2]"+show (shrink $ 1 * 2)     == "(fromJust . toAcyclic) (edge 1 2)"+show (shrink $ 1 * 2 * 3) == "(fromJust . toAcyclic) (edges [(1,2),(1,3),(2,3)])"+show (shrink $ 1 * 2 + 3) == "(fromJust . toAcyclic) (overlay (vertex 3) (edge 1 2))"+@++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Note that the resulting order refines the 'isSubgraphOf' relation:++@'isSubgraphOf' x y ==> x <= y@+-}++-- TODO: Improve the Show instance.+newtype AdjacencyMap a = AAM {+    -- | Extract the underlying acyclic "Algebra.Graph.AdjacencyMap".+    -- Complexity: /O(1)/ time and memory.+    --+    -- @+    -- fromAcyclic 'empty'                == 'AM.empty'+    -- fromAcyclic . 'vertex'             == 'AM.vertex'+    -- fromAcyclic (shrink $ 1 * 3 + 2) == 1 * 3 + 2+    -- 'AM.vertexCount' . fromAcyclic        == 'vertexCount'+    -- 'AM.edgeCount'   . fromAcyclic        == 'edgeCount'+    -- 'AM.isAcyclic'   . fromAcyclic        == 'const' True+    -- @+    fromAcyclic :: AM.AdjacencyMap a+    } deriving (Eq, Ord)++instance (Ord a, Show a) => Show (AdjacencyMap a) where+    showsPrec p aam@(AAM am)+        | null vs    = showString "empty"+        | null es    = showParen (p > 10) $ vshow vs+        | otherwise  = showParen (p > 10) $ showString "(fromJust . toAcyclic) ("+                     . shows am . showString ")"+      where+        vs             = vertexList aam+        es             = edgeList aam+        vshow [x]      = showString "vertex "   . showsPrec 11 x+        vshow xs       = showString "vertices " . showsPrec 11 xs++-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty'     empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount'   empty == 0+-- @+empty :: AdjacencyMap a+empty = coerce AM.empty++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty'     (vertex x) == False+-- 'hasVertex' x (vertex y) == (x == y)+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount'   (vertex x) == 0+-- @+vertex :: a -> AdjacencyMap a+vertex = coerce AM.vertex++-- | Construct the graph comprising a given list of isolated vertices.+-- Complexity: /O(L * log(L))/ time and /O(L)/ memory, where /L/ is the length+-- of the given list.+--+-- @+-- vertices []            == 'empty'+-- vertices [x]           == 'vertex' x+-- 'hasVertex' x . vertices == 'elem' x+-- 'vertexCount' . vertices == 'length' . 'Data.List.nub'+-- 'vertexSet'   . vertices == Set.'Set.fromList'+-- @+vertices :: Ord a => [a] -> AdjacencyMap a+vertices = coerce AM.vertices++-- | Construct the disjoint /union/ of two graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'vertexSet' (union x y) == Set.'Set.unions' [ Set.'Set.map' 'Left'  ('vertexSet' x)+--                                     , Set.'Set.map' 'Right' ('vertexSet' y) ]+--+-- 'edgeSet'   (union x y) == Set.'Set.unions' [ Set.'Set.map' ('Data.Bifunctor.bimap' 'Left'  'Left' ) ('edgeSet' x)+--                                     , Set.'Set.map' ('Data.Bifunctor.bimap' 'Right' 'Right') ('edgeSet' y) ]+-- @+union :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (Either a b)+union (AAM x) (AAM y) = AAM $ AM.overlay (AM.gmap Left x) (AM.gmap Right y)++-- | Construct the /join/ of two graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'vertexSet' (join x y) == Set.'Set.unions' [ Set.'Set.map' 'Left'  ('vertexSet' x)+--                                    , Set.'Set.map' 'Right' ('vertexSet' y) ]+--+-- 'edgeSet'   (join x y) == Set.'Set.unions' [ Set.'Set.map' ('Data.Bifunctor.bimap' 'Left'  'Left' ) ('edgeSet' x)+--                                    , Set.'Set.map' ('Data.Bifunctor.bimap' 'Right' 'Right') ('edgeSet' y)+--                                    , Set.'Set.map' ('Data.Bifunctor.bimap' 'Left'  'Right') (Set.'Set.cartesianProduct' ('vertexSet' x) ('vertexSet' y)) ]+-- @+join :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (Either a b)+join (AAM a) (AAM b) = AAM $ AM.connect (AM.gmap Left a) (AM.gmap Right b)++-- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the+-- first graph is a /subgraph/ of the second.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- isSubgraphOf 'empty'        x                     ==  True+-- isSubgraphOf ('vertex' x)   'empty'                 ==  False+-- isSubgraphOf ('induce' p x) x                     ==  True+-- isSubgraphOf x            ('transitiveClosure' x) ==  True+-- isSubgraphOf x y                                ==> x <= y+-- @+isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool+isSubgraphOf = coerce AM.isSubgraphOf++-- | Check if a graph is empty.+-- Complexity: /O(1)/ time.+--+-- @+-- isEmpty 'empty'                             == True+-- isEmpty ('vertex' x)                        == False+-- isEmpty ('removeVertex' x $ 'vertex' x)       == True+-- isEmpty ('removeEdge' 1 2 $ shrink $ 1 * 2) == False+-- @+isEmpty :: AdjacencyMap a -> Bool+isEmpty = coerce AM.isEmpty++-- | Check if a graph contains a given vertex.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasVertex x 'empty'            == False+-- hasVertex x ('vertex' y)       == (x == y)+-- hasVertex x . 'removeVertex' x == 'const' False+-- @+hasVertex :: Ord a => a -> AdjacencyMap a -> Bool+hasVertex = coerce AM.hasVertex++-- | Check if a graph contains a given edge.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasEdge x y 'empty'            == False+-- hasEdge x y ('vertex' z)       == False+-- hasEdge 1 2 (shrink $ 1 * 2) == True+-- hasEdge x y . 'removeEdge' x y == 'const' False+-- hasEdge x y                  == 'elem' (x,y) . 'edgeList'+-- @+hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool+hasEdge = coerce AM.hasEdge++-- | The number of vertices in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- vertexCount 'empty'             ==  0+-- vertexCount ('vertex' x)        ==  1+-- vertexCount                   ==  'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y+-- @+vertexCount :: AdjacencyMap a -> Int+vertexCount = coerce AM.vertexCount++-- | The number of edges in a graph.+-- Complexity: /O(n)/ time.+--+-- @+-- edgeCount 'empty'            == 0+-- edgeCount ('vertex' x)       == 0+-- edgeCount (shrink $ 1 * 2) == 1+-- edgeCount                  == 'length' . 'edgeList'+-- @+edgeCount :: AdjacencyMap a -> Int+edgeCount = coerce AM.edgeCount++-- | The sorted list of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexList 'empty'      == []+-- vertexList ('vertex' x) == [x]+-- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort'+-- @+vertexList :: AdjacencyMap a -> [a]+vertexList = coerce AM.vertexList++-- | The sorted list of edges of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeList 'empty'            == []+-- edgeList ('vertex' x)       == []+-- edgeList (shrink $ 2 * 1) == [(2,1)]+-- edgeList . 'transpose'      == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList+-- @+edgeList :: AdjacencyMap a -> [(a, a)]+edgeList = coerce AM.edgeList++-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- adjacencyList 'empty'            == []+-- adjacencyList ('vertex' x)       == [(x, [])]+-- adjacencyList (shrink $ 1 * 2) == [(1, [2]), (2, [])]+-- @+adjacencyList :: AdjacencyMap a -> [(a, [a])]+adjacencyList = coerce AM.adjacencyList++-- | The set of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexSet 'empty'      == Set.'Set.empty'+-- vertexSet . 'vertex'   == Set.'Set.singleton'+-- vertexSet . 'vertices' == Set.'Set.fromList'+-- @+vertexSet :: AdjacencyMap a -> Set a+vertexSet = coerce AM.vertexSet++-- | The set of edges of a given graph.+-- Complexity: /O((n + m) * log(m))/ time and /O(m)/ memory.+--+-- @+-- edgeSet 'empty'            == Set.'Set.empty'+-- edgeSet ('vertex' x)       == Set.'Set.empty'+-- edgeSet (shrink $ 1 * 2) == Set.'Set.singleton' (1,2)+-- @+edgeSet :: Eq a => AdjacencyMap a -> Set (a, a)+edgeSet = coerce AM.edgeSet++-- | The /preset/ of an element @x@ is the set of its /direct predecessors/.+-- Complexity: /O(n * log(n))/ time and /O(n)/ memory.+--+-- @+-- preSet x 'empty'            == Set.'Set.empty'+-- preSet x ('vertex' x)       == Set.'Set.empty'+-- preSet 1 (shrink $ 1 * 2) == Set.'Set.empty'+-- preSet 2 (shrink $ 1 * 2) == Set.'Set.fromList' [1]+-- Set.'Set.member' x . preSet x   == 'const' False+-- @+preSet :: Ord a => a -> AdjacencyMap a -> Set a+preSet = coerce AM.preSet++-- | The /postset/ of a vertex is the set of its /direct successors/.+-- Complexity: /O(log(n))/ time and /O(1)/ memory.+--+-- @+-- postSet x 'empty'            == Set.'Set.empty'+-- postSet x ('vertex' x)       == Set.'Set.empty'+-- postSet 1 (shrink $ 1 * 2) == Set.'Set.fromList' [2]+-- postSet 2 (shrink $ 1 * 2) == Set.'Set.empty'+-- Set.'Set.member' x . postSet x   == 'const' False+-- @+postSet :: Ord a => a -> AdjacencyMap a -> Set a+postSet = coerce AM.postSet++-- | Remove a vertex from a given acyclic graph.+-- Complexity: /O(n*log(n))/ time.+--+-- @+-- removeVertex x ('vertex' x)       == 'empty'+-- removeVertex 1 ('vertex' 2)       == 'vertex' 2+-- removeVertex 1 (shrink $ 1 * 2) == 'vertex' 2+-- removeVertex x . removeVertex x == removeVertex x+-- @+removeVertex :: Ord a => a -> AdjacencyMap a -> AdjacencyMap a+removeVertex = coerce AM.removeVertex++-- | Remove an edge from a given acyclic graph.+-- Complexity: /O(log(n))/ time.+--+-- @+-- removeEdge 1 2 (shrink $ 1 * 2)     == 'vertices' [1,2]+-- removeEdge x y . removeEdge x y     == removeEdge x y+-- removeEdge x y . 'removeVertex' x     == 'removeVertex' x+-- removeEdge 1 2 (shrink $ 1 * 2 * 3) == shrink ((1 + 2) * 3)+-- @+removeEdge :: Ord a => a -> a -> AdjacencyMap a -> AdjacencyMap a+removeEdge = coerce AM.removeEdge++-- | Transpose a given acyclic graph.+-- Complexity: /O(m * log(n))/ time, /O(n + m)/ memory.+--+-- @+-- transpose 'empty'       == 'empty'+-- transpose ('vertex' x)  == 'vertex' x+-- transpose . transpose == id+-- 'edgeList' . transpose  == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList'+-- @+transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a+transpose = coerce AM.transpose++-- | Construct the /induced subgraph/ of a given graph by removing the+-- vertices that do not satisfy a given predicate.+-- Complexity: /O(n + m)/ time, assuming that the predicate takes /O(1)/ to+-- be evaluated.+--+-- @+-- induce ('const' True ) x      == x+-- induce ('const' False) x      == 'empty'+-- induce (/= x)               == 'removeVertex' x+-- induce p . induce q         == induce (\x -> p x && q x)+-- 'isSubgraphOf' (induce p x) x == True+-- @+induce :: (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a+induce = coerce AM.induce++-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(n + m)/ time.+--+-- @+-- induceJust ('vertex' 'Nothing') == 'empty'+-- induceJust . 'vertex' . 'Just'  == 'vertex'+-- @+induceJust :: Ord a => AdjacencyMap (Maybe a) -> AdjacencyMap a+induceJust = coerce AM.induceJust++-- | Compute the /Cartesian product/ of graphs.+-- Complexity: /O(n * m * log(n)^2)/ time.+--+-- @+-- 'edgeList' (box (shrink $ 1 * 2) (shrink $ 10 * 20)) == [ ((1,10), (1,20))+--                                                       , ((1,10), (2,10))+--                                                       , ((1,20), (2,20))+--                                                       , ((2,10), (2,20)) ]+-- @+--+-- Up to an isomorphism between the resulting vertex types, this operation+-- is /commutative/ and /associative/, has singleton graphs as /identities/ and+-- 'empty' as the /annihilating zero/. Below @~~@ stands for the equality up to+-- an isomorphism, e.g. @(x, ()) ~~ x@.+--+-- @+-- box x y               ~~ box y x+-- box x (box y z)       ~~ box (box x y) z+-- box x ('vertex' ())     ~~ x+-- box x 'empty'           ~~ 'empty'+-- 'transpose'   (box x y) == box ('transpose' x) ('transpose' y)+-- 'vertexCount' (box x y) == 'vertexCount' x * 'vertexCount' y+-- 'edgeCount'   (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y+-- @+box :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (a, b)+box = coerce AM.box++-- | Compute the /transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time.+--+-- @+-- transitiveClosure 'empty'                    == 'empty'+-- transitiveClosure ('vertex' x)               == 'vertex' x+-- transitiveClosure (shrink $ 1 * 2 + 2 * 3) == shrink (1 * 2 + 1 * 3 + 2 * 3)+-- transitiveClosure . transitiveClosure      == transitiveClosure+-- @+transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+transitiveClosure = coerce AM.transitiveClosure++-- | Compute a /topological sort/ of an acyclic graph.+--+-- @+-- topSort 'empty'                          == []+-- topSort ('vertex' x)                     == [x]+-- topSort (shrink $ 1 * (2 + 4) + 3 * 4) == [1, 2, 3, 4]+-- topSort ('join' x y)                     == 'fmap' 'Left' (topSort x) ++ 'fmap' 'Right' (topSort y)+-- 'Right' . topSort                        == 'AM.topSort' . 'fromAcyclic'+-- @+topSort :: Ord a => AdjacencyMap a -> [a]+topSort g = case AM.topSort (coerce g) of+  Right vs -> vs+  Left _ -> error "Internal error: the acyclicity invariant is violated in topSort"++-- | Compute the acyclic /condensation/ of a graph, where each vertex+-- corresponds to a /strongly-connected component/ of the original graph. Note+-- that component graphs are non-empty, and are therefore of type+-- "Algebra.Graph.NonEmpty.AdjacencyMap".+--+-- @+--            scc 'AM.empty'               == 'empty'+--            scc ('AM.vertex' x)          == 'vertex' (NonEmpty.'NonEmpty.vertex' x)+--            scc ('AM.edge' 1 1)          == 'vertex' (NonEmpty.'NonEmpty.edge' 1 1)+-- 'edgeList' $ scc ('AM.edge' 1 2)          == [ (NonEmpty.'NonEmpty.vertex' 1       , NonEmpty.'NonEmpty.vertex' 2       ) ]+-- 'edgeList' $ scc (3 * 1 * 4 * 1 * 5) == [ (NonEmpty.'NonEmpty.vertex' 3       , NonEmpty.'NonEmpty.vertex' 5       )+--                                       , (NonEmpty.'NonEmpty.vertex' 3       , NonEmpty.'NonEmpty.clique1' [1,4,1])+--                                       , (NonEmpty.'NonEmpty.clique1' [1,4,1], NonEmpty.'NonEmpty.vertex' 5       ) ]+-- @+scc :: (Ord a) => AM.AdjacencyMap a -> AdjacencyMap (NAM.AdjacencyMap a)+scc = coerce AM.scc++-- | Construct an acyclic graph from a given adjacency map, or return 'Nothing'+-- if the input contains cycles.+--+-- @+-- toAcyclic ('AM.path'    [1,2,3]) == 'Just' (shrink $ 1 * 2 + 2 * 3)+-- toAcyclic ('AM.clique'  [3,2,1]) == 'Just' ('transpose' (shrink $ 1 * 2 * 3))+-- toAcyclic ('AM.circuit' [1,2,3]) == 'Nothing'+-- toAcyclic . 'fromAcyclic'     == 'Just'+-- @+toAcyclic :: Ord a => AM.AdjacencyMap a -> Maybe (AdjacencyMap a)+toAcyclic x = if AM.isAcyclic x then Just (AAM x) else Nothing++-- | Construct an acyclic graph from a given adjacency map, keeping only edges+-- @(x,y)@ where @x < y@ according to the supplied 'Ord' @a@ instance.+--+-- @+-- toAcyclicOrd 'empty'       == 'empty'+-- toAcyclicOrd . 'vertex'    == 'vertex'+-- toAcyclicOrd (1 + 2)     == shrink (1 + 2)+-- toAcyclicOrd (1 * 2)     == shrink (1 * 2)+-- toAcyclicOrd (2 * 1)     == shrink (1 + 2)+-- toAcyclicOrd (1 * 2 * 1) == shrink (1 * 2)+-- toAcyclicOrd (1 * 2 * 3) == shrink (1 * 2 * 3)+-- @+toAcyclicOrd :: Ord a => AM.AdjacencyMap a -> AdjacencyMap a+toAcyclicOrd = AAM . filterEdges (<)++-- TODO: Add time complexity+-- TODO: Change Arbitrary instance of Acyclic and Labelled Acyclic graph+-- | Construct an acyclic graph from a given adjacency map using 'scc'.+-- If the graph is acyclic in nature, the same graph is returned as an acyclic graph.+-- If the graph is cyclic, then a representative for every strongly connected+-- component in its condensation graph is chosen an these representatives are+-- used to build an acyclic graph.+--+-- @+-- shrink . 'AM.vertex'      == 'vertex'+-- shrink . 'AM.vertices'    == 'vertices'+-- shrink . 'fromAcyclic' == 'id'+-- @+shrink :: Ord a => AM.AdjacencyMap a -> AdjacencyMap a+shrink = AAM . AM.gmap (NonEmpty.head . NAM.vertexList1) . AM.scc++-- TODO: Provide a faster equivalent in "Algebra.Graph.AdjacencyMap".+-- Keep only the edges that satisfy a given predicate.+filterEdges :: Ord a => (a -> a -> Bool) -> AM.AdjacencyMap a -> AM.AdjacencyMap a+filterEdges p m = AM.fromAdjacencySets+    [ (a, Set.filter (p a) bs) | (a, bs) <- Map.toList (AM.adjacencyMap m) ]++-- | Check if the internal representation of an acyclic graph is consistent,+-- i.e. that all edges refer to existing vertices and the graph is acyclic. It+-- should be impossible to create an inconsistent 'AdjacencyMap'.+--+-- @+-- consistent 'empty'                 == True+-- consistent ('vertex' x)            == True+-- consistent ('vertices' xs)         == True+-- consistent ('union' x y)           == True+-- consistent ('join' x y)            == True+-- consistent ('transpose' x)         == True+-- consistent ('box' x y)             == True+-- consistent ('transitiveClosure' x) == True+-- consistent ('scc' x)               == True+-- 'fmap' consistent ('toAcyclic' x)    /= False+-- consistent ('toAcyclicOrd' x)      == True+-- @+consistent :: Ord a => AdjacencyMap a -> Bool+consistent (AAM m) = AM.consistent m && AM.isAcyclic m
src/Algebra/Graph/AdjacencyIntMap.hs view
@@ -1,7 +1,8 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.AdjacencyIntMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -18,7 +19,7 @@ ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyIntMap (     -- * Data structure-    AdjacencyIntMap, adjacencyIntMap,+    AdjacencyIntMap, adjacencyIntMap, fromAdjacencyMap,      -- * Basic graph construction primitives     empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,@@ -39,21 +40,182 @@     induce,      -- * Relational operations-    compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure+    compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure,++    -- * Miscellaneous+    consistent     ) where -import Data.Foldable (foldMap)+import Control.DeepSeq+import Data.IntMap.Strict (IntMap) import Data.IntSet (IntSet)-import Data.Monoid+import Data.List ((\\))+import Data.Monoid (Sum (..)) import Data.Set (Set) import Data.Tree--import Algebra.Graph.AdjacencyIntMap.Internal+import GHC.Generics  import qualified Data.IntMap.Strict as IntMap import qualified Data.IntSet        as IntSet+import qualified Data.Map.Strict    as Map import qualified Data.Set           as Set +import qualified Algebra.Graph.AdjacencyMap as AM++{-| The 'AdjacencyIntMap' data type represents a graph by a map of vertices to+their adjacency sets. We define a 'Num' instance as a convenient notation for+working with graphs:++    > 0           == vertex 0+    > 1 + 2       == overlay (vertex 1) (vertex 2)+    > 1 * 2       == connect (vertex 1) (vertex 2)+    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))+    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))++__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.++The 'Show' instance is defined using basic graph construction primitives:++@show (empty     :: AdjacencyIntMap Int) == "empty"+show (1         :: AdjacencyIntMap Int) == "vertex 1"+show (1 + 2     :: AdjacencyIntMap Int) == "vertices [1,2]"+show (1 * 2     :: AdjacencyIntMap Int) == "edge 1 2"+show (1 * 2 * 3 :: AdjacencyIntMap Int) == "edges [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: AdjacencyIntMap Int) == "overlay (vertex 3) (edge 1 2)"@++The 'Eq' instance satisfies all axioms of algebraic graphs:++    * 'overlay' is commutative and associative:++        >       x + y == y + x+        > x + (y + z) == (x + y) + z++    * 'connect' is associative and has 'empty' as the identity:++        >   x * empty == x+        >   empty * x == x+        > x * (y * z) == (x * y) * z++    * 'connect' distributes over 'overlay':++        > x * (y + z) == x * y + x * z+        > (x + y) * z == x * z + y * z++    * 'connect' can be decomposed:++        > x * y * z == x * y + x * z + y * z++The following useful theorems can be proved from the above set of axioms.++    * 'overlay' has 'empty' as the identity and is idempotent:++        >   x + empty == x+        >   empty + x == x+        >       x + x == x++    * Absorption and saturation of 'connect':++        > x * y + x + y == x * y+        >     x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ and /m/+will denote the number of vertices and edges in the graph, respectively.++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x     <= x + y+x + y <= x * y@+-}+newtype AdjacencyIntMap = AM {+    -- | The /adjacency map/ of a graph: each vertex is associated with a set of+    -- its direct successors. Complexity: /O(1)/ time and memory.+    --+    -- @+    -- adjacencyIntMap 'empty'      == IntMap.'IntMap.empty'+    -- adjacencyIntMap ('vertex' x) == IntMap.'IntMap.singleton' x IntSet.'IntSet.empty'+    -- adjacencyIntMap ('edge' 1 1) == IntMap.'IntMap.singleton' 1 (IntSet.'IntSet.singleton' 1)+    -- adjacencyIntMap ('edge' 1 2) == IntMap.'IntMap.fromList' [(1,IntSet.'IntSet.singleton' 2), (2,IntSet.'IntSet.empty')]+    -- @+    adjacencyIntMap :: IntMap IntSet } deriving (Eq, Generic)++instance Show AdjacencyIntMap where+    showsPrec p am@(AM m)+        | null vs    = showString "empty"+        | null es    = showParen (p > 10) $ vshow vs+        | vs == used = showParen (p > 10) $ eshow es+        | otherwise  = showParen (p > 10) $+                           showString "overlay (" . vshow (vs \\ used) .+                           showString ") (" . eshow es . showString ")"+      where+        vs             = vertexList am+        es             = edgeList am+        vshow [x]      = showString "vertex "   . showsPrec 11 x+        vshow xs       = showString "vertices " . showsPrec 11 xs+        eshow [(x, y)] = showString "edge "     . showsPrec 11 x .+                         showString " "         . showsPrec 11 y+        eshow xs       = showString "edges "    . showsPrec 11 xs+        used           = IntSet.toAscList (referredToVertexSet m)++instance Ord AdjacencyIntMap where+    compare x y = mconcat+        [ compare (vertexCount  x) (vertexCount  y)+        , compare (vertexIntSet x) (vertexIntSet y)+        , compare (edgeCount    x) (edgeCount    y)+        , compare (edgeSet      x) (edgeSet      y) ]++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyIntMap'+-- for more details.+instance Num AdjacencyIntMap where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id++instance NFData AdjacencyIntMap where+    rnf (AM a) = rnf a++-- | Construct an 'AdjacencyIntMap' from an 'AM.AdjacencyMap' with vertices of+-- type 'Int'.+-- Complexity: /O(n + m)/ time and memory.+--+-- @+-- fromAdjacencyMap == 'stars' . AdjacencyMap.'AM.adjacencyList'+-- @+fromAdjacencyMap :: AM.AdjacencyMap Int -> AdjacencyIntMap+fromAdjacencyMap = AM+                 . IntMap.fromAscList+                 . map (fmap $ IntSet.fromAscList . Set.toAscList)+                 . Map.toAscList+                 . AM.adjacencyMap+ -- | Construct the /empty graph/. -- Complexity: /O(1)/ time and memory. --@@ -72,7 +234,7 @@ -- -- @ -- 'isEmpty'     (vertex x) == False--- 'hasVertex' x (vertex x) == True+-- 'hasVertex' x (vertex y) == (x == y) -- 'vertexCount' (vertex x) == 1 -- 'edgeCount'   (vertex x) == 0 -- @@@ -109,14 +271,14 @@ -- 'edgeCount'   (overlay 1 2) == 0 -- @ overlay :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap-overlay x y = AM $ IntMap.unionWith IntSet.union (adjacencyIntMap x) (adjacencyIntMap y)+overlay (AM x) (AM y) = AM $ IntMap.unionWith IntSet.union x y {-# NOINLINE [1] overlay #-}  -- | /Connect/ two graphs. This is an associative operation with the identity -- 'empty', which distributes over 'overlay' and obeys the decomposition axiom. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the--- number of edges in the resulting graph is quadratic with respect to the number--- of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.+-- number of edges in the resulting graph is quadratic with respect to the+-- number of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/. -- -- @ -- 'isEmpty'     (connect x y) == 'isEmpty'   x   && 'isEmpty'   y@@ -131,8 +293,8 @@ -- 'edgeCount'   (connect 1 2) == 1 -- @ connect :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap-connect x y = AM $ IntMap.unionsWith IntSet.union [ adjacencyIntMap x, adjacencyIntMap y,-    IntMap.fromSet (const . IntMap.keysSet $ adjacencyIntMap y) (IntMap.keysSet $ adjacencyIntMap x) ]+connect (AM x) (AM y) = AM $ IntMap.unionsWith IntSet.union+    [ x, y, IntMap.fromSet (const $ IntMap.keysSet y) (IntMap.keysSet x) ] {-# NOINLINE [1] connect #-}  -- | Construct the graph comprising a given list of isolated vertices.@@ -156,6 +318,7 @@ -- @ -- edges []          == 'empty' -- edges [(x,y)]     == 'edge' x y+-- edges             == 'overlays' . 'map' ('uncurry' 'edge') -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- 'edgeList' . edges  == 'Data.List.nub' . 'Data.List.sort' -- @@@ -203,7 +366,7 @@ -- isSubgraphOf x y                         ==> x <= y -- @ isSubgraphOf :: AdjacencyIntMap -> AdjacencyIntMap -> Bool-isSubgraphOf x y = IntMap.isSubmapOfBy IntSet.isSubsetOf (adjacencyIntMap x) (adjacencyIntMap y)+isSubgraphOf (AM x) (AM y) = IntMap.isSubmapOfBy IntSet.isSubsetOf x y  -- | Check if a graph is empty. -- Complexity: /O(1)/ time.@@ -223,8 +386,7 @@ -- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Int -> AdjacencyIntMap -> Bool@@ -241,7 +403,7 @@ -- hasEdge x y                  == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Int -> Int -> AdjacencyIntMap -> Bool-hasEdge u v a = case IntMap.lookup u (adjacencyIntMap a) of+hasEdge u v (AM m) = case IntMap.lookup u m of     Nothing -> False     Just vs -> IntSet.member v vs @@ -293,6 +455,7 @@ -- @ edgeList :: AdjacencyIntMap -> [(Int, Int)] edgeList (AM m) = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]+{-# INLINE edgeList #-}  -- | The set of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -592,7 +755,7 @@  -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.--- Complexity: /O(m)/ time, assuming that the predicate takes /O(1)/ to+-- Complexity: /O(n + m)/ time, assuming that the predicate takes /O(1)/ to -- be evaluated. -- -- @@@ -694,3 +857,24 @@     | otherwise  = transitiveClosure new   where     new = overlay old (old `compose` old)++-- | Check that the internal graph representation is consistent, i.e. that all+-- edges refer to existing vertices. It should be impossible to create an+-- inconsistent adjacency map, and we use this function in testing.+--+-- @+-- consistent 'empty'         == True+-- consistent ('vertex' x)    == True+-- consistent ('overlay' x y) == True+-- consistent ('connect' x y) == True+-- consistent ('edge' x y)    == True+-- consistent ('edges' xs)    == True+-- consistent ('stars' xs)    == True+-- @+consistent :: AdjacencyIntMap -> Bool+consistent (AM m) = referredToVertexSet m `IntSet.isSubsetOf` IntMap.keysSet m++-- The set of vertices that are referred to by the edges+referredToVertexSet :: IntMap IntSet -> IntSet+referredToVertexSet m = IntSet.fromList $ concat+    [ [x, y] | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]
src/Algebra/Graph/AdjacencyIntMap/Algorithm.hs view
@@ -1,3 +1,5 @@+{-# language LambdaCase #-}+ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.AdjacencyIntMap.Algorithm@@ -12,29 +14,107 @@ -- -- This module provides basic graph algorithms, such as /depth-first search/, -- implemented for the "Algebra.Graph.AdjacencyIntMap" data type.+--+-- Some of the worst-case complexities include the term /min(n,W)/.+-- Following 'IntSet.IntSet' and 'IntMap.IntMap', the /W/ stands for+-- word size (usually 32 or 64 bits). ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyIntMap.Algorithm (     -- * Algorithms-    dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic,-+    bfsForest, bfs, dfsForest, dfsForestFrom, dfs, reachable,+    topSort, isAcyclic,+         -- * Correctness properties-    isDfsForestOf, isTopSortOf+    isDfsForestOf, isTopSortOf,++    -- * Type synonyms+    Cycle     ) where  import Control.Monad-import Data.Maybe+import Control.Monad.Cont+import Control.Monad.State.Strict+import Data.Either+import Data.List.NonEmpty (NonEmpty(..),(<|)) import Data.Tree  import Algebra.Graph.AdjacencyIntMap -import qualified Data.Graph.Typed   as Typed+import qualified Data.List          as List import qualified Data.IntMap.Strict as IntMap import qualified Data.IntSet        as IntSet --- | Compute the /depth-first search/ forest of a graph that corresponds to--- searching from each of the graph vertices in the 'Ord' @a@ order.+-- | Compute the /breadth-first search/ forest of a graph, such that+--   adjacent vertices are explored in increasing order with respect+--   to their 'Ord' instance. The search is seeded by a list of+--   argument vertices that will be the roots of the resulting+--   forest. Duplicates in the list will have their first occurrence+--   expanded and subsequent ones ignored. Argument vertices not in+--   the graph are also ignored.  --+--   Let /L/ be the number of seed vertices. Complexity:+--   /O((L+m)*min(n,W))/ time and /O(n)/ space.+-- -- @+-- 'forest' (bfsForest [1,2] $ 'edge' 1 2)      == 'vertices' [1,2]+-- 'forest' (bfsForest [2]   $ 'edge' 1 2)      == 'vertex' 2+-- 'forest' (bfsForest [3]   $ 'edge' 1 2)      == 'empty'+-- 'forest' (bfsForest [2,1] $ 'edge' 1 2)      == 'vertices' [1,2]+-- 'isSubgraphOf' ('forest' $ bfsForest vs x) x == True+-- bfsForest ('vertexList' g) g               == 'map' (\v -> Node v []) ('nub' $ 'vertexList' g)+-- bfsForest [] x                           == []+-- bfsForest [1,4] (3 * (1 + 4) * (1 + 5))  == [ Node { rootLabel = 1+--                                                    , subForest = [ Node { rootLabel = 5+--                                                                         , subForest = [] }]}+--                                             , Node { rootLabel = 4+--                                                    , subForest = [] }]+-- 'forest' (bfsForest [3] ('circuit' [1..5] + 'circuit' [5,4..1])) == 'path' [3,2,1] + 'path' [3,4,5]+-- +-- @+bfsForest :: [Int] -> AdjacencyIntMap -> Forest Int+bfsForest vs g = evalState (explore [ v | v <- vs, hasVertex v g ]) IntSet.empty where+  explore = unfoldForestM_BF walk <=< filterM discovered+  walk v = (v,) <$> adjacentM v+  adjacentM v = filterM discovered $ IntSet.toList (postIntSet v g)+  discovered v = do new <- gets (not . IntSet.member v)+                    when new $ modify' (IntSet.insert v)+                    return new++-- | This is 'bfsForest' with the resulting forest converted to a+--   level structure. Adjacent vertices are explored in increasing+--   order with respect to their 'Ord' instance. Flattening the result+--   via @'concat' . 'bfs' vs@ gives an enumeration of vertices+--   reachable from @vs@ in breadth first order.+--+--   Let /L/ be the number of seed vertices. Complexity:+--   /O((L+m)*min(n,W))/ time and /O(n)/ space.+-- +-- @+-- bfs vs 'empty'                                         == []+-- bfs [] g                                             == []+-- bfs [1]   ('edge' 1 1)                                 == [[1]]+-- bfs [1]   ('edge' 1 2)                                 == [[1],[2]]+-- bfs [2]   ('edge' 1 2)                                 == [[2]]+-- bfs [1,2] ('edge' 1 2)                                 == [[1,2]]+-- bfs [2,1] ('edge' 1 2)                                 == [[2,1]]+-- bfs [3]   ('edge' 1 2)                                 == []+-- bfs [1,2] ( (1*2) + (3*4) + (5*6) )                  == [[1,2]]+-- bfs [1,3] ( (1*2) + (3*4) + (5*6) )                  == [[1,3],[2,4]]+-- bfs [3] (3 * (1 + 4) * (1 + 5))                      == [[3],[1,4,5]]+-- bfs [2] ('circuit' [1..5] + 'circuit' [5,4..1])          == [[2],[1,3],[5,4]]+-- 'concat' (bfs [3] $ 'circuit' [1..5] + 'circuit' [5,4..1]) == [3,2,4,1,5]+-- bfs vs == 'map' 'concat' . 'List.transpose' . 'map' 'levels' . 'bfsForest' vs+-- @+bfs :: [Int] -> AdjacencyIntMap -> [[Int]]+bfs vs = map concat . List.transpose . map levels . bfsForest vs++-- | Compute the /depth-first search/ forest of a graph, where+--   adjacent vertices are expanded in increasing order with respect+--   to their 'Ord' instance.+--+--   Complexity: /O((n+m)*min(n,W))/ time and /O(n)/ space.+--+-- @ -- dfsForest 'empty'                       == [] -- 'forest' (dfsForest $ 'edge' 1 1)         == 'vertex' 1 -- 'forest' (dfsForest $ 'edge' 1 2)         == 'edge' 1 2@@ -50,13 +130,20 @@ --                                          , Node { rootLabel = 3 --                                                 , subForest = [ Node { rootLabel = 4 --                                                                      , subForest = [] }]}]+-- 'forest' (dfsForest $ 'circuit' [1..5] + 'circuit' [5,4..1]) == 'path' [1,2,3,4,5] -- @ dfsForest :: AdjacencyIntMap -> Forest Int-dfsForest = Typed.dfsForest . Typed.fromAdjacencyIntMap+dfsForest g = dfsForestFrom' (vertexList g) g --- | Compute the /depth-first search/ forest of a graph, searching from each of--- the given vertices in order. Note that the resulting forest does not--- necessarily span the whole graph, as some vertices may be unreachable.+-- | Compute the /depth-first search/ forest of a graph from the given+--   vertices, where adjacent vertices are expanded in increasing+--   order with respect to to their 'Ord' instance. Note that the+--   resulting forest does not necessarily span the whole graph, as+--   some vertices may be unreachable. Any of the given vertices which+--   are not in the graph are ignored.+-- +--   Let /L/ be the number of seed vertices. Complexity:+--   /O((L+m)*min(n,W))/ time and /O(n)/ space. -- -- @ -- dfsForestFrom vs 'empty'                           == []@@ -75,12 +162,29 @@ --                                                                                 , subForest = [] } --                                                     , Node { rootLabel = 4 --                                                            , subForest = [] }]+-- 'forest' (dfsForestFrom [3] $ 'circuit' [1..5] + 'circuit' [5,4..1]) == 'path' [3,2,1,5,4] -- @ dfsForestFrom :: [Int] -> AdjacencyIntMap -> Forest Int-dfsForestFrom vs = Typed.dfsForestFrom vs . Typed.fromAdjacencyIntMap+dfsForestFrom vs g = dfsForestFrom' [ v | v <- vs, hasVertex v g ] g --- | Compute the list of vertices visited by the /depth-first search/ in a graph,--- when searching from each of the given vertices in order.+dfsForestFrom' :: [Int] -> AdjacencyIntMap -> Forest Int+dfsForestFrom' vs g = evalState (explore vs) IntSet.empty where+  explore (v:vs) = discovered v >>= \case+    True -> (:) <$> walk v <*> explore vs+    False -> explore vs+  explore [] = return []+  walk v = Node v <$> explore (adjacent v)+  adjacent v = IntSet.toList (postIntSet v g)+  discovered v = do new <- gets (not . IntSet.member v)+                    when new $ modify' (IntSet.insert v)+                    return new++-- | Compute the vertices visited by /depth-first search/ in a graph+--   from the given vertices. Adjacent vertices are explored in+--   increasing order with respect to their 'Ord' instance.+-- +--   Let /L/ be the number of seed vertices. Complexity:+--   /O((L+m)*min(n,W))/ time and /O(n)/ space. -- -- @ -- dfs vs    $ 'empty'                    == []@@ -93,14 +197,17 @@ -- dfs []    $ x                        == [] -- dfs [1,4] $ 3 * (1 + 4) * (1 + 5)    == [1,5,4] -- 'isSubgraphOf' ('vertices' $ dfs vs x) x == True+-- dfs [3] $ 'circuit' [1..5] + 'circuit' [5,4..1] == [3,2,1,5,4] -- @ dfs :: [Int] -> AdjacencyIntMap -> [Int]-dfs vs = concatMap flatten . dfsForestFrom vs+dfs vs = dfsForestFrom vs >=> flatten --- | Compute the list of vertices that are /reachable/ from a given source--- vertex in a graph. The vertices in the resulting list appear in the--- /depth-first order/.+-- | Compute the list of vertices that are /reachable/ from a given+--   source vertex in a graph. The vertices in the resulting list+--   appear in /depth-first order/. --+--   Complexity: /O(m*min(n,W))/ time and /O(n)/ space.+--  -- @ -- reachable x $ 'empty'                       == [] -- reachable 1 $ 'vertex' 1                    == [1]@@ -115,30 +222,83 @@ reachable :: Int -> AdjacencyIntMap -> [Int] reachable x = dfs [x] --- | Compute the /topological sort/ of a graph or return @Nothing@ if the graph--- is cyclic.+type Cycle = NonEmpty+data NodeState = Entered | Exited+data S = S { parent :: IntMap.IntMap Int+           , entry  :: IntMap.IntMap NodeState+           , order  :: [Int] }++topSort' :: (MonadState S m, MonadCont m)+         => AdjacencyIntMap -> m (Either (Cycle Int) [Int])+topSort' g = callCC $ \cyclic ->+  do let vertices = map fst $ IntMap.toDescList $ adjacencyIntMap g+         adjacent = IntSet.toDescList . flip postIntSet g+         dfsRoot x = nodeState x >>= \case+           Nothing -> enterRoot x >> dfs x >> exit x+           _       -> return ()+         dfs x = forM_ (adjacent x) $ \y ->+                   nodeState y >>= \case+                     Nothing      -> enter x y >> dfs y >> exit y+                     Just Exited  -> return ()+                     Just Entered -> cyclic . Left . retrace x y =<< gets parent+     forM_ vertices dfsRoot+     Right <$> gets order+  where+    nodeState v = gets (IntMap.lookup v . entry)+    enter u v = modify' (\(S m n vs) -> S (IntMap.insert v u m)+                                          (IntMap.insert v Entered n)+                                          vs)+    enterRoot v = modify' (\(S m n vs) -> S m (IntMap.insert v Entered n) vs)+    exit v = modify' (\(S m n vs) -> S m (IntMap.alter (fmap leave) v n) (v:vs))+      where leave = \case+              Entered -> Exited+              Exited  -> error "Internal error: dfs search order violated"+    retrace curr head parent = aux (curr :| []) where+      aux xs@(curr :| _)+        | head == curr = xs+        | otherwise = aux (parent IntMap.! curr <| xs)++-- | Compute a topological sort of a DAG or discover a cycle. --+--   Vertices are expanded in decreasing order with respect to their+--   'Ord' instance. This gives the lexicographically smallest+--   topological ordering in the case of success. In the case of+--   failure, the cycle is characterized by being the+--   lexicographically smallest up to rotation with respect to @Ord+--   (Dual Int)@ in the first connected component of the graph+--   containing a cycle, where the connected components are ordered by+--   their largest vertex with respect to @Ord a@.+--+--   Complexity: /O((n+m)*min(n,W))/ time and /O(n)/ space.+-- -- @--- topSort (1 * 2 + 3 * 1)               == Just [3,1,2]--- topSort (1 * 2 + 2 * 1)               == Nothing--- fmap ('flip' 'isTopSortOf' x) (topSort x) /= Just False--- 'isJust' . topSort                      == 'isAcyclic'+-- topSort (1 * 2 + 3 * 1)                    == Right [3,1,2]+-- topSort ('path' [1..5])                      == Right [1..5]+-- topSort (3 * (1 * 4 + 2 * 5))              == Right [3,1,2,4,5]+-- topSort (1 * 2 + 2 * 1)                    == Left (2 ':|' [1])+-- topSort ('path' [5,4..1] + 'edge' 2 4)         == Left (4 ':|' [3,2])+-- topSort ('circuit' [1..3])                   == Left (3 ':|' [1,2])+-- topSort ('circuit' [1..3] + 'circuit' [3,2,1]) == Left (3 ':|' [2])+-- topSort (1*2 + 2*1 + 3*4 + 4*3 + 5*1)      == Left (1 ':|' [2])+-- fmap ('flip' 'isTopSortOf' x) (topSort x)      /= Right False+-- topSort . 'vertices'                         == Right . 'nub' . 'sort' -- @-topSort :: AdjacencyIntMap -> Maybe [Int]-topSort m = if isTopSortOf result m then Just result else Nothing-  where-    result = Typed.topSort (Typed.fromAdjacencyIntMap m)+topSort :: AdjacencyIntMap -> Either (Cycle Int) [Int]+topSort g = runContT (evalStateT (topSort' g) initialState) id where+  initialState = S IntMap.empty IntMap.empty []  -- | Check if a given graph is /acyclic/. --+--   Complexity: /O((n+m)*min(n,W))/ time and /O(n)/ space.+-- -- @ -- isAcyclic (1 * 2 + 3 * 1) == True -- isAcyclic (1 * 2 + 2 * 1) == False -- isAcyclic . 'circuit'       == 'null'--- isAcyclic                 == 'isJust' . 'topSort'+-- isAcyclic                 == 'isRight' . 'topSort' -- @ isAcyclic :: AdjacencyIntMap -> Bool-isAcyclic = isJust . topSort+isAcyclic = isRight . topSort  -- | Check if a given forest is a correct /depth-first search/ forest of a graph. -- The implementation is based on the paper "Depth-First Search and Strong
− src/Algebra/Graph/AdjacencyIntMap/Internal.hs
@@ -1,206 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.AdjacencyIntMap.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : unstable------ This module exposes the implementation of adjacency maps. The API is unstable--- and unsafe, and is exposed only for documentation. You should use the--- non-internal module "Algebra.Graph.AdjacencyIntMap" instead.-------------------------------------------------------------------------------module Algebra.Graph.AdjacencyIntMap.Internal (-    -- * Adjacency map implementation-    AdjacencyIntMap (..), consistent-  ) where--import Prelude ()-import Prelude.Compat hiding (null)--import Data.Foldable (foldMap)-import Data.Monoid (getSum, Sum (..))-import Data.IntMap.Strict (IntMap, keysSet, fromSet)-import Data.IntSet (IntSet)-import Data.List-import GHC.Generics--import Control.DeepSeq (NFData (..))--import qualified Data.IntMap.Strict as IntMap-import qualified Data.IntSet        as IntSet--{-| The 'AdjacencyIntMap' data type represents a graph by a map of vertices to-their adjacency sets. We define a 'Num' instance as a convenient notation for-working with graphs:--    > 0           == vertex 0-    > 1 + 2       == overlay (vertex 1) (vertex 2)-    > 1 * 2       == connect (vertex 1) (vertex 2)-    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))-    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))--__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',-which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as-additive and multiplicative identities, and 'negate' as additive inverse.-Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when-working with algebraic graphs; we hope that in future Haskell's Prelude will-provide a more fine-grained class hierarchy for algebraic structures, which we-would be able to utilise without violating any laws.--The 'Show' instance is defined using basic graph construction primitives:--@show (empty     :: AdjacencyIntMap Int) == "empty"-show (1         :: AdjacencyIntMap Int) == "vertex 1"-show (1 + 2     :: AdjacencyIntMap Int) == "vertices [1,2]"-show (1 * 2     :: AdjacencyIntMap Int) == "edge 1 2"-show (1 * 2 * 3 :: AdjacencyIntMap Int) == "edges [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: AdjacencyIntMap Int) == "overlay (vertex 3) (edge 1 2)"@--The 'Eq' instance satisfies all axioms of algebraic graphs:--    * 'Algebra.Graph.AdjacencyIntMap.overlay' is commutative and associative:--        >       x + y == y + x-        > x + (y + z) == (x + y) + z--    * 'Algebra.Graph.AdjacencyIntMap.connect' is associative and has-    'Algebra.Graph.AdjacencyIntMap.empty' as the identity:--        >   x * empty == x-        >   empty * x == x-        > x * (y * z) == (x * y) * z--    * 'Algebra.Graph.AdjacencyIntMap.connect' distributes over-    'Algebra.Graph.AdjacencyIntMap.overlay':--        > x * (y + z) == x * y + x * z-        > (x + y) * z == x * z + y * z--    * 'Algebra.Graph.AdjacencyIntMap.connect' can be decomposed:--        > x * y * z == x * y + x * z + y * z--The following useful theorems can be proved from the above set of axioms.--    * 'Algebra.Graph.AdjacencyIntMap.overlay' has-    'Algebra.Graph.AdjacencyIntMap.empty' as the identity and is idempotent:--        >   x + empty == x-        >   empty + x == x-        >       x + x == x--    * Absorption and saturation of 'Algebra.Graph.AdjacencyIntMap.connect':--        > x * y + x + y == x * y-        >     x * x * x == x * x--When specifying the time and memory complexity of graph algorithms, /n/ and /m/-will denote the number of vertices and edges in the graph, respectively.--The total order on graphs is defined using /size-lexicographic/ comparison:--* Compare the number of vertices. In case of a tie, continue.-* Compare the sets of vertices. In case of a tie, continue.-* Compare the number of edges. In case of a tie, continue.-* Compare the sets of edges.--Here are a few examples:--@'Algebra.Graph.AdjacencyIntMap.vertex' 1 < 'Algebra.Graph.AdjacencyIntMap.vertex' 2-'Algebra.Graph.AdjacencyIntMap.vertex' 3 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 2-'Algebra.Graph.AdjacencyIntMap.vertex' 1 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 1-'Algebra.Graph.AdjacencyIntMap.edge' 1 1 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 2-'Algebra.Graph.AdjacencyIntMap.edge' 1 2 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 1 + 'Algebra.Graph.AdjacencyIntMap.edge' 2 2-'Algebra.Graph.AdjacencyIntMap.edge' 1 2 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 3@--Note that the resulting order refines the 'Algebra.Graph.AdjacencyIntMap.isSubgraphOf'-relation and is compatible with 'Algebra.Graph.AdjacencyIntMap.overlay' and-'Algebra.Graph.AdjacencyIntMap.connect' operations:--@'Algebra.Graph.AdjacencyIntMap.isSubgraphOf' x y ==> x <= y@--@'Algebra.Graph.AdjacencyIntMap.empty' <= x-x     <= x + y-x + y <= x * y@--}-newtype AdjacencyIntMap = AM {-    -- | The /adjacency map/ of a graph: each vertex is associated with a set of-    -- its direct successors. Complexity: /O(1)/ time and memory.-    ---    -- @-    -- adjacencyIntMap 'empty'      == IntMap.'IntMap.empty'-    -- adjacencyIntMap ('vertex' x) == IntMap.'IntMap.singleton' x IntSet.'IntSet.empty'-    -- adjacencyIntMap ('Algebra.Graph.AdjacencyIntMap.edge' 1 1) == IntMap.'IntMap.singleton' 1 (IntSet.'IntSet.singleton' 1)-    -- adjacencyIntMap ('Algebra.Graph.AdjacencyIntMap.edge' 1 2) == IntMap.'IntMap.fromList' [(1,IntSet.'IntSet.singleton' 2), (2,IntSet.'IntSet.empty')]-    -- @-    adjacencyIntMap :: IntMap IntSet } deriving (Eq, Generic)--instance Show AdjacencyIntMap where-    showsPrec p (AM m)-        | null vs    = showString "empty"-        | null es    = showParen (p > 10) $ vshow vs-        | vs == used = showParen (p > 10) $ eshow es-        | otherwise  = showParen (p > 10) $-                           showString "overlay (" . vshow (vs \\ used) .-                           showString ") (" . eshow es . showString ")"-      where-        vs             = IntSet.toAscList (keysSet m)-        es             = internalEdgeList m-        vshow [x]      = showString "vertex "   . showsPrec 11 x-        vshow xs       = showString "vertices " . showsPrec 11 xs-        eshow [(x, y)] = showString "edge "     . showsPrec 11 x .-                         showString " "         . showsPrec 11 y-        eshow xs       = showString "edges "    . showsPrec 11 xs-        used           = IntSet.toAscList (referredToVertexSet m)--instance Ord AdjacencyIntMap where-    compare (AM x) (AM y) = mconcat-        [ compare (vNum x) (vNum y)-        , compare (vSet x) (vSet y)-        , compare (eNum x) (eNum y)-        , compare       x        y ]-      where-        vNum = IntMap.size-        vSet = IntMap.keysSet-        eNum = getSum . foldMap (Sum . IntSet.size)---- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyIntMap'--- for more details.-instance Num AdjacencyIntMap where-    fromInteger x = AM $ IntMap.singleton (fromInteger x) IntSet.empty-    x + y  = AM $ IntMap.unionWith IntSet.union (adjacencyIntMap x) (adjacencyIntMap y)-    x * y  = AM $ IntMap.unionsWith IntSet.union [ adjacencyIntMap x, adjacencyIntMap y,-        fromSet (const . keysSet $ adjacencyIntMap y) (keysSet $ adjacencyIntMap x) ]-    signum = const (AM IntMap.empty)-    abs    = id-    negate = id--instance NFData AdjacencyIntMap where-    rnf (AM a) = rnf a---- | Check if the internal graph representation is consistent, i.e. that all--- edges refer to existing vertices. It should be impossible to create an--- inconsistent adjacency map, and we use this function in testing.--- /Note: this function is for internal use only/.------ @--- consistent 'Algebra.Graph.AdjacencyIntMap.empty'         == True--- consistent ('Algebra.Graph.AdjacencyIntMap.vertex' x)    == True--- consistent ('Algebra.Graph.AdjacencyIntMap.overlay' x y) == True--- consistent ('Algebra.Graph.AdjacencyIntMap.connect' x y) == True--- consistent ('Algebra.Graph.AdjacencyIntMap.edge' x y)    == True--- consistent ('Algebra.Graph.AdjacencyIntMap.edges' xs)    == True--- consistent ('Algebra.Graph.AdjacencyIntMap.stars' xs)    == True--- @-consistent :: AdjacencyIntMap -> Bool-consistent (AM m) = referredToVertexSet m `IntSet.isSubsetOf` keysSet m---- The set of vertices that are referred to by the edges-referredToVertexSet :: IntMap IntSet -> IntSet-referredToVertexSet = IntSet.fromList . uncurry (++) . unzip . internalEdgeList---- The list of edges in adjacency map-internalEdgeList :: IntMap IntSet -> [(Int, Int)]-internalEdgeList m = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]
src/Algebra/Graph/AdjacencyMap.hs view
@@ -1,7 +1,8 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.AdjacencyMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -37,22 +38,170 @@      -- * Graph transformation     removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap,-    induce,+    induce, induceJust, +    -- * Graph composition+    compose, box,+     -- * Relational operations-    compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure+    closure, reflexiveClosure, symmetricClosure, transitiveClosure,++    -- * Miscellaneous+    consistent     ) where -import Data.Foldable (foldMap)+import Control.DeepSeq+import Data.List ((\\))+import Data.Map.Strict (Map) import Data.Monoid import Data.Set (Set) import Data.Tree--import Algebra.Graph.AdjacencyMap.Internal+import GHC.Generics  import qualified Data.Map.Strict as Map+import qualified Data.Maybe      as Maybe import qualified Data.Set        as Set +{-| The 'AdjacencyMap' data type represents a graph by a map of vertices to+their adjacency sets. We define a 'Num' instance as a convenient notation for+working with graphs:++    > 0           == vertex 0+    > 1 + 2       == overlay (vertex 1) (vertex 2)+    > 1 * 2       == connect (vertex 1) (vertex 2)+    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))+    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))++__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.++The 'Show' instance is defined using basic graph construction primitives:++@show (empty     :: AdjacencyMap Int) == "empty"+show (1         :: AdjacencyMap Int) == "vertex 1"+show (1 + 2     :: AdjacencyMap Int) == "vertices [1,2]"+show (1 * 2     :: AdjacencyMap Int) == "edge 1 2"+show (1 * 2 * 3 :: AdjacencyMap Int) == "edges [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: AdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"@++The 'Eq' instance satisfies all axioms of algebraic graphs:++    * 'overlay' is commutative and associative:++        >       x + y == y + x+        > x + (y + z) == (x + y) + z++    * 'connect' is associative and has 'empty' as the identity:++        >   x * empty == x+        >   empty * x == x+        > x * (y * z) == (x * y) * z++    * 'connect' distributes over 'overlay':++        > x * (y + z) == x * y + x * z+        > (x + y) * z == x * z + y * z++    * 'connect' can be decomposed:++        > x * y * z == x * y + x * z + y * z++The following useful theorems can be proved from the above set of axioms.++    * 'overlay' has 'empty' as the identity and is idempotent:++        >   x + empty == x+        >   empty + x == x+        >       x + x == x++    * Absorption and saturation of 'connect':++        > x * y + x + y == x * y+        >     x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ and /m/+will denote the number of vertices and edges in the graph, respectively.++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x     <= x + y+x + y <= x * y@+-}+newtype AdjacencyMap a = AM {+    -- | The /adjacency map/ of a graph: each vertex is associated with a set of+    -- its direct successors. Complexity: /O(1)/ time and memory.+    --+    -- @+    -- adjacencyMap 'empty'      == Map.'Map.empty'+    -- adjacencyMap ('vertex' x) == Map.'Map.singleton' x Set.'Set.empty'+    -- adjacencyMap ('edge' 1 1) == Map.'Map.singleton' 1 (Set.'Set.singleton' 1)+    -- adjacencyMap ('edge' 1 2) == Map.'Map.fromList' [(1,Set.'Set.singleton' 2), (2,Set.'Set.empty')]+    -- @+    adjacencyMap :: Map a (Set a) } deriving (Eq, Generic)++instance Ord a => Ord (AdjacencyMap a) where+    compare x y = mconcat+        [ compare (vertexCount x) (vertexCount  y)+        , compare (vertexSet   x) (vertexSet    y)+        , compare (edgeCount   x) (edgeCount    y)+        , compare (edgeSet     x) (edgeSet      y) ]++instance (Ord a, Show a) => Show (AdjacencyMap a) where+    showsPrec p am@(AM m)+        | null vs    = showString "empty"+        | null es    = showParen (p > 10) $ vshow vs+        | vs == used = showParen (p > 10) $ eshow es+        | otherwise  = showParen (p > 10) $ showString "overlay ("+                     . vshow (vs \\ used) . showString ") ("+                     . eshow es . showString ")"+      where+        vs             = vertexList am+        es             = edgeList am+        vshow [x]      = showString "vertex "   . showsPrec 11 x+        vshow xs       = showString "vertices " . showsPrec 11 xs+        eshow [(x, y)] = showString "edge "     . showsPrec 11 x .+                         showString " "         . showsPrec 11 y+        eshow xs       = showString "edges "    . showsPrec 11 xs+        used           = Set.toAscList (referredToVertexSet m)++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'+-- for more details.+instance (Ord a, Num a) => Num (AdjacencyMap a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id++instance NFData a => NFData (AdjacencyMap a) where+    rnf (AM a) = rnf a+ -- | Construct the /empty graph/. -- Complexity: /O(1)/ time and memory. --@@ -71,7 +220,7 @@ -- -- @ -- 'isEmpty'     (vertex x) == False--- 'hasVertex' x (vertex x) == True+-- 'hasVertex' x (vertex y) == (x == y) -- 'vertexCount' (vertex x) == 1 -- 'edgeCount'   (vertex x) == 0 -- @@@ -108,7 +257,7 @@ -- 'edgeCount'   (overlay 1 2) == 0 -- @ overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a-overlay x y = AM $ Map.unionWith Set.union (adjacencyMap x) (adjacencyMap y)+overlay (AM x) (AM y) = AM $ Map.unionWith Set.union x y {-# NOINLINE [1] overlay #-}  -- | /Connect/ two graphs. This is an associative operation with the identity@@ -130,8 +279,8 @@ -- 'edgeCount'   (connect 1 2) == 1 -- @ connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a-connect x y = AM $ Map.unionsWith Set.union $ adjacencyMap x : adjacencyMap y :-    [ Map.fromSet (const . Map.keysSet $ adjacencyMap y) (Map.keysSet $ adjacencyMap x) ]+connect (AM x) (AM y) = AM $ Map.unionsWith Set.union $+    [ x, y, Map.fromSet (const $ Map.keysSet y) (Map.keysSet x) ] {-# NOINLINE [1] connect #-}  -- | Construct the graph comprising a given list of isolated vertices.@@ -155,6 +304,7 @@ -- @ -- edges []          == 'empty' -- edges [(x,y)]     == 'edge' x y+-- edges             == 'overlays' . 'map' ('uncurry' 'edge') -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- 'edgeList' . edges  == 'Data.List.nub' . 'Data.List.sort' -- @@@ -202,7 +352,7 @@ -- isSubgraphOf x y                         ==> x <= y -- @ isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool-isSubgraphOf x y = Map.isSubmapOfBy Set.isSubsetOf (adjacencyMap x) (adjacencyMap y)+isSubgraphOf (AM x) (AM y) = Map.isSubmapOfBy Set.isSubsetOf x y  -- | Check if a graph is empty. -- Complexity: /O(1)/ time.@@ -222,8 +372,7 @@ -- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Ord a => a -> AdjacencyMap a -> Bool@@ -240,7 +389,7 @@ -- hasEdge x y                  == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool-hasEdge u v a = case Map.lookup u (adjacencyMap a) of+hasEdge u v (AM m) = case Map.lookup u m of     Nothing -> False     Just vs -> Set.member v vs @@ -292,6 +441,7 @@ -- @ edgeList :: AdjacencyMap a -> [(a, a)] edgeList (AM m) = [ (x, y) | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ]+{-# INLINE edgeList #-}  -- | The set of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -338,7 +488,7 @@ -- preSet 1 ('edge' 1 2) == Set.'Set.empty' -- preSet y ('edge' x y) == Set.'Set.fromList' [x] -- @-preSet :: Ord a => a -> AdjacencyMap a -> Set.Set a+preSet :: Ord a => a -> AdjacencyMap a -> Set a preSet x = Set.fromAscList . map fst . filter p  . Map.toAscList . adjacencyMap   where     p (_, set) = x `Set.member` set@@ -589,7 +739,7 @@  -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.--- Complexity: /O(m)/ time, assuming that the predicate takes /O(1)/ to+-- Complexity: /O(n + m)/ time, assuming that the predicate takes /O(1)/ to -- be evaluated. -- -- @@@ -602,6 +752,22 @@ induce :: (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a induce p = AM . Map.map (Set.filter p) . Map.filterWithKey (\k _ -> p k) . adjacencyMap +-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(n + m)/ time.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'gmap' 'Just'                                    == 'id'+-- induceJust . 'gmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Ord a => AdjacencyMap (Maybe a) -> AdjacencyMap a+induceJust = AM . Map.map catMaybesSet . catMaybesMap . adjacencyMap+    where+      catMaybesSet = Set.mapMonotonic     Maybe.fromJust . Set.delete Nothing+      catMaybesMap = Map.mapKeysMonotonic Maybe.fromJust . Map.delete Nothing+ -- | Left-to-right /relational composition/ of graphs: vertices @x@ and @z@ are -- connected in the resulting graph if there is a vertex @y@, such that @x@ is -- connected to @y@ in the first graph, and @y@ is connected to @z@ in the@@ -630,6 +796,41 @@     tx = transpose x     vs = vertexSet x `Set.union` vertexSet y +-- | Compute the /Cartesian product/ of graphs.+-- Complexity: /O(n * m * log(n)^2)/ time.+--+-- @+-- box ('path' [0,1]) ('path' "ab") == 'edges' [ ((0,\'a\'), (0,\'b\'))+--                                       , ((0,\'a\'), (1,\'a\'))+--                                       , ((0,\'b\'), (1,\'b\'))+--                                       , ((1,\'a\'), (1,\'b\')) ]+-- @+--+-- Up to an isomorphism between the resulting vertex types, this operation+-- is /commutative/, /associative/, /distributes/ over 'overlay', has singleton+-- graphs as /identities/ and 'empty' as the /annihilating zero/. Below @~~@+-- stands for the equality up to an isomorphism, e.g. @(x, ()) ~~ x@.+--+-- @+-- box x y               ~~ box y x+-- box x (box y z)       ~~ box (box x y) z+-- box x ('overlay' y z)   == 'overlay' (box x y) (box x z)+-- box x ('vertex' ())     ~~ x+-- box x 'empty'           ~~ 'empty'+-- 'transpose'   (box x y) == box ('transpose' x) ('transpose' y)+-- 'vertexCount' (box x y) == 'vertexCount' x * 'vertexCount' y+-- 'edgeCount'   (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y+-- @+box :: (Ord a, Ord b) => AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap (a, b)+box (AM x) (AM y) = overlay (AM $ Map.fromAscList xs) (AM $ Map.fromAscList ys)+  where+    xs = do (a, as) <- Map.toAscList x+            b       <- Set.toAscList (Map.keysSet y)+            return ((a, b), Set.mapMonotonic (,b) as)+    ys = do a       <- Set.toAscList (Map.keysSet x)+            (b, bs) <- Map.toAscList y+            return ((a, b), Set.mapMonotonic (a,) bs)+ -- | Compute the /reflexive and transitive closure/ of a graph. -- Complexity: /O(n * m * log(n)^2)/ time. --@@ -691,3 +892,24 @@     | otherwise  = transitiveClosure new   where     new = overlay old (old `compose` old)++-- | Check that the internal graph representation is consistent, i.e. that all+-- edges refer to existing vertices. It should be impossible to create an+-- inconsistent adjacency map, and we use this function in testing.+--+-- @+-- consistent 'empty'         == True+-- consistent ('vertex' x)    == True+-- consistent ('overlay' x y) == True+-- consistent ('connect' x y) == True+-- consistent ('edge' x y)    == True+-- consistent ('edges' xs)    == True+-- consistent ('stars' xs)    == True+-- @+consistent :: Ord a => AdjacencyMap a -> Bool+consistent (AM m) = referredToVertexSet m `Set.isSubsetOf` Map.keysSet m++-- The set of vertices that are referred to by the edges of an adjacency map.+referredToVertexSet :: Ord a => Map a (Set a) -> Set a+referredToVertexSet m = Set.fromList $ concat+    [ [x, y] | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ]
src/Algebra/Graph/AdjacencyMap/Algorithm.hs view
@@ -1,3 +1,5 @@+{-# language LambdaCase #-}+ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.AdjacencyMap.Algorithm@@ -15,30 +17,104 @@ ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyMap.Algorithm (     -- * Algorithms-    dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic, scc,+    bfsForest, bfs, dfsForest, dfsForestFrom, dfs, reachable,+    topSort, isAcyclic, scc,      -- * Correctness properties-    isDfsForestOf, isTopSortOf+    isDfsForestOf, isTopSortOf,++    -- * Type synonyms+    Cycle     ) where  import Control.Monad-import Data.Foldable (toList)+import Control.Monad.Cont+import Control.Monad.State.Strict+import Data.Either+import Data.List.NonEmpty (NonEmpty(..),(<|)) import Data.Maybe import Data.Tree  import Algebra.Graph.AdjacencyMap+import Algebra.Graph.Internal -import qualified Algebra.Graph.AdjacencyMap.Internal as AM import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty-import qualified Data.Graph                          as KL-import qualified Data.Graph.Typed                    as Typed+import qualified Data.Array                          as Array+import qualified Data.List                           as List import qualified Data.Map.Strict                     as Map import qualified Data.Set                            as Set --- | Compute the /depth-first search/ forest of a graph that corresponds to--- searching from each of the graph vertices in the 'Ord' @a@ order.+-- | Compute the /breadth-first search/ forest of a graph, such that+--   adjacent vertices are explored in increasing order with respect+--   to their 'Ord' instance. The search is seeded by a list of+--   argument vertices that will be the roots of the resulting+--   forest. Duplicates in the list will have their first occurrence+--   expanded and subsequent ones ignored. Argument vertices not in+--   the graph are also ignored. --+--   Let /L/ be the number of seed vertices. Complexity:+--   /O((L+m)*log n)/ time and /O(n)/ space.+-- -- @+-- 'forest' (bfsForest [1,2] $ 'edge' 1 2)      == 'vertices' [1,2]+-- 'forest' (bfsForest [2]   $ 'edge' 1 2)      == 'vertex' 2+-- 'forest' (bfsForest [3]   $ 'edge' 1 2)      == 'empty'+-- 'forest' (bfsForest [2,1] $ 'edge' 1 2)      == 'vertices' [1,2]+-- 'isSubgraphOf' ('forest' $ bfsForest vs x) x == True+-- bfsForest ('vertexList' g) g               == 'map' (\v -> Node v []) ('nub' $ 'vertexList' g)+-- bfsForest [] x                           == []+-- bfsForest [1,4] (3 * (1 + 4) * (1 + 5))  == [ Node { rootLabel = 1+--                                                    , subForest = [ Node { rootLabel = 5+--                                                                         , subForest = [] }]}+--                                             , Node { rootLabel = 4+--                                                    , subForest = [] }]+-- 'forest' (bfsForest [3] ('circuit' [1..5] + 'circuit' [5,4..1])) == 'path' [3,2,1] + 'path' [3,4,5]+--+-- @+bfsForest :: Ord a => [a] -> AdjacencyMap a -> Forest a+bfsForest vs g = evalState (explore [ v | v <- vs, hasVertex v g ]) Set.empty where+  explore = unfoldForestM_BF walk <=< filterM discovered+  walk v = (v,) <$> adjacentM v+  adjacentM v = filterM discovered $ Set.toList (postSet v g)+  discovered v = do new <- gets (not . Set.member v)+                    when new $ modify' (Set.insert v)+                    return new++-- | This is 'bfsForest' with the resulting forest converted to a+--   level structure. Adjacent vertices are explored in increasing+--   order with respect to their 'Ord' instance. Flattening the result+--   via @'concat' . 'bfs' vs@ gives an enumeration of vertices+--   reachable from @vs@ in breadth first order.+--+--   Let /L/ be the number of seed vertices. Complexity:+--   /O((L+m)*log n)/ time and /O(n)/ space.+--+-- @+-- bfs vs 'empty'                                         == []+-- bfs [] g                                             == []+-- bfs [1]   ('edge' 1 1)                                 == [[1]]+-- bfs [1]   ('edge' 1 2)                                 == [[1],[2]]+-- bfs [2]   ('edge' 1 2)                                 == [[2]]+-- bfs [1,2] ('edge' 1 2)                                 == [[1,2]]+-- bfs [2,1] ('edge' 1 2)                                 == [[2,1]]+-- bfs [3]   ('edge' 1 2)                                 == []+-- bfs [1,2] ( (1*2) + (3*4) + (5*6) )                  == [[1,2]]+-- bfs [1,3] ( (1*2) + (3*4) + (5*6) )                  == [[1,3],[2,4]]+-- bfs [3] (3 * (1 + 4) * (1 + 5))                      == [[3],[1,4,5]]+-- bfs [2] ('circuit' [1..5] + 'circuit' [5,4..1])          == [[2],[1,3],[5,4]]+-- 'concat' (bfs [3] $ 'circuit' [1..5] + 'circuit' [5,4..1]) == [3,2,4,1,5]+-- bfs vs == 'map' 'concat' . 'List.transpose' . 'map' 'levels' . 'bfsForest' vs+-- @+bfs :: Ord a => [a] -> AdjacencyMap a -> [[a]]+bfs vs = map concat . List.transpose . map levels . bfsForest vs++-- | Compute the /depth-first search/ forest of a graph, where+--   adjacent vertices are expanded in increasing order with respect+--   to their 'Ord' instance.+--+--   Complexity: /O((n+m)*log n)/ time and /O(n)/ space.+--+-- @ -- dfsForest 'empty'                       == [] -- 'forest' (dfsForest $ 'edge' 1 1)         == 'vertex' 1 -- 'forest' (dfsForest $ 'edge' 1 2)         == 'edge' 1 2@@ -54,14 +130,21 @@ --                                          , Node { rootLabel = 3 --                                                 , subForest = [ Node { rootLabel = 4 --                                                                      , subForest = [] }]}]+-- 'forest' (dfsForest $ 'circuit' [1..5] + 'circuit' [5,4..1]) == 'path' [1,2,3,4,5] -- @ dfsForest :: Ord a => AdjacencyMap a -> Forest a-dfsForest g = dfsForestFrom (vertexList g) g+dfsForest g = dfsForestFrom' (vertexList g) g --- | Compute the /depth-first search/ forest of a graph, searching from each of--- the given vertices in order. Note that the resulting forest does not--- necessarily span the whole graph, as some vertices may be unreachable.+-- | Compute the /depth-first search/ forest of a graph from the given+--   vertices, where adjacent vertices are expanded in increasing+--   order with respect to their 'Ord' instance. Note that the+--   resulting forest does not necessarily span the whole graph, as+--   some vertices may be unreachable. Any of the given vertices which+--   are not in the graph are ignored. --+--   Let /L/ be the number of seed vertices. Complexity: /O((L+m)*log n)/+--   time and /O(n)/ space.+-- -- @ -- dfsForestFrom vs 'empty'                           == [] -- 'forest' (dfsForestFrom [1]   $ 'edge' 1 1)          == 'vertex' 1@@ -79,13 +162,30 @@ --                                                                                 , subForest = [] } --                                                     , Node { rootLabel = 4 --                                                            , subForest = [] }]+--  'forest' (dfsForestFrom [3] $ 'circuit' [1..5] + 'circuit' [5,4..1]) == 'path' [3,2,1,5,4] -- @ dfsForestFrom :: Ord a => [a] -> AdjacencyMap a -> Forest a-dfsForestFrom vs = Typed.dfsForestFrom vs . Typed.fromAdjacencyMap+dfsForestFrom vs g = dfsForestFrom' [ v | v <- vs, hasVertex v g ] g --- | Compute the list of vertices visited by the /depth-first search/ in a--- graph, when searching from each of the given vertices in order.+dfsForestFrom' :: Ord a => [a] -> AdjacencyMap a -> Forest a+dfsForestFrom' vs g = evalState (explore vs) Set.empty where+  explore (v:vs) = discovered v >>= \case+    True -> (:) <$> walk v <*> explore vs+    False -> explore vs+  explore [] = return []+  walk v = Node v <$> explore (adjacent v)+  adjacent v = Set.toList (postSet v g)+  discovered v = do new <- gets (not . Set.member v)+                    when new $ modify' (Set.insert v)+                    return new++-- | Compute the vertices visited by /depth-first search/ in a graph+--   from the given vertices. Adjacent vertices are expanded in+--   increasing order with respect to their 'Ord' instance. --+--   Let /L/ be the number of seed vertices. Complexity: /O((L+m)*log n)/+--   time and /O(n)/ space.+-- -- @ -- dfs vs    $ 'empty'                    == [] -- dfs [1]   $ 'edge' 1 1                 == [1]@@ -97,14 +197,17 @@ -- dfs []    $ x                        == [] -- dfs [1,4] $ 3 * (1 + 4) * (1 + 5)    == [1,5,4] -- 'isSubgraphOf' ('vertices' $ dfs vs x) x == True+-- dfs [3] $ 'circuit' [1..5] + 'circuit' [5,4..1] == [3,2,1,5,4] -- @ dfs :: Ord a => [a] -> AdjacencyMap a -> [a]-dfs vs = concatMap flatten . dfsForestFrom vs+dfs vs = dfsForestFrom vs >=> flatten --- | Compute the list of vertices that are /reachable/ from a given source--- vertex in a graph. The vertices in the resulting list appear in the--- /depth-first order/.+-- | Compute the list of vertices that are /reachable/ from a given+--   source vertex in a graph. The vertices in the resulting list+--   appear in /depth-first order/. --+--   Complexity: /O(m*log n)/ time and /O(n)/ space.+-- -- @ -- reachable x $ 'empty'                       == [] -- reachable 1 $ 'vertex' 1                    == [1]@@ -119,40 +222,99 @@ reachable :: Ord a => a -> AdjacencyMap a -> [a] reachable x = dfs [x] --- | Compute the /topological sort/ of a graph or return @Nothing@ if the graph--- is cyclic.+type Cycle = NonEmpty+data NodeState = Entered | Exited+data S a = S { parent :: Map.Map a a+             , entry  :: Map.Map a NodeState+             , order  :: [a] }++topSort' :: (Ord a, MonadState (S a) m, MonadCont m)+         => AdjacencyMap a -> m (Either (Cycle a) [a])+topSort' g = callCC $ \cyclic ->+  do let vertices = map fst $ Map.toDescList $ adjacencyMap g+         adjacent = Set.toDescList . flip postSet g+         dfsRoot x = nodeState x >>= \case+           Nothing -> enterRoot x >> dfs x >> exit x+           _       -> return ()+         dfs x = forM_ (adjacent x) $ \y ->+                   nodeState y >>= \case+                     Nothing      -> enter x y >> dfs y >> exit y+                     Just Exited  -> return ()+                     Just Entered -> cyclic . Left . retrace x y =<< gets parent+     forM_ vertices dfsRoot+     Right <$> gets order+  where+    nodeState v = gets (Map.lookup v . entry)+    enter u v = modify' (\(S m n vs) -> S (Map.insert v u m)+                                          (Map.insert v Entered n)+                                          vs)+    enterRoot v = modify' (\(S m n vs) -> S m (Map.insert v Entered n) vs)+    exit v = modify' (\(S m n vs) -> S m (Map.alter (fmap leave) v n) (v:vs))+      where leave = \case+              Entered -> Exited+              Exited  -> error "Internal error: dfs search order violated"+    retrace curr head parent = aux (curr :| []) where+      aux xs@(curr :| _)+        | head == curr = xs+        | otherwise = aux (parent Map.! curr <| xs)++-- | Compute a topological sort of a DAG or discover a cycle. --+--   Vertices are expanded in decreasing order with respect to their+--   'Ord' instance. This gives the lexicographically smallest+--   topological ordering in the case of success. In the case of+--   failure, the cycle is characterized by being the+--   lexicographically smallest up to rotation with respect to @Ord+--   (Dual a)@ in the first connected component of the graph+--   containing a cycle, where the connected components are ordered by+--   their largest vertex with respect to @Ord a@.+--+--   Complexity: /O((n+m)*log n)/ time and /O(n)/ space.+-- -- @--- topSort (1 * 2 + 3 * 1)               == Just [3,1,2]--- topSort (1 * 2 + 2 * 1)               == Nothing--- fmap ('flip' 'isTopSortOf' x) (topSort x) /= Just False--- 'isJust' . topSort                      == 'isAcyclic'+-- topSort (1 * 2 + 3 * 1)                    == Right [3,1,2]+-- topSort ('path' [1..5])                      == Right [1..5]+-- topSort (3 * (1 * 4 + 2 * 5))              == Right [3,1,2,4,5]+-- topSort (1 * 2 + 2 * 1)                    == Left (2 ':|' [1])+-- topSort ('path' [5,4..1] + 'edge' 2 4)         == Left (4 ':|' [3,2])+-- topSort ('circuit' [1..3])                   == Left (3 ':|' [1,2])+-- topSort ('circuit' [1..3] + 'circuit' [3,2,1]) == Left (3 ':|' [2])+-- topSort (1*2 + 2*1 + 3*4 + 4*3 + 5*1)      == Left (1 ':|' [2])+-- fmap ('flip' 'isTopSortOf' x) (topSort x)      /= Right False+-- 'isRight' . topSort                          == 'isAcyclic'+-- topSort . 'vertices'                         == Right . 'nub' . 'sort' -- @-topSort :: Ord a => AdjacencyMap a -> Maybe [a]-topSort m = if isTopSortOf result m then Just result else Nothing-  where-    result = Typed.topSort (Typed.fromAdjacencyMap m)+topSort :: Ord a => AdjacencyMap a -> Either (Cycle a) [a]+topSort g = runContT (evalStateT (topSort' g) initialState) id where+  initialState = S Map.empty Map.empty []  -- | Check if a given graph is /acyclic/. --+--   Complexity: /O((n+m)*log n)/ time and /O(n)/ space.+-- -- @ -- isAcyclic (1 * 2 + 3 * 1) == True -- isAcyclic (1 * 2 + 2 * 1) == False -- isAcyclic . 'circuit'       == 'null'--- isAcyclic                 == 'isJust' . 'topSort'+-- isAcyclic                 == 'isRight' . 'topSort' -- @ isAcyclic :: Ord a => AdjacencyMap a -> Bool-isAcyclic = isJust . topSort+isAcyclic = isRight . topSort --- TODO: Benchmark and optimise. -- | Compute the /condensation/ of a graph, where each vertex corresponds to a -- /strongly-connected component/ of the original graph. Note that component -- graphs are non-empty, and are therefore of type -- "Algebra.Graph.NonEmpty.AdjacencyMap". --+-- Details about the implementation can be found at+-- <https://github.com/jitwit/alga-notes/blob/master/gabow.org gabow-notes>.+--+-- Complexity: /O((n+m)*log n)/ time and /O(n+m)/ space.+-- -- @ -- scc 'empty'               == 'empty' -- scc ('vertex' x)          == 'vertex' (NonEmpty.'NonEmpty.vertex' x)+-- scc ('vertices' xs)       == 'vertices' ('map' 'NonEmpty.vertex' xs) -- scc ('edge' 1 1)          == 'vertex' (NonEmpty.'NonEmpty.edge' 1 1) -- scc ('edge' 1 2)          == 'edge'   (NonEmpty.'NonEmpty.vertex' 1) (NonEmpty.'NonEmpty.vertex' 2) -- scc ('circuit' (1:xs))    == 'vertex' (NonEmpty.'NonEmpty.circuit1' (1 'Data.List.NonEmpty.:|' xs))@@ -163,19 +325,99 @@ -- 'isAcyclic' x     == (scc x == 'gmap' NonEmpty.'NonEmpty.vertex' x) -- @ scc :: Ord a => AdjacencyMap a -> AdjacencyMap (NonEmpty.AdjacencyMap a)-scc m = gmap (component Map.!) $ removeSelfLoops $ gmap (leader Map.!) m+scc g = condense g $ execState (gabowSCC g) initialState where+  initialState = SCC 0 0 [] [] Map.empty Map.empty [] [] []++data StateSCC a+  = SCC { preorder      :: {-# unpack #-} !Int+        , component     :: {-# unpack #-} !Int+        , boundaryStack :: [(Int,a)]+        , pathStack     :: [a]+        , preorders     :: Map.Map a Int+        , components    :: Map.Map a Int+        , innerGraphs   :: [AdjacencyMap a]+        , innerEdges    :: [(Int,(a,a))]+        , outerEdges    :: [(a,a)]+        } deriving (Show)++gabowSCC :: Ord a => AdjacencyMap a -> State (StateSCC a) ()+gabowSCC g =+  do let dfs u = do p_u <- enter u+                    forEach (postSet u g) $ \v -> do+                      preorderId v >>= \case+                        Nothing  -> do+                          updated <- dfs v+                          if updated then outedge (u,v) else inedge (p_u,(u,v))+                        Just p_v -> do+                          scc_v <- hasComponent v+                          if scc_v+                            then outedge (u,v)+                            else popBoundary p_v >> inedge (p_u,(u,v))+                    exit u+     forM_ (vertexList g) $ \v -> do+       assigned <- hasPreorderId v+       unless assigned $ void $ dfs v   where-    Typed.GraphKL g decode _ = Typed.fromAdjacencyMap m-    sccs      = map toList (KL.scc g)-    leader    = Map.fromList [ (decode y, x)      | x:xs <- sccs, y <- x:xs ]-    component = Map.fromList [ (x, expand (x:xs)) | x:xs <- sccs ]-    expand xs = fromJust $ NonEmpty.toNonEmpty $ induce (`Set.member` s) m-      where-        s = Set.fromList (map decode xs)+    -- called when visiting vertex v. assigns preorder number to v,+    -- adds the (id, v) pair to the boundary stack b, and adds v to+    -- the path stack s.+    enter v = do SCC pre scc bnd pth pres sccs gs es_i es_o <- get+                 let pre' = pre+1+                     bnd' = (pre,v):bnd+                     pth' = v:pth+                     pres' = Map.insert v pre pres+                 put $! SCC pre' scc bnd' pth' pres' sccs gs es_i es_o+                 return pre --- Remove all self loops from a graph.-removeSelfLoops :: Ord a => AdjacencyMap a -> AdjacencyMap a-removeSelfLoops (AM.AM m) = AM.AM (Map.mapWithKey Set.delete m)+    -- called on back edges. pops the boundary stack while the top+    -- vertex has a larger preorder number than p_v.+    popBoundary p_v = modify'+      (\(SCC pre scc bnd pth pres sccs gs es_i es_o) ->+         SCC pre scc (dropWhile ((>p_v).fst) bnd) pth pres sccs gs es_i es_o)++    -- called when exiting vertex v. if v is the bottom of a scc+    -- boundary, we add a new SCC, otherwise v is part of a larger scc+    -- being constructed and we continue.+    exit v = do newComponent <- (v==).snd.head <$> gets boundaryStack+                when newComponent $ insertComponent v+                return newComponent++    insertComponent v = modify'+      (\(SCC pre scc bnd pth pres sccs gs es_i es_o) ->+         let (curr,v_pth') = span (/=v) pth+             pth' = tail v_pth' -- Here we know that v_pth' starts with v+             (es,es_i') = span ((>=p_v).fst) es_i+             g_i | null es = vertex v+                 | otherwise = edges (snd <$> es)+             p_v = fst $ head bnd+             scc' = scc + 1+             bnd' = tail bnd+             sccs' = List.foldl' (\sccs x -> Map.insert x scc sccs) sccs (v:curr)+             gs' = g_i:gs+          in SCC pre scc' bnd' pth' pres sccs' gs' es_i' es_o)++    inedge uv = modify'+      (\(SCC pre scc bnd pth pres sccs gs es_i es_o) ->+         SCC pre scc bnd pth pres sccs gs (uv:es_i) es_o)++    outedge uv = modify'+      (\(SCC pre scc bnd pth pres sccs gs es_i es_o) ->+         SCC pre scc bnd pth pres sccs gs es_i (uv:es_o))++    hasPreorderId v = gets (Map.member v . preorders)+    preorderId    v = gets (Map.lookup v . preorders)+    hasComponent  v = gets (Map.member v . components)++condense :: Ord a => AdjacencyMap a -> StateSCC a -> AdjacencyMap (NonEmpty.AdjacencyMap a)+condense g (SCC _ n _ _ _ assignment inner _ outer)+  | n == 1 = vertex $ convert g+  | otherwise = gmap (\c -> inner' Array.! (n-1-c)) outer'+  where inner' = Array.listArray (0,n-1) (convert <$> inner)+        outer' = es `overlay` vs+        vs = vertices [0..n-1]+        es = edges [ (sccid x, sccid y) | (x,y) <- outer ]+        sccid v = assignment Map.! v+        convert = fromJust . NonEmpty.toNonEmpty  -- | Check if a given forest is a correct /depth-first search/ forest of a graph. -- The implementation is based on the paper "Depth-First Search and Strong
− src/Algebra/Graph/AdjacencyMap/Internal.hs
@@ -1,207 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.AdjacencyMap.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : unstable------ This module exposes the implementation of adjacency maps. The API is unstable--- and unsafe, and is exposed only for documentation. You should use the--- non-internal module "Algebra.Graph.AdjacencyMap" instead.-------------------------------------------------------------------------------module Algebra.Graph.AdjacencyMap.Internal (-    -- * Adjacency map implementation-    AdjacencyMap (..), consistent, internalEdgeList, referredToVertexSet-  ) where--import Prelude ()-import Prelude.Compat hiding (null)--import Control.DeepSeq-import Data.Foldable (foldMap)-import Data.List-import Data.Map.Strict (Map, keysSet, fromSet)-import Data.Monoid-import Data.Set (Set)-import GHC.Generics--import qualified Data.Map.Strict as Map-import qualified Data.Set        as Set--{-| The 'AdjacencyMap' data type represents a graph by a map of vertices to-their adjacency sets. We define a 'Num' instance as a convenient notation for-working with graphs:--    > 0           == vertex 0-    > 1 + 2       == overlay (vertex 1) (vertex 2)-    > 1 * 2       == connect (vertex 1) (vertex 2)-    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))-    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))--__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',-which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as-additive and multiplicative identities, and 'negate' as additive inverse.-Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when-working with algebraic graphs; we hope that in future Haskell's Prelude will-provide a more fine-grained class hierarchy for algebraic structures, which we-would be able to utilise without violating any laws.--The 'Show' instance is defined using basic graph construction primitives:--@show (empty     :: AdjacencyMap Int) == "empty"-show (1         :: AdjacencyMap Int) == "vertex 1"-show (1 + 2     :: AdjacencyMap Int) == "vertices [1,2]"-show (1 * 2     :: AdjacencyMap Int) == "edge 1 2"-show (1 * 2 * 3 :: AdjacencyMap Int) == "edges [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: AdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"@--The 'Eq' instance satisfies all axioms of algebraic graphs:--    * 'Algebra.Graph.AdjacencyMap.overlay' is commutative and associative:--        >       x + y == y + x-        > x + (y + z) == (x + y) + z--    * 'Algebra.Graph.AdjacencyMap.connect' is associative and has-    'Algebra.Graph.AdjacencyMap.empty' as the identity:--        >   x * empty == x-        >   empty * x == x-        > x * (y * z) == (x * y) * z--    * 'Algebra.Graph.AdjacencyMap.connect' distributes over-    'Algebra.Graph.AdjacencyMap.overlay':--        > x * (y + z) == x * y + x * z-        > (x + y) * z == x * z + y * z--    * 'Algebra.Graph.AdjacencyMap.connect' can be decomposed:--        > x * y * z == x * y + x * z + y * z--The following useful theorems can be proved from the above set of axioms.--    * 'Algebra.Graph.AdjacencyMap.overlay' has 'Algebra.Graph.AdjacencyMap.empty'-    as the identity and is idempotent:--        >   x + empty == x-        >   empty + x == x-        >       x + x == x--    * Absorption and saturation of 'Algebra.Graph.AdjacencyMap.connect':--        > x * y + x + y == x * y-        >     x * x * x == x * x--When specifying the time and memory complexity of graph algorithms, /n/ and /m/-will denote the number of vertices and edges in the graph, respectively.--The total order on graphs is defined using /size-lexicographic/ comparison:--* Compare the number of vertices. In case of a tie, continue.-* Compare the sets of vertices. In case of a tie, continue.-* Compare the number of edges. In case of a tie, continue.-* Compare the sets of edges.--Here are a few examples:--@'Algebra.Graph.AdjacencyMap.vertex' 1 < 'Algebra.Graph.AdjacencyMap.vertex' 2-'Algebra.Graph.AdjacencyMap.vertex' 3 < 'Algebra.Graph.AdjacencyMap.edge' 1 2-'Algebra.Graph.AdjacencyMap.vertex' 1 < 'Algebra.Graph.AdjacencyMap.edge' 1 1-'Algebra.Graph.AdjacencyMap.edge' 1 1 < 'Algebra.Graph.AdjacencyMap.edge' 1 2-'Algebra.Graph.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.AdjacencyMap.edge' 1 1 + 'Algebra.Graph.AdjacencyMap.edge' 2 2-'Algebra.Graph.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.AdjacencyMap.edge' 1 3@--Note that the resulting order refines the 'Algebra.Graph.AdjacencyMap.isSubgraphOf'-relation and is compatible with 'Algebra.Graph.AdjacencyMap.overlay' and-'Algebra.Graph.AdjacencyMap.connect' operations:--@'Algebra.Graph.AdjacencyMap.isSubgraphOf' x y ==> x <= y@--@'Algebra.Graph.AdjacencyMap.empty' <= x-x     <= x + y-x + y <= x * y@--}-newtype AdjacencyMap a = AM {-    -- | The /adjacency map/ of a graph: each vertex is associated with a set of-    -- its direct successors. Complexity: /O(1)/ time and memory.-    ---    -- @-    -- adjacencyMap 'Algebra.Graph.AdjacencyMap.empty'      == Map.'Map.empty'-    -- adjacencyMap ('Algebra.Graph.AdjacencyMap.vertex' x) == Map.'Map.singleton' x Set.'Set.empty'-    -- adjacencyMap ('Algebra.Graph.AdjacencyMap.edge' 1 1) == Map.'Map.singleton' 1 (Set.'Set.singleton' 1)-    -- adjacencyMap ('Algebra.Graph.AdjacencyMap.edge' 1 2) == Map.'Map.fromList' [(1,Set.'Set.singleton' 2), (2,Set.'Set.empty')]-    -- @-    adjacencyMap :: Map a (Set a) } deriving (Eq, Generic)--instance Ord a => Ord (AdjacencyMap a) where-    compare (AM x) (AM y) = mconcat-        [ compare (vNum x) (vNum y)-        , compare (vSet x) (vSet y)-        , compare (eNum x) (eNum y)-        , compare       x        y ]-      where-        vNum = Map.size-        vSet = Map.keysSet-        eNum = getSum . foldMap (Sum . Set.size)--instance (Ord a, Show a) => Show (AdjacencyMap a) where-    showsPrec p (AM m)-        | null vs    = showString "empty"-        | null es    = showParen (p > 10) $ vshow vs-        | vs == used = showParen (p > 10) $ eshow es-        | otherwise  = showParen (p > 10) $-                           showString "overlay (" . vshow (vs \\ used) .-                           showString ") (" . eshow es . showString ")"-      where-        vs             = Set.toAscList (keysSet m)-        es             = internalEdgeList m-        vshow [x]      = showString "vertex "   . showsPrec 11 x-        vshow xs       = showString "vertices " . showsPrec 11 xs-        eshow [(x, y)] = showString "edge "     . showsPrec 11 x .-                         showString " "         . showsPrec 11 y-        eshow xs       = showString "edges "    . showsPrec 11 xs-        used           = Set.toAscList (referredToVertexSet m)---- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'--- for more details.-instance (Ord a, Num a) => Num (AdjacencyMap a) where-    fromInteger x = AM $ Map.singleton (fromInteger x) Set.empty-    x + y  = AM $ Map.unionWith Set.union (adjacencyMap x) (adjacencyMap y)-    x * y  = AM $ Map.unionsWith Set.union [ adjacencyMap x, adjacencyMap y,-        fromSet (const . keysSet $ adjacencyMap y) (keysSet $ adjacencyMap x) ]-    signum = const (AM Map.empty)-    abs    = id-    negate = id--instance NFData a => NFData (AdjacencyMap a) where-    rnf (AM a) = rnf a---- | Check if the internal graph representation is consistent, i.e. that all--- edges refer to existing vertices. It should be impossible to create an--- inconsistent adjacency map, and we use this function in testing.--- /Note: this function is for internal use only/.------ @--- consistent 'Algebra.Graph.AdjacencyMap.empty'         == True--- consistent ('Algebra.Graph.AdjacencyMap.vertex' x)    == True--- consistent ('Algebra.Graph.AdjacencyMap.overlay' x y) == True--- consistent ('Algebra.Graph.AdjacencyMap.connect' x y) == True--- consistent ('Algebra.Graph.AdjacencyMap.edge' x y)    == True--- consistent ('Algebra.Graph.AdjacencyMap.edges' xs)    == True--- consistent ('Algebra.Graph.AdjacencyMap.stars' xs)    == True--- @-consistent :: Ord a => AdjacencyMap a -> Bool-consistent (AM m) = referredToVertexSet m `Set.isSubsetOf` keysSet m---- | The list of edges of an adjacency map.--- /Note: this function is for internal use only/.-internalEdgeList :: Map a (Set a) -> [(a, a)]-internalEdgeList m = [ (x, y) | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ]---- | The set of vertices that are referred to by the edges of an adjacency map.--- /Note: this function is for internal use only/.-referredToVertexSet :: Ord a => Map a (Set a) -> Set a-referredToVertexSet = Set.fromList . uncurry (++) . unzip . internalEdgeList
+ src/Algebra/Graph/Bipartite/Undirected/AdjacencyMap.hs view
@@ -0,0 +1,836 @@+{-# LANGUAGE DeriveGeneric #-}+----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Bipartite.Undirected.AdjacencyMap+-- Copyright  : (c) Andrey Mokhov 2016-2020+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for+-- the motivation behind the library, the underlying theory, and+-- implementation details.+--+-- This module defines the 'AdjacencyMap' data type for undirected bipartite+-- graphs and associated functions. To avoid name clashes with+-- "Algebra.Graph.AdjacencyMap", this module can be imported qualified:+--+-- @+-- import qualified Algebra.Graph.Bipartite.Undirected.AdjacencyMap as Bipartite+-- @+----------------------------------------------------------------------------+module Algebra.Graph.Bipartite.Undirected.AdjacencyMap (+    -- * Data structure+    AdjacencyMap, leftAdjacencyMap, rightAdjacencyMap,++    -- * Basic graph construction primitives+    empty, leftVertex, rightVertex, vertex, edge, overlay, connect, vertices,+    edges, overlays, connects, swap,++    -- * Conversion functions+    toBipartite, toBipartiteWith, fromBipartite, fromBipartiteWith,++    -- * Graph properties+    isEmpty, hasLeftVertex, hasRightVertex, hasVertex, hasEdge, leftVertexCount,+    rightVertexCount, vertexCount, edgeCount, leftVertexList, rightVertexList,+    vertexList, edgeList, leftVertexSet, rightVertexSet, vertexSet, edgeSet,++    -- * Standard families of graphs+    circuit, biclique,++    -- * Algorithms+    OddCycle, detectParts,++    -- * Miscellaneous+    consistent+    ) where++import Control.Monad+import Control.Monad.Trans.Maybe+import Control.Monad.State+import Data.Either+import Data.Foldable+import Data.List+import Data.Map.Strict (Map)+import Data.Maybe+import Data.Set (Set)+import GHC.Generics++import qualified Algebra.Graph.AdjacencyMap as AM++import qualified Data.Map.Strict as Map+import qualified Data.Set        as Set+import qualified Data.Tuple++{-| The 'Bipartite.AdjacencyMap' data type represents an undirected bipartite+graph. The two type parameteters define the types of identifiers of the vertices+of each part.++__Note:__ even if the identifiers and their types for two vertices of different+parts are equal, these vertices are considered to be different. See examples for+more details.++We define a 'Num' instance as a convenient notation for working with bipartite+graphs:++@+0                     == rightVertex 0+'swap' 1                == leftVertex 1+'swap' 1 + 2            == vertices [1] [2]+'swap' 1 * 2            == edge 1 2+'swap' 1 + 2 * 'swap' 3   == overlay (leftVertex 1) (edge 3 2)+'swap' 1 * (2 + 'swap' 3) == connect (leftVertex 1) (vertices [3] [2])+@++__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.++The 'Show' instance is defined using basic graph construction primitives:++@+show empty                 == "empty"+show 1                     == "rightVertex 1"+show ('swap' 2)              == "leftVertex 2"+show (1 + 2)               == "vertices [] [1,2]"+show ('swap' (1 + 2))        == "vertices [1,2] []"+show ('swap' 1 * 2)          == "edge 1 2"+show ('swap' 1 * 2 * 'swap' 3) == "edges [(1,2),(3,2)]"+show ('swap' 1 * 2 + 'swap' 3) == "overlay (leftVertex 3) (edge 1 2)"+@++The 'Eq' instance satisfies all axioms of algebraic graphs:++    * 'overlay' is commutative and associative:++        >       x + y == y + x+        > x + (y + z) == (x + y) + z++    * 'connect' is commutative, associative and has 'empty' as the identity:++        >   x * empty == x+        >   empty * x == x+        >       x * y == y * x+        > x * (y * z) == (x * y) * z++    * 'connect' distributes over 'overlay':++        > x * (y + z) == x * y + x * z+        > (x + y) * z == x * z + y * z++    * 'connect' can be decomposed:++        > x * y * z == x * y + x * z + y * z++    * 'connect' has the same effect as 'overlay' on vertices of one part:++        >  leftVertex x * leftVertex y  ==  leftVertex x + leftVertex y+        > rightVertex x * rightVertex y == rightVertex x + rightVertex y++The following useful theorems can be proved from the above set of axioms.++    * 'overlay' has 'empty' as the identity and is idempotent:++        > x + empty == x+        > empty + x == x+        >     x + x == x++    * Absorption and saturation of 'connect':++        > x * y + x + y == x * y+        >     x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ and /m/+will denote the number of vertices and edges in the graph, respectively. In+addition, /l/ and /r/ will denote the number of vertices in the left and in the+right part of graph, respectively.+-}+data AdjacencyMap a b = BAM {+    -- | The /adjacency map/ of the left part of the graph: each left vertex is+    -- associated with a set of its right neighbours.+    -- Complexity: /O(1)/ time and memory.+    --+    -- @+    -- leftAdjacencyMap 'empty'           == Map.'Map.empty'+    -- leftAdjacencyMap ('leftVertex' x)  == Map.'Map.singleton' x Set.'Set.empty'+    -- leftAdjacencyMap ('rightVertex' x) == Map.'Map.empty'+    -- leftAdjacencyMap ('edge' x y)      == Map.'Map.singleton' x (Set.'Set.singleton' y)+    -- @+    leftAdjacencyMap :: Map a (Set b),++    -- | The /adjacency map/ of the right part of the graph: each right vertex+    -- is associated with a set of left neighbours.+    -- Complexity: /O(1)/ time and memory.+    --+    -- @+    -- rightAdjacencyMap 'empty'           == Map.'Map.empty'+    -- rightAdjacencyMap ('leftVertex' x)  == Map.'Map.empty'+    -- rightAdjacencyMap ('rightVertex' x) == Map.'Map.singleton' x Set.'Set.empty'+    -- rightAdjacencyMap ('edge' x y)      == Map.'Map.singleton' y (Set.'Set.singleton' x)+    -- @+    rightAdjacencyMap :: Map b (Set a)+    } deriving Generic++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'+-- for more details.+instance (Ord a, Ord b, Num b) => Num (AdjacencyMap a b) where+    fromInteger = rightVertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id++instance (Ord a, Ord b) => Eq (AdjacencyMap a b) where+    BAM lr1 rl1 == BAM lr2 rl2 = lr1 == lr2 && Map.keysSet rl1 == Map.keysSet rl2++instance (Ord a, Ord b) => Ord (AdjacencyMap a b) where+    compare x y = mconcat+        [ compare (vertexCount x) (vertexCount  y)+        , compare (vertexSet   x) (vertexSet    y)+        , compare (edgeCount   x) (edgeCount    y)+        , compare (edgeSet     x) (edgeSet      y) ]++instance (Ord a, Ord b, Show a, Show b) => Show (AdjacencyMap a b) where+    showsPrec p bam+        | null lvs && null rvs             = showString "empty"+        | null es                          = showParen (p > 10) $ vshow lvs rvs+        | (lvs == lused) && (rvs == rused) = showParen (p > 10) $ eshow es+        | otherwise                        = showParen (p > 10)+                                           $ showString "overlay ("+                                           . veshow (vs \\ used)+                                           . showString ") ("+                                           . eshow es+                                           . showString ")"+      where+        lvs = leftVertexList bam+        rvs = rightVertexList bam+        vs  = vertexList bam+        es  = edgeList bam+        vshow [x] [] = showString "leftVertex " . showsPrec 11 x+        vshow [] [x] = showString "rightVertex " . showsPrec 11 x+        vshow xs ys  = showString "vertices " . showsPrec 11 xs+                     . showString " " . showsPrec 11 ys+        veshow xs      = vshow (lefts xs) (rights xs)+        eshow [(x, y)] = showString "edge " . showsPrec 11 x+                       . showString " " . showsPrec 11 y+        eshow es       = showString "edges " . showsPrec 11 es+        lused = Set.toAscList $ Set.fromAscList [ u | (u, _) <- edgeList bam ]+        rused = Set.toAscList $ Set.fromList    [ v | (_, v) <- edgeList bam ]+        used  = map Left lused ++ map Right rused++-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' empty           == True+-- 'leftAdjacencyMap' empty  == Map.'Map.empty'+-- 'rightAdjacencyMap' empty == Map.'Map.empty'+-- 'hasVertex' x empty       == False+-- @+empty :: AdjacencyMap a b+empty = BAM Map.empty Map.empty++-- | Construct the bipartite graph comprising /a single isolated vertex/ in+-- the left part.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'leftAdjacencyMap' (leftVertex x)  == Map.'Map.singleton' x Set.'Set.empty'+-- 'rightAdjacencyMap' (leftVertex x) == Map.'Map.empty'+-- 'hasLeftVertex' x (leftVertex y)   == (x == y)+-- 'hasRightVertex' x (leftVertex y)  == False+-- 'hasEdge' x y (leftVertex z)       == False+-- @+leftVertex :: a -> AdjacencyMap a b+leftVertex x = BAM (Map.singleton x Set.empty) Map.empty++-- | Construct the bipartite graph comprising /a single isolated vertex/ in+-- the right part.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'leftAdjacencyMap' (rightVertex x)  == Map.'Map.empty'+-- 'rightAdjacencyMap' (rightVertex x) == Map.'Map.singleton' x Set.'Set.empty'+-- 'hasLeftVertex' x (rightVertex y)   == False+-- 'hasRightVertex' x (rightVertex y)  == (x == y)+-- 'hasEdge' x y (rightVertex z)       == False+-- @+rightVertex :: b -> AdjacencyMap a b+rightVertex y = BAM Map.empty (Map.singleton y Set.empty)++-- | Construct the bipartite graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- vertex . Left  == 'leftVertex'+-- vertex . Right == 'rightVertex'+-- @+vertex :: Either a b -> AdjacencyMap a b+vertex (Left x)  = leftVertex x+vertex (Right y) = rightVertex y++-- | Construct the bipartite graph comprising /a single edge/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- edge x y                     == 'connect' ('leftVertex' x) ('rightVertex' y)+-- 'leftAdjacencyMap' (edge x y)  == Map.'Map.singleton' x (Set.'Set.singleton' y)+-- 'rightAdjacencyMap' (edge x y) == Map.'Map.singleton' y (Set.'Set.singleton' x)+-- 'hasEdge' x y (edge x y)       == True+-- 'hasEdge' 1 2 (edge 2 1)       == False+-- @+edge :: a -> b -> AdjacencyMap a b+edge x y =+    BAM (Map.singleton x (Set.singleton y)) (Map.singleton y (Set.singleton x))++-- | /Overlay/ two bipartite graphs. This is a commutative, associative and+-- idempotent operation with the identity 'empty'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'isEmpty'     (overlay x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (overlay x y) >= 'edgeCount' x+-- 'edgeCount'   (overlay x y) <= 'edgeCount' x   + 'edgeCount' y+-- @+overlay :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b+overlay (BAM lr1 rl1) (BAM lr2 rl2) =+    BAM (Map.unionWith Set.union lr1 lr2) (Map.unionWith Set.union rl1 rl2)++-- | /Connect/ two bipartite graphs, not adding the edges between vertices in+-- the same part. This is a commutative and associative operation with the+-- identity 'empty', which distributes over 'overlay' and obeys the+-- decomposition axiom.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the+-- number of edges in the resulting graph is quadratic with respect to the+-- number of vertices in the arguments: /O(m1 + m2 + l1 * r2 + l2 * r1)/.+--+-- @+-- connect ('leftVertex' x)     ('leftVertex' y)     == 'vertices' [x,y] []+-- connect ('leftVertex' x)     ('rightVertex' y)    == 'edge' x y+-- connect ('rightVertex' x)    ('leftVertex' y)     == 'edge' y x+-- connect ('rightVertex' x)    ('rightVertex' y)    == 'vertices' [] [x,y]+-- connect ('vertices' xs1 ys1) ('vertices' xs2 ys2) == 'overlay' ('biclique' xs1 ys2) ('biclique' xs2 ys1)+-- 'isEmpty'     (connect x y)                     == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (connect x y)                     == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y)                     >= 'vertexCount' x+-- 'vertexCount' (connect x y)                     <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (connect x y)                     >= 'edgeCount' x+-- 'edgeCount'   (connect x y)                     >= 'leftVertexCount' x * 'rightVertexCount' y+-- 'edgeCount'   (connect x y)                     <= 'leftVertexCount' x * 'rightVertexCount' y + 'rightVertexCount' x * 'leftVertexCount' y + 'edgeCount' x + 'edgeCount' y+-- @+connect :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b+connect (BAM lr1 rl1) (BAM lr2 rl2) = BAM lr rl+  where+    l1 = Map.keysSet lr1+    l2 = Map.keysSet lr2+    r1 = Map.keysSet rl1+    r2 = Map.keysSet rl2+    lr = Map.unionsWith Set.union+        [ lr1, lr2, Map.fromSet (const r2) l1, Map.fromSet (const r1) l2 ]+    rl = Map.unionsWith Set.union+        [ rl1, rl2, Map.fromSet (const l2) r1, Map.fromSet (const l1) r2 ]++-- | Construct the graph comprising two given lists of isolated vertices for+-- each part.+-- Complexity: /O(L * log(L))/ time and /O(L)/ memory, where /L/ is the total+-- length of two lists.+--+-- @+-- vertices [] []                    == 'empty'+-- vertices [x] []                   == 'leftVertex' x+-- vertices [] [x]                   == 'rightVertex' x+-- 'hasLeftVertex'  x (vertices xs ys) == 'elem' x xs+-- 'hasRightVertex' y (vertices xs ys) == 'elem' y ys+-- @+vertices :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b+vertices ls rs = BAM (Map.fromList [ (l, Set.empty) | l <- ls ])+                     (Map.fromList [ (r, Set.empty) | r <- rs ])++-- | Construct the graph from a list of edges.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- edges []            == 'empty'+-- edges [(x,y)]       == 'edge' x y+-- edges               == 'overlays' . 'map' ('uncurry' 'edge')+-- 'hasEdge' x y . edges == 'elem' (x,y)+-- 'edgeCount'   . edges == 'length' . 'nub'+-- @+edges :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b+edges es = BAM (Map.fromListWith Set.union [ (x, Set.singleton y) | (x, y) <- es ])+               (Map.fromListWith Set.union [ (y, Set.singleton x) | (x, y) <- es ])++-- | Overlay a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- overlays []        == 'empty'+-- overlays [x]       == x+-- overlays [x,y]     == 'overlay' x y+-- overlays           == 'foldr' 'overlay' 'empty'+-- 'isEmpty' . overlays == 'all' 'isEmpty'+-- @+overlays :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b+overlays ams = BAM (Map.unionsWith Set.union (map leftAdjacencyMap  ams))+                   (Map.unionsWith Set.union (map rightAdjacencyMap ams))++-- | Connect a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- connects []        == 'empty'+-- connects [x]       == x+-- connects [x,y]     == connect x y+-- connects           == 'foldr' 'connect' 'empty'+-- 'isEmpty' . connects == 'all' 'isEmpty'+-- @+connects :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b+connects = foldr connect empty++-- | Swap parts of a given graph.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- swap 'empty'            == 'empty'+-- swap . 'leftVertex'     == 'rightVertex'+-- swap ('vertices' xs ys) == 'vertices' ys xs+-- swap ('edge' x y)       == 'edge' y x+-- swap . 'edges'          == 'edges' . 'map' Data.Tuple.'Data.Tuple.swap'+-- swap . swap           == 'id'+-- @+swap :: AdjacencyMap a b -> AdjacencyMap b a+swap (BAM lr rl) = BAM rl lr++-- | Construct a bipartite 'AdjacencyMap' from an "Algebra.Graph.AdjacencyMap"+-- with given part identifiers, adding all needed edges to make the graph+-- undirected and removing all edges within the same parts.+-- Complexity: /O(m * log(n))/.+--+-- @+-- toBipartite 'Algebra.Graph.AdjacencyMap.empty'                      == 'empty'+-- toBipartite ('Algebra.Graph.AdjacencyMap.vertex' (Left x))          == 'leftVertex' x+-- toBipartite ('Algebra.Graph.AdjacencyMap.vertex' (Right x))         == 'rightVertex' x+-- toBipartite ('Algebra.Graph.AdjacencyMap.edge' (Left x) (Left y))   == 'vertices' [x,y] []+-- toBipartite ('Algebra.Graph.AdjacencyMap.edge' (Left x) (Right y))  == 'edge' x y+-- toBipartite ('Algebra.Graph.AdjacencyMap.edge' (Right x) (Left y))  == 'edge' y x+-- toBipartite ('Algebra.Graph.AdjacencyMap.edge' (Right x) (Right y)) == 'vertices' [] [x,y]+-- toBipartite ('Algebra.Graph.AdjacencyMap.clique' xs)                == 'uncurry' 'biclique' ('partitionEithers' xs)+-- toBipartite . 'fromBipartite'            == 'id'+-- @+toBipartite :: (Ord a, Ord b) => AM.AdjacencyMap (Either a b) -> AdjacencyMap a b+toBipartite m = BAM (Map.fromAscList [ (x, setRights ys) | (Left  x, ys) <- symmetricList ])+                    (Map.fromAscList [ (x, setLefts  ys) | (Right x, ys) <- symmetricList ])+  where+    setRights     = Set.fromAscList . rights . Set.toAscList+    setLefts      = Set.fromAscList . lefts  . Set.toAscList+    symmetricList = Map.toAscList $ AM.adjacencyMap $ AM.symmetricClosure m++-- | Construct a bipartite 'AdjacencyMap' from "Algebra.Graph.AdjacencyMap"+-- with part identifiers obtained from a given function, adding all neeeded+-- edges to make the graph undirected and removing all edges within the same+-- parts.+-- Complexity: /O(m * log(n))/.+--+-- @+-- toBipartiteWith f 'Algebra.Graph.AdjacencyMap.empty' == 'empty'+-- toBipartiteWith Left x  == 'vertices' ('vertexList' x) []+-- toBipartiteWith Right x == 'vertices' [] ('vertexList' x)+-- toBipartiteWith f       == 'toBipartite' . 'Algebra.Graph.AdjacencyMap.gmap' f+-- toBipartiteWith id      == 'toBipartite'+-- @+toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c) -> AM.AdjacencyMap a -> AdjacencyMap b c+toBipartiteWith f = toBipartite . AM.gmap f++-- | Construct an 'Algrebra.Graph.AdjacencyMap' from a bipartite 'AdjacencyMap'.+-- Complexity: /O(m * log(n))/.+--+-- @+-- fromBipartite 'empty'          == 'Algebra.Graph.AdjacencyMap.empty'+-- fromBipartite ('leftVertex' x) == 'Algebra.Graph.AdjacencyMap.vertex' (Left x)+-- fromBipartite ('edge' x y)     == 'Algebra.Graph.AdjacencyMap.edges' [(Left x, Right y), (Right y, Left x)]+-- 'toBipartite' . fromBipartite  == 'id'+-- @+fromBipartite :: (Ord a, Ord b) => AdjacencyMap a b -> AM.AdjacencyMap (Either a b)+fromBipartite (BAM lr rl) = AM.fromAdjacencySets $+    [ (Left  x, Set.mapMonotonic Right ys) | (x, ys) <- Map.toAscList lr ] +++    [ (Right y, Set.mapMonotonic Left  xs) | (y, xs) <- Map.toAscList rl ]++-- | Construct an 'Algrebra.Graph.AdjacencyMap' from a bipartite 'AdjacencyMap'+-- given a way to inject vertices from different parts into the resulting vertex+-- type.+-- Complexity: /O(m * log(n))/.+--+-- @+-- fromBipartiteWith Left Right             == 'fromBipartite'+-- fromBipartiteWith id id ('vertices' xs ys) == 'Algebra.Graph.AdjacencyMap.vertices' (xs ++ ys)+-- fromBipartiteWith id id . 'edges'          == 'Algebra.Graph.AdjacencyMap.symmetricClosure' . 'Algebra.Graph.AdjacencyMap.edges'+-- @+fromBipartiteWith :: Ord c => (a -> c) -> (b -> c) -> AdjacencyMap a b -> AM.AdjacencyMap c+fromBipartiteWith f g (BAM lr rl) = AM.fromAdjacencySets $+    [ (f x, Set.map g ys) | (x, ys) <- Map.toAscList lr ] +++    [ (g y, Set.map f xs) | (y, xs) <- Map.toAscList rl ]++-- | Check if a graph is empty.+-- Complecity: /O(1)/ time.+--+-- @+-- isEmpty 'empty'                 == True+-- isEmpty ('overlay' 'empty' 'empty') == True+-- isEmpty ('vertex' x)            == False+-- isEmpty                       == (==) 'empty'+-- @+isEmpty :: AdjacencyMap a b -> Bool+isEmpty (BAM lr rl) = Map.null lr && Map.null rl++-- | Check if a graph contains a given vertex in the left part.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasLeftVertex x 'empty'           == False+-- hasLeftVertex x ('leftVertex' y)  == (x == y)+-- hasLeftVertex x ('rightVertex' y) == False+-- @+hasLeftVertex :: Ord a => a -> AdjacencyMap a b -> Bool+hasLeftVertex x (BAM lr _) = Map.member x lr++-- | Check if a graph contains a given vertex in the right part.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasRightVertex x 'empty'           == False+-- hasRightVertex x ('leftVertex' y)  == False+-- hasRightVertex x ('rightVertex' y) == (x == y)+-- @+hasRightVertex :: Ord b => b -> AdjacencyMap a b -> Bool+hasRightVertex y (BAM _ rl) = Map.member y rl++-- | Check if a graph contains a given vertex.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasVertex . Left  == 'hasLeftVertex'+-- hasVertex . Right == 'hasRightVertex'+-- @+hasVertex :: (Ord a, Ord b) => Either a b -> AdjacencyMap a b -> Bool+hasVertex (Left x)  = hasLeftVertex x+hasVertex (Right y) = hasRightVertex y++-- | Check if a graph contains a given edge.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasEdge x y 'empty'      == False+-- hasEdge x y ('vertex' z) == False+-- hasEdge x y ('edge' x y) == True+-- hasEdge x y            == 'elem' (x,y) . 'edgeList'+-- @+hasEdge :: (Ord a, Ord b) => a -> b -> AdjacencyMap a b -> Bool+hasEdge x y (BAM m _) = (Set.member y <$> Map.lookup x m) == Just True++-- | The number of vertices in the left part in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- leftVertexCount 'empty'           == 0+-- leftVertexCount ('leftVertex' x)  == 1+-- leftVertexCount ('rightVertex' x) == 0+-- leftVertexCount ('edge' x y)      == 1+-- leftVertexCount . 'edges'         == 'length' . 'nub' . 'map' 'fst'+-- @+leftVertexCount :: AdjacencyMap a b -> Int+leftVertexCount = Map.size . leftAdjacencyMap++-- | The number of vertices in the right part in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- rightVertexCount 'empty'           == 0+-- rightVertexCount ('leftVertex' x)  == 0+-- rightVertexCount ('rightVertex' x) == 1+-- rightVertexCount ('edge' x y)      == 1+-- rightVertexCount . 'edges'         == 'length' . 'nub' . 'map' 'snd'+-- @+rightVertexCount :: AdjacencyMap a b -> Int+rightVertexCount = Map.size . rightAdjacencyMap++-- | The number of vertices in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- vertexCount 'empty'      == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount ('edge' x y) == 2+-- vertexCount x          == 'leftVertexCount' x + 'rightVertexCount' x+-- @+vertexCount :: AdjacencyMap a b -> Int+vertexCount g = leftVertexCount g + rightVertexCount g++-- | The number of edges in a graph.+-- Complexity: /O(n)/ time.+--+-- @+-- edgeCount 'empty'      == 0+-- edgeCount ('vertex' x) == 0+-- edgeCount ('edge' x y) == 1+-- edgeCount . 'edges'    == 'length' . 'nub'+-- @+edgeCount :: AdjacencyMap a b -> Int+edgeCount = Map.foldr ((+) . Set.size) 0 . leftAdjacencyMap++-- | The sorted list of vertices of the left part of a given graph.+-- Complexity: /O(l)/ time and memory.+--+-- @+-- leftVertexList 'empty'              == []+-- leftVertexList ('leftVertex' x)     == [x]+-- leftVertexList ('rightVertex' x)    == []+-- leftVertexList . 'flip' 'vertices' [] == 'nub' . 'sort'+-- @+leftVertexList :: AdjacencyMap a b -> [a]+leftVertexList = Map.keys . leftAdjacencyMap++-- | The sorted list of vertices of the right part of a given graph.+-- Complexity: /O(r)/ time and memory.+--+-- @+-- rightVertexList 'empty'           == []+-- rightVertexList ('leftVertex' x)  == []+-- rightVertexList ('rightVertex' x) == [x]+-- rightVertexList . 'vertices' []   == 'nub' . 'sort'+-- @+rightVertexList :: AdjacencyMap a b -> [b]+rightVertexList = Map.keys . rightAdjacencyMap++-- | The sorted list of vertices of a given graph.+-- Complexity: /O(n)/ time and memory+--+-- @+-- vertexList 'empty'                             == []+-- vertexList ('vertex' x)                        == [x]+-- vertexList ('edge' x y)                        == [Left x, Right y]+-- vertexList ('vertices' ('lefts' xs) ('rights' xs)) == 'nub' ('sort' xs)+-- @+vertexList :: AdjacencyMap a b -> [Either a b]+vertexList g = map Left (leftVertexList g) ++ map Right (rightVertexList g)++-- | The sorted list of edges of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeList 'empty'      == []+-- edgeList ('vertex' x) == []+-- edgeList ('edge' x y) == [(x,y)]+-- edgeList . 'edges'    == 'nub' . 'sort'+-- @+edgeList :: AdjacencyMap a b -> [(a, b)]+edgeList (BAM lr _) = [ (x, y) | (x, ys) <- Map.toAscList lr, y <- Set.toAscList ys ]++-- | The set of vertices of the left part of a given graph.+-- Complexity: /O(l)/ time and memory.+--+-- @+-- leftVertexSet 'empty'              == Set.'Set.empty'+-- leftVertexSet . 'leftVertex'       == Set.'Set.singleton'+-- leftVertexSet . 'rightVertex'      == 'const' Set.'Set.empty'+-- leftVertexSet . 'flip' 'vertices' [] == Set.'Set.fromList'+-- @+leftVertexSet :: AdjacencyMap a b -> Set a+leftVertexSet = Map.keysSet . leftAdjacencyMap++-- | The set of vertices of the right part of a given graph.+-- Complexity: /O(r)/ time and memory.+--+-- @+-- rightVertexSet 'empty'         == Set.'Set.empty'+-- rightVertexSet . 'leftVertex'  == 'const' Set.'Set.empty'+-- rightVertexSet . 'rightVertex' == Set.'Set.singleton'+-- rightVertexSet . 'vertices' [] == Set.'Set.fromList'+-- @+rightVertexSet :: AdjacencyMap a b -> Set b+rightVertexSet = Map.keysSet . rightAdjacencyMap++-- | The set of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexSet 'empty'                             == Set.'Set.empty'+-- vertexSet . 'vertex'                          == Set.'Set.singleton'+-- vertexSet ('edge' x y)                        == Set.'Set.fromList' [Left x, Right y]+-- vertexSet ('vertices' ('lefts' xs) ('rights' xs)) == Set.'Set.fromList' xs+-- @+vertexSet :: (Ord a, Ord b) => AdjacencyMap a b -> Set (Either a b)+vertexSet = Set.fromAscList . vertexList++-- | The set of edges of a given graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeSet 'empty'      == Set.'Data.Set.empty'+-- edgeSet ('vertex' x) == Set.'Data.Set.empty'+-- edgeSet ('edge' x y) == Set.'Data.Set.singleton' (x,y)+-- edgeSet . 'edges'    == Set.'Data.Set.fromList'+-- @+edgeSet :: (Ord a, Ord b) => AdjacencyMap a b -> Set (a, b)+edgeSet = Set.fromAscList . edgeList++-- | The /circuit/ on a list of vertices.+-- Complexity: /O(n * log(n))/ time and /O(n)/ memory.+--+-- @+-- circuit []                    == 'empty'+-- circuit [(x,y)]               == 'edge' x y+-- circuit [(1,2), (3,4)]        == 'biclique' [1,3] [2,4]+-- circuit [(1,2), (3,4), (5,6)] == 'edges' [(1,2), (3,2), (3,4), (5,4), (5,6), (1,6)]+-- circuit . 'reverse'             == 'swap' . circuit . 'map' Data.Tuple.'Data.Tuple.swap'+-- @+circuit :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b+circuit [] = empty+circuit xs = edges $ xs ++ zip (drop 1 $ cycle as) bs+  where+    (as, bs) = unzip xs++-- | The /biclique/ on two lists of vertices.+-- Complexity: /O(n * log(n) + m)/ time and /O(n + m)/ memory.+--+-- @+-- biclique [] [] == 'empty'+-- biclique xs [] == 'vertices' xs []+-- biclique [] ys == 'vertices' [] ys+-- biclique xs ys == 'connect' ('vertices' xs []) ('vertices' [] ys)+-- @+biclique :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b+biclique xs ys = BAM (Map.fromSet (const sys) sxs) (Map.fromSet (const sxs) sys)+  where+    sxs = Set.fromList xs+    sys = Set.fromList ys++data Part = LeftPart | RightPart deriving (Show, Eq)++otherPart :: Part -> Part+otherPart LeftPart  = RightPart+otherPart RightPart = LeftPart++-- | An cycle of odd length. For example, @[1, 2, 3]@ represents the cycle+-- @1 -> 2 -> 3 -> 1@.+type OddCycle a = [a] -- TODO: Make this representation type-safe++-- | Test the bipartiteness of given graph. In case of success, return an+-- 'AdjacencyMap' with the same set of edges and each vertex marked with the+-- part it belongs to. In case of failure, return any cycle of odd length in the+-- graph.+--+-- The returned partition is lexicographically minimal. That is, consider the+-- string of part identifiers for each vertex in ascending order. Then,+-- considering that the identifier of the left part is less then the identifier+-- of the right part, this string is lexicographically minimal of all such+-- strings for all partitions.+--+-- The returned cycle is optimal in the following way: there exists a path that+-- is either empty or ends in a vertex adjacent to the first vertex in the+-- cycle, such that all vertices in @path ++ cycle@ are distinct and+-- @path ++ cycle@ is lexicographically minimal among all such pairs of paths+-- and cycles.+--+-- /Note/: since 'AdjacencyMap' represents __undirected__ bipartite graphs, all+-- edges in the input graph are treated as undirected. See the examples and the+-- correctness property for a clarification.+--+-- It is advised to use 'leftVertexList' and 'rightVertexList' to obtain the+-- partition of the vertices and 'hasLeftVertex' and 'hasRightVertex' to check+-- whether a vertex belongs to a part.+--+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- detectParts 'Algebra.Graph.AdjacencyMap.empty'                                       == Right 'empty'+-- detectParts ('Algebra.Graph.AdjacencyMap.vertex' x)                                  == Right ('leftVertex' x)+-- detectParts ('Algebra.Graph.AdjacencyMap.edge' x x)                                  == Left [x]+-- detectParts ('Algebra.Graph.AdjacencyMap.edge' 1 2)                                  == Right ('edge' 1 2)+-- detectParts (1 * (2 + 3))                               == Right ('edges' [(1,2), (1,3)])+-- detectParts (1 * 2 * 3)                                 == Left [1, 2, 3]+-- detectParts ((1 + 3) * (2 + 4) + 6 * 5)                 == Right ('swap' (1 + 3) * (2 + 4) + 'swap' 5 * 6)+-- detectParts ((1 * 3 * 4) + 2 * (1 + 2))                 == Left [2]+-- detectParts ('Algebra.Graph.AdjacencyMap.clique' [1..10])                            == Left [1, 2, 3]+-- detectParts ('Algebra.Graph.AdjacencyMap.circuit' [1..10])                           == Right ('circuit' [(x, x + 1) | x <- [1,3,5,7,9]])+-- detectParts ('Algebra.Graph.AdjacencyMap.circuit' [1..11])                           == Left [1..11]+-- detectParts ('Algebra.Graph.AdjacencyMap.biclique' [] xs)                            == Right ('vertices' xs [])+-- detectParts ('Algebra.Graph.AdjacencyMap.biclique' ('map' Left (x:xs)) ('map' Right ys)) == Right ('biclique' ('map' Left (x:xs)) ('map' Right ys))+-- 'isRight' (detectParts ('Algebra.Graph.AdjacencyMap.star' x ys))                       == 'notElem' x ys+-- 'isRight' (detectParts ('fromBipartite' ('toBipartite' x)))   == True+-- @+--+-- The correctness of 'detectParts' can be expressed by the following property:+--+-- @+-- let undirected = 'Algebra.Graph.AdjacencyMap.symmetricClosure' input in+-- case detectParts input of+--     Left cycle -> 'mod' (length cycle) 2 == 1 && 'Algebra.Graph.AdjacencyMap.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.circuit' cycle) undirected+--     Right result -> 'Algebra.Graph.AdjacencyMap.gmap' 'Data.Either.Extra.fromEither' ('fromBipartite' result) == undirected+-- @+detectParts :: Ord a => AM.AdjacencyMap a -> Either (OddCycle a) (AdjacencyMap a a)+detectParts x = case runState (runMaybeT dfs) Map.empty of+    (Nothing, m) -> Right $ toBipartiteWith (toEither m) g+    (Just c,  _) -> Left  $ oddCycle c+  where+    -- g :: AM.AdjacencyMap a+    g = AM.symmetricClosure x++    -- type PartMap a = Map a Part+    -- type PartMonad a = MaybeT (State (PartMap a)) [a]+    -- dfs :: PartMonad a+    dfs = asum [ processVertex v | v <- AM.vertexList g ]++    -- processVertex :: a -> PartMonad a+    processVertex v = do m <- get+                         guard (Map.notMember v m)+                         inVertex LeftPart v++    -- inVertex :: Part -> a -> PartMonad a+    inVertex p v = ((:) v) <$> do modify (Map.insert v p)+                                  let q = otherPart p+                                  asum [ onEdge q u | u <- Set.toAscList (AM.postSet v g) ]++    {-# INLINE onEdge #-}+    -- onEdge :: Part -> a -> PartMonad a+    onEdge p v = do m <- get+                    case Map.lookup v m of+                        Nothing -> inVertex p v+                        Just q  -> do guard (p /= q)+                                      return [v]++    -- toEither :: PartMap a -> a -> Either a a+    toEither m v = case fromJust (Map.lookup v m) of+                       LeftPart  -> Left  v+                       RightPart -> Right v++    -- oddCycle :: [a] -> [a]+    oddCycle c = init $ dropWhile (/= last c) c++-- | Check that the internal graph representation is consistent, i.e. that all+-- edges that are present in the 'leftAdjacencyMap' are also present in the+-- 'rightAdjacencyMap' map. It should be impossible to create an inconsistent+-- adjacency map, and we use this function in testing.+--+-- @+-- consistent 'empty'           == True+-- consistent ('vertex' x)      == True+-- consistent ('edge' x y)      == True+-- consistent ('edges' x)       == True+-- consistent ('toBipartite' x) == True+-- consistent ('swap' x)        == True+-- consistent ('circuit' x)     == True+-- consistent ('biclique' x y)  == True+-- @+consistent :: (Ord a, Ord b) => AdjacencyMap a b -> Bool+consistent (BAM lr rl) = edgeList lr == sort (map Data.Tuple.swap $ edgeList rl)+  where+    edgeList lr = [ (u, v) | (u, vs) <- Map.toAscList lr, v <- Set.toAscList vs ]
src/Algebra/Graph/Class.hs view
@@ -15,8 +15,7 @@ -- implemented fully polymorphically and require the use of an intermediate data -- type are not included. For example, to compute the number of vertices in a -- 'Graph' expression you will need to use a concrete data type, such as--- "Algebra.Graph.Fold". Other useful 'Graph' instances are defined in--- "Algebra.Graph", "Algebra.Graph.AdjacencyMap" and "Algebra.Graph.Relation".+-- "Algebra.Graph.Graph" or "Algebra.Graph.AdjacencyMap". -- -- See "Algebra.Graph.HigherKinded.Class" for the higher-kinded version of the -- core graph type class.@@ -47,18 +46,15 @@     path, circuit, clique, biclique, star, tree, forest     ) where -import Prelude ()-import Prelude.Compat- import Data.Tree  import Algebra.Graph.Label (Dioid, one)  import qualified Algebra.Graph                       as G+import qualified Algebra.Graph.Undirected            as UG import qualified Algebra.Graph.AdjacencyMap          as AM import qualified Algebra.Graph.Labelled              as LG import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM-import qualified Algebra.Graph.Fold                  as F import qualified Algebra.Graph.AdjacencyIntMap       as AIM import qualified Algebra.Graph.Relation              as R import qualified Algebra.Graph.Relation.Symmetric    as RS@@ -129,19 +125,21 @@     overlay = G.overlay     connect = G.connect +instance Graph (UG.Graph a) where+    type Vertex (UG.Graph a) = a+    empty = UG.empty+    vertex = UG.vertex+    overlay = UG.overlay+    connect = UG.connect++instance Undirected (UG.Graph a)+ instance Ord a => Graph (AM.AdjacencyMap a) where     type Vertex (AM.AdjacencyMap a) = a     empty   = AM.empty     vertex  = AM.vertex     overlay = AM.overlay     connect = AM.connect--instance Graph (F.Fold a) where-    type Vertex (F.Fold a) = a-    empty   = F.empty-    vertex  = F.vertex-    overlay = F.overlay-    connect = F.connect  instance Graph AIM.AdjacencyIntMap where     type Vertex AIM.AdjacencyIntMap = Int
src/Algebra/Graph/Export.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Export--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -23,14 +23,12 @@      -- * Generic graph export     export-  ) where--import Prelude ()-import Prelude.Compat hiding (unlines)+    ) where  import Data.Foldable (fold) import Data.Semigroup import Data.String hiding (unlines)+import Prelude hiding (unlines)  import Algebra.Graph.ToGraph (ToGraph, ToVertex, toAdjacencyMap) import Algebra.Graph.AdjacencyMap (vertexList, edgeList)
src/Algebra/Graph/Export/Dot.hs view
@@ -19,7 +19,7 @@      -- * Export functions     export, exportAsIs, exportViaShow-  ) where+    ) where  import Data.List hiding (unlines) import Data.Monoid
− src/Algebra/Graph/Fold.hs
@@ -1,736 +0,0 @@-{-# LANGUAGE RankNTypes #-}--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Fold--- Copyright  : (c) Andrey Mokhov 2016-2018--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : experimental------ __Alga__ is a library for algebraic construction and manipulation of graphs--- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the--- motivation behind the library, the underlying theory, and implementation details.------ This module defines the 'Fold' data type -- the Boehm-Berarducci encoding of--- algebraic graphs, which is used for generalised graph folding and for the--- implementation of polymorphic graph construction and transformation algorithms.--- 'Fold' is an instance of type classes defined in modules "Algebra.Graph.Class"--- and "Algebra.Graph.HigherKinded.Class", which can be used for polymorphic--- graph construction and manipulation.-------------------------------------------------------------------------------module Algebra.Graph.Fold (-    -- * Boehm-Berarducci encoding of algebraic graphs-    Fold,--    -- * Basic graph construction primitives-    empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,--    -- * Graph folding-    foldg,--    -- * Relations on graphs-    isSubgraphOf,--    -- * Graph properties-    isEmpty, size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList,-    edgeList, vertexSet, edgeSet, adjacencyList,--    -- * Standard families of graphs-    path, circuit, clique, biclique, star, stars,--    -- * Graph transformation-    removeVertex, removeEdge, transpose, induce, simplify,-    ) where--import Prelude ()-import Prelude.Compat--import Control.Applicative (Alternative)-import Control.Monad.Compat (MonadPlus (..), ap)-import Data.Function--import Control.DeepSeq (NFData (..))--import Algebra.Graph.ToGraph (ToGraph, ToVertex, toGraph)--import qualified Algebra.Graph              as G-import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.ToGraph      as T-import qualified Control.Applicative        as Ap-import qualified Data.Set                   as Set--{-| The 'Fold' data type is the Boehm-Berarducci encoding of the core graph-construction primitives 'empty', 'vertex', 'overlay' and 'connect'. We define a-'Num' instance as a convenient notation for working with graphs:--    > 0           == vertex 0-    > 1 + 2       == overlay (vertex 1) (vertex 2)-    > 1 * 2       == connect (vertex 1) (vertex 2)-    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))-    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))--__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',-which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as-additive and multiplicative identities, and 'negate' as additive inverse.-Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when-working with algebraic graphs; we hope that in future Haskell's Prelude will-provide a more fine-grained class hierarchy for algebraic structures, which we-would be able to utilise without violating any laws.--The 'Show' instance is defined using basic graph construction primitives:--@show (empty     :: Fold Int) == "empty"-show (1         :: Fold Int) == "vertex 1"-show (1 + 2     :: Fold Int) == "vertices [1,2]"-show (1 * 2     :: Fold Int) == "edge 1 2"-show (1 * 2 * 3 :: Fold Int) == "edges [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: Fold Int) == "overlay (vertex 3) (edge 1 2)"@--The 'Eq' instance is currently implemented using the 'AM.AdjacencyMap' as the-/canonical graph representation/ and satisfies all axioms of algebraic graphs:--    * 'overlay' is commutative and associative:--        >       x + y == y + x-        > x + (y + z) == (x + y) + z--    * 'connect' is associative and has 'empty' as the identity:--        >   x * empty == x-        >   empty * x == x-        > x * (y * z) == (x * y) * z--    * 'connect' distributes over 'overlay':--        > x * (y + z) == x * y + x * z-        > (x + y) * z == x * z + y * z--    * 'connect' can be decomposed:--        > x * y * z == x * y + x * z + y * z--The following useful theorems can be proved from the above set of axioms.--    * 'overlay' has 'empty' as the identity and is idempotent:--        >   x + empty == x-        >   empty + x == x-        >       x + x == x--    * Absorption and saturation of 'connect':--        > x * y + x + y == x * y-        >     x * x * x == x * x--When specifying the time and memory complexity of graph algorithms, /n/ will-denote the number of vertices in the graph, /m/ will denote the number of-edges in the graph, and /s/ will denote the /size/ of the corresponding-graph expression. For example, if g is a 'Fold' then /n/, /m/ and /s/ can be-computed as follows:--@n == 'vertexCount' g-m == 'edgeCount' g-s == 'size' g@--Note that 'size' counts all leaves of the expression:--@'vertexCount' 'empty'           == 0-'size'        'empty'           == 1-'vertexCount' ('vertex' x)      == 1-'size'        ('vertex' x)      == 1-'vertexCount' ('empty' + 'empty') == 0-'size'        ('empty' + 'empty') == 2@--Converting a 'Fold' to the corresponding 'AM.AdjacencyMap' takes /O(s + m * log(m))/-time and /O(s + m)/ memory. This is also the complexity of the graph equality test,-because it is currently implemented by converting graph expressions to canonical-representations based on adjacency maps.--The total order on graphs is defined using /size-lexicographic/ comparison:--* Compare the number of vertices. In case of a tie, continue.-* Compare the sets of vertices. In case of a tie, continue.-* Compare the number of edges. In case of a tie, continue.-* Compare the sets of edges.--Here are a few examples:--@'vertex' 1 < 'vertex' 2-'vertex' 3 < 'edge' 1 2-'vertex' 1 < 'edge' 1 1-'edge' 1 1 < 'edge' 1 2-'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2-'edge' 1 2 < 'edge' 1 3@--Note that the resulting order refines the 'isSubgraphOf' relation and is-compatible with 'overlay' and 'connect' operations:--@'isSubgraphOf' x y ==> x <= y@--@'empty' <= x-x     <= x + y-x + y <= x * y@--}-newtype Fold a = Fold { runFold :: forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b }--instance (Ord a, Show a) => Show (Fold a) where-    showsPrec p = showsPrec p . foldg AM.empty AM.vertex AM.overlay AM.connect--instance Ord a => Eq (Fold a) where-    x == y = T.toAdjacencyMap x == T.toAdjacencyMap y--instance Ord a => Ord (Fold a) where-    compare x y = compare (T.toAdjacencyMap x) (T.toAdjacencyMap y)--instance NFData a => NFData (Fold a) where-    rnf = foldg () rnf seq seq---- | __Note:__ this does not satisfy the usual ring laws; see 'Fold' for more--- details.-instance Num a => Num (Fold a) where-    fromInteger = vertex . fromInteger-    (+)         = overlay-    (*)         = connect-    signum      = const empty-    abs         = id-    negate      = id--instance Functor Fold where-    fmap f = foldg empty (vertex . f) overlay connect--instance Applicative Fold where-    pure  = vertex-    (<*>) = ap--instance Alternative Fold where-    empty = empty-    (<|>) = overlay--instance MonadPlus Fold where-    mzero = empty-    mplus = overlay--instance Monad Fold where-    return = vertex-    g >>=f = foldg empty f overlay connect g--instance ToGraph (Fold a) where-    type ToVertex (Fold a) = a-    foldg = foldg---- | Construct the /empty graph/.--- Complexity: /O(1)/ time, memory and size.------ @--- 'isEmpty'     empty == True--- 'hasVertex' x empty == False--- 'vertexCount' empty == 0--- 'edgeCount'   empty == 0--- 'size'        empty == 1--- @-empty :: Fold a-empty = Fold $ \e _ _ _ -> e-{-# NOINLINE [1] empty #-}---- | Construct the graph comprising /a single isolated vertex/.--- Complexity: /O(1)/ time, memory and size.------ @--- 'isEmpty'     (vertex x) == False--- 'hasVertex' x (vertex x) == True--- 'vertexCount' (vertex x) == 1--- 'edgeCount'   (vertex x) == 0--- 'size'        (vertex x) == 1--- @-vertex :: a -> Fold a-vertex x = Fold $ \_ v _ _ -> v x-{-# NOINLINE [1] vertex #-}---- | Construct the graph comprising /a single edge/.--- Complexity: /O(1)/ time, memory and size.------ @--- edge x y               == 'connect' ('vertex' x) ('vertex' y)--- 'hasEdge' x y (edge x y) == True--- 'edgeCount'   (edge x y) == 1--- 'vertexCount' (edge 1 1) == 1--- 'vertexCount' (edge 1 2) == 2--- @-edge :: a -> a -> Fold a-edge x y = Fold $ \_ v _ c -> v x `c` v y---- | /Overlay/ two graphs. This is a commutative, associative and idempotent--- operation with the identity 'empty'.--- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size.------ @--- 'isEmpty'     (overlay x y) == 'isEmpty'   x   && 'isEmpty'   y--- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y--- 'vertexCount' (overlay x y) >= 'vertexCount' x--- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y--- 'edgeCount'   (overlay x y) >= 'edgeCount' x--- 'edgeCount'   (overlay x y) <= 'edgeCount' x   + 'edgeCount' y--- 'size'        (overlay x y) == 'size' x        + 'size' y--- 'vertexCount' (overlay 1 2) == 2--- 'edgeCount'   (overlay 1 2) == 0--- @-overlay :: Fold a -> Fold a -> Fold a-overlay x y = Fold $ \e v o c -> runFold x e v o c `o` runFold y e v o c-{-# NOINLINE [1] overlay #-}---- | /Connect/ two graphs. This is an associative operation with the identity--- 'empty', which distributes over 'overlay' and obeys the decomposition axiom.--- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size. Note that the number--- of edges in the resulting graph is quadratic with respect to the number of--- vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.------ @--- 'isEmpty'     (connect x y) == 'isEmpty'   x   && 'isEmpty'   y--- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y--- 'vertexCount' (connect x y) >= 'vertexCount' x--- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y--- 'edgeCount'   (connect x y) >= 'edgeCount' x--- 'edgeCount'   (connect x y) >= 'edgeCount' y--- 'edgeCount'   (connect x y) >= 'vertexCount' x * 'vertexCount' y--- 'edgeCount'   (connect x y) <= 'vertexCount' x * 'vertexCount' y + 'edgeCount' x + 'edgeCount' y--- 'size'        (connect x y) == 'size' x        + 'size' y--- 'vertexCount' (connect 1 2) == 2--- 'edgeCount'   (connect 1 2) == 1--- @-connect :: Fold a -> Fold a -> Fold a-connect x y = Fold $ \e v o c -> runFold x e v o c `c` runFold y e v o c-{-# NOINLINE [1] connect #-}---- | Construct the graph comprising a given list of isolated vertices.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- vertices []            == 'empty'--- vertices [x]           == 'vertex' x--- 'hasVertex' x . vertices == 'elem' x--- 'vertexCount' . vertices == 'length' . 'Data.List.nub'--- 'vertexSet'   . vertices == Set.'Set.fromList'--- @-vertices :: [a] -> Fold a-vertices = overlays . map vertex-{-# NOINLINE [1] vertices #-}---- | Construct the graph from a list of edges.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- edges []          == 'empty'--- edges [(x,y)]     == 'edge' x y--- 'edgeCount' . edges == 'length' . 'Data.List.nub'--- @-edges :: [(a, a)] -> Fold a-edges es = Fold $ \e v o c -> foldr (flip o . uncurry (c `on` v)) e es---- | Overlay a given list of graphs.--- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length--- of the given list, and /S/ is the sum of sizes of the graphs in the list.------ @--- overlays []        == 'empty'--- overlays [x]       == x--- overlays [x,y]     == 'overlay' x y--- overlays           == 'foldr' 'overlay' 'empty'--- 'isEmpty' . overlays == 'all' 'isEmpty'--- @-overlays :: [Fold a] -> Fold a-overlays = foldr overlay empty-{-# INLINE [2] overlays #-}---- | Connect a given list of graphs.--- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length--- of the given list, and /S/ is the sum of sizes of the graphs in the list.------ @--- connects []        == 'empty'--- connects [x]       == x--- connects [x,y]     == 'connect' x y--- connects           == 'foldr' 'connect' 'empty'--- 'isEmpty' . connects == 'all' 'isEmpty'--- @-connects :: [Fold a] -> Fold a-connects = foldr connect empty-{-# INLINE [2] connects #-}---- | Generalised 'Graph' folding: recursively collapse a 'Graph' by applying--- the provided functions to the leaves and internal nodes of the expression.--- The order of arguments is: empty, vertex, overlay and connect.--- Complexity: /O(s)/ applications of given functions. As an example, the--- complexity of 'size' is /O(s)/, since all functions have cost /O(1)/.------ @--- foldg 'empty' 'vertex'        'overlay' 'connect'        == id--- foldg 'empty' 'vertex'        'overlay' ('flip' 'connect') == 'transpose'--- foldg 1     ('const' 1)     (+)     (+)            == 'size'--- foldg True  ('const' False) (&&)    (&&)           == 'isEmpty'--- foldg False (== x)        (||)    (||)           == 'hasVertex' x--- @-foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Fold a -> b-foldg e v o c g = runFold g e v o c---- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the--- first graph is a /subgraph/ of the second.--- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a--- graph can be quadratic with respect to the expression size /s/.------ @--- isSubgraphOf 'empty'         x             ==  True--- isSubgraphOf ('vertex' x)    'empty'         ==  False--- isSubgraphOf x             ('overlay' x y) ==  True--- isSubgraphOf ('overlay' x y) ('connect' x y) ==  True--- isSubgraphOf ('path' xs)     ('circuit' xs)  ==  True--- isSubgraphOf x y                         ==> x <= y--- @-isSubgraphOf :: Ord a => Fold a -> Fold a -> Bool-isSubgraphOf x y = overlay x y == y---- | Check if a graph is empty. A convenient alias for 'null'.--- Complexity: /O(s)/ time.------ @--- isEmpty 'empty'                       == True--- isEmpty ('overlay' 'empty' 'empty')       == True--- isEmpty ('vertex' x)                  == False--- isEmpty ('removeVertex' x $ 'vertex' x) == True--- isEmpty ('removeEdge' x y $ 'edge' x y) == False--- @-isEmpty :: Fold a -> Bool-isEmpty = T.isEmpty---- | The /size/ of a graph, i.e. the number of leaves of the expression--- including 'empty' leaves.--- Complexity: /O(s)/ time.------ @--- size 'empty'         == 1--- size ('vertex' x)    == 1--- size ('overlay' x y) == size x + size y--- size ('connect' x y) == size x + size y--- size x             >= 1--- size x             >= 'vertexCount' x--- @-size :: Fold a -> Int-size = T.size---- | Check if a graph contains a given vertex.--- Complexity: /O(s)/ time.------ @--- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False--- hasVertex x . 'removeVertex' x == 'const' False--- @-hasVertex :: Eq a => a -> Fold a -> Bool-hasVertex = T.hasVertex---- | Check if a graph contains a given edge.--- Complexity: /O(s)/ time.------ @--- hasEdge x y 'empty'            == False--- hasEdge x y ('vertex' z)       == False--- hasEdge x y ('edge' x y)       == True--- hasEdge x y . 'removeEdge' x y == 'const' False--- hasEdge x y                  == 'elem' (x,y) . 'edgeList'--- @-hasEdge :: Eq a => a -> a -> Fold a -> Bool-hasEdge = T.hasEdge---- | The number of vertices in a graph.--- Complexity: /O(s * log(n))/ time.------ @--- vertexCount 'empty'             ==  0--- vertexCount ('vertex' x)        ==  1--- vertexCount                   ==  'length' . 'vertexList'--- vertexCount x \< vertexCount y ==> x \< y--- @-vertexCount :: Ord a => Fold a -> Int-vertexCount = T.vertexCount---- | The number of edges in a graph.--- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a--- graph can be quadratic with respect to the expression size /s/.------ @--- edgeCount 'empty'      == 0--- edgeCount ('vertex' x) == 0--- edgeCount ('edge' x y) == 1--- edgeCount            == 'length' . 'edgeList'--- @-edgeCount :: Ord a => Fold a -> Int-edgeCount = T.edgeCount---- | The sorted list of vertices of a given graph.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexList 'empty'      == []--- vertexList ('vertex' x) == [x]--- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort'--- @-vertexList :: Ord a => Fold a -> [a]-vertexList = T.vertexList---- | The sorted list of edges of a graph.--- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of--- edges /m/ of a graph can be quadratic with respect to the expression size /s/.------ @--- edgeList 'empty'          == []--- edgeList ('vertex' x)     == []--- edgeList ('edge' x y)     == [(x,y)]--- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)]--- edgeList . 'edges'        == 'Data.List.nub' . 'Data.List.sort'--- edgeList . 'transpose'    == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList--- @-edgeList :: Ord a => Fold a -> [(a, a)]-edgeList = T.edgeList---- | The set of vertices of a given graph.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexSet 'empty'      == Set.'Set.empty'--- vertexSet . 'vertex'   == Set.'Set.singleton'--- vertexSet . 'vertices' == Set.'Set.fromList'--- @-vertexSet :: Ord a => Fold a -> Set.Set a-vertexSet = T.vertexSet---- | The set of edges of a given graph.--- Complexity: /O(s * log(m))/ time and /O(m)/ memory.------ @--- edgeSet 'empty'      == Set.'Set.empty'--- edgeSet ('vertex' x) == Set.'Set.empty'--- edgeSet ('edge' x y) == Set.'Set.singleton' (x,y)--- edgeSet . 'edges'    == Set.'Set.fromList'--- @-edgeSet :: Ord a => Fold a -> Set.Set (a, a)-edgeSet = T.edgeSet---- | The sorted /adjacency list/ of a graph.--- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a--- graph can be quadratic with respect to the expression size /s/.------ @--- adjacencyList 'empty'          == []--- adjacencyList ('vertex' x)     == [(x, [])]--- adjacencyList ('edge' 1 2)     == [(1, [2]), (2, [])]--- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]--- 'stars' . adjacencyList        == id--- @-adjacencyList :: Ord a => Fold a -> [(a, [a])]-adjacencyList = T.adjacencyList---- | The /path/ on a list of vertices.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- path []        == 'empty'--- path [x]       == 'vertex' x--- path [x,y]     == 'edge' x y--- path . 'reverse' == 'transpose' . path--- @-path :: [a] -> Fold a-path xs = case xs of []     -> empty-                     [x]    -> vertex x-                     (_:ys) -> edges (zip xs ys)---- | The /circuit/ on a list of vertices.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- circuit []        == 'empty'--- circuit [x]       == 'edge' x x--- circuit [x,y]     == 'edges' [(x,y), (y,x)]--- circuit . 'reverse' == 'transpose' . circuit--- @-circuit :: [a] -> Fold a-circuit []     = empty-circuit (x:xs) = path $ [x] ++ xs ++ [x]---- | The /clique/ on a list of vertices.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- clique []         == 'empty'--- clique [x]        == 'vertex' x--- clique [x,y]      == 'edge' x y--- clique [x,y,z]    == 'edges' [(x,y), (x,z), (y,z)]--- clique (xs ++ ys) == 'connect' (clique xs) (clique ys)--- clique . 'reverse'  == 'transpose' . clique--- @-clique :: [a] -> Fold a-clique = connects . map vertex-{-# NOINLINE [1] clique #-}---- | The /biclique/ on two lists of vertices.--- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the--- lengths of the given lists.------ @--- biclique []      []      == 'empty'--- biclique [x]     []      == 'vertex' x--- biclique []      [y]     == 'vertex' y--- biclique [x1,x2] [y1,y2] == 'edges' [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]--- biclique xs      ys      == 'connect' ('vertices' xs) ('vertices' ys)--- @-biclique :: [a] -> [a] -> Fold a-biclique xs [] = vertices xs-biclique [] ys = vertices ys-biclique xs ys = connect (vertices xs) (vertices ys)---- | The /star/ formed by a centre vertex connected to a list of leaves.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- star x []    == 'vertex' x--- star x [y]   == 'edge' x y--- star x [y,z] == 'edges' [(x,y), (x,z)]--- star x ys    == 'connect' ('vertex' x) ('vertices' ys)--- @-star :: a -> [a] -> Fold a-star x [] = vertex x-star x ys = connect (vertex x) (vertices ys)-{-# INLINE star #-}---- | The /stars/ formed by overlaying a list of 'star's. An inverse of--- 'adjacencyList'.--- Complexity: /O(L)/ time, memory and size, where /L/ is the total size of the--- input.------ @--- stars []                      == 'empty'--- stars [(x, [])]               == 'vertex' x--- stars [(x, [y])]              == 'edge' x y--- stars [(x, ys)]               == 'star' x ys--- stars                         == 'overlays' . 'map' ('uncurry' 'star')--- stars . 'adjacencyList'         == id--- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys)--- @-stars :: [(a, [a])] -> Fold a-stars = overlays . map (uncurry star)-{-# INLINE stars #-}---- | Remove a vertex from a given graph.--- Complexity: /O(s)/ time, memory and size.------ @--- removeVertex x ('vertex' x)       == 'empty'--- removeVertex 1 ('vertex' 2)       == 'vertex' 2--- removeVertex x ('edge' x x)       == 'empty'--- removeVertex 1 ('edge' 1 2)       == 'vertex' 2--- removeVertex x . removeVertex x == removeVertex x--- @-removeVertex :: Eq a => a -> Fold a -> Fold a-removeVertex v = induce (/= v)---- | Remove an edge from a given graph.--- Complexity: /O(s)/ time, memory and size.------ @--- removeEdge x y ('edge' x y)       == 'vertices' [x,y]--- removeEdge x y . removeEdge x y == removeEdge x y--- removeEdge x y . 'removeVertex' x == 'removeVertex' x--- removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2--- removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2--- 'size' (removeEdge x y z)         <= 3 * 'size' z--- @-removeEdge :: Eq a => a -> a -> Fold a -> Fold a-removeEdge s t = filterContext s (/=s) (/=t)---- TODO: Export--- Filter vertices in a subgraph context.-filterContext :: Eq a => a -> (a -> Bool) -> (a -> Bool) -> Fold a -> Fold a-filterContext s i o g = maybe g go $ G.context (==s) (toGraph g)-  where-    go (G.Context is os) = induce (/=s) g `overlay` transpose (star s (filter i is))-                                          `overlay` star            s (filter o os)---- | Transpose a given graph.--- Complexity: /O(s)/ time, memory and size.------ @--- transpose 'empty'       == 'empty'--- transpose ('vertex' x)  == 'vertex' x--- transpose ('edge' x y)  == 'edge' y x--- transpose . transpose == id--- transpose ('box' x y)   == 'box' (transpose x) (transpose y)--- 'edgeList' . transpose  == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList'--- @-transpose :: Fold a -> Fold a-transpose = foldg empty vertex overlay (flip connect)-{-# NOINLINE [1] transpose #-}--{-# RULES-"transpose/empty"    transpose empty = empty-"transpose/vertex"   forall x. transpose (vertex x) = vertex x-"transpose/overlay"  forall g1 g2. transpose (overlay g1 g2) = overlay (transpose g1) (transpose g2)-"transpose/connect"  forall g1 g2. transpose (connect g1 g2) = connect (transpose g2) (transpose g1)--"transpose/overlays" forall xs. transpose (overlays xs) = overlays (map transpose xs)-"transpose/connects" forall xs. transpose (connects xs) = connects (reverse (map transpose xs))--"transpose/vertices" forall xs. transpose (vertices xs) = vertices xs-"transpose/clique"   forall xs. transpose (clique xs)   = clique (reverse xs)- #-}---- | Construct the /induced subgraph/ of a given graph by removing the--- vertices that do not satisfy a given predicate.--- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes--- /O(1)/ to be evaluated.------ @--- induce ('const' True ) x      == x--- induce ('const' False) x      == 'empty'--- induce (/= x)               == 'removeVertex' x--- induce p . induce q         == induce (\\x -> p x && q x)--- 'isSubgraphOf' (induce p x) x == True--- @-induce :: (a -> Bool) -> Fold a -> Fold a-induce p = foldg empty (\x -> if p x then vertex x else empty) (k overlay) (k connect)-  where-    k f x y | isEmpty x = y -- Constant folding to get rid of Empty leaves-            | isEmpty y = x-            | otherwise = f x y---- | Simplify a graph expression. Semantically, this is the identity function,--- but it simplifies a given polymorphic graph expression according to the laws--- of the algebra. The function does not compute the simplest possible expression,--- but uses heuristics to obtain useful simplifications in reasonable time.--- Complexity: the function performs /O(s)/ graph comparisons. It is guaranteed--- that the size of the result does not exceed the size of the given expression.--- Below the operator @~>@ denotes the /is simplified to/ relation.------ @--- simplify             == id--- 'size' (simplify x)    <= 'size' x--- simplify 'empty'       ~> 'empty'--- simplify 1           ~> 1--- simplify (1 + 1)     ~> 1--- simplify (1 + 2 + 1) ~> 1 + 2--- simplify (1 * 1 * 1) ~> 1 * 1--- @-simplify :: Ord a => Fold a -> Fold a-simplify = foldg empty vertex (simple overlay) (simple connect)--simple :: Eq g => (g -> g -> g) -> g -> g -> g-simple op x y-    | x == z    = x-    | y == z    = y-    | otherwise = z-  where-    z = op x y
src/Algebra/Graph/HigherKinded/Class.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.HigherKinded.Class--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -53,15 +52,11 @@     removeVertex, replaceVertex, mergeVertices, splitVertex, induce     ) where -import Prelude ()-import Prelude.Compat- import Control.Applicative (Alternative(empty, (<|>)))-import Control.Monad.Compat (MonadPlus, mfilter)+import Control.Monad (MonadPlus, mfilter) import Data.Tree -import qualified Algebra.Graph      as G-import qualified Algebra.Graph.Fold as F+import qualified Algebra.Graph as G  {-| The core type class for constructing algebraic graphs is defined by introducing@@ -122,19 +117,12 @@ edges in the graph, and /s/ will denote the /size/ of the corresponding 'Graph' expression. -}-class (-#if !MIN_VERSION_base(4,8,0)-  Alternative g,-#endif-  MonadPlus g) => Graph g where+class MonadPlus g => Graph g where     -- | Connect two graphs.     connect :: g a -> g a -> g a  instance Graph G.Graph where     connect = G.connect--instance Graph F.Fold where-    connect = F.connect  -- | Construct the graph comprising a single isolated vertex. An alias for 'pure'. vertex :: Graph g => a -> g a
src/Algebra/Graph/Internal.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2020 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -20,31 +20,32 @@     List (..),      -- * Graph traversal-    Focus (..), emptyFocus, vertexFocus, overlayFoci, connectFoci, Hit (..),-    foldr1Safe, maybeF,+    Focus (..), emptyFocus, vertexFocus, overlayFoci, connectFoci, foldr1Safe,+    maybeF,      -- * Utilities-    setProduct, setProductWith-  ) where--import Prelude ()-import Prelude.Compat+    setProduct, setProductWith, forEach, forEachInt, coerce00, coerce10,+    coerce20, coerce01, coerce11, coerce21+    ) where +import Data.Coerce import Data.Foldable import Data.Semigroup+import Data.IntSet (IntSet) import Data.Set (Set) +import qualified Data.IntSet as IntSet import qualified Data.Set as Set import qualified GHC.Exts as Exts  -- | An abstract list data type with /O(1)/ time concatenation (the current -- implementation uses difference lists). Here @a@ is the type of list elements. -- 'List' @a@ is a 'Monoid': 'mempty' corresponds to the empty list and two lists--- can be concatenated with 'mappend' (or operator 'Data.Monoid.<>'). Singleton+-- can be concatenated with 'mappend' (or operator 'Data.Semigroup.<>'). Singleton -- lists can be constructed using the function 'pure' from the 'Applicative' -- instance. 'List' @a@ is also an instance of 'IsList', therefore you can use -- list literals, e.g. @[1,4]@ @::@ 'List' @Int@ is the same as 'pure' @1@--- 'Data.Monoid.<>' 'pure' @4@; note that this requires the @OverloadedLists@+-- 'Data.Semigroup.<>' 'pure' @4@; note that this requires the @OverloadedLists@ -- GHC extension. To extract plain Haskell lists you can use the 'toList' -- function from the 'Foldable' instance. newtype List a = List (Endo [a]) deriving (Monoid, Semigroup)@@ -66,9 +67,7 @@  instance Foldable List where     foldMap f = foldMap f . Exts.toList-#if MIN_VERSION_base(4,8,0)     toList    = Exts.toList-#endif  instance Functor List where     fmap f = Exts.fromList . map f . toList@@ -110,21 +109,10 @@     xs = if ok y then vs x else is x     ys = if ok x then vs y else os y --- | An auxiliary data type for 'hasEdge': when searching for an edge, we can hit--- its 'Tail', i.e. the source vertex, the whole 'Edge', or 'Miss' it entirely.-data Hit = Miss | Tail | Edge deriving (Eq, Ord)- -- | A safe version of 'foldr1'. foldr1Safe :: (a -> a -> a) -> [a] -> Maybe a foldr1Safe f = foldr (maybeF f) Nothing-{-# INLINE [0] foldr1Safe #-}---- | Tragetting 'map' directly-{-# RULES-"foldr1Safe/build"-  forall k f lst.-  foldr1Safe k (map f lst) = foldr (maybeF k . f) Nothing lst- #-}+{-# INLINE foldr1Safe #-}  -- | Auxiliary function that try to apply a function to a base case and a 'Maybe' -- value and return 'Just' the result or 'Just' the base case.@@ -144,3 +132,40 @@ -- resulting pair. setProductWith :: Ord c => (a -> b -> c) -> Set a -> Set b -> Set c setProductWith f x y = Set.fromList [ f a b | a <- Set.toAscList x, b <- Set.toAscList y ]++-- | Perform an applicative action for each member of a Set.+forEach :: Applicative f => Set a -> (a -> f b) -> f ()+forEach s f = Set.foldr (\a u -> f a *> u) (pure ()) s++-- | Perform an applicative action for each member of an IntSet.+forEachInt :: Applicative f => IntSet -> (Int -> f a) -> f ()+forEachInt s f = IntSet.foldr (\a u -> f a *> u) (pure ()) s++-- TODO: Get rid of this boilerplate.++-- | Help GHC with type inference when direct use of 'coerce' does not compile.+coerce00 :: Coercible f g => f x -> g x+coerce00 = coerce++-- | Help GHC with type inference when direct use of 'coerce' does not compile.+coerce10 :: (Coercible a b, Coercible f g) => (a -> f x) -> (b -> g x)+coerce10 = coerce++-- | Help GHC with type inference when direct use of 'coerce' does not compile.+coerce20 :: (Coercible a b, Coercible c d, Coercible f g)+         => (a -> c -> f x) -> (b -> d -> g x)+coerce20 = coerce++-- | Help GHC with type inference when direct use of 'coerce' does not compile.+coerce01 :: (Coercible a b, Coercible f g) => (f x -> a) -> (g x -> b)+coerce01 = coerce++-- | Help GHC with type inference when direct use of 'coerce' does not compile.+coerce11 :: (Coercible a b, Coercible c d, Coercible f g)+         => (a -> f x -> c) -> (b -> g x -> d)+coerce11 = coerce++-- | Help GHC with type inference when direct use of 'coerce' does not compile.+coerce21 :: (Coercible a b, Coercible c d, Coercible p q, Coercible f g)+         => (a -> c -> f x -> p) -> (b -> d -> g x -> q)+coerce21 = coerce
src/Algebra/Graph/Label.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Label--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -29,14 +29,12 @@     Optimum (..), ShortestPath, AllShortestPaths, CountShortestPaths, WidestPath     ) where -import Prelude ()-import Prelude.Compat- import Control.Applicative import Control.Monad+import Data.Coerce import Data.Maybe import Data.Monoid (Any (..), Monoid (..), Sum (..))-import Data.Semigroup (Min (..), Max (..), Semigroup (..))+import Data.Semigroup (Max (..), Min (..), Semigroup (..)) import Data.Set (Set) import GHC.Exts (IsList (..)) @@ -135,8 +133,8 @@       where         f = fromInteger x -    (+) = liftA2 (+)-    (*) = liftA2 (*)+    (+) = coerce ((+) :: Extended a -> Extended a -> Extended a)+    (*) = coerce ((*) :: Extended a -> Extended a -> Extended a)      negate _ = error "NonNegative values cannot be negated" @@ -284,12 +282,16 @@ fromExtended (Finite a) = Just a fromExtended Infinite   = Nothing -instance Num a => Num (Extended a) where+-- A type alias for a binary function on Extended.+instance (Num a, Eq a) => Num (Extended a) where     fromInteger = Finite . fromInteger      (+) = liftA2 (+)-    (*) = liftA2 (*) +    Finite 0 * _ = Finite 0+    _ * Finite 0 = Finite 0+    x * y = liftA2 (*) x y+     negate = fmap negate     signum = fmap signum     abs    = fmap abs@@ -297,8 +299,8 @@ -- | If @a@ is a monoid, 'Minimum' @a@ forms the following 'Dioid': -- -- @--- 'zero'  = 'pure' 'mempty'--- 'one'   = 'noMinimum'+-- 'zero'  = 'noMinimum'+-- 'one'   = 'pure' 'mempty' -- ('<+>') = 'liftA2' 'min' -- ('<.>') = 'liftA2' 'mappend' -- @@@ -323,18 +325,19 @@ noMinimum = Minimum Infinite  instance Ord a => Semigroup (Minimum a) where-    (<>) = liftA2 min+    (<>) = min  instance (Monoid a, Ord a) => Monoid (Minimum a) where-    mempty = pure mempty +    mempty  = noMinimum+    mappend = (<>)  instance (Monoid a, Ord a) => Semiring (Minimum a) where-    one = noMinimum+    one   = pure mempty     (<.>) = liftA2 mappend  instance (Monoid a, Ord a) => Dioid (Minimum a) -instance (Num a, Show a) => Show (Minimum a) where+instance Show a => Show (Minimum a) where     show (Minimum Infinite  ) = "one"     show (Minimum (Finite x)) = show x @@ -361,9 +364,6 @@     one                       = PowerSet (Set.singleton mempty)     PowerSet x <.> PowerSet y = PowerSet (setProductWith mappend x y) -instance (Monoid a, Ord a) => StarSemiring (PowerSet a) where-    star _ = one- instance (Monoid a, Ord a) => Dioid (PowerSet a) where  -- | The type of /free labels/ over the underlying set of symbols @a@. This data@@ -445,6 +445,7 @@ data Optimum o a = Optimum { getOptimum :: o, getArgument :: a }     deriving (Eq, Ord, Show) +-- TODO: Add tests. -- This is similar to geodetic semirings. -- See http://vlado.fmf.uni-lj.si/vlado/papers/SemiRingSNA.pdf instance (Eq o, Monoid a, Monoid o) => Semigroup (Optimum o a) where@@ -455,32 +456,40 @@               o = mappend o1 o2               a = if o == o1 then a1 else a2 +-- TODO: Add tests. instance (Eq o, Monoid a, Monoid o) => Monoid (Optimum o a) where     mempty  = Optimum mempty mempty     mappend = (<>) +-- TODO: Add tests. instance (Eq o, Semiring a, Semiring o) => Semiring (Optimum o a) where     one = Optimum one one     Optimum o1 a1 <.> Optimum o2 a2 = Optimum (o1 <.> o2) (a1 <.> a2) +-- TODO: Add tests. instance (Eq o, StarSemiring a, StarSemiring o) => StarSemiring (Optimum o a) where     star (Optimum o a) = Optimum (star o) (star a) +-- TODO: Add tests. instance (Eq o, Dioid a, Dioid o) => Dioid (Optimum o a) where  -- | A /path/ is a list of edges. type Path a = [(a, a)] --- | The 'Optimum' semiring specialised to /finding the lexicographically--- smallest shortest path/.+-- TODO: Add tests.+-- | The 'Optimum' semiring specialised to+-- /finding the lexicographically smallest shortest path/. type ShortestPath e a = Optimum (Distance e) (Minimum (Path a)) +-- TODO: Add tests. -- | The 'Optimum' semiring specialised to /finding all shortest paths/. type AllShortestPaths e a = Optimum (Distance e) (PowerSet (Path a)) +-- TODO: Add tests. -- | The 'Optimum' semiring specialised to /counting all shortest paths/.-type CountShortestPaths e a = Optimum (Distance e) (Count Integer)+type CountShortestPaths e = Optimum (Distance e) (Count Integer) --- | The 'Optimum' semiring specialised to /finding the lexicographically--- smallest widest path/.+-- TODO: Add tests.+-- | The 'Optimum' semiring specialised to+-- /finding the lexicographically smallest widest path/. type WidestPath e a = Optimum (Capacity e) (Minimum (Path a))
src/Algebra/Graph/Labelled.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE DeriveFunctor, FlexibleInstances #-}+{-# LANGUAGE DeriveFunctor, DeriveGeneric, FlexibleInstances #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Labelled--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -15,7 +15,7 @@ -- graphs with edge labels. The API will be expanded in the next release. ----------------------------------------------------------------------------- module Algebra.Graph.Labelled (-    -- * Algebraic data type for edge-labeleld graphs+    -- * Algebraic data type for edge-labelled graphs     Graph (..), empty, vertex, edge, (-<), (>-), overlay, connect, vertices,     edges, overlays, @@ -31,7 +31,7 @@      -- * Graph transformation     removeVertex, removeEdge, replaceVertex, replaceEdge, transpose, emap,-    induce,+    induce, induceJust,      -- * Relational operations     closure, reflexiveClosure, symmetricClosure, transitiveClosure,@@ -43,12 +43,10 @@     Context (..), context     ) where -import Prelude ()-import Prelude.Compat- import Data.Bifunctor-import Data.Monoid (Any (..))-import Data.Semigroup ((<>))+import Data.Monoid+import Control.DeepSeq+import GHC.Generics  import Algebra.Graph.Internal (List (..)) import Algebra.Graph.Label@@ -65,7 +63,7 @@ data Graph e a = Empty                | Vertex a                | Connect e (Graph e a) (Graph e a)-               deriving (Functor, Show)+               deriving (Functor, Show, Generic)  instance (Eq e, Monoid e, Ord a) => Eq (Graph e a) where     x == y = toAdjacencyMap x == toAdjacencyMap y@@ -86,6 +84,11 @@ instance Bifunctor Graph where   bimap f g = foldg Empty (Vertex . g) (Connect . f) +instance (NFData e, NFData a) => NFData (Graph e a) where+    rnf Empty           = ()+    rnf (Vertex  x    ) = rnf x+    rnf (Connect e x y) = e `seq` rnf x `seq` rnf y+ -- TODO: This is a very inefficient implementation. Find a way to construct an -- adjacency map directly, without building intermediate representations for all -- subgraphs.@@ -153,7 +156,7 @@ -- -- @ -- 'isEmpty'     (vertex x) == False--- 'hasVertex' x (vertex x) == True+-- 'hasVertex' x (vertex y) == (x == y) -- 'Algebra.Graph.ToGraph.vertexCount' (vertex x) == 1 -- 'Algebra.Graph.ToGraph.edgeCount'   (vertex x) == 0 -- @@@ -286,7 +289,7 @@ overlays :: Monoid e => [Graph e a] -> Graph e a overlays = foldr overlay empty --- | Check if a graph is empty. A convenient alias for 'null'.+-- | Check if a graph is empty. -- Complexity: /O(s)/ time. -- -- @@@ -319,8 +322,7 @@ -- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Eq a => a -> Graph e a -> Bool@@ -488,6 +490,7 @@ emap :: (e -> f) -> Graph e a -> Graph f a emap f = foldg Empty Vertex (Connect . f) +-- TODO: Implement via 'induceJust' to reduce code duplication. -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate. -- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes@@ -502,6 +505,23 @@ -- @ induce :: (a -> Bool) -> Graph e a -> Graph e a induce p = foldg Empty (\x -> if p x then Vertex x else Empty) c+  where+    c _ x     Empty = x -- Constant folding to get rid of Empty leaves+    c _ Empty y     = y+    c e x     y     = Connect e x y++-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'fmap' 'Just'                                    == 'id'+-- induceJust . 'fmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Graph e (Maybe a) -> Graph e a+induceJust = foldg Empty (maybe Empty Vertex) c   where     c _ x     Empty = x -- Constant folding to get rid of Empty leaves     c _ Empty y     = y
src/Algebra/Graph/Labelled/AdjacencyMap.hs view
@@ -1,7 +1,8 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Labelled.AdjacencyMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -32,29 +33,83 @@      -- * Graph transformation     removeVertex, removeEdge, replaceVertex, replaceEdge, transpose, gmap,-    emap, induce,+    emap, induce, induceJust,      -- * Relational operations-    closure, reflexiveClosure, symmetricClosure, transitiveClosure-  ) where+    closure, reflexiveClosure, symmetricClosure, transitiveClosure, -import Prelude ()-import Prelude.Compat+    -- * Miscellaneous+    consistent+    ) where -import Data.Foldable (foldMap)+import Control.DeepSeq import Data.Maybe import Data.Map (Map)-import Data.Monoid (Monoid, Sum (..))-import Data.Set (Set)+import Data.Monoid (Sum (..))+import Data.Set (Set, (\\))+import GHC.Generics  import Algebra.Graph.Label-import Algebra.Graph.Labelled.AdjacencyMap.Internal -import qualified Algebra.Graph.AdjacencyMap          as AM-import qualified Algebra.Graph.AdjacencyMap.Internal as AMI-import qualified Data.Map.Strict                     as Map-import qualified Data.Set                            as Set+import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Data.Map.Strict            as Map+import qualified Data.Set                   as Set +-- | Edge-labelled graphs, where the type variable @e@ stands for edge labels.+-- For example, 'AdjacencyMap' @Bool@ @a@ is isomorphic to unlabelled graphs+-- defined in the top-level module "Algebra.Graph.AdjacencyMap", where @False@+-- and @True@ denote the lack of and the existence of an unlabelled edge,+-- respectively.+newtype AdjacencyMap e a = AM {+    -- | The /adjacency map/ of an edge-labelled graph: each vertex is+    -- associated with a map from its direct successors to the corresponding+    -- edge labels.+    adjacencyMap :: Map a (Map a e) } deriving (Eq, Generic, NFData)++instance (Ord a, Show a, Ord e, Show e) => Show (AdjacencyMap e a) where+    showsPrec p lam@(AM m)+        | Set.null vs = showString "empty"+        | null es     = showParen (p > 10) $ vshow vs+        | vs == used  = showParen (p > 10) $ eshow es+        | otherwise   = showParen (p > 10) $+                            showString "overlay (" . vshow (vs \\ used) .+                            showString ") ("       . eshow es . showString ")"+      where+        vs   = vertexSet lam+        es   = edgeList lam+        used = referredToVertexSet m+        vshow vs = case Set.toAscList vs of+            [x] -> showString "vertex "   . showsPrec 11 x+            xs  -> showString "vertices " . showsPrec 11 xs+        eshow es = case es of+            [(e, x, y)] -> showString "edge "  . showsPrec 11 e .+                           showString " "      . showsPrec 11 x .+                           showString " "      . showsPrec 11 y+            xs          -> showString "edges " . showsPrec 11 xs++instance (Ord e, Monoid e, Ord a) => Ord (AdjacencyMap e a) where+    compare x y = mconcat+        [ compare (vertexCount x) (vertexCount y)+        , compare (vertexSet   x) (vertexSet   y)+        , compare (edgeCount   x) (edgeCount   y)+        , compare (eSet        x) (eSet        y)+        , cmp ]+      where+        eSet = Set.map (\(_, x, y) -> (x, y)) . edgeSet+        cmp | x == y               = EQ+            | overlays [x, y] == y = LT+            | otherwise            = compare x y++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'+-- for more details.+instance (Eq e, Dioid e, Num a, Ord a) => Num (AdjacencyMap e a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect mempty+    signum      = const empty+    abs         = id+    negate      = id+ -- | Construct the /empty graph/. -- Complexity: /O(1)/ time and memory. --@@ -72,7 +127,7 @@ -- -- @ -- 'isEmpty'     (vertex x) == False--- 'hasVertex' x (vertex x) == True+-- 'hasVertex' x (vertex y) == (x == y) -- 'vertexCount' (vertex x) == 1 -- 'edgeCount'   (vertex x) == 0 -- @@@ -268,8 +323,7 @@ -- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Ord a => a -> AdjacencyMap e a -> Bool@@ -395,14 +449,16 @@ postSet :: Ord a => a -> AdjacencyMap e a -> Set a postSet x = Map.keysSet . Map.findWithDefault Map.empty x . adjacencyMap +-- TODO: Optimise. -- | Convert a graph to the corresponding unlabelled 'AM.AdjacencyMap' by -- forgetting labels on all non-'zero' edges.+-- Complexity: /O((n + m) * log(n))/ time and memory. -- -- @ -- 'hasEdge' x y == 'AM.hasEdge' x y . skeleton -- @-skeleton :: AdjacencyMap e a -> AM.AdjacencyMap a-skeleton (AM m) = AMI.AM (Map.map Map.keysSet m)+skeleton :: Ord a => AdjacencyMap e a -> AM.AdjacencyMap a+skeleton (AM m) = AM.fromAdjacencySets $ Map.toAscList $ Map.map Map.keysSet m  -- | Remove a vertex from a given graph. -- Complexity: /O(n*log(n))/ time.@@ -528,7 +584,7 @@  -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.--- Complexity: /O(m)/ time, assuming that the predicate takes /O(1)/ to+-- Complexity: /O(n + m)/ time, assuming that the predicate takes /O(1)/ to -- be evaluated. -- -- @@@ -542,6 +598,21 @@ induce p = AM . Map.map (Map.filterWithKey (\k _ -> p k)) .     Map.filterWithKey (\k _ -> p k) . adjacencyMap +-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(n + m)/ time.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'gmap' 'Just'                                    == 'id'+-- induceJust . 'gmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Ord a => AdjacencyMap e (Maybe a) -> AdjacencyMap e a+induceJust = AM . Map.map catMaybesMap . catMaybesMap . adjacencyMap+  where+    catMaybesMap = Map.mapKeysMonotonic fromJust . Map.delete Nothing+ -- | Compute the /reflexive and transitive closure/ of a graph over the -- underlying star semiring using the Warshall-Floyd-Kleene algorithm. --@@ -610,3 +681,16 @@         starkk  = star (get k k)         go i ik = Map.fromAscList             [ (j, e) | j <- vs, let e = get i j <+> ik <.> get k j, e /= zero ]++-- | Check that the internal graph representation is consistent, i.e. that all+-- edges refer to existing vertices, and there are no 'zero'-labelled edges. It+-- should be impossible to create an inconsistent adjacency map, and we use this+-- function in testing.+consistent :: (Ord a, Eq e, Monoid e) => AdjacencyMap e a -> Bool+consistent (AM m) = referredToVertexSet m `Set.isSubsetOf` Map.keysSet m+    && and [ e /= zero | (_, es) <- Map.toAscList m, (_, e) <- Map.toAscList es ]++-- The set of vertices that are referred to by the edges in an adjacency map+referredToVertexSet :: Ord a => Map a (Map a e) -> Set a+referredToVertexSet m = Set.fromList $ concat+    [ [x, y] | (x, ys) <- Map.toAscList m, (y, _) <- Map.toAscList ys ]
− src/Algebra/Graph/Labelled/AdjacencyMap/Internal.hs
@@ -1,115 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Labelled.AdjdacencyMap.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : unstable------ This module exposes the implementation of edge-labelled adjacency maps. The--- API is unstable and unsafe, and is exposed only for documentation. You should--- use the non-internal module "Algebra.Graph.Labelled.AdjdacencyMap" instead.-------------------------------------------------------------------------------module Algebra.Graph.Labelled.AdjacencyMap.Internal (-    -- * Labelled adjacency map implementation-    AdjacencyMap (..), consistent-    ) where--import Prelude ()-import Prelude.Compat--import Control.DeepSeq-import Data.Map.Strict (Map)-import Data.Monoid (Monoid, getSum, Sum (..))-import Data.Set (Set, (\\))-import GHC.Generics--import qualified Data.Map.Strict as Map-import qualified Data.Set        as Set--import Algebra.Graph.Label---- | Edge-labelled graphs, where the type variable @e@ stands for edge labels.--- For example, 'AdjacencyMap' @Bool@ @a@ is isomorphic to unlabelled graphs--- defined in the top-level module "Algebra.Graph.AdjacencyMap", where @False@--- and @True@ denote the lack of and the existence of an unlabelled edge,--- respectively.-newtype AdjacencyMap e a = AM {-    -- | The /adjacency map/ of an edge-labelled graph: each vertex is-    -- associated with a map from its direct successors to the corresponding-    -- edge labels.-    adjacencyMap :: Map a (Map a e) } deriving (Eq, Generic, NFData)--instance (Ord a, Show a, Ord e, Show e) => Show (AdjacencyMap e a) where-    showsPrec p (AM m)-        | Set.null vs = showString "empty"-        | null es     = showParen (p > 10) $ vshow vs-        | vs == used  = showParen (p > 10) $ eshow es-        | otherwise   = showParen (p > 10) $-                            showString "overlay (" . vshow (vs \\ used) .-                            showString ") ("       . eshow es . showString ")"-      where-        vs   = Map.keysSet m-        es   = internalEdgeList m-        used = referredToVertexSet m-        vshow vs = case Set.toAscList vs of-            [x] -> showString "vertex "   . showsPrec 11 x-            xs  -> showString "vertices " . showsPrec 11 xs-        eshow es = case es of-            [(e, x, y)] -> showString "edge "  . showsPrec 11 e .-                           showString " "      . showsPrec 11 x .-                           showString " "      . showsPrec 11 y-            xs          -> showString "edges " . showsPrec 11 xs--instance (Ord e, Monoid e, Ord a) => Ord (AdjacencyMap e a) where-    compare (AM x) (AM y) = mconcat-        [ compare (vNum x) (vNum y)-        , compare (vSet x) (vSet y)-        , compare (eNum x) (eNum y)-        , compare (eSet x) (eSet y)-        , cmp ]-      where-        vNum   = Map.size-        vSet   = Map.keysSet-        eNum   = getSum . foldMap (Sum . Map.size)-        eSet m = [ (x, y) | (x, ys) <- Map.toAscList m, (y, _) <- Map.toAscList ys ]-        cmp | x == y               = EQ-            | overlays [x, y] == y = LT-            | otherwise            = compare x y---- Overlay a list of adjacency maps.-overlays :: (Eq e, Monoid e, Ord a) => [Map a (Map a e)] -> Map a (Map a e)-overlays = Map.unionsWith (\x -> Map.filter (/= zero) . Map.unionWith mappend x)---- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'--- for more details.-instance (Eq e, Dioid e, Num a, Ord a) => Num (AdjacencyMap e a) where-    fromInteger x = AM $ Map.singleton (fromInteger x) Map.empty-    AM x + AM y   = AM $ overlays [x, y]-    AM x * AM y   = AM $ overlays $ x : y :-        [ Map.fromSet (const targets) (Map.keysSet x) ]-      where-        targets = Map.fromSet (const one) (Map.keysSet y)-    signum      = const (AM Map.empty)-    abs         = id-    negate      = id---- | Check if the internal graph representation is consistent, i.e. that all--- edges refer to existing vertices, and there are no 'zero'-labelled edges. It--- should be impossible to create an inconsistent adjacency map, and we use this--- function in testing.--- /Note: this function is for internal use only/.-consistent :: (Ord a, Eq e, Monoid e) => AdjacencyMap e a -> Bool-consistent (AM m) = referredToVertexSet m `Set.isSubsetOf` Map.keysSet m-    && and [ e /= zero | (_, es) <- Map.toAscList m, (_, e) <- Map.toAscList es ]---- The set of vertices that are referred to by the edges in an adjacency map-referredToVertexSet :: Ord a => Map a (Map a e) -> Set a-referredToVertexSet m = Set.fromList $ concat-    [ [x, y] | (x, ys) <- Map.toAscList m, (y, _) <- Map.toAscList ys ]---- The list of edges in an adjacency map-internalEdgeList :: Map a (Map a e) -> [(e, a, a)]-internalEdgeList m =-    [ (e, x, y) | (x, ys) <- Map.toAscList m, (y, e) <- Map.toAscList ys ]
src/Algebra/Graph/Labelled/Example/Automaton.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, OverloadedLists, TypeFamilies #-}+{-# LANGUAGE OverloadedLists, TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Labelled.Example.Automaton@@ -24,17 +24,6 @@ import Algebra.Graph.ToGraph  import qualified Data.Map as Map--#if !MIN_VERSION_base(4,8,0)-import Data.Set (Set)-import qualified Data.Set as Set-import GHC.Exts hiding (Any)--instance Ord a => IsList (Set a) where-    type Item (Set a) = a-    fromList = Set.fromList-    toList   = Set.toList-#endif  -- | The alphabet of actions for ordering coffee or tea. data Alphabet = Coffee -- ^ Order coffee
src/Algebra/Graph/NonEmpty.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE CPP, DeriveFunctor #-}+{-# LANGUAGE DeriveFunctor #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.NonEmpty--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -47,17 +47,13 @@      -- * Graph transformation     removeVertex1, removeEdge, replaceVertex, mergeVertices, splitVertex1,-    transpose, induce1, simplify, sparsify, sparsifyKL,+    transpose, induce1, induceJust1, simplify, sparsify, sparsifyKL,      -- * Graph composition     box     ) where -import Prelude ()-import Prelude.Compat- import Control.DeepSeq-import Control.Monad.Compat import Control.Monad.State import Data.List.NonEmpty (NonEmpty (..)) import Data.Semigroup ((<>))@@ -244,7 +240,7 @@ -- Complexity: /O(1)/ time, memory and size. -- -- @--- 'hasVertex' x (vertex x) == True+-- 'hasVertex' x (vertex y) == (x == y) -- 'vertexCount' (vertex x) == 1 -- 'edgeCount'   (vertex x) == 0 -- 'size'        (vertex x) == 1@@ -339,6 +335,7 @@ -- -- @ -- edges1 [(x,y)]     == 'edge' x y+-- edges1             == 'overlays1' . 'fmap' ('uncurry' 'edge') -- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub' -- @ edges1 :: NonEmpty (a, a) -> Graph a@@ -446,14 +443,13 @@ -- Complexity: /O(s)/ time. -- -- @--- hasVertex x ('vertex' x) == True--- hasVertex 1 ('vertex' 2) == False+-- hasVertex x ('vertex' y) == (x == y) -- @ hasVertex :: Eq a => a -> Graph a -> Bool hasVertex v = foldg1 (==v) (||) (||) {-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-} --- TODO: Reduce code duplication with 'Algebra.Graph.hasEdge'.+-- See the Note [The implementation of hasEdge] in "Algebra.Graph". -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time. --@@ -464,17 +460,15 @@ -- hasEdge x y                  == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Eq a => a -> a -> Graph a -> Bool-hasEdge s t g = hit g == Edge+hasEdge s t g = foldg1 v o c g 0 == 2   where-    hit (Vertex x   ) = if x == s then Tail else Miss-    hit (Overlay x y) = case hit x of-        Miss -> hit y-        Tail -> max Tail (hit y)-        Edge -> Edge-    hit (Connect x y) = case hit x of-        Miss -> hit y-        Tail -> if hasVertex t y then Edge else Tail-        Edge -> Edge+    v x 0   = if x == s then 1 else 0+    v x _   = if x == t then 2 else 1+    o x y a = case x a of+        0 -> y a+        1 -> if y a == 2 then 2 else 1+        _ -> 2 :: Int+    c x y a = case x a of { 2 -> 2; res -> y res } {-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-}  -- | The number of vertices in a graph.@@ -829,6 +823,7 @@ "transpose/clique1"   forall xs. transpose (clique1 xs) = clique1 (NonEmpty.reverse xs)  #-} +-- TODO: Implement via 'induceJust1' to reduce code duplication. -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate. Returns @Nothing@ if the -- resulting graph is empty.@@ -843,13 +838,28 @@ -- @ induce1 :: (a -> Bool) -> Graph a -> Maybe (Graph a) induce1 p = foldg1-  (\x -> if p x then Just (Vertex x) else Nothing)-  (k Overlay)-  (k Connect)+    (\x -> if p x then Just (Vertex x) else Nothing) (k Overlay) (k Connect)   where-    k _ Nothing a = a-    k _ a Nothing = a-    k f (Just a) (Just b) = Just $ f a b+    k _ Nothing  a        = a+    k _ a        Nothing  = a+    k f (Just a) (Just b) = Just (f a b)++-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'. Returns 'Nothing' if the resulting graph is empty.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- induceJust1 ('vertex' 'Nothing')                               == 'Nothing'+-- induceJust1 ('edge' ('Just' x) 'Nothing')                        == 'Just' ('vertex' x)+-- induceJust1 . 'fmap' 'Just'                                    == 'Just'+-- induceJust1 . 'fmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce1' p+-- @+induceJust1 :: Graph (Maybe a) -> Maybe (Graph a)+induceJust1 = foldg1 (fmap Vertex) (k Overlay) (k Connect)+  where+    k _ Nothing  a        = a+    k _ a        Nothing  = a+    k f (Just a) (Just b) = Just (f a b)  -- | Simplify a graph expression. Semantically, this is the identity function, -- but it simplifies a given expression according to the laws of the algebra.
src/Algebra/Graph/NonEmpty/AdjacencyMap.hs view
@@ -1,7 +1,8 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.NonEmpty.AdjacencyMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -24,7 +25,7 @@ ----------------------------------------------------------------------------- module Algebra.Graph.NonEmpty.AdjacencyMap (     -- * Data structure-    AdjacencyMap, toNonEmpty,+    AdjacencyMap, toNonEmpty, fromNonEmpty,      -- * Basic graph construction primitives     vertex, edge, overlay, connect, vertices1, edges1, overlays1, connects1,@@ -41,38 +42,142 @@      -- * Graph transformation     removeVertex1, removeEdge, replaceVertex, mergeVertices, transpose, gmap,-    induce1,+    induce1, induceJust1,      -- * Graph closure-    closure, reflexiveClosure, symmetricClosure, transitiveClosure+    closure, reflexiveClosure, symmetricClosure, transitiveClosure,++    -- * Miscellaneous+    consistent     ) where  import Prelude hiding (reverse)+import Control.DeepSeq+import Data.Coerce+import Data.List ((\\)) import Data.List.NonEmpty (NonEmpty (..), nonEmpty, toList, reverse) import Data.Maybe import Data.Set (Set) import Data.Tree--import Algebra.Graph.NonEmpty.AdjacencyMap.Internal+import GHC.Generics  import qualified Algebra.Graph.AdjacencyMap as AM import qualified Data.Set                   as Set --- Lift a function to non-empty adjacency maps-via :: (AM.AdjacencyMap a -> AM.AdjacencyMap b)-    ->     AdjacencyMap a ->    AdjacencyMap b-via f = NAM . f . am+{-| The 'AdjacencyMap' data type represents a graph by a map of vertices to+their adjacency sets. We define a 'Num' instance as a convenient notation for+working with graphs: --- Lift a two-argument function to non-empty adjacency maps-via2 :: (AM.AdjacencyMap a -> AM.AdjacencyMap b -> AM.AdjacencyMap c)-     ->     AdjacencyMap a ->    AdjacencyMap b ->    AdjacencyMap c-via2 f (NAM x) (NAM y) = NAM (f x y)+    > 0           == vertex 0+    > 1 + 2       == overlay (vertex 1) (vertex 2)+    > 1 * 2       == connect (vertex 1) (vertex 2)+    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))+    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3)) --- Lift a list function to non-empty adjacency maps-viaL :: (         [AM.AdjacencyMap a] -> AM.AdjacencyMap b)-     ->  NonEmpty (   AdjacencyMap a) ->    AdjacencyMap b-viaL f = NAM . f . fmap am . toList+__Note:__ the 'signum' method of the type class 'Num' cannot be implemented and+will throw an error. Furthermore, the 'Num' instance does not satisfy several+"customary laws" of 'Num', which dictate that 'fromInteger' @0@ and+'fromInteger' @1@ should act as additive and multiplicative identities, and+'negate' as additive inverse. Nevertheless, overloading 'fromInteger', '+' and+'*' is very convenient when working with algebraic graphs; we hope that in+future Haskell's Prelude will provide a more fine-grained class hierarchy for+algebraic structures, which we would be able to utilise without violating any+laws. +The 'Show' instance is defined using basic graph construction primitives:++@show (1         :: AdjacencyMap Int) == "vertex 1"+show (1 + 2     :: AdjacencyMap Int) == "vertices1 [1,2]"+show (1 * 2     :: AdjacencyMap Int) == "edge 1 2"+show (1 * 2 * 3 :: AdjacencyMap Int) == "edges1 [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: AdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"@++The 'Eq' instance satisfies the following laws of algebraic graphs:++    * 'overlay' is commutative, associative and idempotent:++        >       x + y == y + x+        > x + (y + z) == (x + y) + z+        >       x + x == x++    * 'connect' is associative:++        > x * (y * z) == (x * y) * z++    * 'connect' distributes over 'overlay':++        > x * (y + z) == x * y + x * z+        > (x + y) * z == x * z + y * z++    * 'connect' can be decomposed:++        > x * y * z == x * y + x * z + y * z++    * 'connect' satisfies absorption and saturation:++        > x * y + x + y == x * y+        >     x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ and /m/+will denote the number of vertices and edges in the graph, respectively.++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the+'isSubgraphOf' relation and is compatible+with 'overlay' and+'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@x     <= x + y+x + y <= x * y@+-}+newtype AdjacencyMap a = NAM { am :: AM.AdjacencyMap a }+    deriving (Eq, Generic, NFData, Ord)++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap' for+-- more details.+instance (Ord a, Num a) => Num (AdjacencyMap a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = error "NonEmpty.AdjacencyMap.signum cannot be implemented."+    abs         = id+    negate      = id++instance (Ord a, Show a) => Show (AdjacencyMap a) where+    showsPrec p nam+        | null vs    = error "NonEmpty.AdjacencyMap.Show: Graph is empty"+        | null es    = showParen (p > 10) $ vshow vs+        | vs == used = showParen (p > 10) $ eshow es+        | otherwise  = showParen (p > 10) $+                           showString "overlay (" . vshow (vs \\ used) .+                           showString ") (" . eshow es . showString ")"+      where+        vs             = toList (vertexList1 nam)+        es             = edgeList nam+        vshow [x]      = showString "vertex "    . showsPrec 11 x+        vshow xs       = showString "vertices1 " . showsPrec 11 xs+        eshow [(x, y)] = showString "edge "      . showsPrec 11 x .+                         showString " "          . showsPrec 11 y+        eshow xs       = showString "edges1 "    . showsPrec 11 xs+        used           = Set.toAscList $ Set.fromList $ uncurry (++) $ unzip es+ -- Unsafe creation of a NonEmpty list. unsafeNonEmpty :: [a] -> NonEmpty a unsafeNonEmpty = fromMaybe (error msg) . nonEmpty@@ -84,23 +189,34 @@ -- Complexity: /O(1)/ time, memory and size. -- -- @--- toNonEmpty 'AM.empty'              == Nothing--- toNonEmpty ('Algebra.Graph.ToGraph.toAdjacencyMap' x) == Just (x :: 'AdjacencyMap' a)+-- toNonEmpty 'AM.empty'          == 'Nothing'+-- toNonEmpty . 'fromNonEmpty' == 'Just' -- @ toNonEmpty :: AM.AdjacencyMap a -> Maybe (AdjacencyMap a) toNonEmpty x | AM.isEmpty x = Nothing              | otherwise    = Just (NAM x) +-- | Convert a NonEmpty.'AdjacencyMap' into an 'AM.AdjacencyMap'. The resulting+-- graph is guaranteed to be non-empty.+-- Complexity: /O(1)/ time, memory and size.+--+-- @+-- 'isEmpty' . fromNonEmpty    == 'const' 'False'+-- 'toNonEmpty' . fromNonEmpty == 'Just'+-- @+fromNonEmpty :: AdjacencyMap a -> AM.AdjacencyMap a+fromNonEmpty = am+ -- | Construct the graph comprising /a single isolated vertex/. -- Complexity: /O(1)/ time and memory. -- -- @--- 'AdjacencyMap.hasVertex' x (vertex x) == True--- 'AdjacencyMap.vertexCount' (vertex x) == 1--- 'AdjacencyMap.edgeCount'   (vertex x) == 0+-- 'hasVertex' x (vertex y) == (x == y)+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount'   (vertex x) == 0 -- @ vertex :: a -> AdjacencyMap a-vertex = NAM . AM.vertex+vertex = coerce AM.vertex {-# NOINLINE [1] vertex #-}  -- | /Overlay/ two graphs. This is a commutative, associative and idempotent@@ -117,7 +233,7 @@ -- 'edgeCount'   (overlay 1 2) == 0 -- @ overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a-overlay = via2 AM.overlay+overlay = coerce AM.overlay {-# NOINLINE [1] overlay #-}  -- | /Connect/ two graphs. This is an associative operation with the identity@@ -138,7 +254,7 @@ -- 'edgeCount'   (connect 1 2) == 1 -- @ connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a-connect = via2 AM.connect+connect = coerce AM.connect {-# NOINLINE [1] connect #-}  -- | Construct the graph comprising /a single edge/.@@ -152,7 +268,7 @@ -- 'vertexCount' (edge 1 2) == 2 -- @ edge :: Ord a => a -> a -> AdjacencyMap a-edge x y = NAM (AM.edge x y)+edge = coerce AM.edge  -- | Construct the graph comprising a given list of isolated vertices. -- Complexity: /O(L * log(L))/ time and /O(L)/ memory, where /L/ is the length@@ -165,7 +281,7 @@ -- 'vertexSet'   . vertices1 == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @ vertices1 :: Ord a => NonEmpty a -> AdjacencyMap a-vertices1 = NAM . AM.vertices . toList+vertices1 = coerce AM.vertices . toList {-# NOINLINE [1] vertices1 #-}  -- | Construct the graph from a list of edges.@@ -173,10 +289,11 @@ -- -- @ -- edges1 [(x,y)]     == 'edge' x y+-- edges1             == 'overlays1' . 'fmap' ('uncurry' 'edge') -- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub' -- @ edges1 :: Ord a => NonEmpty (a, a) -> AdjacencyMap a-edges1 = NAM . AM.edges . toList+edges1 = coerce AM.edges . toList  -- | Overlay a given list of graphs. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -186,7 +303,7 @@ -- overlays1 [x,y] == 'overlay' x y -- @ overlays1 :: Ord a => NonEmpty (AdjacencyMap a) -> AdjacencyMap a-overlays1 = viaL AM.overlays+overlays1 = coerce AM.overlays . toList {-# NOINLINE overlays1 #-}  -- | Connect a given list of graphs.@@ -197,7 +314,7 @@ -- connects1 [x,y] == 'connect' x y -- @ connects1 :: Ord a => NonEmpty (AdjacencyMap a) -> AdjacencyMap a-connects1 = viaL AM.connects+connects1 = coerce AM.connects . toList {-# NOINLINE connects1 #-}  -- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the@@ -211,17 +328,16 @@ -- isSubgraphOf x y                         ==> x <= y -- @ isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool-isSubgraphOf (NAM x) (NAM y) = AM.isSubgraphOf x y+isSubgraphOf = coerce AM.isSubgraphOf  -- | Check if a graph contains a given vertex. -- Complexity: /O(log(n))/ time. -- -- @--- hasVertex x ('vertex' x) == True--- hasVertex 1 ('vertex' 2) == False+-- hasVertex x ('vertex' y) == (x == y) -- @ hasVertex :: Ord a => a -> AdjacencyMap a -> Bool-hasVertex x = AM.hasVertex x . am+hasVertex = coerce AM.hasVertex  -- | Check if a graph contains a given edge. -- Complexity: /O(log(n))/ time.@@ -233,7 +349,7 @@ -- hasEdge x y                  == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool-hasEdge x y = AM.hasEdge x y . am+hasEdge = coerce AM.hasEdge  -- | The number of vertices in a graph. -- Complexity: /O(1)/ time.@@ -244,7 +360,7 @@ -- vertexCount x \< vertexCount y ==> x \< y -- @ vertexCount :: AdjacencyMap a -> Int-vertexCount = AM.vertexCount . am+vertexCount = coerce AM.vertexCount  -- | The number of edges in a graph. -- Complexity: /O(n)/ time.@@ -255,7 +371,7 @@ -- edgeCount            == 'length' . 'edgeList' -- @ edgeCount :: AdjacencyMap a -> Int-edgeCount = AM.edgeCount . am+edgeCount = coerce AM.edgeCount  -- | The sorted list of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -265,7 +381,7 @@ -- vertexList1 . 'vertices1' == 'Data.List.NonEmpty.nub' . 'Data.List.NonEmpty.sort' -- @ vertexList1 :: AdjacencyMap a -> NonEmpty a-vertexList1 = unsafeNonEmpty . AM.vertexList . am+vertexList1 = unsafeNonEmpty . coerce AM.vertexList  -- | The sorted list of edges of a graph. -- Complexity: /O(n + m)/ time and /O(m)/ memory.@@ -278,7 +394,7 @@ -- edgeList . 'transpose'    == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @ edgeList :: AdjacencyMap a -> [(a, a)]-edgeList = AM.edgeList . am+edgeList = coerce AM.edgeList  -- | The set of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -289,7 +405,7 @@ -- vertexSet . 'clique1'   == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @ vertexSet :: AdjacencyMap a -> Set a-vertexSet = AM.vertexSet . am+vertexSet = coerce AM.vertexSet  -- | The set of edges of a given graph. -- Complexity: /O((n + m) * log(m))/ time and /O(m)/ memory.@@ -300,7 +416,7 @@ -- edgeSet . 'edges'    == Set.'Set.fromList' -- @ edgeSet :: Ord a => AdjacencyMap a -> Set (a, a)-edgeSet = Set.fromAscList . edgeList+edgeSet = coerce AM.edgeSet  -- | The /preset/ of an element @x@ is the set of its /direct predecessors/. -- Complexity: /O(n * log(n))/ time and /O(n)/ memory.@@ -311,7 +427,7 @@ -- preSet y ('edge' x y) == Set.'Set.fromList' [x] -- @ preSet :: Ord a => a -> AdjacencyMap a -> Set.Set a-preSet x = AM.preSet x . am+preSet = coerce AM.preSet  -- | The /postset/ of a vertex is the set of its /direct successors/. -- Complexity: /O(log(n))/ time and /O(1)/ memory.@@ -322,7 +438,7 @@ -- postSet 2 ('edge' 1 2) == Set.'Set.empty' -- @ postSet :: Ord a => a -> AdjacencyMap a -> Set a-postSet x = AM.postSet x . am+postSet = coerce AM.postSet  -- | The /path/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -333,7 +449,7 @@ -- path1 . 'Data.List.NonEmpty.reverse' == 'transpose' . path1 -- @ path1 :: Ord a => NonEmpty a -> AdjacencyMap a-path1 = NAM . AM.path . toList+path1 = coerce AM.path . toList  -- | The /circuit/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -344,7 +460,7 @@ -- circuit1 . 'Data.List.NonEmpty.reverse' == 'transpose' . circuit1 -- @ circuit1 :: Ord a => NonEmpty a -> AdjacencyMap a-circuit1 = NAM . AM.circuit . toList+circuit1 = coerce AM.circuit . toList  -- | The /clique/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -357,7 +473,7 @@ -- clique1 . 'Data.List.NonEmpty.reverse'  == 'transpose' . clique1 -- @ clique1 :: Ord a => NonEmpty a -> AdjacencyMap a-clique1 = NAM . AM.clique . toList+clique1 = coerce AM.clique . toList {-# NOINLINE [1] clique1 #-}  -- | The /biclique/ on two lists of vertices.@@ -368,7 +484,7 @@ -- biclique1 xs      ys      == 'connect' ('vertices1' xs) ('vertices1' ys) -- @ biclique1 :: Ord a => NonEmpty a -> NonEmpty a -> AdjacencyMap a-biclique1 xs ys = NAM $ AM.biclique (toList xs) (toList ys)+biclique1 xs ys = coerce AM.biclique (toList xs) (toList ys)  -- TODO: Optimise. -- | The /star/ formed by a centre vertex connected to a list of leaves.@@ -380,7 +496,7 @@ -- star x [y,z] == 'edges1' [(x,y), (x,z)] -- @ star :: Ord a => a -> [a] -> AdjacencyMap a-star x = NAM . AM.star x+star = coerce AM.star {-# INLINE star #-}  -- | The /stars/ formed by overlaying a list of 'star's. An inverse of@@ -396,7 +512,7 @@ -- 'overlay' (stars1 xs) (stars1 ys) == stars1 (xs '<>' ys) -- @ stars1 :: Ord a => NonEmpty (a, [a]) -> AdjacencyMap a-stars1 = NAM . AM.stars . toList+stars1 = coerce AM.stars . toList  -- | The /tree graph/ constructed from a given 'Tree' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -408,7 +524,7 @@ -- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges1' [(1,2), (1,3), (3,4), (3,5)] -- @ tree :: Ord a => Tree a -> AdjacencyMap a-tree = NAM . AM.tree+tree = coerce AM.tree  -- | Remove a vertex from a given graph. -- Complexity: /O(n*log(n))/ time.@@ -421,7 +537,7 @@ -- removeVertex1 x 'Control.Monad.>=>' removeVertex1 x == removeVertex1 x -- @ removeVertex1 :: Ord a => a -> AdjacencyMap a -> Maybe (AdjacencyMap a)-removeVertex1 x = toNonEmpty . AM.removeVertex x . am+removeVertex1 = fmap toNonEmpty . coerce AM.removeVertex  -- | Remove an edge from a given graph. -- Complexity: /O(log(n))/ time.@@ -433,7 +549,7 @@ -- removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2 -- @ removeEdge :: Ord a => a -> a -> AdjacencyMap a -> AdjacencyMap a-removeEdge x y = via (AM.removeEdge x y)+removeEdge = coerce AM.removeEdge  -- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a -- given 'AdjacencyMap'. If @y@ already exists, @x@ and @y@ will be merged.@@ -445,7 +561,7 @@ -- replaceVertex x y            == 'mergeVertices' (== x) y -- @ replaceVertex :: Ord a => a -> a -> AdjacencyMap a -> AdjacencyMap a-replaceVertex u v = via (AM.replaceVertex u v)+replaceVertex = coerce AM.replaceVertex  -- | Merge vertices satisfying a given predicate into a given vertex. -- Complexity: /O((n + m) * log(n))/ time, assuming that the predicate takes@@ -458,7 +574,7 @@ -- mergeVertices 'odd'  1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: Ord a => (a -> Bool) -> a -> AdjacencyMap a -> AdjacencyMap a-mergeVertices p v = via (AM.mergeVertices p v)+mergeVertices = coerce AM.mergeVertices  -- | Transpose a given graph. -- Complexity: /O(m * log(n))/ time, /O(n + m)/ memory.@@ -470,7 +586,7 @@ -- 'edgeList' . transpose  == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a-transpose = via AM.transpose+transpose = coerce AM.transpose {-# NOINLINE [1] transpose #-}  {-# RULES@@ -497,7 +613,7 @@ -- gmap f . gmap g   == gmap (f . g) -- @ gmap :: (Ord a, Ord b) => (a -> b) -> AdjacencyMap a -> AdjacencyMap b-gmap f = via (AM.gmap f)+gmap = coerce AM.gmap  -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.@@ -511,8 +627,21 @@ -- induce1 p 'Control.Monad.>=>' induce1 q == induce1 (\\x -> p x && q x) -- @ induce1 :: (a -> Bool) -> AdjacencyMap a -> Maybe (AdjacencyMap a)-induce1 p = toNonEmpty . AM.induce p . am+induce1 = fmap toNonEmpty . coerce AM.induce +-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'. Returns 'Nothing' if the resulting graph is empty.+-- Complexity: /O(n + m)/ time.+--+-- @+-- induceJust1 ('vertex' 'Nothing')                               == 'Nothing'+-- induceJust1 ('edge' ('Just' x) 'Nothing')                        == 'Just' ('vertex' x)+-- induceJust1 . 'gmap' 'Just'                                    == 'Just'+-- induceJust1 . 'gmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce1' p+-- @+induceJust1 :: Ord a => AdjacencyMap (Maybe a) -> Maybe (AdjacencyMap a)+induceJust1 m = toNonEmpty (AM.induceJust (coerce m))+ -- | Compute the /reflexive and transitive closure/ of a graph. -- Complexity: /O(n * m * log(n)^2)/ time. --@@ -527,7 +656,7 @@ -- 'postSet' x (closure y)    == Set.'Set.fromList' ('Algebra.Graph.ToGraph.reachable' x y) -- @ closure :: Ord a => AdjacencyMap a -> AdjacencyMap a-closure = via (AM.closure)+closure = coerce AM.closure  -- | Compute the /reflexive closure/ of a graph by adding a self-loop to every -- vertex.@@ -540,7 +669,7 @@ -- reflexiveClosure . reflexiveClosure == reflexiveClosure -- @ reflexiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a-reflexiveClosure = via AM.reflexiveClosure+reflexiveClosure = coerce AM.reflexiveClosure  -- | Compute the /symmetric closure/ of a graph by overlaying it with its own -- transpose.@@ -553,7 +682,7 @@ -- symmetricClosure . symmetricClosure == symmetricClosure -- @ symmetricClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a-symmetricClosure = via AM.symmetricClosure+symmetricClosure = coerce AM.symmetricClosure  -- | Compute the /transitive closure/ of a graph. -- Complexity: /O(n * m * log(n)^2)/ time.@@ -565,4 +694,21 @@ -- transitiveClosure . transitiveClosure == transitiveClosure -- @ transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a-transitiveClosure = via AM.transitiveClosure+transitiveClosure = coerce AM.transitiveClosure++-- TODO: Add tests.+-- | Check that the internal graph representation is consistent, i.e. that all+-- edges refer to existing vertices, and the graph is non-empty. It should be+-- impossible to create an inconsistent adjacency map, and we use this function+-- in testing.+--+-- @+-- consistent ('vertex' x)    == True+-- consistent ('overlay' x y) == True+-- consistent ('connect' x y) == True+-- consistent ('edge' x y)    == True+-- consistent ('edges' xs)    == True+-- consistent ('stars' xs)    == True+-- @+consistent :: Ord a => AdjacencyMap a -> Bool+consistent (NAM x) = AM.consistent x && not (AM.isEmpty x)
− src/Algebra/Graph/NonEmpty/AdjacencyMap/Internal.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.NonEmpty.AdjacencyMap.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : experimental------ This module exposes the implementation of non-empty adjacency maps. The API--- is unstable and unsafe, and is exposed only for documentation. You should use--- the non-internal module "Algebra.Graph.NonEmpty.AdjacencyMap" instead.-------------------------------------------------------------------------------module Algebra.Graph.NonEmpty.AdjacencyMap.Internal (-    -- * Adjacency map implementation-    AdjacencyMap (..), consistent-    ) where--import Control.DeepSeq-import Data.List-import GHC.Generics--import qualified Algebra.Graph.AdjacencyMap          as AM-import qualified Algebra.Graph.AdjacencyMap.Internal as AM-import qualified Data.Map.Strict                     as Map-import qualified Data.Set                            as Set--{-| The 'AdjacencyMap' data type represents a graph by a map of vertices to-their adjacency sets. We define a 'Num' instance as a convenient notation for-working with graphs:--    > 0           == vertex 0-    > 1 + 2       == overlay (vertex 1) (vertex 2)-    > 1 * 2       == connect (vertex 1) (vertex 2)-    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))-    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))--__Note:__ the 'signum' method of the type class 'Num' cannot be implemented and-will throw an error. Furthermore, the 'Num' instance does not satisfy several-"customary laws" of 'Num', which dictate that 'fromInteger' @0@ and-'fromInteger' @1@ should act as additive and multiplicative identities, and-'negate' as additive inverse. Nevertheless, overloading 'fromInteger', '+' and-'*' is very convenient when working with algebraic graphs; we hope that in-future Haskell's Prelude will provide a more fine-grained class hierarchy for-algebraic structures, which we would be able to utilise without violating any-laws.--The 'Show' instance is defined using basic graph construction primitives:--@show (1         :: AdjacencyMap Int) == "vertex 1"-show (1 + 2     :: AdjacencyMap Int) == "vertices1 [1,2]"-show (1 * 2     :: AdjacencyMap Int) == "edge 1 2"-show (1 * 2 * 3 :: AdjacencyMap Int) == "edges1 [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: AdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"@--The 'Eq' instance satisfies the following laws of algebraic graphs:--    * 'Algebra.Graph.NonEmpty.AdjacencyMap.overlay' is commutative, associative and idempotent:--        >       x + y == y + x-        > x + (y + z) == (x + y) + z-        >       x + x == x--    * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' is associative:--        > x * (y * z) == (x * y) * z--    * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' distributes over 'Algebra.Graph.NonEmpty.AdjacencyMap.overlay':--        > x * (y + z) == x * y + x * z-        > (x + y) * z == x * z + y * z--    * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' can be decomposed:--        > x * y * z == x * y + x * z + y * z--    * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' satisfies absorption and saturation:--        > x * y + x + y == x * y-        >     x * x * x == x * x--When specifying the time and memory complexity of graph algorithms, /n/ and /m/-will denote the number of vertices and edges in the graph, respectively.--The total order on graphs is defined using /size-lexicographic/ comparison:--* Compare the number of vertices. In case of a tie, continue.-* Compare the sets of vertices. In case of a tie, continue.-* Compare the number of edges. In case of a tie, continue.-* Compare the sets of edges.--Here are a few examples:--@'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 1 < 'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 2-'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 3 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2-'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 1 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1-'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2-'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1 + 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 2 2-'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 3@--Note that the resulting order refines the-'Algebra.Graph.NonEmpty.AdjacencyMap.isSubgraphOf' relation and is compatible-with 'Algebra.Graph.NonEmpty.AdjacencyMap.overlay' and-'Algebra.Graph.NonEmpty.AdjacencyMap.connect' operations:--@'Algebra.Graph.NonEmpty.AdjacencyMap.isSubgraphOf' x y ==> x <= y@--@x     <= x + y-x + y <= x * y@--}-newtype AdjacencyMap a = NAM {-    -- | The /adjacency map/ of a graph: each vertex is associated with a set of-    -- its direct successors. Complexity: /O(1)/ time and memory.-    ---    -- @-    -- adjacencyMap ('vertex' x) == Map.'Map.singleton' x Set.'Set.empty'-    -- adjacencyMap ('Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1) == Map.'Map.singleton' 1 (Set.'Set.singleton' 1)-    -- adjacencyMap ('Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2) == Map.'Map.fromList' [(1,Set.'Set.singleton' 2), (2,Set.'Set.empty')]-    -- @-    am :: AM.AdjacencyMap a } deriving (Eq, Generic, NFData, Ord)---- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap' for--- more details.-instance (Ord a, Num a) => Num (AdjacencyMap a) where-    fromInteger   = NAM . AM.vertex . fromInteger-    NAM x + NAM y = NAM (AM.overlay x y)-    NAM x * NAM y = NAM (AM.connect x y)-    signum        = error "NonEmpty.AdjacencyMap.signum cannot be implemented."-    abs           = id-    negate        = id--instance (Ord a, Show a) => Show (AdjacencyMap a) where-    showsPrec p (NAM (AM.AM m))-        | null vs    = error "NonEmpty.AdjacencyMap.Show: Graph is empty"-        | null es    = showParen (p > 10) $ vshow vs-        | vs == used = showParen (p > 10) $ eshow es-        | otherwise  = showParen (p > 10) $-                           showString "overlay (" . vshow (vs \\ used) .-                           showString ") (" . eshow es . showString ")"-      where-        vs             = Set.toAscList (Map.keysSet m)-        es             = AM.internalEdgeList m-        vshow [x]      = showString "vertex "    . showsPrec 11 x-        vshow xs       = showString "vertices1 " . showsPrec 11 xs-        eshow [(x, y)] = showString "edge "      . showsPrec 11 x .-                         showString " "          . showsPrec 11 y-        eshow xs       = showString "edges1 "    . showsPrec 11 xs-        used           = Set.toAscList (AM.referredToVertexSet m)---- | Check if the internal graph representation is consistent, i.e. that all--- edges refer to existing vertices, and the graph is non-empty. It should be--- impossible to create an inconsistent adjacency map, and we use this function--- in testing.--- /Note: this function is for internal use only/.------ @--- consistent ('vertex' x)    == True--- consistent ('overlay' x y) == True--- consistent ('connect' x y) == True--- consistent ('Algebra.Graph.NonEmpty.AdjacencyMap.edge' x y)    == True--- consistent ('Algebra.Graph.NonEmpty.AdjacencyMap.edges' xs)    == True--- consistent ('Algebra.Graph.NonEmpty.AdjacencyMap.stars' xs)    == True--- @-consistent :: Ord a => AdjacencyMap a -> Bool-consistent (NAM x) = AM.consistent x && not (AM.isEmpty x)
src/Algebra/Graph/Relation.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -32,23 +32,227 @@     path, circuit, clique, biclique, star, stars, tree, forest,      -- * Graph transformation-    removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce,+    removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap,+    induce, induceJust,      -- * Relational operations-    compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure-  ) where+    compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure, -import Prelude ()-import Prelude.Compat+    -- * Miscellaneous+    consistent+    ) where +import Control.DeepSeq+import Data.Set (Set, union) import Data.Tree import Data.Tuple -import Algebra.Graph.Relation.Internal+import qualified Data.Maybe as Maybe+import qualified Data.Set   as Set+import qualified Data.Tree  as Tree -import qualified Data.Set    as Set-import qualified Data.Tree   as Tree+import Algebra.Graph.Internal +{-| The 'Relation' data type represents a graph as a /binary relation/. We+define a 'Num' instance as a convenient notation for working with graphs:++    > 0           == vertex 0+    > 1 + 2       == overlay (vertex 1) (vertex 2)+    > 1 * 2       == connect (vertex 1) (vertex 2)+    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))+    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))++__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.++The 'Show' instance is defined using basic graph construction primitives:++@show (empty     :: Relation Int) == "empty"+show (1         :: Relation Int) == "vertex 1"+show (1 + 2     :: Relation Int) == "vertices [1,2]"+show (1 * 2     :: Relation Int) == "edge 1 2"+show (1 * 2 * 3 :: Relation Int) == "edges [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: Relation Int) == "overlay (vertex 3) (edge 1 2)"@++The 'Eq' instance satisfies all axioms of algebraic graphs:++    * 'overlay' is commutative and associative:++        >       x + y == y + x+        > x + (y + z) == (x + y) + z++    * 'connect' is associative and has 'empty' as the identity:++        >   x * empty == x+        >   empty * x == x+        > x * (y * z) == (x * y) * z++    * 'connect' distributes over 'overlay':++        > x * (y + z) == x * y + x * z+        > (x + y) * z == x * z + y * z++    * 'connect' can be decomposed:++        > x * y * z == x * y + x * z + y * z++The following useful theorems can be proved from the above set of axioms.++    * 'overlay' has 'empty' as the+    identity and is idempotent:++        >   x + empty == x+        >   empty + x == x+        >       x + x == x++    * Absorption and saturation of 'connect':++        > x * y + x + y == x * y+        >     x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ and /m/+will denote the number of vertices and edges in the graph, respectively.++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the+'isSubgraphOf' relation and is compatible with+'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x     <= x + y+x + y <= x * y@+-}+data Relation a = Relation {+    -- | The /domain/ of the relation. Complexity: /O(1)/ time and memory.+    domain :: Set a,+    -- | The set of pairs of elements that are /related/. It is guaranteed that+    -- each element belongs to the domain. Complexity: /O(1)/ time and memory.+    relation :: Set (a, a)+  } deriving Eq++instance (Ord a, Show a) => Show (Relation a) where+    showsPrec p (Relation d r)+        | Set.null d = showString "empty"+        | Set.null r = showParen (p > 10) $ vshow (Set.toAscList d)+        | d == used  = showParen (p > 10) $ eshow (Set.toAscList r)+        | otherwise  = showParen (p > 10) $+                           showString "overlay (" .+                           vshow (Set.toAscList $ Set.difference d used) .+                           showString ") (" . eshow (Set.toAscList r) .+                           showString ")"+      where+        vshow [x]      = showString "vertex "   . showsPrec 11 x+        vshow xs       = showString "vertices " . showsPrec 11 xs+        eshow [(x, y)] = showString "edge "     . showsPrec 11 x .+                         showString " "         . showsPrec 11 y+        eshow xs       = showString "edges "    . showsPrec 11 xs+        used           = referredToVertexSet r++instance Ord a => Ord (Relation a) where+    compare x y = mconcat+        [ compare (vertexCount x) (vertexCount  y)+        , compare (vertexSet   x) (vertexSet    y)+        , compare (edgeCount   x) (edgeCount    y)+        , compare (edgeSet     x) (edgeSet      y) ]++instance NFData a => NFData (Relation a) where+    rnf (Relation d r) = rnf d `seq` rnf r `seq` ()++-- | __Note:__ this does not satisfy the usual ring laws; see 'Relation' for+-- more details.+instance (Ord a, Num a) => Num (Relation a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id++-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty'     empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount'   empty == 0+-- @+empty :: Relation a+empty = Relation Set.empty Set.empty++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty'     (vertex x) == False+-- 'hasVertex' x (vertex y) == (x == y)+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount'   (vertex x) == 0+-- @+vertex :: a -> Relation a+vertex x = Relation (Set.singleton x) Set.empty++-- | /Overlay/ two graphs. This is a commutative, associative and idempotent+-- operation with the identity 'empty'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'isEmpty'     (overlay x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (overlay x y) >= 'edgeCount' x+-- 'edgeCount'   (overlay x y) <= 'edgeCount' x   + 'edgeCount' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount'   (overlay 1 2) == 0+-- @+overlay :: Ord a => Relation a -> Relation a -> Relation a+overlay x y = Relation (domain x `union` domain y) (relation x `union` relation y)++-- | /Connect/ two graphs. This is an associative operation with the identity+-- 'empty', which distributes over 'overlay' and obeys the decomposition axiom.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the+-- number of edges in the resulting graph is quadratic with respect to the number+-- of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.+--+-- @+-- 'isEmpty'     (connect x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y) >= 'vertexCount' x+-- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (connect x y) >= 'edgeCount' x+-- 'edgeCount'   (connect x y) >= 'edgeCount' y+-- 'edgeCount'   (connect x y) >= 'vertexCount' x * 'vertexCount' y+-- 'edgeCount'   (connect x y) <= 'vertexCount' x * 'vertexCount' y + 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (connect 1 2) == 2+-- 'edgeCount'   (connect 1 2) == 1+-- @+connect :: Ord a => Relation a -> Relation a -> Relation a+connect x y = Relation (domain x `union` domain y)+    (relation x `union` relation y `union` (domain x `setProduct` domain y))+ -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory and size. --@@ -82,6 +286,7 @@ -- @ -- edges []          == 'empty' -- edges [(x,y)]     == 'edge' x y+-- edges             == 'overlays' . 'map' ('uncurry' 'edge') -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- @ edges :: Ord a => [(a, a)] -> Relation a@@ -147,8 +352,7 @@ -- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Ord a => a -> Relation a -> Bool@@ -489,7 +693,7 @@  -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.--- Complexity: /O(m)/ time, assuming that the predicate takes /O(1)/ to+-- Complexity: /O(n + m)/ time, assuming that the predicate takes /O(1)/ to -- be evaluated. -- -- @@@ -504,6 +708,26 @@   where     pp (x, y) = p x && p y +-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(n + m)/ time.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'gmap' 'Just'                                    == 'id'+-- induceJust . 'gmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Ord a => Relation (Maybe a) -> Relation a+induceJust (Relation d r) = Relation (catMaybesSet d) (catMaybesSet2 r)+  where+    catMaybesSet         = Set.mapMonotonic Maybe.fromJust . Set.delete Nothing+    catMaybesSet2        = Set.mapMonotonic (\(x, y) -> (Maybe.fromJust x, Maybe.fromJust y))+                         . Set.filter p+    p (Nothing, _)       = False+    p (_,       Nothing) = False+    p (_,       _)       = True+ -- | Left-to-right /relational composition/ of graphs: vertices @x@ and @z@ are -- connected in the resulting graph if there is a vertex @y@, such that @x@ is -- connected to @y@ in the first graph, and @y@ is connected to @z@ in the@@ -590,3 +814,24 @@     | otherwise  = transitiveClosure new   where     new = overlay old (old `compose` old)++-- | Check that the internal representation of a relation is consistent, i.e. if all+-- pairs of elements in the 'relation' refer to existing elements in the 'domain'.+-- It should be impossible to create an inconsistent 'Relation', and we use this+-- function in testing.+--+-- @+-- consistent 'empty'         == True+-- consistent ('vertex' x)    == True+-- consistent ('overlay' x y) == True+-- consistent ('connect' x y) == True+-- consistent ('edge' x y)    == True+-- consistent ('edges' xs)    == True+-- consistent ('stars' xs)    == True+-- @+consistent :: Ord a => Relation a -> Bool+consistent (Relation d r) = referredToVertexSet r `Set.isSubsetOf` d++-- The set of elements that appear in a given set of pairs.+referredToVertexSet :: Ord a => Set (a, a) -> Set a+referredToVertexSet = Set.fromList . uncurry (++) . unzip . Set.toAscList
− src/Algebra/Graph/Relation/Internal.hs
@@ -1,250 +0,0 @@--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Relation.Internal--- Copyright  : (c) Andrey Mokhov 2016-2019--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : unstable------ This module exposes the implementation of the 'Relation' data type. The API--- is unstable and unsafe, and is exposed only for documentation. You should--- use the non-internal module "Algebra.Graph.Relation" instead.-------------------------------------------------------------------------------module Algebra.Graph.Relation.Internal (-    -- * Binary relation implementation-    Relation (..), empty, vertex, overlay, connect, setProduct, consistent,-    referredToVertexSet-    ) where--import Control.DeepSeq (NFData, rnf)-import Data.Monoid (mconcat)-import Data.Set (Set, union)--import Algebra.Graph.Internal--import qualified Data.Set as Set--{-| The 'Relation' data type represents a graph as a /binary relation/. We-define a 'Num' instance as a convenient notation for working with graphs:--    > 0           == vertex 0-    > 1 + 2       == overlay (vertex 1) (vertex 2)-    > 1 * 2       == connect (vertex 1) (vertex 2)-    > 1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))-    > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))--__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',-which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as-additive and multiplicative identities, and 'negate' as additive inverse.-Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when-working with algebraic graphs; we hope that in future Haskell's Prelude will-provide a more fine-grained class hierarchy for algebraic structures, which we-would be able to utilise without violating any laws.--The 'Show' instance is defined using basic graph construction primitives:--@show (empty     :: Relation Int) == "empty"-show (1         :: Relation Int) == "vertex 1"-show (1 + 2     :: Relation Int) == "vertices [1,2]"-show (1 * 2     :: Relation Int) == "edge 1 2"-show (1 * 2 * 3 :: Relation Int) == "edges [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: Relation Int) == "overlay (vertex 3) (edge 1 2)"@--The 'Eq' instance satisfies all axioms of algebraic graphs:--    * 'Algebra.Graph.Relation.overlay' is commutative and associative:--        >       x + y == y + x-        > x + (y + z) == (x + y) + z--    * 'Algebra.Graph.Relation.connect' is associative and has-    'Algebra.Graph.Relation.empty' as the identity:--        >   x * empty == x-        >   empty * x == x-        > x * (y * z) == (x * y) * z--    * 'Algebra.Graph.Relation.connect' distributes over-    'Algebra.Graph.Relation.overlay':--        > x * (y + z) == x * y + x * z-        > (x + y) * z == x * z + y * z--    * 'Algebra.Graph.Relation.connect' can be decomposed:--        > x * y * z == x * y + x * z + y * z--The following useful theorems can be proved from the above set of axioms.--    * 'Algebra.Graph.Relation.overlay' has 'Algebra.Graph.Relation.empty' as the-    identity and is idempotent:--        >   x + empty == x-        >   empty + x == x-        >       x + x == x--    * Absorption and saturation of 'Algebra.Graph.Relation.connect':--        > x * y + x + y == x * y-        >     x * x * x == x * x--When specifying the time and memory complexity of graph algorithms, /n/ and /m/-will denote the number of vertices and edges in the graph, respectively.--The total order on graphs is defined using /size-lexicographic/ comparison:--* Compare the number of vertices. In case of a tie, continue.-* Compare the sets of vertices. In case of a tie, continue.-* Compare the number of edges. In case of a tie, continue.-* Compare the sets of edges.--Here are a few examples:--@'vertex' 1 < 'vertex' 2-'vertex' 3 < 'Algebra.Graph.Relation.edge' 1 2-'vertex' 1 < 'Algebra.Graph.Relation.edge' 1 1-'Algebra.Graph.Relation.edge' 1 1 < 'Algebra.Graph.Relation.edge' 1 2-'Algebra.Graph.Relation.edge' 1 2 < 'Algebra.Graph.Relation.edge' 1 1 + 'Algebra.Graph.Relation.edge' 2 2-'Algebra.Graph.Relation.edge' 1 2 < 'Algebra.Graph.Relation.edge' 1 3@--Note that the resulting order refines the-'Algebra.Graph.Relation.isSubgraphOf' relation and is compatible with-'overlay' and 'connect' operations:--@'Algebra.Graph.Relation.isSubgraphOf' x y ==> x <= y@--@'empty' <= x-x     <= x + y-x + y <= x * y@--}-data Relation a = Relation {-    -- | The /domain/ of the relation. Complexity: /O(1)/ time and memory.-    domain :: Set a,-    -- | The set of pairs of elements that are /related/. It is guaranteed that-    -- each element belongs to the domain. Complexity: /O(1)/ time and memory.-    relation :: Set (a, a)-  } deriving Eq--instance (Ord a, Show a) => Show (Relation a) where-    showsPrec p (Relation d r)-        | Set.null d = showString "empty"-        | Set.null r = showParen (p > 10) $ vshow (Set.toAscList d)-        | d == used  = showParen (p > 10) $ eshow (Set.toAscList r)-        | otherwise  = showParen (p > 10) $-                           showString "overlay (" .-                           vshow (Set.toAscList $ Set.difference d used) .-                           showString ") (" . eshow (Set.toAscList r) .-                           showString ")"-      where-        vshow [x]      = showString "vertex "   . showsPrec 11 x-        vshow xs       = showString "vertices " . showsPrec 11 xs-        eshow [(x, y)] = showString "edge "     . showsPrec 11 x .-                         showString " "         . showsPrec 11 y-        eshow xs       = showString "edges "    . showsPrec 11 xs-        used           = referredToVertexSet r--instance Ord a => Ord (Relation a) where-    compare x y = mconcat-        [ compare (Set.size $ domain   x) (Set.size $ domain   y)-        , compare (           domain   x) (           domain   y)-        , compare (Set.size $ relation x) (Set.size $ relation y)-        , compare (           relation x) (           relation y) ]---- | Construct the /empty graph/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.Relation.isEmpty'     empty == True--- 'Algebra.Graph.Relation.hasVertex' x empty == False--- 'Algebra.Graph.Relation.vertexCount' empty == 0--- 'Algebra.Graph.Relation.edgeCount'   empty == 0--- @-empty :: Relation a-empty = Relation Set.empty Set.empty---- | Construct the graph comprising /a single isolated vertex/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.Relation.isEmpty'     (vertex x) == False--- 'Algebra.Graph.Relation.hasVertex' x (vertex x) == True--- 'Algebra.Graph.Relation.vertexCount' (vertex x) == 1--- 'Algebra.Graph.Relation.edgeCount'   (vertex x) == 0--- @-vertex :: a -> Relation a-vertex x = Relation (Set.singleton x) Set.empty---- | /Overlay/ two graphs. This is a commutative, associative and idempotent--- operation with the identity 'empty'.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- 'Algebra.Graph.Relation.isEmpty'     (overlay x y) == 'Algebra.Graph.Relation.isEmpty'   x   && 'iAlgebra.Graph.Relation.sEmpty'   y--- 'Algebra.Graph.Relation.hasVertex' z (overlay x y) == 'Algebra.Graph.Relation.hasVertex' z x || 'Algebra.Graph.Relation.hasVertex' z y--- 'Algebra.Graph.Relation.vertexCount' (overlay x y) >= 'Algebra.Graph.Relation.vertexCount' x--- 'Algebra.Graph.Relation.vertexCount' (overlay x y) <= 'Algebra.Graph.Relation.vertexCount' x + 'Algebra.Graph.Relation.vertexCount' y--- 'Algebra.Graph.Relation.edgeCount'   (overlay x y) >= 'Algebra.Graph.Relation.edgeCount' x--- 'Algebra.Graph.Relation.edgeCount'   (overlay x y) <= 'Algebra.Graph.Relation.edgeCount' x   + 'Algebra.Graph.Relation.edgeCount' y--- 'Algebra.Graph.Relation.vertexCount' (overlay 1 2) == 2--- 'Algebra.Graph.Relation.edgeCount'   (overlay 1 2) == 0--- @-overlay :: Ord a => Relation a -> Relation a -> Relation a-overlay x y = Relation (domain x `union` domain y) (relation x `union` relation y)---- | /Connect/ two graphs. This is an associative operation with the identity--- 'empty', which distributes over 'overlay' and obeys the decomposition axiom.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the--- number of edges in the resulting graph is quadratic with respect to the number--- of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.------ @--- 'Algebra.Graph.Relation.isEmpty'     (connect x y) == 'Algebra.Graph.Relation.isEmpty'   x   && 'Algebra.Graph.Relation.isEmpty'   y--- 'Algebra.Graph.Relation.hasVertex' z (connect x y) == 'Algebra.Graph.Relation.hasVertex' z x || 'Algebra.Graph.Relation.hasVertex' z y--- 'Algebra.Graph.Relation.vertexCount' (connect x y) >= 'Algebra.Graph.Relation.vertexCount' x--- 'Algebra.Graph.Relation.vertexCount' (connect x y) <= 'Algebra.Graph.Relation.vertexCount' x + 'Algebra.Graph.Relation.vertexCount' y--- 'Algebra.Graph.Relation.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.edgeCount' x--- 'Algebra.Graph.Relation.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.edgeCount' y--- 'Algebra.Graph.Relation.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.vertexCount' x * 'Algebra.Graph.Relation.vertexCount' y--- 'Algebra.Graph.Relation.edgeCount'   (connect x y) <= 'Algebra.Graph.Relation.vertexCount' x * 'Algebra.Graph.Relation.vertexCount' y + 'Algebra.Graph.Relation.edgeCount' x + 'Algebra.Graph.Relation.edgeCount' y--- 'Algebra.Graph.Relation.vertexCount' (connect 1 2) == 2--- 'Algebra.Graph.Relation.edgeCount'   (connect 1 2) == 1--- @-connect :: Ord a => Relation a -> Relation a -> Relation a-connect x y = Relation (domain x `union` domain y)-    (relation x `union` relation y `union` (domain x `setProduct` domain y))--instance NFData a => NFData (Relation a) where-    rnf (Relation d r) = rnf d `seq` rnf r `seq` ()---- | __Note:__ this does not satisfy the usual ring laws; see 'Relation' for--- more details.-instance (Ord a, Num a) => Num (Relation a) where-    fromInteger = vertex . fromInteger-    (+)         = overlay-    (*)         = connect-    signum      = const empty-    abs         = id-    negate      = id---- | Check if the internal representation of a relation is consistent, i.e. if all--- pairs of elements in the 'relation' refer to existing elements in the 'domain'.--- It should be impossible to create an inconsistent 'Relation', and we use this--- function in testing.--- /Note: this function is for internal use only/.------ @--- consistent 'Algebra.Graph.Relation.empty'         == True--- consistent ('Algebra.Graph.Relation.vertex' x)    == True--- consistent ('Algebra.Graph.Relation.overlay' x y) == True--- consistent ('Algebra.Graph.Relation.connect' x y) == True--- consistent ('Algebra.Graph.Relation.edge' x y)    == True--- consistent ('Algebra.Graph.Relation.edges' xs)    == True--- consistent ('Algebra.Graph.Relation.stars' xs)    == True--- @-consistent :: Ord a => Relation a -> Bool-consistent (Relation d r) = referredToVertexSet r `Set.isSubsetOf` d---- | The set of elements that appear in a given set of pairs.--- /Note: this function is for internal use only/.-referredToVertexSet :: Ord a => Set (a, a) -> Set a-referredToVertexSet = Set.fromList . uncurry (++) . unzip . Set.toAscList
− src/Algebra/Graph/Relation/InternalDerived.hs
@@ -1,130 +0,0 @@--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Relation.InternalDerived--- Copyright  : (c) Andrey Mokhov 2016-2019--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : unstable------ This module exposes the implementation of derived binary relation data types.--- The API is unstable and unsafe, and is exposed only for documentation. You--- should use the non-internal modules "Algebra.Graph.Relation.Reflexive",--- "Algebra.Graph.Relation.Symmetric", "Algebra.Graph.Relation.Transitive" and--- "Algebra.Graph.Relation.Preorder" instead.-------------------------------------------------------------------------------module Algebra.Graph.Relation.InternalDerived (-    -- * Implementation of derived binary relations-    ReflexiveRelation (..), TransitiveRelation (..), PreorderRelation (..)-  ) where--import Control.DeepSeq (NFData (..))--import Algebra.Graph.Class-import Algebra.Graph.Relation (Relation, reflexiveClosure, transitiveClosure, closure)--{-| The 'ReflexiveRelation' data type represents a /reflexive binary relation/-over a set of elements. Reflexive relations satisfy all laws of the-'Reflexive' type class and, in particular, the /self-loop/ axiom:--@'vertex' x == 'vertex' x * 'vertex' x@--The 'Show' instance produces reflexively closed expressions:--@show (1     :: ReflexiveRelation Int) == "edge 1 1"-show (1 * 2 :: ReflexiveRelation Int) == "edges [(1,1),(1,2),(2,2)]"@--}-newtype ReflexiveRelation a = ReflexiveRelation { fromReflexive :: Relation a }-    deriving (Num, NFData)--instance Ord a => Eq (ReflexiveRelation a) where-    x == y = reflexiveClosure (fromReflexive x) == reflexiveClosure (fromReflexive y)--instance (Ord a, Show a) => Show (ReflexiveRelation a) where-    show = show . reflexiveClosure . fromReflexive--instance Ord a => Graph (ReflexiveRelation a) where-    type Vertex (ReflexiveRelation a) = a-    empty       = ReflexiveRelation empty-    vertex      = ReflexiveRelation . vertex-    overlay x y = ReflexiveRelation $ fromReflexive x `overlay` fromReflexive y-    connect x y = ReflexiveRelation $ fromReflexive x `connect` fromReflexive y--instance Ord a => Reflexive (ReflexiveRelation a)---- TODO: Optimise the implementation by caching the results of transitive closure.-{-| The 'TransitiveRelation' data type represents a /transitive binary relation/-over a set of elements. Transitive relations satisfy all laws of the-'Transitive' type class and, in particular, the /closure/ axiom:--@y /= 'empty' ==> x * y + x * z + y * z == x * y + y * z@--For example, the following holds:--@'path' xs == ('clique' xs :: TransitiveRelation Int)@--The 'Show' instance produces transitively closed expressions:--@show (1 * 2         :: TransitiveRelation Int) == "edge 1 2"-show (1 * 2 + 2 * 3 :: TransitiveRelation Int) == "edges [(1,2),(1,3),(2,3)]"@--}-newtype TransitiveRelation a = TransitiveRelation { fromTransitive :: Relation a }-    deriving (Num, NFData)--instance Ord a => Eq (TransitiveRelation a) where-    x == y = transitiveClosure (fromTransitive x) == transitiveClosure (fromTransitive y)--instance (Ord a, Show a) => Show (TransitiveRelation a) where-    show = show . transitiveClosure . fromTransitive---- TODO: To be derived automatically using GeneralizedNewtypeDeriving in GHC 8.2-instance Ord a => Graph (TransitiveRelation a) where-    type Vertex (TransitiveRelation a) = a-    empty       = TransitiveRelation empty-    vertex      = TransitiveRelation . vertex-    overlay x y = TransitiveRelation $ fromTransitive x `overlay` fromTransitive y-    connect x y = TransitiveRelation $ fromTransitive x `connect` fromTransitive y--instance Ord a => Transitive (TransitiveRelation a)---- TODO: Optimise the implementation by caching the results of preorder closure.-{-| The 'PreorderRelation' data type represents a-/binary relation that is both reflexive and transitive/. Preorders satisfy all-laws of the 'Preorder' type class and, in particular, the /self-loop/ axiom:--@'vertex' x == 'vertex' x * 'vertex' x@--and the /closure/ axiom:--@y /= 'empty' ==> x * y + x * z + y * z == x * y + y * z@--For example, the following holds:--@'path' xs == ('clique' xs :: PreorderRelation Int)@--The 'Show' instance produces reflexively and transitively closed expressions:--@show (1             :: PreorderRelation Int) == "edge 1 1"-show (1 * 2         :: PreorderRelation Int) == "edges [(1,1),(1,2),(2,2)]"-show (1 * 2 + 2 * 3 :: PreorderRelation Int) == "edges [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]"@--}-newtype PreorderRelation a = PreorderRelation { fromPreorder :: Relation a }-    deriving (Num, NFData)--instance (Ord a, Show a) => Show (PreorderRelation a) where-    show = show . closure . fromPreorder--instance Ord a => Eq (PreorderRelation a) where-    x == y = closure (fromPreorder x) == closure (fromPreorder y)---- TODO: To be derived automatically using GeneralizedNewtypeDeriving in GHC 8.2-instance Ord a => Graph (PreorderRelation a) where-    type Vertex (PreorderRelation a) = a-    empty       = PreorderRelation empty-    vertex      = PreorderRelation . vertex-    overlay x y = PreorderRelation $ fromPreorder x `overlay` fromPreorder y-    connect x y = PreorderRelation $ fromPreorder x `connect` fromPreorder y--instance Ord a => Reflexive  (PreorderRelation a)-instance Ord a => Transitive (PreorderRelation a)-instance Ord a => Preorder   (PreorderRelation a)-
src/Algebra/Graph/Relation/Preorder.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation.Preorder--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,10 +12,57 @@ module Algebra.Graph.Relation.Preorder (     -- * Data structure     PreorderRelation, fromRelation, toRelation-  ) where+    ) where +import Control.DeepSeq import Algebra.Graph.Relation-import Algebra.Graph.Relation.InternalDerived++import qualified Algebra.Graph.Class as C++-- TODO: Optimise the implementation by caching the results of preorder closure.+{-| The 'PreorderRelation' data type represents a+/binary relation that is both reflexive and transitive/. Preorders satisfy all+laws of the 'Preorder' type class and, in particular, the /self-loop/ axiom:++@'vertex' x == 'vertex' x * 'vertex' x@++and the /closure/ axiom:++@y /= 'empty' ==> x * y + x * z + y * z == x * y + y * z@++For example, the following holds:++@'path' xs == ('clique' xs :: PreorderRelation Int)@++The 'Show' instance produces reflexively and transitively closed expressions:++@show (1             :: PreorderRelation Int) == "edge 1 1"+show (1 * 2         :: PreorderRelation Int) == "edges [(1,1),(1,2),(2,2)]"+show (1 * 2 + 2 * 3 :: PreorderRelation Int) == "edges [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]"@+-}+newtype PreorderRelation a = PreorderRelation { fromPreorder :: Relation a }+    deriving (Num, NFData)++instance (Ord a, Show a) => Show (PreorderRelation a) where+    show = show . toRelation++instance Ord a => Eq (PreorderRelation a) where+    x == y = toRelation x == toRelation y++instance Ord a => Ord (PreorderRelation a) where+    compare x y = compare (toRelation x) (toRelation y)++-- TODO: To be derived automatically using GeneralizedNewtypeDeriving in GHC 8.2+instance Ord a => C.Graph (PreorderRelation a) where+    type Vertex (PreorderRelation a) = a+    empty       = PreorderRelation empty+    vertex      = PreorderRelation . vertex+    overlay x y = PreorderRelation $ fromPreorder x `overlay` fromPreorder y+    connect x y = PreorderRelation $ fromPreorder x `connect` fromPreorder y++instance Ord a => C.Reflexive  (PreorderRelation a)+instance Ord a => C.Transitive (PreorderRelation a)+instance Ord a => C.Preorder   (PreorderRelation a)  -- | Construct a preorder relation from a 'Relation'. -- Complexity: /O(1)/ time.
src/Algebra/Graph/Relation/Reflexive.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation.Reflexive--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,10 +12,44 @@ module Algebra.Graph.Relation.Reflexive (     -- * Data structure     ReflexiveRelation, fromRelation, toRelation-  ) where+    ) where +import Control.DeepSeq import Algebra.Graph.Relation-import Algebra.Graph.Relation.InternalDerived++import qualified Algebra.Graph.Class as C++{-| The 'ReflexiveRelation' data type represents a /reflexive binary relation/+over a set of elements. Reflexive relations satisfy all laws of the+'Reflexive' type class and, in particular, the /self-loop/ axiom:++@'vertex' x == 'vertex' x * 'vertex' x@++The 'Show' instance produces reflexively closed expressions:++@show (1     :: ReflexiveRelation Int) == "edge 1 1"+show (1 * 2 :: ReflexiveRelation Int) == "edges [(1,1),(1,2),(2,2)]"@+-}+newtype ReflexiveRelation a = ReflexiveRelation { fromReflexive :: Relation a }+    deriving (Num, NFData)++instance Ord a => Eq (ReflexiveRelation a) where+    x == y = toRelation x == toRelation y++instance Ord a => Ord (ReflexiveRelation a) where+    compare x y = compare (toRelation x) (toRelation y)++instance (Ord a, Show a) => Show (ReflexiveRelation a) where+    show = show . toRelation++instance Ord a => C.Graph (ReflexiveRelation a) where+    type Vertex (ReflexiveRelation a) = a+    empty       = ReflexiveRelation empty+    vertex      = ReflexiveRelation . vertex+    overlay x y = ReflexiveRelation $ fromReflexive x `overlay` fromReflexive y+    connect x y = ReflexiveRelation $ fromReflexive x `connect` fromReflexive y++instance Ord a => C.Reflexive (ReflexiveRelation a)  -- | Construct a reflexive relation from a 'Relation'. -- Complexity: /O(1)/ time.
src/Algebra/Graph/Relation/Symmetric.hs view
@@ -34,19 +34,101 @@     path, circuit, clique, biclique, star, stars, tree, forest,      -- * Graph transformation-    removeVertex, removeEdge, replaceVertex, mergeVertices, gmap, induce,-  ) where+    removeVertex, removeEdge, replaceVertex, mergeVertices, gmap, induce, induceJust, -import Algebra.Graph.Relation.Symmetric.Internal+    -- * Miscellaneous+    consistent++    ) where++import Control.DeepSeq+import Data.Coerce import Data.Set (Set) import Data.Tree-import Data.Tuple  import qualified Data.Set as Set -import qualified Algebra.Graph.Relation          as R-import qualified Algebra.Graph.Relation.Internal as RI+import qualified Algebra.Graph.Relation as R +{-| This data type represents a /symmetric binary relation/ over a set of+elements of type @a@. Symmetric relations satisfy all laws of the+'Algebra.Graph.Class.Undirected' type class, including the commutativity of+'connect':++@'connect' x y == 'connect' y x@++The 'Show' instance lists edge vertices in non-decreasing order:++@show (empty     :: Relation Int) == "empty"+show (1         :: Relation Int) == "vertex 1"+show (1 + 2     :: Relation Int) == "vertices [1,2]"+show (1 * 2     :: Relation Int) == "edge 1 2"+show (2 * 1     :: Relation Int) == "edge 1 2"+show (1 * 2 * 1 :: Relation Int) == "edges [(1,1),(1,2)]"+show (3 * 2 * 1 :: Relation Int) == "edges [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: Relation Int) == "overlay (vertex 3) (edge 1 2)"@++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 2 1 < 'edge' 1 3@++@'edge' 1 2 == 'edge' 2 1@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x     <= x + y+x + y <= x * y@+-}+newtype Relation a = SR {+    -- | Extract the underlying symmetric "Algebra.Graph.Relation".+    -- Complexity: /O(1)/ time and memory.+    --+    -- @+    -- fromSymmetric ('edge' 1 2)    == 'R.edges' [(1,2), (2,1)]+    -- 'R.vertexCount' . fromSymmetric == 'vertexCount'+    -- 'R.edgeCount'   . fromSymmetric <= (*2) . 'edgeCount'+    -- @+    fromSymmetric :: R.Relation a+    } deriving (Eq, NFData)++instance (Ord a, Show a) => Show (Relation a) where+    show = show . toRelation+      where+        toRelation r = R.vertices (vertexList r) `R.overlay` R.edges (edgeList r)++instance Ord a => Ord (Relation a) where+    compare x y = mconcat+        [ compare (vertexCount x) (vertexCount  y)+        , compare (vertexSet   x) (vertexSet    y)+        , compare (edgeCount   x) (edgeCount    y)+        , compare (edgeSet     x) (edgeSet      y) ]++-- | __Note:__ this does not satisfy the usual ring laws; see 'Relation' for+-- more details.+instance (Ord a, Num a) => Num (Relation a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id+ -- | Construct a symmetric relation from a given "Algebra.Graph.Relation". -- Complexity: /O(m*log(m))/ time. --@@ -60,6 +142,69 @@ toSymmetric :: Ord a => R.Relation a -> Relation a toSymmetric = SR . R.symmetricClosure +-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty'     empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount'   empty == 0+-- @+empty :: Relation a+empty = coerce R.empty++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty'     (vertex x) == False+-- 'hasVertex' x (vertex y) == (x == y)+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount'   (vertex x) == 0+-- @+vertex :: a -> Relation a+vertex = coerce R.vertex++-- | /Overlay/ two graphs. This is a commutative, associative and idempotent+-- operation with the identity 'empty'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'isEmpty'     (overlay x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (overlay x y) >= 'edgeCount' x+-- 'edgeCount'   (overlay x y) <= 'edgeCount' x   + 'edgeCount' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount'   (overlay 1 2) == 0+-- @+overlay :: Ord a => Relation a -> Relation a -> Relation a+overlay = coerce R.overlay++-- | /Connect/ two graphs. This is a commutative and associative operation with+-- the identity 'empty', which distributes over 'overlay' and obeys the+-- decomposition axiom.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the+-- number of edges in the resulting graph is quadratic with respect to the number+-- of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.+--+-- @+-- connect x y               == connect y x+-- 'isEmpty'     (connect x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y) >= 'vertexCount' x+-- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (connect x y) >= 'edgeCount' x+-- 'edgeCount'   (connect x y) >= 'edgeCount' y+-- 'edgeCount'   (connect x y) >= 'vertexCount' x * 'vertexCount' y \`div\` 2+-- 'vertexCount' (connect 1 2) == 2+-- 'edgeCount'   (connect 1 2) == 1+-- @+connect :: Ord a => Relation a -> Relation a -> Relation a+connect x y = coerce R.connect x y `overlay` biclique (vertexList y) (vertexList x)+ -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory and size. --@@ -73,7 +218,7 @@ -- 'vertexCount' (edge 1 2) == 2 -- @ edge :: Ord a => a -> a -> Relation a-edge x y = SR $ RI.Relation (Set.fromList [x, y]) (Set.fromList [(x,y), (y,x)])+edge x y = SR $ R.edges [(x,y), (y,x)]  -- | Construct the graph comprising a given list of isolated vertices. -- Complexity: /O(L * log(L))/ time and /O(L)/ memory, where /L/ is the length@@ -87,7 +232,7 @@ -- 'vertexSet'   . vertices == Set.'Set.fromList' -- @ vertices :: Ord a => [a] -> Relation a-vertices = SR . R.vertices+vertices = coerce R.vertices  -- TODO: Optimise by avoiding multiple list traversal. -- | Construct the graph from a list of edges.@@ -99,8 +244,7 @@ -- edges [(x,y), (y,x)] == 'edge' x y -- @ edges :: Ord a => [(a, a)] -> Relation a-edges es = SR $ RI.Relation-    (Set.fromList $ uncurry (++) $ unzip es) (Set.fromList (es ++ map swap es))+edges = toSymmetric . R.edges  -- | Overlay a given list of graphs. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -113,7 +257,7 @@ -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: Ord a => [Relation a] -> Relation a-overlays = SR . R.overlays . map fromSymmetric+overlays = coerce R.overlays  -- | Connect a given list of graphs. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -143,7 +287,7 @@ -- isSubgraphOf x y                         ==> x <= y -- @ isSubgraphOf :: Ord a => Relation a -> Relation a -> Bool-isSubgraphOf x y = R.isSubgraphOf (fromSymmetric x) (fromSymmetric y)+isSubgraphOf = coerce R.isSubgraphOf  -- | Check if a relation is empty. -- Complexity: /O(1)/ time.@@ -156,19 +300,18 @@ -- isEmpty ('removeEdge' x y $ 'edge' x y) == False -- @ isEmpty :: Relation a -> Bool-isEmpty = R.isEmpty . fromSymmetric+isEmpty = coerce R.isEmpty  -- | Check if a graph contains a given vertex. -- Complexity: /O(log(n))/ time. -- -- @ -- hasVertex x 'empty'            == False--- hasVertex x ('vertex' x)       == True--- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x ('vertex' y)       == (x == y) -- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Ord a => a -> Relation a -> Bool-hasVertex x = R.hasVertex x . fromSymmetric+hasVertex = coerce R.hasVertex  -- | Check if a graph contains a given edge. -- Complexity: /O(log(n))/ time.@@ -179,10 +322,10 @@ -- hasEdge x y ('edge' x y)       == True -- hasEdge x y ('edge' y x)       == True -- hasEdge x y . 'removeEdge' x y == 'const' False--- hasEdge x y                  == 'elem' (min x y, max x y) . 'edgeList'+-- hasEdge x y                  == 'elem' ('min' x y, 'max' x y) . 'edgeList' -- @ hasEdge :: Ord a => a -> a -> Relation a -> Bool-hasEdge x y = R.hasEdge x y . fromSymmetric+hasEdge = coerce R.hasEdge  -- | The number of vertices in a graph. -- Complexity: /O(1)/ time.@@ -194,7 +337,7 @@ -- vertexCount x \< vertexCount y ==> x \< y -- @ vertexCount :: Relation a -> Int-vertexCount = R.vertexCount . fromSymmetric+vertexCount = coerce R.vertexCount  -- | The number of edges in a graph. -- Complexity: /O(1)/ time.@@ -206,7 +349,7 @@ -- edgeCount            == 'length' . 'edgeList' -- @ edgeCount :: Ord a => Relation a -> Int-edgeCount = length . edgeList+edgeCount = Set.size . edgeSet  -- | The sorted list of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -217,7 +360,7 @@ -- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort' -- @ vertexList :: Relation a -> [a]-vertexList = R.vertexList . fromSymmetric+vertexList = coerce R.vertexList  -- | The sorted list of edges of a graph, where edge vertices appear in the -- non-decreasing order.@@ -229,7 +372,7 @@ -- @ -- edgeList 'empty'          == [] -- edgeList ('vertex' x)     == []--- edgeList ('edge' x y)     == [(min x y, max y x)]+-- edgeList ('edge' x y)     == [('min' x y, 'max' y x)] -- edgeList ('star' 2 [3,1]) == [(1,2), (2,3)] -- @ edgeList :: Ord a => Relation a -> [(a, a)]@@ -244,8 +387,24 @@ -- vertexSet . 'vertices' == Set.'Set.fromList' -- @ vertexSet :: Relation a -> Set a-vertexSet = R.vertexSet . fromSymmetric+vertexSet = coerce R.vertexSet +-- | The set of edges of a given graph, where edge vertices appear in the+-- non-decreasing order.+-- Complexity: /O(m)/ time.+--+-- Note: If you need the set of edges where an edge appears in both directions,+-- use @'R.relation' . 'fromSymmetric'@. The latter is much+-- faster than this function, and takes only /O(1)/ time and memory.+--+-- @+-- edgeSet 'empty'      == Set.'Set.empty'+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('edge' x y) == Set.'Set.singleton' ('min' x y, 'max' x y)+-- @+edgeSet :: Ord a => Relation a -> Set (a, a)+edgeSet = Set.filter (uncurry (<=)) . R.edgeSet . fromSymmetric+ -- | The sorted /adjacency list/ of a graph. -- Complexity: /O(n + m)/ time and /O(m)/ memory. --@@ -257,7 +416,7 @@ -- 'stars' . adjacencyList        == id -- @ adjacencyList :: Eq a => Relation a -> [(a, [a])]-adjacencyList = R.adjacencyList . fromSymmetric+adjacencyList = coerce R.adjacencyList  -- | The /path/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -269,9 +428,7 @@ -- path       == path . 'reverse' -- @ path :: Ord a => [a] -> Relation a-path xs = case xs of []     -> empty-                     [x]    -> vertex x-                     (_:ys) -> edges (zip xs ys)+path = toSymmetric . R.path  -- | The /circuit/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -283,11 +440,11 @@ -- circuit       == circuit . 'reverse' -- @ circuit :: Ord a => [a] -> Relation a-circuit []     = empty-circuit (x:xs) = path $ [x] ++ xs ++ [x]+circuit = toSymmetric . R.circuit +-- TODO: Optimise by avoiding the call to 'R.symmetricClosure'. -- | The /clique/ on a list of vertices.--- Complexity: /O((n + m) * log(n))/ time + /O(m*log(m)) time from computing the symmetricClosure and /O(n + m)/ memory.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. -- -- @ -- clique []         == 'empty'@@ -298,10 +455,11 @@ -- clique            == clique . 'reverse' -- @ clique :: Ord a => [a] -> Relation a-clique = SR . R.symmetricClosure . R.clique+clique = toSymmetric . R.clique +-- TODO: Optimise by avoiding the call to 'R.symmetricClosure'. -- | The /biclique/ on two lists of vertices.--- Complexity: /O(n * log(n) + m)/ time + /O(m*log(m)) time from computing the symmetricClosure and /O(n + m)/ memory.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. -- -- @ -- biclique []      []      == 'empty'@@ -311,7 +469,7 @@ -- biclique xs      ys      == 'connect' ('vertices' xs) ('vertices' ys) -- @ biclique :: Ord a => [a] -> [a] -> Relation a-biclique xs = SR . R.symmetricClosure . R.biclique xs+biclique xs ys = toSymmetric (R.biclique xs ys)  -- TODO: Optimise. -- | The /star/ formed by a centre vertex connected to a list of leaves.@@ -324,8 +482,7 @@ -- star x ys    == 'connect' ('vertex' x) ('vertices' ys) -- @ star :: Ord a => a -> [a] -> Relation a-star x [] = vertex x-star x ys = connect (vertex x) (vertices ys)+star x = toSymmetric . R.star x  -- | The /stars/ formed by overlaying a list of 'star's. An inverse of -- 'adjacencyList'.@@ -342,10 +499,7 @@ -- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys) -- @ stars :: Ord a => [(a, [a])] -> Relation a-stars as = SR $ RI.Relation (Set.fromList vs) (Set.fromList es)-  where-    vs = concatMap (uncurry (:)) as-    es = [ (x, y) | (x, ys) <- as, y <- ys ] ++ [ (y, x) | (x, ys) <- as, y <- ys ]+stars = toSymmetric . R.stars  -- | The /tree graph/ constructed from a given 'Tree.Tree' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -357,9 +511,7 @@ -- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)] -- @ tree :: Ord a => Tree a -> Relation a-tree (Node x []) = vertex x-tree (Node x f ) = star x (map rootLabel f)-    `overlay` forest (filter (not . null . subForest) f)+tree = toSymmetric . R.tree  -- | The /forest graph/ constructed from a given 'Tree.Forest' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -371,7 +523,7 @@ -- forest                                                     == 'overlays' . 'map' 'tree' -- @ forest :: Ord a => Forest a -> Relation a-forest = overlays . map tree+forest = toSymmetric . R.forest  -- | Remove a vertex from a given graph. -- Complexity: /O(n + m)/ time.@@ -384,7 +536,7 @@ -- removeVertex x . removeVertex x == removeVertex x -- @ removeVertex :: Ord a => a -> Relation a -> Relation a-removeVertex x = SR . R.removeVertex x . fromSymmetric+removeVertex = coerce R.removeVertex  -- | Remove an edge from a given graph. -- Complexity: /O(log(m))/ time.@@ -398,9 +550,7 @@ -- removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2 -- @ removeEdge :: Ord a => a -> a -> Relation a -> Relation a-removeEdge x y r = SR $ RI.Relation d (Set.delete (y, x) $ Set.delete (x, y) rr)-  where-    RI.Relation d rr = fromSymmetric r+removeEdge x y = SR . R.removeEdge x y . R.removeEdge y x . fromSymmetric  -- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a -- given 'Relation'. If @y@ already exists, @x@ and @y@ will be merged.@@ -412,7 +562,7 @@ -- replaceVertex x y            == 'mergeVertices' (== x) y -- @ replaceVertex :: Ord a => a -> a -> Relation a -> Relation a-replaceVertex u v = gmap $ \w -> if w == u then v else w+replaceVertex = coerce R.replaceVertex  -- | Merge vertices satisfying a given predicate into a given vertex. -- Complexity: /O((n + m) * log(n))/ time, assuming that the predicate takes@@ -425,7 +575,7 @@ -- mergeVertices 'odd'  1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: Ord a => (a -> Bool) -> a -> Relation a -> Relation a-mergeVertices p v = gmap $ \u -> if p u then v else u+mergeVertices = coerce R.mergeVertices  -- | Transform a graph by applying a function to each of its vertices. This is -- similar to @Functor@'s 'fmap' but can be used with non-fully-parametric@@ -440,11 +590,11 @@ -- gmap f . gmap g   == gmap (f . g) -- @ gmap :: Ord b => (a -> b) -> Relation a -> Relation b-gmap f = SR . R.gmap f . fromSymmetric+gmap = coerce R.gmap  -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.--- Complexity: /O(m)/ time, assuming that the predicate takes /O(1)/ to+-- Complexity: /O(n + m)/ time, assuming that the predicate takes /O(1)/ to -- be evaluated. -- -- @@@ -455,17 +605,47 @@ -- 'isSubgraphOf' (induce p x) x == True -- @ induce :: (a -> Bool) -> Relation a -> Relation a-induce p = SR . R.induce p . fromSymmetric+induce = coerce R.induce +-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(n + m)/ time.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'gmap' 'Just'                                    == 'id'+-- induceJust . 'gmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Ord a => Relation (Maybe a) -> Relation a+induceJust = coerce R.induceJust+ -- | The set of /neighbours/ of an element @x@ is the set of elements that are -- related to it, i.e. @neighbours x == { a | aRx }@. In the context of undirected -- graphs, this corresponds to the set of /adjacent/ vertices of vertex @x@. -- -- @--- neighbours x 'Algebra.Graph.Class.empty'      == Set.'Set.empty'--- neighbours x ('Algebra.Graph.Class.vertex' x) == Set.'Set.empty'--- neighbours x ('Algebra.Graph.Class.edge' x y) == Set.'Set.fromList' [y]--- neighbours y ('Algebra.Graph.Class.edge' x y) == Set.'Set.fromList' [x]+-- neighbours x 'empty'      == Set.'Set.empty'+-- neighbours x ('vertex' x) == Set.'Set.empty'+-- neighbours x ('edge' x y) == Set.'Set.fromList' [y]+-- neighbours y ('edge' x y) == Set.'Set.fromList' [x] -- @ neighbours :: Ord a => a -> Relation a -> Set a-neighbours x = R.postSet x . fromSymmetric+neighbours = coerce R.postSet++-- | Check that the internal representation of a symmetric relation is+-- consistent, i.e. that (i) that all edges refer to existing vertices, and (ii)+-- all edges have their symmetric counterparts. It should be impossible to+-- create an inconsistent 'Relation', and we use this function in testing.+--+-- @+-- consistent 'empty'         == True+-- consistent ('vertex' x)    == True+-- consistent ('overlay' x y) == True+-- consistent ('connect' x y) == True+-- consistent ('edge' x y)    == True+-- consistent ('edges' xs)    == True+-- consistent ('stars' xs)    == True+-- @+consistent :: Ord a => Relation a -> Bool+consistent (SR r) = R.consistent r && r == R.transpose r
− src/Algebra/Graph/Relation/Symmetric/Internal.hs
@@ -1,215 +0,0 @@--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Relation.Symmetric.Internal--- Copyright  : (c) Andrey Mokhov 2016-2019--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : unstable------ This module exposes the implementation of symmetric binary relation data type.--- The API is unstable and unsafe, and is exposed only for documentation. You--- should use the non-internal module "Algebra.Graph.Relation.Symmetric" instead.--------------------------------------------------------------------------------module Algebra.Graph.Relation.Symmetric.Internal (-    -- * Implementation of symmetric binary relations-    Relation (..), fromSymmetric, empty, vertex, overlay, connect, edgeSet,-    consistent-  ) where--import Algebra.Graph.Internal-import Control.DeepSeq-import Data.Monoid (mconcat)-import Data.Set (Set)--import qualified Data.Set as Set--import qualified Algebra.Graph.Relation.Internal as RI-import qualified Algebra.Graph.Relation          as R--{-| This data type represents a /symmetric binary relation/ over a set of-elements of type @a@. Symmetric relations satisfy all laws of the-'Algebra.Graph.Class.Undirected' type class, including the commutativity of-'connect':--@'connect' x y == 'connect' y x@--The 'Show' instance lists edge vertices in non-decreasing order:--@show (empty     :: Relation Int) == "empty"-show (1         :: Relation Int) == "vertex 1"-show (1 + 2     :: Relation Int) == "vertices [1,2]"-show (1 * 2     :: Relation Int) == "edge 1 2"-show (2 * 1     :: Relation Int) == "edge 1 2"-show (1 * 2 * 1 :: Relation Int) == "edges [(1,1),(1,2)]"-show (3 * 2 * 1 :: Relation Int) == "edges [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: Relation Int) == "overlay (vertex 3) (edge 1 2)"@--The total order on graphs is defined using /size-lexicographic/ comparison:--* Compare the number of vertices. In case of a tie, continue.-* Compare the sets of vertices. In case of a tie, continue.-* Compare the number of edges. In case of a tie, continue.-* Compare the sets of edges.--Here are a few examples:--@'vertex' 1 < 'vertex' 2-'vertex' 3 < 'Algebra.Graph.Relation.Symmetric.edge' 1 2-'vertex' 1 < 'Algebra.Graph.Relation.Symmetric.edge' 1 1-'Algebra.Graph.Relation.Symmetric.edge' 1 1 < 'Algebra.Graph.Relation.Symmetric.edge' 1 2-'Algebra.Graph.Relation.Symmetric.edge' 1 2 < 'Algebra.Graph.Relation.Symmetric.edge' 1 1 + 'Algebra.Graph.Relation.Symmetric.edge' 2 2-'Algebra.Graph.Relation.Symmetric.edge' 2 1 < 'Algebra.Graph.Relation.Symmetric.edge' 1 3@--@'Algebra.Graph.Relation.Symmetric.edge' 1 2 == 'Algebra.Graph.Relation.Symmetric.edge' 2 1@--Note that the resulting order refines the-'Algebra.Graph.Relation.Symmetric.isSubgraphOf' relation and is compatible with-'overlay' and 'connect' operations:--@'Algebra.Graph.Relation.Symmetric.isSubgraphOf' x y ==> x <= y@--@'empty' <= x-x     <= x + y-x + y <= x * y@--}-newtype Relation a = SR (RI.Relation a) deriving NFData--instance Ord a => Eq (Relation a) where-    x == y = fromSymmetric x == fromSymmetric y--instance (Ord a, Show a) => Show (Relation a) where-    show r@(SR (RI.Relation d _)) = show (RI.Relation d $ edgeSet r)--instance Ord a => Ord (Relation a) where-    compare rx@(SR (RI.Relation vx _)) ry@(SR (RI.Relation vy _)) = mconcat-        [ compare (Set.size vx) (Set.size vy)-        , compare vx            vy-        , compare (Set.size ex) (Set.size ey)-        , compare ex            ey ]-      where-        ex = edgeSet rx-        ey = edgeSet ry--instance (Ord a, Num a) => Num (Relation a) where-    fromInteger = vertex . fromInteger-    (+)         = overlay-    (*)         = connect-    signum      = const empty-    abs         = id-    negate      = id---- | Extract the underlying symmetric "Algebra.Graph.Relation".--- Complexity: /O(1)/ time and memory.------ @--- fromSymmetric ('Algebra.Graph.Relation.Symmetric.edge' 1 2)    == 'Algebra.Graph.Relation.edges' [(1,2), (2,1)]--- 'Algebra.Graph.Relation.vertexCount' . fromSymmetric == 'Algebra.Graph.Relation.Symmetric.vertexCount'--- 'Algebra.Graph.Relation.edgeCount'   . fromSymmetric <= (*2) . 'Algebra.Graph.Relation.Symmetric.edgeCount'--- @-fromSymmetric :: Relation a -> RI.Relation a-fromSymmetric (SR x) = x---- | Construct the /empty graph/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.Relation.Symmetric.isEmpty'     empty == True--- 'Algebra.Graph.Relation.Symmetric.hasVertex' x empty == False--- 'Algebra.Graph.Relation.Symmetric.vertexCount' empty == 0--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   empty == 0--- @-empty :: Relation a-empty = SR $ RI.Relation Set.empty Set.empty---- | Construct the graph comprising /a single isolated vertex/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.Relation.Symmetric.isEmpty'     (vertex x) == False--- 'Algebra.Graph.Relation.Symmetric.hasVertex' x (vertex x) == True--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (vertex x) == 1--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (vertex x) == 0--- @-vertex :: a -> Relation a-vertex x = SR $ RI.Relation (Set.singleton x) Set.empty---- | /Overlay/ two graphs. This is a commutative, associative and idempotent--- operation with the identity 'empty'.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- 'Algebra.Graph.Relation.Symmetric.isEmpty'     (overlay x y) == 'Algebra.Graph.Relation.Symmetric.isEmpty'   x   && 'Algebra.Graph.Relation.Symmetric.isEmpty'   y--- 'Algebra.Graph.Relation.Symmetric.hasVertex' z (overlay x y) == 'Algebra.Graph.Relation.Symmetric.hasVertex' z x || 'Algebra.Graph.Relation.Symmetric.hasVertex' z y--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (overlay x y) >= 'Algebra.Graph.Relation.Symmetric.vertexCount' x--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (overlay x y) <= 'Algebra.Graph.Relation.Symmetric.vertexCount' x + 'Algebra.Graph.Relation.Symmetric.vertexCount' y--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (overlay x y) >= 'Algebra.Graph.Relation.Symmetric.edgeCount' x--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (overlay x y) <= 'Algebra.Graph.Relation.Symmetric.edgeCount' x   + 'Algebra.Graph.Relation.Symmetric.edgeCount' y--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (overlay 1 2) == 2--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (overlay 1 2) == 0--- @-overlay :: Ord a => Relation a -> Relation a -> Relation a-overlay (SR x) (SR y) = SR $ RI.Relation (R.domain   x `Set.union` R.domain   y)-                                         (R.relation x `Set.union` R.relation y)---- | /Connect/ two graphs. This is a commutative and associative operation with--- the identity 'empty', which distributes over 'overlay' and obeys the--- decomposition axiom.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the--- number of edges in the resulting graph is quadratic with respect to the number--- of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.------ @--- connect x y               == connect y x--- 'Algebra.Graph.Relation.Symmetric.isEmpty'     (connect x y) == 'Algebra.Graph.Relation.Symmetric.isEmpty'   x   && 'Algebra.Graph.Relation.Symmetric.isEmpty'   y--- 'Algebra.Graph.Relation.Symmetric.hasVertex' z (connect x y) == 'Algebra.Graph.Relation.Symmetric.hasVertex' z x || 'Algebra.Graph.Relation.Symmetric.hasVertex' z y--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (connect x y) >= 'Algebra.Graph.Relation.Symmetric.vertexCount' x--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (connect x y) <= 'Algebra.Graph.Relation.Symmetric.vertexCount' x + 'Algebra.Graph.Relation.Symmetric.vertexCount' y--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.Symmetric.edgeCount' x--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.Symmetric.edgeCount' y--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.Symmetric.vertexCount' x * 'Algebra.Graph.Relation.Symmetric.vertexCount' y \`div\` 2--- 'Algebra.Graph.Relation.Symmetric.vertexCount' (connect 1 2) == 2--- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect 1 2) == 1--- @-connect :: Ord a => Relation a -> Relation a -> Relation a-connect (SR x) (SR y) = SR $ RI.Relation (R.domain x `Set.union` R.domain y)-    (Set.unions [R.relation x, R.relation y, R.domain x `setProduct` R.domain y-                                           , R.domain y `setProduct` R.domain x ])---- | The set of edges of a given graph, where edge vertices appear in the--- non-decreasing order.--- Complexity: /O(m)/ time.------ Note: If you need the set of edges where an edge appears in both directions,--- use @'Algebra.Graph.Relation.relation' . 'fromSymmetric'@. The latter is much--- faster than this function, and takes only /O(1)/ time and memory.------ @--- edgeSet 'empty'      == Set.'Set.empty'--- edgeSet ('vertex' x) == Set.'Set.empty'--- edgeSet ('Algebra.Graph.Relation.Symmetric.edge' x y) == Set.'Set.singleton' (min x y, max x y)--- @-edgeSet :: Ord a => Relation a -> Set (a, a)-edgeSet (SR (RI.Relation _ r)) = Set.filter (uncurry (<=)) r---- | Check if the internal representation of a symmetric relation is consistent,--- i.e. if (i) all pairs of elements in the 'RI.relation' refer to existing--- elements in the 'RI.domain', and (ii) all edges have their symmetric--- counterparts. It should be impossible to create an inconsistent 'Relation',--- and we use this function in testing.--- /Note: this function is for internal use only/.------ @--- consistent 'Algebra.Graph.Relation.Symmetric.empty'         == True--- consistent ('Algebra.Graph.Relation.Symmetric.vertex' x)    == True--- consistent ('Algebra.Graph.Relation.Symmetric.overlay' x y) == True--- consistent ('Algebra.Graph.Relation.Symmetric.connect' x y) == True--- consistent ('Algebra.Graph.Relation.Symmetric.edge' x y)    == True--- consistent ('Algebra.Graph.Relation.Symmetric.edges' xs)    == True--- consistent ('Algebra.Graph.Relation.Symmetric.stars' xs)    == True--- @-consistent :: Ord a => Relation a -> Bool-consistent (SR r) =-    RI.referredToVertexSet (R.relation r) `Set.isSubsetOf` R.domain r-    &&-    r == R.transpose r
src/Algebra/Graph/Relation/Transitive.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation.Transitive--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,10 +12,50 @@ module Algebra.Graph.Relation.Transitive (     -- * Data structure     TransitiveRelation, fromRelation, toRelation-  ) where+    ) where +import Control.DeepSeq import Algebra.Graph.Relation-import Algebra.Graph.Relation.InternalDerived++import qualified Algebra.Graph.Class as C++-- TODO: Optimise the implementation by caching the results of transitive closure.+{-| The 'TransitiveRelation' data type represents a /transitive binary relation/+over a set of elements. Transitive relations satisfy all laws of the+'Transitive' type class and, in particular, the /closure/ axiom:++@y /= 'empty' ==> x * y + x * z + y * z == x * y + y * z@++For example, the following holds:++@'path' xs == ('clique' xs :: TransitiveRelation Int)@++The 'Show' instance produces transitively closed expressions:++@show (1 * 2         :: TransitiveRelation Int) == "edge 1 2"+show (1 * 2 + 2 * 3 :: TransitiveRelation Int) == "edges [(1,2),(1,3),(2,3)]"@+-}+newtype TransitiveRelation a = TransitiveRelation { fromTransitive :: Relation a }+    deriving (Num, NFData)++instance Ord a => Eq (TransitiveRelation a) where+    x == y = toRelation x == toRelation y++instance Ord a => Ord (TransitiveRelation a) where+    compare x y = compare (toRelation x) (toRelation y)++instance (Ord a, Show a) => Show (TransitiveRelation a) where+    show = show . toRelation++-- TODO: To be derived automatically using GeneralizedNewtypeDeriving in GHC 8.2+instance Ord a => C.Graph (TransitiveRelation a) where+    type Vertex (TransitiveRelation a) = a+    empty       = TransitiveRelation empty+    vertex      = TransitiveRelation . vertex+    overlay x y = TransitiveRelation $ fromTransitive x `overlay` fromTransitive y+    connect x y = TransitiveRelation $ fromTransitive x `connect` fromTransitive y++instance Ord a => C.Transitive (TransitiveRelation a)  -- | Construct a transitive relation from a 'Relation'. -- Complexity: /O(1)/ time.
src/Algebra/Graph/ToGraph.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.ToGraph--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -41,10 +41,14 @@ -- of 'foldMap' and 'Data.Foldable.toList' violate this requirement, for example -- @[1] ++ [1] /= [1]@, and are therefore disallowed. ------------------------------------------------------------------------------module Algebra.Graph.ToGraph (ToGraph (..)) where+module Algebra.Graph.ToGraph (+    -- * Type class+    ToGraph (..), -import Prelude ()-import Prelude.Compat+    -- * Derived functions+    adjacencyMap, adjacencyIntMap, adjacencyMapTranspose, adjacencyIntMapTranspose+    ) where+ import Data.IntMap (IntMap) import Data.IntSet (IntSet) import Data.Map    (Map)@@ -54,14 +58,11 @@ import qualified Algebra.Graph                                as G import qualified Algebra.Graph.AdjacencyMap                   as AM import qualified Algebra.Graph.AdjacencyMap.Algorithm         as AM-import qualified Algebra.Graph.AdjacencyMap.Internal          as AM import qualified Algebra.Graph.Labelled                       as LG import qualified Algebra.Graph.Labelled.AdjacencyMap          as LAM import qualified Algebra.Graph.NonEmpty.AdjacencyMap          as NAM-import qualified Algebra.Graph.NonEmpty.AdjacencyMap.Internal as NAM import qualified Algebra.Graph.AdjacencyIntMap                as AIM import qualified Algebra.Graph.AdjacencyIntMap.Algorithm      as AIM-import qualified Algebra.Graph.AdjacencyIntMap.Internal       as AIM import qualified Algebra.Graph.Relation                       as R import qualified Algebra.Graph.Relation.Symmetric             as SR import qualified Data.IntMap                                  as IntMap@@ -104,19 +105,6 @@     isEmpty :: t -> Bool     isEmpty = foldg True (const False) (&&) (&&) -    -- | The /size/ of a graph, i.e. the number of leaves of the expression-    -- including 'empty' leaves.-    ---    -- __Note:__ The default implementation of this function violates the-    -- requirement that the four arguments of 'foldg' should satisfy the laws-    -- of algebraic graphs, since @1 + 1 /= 1@. Use this function with care.-    ---    -- @-    -- size == 'foldg' 1 ('const' 1) (+) (+)-    -- @-    size :: t -> Int-    size = foldg 1 (const 1) (+) (+)-     -- | Check if a graph contains a given vertex.     --     -- @@@ -234,44 +222,6 @@     adjacencyList :: Ord (ToVertex t) => t -> [(ToVertex t, [ToVertex t])]     adjacencyList = AM.adjacencyList . toAdjacencyMap -    -- | The /adjacency map/ of a graph: each vertex is associated with a set-    -- of its /direct successors/.-    ---    -- @-    -- adjacencyMap == Algebra.Graph.AdjacencyMap.'Algebra.Graph.AdjacencyMap.adjacencyMap' . 'toAdjacencyMap'-    -- @-    adjacencyMap :: Ord (ToVertex t) => t -> Map (ToVertex t) (Set (ToVertex t))-    adjacencyMap = AM.adjacencyMap . toAdjacencyMap--    -- | The /adjacency map/ of a graph: each vertex is associated with a set-    -- of its /direct successors/. Like 'adjacencyMap' but specialised for-    -- graphs with vertices of type 'Int'.-    ---    -- @-    -- adjacencyIntMap == Algebra.Graph.AdjacencyIntMap.'Algebra.Graph.AdjacencyIntMap.adjacencyIntMap' . 'toAdjacencyIntMap'-    -- @-    adjacencyIntMap :: ToVertex t ~ Int => t -> IntMap IntSet-    adjacencyIntMap = AIM.adjacencyIntMap . toAdjacencyIntMap--    -- | The transposed /adjacency map/ of a graph: each vertex is associated-    -- with a set of its /direct predecessors/.-    ---    -- @-    -- adjacencyMapTranspose == Algebra.Graph.AdjacencyMap.'Algebra.Graph.AdjacencyMap.adjacencyMap' . 'toAdjacencyMapTranspose'-    -- @-    adjacencyMapTranspose :: Ord (ToVertex t) => t -> Map (ToVertex t) (Set (ToVertex t))-    adjacencyMapTranspose = AM.adjacencyMap . toAdjacencyMapTranspose--    -- | The transposed /adjacency map/ of a graph: each vertex is associated-    -- with a set of its /direct predecessors/. Like 'adjacencyMapTranspose' but-    -- specialised for graphs with vertices of type 'Int'.-    ---    -- @-    -- adjacencyIntMapTranspose == Algebra.Graph.AdjacencyIntMap.'Algebra.Graph.AdjacencyIntMap.adjacencyIntMap' . 'toAdjacencyIntMapTranspose'-    -- @-    adjacencyIntMapTranspose :: ToVertex t ~ Int => t -> IntMap IntSet-    adjacencyIntMapTranspose = AIM.adjacencyIntMap . toAdjacencyIntMapTranspose-     -- | Compute the /depth-first search/ forest of a graph that corresponds to     -- searching from each of the graph vertices in the 'Ord' @a@ order.     --@@ -310,13 +260,13 @@     reachable :: Ord (ToVertex t) => ToVertex t -> t -> [ToVertex t]     reachable x = AM.reachable x . toAdjacencyMap -    -- | Compute the /topological sort/ of a graph or return @Nothing@ if the+    -- | Compute the /topological sort/ of a graph or a @AM.Cycle@ if the     -- graph is cyclic.     --     -- @     -- topSort == Algebra.Graph.AdjacencyMap.'AM.topSort' . toAdjacencyMap     -- @-    topSort :: Ord (ToVertex t) => t -> Maybe [ToVertex t]+    topSort :: Ord (ToVertex t) => t -> Either (AM.Cycle (ToVertex t)) [ToVertex t]     topSort = AM.topSort . toAdjacencyMap      -- | Check if a given graph is /acyclic/.@@ -405,11 +355,6 @@     adjacencyList              = AM.adjacencyList     preSet                     = AM.preSet     postSet                    = AM.postSet-    adjacencyMap               = AM.adjacencyMap-    adjacencyIntMap            = IntMap.fromAscList-                               . map (fmap $ IntSet.fromAscList . Set.toAscList)-                               . Map.toAscList-                               . AM.adjacencyMap     dfsForest                  = AM.dfsForest     dfsForestFrom              = AM.dfsForestFrom     dfs                        = AM.dfs@@ -417,7 +362,7 @@     topSort                    = AM.topSort     isAcyclic                  = AM.isAcyclic     toAdjacencyMap             = id-    toAdjacencyIntMap          = AIM.AM . adjacencyIntMap+    toAdjacencyIntMap          = AIM.fromAdjacencyMap     toAdjacencyMapTranspose    = AM.transpose . toAdjacencyMap     toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap     isDfsForestOf              = AM.isDfsForestOf@@ -442,18 +387,13 @@     adjacencyList              = AIM.adjacencyList     preIntSet                  = AIM.preIntSet     postIntSet                 = AIM.postIntSet-    adjacencyMap               = Map.fromAscList-                               . map (fmap $ Set.fromAscList . IntSet.toAscList)-                               . IntMap.toAscList-                               . AIM.adjacencyIntMap     dfsForest                  = AIM.dfsForest     dfsForestFrom              = AIM.dfsForestFrom     dfs                        = AIM.dfs     reachable                  = AIM.reachable     topSort                    = AIM.topSort     isAcyclic                  = AIM.isAcyclic-    adjacencyIntMap            = AIM.adjacencyIntMap-    toAdjacencyMap             = AM.AM . adjacencyMap+    toAdjacencyMap             = AM.stars . AIM.adjacencyList     toAdjacencyIntMap          = id     toAdjacencyMapTranspose    = AM.transpose . toAdjacencyMap     toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap@@ -499,34 +439,32 @@ -- | See "Algebra.Graph.NonEmpty.AdjacencyMap". instance Ord a => ToGraph (NAM.AdjacencyMap a) where     type ToVertex (NAM.AdjacencyMap a) = a-    toGraph                    = toGraph . NAM.am+    toGraph                    = toGraph . toAdjacencyMap     isEmpty _                  = False     hasVertex                  = NAM.hasVertex     hasEdge                    = NAM.hasEdge     vertexCount                = NAM.vertexCount     edgeCount                  = NAM.edgeCount-    vertexList                 = vertexList . NAM.am+    vertexList                 = vertexList . toAdjacencyMap     vertexSet                  = NAM.vertexSet-    vertexIntSet               = vertexIntSet . NAM.am+    vertexIntSet               = vertexIntSet . toAdjacencyMap     edgeList                   = NAM.edgeList     edgeSet                    = NAM.edgeSet-    adjacencyList              = adjacencyList . NAM.am+    adjacencyList              = adjacencyList . toAdjacencyMap     preSet                     = NAM.preSet     postSet                    = NAM.postSet-    adjacencyMap               = adjacencyMap . NAM.am-    adjacencyIntMap            = adjacencyIntMap . NAM.am-    dfsForest                  = dfsForest . NAM.am-    dfsForestFrom xs           = dfsForestFrom xs . NAM.am-    dfs xs                     = dfs xs . NAM.am-    reachable x                = reachable x . NAM.am-    topSort                    = topSort . NAM.am-    isAcyclic                  = isAcyclic . NAM.am-    toAdjacencyMap             = NAM.am-    toAdjacencyIntMap          = toAdjacencyIntMap . NAM.am-    toAdjacencyMapTranspose    = NAM.am . NAM.transpose+    dfsForest                  = dfsForest . toAdjacencyMap+    dfsForestFrom xs           = dfsForestFrom xs . toAdjacencyMap+    dfs xs                     = dfs xs . toAdjacencyMap+    reachable x                = reachable x . toAdjacencyMap+    topSort                    = topSort . toAdjacencyMap+    isAcyclic                  = isAcyclic . toAdjacencyMap+    toAdjacencyMap             = NAM.fromNonEmpty+    toAdjacencyIntMap          = toAdjacencyIntMap . toAdjacencyMap+    toAdjacencyMapTranspose    = toAdjacencyMap . NAM.transpose     toAdjacencyIntMapTranspose = toAdjacencyIntMap . NAM.transpose-    isDfsForestOf f            = isDfsForestOf f . NAM.am-    isTopSortOf x              = isTopSortOf x . NAM.am+    isDfsForestOf f            = isDfsForestOf f . toAdjacencyMap+    isTopSortOf x              = isTopSortOf x . toAdjacencyMap  -- TODO: Get rid of "Relation.Internal" and move this instance to "Relation". -- | See "Algebra.Graph.Relation".@@ -545,14 +483,8 @@     edgeList                   = R.edgeList     edgeSet                    = R.edgeSet     adjacencyList              = R.adjacencyList-    adjacencyMap               = Map.fromAscList-                               . map (fmap Set.fromAscList)-                               . R.adjacencyList-    adjacencyIntMap            = IntMap.fromAscList-                               . map (fmap IntSet.fromAscList)-                               . R.adjacencyList-    toAdjacencyMap             = AM.AM . adjacencyMap-    toAdjacencyIntMap          = AIM.AM . adjacencyIntMap+    toAdjacencyMap             = AM.stars . R.adjacencyList+    toAdjacencyIntMap          = AIM.stars . R.adjacencyList     toAdjacencyMapTranspose    = AM.transpose . toAdjacencyMap     toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap @@ -574,9 +506,45 @@     edgeList                   = SR.edgeList     edgeSet                    = SR.edgeSet     adjacencyList              = SR.adjacencyList-    adjacencyMap               = adjacencyMap . SR.fromSymmetric-    adjacencyIntMap            = adjacencyIntMap . SR.fromSymmetric-    toAdjacencyMap             = AM.AM . adjacencyMap-    toAdjacencyIntMap          = AIM.AM . adjacencyIntMap+    toAdjacencyMap             = toAdjacencyMap . SR.fromSymmetric+    toAdjacencyIntMap          = toAdjacencyIntMap . SR.fromSymmetric     toAdjacencyMapTranspose    = toAdjacencyMap     toAdjacencyIntMapTranspose = toAdjacencyIntMap++-- | The /adjacency map/ of a graph: each vertex is associated with a set of its+-- /direct successors/.+--+-- @+-- adjacencyMap == Algebra.Graph.AdjacencyMap.'Algebra.Graph.AdjacencyMap.adjacencyMap' . 'toAdjacencyMap'+-- @+adjacencyMap :: ToGraph t => Ord (ToVertex t) => t -> Map (ToVertex t) (Set (ToVertex t))+adjacencyMap = AM.adjacencyMap . toAdjacencyMap++-- | The /adjacency map/ of a graph: each vertex is associated with a set of its+-- /direct successors/. Like 'adjacencyMap' but specialised for graphs with+-- vertices of type 'Int'.+--+-- @+-- adjacencyIntMap == Algebra.Graph.AdjacencyIntMap.'Algebra.Graph.AdjacencyIntMap.adjacencyIntMap' . 'toAdjacencyIntMap'+-- @+adjacencyIntMap :: (ToGraph t, ToVertex t ~ Int) => t -> IntMap IntSet+adjacencyIntMap = AIM.adjacencyIntMap . toAdjacencyIntMap++-- | The transposed /adjacency map/ of a graph: each vertex is associated with a+-- set of its /direct predecessors/.+--+-- @+-- adjacencyMapTranspose == Algebra.Graph.AdjacencyMap.'Algebra.Graph.AdjacencyMap.adjacencyMap' . 'toAdjacencyMapTranspose'+-- @+adjacencyMapTranspose :: (ToGraph t, Ord (ToVertex t)) => t -> Map (ToVertex t) (Set (ToVertex t))+adjacencyMapTranspose = AM.adjacencyMap . toAdjacencyMapTranspose++-- | The transposed /adjacency map/ of a graph: each vertex is associated with a+-- set of its /direct predecessors/. Like 'adjacencyMapTranspose' but+-- specialised for graphs with vertices of type 'Int'.+--+-- @+-- adjacencyIntMapTranspose == Algebra.Graph.AdjacencyIntMap.'Algebra.Graph.AdjacencyIntMap.adjacencyIntMap' . 'toAdjacencyIntMapTranspose'+-- @+adjacencyIntMapTranspose :: (ToGraph t, ToVertex t ~ Int) => t -> IntMap IntSet+adjacencyIntMapTranspose = AIM.adjacencyIntMap . toAdjacencyIntMapTranspose
+ src/Algebra/Graph/Undirected.hs view
@@ -0,0 +1,819 @@+{-# LANGUAGE DeriveGeneric #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Undirected+-- Copyright  : (c) Andrey Mokhov 2016-2020+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module defines an undirected version of algebraic graphs. Undirected+-- graphs satisfy all laws of the 'Algebra.Graph.Class.Undirected' type class,+-- including the commutativity of 'connect'.+--+-- To avoid name clashes with "Algebra.Graph", this module can be imported+-- qualified:+--+-- @+-- import qualified Algebra.Graph.Undirected as Undirected+-- @++-----------------------------------------------------------------------------+module Algebra.Graph.Undirected (+    -- * Algebraic data type for graphs+    Graph, fromUndirected, toUndirected,++    -- * Basic graph construction primitives+    empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,++    -- * Graph folding+    foldg,++    -- * Relations on graphs+    isSubgraphOf, toRelation,++    -- * Graph properties+    isEmpty, size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList,+    edgeList, vertexSet, edgeSet, adjacencyList, neighbours,++    -- * Standard families of graphs+    path, circuit, clique, biclique, star, stars, tree, forest,++    -- * Graph transformation+    removeVertex, removeEdge, replaceVertex, mergeVertices, induce, induceJust,+    complement+    ) where++import Algebra.Graph.Internal+import Algebra.Graph.ToGraph (toGraph)+import Control.Applicative (Alternative)+import Control.DeepSeq+import Control.Monad+import Data.Coerce+import Data.List+import GHC.Generics+import Data.Set (Set)+import Data.Tree (Tree, Forest)++import qualified Algebra.Graph                    as G+import qualified Algebra.Graph.Relation.Symmetric as R+import qualified Data.Set                         as Set++-- TODO: Specialise the API for graphs with vertices of type 'Int'.++{-| The 'Graph' data type provides the four algebraic graph construction+primitives 'empty', 'vertex', 'overlay' and 'connect', as well as various+derived functions. The only difference compared to the 'Algebra.Graph.Graph'+data type defined in "Algebra.Graph" is that the 'connect' operation is+/commutative/. We define a 'Num' instance as a convenient notation for working+with undirected graphs:++    > 0           == vertex 0+    > 1 + 2       == vertices [1,2]+    > 1 * 2       == edge 1 2+    > 1 + 2 * 3   == overlay (vertex 1) (edge 2 3)+    > 1 * (2 + 3) == edges [(1,2),(1,3)]++__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.++The 'Eq' instance is currently implemented using the 'R.Relation' as the+/canonical graph representation/ and satisfies all axioms of algebraic graphs:++    * 'overlay' is commutative and associative:++        >       x + y == y + x+        > x + (y + z) == (x + y) + z++    * 'connect' is associative, commutative and has 'empty' as the identity:++        >   x * empty == x+        >   empty * x == x+        >       x * y == y * x+        > x * (y * z) == (x * y) * z++    * 'connect' distributes over 'overlay':++        > x * (y + z) == x * y + x * z+        > (x + y) * z == x * z + y * z++    * 'connect' can be decomposed:++        > x * y * z == x * y + x * z + y * z++The following useful theorems can be proved from the above set of axioms.++    * 'overlay' has 'empty' as the identity and is idempotent:++        >   x + empty == x+        >   empty + x == x+        >       x + x == x++    * Absorption and saturation of 'connect':++        > x * y + x + y == x * y+        >     x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ will+denote the number of vertices in the graph, /m/ will denote the number of edges+in the graph, and /s/ will denote the /size/ of the corresponding 'Graph'+expression. For example, if @g@ is a 'Graph' then /n/, /m/ and /s/ can be+computed as follows:++@n == 'vertexCount' g+m == 'edgeCount' g+s == 'size' g@++Note that 'size' counts all leaves of the expression:++@'vertexCount' 'empty'           == 0+'size'        'empty'           == 1+'vertexCount' ('vertex' x)      == 1+'size'        ('vertex' x)      == 1+'vertexCount' ('empty' + 'empty') == 0+'size'        ('empty' + 'empty') == 2@++Converting an undirected 'Graph' to the corresponding 'R.Relation' takes+/O(s + m * log(m))/ time and /O(s + m)/ memory. This is also the complexity of+the graph equality test, because it is currently implemented by converting graph+expressions to canonical representations based on adjacency maps.++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3+'edge' 1 2 == 'edge' 2 1@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x     <= x + y+x + y <= x * y@+-}+newtype Graph a = UG (G.Graph a)+    deriving (Alternative, Applicative, Functor, Generic, Monad, MonadPlus, NFData)++instance (Show a, Ord a) => Show (Graph a) where+    show = show . toRelation++-- | __Note:__ this does not satisfy the usual ring laws; see 'Graph' for more+-- details.+instance Num a => Num (Graph a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id++instance Ord a => Eq (Graph a) where+    (==) = eqR++instance Ord a => Ord (Graph a) where+    compare = ordR++-- TODO: Find a more efficient equality check.+-- Check if two graphs are equal by converting them to symmetric relations.+eqR :: Ord a => Graph a -> Graph a -> Bool+eqR x y = toRelation x == toRelation y++-- TODO: Find a more efficient comparison.+-- Compare two graphs by converting them to their symmetric relations.+ordR :: Ord a => Graph a -> Graph a -> Ordering+ordR x y = compare (toRelation x) (toRelation y)++-- | Construct an undirected graph from a given "Algebra.Graph".+-- Complexity: /O(1)/ time.+--+-- @+-- toUndirected ('Algebra.Graph.edge' 1 2)         == 'edge' 1 2+-- toUndirected . 'fromUndirected'   == id+-- 'vertexCount' . toUndirected      == 'Algebra.Graph.vertexCount'+-- (*2) . 'edgeCount' . toUndirected >= 'Algebra.Graph.edgeCount'+-- @+toUndirected :: G.Graph a -> Graph a+toUndirected = coerce++-- | Extract the underlying "Algebra.Graph".+-- Complexity: /O(n + m)/ time.+--+-- @+-- fromUndirected ('Algebra.Graph.edge' 1 2)     == 'Algebra.Graph.edges' [(1,2),(2,1)]+-- 'toUndirected' . 'fromUndirected' == id+-- 'Algebra.Graph.vertexCount' . fromUndirected  == 'vertexCount'+-- 'Algebra.Graph.edgeCount' . fromUndirected    <= (*2) . 'edgeCount'+-- @+fromUndirected :: Ord a => Graph a -> G.Graph a+fromUndirected = toGraph . toRelation++-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time, memory and size.+--+-- @+-- 'isEmpty'     empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount'   empty == 0+-- 'size'        empty == 1+-- @+empty :: Graph a+empty = coerce00 G.empty+{-# INLINE empty #-}++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time, memory and size.+--+-- @+-- 'isEmpty'     (vertex x) == False+-- 'hasVertex' x (vertex y) == (x == y)+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount'   (vertex x) == 0+-- 'size'        (vertex x) == 1+-- @+vertex :: a -> Graph a+vertex = coerce10 G.vertex+{-# INLINE vertex #-}++-- | Construct the graph comprising /a single edge/.+-- Complexity: /O(1)/ time, memory and size.+--+-- @+-- edge x y               == 'connect' ('vertex' x) ('vertex' y)+-- edge x y               == 'edge' y x+-- edge x y               == 'edges' [(x,y), (y,x)]+-- 'hasEdge' x y (edge x y) == True+-- 'edgeCount'   (edge x y) == 1+-- 'vertexCount' (edge 1 1) == 1+-- 'vertexCount' (edge 1 2) == 2+-- @+edge :: a -> a -> Graph a+edge = coerce20 G.edge+{-# INLINE edge #-}++-- | /Overlay/ two graphs. This is a commutative, associative and idempotent+-- operation with the identity 'empty'.+-- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size.+--+-- @+-- 'isEmpty'     (overlay x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (overlay x y) >= 'edgeCount' x+-- 'edgeCount'   (overlay x y) <= 'edgeCount' x   + 'edgeCount' y+-- 'size'        (overlay x y) == 'size' x        + 'size' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount'   (overlay 1 2) == 0+-- @+overlay :: Graph a -> Graph a -> Graph a+overlay = coerce20 G.overlay+{-# INLINE overlay #-}++-- | /Connect/ two graphs. This is a commutative and associative operation with+-- the identity 'empty', which distributes over 'overlay' and obeys the+-- decomposition axiom.+-- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size. Note that the number+-- of edges in the resulting graph is quadratic with respect to the number of+-- vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.+--+-- @+-- 'connect' x y               == 'connect' y x+-- 'isEmpty'     (connect x y) == 'isEmpty'   x   && 'isEmpty'   y+-- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y) >= 'vertexCount' x+-- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount'   (connect x y) >= 'edgeCount' x+-- 'edgeCount'   (connect x y) >= 'edgeCount' y+-- 'edgeCount'   (connect x y) >= 'vertexCount' x * 'vertexCount' y+-- 'edgeCount'   (connect x y) >= 'vertexCount' x * 'vertexCount' y `div` 2+-- 'size'        (connect x y) == 'size' x        + 'size' y+-- 'vertexCount' (connect 1 2) == 2+-- 'edgeCount'   (connect 1 2) == 1+-- @+connect :: Graph a -> Graph a -> Graph a+connect = coerce20 G.connect+{-# INLINE connect #-}++-- | Construct the graph comprising a given list of isolated vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- vertices []            == 'empty'+-- vertices [x]           == 'vertex' x+-- 'hasVertex' x . vertices == 'elem' x+-- 'vertexCount' . vertices == 'length' . 'Data.List.nub'+-- 'vertexSet'   . vertices == Set . 'Set.fromList'+-- @+vertices :: [a] -> Graph a+vertices = coerce10 G.vertices+{-# INLINE vertices #-}++-- | Construct the graph from a list of edges.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- edges []             == 'empty'+-- edges [(x,y)]        == 'edge' x y+-- edges [(x,y), (y,x)] == 'edge' x y+-- @+edges :: [(a, a)] -> Graph a+edges = coerce10 G.edges+{-# INLINE edges #-}++-- | Overlay a given list of graphs.+-- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length+-- of the given list, and /S/ is the sum of sizes of the graphs in the list.+--+-- @+-- overlays []        == 'empty'+-- overlays [x]       == x+-- overlays [x,y]     == 'overlay' x y+-- overlays           == 'foldr' 'overlay' 'empty'+-- 'isEmpty' . overlays == 'all' 'isEmpty'+-- @+overlays :: [Graph a] -> Graph a+overlays = coerce10 G.overlays+{-# INLINE overlays #-}++-- | Connect a given list of graphs.+-- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length+-- of the given list, and /S/ is the sum of sizes of the graphs in the list.+--+-- @+-- connects []        == 'empty'+-- connects [x]       == x+-- connects [x,y]     == 'connect' x y+-- connects           == 'foldr' 'connect' 'empty'+-- 'isEmpty' . connects == 'all' 'isEmpty'+-- connects           == connects . 'reverse'+-- @+connects :: [Graph a] -> Graph a+connects = coerce10 G.connects+{-# INLINE connects #-}++-- | Generalised 'Graph' folding: recursively collapse a 'Graph' by applying+-- the provided functions to the leaves and internal nodes of the expression.+-- The order of arguments is: empty, vertex, overlay and connect.+-- Complexity: /O(s)/ applications of given functions. As an example, the+-- complexity of 'size' is /O(s)/, since all functions have cost /O(1)/.+--+-- @+-- foldg 'empty' 'vertex'        'overlay' 'connect'        == id+-- foldg 'empty' 'vertex'        'overlay' ('flip' 'connect') == id+-- foldg 1     ('const' 1)     (+)     (+)            == 'size'+-- foldg True  ('const' False) (&&)    (&&)           == 'isEmpty'+-- foldg False (== x)        (||)    (||)           == 'hasVertex' x+-- @+foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b+foldg = coerce G.foldg+  where+    coerce :: (b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> G.Graph a -> b)+           -> (b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) ->   Graph a -> b)+    coerce = Data.Coerce.coerce+{-# INLINE foldg #-}++-- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the+-- first graph is a /subgraph/ of the second.+-- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a+-- graph can be quadratic with respect to the expression size /s/.+--+-- @+-- isSubgraphOf 'empty'         x             ==  True+-- isSubgraphOf ('vertex' x)    'empty'         ==  False+-- isSubgraphOf x             ('overlay' x y) ==  True+-- isSubgraphOf ('overlay' x y) ('connect' x y) ==  True+-- isSubgraphOf ('path' xs)     ('circuit' xs)  ==  True+-- isSubgraphOf ('edge' x y)    ('edge' y x)    ==  True+-- isSubgraphOf x y                         ==> x <= y+-- @+isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool+isSubgraphOf x y = R.isSubgraphOf (toRelation x) (toRelation y)+{-# NOINLINE [1] isSubgraphOf #-}++-- TODO: This is a very inefficient implementation. Find a way to construct a+-- symmetric relation directly, without building intermediate representations+-- for all subgraphs.+-- | Convert an undirected graph to a symmetric 'R.Relation'.+toRelation :: Ord a => Graph a -> R.Relation a+toRelation = foldg R.empty R.vertex R.overlay R.connect+{-# INLINE toRelation #-}++-- | Check if a graph is empty.+-- Complexity: /O(s)/ time.+--+-- @+-- isEmpty 'empty'                       == True+-- isEmpty ('overlay' 'empty' 'empty')       == True+-- isEmpty ('vertex' x)                  == False+-- isEmpty ('removeVertex' x $ 'vertex' x) == True+-- isEmpty ('removeEdge' x y $ 'edge' x y) == False+-- @+isEmpty :: Graph a -> Bool+isEmpty = coerce01 G.isEmpty+{-# INLINE isEmpty #-}++-- | The /size/ of a graph, i.e. the number of leaves of the expression+-- including 'empty' leaves.+-- Complexity: /O(s)/ time.+--+-- @+-- size 'empty'         == 1+-- size ('vertex' x)    == 1+-- size ('overlay' x y) == size x + size y+-- size ('connect' x y) == size x + size y+-- size x             >= 1+-- size x             >= 'vertexCount' x+-- @+size :: Graph a -> Int+size = coerce01 G.size+{-# INLINE size #-}++-- | Check if a graph contains a given vertex.+-- Complexity: /O(s)/ time.+--+-- @+-- hasVertex x 'empty'            == False+-- hasVertex x ('vertex' y)       == (x == y)+-- hasVertex x . 'removeVertex' x == 'const' False+-- @+hasVertex :: Eq a => a -> Graph a -> Bool+hasVertex = coerce11 G.hasVertex+{-# INLINE hasVertex #-}+{-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-}++-- TODO: Optimise this further.+-- | Check if a graph contains a given edge.+-- Complexity: /O(s)/ time.+--+-- @+-- hasEdge x y 'empty'            == False+-- hasEdge x y ('vertex' z)       == False+-- hasEdge x y ('edge' x y)       == True+-- hasEdge x y ('edge' y x)       == True+-- hasEdge x y . 'removeEdge' x y == 'const' False+-- hasEdge x y                  == 'elem' (min x y, max x y) . 'edgeList'+-- @+hasEdge :: Eq a => a -> a -> Graph a -> Bool+hasEdge s t (UG g) = G.hasEdge s t g || G.hasEdge t s g+{-# INLINE hasEdge #-}+{-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-}++-- | The number of vertices in a graph.+-- Complexity: /O(s * log(n))/ time.+--+-- @+-- vertexCount 'empty'             ==  0+-- vertexCount ('vertex' x)        ==  1+-- vertexCount                   ==  'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y+-- @+vertexCount :: Ord a => Graph a -> Int+vertexCount = coerce01 G.vertexCount+{-# INLINE [1] vertexCount #-}++-- | The number of edges in a graph.+-- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a+-- graph can be quadratic with respect to the expression size /s/.+--+-- @+-- edgeCount 'empty'      == 0+-- edgeCount ('vertex' x) == 0+-- edgeCount ('edge' x y) == 1+-- edgeCount            == 'length' . 'edgeList'+-- @+edgeCount :: Ord a => Graph a -> Int+edgeCount = R.edgeCount . toRelation+{-# INLINE [1] edgeCount #-}++-- | The sorted list of vertices of a given graph.+-- Complexity: /O(s * log(n))/ time and /O(n)/ memory.+--+-- @+-- vertexList 'empty'      == []+-- vertexList ('vertex' x) == [x]+-- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort'+-- @+vertexList :: Ord a => Graph a -> [a]+vertexList = coerce01 G.vertexList+{-# INLINE [1] vertexList #-}++-- | The sorted list of edges of a graph.+-- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of+-- edges /m/ of a graph can be quadratic with respect to the expression size /s/.+--+-- @+-- edgeList 'empty'          == []+-- edgeList ('vertex' x)     == []+-- edgeList ('edge' x y)     == [(min x y, max y x)]+-- edgeList ('star' 2 [3,1]) == [(1,2), (2,3)]+-- @+edgeList :: Ord a => Graph a -> [(a, a)]+edgeList = R.edgeList . toRelation+{-# INLINE [1] edgeList #-}++-- | The set of vertices of a given graph.+-- Complexity: /O(s * log(n))/ time and /O(n)/ memory.+--+-- @+-- vertexSet 'empty'      == Set.'Set.empty'+-- vertexSet . 'vertex'   == Set.'Set.singleton'+-- vertexSet . 'vertices' == Set.'Set.fromList'+-- @+vertexSet :: Ord a => Graph a -> Set a+vertexSet = coerce01 G.vertexSet+{-# INLINE vertexSet #-}++-- | The set of edges of a given graph.+-- Complexity: /O(s * log(m))/ time and /O(m)/ memory.+--+-- @+-- edgeSet 'empty'      == Set.'Set.empty'+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('edge' x y) == Set.'Set.singleton' ('min' x y, 'max' x y)+-- @+edgeSet :: Ord a => Graph a -> Set (a, a)+edgeSet = R.edgeSet . toRelation+{-# INLINE [1] edgeSet #-}++-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- adjacencyList 'empty'          == []+-- adjacencyList ('vertex' x)     == [(x, [])]+-- adjacencyList ('edge' 1 2)     == [(1, [2]), (2, [1])]+-- adjacencyList ('star' 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]+-- 'stars' . adjacencyList        == id+-- @+adjacencyList :: Ord a => Graph a -> [(a, [a])]+adjacencyList = R.adjacencyList . toRelation+{-# INLINE adjacencyList #-}+{-# SPECIALISE adjacencyList :: Graph Int -> [(Int, [Int])] #-}++-- | The set of vertices /adjacent/ to a given vertex.+--+-- @+-- neighbours x 'empty'      == Set.'Set.empty'+-- neighbours x ('vertex' x) == Set.'Set.empty'+-- neighbours x ('edge' x y) == Set.'Set.fromList' [y]+-- neighbours y ('edge' x y) == Set.'Set.fromList' [x]+-- @+neighbours :: Ord a => a -> Graph a -> Set a+neighbours x = R.neighbours x . toRelation+{-# INLINE neighbours #-}++-- | The /path/ on a list of vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- path []        == 'empty'+-- path [x]       == 'vertex' x+-- path [x,y]     == 'edge' x y+-- path . 'reverse' == path+-- @+path :: [a] -> Graph a+path = coerce10 G.path+{-# INLINE path #-}++-- | The /circuit/ on a list of vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- circuit []        == 'empty'+-- circuit [x]       == 'edge' x x+-- circuit [x,y]     == 'edge' (x,y)+-- circuit . 'reverse' == circuit+-- @+circuit :: [a] -> Graph a+circuit = coerce10 G.circuit+{-# INLINE circuit #-}++-- | The /clique/ on a list of vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- clique []         == 'empty'+-- clique [x]        == 'vertex' x+-- clique [x,y]      == 'edge' x y+-- clique [x,y,z]    == 'edges' [(x,y), (x,z), (y,z)]+-- clique (xs ++ ys) == 'connect' (clique xs) (clique ys)+-- clique . 'reverse'  == clique+-- @+clique :: [a] -> Graph a+clique = coerce10 G.clique+{-# INLINE clique #-}++-- | The /biclique/ on two lists of vertices.+-- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the+-- lengths of the given lists.+--+-- @+-- biclique []      []      == 'empty'+-- biclique [x]     []      == 'vertex' x+-- biclique []      [y]     == 'vertex' y+-- biclique [x1,x2] [y1,y2] == 'edges' [(x1,y1), (x1,y2), (x2,x2), (x2,y2)]+-- biclique xs      ys      == 'connect' ('vertices' xs) ('vertices' ys)+-- @+biclique :: [a] -> [a] -> Graph a+biclique = coerce20 G.biclique+{-# INLINE biclique #-}++-- | The /star/ formed by a centre vertex connected to a list of leaves.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- star x []    == 'vertex' x+-- star x [y]   == 'edge' x y+-- star x [y,z] == 'edges' [(x,y), (x,z)]+-- star x ys    == 'connect' ('vertex' x) ('vertices' ys)+-- @+star :: a -> [a] -> Graph a+star = coerce20 G.star+{-# INLINE star #-}++-- | The /stars/ formed by overlaying a list of 'star's. An inverse of+-- 'adjacencyList'.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the total size of the+-- input.+--+-- @+-- stars []                      == 'empty'+-- stars [(x, [])]               == 'vertex' x+-- stars [(x, [y])]              == 'edge' x y+-- stars [(x, ys)]               == 'star' x ys+-- stars                         == 'overlays' . 'map' ('uncurry' 'star')+-- stars . 'adjacencyList'         == id+-- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys)+-- @+stars :: [(a, [a])] -> Graph a+stars = coerce10 G.stars+{-# INLINE stars #-}++-- | The /tree graph/ constructed from a given 'Tree' data structure.+-- Complexity: /O(T)/ time, memory and size, where /T/ is the size of the+-- given tree (i.e. the number of vertices in the tree).+--+-- @+-- tree (Node x [])                                         == 'vertex' x+-- tree (Node x [Node y [Node z []]])                       == 'path' [x,y,z]+-- tree (Node x [Node y [], Node z []])                     == 'star' x [y,z]+-- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)]+-- @+tree :: Tree a -> Graph a+tree = coerce10 G.tree+{-# INLINE tree #-}++-- | The /forest graph/ constructed from a given 'Forest' data structure.+-- Complexity: /O(F)/ time, memory and size, where /F/ is the size of the+-- given forest (i.e. the number of vertices in the forest).+--+-- @+-- forest []                                                  == 'empty'+-- forest [x]                                                 == 'tree' x+-- forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == 'edges' [(1,2), (1,3), (4,5)]+-- forest                                                     == 'overlays' . 'map' 'tree'+-- @+forest :: Forest a -> Graph a+forest = coerce10 G.forest+{-# INLINE forest #-}++-- | Remove a vertex from a given graph.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- removeVertex x ('vertex' x)       == 'empty'+-- removeVertex 1 ('vertex' 2)       == 'vertex' 2+-- removeVertex x ('edge' x x)       == 'empty'+-- removeVertex 1 ('edge' 1 2)       == 'vertex' 2+-- removeVertex x . removeVertex x == removeVertex x+-- @+removeVertex :: Eq a => a -> Graph a -> Graph a+removeVertex = coerce11 G.removeVertex+{-# INLINE removeVertex #-}+{-# SPECIALISE removeVertex :: Int -> Graph Int -> Graph Int #-}++-- TODO: Optimise by doing a single graph traversal.+-- | Remove an edge from a given graph.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- removeEdge x y ('edge' x y)       == 'vertices' [x,y]+-- removeEdge x y . removeEdge x y == removeEdge x y+-- removeEdge x y                  == removeEdge y x+-- removeEdge x y . 'removeVertex' x == 'removeVertex' x+-- removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2+-- removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2+-- @+removeEdge :: Eq a => a -> a -> Graph a -> Graph a+removeEdge s t = Data.Coerce.coerce $ G.removeEdge s t . G.removeEdge t s+{-# INLINE removeEdge #-}+{-# SPECIALISE removeEdge :: Int -> Int -> Graph Int -> Graph Int #-}++-- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'Graph'. If @y@ already exists, @x@ and @y@ will be merged.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- replaceVertex x x            == id+-- replaceVertex x y ('vertex' x) == 'vertex' y+-- replaceVertex x y            == 'mergeVertices' (== x) y+-- @+replaceVertex :: Eq a => a -> a -> Graph a -> Graph a+replaceVertex = coerce21 G.replaceVertex+{-# INLINE replaceVertex #-}+{-# SPECIALISE replaceVertex :: Int -> Int -> Graph Int -> Graph Int #-}++-- | Merge vertices satisfying a given predicate into a given vertex.+-- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes+-- /O(1)/ to be evaluated.+--+-- @+-- mergeVertices ('const' False) x    == id+-- mergeVertices (== x) y           == 'replaceVertex' x y+-- mergeVertices 'even' 1 (0 * 2)     == 1 * 1+-- mergeVertices 'odd'  1 (3 + 4 * 5) == 4 * 1+-- @+mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a+mergeVertices = coerce21 G.mergeVertices+{-# INLINE mergeVertices #-}++-- TODO: Implement via 'induceJust' to reduce code duplication.+-- | Construct the /induced subgraph/ of a given graph by removing the+-- vertices that do not satisfy a given predicate.+-- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes+-- /O(1)/ to be evaluated.+--+-- @+-- induce ('const' True ) x      == x+-- induce ('const' False) x      == 'empty'+-- induce (/= x)               == 'removeVertex' x+-- induce p . induce q         == induce (\\x -> p x && q x)+-- 'isSubgraphOf' (induce p x) x == True+-- @+induce :: (a -> Bool) -> Graph a -> Graph a+induce = coerce20 G.induce+{-# INLINE induce #-}++-- | Construct the /induced subgraph/ of a given graph by removing the vertices+-- that are 'Nothing'.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- induceJust ('vertex' 'Nothing')                               == 'empty'+-- induceJust ('edge' ('Just' x) 'Nothing')                        == 'vertex' x+-- induceJust . 'fmap' 'Just'                                    == 'id'+-- induceJust . 'fmap' (\\x -> if p x then 'Just' x else 'Nothing') == 'induce' p+-- @+induceJust :: Graph (Maybe a) -> Graph a+induceJust = coerce10 G.induceJust+{-# INLINE induceJust #-}++-- | The edge complement of a graph. Note that, as can be seen from the examples+-- below, this operation ignores self-loops.+-- Complexity: /O(n^2 * log n)/ time, /O(n^2)/ memory.+--+-- @+-- complement 'empty'           == 'empty'+-- complement ('vertex' x)      == ('vertex' x)+-- complement ('edge' 1 2)      == ('vertices' [1, 2])+-- complement ('edge' 0 0)      == ('edge' 0 0)+-- complement ('star' 1 [2, 3]) == ('overlay' ('vertex' 1) ('edge' 2 3))+-- complement . complement    == id+-- @+complement :: Ord a => Graph a -> Graph a+complement g = overlay (vertices vsOld) (edges $ Set.toAscList esNew)+  where+    vsOld = vertexList g+    esOld = edgeSet g+    loops = Set.filter (uncurry (==)) esOld+    esAll = Set.fromAscList [ (x, y) | x:ys <- tails vsOld, y <- ys ]+    esNew = Set.union loops (Set.difference esAll esOld)
src/Data/Graph/Typed.hs view
@@ -19,21 +19,22 @@     GraphKL(..), fromAdjacencyMap, fromAdjacencyIntMap,      -- * Basic algorithms-    dfsForest, dfsForestFrom, dfs, topSort-  ) where--import Algebra.Graph.AdjacencyMap.Internal    as AM-import Algebra.Graph.AdjacencyIntMap.Internal as AIM+    dfsForest, dfsForestFrom, dfs, topSort, scc+    ) where  import Data.Tree import Data.Maybe+import Data.Foldable -import qualified Data.Graph         as KL-import qualified Data.Map.Strict    as Map-import qualified Data.IntMap.Strict as IntMap-import qualified Data.Set           as Set-import qualified Data.IntSet        as IntSet+import qualified Data.Graph as KL +import qualified Algebra.Graph.AdjacencyMap          as AM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty+import qualified Algebra.Graph.AdjacencyIntMap       as AIM++import qualified Data.Map.Strict                     as Map+import qualified Data.Set                            as Set+ -- | 'GraphKL' encapsulates King-Launchbury graphs, which are implemented in -- the "Data.Graph" module of the @containers@ library. data GraphKL a = GraphKL {@@ -46,25 +47,25 @@     -- Returns 'Nothing' if the argument is not in the graph.     toVertexKL :: a -> Maybe KL.Vertex } --- | Build 'GraphKL' from an 'AdjacencyMap'.--- If @fromAdjacencyMap g == h@ then the following holds:+-- | Build 'GraphKL' from an 'AM.AdjacencyMap'. If @fromAdjacencyMap g == h@+-- then the following holds: -- -- @--- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h)                               == 'Algebra.Graph.AdjacencyMap.vertexList' g--- map (\\(x, y) -> ('fromVertexKL' h x, 'fromVertexKL' h y)) ('Data.Graph.edges' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.edgeList' g+-- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h)                               == 'AM.vertexList' g+-- map (\\(x, y) -> ('fromVertexKL' h x, 'fromVertexKL' h y)) ('Data.Graph.edges' $ 'toGraphKL' h) == 'AM.edgeList' g -- 'toGraphKL' (fromAdjacencyMap (1 * 2 + 3 * 1))                                == 'array' (0,2) [(0,[1]), (1,[]), (2,[0])] -- 'toGraphKL' (fromAdjacencyMap (1 * 2 + 2 * 1))                                == 'array' (0,1) [(0,[1]), (1,[0])] -- @-fromAdjacencyMap :: Ord a => AdjacencyMap a -> GraphKL a-fromAdjacencyMap (AM.AM m) = GraphKL+fromAdjacencyMap :: Ord a => AM.AdjacencyMap a -> GraphKL a+fromAdjacencyMap am = GraphKL     { toGraphKL    = g     , fromVertexKL = \u -> case r u of (_, v, _) -> v     , toVertexKL   = t }   where-    (g, r, t) = KL.graphFromEdges [ ((), v, Set.toAscList us) | (v, us) <- Map.toAscList m ]+    (g, r, t) = KL.graphFromEdges [ ((), x, ys) | (x, ys) <- AM.adjacencyList am ] --- | Build 'GraphKL' from an 'AdjacencyIntMap'.--- If @fromAdjacencyIntMap g == h@ then the following holds:+-- | Build 'GraphKL' from an 'AIM.AdjacencyIntMap'. If+-- @fromAdjacencyIntMap g == h@ then the following holds: -- -- @ -- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h)                               == 'Data.IntSet.toAscList' ('Algebra.Graph.AdjacencyIntMap.vertexIntSet' g)@@ -72,32 +73,33 @@ -- 'toGraphKL' (fromAdjacencyIntMap (1 * 2 + 3 * 1))                             == 'array' (0,2) [(0,[1]), (1,[]), (2,[0])] -- 'toGraphKL' (fromAdjacencyIntMap (1 * 2 + 2 * 1))                             == 'array' (0,1) [(0,[1]), (1,[0])] -- @-fromAdjacencyIntMap :: AdjacencyIntMap -> GraphKL Int-fromAdjacencyIntMap (AIM.AM m) = GraphKL+fromAdjacencyIntMap :: AIM.AdjacencyIntMap -> GraphKL Int+fromAdjacencyIntMap aim = GraphKL     { toGraphKL    = g-    , fromVertexKL = \u -> case r u of (_, v, _) -> v+    , fromVertexKL = \x -> case r x of (_, v, _) -> v     , toVertexKL   = t }   where-    (g, r, t) = KL.graphFromEdges [ ((), v, IntSet.toAscList us) | (v, us) <- IntMap.toAscList m ]+    (g, r, t) = KL.graphFromEdges [ ((), x, ys) | (x, ys) <- AIM.adjacencyList aim ]  -- | Compute the /depth-first search/ forest of a graph. ----- In the following we will use the helper function:+-- In the following examples we will use the helper function: -- -- @--- (%) :: (GraphKL Int -> a) -> AM.AdjacencyMap Int -> a--- a % g = a $ fromAdjacencyMap g+-- (%) :: (GraphKL Int -> a) -> 'AM.AdjacencyMap' Int -> a+-- a % g = a $ 'fromAdjacencyMap' g -- @--- for greater clarity. (One could use an AdjacencyIntMap just as well) --+-- for greater clarity.+-- -- @--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % 'Algebra.Graph.AdjacencyMap.edge' 1 1)           == 'AM.vertex' 1--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % 'Algebra.Graph.AdjacencyMap.edge' 1 2)           == 'Algebra.Graph.AdjacencyMap.edge' 1 2--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % 'Algebra.Graph.AdjacencyMap.edge' 2 1)           == 'AM.vertices' [1, 2]--- 'AM.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.forest' $ dfsForest % x) x == True--- dfsForest % 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % x)      == dfsForest % x+-- 'AM.forest' (dfsForest % 'AM.edge' 1 1)           == 'AM.vertex' 1+-- 'AM.forest' (dfsForest % 'AM.edge' 1 2)           == 'AM.edge' 1 2+-- 'AM.forest' (dfsForest % 'AM.edge' 2 1)           == 'AM.vertices' [1, 2]+-- 'AM.isSubgraphOf' ('AM.forest' $ dfsForest % x) x == True+-- dfsForest % 'AM.forest' (dfsForest % x)      == dfsForest % x -- dfsForest % 'AM.vertices' vs                 == 'map' (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs)--- 'Algebra.Graph.AdjacencyMap.dfsForestFrom' ('Algebra.Graph.AdjacencyMap.vertexList' x) % x        == dfsForest % x+-- 'AM.dfsForestFrom' ('AM.vertexList' x) % x        == dfsForest % x -- dfsForest % (3 * (1 + 4) * (1 + 5))     == [ Node { rootLabel = 1 --                                                   , subForest = [ Node { rootLabel = 5 --                                                                        , subForest = [] }]}@@ -112,15 +114,24 @@ -- the given vertices in order. Note that the resulting forest does not -- necessarily span the whole graph, as some vertices may be unreachable. --+-- In the following examples we will use the helper function:+-- -- @--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [1]    % 'Algebra.Graph.AdjacencyMap.edge' 1 1)       == 'AM.vertex' 1--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [1]    % 'Algebra.Graph.AdjacencyMap.edge' 1 2)       == 'Algebra.Graph.AdjacencyMap.edge' 1 2--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [2]    % 'Algebra.Graph.AdjacencyMap.edge' 1 2)       == 'AM.vertex' 2--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [3]    % 'Algebra.Graph.AdjacencyMap.edge' 1 2)       == 'AM.empty'--- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [2, 1] % 'Algebra.Graph.AdjacencyMap.edge' 1 2)       == 'Algebra.Graph.AdjacencyMap.vertices' [1, 2]--- 'Algebra.Graph.AdjacencyMap.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.forest' $ dfsForestFrom vs % x) x == True--- dfsForestFrom ('Algebra.Graph.AdjacencyMap.vertexList' x) % x               == 'dfsForest' % x--- dfsForestFrom vs               % 'Algebra.Graph.AdjacencyMap.vertices' vs   == 'map' (\\v -> Node v []) ('Data.List.nub' vs)+-- (%) :: (GraphKL Int -> a) -> 'AM.AdjacencyMap' Int -> a+-- a % g = a $ 'fromAdjacencyMap' g+-- @+--+-- for greater clarity.+--+-- @+-- 'AM.forest' (dfsForestFrom [1]    % 'AM.edge' 1 1)       == 'AM.vertex' 1+-- 'AM.forest' (dfsForestFrom [1]    % 'AM.edge' 1 2)       == 'AM.edge' 1 2+-- 'AM.forest' (dfsForestFrom [2]    % 'AM.edge' 1 2)       == 'AM.vertex' 2+-- 'AM.forest' (dfsForestFrom [3]    % 'AM.edge' 1 2)       == 'AM.empty'+-- 'AM.forest' (dfsForestFrom [2, 1] % 'AM.edge' 1 2)       == 'AM.vertices' [1, 2]+-- 'AM.isSubgraphOf' ('AM.forest' $ dfsForestFrom vs % x) x == True+-- dfsForestFrom ('AM.vertexList' x) % x               == 'dfsForest' % x+-- dfsForestFrom vs               % 'AM.vertices' vs   == 'map' (\\v -> Node v []) ('Data.List.nub' vs) -- dfsForestFrom []               % x             == [] -- dfsForestFrom [1, 4] % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1 --                                                          , subForest = [ Node { rootLabel = 5@@ -131,30 +142,61 @@ dfsForestFrom :: [a] -> GraphKL a -> Forest a dfsForestFrom vs (GraphKL g r t) = fmap (fmap r) (KL.dfs g (mapMaybe t vs)) --- | Compute the list of vertices visited by the /depth-first search/ in a graph,--- when searching from each of the given vertices in order.+-- | Compute the list of vertices visited by the /depth-first search/ in a+-- graph, when searching from each of the given vertices in order. --+-- In the following examples we will use the helper function:+-- -- @--- dfs [1]   % 'Algebra.Graph.AdjacencyMap.edge' 1 1                 == [1]--- dfs [1]   % 'Algebra.Graph.AdjacencyMap.edge' 1 2                 == [1,2]--- dfs [2]   % 'Algebra.Graph.AdjacencyMap.edge' 1 2                 == [2]--- dfs [3]   % 'Algebra.Graph.AdjacencyMap.edge' 1 2                 == []--- dfs [1,2] % 'Algebra.Graph.AdjacencyMap.edge' 1 2                 == [1,2]--- dfs [2,1] % 'Algebra.Graph.AdjacencyMap.edge' 1 2                 == [2,1]+-- (%) :: (GraphKL Int -> a) -> 'AM.AdjacencyMap' Int -> a+-- a % g = a $ 'fromAdjacencyMap' g+-- @+--+-- for greater clarity.+--+-- @+-- dfs [1]   % 'AM.edge' 1 1                 == [1]+-- dfs [1]   % 'AM.edge' 1 2                 == [1,2]+-- dfs [2]   % 'AM.edge' 1 2                 == [2]+-- dfs [3]   % 'AM.edge' 1 2                 == []+-- dfs [1,2] % 'AM.edge' 1 2                 == [1,2]+-- dfs [2,1] % 'AM.edge' 1 2                 == [2,1] -- dfs []    % x                        == []--- dfs [1,4] % (3 * (1 + 4) * (1 + 5))  == [1, 5, 4]--- 'Algebra.Graph.AdjacencyMap.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.vertices' $ dfs vs x) x == True+-- dfs [1,4] % (3 * (1 + 4) * (1 + 5))  == [1,5,4]+-- 'AM.isSubgraphOf' ('AM.vertices' $ dfs vs x) x == True -- @ dfs :: [a] -> GraphKL a -> [a] dfs vs = concatMap flatten . dfsForestFrom vs --- | Compute the /topological sort/ of a graph.--- Unlike the (Int)AdjacencyMap algorithm this returns+-- | Compute the /topological sort/ of a graph. Note that this function returns -- a result even if the graph is cyclic. --+-- In the following examples we will use the helper function:+-- -- @+-- (%) :: (GraphKL Int -> a) -> 'AM.AdjacencyMap' Int -> a+-- a % g = a $ 'fromAdjacencyMap' g+-- @+--+-- for greater clarity.+--+-- @ -- topSort % (1 * 2 + 3 * 1) == [3,1,2] -- topSort % (1 * 2 + 2 * 1) == [1,2] -- @ topSort :: GraphKL a -> [a] topSort (GraphKL g r _) = map r (KL.topSort g)++scc :: Ord a => AM.AdjacencyMap a -> AM.AdjacencyMap (NonEmpty.AdjacencyMap a)+scc m = AM.gmap (component Map.!) $ removeSelfLoops $ AM.gmap (leader Map.!) m+  where+    GraphKL g decode _ = fromAdjacencyMap m+    sccs      = map toList (KL.scc g)+    leader    = Map.fromList [ (decode y, x)      | x:xs <- sccs, y <- x:xs ]+    component = Map.fromList [ (x, expand (x:xs)) | x:xs <- sccs ]+    expand xs = fromJust $ NonEmpty.toNonEmpty $ AM.induce (`Set.member` s) m+      where+        s = Set.fromList (map decode xs)++removeSelfLoops :: Ord a => AM.AdjacencyMap a -> AM.AdjacencyMap a+removeSelfLoops m = foldr (\x -> AM.removeEdge x x) m (AM.vertexList m)
test/Algebra/Graph/Test.hs view
@@ -1,4 +1,14 @@ {-# LANGUAGE RankNTypes #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test+-- Copyright  : (c) Andrey Mokhov 2016-2020+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Basic testsuite infrastructure.+----------------------------------------------------------------------------- module Algebra.Graph.Test (     module Data.List,     module Data.List.Extra,@@ -6,7 +16,7 @@     module Test.QuickCheck.Function,      GraphTestsuite, (//), axioms, theorems, undirectedAxioms, reflexiveAxioms,-    transitiveAxioms, preorderAxioms, test,+    transitiveAxioms, preorderAxioms, size10, test     ) where  import Data.List (sort)@@ -15,11 +25,14 @@ import System.Exit (exitFailure) import Test.QuickCheck hiding ((===)) import Test.QuickCheck.Function-import Test.QuickCheck.Test (isSuccess)  import Algebra.Graph.Class import Algebra.Graph.Test.Arbitrary () +-- | Test a property only on small (at most size 10) inputs.+size10 :: Testable prop => prop -> Property+size10 = mapSize (min 10)+ test :: Testable a => String -> a -> IO () test str p = do     result <- quickCheckWithResult (stdArgs { chatty = False }) p@@ -43,50 +56,48 @@ infixl 6 + infixl 7 * -type GraphTestsuite g = g -> g -> g -> Property+type GraphTestsuite g = (Ord g, Graph g) => g -> g -> g -> Property -axioms :: (Eq g, Graph g) => GraphTestsuite g+axioms :: GraphTestsuite g axioms x y z = conjoin-    [       x + y == y + x                      // "Overlay commutativity"-    , x + (y + z) == (x + y) + z                // "Overlay associativity"-    ,   empty * x == x                          // "Left connect identity"-    ,   x * empty == x                          // "Right connect identity"-    , x * (y * z) == (x * y) * z                // "Connect associativity"-    , x * (y + z) == x * y + x * z              // "Left distributivity"-    , (x + y) * z == x * z + y * z              // "Right distributivity"-    ,   x * y * z == x * y + x * z + y * z      // "Decomposition" ]+    [       x + y == y + x                 // "Overlay commutativity"+    , x + (y + z) == (x + y) + z           // "Overlay associativity"+    ,   empty * x == x                     // "Left connect identity"+    ,   x * empty == x                     // "Right connect identity"+    , x * (y * z) == (x * y) * z           // "Connect associativity"+    , x * (y + z) == x * y + x * z         // "Left distributivity"+    , (x + y) * z == x * z + y * z         // "Right distributivity"+    ,   x * y * z == x * y + x * z + y * z // "Decomposition" ] -theorems :: (Ord g, Graph g) => GraphTestsuite g+theorems :: GraphTestsuite g theorems x y z = conjoin-    [     x + empty == x                        // "Overlay identity"-    ,         x + x == x                        // "Overlay idempotence"-    , x + y + x * y == x * y                    // "Absorption"+    [     x + empty == x                     // "Overlay identity"+    ,         x + x == x                     // "Overlay idempotence"+    , x + y + x * y == x * y                 // "Absorption"     ,     x * y * z == x * y + x * z + y * z-                     + x + y + z + empty        // "Full decomposition"-    ,         x * x == x * x * x                // "Connect saturation"-    ,         empty <= x                        // "Lower bound"-    ,             x <= x + y                    // "Overlay order"-    ,         x + y <= x * y                    // "Overlay-connect order" ]+                     + x + y + z + empty     // "Full decomposition"+    ,         x * x == x * x * x             // "Connect saturation"+    ,         empty <= x                     // "Lower bound"+    ,             x <= x + y                 // "Overlay order"+    ,         x + y <= x * y                 // "Overlay-connect order" ] -undirectedAxioms :: (Eq g, Graph g) => GraphTestsuite g+undirectedAxioms :: GraphTestsuite g undirectedAxioms x y z = conjoin     [ axioms x y z-    , x * y == y * x                            // "Connect commutativity" ]+    , x * y == y * x // "Connect commutativity" ] -reflexiveAxioms :: (Eq g, Graph g, Arbitrary (Vertex g), Show (Vertex g)) => GraphTestsuite g+reflexiveAxioms :: forall g. (Arbitrary (Vertex g), Show (Vertex g)) => GraphTestsuite g reflexiveAxioms x y z = conjoin     [ axioms x y z-    , forAll arbitrary (\v -> vertex v `asTypeOf` x == vertex v * vertex v)-                                                // "Vertex self-loop" ]+    , forAll arbitrary (\v -> vertex @g v == vertex v * vertex v) // "Vertex self-loop" ] -transitiveAxioms :: (Eq g, Graph g) => GraphTestsuite g+transitiveAxioms :: GraphTestsuite g transitiveAxioms x y z = conjoin     [ axioms x y z-    , y == empty || x * y * z == x * y + y * z  // "Closure" ]+    , y == empty || x * y * z == x * y + y * z // "Closure" ] -preorderAxioms :: (Eq g, Graph g, Arbitrary (Vertex g), Show (Vertex g)) => GraphTestsuite g+preorderAxioms :: forall g. (Arbitrary (Vertex g), Show (Vertex g)) => GraphTestsuite g preorderAxioms x y z = conjoin     [ axioms x y z-    , forAll arbitrary (\v -> vertex v `asTypeOf` x == vertex v * vertex v)-                                                // "Vertex self-loop"-    , y == empty || x * y * z == x * y + y * z  // "Closure" ]+    , forAll arbitrary (\v -> vertex @g v == vertex v * vertex v) // "Vertex self-loop"+    , y == empty || x * y * z == x * y + y * z                    // "Closure" ]
test/Algebra/Graph/Test/API.hs view
@@ -1,307 +1,660 @@-{-# LANGUAGE ConstrainedClassMethods, RankNTypes #-}+{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, RecordWildCards #-}+{-# OPTIONS_GHC -Wno-missing-fields #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.API--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental ----- Graph manipulation API used for generic testing.+-- The complete graph API used for generic testing. ----------------------------------------------------------------------------- module Algebra.Graph.Test.API (-    -- * Graph manipulation API-    GraphAPI (..)-  ) where+    -- * Graph API+    API (..), Mono (..), toIntAPI, +    -- * APIs of various graph data types+    adjacencyMapAPI, adjacencyIntMapAPI, graphAPI, undirectedGraphAPI, relationAPI,+    symmetricRelationAPI, labelledGraphAPI, labelledAdjacencyMapAPI+    ) where++import Data.Coerce+import Data.List.NonEmpty (NonEmpty) import Data.Monoid (Any)+import Data.IntMap (IntMap) import Data.IntSet (IntSet)+import Data.Map (Map) import Data.Set (Set) import Data.Tree+import Test.QuickCheck -import Algebra.Graph.Class (Graph (..))+import qualified Algebra.Graph                                as G+import qualified Algebra.Graph.Undirected                     as UG+import qualified Algebra.Graph.AdjacencyIntMap                as AIM+import qualified Algebra.Graph.AdjacencyIntMap.Algorithm      as AIM+import qualified Algebra.Graph.AdjacencyMap                   as AM+import qualified Algebra.Graph.AdjacencyMap.Algorithm         as AM+import qualified Algebra.Graph.Labelled                       as LG+import qualified Algebra.Graph.Labelled.AdjacencyMap          as LAM+import qualified Algebra.Graph.Relation                       as R+import qualified Algebra.Graph.Relation.Symmetric             as SR+import qualified Algebra.Graph.ToGraph                        as T -import qualified Algebra.Graph                       as G-import qualified Algebra.Graph.AdjacencyMap          as AM-import qualified Algebra.Graph.Labelled              as LG-import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM-import qualified Algebra.Graph.Fold                  as Fold-import qualified Algebra.Graph.HigherKinded.Class    as HClass-import qualified Algebra.Graph.AdjacencyIntMap       as AIM-import qualified Algebra.Graph.Relation              as R-import qualified Algebra.Graph.Relation.Symmetric    as SR+import Algebra.Graph.Test.Arbitrary () -import qualified Algebra.Graph.AdjacencyMap.Internal       as AMI-import qualified Algebra.Graph.AdjacencyIntMap.Internal    as AIMI-import qualified Algebra.Graph.Relation.Internal           as RI-import qualified Algebra.Graph.Relation.Symmetric.Internal as SRI+-- | A wrapper for monomorphic data types. We cannot use 'AIM.AdjacencyIntMap'+-- directly when defining an 'API', but we can if we wrap it into 'Mono'.+newtype Mono g a = Mono { getMono :: g }+    deriving (Arbitrary, Eq, Num, Ord) -class Graph g => GraphAPI g where-    consistent           :: g -> Bool-    consistent           = notImplemented-    edge                 :: Vertex g -> Vertex g -> g-    edge                 = notImplemented-    vertices             :: [Vertex g] -> g-    vertices             = notImplemented-    edges                :: [(Vertex g, Vertex g)] -> g-    edges                = notImplemented-    overlays             :: [g] -> g-    overlays             = notImplemented-    connects             :: [g] -> g-    connects             = notImplemented-    fromAdjacencySets    :: [(Vertex g, Set (Vertex g))] -> g-    fromAdjacencySets    = notImplemented-    fromAdjacencyIntSets :: [(Int, IntSet)] -> g-    fromAdjacencyIntSets = notImplemented-    isSubgraphOf         :: g -> g -> Bool-    isSubgraphOf         = notImplemented-    (===)                :: g -> g -> Bool-    (===)                = notImplemented-    neighbours           :: Vertex g -> g -> Set (Vertex g)-    neighbours           = notImplemented-    path                 :: [Vertex g] -> g-    path                 = notImplemented-    circuit              :: [Vertex g] -> g-    circuit              = notImplemented-    clique               :: [Vertex g] -> g-    clique               = notImplemented-    biclique             :: [Vertex g] -> [Vertex g] -> g-    biclique             = notImplemented-    star                 :: Vertex g -> [Vertex g] -> g-    star                 = notImplemented-    stars                :: [(Vertex g, [Vertex g])] -> g-    stars                = notImplemented-    tree                 :: Tree (Vertex g) -> g-    tree                 = notImplemented-    forest               :: Forest (Vertex g) -> g-    forest               = notImplemented-    mesh                 :: Vertex g ~ (a, b) => [a] -> [b] -> g-    mesh                 = notImplemented-    torus                :: Vertex g ~ (a, b) => [a] -> [b] -> g-    torus                = notImplemented-    deBruijn             :: Vertex g ~ [a] => Int -> [a] -> g-    deBruijn             = notImplemented-    removeVertex         :: Vertex g -> g -> g-    removeVertex         = notImplemented-    removeEdge           :: Vertex g -> Vertex g -> g -> g-    removeEdge           = notImplemented-    replaceVertex        :: Vertex g -> Vertex g -> g -> g-    replaceVertex        = notImplemented-    mergeVertices        :: (Vertex g -> Bool) -> Vertex g -> g -> g-    mergeVertices        = notImplemented-    splitVertex          :: Vertex g -> [Vertex g] -> g -> g-    splitVertex          = notImplemented-    transpose            :: g -> g-    transpose            = notImplemented-    gmap                 :: Vertex g ~ Int => (Int -> Int) -> g -> g-    gmap                 = notImplemented-    induce               :: (Vertex g -> Bool) -> g -> g-    induce               = notImplemented-    compose              :: g -> g -> g-    compose              = notImplemented-    closure              :: g -> g-    closure              = notImplemented-    reflexiveClosure     :: g -> g-    reflexiveClosure     = notImplemented-    symmetricClosure     :: g -> g-    symmetricClosure     = notImplemented-    transitiveClosure    :: g -> g-    transitiveClosure    = notImplemented-    bind                 :: Vertex g ~ Int => g -> (Int -> g) -> g-    bind                 = notImplemented-    simplify             :: g -> g-    simplify             = notImplemented-    box                  :: forall a b f. (Vertex (f a) ~ a, Vertex (f b) ~ b, Vertex (f (a, b)) ~ (a, b), g ~ f (a, b)) => f a -> f b -> f (a, b)-    box                  = notImplemented+instance Show g => Show (Mono g a) where+    show = show . getMono -notImplemented :: a-notImplemented = error "Not implemented"+-- | Convert a polymorphic API dictionary into a monomorphic 'Int' version.+toIntAPI :: API g Ord -> API g ((~) Int)+toIntAPI API{..} = API{..} -instance Ord a => GraphAPI (AM.AdjacencyMap a) where-    consistent        = AMI.consistent-    edge              = AM.edge-    vertices          = AM.vertices-    edges             = AM.edges-    overlays          = AM.overlays-    connects          = AM.connects-    fromAdjacencySets = AM.fromAdjacencySets-    isSubgraphOf      = AM.isSubgraphOf-    path              = AM.path-    circuit           = AM.circuit-    clique            = AM.clique-    biclique          = AM.biclique-    star              = AM.star-    stars             = AM.stars-    tree              = AM.tree-    forest            = AM.forest-    removeVertex      = AM.removeVertex-    removeEdge        = AM.removeEdge-    replaceVertex     = AM.replaceVertex-    mergeVertices     = AM.mergeVertices-    transpose         = AM.transpose-    gmap              = AM.gmap-    induce            = AM.induce-    compose           = AM.compose-    closure           = AM.closure-    reflexiveClosure  = AM.reflexiveClosure-    symmetricClosure  = AM.symmetricClosure-    transitiveClosure = AM.transitiveClosure+-- TODO: Add missing API entries for Acyclic, NonEmpty and Symmetric graphs.+-- | The complete graph API dictionary. A graph data type, such as 'G.Graph',+-- typically implements only a part of the whole API.+data API g c where+    API :: ( Arbitrary (g Int), Num (g Int), Ord (g Int), Ord (g (Int, Int))+           , Ord (g (Int, Char)), Ord (g [Int]), Ord (g [Char])+           , Ord (g (Int, (Int, Int))), Ord (g ((Int, Int), Int))+           , Show (g Int)) =>+        { empty                      :: forall a. c a => g a+        , vertex                     :: forall a. c a => a -> g a+        , edge                       :: forall a. c a => a -> a -> g a+        , overlay                    :: forall a. c a => g a -> g a -> g a+        , connect                    :: forall a. c a => g a -> g a -> g a+        , vertices                   :: forall a. c a => [a] -> g a+        , edges                      :: forall a. c a => [(a, a)] -> g a+        , overlays                   :: forall a. c a => [g a] -> g a+        , connects                   :: forall a. c a => [g a] -> g a+        , toGraph                    :: forall a. c a => g a -> G.Graph a+        , foldg                      :: forall a. c a => forall r. r -> (a -> r) -> (r -> r -> r) -> (r -> r -> r) -> g a -> r+        , isSubgraphOf               :: forall a. c a => g a -> g a -> Bool+        , structEq                   :: forall a. c a => g a -> g a -> Bool+        , isEmpty                    :: forall a. c a => g a -> Bool+        , size                       :: forall a. c a => g a -> Int+        , hasVertex                  :: forall a. c a => a -> g a -> Bool+        , hasEdge                    :: forall a. c a => a -> a -> g a -> Bool+        , vertexCount                :: forall a. c a => g a -> Int+        , edgeCount                  :: forall a. c a => g a -> Int+        , vertexList                 :: forall a. c a => g a -> [a]+        , edgeList                   :: forall a. c a => g a -> [(a, a)]+        , vertexSet                  :: forall a. c a => g a -> Set a+        , vertexIntSet               :: g Int -> IntSet+        , edgeSet                    :: forall a. c a => g a -> Set (a, a)+        , preSet                     :: forall a. c a => a -> g a -> Set a+        , preIntSet                  :: Int -> g Int -> IntSet+        , postSet                    :: forall a. c a => a -> g a -> Set a+        , postIntSet                 :: Int -> g Int -> IntSet+        , neighbours                 :: forall a. c a => a -> g a -> Set a+        , adjacencyList              :: forall a. c a => g a -> [(a, [a])]+        , adjacencyMap               :: forall a. c a => g a -> Map a (Set a)+        , adjacencyIntMap            :: g Int -> IntMap IntSet+        , adjacencyMapTranspose      :: forall a. c a => g a -> Map a (Set a)+        , adjacencyIntMapTranspose   :: g Int -> IntMap IntSet+        , bfsForest                  :: forall a. c a => [a] -> g a -> Forest a+        , bfs                        :: forall a. c a => [a] -> g a -> [[a]]+        , dfsForest                  :: forall a. c a => g a -> Forest a+        , dfsForestFrom              :: forall a. c a => [a] -> g a -> Forest a+        , dfs                        :: forall a. c a => [a] -> g a -> [a]+        , reachable                  :: forall a. c a => a -> g a -> [a]+        , topSort                    :: forall a. c a => g a -> Either (NonEmpty a) [a]+        , isAcyclic                  :: forall a. c a => g a -> Bool+        , toAdjacencyMap             :: forall a. c a => g a -> AM.AdjacencyMap a+        , toAdjacencyIntMap          :: g Int -> AIM.AdjacencyIntMap+        , toAdjacencyMapTranspose    :: forall a. c a => g a -> AM.AdjacencyMap a+        , toAdjacencyIntMapTranspose :: g Int -> AIM.AdjacencyIntMap+        , isDfsForestOf              :: forall a. c a => Forest a -> g a -> Bool+        , isTopSortOf                :: forall a. c a => [a] -> g a -> Bool+        , path                       :: forall a. c a => [a] -> g a+        , circuit                    :: forall a. c a => [a] -> g a+        , clique                     :: forall a. c a => [a] -> g a+        , biclique                   :: forall a. c a => [a] -> [a] -> g a+        , star                       :: forall a. c a => a -> [a] -> g a+        , stars                      :: forall a. c a => [(a, [a])] -> g a+        , tree                       :: forall a. c a => Tree a -> g a+        , forest                     :: forall a. c a => Forest a -> g a+        , mesh                       :: forall a b. (c a, c b) => [a] -> [b] -> g (a, b)+        , torus                      :: forall a b. (c a, c b) => [a] -> [b] -> g (a, b)+        , deBruijn                   :: forall a. c a => Int -> [a] -> g [a]+        , removeVertex               :: forall a. c a => a -> g a -> g a+        , removeEdge                 :: forall a. c a => a -> a -> g a -> g a+        , replaceVertex              :: forall a. c a => a -> a -> g a -> g a+        , mergeVertices              :: forall a. c a => (a -> Bool) -> a -> g a -> g a+        , splitVertex                :: forall a. c a => a -> [a] -> g a -> g a+        , transpose                  :: forall a. c a => g a -> g a+        , gmap                       :: forall a b. (c a, c b) => (a -> b) -> g a -> g b+        , bind                       :: forall a b. (c a, c b) => g a -> (a -> g b) -> g b+        , induce                     :: forall a. c a => (a -> Bool) -> g a -> g a+        , induceJust                 :: forall a. c a => g (Maybe a) -> g a+        , simplify                   :: forall a. c a => g a -> g a+        , compose                    :: forall a. c a => g a -> g a -> g a+        , box                        :: forall a b. (c a, c b) => g a -> g b -> g (a, b)+        , closure                    :: forall a. c a => g a -> g a+        , reflexiveClosure           :: forall a. c a => g a -> g a+        , symmetricClosure           :: forall a. c a => g a -> g a+        , transitiveClosure          :: forall a. c a => g a -> g a+        , consistent                 :: forall a. c a => g a -> Bool+        , fromAdjacencySets          :: forall a. c a => [(a, Set a)] -> g a+        , fromAdjacencyIntSets       :: [(Int, IntSet)] -> g Int } -> API g c -instance Ord a => GraphAPI (Fold.Fold a) where-    edge          = Fold.edge-    vertices      = Fold.vertices-    edges         = Fold.edges-    overlays      = Fold.overlays-    connects      = Fold.connects-    isSubgraphOf  = Fold.isSubgraphOf-    path          = Fold.path-    circuit       = Fold.circuit-    clique        = Fold.clique-    biclique      = Fold.biclique-    star          = Fold.star-    stars         = Fold.stars-    tree          = HClass.tree-    forest        = HClass.forest-    mesh          = HClass.mesh-    torus         = HClass.torus-    deBruijn      = HClass.deBruijn-    removeVertex  = Fold.removeVertex-    removeEdge    = Fold.removeEdge-    replaceVertex = HClass.replaceVertex-    mergeVertices = HClass.mergeVertices-    splitVertex   = HClass.splitVertex-    transpose     = Fold.transpose-    gmap          = fmap-    induce        = Fold.induce-    bind          = (>>=)-    simplify      = Fold.simplify+-- | The API of 'AM.AdjacencyMap'.+adjacencyMapAPI :: API AM.AdjacencyMap Ord+adjacencyMapAPI = API+    { empty                      = AM.empty+    , vertex                     = AM.vertex+    , edge                       = AM.edge+    , overlay                    = AM.overlay+    , connect                    = AM.connect+    , vertices                   = AM.vertices+    , edges                      = AM.edges+    , overlays                   = AM.overlays+    , connects                   = AM.connects+    , toGraph                    = T.toGraph+    , foldg                      = T.foldg+    , isSubgraphOf               = AM.isSubgraphOf+    , isEmpty                    = AM.isEmpty+    , size                       = G.size . T.toGraph+    , hasVertex                  = AM.hasVertex+    , hasEdge                    = AM.hasEdge+    , vertexCount                = AM.vertexCount+    , edgeCount                  = AM.edgeCount+    , vertexList                 = AM.vertexList+    , edgeList                   = AM.edgeList+    , vertexSet                  = AM.vertexSet+    , vertexIntSet               = T.vertexIntSet+    , edgeSet                    = AM.edgeSet+    , preSet                     = AM.preSet+    , preIntSet                  = T.preIntSet+    , postSet                    = AM.postSet+    , postIntSet                 = T.postIntSet+    , adjacencyList              = AM.adjacencyList+    , adjacencyMap               = AM.adjacencyMap+    , adjacencyIntMap            = T.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose+    , bfsForest                  = AM.bfsForest+    , bfs                        = AM.bfs+    , dfsForest                  = AM.dfsForest+    , dfsForestFrom              = AM.dfsForestFrom+    , dfs                        = AM.dfs+    , reachable                  = AM.reachable+    , topSort                    = AM.topSort+    , isAcyclic                  = AM.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap+    , toAdjacencyIntMap          = T.toAdjacencyIntMap+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose+    , isDfsForestOf              = AM.isDfsForestOf+    , isTopSortOf                = AM.isTopSortOf+    , path                       = AM.path+    , circuit                    = AM.circuit+    , clique                     = AM.clique+    , biclique                   = AM.biclique+    , star                       = AM.star+    , stars                      = AM.stars+    , tree                       = AM.tree+    , forest                     = AM.forest+    , removeVertex               = AM.removeVertex+    , removeEdge                 = AM.removeEdge+    , replaceVertex              = AM.replaceVertex+    , mergeVertices              = AM.mergeVertices+    , transpose                  = AM.transpose+    , gmap                       = AM.gmap+    , induce                     = AM.induce+    , induceJust                 = AM.induceJust+    , compose                    = AM.compose+    , box                        = AM.box+    , closure                    = AM.closure+    , reflexiveClosure           = AM.reflexiveClosure+    , symmetricClosure           = AM.symmetricClosure+    , transitiveClosure          = AM.transitiveClosure+    , consistent                 = AM.consistent+    , fromAdjacencySets          = AM.fromAdjacencySets } -instance Ord a => GraphAPI (G.Graph a) where-    edge          = G.edge-    vertices      = G.vertices-    edges         = G.edges-    overlays      = G.overlays-    connects      = G.connects-    isSubgraphOf  = G.isSubgraphOf-    (===)         = (G.===)-    path          = G.path-    circuit       = G.circuit-    clique        = G.clique-    biclique      = G.biclique-    star          = G.star-    stars         = G.stars-    tree          = G.tree-    forest        = G.forest-    mesh          = G.mesh-    torus         = G.torus-    deBruijn      = G.deBruijn-    removeVertex  = G.removeVertex-    removeEdge    = G.removeEdge-    replaceVertex = G.replaceVertex-    mergeVertices = G.mergeVertices-    splitVertex   = G.splitVertex-    transpose     = G.transpose-    gmap          = fmap-    induce        = G.induce-    compose       = G.compose-    bind          = (>>=)-    simplify      = G.simplify-    box           = G.box+-- | The API of 'G.Graph'.+graphAPI :: API G.Graph Ord+graphAPI = API+    { empty                      = G.empty+    , vertex                     = G.vertex+    , edge                       = G.edge+    , overlay                    = G.overlay+    , connect                    = G.connect+    , vertices                   = G.vertices+    , edges                      = G.edges+    , overlays                   = G.overlays+    , connects                   = G.connects+    , toGraph                    = id+    , foldg                      = G.foldg+    , isSubgraphOf               = G.isSubgraphOf+    , structEq                   = (G.===)+    , isEmpty                    = G.isEmpty+    , size                       = G.size+    , hasVertex                  = G.hasVertex+    , hasEdge                    = G.hasEdge+    , vertexCount                = G.vertexCount+    , edgeCount                  = G.edgeCount+    , vertexList                 = G.vertexList+    , edgeList                   = G.edgeList+    , vertexSet                  = G.vertexSet+    , vertexIntSet               = T.vertexIntSet+    , edgeSet                    = G.edgeSet+    , preSet                     = T.preSet+    , preIntSet                  = T.preIntSet+    , postSet                    = T.postSet+    , postIntSet                 = T.postIntSet+    , adjacencyList              = G.adjacencyList+    , adjacencyMap               = T.adjacencyMap+    , adjacencyIntMap            = T.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose+    , dfsForest                  = T.dfsForest+    , dfsForestFrom              = T.dfsForestFrom+    , dfs                        = T.dfs+    , reachable                  = T.reachable+    , topSort                    = T.topSort+    , isAcyclic                  = T.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap+    , toAdjacencyIntMap          = T.toAdjacencyIntMap+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose+    , isDfsForestOf              = T.isDfsForestOf+    , isTopSortOf                = T.isTopSortOf+    , path                       = G.path+    , circuit                    = G.circuit+    , clique                     = G.clique+    , biclique                   = G.biclique+    , star                       = G.star+    , stars                      = G.stars+    , tree                       = G.tree+    , forest                     = G.forest+    , mesh                       = G.mesh+    , torus                      = G.torus+    , deBruijn                   = G.deBruijn+    , removeVertex               = G.removeVertex+    , removeEdge                 = G.removeEdge+    , replaceVertex              = G.replaceVertex+    , mergeVertices              = G.mergeVertices+    , splitVertex                = G.splitVertex+    , transpose                  = G.transpose+    , gmap                       = fmap+    , bind                       = (>>=)+    , induce                     = G.induce+    , induceJust                 = G.induceJust+    , simplify                   = G.simplify+    , compose                    = G.compose+    , box                        = G.box } -instance GraphAPI AIM.AdjacencyIntMap where-    consistent           = AIMI.consistent-    edge                 = AIM.edge-    vertices             = AIM.vertices-    edges                = AIM.edges-    overlays             = AIM.overlays-    connects             = AIM.connects-    fromAdjacencyIntSets = AIM.fromAdjacencyIntSets-    isSubgraphOf         = AIM.isSubgraphOf-    path                 = AIM.path-    circuit              = AIM.circuit-    clique               = AIM.clique-    biclique             = AIM.biclique-    star                 = AIM.star-    stars                = AIM.stars-    tree                 = AIM.tree-    forest               = AIM.forest-    removeVertex         = AIM.removeVertex-    removeEdge           = AIM.removeEdge-    replaceVertex        = AIM.replaceVertex-    mergeVertices        = AIM.mergeVertices-    transpose            = AIM.transpose-    gmap                 = AIM.gmap-    induce               = AIM.induce-    compose              = AIM.compose-    closure              = AIM.closure-    reflexiveClosure     = AIM.reflexiveClosure-    symmetricClosure     = AIM.symmetricClosure-    transitiveClosure    = AIM.transitiveClosure+-- | The API of 'UG.Graph'.+undirectedGraphAPI :: API UG.Graph Ord+undirectedGraphAPI = API+    { empty                      = UG.empty+    , vertex                     = UG.vertex+    , edge                       = UG.edge+    , overlay                    = UG.overlay+    , connect                    = UG.connect+    , vertices                   = UG.vertices+    , edges                      = UG.edges+    , overlays                   = UG.overlays+    , connects                   = UG.connects+    , toGraph                    = UG.fromUndirected+    , foldg                      = UG.foldg+    , isSubgraphOf               = UG.isSubgraphOf+    , isEmpty                    = UG.isEmpty+    , size                       = UG.size+    , hasVertex                  = UG.hasVertex+    , hasEdge                    = UG.hasEdge+    , vertexCount                = UG.vertexCount+    , edgeCount                  = UG.edgeCount+    , vertexList                 = UG.vertexList+    , edgeList                   = UG.edgeList+    , vertexSet                  = UG.vertexSet+    , edgeSet                    = UG.edgeSet+    , neighbours                 = UG.neighbours+    , adjacencyList              = UG.adjacencyList+    , path                       = UG.path+    , circuit                    = UG.circuit+    , clique                     = UG.clique+    , biclique                   = UG.biclique+    , star                       = UG.star+    , stars                      = UG.stars+    , tree                       = UG.tree+    , forest                     = UG.forest+    , removeVertex               = UG.removeVertex+    , removeEdge                 = UG.removeEdge+    , replaceVertex              = UG.replaceVertex+    , mergeVertices              = UG.mergeVertices+    , transpose                  = id+    , gmap                       = fmap+    , induce                     = UG.induce+    , induceJust                 = UG.induceJust } -instance Ord a => GraphAPI (R.Relation a) where-    consistent        = RI.consistent-    edge              = R.edge-    vertices          = R.vertices-    edges             = R.edges-    overlays          = R.overlays-    connects          = R.connects-    isSubgraphOf      = R.isSubgraphOf-    path              = R.path-    circuit           = R.circuit-    clique            = R.clique-    biclique          = R.biclique-    star              = R.star-    stars             = R.stars-    tree              = R.tree-    forest            = R.forest-    removeVertex      = R.removeVertex-    removeEdge        = R.removeEdge-    replaceVertex     = R.replaceVertex-    mergeVertices     = R.mergeVertices-    transpose         = R.transpose-    gmap              = R.gmap-    induce            = R.induce-    compose           = R.compose-    closure           = R.closure-    reflexiveClosure  = R.reflexiveClosure-    symmetricClosure  = R.symmetricClosure-    transitiveClosure = R.transitiveClosure+-- | The API of 'AIM.AdjacencyIntMap'.+adjacencyIntMapAPI :: API (Mono AIM.AdjacencyIntMap) ((~) Int)+adjacencyIntMapAPI = API+    { empty                      = coerce AIM.empty+    , vertex                     = coerce AIM.vertex+    , edge                       = coerce AIM.edge+    , overlay                    = coerce AIM.overlay+    , connect                    = coerce AIM.connect+    , vertices                   = coerce AIM.vertices+    , edges                      = coerce AIM.edges+    , overlays                   = coerce AIM.overlays+    , connects                   = coerce AIM.connects+    , toGraph                    = T.toGraph . getMono+    , foldg                      = \e v o c -> T.foldg e v o c . getMono+    , isSubgraphOf               = coerce AIM.isSubgraphOf+    , isEmpty                    = coerce AIM.isEmpty+    , size                       = G.size . T.toGraph . getMono+    , hasVertex                  = coerce AIM.hasVertex+    , hasEdge                    = coerce AIM.hasEdge+    , vertexCount                = coerce AIM.vertexCount+    , edgeCount                  = coerce AIM.edgeCount+    , vertexList                 = coerce AIM.vertexList+    , edgeList                   = coerce AIM.edgeList+    , vertexSet                  = T.vertexSet . getMono+    , vertexIntSet               = coerce AIM.vertexIntSet+    , edgeSet                    = coerce AIM.edgeSet+    , preSet                     = \x -> T.preSet x . getMono+    , preIntSet                  = coerce AIM.preIntSet+    , postSet                    = \x -> T.postSet x . getMono+    , postIntSet                 = coerce AIM.postIntSet+    , adjacencyList              = coerce AIM.adjacencyList+    , adjacencyMap               = T.adjacencyMap . getMono+    , adjacencyIntMap            = coerce AIM.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose . getMono+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose . getMono+    , bfsForest                  = coerce AIM.bfsForest+    , bfs                        = coerce AIM.bfs+    , dfsForest                  = coerce AIM.dfsForest+    , dfsForestFrom              = coerce AIM.dfsForestFrom+    , dfs                        = coerce AIM.dfs+    , reachable                  = coerce AIM.reachable+    , topSort                    = coerce AIM.topSort+    , isAcyclic                  = coerce AIM.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap . getMono+    , toAdjacencyIntMap          = T.toAdjacencyIntMap . getMono+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose . getMono+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose . getMono+    , isDfsForestOf              = coerce AIM.isDfsForestOf+    , isTopSortOf                = coerce AIM.isTopSortOf+    , path                       = coerce AIM.path+    , circuit                    = coerce AIM.circuit+    , clique                     = coerce AIM.clique+    , biclique                   = coerce AIM.biclique+    , star                       = coerce AIM.star+    , stars                      = coerce AIM.stars+    , tree                       = coerce AIM.tree+    , forest                     = coerce AIM.forest+    , removeVertex               = coerce AIM.removeVertex+    , removeEdge                 = coerce AIM.removeEdge+    , replaceVertex              = coerce AIM.replaceVertex+    , mergeVertices              = coerce AIM.mergeVertices+    , transpose                  = coerce AIM.transpose+    , gmap                       = coerce AIM.gmap+    , induce                     = coerce AIM.induce+    , compose                    = coerce AIM.compose+    , closure                    = coerce AIM.closure+    , reflexiveClosure           = coerce AIM.reflexiveClosure+    , symmetricClosure           = coerce AIM.symmetricClosure+    , transitiveClosure          = coerce AIM.transitiveClosure+    , consistent                 = coerce AIM.consistent+    , fromAdjacencyIntSets       = coerce AIM.fromAdjacencyIntSets } -instance Ord a => GraphAPI (SR.Relation a) where-    consistent        = SRI.consistent-    edge              = SR.edge-    vertices          = SR.vertices-    edges             = SR.edges-    overlays          = SR.overlays-    connects          = SR.connects-    isSubgraphOf      = SR.isSubgraphOf-    neighbours        = SR.neighbours-    path              = SR.path-    circuit           = SR.circuit-    clique            = SR.clique-    biclique          = SR.biclique-    star              = SR.star-    stars             = SR.stars-    tree              = SR.tree-    forest            = SR.forest-    removeVertex      = SR.removeVertex-    removeEdge        = SR.removeEdge-    replaceVertex     = SR.replaceVertex-    mergeVertices     = SR.mergeVertices-    transpose         = id-    gmap              = SR.gmap-    induce            = SR.induce+-- | The API of 'R.Relation'.+relationAPI :: API R.Relation Ord+relationAPI = API+    { empty                      = R.empty+    , vertex                     = R.vertex+    , edge                       = R.edge+    , overlay                    = R.overlay+    , connect                    = R.connect+    , vertices                   = R.vertices+    , edges                      = R.edges+    , overlays                   = R.overlays+    , connects                   = R.connects+    , toGraph                    = T.toGraph+    , foldg                      = T.foldg+    , isSubgraphOf               = R.isSubgraphOf+    , isEmpty                    = R.isEmpty+    , size                       = G.size . T.toGraph+    , hasVertex                  = R.hasVertex+    , hasEdge                    = R.hasEdge+    , vertexCount                = R.vertexCount+    , edgeCount                  = R.edgeCount+    , vertexList                 = R.vertexList+    , edgeList                   = R.edgeList+    , vertexSet                  = R.vertexSet+    , vertexIntSet               = T.vertexIntSet+    , edgeSet                    = R.edgeSet+    , preSet                     = R.preSet+    , preIntSet                  = T.preIntSet+    , postSet                    = R.postSet+    , postIntSet                 = T.postIntSet+    , adjacencyList              = R.adjacencyList+    , adjacencyMap               = T.adjacencyMap+    , adjacencyIntMap            = T.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose+    , dfsForest                  = T.dfsForest+    , dfsForestFrom              = T.dfsForestFrom+    , dfs                        = T.dfs+    , reachable                  = T.reachable+    , topSort                    = T.topSort+    , isAcyclic                  = T.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap+    , toAdjacencyIntMap          = T.toAdjacencyIntMap+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose+    , isDfsForestOf              = T.isDfsForestOf+    , isTopSortOf                = T.isTopSortOf+    , path                       = R.path+    , circuit                    = R.circuit+    , clique                     = R.clique+    , biclique                   = R.biclique+    , star                       = R.star+    , stars                      = R.stars+    , tree                       = R.tree+    , forest                     = R.forest+    , removeVertex               = R.removeVertex+    , removeEdge                 = R.removeEdge+    , replaceVertex              = R.replaceVertex+    , mergeVertices              = R.mergeVertices+    , transpose                  = R.transpose+    , gmap                       = R.gmap+    , induce                     = R.induce+    , induceJust                 = R.induceJust+    , compose                    = R.compose+    , closure                    = R.closure+    , reflexiveClosure           = R.reflexiveClosure+    , symmetricClosure           = R.symmetricClosure+    , transitiveClosure          = R.transitiveClosure+    , consistent                 = R.consistent } -instance Ord a => GraphAPI (LG.Graph Any a) where-    vertices     = LG.vertices-    overlays     = LG.overlays-    isSubgraphOf = LG.isSubgraphOf-    removeVertex = LG.removeVertex-    induce       = LG.induce+-- | The API of 'SR.Relation'.+symmetricRelationAPI :: API SR.Relation Ord+symmetricRelationAPI = API+    { empty                      = SR.empty+    , vertex                     = SR.vertex+    , edge                       = SR.edge+    , overlay                    = SR.overlay+    , connect                    = SR.connect+    , vertices                   = SR.vertices+    , edges                      = SR.edges+    , overlays                   = SR.overlays+    , connects                   = SR.connects+    , toGraph                    = T.toGraph+    , foldg                      = T.foldg+    , isSubgraphOf               = SR.isSubgraphOf+    , isEmpty                    = SR.isEmpty+    , size                       = G.size . T.toGraph+    , hasVertex                  = SR.hasVertex+    , hasEdge                    = SR.hasEdge+    , vertexCount                = SR.vertexCount+    , edgeCount                  = SR.edgeCount+    , vertexList                 = SR.vertexList+    , edgeList                   = SR.edgeList+    , vertexSet                  = SR.vertexSet+    , vertexIntSet               = T.vertexIntSet+    , edgeSet                    = SR.edgeSet+    , preSet                     = T.preSet+    , preIntSet                  = T.preIntSet+    , postSet                    = T.postSet+    , postIntSet                 = T.postIntSet+    , neighbours                 = SR.neighbours+    , adjacencyList              = SR.adjacencyList+    , adjacencyMap               = T.adjacencyMap+    , adjacencyIntMap            = T.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose+    , dfsForest                  = T.dfsForest+    , dfsForestFrom              = T.dfsForestFrom+    , dfs                        = T.dfs+    , reachable                  = T.reachable+    , topSort                    = T.topSort+    , isAcyclic                  = T.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap+    , toAdjacencyIntMap          = T.toAdjacencyIntMap+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose+    , isDfsForestOf              = T.isDfsForestOf+    , isTopSortOf                = T.isTopSortOf+    , path                       = SR.path+    , circuit                    = SR.circuit+    , clique                     = SR.clique+    , biclique                   = SR.biclique+    , star                       = SR.star+    , stars                      = SR.stars+    , tree                       = SR.tree+    , forest                     = SR.forest+    , removeVertex               = SR.removeVertex+    , removeEdge                 = SR.removeEdge+    , replaceVertex              = SR.replaceVertex+    , mergeVertices              = SR.mergeVertices+    , transpose                  = id+    , gmap                       = SR.gmap+    , induce                     = SR.induce+    , induceJust                 = SR.induceJust+    , consistent                 = SR.consistent } -instance Ord a => GraphAPI (LAM.AdjacencyMap Any a) where-    vertices     = LAM.vertices-    overlays     = LAM.overlays-    isSubgraphOf = LAM.isSubgraphOf-    removeVertex = LAM.removeVertex-    induce       = LAM.induce+-- | The API of 'LG.Graph'.+labelledGraphAPI :: API (LG.Graph Any) Ord+labelledGraphAPI = API+    { empty                      = LG.empty+    , vertex                     = LG.vertex+    , edge                       = LG.edge mempty+    , overlay                    = LG.overlay+    , connect                    = LG.connect mempty+    , vertices                   = LG.vertices+    , edges                      = LG.edges . map (\(x, y) -> (mempty, x, y))+    , overlays                   = LG.overlays+    , toGraph                    = T.toGraph+    , foldg                      = T.foldg+    , isSubgraphOf               = LG.isSubgraphOf+    , isEmpty                    = LG.isEmpty+    , size                       = LG.size+    , hasVertex                  = LG.hasVertex+    , hasEdge                    = LG.hasEdge+    , vertexCount                = T.vertexCount+    , edgeCount                  = T.edgeCount+    , vertexList                 = LG.vertexList+    , edgeList                   = T.edgeList+    , vertexSet                  = LG.vertexSet+    , vertexIntSet               = T.vertexIntSet+    , edgeSet                    = T.edgeSet+    , preSet                     = T.preSet+    , preIntSet                  = T.preIntSet+    , postSet                    = T.postSet+    , postIntSet                 = T.postIntSet+    , adjacencyList              = T.adjacencyList+    , adjacencyMap               = T.adjacencyMap+    , adjacencyIntMap            = T.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose+    , dfsForest                  = T.dfsForest+    , dfsForestFrom              = T.dfsForestFrom+    , dfs                        = T.dfs+    , reachable                  = T.reachable+    , topSort                    = T.topSort+    , isAcyclic                  = T.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap+    , toAdjacencyIntMap          = T.toAdjacencyIntMap+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose+    , isDfsForestOf              = T.isDfsForestOf+    , isTopSortOf                = T.isTopSortOf+    , removeVertex               = LG.removeVertex+    , removeEdge                 = LG.removeEdge+    , replaceVertex              = LG.replaceVertex+    , transpose                  = LG.transpose+    , gmap                       = fmap+    , induce                     = LG.induce+    , induceJust                 = LG.induceJust+    , closure                    = LG.closure+    , reflexiveClosure           = LG.reflexiveClosure+    , symmetricClosure           = LG.symmetricClosure+    , transitiveClosure          = LG.transitiveClosure }++-- | The API of 'LAM.AdjacencyMap'.+labelledAdjacencyMapAPI :: API (LAM.AdjacencyMap Any) Ord+labelledAdjacencyMapAPI = API+    { empty                      = LAM.empty+    , vertex                     = LAM.vertex+    , edge                       = LAM.edge mempty+    , overlay                    = LAM.overlay+    , connect                    = LAM.connect mempty+    , vertices                   = LAM.vertices+    , edges                      = LAM.edges . map (\(x, y) -> (mempty, x, y))+    , overlays                   = LAM.overlays+    , toGraph                    = T.toGraph+    , foldg                      = T.foldg+    , isSubgraphOf               = LAM.isSubgraphOf+    , isEmpty                    = LAM.isEmpty+    , size                       = G.size . T.toGraph+    , hasVertex                  = LAM.hasVertex+    , hasEdge                    = LAM.hasEdge+    , vertexCount                = LAM.vertexCount+    , edgeCount                  = LAM.edgeCount+    , vertexList                 = LAM.vertexList+    , edgeList                   = T.edgeList+    , vertexSet                  = LAM.vertexSet+    , vertexIntSet               = T.vertexIntSet+    , edgeSet                    = T.edgeSet+    , preSet                     = LAM.preSet+    , preIntSet                  = T.preIntSet+    , postSet                    = LAM.postSet+    , postIntSet                 = T.postIntSet+    , adjacencyList              = T.adjacencyList+    , adjacencyMap               = T.adjacencyMap+    , adjacencyIntMap            = T.adjacencyIntMap+    , adjacencyMapTranspose      = T.adjacencyMapTranspose+    , adjacencyIntMapTranspose   = T.adjacencyIntMapTranspose+    , dfsForest                  = T.dfsForest+    , dfsForestFrom              = T.dfsForestFrom+    , dfs                        = T.dfs+    , reachable                  = T.reachable+    , topSort                    = T.topSort+    , isAcyclic                  = T.isAcyclic+    , toAdjacencyMap             = T.toAdjacencyMap+    , toAdjacencyIntMap          = T.toAdjacencyIntMap+    , toAdjacencyMapTranspose    = T.toAdjacencyMapTranspose+    , toAdjacencyIntMapTranspose = T.toAdjacencyIntMapTranspose+    , isDfsForestOf              = T.isDfsForestOf+    , isTopSortOf                = T.isTopSortOf+    , removeVertex               = LAM.removeVertex+    , removeEdge                 = LAM.removeEdge+    , replaceVertex              = LAM.replaceVertex+    , transpose                  = LAM.transpose+    , gmap                       = LAM.gmap+    , induce                     = LAM.induce+    , induceJust                 = LAM.induceJust+    , closure                    = LAM.closure+    , reflexiveClosure           = LAM.reflexiveClosure+    , symmetricClosure           = LAM.symmetricClosure+    , transitiveClosure          = LAM.transitiveClosure+    , consistent                 = LAM.consistent }
+ test/Algebra/Graph/Test/Acyclic/AdjacencyMap.hs view
@@ -0,0 +1,502 @@+{-# LANGUAGE OverloadedLists, ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test.Acyclic.AdjacencyMap+-- Copyright  : (c) Andrey Mokhov 2016-2019+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Testsuite for "Algebra.Graph.Acyclic.AdjacencyMap".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Acyclic.AdjacencyMap (testAcyclicAdjacencyMap) where++import Algebra.Graph.Acyclic.AdjacencyMap+import Algebra.Graph.Internal+import Algebra.Graph.Test hiding (shrink)++import Data.Bifunctor+import Data.Tuple++import qualified Algebra.Graph.AdjacencyMap           as AM+import qualified Algebra.Graph.AdjacencyMap.Algorithm as AM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap  as NonEmpty+import qualified Data.Set                             as Set++type AAI = AdjacencyMap Int+type AI  = AM.AdjacencyMap Int++-- TODO: Switch to using generic tests.+testAcyclicAdjacencyMap :: IO ()+testAcyclicAdjacencyMap = do+    putStrLn "\n============ Acyclic.AdjacencyMap.Show ============"+    test "show empty                       == \"empty\"" $+          show (empty :: AAI)              == "empty"++    test "show (shrink 1)                  == \"vertex 1\"" $+          show (shrink 1 :: AAI)           == "vertex 1"++    test "show (shrink $ 1 + 2)            == \"vertices [1,2]\"" $+          show (shrink $ 1 + 2 :: AAI)     == "vertices [1,2]"++    test "show (shrink $ 1 * 2)            == \"(fromJust . toAcyclic) (edge 1 2)\"" $+          show (shrink $ 1 * 2 :: AAI)     == "(fromJust . toAcyclic) (edge 1 2)"++    test "show (shrink $ 1 * 2 * 3)        == \"(fromJust . toAcyclic) (edges [(1,2),(1,3),(2,3)])\"" $+          show (shrink $ 1 * 2 * 3 :: AAI) == "(fromJust . toAcyclic) (edges [(1,2),(1,3),(2,3)])"++    test "show (shrink $ 1 * 2 + 3)        == \"(fromJust . toAcyclic) (overlay (vertex 3) (edge 1 2))\"" $+          show (shrink $ 1 * 2 + 3 :: AAI) == "(fromJust . toAcyclic) (overlay (vertex 3) (edge 1 2))"++    putStrLn "\n============ Acyclic.AdjacencyMap.fromAcyclic ============"+    test "fromAcyclic empty                == empty" $+          fromAcyclic (empty :: AAI)       == AM.empty++    test "fromAcyclic . vertex             == vertex" $ \(x :: Int) ->+         (fromAcyclic . vertex) x          == AM.vertex x++    test "fromAcyclic (shrink $ 1 * 3 * 2) == star 1 [2,3]" $+          fromAcyclic (shrink $ 1 * 3 + 2) == 1 * 3 + (2 :: AI)++    test "vertexCount . fromAcyclic        == vertexCount" $ \(x :: AAI) ->+         (AM.vertexCount . fromAcyclic) x  == vertexCount x++    test "edgeCount   . fromAcyclic        == edgeCount" $ \(x :: AAI) ->+         (AM.edgeCount . fromAcyclic) x    == edgeCount x++    test "isAcyclic   . fromAcyclic        == const True" $ \(x :: AAI) ->+         (AM.isAcyclic . fromAcyclic) x    == const True x++    putStrLn "\n============ Acyclic.AdjacencyMap.empty ============"+    test "isEmpty     empty          == True" $+          isEmpty     (empty :: AAI) == True++    test "hasVertex x empty          == False" $ \x ->+          hasVertex x (empty :: AAI) == False++    test "vertexCount empty          == 0" $+          vertexCount (empty :: AAI) == 0++    test "edgeCount   empty          == 0" $+          edgeCount   (empty :: AAI) == 0++    putStrLn "\n============ Acyclic.AdjacencyMap.vertex ============"+    test "isEmpty     (vertex x) == False" $ \(x :: Int) ->+          isEmpty     (vertex x) == False++    test "hasVertex x (vertex y) == (x == y)" $ \(x :: Int) y ->+          hasVertex x (vertex y) == (x == y)++    test "vertexCount (vertex x) == 1" $ \(x :: Int) ->+          vertexCount (vertex x) == 1++    test "edgeCount   (vertex x) == 0" $ \(x :: Int) ->+          edgeCount   (vertex x) == 0++    putStrLn "\n============ Acyclic.AdjacencyMap.vertices ============"+    test "vertices []                == empty" $+          vertices []                == (empty :: AAI)++    test "vertices [x]               == vertex x" $ \(x :: Int) ->+          vertices [x]               == vertex x++    test "hasVertex x . vertices     == elem x" $ \(x :: Int) xs ->+         (hasVertex x . vertices) xs == elem x xs++    test "vertexCount . vertices     == length . nub" $ \(xs :: [Int]) ->+         (vertexCount . vertices) xs == (length . nubOrd) xs++    test "vertexSet   . vertices     == Set.fromList" $ \(xs :: [Int]) ->+         (vertexSet   . vertices) xs == Set.fromList xs++    putStrLn "\n============ Acyclic.AdjacencyMap.union ============"+    test "vertexSet (union x y) == <correct result>" $ \(x :: AAI) (y :: AAI) ->+          vertexSet (union x y) == Set.unions ([ Set.map Left  (vertexSet x)+                                               , Set.map Right (vertexSet y) ] ++ [])++    test "edgeSet   (union x y) == <correct result>" $ \(x :: AAI) (y :: AAI) ->+          edgeSet   (union x y) == Set.unions ([ Set.map (bimap Left  Left ) (edgeSet x)+                                               , Set.map (bimap Right Right) (edgeSet y) ] ++ [])++    putStrLn "\n============ Acyclic.AdjacencyMap.join ============"+    test "vertexSet (join x y) == <correct result>" $ \(x :: AAI) (y :: AAI) ->+          vertexSet (join x y) == Set.unions ([ Set.map Left  (vertexSet x)+                                              , Set.map Right (vertexSet y) ] ++ [])++    test "edgeSet   (join x y) == <correct result>" $ \(x :: AAI) (y :: AAI) ->+          edgeSet   (join x y) == Set.unions ([ Set.map (bimap Left  Left ) (edgeSet x)+                                              , Set.map (bimap Right Right) (edgeSet y)+                                              , Set.map (bimap Left  Right) (setProduct (vertexSet x) (vertexSet y)) ] ++ [])++    putStrLn "\n============ Acyclic.AdjacencyMap.isSubgraphOf ============"+    test "isSubgraphOf empty        x          == True" $ \(x :: AAI) ->+          isSubgraphOf empty        x          == True++    test "isSubgraphOf (vertex x)   empty      == False" $ \(x :: Int) ->+          isSubgraphOf (vertex x)   empty      == False++    test "isSubgraphOf (induce p x) x          == True" $ \(x :: AAI) (apply -> p) ->+          isSubgraphOf (induce p x) x          == True++    test "isSubgraphOf x (transitiveClosure x) == True" $ \(x :: AAI) ->+          isSubgraphOf x (transitiveClosure x) == True++    test "isSubgraphOf x y                     ==> x <= y" $ \(x :: AAI) z ->+        let connect x y = shrink $ fromAcyclic x + fromAcyclic y+            -- TODO: Make the precondition stronger+            y = connect x (vertices z) -- Make sure we hit the precondition+        in isSubgraphOf x y                   ==> x <= y++    putStrLn "\n============ Acyclic.AdjacencyMap.isEmpty ============"+    test "isEmpty empty                                    == True" $+          isEmpty (empty :: AAI)                           == True++    test "isEmpty (vertex x)                               == False" $ \(x :: Int) ->+          isEmpty (vertex x)                               == False++    test "isEmpty (removeVertex x $ vertex x)              == True" $ \(x :: Int) ->+          isEmpty (removeVertex x $ vertex x)              == True++    test "isEmpty (removeEdge 1 2 $ shrink $ 1 * 2)        == False" $+          isEmpty (removeEdge 1 2 $ shrink $ 1 * 2 :: AAI) == False++    putStrLn "\n============ Acyclic.AdjacencyMap.hasVertex ============"+    test "hasVertex x empty               == False" $ \(x :: Int) ->+          hasVertex x empty               == False++    test "hasVertex x (vertex y)          == (x == y)" $ \(x :: Int) y ->+          hasVertex x (vertex y)          == (x == y)++    test "hasVertex x . removeVertex x    == const False" $ \(x :: Int) y ->+         (hasVertex x . removeVertex x) y == const False y++    putStrLn "\n============ Acyclic.AdjacencyMap.hasEdge ============"+    test "hasEdge x y empty                      == False" $ \(x :: Int) y ->+          hasEdge x y empty                      == False++    test "hasEdge x y (vertex z)                 == False" $ \(x :: Int) y z ->+          hasEdge x y (vertex z)                 == False++    test "hasEdge 1 2 (shrink $ 1 * 2)           == True" $+          hasEdge 1 2 (shrink $ 1 * 2 :: AAI)    == True++    test "hasEdge x y . removeEdge x y           == const False" $ \(x :: Int) y z ->+         (hasEdge x y . removeEdge x y) z        == const False z++    test "hasEdge x y                            == elem (x,y) . edgeList" $ \(x :: Int) y z -> do+        (u, v) <- elements ((x, y) : edgeList z)+        return $ hasEdge u v z                   == elem (u, v) (edgeList z)++    putStrLn "\n============ Acyclic.AdjacencyMap.vertexCount ============"+    test "vertexCount empty                 == 0" $+          vertexCount (empty :: AAI)        == 0++    test "vertexCount (vertex x)            == 1" $ \(x :: Int) ->+          vertexCount (vertex x)            == 1++    test "vertexCount                       == length . vertexList" $ \(x :: AAI) ->+          vertexCount x                     == (length . vertexList) x++    test "vertexCount x < vertexCount y     ==> x < y" $ \(x :: AAI) y ->+        if vertexCount x < vertexCount y+        then property (x < y)+        else (vertexCount x > vertexCount y ==> x > y)++    putStrLn "\n============ Acyclic.AdjacencyMap.edgeCount ============"+    test "edgeCount empty                   == 0" $+          edgeCount (empty :: AAI)          == 0++    test "edgeCount (vertex x)              == 0" $ \(x :: Int) ->+          edgeCount (vertex x)              == 0++    test "edgeCount (shrink $ 1 * 2)        == 1" $+          edgeCount (shrink $ 1 * 2 :: AAI) == 1++    test "edgeCount                         == length . edgeList" $ \(x :: AAI) ->+          edgeCount x                       == (length . edgeList) x++    putStrLn "\n============ Acyclic.AdjacencyMap.vertexList ============"+    test "vertexList empty          == []" $+          vertexList (empty :: AAI) == []++    test "vertexList (vertex x)     == [x]" $ \(x :: Int) ->+          vertexList (vertex x)     == [x]++    test "vertexList . vertices     == nub . sort" $ \(xs :: [Int]) ->+         (vertexList . vertices) xs == (nubOrd . sort) xs++    putStrLn "\n============ Acyclic.AdjacencyMap.edgeList ============"+    test "edgeList empty                   == []" $+          edgeList (empty :: AAI)          == []++    test "edgeList (vertex x)              == []" $ \(x :: Int) ->+          edgeList (vertex x)              == []++    test "edgeList (shrink $ 2 * 1)        == [(2,1)]" $+          edgeList (shrink $ 2 * 1 :: AAI) == [(2,1)]++    test "edgeList . transpose             == sort . map swap . edgeList" $ \(x :: AAI) ->+         (edgeList . transpose) x          == (sort . map swap . edgeList) x++    putStrLn "\n============ Acyclic.AdjacencyMap.adjacencyList ============"+    test "adjacencyList empty                   == []" $+          adjacencyList (empty :: AAI)          == []++    test "adjacencyList (vertex x)              == [(x, [])]" $ \(x :: Int) ->+          adjacencyList (vertex x)              == [(x, [])]++    test "adjacencyList (shrink $ 1 * 2)        == [(1, [2]), (2, [])]" $+          adjacencyList (shrink $ 1 * 2 :: AAI) == [(1, [2]), (2, [])]++    putStrLn "\n============ Acyclic.AdjacencyMap.vertexSet ============"+    test "vertexSet empty          == Set.empty" $+          vertexSet (empty :: AAI) == Set.empty++    test "vertexSet . vertex       == Set.singleton" $ \(x :: Int) ->+         (vertexSet . vertex) x    == Set.singleton x++    test "vertexSet . vertices     == Set.fromList" $ \(xs :: [Int]) ->+         (vertexSet . vertices) xs == Set.fromList xs++    putStrLn "\n============ Acyclic.AdjacencyMap.edgeSet ============"+    test "edgeSet empty                   == Set.empty" $+          edgeSet (empty :: AAI)          == Set.empty++    test "edgeSet (vertex x)              == Set.empty" $ \(x :: Int) ->+          edgeSet (vertex x)              == Set.empty++    test "edgeSet (shrink $ 1 * 2)        == Set.singleton (1,2)" $+          edgeSet (shrink $ 1 * 2 :: AAI) == Set.singleton (1,2)++    putStrLn "\n============ Acyclic.AdjacencyMap.preSet ============"+    test "preSet x empty                   == Set.empty" $ \(x :: Int) ->+          preSet x empty                   == Set.empty++    test "preSet x (vertex x)              == Set.empty" $ \(x :: Int) ->+          preSet x (vertex x)              == Set.empty++    test "preSet 1 (shrink $ 1 * 2)        == Set.empty" $+          preSet 1 (shrink $ 1 * 2 :: AAI) == Set.empty++    test "preSet 2 (shrink $ 1 * 2)        == Set.fromList [1]" $+          preSet 2 (shrink $ 1 * 2 :: AAI) == Set.fromList [1]++    test "Set.member x . preSet x          == const False" $ \(x :: Int) y ->+         (Set.member x . preSet x) y       == const False y++    putStrLn "\n============ Acyclic.AdjacencyMap.postSet ============"+    test "postSet x empty                   == Set.empty" $ \(x :: Int) ->+          postSet x empty                   == Set.empty++    test "postSet x (vertex x)              == Set.empty" $ \(x :: Int) ->+          postSet x (vertex x)              == Set.empty++    test "postSet 1 (shrink $ 1 * 2)        == Set.fromList [2]" $+          postSet 1 (shrink $ 1 * 2 :: AAI) == Set.fromList [2]++    test "postSet 2 (shrink $ 1 * 2)        == Set.empty" $+          postSet 2 (shrink $ 1 * 2 :: AAI) == Set.empty++    test "Set.member x . postSet x          == const False" $ \(x :: Int) y ->+         (Set.member x . postSet x) y       == const False y++    putStrLn "\n============ Acyclic.AdjacencyMap.removeVertex ============"+    test "removeVertex x (vertex x)              == empty" $ \(x :: Int) ->+          removeVertex x (vertex x)              == empty++    test "removeVertex 1 (vertex 2)              == vertex 2" $+          removeVertex 1 (vertex 2 :: AAI)       == vertex 2++    test "removeVertex 1 (shrink $ 1 * 2)        == vertex 2" $+          removeVertex 1 (shrink $ 1 * 2 :: AAI) == vertex 2++    test "removeVertex x . removeVertex x        == removeVertex x" $ \(x :: Int) y ->+         (removeVertex x . removeVertex x) y     == removeVertex x y++    putStrLn "\n============ Acyclic.AdjacencyMap.removeEdge ============"+    test "removeEdge 1 2 (shrink $ 1 * 2)            == vertices [1,2]" $+          removeEdge 1 2 (shrink $ 1 * 2 :: AAI)     == vertices [1,2]++    test "removeEdge x y . removeEdge x y            == removeEdge x y" $ \(x :: Int) y z ->+         (removeEdge x y . removeEdge x y) z         == removeEdge x y z++    test "removeEdge x y . removeVertex x            == removeVertex x" $ \(x :: Int) y z ->+         (removeEdge x y . removeVertex x) z         == removeVertex x z++    test "removeEdge 1 2 (shrink $ 1 * 2 * 3)        == shrink ((1 + 2) * 3)" $+          removeEdge 1 2 (shrink $ 1 * 2 * 3 :: AAI) == shrink ((1 + 2) * 3)++    putStrLn "\n============ Acyclic.AdjacencyMap.transpose ============"+    test "transpose empty          == empty" $+          transpose (empty :: AAI) == empty++    test "transpose (vertex x)     == vertex x" $ \(x :: Int) ->+          transpose (vertex x)     == vertex x++    test "transpose . transpose    == id" $ size10 $ \(x :: AAI) ->+         (transpose . transpose) x == id x++    test "edgeList . transpose     == sort . map swap . edgeList" $ \(x :: AAI) ->+         (edgeList . transpose) x  == (sort . map swap . edgeList) x++    putStrLn "\n============ Acyclic.AdjacencyMap.induce ============"+    test "induce (const True ) x      == x" $ \(x :: AAI) ->+          induce (const True ) x      == x++    test "induce (const False) x      == empty" $ \(x :: AAI) ->+          induce (const False) x      == empty++    test "induce (/= x)               == removeVertex x" $ \x (y :: AAI) ->+          induce (/= x) y             == removeVertex x y++    test "induce p . induce q         == induce (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) (y :: AAI) ->+         (induce p . induce q) y      == induce (\x -> p x && q x) y++    test "isSubgraphOf (induce p x) x == True" $ \(apply -> p) (x :: AAI) ->+          isSubgraphOf (induce p x) x == True++    putStrLn "\n============ Acyclic.AdjacencyMap.induceJust ============"+    test "induceJust (vertex Nothing)   == empty" $+          induceJust (vertex Nothing)   == (empty :: AAI)++    test "induceJust . vertex . Just    == vertex" $ \(x :: Int) ->+         (induceJust . vertex . Just) x == vertex x++    putStrLn "\n============ Acyclic.AdjacencyMap.box ============"+    test "edgeList (box (shrink $ 1 * 2) (shrink $ 10 * 20)) == <correct result>\n" $+          edgeList (box (shrink $ 1 * 2) (shrink $ 10 * 20)) == [ ((1,10), (1,20))+                                                                , ((1,10), (2,10))+                                                                , ((1,20), (2,20))+                                                                , ((2,10), (2 :: Int,20 :: Int)) ]++    let gmap f = shrink . AM.gmap f . fromAcyclic+        unit = gmap $ \(a :: Int, ()      ) -> a+        comm = gmap $ \(a :: Int, b :: Int) -> (b, a)+    test "box x y               ~~ box y x" $ size10 $ \x y ->+          comm (box x y)        == box y x++    test "box x (vertex ())     ~~ x" $ size10 $ \x ->+     unit(box x (vertex ()))    == (x `asTypeOf` empty)++    test "box x empty           ~~ empty" $ size10 $ \x ->+     unit(box x empty)          == empty++    let assoc = gmap $ \(a :: Int, (b :: Int, c :: Int)) -> ((a, b), c)+    test "box x (box y z)       ~~ box (box x y) z" $ size10 $ \x y z ->+      assoc (box x (box y z))   == box (box x y) z++    test "transpose   (box x y) == box (transpose x) (transpose y)" $ size10 $ \(x :: AAI) (y :: AAI) ->+          transpose   (box x y) == box (transpose x) (transpose y)++    test "vertexCount (box x y) == vertexCount x * vertexCount y" $ size10 $ \(x :: AAI) (y :: AAI) ->+          vertexCount (box x y) == vertexCount x * vertexCount y++    test "edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y" $ size10 $ \(x :: AAI) (y :: AAI) ->+          edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y++    putStrLn "\n============ Acyclic.AdjacencyMap.transitiveClosure ============"+    test "transitiveClosure empty               == empty" $+          transitiveClosure empty               == (empty :: AAI)++    test "transitiveClosure (vertex x)          == vertex x" $ \(x :: Int) ->+          transitiveClosure (vertex x)          == vertex x++    test "transitiveClosure (shrink $ 1 * 2 + 2 * 3)         == shrink (1 * 2 + 1 * 3 + 2 * 3)" $+          transitiveClosure (shrink $ 1 * 2 + 2 * 3  :: AAI) == shrink (1 * 2 + 1 * 3 + 2 * 3)++    test "transitiveClosure . transitiveClosure    == transitiveClosure" $ \(x :: AAI) ->+         (transitiveClosure . transitiveClosure) x == transitiveClosure x++    putStrLn "\n============ Acyclic.AdjacencyMap.topSort ============"+    test "topSort empty                          == []" $+          topSort (empty :: AAI)                 == []++    test "topSort (vertex x)                     == [x]" $ \(x :: Int) ->+          topSort (vertex x)                     == [x]++    test "topSort (shrink $ 1 * (2 + 4) + 3 * 4) == [1, 2, 3, 4]" $+          topSort (shrink $ 1 * (2 + 4) + 3 * 4) == [1, 2, 3, 4 :: Int]++    test "topSort (join x y)                     == fmap Left (topSort x) ++ fmap Right (topSort y)" $ \(x :: AAI) (y :: AAI) ->+          topSort (join x y)                     == fmap Left (topSort x) ++ fmap Right (topSort y)++    test "Right . topSort                        == AM.topSort . fromAcyclic" $ \(x :: AAI) ->+          Right (topSort x)                      == AM.topSort (fromAcyclic x)++    putStrLn "\n============ Acyclic.AdjacencyMap.scc ============"+    test "           scc empty               == empty" $+                     scc (AM.empty :: AI)    == empty++    test "           scc (vertex x)          == vertex (NonEmpty.vertex x)" $ \(x :: Int) ->+                     scc (AM.vertex x)       == vertex (NonEmpty.vertex x)++    test "           scc (edge 1 1)          == vertex (NonEmpty.edge 1 1)" $+                     scc (AM.edge 1 1 :: AI) == vertex (NonEmpty.edge 1 1)++    test "edgeList $ scc (edge 1 2)          == [ (NonEmpty.vertex 1       , NonEmpty.vertex 2       ) ]" $+          edgeList (scc (AM.edge 1 2 :: AI)) == [ (NonEmpty.vertex 1       , NonEmpty.vertex 2       ) ]++    test "edgeList $ scc (3 * 1 * 4 * 1 * 5) == <correct result>" $+          edgeList (scc (3 * 1 * 4 * 1 * 5)) == [ (NonEmpty.vertex 3       , NonEmpty.vertex (5 :: Int))+                                                , (NonEmpty.vertex 3       , NonEmpty.clique1 [1,4,1])+                                                , (NonEmpty.clique1 [1,4,1], NonEmpty.vertex 5       ) ]++    putStrLn "\n============ Acyclic.AdjacencyMap.toAcyclic ============"+    test "toAcyclic (path    [1,2,3])           == Just (shrink $ 1 * 2 + 2 * 3)" $+          toAcyclic (AM.path [1,2,3])           == Just (shrink $ 1 * 2 + 2 * 3 :: AAI)++    test "toAcyclic (clique  [3,2,1])           == Just (transpose (shrink $ 1 * 2 * 3))" $+          toAcyclic (AM.clique [3,2,1])         == Just (transpose (shrink $ 1 * 2 * 3 :: AAI))++    test "toAcyclic (circuit [1,2,3])           == Nothing" $+          toAcyclic (AM.circuit [1,2,3 :: Int]) == Nothing++    test "toAcyclic . fromAcyclic               == Just" $ \(x :: AAI) ->+         (toAcyclic . fromAcyclic) x            == Just x++    putStrLn "\n============ Acyclic.AdjacencyMap.toAcyclicOrd ============"+    test "toAcyclicOrd empty          == empty" $+          toAcyclicOrd AM.empty       == (empty :: AAI)++    test "toAcyclicOrd . vertex       == vertex" $ \(x :: Int) ->+         (toAcyclicOrd . AM.vertex) x == vertex x++    test "toAcyclicOrd (1 + 2)        == shrink (1 + 2)" $+          toAcyclicOrd (1 + 2)        == (shrink $ 1 + 2 :: AAI)++    test "toAcyclicOrd (1 * 2)        == shrink (1 * 2)" $+          toAcyclicOrd (1 * 2)        == (shrink $ 1 * 2 :: AAI)++    test "toAcyclicOrd (2 * 1)        == shrink (1 + 2)" $+          toAcyclicOrd (2 * 1)        == (shrink $ 1 + 2 :: AAI)++    test "toAcyclicOrd (1 * 2 * 1)    == shrink (1 * 2)" $+          toAcyclicOrd (1 * 2 * 1)    == (shrink $ 1 * 2 :: AAI)++    test "toAcyclicOrd (1 * 2 * 3)    == shrink (1 * 2 * 3)" $+          toAcyclicOrd (1 * 2 * 3)    == (shrink $ 1 * 2 * 3 :: AAI)+++    putStrLn "\n============ Acyclic.AdjacencyMap.shrink ============"+    test "shrink . AM.vertex       == vertex" $ \x ->+          (shrink . AM.vertex) x   == (vertex x :: AAI)++    test "shrink . AM.vertices     == vertices" $ \x ->+          (shrink . AM.vertices) x == (vertices x :: AAI)++    test "shrink . fromAcyclic     == id" $ \(x :: AAI) ->+          (shrink . fromAcyclic) x == id x++    putStrLn "\n============ Acyclic.AdjacencyMap.consistent ============"+    test "Arbitrary"         $ \(x :: AAI)            -> consistent x+    test "empty"             $                           consistent (empty :: AAI)+    test "vertex"            $ \(x :: Int)            -> consistent (vertex x)+    test "vertices"          $ \(xs :: [Int])         -> consistent (vertices xs)+    test "union"             $ \(x :: AAI) (y :: AAI) -> consistent (union x y)+    test "join"              $ \(x :: AAI) (y :: AAI) -> consistent (join x y)+    test "transpose"         $ \(x :: AAI)            -> consistent (transpose x)+    test "box"      $ size10 $ \(x :: AAI) (y :: AAI) -> consistent (box x y)+    test "transitiveClosure" $ \(x :: AAI)            -> consistent (transitiveClosure x)+    test "scc"               $ \(x :: AI)             -> consistent (scc x)+    test "toAcyclic"         $ \(x :: AI)        -> fmap consistent (toAcyclic x) /= Just False+    test "toAcyclicOrd"      $ \(x :: AI)             -> consistent (toAcyclicOrd x)
test/Algebra/Graph/Test/AdjacencyIntMap.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.AdjacencyIntMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2020 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -11,20 +11,27 @@ module Algebra.Graph.Test.AdjacencyIntMap (     -- * Testsuite     testAdjacencyIntMap-  ) where+    ) where  import Algebra.Graph.AdjacencyIntMap import Algebra.Graph.Test+import Algebra.Graph.Test.API (Mono (..), adjacencyIntMapAPI) import Algebra.Graph.Test.Generic -t :: Testsuite-t = testsuite "AdjacencyIntMap." empty+import qualified Algebra.Graph.AdjacencyMap as AdjacencyMap +t :: TestsuiteInt (Mono AdjacencyIntMap)+t = ("AdjacencyIntMap.", adjacencyIntMapAPI)+ testAdjacencyIntMap :: IO () testAdjacencyIntMap = do     putStrLn "\n============ AdjacencyIntMap ============"-    test "Axioms of graphs" (axioms :: GraphTestsuite AdjacencyIntMap)+    test "Axioms of graphs" (axioms @ AdjacencyIntMap) +    putStrLn $ "\n============ AdjacencyIntMap.fromAdjacencyMap ============"+    test "fromAdjacencyMap == stars . AdjacencyMap.adjacencyList" $ \x ->+          fromAdjacencyMap x == (stars . AdjacencyMap.adjacencyList) x+     testConsistent           t     testShow                 t     testBasicPrimitives      t@@ -34,6 +41,8 @@     testGraphFamilies        t     testTransformations      t     testRelational           t+    testBfsForest            t+    testBfs                  t     testDfsForest            t     testDfsForestFrom        t     testDfs                  t
test/Algebra/Graph/Test/AdjacencyMap.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.AdjacencyMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2020 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,26 +12,31 @@ module Algebra.Graph.Test.AdjacencyMap (     -- * Testsuite     testAdjacencyMap-  ) where+    ) where  import Data.List.NonEmpty  import Algebra.Graph.AdjacencyMap import Algebra.Graph.AdjacencyMap.Algorithm import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, adjacencyMapAPI) import Algebra.Graph.Test.Generic  import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty+import qualified Data.Graph.Typed                    as KL -t :: Testsuite-t = testsuite "AdjacencyMap." empty+tPoly :: Testsuite AdjacencyMap Ord+tPoly = ("AdjacencyMap.", adjacencyMapAPI) +t :: TestsuiteInt AdjacencyMap+t = fmap toIntAPI tPoly+ type AI = AdjacencyMap Int  testAdjacencyMap :: IO () testAdjacencyMap = do     putStrLn "\n============ AdjacencyMap ============"-    test "Axioms of graphs" (axioms :: GraphTestsuite AI)+    test "Axioms of graphs" (axioms @ AI)      testConsistent        t     testShow              t@@ -42,6 +47,9 @@     testGraphFamilies     t     testTransformations   t     testRelational        t+    testBox               tPoly+    testBfsForest         t+    testBfs               t     testDfsForest         t     testDfsForestFrom     t     testDfs               t@@ -50,6 +58,7 @@     testIsAcyclic         t     testIsDfsForestOf     t     testIsTopSortOf       t+    testInduceJust        tPoly      putStrLn "\n============ AdjacencyMap.scc ============"     test "scc empty               == empty" $@@ -58,6 +67,9 @@     test "scc (vertex x)          == vertex (NonEmpty.vertex x)" $ \(x :: Int) ->           scc (vertex x)          == vertex (NonEmpty.vertex x) +    test "scc (vertices xs)       == vertices (map NonEmpty.vertex xs)" $ \(xs :: [Int]) ->+          scc (vertices xs)       == vertices (Prelude.map NonEmpty.vertex xs)+     test "scc (edge 1 1)          == vertex (NonEmpty.edge 1 1)" $           scc (edge 1 1 :: AI)    == vertex (NonEmpty.edge 1 1) @@ -77,3 +89,6 @@      test "isAcyclic x     == (scc x == gmap NonEmpty.vertex x)" $ \(x :: AI) ->           isAcyclic x     == (scc x == gmap NonEmpty.vertex x)++    test "scc g == KL.scc g" $ \(g :: AI) ->+          scc g == KL.scc g
test/Algebra/Graph/Test/Arbitrary.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Arbitrary--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -11,11 +11,8 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Test.Arbitrary (     -- * Generators of arbitrary graph instances-    arbitraryGraph, arbitraryRelation, arbitraryAdjacencyMap, arbitraryAdjacencyIntMap-  ) where--import Prelude ()-import Prelude.Compat+    arbitraryGraph, arbitraryRelation, arbitraryAdjacencyMap,+    ) where  import Control.Monad import Data.List.NonEmpty (NonEmpty (..), toList)@@ -24,24 +21,24 @@ import Test.QuickCheck  import Algebra.Graph-import Algebra.Graph.AdjacencyMap.Internal-import Algebra.Graph.AdjacencyIntMap.Internal import Algebra.Graph.Export-import Algebra.Graph.Fold (Fold) import Algebra.Graph.Label-import Algebra.Graph.Relation.InternalDerived-import Algebra.Graph.Relation.Symmetric.Internal -import qualified Algebra.Graph.AdjacencyIntMap       as AdjacencyIntMap-import qualified Algebra.Graph.AdjacencyMap          as AdjacencyMap-import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NAM-import qualified Algebra.Graph.Class                 as C-import qualified Algebra.Graph.Fold                  as Fold-import qualified Algebra.Graph.Labelled              as LG-import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM-import qualified Algebra.Graph.NonEmpty              as NonEmpty-import qualified Algebra.Graph.Relation              as Relation-import qualified Algebra.Graph.Relation.Symmetric    as Symmetric+import qualified Algebra.Graph.Undirected                        as UG+import qualified Algebra.Graph.Acyclic.AdjacencyMap              as AAM+import qualified Algebra.Graph.AdjacencyIntMap                   as AIM+import qualified Algebra.Graph.AdjacencyMap                      as AM+import qualified Algebra.Graph.Bipartite.Undirected.AdjacencyMap as BAM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap             as NAM+import qualified Algebra.Graph.Class                             as C+import qualified Algebra.Graph.Labelled                          as LG+import qualified Algebra.Graph.Labelled.AdjacencyMap             as LAM+import qualified Algebra.Graph.NonEmpty                          as NonEmpty+import qualified Algebra.Graph.Relation                          as Relation+import qualified Algebra.Graph.Relation.Preorder                 as Preorder+import qualified Algebra.Graph.Relation.Reflexive                as Reflexive+import qualified Algebra.Graph.Relation.Symmetric                as Symmetric+import qualified Algebra.Graph.Relation.Transitive               as Transitive  -- | Generate an arbitrary 'C.Graph' value of a specified size. arbitraryGraph :: (C.Graph g, Arbitrary (C.Vertex g)) => Gen g@@ -64,19 +61,23 @@     shrink (Connect x y) = [Empty, x, y, Overlay x y]                         ++ [Connect x' y' | (x', y') <- shrink (x, y) ] -instance (Eq a, Ord a, Arbitrary a) => Arbitrary (Fold a) where+-- An Arbitrary instance for Graph.Undirected+instance Arbitrary a => Arbitrary (UG.Graph a) where     arbitrary = arbitraryGraph -    shrink g = oneLessVertex ++ oneLessEdge-      where-         oneLessVertex =-           let vertices = Fold.vertexList g-           in  [ Fold.removeVertex v g | v <- vertices ]+-- An Arbitrary instance for Acyclic.AdjacencyMap+instance (Ord a, Arbitrary a) => Arbitrary (AAM.AdjacencyMap a) where+    arbitrary = AAM.shrink <$> arbitrary -         oneLessEdge =-           let edges = Fold.edgeList g-           in  [ Fold.removeEdge v w g | (v, w) <- edges ]+    shrink g = shrinkVertices ++ shrinkEdges+      where+        shrinkVertices =+          let vertices = AAM.vertexList g+          in [ AAM.removeVertex x g | x <- vertices ] +        shrinkEdges =+          let edges = AAM.edgeList g+          in [ AAM.removeEdge x y g | (x, y) <- edges ]  -- | Generate an arbitrary 'NonEmpty.Graph' value of a specified size. arbitraryNonEmptyGraph :: Arbitrary a => Gen (NonEmpty.Graph a)@@ -106,46 +107,44 @@ instance (Arbitrary a, Ord a) => Arbitrary (Relation.Relation a) where     arbitrary = arbitraryRelation -    shrink g = oneLessVertex ++ oneLessEdge+    shrink g = shrinkVertices ++ shrinkEdges       where-         oneLessVertex =+         shrinkVertices =            let vertices = Relation.vertexList g            in  [ Relation.removeVertex v g | v <- vertices ] -         oneLessEdge =+         shrinkEdges =            let edges = Relation.edgeList g            in  [ Relation.removeEdge v w g | (v, w) <- edges ] --instance (Arbitrary a, Ord a) => Arbitrary (ReflexiveRelation a) where-    arbitrary = ReflexiveRelation <$> arbitraryRelation+-- TODO: Simplify.+instance (Arbitrary a, Ord a) => Arbitrary (Reflexive.ReflexiveRelation a) where+    arbitrary = Reflexive.fromRelation . Relation.reflexiveClosure+        <$> arbitraryRelation  instance (Arbitrary a, Ord a) => Arbitrary (Symmetric.Relation a) where-    arbitrary = SR . Relation.symmetricClosure <$> arbitraryRelation+    arbitrary = Symmetric.toSymmetric <$> arbitraryRelation -instance (Arbitrary a, Ord a) => Arbitrary (TransitiveRelation a) where-    arbitrary = TransitiveRelation <$> arbitraryRelation+instance (Arbitrary a, Ord a) => Arbitrary (Transitive.TransitiveRelation a) where+    arbitrary = Transitive.fromRelation . Relation.transitiveClosure+        <$> arbitraryRelation -instance (Arbitrary a, Ord a) => Arbitrary (PreorderRelation a) where-    arbitrary = PreorderRelation <$> arbitraryRelation+instance (Arbitrary a, Ord a) => Arbitrary (Preorder.PreorderRelation a) where+    arbitrary = Preorder.fromRelation . Relation.closure+        <$> arbitraryRelation  -- | Generate an arbitrary 'AdjacencyMap'. It is guaranteed that the -- resulting adjacency map is 'consistent'.-arbitraryAdjacencyMap :: (Arbitrary a, Ord a) => Gen (AdjacencyMap a)-arbitraryAdjacencyMap = AdjacencyMap.stars <$> arbitrary+arbitraryAdjacencyMap :: (Arbitrary a, Ord a) => Gen (AM.AdjacencyMap a)+arbitraryAdjacencyMap = AM.stars <$> arbitrary -instance (Arbitrary a, Ord a) => Arbitrary (AdjacencyMap a) where+instance (Arbitrary a, Ord a) => Arbitrary (AM.AdjacencyMap a) where     arbitrary = arbitraryAdjacencyMap -    shrink g = oneLessVertex ++ oneLessEdge+    shrink g = shrinkVertices ++ shrinkEdges       where-         oneLessVertex =-           let vertices = AdjacencyMap.vertexList g-           in  [ AdjacencyMap.removeVertex v g | v <- vertices ]--         oneLessEdge =-           let edges = AdjacencyMap.edgeList g-           in  [ AdjacencyMap.removeEdge v w g | (v, w) <- edges ]+         shrinkVertices = [ AM.removeVertex v g | v <- AM.vertexList g ]+         shrinkEdges    = [ AM.removeEdge v w g | (v, w) <- AM.edgeList g ]  -- | Generate an arbitrary non-empty 'NAM.AdjacencyMap'. It is guaranteed that -- the resulting adjacency map is 'consistent'.@@ -163,33 +162,23 @@ instance (Arbitrary a, Ord a) => Arbitrary (NAM.AdjacencyMap a) where     arbitrary = arbitraryNonEmptyAdjacencyMap -    shrink g = oneLessVertex ++ oneLessEdge+    shrink g = shrinkVertices ++ shrinkEdges       where-         oneLessVertex =+         shrinkVertices =            let vertices = toList $ NAM.vertexList1 g            in catMaybes [ NAM.removeVertex1 v g | v <- vertices ] -         oneLessEdge =+         shrinkEdges =            let edges = NAM.edgeList g            in  [ NAM.removeEdge v w g | (v, w) <- edges ] --- | Generate an arbitrary 'AdjacencyIntMap'. It is guaranteed that the--- resulting adjacency map is 'consistent'.-arbitraryAdjacencyIntMap :: Gen AdjacencyIntMap-arbitraryAdjacencyIntMap = AdjacencyIntMap.stars <$> arbitrary--instance Arbitrary AdjacencyIntMap where-    arbitrary = arbitraryAdjacencyIntMap+instance Arbitrary AIM.AdjacencyIntMap where+    arbitrary = AIM.stars <$> arbitrary -    shrink g = oneLessVertex ++ oneLessEdge+    shrink g = shrinkVertices ++ shrinkEdges       where-         oneLessVertex =-           let vertices = AdjacencyIntMap.vertexList g-           in  [ AdjacencyIntMap.removeVertex v g | v <- vertices ]--         oneLessEdge =-           let edges = AdjacencyIntMap.edgeList g-           in  [ AdjacencyIntMap.removeEdge v w g | (v, w) <- edges ]+         shrinkVertices = [ AIM.removeVertex x g | x <- AIM.vertexList g ]+         shrinkEdges    = [ AIM.removeEdge x y g | (x, y) <- AIM.edgeList g ]  -- | Generate an arbitrary labelled 'LAM.AdjacencyMap'. It is guaranteed -- that the resulting adjacency map is 'consistent'.@@ -199,13 +188,13 @@ instance (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Arbitrary (LAM.AdjacencyMap e a) where     arbitrary = arbitraryLabelledAdjacencyMap -    shrink g = oneLessVertex ++ oneLessEdge+    shrink g = shrinkVertices ++ shrinkEdges       where-         oneLessVertex =+         shrinkVertices =            let vertices = LAM.vertexList g            in  [ LAM.removeVertex v g | v <- vertices ] -         oneLessEdge =+         shrinkEdges =            let edges = LAM.edgeList g            in  [ LAM.removeEdge v w g | (_, v, w) <- edges ] @@ -249,3 +238,22 @@  instance (Arbitrary a, Num a, Ord a) => Arbitrary (Distance a) where     arbitrary = (\x -> if x < 0 then distance infinite else distance (unsafeFinite x)) <$> arbitrary++instance (Arbitrary a, Num a, Ord a) => Arbitrary (Capacity a) where+    arbitrary = (\x -> if x < 0 then capacity infinite else capacity (unsafeFinite x)) <$> arbitrary++instance (Arbitrary a, Num a, Ord a) => Arbitrary (Count a) where+    arbitrary = (\x -> if x < 0 then count infinite else count (unsafeFinite x)) <$> arbitrary++instance Arbitrary a => Arbitrary (Minimum a) where+    arbitrary = frequency [(10, pure <$> arbitrary), (1, pure noMinimum)]++instance (Arbitrary a, Ord a) => Arbitrary (PowerSet a) where+    arbitrary = PowerSet <$> arbitrary++instance (Arbitrary o, Arbitrary a) => Arbitrary (Optimum o a) where+    arbitrary = Optimum <$> arbitrary <*> arbitrary++instance (Arbitrary a, Arbitrary b, Ord a, Ord b) => Arbitrary (BAM.AdjacencyMap a b) where+    arbitrary = BAM.toBipartite <$> arbitrary+    shrink = map BAM.toBipartite . shrink . BAM.fromBipartite
+ test/Algebra/Graph/Test/Bipartite/Undirected/AdjacencyMap.hs view
@@ -0,0 +1,628 @@+{-# LANGUAGE ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test.Bipartite.Undirected.AdjacencyMap+-- Copyright  : (c) Andrey Mokhov 2016-2020+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Testsuite for "Algebra.Graph.Bipartite.Undirected.AdjacencyMap".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Bipartite.Undirected.AdjacencyMap (+    -- * Testsuite+    testBipartiteUndirectedAdjacencyMap+    ) where++import Algebra.Graph.Bipartite.Undirected.AdjacencyMap+import Algebra.Graph.Test+import Data.Either+import Data.Either.Extra+import Data.List+import Data.Map.Strict (Map)+import Data.Set (Set)++import qualified Algebra.Graph.AdjacencyMap                      as AM+import qualified Algebra.Graph.Bipartite.Undirected.AdjacencyMap as B+import qualified Data.Map.Strict                                 as Map+import qualified Data.Set                                        as Set+import qualified Data.Tuple++type AI   = AM.AdjacencyMap Int+type AII  = AM.AdjacencyMap (Either Int Int)+type BAII = AdjacencyMap Int Int++testBipartiteUndirectedAdjacencyMap :: IO ()+testBipartiteUndirectedAdjacencyMap = do+    -- Help with type inference by shadowing overly polymorphic functions+    let consistent :: BAII -> Bool+        consistent = B.consistent+        show :: BAII -> String+        show = Prelude.show+        leftAdjacencyMap :: BAII -> Map Int (Set Int)+        leftAdjacencyMap = B.leftAdjacencyMap+        rightAdjacencyMap :: BAII -> Map Int (Set Int)+        empty :: BAII+        empty = B.empty+        vertex :: Either Int Int -> BAII+        vertex = B.vertex+        leftVertex :: Int -> BAII+        leftVertex = B.leftVertex+        rightVertex :: Int -> BAII+        rightVertex = B.rightVertex+        edge :: Int -> Int -> BAII+        edge = B.edge+        rightAdjacencyMap = B.rightAdjacencyMap+        isEmpty :: BAII -> Bool+        isEmpty = B.isEmpty+        hasLeftVertex :: Int -> BAII -> Bool+        hasLeftVertex = B.hasLeftVertex+        hasRightVertex :: Int -> BAII -> Bool+        hasRightVertex = B.hasRightVertex+        hasVertex :: Either Int Int -> BAII -> Bool+        hasVertex = B.hasVertex+        hasEdge :: Int -> Int -> BAII -> Bool+        hasEdge = B.hasEdge+        vertexCount :: BAII -> Int+        vertexCount = B.vertexCount+        edgeCount :: BAII -> Int+        edgeCount = B.edgeCount+        vertices :: [Int] -> [Int] -> BAII+        vertices = B.vertices+        edges :: [(Int, Int)] -> BAII+        edges = B.edges+        overlays :: [BAII] -> BAII+        overlays = B.overlays+        connects :: [BAII] -> BAII+        connects = B.connects+        swap :: BAII -> BAII+        swap = B.swap+        toBipartite :: AII -> BAII+        toBipartite = B.toBipartite+        toBipartiteWith :: Ord a => (a -> Either Int Int) -> AM.AdjacencyMap a -> BAII+        toBipartiteWith = B.toBipartiteWith+        fromBipartite :: BAII -> AII+        fromBipartite = B.fromBipartite+        biclique :: [Int] -> [Int] -> BAII+        biclique = B.biclique++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.consistent ============"+    test "consistent empty            == True" $+          consistent empty            == True+    test "consistent (vertex x)       == True" $ \x ->+          consistent (vertex x)       == True+    test "consistent (edge x y)       == True" $ \x y ->+          consistent (edge x y)       == True+    test "consistent (edges x)        == True" $ \x ->+          consistent (edges x)        == True+    test "consistent (toBipartite x)  == True" $ \x ->+          consistent (toBipartite x)  == True+    test "consistent (swap x)         == True" $ \x ->+          consistent (swap x)         == True+    test "consistent (biclique xs ys) == True" $ \xs ys ->+          consistent (biclique xs ys) == True+    test "consistent (circuit xs)     == True" $ \xs ->+          consistent (circuit xs)     == True++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.leftAdjacencyMap ============"+    test "leftAdjacencyMap empty           == Map.empty" $+          leftAdjacencyMap empty           == Map.empty+    test "leftAdjacencyMap (leftVertex x)  == Map.singleton x Set.empty" $ \x ->+          leftAdjacencyMap (leftVertex x)  == Map.singleton x Set.empty+    test "leftAdjacencyMap (rightVertex x) == Map.empty" $ \x ->+          leftAdjacencyMap (rightVertex x) == Map.empty+    test "leftAdjacencyMap (edge x y)      == Map.singleton x (Set.singleton y)" $ \x y ->+          leftAdjacencyMap (edge x y)      == Map.singleton x (Set.singleton y)++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.rightAdjacencyMap ============"+    test "rightAdjacencyMap empty           == Map.empty" $+          rightAdjacencyMap empty           == Map.empty+    test "rightAdjacencyMap (leftVertex x)  == Map.empty" $ \x ->+          rightAdjacencyMap (leftVertex x)  == Map.empty+    test "rightAdjacencyMap (rightVertex x) == Map.singleton x Set.empty" $ \x ->+          rightAdjacencyMap (rightVertex x) == Map.singleton x Set.empty+    test "rightAdjacencyMap (edge x y)      == Map.singleton y (Set.singleton x)" $ \x y ->+          rightAdjacencyMap (edge x y)      == Map.singleton y (Set.singleton x)++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.Num ============"+    test "0                     == rightVertex 0" $+          0                     == rightVertex 0+    test "swap 1                == leftVertex 1" $+          swap 1                == leftVertex 1+    test "swap 1 + 2            == vertices [1] [2]" $+          swap 1 + 2            == vertices [1] [2]+    test "swap 1 * 2            == edge 1 2" $+          swap 1 * 2            == edge 1 2+    test "swap 1 + 2 * swap 3   == overlay (leftVertex 1) (edge 3 2)" $+          swap 1 + 2 * swap 3   == overlay (leftVertex 1) (edge 3 2)+    test "swap 1 * (2 + swap 3) == connect (leftVertex 1) (vertices [3] [2])" $+          swap 1 * (2 + swap 3) == connect (leftVertex 1) (vertices [3] [2])++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.Eq ============"+    test "(x == y) == (leftAdjacencyMap x == leftAdjacencyMap y && rightAdjacencyMap x == rightAdjacencyMap y)" $ \(x :: BAII) (y :: BAII) ->+          (x == y) == (leftAdjacencyMap x == leftAdjacencyMap y && rightAdjacencyMap x == rightAdjacencyMap y)++    putStrLn ""+    test "      x + y == y + x" $ \(x :: BAII) y ->+                x + y == y + x+    test "x + (y + z) == (x + y) + z" $ \(x :: BAII) y z ->+          x + (y + z) == (x + y) + z+    test "  x * empty == x" $ \(x :: BAII) ->+            x * empty == x+    test "  empty * x == x" $ \(x :: BAII) ->+            empty * x == x+    test "      x * y == y * x" $ \(x :: BAII) y ->+                x * y == y * x+    test "x * (y * z) == (x * y) * z" $ size10 $ \(x :: BAII) y z ->+          x * (y * z) == (x * y) * z+    test "x * (y + z) == x * y + x * z" $ size10 $ \(x :: BAII) y z ->+          x * (y + z) == x * (y + z)+    test "(x + y) * z == x * z + y * z" $ size10 $ \(x :: BAII) y z ->+          (x + y) * z == x * z + y * z+    test "  x * y * z == x * y + x * z + y * z" $ size10 $ \(x :: BAII) y z ->+            x * y * z == x * y + x * z + y * z+    test "  x + empty == x" $ \(x :: BAII) ->+            x + empty == x+    test "  empty + x == x" $ \(x :: BAII) ->+            empty + x == x+    test "      x + x == x" $ \(x :: BAII) ->+                x + x == x+    test "x * y + x + y == x * y" $ \(x :: BAII) (y :: BAII) ->+          x * y + x + y == x * y+    test "    x * x * x == x * x" $ size10 $ \(x :: BAII) ->+              x * x * x == x * x++    putStrLn ""+    test " leftVertex x * leftVertex y  ==  leftVertex x + leftVertex y " $ \(x :: Int) y ->+           leftVertex x * leftVertex y  ==  leftVertex x + leftVertex y+    test "rightVertex x * rightVertex y == rightVertex x + rightVertex y" $ \(x :: Int) y ->+          rightVertex x * rightVertex y == rightVertex x + rightVertex y++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.Show ============"+    test "show empty                 == \"empty\"" $+          show empty                 == "empty"+    test "show 1                     == \"rightVertex 1\"" $+          show 1                     == "rightVertex 1"+    test "show (swap 2)              == \"leftVertex 2\"" $+          show (swap 2)              == "leftVertex 2"+    test "show 1 + 2                 == \"vertices [] [1,2]\"" $+          show (1 + 2)               == "vertices [] [1,2]"+    test "show (swap (1 + 2))        == \"vertices [1,2] []\"" $+          show (swap (1 + 2))        == "vertices [1,2] []"+    test "show (swap 1 * 2)          == \"edge 1 2\"" $+          show (swap 1 * 2)          == "edge 1 2"+    test "show (swap 1 * 2 * swap 3) == \"edges [(1,2),(3,2)]\"" $+          show (swap 1 * 2 * swap 3) == "edges [(1,2),(3,2)]"+    test "show (swap 1 * 2 + swap 3) == \"overlay (leftVertex 3) (edge 1 2)\"" $+          show (swap 1 * 2 + swap 3) == "overlay (leftVertex 3) (edge 1 2)"++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.empty ============"+    test "isEmpty empty           == True" $+          isEmpty empty           == True+    test "leftAdjacencyMap empty  == Map.empty" $+          leftAdjacencyMap empty  == Map.empty+    test "rightAdjacencyMap empty == Map.empty" $+          rightAdjacencyMap empty == Map.empty+    test "hasVertex x empty       == False" $ \x ->+          hasVertex x empty       == False++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.leftVertex ============"+    test "leftAdjacencyMap (leftVertex x)  == Map.singleton x Set.empty" $ \x ->+          leftAdjacencyMap (leftVertex x)  == Map.singleton x Set.empty+    test "rightAdjacencyMap (leftVertex x) == Map.empty" $ \x ->+          rightAdjacencyMap (leftVertex x) == Map.empty+    test "hasLeftVertex x (leftVertex y)   == (x == y)" $ \x y ->+          hasLeftVertex x (leftVertex y)   == (x == y)+    test "hasRightVertex x (leftVertex y)  == False" $ \x y ->+          hasRightVertex x (leftVertex y)  == False+    test "hasEdge x y (leftVertex z)       == False" $ \x y z ->+          hasEdge x y (leftVertex z)       == False++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.rightVertex ============"+    test "leftAdjacencyMap (rightVertex x)  == Map.empty" $ \x ->+          leftAdjacencyMap (rightVertex x)  == Map.empty+    test "rightAdjacencyMap (rightVertex x) == Map.singleton x Set.empty" $  \x ->+          rightAdjacencyMap (rightVertex x) == Map.singleton x Set.empty+    test "hasLeftVertex x (rightVertex y)   == False" $ \x y ->+          hasLeftVertex x (rightVertex y)   == False+    test "hasRightVertex x (rightVertex y)  == (x == y)" $ \x y ->+          hasRightVertex x (rightVertex y)  == (x == y)+    test "hasEdge x y (rightVertex z)       == False" $ \x y z ->+          hasEdge x y (rightVertex z)       == False++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.vertex ============"+    test "vertex (Left x)  == leftVertex x" $ \x ->+          vertex (Left x)  == leftVertex x+    test "vertex (Right x) == rightVertex x" $ \x ->+          vertex (Right x) == rightVertex x++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.edge ============"+    test "edge x y                     == connect (leftVertex x) (rightVertex y)" $ \x y ->+          edge x y                     == connect (leftVertex x) (rightVertex y)+    test "leftAdjacencyMap (edge x y)  == Map.singleton x (Set.singleton y)" $ \x y ->+          leftAdjacencyMap (edge x y)  == Map.singleton x (Set.singleton y)+    test "rightAdjacencyMap (edge x y) == Map.singleton y (Set.singleton x)" $ \x y ->+          rightAdjacencyMap (edge x y) == Map.singleton y (Set.singleton x)+    test "hasEdge x y (edge x y)       == True" $ \x y ->+          hasEdge x y (edge x y)       == True+    test "hasEdge 1 2 (edge 2 1)       == False" $+          hasEdge 1 2 (edge 2 1)       == False++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.overlay ============"+    test "isEmpty     (overlay x y) == isEmpty   x   && isEmpty   y" $ \x y ->+          isEmpty     (overlay x y) ==(isEmpty   x   && isEmpty   y)+    test "hasVertex z (overlay x y) == hasVertex z x || hasVertex z y" $ \x y z ->+          hasVertex z (overlay x y) ==(hasVertex z x || hasVertex z y)+    test "vertexCount (overlay x y) >= vertexCount x" $ \x y ->+          vertexCount (overlay x y) >= vertexCount x+    test "vertexCount (overlay x y) <= vertexCount x + vertexCount y" $ \x y ->+          vertexCount (overlay x y) <= vertexCount x + vertexCount y+    test "edgeCount   (overlay x y) >= edgeCount x" $ \x y ->+          edgeCount   (overlay x y) >= edgeCount x+    test "edgeCount   (overlay x y) <= edgeCount x   + edgeCount y" $ \x y ->+          edgeCount   (overlay x y) <= edgeCount x   + edgeCount y++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.connect ============"+    test "connect (leftVertex x)     (leftVertex y)     == vertices [x,y] []" $ \x y ->+          connect (leftVertex x)     (leftVertex y)     == vertices [x,y] []+    test "connect (leftVertex x)     (rightVertex y)    == edge x y" $ \x y ->+          connect (leftVertex x)     (rightVertex y)    == edge x y+    test "connect (rightVertex x)    (leftVertex y)     == edge y x" $ \x y ->+          connect (rightVertex x)    (leftVertex y)     == edge y x+    test "connect (rightVertex x)    (rightVertex y)    == vertices [] [x,y]" $ \x y ->+          connect (rightVertex x)    (rightVertex y)    == vertices [] [x,y]+    test "connect (vertices xs1 ys1) (vertices xs2 ys2) == overlay (biclique xs1 ys2) (biclique xs2 ys1)" $ \xs1 ys1 xs2 ys2 ->+          connect (vertices xs1 ys1) (vertices xs2 ys2) == overlay (biclique xs1 ys2) (biclique xs2 ys1)+    test "isEmpty     (connect x y)                     == isEmpty   x   && isEmpty   y" $ \x y ->+          isEmpty     (connect x y)                     ==(isEmpty   x   && isEmpty   y)+    test "hasVertex z (connect x y)                     == hasVertex z x || hasVertex z y" $ \x y z ->+          hasVertex z (connect x y)                     ==(hasVertex z x || hasVertex z y)+    test "vertexCount (connect x y)                     >= vertexCount x" $ \x y ->+          vertexCount (connect x y)                     >= vertexCount x+    test "vertexCount (connect x y)                     <= vertexCount x + vertexCount y" $ \x y ->+          vertexCount (connect x y)                     <= vertexCount x + vertexCount y+    test "edgeCount   (connect x y)                     >= edgeCount x" $ \x y ->+          edgeCount   (connect x y)                     >= edgeCount x+    test "edgeCount   (connect x y)                     >= leftVertexCount x * rightVertexCount y" $ \x y ->+          edgeCount   (connect x y)                     >= leftVertexCount x * rightVertexCount y+    test "edgeCount   (connect x y)                     <= leftVertexCount x * rightVertexCount y + rightVertexCount x * leftVertexCount y + edgeCount x + edgeCount y" $ \x y ->+          edgeCount   (connect x y)                     <= leftVertexCount x * rightVertexCount y + rightVertexCount x * leftVertexCount y + edgeCount x + edgeCount y++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.vertices ============"+    test "vertices [] []                    == empty" $+          vertices [] []                    == empty+    test "vertices [x] []                   == leftVertex x" $ \x ->+          vertices [x] []                   == leftVertex x+    test "vertices [] [x]                   == rightVertex x" $ \x ->+          vertices [] [x]                   == rightVertex x+    test "hasLeftVertex  x (vertices xs ys) == elem x xs" $ \x xs ys ->+          hasLeftVertex  x (vertices xs ys) == elem x xs+    test "hasRightVertex y (vertices xs ys) == elem y ys" $ \y xs ys ->+          hasRightVertex y (vertices xs ys) == elem y ys++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.edges ============"+    test "edges []            == empty" $+          edges []            == empty+    test "edges [(x,y)]       == edge x y" $ \x y ->+          edges [(x,y)]       == edge x y+    test "edges               == overlays . map (uncurry edge)" $ \xs ->+          edges xs            == (overlays . map (uncurry edge)) xs+    test "hasEdge x y . edges == elem (x,y)" $ \x y es ->+         (hasEdge x y . edges) es == elem (x,y) es+    test "edgeCount   . edges == length . nub" $ \es ->+         (edgeCount   . edges) es == (length . nubOrd) es++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.overlays ============"+    test "overlays []        == empty" $+          overlays []        == empty+    test "overlays [x]       == x" $ \x ->+          overlays [x]       == x+    test "overlays [x,y]     == overlay x y" $ \x y ->+          overlays [x,y]     == overlay x y+    test "overlays           == foldr overlay empty" $ size10 $ \xs ->+          overlays xs        == foldr overlay empty xs+    test "isEmpty . overlays == all isEmpty" $ size10 $ \xs ->+         (isEmpty . overlays) xs == all isEmpty xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.connects ============"+    test "connects []        == empty" $+          connects []        == empty+    test "connects [x]       == x" $ \x ->+          connects [x]       == x+    test "connects [x,y]     == connect x y" $ \x y ->+          connects [x,y]     == connect x y+    test "connects           == foldr connect empty" $ size10 $ \xs ->+          connects xs        == foldr connect empty xs+    test "isEmpty . connects == all isEmpty" $ size10 $ \ xs ->+         (isEmpty . connects) xs == all isEmpty xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.swap ============"+    test "swap empty            == empty" $+          swap empty            == empty+    test "swap . leftVertex     == rightVertex" $ \x ->+         (swap . leftVertex) x  == rightVertex x+    test "swap (vertices xs ys) == vertices ys xs" $ \xs ys ->+          swap (vertices xs ys) == vertices ys xs+    test "swap (edge x y)       == edge y x" $ \x y ->+          swap (edge x y)       == edge y x+    test "swap . edges          == edges . map Data.Tuple.swap" $ \es ->+         (swap . edges) es      == (edges . map Data.Tuple.swap) es+    test "swap . swap           == id" $ \x ->+         (swap . swap) x        == x++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.toBipartite ============"+    test "toBipartite empty                      == empty" $+          toBipartite AM.empty                   == empty+    test "toBipartite (vertex (Left x))          == leftVertex x" $ \x ->+          toBipartite (AM.vertex (Left x))       == leftVertex x+    test "toBipartite (vertex (Right x))         == rightVertex x" $ \x ->+          toBipartite (AM.vertex (Right x))      == rightVertex x+    test "toBipartite (edge (Left x) (Left y))   == vertices [x,y] []" $ \x y ->+          toBipartite (AM.edge (Left x) (Left y)) == vertices [x,y] []+    test "toBipartite (edge (Left x) (Right y))  == edge x y" $ \x y ->+          toBipartite (AM.edge (Left x) (Right y)) == edge x y+    test "toBipartite (edge (Right x) (Left y))  == edge y x" $ \x y ->+          toBipartite (AM.edge (Right x) (Left y)) == edge y x+    test "toBipartite (edge (Right x) (Right y)) == vertices [] [x,y]" $ \x y ->+          toBipartite (AM.edge (Right x) (Right y)) == vertices [] [x,y]+    test "toBipartite (clique xs)                == uncurry biclique (partitionEithers xs)" $ \xs ->+          toBipartite (AM.clique xs)             == uncurry biclique (partitionEithers xs)++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.toBipartiteWith ============"+    test "toBipartiteWith f empty == empty" $ \(apply -> f) ->+          toBipartiteWith f (AM.empty :: AII) == empty+    test "toBipartiteWith Left x  == vertices (vertexList x) []" $ \x ->+          toBipartiteWith Left x  == vertices (AM.vertexList x) []+    test "toBipartiteWith Right x == vertices [] (vertexList x)" $ \x ->+          toBipartiteWith Right x == vertices [] (AM.vertexList x)+    test "toBipartiteWith f       == toBipartite . gmap f" $ \(apply -> f) x ->+          toBipartiteWith f x     == (toBipartite . AM.gmap f) (x :: AII)+    test "toBipartiteWith id      == toBipartite" $ \x ->+          toBipartiteWith id x    == toBipartite x++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.fromBipartite ============"+    test "fromBipartite empty          == empty" $+          fromBipartite empty          == AM.empty+    test "fromBipartite (leftVertex x) == vertex (Left x)" $ \x ->+          fromBipartite (leftVertex x) == AM.vertex (Left x)+    test "fromBipartite (edge x y)     == edges [(Left x, Right y), (Right y, Left x)]" $ \x y ->+          fromBipartite (edge x y)     == AM.edges [(Left x, Right y), (Right y, Left x)]+    test "toBipartite . fromBipartite  == id" $ \x ->+         (toBipartite . fromBipartite) x == x++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.fromBipartiteWith ============"+    test "fromBipartiteWith Left Right             == fromBipartite" $ \x ->+          fromBipartiteWith Left Right x           == fromBipartite x+    test "fromBipartiteWith id id (vertices xs ys) == vertices (xs ++ ys)" $ \xs ys ->+          fromBipartiteWith id id (vertices xs ys) == AM.vertices (xs ++ ys)+    test "fromBipartiteWith id id . edges          == edges" $ \xs ->+         (fromBipartiteWith id id . edges) xs      == (AM.symmetricClosure . AM.edges) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.isEmpty ============"+    test "isEmpty empty                 == True" $+          isEmpty empty                 == True+    test "isEmpty (overlay empty empty) == True" $+          isEmpty (overlay empty empty) == True+    test "isEmpty (vertex x)            == False" $ \x ->+          isEmpty (vertex x)            == False+    test "isEmpty                       == (==) empty" $ \x ->+          isEmpty x                     == (==) empty x++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.hasLeftVertex ============"+    test "hasLeftVertex x empty           == False" $ \x ->+          hasLeftVertex x empty           == False+    test "hasLeftVertex x (leftVertex y)  == (x == y)" $ \x y ->+          hasLeftVertex x (leftVertex y)  == (x == y)+    test "hasLeftVertex x (rightVertex y) == False" $ \x y ->+          hasLeftVertex x (rightVertex y) == False++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.hasRightVertex ============"+    test "hasRightVertex x empty           == False" $ \x ->+          hasRightVertex x empty           == False+    test "hasRightVertex x (leftVertex y)  == False" $ \x y ->+          hasRightVertex x (leftVertex y)  == False+    test "hasRightVertex x (rightVertex y) == (x == y)" $ \x y ->+          hasRightVertex x (rightVertex y) == (x == y)++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.hasVertex ============"+    test "hasVertex . Left  == hasLeftVertex" $ \x y ->+         (hasVertex . Left) x y == hasLeftVertex x y+    test "hasVertex . Right == hasRightVertex" $ \x y ->+         (hasVertex . Right) x y == hasRightVertex x y++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.hasEdge ============"+    test "hasEdge x y empty      == False" $ \x y ->+          hasEdge x y empty      == False+    test "hasEdge x y (vertex z) == False" $ \x y z ->+          hasEdge x y (vertex z) == False+    test "hasEdge x y (edge x y) == True" $ \x y ->+          hasEdge x y (edge x y) == True+    test "hasEdge x y            == elem (x,y) . edgeList" $ \x y z -> do+        let es = edgeList z+        (x, y) <- elements ((x, y) : es)+        return $ hasEdge x y z == elem (x, y) es++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.leftVertexCount ============"+    test "leftVertexCount empty           == 0" $+          leftVertexCount empty           == 0+    test "leftVertexCount (leftVertex x)  == 1" $ \x ->+          leftVertexCount (leftVertex x)  == 1+    test "leftVertexCount (rightVertex x) == 0" $ \x ->+          leftVertexCount (rightVertex x) == 0+    test "leftVertexCount (edge x y)      == 1" $ \x y ->+          leftVertexCount (edge x y)      == 1+    test "leftVertexCount . edges         == length . nub . map fst" $ \xs ->+         (leftVertexCount . edges) xs     == (length . nub . map fst) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.rightVertexCount ============"+    test "rightVertexCount empty           == 0" $+          rightVertexCount empty           == 0+    test "rightVertexCount (leftVertex x)  == 0" $ \x ->+          rightVertexCount (leftVertex x)  == 0+    test "rightVertexCount (rightVertex x) == 1" $ \x ->+          rightVertexCount (rightVertex x) == 1+    test "rightVertexCount (edge x y)      == 1" $ \x y ->+          rightVertexCount (edge x y)      == 1+    test "rightVertexCount . edges         == length . nub . map snd" $ \xs ->+         (rightVertexCount . edges) xs     == (length . nub . map snd) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.vertexCount ============"+    test "vertexCount empty      == 0" $+          vertexCount empty      == 0+    test "vertexCount (vertex x) == 1" $ \x ->+          vertexCount (vertex x) == 1+    test "vertexCount (edge x y) == 2" $ \x y ->+          vertexCount (edge x y) == 2+    test "vertexCount x          == leftVertexCount x + rightVertexCount x" $ \x ->+          vertexCount x          == leftVertexCount x + rightVertexCount x++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.edgeCount ============"+    test "edgeCount empty      == 0" $+          edgeCount empty      == 0+    test "edgeCount (vertex x) == 0" $ \x ->+          edgeCount (vertex x) == 0+    test "edgeCount (edge x y) == 1" $ \x y ->+          edgeCount (edge x y) == 1+    test "edgeCount . edges    == length . nub" $ \xs ->+         (edgeCount . edges) xs == (length . nubOrd) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.leftVertexList ============"+    test "leftVertexList empty              == []" $+          leftVertexList empty              == []+    test "leftVertexList (leftVertex x)     == [x]" $ \x ->+          leftVertexList (leftVertex x)     == [x]+    test "leftVertexList (rightVertex x)    == []" $ \x ->+          leftVertexList (rightVertex x)    == []+    test "leftVertexList . flip vertices [] == nub . sort" $ \xs ->+         (leftVertexList . flip vertices []) xs == (nubOrd . sort) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.rightVertexList ============"+    test "rightVertexList empty           == []" $+          rightVertexList empty           == []+    test "rightVertexList (leftVertex x)  == []" $ \x ->+          rightVertexList (leftVertex x)  == []+    test "rightVertexList (rightVertex x) == [x]" $ \x ->+          rightVertexList (rightVertex x) == [x]+    test "rightVertexList . vertices []   == nub . sort" $ \xs ->+         (rightVertexList . vertices []) xs == (nubOrd . sort) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.vertexList ============"+    test "vertexList empty                             == []" $+          vertexList empty                             == []+    test "vertexList (vertex x)                        == [x]" $ \x ->+          vertexList (vertex x)                        == [x]+    test "vertexList (edge x y)                        == [Left x, Right y]" $ \x y ->+          vertexList (edge x y)                        == [Left x, Right y]+    test "vertexList (vertices (lefts xs) (rights xs)) == nub (sort xs)" $ \xs ->+          vertexList (vertices (lefts xs) (rights xs)) == nubOrd (sort xs)++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.edgeList ============"+    test "edgeList empty      == []" $+          edgeList empty      == []+    test "edgeList (vertex x) == []" $ \x ->+          edgeList (vertex x) == []+    test "edgeList (edge x y) == [(x,y)]" $ \x y ->+          edgeList (edge x y) == [(x,y)]+    test "edgeList . edges    == nub . sort" $ \xs ->+         (edgeList . edges) xs == (nubOrd . sort) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.leftVertexSet ============"+    test "leftVertexSet empty              == Set.empty" $+          leftVertexSet empty              == Set.empty+    test "leftVertexSet . leftVertex       == Set.singleton" $ \x ->+         (leftVertexSet . leftVertex) x    == Set.singleton x+    test "leftVertexSet . rightVertex      == const Set.empty" $ \x ->+         (leftVertexSet . rightVertex) x   == const Set.empty x+    test "leftVertexSet . flip vertices [] == Set.fromList" $ \xs ->+         (leftVertexSet . flip vertices []) xs == Set.fromList xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.rightVertexSet ============"+    test "rightVertexSet empty         == Set.empty" $+          rightVertexSet empty         == Set.empty+    test "rightVertexSet . leftVertex  == const Set.empty" $ \x ->+         (rightVertexSet . leftVertex) x == const Set.empty x+    test "rightVertexSet . rightVertex == Set.singleton" $ \x ->+         (rightVertexSet . rightVertex) x == Set.singleton x+    test "rightVertexSet . vertices [] == Set.fromList" $ \xs ->+         (rightVertexSet . vertices []) xs == Set.fromList xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.vertexSet ============"+    test "vertexSet empty                             == Set.empty" $+          vertexSet empty                             == Set.empty+    test "vertexSet . vertex                          == Set.singleton" $ \x ->+         (vertexSet . vertex) x                       == Set.singleton x+    test "vertexSet (edge x y)                        == Set.fromList [Left x, Right y]" $ \x y ->+          vertexSet (edge x y)                        == Set.fromList [Left x, Right y]+    test "vertexSet (vertices (lefts xs) (rights xs)) == Set.fromList xs" $ \xs ->+          vertexSet (vertices (lefts xs) (rights xs)) == Set.fromList xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.edgeSet ============"+    test "edgeSet empty      == Set.empty" $+          edgeSet empty      == Set.empty+    test "edgeSet (vertex x) == Set.empty" $ \x ->+          edgeSet (vertex x) == Set.empty+    test "edgeSet (edge x y) == Set.singleton (x,y)" $ \x y ->+          edgeSet (edge x y) == Set.singleton (x,y)+    test "edgeSet . edges    == Set.fromList" $ \xs ->+         (edgeSet . edges) xs == Set.fromList xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.circuit ============"+    test "circuit []                    == empty" $+          circuit []                    == empty+    test "circuit [(x,y)]               == edge x y" $ \x y ->+          circuit [(x,y)]               == edge x y+    test "circuit [(1,2), (3,4)]        == biclique [1,3] [2,4]" $+          circuit [(1,2), (3,4)]        == biclique [1,3 :: Int] [2,4 :: Int]+    test "circuit [(1,2), (3,4), (5,6)] == edges [(1,2), (3,2), (3,4), (5,4), (5,6), (1,6)]" $+          circuit [(1,2), (3,4), (5,6)] == edges [(1,2), (3,2), (3,4), (5,4), (5,6), (1,6)]+    test "circuit . reverse             == swap . circuit . map Data.Tuple.swap" $ \xs ->+         (circuit . reverse) xs         == (swap . circuit . map Data.Tuple.swap) xs++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.biclique ============"+    test "biclique [] [] == empty" $+          biclique [] [] == empty+    test "biclique xs [] == vertices xs []" $ \xs ->+          biclique xs [] == vertices xs []+    test "biclique [] ys == vertices [] ys" $ \ys ->+          biclique [] ys == vertices [] ys+    test "biclique xs ys == connect (vertices xs []) (vertices [] ys)" $ \xs ys ->+          biclique xs ys == connect (vertices xs []) (vertices [] ys)++    putStrLn "\n============ Bipartite.Undirected.AdjacencyMap.detectParts ============"+    test "detectParts empty                                       == Right empty" $+          detectParts AM.empty                                    == Right empty+    test "detectParts (vertex x)                                  == Right (leftVertex x)" $ \x ->+          detectParts (AM.vertex x)                               == Right (leftVertex x)+    test "detectParts (edge x x)                                  == Left [x]" $ \x ->+          detectParts (AM.edge x x :: AI)                         == Left [x]+    test "detectParts (edge 1 2)                                  == Right (edge 1 2)" $+          detectParts (AM.edge 1 2)                               == Right (edge 1 2)+    test "detectParts (1 * (2 + 3))                               == Right (edges [(1,2), (1,3)])" $+          detectParts (1 * (2 + 3))                               == Right (edges [(1,2), (1,3)])+    test "detectParts (1 * 2 * 3)                                 == Left [1, 2, 3]" $+          detectParts (1 * 2 * 3 :: AI)                           == Left [1, 2, 3]+    test "detectParts ((1 + 3) * (2 + 4) + 6 * 5)                 == Right (swap (1 + 3) * (2 + 4) + swap 5 * 6)" $+          detectParts ((1 + 3) * (2 + 4) + 6 * 5)                 == Right (swap (1 + 3) * (2 + 4) + swap 5 * 6)+    test "detectParts ((1 * 3 * 4) + 2 * (1 + 2))                 == Left [2]" $+          detectParts ((1 * 3 * 4) + 2 * (1 + 2) :: AI)           == Left [2]+    test "detectParts (clique [1..10])                            == Left [1, 2, 3]" $+          detectParts (AM.clique [1..10] :: AI)                   == Left [1, 2, 3]+    test "detectParts (circuit [1..11])                           == Left [1..11]" $+          detectParts (AM.circuit [1..11] :: AI)                  == Left [1..11]+    test "detectParts (circuit [1..10])                           == Right (circuit [(x, x + 1) | x <- [1,3,5,7,9]])" $+          detectParts (AM.circuit [1..10] :: AI)                  == Right (circuit [(x, x + 1) | x <- [1,3,5,7,9]])+    test "detectParts (biclique [] xs)                            == Right (vertices xs [])" $ \xs ->+          detectParts (AM.biclique [] xs)                         == Right (vertices xs [])+    test "detectParts (biclique (map Left (x:xs)) (map Right ys)) == Right (biclique (map Left (x:xs)) (map Right ys))" $ \(x :: Int) xs (ys :: [Int]) ->+          detectParts (AM.biclique (map Left (x:xs)) (map Right ys)) == Right (B.biclique (map Left (x:xs)) (map Right ys))+    test "isRight (detectParts (star x ys))                       == notElem x ys" $ \(x :: Int) ys ->+          isRight (detectParts (AM.star x ys))                    == notElem x ys+    test "isRight (detectParts (fromBipartite x))                 == True" $ \x ->+          isRight (detectParts (fromBipartite x))                 == True++    putStrLn ""+    test "Correctness of detectParts" $ \input ->+        let undirected = AM.symmetricClosure input in+        case detectParts input of+            Left cycle -> mod (length cycle) 2 == 1 && AM.isSubgraphOf (AM.circuit cycle) undirected+            Right bipartite -> AM.gmap fromEither (fromBipartite bipartite) == undirected
test/Algebra/Graph/Test/Export.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Export@@ -12,19 +12,13 @@ module Algebra.Graph.Test.Export (     -- * Testsuite     testExport-  ) where--import Prelude ()-import Prelude.Compat--#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup-#endif+    ) where  import Algebra.Graph (Graph, circuit) import Algebra.Graph.Export hiding (unlines) import Algebra.Graph.Export.Dot (Attribute (..)) import Algebra.Graph.Test+import Data.Semigroup ((<>))  import qualified Algebra.Graph.Export     as E import qualified Algebra.Graph.Export.Dot as ED
− test/Algebra/Graph/Test/Fold.hs
@@ -1,40 +0,0 @@--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Test.Fold--- Copyright  : (c) Andrey Mokhov 2016-2018--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : experimental------ Testsuite for "Algebra.Graph.Fold" and polymorphic functions defined in--- "Algebra.Graph.Class".-------------------------------------------------------------------------------module Algebra.Graph.Test.Fold (-    -- * Testsuite-    testFold-  ) where--import Algebra.Graph.Fold-import Algebra.Graph.Test-import Algebra.Graph.Test.Generic--t :: Testsuite-t = testsuite "Fold." (empty :: Fold Int)--type F = Fold Int--testFold :: IO ()-testFold = do-    putStrLn "\n============ Fold ============"-    test "Axioms of graphs" (axioms :: GraphTestsuite F)--    testShow            t-    testBasicPrimitives t-    testIsSubgraphOf    t-    testToGraph         t-    testSize            t-    testGraphFamilies   t-    testTransformations t-    testSplitVertex     t-    testBind            t-    testSimplify        t
test/Algebra/Graph/Test/Generic.hs view
@@ -1,1833 +1,2069 @@-{-# LANGUAGE GADTs, RankNTypes, ViewPatterns #-}--------------------------------------------------------------------------------- |--- Module     : Algebra.Graph.Test.Generic--- Copyright  : (c) Andrey Mokhov 2016-2019--- License    : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability  : experimental------ Generic graph API testing.-------------------------------------------------------------------------------module Algebra.Graph.Test.Generic where--import Prelude ()-import Prelude.Compat--import Control.Monad (when)-import Data.Orphans ()--import Data.List (nub)-import Data.Maybe-import Data.Tree-import Data.Tuple--import Algebra.Graph.Class (Graph (..))-import Algebra.Graph.ToGraph-import Algebra.Graph.Test-import Algebra.Graph.Test.API--import qualified Algebra.Graph                        as G-import qualified Algebra.Graph.AdjacencyMap           as AM-import qualified Algebra.Graph.AdjacencyMap.Algorithm as AM-import qualified Algebra.Graph.AdjacencyIntMap        as AIM-import qualified Data.Set                             as Set-import qualified Data.IntSet                          as IntSet--data Testsuite where-    Testsuite :: (Arbitrary g, GraphAPI g, Num g, Ord g, Show g, ToGraph g, ToVertex g ~ Int, Vertex g ~ Int)-              => String -> (forall r. (g -> r) -> g -> r) -> Testsuite--testsuite :: (Arbitrary g, GraphAPI g, Num g, Ord g, Show g, ToGraph g, ToVertex g ~ Int, Vertex g ~ Int)-          => String -> g -> Testsuite-testsuite prefix g = Testsuite prefix (\f x -> f (x `asTypeOf` g))--size10 :: Testable prop => prop -> Property-size10 = mapSize (min 10)--testBasicPrimitives :: Testsuite -> IO ()-testBasicPrimitives = mconcat [ testOrd-                              , testEmpty-                              , testVertex-                              , testEdge-                              , testOverlay-                              , testConnect-                              , testVertices-                              , testEdges-                              , testOverlays-                              , testConnects ]--testSymmetricBasicPrimitives :: Testsuite -> IO ()-testSymmetricBasicPrimitives = mconcat [ testSymmetricOrd-                                       , testEmpty-                                       , testVertex-                                       , testSymmetricEdge-                                       , testOverlay-                                       , testSymmetricConnect-                                       , testVertices-                                       , testSymmetricEdges-                                       , testOverlays-                                       , testSymmetricConnects ]--testToGraph :: Testsuite -> IO ()-testToGraph = mconcat [ testToGraphDefault-                      , testFoldg-                      , testIsEmpty-                      , testHasVertex-                      , testHasEdge-                      , testVertexCount-                      , testEdgeCount-                      , testVertexList-                      , testVertexSet-                      , testVertexIntSet-                      , testEdgeList-                      , testEdgeSet-                      , testAdjacencyList-                      , testPreSet-                      , testPreIntSet-                      , testPostSet-                      , testPostIntSet ]--testSymmetricToGraph :: Testsuite -> IO ()-testSymmetricToGraph = mconcat [ testSymmetricToGraphDefault-                               , testIsEmpty-                               , testHasVertex-                               , testSymmetricHasEdge-                               , testVertexCount-                               , testEdgeCount-                               , testVertexList-                               , testVertexSet-                               , testVertexIntSet-                               , testSymmetricEdgeList-                               , testSymmetricEdgeSet-                               , testSymmetricAdjacencyList-                               , testNeighbours ]--testRelational :: Testsuite -> IO ()-testRelational = mconcat [ testCompose-                         , testClosure-                         , testReflexiveClosure-                         , testSymmetricClosure-                         , testTransitiveClosure ]--testGraphFamilies :: Testsuite -> IO ()-testGraphFamilies = mconcat [ testPath-                            , testCircuit-                            , testClique-                            , testBiclique-                            , testStar-                            , testStars-                            , testTree-                            , testForest ]--testSymmetricGraphFamilies :: Testsuite -> IO ()-testSymmetricGraphFamilies = mconcat [ testSymmetricPath-                                     , testSymmetricCircuit-                                     , testSymmetricClique-                                     , testBiclique-                                     , testStar-                                     , testStars-                                     , testTree-                                     , testForest ]--testTransformations :: Testsuite -> IO ()-testTransformations = mconcat [ testRemoveVertex-                              , testRemoveEdge-                              , testReplaceVertex-                              , testMergeVertices-                              , testTranspose-                              , testGmap-                              , testInduce ]--testSymmetricTransformations :: Testsuite -> IO ()-testSymmetricTransformations = mconcat [ testRemoveVertex-                                       , testSymmetricRemoveEdge-                                       , testReplaceVertex-                                       , testMergeVertices-                                       , testGmap-                                       , testInduce ]--testConsistent :: Testsuite -> IO ()-testConsistent (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "consistent ============"-    test "Consistency of the Arbitrary instance" $ \x -> consistent % x--    putStrLn ""-    test "consistent empty         == True" $-          consistent % empty       == True--    test "consistent (vertex x)    == True" $ \x ->-          consistent % (vertex x)  == True--    test "consistent (overlay x y) == True" $ \x y ->-          consistent % (overlay x y) == True--    test "consistent (connect x y) == True" $ \x y ->-          consistent % (connect x y) == True--    test "consistent (edge x y)    == True" $ \x y ->-          consistent % (edge x y)  == True--    test "consistent (edges xs)    == True" $ \xs ->-          consistent % (edges xs)  == True--    test "consistent (stars xs)    == True" $ \xs ->-          consistent % (stars xs)  == True--testShow :: Testsuite -> IO ()-testShow (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "Show ============"-    test "show (empty    ) == \"empty\"" $-          show % empty     ==  "empty"--    test "show (1        ) == \"vertex 1\"" $-          show % 1         ==  "vertex 1"--    test "show (1 + 2    ) == \"vertices [1,2]\"" $-          show % (1 + 2)   ==  "vertices [1,2]"--    test "show (1 * 2    ) == \"edge 1 2\"" $-          show % (1 * 2)   ==  "edge 1 2"--    test "show (1 * 2 * 3) == \"edges [(1,2),(1,3),(2,3)]\"" $-          show % (1 * 2 * 3) == "edges [(1,2),(1,3),(2,3)]"--    test "show (1 * 2 + 3) == \"overlay (vertex 3) (edge 1 2)\"" $-          show % (1 * 2 + 3) == "overlay (vertex 3) (edge 1 2)"--    putStrLn ""-    test "show (vertex (-1)                            ) == \"vertex (-1)\"" $-          show % (vertex (-1)                            ) == "vertex (-1)"--    test "show (vertex (-1) + vertex (-2)              ) == \"vertices [-2,-1]\"" $-          show % (vertex (-1) + vertex (-2)              ) == "vertices [-2,-1]"--    test "show (vertex (-2) * vertex (-1)              ) == \"edge (-2) (-1)\"" $-          show % (vertex (-2) * vertex (-1)              ) == "edge (-2) (-1)"--    test "show (vertex (-3) * vertex (-2) * vertex (-1)) == \"edges [(-3,-2),(-3,-1),(-2,-1)]\"" $-          show % (vertex (-3) * vertex (-2) * vertex (-1)) == "edges [(-3,-2),(-3,-1),(-2,-1)]"--    test "show (vertex (-3) * vertex (-2) + vertex (-1)) == \"overlay (vertex (-1)) (edge (-3) (-2))\"" $-          show % (vertex (-3) * vertex (-2) + vertex (-1)) == "overlay (vertex (-1)) (edge (-3) (-2))"--testSymmetricShow :: Testsuite -> IO ()-testSymmetricShow t@(Testsuite _ (%)) = do-    testShow t-    putStrLn ""-    test "show (2 * 1    ) == \"edge 1 2\"" $-          show % (2 * 1)   ==  "edge 1 2"--    test "show (1 * 2 * 1) == \"edges [(1,1),(1,2)]\"" $-          show % (1 * 2 * 1) == "edges [(1,1),(1,2)]"--    test "show (3 * 2 * 1) == \"edges [(1,2),(1,3),(2,3)]\"" $-          show % (3 * 2 * 1) == "edges [(1,2),(1,3),(2,3)]"--testOrd :: Testsuite -> IO ()-testOrd (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "Ord ============"-    test "vertex 1 <  vertex 2" $-          vertex 1 < id % vertex 2--    test "vertex 3 <  edge 1 2" $-          vertex 3 < id % edge 1 2--    test "vertex 1 <  edge 1 1" $-          vertex 1 < id % edge 1 1--    test "edge 1 1 <  edge 1 2" $-          edge 1 1 < id % edge 1 2--    test "edge 1 2 <  edge 1 1 + edge 2 2" $-          edge 1 2 < id % edge 1 1 + edge 2 2--    test "edge 1 2 <  edge 1 3" $-          edge 1 2 < id % edge 1 3--    test "x        <= x + y" $ \x y ->-          id % x   <= x + y--    test "x + y    <= x * y" $ \x y ->-          id % x + y <= x * y--testSymmetricOrd :: Testsuite -> IO ()-testSymmetricOrd (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "Ord ============"-    test "vertex 1 <  vertex 2" $-          vertex 1 < id % vertex 2--    test "vertex 3 <  edge 1 2" $-          vertex 3 < id % edge 1 2--    test "vertex 1 <  edge 1 1" $-          vertex 1 < id % edge 1 1--    test "edge 1 1 <  edge 1 2" $-          edge 1 1 < id % edge 1 2--    test "edge 1 2 <  edge 1 1 + edge 2 2" $-          edge 1 2 < id % edge 1 1 + edge 2 2--    test "edge 2 1 <  edge 1 3" $-          edge 2 1 < id % edge 1 3--    test "edge 1 2 == edge 2 1" $-          edge 1 2 == id % edge 2 1--    test "x        <= x + y" $ \x y ->-          id % x   <= x + y--    test "x + y    <= x * y" $ \x y ->-          id % x + y <= x * y--testEmpty :: Testsuite -> IO ()-testEmpty (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "empty ============"-    test "isEmpty     empty == True" $-          isEmpty   % empty == True--    test "hasVertex x empty == False" $ \x ->-          hasVertex x % empty == False--    test "vertexCount empty == 0" $-          vertexCount % empty == 0--    test "edgeCount   empty == 0" $-          edgeCount % empty == 0--testVertex :: Testsuite -> IO ()-testVertex (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "vertex ============"-    test "isEmpty     (vertex x) == False" $ \x ->-          isEmpty    % vertex x  == False--    test "hasVertex x (vertex x) == True" $ \x ->-          hasVertex x % vertex x == True--    test "vertexCount (vertex x) == 1" $ \x ->-          vertexCount % vertex x == 1--    test "edgeCount   (vertex x) == 0" $ \x ->-          edgeCount  % vertex x  == 0--testEdge :: Testsuite -> IO ()-testEdge (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edge ============"-    test "edge x y               == connect (vertex x) (vertex y)" $ \x y ->-          edge x y               == connect (vertex x) % vertex y--    test "hasEdge x y (edge x y) == True" $ \x y ->-          hasEdge x y % edge x y == True--    test "edgeCount   (edge x y) == 1" $ \x y ->-          edgeCount %  edge x y  == 1--    test "vertexCount (edge 1 1) == 1" $-          vertexCount % edge 1 1 == 1--    test "vertexCount (edge 1 2) == 2" $-          vertexCount % edge 1 2 == 2--testSymmetricEdge :: Testsuite -> IO ()-testSymmetricEdge (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edge ============"-    test "edge x y               == connect (vertex x) (vertex y)" $ \x y ->-          edge x y               == connect (vertex x) % vertex y--    test "edge x y               == edge y x" $ \x y ->-          edge x y               == id % edge y x--    test "edge x y               == edges [(x,y), (y,x)]" $ \x y ->-          edge x y               == id % edges [(x,y), (y,x)]--    test "hasEdge x y (edge x y) == True" $ \x y ->-          hasEdge x y % edge x y == True--    test "edgeCount   (edge x y) == 1" $ \x y ->-          edgeCount % edge x y   == 1--    test "vertexCount (edge 1 1) == 1" $-          vertexCount % edge 1 1 == 1--    test "vertexCount (edge 1 2) == 2" $-          vertexCount % edge 1 2 == 2--testOverlay :: Testsuite -> IO ()-testOverlay (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "overlay ============"-    test "isEmpty     (overlay x y) == isEmpty   x   && isEmpty   y" $ \x y ->-          isEmpty   %  overlay x y  == (isEmpty  x   && isEmpty   y)--    test "hasVertex z (overlay x y) == hasVertex z x || hasVertex z y" $ \x y z ->-          hasVertex z % overlay x y == (hasVertex z x || hasVertex z y)--    test "vertexCount (overlay x y) >= vertexCount x" $ \x y ->-          vertexCount % overlay x y >= vertexCount x--    test "vertexCount (overlay x y) <= vertexCount x + vertexCount y" $ \x y ->-          vertexCount % overlay x y <= vertexCount x + vertexCount y--    test "edgeCount   (overlay x y) >= edgeCount x" $ \x y ->-          edgeCount %  overlay x y  >= edgeCount x--    test "edgeCount   (overlay x y) <= edgeCount x   + edgeCount y" $ \x y ->-          edgeCount %  overlay x y  <= edgeCount x   + edgeCount y--    test "vertexCount (overlay 1 2) == 2" $-          vertexCount % overlay 1 2 == 2--    test "edgeCount   (overlay 1 2) == 0" $-          edgeCount %  overlay 1 2  == 0--testConnect :: Testsuite -> IO ()-testConnect (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "connect ============"-    test "isEmpty     (connect x y) == isEmpty   x   && isEmpty   y" $ \x y ->-          isEmpty    % connect x y  == (isEmpty   x   && isEmpty   y)--    test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \x y z ->-          hasVertex z % connect x y == (hasVertex z x || hasVertex z y)--    test "vertexCount (connect x y) >= vertexCount x" $ \x y ->-          vertexCount % connect x y >= vertexCount x--    test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \x y ->-          vertexCount % connect x y <= vertexCount x + vertexCount y--    test "edgeCount   (connect x y) >= edgeCount x" $ \x y ->-          edgeCount  % connect x y  >= edgeCount x--    test "edgeCount   (connect x y) >= edgeCount y" $ \x y ->-          edgeCount  % connect x y  >= edgeCount y--    test "edgeCount   (connect x y) >= vertexCount x * vertexCount y" $ \x y ->-          edgeCount  % connect x y  >= vertexCount x * vertexCount y--    test "edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \x y ->-          edgeCount  % connect x y  <= vertexCount x * vertexCount y + edgeCount x + edgeCount y--    test "vertexCount (connect 1 2) == 2" $-          vertexCount % connect 1 2 == 2--    test "edgeCount   (connect 1 2) == 1" $-          edgeCount  % connect 1 2  == 1--testSymmetricConnect :: Testsuite -> IO ()-testSymmetricConnect (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "connect ============"-    test "connect x y               == connect y x" $ \x y ->-          connect x y               == id % connect y x--    test "isEmpty     (connect x y) == isEmpty   x   && isEmpty   y" $ \x y ->-          isEmpty    % connect x y  == (isEmpty   x   && isEmpty   y)--    test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \x y z ->-          hasVertex z % connect x y == (hasVertex z x || hasVertex z y)--    test "vertexCount (connect x y) >= vertexCount x" $ \x y ->-          vertexCount % connect x y >= vertexCount x--    test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \x y ->-          vertexCount % connect x y <= vertexCount x + vertexCount y--    test "edgeCount   (connect x y) >= edgeCount x" $ \x y ->-          edgeCount  % connect x y  >= edgeCount x--    test "edgeCount   (connect x y) >= edgeCount y" $ \x y ->-          edgeCount  % connect x y  >= edgeCount y--    test "edgeCount   (connect x y) >= vertexCount x * vertexCount y `div` 2" $ \x y ->-          edgeCount  % connect x y  >= vertexCount x * vertexCount y `div` 2--    test "edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \x y ->-          edgeCount  % connect x y  <= vertexCount x * vertexCount y + edgeCount x + edgeCount y--    test "vertexCount (connect 1 2) == 2" $-          vertexCount % connect 1 2 == 2--    test "edgeCount   (connect 1 2) == 1" $-          edgeCount  % connect 1 2  == 1--testVertices :: Testsuite -> IO ()-testVertices (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "vertices ============"-    test "vertices []            == empty" $-          vertices []            == id % empty--    test "vertices [x]           == vertex x" $ \x ->-          vertices [x]           == id % vertex x--    test "hasVertex x . vertices == elem x" $ \x xs ->-          hasVertex x % vertices xs == elem x xs--    test "vertexCount . vertices == length . nub" $ \xs ->-          vertexCount % vertices xs == (length . nubOrd) xs--    test "vertexSet   . vertices == Set.fromList" $ \xs ->-          vertexSet % vertices xs == Set.fromList xs--testEdges :: Testsuite -> IO ()-testEdges (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edges ============"-    test "edges []          == empty" $-          edges []          == id % empty--    test "edges [(x,y)]     == edge x y" $ \x y ->-          edges [(x,y)]     == id % edge x y--    test "edgeCount . edges == length . nub" $ \xs ->-          edgeCount % edges xs == (length . nubOrd) xs--testSymmetricEdges :: Testsuite -> IO ()-testSymmetricEdges (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edges ============"-    test "edges []             == empty" $-          edges []             == id % empty--    test "edges [(x,y)]        == edge x y" $ \x y ->-          edges [(x,y)]        == id % edge x y--    test "edges [(x,y), (y,x)] == edge x y" $ \x y ->-          edges [(x,y), (y,x)] == id % edge x y--testOverlays :: Testsuite -> IO ()-testOverlays (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "overlays ============"-    test "overlays []        == empty" $-          overlays []        == id % empty--    test "overlays [x]       == x" $ \x ->-          overlays [x]       == id % x--    test "overlays [x,y]     == overlay x y" $ \x y ->-          overlays [x,y]     == id % overlay x y--    test "overlays           == foldr overlay empty" $ size10 $ \xs ->-          overlays xs        == id % foldr overlay empty xs--    test "isEmpty . overlays == all isEmpty" $ size10 $ \xs ->-          isEmpty % overlays xs == all isEmpty xs--testConnects :: Testsuite -> IO ()-testConnects (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "connects ============"-    test "connects []        == empty" $-          connects []        == id % empty--    test "connects [x]       == x" $ \x ->-          connects [x]       == id % x--    test "connects [x,y]     == connect x y" $ \x y ->-          connects [x,y]     == id % connect x y--    test "connects           == foldr connect empty" $ size10 $ \xs ->-          connects xs        == id % foldr connect empty xs--    test "isEmpty . connects == all isEmpty" $ size10 $ \xs ->-          isEmpty % connects xs == all isEmpty xs--testSymmetricConnects :: Testsuite -> IO ()-testSymmetricConnects t@(Testsuite _ (%)) = do-    testConnects t-    test "connects           == connects . reverse" $ size10 $ \xs ->-          connects xs        == id % connects (reverse xs)--testStars :: Testsuite -> IO ()-testStars (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "stars ============"-    test "stars []                      == empty" $-          stars []                      == id % empty--    test "stars [(x, [])]               == vertex x" $ \x ->-          stars [(x, [])]               == id % vertex x--    test "stars [(x, [y])]              == edge x y" $ \x y ->-          stars [(x, [y])]              == id % edge x y--    test "stars [(x, ys)]               == star x ys" $ \x ys ->-          stars [(x, ys)]               == id % star x ys--    test "stars                         == overlays . map (uncurry star)" $ \xs ->-          stars xs                      == id % overlays (map (uncurry star) xs)--    test "stars . adjacencyList         == id" $ \x ->-         (stars . adjacencyList) x      == id % x--    test "overlay (stars xs) (stars ys) == stars (xs ++ ys)" $ \xs ys ->-          overlay (stars xs) % stars ys == stars (xs ++ ys)--testFromAdjacencySets :: Testsuite -> IO ()-testFromAdjacencySets (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencySets ============"-    test "fromAdjacencySets []                                  == empty" $-          fromAdjacencySets []                                  == id % empty--    test "fromAdjacencySets [(x, Set.empty)]                    == vertex x" $ \x ->-          fromAdjacencySets [(x, Set.empty)]                    == id % vertex x--    test "fromAdjacencySets [(x, Set.singleton y)]              == edge x y" $ \x y ->-          fromAdjacencySets [(x, Set.singleton y)]              == id % edge x y--    test "fromAdjacencySets . map (fmap Set.fromList)           == stars" $ \x ->-         (fromAdjacencySets . map (fmap Set.fromList)) x        == id % stars x--    test "overlay (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)" $ \xs ys ->-          overlay (fromAdjacencySets xs) % fromAdjacencySets ys == fromAdjacencySets (xs ++ ys)--testFromAdjacencyIntSets :: Testsuite -> IO ()-testFromAdjacencyIntSets (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencyIntSets ============"-    test "fromAdjacencyIntSets []                                     == empty" $-          fromAdjacencyIntSets []                                     == id % empty--    test "fromAdjacencyIntSets [(x, IntSet.empty)]                    == vertex x" $ \x ->-          fromAdjacencyIntSets [(x, IntSet.empty)]                    == id % vertex x--    test "fromAdjacencyIntSets [(x, IntSet.singleton y)]              == edge x y" $ \x y ->-          fromAdjacencyIntSets [(x, IntSet.singleton y)]              == id % edge x y--    test "fromAdjacencyIntSets . map (fmap IntSet.fromList)           == stars" $ \x ->-         (fromAdjacencyIntSets . map (fmap IntSet.fromList)) x        == id % stars x--    test "overlay (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)" $ \xs ys ->-          overlay (fromAdjacencyIntSets xs) % fromAdjacencyIntSets ys == fromAdjacencyIntSets (xs ++ ys)--testIsSubgraphOf :: Testsuite -> IO ()-testIsSubgraphOf (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "isSubgraphOf ============"-    test "isSubgraphOf empty         x             ==  True" $ \x ->-          isSubgraphOf empty       % x             ==  True--    test "isSubgraphOf (vertex x)    empty         ==  False" $ \x ->-          isSubgraphOf (vertex x)  % empty         ==  False--    test "isSubgraphOf x             (overlay x y) ==  True" $ \x y ->-          isSubgraphOf x            % overlay x y  ==  True--    test "isSubgraphOf (overlay x y) (connect x y) ==  True" $ \x y ->-          isSubgraphOf (overlay x y) % connect x y ==  True--    test "isSubgraphOf (path xs)     (circuit xs)  ==  True" $ \xs ->-          isSubgraphOf (path xs)    % circuit xs   ==  True--    test "isSubgraphOf x y                         ==> x <= y" $ \x z ->-        let y = x + z -- Make sure we hit the precondition-        in isSubgraphOf x % y                      ==> x <= y--testSymmetricIsSubgraphOf :: Testsuite -> IO ()-testSymmetricIsSubgraphOf t@(Testsuite _ (%)) = do-    testIsSubgraphOf t-    test "isSubgraphOf (edge x y) (edge y x)       ==  True" $ \x y ->-          isSubgraphOf (edge x y) % edge y x       ==  True--testToGraphDefault :: Testsuite -> IO ()-testToGraphDefault (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"-    test "toGraph                    == foldg Empty Vertex Overlay Connect" $ \x ->-          toGraph % x                == foldg G.Empty G.Vertex G.Overlay G.Connect x--    test "foldg                      == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->-          foldg e v o c x            == (G.foldg (e :: Int) v o c . toGraph) % x--    test "isEmpty                    == foldg True (const False) (&&) (&&)" $ \x ->-          isEmpty x                  == foldg True (const False) (&&) (&&) % x--    test "size                       == foldg 1 (const 1) (+) (+)" $ \x ->-          size x                     == foldg 1 (const 1) (+) (+) % x--    test "hasVertex x                == foldg False (==x) (||) (||)" $ \x y ->-          hasVertex x y              == foldg False (==x) (||) (||) % y--    test "hasEdge x y                == Algebra.Graph.hasEdge x y . toGraph" $ \x y z ->-          hasEdge x y z              == (G.hasEdge x y . toGraph) % z--    test "vertexCount                == Set.size . vertexSet" $ \x ->-          vertexCount x              == (Set.size . vertexSet) % x--    test "edgeCount                  == Set.size . edgeSet" $ \x ->-          edgeCount x                == (Set.size . edgeSet) % x--    test "vertexList                 == Set.toAscList . vertexSet" $ \x ->-          vertexList x               == (Set.toAscList . vertexSet) % x--    test "edgeList                   == Set.toAscList . edgeSet" $ \x ->-          edgeList x                 == (Set.toAscList . edgeSet) % x--    test "vertexSet                  == foldg Set.empty Set.singleton Set.union Set.union" $ \x ->-          vertexSet x                == foldg Set.empty Set.singleton Set.union Set.union % x--    test "vertexIntSet               == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union" $ \x ->-          vertexIntSet x             == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union % x--    test "edgeSet                    == Algebra.Graph.AdjacencyMap.edgeSet . foldg empty vertex overlay connect" $ \x ->-          edgeSet x                  == (AM.edgeSet . foldg empty vertex overlay connect) % x--    test "preSet x                   == Algebra.Graph.AdjacencyMap.preSet x . toAdjacencyMap" $ \x y ->-          preSet x y                 == (AM.preSet x . toAdjacencyMap) % y--    test "preIntSet x                == Algebra.Graph.AdjacencyIntMap.preIntSet x . toAdjacencyIntMap" $ \x y ->-          preIntSet x y              == (AIM.preIntSet x . toAdjacencyIntMap) % y--    test "postSet x                  == Algebra.Graph.AdjacencyMap.postSet x . toAdjacencyMap" $ \x y ->-          postSet x y                == (AM.postSet x . toAdjacencyMap) % y--    test "postIntSet x               == Algebra.Graph.AdjacencyIntMap.postIntSet x . toAdjacencyIntMap" $ \x y ->-          postIntSet x y             == (AIM.postIntSet x . toAdjacencyIntMap) % y--    test "adjacencyList              == Algebra.Graph.AdjacencyMap.adjacencyList . toAdjacencyMap" $ \x ->-          adjacencyList x            == (AM.adjacencyList . toAdjacencyMap) % x--    test "adjacencyMap               == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMap" $ \x ->-          adjacencyMap x             == (AM.adjacencyMap . toAdjacencyMap) % x--    test "adjacencyIntMap            == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMap" $ \x ->-          adjacencyIntMap x          == (AIM.adjacencyIntMap . toAdjacencyIntMap) % x--    test "adjacencyMapTranspose      == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMapTranspose" $ \x ->-          adjacencyMapTranspose x    == (AM.adjacencyMap . toAdjacencyMapTranspose) % x--    test "adjacencyIntMapTranspose   == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMapTranspose" $ \x ->-          adjacencyIntMapTranspose x == (AIM.adjacencyIntMap . toAdjacencyIntMapTranspose) % x--    test "dfsForest                  == Algebra.Graph.AdjacencyMap.dfsForest . toAdjacencyMap" $ \x ->-          dfsForest x                == (AM.dfsForest . toAdjacencyMap) % x--    test "dfsForestFrom vs           == Algebra.Graph.AdjacencyMap.dfsForestFrom vs . toAdjacencyMap" $ \vs x ->-          dfsForestFrom vs x         == (AM.dfsForestFrom vs . toAdjacencyMap) % x--    test "dfs vs                     == Algebra.Graph.AdjacencyMap.dfs vs . toAdjacencyMap" $ \vs x ->-          dfs vs x                   == (AM.dfs vs . toAdjacencyMap) % x--    test "reachable x                == Algebra.Graph.AdjacencyMap.reachable x . toAdjacencyMap" $ \x y ->-          reachable x y              == (AM.reachable x . toAdjacencyMap) % y--    test "topSort                    == Algebra.Graph.AdjacencyMap.topSort . toAdjacencyMap" $ \x ->-          topSort x                  == (AM.topSort . toAdjacencyMap) % x--    test "isAcyclic                  == Algebra.Graph.AdjacencyMap.isAcyclic . toAdjacencyMap" $ \x ->-          isAcyclic x                == (AM.isAcyclic . toAdjacencyMap) % x--    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->-          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x--    test "toAdjacencyMap             == foldg empty vertex overlay connect" $ \x ->-          toAdjacencyMap x           == foldg AM.empty AM.vertex AM.overlay AM.connect % x--    test "toAdjacencyMapTranspose    == foldg empty vertex overlay (flip connect)" $ \x ->-          toAdjacencyMapTranspose x  == foldg AM.empty AM.vertex AM.overlay (flip AM.connect) % x--    test "toAdjacencyIntMap          == foldg empty vertex overlay connect" $ \x ->-          toAdjacencyIntMap x        == foldg AIM.empty AIM.vertex AIM.overlay AIM.connect % x--    test "toAdjacencyIntMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->-          toAdjacencyIntMapTranspose x == foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect) % x--    test "isDfsForestOf f            == Algebra.Graph.AdjacencyMap.isDfsForestOf f . toAdjacencyMap" $ \f x ->-          isDfsForestOf f x          == (AM.isDfsForestOf f . toAdjacencyMap) % x--    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->-          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x---- TODO: We currently do not test 'edgeSet'.-testSymmetricToGraphDefault :: Testsuite -> IO ()-testSymmetricToGraphDefault (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"-    test "toGraph                    == foldg Empty Vertex Overlay Connect" $ \x ->-          toGraph % x                == foldg G.Empty G.Vertex G.Overlay G.Connect x--    test "foldg                      == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->-          foldg e v o c x            == (G.foldg (e :: Int) v o c . toGraph) % x--    test "isEmpty                    == foldg True (const False) (&&) (&&)" $ \x ->-          isEmpty x                  == foldg True (const False) (&&) (&&) % x--    test "size                       == foldg 1 (const 1) (+) (+)" $ \x ->-          size x                     == foldg 1 (const 1) (+) (+) % x--    test "hasVertex x                == foldg False (==x) (||) (||)" $ \x y ->-          hasVertex x y              == foldg False (==x) (||) (||) % y--    test "hasEdge x y                == Algebra.Graph.hasEdge x y . toGraph" $ \x y z ->-          hasEdge x y z              == (G.hasEdge x y . toGraph) % z--    test "vertexCount                == Set.size . vertexSet" $ \x ->-          vertexCount x              == (Set.size . vertexSet) % x--    test "edgeCount                  == Set.size . edgeSet" $ \x ->-          edgeCount x                == (Set.size . edgeSet) % x--    test "vertexList                 == Set.toAscList . vertexSet" $ \x ->-          vertexList x               == (Set.toAscList . vertexSet) % x--    test "edgeList                   == Set.toAscList . edgeSet" $ \x ->-          edgeList x                 == (Set.toAscList . edgeSet) % x--    test "vertexSet                  == foldg Set.empty Set.singleton Set.union Set.union" $ \x ->-          vertexSet x                == foldg Set.empty Set.singleton Set.union Set.union % x--    test "vertexIntSet               == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union" $ \x ->-          vertexIntSet x             == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union % x--    test "adjacencyList              == Algebra.Graph.AdjacencyMap.adjacencyList . toAdjacencyMap" $ \x ->-          adjacencyList x            == (AM.adjacencyList . toAdjacencyMap) % x--    test "adjacencyMap               == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMap" $ \x ->-          adjacencyMap x             == (AM.adjacencyMap . toAdjacencyMap) % x--    test "adjacencyIntMap            == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMap" $ \x ->-          adjacencyIntMap x          == (AIM.adjacencyIntMap . toAdjacencyIntMap) % x--    test "adjacencyMapTranspose      == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMapTranspose" $ \x ->-          adjacencyMapTranspose x    == (AM.adjacencyMap . toAdjacencyMapTranspose) % x--    test "adjacencyIntMapTranspose   == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMapTranspose" $ \x ->-          adjacencyIntMapTranspose x == (AIM.adjacencyIntMap . toAdjacencyIntMapTranspose) % x--    test "dfsForest                  == Algebra.Graph.AdjacencyMap.dfsForest . toAdjacencyMap" $ \x ->-          dfsForest x                == (AM.dfsForest . toAdjacencyMap) % x--    test "dfsForestFrom vs           == Algebra.Graph.AdjacencyMap.dfsForestFrom vs . toAdjacencyMap" $ \vs x ->-          dfsForestFrom vs x         == (AM.dfsForestFrom vs . toAdjacencyMap) % x--    test "dfs vs                     == Algebra.Graph.AdjacencyMap.dfs vs . toAdjacencyMap" $ \vs x ->-          dfs vs x                   == (AM.dfs vs . toAdjacencyMap) % x--    test "reachable x                == Algebra.Graph.AdjacencyMap.reachable x . toAdjacencyMap" $ \x y ->-          reachable x y              == (AM.reachable x . toAdjacencyMap) % y--    test "topSort                    == Algebra.Graph.AdjacencyMap.topSort . toAdjacencyMap" $ \x ->-          topSort x                  == (AM.topSort . toAdjacencyMap) % x--    test "isAcyclic                  == Algebra.Graph.AdjacencyMap.isAcyclic . toAdjacencyMap" $ \x ->-          isAcyclic x                == (AM.isAcyclic . toAdjacencyMap) % x--    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->-          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x--    test "toAdjacencyMap             == foldg empty vertex overlay connect" $ \x ->-          toAdjacencyMap x           == foldg AM.empty AM.vertex AM.overlay AM.connect % x--    test "toAdjacencyMapTranspose    == foldg empty vertex overlay (flip connect)" $ \x ->-          toAdjacencyMapTranspose x  == foldg AM.empty AM.vertex AM.overlay (flip AM.connect) % x--    test "toAdjacencyIntMap          == foldg empty vertex overlay connect" $ \x ->-          toAdjacencyIntMap x        == foldg AIM.empty AIM.vertex AIM.overlay AIM.connect % x--    test "toAdjacencyIntMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->-          toAdjacencyIntMapTranspose x == foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect) % x--    test "isDfsForestOf f            == Algebra.Graph.AdjacencyMap.isDfsForestOf f . toAdjacencyMap" $ \f x ->-          isDfsForestOf f x          == (AM.isDfsForestOf f . toAdjacencyMap) % x--    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->-          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x--testFoldg :: Testsuite -> IO ()-testFoldg (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "foldg ============"-    test "foldg empty vertex        overlay connect        == id" $ \x ->-          foldg empty vertex        overlay connect % x    == id x--    test "foldg empty vertex        overlay (flip connect) == transpose" $ \x ->-          foldg empty vertex        overlay (flip connect) % x == transpose x--    test "foldg 1     (const 1)     (+)     (+)            == size" $ \x ->-          foldg 1     (const 1)     (+)     (+) % x        == size x--    test "foldg True  (const False) (&&)    (&&)           == isEmpty" $ \x ->-          foldg True  (const False) (&&)    (&&) % x       == isEmpty x--testIsEmpty :: Testsuite -> IO ()-testIsEmpty (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "isEmpty ============"-    test "isEmpty empty                       == True" $-          isEmpty % empty                     == True--    test "isEmpty (overlay empty empty)       == True" $-          isEmpty % overlay empty empty       == True--    test "isEmpty (vertex x)                  == False" $ \x ->-          isEmpty % vertex x                  == False--    test "isEmpty (removeVertex x $ vertex x) == True" $ \x ->-          isEmpty (removeVertex x % vertex x) == True--    test "isEmpty (removeEdge x y $ edge x y) == False" $ \x y ->-          isEmpty (removeEdge x y % edge x y) == False--testSize :: Testsuite -> IO ()-testSize (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "size ============"-    test "size empty         == 1" $-          size % empty       == 1--    test "size (vertex x)    == 1" $ \x ->-          size % vertex x    == 1--    test "size (overlay x y) == size x + size y" $ \x y ->-          size % overlay x y == size x + size y--    test "size (connect x y) == size x + size y" $ \x y ->-          size % connect x y == size x + size y--    test "size x             >= 1" $ \x ->-          size % x           >= 1--    test "size x             >= vertexCount x" $ \x ->-          size % x           >= vertexCount x--testHasVertex :: Testsuite -> IO ()-testHasVertex (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "hasVertex ============"-    test "hasVertex x empty            == False" $ \x ->-          hasVertex x % empty          == False--    test "hasVertex x (vertex x)       == True" $ \x ->-          hasVertex x % vertex x       == True--    test "hasVertex 1 (vertex 2)       == False" $-          hasVertex 1 % vertex 2       == False--    test "hasVertex x . removeVertex x == const False" $ \x y ->-         (hasVertex x . removeVertex x) y == const False % y--testHasEdge :: Testsuite -> IO ()-testHasEdge (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "hasEdge ============"-    test "hasEdge x y empty            == False" $ \x y ->-          hasEdge x y % empty          == False--    test "hasEdge x y (vertex z)       == False" $ \x y z ->-          hasEdge x y % vertex z       == False--    test "hasEdge x y (edge x y)       == True" $ \x y ->-          hasEdge x y % edge x y       == True--    test "hasEdge x y . removeEdge x y == const False" $ \x y z ->-         (hasEdge x y . removeEdge x y) z == const False % z--    test "hasEdge x y                  == elem (x,y) . edgeList" $ \x y z -> do-        (u, v) <- elements ((x, y) : edgeList z)-        return $ hasEdge u v z == elem (u, v) (edgeList % z)--testSymmetricHasEdge :: Testsuite -> IO ()-testSymmetricHasEdge (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "hasEdge ============"-    test "hasEdge x y empty            == False" $ \x y ->-          hasEdge x y % empty          == False--    test "hasEdge x y (vertex z)       == False" $ \x y z ->-          hasEdge x y % vertex z       == False--    test "hasEdge x y (edge x y)       == True" $ \x y ->-          hasEdge x y % edge x y       == True--    test "hasEdge x y (edge y x)       == True" $ \x y ->-          hasEdge x y % edge y x       == True--    test "hasEdge x y . removeEdge x y == const False" $ \x y z ->-         (hasEdge x y . removeEdge x y) z == const False % z--    test "hasEdge x y                  == elem (min x y, max x y) . edgeList" $ \x y z -> do-        (u, v) <- elements ((x, y) : edgeList z)-        return $ hasEdge u v z == elem (min u v, max u v) (edgeList % z)--testVertexCount :: Testsuite -> IO ()-testVertexCount (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "vertexCount ============"-    test "vertexCount empty             ==  0" $-          vertexCount % empty           ==  0--    test "vertexCount (vertex x)        ==  1" $ \x ->-          vertexCount % (vertex x)      ==  1--    test "vertexCount                   ==  length . vertexList" $ \x ->-          vertexCount % x               == (length . vertexList) x--    test "vertexCount x < vertexCount y ==> x < y" $ \x y ->-        if vertexCount x < vertexCount % y-        then property (x < y)-        else (vertexCount x > vertexCount y ==> x > y)--testEdgeCount :: Testsuite -> IO ()-testEdgeCount (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edgeCount ============"-    test "edgeCount empty      == 0" $-          edgeCount % empty    == 0--    test "edgeCount (vertex x) == 0" $ \x ->-          edgeCount % vertex x == 0--    test "edgeCount (edge x y) == 1" $ \x y ->-          edgeCount % edge x y == 1--    test "edgeCount            == length . edgeList" $ \x ->-          edgeCount % x        == (length . edgeList) x--testVertexList :: Testsuite -> IO ()-testVertexList (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "vertexList ============"-    test "vertexList empty      == []" $-          vertexList % empty    == []--    test "vertexList (vertex x) == [x]" $ \x ->-          vertexList % vertex x == [x]--    test "vertexList . vertices == nub . sort" $ \xs ->-          vertexList % vertices xs == (nubOrd . sort) xs--testEdgeList :: Testsuite -> IO ()-testEdgeList (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edgeList ============"-    test "edgeList empty          == []" $-          edgeList % empty        == []--    test "edgeList (vertex x)     == []" $ \x ->-          edgeList % vertex x     == []--    test "edgeList (edge x y)     == [(x,y)]" $ \x y ->-          edgeList % edge x y     == [(x,y)]--    test "edgeList (star 2 [3,1]) == [(2,1), (2,3)]" $-          edgeList % star 2 [3,1] == [(2,1), (2,3)]--    test "edgeList . edges        == nub . sort" $ \xs ->-          edgeList % edges xs     == (nubOrd . sort) xs--testSymmetricEdgeList :: Testsuite -> IO ()-testSymmetricEdgeList (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edgeList ============"-    test "edgeList empty          == []" $-          edgeList % empty        == []--    test "edgeList (vertex x)     == []" $ \x ->-          edgeList % vertex x     == []--    test "edgeList (edge x y)     == [(min x y, max y x)]" $ \x y ->-          edgeList % edge x y     == [(min x y, max y x)]--    test "edgeList (star 2 [3,1]) == [(1,2), (2,3)]" $-          edgeList % star 2 [3,1] == [(1,2), (2,3)]--testAdjacencyList :: Testsuite -> IO ()-testAdjacencyList (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "adjacencyList ============"-    test "adjacencyList empty          == []" $-          adjacencyList % empty        == []--    test "adjacencyList (vertex x)     == [(x, [])]" $ \x ->-          adjacencyList % vertex x     == [(x, [])]--    test "adjacencyList (edge 1 2)     == [(1, [2]), (2, [])]" $-          adjacencyList % edge 1 2     == [(1, [2]), (2, [])]--    test "adjacencyList (star 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]" $-          adjacencyList % star 2 [3,1] == [(1, []), (2, [1,3]), (3, [])]--testSymmetricAdjacencyList :: Testsuite -> IO ()-testSymmetricAdjacencyList (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "adjacencyList ============"-    test "adjacencyList empty          == []" $-          adjacencyList % empty        == []--    test "adjacencyList (vertex x)     == [(x, [])]" $ \x ->-          adjacencyList % vertex x     == [(x, [])]--    test "adjacencyList (edge 1 2)     == [(1, [2]), (2, [1])]" $-          adjacencyList % edge 1 2     == [(1, [2]), (2, [1])]--    test "adjacencyList (star 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]" $-          adjacencyList % star 2 [3,1] == [(1, [2]), (2, [1,3]), (3, [2])]--testVertexSet :: Testsuite -> IO ()-testVertexSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "vertexSet ============"-    test "vertexSet empty      == Set.empty" $-          vertexSet % empty    == Set.empty--    test "vertexSet . vertex   == Set.singleton" $ \x ->-          vertexSet % vertex x == Set.singleton x--    test "vertexSet . vertices == Set.fromList" $ \xs ->-          vertexSet % vertices xs == Set.fromList xs--testVertexIntSet :: Testsuite -> IO ()-testVertexIntSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "vertexIntSet ============"-    test "vertexIntSet empty      == IntSet.empty" $-          vertexIntSet % empty    == IntSet.empty--    test "vertexIntSet . vertex   == IntSet.singleton" $ \x ->-          vertexIntSet % vertex x == IntSet.singleton x--    test "vertexIntSet . vertices == IntSet.fromList" $ \xs ->-          vertexIntSet % vertices xs == IntSet.fromList xs--    test "vertexIntSet . clique   == IntSet.fromList" $ \xs ->-          vertexIntSet % clique xs == IntSet.fromList xs--testEdgeSet :: Testsuite -> IO ()-testEdgeSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edgeSet ============"-    test "edgeSet empty      == Set.empty" $-          edgeSet % empty    == Set.empty--    test "edgeSet (vertex x) == Set.empty" $ \x ->-          edgeSet % vertex x == Set.empty--    test "edgeSet (edge x y) == Set.singleton (x,y)" $ \x y ->-          edgeSet % edge x y == Set.singleton (x,y)--    test "edgeSet . edges    == Set.fromList" $ \xs ->-          edgeSet % edges xs == Set.fromList xs--testSymmetricEdgeSet :: Testsuite -> IO ()-testSymmetricEdgeSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "edgeSet ============"-    test "edgeSet empty      == Set.empty" $-          edgeSet % empty    == Set.empty--    test "edgeSet (vertex x) == Set.empty" $ \x ->-          edgeSet % vertex x == Set.empty--    test "edgeSet ('edge' x y) == Set.'Set.singleton' (min x y, max x y)" $ \x y ->-          edgeSet % edge x y   == Set.singleton (min x y, max x y)--testPreSet :: Testsuite -> IO ()-testPreSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "preSet ============"-    test "preSet x empty      == Set.empty" $ \x ->-          preSet x % empty    == Set.empty--    test "preSet x (vertex x) == Set.empty" $ \x ->-          preSet x % vertex x == Set.empty--    test "preSet 1 (edge 1 2) == Set.empty" $-          preSet 1 % edge 1 2 == Set.empty--    test "preSet y (edge x y) == Set.fromList [x]" $ \x y ->-          preSet y % edge x y == Set.fromList [x]--testPostSet :: Testsuite -> IO ()-testPostSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "postSet ============"-    test "postSet x empty      == Set.empty" $ \x ->-          postSet x % empty    == Set.empty--    test "postSet x (vertex x) == Set.empty" $ \x ->-          postSet x % vertex x == Set.empty--    test "postSet x (edge x y) == Set.fromList [y]" $ \x y ->-          postSet x % edge x y == Set.fromList [y]--    test "postSet 2 (edge 1 2) == Set.empty" $-          postSet 2 % edge 1 2 == Set.empty--testPreIntSet :: Testsuite -> IO ()-testPreIntSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "preIntSet ============"-    test "preIntSet x empty      == IntSet.empty" $ \x ->-          preIntSet x % empty    == IntSet.empty--    test "preIntSet x (vertex x) == IntSet.empty" $ \x ->-          preIntSet x % vertex x == IntSet.empty--    test "preIntSet 1 (edge 1 2) == IntSet.empty" $-          preIntSet 1 % edge 1 2 == IntSet.empty--    test "preIntSet y (edge x y) == IntSet.fromList [x]" $ \x y ->-          preIntSet y % edge x y == IntSet.fromList [x]--testPostIntSet :: Testsuite -> IO ()-testPostIntSet (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "postIntSet ============"-    test "postIntSet x empty      == IntSet.empty" $ \x ->-          postIntSet x % empty    == IntSet.empty--    test "postIntSet x (vertex x) == IntSet.empty" $ \x ->-          postIntSet x % vertex x == IntSet.empty--    test "postIntSet 2 (edge 1 2) == IntSet.empty" $-          postIntSet 2 % edge 1 2 == IntSet.empty--    test "postIntSet x (edge x y) == IntSet.fromList [y]" $ \x y ->-          postIntSet x % edge x y == IntSet.fromList [y]--testNeighbours :: Testsuite -> IO ()-testNeighbours (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "neighbours ============"-    test "neighbours x empty      == Set.empty" $ \x ->-          neighbours x % empty    == Set.empty--    test "neighbours x (vertex x) == Set.empty" $ \x ->-          neighbours x % vertex x == Set.empty--    test "neighbours x (edge x y) == Set.fromList [y]" $ \x y ->-          neighbours x % edge x y == Set.fromList [y]--    test "neighbours y (edge x y) == Set.fromList [x]" $ \x y ->-          neighbours y % edge x y == Set.fromList [x]--testPath :: Testsuite -> IO ()-testPath (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "path ============"-    test "path []    == empty" $-          path []    == id % empty--    test "path [x]   == vertex x" $ \x ->-          path [x]   == id % vertex x--    test "path [x,y] == edge x y" $ \x y ->-          path [x,y] == id % edge x y--testSymmetricPath :: Testsuite -> IO ()-testSymmetricPath t@(Testsuite _ (%)) = do-    testPath t-    test "path       == path . reverse" $ \xs ->-          path xs    == id % path (reverse xs)--testCircuit :: Testsuite -> IO ()-testCircuit (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "circuit ============"-    test "circuit []    == empty" $-          circuit []    == id % empty--    test "circuit [x]   == edge x x" $ \x ->-          circuit [x]   == id % edge x x--    test "circuit [x,y] == edges [(x,y), (y,x)]" $ \x y ->-          circuit [x,y] == id % edges [(x,y), (y,x)]--testSymmetricCircuit :: Testsuite -> IO ()-testSymmetricCircuit t@(Testsuite _ (%)) = do-    testCircuit t-    test "circuit       == circuit . reverse" $ \xs ->-          circuit xs    == id % circuit (reverse xs)--testClique :: Testsuite -> IO ()-testClique (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "clique ============"-    test "clique []         == empty" $-          clique []         == id % empty--    test "clique [x]        == vertex x" $ \x ->-          clique [x]        == id % vertex x--    test "clique [x,y]      == edge x y" $ \x y ->-          clique [x,y]      == id % edge x y--    test "clique [x,y,z]    == edges [(x,y), (x,z), (y,z)]" $ \x y z ->-          clique [x,y,z]    == id % edges [(x,y), (x,z), (y,z)]--    test "clique (xs ++ ys) == connect (clique xs) (clique ys)" $ \xs ys ->-          clique (xs ++ ys) == connect (clique xs) % clique ys--testSymmetricClique :: Testsuite -> IO ()-testSymmetricClique t@(Testsuite _ (%)) = do-    testClique t-    test "clique            == clique . reverse" $ \xs->-          clique xs         == id % clique (reverse xs)--testBiclique :: Testsuite -> IO ()-testBiclique (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "biclique ============"-    test "biclique []      []      == empty" $-          biclique []      []      == id % empty--    test "biclique [x]     []      == vertex x" $ \x ->-          biclique [x]     []      == id % vertex x--    test "biclique []      [y]     == vertex y" $ \y ->-          biclique []      [y]     == id % vertex y--    test "biclique [x1,x2] [y1,y2] == edges [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]" $ \x1 x2 y1 y2 ->-          biclique [x1,x2] [y1,y2] == id % edges [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]--    test "biclique xs      ys      == connect (vertices xs) (vertices ys)" $ \xs ys ->-          biclique xs      ys      == connect (vertices xs) % vertices ys--testStar :: Testsuite -> IO ()-testStar (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "star ============"-    test "star x []    == vertex x" $ \x ->-          star x []    == id % vertex x--    test "star x [y]   == edge x y" $ \x y ->-          star x [y]   == id % edge x y--    test "star x [y,z] == edges [(x,y), (x,z)]" $ \x y z ->-          star x [y,z] == id % edges [(x,y), (x,z)]--    test "star x ys    == connect (vertex x) (vertices ys)" $ \x ys ->-          star x ys    == connect (vertex x) % (vertices ys)--testTree :: Testsuite -> IO ()-testTree (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "tree ============"-    test "tree (Node x [])                                         == vertex x" $ \x ->-          tree (Node x [])                                         == id % vertex x--    test "tree (Node x [Node y [Node z []]])                       == path [x,y,z]" $ \x y z ->-          tree (Node x [Node y [Node z []]])                       == id % path [x,y,z]--    test "tree (Node x [Node y [], Node z []])                     == star x [y,z]" $ \x y z ->-          tree (Node x [Node y [], Node z []])                     == id % star x [y,z]--    test "tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges [(1,2), (1,3), (3,4), (3,5)]" $-          tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == id % edges [(1,2), (1,3), (3,4), (3,5)]--testForest :: Testsuite -> IO ()-testForest (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "forest ============"-    test "forest []                                                  == empty" $-          forest []                                                  == id % empty--    test "forest [x]                                                 == tree x" $ \x ->-          forest [x]                                                 == id % tree x--    test "forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == edges [(1,2), (1,3), (4,5)]" $-          forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == id % edges [(1,2), (1,3), (4,5)]--    test "forest                                                     == overlays . map tree" $ \x ->-          forest x                                                   == id % (overlays . map tree) x--testRemoveVertex :: Testsuite -> IO ()-testRemoveVertex (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "removeVertex ============"-    test "removeVertex x (vertex x)       == empty" $ \x ->-          removeVertex x % vertex x       == empty--    test "removeVertex 1 (vertex 2)       == vertex 2" $-          removeVertex 1 % (vertex 2)     == vertex 2--    test "removeVertex x (edge x x)       == empty" $ \x ->-          removeVertex x % (edge x x)     == empty--    test "removeVertex 1 (edge 1 2)       == vertex 2" $-          removeVertex 1 % (edge 1 2)     == vertex 2--    test "removeVertex x . removeVertex x == removeVertex x" $ \x y ->-         (removeVertex x . removeVertex x) y == removeVertex x % y--testRemoveEdge :: Testsuite -> IO ()-testRemoveEdge (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "removeEdge ============"-    test "removeEdge x y (edge x y)       == vertices [x,y]" $ \x y ->-          removeEdge x y % edge x y       == vertices [x,y]--    test "removeEdge x y . removeEdge x y == removeEdge x y" $ \x y z ->-         (removeEdge x y . removeEdge x y) z == removeEdge x y % z--    test "removeEdge x y . removeVertex x == removeVertex x" $ \x y z ->-         (removeEdge x y . removeVertex x) z == removeVertex x % z--    test "removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2" $-          removeEdge 1 1 % (1 * 1 * 2 * 2) == 1 * 2 * 2--    test "removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2" $-          removeEdge 1 2 % (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2--    -- TODO: Ouch. Generic tests are becoming awkward. We need a better way.-    when (prefix == "Fold." || prefix == "Graph.") $ do-        test "size (removeEdge x y z)         <= 3 * size z" $ \x y z ->-              size % (removeEdge x y z)       <= 3 * size z--testSymmetricRemoveEdge :: Testsuite -> IO ()-testSymmetricRemoveEdge t@(Testsuite _ (%)) = do-    testRemoveEdge t-    test "removeEdge x y                  == removeEdge y x" $ \x y z ->-          removeEdge x y z                == removeEdge y x % z--testReplaceVertex :: Testsuite -> IO ()-testReplaceVertex (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "replaceVertex ============"-    test "replaceVertex x x            == id" $ \x y ->-          replaceVertex x x % y        == y--    test "replaceVertex x y (vertex x) == vertex y" $ \x y ->-          replaceVertex x y % vertex x == vertex y--    test "replaceVertex x y            == mergeVertices (== x) y" $ \x y z ->-          replaceVertex x y % z        == mergeVertices (== x) y z--testMergeVertices :: Testsuite -> IO ()-testMergeVertices (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "mergeVertices ============"-    test "mergeVertices (const False) x    == id" $ \x y ->-          mergeVertices (const False) x % y == y--    test "mergeVertices (== x) y           == replaceVertex x y" $ \x y z ->-          mergeVertices (== x) y % z       == replaceVertex x y z--    test "mergeVertices even 1 (0 * 2)     == 1 * 1" $-          mergeVertices even 1 % (0 * 2)   == 1 * 1--    test "mergeVertices odd  1 (3 + 4 * 5) == 4 * 1" $-          mergeVertices odd  1 % (3 + 4 * 5) == 4 * 1--testTranspose :: Testsuite -> IO ()-testTranspose (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "transpose ============"-    test "transpose empty       == empty" $-          transpose % empty     == empty--    test "transpose (vertex x)  == vertex x" $ \x ->-          transpose % vertex x  == vertex x--    test "transpose (edge x y)  == edge y x" $ \x y ->-          transpose % edge x y  == edge y x--    test "transpose . transpose == id" $ size10 $ \x ->-         (transpose . transpose) % x == x--    test "edgeList . transpose  == sort . map swap . edgeList" $ \x ->-          edgeList % transpose x == (sort . map swap . edgeList) x--testGmap :: Testsuite -> IO ()-testGmap (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "gmap ============"-    test "gmap f empty      == empty" $ \(apply -> f) ->-          gmap f % empty      == empty--    test "gmap f (vertex x) == vertex (f x)" $ \(apply -> f) x ->-          gmap f % vertex x == vertex (f x)--    test "gmap f (edge x y) == edge (f x) (f y)" $ \(apply -> f) x y ->-          gmap f % edge x y == edge (f x) (f y)--    test "gmap id           == id" $ \x ->-          gmap id % x       == x--    test "gmap f . gmap g   == gmap (f . g)" $ \(apply -> f) (apply -> g) x ->-         (gmap f . gmap g) x == gmap (f . g) % x--testInduce :: Testsuite -> IO ()-testInduce (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "induce ============"-    test "induce (const True ) x      == x" $ \x ->-          induce (const True ) % x    == x--    test "induce (const False) x      == empty" $ \x ->-          induce (const False) % x    == empty--    test "induce (/= x)               == removeVertex x" $ \x y ->-          induce (/= x) % y           == removeVertex x y--    test "induce p . induce q         == induce (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) y ->-         (induce p . induce q) % y    == induce (\x -> p x && q x) y--    test "isSubgraphOf (induce p x) x == True" $ \(apply -> p) x ->-          isSubgraphOf (induce p x) % x == True--testCompose :: Testsuite -> IO ()-testCompose (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "compose ============"-    test "compose empty            x                == empty" $ \x ->-          compose empty          % x                == empty--    test "compose x                empty            == empty" $ \x ->-          compose x              % empty            == empty--    test "compose (vertex x)       y                == empty" $ \x y ->-          compose (vertex x)     % y                == empty--    test "compose x                (vertex y)       == empty" $ \x y ->-          compose x              % (vertex y)       == empty--    test "compose x                (compose y z)    == compose (compose x y) z" $ size10 $ \x y z ->-          compose x              % (compose y z)    == compose (compose x y) z--    test "compose x                (overlay y z)    == overlay (compose x y) (compose x z)" $ size10 $ \x y z ->-          compose x              % (overlay y z)    == overlay (compose x y) (compose x z)--    test "compose (overlay x y) z                   == overlay (compose x z) (compose y z)" $ size10 $ \x y z ->-          compose (overlay x y) % z                 == overlay (compose x z) (compose y z)--    test "compose (edge x y)       (edge y z)       == edge x z" $ \x y z ->-          compose (edge x y) %     (edge y z)       == edge x z--    test "compose (path    [1..5]) (path    [1..5]) == edges [(1,3),(2,4),(3,5)]" $-          compose (path    [1..5])%(path    [1..5]) == edges [(1,3),(2,4),(3,5)]--    test "compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4]" $-          compose (circuit [1..5])%(circuit [1..5]) == circuit [1,3,5,2,4]--testClosure :: Testsuite -> IO ()-testClosure (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "closure ============"-    test "closure empty           == empty" $-          closure % empty         == empty--    test "closure (vertex x)      == edge x x" $ \x ->-          closure % (vertex x)    == edge x x--    test "closure (edge x x)      == edge x x" $ \x ->-          closure % (edge x x)    == edge x x--    test "closure (edge x y)      == edges [(x,x), (x,y), (y,y)]" $ \x y ->-          closure % (edge x y)    == edges [(x,x), (x,y), (y,y)]--    test "closure (path $ nub xs) == reflexiveClosure (clique $ nub xs)" $ \xs ->-          closure % (path $ nubOrd xs) == reflexiveClosure (clique $ nubOrd xs)--    test "closure                 == reflexiveClosure . transitiveClosure" $ size10 $ \x ->-          closure % x             == (reflexiveClosure . transitiveClosure) x--    test "closure                 == transitiveClosure . reflexiveClosure" $ size10 $ \x ->-          closure % x             == (transitiveClosure . reflexiveClosure) x--    test "closure . closure       == closure" $ size10 $ \x ->-         (closure . closure) % x  == closure x--    test "postSet x (closure y)   == Set.fromList (reachable x y)" $ size10 $ \x y ->-          postSet x % (closure y) == Set.fromList (reachable x y)--testReflexiveClosure :: Testsuite -> IO ()-testReflexiveClosure (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "reflexiveClosure ============"-    test "reflexiveClosure empty              == empty" $-          reflexiveClosure % empty            == empty--    test "reflexiveClosure (vertex x)         == edge x x" $ \x ->-          reflexiveClosure % vertex x         == edge x x--    test "reflexiveClosure (edge x x)         == edge x x" $ \x ->-          reflexiveClosure % edge x x         == edge x x--    test "reflexiveClosure (edge x y)         == edges [(x,x), (x,y), (y,y)]" $ \x y ->-          reflexiveClosure % edge x y         == edges [(x,x), (x,y), (y,y)]--    test "reflexiveClosure . reflexiveClosure == reflexiveClosure" $ \x ->-         (reflexiveClosure . reflexiveClosure) x == reflexiveClosure % x--testSymmetricClosure :: Testsuite -> IO ()-testSymmetricClosure (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "symmetricClosure ============"-    test "symmetricClosure empty              == empty" $-          symmetricClosure % empty            == empty--    test "symmetricClosure (vertex x)         == vertex x" $ \x ->-          symmetricClosure % vertex x         == vertex x--    test "symmetricClosure (edge x y)         == edges [(x,y), (y,x)]" $ \x y ->-          symmetricClosure % edge x y         == edges [(x,y), (y,x)]--    test "symmetricClosure x                  == overlay x (transpose x)" $ \x ->-          symmetricClosure % x                == overlay x (transpose x)--    test "symmetricClosure . symmetricClosure == symmetricClosure" $ \x ->-         (symmetricClosure . symmetricClosure) x == symmetricClosure % x--testTransitiveClosure :: Testsuite -> IO ()-testTransitiveClosure (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "transitiveClosure ============"-    test "transitiveClosure empty               == empty" $-          transitiveClosure % empty             == empty--    test "transitiveClosure (vertex x)          == vertex x" $ \x ->-          transitiveClosure % (vertex x)        == vertex x--    test "transitiveClosure (edge x y)          == edge x y" $ \x y ->-          transitiveClosure % (edge x y)        == edge x y--    test "transitiveClosure (path $ nub xs)     == clique (nub $ xs)" $ \xs ->-          transitiveClosure % (path $ nubOrd xs) == clique (nubOrd xs)--    test "transitiveClosure . transitiveClosure == transitiveClosure" $ size10 $ \x ->-         (transitiveClosure . transitiveClosure) x == transitiveClosure % x--testSplitVertex :: Testsuite -> IO ()-testSplitVertex (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "splitVertex ============"-    test "splitVertex x []                   == removeVertex x" $ \x y ->-          splitVertex x [] % y               == removeVertex x y--    test "splitVertex x [x]                  == id" $ \x y ->-          splitVertex x [x] % y              == y--    test "splitVertex x [y]                  == replaceVertex x y" $ \x y z ->-          splitVertex x [y] % z              == replaceVertex x y z--    test "splitVertex 1 [0, 1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3)" $-          splitVertex 1 [0, 1] % (1 * (2 + 3)) == (0 + 1) * (2 + 3)--testBind :: Testsuite -> IO ()-testBind (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "bind ============"-    test "bind empty f         == empty" $ \(apply -> f) ->-          bind empty f         == id % empty--    test "bind (vertex x) f    == f x" $ \(apply -> f) x ->-          bind (vertex x) f    == id % f x--    test "bind (edge x y) f    == connect (f x) (f y)" $ \(apply -> f) x y ->-          bind (edge x y) f    == connect (f x) % f y--    test "bind (vertices xs) f == overlays (map f xs)" $ size10 $ \xs (apply -> f) ->-          bind (vertices xs) f == id % overlays (map f xs)--    test "bind x (const empty) == empty" $ \x ->-          bind x (const empty) == id % empty--    test "bind x vertex        == x" $ \x ->-          bind x vertex        == id % x--    test "bind (bind x f) g    == bind x (\\y -> bind (f y) g)" $ size10 $ \x (apply -> f) (apply -> g) ->-          bind (bind x f) g    == bind (id % x) (\y -> bind (f y) g)--testSimplify :: Testsuite -> IO ()-testSimplify (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "simplify ============"-    test "simplify              == id" $ \x ->-          simplify % x          == x--    test "size (simplify x)     <= size x" $ \x ->-          size % simplify x     <= size x---testDfsForest :: Testsuite -> IO ()-testDfsForest (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "dfsForest ============"-    test "dfsForest empty                       == []" $-          dfsForest % empty                     == []--    test "forest (dfsForest $ edge 1 1)         == vertex 1" $-          forest (dfsForest % edge 1 1)         == id % vertex 1--    test "forest (dfsForest $ edge 1 2)         == edge 1 2" $-          forest (dfsForest % edge 1 2)         == id % edge 1 2--    test "forest (dfsForest $ edge 2 1)         == vertices [1,2]" $-          forest (dfsForest % edge 2 1)         == id % vertices [1,2]--    test "isSubgraphOf (forest $ dfsForest x) x == True" $ \x ->-          isSubgraphOf (forest $ dfsForest x) % x == True--    test "isDfsForestOf (dfsForest x) x         == True" $ \x ->-          isDfsForestOf (dfsForest x) % x       == True--    test "dfsForest . forest . dfsForest        == dfsForest" $ \x ->-          dfsForest % forest (dfsForest x)      == dfsForest % x--    test "dfsForest (vertices vs)               == map (\\v -> Node v []) (nub $ sort vs)" $ \vs ->-          dfsForest % vertices vs               == map (\v -> Node v []) (nub $ sort vs)--    test "dfsForest $ 3 * (1 + 4) * (1 + 5)     == <correct result>" $-          dfsForest % (3 * (1 + 4) * (1 + 5))   == [ Node { rootLabel = 1-                                                   , subForest = [ Node { rootLabel = 5-                                                                        , subForest = [] }]}-                                                   , Node { rootLabel = 3-                                                   , subForest = [ Node { rootLabel = 4-                                                                        , subForest = [] }]}]--testDfsForestFrom :: Testsuite -> IO ()-testDfsForestFrom (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "dfsForestFrom ============"-    test "dfsForestFrom vs empty                           == []" $ \vs ->-          dfsForestFrom vs % empty                         == []--    test "forest (dfsForestFrom [1]   $ edge 1 1)          == vertex 1" $-          forest (dfsForestFrom [1]   % edge 1 1)          == id % vertex 1--    test "forest (dfsForestFrom [1]   $ edge 1 2)          == edge 1 2" $-          forest (dfsForestFrom [1]   % edge 1 2)          == id % edge 1 2--    test "forest (dfsForestFrom [2]   $ edge 1 2)          == vertex 2" $-          forest (dfsForestFrom [2]   % edge 1 2)          == id % vertex 2--    test "forest (dfsForestFrom [3]   $ edge 1 2)          == empty" $-          forest (dfsForestFrom [3]   % edge 1 2)          == id % empty--    test "forest (dfsForestFrom [2,1] $ edge 1 2)          == vertices [1,2]" $-          forest (dfsForestFrom [2,1] % edge 1 2)          == id % vertices [1,2]--    test "isSubgraphOf (forest $ dfsForestFrom vs x) x     == True" $ \vs x ->-          isSubgraphOf (forest $ dfsForestFrom vs x) % x   == True--    test "isDfsForestOf (dfsForestFrom (vertexList x) x) x == True" $ \x ->-          isDfsForestOf (dfsForestFrom (vertexList x) x) % x == True--    test "dfsForestFrom (vertexList x) x                   == dfsForest x" $ \x ->-          dfsForestFrom (vertexList x) % x                 == dfsForest % x--    test "dfsForestFrom vs             (vertices vs)       == map (\\v -> Node v []) (nub vs)" $ \vs ->-          dfsForestFrom vs           %  vertices vs        == map (\v -> Node v []) (nub vs)--    test "dfsForestFrom []             x                   == []" $ \x ->-          dfsForestFrom []           % x                   == []--    test "dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5)      == <correct result>" $-          dfsForestFrom [1,4] % (3 * (1 + 4) * (1 + 5))    == [ Node { rootLabel = 1-                                                                     , subForest = [ Node { rootLabel = 5-                                                                                          , subForest = [] }]}-                                                              , Node { rootLabel = 4-                                                                     , subForest = [] }]--testDfs :: Testsuite -> IO ()-testDfs (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "dfs ============"-    test "dfs vs    $ empty                    == []" $ \vs ->-          dfs vs    % empty                    == []--    test "dfs [1]   $ edge 1 1                 == [1]" $-          dfs [1]   % edge 1 1                 == [1]--    test "dfs [1]   $ edge 1 2                 == [1,2]" $-          dfs [1]   % edge 1 2                 == [1,2]--    test "dfs [2]   $ edge 1 2                 == [2]" $-          dfs [2]   % edge 1 2                 == [2]--    test "dfs [3]   $ edge 1 2                 == []" $-          dfs [3]   % edge 1 2                 == []--    test "dfs [1,2] $ edge 1 2                 == [1,2]" $-          dfs [1,2] % edge 1 2                 == [1,2]--    test "dfs [2,1] $ edge 1 2                 == [2,1]" $-          dfs [2,1] % edge 1 2                 == [2,1]--    test "dfs []    $ x                        == []" $ \x ->-          dfs []    % x                        == []--    test "dfs [1,4] $ 3 * (1 + 4) * (1 + 5)    == [1,5,4]" $-          dfs [1,4] % (3 * (1 + 4) * (1 + 5))  == [1,5,4]--    test "isSubgraphOf (vertices $ dfs vs x) x == True" $ \vs x ->-          isSubgraphOf (vertices $ dfs vs x) % x == True--testReachable :: Testsuite -> IO ()-testReachable (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "dfs ============"-    test "reachable x $ empty                       == []" $ \x ->-          reachable x % empty                       == []--    test "reachable 1 $ vertex 1                    == [1]" $-          reachable 1 % vertex 1                    == [1]--    test "reachable 1 $ vertex 2                    == []" $-          reachable 1 % vertex 2                    == []--    test "reachable 1 $ edge 1 1                    == [1]" $-          reachable 1 % edge 1 1                    == [1]--    test "reachable 1 $ edge 1 2                    == [1,2]" $-          reachable 1 % edge 1 2                    == [1,2]--    test "reachable 4 $ path    [1..8]              == [4..8]" $-          reachable 4 % path    [1..8]              == [4..8]--    test "reachable 4 $ circuit [1..8]              == [4..8] ++ [1..3]" $-          reachable 4 % circuit [1..8]              == [4..8] ++ [1..3]--    test "reachable 8 $ clique  [8,7..1]            == [8] ++ [1..7]" $-          reachable 8 % clique  [8,7..1]            == [8] ++ [1..7]--    test "isSubgraphOf (vertices $ reachable x y) y == True" $ \x y ->-          isSubgraphOf (vertices $ reachable x y) % y == True--testTopSort :: Testsuite -> IO ()-testTopSort (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "topSort ============"-    test "topSort (1 * 2 + 3 * 1)               == Just [3,1,2]" $-          topSort % (1 * 2 + 3 * 1)             == Just [3,1,2]--    test "topSort (1 * 2 + 2 * 1)               == Nothing" $-          topSort % (1 * 2 + 2 * 1)             == Nothing--    test "fmap (flip isTopSortOf x) (topSort x) /= Just False" $ \x ->-          fmap (flip isTopSortOf x) (topSort % x) /= Just False--testIsAcyclic :: Testsuite -> IO ()-testIsAcyclic (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "testIsAcyclic ============"-    test "isAcyclic (1 * 2 + 3 * 1) == True" $-          isAcyclic % (1 * 2 + 3 * 1) == True--    test "isAcyclic (1 * 2 + 2 * 1) == False" $-          isAcyclic % (1 * 2 + 2 * 1) == False--    test "isAcyclic . circuit       == null" $ \xs ->-          isAcyclic % circuit xs    == null xs--    test "isAcyclic                 == isJust . topSort" $ \x ->-          isAcyclic % x             == isJust (topSort x)--testIsDfsForestOf :: Testsuite -> IO ()-testIsDfsForestOf (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "isDfsForestOf ============"-    test "isDfsForestOf []                              empty            == True" $-          isDfsForestOf [] %                            empty            == True--    test "isDfsForestOf []                              (vertex 1)       == False" $-          isDfsForestOf [] %                            (vertex 1)       == False--    test "isDfsForestOf [Node 1 []]                     (vertex 1)       == True" $-          isDfsForestOf [Node 1 []] %                   (vertex 1)       == True--    test "isDfsForestOf [Node 1 []]                     (vertex 2)       == False" $-          isDfsForestOf [Node 1 []] %                   (vertex 2)       == False--    test "isDfsForestOf [Node 1 [], Node 1 []]          (vertex 1)       == False" $-          isDfsForestOf [Node 1 [], Node 1 []] %        (vertex 1)       == False--    test "isDfsForestOf [Node 1 []]                     (edge 1 1)       == True" $-          isDfsForestOf [Node 1 []] %                   (edge 1 1)       == True--    test "isDfsForestOf [Node 1 []]                     (edge 1 2)       == False" $-          isDfsForestOf [Node 1 []] %                   (edge 1 2)       == False--    test "isDfsForestOf [Node 1 [], Node 2 []]          (edge 1 2)       == False" $-          isDfsForestOf [Node 1 [], Node 2 []] %        (edge 1 2)       == False--    test "isDfsForestOf [Node 2 [], Node 1 []]          (edge 1 2)       == True" $-          isDfsForestOf [Node 2 [], Node 1 []] %        (edge 1 2)       == True--    test "isDfsForestOf [Node 1 [Node 2 []]]            (edge 1 2)       == True" $-          isDfsForestOf [Node 1 [Node 2 []]] %          (edge 1 2)       == True--    test "isDfsForestOf [Node 1 [], Node 2 []]          (vertices [1,2]) == True" $-          isDfsForestOf [Node 1 [], Node 2 []] %        (vertices [1,2]) == True--    test "isDfsForestOf [Node 2 [], Node 1 []]          (vertices [1,2]) == True" $-          isDfsForestOf [Node 2 [], Node 1 []] %        (vertices [1,2]) == True--    test "isDfsForestOf [Node 1 [Node 2 []]]            (vertices [1,2]) == False" $-          isDfsForestOf [Node 1 [Node 2 []]] %          (vertices [1,2]) == False--    test "isDfsForestOf [Node 1 [Node 2 [Node 3 []]]]   (path [1,2,3])   == True" $-          isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] % (path [1,2,3])   == True--    test "isDfsForestOf [Node 1 [Node 3 [Node 2 []]]]   (path [1,2,3])   == False" $-          isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] % (path [1,2,3])   == False--    test "isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] (path [1,2,3])   == True" $-          isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] % (path [1,2,3]) == True--    test "isDfsForestOf [Node 2 [Node 3 []], Node 1 []] (path [1,2,3])   == True" $-          isDfsForestOf [Node 2 [Node 3 []], Node 1 []] % (path [1,2,3]) == True--    test "isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] (path [1,2,3])   == False" $-          isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] % (path [1,2,3]) == False--testIsTopSortOf :: Testsuite -> IO ()-testIsTopSortOf (Testsuite prefix (%)) = do-    putStrLn $ "\n============ " ++ prefix ++ "isTopSortOf ============"-    test "isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True" $-          isTopSortOf [3,1,2] % (1 * 2 + 3 * 1) == True--    test "isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False" $-          isTopSortOf [1,2,3] % (1 * 2 + 3 * 1) == False--    test "isTopSortOf []      (1 * 2 + 3 * 1) == False" $-          isTopSortOf []    % (1 * 2 + 3 * 1) == False--    test "isTopSortOf []      empty           == True" $-          isTopSortOf []    % empty           == True--    test "isTopSortOf [x]     (vertex x)      == True" $ \x ->-          isTopSortOf [x]    % vertex x       == True--    test "isTopSortOf [x]     (edge x x)      == False" $ \x ->-          isTopSortOf [x]    % edge x x       == False+{-# LANGUAGE RecordWildCards, GADTs, ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test.Generic+-- Copyright  : (c) Andrey Mokhov 2016-2019+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Generic graph API testing.+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Generic where++import Control.Monad (when)+import Data.Either+import Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import Data.Tree+import Data.Tuple++import Algebra.Graph.Test+import Algebra.Graph.Test.API++import qualified Algebra.Graph                        as G+import qualified Algebra.Graph.AdjacencyMap           as AM+import qualified Algebra.Graph.AdjacencyMap.Algorithm as AM+import qualified Algebra.Graph.AdjacencyIntMap        as AIM+import qualified Data.Set                             as Set+import qualified Data.IntSet                          as IntSet++type ModulePrefix = String+type Testsuite g c = (ModulePrefix, API g c)+type TestsuiteInt g = (ModulePrefix, API g ((~) Int))++testBasicPrimitives :: TestsuiteInt g -> IO ()+testBasicPrimitives = mconcat [ testOrd+                              , testEmpty+                              , testVertex+                              , testEdge+                              , testOverlay+                              , testConnect+                              , testVertices+                              , testEdges+                              , testOverlays+                              , testConnects ]++testSymmetricBasicPrimitives :: TestsuiteInt g -> IO ()+testSymmetricBasicPrimitives = mconcat [ testSymmetricOrd+                                       , testEmpty+                                       , testVertex+                                       , testSymmetricEdge+                                       , testOverlay+                                       , testSymmetricConnect+                                       , testVertices+                                       , testSymmetricEdges+                                       , testOverlays+                                       , testSymmetricConnects ]++testToGraph :: TestsuiteInt g -> IO ()+testToGraph = mconcat [ testToGraphDefault+                      , testFoldg+                      , testIsEmpty+                      , testHasVertex+                      , testHasEdge+                      , testVertexCount+                      , testEdgeCount+                      , testVertexList+                      , testVertexSet+                      , testVertexIntSet+                      , testEdgeList+                      , testEdgeSet+                      , testAdjacencyList+                      , testPreSet+                      , testPreIntSet+                      , testPostSet+                      , testPostIntSet ]++testSymmetricToGraph :: TestsuiteInt g -> IO ()+testSymmetricToGraph = mconcat [ testSymmetricToGraphDefault+                               , testIsEmpty+                               , testHasVertex+                               , testSymmetricHasEdge+                               , testVertexCount+                               , testEdgeCount+                               , testVertexList+                               , testVertexSet+                               , testVertexIntSet+                               , testSymmetricEdgeList+                               , testSymmetricEdgeSet+                               , testSymmetricAdjacencyList+                               , testNeighbours ]++testRelational :: TestsuiteInt g -> IO ()+testRelational = mconcat [ testCompose+                         , testClosure+                         , testReflexiveClosure+                         , testSymmetricClosure+                         , testTransitiveClosure ]++testGraphFamilies :: TestsuiteInt g -> IO ()+testGraphFamilies = mconcat [ testPath+                            , testCircuit+                            , testClique+                            , testBiclique+                            , testStar+                            , testStars+                            , testTree+                            , testForest ]++testSymmetricGraphFamilies :: TestsuiteInt g -> IO ()+testSymmetricGraphFamilies = mconcat [ testSymmetricPath+                                     , testSymmetricCircuit+                                     , testSymmetricClique+                                     , testBiclique+                                     , testStar+                                     , testStars+                                     , testTree+                                     , testForest ]++testTransformations :: TestsuiteInt g -> IO ()+testTransformations = mconcat [ testRemoveVertex+                              , testRemoveEdge+                              , testReplaceVertex+                              , testMergeVertices+                              , testTranspose+                              , testGmap+                              , testInduce ]++testSymmetricTransformations :: TestsuiteInt g -> IO ()+testSymmetricTransformations = mconcat [ testRemoveVertex+                                       , testSymmetricRemoveEdge+                                       , testReplaceVertex+                                       , testMergeVertices+                                       , testGmap+                                       , testInduce ]++testConsistent :: TestsuiteInt g -> IO ()+testConsistent (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "consistent ============"+    test "Consistency of the Arbitrary instance" $ \x -> consistent x++    putStrLn ""+    test "consistent empty         == True" $+          consistent empty         == True++    test "consistent (vertex x)    == True" $ \x ->+          consistent (vertex x)    == True++    test "consistent (overlay x y) == True" $ \x y ->+          consistent (overlay x y) == True++    test "consistent (connect x y) == True" $ \x y ->+          consistent (connect x y) == True++    test "consistent (edge x y)    == True" $ \x y ->+          consistent (edge x y)    == True++    test "consistent (edges xs)    == True" $ \xs ->+          consistent (edges xs)    == True++    test "consistent (stars xs)    == True" $ \xs ->+          consistent (stars xs)    == True++testShow :: TestsuiteInt g -> IO ()+testShow (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "Show ============"+    test "show (empty    ) == \"empty\"" $+          show (empty    ) ==  "empty"++    test "show (1        ) == \"vertex 1\"" $+          show (1 `asTypeOf` empty) ==  "vertex 1"++    test "show (1 + 2    ) == \"vertices [1,2]\"" $+          show (1 + 2 `asTypeOf` empty) ==  "vertices [1,2]"++    test "show (1 * 2    ) == \"edge 1 2\"" $+          show (1 * 2 `asTypeOf` empty) ==  "edge 1 2"++    test "show (1 * 2 * 3) == \"edges [(1,2),(1,3),(2,3)]\"" $+          show (1 * 2 * 3 `asTypeOf` empty) == "edges [(1,2),(1,3),(2,3)]"++    test "show (1 * 2 + 3) == \"overlay (vertex 3) (edge 1 2)\"" $+          show (1 * 2 + 3 `asTypeOf` empty) == "overlay (vertex 3) (edge 1 2)"++    putStrLn ""+    test "show (vertex (-1)                            ) == \"vertex (-1)\"" $+          show (vertex (-1)                            ) == "vertex (-1)"++    test "show (vertex (-1) + vertex (-2)              ) == \"vertices [-2,-1]\"" $+          show (vertex (-1) + vertex (-2)              ) == "vertices [-2,-1]"++    test "show (vertex (-2) * vertex (-1)              ) == \"edge (-2) (-1)\"" $+          show (vertex (-2) * vertex (-1)              ) == "edge (-2) (-1)"++    test "show (vertex (-3) * vertex (-2) * vertex (-1)) == \"edges [(-3,-2),(-3,-1),(-2,-1)]\"" $+          show (vertex (-3) * vertex (-2) * vertex (-1)) == "edges [(-3,-2),(-3,-1),(-2,-1)]"++    test "show (vertex (-3) * vertex (-2) + vertex (-1)) == \"overlay (vertex (-1)) (edge (-3) (-2))\"" $+          show (vertex (-3) * vertex (-2) + vertex (-1)) == "overlay (vertex (-1)) (edge (-3) (-2))"++testSymmetricShow :: TestsuiteInt g -> IO ()+testSymmetricShow t@(_, API{..}) = do+    testShow t+    putStrLn ""+    test "show (2 * 1    ) == \"edge 1 2\"" $+          show (2 * 1 `asTypeOf` empty) ==  "edge 1 2"++    test "show (1 * 2 * 1) == \"edges [(1,1),(1,2)]\"" $+          show (1 * 2 * 1 `asTypeOf` empty) == "edges [(1,1),(1,2)]"++    test "show (3 * 2 * 1) == \"edges [(1,2),(1,3),(2,3)]\"" $+          show (3 * 2 * 1 `asTypeOf` empty) == "edges [(1,2),(1,3),(2,3)]"++testOrd :: TestsuiteInt g -> IO ()+testOrd (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "Ord ============"+    test "vertex 1 <  vertex 2" $+          vertex 1 <  vertex 2++    test "vertex 3 <  edge 1 2" $+          vertex 3 <  edge 1 2++    test "vertex 1 <  edge 1 1" $+          vertex 1 <  edge 1 1++    test "edge 1 1 <  edge 1 2" $+          edge 1 1 <  edge 1 2++    test "edge 1 2 <  edge 1 1 + edge 2 2" $+          edge 1 2 <  edge 1 1 + edge 2 2++    test "edge 1 2 <  edge 1 3" $+          edge 1 2 <  edge 1 3++    test "x        <= x + y" $ \x y ->+          x        <= x + (y `asTypeOf` empty)++    test "x + y    <= x * y" $ \x y ->+          x + y    <= x * (y `asTypeOf` empty)++testSymmetricOrd :: TestsuiteInt g -> IO ()+testSymmetricOrd (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "Ord ============"+    test "vertex 1 <  vertex 2" $+          vertex 1 <  vertex 2++    test "vertex 3 <  edge 1 2" $+          vertex 3 <  edge 1 2++    test "vertex 1 <  edge 1 1" $+          vertex 1 <  edge 1 1++    test "edge 1 1 <  edge 1 2" $+          edge 1 1 <  edge 1 2++    test "edge 1 2 <  edge 1 1 + edge 2 2" $+          edge 1 2 <  edge 1 1 + edge 2 2++    test "edge 2 1 <  edge 1 3" $+          edge 2 1 <  edge 1 3++    test "edge 1 2 == edge 2 1" $+          edge 1 2 == edge 2 1++    test "x        <= x + y" $ \x y ->+          x        <= x + (y `asTypeOf` empty)++    test "x + y    <= x * y" $ \x y ->+          x + y    <= x * (y `asTypeOf` empty)++testEmpty :: TestsuiteInt g -> IO ()+testEmpty (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "empty ============"+    test "isEmpty     empty == True" $+          isEmpty     empty == True++    test "hasVertex x empty == False" $ \x ->+          hasVertex x empty == False++    test "vertexCount empty == 0" $+          vertexCount empty == 0++    test "edgeCount   empty == 0" $+          edgeCount   empty == 0++testVertex :: TestsuiteInt g -> IO ()+testVertex (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "vertex ============"+    test "isEmpty     (vertex x) == False" $ \x ->+          isEmpty     (vertex x) == False++    test "hasVertex x (vertex y) == (x == y)" $ \x y ->+          hasVertex x (vertex y) == (x == y)++    test "vertexCount (vertex x) == 1" $ \x ->+          vertexCount (vertex x) == 1++    test "edgeCount   (vertex x) == 0" $ \x ->+          edgeCount   (vertex x) == 0++testEdge :: TestsuiteInt g -> IO ()+testEdge (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edge ============"+    test "edge x y               == connect (vertex x) (vertex y)" $ \x y ->+          edge x y               == connect (vertex x) (vertex y)++    test "hasEdge x y (edge x y) == True" $ \x y ->+          hasEdge x y (edge x y) == True++    test "edgeCount   (edge x y) == 1" $ \x y ->+          edgeCount   (edge x y) == 1++    test "vertexCount (edge 1 1) == 1" $+          vertexCount (edge 1 1) == 1++    test "vertexCount (edge 1 2) == 2" $+          vertexCount (edge 1 2) == 2++testSymmetricEdge :: TestsuiteInt g -> IO ()+testSymmetricEdge (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edge ============"+    test "edge x y               == connect (vertex x) (vertex y)" $ \x y ->+          edge x y               == connect (vertex x) (vertex y)++    test "edge x y               == edge y x" $ \x y ->+          edge x y               == edge y x++    test "edge x y               == edges [(x,y), (y,x)]" $ \x y ->+          edge x y               == edges [(x,y), (y,x)]++    test "hasEdge x y (edge x y) == True" $ \x y ->+          hasEdge x y (edge x y) == True++    test "edgeCount   (edge x y) == 1" $ \x y ->+          edgeCount   (edge x y) == 1++    test "vertexCount (edge 1 1) == 1" $+          vertexCount (edge 1 1) == 1++    test "vertexCount (edge 1 2) == 2" $+          vertexCount (edge 1 2) == 2++testOverlay :: TestsuiteInt g -> IO ()+testOverlay (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "overlay ============"+    test "isEmpty     (overlay x y) == isEmpty   x   && isEmpty   y" $ \x y ->+          isEmpty     (overlay x y) ==(isEmpty   x   && isEmpty   y)++    test "hasVertex z (overlay x y) == hasVertex z x || hasVertex z y" $ \x y z ->+          hasVertex z (overlay x y) ==(hasVertex z x || hasVertex z y)++    test "vertexCount (overlay x y) >= vertexCount x" $ \x y ->+          vertexCount (overlay x y) >= vertexCount x++    test "vertexCount (overlay x y) <= vertexCount x + vertexCount y" $ \x y ->+          vertexCount (overlay x y) <= vertexCount x + vertexCount y++    test "edgeCount   (overlay x y) >= edgeCount x" $ \x y ->+          edgeCount   (overlay x y) >= edgeCount x++    test "edgeCount   (overlay x y) <= edgeCount x   + edgeCount y" $ \x y ->+          edgeCount   (overlay x y) <= edgeCount x   + edgeCount y++    test "vertexCount (overlay 1 2) == 2" $+          vertexCount (overlay 1 2) == 2++    test "edgeCount   (overlay 1 2) == 0" $+          edgeCount   (overlay 1 2) == 0++testConnect :: TestsuiteInt g -> IO ()+testConnect (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "connect ============"+    test "isEmpty     (connect x y) == isEmpty   x   && isEmpty   y" $ \x y ->+          isEmpty     (connect x y) ==(isEmpty   x   && isEmpty   y)++    test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \x y z ->+          hasVertex z (connect x y) ==(hasVertex z x || hasVertex z y)++    test "vertexCount (connect x y) >= vertexCount x" $ \x y ->+          vertexCount (connect x y) >= vertexCount x++    test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \x y ->+          vertexCount (connect x y) <= vertexCount x + vertexCount y++    test "edgeCount   (connect x y) >= edgeCount x" $ \x y ->+          edgeCount   (connect x y) >= edgeCount x++    test "edgeCount   (connect x y) >= edgeCount y" $ \x y ->+          edgeCount   (connect x y) >= edgeCount y++    test "edgeCount   (connect x y) >= vertexCount x * vertexCount y" $ \x y ->+          edgeCount   (connect x y) >= vertexCount x * vertexCount y++    test "edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \x y ->+          edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y++    test "vertexCount (connect 1 2) == 2" $+          vertexCount (connect 1 2) == 2++    test "edgeCount   (connect 1 2) == 1" $+          edgeCount   (connect 1 2) == 1++testSymmetricConnect :: TestsuiteInt g -> IO ()+testSymmetricConnect (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "connect ============"+    test "connect x y               == connect y x" $ \x y ->+          connect x y               == connect y x++    test "isEmpty     (connect x y) == isEmpty   x   && isEmpty   y" $ \x y ->+          isEmpty     (connect x y) ==(isEmpty   x   && isEmpty   y)++    test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \x y z ->+          hasVertex z (connect x y) ==(hasVertex z x || hasVertex z y)++    test "vertexCount (connect x y) >= vertexCount x" $ \x y ->+          vertexCount (connect x y) >= vertexCount x++    test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \x y ->+          vertexCount (connect x y) <= vertexCount x + vertexCount y++    test "edgeCount   (connect x y) >= edgeCount x" $ \x y ->+          edgeCount   (connect x y) >= edgeCount x++    test "edgeCount   (connect x y) >= edgeCount y" $ \x y ->+          edgeCount   (connect x y) >= edgeCount y++    test "edgeCount   (connect x y) >= vertexCount x * vertexCount y `div` 2" $ \x y ->+          edgeCount   (connect x y) >= vertexCount x * vertexCount y `div` 2++    test "edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \x y ->+          edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y++    test "vertexCount (connect 1 2) == 2" $+          vertexCount (connect 1 2) == 2++    test "edgeCount   (connect 1 2) == 1" $+          edgeCount   (connect 1 2) == 1++testVertices :: TestsuiteInt g -> IO ()+testVertices (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "vertices ============"+    test "vertices []            == empty" $+          vertices []            == empty++    test "vertices [x]           == vertex x" $ \x ->+          vertices [x]           == vertex x++    test "hasVertex x . vertices == elem x" $ \x xs ->+         (hasVertex x . vertices) xs == elem x xs++    test "vertexCount . vertices == length . nub" $ \xs ->+         (vertexCount . vertices) xs == (length . nubOrd) xs++    test "vertexSet   . vertices == Set.fromList" $ \xs ->+         (vertexSet   . vertices) xs == Set.fromList xs++testEdges :: TestsuiteInt g -> IO ()+testEdges (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edges ============"+    test "edges []          == empty" $+          edges []          == empty++    test "edges [(x,y)]     == edge x y" $ \x y ->+          edges [(x,y)]     == edge x y++    test "edges             == overlays . map (uncurry edge)" $ \xs ->+          edges xs          == (overlays . map (uncurry edge)) xs++    test "edgeCount . edges == length . nub" $ \xs ->+         (edgeCount . edges) xs == (length . nubOrd) xs++testSymmetricEdges :: TestsuiteInt g -> IO ()+testSymmetricEdges (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edges ============"+    test "edges []             == empty" $+          edges []             == empty++    test "edges [(x,y)]        == edge x y" $ \x y ->+          edges [(x,y)]        == edge x y++    test "edges [(x,y), (y,x)] == edge x y" $ \x y ->+          edges [(x,y), (y,x)] == edge x y++testOverlays :: TestsuiteInt g -> IO ()+testOverlays (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "overlays ============"+    test "overlays []        == empty" $+          overlays []        == empty++    test "overlays [x]       == x" $ \x ->+          overlays [x]       == x++    test "overlays [x,y]     == overlay x y" $ \x y ->+          overlays [x,y]     == overlay x y++    test "overlays           == foldr overlay empty" $ size10 $ \xs ->+          overlays xs        == foldr overlay empty xs++    test "isEmpty . overlays == all isEmpty" $ size10 $ \xs ->+         (isEmpty . overlays) xs == all isEmpty xs++testConnects :: TestsuiteInt g -> IO ()+testConnects (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "connects ============"+    test "connects []        == empty" $+          connects []        == empty++    test "connects [x]       == x" $ \x ->+          connects [x]       == x++    test "connects [x,y]     == connect x y" $ \x y ->+          connects [x,y]     == connect x y++    test "connects           == foldr connect empty" $ size10 $ \xs ->+          connects xs        == foldr connect empty xs++    test "isEmpty . connects == all isEmpty" $ size10 $ \xs ->+         (isEmpty . connects) xs == all isEmpty xs++testSymmetricConnects :: TestsuiteInt g -> IO ()+testSymmetricConnects t@(_, API{..}) = do+    testConnects t+    test "connects           == connects . reverse" $ size10 $ \xs ->+          connects xs        == connects (reverse xs)++testStars :: TestsuiteInt g -> IO ()+testStars (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "stars ============"+    test "stars []                      == empty" $+          stars []                      == empty++    test "stars [(x, [])]               == vertex x" $ \x ->+          stars [(x, [])]               == vertex x++    test "stars [(x, [y])]              == edge x y" $ \x y ->+          stars [(x, [y])]              == edge x y++    test "stars [(x, ys)]               == star x ys" $ \x ys ->+          stars [(x, ys)]               == star x ys++    test "stars                         == overlays . map (uncurry star)" $ \xs ->+          stars xs                      == overlays (map (uncurry star) xs)++    test "stars . adjacencyList         == id" $ \x ->+         (stars . adjacencyList) x      == id x++    test "overlay (stars xs) (stars ys) == stars (xs ++ ys)" $ \xs ys ->+          overlay (stars xs) (stars ys) == stars (xs ++ ys)++testFromAdjacencySets :: TestsuiteInt g -> IO ()+testFromAdjacencySets (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencySets ============"+    test "fromAdjacencySets []                                  == empty" $+          fromAdjacencySets []                                  == empty++    test "fromAdjacencySets [(x, Set.empty)]                    == vertex x" $ \x ->+          fromAdjacencySets [(x, Set.empty)]                    == vertex x++    test "fromAdjacencySets [(x, Set.singleton y)]              == edge x y" $ \x y ->+          fromAdjacencySets [(x, Set.singleton y)]              == edge x y++    test "fromAdjacencySets . map (fmap Set.fromList)           == stars" $ \x ->+         (fromAdjacencySets . map (fmap Set.fromList)) x        == stars x++    test "overlay (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)" $ \xs ys ->+          overlay (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)++testFromAdjacencyIntSets :: TestsuiteInt g -> IO ()+testFromAdjacencyIntSets (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencyIntSets ============"+    test "fromAdjacencyIntSets []                                     == empty" $+          fromAdjacencyIntSets []                                     == empty++    test "fromAdjacencyIntSets [(x, IntSet.empty)]                    == vertex x" $ \x ->+          fromAdjacencyIntSets [(x, IntSet.empty)]                    == vertex x++    test "fromAdjacencyIntSets [(x, IntSet.singleton y)]              == edge x y" $ \x y ->+          fromAdjacencyIntSets [(x, IntSet.singleton y)]              == edge x y++    test "fromAdjacencyIntSets . map (fmap IntSet.fromList)           == stars" $ \x ->+         (fromAdjacencyIntSets . map (fmap IntSet.fromList)) x        == stars x++    test "overlay (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)" $ \xs ys ->+          overlay (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)++testIsSubgraphOf :: TestsuiteInt g -> IO ()+testIsSubgraphOf (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "isSubgraphOf ============"+    test "isSubgraphOf empty         x             ==  True" $ \x ->+          isSubgraphOf empty         x             ==  True++    test "isSubgraphOf (vertex x)    empty         ==  False" $ \x ->+          isSubgraphOf (vertex x)    empty         ==  False++    test "isSubgraphOf x             (overlay x y) ==  True" $ \x y ->+          isSubgraphOf x             (overlay x y) ==  True++    test "isSubgraphOf (overlay x y) (connect x y) ==  True" $ \x y ->+          isSubgraphOf (overlay x y) (connect x y) ==  True++    test "isSubgraphOf (path xs)     (circuit xs)  ==  True" $ \xs ->+          isSubgraphOf (path xs)     (circuit xs)  ==  True++    test "isSubgraphOf x y                         ==> x <= y" $ \x z ->+        let y = x + z -- Make sure we hit the precondition+        in isSubgraphOf x y                        ==> x <= y++testSymmetricIsSubgraphOf :: TestsuiteInt g -> IO ()+testSymmetricIsSubgraphOf t@(_, API{..}) = do+    testIsSubgraphOf t+    test "isSubgraphOf (edge x y) (edge y x)       ==  True" $ \x y ->+          isSubgraphOf (edge x y) (edge y x)       ==  True++testToGraphDefault :: TestsuiteInt g -> IO ()+testToGraphDefault (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"+    test "toGraph                    == foldg Empty Vertex Overlay Connect" $ \x ->+          toGraph x                  == foldg G.Empty G.Vertex G.Overlay G.Connect x++    test "foldg                      == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->+          foldg e v o c x            == (G.foldg (e :: Int) v o c . toGraph) x++    test "isEmpty                    == foldg True (const False) (&&) (&&)" $ \x ->+          isEmpty x                  == foldg True (const False) (&&) (&&) x++    test "size                       == foldg 1 (const 1) (+) (+)" $ \x ->+          size x                     == foldg 1 (const 1) (+) (+) x++    test "hasVertex x                == foldg False (==x) (||) (||)" $ \x y ->+          hasVertex x y              == foldg False (==x) (||) (||) y++    test "hasEdge x y                == Algebra.Graph.hasEdge x y . toGraph" $ \x y z ->+          hasEdge x y z              == (G.hasEdge x y . toGraph) z++    test "vertexCount                == Set.size . vertexSet" $ \x ->+          vertexCount x              == (Set.size . vertexSet) x++    test "edgeCount                  == Set.size . edgeSet" $ \x ->+          edgeCount x                == (Set.size . edgeSet) x++    test "vertexList                 == Set.toAscList . vertexSet" $ \x ->+          vertexList x               == (Set.toAscList . vertexSet) x++    test "edgeList                   == Set.toAscList . edgeSet" $ \x ->+          edgeList x                 == (Set.toAscList . edgeSet) x++    test "vertexSet                  == foldg Set.empty Set.singleton Set.union Set.union" $ \x ->+          vertexSet x                == foldg Set.empty Set.singleton Set.union Set.union x++    test "vertexIntSet               == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union" $ \x ->+          vertexIntSet x             == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union x++    test "edgeSet                    == Algebra.Graph.AdjacencyMap.edgeSet . foldg empty vertex overlay connect" $ \x ->+          edgeSet x                  == (AM.edgeSet . foldg AM.empty AM.vertex AM.overlay AM.connect) x++    test "preSet x                   == Algebra.Graph.AdjacencyMap.preSet x . toAdjacencyMap" $ \x y ->+          preSet x y                 == (AM.preSet x . toAdjacencyMap) y++    test "preIntSet x                == Algebra.Graph.AdjacencyIntMap.preIntSet x . toAdjacencyIntMap" $ \x y ->+          preIntSet x y              == (AIM.preIntSet x . toAdjacencyIntMap) y++    test "postSet x                  == Algebra.Graph.AdjacencyMap.postSet x . toAdjacencyMap" $ \x y ->+          postSet x y                == (AM.postSet x . toAdjacencyMap) y++    test "postIntSet x               == Algebra.Graph.AdjacencyIntMap.postIntSet x . toAdjacencyIntMap" $ \x y ->+          postIntSet x y             == (AIM.postIntSet x . toAdjacencyIntMap) y++    test "adjacencyList              == Algebra.Graph.AdjacencyMap.adjacencyList . toAdjacencyMap" $ \x ->+          adjacencyList x            == (AM.adjacencyList . toAdjacencyMap) x++    test "adjacencyMap               == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMap" $ \x ->+          adjacencyMap x             == (AM.adjacencyMap . toAdjacencyMap) x++    test "adjacencyIntMap            == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMap" $ \x ->+          adjacencyIntMap x          == (AIM.adjacencyIntMap . toAdjacencyIntMap) x++    test "adjacencyMapTranspose      == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMapTranspose" $ \x ->+          adjacencyMapTranspose x    == (AM.adjacencyMap . toAdjacencyMapTranspose) x++    test "adjacencyIntMapTranspose   == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMapTranspose" $ \x ->+          adjacencyIntMapTranspose x == (AIM.adjacencyIntMap . toAdjacencyIntMapTranspose) x++    test "dfsForest                  == Algebra.Graph.AdjacencyMap.dfsForest . toAdjacencyMap" $ \x ->+          dfsForest x                == (AM.dfsForest . toAdjacencyMap) x++    test "dfsForestFrom vs           == Algebra.Graph.AdjacencyMap.dfsForestFrom vs . toAdjacencyMap" $ \vs x ->+          dfsForestFrom vs x         == (AM.dfsForestFrom vs . toAdjacencyMap) x++    test "dfs vs                     == Algebra.Graph.AdjacencyMap.dfs vs . toAdjacencyMap" $ \vs x ->+          dfs vs x                   == (AM.dfs vs . toAdjacencyMap) x++    test "reachable x                == Algebra.Graph.AdjacencyMap.reachable x . toAdjacencyMap" $ \x y ->+          reachable x y              == (AM.reachable x . toAdjacencyMap) y++    test "topSort                    == Algebra.Graph.AdjacencyMap.topSort . toAdjacencyMap" $ \x ->+          topSort x                  == (AM.topSort . toAdjacencyMap) x++    test "isAcyclic                  == Algebra.Graph.AdjacencyMap.isAcyclic . toAdjacencyMap" $ \x ->+          isAcyclic x                == (AM.isAcyclic . toAdjacencyMap) x++    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) x++    test "toAdjacencyMap             == foldg empty vertex overlay connect" $ \x ->+          toAdjacencyMap x           == foldg AM.empty AM.vertex AM.overlay AM.connect x++    test "toAdjacencyMapTranspose    == foldg empty vertex overlay (flip connect)" $ \x ->+          toAdjacencyMapTranspose x  == foldg AM.empty AM.vertex AM.overlay (flip AM.connect) x++    test "toAdjacencyIntMap          == foldg empty vertex overlay connect" $ \x ->+          toAdjacencyIntMap x        == foldg AIM.empty AIM.vertex AIM.overlay AIM.connect x++    test "toAdjacencyIntMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->+          toAdjacencyIntMapTranspose x == foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect) x++    test "isDfsForestOf f            == Algebra.Graph.AdjacencyMap.isDfsForestOf f . toAdjacencyMap" $ \f x ->+          isDfsForestOf f x          == (AM.isDfsForestOf f . toAdjacencyMap) x++    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) x++-- TODO: We currently do not test 'edgeSet'.+testSymmetricToGraphDefault :: TestsuiteInt g -> IO ()+testSymmetricToGraphDefault (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"+    test "toGraph                    == foldg Empty Vertex Overlay Connect" $ \x ->+          toGraph x                  == foldg G.Empty G.Vertex G.Overlay G.Connect x++    test "foldg                      == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->+          foldg e v o c x            == (G.foldg (e :: Int) v o c . toGraph) x++    test "isEmpty                    == foldg True (const False) (&&) (&&)" $ \x ->+          isEmpty x                  == foldg True (const False) (&&) (&&) x++    test "size                       == foldg 1 (const 1) (+) (+)" $ \x ->+          size x                     == foldg 1 (const 1) (+) (+) x++    test "hasVertex x                == foldg False (==x) (||) (||)" $ \x y ->+          hasVertex x y              == foldg False (==x) (||) (||) y++    test "hasEdge x y                == Algebra.Graph.hasEdge x y . toGraph" $ \x y z ->+          hasEdge x y z              == (G.hasEdge x y . toGraph) z++    test "vertexCount                == Set.size . vertexSet" $ \x ->+          vertexCount x              == (Set.size . vertexSet) x++    test "edgeCount                  == Set.size . edgeSet" $ \x ->+          edgeCount x                == (Set.size . edgeSet) x++    test "vertexList                 == Set.toAscList . vertexSet" $ \x ->+          vertexList x               == (Set.toAscList . vertexSet) x++    test "edgeList                   == Set.toAscList . edgeSet" $ \x ->+          edgeList x                 == (Set.toAscList . edgeSet) x++    test "vertexSet                  == foldg Set.empty Set.singleton Set.union Set.union" $ \x ->+          vertexSet x                == foldg Set.empty Set.singleton Set.union Set.union x++    test "vertexIntSet               == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union" $ \x ->+          vertexIntSet x             == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union x++    test "adjacencyList              == Algebra.Graph.AdjacencyMap.adjacencyList . toAdjacencyMap" $ \x ->+          adjacencyList x            == (AM.adjacencyList . toAdjacencyMap) x++    test "adjacencyMap               == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMap" $ \x ->+          adjacencyMap x             == (AM.adjacencyMap . toAdjacencyMap) x++    test "adjacencyIntMap            == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMap" $ \x ->+          adjacencyIntMap x          == (AIM.adjacencyIntMap . toAdjacencyIntMap) x++    test "adjacencyMapTranspose      == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMapTranspose" $ \x ->+          adjacencyMapTranspose x    == (AM.adjacencyMap . toAdjacencyMapTranspose) x++    test "adjacencyIntMapTranspose   == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMapTranspose" $ \x ->+          adjacencyIntMapTranspose x == (AIM.adjacencyIntMap . toAdjacencyIntMapTranspose) x++    test "dfsForest                  == Algebra.Graph.AdjacencyMap.dfsForest . toAdjacencyMap" $ \x ->+          dfsForest x                == (AM.dfsForest . toAdjacencyMap) x++    test "dfsForestFrom vs           == Algebra.Graph.AdjacencyMap.dfsForestFrom vs . toAdjacencyMap" $ \vs x ->+          dfsForestFrom vs x         == (AM.dfsForestFrom vs . toAdjacencyMap) x++    test "dfs vs                     == Algebra.Graph.AdjacencyMap.dfs vs . toAdjacencyMap" $ \vs x ->+          dfs vs x                   == (AM.dfs vs . toAdjacencyMap) x++    test "reachable x                == Algebra.Graph.AdjacencyMap.reachable x . toAdjacencyMap" $ \x y ->+          reachable x y              == (AM.reachable x . toAdjacencyMap) y++    test "topSort                    == Algebra.Graph.AdjacencyMap.topSort . toAdjacencyMap" $ \x ->+          topSort x                  == (AM.topSort . toAdjacencyMap) x++    test "isAcyclic                  == Algebra.Graph.AdjacencyMap.isAcyclic . toAdjacencyMap" $ \x ->+          isAcyclic x                == (AM.isAcyclic . toAdjacencyMap) x++    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) x++    test "toAdjacencyMap             == foldg empty vertex overlay connect" $ \x ->+          toAdjacencyMap x           == foldg AM.empty AM.vertex AM.overlay AM.connect x++    test "toAdjacencyMapTranspose    == foldg empty vertex overlay (flip connect)" $ \x ->+          toAdjacencyMapTranspose x  == foldg AM.empty AM.vertex AM.overlay (flip AM.connect) x++    test "toAdjacencyIntMap          == foldg empty vertex overlay connect" $ \x ->+          toAdjacencyIntMap x        == foldg AIM.empty AIM.vertex AIM.overlay AIM.connect x++    test "toAdjacencyIntMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->+          toAdjacencyIntMapTranspose x == foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect) x++    test "isDfsForestOf f            == Algebra.Graph.AdjacencyMap.isDfsForestOf f . toAdjacencyMap" $ \f x ->+          isDfsForestOf f x          == (AM.isDfsForestOf f . toAdjacencyMap) x++    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) x++testFoldg :: TestsuiteInt g -> IO ()+testFoldg (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "foldg ============"+    test "foldg empty vertex        overlay connect        == id" $ \x ->+          foldg empty vertex        overlay connect x      == id x++    test "foldg empty vertex        overlay (flip connect) == transpose" $ \x ->+          foldg empty vertex        overlay (flip connect) x == transpose x++    test "foldg 1     (const 1)     (+)     (+)            == size" $ \x ->+          foldg 1     (const 1)     (+)     (+) x          == size x++    test "foldg True  (const False) (&&)    (&&)           == isEmpty" $ \x ->+          foldg True  (const False) (&&)    (&&) x         == isEmpty x++testIsEmpty :: TestsuiteInt g -> IO ()+testIsEmpty (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "isEmpty ============"+    test "isEmpty empty                       == True" $+          isEmpty empty                       == True++    test "isEmpty (overlay empty empty)       == True" $+          isEmpty (overlay empty empty)       == True++    test "isEmpty (vertex x)                  == False" $ \x ->+          isEmpty (vertex x)                  == False++    test "isEmpty (removeVertex x $ vertex x) == True" $ \x ->+          isEmpty (removeVertex x $ vertex x) == True++    test "isEmpty (removeEdge x y $ edge x y) == False" $ \x y ->+          isEmpty (removeEdge x y $ edge x y) == False++testSize :: TestsuiteInt g -> IO ()+testSize (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "size ============"+    test "size empty         == 1" $+          size empty         == 1++    test "size (vertex x)    == 1" $ \x ->+          size (vertex x)    == 1++    test "size (overlay x y) == size x + size y" $ \x y ->+          size (overlay x y) == size x + size y++    test "size (connect x y) == size x + size y" $ \x y ->+          size (connect x y) == size x + size y++    test "size x             >= 1" $ \x ->+          size x             >= 1++    test "size x             >= vertexCount x" $ \x ->+          size x             >= vertexCount x++testHasVertex :: TestsuiteInt g -> IO ()+testHasVertex (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "hasVertex ============"+    test "hasVertex x empty            == False" $ \x ->+          hasVertex x empty            == False++    test "hasVertex x (vertex y)       == (x == y)" $ \x y ->+          hasVertex x (vertex y)       == (x == y)++    test "hasVertex x . removeVertex x == const False" $ \x y ->+         (hasVertex x . removeVertex x) y == const False y++testHasEdge :: TestsuiteInt g -> IO ()+testHasEdge (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "hasEdge ============"+    test "hasEdge x y empty            == False" $ \x y ->+          hasEdge x y empty            == False++    test "hasEdge x y (vertex z)       == False" $ \x y z ->+          hasEdge x y (vertex z)       == False++    test "hasEdge x y (edge x y)       == True" $ \x y ->+          hasEdge x y (edge x y)       == True++    test "hasEdge x y . removeEdge x y == const False" $ \x y z ->+         (hasEdge x y . removeEdge x y) z == const False z++    test "hasEdge x y                  == elem (x,y) . edgeList" $ \x y z -> do+        let es = edgeList z+        (x, y) <- elements ((x, y) : es)+        return $ hasEdge x y z == elem (x, y) es++testSymmetricHasEdge :: TestsuiteInt g -> IO ()+testSymmetricHasEdge (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "hasEdge ============"+    test "hasEdge x y empty            == False" $ \x y ->+          hasEdge x y empty            == False++    test "hasEdge x y (vertex z)       == False" $ \x y z ->+          hasEdge x y (vertex z)       == False++    test "hasEdge x y (edge x y)       == True" $ \x y ->+          hasEdge x y (edge x y)       == True++    test "hasEdge x y (edge y x)       == True" $ \x y ->+          hasEdge x y (edge y x)       == True++    test "hasEdge x y . removeEdge x y == const False" $ \x y z ->+         (hasEdge x y . removeEdge x y) z == const False z++    test "hasEdge x y                  == elem (min x y, max x y) . edgeList" $ \x y z -> do+        (u, v) <- elements ((x, y) : edgeList z)+        return $ hasEdge u v z == elem (min u v, max u v) (edgeList z)++testVertexCount :: TestsuiteInt g -> IO ()+testVertexCount (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "vertexCount ============"+    test "vertexCount empty             ==  0" $+          vertexCount empty             ==  0++    test "vertexCount (vertex x)        ==  1" $ \x ->+          vertexCount (vertex x)        ==  1++    test "vertexCount                   ==  length . vertexList" $ \x ->+          vertexCount x                 == (length . vertexList) x++    test "vertexCount x < vertexCount y ==> x < y" $ \x y ->+        if vertexCount x < vertexCount y+        then property (x < y)+        else (vertexCount x > vertexCount y ==> x > y)++testEdgeCount :: TestsuiteInt g -> IO ()+testEdgeCount (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeCount ============"+    test "edgeCount empty      == 0" $+          edgeCount empty      == 0++    test "edgeCount (vertex x) == 0" $ \x ->+          edgeCount (vertex x) == 0++    test "edgeCount (edge x y) == 1" $ \x y ->+          edgeCount (edge x y) == 1++    test "edgeCount            == length . edgeList" $ \x ->+          edgeCount x          == (length . edgeList) x++testVertexList :: TestsuiteInt g -> IO ()+testVertexList (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "vertexList ============"+    test "vertexList empty      == []" $+          vertexList empty      == []++    test "vertexList (vertex x) == [x]" $ \x ->+          vertexList (vertex x) == [x]++    test "vertexList . vertices == nub . sort" $ \xs ->+         (vertexList . vertices) xs == (nubOrd . sort) xs++testEdgeList :: TestsuiteInt g -> IO ()+testEdgeList (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeList ============"+    test "edgeList empty          == []" $+          edgeList empty          == []++    test "edgeList (vertex x)     == []" $ \x ->+          edgeList (vertex x)     == []++    test "edgeList (edge x y)     == [(x,y)]" $ \x y ->+          edgeList (edge x y)     == [(x,y)]++    test "edgeList (star 2 [3,1]) == [(2,1), (2,3)]" $+          edgeList (star 2 [3,1]) == [(2,1), (2,3)]++    test "edgeList . edges        == nub . sort" $ \xs ->+         (edgeList . edges) xs    == (nubOrd . sort) xs++testSymmetricEdgeList :: TestsuiteInt g -> IO ()+testSymmetricEdgeList (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeList ============"+    test "edgeList empty          == []" $+          edgeList empty          == []++    test "edgeList (vertex x)     == []" $ \x ->+          edgeList (vertex x)     == []++    test "edgeList (edge x y)     == [(min x y, max y x)]" $ \x y ->+          edgeList (edge x y)     == [(min x y, max y x)]++    test "edgeList (star 2 [3,1]) == [(1,2), (2,3)]" $+          edgeList (star 2 [3,1]) == [(1,2), (2,3)]++testAdjacencyList :: TestsuiteInt g -> IO ()+testAdjacencyList (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "adjacencyList ============"+    test "adjacencyList empty          == []" $+          adjacencyList empty          == []++    test "adjacencyList (vertex x)     == [(x, [])]" $ \x ->+          adjacencyList (vertex x)     == [(x, [])]++    test "adjacencyList (edge 1 2)     == [(1, [2]), (2, [])]" $+          adjacencyList (edge 1 2)     == [(1, [2]), (2, [])]++    test "adjacencyList (star 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]" $+          adjacencyList (star 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]++testSymmetricAdjacencyList :: TestsuiteInt g -> IO ()+testSymmetricAdjacencyList (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "adjacencyList ============"+    test "adjacencyList empty          == []" $+          adjacencyList empty          == []++    test "adjacencyList (vertex x)     == [(x, [])]" $ \x ->+          adjacencyList (vertex x)     == [(x, [])]++    test "adjacencyList (edge 1 2)     == [(1, [2]), (2, [1])]" $+          adjacencyList (edge 1 2)     == [(1, [2]), (2, [1])]++    test "adjacencyList (star 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]" $+          adjacencyList (star 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]++testVertexSet :: TestsuiteInt g -> IO ()+testVertexSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "vertexSet ============"+    test "vertexSet empty      == Set.empty" $+          vertexSet empty      == Set.empty++    test "vertexSet . vertex   == Set.singleton" $ \x ->+         (vertexSet . vertex) x == Set.singleton x++    test "vertexSet . vertices == Set.fromList" $ \xs ->+         (vertexSet . vertices) xs == Set.fromList xs++testVertexIntSet :: TestsuiteInt g -> IO ()+testVertexIntSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "vertexIntSet ============"+    test "vertexIntSet empty      == IntSet.empty" $+          vertexIntSet empty      == IntSet.empty++    test "vertexIntSet . vertex   == IntSet.singleton" $ \x ->+         (vertexIntSet . vertex) x == IntSet.singleton x++    test "vertexIntSet . vertices == IntSet.fromList" $ \xs ->+         (vertexIntSet . vertices) xs == IntSet.fromList xs++    test "vertexIntSet . clique   == IntSet.fromList" $ \xs ->+         (vertexIntSet . clique) xs == IntSet.fromList xs++testEdgeSet :: TestsuiteInt g -> IO ()+testEdgeSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeSet ============"+    test "edgeSet empty      == Set.empty" $+          edgeSet empty      == Set.empty++    test "edgeSet (vertex x) == Set.empty" $ \x ->+          edgeSet (vertex x) == Set.empty++    test "edgeSet (edge x y) == Set.singleton (x,y)" $ \x y ->+          edgeSet (edge x y) == Set.singleton (x,y)++    test "edgeSet . edges    == Set.fromList" $ \xs ->+         (edgeSet . edges) xs == Set.fromList xs++testSymmetricEdgeSet :: TestsuiteInt g -> IO ()+testSymmetricEdgeSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeSet ============"+    test "edgeSet empty      == Set.empty" $+          edgeSet empty      == Set.empty++    test "edgeSet (vertex x) == Set.empty" $ \x ->+          edgeSet (vertex x) == Set.empty++    test "edgeSet (edge x y) == Set.singleton (min x y, max x y)" $ \x y ->+          edgeSet (edge x y) == Set.singleton (min x y, max x y)++testPreSet :: TestsuiteInt g -> IO ()+testPreSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "preSet ============"+    test "preSet x empty      == Set.empty" $ \x ->+          preSet x empty      == Set.empty++    test "preSet x (vertex x) == Set.empty" $ \x ->+          preSet x (vertex x) == Set.empty++    test "preSet 1 (edge 1 2) == Set.empty" $+          preSet 1 (edge 1 2) == Set.empty++    test "preSet y (edge x y) == Set.fromList [x]" $ \x y ->+          preSet y (edge x y) == Set.fromList [x]++testPostSet :: TestsuiteInt g -> IO ()+testPostSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "postSet ============"+    test "postSet x empty      == Set.empty" $ \x ->+          postSet x empty      == Set.empty++    test "postSet x (vertex x) == Set.empty" $ \x ->+          postSet x (vertex x) == Set.empty++    test "postSet x (edge x y) == Set.fromList [y]" $ \x y ->+          postSet x (edge x y) == Set.fromList [y]++    test "postSet 2 (edge 1 2) == Set.empty" $+          postSet 2 (edge 1 2) == Set.empty++testPreIntSet :: TestsuiteInt g -> IO ()+testPreIntSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "preIntSet ============"+    test "preIntSet x empty      == IntSet.empty" $ \x ->+          preIntSet x empty      == IntSet.empty++    test "preIntSet x (vertex x) == IntSet.empty" $ \x ->+          preIntSet x (vertex x) == IntSet.empty++    test "preIntSet 1 (edge 1 2) == IntSet.empty" $+          preIntSet 1 (edge 1 2) == IntSet.empty++    test "preIntSet y (edge x y) == IntSet.fromList [x]" $ \x y ->+          preIntSet y (edge x y) == IntSet.fromList [x]++testPostIntSet :: TestsuiteInt g -> IO ()+testPostIntSet (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "postIntSet ============"+    test "postIntSet x empty      == IntSet.empty" $ \x ->+          postIntSet x empty      == IntSet.empty++    test "postIntSet x (vertex x) == IntSet.empty" $ \x ->+          postIntSet x (vertex x) == IntSet.empty++    test "postIntSet 2 (edge 1 2) == IntSet.empty" $+          postIntSet 2 (edge 1 2) == IntSet.empty++    test "postIntSet x (edge x y) == IntSet.fromList [y]" $ \x y ->+          postIntSet x (edge x y) == IntSet.fromList [y]++testNeighbours :: TestsuiteInt g -> IO ()+testNeighbours (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "neighbours ============"+    test "neighbours x empty      == Set.empty" $ \x ->+          neighbours x empty      == Set.empty++    test "neighbours x (vertex x) == Set.empty" $ \x ->+          neighbours x (vertex x) == Set.empty++    test "neighbours x (edge x y) == Set.fromList [y]" $ \x y ->+          neighbours x (edge x y) == Set.fromList [y]++    test "neighbours y (edge x y) == Set.fromList [x]" $ \x y ->+          neighbours y (edge x y) == Set.fromList [x]++testPath :: TestsuiteInt g -> IO ()+testPath (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "path ============"+    test "path []    == empty" $+          path []    == empty++    test "path [x]   == vertex x" $ \x ->+          path [x]   == vertex x++    test "path [x,y] == edge x y" $ \x y ->+          path [x,y] == edge x y++testSymmetricPath :: TestsuiteInt g -> IO ()+testSymmetricPath t@(_, API{..}) = do+    testPath t+    test "path       == path . reverse" $ \xs ->+          path xs    ==(path . reverse) xs++testCircuit :: TestsuiteInt g -> IO ()+testCircuit (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "circuit ============"+    test "circuit []    == empty" $+          circuit []    == empty++    test "circuit [x]   == edge x x" $ \x ->+          circuit [x]   == edge x x++    test "circuit [x,y] == edges [(x,y), (y,x)]" $ \x y ->+          circuit [x,y] == edges [(x,y), (y,x)]++testSymmetricCircuit :: TestsuiteInt g -> IO ()+testSymmetricCircuit t@(_, API{..}) = do+    testCircuit t+    test "circuit       == circuit . reverse" $ \xs ->+          circuit xs    ==(circuit . reverse) xs++testClique :: TestsuiteInt g -> IO ()+testClique (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "clique ============"+    test "clique []         == empty" $+          clique []         == empty++    test "clique [x]        == vertex x" $ \x ->+          clique [x]        == vertex x++    test "clique [x,y]      == edge x y" $ \x y ->+          clique [x,y]      == edge x y++    test "clique [x,y,z]    == edges [(x,y), (x,z), (y,z)]" $ \x y z ->+          clique [x,y,z]    == edges [(x,y), (x,z), (y,z)]++    test "clique (xs ++ ys) == connect (clique xs) (clique ys)" $ \xs ys ->+          clique (xs ++ ys) == connect (clique xs) (clique ys)++testSymmetricClique :: TestsuiteInt g -> IO ()+testSymmetricClique t@(_, API{..}) = do+    testClique t+    test "clique            == clique . reverse" $ \xs->+          clique xs         ==(clique . reverse) xs++testBiclique :: TestsuiteInt g -> IO ()+testBiclique (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "biclique ============"+    test "biclique []      []      == empty" $+          biclique []      []      == empty++    test "biclique [x]     []      == vertex x" $ \x ->+          biclique [x]     []      == vertex x++    test "biclique []      [y]     == vertex y" $ \y ->+          biclique []      [y]     == vertex y++    test "biclique [x1,x2] [y1,y2] == edges [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]" $ \x1 x2 y1 y2 ->+          biclique [x1,x2] [y1,y2] == edges [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]++    test "biclique xs      ys      == connect (vertices xs) (vertices ys)" $ \xs ys ->+          biclique xs      ys      == connect (vertices xs) (vertices ys)++testStar :: TestsuiteInt g -> IO ()+testStar (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "star ============"+    test "star x []    == vertex x" $ \x ->+          star x []    == vertex x++    test "star x [y]   == edge x y" $ \x y ->+          star x [y]   == edge x y++    test "star x [y,z] == edges [(x,y), (x,z)]" $ \x y z ->+          star x [y,z] == edges [(x,y), (x,z)]++    test "star x ys    == connect (vertex x) (vertices ys)" $ \x ys ->+          star x ys    == connect (vertex x) (vertices ys)++testTree :: TestsuiteInt g -> IO ()+testTree (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "tree ============"+    test "tree (Node x [])                                         == vertex x" $ \x ->+          tree (Node x [])                                         == vertex x++    test "tree (Node x [Node y [Node z []]])                       == path [x,y,z]" $ \x y z ->+          tree (Node x [Node y [Node z []]])                       == path [x,y,z]++    test "tree (Node x [Node y [], Node z []])                     == star x [y,z]" $ \x y z ->+          tree (Node x [Node y [], Node z []])                     == star x [y,z]++    test "tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges [(1,2), (1,3), (3,4), (3,5)]" $+          tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges [(1,2), (1,3), (3,4), (3,5)]++testForest :: TestsuiteInt g -> IO ()+testForest (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "forest ============"+    test "forest []                                                  == empty" $+          forest []                                                  == empty++    test "forest [x]                                                 == tree x" $ \x ->+          forest [x]                                                 == tree x++    test "forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == edges [(1,2), (1,3), (4,5)]" $+          forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == edges [(1,2), (1,3), (4,5)]++    test "forest                                                     == overlays . map tree" $ \x ->+          forest x                                                   ==(overlays . map tree) x++testMesh :: Testsuite g Ord -> IO ()+testMesh (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "mesh ============"+    test "mesh xs     []    == empty" $ \(xs :: [Int]) ->+          mesh xs ([] :: [Int]) == empty++    test "mesh []     ys    == empty" $ \(ys :: [Int]) ->+          mesh ([] :: [Int]) ys == empty++    test "mesh [x]    [y]   == vertex (x, y)" $ \(x :: Int) (y :: Int) ->+          mesh [x]    [y]   == vertex (x, y)++    test "mesh xs     ys    == box (path xs) (path ys)" $ \(xs :: [Int]) (ys :: [Int]) ->+          mesh xs     ys    == box (path xs) (path ys)++    test "mesh [1..3] \"ab\"  == <correct result>" $+          mesh [1..3]  "ab"   == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))+                                       , ((2,'a'),(3,'a')), ((2,'b'),(3,'b')), ((3,'a'),(3 :: Int,'b')) ]++    test "size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)" $ \(xs :: [Int]) (ys :: [Int]) ->+          size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)++testTorus :: Testsuite g Ord -> IO ()+testTorus (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "torus ============"+    test "torus xs     []    == empty" $ \(xs :: [Int]) ->+          torus xs ([] :: [Int]) == empty++    test "torus []     ys    == empty" $ \(ys :: [Int]) ->+          torus ([] :: [Int]) ys == empty++    test "torus [x]    [y]   == edge (x,y) (x,y)" $ \(x :: Int) (y :: Int) ->+          torus [x]    [y]   == edge (x,y) (x,y)++    test "torus xs     ys    == box (circuit xs) (circuit ys)" $ \(xs :: [Int]) (ys :: [Int]) ->+          torus xs     ys    == box (circuit xs) (circuit ys)++    test "torus [1,2]  \"ab\"  == <correct result>" $+          torus [1,2]   "ab"   == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))+                                        , ((2,'a'),(1,'a')), ((2,'a'),(2,'b')), ((2,'b'),(1,'b')), ((2,'b'),(2 :: Int,'a')) ]++    test "size (torus xs ys) == max 1 (3 * length xs * length ys)" $ \(xs :: [Int]) (ys :: [Int]) ->+          size (torus xs ys) == max 1 (3 * length xs * length ys)++testDeBruijn :: Testsuite g Ord -> IO ()+testDeBruijn (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "deBruijn ============"+    test "          deBruijn 0 xs               == edge [] []" $ \(xs :: [Int]) ->+                    deBruijn 0 xs               == edge [] []++    test "n > 0 ==> deBruijn n []               == empty" $ \n ->+          n > 0 ==> deBruijn n ([] :: [Int])    == empty++    test "          deBruijn 1 [0,1]            == edges [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ]" $+                    deBruijn 1 [0,1::Int]       == edges [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ]++    test "          deBruijn 2 \"0\"              == edge \"00\" \"00\"" $+                    deBruijn 2  "0"               == edge "00" "00"++    test "          deBruijn 2 \"01\"             == <correct result>" $+                    deBruijn 2  "01"              == edges [ ("00","00"), ("00","01"), ("01","10"), ("01","11")+                                                           , ("10","00"), ("10","01"), ("11","10"), ("11","11") ]++    test "          transpose   (deBruijn n xs) == gmap reverse $ deBruijn n xs" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->+                    transpose   (deBruijn n xs) == gmap reverse (deBruijn n xs)++    test "          vertexCount (deBruijn n xs) == (length $ nub xs)^n" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->+                    vertexCount (deBruijn n xs) == (length $ nubOrd xs)^n++    test "n > 0 ==> edgeCount   (deBruijn n xs) == (length $ nub xs)^(n + 1)" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->+          n > 0 ==> edgeCount   (deBruijn n xs) == (length $ nubOrd xs)^(n + 1)++testBox :: Testsuite g Ord -> IO ()+testBox (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "box ============"+    let unit = gmap $ \(a :: Int, ()      ) -> a+        comm = gmap $ \(a :: Int, b :: Int) -> (b, a)+    test "box x y               ~~ box y x" $ mapSize (min 10) $ \x y ->+          comm (box x y)        == box y x++    test "box x (overlay y z)   == overlay (box x y) (box x z)" $ mapSize (min 10) $ \x y z ->+        let _ = x + y + z + vertex (0 :: Int) in+          box x (overlay y z)   == overlay (box x y) (box x z)++    test "box x (vertex ())     ~~ x" $ mapSize (min 10) $ \x ->+     unit(box x (vertex ()))    == (x `asTypeOf` empty)++    test "box x empty           ~~ empty" $ mapSize (min 10) $ \x ->+     unit(box x empty)          == empty++    let assoc = gmap $ \(a :: Int, (b :: Int, c :: Int)) -> ((a, b), c)+    test "box x (box y z)       ~~ box (box x y) z" $ mapSize (min 10) $ \x y z ->+      assoc (box x (box y z))   == box (box x y) z++    test "transpose   (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \x y ->+        let _ = x + y + vertex (0 :: Int) in+          transpose   (box x y) == box (transpose x) (transpose y)++    test "vertexCount (box x y) == vertexCount x * vertexCount y" $ mapSize (min 10) $ \x y ->+        let _ = x + y + vertex (0 :: Int) in+          vertexCount (box x y) == vertexCount x * vertexCount y++    test "edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y" $ mapSize (min 10) $ \x y ->+        let _ = x + y + vertex (0 :: Int) in+          edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y++testRemoveVertex :: TestsuiteInt g -> IO ()+testRemoveVertex (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "removeVertex ============"+    test "removeVertex x (vertex x)       == empty" $ \x ->+          removeVertex x (vertex x)       == empty++    test "removeVertex 1 (vertex 2)       == vertex 2" $+          removeVertex 1 (vertex 2)       == vertex 2++    test "removeVertex x (edge x x)       == empty" $ \x ->+          removeVertex x (edge x x)       == empty++    test "removeVertex 1 (edge 1 2)       == vertex 2" $+          removeVertex 1 (edge 1 2)       == vertex 2++    test "removeVertex x . removeVertex x == removeVertex x" $ \x y ->+         (removeVertex x . removeVertex x) y == removeVertex x y++testRemoveEdge :: TestsuiteInt g -> IO ()+testRemoveEdge (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "removeEdge ============"+    test "removeEdge x y (edge x y)       == vertices [x,y]" $ \x y ->+          removeEdge x y (edge x y)       == vertices [x,y]++    test "removeEdge x y . removeEdge x y == removeEdge x y" $ \x y z ->+         (removeEdge x y . removeEdge x y) z == removeEdge x y z++    test "removeEdge x y . removeVertex x == removeVertex x" $ \x y z ->+         (removeEdge x y . removeVertex x) z == removeVertex x z++    test "removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2" $+          removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2++    test "removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2" $+          removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2++    -- TODO: Ouch. Generic tests are becoming awkward. We need a better way.+    when (prefix == "Fold." || prefix == "Graph.") $ do+        test "size (removeEdge x y z)         <= 3 * size z" $ \x y z ->+              size (removeEdge x y z)         <= 3 * size z++testSymmetricRemoveEdge :: TestsuiteInt g -> IO ()+testSymmetricRemoveEdge t@(_, API{..}) = do+    testRemoveEdge t+    test "removeEdge x y                  == removeEdge y x" $ \x y z ->+          removeEdge x y z                == removeEdge y x z++testReplaceVertex :: TestsuiteInt g -> IO ()+testReplaceVertex (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "replaceVertex ============"+    test "replaceVertex x x            == id" $ \x y ->+          replaceVertex x x y          == id y++    test "replaceVertex x y (vertex x) == vertex y" $ \x y ->+          replaceVertex x y (vertex x) == vertex y++    test "replaceVertex x y            == mergeVertices (== x) y" $ \x y z ->+          replaceVertex x y z          == mergeVertices (== x) y z++testMergeVertices :: TestsuiteInt g -> IO ()+testMergeVertices (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "mergeVertices ============"+    test "mergeVertices (const False) x    == id" $ \x y ->+          mergeVertices (const False) x y  == id y++    test "mergeVertices (== x) y           == replaceVertex x y" $ \x y z ->+          mergeVertices (== x) y z         == replaceVertex x y z++    test "mergeVertices even 1 (0 * 2)     == 1 * 1" $+          mergeVertices even 1 (0 * 2)     == 1 * 1++    test "mergeVertices odd  1 (3 + 4 * 5) == 4 * 1" $+          mergeVertices odd  1 (3 + 4 * 5) == 4 * 1++testTranspose :: TestsuiteInt g -> IO ()+testTranspose (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "transpose ============"+    test "transpose empty       == empty" $+          transpose empty       == empty++    test "transpose (vertex x)  == vertex x" $ \x ->+          transpose (vertex x)  == vertex x++    test "transpose (edge x y)  == edge y x" $ \x y ->+          transpose (edge x y)  == edge y x++    test "transpose . transpose == id" $ size10 $ \x ->+         (transpose . transpose) x == id x++    test "edgeList . transpose  == sort . map swap . edgeList" $ \x ->+         (edgeList . transpose) x == (sort . map swap . edgeList) x++testGmap :: TestsuiteInt g -> IO ()+testGmap (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "gmap ============"+    test "gmap f empty      == empty" $ \(apply -> f) ->+          gmap f empty      == empty++    test "gmap f (vertex x) == vertex (f x)" $ \(apply -> f) x ->+          gmap f (vertex x) == vertex (f x)++    test "gmap f (edge x y) == edge (f x) (f y)" $ \(apply -> f) x y ->+          gmap f (edge x y) == edge (f x) (f y)++    test "gmap id           == id" $ \x ->+          gmap id x         == id x++    test "gmap f . gmap g   == gmap (f . g)" $ \(apply -> f :: Int -> Int) (apply -> g :: Int -> Int) x ->+         (gmap f . gmap g) x == gmap (f . g) x++testInduce :: TestsuiteInt g -> IO ()+testInduce (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "induce ============"+    test "induce (const True ) x      == x" $ \x ->+          induce (const True ) x      == x++    test "induce (const False) x      == empty" $ \x ->+          induce (const False) x      == empty++    test "induce (/= x)               == removeVertex x" $ \x y ->+          induce (/= x) y             == removeVertex x y++    test "induce p . induce q         == induce (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) y ->+         (induce p . induce q) y      == induce (\x -> p x && q x) y++    test "isSubgraphOf (induce p x) x == True" $ \(apply -> p) x ->+          isSubgraphOf (induce p x) x == True++testInduceJust :: Testsuite g Ord -> IO ()+testInduceJust (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "induceJust ============"+    test "induceJust (vertex Nothing)                               == empty" $+          induceJust (vertex (Nothing :: Maybe Int))                == empty++    test "induceJust (edge (Just x) Nothing)                        == vertex x" $ \x ->+          induceJust (edge (Just x) (Nothing :: Maybe Int))         == vertex x++    test "induceJust . gmap Just                                    == id" $ \(x :: g Int) ->+         (induceJust . gmap Just) x                                 == id x++    test "induceJust . gmap (\\x -> if p x then Just x else Nothing) == induce p" $ \(x :: g Int) (apply -> p) ->+         (induceJust . gmap (\x -> if p x then Just x else Nothing)) x == induce p x++testCompose :: TestsuiteInt g -> IO ()+testCompose (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "compose ============"+    test "compose empty            x                == empty" $ \x ->+          compose empty            x                == empty++    test "compose x                empty            == empty" $ \x ->+          compose x                empty            == empty++    test "compose (vertex x)       y                == empty" $ \x y ->+          compose (vertex x)       y                == empty++    test "compose x                (vertex y)       == empty" $ \x y ->+          compose x                (vertex y)       == empty++    test "compose x                (compose y z)    == compose (compose x y) z" $ size10 $ \x y z ->+          compose x                (compose y z)    == compose (compose x y) z++    test "compose x                (overlay y z)    == overlay (compose x y) (compose x z)" $ size10 $ \x y z ->+          compose x                (overlay y z)    == overlay (compose x y) (compose x z)++    test "compose (overlay x y) z                   == overlay (compose x z) (compose y z)" $ size10 $ \x y z ->+          compose (overlay x y) z                   == overlay (compose x z) (compose y z)++    test "compose (edge x y)       (edge y z)       == edge x z" $ \x y z ->+          compose (edge x y)       (edge y z)       == edge x z++    test "compose (path    [1..5]) (path    [1..5]) == edges [(1,3),(2,4),(3,5)]" $+          compose (path    [1..5]) (path    [1..5]) == edges [(1,3),(2,4),(3,5)]++    test "compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4]" $+          compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4]++testClosure :: TestsuiteInt g -> IO ()+testClosure (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "closure ============"+    test "closure empty           == empty" $+          closure empty           == empty++    test "closure (vertex x)      == edge x x" $ \x ->+          closure (vertex x)      == edge x x++    test "closure (edge x x)      == edge x x" $ \x ->+          closure (edge x x)      == edge x x++    test "closure (edge x y)      == edges [(x,x), (x,y), (y,y)]" $ \x y ->+          closure (edge x y)      == edges [(x,x), (x,y), (y,y)]++    test "closure (path $ nub xs) == reflexiveClosure (clique $ nub xs)" $ \xs ->+          closure (path $ nubOrd xs) == reflexiveClosure (clique $ nubOrd xs)++    test "closure                 == reflexiveClosure . transitiveClosure" $ size10 $ \x ->+          closure x               == (reflexiveClosure . transitiveClosure) x++    test "closure                 == transitiveClosure . reflexiveClosure" $ size10 $ \x ->+          closure x               == (transitiveClosure . reflexiveClosure) x++    test "closure . closure       == closure" $ size10 $ \x ->+         (closure . closure) x    == closure x++    test "postSet x (closure y)   == Set.fromList (reachable x y)" $ size10 $ \x y ->+          postSet x (closure y)   == Set.fromList (reachable x y)++testReflexiveClosure :: TestsuiteInt g -> IO ()+testReflexiveClosure (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "reflexiveClosure ============"+    test "reflexiveClosure empty              == empty" $+          reflexiveClosure empty              == empty++    test "reflexiveClosure (vertex x)         == edge x x" $ \x ->+          reflexiveClosure (vertex x)         == edge x x++    test "reflexiveClosure (edge x x)         == edge x x" $ \x ->+          reflexiveClosure (edge x x)         == edge x x++    test "reflexiveClosure (edge x y)         == edges [(x,x), (x,y), (y,y)]" $ \x y ->+          reflexiveClosure (edge x y)         == edges [(x,x), (x,y), (y,y)]++    test "reflexiveClosure . reflexiveClosure == reflexiveClosure" $ \x ->+         (reflexiveClosure . reflexiveClosure) x == reflexiveClosure x++testSymmetricClosure :: TestsuiteInt g -> IO ()+testSymmetricClosure (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "symmetricClosure ============"+    test "symmetricClosure empty              == empty" $+          symmetricClosure empty              == empty++    test "symmetricClosure (vertex x)         == vertex x" $ \x ->+          symmetricClosure (vertex x)         == vertex x++    test "symmetricClosure (edge x y)         == edges [(x,y), (y,x)]" $ \x y ->+          symmetricClosure (edge x y)         == edges [(x,y), (y,x)]++    test "symmetricClosure x                  == overlay x (transpose x)" $ \x ->+          symmetricClosure x                  == overlay x (transpose x)++    test "symmetricClosure . symmetricClosure == symmetricClosure" $ \x ->+         (symmetricClosure . symmetricClosure) x == symmetricClosure x++testTransitiveClosure :: TestsuiteInt g -> IO ()+testTransitiveClosure (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "transitiveClosure ============"+    test "transitiveClosure empty               == empty" $+          transitiveClosure empty               == empty++    test "transitiveClosure (vertex x)          == vertex x" $ \x ->+          transitiveClosure (vertex x)          == vertex x++    test "transitiveClosure (edge x y)          == edge x y" $ \x y ->+          transitiveClosure (edge x y)          == edge x y++    test "transitiveClosure (path $ nub xs)     == clique (nub $ xs)" $ \xs ->+          transitiveClosure (path $ nubOrd xs)  == clique (nubOrd xs)++    test "transitiveClosure . transitiveClosure == transitiveClosure" $ size10 $ \x ->+         (transitiveClosure . transitiveClosure) x == transitiveClosure x++testSplitVertex :: TestsuiteInt g -> IO ()+testSplitVertex (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "splitVertex ============"+    test "splitVertex x []                   == removeVertex x" $ \x y ->+          splitVertex x [] y                 == removeVertex x y++    test "splitVertex x [x]                  == id" $ \x y ->+          splitVertex x [x] y                == id y++    test "splitVertex x [y]                  == replaceVertex x y" $ \x y z ->+          splitVertex x [y] z                == replaceVertex x y z++    test "splitVertex 1 [0, 1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3)" $+          splitVertex 1 [0, 1] (1 * (2 + 3)) == (0 + 1) * (2 + 3)++testBind :: TestsuiteInt g -> IO ()+testBind (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "bind ============"+    test "bind empty f         == empty" $ \(apply -> f) ->+          bind empty f         == empty++    test "bind (vertex x) f    == f x" $ \(apply -> f) x ->+          bind (vertex x) f    == f x++    test "bind (edge x y) f    == connect (f x) (f y)" $ \(apply -> f) x y ->+          bind (edge x y) f    == connect (f x) (f y)++    test "bind (vertices xs) f == overlays (map f xs)" $ size10 $ \xs (apply -> f) ->+          bind (vertices xs) f == overlays (map f xs)++    test "bind x (const empty) == empty" $ \x ->+          bind x (const empty) == empty++    test "bind x vertex        == x" $ \x ->+          bind x vertex        == x++    test "bind (bind x f) g    == bind x (\\y -> bind (f y) g)" $ size10 $ \x (apply -> f) (apply -> g) ->+          bind (bind x f) g    == bind x (\y  -> bind (f y) g)++testSimplify :: TestsuiteInt g -> IO ()+testSimplify (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "simplify ============"+    test "simplify              == id" $ \x ->+          simplify x            == id x++    test "size (simplify x)     <= size x" $ \x ->+          size (simplify x)     <= size x++testBfsForest :: TestsuiteInt g -> IO ()+testBfsForest (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "bfsForest ============"+    test "bfsForest vs empty                           == []" $ \vs ->+          bfsForest vs empty                           == []++    test "forest (bfsForest [1]   $ edge 1 1)          == vertex 1" $+          forest (bfsForest [1]   $ edge 1 1)          == vertex 1++    test "forest (bfsForest [1]   $ edge 1 2)          == edge 1 2" $+          forest (bfsForest [1]   $ edge 1 2)          == edge 1 2++    test "forest (bfsForest [2]   $ edge 1 2)          == vertex 2" $+          forest (bfsForest [2]   $ edge 1 2)          == vertex 2++    test "forest (bfsForest [3]   $ edge 1 2)          == empty" $+          forest (bfsForest [3]   $ edge 1 2)          == empty++    test "forest (bfsForest [2,1] $ edge 1 2)          == vertices [1,2]" $+          forest (bfsForest [2,1] $ edge 1 2)          == vertices [1,2]++    test "isSubgraphOf (forest $ bfsForest vs x) x     == True" $ \vs x ->+          isSubgraphOf (forest $ bfsForest vs x) x     == True++    test "bfsForest (vertexList g) g                   == <correct result>" $ \g ->+          bfsForest (vertexList g) g                   ==+          map (\v -> Node v []) (nub $ vertexList g)++    test "bfsForest []             x                   == []" $ \x ->+          bfsForest []             x                   == []++    test "bfsForest [1,4] $ 3 * (1 + 4) * (1 + 5)      == <correct result>" $+          bfsForest [1,4]  (3 * (1 + 4) * (1 + 5))     == [ Node { rootLabel = 1+                                                                 , subForest = [ Node { rootLabel = 5+                                                                                      , subForest = [] }]}+                                                          , Node { rootLabel = 4+                                                                 , subForest = [] }]++    test "bfsForest [3] (circuit [1..5] + (circuit [5,4..1])) == <correct result>" $+          bfsForest [3] (circuit [1..5] + (circuit [5,4..1])) ==+          [ Node { rootLabel = 3+                 , subForest = [ Node { rootLabel = 2+                                      , subForest = [ Node { rootLabel = 1+                                                           , subForest = []}]}+                               , Node { rootLabel = 4+                                      , subForest = [ Node { rootLabel = 5+                                                           , subForest = []}]}]}]++testBfs :: TestsuiteInt g -> IO ()+testBfs (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "bfs ============"++    test "bfs vs    $ empty                             == []" $ \vs ->+          bfs vs      empty                             == []++    test "bfs []      g                                 == []" $ \g ->+          bfs []      g                                 == []++    test "bfs [1]    (edge 1 1)                         == [[1]]" $+          bfs [1]    (edge 1 1)                         == [[1]]++    test "bfs [1]    (edge 1 2)                         == [[1],[2]]" $+          bfs [1]    (edge 1 2)                         == [[1],[2]]++    test "bfs [2]    (edge 1 2)                         == [[2]]" $+          bfs [2]    (edge 1 2)                         == [[2]]++    test "bfs [1,2]  (edge 1 2)                         == [[1,2]]" $+          bfs [1,2]  (edge 1 2)                         == [[1,2]]++    test "bfs [2,1]  (edge 1 2)                         == [[2,1]]" $+          bfs [2,1]  (edge 1 2)                         == [[2,1]]++    test "bfs [3]    (edge 1 2)                         == []" $+          bfs [3]    (edge 1 2)                         == []++    test "bfs [1,2] ((1*2) + (3*4) + (5*6))             == [[1,2]]" $+          bfs [1,2] ((1*2) + (3*4) + (5*6))             == [[1,2]]++    test "bfs [1,3] ((1*2) + (3*4) + (5*6))             == [[1,3],[2,4]]" $+          bfs [1,3] ((1*2) + (3*4) + (5*6))             == [[1,3],[2,4]]++    test "bfs [3]  (3 * (1 + 4) * (1 + 5))              == [[3],[1,4,5]]" $+          bfs [3]  (3 * (1 + 4) * (1 + 5))              == [[3],[1,4,5]]++    test "bfs [2] (circuit [1..5] + (circuit [5,4..1])) == [[2],[1,3],[5,4]]" $+          bfs [2] (circuit [1..5] + (circuit [5,4..1])) == [[2],[1,3],[5,4]]++    test "concat (bfs [3] $ circuit [1..5] + circuit [5,4..1]) == [3,2,4,1,5]" $+          concat (bfs [3] $ circuit [1..5] + circuit [5,4..1]) == [3,2,4,1,5]++    test "isSubgraphOf (vertices $ concat $ bfs vs x) x == True" $ \vs x ->+          isSubgraphOf (vertices $ concat $ bfs vs x) x == True++    test "bfs vs == map concat . List.transpose . map levels . bfsForest vs" $ \vs g ->+          (bfs vs) g == (map concat . List.transpose . map levels . bfsForest vs) g++testDfsForest :: TestsuiteInt g -> IO ()+testDfsForest (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "dfsForest ============"+    test "dfsForest empty                       == []" $+          dfsForest empty                       == []++    test "forest (dfsForest $ edge 1 1)         == vertex 1" $+          forest (dfsForest $ edge 1 1)         == vertex 1++    test "forest (dfsForest $ edge 1 2)         == edge 1 2" $+          forest (dfsForest $ edge 1 2)         == edge 1 2++    test "forest (dfsForest $ edge 2 1)         == vertices [1,2]" $+          forest (dfsForest $ edge 2 1)         == vertices [1,2]++    test "isSubgraphOf (forest $ dfsForest x) x == True" $ \x ->+          isSubgraphOf (forest $ dfsForest x) x == True++    test "isDfsForestOf (dfsForest x) x         == True" $ \x ->+          isDfsForestOf (dfsForest x) x         == True++    test "dfsForest . forest . dfsForest        == dfsForest" $ \x ->+         (dfsForest . forest . dfsForest) x     == dfsForest x++    test "dfsForest (vertices vs)               == map (\\v -> Node v []) (nub $ sort vs)" $ \vs ->+          dfsForest (vertices vs)               == map (\v -> Node v []) (nub $ sort vs)++    test "dfsForest $ 3 * (1 + 4) * (1 + 5)     == <correct result>" $+          dfsForest  (3 * (1 + 4) * (1 + 5))    == [ Node { rootLabel = 1+                                                   , subForest = [ Node { rootLabel = 5+                                                                        , subForest = [] }]}+                                                   , Node { rootLabel = 3+                                                   , subForest = [ Node { rootLabel = 4+                                                                        , subForest = [] }]}]+    test "forest (dfsForest $ circuit [1..5] + circuit [5,4..1]) == path [1,2,3,4,5]" $+          forest (dfsForest $ circuit [1..5] + circuit [5,4..1]) == path [1,2,3,4,5]++testDfsForestFrom :: TestsuiteInt g -> IO ()+testDfsForestFrom (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "dfsForestFrom ============"+    test "dfsForestFrom vs empty                           == []" $ \vs ->+          dfsForestFrom vs empty                           == []++    test "forest (dfsForestFrom [1]   $ edge 1 1)          == vertex 1" $+          forest (dfsForestFrom [1]   $ edge 1 1)          == vertex 1++    test "forest (dfsForestFrom [1]   $ edge 1 2)          == edge 1 2" $+          forest (dfsForestFrom [1]   $ edge 1 2)          == edge 1 2++    test "forest (dfsForestFrom [2]   $ edge 1 2)          == vertex 2" $+          forest (dfsForestFrom [2]   $ edge 1 2)          == vertex 2++    test "forest (dfsForestFrom [3]   $ edge 1 2)          == empty" $+          forest (dfsForestFrom [3]   $ edge 1 2)          == empty++    test "forest (dfsForestFrom [2,1] $ edge 1 2)          == vertices [1,2]" $+          forest (dfsForestFrom [2,1] $ edge 1 2)          == vertices [1,2]++    test "isSubgraphOf (forest $ dfsForestFrom vs x) x     == True" $ \vs x ->+          isSubgraphOf (forest $ dfsForestFrom vs x) x     == True++    test "isDfsForestOf (dfsForestFrom (vertexList x) x) x == True" $ \x ->+          isDfsForestOf (dfsForestFrom (vertexList x) x) x == True++    test "dfsForestFrom (vertexList x) x                   == dfsForest x" $ \x ->+          dfsForestFrom (vertexList x) x                   == dfsForest x++    test "dfsForestFrom vs             (vertices vs)       == map (\\v -> Node v []) (nub vs)" $ \vs ->+          dfsForestFrom vs             (vertices vs)       == map (\v -> Node v []) (nub vs)++    test "dfsForestFrom []             x                   == []" $ \x ->+          dfsForestFrom []             x                   == []++    test "dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5)      == <correct result>" $+          dfsForestFrom [1,4]  (3 * (1 + 4) * (1 + 5))     == [ Node { rootLabel = 1+                                                                     , subForest = [ Node { rootLabel = 5+                                                                                          , subForest = [] }]}+                                                              , Node { rootLabel = 4+                                                                     , subForest = [] }]+    test "forest (dfsForestFrom [3] $ circuit [1..5] + circuit [5,4..1]) == path [3,2,1,5,4]" $+          forest (dfsForestFrom [3] $ circuit [1..5] + circuit [5,4..1]) == path [3,2,1,5,4]+++testDfs :: TestsuiteInt g -> IO ()+testDfs (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "dfs ============"+    test "dfs vs    $ empty                    == []" $ \vs ->+          dfs vs      empty                    == []++    test "dfs [1]   $ edge 1 1                 == [1]" $+          dfs [1]    (edge 1 1)                == [1]++    test "dfs [1]   $ edge 1 2                 == [1,2]" $+          dfs [1]    (edge 1 2)                == [1,2]++    test "dfs [2]   $ edge 1 2                 == [2]" $+          dfs [2]    (edge 1 2)                 == [2]++    test "dfs [3]   $ edge 1 2                 == []" $+          dfs [3]    (edge 1 2)                == []++    test "dfs [1,2] $ edge 1 2                 == [1,2]" $+          dfs [1,2]  (edge 1 2)                == [1,2]++    test "dfs [2,1] $ edge 1 2                 == [2,1]" $+          dfs [2,1]  (edge 1 2)                 == [2,1]++    test "dfs []    $ x                        == []" $ \x ->+          dfs []      x                        == []++    test "dfs [1,4] $ 3 * (1 + 4) * (1 + 5)    == [1,5,4]" $+          dfs [1,4]  (3 * (1 + 4) * (1 + 5))   == [1,5,4]++    test "isSubgraphOf (vertices $ dfs vs x) x == True" $ \vs x ->+          isSubgraphOf (vertices $ dfs vs x) x == True++    test "dfs [3] (circuit [1..5] + circuit [5,4..1]) == [3,2,1,5,4]" $+          dfs [3] (circuit [1..5] + circuit [5,4..1]) == [3,2,1,5,4]++testReachable :: TestsuiteInt g -> IO ()+testReachable (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "dfs ============"+    test "reachable x $ empty                       == []" $ \x ->+          reachable x   empty                       == []++    test "reachable 1 $ vertex 1                    == [1]" $+          reachable 1  (vertex 1)                   == [1]++    test "reachable 1 $ vertex 2                    == []" $+          reachable 1  (vertex 2)                   == []++    test "reachable 1 $ edge 1 1                    == [1]" $+          reachable 1  (edge 1 1)                   == [1]++    test "reachable 1 $ edge 1 2                    == [1,2]" $+          reachable 1  (edge 1 2)                   == [1,2]++    test "reachable 4 $ path    [1..8]              == [4..8]" $+          reachable 4  (path    [1..8])             == [4..8]++    test "reachable 4 $ circuit [1..8]              == [4..8] ++ [1..3]" $+          reachable 4  (circuit [1..8])             == [4..8] ++ [1..3]++    test "reachable 8 $ clique  [8,7..1]            == [8] ++ [1..7]" $+          reachable 8  (clique  [8,7..1])           == [8] ++ [1..7]++    test "isSubgraphOf (vertices $ reachable x y) y == True" $ \x y ->+          isSubgraphOf (vertices $ reachable x y) y == True++testTopSort :: TestsuiteInt g -> IO ()+testTopSort (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "topSort ============"+    test "topSort (1 * 2 + 3 * 1)                    == Right [3,1,2]" $+          topSort (1 * 2 + 3 * 1)                    == Right [3,1,2]++    test "topSort (path [1..5])                      == Right [1..5]" $+          topSort (path [1..5])                      == Right [1..5]++    test "topSort (3 * (1 * 4 + 2 * 5))              == Right [3,1,2,4,5]" $+          topSort (3 * (1 * 4 + 2 * 5))              == Right [3,1,2,4,5]++    test "topSort (1 * 2 + 2 * 1)                    == Left (2 :| [1])" $+          topSort (1 * 2 + 2 * 1)                    == Left (2 :| [1])++    test "topSort (path [5,4..1] + edge 2 4)         == Left (4 :| [3,2])" $+          topSort (path [5,4..1] + edge 2 4)         == Left (4 :| [3,2])++    test "topSort (circuit [1..5])                   == Left (3 :| [1,2])" $+          topSort (circuit [1..3])                   == Left (3 :| [1,2])++    test "topSort (circuit [1..3] + circuit [3,2,1]) == Left (3 :| [2])" $+          topSort (circuit [1..3] + circuit [3,2,1]) == Left (3 :| [2])++    test "topSort (1*2 + 2*1 + 3*4 + 4*3 + 5*1)      == Left (1 :| [2])" $+          topSort (1*2 + 2*1 + 3*4 + 4*3 + 5*1)      == Left (1 :| [2])++    test "fmap (flip isTopSortOf x) (topSort x) /= Right False" $ \x ->+          fmap (flip isTopSortOf x) (topSort x) /= Right False++    test "topSort . vertices     == Right . nub . sort" $ \vs ->+         (topSort . vertices) vs == (Right . nubOrd . sort) vs++++testIsAcyclic :: TestsuiteInt g -> IO ()+testIsAcyclic (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "testIsAcyclic ============"+    test "isAcyclic (1 * 2 + 3 * 1) == True" $+          isAcyclic (1 * 2 + 3 * 1) == True++    test "isAcyclic (1 * 2 + 2 * 1) == False" $+          isAcyclic (1 * 2 + 2 * 1) == False++    test "isAcyclic . circuit       == null" $ \xs ->+         (isAcyclic . circuit) xs  == null xs++    test "isAcyclic                 == isRight . topSort" $ \x ->+          isAcyclic x               == isRight (topSort x)++testIsDfsForestOf :: TestsuiteInt g -> IO ()+testIsDfsForestOf (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "isDfsForestOf ============"+    test "isDfsForestOf []                              empty            == True" $+          isDfsForestOf []                              empty            == True++    test "isDfsForestOf []                              (vertex 1)       == False" $+          isDfsForestOf []                              (vertex 1)       == False++    test "isDfsForestOf [Node 1 []]                     (vertex 1)       == True" $+          isDfsForestOf [Node 1 []]                     (vertex 1)       == True++    test "isDfsForestOf [Node 1 []]                     (vertex 2)       == False" $+          isDfsForestOf [Node 1 []]                     (vertex 2)       == False++    test "isDfsForestOf [Node 1 [], Node 1 []]          (vertex 1)       == False" $+          isDfsForestOf [Node 1 [], Node 1 []]          (vertex 1)       == False++    test "isDfsForestOf [Node 1 []]                     (edge 1 1)       == True" $+          isDfsForestOf [Node 1 []]                     (edge 1 1)       == True++    test "isDfsForestOf [Node 1 []]                     (edge 1 2)       == False" $+          isDfsForestOf [Node 1 []]                     (edge 1 2)       == False++    test "isDfsForestOf [Node 1 [], Node 2 []]          (edge 1 2)       == False" $+          isDfsForestOf [Node 1 [], Node 2 []]          (edge 1 2)       == False++    test "isDfsForestOf [Node 2 [], Node 1 []]          (edge 1 2)       == True" $+          isDfsForestOf [Node 2 [], Node 1 []]          (edge 1 2)       == True++    test "isDfsForestOf [Node 1 [Node 2 []]]            (edge 1 2)       == True" $+          isDfsForestOf [Node 1 [Node 2 []]]            (edge 1 2)       == True++    test "isDfsForestOf [Node 1 [], Node 2 []]          (vertices [1,2]) == True" $+          isDfsForestOf [Node 1 [], Node 2 []]          (vertices [1,2]) == True++    test "isDfsForestOf [Node 2 [], Node 1 []]          (vertices [1,2]) == True" $+          isDfsForestOf [Node 2 [], Node 1 []]          (vertices [1,2]) == True++    test "isDfsForestOf [Node 1 [Node 2 []]]            (vertices [1,2]) == False" $+          isDfsForestOf [Node 1 [Node 2 []]]            (vertices [1,2]) == False++    test "isDfsForestOf [Node 1 [Node 2 [Node 3 []]]]   (path [1,2,3])   == True" $+          isDfsForestOf [Node 1 [Node 2 [Node 3 []]]]   (path [1,2,3])   == True++    test "isDfsForestOf [Node 1 [Node 3 [Node 2 []]]]   (path [1,2,3])   == False" $+          isDfsForestOf [Node 1 [Node 3 [Node 2 []]]]   (path [1,2,3])   == False++    test "isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] (path [1,2,3])   == True" $+          isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] (path [1,2,3])   == True++    test "isDfsForestOf [Node 2 [Node 3 []], Node 1 []] (path [1,2,3])   == True" $+          isDfsForestOf [Node 2 [Node 3 []], Node 1 []] (path [1,2,3])   == True++    test "isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] (path [1,2,3])   == False" $+          isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] (path [1,2,3])   == False++testIsTopSortOf :: TestsuiteInt g -> IO ()+testIsTopSortOf (prefix, API{..}) = do+    putStrLn $ "\n============ " ++ prefix ++ "isTopSortOf ============"+    test "isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True" $+          isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True++    test "isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False" $+          isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False++    test "isTopSortOf []      (1 * 2 + 3 * 1) == False" $+          isTopSortOf []      (1 * 2 + 3 * 1) == False++    test "isTopSortOf []      empty           == True" $+          isTopSortOf []      empty           == True++    test "isTopSortOf [x]     (vertex x)      == True" $ \x ->+          isTopSortOf [x]     (vertex x)      == True++    test "isTopSortOf [x]     (edge x x)      == False" $ \x ->+          isTopSortOf [x]     (edge x x)      == False
test/Algebra/Graph/Test/Graph.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Graph--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2020 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,30 +12,31 @@ module Algebra.Graph.Test.Graph (     -- * Testsuite     testGraph-  ) where--import Prelude ()-import Prelude.Compat+    ) where  import Data.Either  import Algebra.Graph import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, graphAPI) import Algebra.Graph.Test.Generic import Algebra.Graph.ToGraph (reachable)  import qualified Data.Graph as KL -t :: Testsuite-t = testsuite "Graph." empty+tPoly :: Testsuite Graph Ord+tPoly = ("Graph.", graphAPI) +t :: TestsuiteInt Graph+t = fmap toIntAPI tPoly+ type G = Graph Int  testGraph :: IO () testGraph = do     putStrLn "\n============ Graph ============"-    test "Axioms of graphs"   (axioms   :: GraphTestsuite G)-    test "Theorems of graphs" (theorems :: GraphTestsuite G)+    test "Axioms of graphs"   (axioms   @ G)+    test "Theorems of graphs" (theorems @ G)      testBasicPrimitives t     testIsSubgraphOf    t@@ -43,6 +44,7 @@     testSize            t     testGraphFamilies   t     testTransformations t+    testInduceJust      tPoly      ----------------------------------------------------------------     -- Generic relational composition tests, plus an additional one@@ -67,103 +69,14 @@     test "x + y === x * y     == False" $ \(x :: G) y ->          (x + y === x * y)    == False -    putStrLn "\n============ Graph.mesh ============"-    test "mesh xs     []    == empty" $ \xs ->-          mesh xs     []    == (empty :: Graph (Int, Int)) -    test "mesh []     ys    == empty" $ \ys ->-          mesh []     ys    == (empty :: Graph (Int, Int))--    test "mesh [x]    [y]   == vertex (x, y)" $ \(x :: Int) (y :: Int) ->-          mesh [x]    [y]   == vertex (x, y)--    test "mesh xs     ys    == box (path xs) (path ys)" $ \(xs :: [Int]) (ys :: [Int]) ->-          mesh xs     ys    == box (path xs) (path ys)--    test "mesh [1..3] \"ab\"  == <correct result>" $-          mesh [1..3]  "ab"   == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))-                                    , ((2,'a'),(3,'a')), ((2,'b'),(3,'b')), ((3,'a'),(3 :: Int,'b')) ]-    test "size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)" $ \(xs :: [Int]) (ys :: [Int]) ->-          size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)--    putStrLn "\n============ Graph.torus ============"-    test "torus xs     []    == empty" $ \xs ->-          torus xs     []    == (empty :: Graph (Int, Int))--    test "torus []     ys    == empty" $ \ys ->-          torus []     ys    == (empty :: Graph (Int, Int))--    test "torus [x]    [y]   == edge (x,y) (x,y)" $ \(x :: Int) (y :: Int) ->-          torus [x]    [y]   == edge (x,y) (x,y)--    test "torus xs     ys    == box (circuit xs) (circuit ys)" $ \(xs :: [Int]) (ys :: [Int]) ->-          torus xs     ys    == box (circuit xs) (circuit ys)--    test "torus [1,2]  \"ab\"  == <correct result>" $-          torus [1,2]   "ab"   == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))-                                      , ((2,'a'),(1,'a')), ((2,'a'),(2,'b')), ((2,'b'),(1,'b')), ((2,'b'),(2 :: Int,'a')) ]--    test "size (torus xs ys) == max 1 (3 * length xs * length ys)" $ \(xs :: [Int]) (ys :: [Int]) ->-          size (torus xs ys) == max 1 (3 * length xs * length ys)---    putStrLn "\n============ Graph.deBruijn ============"-    test "          deBruijn 0 xs               == edge [] []" $ \(xs :: [Int]) ->-                    deBruijn 0 xs               ==(edge [] [] :: Graph [Int])--    test "n > 0 ==> deBruijn n []               == empty" $ \n ->-          n > 0 ==> deBruijn n []               == (empty :: Graph [Int])--    test "          deBruijn 1 [0,1]            == edges [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ]" $-                    deBruijn 1 [0,1::Int]       == edges [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ]--    test "          deBruijn 2 \"0\"              == edge \"00\" \"00\"" $-                    deBruijn 2 "0"              == edge "00" "00"--    test "          deBruijn 2 \"01\"             == <correct result>" $-                    deBruijn 2 "01"             == edges [ ("00","00"), ("00","01"), ("01","10"), ("01","11")-                                                         , ("10","00"), ("10","01"), ("11","10"), ("11","11") ]--    test "          transpose   (deBruijn n xs) == fmap reverse $ deBruijn n xs" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->-                    transpose   (deBruijn n xs) == fmap reverse (deBruijn n xs)--    test "          vertexCount (deBruijn n xs) == (length $ nub xs)^n" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->-                    vertexCount (deBruijn n xs) == (length $ nubOrd xs)^n--    test "n > 0 ==> edgeCount   (deBruijn n xs) == (length $ nub xs)^(n + 1)" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->-          n > 0 ==> edgeCount   (deBruijn n xs) == (length $ nubOrd xs)^(n + 1)-+    testMesh        tPoly+    testTorus       tPoly+    testDeBruijn    tPoly     testSplitVertex t     testBind        t     testSimplify    t--    putStrLn "\n============ Graph.box ============"-    let unit = fmap $ \(a, ()) -> a-        comm = fmap $ \(a,  b) -> (b, a)-    test "box x y               ~~ box y x" $ mapSize (min 10) $ \(x :: G) (y :: G) ->-          comm (box x y)        == box y x--    test "box x (overlay y z)   == overlay (box x y) (box x z)" $ mapSize (min 10) $ \(x :: G) (y :: G) z ->-          box x (overlay y z)   == overlay (box x y) (box x z)--    test "box x (vertex ())     ~~ x" $ mapSize (min 10) $ \(x :: G) ->-     unit(box x (vertex ()))    == x--    test "box x empty           ~~ empty" $ mapSize (min 10) $ \(x :: G) ->-     unit(box x empty)          == empty--    let assoc = fmap $ \(a, (b, c)) -> ((a, b), c)-    test "box x (box y z)       ~~ box (box x y) z" $ mapSize (min 10) $ \(x :: G) (y :: G) (z :: G) ->-      assoc (box x (box y z))   == box (box x y) z--    test "transpose   (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \(x :: G) (y :: G) ->-          transpose   (box x y) == box (transpose x) (transpose y)--    test "vertexCount (box x y) == vertexCount x * vertexCount y" $ mapSize (min 10) $ \(x :: G) (y :: G) ->-          vertexCount (box x y) == vertexCount x * vertexCount y--    test "edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y" $ mapSize (min 10) $ \(x :: G) (y :: G) ->-          edgeCount   (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y+    testBox         tPoly      putStrLn "\n============ Graph.sparsify ============"     test "sort . reachable x       == sort . rights . reachable (Right x) . sparsify" $ \x (y :: G) ->@@ -198,7 +111,7 @@         let x = vertices [1..n] `overlay` edges es         return $ length (KL.edges $ sparsifyKL n x) <= 3 * size x -    putStrLn "\n============ Labelled.Graph.context ============"+    putStrLn "\n============ Graph.context ============"     test "context (const False) x                   == Nothing" $ \x ->           context (const False) (x :: G)            == Nothing @@ -213,3 +126,22 @@      test "context (== 4)        (3 * 1 * 4 * 1 * 5) == Just (Context [3,1] [1,5])" $           context (== 4)        (3 * 1 * 4 * 1 * 5 :: G) == Just (Context [3,1] [1,5])++    putStrLn "\n============ Graph.buildg ============"+    test "buildg (\\e _ _ _ -> e)                                     == empty" $+          buildg (\e _ _ _ -> e)                                      == (empty :: G)++    test "buildg (\\_ v _ _ -> v x)                                   == vertex x" $ \(x :: Int) ->+          buildg (\_ v _ _ -> v x)                                    == vertex x++    test "buildg (\\e v o c -> o (foldg e v o c x) (foldg e v o c y)) == overlay x y" $ \(x :: G) y ->+          buildg (\e v o c -> o (foldg e v o c x) (foldg e v o c y))  == overlay x y++    test "buildg (\\e v o c -> c (foldg e v o c x) (foldg e v o c y)) == connect x y" $ \(x :: G) y ->+          buildg (\e v o c -> c (foldg e v o c x) (foldg e v o c y))  == connect x y++    test "buildg (\\e v o _ -> foldr o e (map v xs))                  == vertices xs" $ \(xs :: [Int]) ->+          buildg (\e v o _ -> foldr o e (map v xs))                   == vertices xs++    test "buildg (\\e v o c -> foldg e v o (flip c) g)                == transpose g" $ \(g :: G) ->+          buildg (\e v o c -> foldg e v o (flip c) g)                 == transpose g
test/Algebra/Graph/Test/Internal.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE CPP, OverloadedLists #-}+{-# LANGUAGE OverloadedLists #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,19 +12,11 @@ module Algebra.Graph.Test.Internal (     -- * Testsuite     testInternal-  ) where--import Prelude ()-import Prelude.Compat--#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup-#endif--import Control.Applicative (pure)+    ) where  import Algebra.Graph.Internal import Algebra.Graph.Test+import Data.Semigroup ((<>))  testInternal :: IO () testInternal = do
+ test/Algebra/Graph/Test/Label.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE OverloadedLists #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test.Label+-- Copyright  : (c) Andrey Mokhov 2016-2020+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Testsuite for "Algebra.Graph.Label".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Label (+  -- * Testsuite+  testLabel+  ) where++import Algebra.Graph.Test+import Algebra.Graph.Label+import Data.Monoid++type Unary a          = a -> a+type Binary a         = a -> a -> a+type Additive a       = Binary a+type Multiplicative a = Binary a+type Star a           = Unary a+type Identity a       = a+type Zero a           = a+type One a            = a++associative :: Eq a => Binary a -> a -> a -> a -> Property+associative (<>) a b c = (a <> b) <> c == a <> (b <> c) // "Associative"++commutative :: Eq a => Binary a -> a -> a -> Property+commutative (<>) a b = a <> b == b <> a // "Commutative"++idempotent :: Eq a => Binary a -> a -> Property+idempotent (<>) a = a <> a == a // "Idempotent"++annihilatingZero :: Eq a => Binary a -> Zero a -> a -> Property+annihilatingZero (<>) z a = conjoin+    [ a <> z == z // "Left"+    , z <> a == z // "Right" ] // "Annihilating zero"++closure :: Eq a => Additive a -> Multiplicative a -> One a -> Star a -> a -> Property+closure (+) (*) o s a = conjoin+    [ s a == o + (a * s a) // "Left"+    , s a == o + (s a * a) // "Right" ] // "Closure"++leftDistributive :: Eq a => Additive a -> Multiplicative a -> a -> a -> a -> Property+leftDistributive (+) (*) a b c =+    a * (b + c) == (a * b) + (a * c) // "Left distributive"++rightDistributive :: Eq a => Additive a -> Multiplicative a -> a -> a -> a -> Property+rightDistributive (+) (*) a b c =+    (a + b) * c == (a * c) + (b * c) // "Right distributive"++distributive :: Eq a => Additive a -> Multiplicative a -> a -> a -> a -> Property+distributive p m a b c = conjoin+    [ leftDistributive p m a b c+    , rightDistributive p m a b c ] // "Distributive"++identity :: Eq a => Binary a -> Identity a -> a -> Property+identity (<>) e a = conjoin+    [ a <> e == a // "Left"+    , e <> a == a // "Right" ] // "Identity"++semigroup :: Eq a => Binary a -> a -> a -> a -> Property+semigroup f a b c = associative f a b c // "Semigroup"++monoid :: Eq a => Binary a -> Identity a -> a -> a -> a -> Property+monoid f e a b c = conjoin+    [ semigroup f a b c+    , identity f e a ] // "Monoid"++commutativeMonoid :: Eq a => Binary a -> Identity a -> a -> a -> a -> Property+commutativeMonoid f e a b c = conjoin+    [ monoid f e a b c+    , commutative f a b ] // "Commutative monoid"++leftNearRing :: Eq a => Additive a -> Zero a -> Multiplicative a -> One a -> a -> a -> a -> Property+leftNearRing (+) z (*) o a b c = conjoin+    [ commutativeMonoid (+) z a b c+    , monoid (*) o a b c+    , leftDistributive (+) (*) a b c+    , annihilatingZero (*) z a ] // "Left near ring"++semiring :: Eq a => Additive a -> Zero a -> Multiplicative a -> One a -> a -> a -> a -> Property+semiring (+) z (*) o a b c = conjoin+    [ commutativeMonoid (+) z a b c+    , monoid (*) o a b c+    , distributive (+) (*) a b c+    , annihilatingZero (*) z a ] // "Semiring"++dioid :: Eq a => Additive a -> Zero a -> Multiplicative a -> One a -> a -> a -> a -> Property+dioid (+) z (*) o a b c = conjoin+    [ semiring (+) z (*) o a b c+    , idempotent (+) a ] // "Dioid"++starSemiring :: Eq a => Additive a -> Zero a -> Multiplicative a -> One a -> Star a -> a -> a -> a -> Property+starSemiring (+) z (*) o s a b c = conjoin+    [ semiring (+) z (*) o a b c+    , closure (+) (*) o s a ] // "Star semiring"++testLeftNearRing :: (Eq a, Semiring a) => a -> a -> a -> Property+testLeftNearRing = leftNearRing (<+>) zero (<.>) one++testSemiring :: (Eq a, Semiring a) => a -> a -> a -> Property+testSemiring = semiring (<+>) zero (<.>) one++testDioid :: (Eq a, Dioid a) => a -> a -> a -> Property+testDioid = dioid (<+>) zero (<.>) one++testStarSemiring :: (Eq a, StarSemiring a) => a -> a -> a -> Property+testStarSemiring = starSemiring (<+>) zero (<.>) one star++testLabel :: IO ()+testLabel = do+    putStrLn "\n============ Graph.Label ============"+    putStrLn "\n============ Any: instances ============"+    test "Semiring"     $ testSemiring     @Any+    test "StarSemiring" $ testStarSemiring @Any+    test "Dioid"        $ testDioid        @Any++    putStrLn "\n============ Distance Int: instances ============"+    test "Semiring"     $ testSemiring     @(Distance Int)+    test "StarSemiring" $ testStarSemiring @(Distance Int)+    test "Dioid"        $ testDioid        @(Distance Int)++    putStrLn "\n============ Capacity Int: instances ============"+    test "Semiring"     $ testSemiring     @(Capacity Int)+    test "StarSemiring" $ testStarSemiring @(Capacity Int)+    test "Dioid"        $ testDioid        @(Capacity Int)++    putStrLn "\n============ Minimum (Path Int): instances ============"+    test "LeftNearRing" $ testLeftNearRing @(Minimum (Path Int))++    putStrLn "\n============ PowerSet (Path Int): instances ============"+    test "Semiring" $ size10 $ testSemiring @(PowerSet (Path Int))+    test "Dioid"    $ size10 $ testDioid    @(PowerSet (Path Int))++    putStrLn "\n============ Count Int: instances ============"+    test "Semiring"     $ testSemiring     @(Count Int)+    test "StarSemiring" $ testStarSemiring @(Count Int)
test/Algebra/Graph/Test/Labelled/AdjacencyMap.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Labelled.AdjacencyMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -18,8 +18,8 @@  import Algebra.Graph.Label import Algebra.Graph.Labelled.AdjacencyMap-import Algebra.Graph.Labelled.AdjacencyMap.Internal import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, labelledAdjacencyMapAPI) import Algebra.Graph.Test.Generic import Algebra.Graph.ToGraph (reachable) @@ -27,9 +27,12 @@ import qualified Data.Map                   as Map import qualified Data.Set                   as Set -t :: Testsuite-t = testsuite "Labelled.AdjacencyMap." (empty :: LAI)+tPoly :: Testsuite (AdjacencyMap Any) Ord+tPoly = ("Labelled.AdjacencyMap.", labelledAdjacencyMapAPI) +t :: TestsuiteInt (AdjacencyMap Any)+t = fmap toIntAPI tPoly+ type S = Sum Int type D = Distance Int @@ -39,7 +42,7 @@  testLabelledAdjacencyMap :: IO () testLabelledAdjacencyMap = do-    putStrLn "\n============ Labelled.AdjacencyMap.Internal.consistent ============"+    putStrLn "\n============ Labelled.AdjacencyMap.consistent ============"     test "arbitraryLabelledAdjacencyMap" $ \x -> consistent (x           :: LAS)     test "empty" $                      consistent (empty                :: LAS)     test "vertex" $ \x               -> consistent (vertex x             :: LAS)@@ -350,7 +353,7 @@     test "transpose (edge e x y) == edge e y x" $ \e x y ->           transpose (edge e x y) == (edge e y x :: LAS) -    test "transpose . transpose == id" $ size10 $ \x ->+    test "transpose . transpose  == id" $ size10 $ \x ->          (transpose . transpose) x == (x :: LAS)      putStrLn "\n============ Labelled.AdjacencyMap.gmap ============"@@ -403,6 +406,7 @@         in (emap g . emap h) x   == (emap (g . h) x :: LAS)      testInduce t+    testInduceJust tPoly      putStrLn "\n============ Labelled.AdjacencyMap.closure ============"     test "closure empty         == empty" $
test/Algebra/Graph/Test/Labelled/Graph.hs view
@@ -19,14 +19,18 @@ import Algebra.Graph.Label import Algebra.Graph.Labelled import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, labelledGraphAPI) import Algebra.Graph.Test.Generic  import qualified Algebra.Graph.ToGraph as T import qualified Data.Set              as Set -t :: Testsuite-t = testsuite "Labelled.Graph." (empty :: LAI)+tPoly :: Testsuite (Graph Any) Ord+tPoly = ("Labelled.Graph.", labelledGraphAPI) +t :: TestsuiteInt (Graph Any)+t = fmap toIntAPI tPoly+ type S = Sum Int type D = Distance Int @@ -376,7 +380,8 @@             g = (l*)         in (emap g . emap h) x   == (emap (g . h) x :: LAS) -    testInduce t+    testInduce     t+    testInduceJust tPoly      putStrLn "\n============ Labelled.Graph.closure ============"     test "closure empty         == empty" $
test/Algebra/Graph/Test/NonEmpty/AdjacencyMap.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE CPP, OverloadedLists, ViewPatterns #-}+{-# LANGUAGE OverloadedLists, ViewPatterns #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.NonEmpty.AdjacencyMap--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,22 +12,16 @@ module Algebra.Graph.Test.NonEmpty.AdjacencyMap (     -- * Testsuite     testNonEmptyAdjacencyMap-  ) where--import Prelude ()-import Prelude.Compat--#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup-#endif+    ) where  import Control.Monad+import Data.Semigroup ((<>)) import Data.Tree import Data.Tuple  import Algebra.Graph.NonEmpty.AdjacencyMap import Algebra.Graph.Test hiding (axioms, theorems)-import Algebra.Graph.ToGraph (toAdjacencyMap, reachable)+import Algebra.Graph.ToGraph (reachable)  import qualified Algebra.Graph.AdjacencyMap          as AM import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty@@ -119,15 +113,19 @@           show (vertex (-1) * vertex (-2) + vertex (-3) :: AdjacencyMap Int) == "overlay (vertex (-3)) (edge (-1) (-2))"      putStrLn $ "\n============ NonEmpty.AdjacencyMap.toNonEmpty ============"-    test "toNonEmpty empty              == Nothing" $+    test "toNonEmpty empty          == Nothing" $           toNonEmpty (AM.empty :: AM.AdjacencyMap Int) == Nothing -    test "toNonEmpty (toAdjacencyMap x) == Just (x :: NonEmpty.AdjacencyMap a)" $ \x ->-          toNonEmpty (toAdjacencyMap x) == Just (x :: G)+    test "toNonEmpty . fromNonEmpty == Just" $ \(x :: G) ->+         (toNonEmpty . fromNonEmpty) x == Just x +    putStrLn $ "\n============ NonEmpty.AdjacencyMap.fromNonEmpty ============"+    test "isEmpty . fromNonEmpty    == const False" $ \(x :: G) ->+         (AM.isEmpty . fromNonEmpty) x == const False x+     putStrLn $ "\n============ NonEmpty.AdjacencyMap.vertex ============"-    test "hasVertex x (vertex x) == True" $ \(x :: Int) ->-          hasVertex x (vertex x) == True+    test "hasVertex x (vertex y) == (x == y)" $ \(x :: Int) y ->+          hasVertex x (vertex y) == (x == y)      test "vertexCount (vertex x) == 1" $ \(x :: Int) ->           vertexCount (vertex x) == 1@@ -221,6 +219,10 @@     test "edges1 [(x,y)]     == edge x y" $ \(x :: Int) y ->           edges1 [(x,y)]     == edge x y +    test "edges1             == overlays1 . fmap (uncurry edge)" $ \(xs' :: NonEmptyList (Int, Int)) ->+        let xs = NonEmpty.fromList (getNonEmpty xs')+        in edges1 xs         == (overlays1 . fmap (uncurry edge)) xs+     test "edgeCount . edges1 == length . nub" $ \(xs' :: NonEmptyList (Int, Int)) ->         let xs = NonEmpty.fromList (getNonEmpty xs')         in (edgeCount . edges1) xs == (NonEmpty.length . NonEmpty.nub) xs@@ -255,11 +257,8 @@         in isSubgraphOf x y                        ==> x <= y      putStrLn $ "\n============ NonEmpty.AdjacencyMap.hasVertex ============"-    test "hasVertex x (vertex x) == True" $ \(x :: Int) ->-          hasVertex x (vertex x) == True--    test "hasVertex 1 (vertex 2) == False" $-          hasVertex 1 (vertex 2 :: G) == False+    test "hasVertex x (vertex y) == (x == y)" $ \(x :: Int) y ->+          hasVertex x (vertex y) == (x == y)      putStrLn $ "\n============ NonEmpty.AdjacencyMap.hasEdge ============"     test "hasEdge x y (vertex z)       == False" $ \(x :: Int) y z ->@@ -545,6 +544,19 @@      test "induce1 p >=> induce1 q == induce1 (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) (y :: G) ->          (induce1 p >=> induce1 q) y == induce1 (\x -> p x && q x) y++    putStrLn $ "\n============ NonEmpty.AdjacencyMap.induceJust1 ============"+    test "induceJust1 (vertex Nothing)                               == Nothing" $+          induceJust1 (vertex (Nothing :: Maybe Int))                == Nothing++    test "induceJust1 (edge (Just x) Nothing)                        == Just (vertex x)" $ \(x :: G) ->+          induceJust1 (edge (Just x) Nothing)                        == Just (vertex x)++    test "induceJust1 . gmap Just                                    == Just" $ \(x :: G) ->+         (induceJust1 . gmap Just) x                                 == Just x++    test "induceJust1 . gmap (\\x -> if p x then Just x else Nothing) == induce1 p" $ \(x :: G) (apply -> p) ->+         (induceJust1 . gmap (\x -> if p x then Just x else Nothing)) x == induce1 p x      putStrLn $ "\n============ NonEmpty.AdjacencyMap.closure ============"     test "closure (vertex x)      == edge x x" $ \(x :: Int) ->
test/Algebra/Graph/Test/NonEmpty/Graph.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE CPP, OverloadedLists, ViewPatterns #-}+{-# LANGUAGE OverloadedLists, ViewPatterns #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.NonEmpty.Graph--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -12,18 +12,12 @@ module Algebra.Graph.Test.NonEmpty.Graph (     -- * Testsuite     testNonEmptyGraph-  ) where--import Prelude ()-import Prelude.Compat--#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup-#endif+    ) where  import Control.Monad import Data.Either import Data.Maybe+import Data.Semigroup ((<>)) import Data.Tree import Data.Tuple @@ -125,8 +119,8 @@           toNonEmpty (toGraph x) == Just (x :: G)      putStrLn $ "\n============ NonEmpty.Graph.vertex ============"-    test "hasVertex x (vertex x) == True" $ \(x :: Int) ->-          hasVertex x (vertex x) == True+    test "hasVertex x (vertex y) == (x == y)" $ \(x :: Int) y ->+          hasVertex x (vertex y) == (x == y)      test "vertexCount (vertex x) == 1" $ \(x :: Int) ->           vertexCount (vertex x) == 1@@ -237,6 +231,10 @@     test "edges1 [(x,y)]     == edge x y" $ \(x :: Int) y ->           edges1 [(x,y)]     == edge x y +    test "edges1             == overlays1 . fmap (uncurry edge)" $ \(xs' :: NonEmptyList (Int, Int)) ->+        let xs = NonEmpty.fromList (getNonEmpty xs')+        in edges1 xs         == (overlays1 . fmap (uncurry edge)) xs+     test "edgeCount . edges1 == length . nub" $ \(xs' :: NonEmptyList (Int, Int)) ->         let xs = NonEmpty.fromList (getNonEmpty xs')         in (edgeCount . edges1) xs == (NonEmpty.length . NonEmpty.nub) xs@@ -313,11 +311,8 @@           size x             >= vertexCount x      putStrLn $ "\n============ NonEmpty.Graph.hasVertex ============"-    test "hasVertex x (vertex x) == True" $ \(x :: Int) ->-          hasVertex x (vertex x) == True--    test "hasVertex 1 (vertex 2) == False" $-          hasVertex 1 (vertex 2 :: G) == False+    test "hasVertex x (vertex y) == (x == y)" $ \(x :: Int) y ->+          hasVertex x (vertex y) == (x == y)      putStrLn $ "\n============ NonEmpty.Graph.hasEdge ============"     test "hasEdge x y (vertex z)       == False" $ \(x :: Int) y z ->@@ -626,6 +621,19 @@      test "induce1 p >=> induce1 q == induce1 (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) (y :: G) ->          (induce1 p >=> induce1 q) y == induce1 (\x -> p x && q x) y++    putStrLn $ "\n============ NonEmpty.Graph.induceJust1 ============"+    test "induceJust1 (vertex Nothing)                               == Nothing" $+          induceJust1 (vertex (Nothing :: Maybe Int))                == Nothing++    test "induceJust1 (edge (Just x) Nothing)                        == Just (vertex x)" $ \(x :: G) ->+          induceJust1 (edge (Just x) Nothing)                        == Just (vertex x)++    test "induceJust1 . fmap Just                                    == Just" $ \(x :: G) ->+         (induceJust1 . fmap Just) x                                 == Just x++    test "induceJust1 . fmap (\\x -> if p x then Just x else Nothing) == induce1 p" $ \(x :: G) (apply -> p) ->+         (induceJust1 . fmap (\x -> if p x then Just x else Nothing)) x == induce1 p x      putStrLn $ "\n============ NonEmpty.Graph.simplify ============"     test "simplify             ==  id" $ \(x :: G) ->
test/Algebra/Graph/Test/Relation.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Relation--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2020 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -11,26 +11,30 @@ module Algebra.Graph.Test.Relation (     -- * Testsuite     testRelation-  ) where+    ) where  import Algebra.Graph.Relation import Algebra.Graph.Relation.Preorder import Algebra.Graph.Relation.Reflexive import Algebra.Graph.Relation.Transitive import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, relationAPI) import Algebra.Graph.Test.Generic  import qualified Algebra.Graph.Class as C -t :: Testsuite-t = testsuite "Relation." empty+tPoly :: Testsuite Relation Ord+tPoly = ("Relation.", relationAPI) +t :: TestsuiteInt Relation+t = fmap toIntAPI tPoly+ type RI = Relation Int  testRelation :: IO () testRelation = do     putStrLn "\n============ Relation ============"-    test "Axioms of graphs" $ size10 (axioms :: GraphTestsuite RI)+    test "Axioms of graphs" $ size10 $ axioms @ RI      testConsistent      t     testShow            t@@ -40,21 +44,22 @@     testGraphFamilies   t     testTransformations t     testRelational      t+    testInduceJust      tPoly      putStrLn "\n============ ReflexiveRelation ============"-    test "Axioms of reflexive graphs" $ size10-        (reflexiveAxioms :: GraphTestsuite (ReflexiveRelation Int))+    test "Axioms of reflexive graphs" $ size10 $+        reflexiveAxioms @ (ReflexiveRelation Int)      putStrLn "\n============ TransitiveRelation ============"-    test "Axioms of transitive graphs" $ size10-        (transitiveAxioms :: GraphTestsuite (TransitiveRelation Int))+    test "Axioms of transitive graphs" $ size10 $+        transitiveAxioms @ (TransitiveRelation Int)      test "path xs == (clique xs :: TransitiveRelation Int)" $ size10 $ \xs ->           C.path xs == (C.clique xs :: TransitiveRelation Int)      putStrLn "\n============ PreorderRelation ============"-    test "Axioms of preorder graphs" $ size10-        (preorderAxioms :: GraphTestsuite (PreorderRelation Int))+    test "Axioms of preorder graphs" $ size10 $+        preorderAxioms @ (PreorderRelation Int)      test "path xs == (clique xs :: PreorderRelation Int)" $ size10 $ \xs ->           C.path xs == (C.clique xs :: PreorderRelation Int)
test/Algebra/Graph/Test/Relation/SymmetricRelation.hs view
@@ -1,35 +1,38 @@ ----------------------------------------------------------------------------- -- |--- Module     : Algebra.Graph.Test.Relation--- Copyright  : (c) Andrey Mokhov 2016-2019+-- Module     : Algebra.Graph.Test.Relation.SymmetricRelation+-- Copyright  : (c) Andrey Mokhov 2016-2020 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental ----- Testsuite for "Algebra.Graph.Relation".+-- Testsuite for "Algebra.Graph.Relation.Symmetric". ----------------------------------------------------------------------------- module Algebra.Graph.Test.Relation.SymmetricRelation (     -- * Testsuite     testSymmetricRelation-  ) where+    ) where  import Algebra.Graph.Relation.Symmetric import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, symmetricRelationAPI) import Algebra.Graph.Test.Generic  import qualified Algebra.Graph.Relation as R -t :: Testsuite-t = testsuite "Symmetric.Relation." empty+tPoly :: Testsuite Relation Ord+tPoly = ("Symmetric.Relation.", symmetricRelationAPI) +t :: TestsuiteInt Relation+t = fmap toIntAPI tPoly+ type RI  = R.Relation Int type SRI = Relation Int  testSymmetricRelation :: IO () testSymmetricRelation = do     putStrLn "\n============ Symmetric.Relation ============"-    test "Axioms of undirected graphs" $-        size10 (undirectedAxioms :: GraphTestsuite SRI)+    test "Axioms of undirected graphs" $ size10 $ undirectedAxioms @ SRI      testConsistent    t     testSymmetricShow t@@ -65,4 +68,5 @@     testSymmetricToGraph         t     testSymmetricGraphFamilies   t     testSymmetricTransformations t+    testInduceJust               tPoly 
test/Algebra/Graph/Test/RewriteRules.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TemplateHaskell, RankNTypes #-}+ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.RewriteRules@@ -13,114 +14,352 @@  import Data.Maybe (fromMaybe) +import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Data.Set                   as Set+ import Algebra.Graph hiding ((===)) import Algebra.Graph.Internal +import GHC.Base (build)+ import Test.Inspection --- Naming convention: we use the suffix "R" to indicate the desired outcome of--- rewrite rules, and suffices "1", "2", etc. to indicate initial expressions.+type Build  a = forall b. (a -> b -> b) -> b -> b+type Buildg a = forall b. b -> (a -> b) -> (b -> b ->b ) -> (b -> b-> b) -> b --- Testsuite for 'overlays' and 'connects'.-vertices1, verticesR :: [a] -> Graph a-vertices1 = overlays . map vertex-verticesR = fromMaybe Empty . foldr (maybeF Overlay . Vertex) Nothing+-- Naming convention+--- We use:+--- * the suffix "R" to indicate the desired outcome of rewrite rules.+--- * the suffix "C" when testing the "good consumer" property.+--- * the suffix "P" when testing the "good producer" property.+--- * the suffix "I" when testing inlining.+--- * the suffix "T" when testing specialisation for a type -inspect $ 'vertices1 === 'verticesR+-- 'foldg'+emptyI, emptyIR :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b+emptyI  e v o c = foldg e v o c Empty+emptyIR e _ _ _ = e -clique1, cliqueR :: [a] -> Graph a-clique1 = connects . map vertex-cliqueR = fromMaybe Empty . foldr (maybeF Connect . Vertex) Nothing+inspect $ 'emptyI === 'emptyIR -inspect $ 'clique1 === 'cliqueR+vertexI, vertexIR :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> a -> b+vertexI  e v o c x = foldg e v o c (Vertex x)+vertexIR _ v _ _ x = v x --- Testsuite for 'transpose'.-empty1, emptyR :: Graph a-empty1 = transpose Empty-emptyR = Empty+inspect $ 'vertexI === 'vertexIR -inspect $ 'empty1 === 'emptyR+overlayI, overlayIR ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> Graph a -> b+overlayI  e v o c x y = foldg e v o c (Overlay x y)+overlayIR e v o c x y = o (foldg e v o c x) (foldg e v o c y) -vertex1, vertexR :: a -> Graph a-vertex1 = transpose . vertex-vertexR = Vertex+inspect $ 'overlayI === 'overlayIR -inspect $ 'vertex1 === 'vertexR+connectI, connectIR ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> Graph a -> b+connectI  e v o c x y = foldg e v o c (Connect x y)+connectIR e v o c x y = c (foldg e v o c x) (foldg e v o c y) -overlay1, overlayR :: Graph a -> Graph a -> Graph a-overlay1 x y = transpose (Overlay x y)-overlayR x y = Overlay (transpose x) (transpose y)+inspect $ 'connectI === 'connectIR -inspect $ 'overlay1 === 'overlayR+-- overlays+overlaysC :: Build (Graph a) -> Graph a+overlaysC xs = overlays (build xs) -connect1, connectR :: Graph a -> Graph a -> Graph a-connect1 x y = transpose (Connect x y)-connectR x y = Connect (transpose y) (transpose x)+inspect $ 'overlaysC `hasNoType` ''[] -inspect $ 'connect1 === 'connectR+overlaysP, overlaysPR ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> [Graph a] -> b+overlaysP  e v o c xs = foldg e v o c (overlays xs)+overlaysPR e v o c xs = fromMaybe e (foldr (maybeF o . foldg e v o c) Nothing xs) -overlays1, overlaysR :: [Graph a] -> Graph a-overlays1 = transpose . overlays-overlaysR = overlays . map transpose+inspect $ 'overlaysP === 'overlaysPR -inspect $ 'overlays1 === 'overlaysR+-- vertices+verticesCP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Build a -> b+verticesCP e v o c xs = foldg e v o c (vertices (build xs)) -connects1, connectsR :: [Graph a] -> Graph a-connects1 = transpose . connects-connectsR = fromMaybe Empty . foldr (maybeF (flip Connect) . transpose) Nothing+inspect $ 'verticesCP `hasNoType` ''[]+inspect $ 'verticesCP `hasNoType` ''Graph -inspect $ 'connects1 === 'connectsR+-- connects+connectsC :: Build (Graph a) -> Graph a+connectsC xs = connects (build xs) -vertices2 :: [a] -> Graph a-vertices2 = transpose . overlays . map vertex+inspect $ 'connectsC `hasNoType` ''[] -inspect $ 'vertices2 === 'vertices1+connectsP, connectsPR ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> [Graph a] -> b+connectsP  e v o c xs = foldg e v o c (connects xs)+connectsPR e v o c xs = fromMaybe e (foldr (maybeF c . foldg e v o c) Nothing xs) --- Note that we currently have these three tests:--- * vertices2 === vertices1--- * vertices1 === verticesR--- * vertices2 =/= verticesR--- This non-transitivity is awkward, and feels like a bug in the inspection--- testing library. See https://github.com/nomeata/inspection-testing/issues/23.-inspect $ 'vertices2 =/= 'verticesR+inspect $ 'connectsP === 'connectsPR -cliqueT1, cliqueTR :: [a] -> Graph a-cliqueT1 = transpose . connects . map vertex-cliqueTR = fromMaybe Empty . foldr (maybeF (flip Connect) . Vertex) Nothing+-- isSubgraphOf+isSubgraphOfC :: Ord a => Buildg a -> Buildg a -> Bool+isSubgraphOfC x y = isSubgraphOf (buildg x) (buildg y) -inspect $ 'cliqueT1 === 'cliqueTR+inspect $ 'isSubgraphOfC `hasNoType` ''Graph -starT1, starTR :: a -> [a] -> Graph a-starT1 x = transpose . star x-starTR a [] = vertex a-starTR a xs = connect (vertices xs) (vertex a)+-- clique+cliqueCP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Build a -> b+cliqueCP e v o c xs = foldg e v o c (clique (build xs)) -inspect $ 'starT1 === 'starTR+inspect $ 'cliqueCP `hasNoType` ''[]+inspect $ 'cliqueCP `hasNoType` ''Graph -fmapFmap1, fmapFmapR :: Graph a -> (a -> b) -> (b -> c) -> Graph c-fmapFmap1 g f h = fmap h (fmap f g)-fmapFmapR g f h = fmap (h . f) g+-- edges+edgesCP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Build (a,a) -> b+edgesCP e v o c xs = foldg e v o c (edges (build xs)) -inspect $ 'fmapFmap1 === 'fmapFmapR+inspect $ 'edgesCP `hasNoType` ''[]+inspect $ 'edgesCP `hasNoType` ''Graph -bind2, bind2R :: (a -> Graph b) -> (b -> Graph c) -> Graph a -> Graph c-bind2 f g x = x >>= f >>= g-bind2R f g x = x >>= (\x -> f x >>= g)+-- star+starCP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> a -> Build a -> b+starCP e v o c x xs = foldg e v o c (star x (build xs)) -inspect $ 'bind2 === 'bind2R+inspect $ 'starCP `hasNoType` ''[]+inspect $ 'starCP `hasNoType` ''Graph --- Ideally, we want this test to pass.--- Strangely, '<*>' in 'ovApR' does not inline and makes the test fail.------ This is corrected below, where '<*>' was inlined "by hand"-ovAp, ovApR :: Graph (a -> b) -> Graph (a -> b) -> Graph a -> Graph b-ovAp  x y z = overlay x y <*> z-ovApR x y z = overlay (x <*> z) (y <*> z)+-- fmap+fmapCP ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> (c -> a) -> Buildg c -> b+fmapCP  e v o c f g = foldg e v o c (fmap f (buildg g)) -inspect $ 'ovAp =/= 'ovApR+inspect $ 'fmapCP `hasNoType` ''Graph -ovAp', ovApR' :: Graph (a -> b) -> Graph (a -> b) -> Graph a -> Graph b-ovAp'  x y z = overlay x y <*> z-ovApR' x y z = overlay (x >>= (<$> z)) (y >>= (<$> z))+-- bind+bindC, bindCR :: (a -> Graph b) -> Buildg a -> Graph b+bindC  f g = (buildg g) >>= f+bindCR f g = g Empty (\x -> f x) Overlay Connect -inspect $ 'ovAp' === 'ovApR'+inspect $ 'bindC === 'bindCR++bindP, bindPR ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> (c -> Graph a) -> Graph c -> b+bindP  e v o c f g = foldg e v o c (g >>= f)+bindPR e v o c f g = foldg e (foldg e v o c . f) o c g++inspect $ 'bindP === 'bindPR++-- ap+apC, apCR :: Buildg (a -> b) -> Graph a -> Graph b+apC  f x = buildg f <*> x+apCR f x = f Empty (\v -> foldg Empty (Vertex . v) Overlay Connect x) Overlay Connect++inspect $ 'apC === 'apCR++apP, apPR ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph (c -> a) -> Graph c -> b+apP  e v o c f x = foldg e v o c (f <*> x)+apPR e v o c f x =+  foldg e (\w -> foldg e (v . w) o c x) o c f++inspect $ 'apP === 'apPR++-- eq+eqC :: Ord a => Buildg a -> Buildg a -> Bool+eqC x y = buildg x == buildg y++inspect $ 'eqC `hasNoType` ''Graph++eqT :: Graph Int -> Graph Int -> Bool+eqT x y = x == y++inspect $ 'eqT `hasNoType` ''AM.AdjacencyMap++-- ord+ordC :: Ord a => Buildg a -> Buildg a -> Ordering+ordC x y = compare (buildg x) (buildg y)++inspect $ 'ordC `hasNoType` ''Graph++ordT :: Graph Int -> Graph Int -> Ordering+ordT x y = compare x y++inspect $ 'ordT  `hasNoType` ''AM.AdjacencyMap++-- isEmpty+isEmptyC :: Buildg a -> Bool+isEmptyC g = isEmpty (buildg g)++inspect $ 'isEmptyC `hasNoType` ''Graph++-- size+sizeC :: Buildg a -> Int+sizeC g = size (buildg g)++inspect $ 'sizeC `hasNoType` ''Graph++-- vertexSet+vertexSetC :: Ord a => Buildg a -> Set.Set a+vertexSetC g = vertexSet (buildg g)++inspect $ 'vertexSetC `hasNoType` ''Graph++-- vertexCount+vertexCountC :: Ord a => Buildg a -> Int+vertexCountC g = vertexCount (buildg g)++inspect $ 'vertexSetC `hasNoType` ''Graph++vertexCountT :: Graph Int -> Int+vertexCountT g = vertexCount g++inspect $ 'vertexCountT  `hasNoType` ''Set.Set++-- edgeCount+edgeCountC :: Ord a => Buildg a -> Int+edgeCountC g = edgeCount (buildg g)++inspect $ 'edgeCountC `hasNoType` ''Graph++edgeCountT :: Graph Int -> Int+edgeCountT g = edgeCount g++inspect $ 'edgeCountT `hasNoType` ''Set.Set++-- vertexList+vertexListCP :: Ord a => (a -> b -> b) -> b -> Buildg a -> b+vertexListCP k c g = foldr k c (vertexList (buildg g))++inspect $ 'vertexListCP `hasNoType` ''Graph+inspect $ 'vertexListCP `hasNoType` ''[]++vertexListT :: Graph Int -> [Int]+vertexListT g = vertexList g++inspect $ 'vertexListT `hasNoType` ''Set.Set++-- edgeSet+edgeSetC :: Ord a => Buildg a -> Set.Set (a,a)+edgeSetC g = edgeSet (buildg g)++inspect $ 'edgeSetC `hasNoType` ''Graph++edgeSetT :: Graph Int -> Set.Set (Int,Int)+edgeSetT g = edgeSet g++inspect $ 'vertexListT `hasNoType` ''AM.AdjacencyMap++-- edgeList+edgeListCP :: Ord a => ((a,a) -> b -> b) -> b -> Buildg a -> b+edgeListCP k c g = foldr k c (edgeList (buildg g))++inspect $ 'edgeListCP `hasNoType` ''Graph+inspect $ 'edgeListCP `hasNoType` ''[]++edgeListT :: Graph Int -> [(Int,Int)]+edgeListT g = edgeList g++inspect $ 'edgeListT `hasNoType` ''AM.AdjacencyMap++-- hasVertex+hasVertexC :: Eq a => a -> Buildg a -> Bool+hasVertexC x g = hasVertex x (buildg g)++inspect $ 'hasVertexC `hasNoType` ''Graph++-- hasEdge+hasEdgeC :: Eq a => a -> a -> Buildg a -> Bool+hasEdgeC x y g = hasEdge x y (buildg g)++inspect $ 'hasEdgeC `hasNoType` ''Graph++-- adjacencyList+adjacencyListC :: Ord a => Buildg a -> [(a, [a])]+adjacencyListC g = adjacencyList (buildg g)++inspect $ 'adjacencyListC `hasNoType` ''Graph++-- path+pathP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> [a] -> b+pathP e v o c xs = foldg e v o c (path xs)++inspect $ 'pathP `hasNoType` ''Graph++-- circuit+circuitP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> [a] -> b+circuitP e v o c xs = foldg e v o c (circuit xs)++inspect $ 'circuitP `hasNoType` ''Graph++-- biclique+bicliqueCP :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Build a -> Build a -> b+bicliqueCP e v o c xs ys = foldg e v o c (biclique (build xs) (build ys))++inspect $ 'bicliqueCP `hasNoType` ''[]+inspect $ 'bicliqueCP `hasNoType` ''Graph++-- replaceVertex+replaceVertexCP :: Eq a => a -> a ->+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Buildg a -> b+replaceVertexCP u v e v' o c g =+  foldg e v' o c (replaceVertex u v (buildg g))++inspect $ 'replaceVertexCP `hasNoType` ''Graph++-- mergeVertices+mergeVerticesCP :: (a -> Bool) -> a ->+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Buildg a -> b+mergeVerticesCP p v e v' o c g =+  foldg e v' o c (mergeVertices p v (buildg g))++inspect $ 'mergeVerticesCP `hasNoType` ''Graph++-- splitVertex+splitVertexCP :: Eq a => a -> Build a ->+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Buildg a -> b+splitVertexCP x us e v o c g = foldg e v o c (splitVertex x (build us) (buildg g))++inspect $ 'splitVertexCP `hasNoType` ''[]+inspect $ 'splitVertexCP `hasNoType` ''Graph++-- transpose+transposeCP ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Buildg a -> b+transposeCP e v o c g = foldg e v o c (transpose (buildg g))++inspect $ 'transposeCP `hasNoType` ''Graph++-- simplify+simple :: Eq g => (g -> g -> g) -> g -> g -> g+simple op x y+    | x == z    = x+    | y == z    = y+    | otherwise = z+  where+    z = op x y++simplifyC, simplifyCR :: Ord a => Buildg a -> Graph a+simplifyC  g = simplify (buildg g)+simplifyCR g = g Empty Vertex (simple Overlay) (simple Connect)++inspect $ 'simplifyC === 'simplifyCR++-- compose+composeCP :: Ord a => b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Buildg a -> Buildg a -> b+composeCP e v o c x y = foldg e v o c $ compose (buildg x) (buildg y)++inspect $ 'composeCP `hasNoType` ''Graph++-- induce+induceCP ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> (a -> Bool) -> Buildg a -> b+induceCP e v o c p g = foldg e v o c (induce p (buildg g))++inspect $ 'induceCP `hasNoType` ''Graph++-- induceJust+induceJustCP ::+  b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Buildg (Maybe a) -> b+induceJustCP e v o c g = foldg e v o c (induceJust (buildg g))++inspect $ 'induceJustCP `hasNoType` ''Graph++-- context+contextC :: (a -> Bool) -> Buildg a -> Maybe (Context a)+contextC p g = context p (buildg g)++inspect $ 'contextC `hasNoType` ''Graph
+ test/Algebra/Graph/Test/Undirected.hs view
@@ -0,0 +1,90 @@+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test.Undirected+-- Copyright  : (c) Andrey Mokhov 2016-2020+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Testsuite for "Algebra.Graph.Undirected".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Undirected (+    -- * Testsuite+    testUndirected+    ) where++import Algebra.Graph.Undirected+import Algebra.Graph.Test+import Algebra.Graph.Test.API (toIntAPI, undirectedGraphAPI)+import Algebra.Graph.Test.Generic++import qualified Algebra.Graph as G+import qualified Algebra.Graph.Undirected as U++tPoly :: Testsuite Graph Ord+tPoly = ("Graph.Undirected.", undirectedGraphAPI)++t :: TestsuiteInt Graph+t = fmap toIntAPI tPoly++type G = Graph Int+type UGI = U.Graph Int+type AGI = G.Graph Int++testUndirected :: IO ()+testUndirected = do+    putStrLn "\n============ Graph.Undirected ============"+    test "Axioms of undirected graphs" $ size10 $ undirectedAxioms @ G++    testSymmetricShow t++    putStrLn $ "\n============ Graph.Undirected.toUndirected ============"+    test "toUndirected (edge 1 2)         == edge 1 2" $+          toUndirected (G.edge 1 2)       == edge 1 (2 :: Int)++    test "toUndirected . fromUndirected   == id" $ \(x :: G) ->+          (toUndirected . fromUndirected) x == id x++    test "vertexCount      . toUndirected == vertexCount" $ \(x :: AGI) ->+          vertexCount (toUndirected x) == G.vertexCount x++    test "(*2) . edgeCount . toUndirected >= edgeCount" $ \(x :: AGI) ->+          ((*2) . edgeCount . toUndirected) x >= G.edgeCount x++    putStrLn $ "\n============ Graph.Undirected.fromUndirected ============"+    test "fromUndirected (edge 1 2)    == edges [(1,2),(2,1)]" $+          fromUndirected (edge 1 2)    == G.edges [(1,2), (2,1 :: Int)]++    test "toUndirected . fromUndirected == id" $ \(x :: G) ->+          (toUndirected . fromUndirected) x == id x++    test "vertexCount . fromUndirected == vertexCount" $ \(x :: G) ->+          (G.vertexCount . fromUndirected) x == vertexCount x++    test "edgeCount   . fromUndirected <= (*2) . edgeCount" $ \(x :: G) ->+          (G.edgeCount . fromUndirected) x <= ((*2) . edgeCount) x++    putStrLn $ "\n============ Graph.Undirected.complement ================"+    test "complement empty              == empty" $+          complement (empty :: UGI)     == empty++    test "complement (vertex x)         == vertex x" $ \x ->+          complement (vertex x :: UGI)  == vertex x++    test "complement (edge 1 1)         == edge 1 1" $+          complement (edge 1 1)         == edge 1 (1 :: Int)++    test "complement (edge 1 2)         == vertices [1, 2]" $+          complement (edge 1 2 :: UGI)  == vertices [1, 2]++    test "complement (star 1 [2, 3])    == overlay (vertex 1) (edge 2 3)" $+          complement (star 1 [2, 3])    == overlay (vertex 1) (edge 2 3 :: UGI)++    test "complement . complement       == id" $ \(x :: UGI) ->+         (complement . complement $ x)  == x++    testSymmetricBasicPrimitives t+    testSymmetricIsSubgraphOf    t+    testSymmetricGraphFamilies   t+    testSymmetricTransformations t+    testInduceJust               tPoly
test/Data/Graph/Test/Typed.hs view
@@ -11,7 +11,7 @@ module Data.Graph.Test.Typed (     -- * Testsuite     testTyped-  ) where+    ) where  import qualified Algebra.Graph.AdjacencyMap as AM import qualified Algebra.Graph.AdjacencyIntMap as AIM@@ -26,6 +26,7 @@  type AI = AM.AdjacencyMap Int +-- TODO: Improve the alignment in the testsuite to match the documentation. (%) :: (GraphKL Int -> a) -> AM.AdjacencyMap Int -> a a % g = a $ fromAdjacencyMap g @@ -149,8 +150,8 @@     test "dfs []     % x                         == []" $ \x ->           dfs []     % x                         == [] -    test "dfs [1, 4] % 3 * (1 + 4) * (1 + 5)     == [1, 5, 4]" $-          dfs [1, 4] % (3 * (1 + 4) * (1 + 5))   == [1, 5, 4]+    test "dfs [1, 4] % 3 * (1 + 4) * (1 + 5)     == [1,5,4]" $+          dfs [1, 4] % (3 * (1 + 4) * (1 + 5))   == [1,5,4]      test "isSubgraphOf (vertices $ dfs vs % x) x == True" $ \vs x ->           AM.isSubgraphOf (AM.vertices $ dfs vs % x) x == True
test/Main.hs view
@@ -1,15 +1,18 @@+import Algebra.Graph.Test.Acyclic.AdjacencyMap import Algebra.Graph.Test.AdjacencyIntMap import Algebra.Graph.Test.AdjacencyMap-import Algebra.Graph.Test.NonEmpty.AdjacencyMap+import Algebra.Graph.Test.Bipartite.Undirected.AdjacencyMap import Algebra.Graph.Test.Export-import Algebra.Graph.Test.Fold import Algebra.Graph.Test.Graph-import Algebra.Graph.Test.NonEmpty.Graph import Algebra.Graph.Test.Internal+import Algebra.Graph.Test.Label import Algebra.Graph.Test.Labelled.AdjacencyMap import Algebra.Graph.Test.Labelled.Graph+import Algebra.Graph.Test.NonEmpty.AdjacencyMap+import Algebra.Graph.Test.NonEmpty.Graph import Algebra.Graph.Test.Relation import Algebra.Graph.Test.Relation.SymmetricRelation+import Algebra.Graph.Test.Undirected import Data.Graph.Test.Typed  import Control.Monad@@ -19,23 +22,26 @@ -- you would like to execute only some specific testsuites, you can specify -- their names in the command line. For example: ----- stack test --test-arguments "Graph SymmetricRelation"+-- > stack test --test-arguments "Graph Symmetric.Relation" -- -- will test the modules "Algebra.Graph" and "Algebra.Graph.Symmetric.Relation". main :: IO () main = do     selected <- getArgs     let go current = when (null selected || current `elem` selected)-    go "AdjacencyIntMap"      testAdjacencyIntMap-    go "AdjacencyMap"         testAdjacencyMap-    go "Export"               testExport-    go "Fold"                 testFold-    go "Graph"                testGraph-    go "Internal"             testInternal-    go "LabelledAdjacencyMap" testLabelledAdjacencyMap-    go "LabelledGraph"        testLabelledGraph-    go "NonEmptyAdjacencyMap" testNonEmptyAdjacencyMap-    go "NonEmptyGraph"        testNonEmptyGraph-    go "Relation"             testRelation-    go "SymmetricRelation"    testSymmetricRelation-    go "Typed"                testTyped+    go "Acyclic.AdjacencyMap"              testAcyclicAdjacencyMap+    go "AdjacencyIntMap"                   testAdjacencyIntMap+    go "AdjacencyMap"                      testAdjacencyMap+    go "Bipartite.Undirected.AdjacencyMap" testBipartiteUndirectedAdjacencyMap+    go "Export"                            testExport+    go "Graph"                             testGraph+    go "Internal"                          testInternal+    go "Label"                             testLabel+    go "Labelled.AdjacencyMap"             testLabelledAdjacencyMap+    go "Labelled.Graph"                    testLabelledGraph+    go "NonEmpty.AdjacencyMap"             testNonEmptyAdjacencyMap+    go "NonEmpty.Graph"                    testNonEmptyGraph+    go "Relation"                          testRelation+    go "Symmetric.Relation"                testSymmetricRelation+    go "Typed"                             testTyped+    go "Undirected"                        testUndirected