-----------------------------------------------------------------------------
-- |
-- Module     : Algebra.Graph.Relation.Symmetric
-- Copyright  : (c) Andrey Mokhov 2016-2022
-- License    : MIT (see the file LICENSE)
-- Maintainer : andrey.mokhov@gmail.com
-- Stability  : experimental
--
-- An abstract implementation of symmetric binary relations. To avoid name
-- clashes with "Algebra.Graph.Relation", this module can be imported qualified:
--
-- @
-- import qualified Algebra.Graph.Relation.Symmetric as Symmetric
-- @
--
-- 'Relation' is an instance of the 'Algebra.Graph.Class.Graph' type
-- class, which can be used for polymorphic graph construction and manipulation.
-----------------------------------------------------------------------------
module Algebra.Graph.Relation.Symmetric (
    -- * Data structure
    Relation, toSymmetric, fromSymmetric,

    -- * Basic graph construction primitives
    empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,

    -- * Relations on graphs
    isSubgraphOf,

    -- * Graph properties
    isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,
    adjacencyList, vertexSet, edgeSet, neighbours,

    -- * Standard families of graphs
    path, circuit, clique, biclique, star, stars, tree, forest,

    -- * Graph transformation
    removeVertex, removeEdge, replaceVertex, mergeVertices, gmap, induce, induceJust,

    -- * Miscellaneous
    consistent

    ) where

import Control.DeepSeq
import Data.Coerce
import Data.Set (Set)
import Data.String
import Data.Tree

import qualified Data.Set as Set

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'
    -- @
    Relation a -> Relation a
fromSymmetric :: R.Relation a
    } deriving (Relation a -> Relation a -> Bool
(Relation a -> Relation a -> Bool)
-> (Relation a -> Relation a -> Bool) -> Eq (Relation a)
forall a. Eq a => Relation a -> Relation a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Relation a -> Relation a -> Bool
$c/= :: forall a. Eq a => Relation a -> Relation a -> Bool
== :: Relation a -> Relation a -> Bool
$c== :: forall a. Eq a => Relation a -> Relation a -> Bool
Eq, String -> Relation a
(String -> Relation a) -> IsString (Relation a)
forall a. IsString a => String -> Relation a
forall a. (String -> a) -> IsString a
fromString :: String -> Relation a
$cfromString :: forall a. IsString a => String -> Relation a
IsString, Relation a -> ()
(Relation a -> ()) -> NFData (Relation a)
forall a. NFData a => Relation a -> ()
forall a. (a -> ()) -> NFData a
rnf :: Relation a -> ()
$crnf :: forall a. NFData a => Relation a -> ()
NFData)

instance (Ord a, Show a) => Show (Relation a) where
    show :: Relation a -> String
show = Relation a -> String
forall a. Show a => a -> String
show (Relation a -> String)
-> (Relation a -> Relation a) -> Relation a -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toRelation
      where
        toRelation :: Relation a -> Relation a
toRelation Relation a
r = [a] -> Relation a
forall a. Ord a => [a] -> Relation a
R.vertices (Relation a -> [a]
forall a. Relation a -> [a]
vertexList Relation a
r) Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
`R.overlay` [(a, a)] -> Relation a
forall a. Ord a => [(a, a)] -> Relation a
R.edges (Relation a -> [(a, a)]
forall a. Ord a => Relation a -> [(a, a)]
edgeList Relation a
r)

instance Ord a => Ord (Relation a) where
    compare :: Relation a -> Relation a -> Ordering
compare Relation a
x Relation a
y = [Ordering] -> Ordering
forall a. Monoid a => [a] -> a
mconcat
        [ Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Relation a -> Int
forall a. Relation a -> Int
vertexCount Relation a
x) (Relation a -> Int
forall a. Relation a -> Int
vertexCount  Relation a
y)
        , Set a -> Set a -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Relation a -> Set a
forall a. Relation a -> Set a
vertexSet   Relation a
x) (Relation a -> Set a
forall a. Relation a -> Set a
vertexSet    Relation a
y)
        , Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Relation a -> Int
forall a. Ord a => Relation a -> Int
edgeCount   Relation a
x) (Relation a -> Int
forall a. Ord a => Relation a -> Int
edgeCount    Relation a
y)
        , Set (a, a) -> Set (a, a) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Relation a -> Set (a, a)
forall a. Ord a => Relation a -> Set (a, a)
edgeSet     Relation a
x) (Relation a -> Set (a, a)
forall a. Ord a => Relation a -> Set (a, a)
edgeSet      Relation a
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 :: Integer -> Relation a
fromInteger = a -> Relation a
forall a. a -> Relation a
vertex (a -> Relation a) -> (Integer -> a) -> Integer -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> a
forall a. Num a => Integer -> a
fromInteger
    + :: Relation a -> Relation a -> Relation a
(+)         = Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
overlay
    * :: Relation a -> Relation a -> Relation a
(*)         = Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
connect
    signum :: Relation a -> Relation a
signum      = Relation a -> Relation a -> Relation a
forall a b. a -> b -> a
const Relation a
forall a. Relation a
empty
    abs :: Relation a -> Relation a
abs         = Relation a -> Relation a
forall a. a -> a
id
    negate :: Relation a -> Relation a
negate      = Relation a -> Relation a
forall a. a -> a
id

-- | Defined via 'overlay'.
instance Ord a => Semigroup (Relation a) where
    <> :: Relation a -> Relation a -> Relation a
(<>) = Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
overlay

-- | Defined via 'overlay' and 'empty'.
instance Ord a => Monoid (Relation a) where
    mempty :: Relation a
mempty = Relation a
forall a. Relation a
empty

-- | Construct a symmetric relation from a given "Algebra.Graph.Relation".
-- Complexity: /O(m*log(m))/ time.
--
-- @
-- toSymmetric ('Algebra.Graph.Relation.edge' 1 2)         == 'edge' 1 2
-- toSymmetric . 'fromSymmetric'    == id
-- 'fromSymmetric'    . toSymmetric == 'Algebra.Graph.Relation.symmetricClosure'
-- 'vertexCount'      . toSymmetric == 'Algebra.Graph.Relation.vertexCount'
-- (*2) . 'edgeCount' . toSymmetric >= 'Algebra.Graph.Relation.edgeCount'
-- @
toSymmetric :: Ord a => R.Relation a -> Relation a
toSymmetric :: Relation a -> Relation a
toSymmetric = Relation a -> Relation a
forall a. Relation a -> Relation a
SR (Relation a -> Relation a)
-> (Relation a -> Relation a) -> Relation a -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
R.symmetricClosure

-- | Construct the /empty graph/.
--
-- @
-- 'isEmpty'     empty == True
-- 'hasVertex' x empty == False
-- 'vertexCount' empty == 0
-- 'edgeCount'   empty == 0
-- @
empty :: Relation a
empty :: Relation a
empty = Relation a -> Relation a
coerce Relation a
forall a. Relation a
R.empty

-- | Construct the graph comprising /a single isolated vertex/.
--
-- @
-- 'isEmpty'     (vertex x) == False
-- 'hasVertex' x (vertex y) == (x == y)
-- 'vertexCount' (vertex x) == 1
-- 'edgeCount'   (vertex x) == 0
-- @
vertex :: a -> Relation a
vertex :: a -> Relation a
vertex = (a -> Relation a) -> a -> Relation a
coerce a -> Relation a
forall a. a -> Relation a
R.vertex

-- | Construct the graph comprising /a single edge/.
--
-- @
-- 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 :: Ord a => a -> a -> Relation a
edge :: a -> a -> Relation a
edge a
x a
y = Relation a -> Relation a
forall a. Relation a -> Relation a
SR (Relation a -> Relation a) -> Relation a -> Relation a
forall a b. (a -> b) -> a -> b
$ [(a, a)] -> Relation a
forall a. Ord a => [(a, a)] -> Relation a
R.edges [(a
x,a
y), (a
y,a
x)]

-- | /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 :: Relation a -> Relation a -> Relation a
overlay = (Relation a -> Relation a -> Relation a)
-> Relation a -> Relation a -> Relation a
coerce Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
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 :: Relation a -> Relation a -> Relation a
connect Relation a
x Relation a
y = (Relation a -> Relation a -> Relation a)
-> Relation a -> Relation a -> Relation a
coerce Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
R.connect Relation a
x Relation a
y Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
`overlay` [a] -> [a] -> Relation a
forall a. Ord a => [a] -> [a] -> Relation a
biclique (Relation a -> [a]
forall a. Relation a -> [a]
vertexList Relation a
y) (Relation a -> [a]
forall a. Relation a -> [a]
vertexList Relation a
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
-- of the given list.
--
-- @
-- vertices []            == 'empty'
-- vertices [x]           == 'vertex' x
-- vertices               == 'overlays' . map 'vertex'
-- 'hasVertex' x . vertices == 'elem' x
-- 'vertexCount' . vertices == 'length' . 'Data.List.nub'
-- 'vertexSet'   . vertices == Set.'Set.fromList'
-- @
vertices :: Ord a => [a] -> Relation a
vertices :: [a] -> Relation a
vertices = ([a] -> Relation a) -> [a] -> Relation a
coerce [a] -> Relation a
forall a. Ord a => [a] -> Relation a
R.vertices

-- TODO: Optimise by avoiding multiple list traversal.
-- | 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 [(x,y), (y,x)] == 'edge' x y
-- @
edges :: Ord a => [(a, a)] -> Relation a
edges :: [(a, a)] -> Relation a
edges = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> ([(a, a)] -> Relation a) -> [(a, a)] -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(a, a)] -> Relation a
forall a. Ord a => [(a, a)] -> Relation a
R.edges

-- | 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 => [Relation a] -> Relation a
overlays :: [Relation a] -> Relation a
overlays = ([Relation a] -> Relation a) -> [Relation a] -> Relation a
coerce [Relation a] -> Relation a
forall a. Ord a => [Relation a] -> Relation a
R.overlays

-- | 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           == connects . 'reverse'
-- @
connects :: Ord a => [Relation a] -> Relation a
connects :: [Relation a] -> Relation a
connects = (Relation a -> Relation a -> Relation a)
-> Relation a -> [Relation a] -> Relation a
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Relation a -> Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a -> Relation a
connect Relation a
forall a. Relation a
empty

-- | 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 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 => Relation a -> Relation a -> Bool
isSubgraphOf :: Relation a -> Relation a -> Bool
isSubgraphOf = (Relation a -> Relation a -> Bool)
-> Relation a -> Relation a -> Bool
coerce Relation a -> Relation a -> Bool
forall a. Ord a => Relation a -> Relation a -> Bool
R.isSubgraphOf

-- | Check if a relation is empty.
-- Complexity: /O(1)/ 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 :: Relation a -> Bool
isEmpty :: Relation a -> Bool
isEmpty = (Relation a -> Bool) -> Relation a -> Bool
coerce Relation a -> Bool
forall a. Relation a -> Bool
R.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 -> Relation a -> Bool
hasVertex :: a -> Relation a -> Bool
hasVertex = (a -> Relation a -> Bool) -> a -> Relation a -> Bool
coerce a -> Relation a -> Bool
forall a. Ord a => a -> Relation a -> Bool
R.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 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 :: Ord a => a -> a -> Relation a -> Bool
hasEdge :: a -> a -> Relation a -> Bool
hasEdge = (a -> a -> Relation a -> Bool) -> a -> a -> Relation a -> Bool
coerce a -> a -> Relation a -> Bool
forall a. Ord a => a -> a -> Relation a -> Bool
R.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 :: Relation a -> Int
vertexCount :: Relation a -> Int
vertexCount = (Relation a -> Int) -> Relation a -> Int
coerce Relation a -> Int
forall a. Relation a -> Int
R.vertexCount

-- | The number of edges in a graph.
-- Complexity: /O(1)/ time.
--
-- @
-- edgeCount 'empty'      == 0
-- edgeCount ('vertex' x) == 0
-- edgeCount ('edge' x y) == 1
-- edgeCount            == 'length' . 'edgeList'
-- @
edgeCount :: Ord a => Relation a -> Int
edgeCount :: Relation a -> Int
edgeCount = Set (a, a) -> Int
forall a. Set a -> Int
Set.size (Set (a, a) -> Int)
-> (Relation a -> Set (a, a)) -> Relation a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Set (a, a)
forall a. Ord a => Relation a -> Set (a, a)
edgeSet

-- | 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 :: Relation a -> [a]
vertexList :: Relation a -> [a]
vertexList = (Relation a -> [a]) -> Relation a -> [a]
coerce Relation a -> [a]
forall a. Relation a -> [a]
R.vertexList

-- | The sorted list of edges of a graph, where edge vertices appear in the
-- non-decreasing order.
-- Complexity: /O(n + m)/ time and /O(m)/ memory.
--
-- Note: If you need the sorted list of edges where an edge appears in both
-- directions, use @'Algebra.Graph.Relation.edgeList' . 'fromSymmetric'@.
--
-- @
-- 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 => Relation a -> [(a, a)]
edgeList :: Relation a -> [(a, a)]
edgeList = Set (a, a) -> [(a, a)]
forall a. Set a -> [a]
Set.toAscList (Set (a, a) -> [(a, a)])
-> (Relation a -> Set (a, a)) -> Relation a -> [(a, a)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Set (a, a)
forall a. Ord a => Relation a -> Set (a, a)
edgeSet

-- | The set of vertices of a given graph.
-- Complexity: /O(1)/ time.
--
-- @
-- vertexSet 'empty'      == Set.'Set.empty'
-- vertexSet . 'vertex'   == Set.'Set.singleton'
-- vertexSet . 'vertices' == Set.'Set.fromList'
-- @
vertexSet :: Relation a -> Set a
vertexSet :: Relation a -> Set a
vertexSet = (Relation a -> Set a) -> Relation a -> Set a
coerce Relation a -> Set a
forall a. Relation a -> Set a
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 :: Relation a -> Set (a, a)
edgeSet = ((a, a) -> Bool) -> Set (a, a) -> Set (a, a)
forall a. (a -> Bool) -> Set a -> Set a
Set.filter ((a -> a -> Bool) -> (a, a) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry a -> a -> Bool
forall a. Ord a => a -> a -> Bool
(<=)) (Set (a, a) -> Set (a, a))
-> (Relation a -> Set (a, a)) -> Relation a -> Set (a, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Set (a, a)
forall a. Relation a -> Set (a, a)
R.edgeSet (Relation a -> Set (a, a))
-> (Relation a -> Relation a) -> Relation a -> Set (a, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Relation a
forall a. Relation a -> Relation a
fromSymmetric

-- | The sorted /adjacency list/ of a graph.
-- Complexity: /O(n + m)/ time and 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 :: Eq a => Relation a -> [(a, [a])]
adjacencyList :: Relation a -> [(a, [a])]
adjacencyList = (Relation a -> [(a, [a])]) -> Relation a -> [(a, [a])]
coerce Relation a -> [(a, [a])]
forall a. Eq a => Relation a -> [(a, [a])]
R.adjacencyList

-- | The /path/ on a list of vertices.
-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.
--
-- @
-- path []    == 'empty'
-- path [x]   == 'vertex' x
-- path [x,y] == 'edge' x y
-- path       == path . 'reverse'
-- @
path :: Ord a => [a] -> Relation a
path :: [a] -> Relation a
path = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> ([a] -> Relation a) -> [a] -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Relation a
forall a. Ord a => [a] -> Relation a
R.path

-- | The /circuit/ on a list of vertices.
-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.
--
-- @
-- circuit []    == 'empty'
-- circuit [x]   == 'edge' x x
-- circuit [x,y] == 'edge' x y
-- circuit       == circuit . 'reverse'
-- @
circuit :: Ord a => [a] -> Relation a
circuit :: [a] -> Relation a
circuit = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> ([a] -> Relation a) -> [a] -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Relation a
forall a. Ord a => [a] -> Relation a
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 and /O(n + m)/ memory.
--
-- @
-- 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            == clique . 'reverse'
-- @
clique :: Ord a => [a] -> Relation a
clique :: [a] -> Relation a
clique = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> ([a] -> Relation a) -> [a] -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Relation a
forall a. Ord a => [a] -> Relation a
R.clique

-- TODO: Optimise by avoiding the call to 'R.symmetricClosure'.
-- | The /biclique/ on two lists of vertices.
-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.
--
-- @
-- 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 :: Ord a => [a] -> [a] -> Relation a
biclique :: [a] -> [a] -> Relation a
biclique [a]
xs [a]
ys = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric ([a] -> [a] -> Relation a
forall a. Ord a => [a] -> [a] -> Relation a
R.biclique [a]
xs [a]
ys)

-- TODO: Optimise.
-- | The /star/ formed by a centre vertex connected to a list of leaves.
-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.
--
-- @
-- 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 :: Ord a => a -> [a] -> Relation a
star :: a -> [a] -> Relation a
star a
x = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> ([a] -> Relation a) -> [a] -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> [a] -> Relation a
forall a. Ord a => a -> [a] -> Relation a
R.star a
x

-- | The /stars/ formed by overlaying a list of 'star's. An inverse of
-- 'adjacencyList'.
-- Complexity: /O(L * log(n))/ 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 :: Ord a => [(a, [a])] -> Relation a
stars :: [(a, [a])] -> Relation a
stars = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> ([(a, [a])] -> Relation a) -> [(a, [a])] -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(a, [a])] -> Relation a
forall a. Ord a => [(a, [a])] -> Relation a
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.
--
-- @
-- 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 :: Ord a => Tree a -> Relation a
tree :: Tree a -> Relation a
tree = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> (Tree a -> Relation a) -> Tree a -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Tree a -> Relation a
forall a. Ord a => Tree a -> Relation a
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.
--
-- @
-- 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 :: Ord a => Forest a -> Relation a
forest :: Forest a -> Relation a
forest = Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
toSymmetric (Relation a -> Relation a)
-> (Forest a -> Relation a) -> Forest a -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Forest a -> Relation a
forall a. Ord a => Forest a -> Relation a
R.forest

-- | Remove a vertex from a given graph.
-- Complexity: /O(n + m)/ time.
--
-- @
-- 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 :: Ord a => a -> Relation a -> Relation a
removeVertex :: a -> Relation a -> Relation a
removeVertex = (a -> Relation a -> Relation a) -> a -> Relation a -> Relation a
coerce a -> Relation a -> Relation a
forall a. Ord a => a -> Relation a -> Relation a
R.removeVertex

-- | Remove an edge from a given graph.
-- Complexity: /O(log(m))/ time.
--
-- @
-- 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 :: Ord a => a -> a -> Relation a -> Relation a
removeEdge :: a -> a -> Relation a -> Relation a
removeEdge a
x a
y = Relation a -> Relation a
forall a. Relation a -> Relation a
SR (Relation a -> Relation a)
-> (Relation a -> Relation a) -> Relation a -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> a -> Relation a -> Relation a
forall a. Ord a => a -> a -> Relation a -> Relation a
R.removeEdge a
x a
y (Relation a -> Relation a)
-> (Relation a -> Relation a) -> Relation a -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> a -> Relation a -> Relation a
forall a. Ord a => a -> a -> Relation a -> Relation a
R.removeEdge a
y a
x (Relation a -> Relation a)
-> (Relation a -> Relation a) -> Relation a -> Relation a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Relation a -> Relation a
forall a. Relation a -> Relation a
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.
-- Complexity: /O((n + m) * log(n))/ time.
--
-- @
-- replaceVertex x x            == id
-- replaceVertex x y ('vertex' x) == 'vertex' y
-- replaceVertex x y            == 'mergeVertices' (== x) y
-- @
replaceVertex :: Ord a => a -> a -> Relation a -> Relation a
replaceVertex :: a -> a -> Relation a -> Relation a
replaceVertex = (a -> a -> Relation a -> Relation a)
-> a -> a -> Relation a -> Relation a
coerce a -> a -> Relation a -> Relation a
forall a. Ord a => a -> a -> Relation a -> Relation a
R.replaceVertex

-- | Merge vertices satisfying a given predicate into a given vertex.
-- Complexity: /O((n + m) * log(n))/ time, assuming that the predicate takes
-- constant time.
--
-- @
-- 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 :: Ord a => (a -> Bool) -> a -> Relation a -> Relation a
mergeVertices :: (a -> Bool) -> a -> Relation a -> Relation a
mergeVertices = ((a -> Bool) -> a -> Relation a -> Relation a)
-> (a -> Bool) -> a -> Relation a -> Relation a
coerce (a -> Bool) -> a -> Relation a -> Relation a
forall a. Ord a => (a -> Bool) -> a -> Relation a -> Relation a
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
-- 'Relation'.
-- Complexity: /O((n + m) * log(n))/ time.
--
-- @
-- gmap f 'empty'      == 'empty'
-- gmap f ('vertex' x) == 'vertex' (f x)
-- gmap f ('edge' x y) == 'edge' (f x) (f y)
-- gmap id           == id
-- gmap f . gmap g   == gmap (f . g)
-- @
gmap :: Ord b => (a -> b) -> Relation a -> Relation b
gmap :: (a -> b) -> Relation a -> Relation b
gmap = ((a -> b) -> Relation a -> Relation b)
-> (a -> b) -> Relation a -> Relation b
coerce (a -> b) -> Relation a -> Relation b
forall b a. Ord b => (a -> b) -> Relation a -> Relation b
R.gmap

-- | 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 constant time.
--
-- @
-- 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) -> Relation a -> Relation a
induce :: (a -> Bool) -> Relation a -> Relation a
induce = ((a -> Bool) -> Relation a -> Relation a)
-> (a -> Bool) -> Relation a -> Relation a
coerce (a -> Bool) -> Relation a -> Relation a
forall a. (a -> Bool) -> Relation a -> Relation a
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 :: Relation (Maybe a) -> Relation a
induceJust = (Relation (Maybe a) -> Relation a)
-> Relation (Maybe a) -> Relation a
coerce Relation (Maybe a) -> Relation a
forall a. Ord a => Relation (Maybe a) -> Relation a
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 '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 :: a -> Relation a -> Set a
neighbours = (a -> Relation a -> Set a) -> a -> Relation a -> Set a
coerce a -> Relation a -> Set a
forall a. Ord a => a -> Relation a -> Set a
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 :: Relation a -> Bool
consistent (SR Relation a
r) = Relation a -> Bool
forall a. Ord a => Relation a -> Bool
R.consistent Relation a
r Bool -> Bool -> Bool
&& Relation a
r Relation a -> Relation a -> Bool
forall a. Eq a => a -> a -> Bool
== Relation a -> Relation a
forall a. Ord a => Relation a -> Relation a
R.transpose Relation a
r