packages feed

ecta-1.0.0.0: test/Data/Persistent/UnionFindSpec.hs

module Data.Persistent.UnionFindSpec ( spec ) where

import Control.Monad.State ( State, evalState, MonadState(..), modify )
import Control.Monad.Writer ( WriterT(..), MonadWriter(..) )
import Data.Equivalence.Monad ( EquivM, runEquivM, equate, equivalent )

import Test.Hspec
import Test.QuickCheck

import Data.Persistent.UnionFind


-----------------------------------------------------------


--------------------------------------------------------------
--------------------------- Commands -------------------------
--------------------------------------------------------------

type EquivTestM s = WriterT [Bool] (EquivM s [UVar] UVar)

-- Needed to work with ST type constraints
newtype ForAllEquivM c v a = ForAllEquivM { unForAllEquivM :: forall s. EquivM s c v a }

runEquivTestM :: (forall s. EquivTestM s a) -> (a, [Bool])
runEquivTestM = \m -> runEquivM (:[]) (++) (unForAllEquivM $ runWriterT' m)
  where
    runWriterT' :: (forall s. EquivTestM s a) -> ForAllEquivM [UVar] UVar (a, [Bool])
    runWriterT' m = ForAllEquivM $ runWriterT m

type PersistentUFTestM = WriterT [Bool] (State UnionFind)

runPersistentUFTestM :: PersistentUFTestM a -> (a, [Bool])
runPersistentUFTestM m = evalState (runWriterT m) empty

data UnionFindCommand = Union      UVar UVar
                      | CheckEquiv UVar UVar
  deriving ( Show )


interpCommandEquiv :: UnionFindCommand -> EquivTestM s ()
interpCommandEquiv (Union      uv1 uv2) = equate uv1 uv2
interpCommandEquiv (CheckEquiv uv1 uv2) = tell . (:[]) =<< equivalent uv1 uv2

interpCommandPersistentUF :: UnionFindCommand -> PersistentUFTestM ()
interpCommandPersistentUF (Union      uv1 uv2) = modify (union uv1 uv2)
interpCommandPersistentUF (CheckEquiv uv1 uv2) = do uf <- get
                                                    let (uv1Rep, uf')  = find uv1 uf
                                                    let (uv2Rep, uf'') = find uv2 uf'
                                                    put uf''
                                                    tell [uv1Rep == uv2Rep]



--------------------------------------------------------------
-------------------------- Generators ------------------------
--------------------------------------------------------------

instance Arbitrary UVar where
  arbitrary = intToUVar <$> chooseInt (0, 10)
  shrink _ = []

instance Arbitrary UnionFindCommand where
  arbitrary = oneof [ Union      <$> arbitrary <*> arbitrary
                    , CheckEquiv <$> arbitrary <*> arbitrary
                    ]

  shrink _ = []

--------------------------------------------------------------
----------------------------- Main ---------------------------
--------------------------------------------------------------

spec :: Spec
spec = do
  it "random stream of union/check-equiv commands gives same result as EquivM library" $
    property $ \cmds ->    runEquivTestM        (mapM_ @[] interpCommandEquiv        cmds)
                        == runPersistentUFTestM (mapM_ @[] interpCommandPersistentUF cmds)