{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Test.QuickCheck
import Data.Word
import Data.Monoid
import Data.DisjointSet (DisjointSet)
import Data.DisjointMap (DisjointMap)
import Data.Set (Set)
import Data.Foldable (toList)
import Test.QuickCheck.Classes (jsonProps)
import Data.Proxy (Proxy(..))
import Data.Aeson (ToJSON,FromJSON)
import qualified Data.Foldable as F
import qualified Data.Set as S
import qualified Data.DisjointSet as DS
import qualified Data.DisjointMap as DM
import qualified GHC.OldList as L
main :: IO ()
main = do
putStrLn "\nBeginning QuickCheck Tests"
quickCheck propUnionAll
quickCheck propUnionAppend
quickCheck propSingletons
quickCheck propEquivalances
quickCheck propMapUnionAppend
putStrLn "* Disjoint Set JSON"
F.forM_ (jsonProps (Proxy :: Proxy (DisjointSet Word8))) $ \(name,p) -> do
putStrLn name
quickCheck p
putStrLn "* Disjoint Map JSON"
F.forM_ (jsonProps (Proxy :: Proxy (DisjointMap Word8 WrapWord8))) $ \(name,p) -> do
putStrLn name
quickCheck p
propUnionAll :: [Word] -> Bool
propUnionAll xs =
let pairs = zip xs (drop 1 xs)
ds = L.foldl' (\s (a,b) -> DS.union a b s) DS.empty pairs
roots = mapM (\x -> DS.representative x ds) xs
in case roots of
Nothing -> L.length xs == 1
Just [] -> L.null xs
Just (y : ys) -> L.all (== y) ys
propUnionAppend :: [(Word,Word)] -> Bool
propUnionAppend xs =
let r1 = unionPairs xs
(xs1,xs2) = splitList xs
r2 = unionPairs xs1 <> unionPairs xs2
in r1 == r2
propMapUnionAppend :: [(Word,Word)] -> [(Word,Sum Word)] -> Bool
propMapUnionAppend xs ys =
let r1 = unionMapPairs xs <> mapFromPairs ys
(xs1,xs2) = splitList xs
(ys1,ys2) = splitList ys
r2 = unionMapPairs xs1 <> mapFromPairs ys1 <> unionMapPairs xs2 <> mapFromPairs ys2
in r1 == r2
propSingletons :: [Set Word] -> Bool
propSingletons xs = foldMap unionFoldable xs == foldMap DS.singletons xs
propEquivalances :: [(Word,Word)] -> Bool
propEquivalances xs =
let s = foldMap (\(a,b) -> DS.singletons (S.fromList [a,b])) xs
All r = foldMap (\(a,b) -> All $ DS.equivalences a s == DS.equivalences b s) xs
in r
splitList :: [a] -> ([a],[a])
splitList xs =
let halfLen = div (L.length xs) 2
xs1 = L.drop halfLen xs
xs2 = L.take halfLen xs
in (xs1,xs2)
unionFoldable :: Ord a => Foldable t => t a -> DisjointSet a
unionFoldable xs =
let ys = toList xs
pairs = zip ys (drop 1 ys)
in case ys of
[] -> DS.empty
z : _ -> unionPairsGo pairs (DS.singleton z)
mapFromPairs :: (Ord k, Monoid v) => Foldable t => t (k,v) -> DisjointMap k v
mapFromPairs = F.foldl' (\dm (k,v) -> DM.insert k v dm) DM.empty
unionPairs :: Ord a => [(a,a)] -> DisjointSet a
unionPairs xs = unionPairsGo xs DS.empty
unionPairsGo :: Ord a => [(a,a)] -> DisjointSet a -> DisjointSet a
unionPairsGo [] !ds = ds
unionPairsGo ((a,b):xs) !ds = unionPairsGo xs (DS.union a b ds)
unionMapPairs :: (Ord k, Monoid v) => [(k,k)] -> DisjointMap k v
unionMapPairs xs = unionMapPairsGo xs DM.empty
unionMapPairsGo :: (Ord k, Monoid v) => [(k,k)] -> DisjointMap k v -> DisjointMap k v
unionMapPairsGo [] !ds = ds
unionMapPairsGo ((a,b):xs) !ds = unionMapPairsGo xs (DM.union a b ds)
instance (Arbitrary a, Ord a) => Arbitrary (DisjointSet a) where
arbitrary = do
xs <- arbitrary
ys <- arbitrary
let s1 = foldMap (\(a,b) -> DS.doubleton a b) (xs :: [(a,a)])
s2 = foldMap DS.singleton (ys :: [a])
return (s1 <> s2)
instance (Arbitrary k, Ord k, Monoid v, Arbitrary v) => Arbitrary (DisjointMap k v) where
arbitrary = do
xs <- arbitrary
ys <- arbitrary
let s1 = foldMap (\(k,v) -> DM.singleton k v) (xs :: [(k,v)])
s2 = foldMap (\(k1,k2) -> DM.union k1 k2 DM.empty) (ys :: [(k,k)])
return (s1 <> s2)
newtype WrapWord8 = WrapWord8 Word8
deriving (FromJSON,ToJSON,Show,Eq,Arbitrary,Ord)
instance Monoid WrapWord8 where
mempty = WrapWord8 0
mappend (WrapWord8 a) (WrapWord8 b) = WrapWord8 (a + b)