algebraic-graphs-0.4: test/Algebra/Graph/Test/Arbitrary.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Module : Algebra.Graph.Test.Arbitrary
-- Copyright : (c) Andrey Mokhov 2016-2018
-- License : MIT (see the file LICENSE)
-- Maintainer : andrey.mokhov@gmail.com
-- Stability : experimental
--
-- Generators and orphan Arbitrary instances for various data types.
-----------------------------------------------------------------------------
module Algebra.Graph.Test.Arbitrary (
-- * Generators of arbitrary graph instances
arbitraryGraph, arbitraryRelation, arbitraryAdjacencyMap, arbitraryAdjacencyIntMap
) where
import Prelude ()
import Prelude.Compat
import Control.Monad
import Data.List.NonEmpty (NonEmpty (..), toList)
import Data.Maybe (catMaybes)
import Data.Tree
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
-- | Generate an arbitrary 'C.Graph' value of a specified size.
arbitraryGraph :: (C.Graph g, Arbitrary (C.Vertex g)) => Gen g
arbitraryGraph = sized expr
where
expr 0 = return C.empty
expr 1 = C.vertex <$> arbitrary
expr n = do
left <- choose (0, n)
oneof [ C.overlay <$> expr left <*> expr (n - left)
, C.connect <$> expr left <*> expr (n - left) ]
instance Arbitrary a => Arbitrary (Graph a) where
arbitrary = arbitraryGraph
shrink Empty = []
shrink (Vertex _) = [Empty]
shrink (Overlay x y) = [Empty, x, y]
++ [Overlay x' y' | (x', y') <- shrink (x, y) ]
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
arbitrary = arbitraryGraph
shrink g = oneLessVertex ++ oneLessEdge
where
oneLessVertex =
let vertices = Fold.vertexList g
in [ Fold.removeVertex v g | v <- vertices ]
oneLessEdge =
let edges = Fold.edgeList g
in [ Fold.removeEdge v w g | (v, w) <- edges ]
-- | Generate an arbitrary 'NonEmpty.Graph' value of a specified size.
arbitraryNonEmptyGraph :: Arbitrary a => Gen (NonEmpty.Graph a)
arbitraryNonEmptyGraph = sized expr
where
expr 0 = NonEmpty.vertex <$> arbitrary -- can't generate non-empty graph of size 0
expr 1 = NonEmpty.vertex <$> arbitrary
expr n = do
left <- choose (1, n)
oneof [ NonEmpty.overlay <$> expr left <*> expr (n - left)
, NonEmpty.connect <$> expr left <*> expr (n - left) ]
instance Arbitrary a => Arbitrary (NonEmpty.Graph a) where
arbitrary = arbitraryNonEmptyGraph
shrink (NonEmpty.Vertex _) = []
shrink (NonEmpty.Overlay x y) = [x, y]
++ [NonEmpty.Overlay x' y' | (x', y') <- shrink (x, y) ]
shrink (NonEmpty.Connect x y) = [x, y, NonEmpty.Overlay x y]
++ [NonEmpty.Connect x' y' | (x', y') <- shrink (x, y) ]
-- | Generate an arbitrary 'Relation'.
arbitraryRelation :: (Arbitrary a, Ord a) => Gen (Relation.Relation a)
arbitraryRelation = Relation.stars <$> arbitrary
-- TODO: Implement a custom shrink method.
instance (Arbitrary a, Ord a) => Arbitrary (Relation.Relation a) where
arbitrary = arbitraryRelation
shrink g = oneLessVertex ++ oneLessEdge
where
oneLessVertex =
let vertices = Relation.vertexList g
in [ Relation.removeVertex v g | v <- vertices ]
oneLessEdge =
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
instance (Arbitrary a, Ord a) => Arbitrary (Symmetric.Relation a) where
arbitrary = SR . Relation.symmetricClosure <$> arbitraryRelation
instance (Arbitrary a, Ord a) => Arbitrary (TransitiveRelation a) where
arbitrary = TransitiveRelation <$> arbitraryRelation
instance (Arbitrary a, Ord a) => Arbitrary (PreorderRelation a) where
arbitrary = PreorderRelation <$> 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
instance (Arbitrary a, Ord a) => Arbitrary (AdjacencyMap a) where
arbitrary = arbitraryAdjacencyMap
shrink g = oneLessVertex ++ oneLessEdge
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 ]
-- | Generate an arbitrary non-empty 'NAM.AdjacencyMap'. It is guaranteed that
-- the resulting adjacency map is 'consistent'.
arbitraryNonEmptyAdjacencyMap :: (Arbitrary a, Ord a) => Gen (NAM.AdjacencyMap a)
arbitraryNonEmptyAdjacencyMap = NAM.stars1 <$> nonEmpty
where
nonEmpty = do
xs <- arbitrary
case xs of
[] -> do
x <- arbitrary
return ((x, []) :| []) -- There must be at least one vertex
(x:xs) -> return (x :| xs)
instance (Arbitrary a, Ord a) => Arbitrary (NAM.AdjacencyMap a) where
arbitrary = arbitraryNonEmptyAdjacencyMap
shrink g = oneLessVertex ++ oneLessEdge
where
oneLessVertex =
let vertices = toList $ NAM.vertexList1 g
in catMaybes [ NAM.removeVertex1 v g | v <- vertices ]
oneLessEdge =
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
shrink g = oneLessVertex ++ oneLessEdge
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 ]
-- | Generate an arbitrary labelled 'LAM.AdjacencyMap'. It is guaranteed
-- that the resulting adjacency map is 'consistent'.
arbitraryLabelledAdjacencyMap :: (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Gen (LAM.AdjacencyMap e a)
arbitraryLabelledAdjacencyMap = LAM.fromAdjacencyMaps <$> arbitrary
instance (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Arbitrary (LAM.AdjacencyMap e a) where
arbitrary = arbitraryLabelledAdjacencyMap
shrink g = oneLessVertex ++ oneLessEdge
where
oneLessVertex =
let vertices = LAM.vertexList g
in [ LAM.removeVertex v g | v <- vertices ]
oneLessEdge =
let edges = LAM.edgeList g
in [ LAM.removeEdge v w g | (_, v, w) <- edges ]
-- | Generate an arbitrary labelled 'LAM.Graph' value of a specified size.
arbitraryLabelledGraph :: (Arbitrary a, Arbitrary e) => Gen (LG.Graph e a)
arbitraryLabelledGraph = sized expr
where
expr 0 = return LG.empty
expr 1 = LG.vertex <$> arbitrary
expr n = do
label <- arbitrary
left <- choose (0, n)
LG.connect label <$> expr left <*> expr (n - left)
instance (Arbitrary a, Arbitrary e, Monoid e) => Arbitrary (LG.Graph e a) where
arbitrary = arbitraryLabelledGraph
shrink LG.Empty = []
shrink (LG.Vertex _) = [LG.Empty]
shrink (LG.Connect e x y) = [LG.Empty, x, y, LG.Connect mempty x y]
++ [LG.Connect e x' y' | (x', y') <- shrink (x, y) ]
instance Arbitrary a => Arbitrary (Tree a) where
arbitrary = sized go
where
go 0 = do
root <- arbitrary
return $ Node root []
go n = do
subTrees <- choose (0, n - 1)
let subSize = (n - 1) `div` subTrees
root <- arbitrary
children <- replicateM subTrees (go subSize)
return $ Node root children
shrink (Node r fs) = [Node r fs' | fs' <- shrink fs]
-- TODO: Implement a custom shrink method.
instance Arbitrary s => Arbitrary (Doc s) where
arbitrary = mconcat . map literal <$> arbitrary
instance (Arbitrary a, Num a, Ord a) => Arbitrary (Distance a) where
arbitrary = (\x -> if x < 0 then distance infinite else distance (unsafeFinite x)) <$> arbitrary