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 +23/−0
- CHANGES.md +23/−0
- LICENSE +1/−1
- algebraic-graphs.cabal +52/−79
- src/Algebra/Graph.hs +278/−140
- src/Algebra/Graph/Acyclic/AdjacencyMap.hs +543/−0
- src/Algebra/Graph/AdjacencyIntMap.hs +202/−18
- src/Algebra/Graph/AdjacencyIntMap/Algorithm.hs +190/−30
- src/Algebra/Graph/AdjacencyIntMap/Internal.hs +0/−206
- src/Algebra/Graph/AdjacencyMap.hs +238/−16
- src/Algebra/Graph/AdjacencyMap/Algorithm.hs +285/−43
- src/Algebra/Graph/AdjacencyMap/Internal.hs +0/−207
- src/Algebra/Graph/Bipartite/Undirected/AdjacencyMap.hs +836/−0
- src/Algebra/Graph/Class.hs +11/−13
- src/Algebra/Graph/Export.hs +3/−5
- src/Algebra/Graph/Export/Dot.hs +1/−1
- src/Algebra/Graph/Fold.hs +0/−736
- src/Algebra/Graph/HigherKinded/Class.hs +4/−16
- src/Algebra/Graph/Internal.hs +49/−24
- src/Algebra/Graph/Label.hs +32/−23
- src/Algebra/Graph/Labelled.hs +34/−14
- src/Algebra/Graph/Labelled/AdjacencyMap.hs +104/−20
- src/Algebra/Graph/Labelled/AdjacencyMap/Internal.hs +0/−115
- src/Algebra/Graph/Labelled/Example/Automaton.hs +1/−12
- src/Algebra/Graph/NonEmpty.hs +37/−27
- src/Algebra/Graph/NonEmpty/AdjacencyMap.hs +208/−62
- src/Algebra/Graph/NonEmpty/AdjacencyMap/Internal.hs +0/−165
- src/Algebra/Graph/Relation.hs +257/−12
- src/Algebra/Graph/Relation/Internal.hs +0/−250
- src/Algebra/Graph/Relation/InternalDerived.hs +0/−130
- src/Algebra/Graph/Relation/Preorder.hs +50/−3
- src/Algebra/Graph/Relation/Reflexive.hs +37/−3
- src/Algebra/Graph/Relation/Symmetric.hs +237/−57
- src/Algebra/Graph/Relation/Symmetric/Internal.hs +0/−215
- src/Algebra/Graph/Relation/Transitive.hs +43/−3
- src/Algebra/Graph/ToGraph.hs +69/−101
- src/Algebra/Graph/Undirected.hs +819/−0
- src/Data/Graph/Typed.hs +95/−53
- test/Algebra/Graph/Test.hs +43/−32
- test/Algebra/Graph/Test/API.hs +633/−280
- test/Algebra/Graph/Test/Acyclic/AdjacencyMap.hs +502/−0
- test/Algebra/Graph/Test/AdjacencyIntMap.hs +14/−5
- test/Algebra/Graph/Test/AdjacencyMap.hs +20/−5
- test/Algebra/Graph/Test/Arbitrary.hs +81/−73
- test/Algebra/Graph/Test/Bipartite/Undirected/AdjacencyMap.hs +628/−0
- test/Algebra/Graph/Test/Export.hs +3/−9
- test/Algebra/Graph/Test/Fold.hs +0/−40
- test/Algebra/Graph/Test/Generic.hs +2069/−1833
- test/Algebra/Graph/Test/Graph.hs +35/−103
- test/Algebra/Graph/Test/Internal.hs +4/−12
- test/Algebra/Graph/Test/Label.hs +143/−0
- test/Algebra/Graph/Test/Labelled/AdjacencyMap.hs +10/−6
- test/Algebra/Graph/Test/Labelled/Graph.hs +8/−3
- test/Algebra/Graph/Test/NonEmpty/AdjacencyMap.hs +33/−21
- test/Algebra/Graph/Test/NonEmpty/Graph.hs +25/−17
- test/Algebra/Graph/Test/Relation.hs +16/−11
- test/Algebra/Graph/Test/Relation/SymmetricRelation.hs +12/−8
- test/Algebra/Graph/Test/RewriteRules.hs +315/−76
- test/Algebra/Graph/Test/Undirected.hs +90/−0
- test/Data/Graph/Test/Typed.hs +4/−3
- test/Main.hs +23/−17
+ 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