lattices-2.1: test/Tests.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main (main) where
import Prelude ()
import Prelude.Compat
import Control.Monad (ap, guard)
import Data.Int (Int8)
import Data.List (genericLength, nub)
import Data.Maybe (isJust, listToMaybe)
import Data.Semigroup (All, Any, Endo (..), (<>))
import Data.Typeable (Typeable, typeOf)
import Data.Universe.Class (Finite (..), Universe (..))
import Data.Universe.Helpers (Natural, Tagged (..))
import Test.QuickCheck
(Arbitrary (..), Property, discard, label, (=/=), (===))
import Test.QuickCheck.Function
import Test.Tasty
import Test.Tasty.QuickCheck (testProperty)
import qualified Test.QuickCheck as QC
import Algebra.Heyting
import Algebra.Lattice
import Algebra.PartialOrd
import Algebra.Lattice.M2 (M2 (..))
import Algebra.Lattice.M3 (M3 (..))
import Algebra.Lattice.N5 (N5 (..))
import Algebra.Lattice.ZeroHalfOne (ZeroHalfOne (..))
import qualified Algebra.Heyting.Free as HF
import qualified Algebra.Lattice.Divisibility as Div
import qualified Algebra.Lattice.Dropped as D
import qualified Algebra.Lattice.Free as F
import qualified Algebra.Lattice.Levitated as L
import qualified Algebra.Lattice.Lexicographic as LO
import qualified Algebra.Lattice.Lifted as U
import qualified Algebra.Lattice.Op as Op
import qualified Algebra.Lattice.Ordered as O
import qualified Algebra.Lattice.Wide as W
import Data.HashMap.Lazy (HashMap)
import Data.HashSet (HashSet)
import Data.IntMap (IntMap)
import Data.IntSet (IntSet)
import Data.Map (Map)
import Data.Set (Set)
import Algebra.PartialOrd.Instances ()
import Data.Universe.Instances.Eq ()
import Data.Universe.Instances.Ord ()
import Data.Universe.Instances.Show ()
import Test.QuickCheck.Instances ()
-- For old GHC to work
data Proxy (a :: *) = Proxy
data Proxy1 (a :: * -> *) = Proxy1
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "Tests"
[ allLatticeLaws (LBounded Partial Modular) (Proxy :: Proxy M3) -- non distributive lattice!
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy M2) -- M2
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy (Set Bool)) -- isomorphic to M2
, allLatticeLaws (LBounded Partial NonModular) (Proxy :: Proxy N5)
, allLatticeLaws (LHeyting Total IsBoolean) (Proxy :: Proxy ())
, allLatticeLaws (LHeyting Total IsBoolean) (Proxy :: Proxy Bool)
, allLatticeLaws (LHeyting Total DeMorgan) (Proxy :: Proxy ZeroHalfOne)
, allLatticeLaws (LNormal Partial Distributive) (Proxy :: Proxy (Map Int (O.Ordered Int)))
, allLatticeLaws (LNormal Partial Distributive) (Proxy :: Proxy (IntMap (O.Ordered Int)))
, allLatticeLaws (LNormal Partial Distributive) (Proxy :: Proxy (HashMap Int (O.Ordered Int)))
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy (Set Int8))
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy (HashSet Int8))
, allLatticeLaws (LBoundedJoin Partial Distributive) (Proxy :: Proxy (Set Int))
, allLatticeLaws (LBoundedJoin Partial Distributive) (Proxy :: Proxy IntSet)
, allLatticeLaws (LBoundedJoin Partial Distributive) (Proxy :: Proxy (HashSet Int))
, allLatticeLaws (LHeyting Total DeMorgan) (Proxy :: Proxy (O.Ordered Int8))
, allLatticeLaws (LBoundedJoin Partial Distributive) (Proxy :: Proxy (Div.Divisibility Int))
, allLatticeLaws (LNormal Total Distributive) (Proxy :: Proxy (LO.Lexicographic (O.Ordered Int) (O.Ordered Int)))
, allLatticeLaws (LBounded Partial Modular) (Proxy :: Proxy (W.Wide Int))
, allLatticeLaws (LBounded Partial NonModular) (Proxy :: Proxy (LO.Lexicographic (Set Bool) (Set Bool)))
, allLatticeLaws (LBounded Partial NonModular) (Proxy :: Proxy (LO.Lexicographic M2 M2)) -- non distributive!
, allLatticeLaws LNotLattice (Proxy :: Proxy String)
, allLatticeLaws (LBounded Partial Modular) (Proxy :: Proxy (M2, M2))
, allLatticeLaws (LBounded Partial Distributive) (Proxy :: Proxy (Either M2 M2))
, allLatticeLaws (LBounded Partial NonModular) (Proxy :: Proxy (Either M3 N5)) -- non modular, though it takes QC time to find
, allLatticeLaws (LHeyting Total IsBoolean) (Proxy :: Proxy All)
, allLatticeLaws (LHeyting Total IsBoolean) (Proxy :: Proxy Any)
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy (Endo Bool)) -- note: it's partial!
, allLatticeLaws (LBounded Partial Modular) (Proxy :: Proxy (Endo M3))
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy (Int8 -> Bool))
, allLatticeLaws (LHeyting Partial IsBoolean) (Proxy :: Proxy (Int8 -> M2))
, allLatticeLaws (LBounded Partial Modular) (Proxy :: Proxy (Int8 -> M3))
, allLatticeLaws (LNormal Partial Distributive) (Proxy :: Proxy (F.Free Int8))
, allLatticeLaws (LHeyting Partial NonBoolean) (Proxy :: Proxy (HF.Free Var))
, allLatticeLaws (LBoundedMeet Total Distributive) (Proxy :: Proxy (D.Dropped (O.Ordered Int)))
, allLatticeLaws (LBounded Total Distributive) (Proxy :: Proxy (L.Levitated (O.Ordered Int)))
, allLatticeLaws (LBoundedJoin Total Distributive) (Proxy :: Proxy (U.Lifted (O.Ordered Int)))
, allLatticeLaws (LNormal Total Distributive ) (Proxy :: Proxy (Op.Op (O.Ordered Int)))
, testProperty "Lexicographic M2 M2 contains M3" $ QC.property $
isJust searchM3LexM2
, monadLaws "Dropped" (Proxy1 :: Proxy1 D.Dropped)
, monadLaws "Levitated" (Proxy1 :: Proxy1 L.Levitated)
, monadLaws "Lexicographic" (Proxy1 :: Proxy1 (LO.Lexicographic Bool))
, monadLaws "Lifted" (Proxy1 :: Proxy1 U.Lifted)
, monadLaws "Op" (Proxy1 :: Proxy1 Op.Op)
, monadLaws "Ordered" (Proxy1 :: Proxy1 O.Ordered)
, monadLaws "Wide" (Proxy1 :: Proxy1 W.Wide)
, monadLaws "Heyting.Free" (Proxy1 :: Proxy1 HF.Free)
, finiteLaws (Proxy :: Proxy M2)
, finiteLaws (Proxy :: Proxy M3)
, finiteLaws (Proxy :: Proxy N5)
, finiteLaws (Proxy :: Proxy ZeroHalfOne)
, finiteLaws (Proxy :: Proxy OInt8)
, finiteLaws (Proxy :: Proxy (Div.Divisibility Int8))
, finiteLaws (Proxy :: Proxy (W.Wide Int8))
, finiteLaws (Proxy :: Proxy (D.Dropped OInt8))
, finiteLaws (Proxy :: Proxy (L.Levitated OInt8))
, finiteLaws (Proxy :: Proxy (U.Lifted OInt8))
, finiteLaws (Proxy :: Proxy (LO.Lexicographic OInt8 OInt8))
]
type OInt8 = O.Ordered Int8
-------------------------------------------------------------------------------
-- Monad laws
-------------------------------------------------------------------------------
monadLaws :: forall (m :: * -> *). ( Monad m
#if !MIN_VERSION_base(4, 8, 0)
, Applicative m
#endif
, Arbitrary (m Int)
, Eq (m Int)
, Show (m Int)
, Arbitrary (m (Fun Int Int))
, Show (m (Fun Int Int)))
=> String
-> Proxy1 m
-> TestTree
monadLaws name _ = testGroup ("Monad laws: " <> name)
[ testProperty "left identity" leftIdentityProp
, testProperty "right identity" rightIdentityProp
, testProperty "composition" compositionProp
, testProperty "Applicative pure" pureProp
, testProperty "Applicative ap" apProp
]
where
leftIdentityProp :: Int -> Fun Int (m Int) -> Property
leftIdentityProp x (Fun _ k) = (return x >>= k) === k x
rightIdentityProp :: m Int -> Property
rightIdentityProp m = (m >>= return) === m
compositionProp :: m Int -> Fun Int (m Int) -> Fun Int (m Int) -> Property
compositionProp m (Fun _ k) (Fun _ h) = (m >>= (\x -> k x >>= h)) === ((m >>= k) >>= h)
pureProp :: Int -> Property
pureProp x = pure x === (return x :: m Int)
apProp :: m (Fun Int Int) -> m Int -> Property
apProp f x = (f' <*> x) === ap f' x
where f' = apply <$> f
{-# NOINLINE monadLaws #-}
-------------------------------------------------------------------------------
-- Partial ord laws
-------------------------------------------------------------------------------
data IsTotal a where
Total :: Ord a => IsTotal a
Partial :: PartialOrd a => IsTotal a
partialOrdLaws
:: forall a. (Eq a, Show a, Arbitrary a, PartialOrd a)
=> IsTotal a
-> Proxy a
-> TestTree
partialOrdLaws total _ = testGroup "PartialOrd" $
[ testProperty "reflexive" reflProp
, testProperty "anti-symmetric" antiSymProp
, testProperty "transitive" transitiveProp
] ++ case total of
Partial -> []
Total ->
[ testProperty "total" totalProp
, testProperty "leq/compare agree" leqCompareProp
]
where
reflProp :: a -> Property
reflProp x = QC.property $ leq x x
antiSymProp :: a -> a -> Property
antiSymProp x y
| leq x y && leq y x = label "same" $ x === y
| otherwise = label "diff" $ x =/= y
transitiveProp :: a -> a -> a -> Property
transitiveProp x y z = case p of
[] -> label "non-related" $ QC.property True
((x', _, z') : _) -> label "related" $ QC.property $ leq x' z'
where
p = [ (x', y', z')
| (x', y', z') <- [(x,y,z),(y,x,z),(z,y,x),(y,z,x),(z,x,y),(x,z,y)]
, leq x' y'
, leq y' z'
]
totalProp :: a -> a -> Property
totalProp x y = QC.property $ leq x y || leq y x
leqCompareProp :: Ord a => a -> a -> Property
leqCompareProp x y = agree (leq x y) (leq y x) (compare x y)
where
agree True True = (=== EQ)
agree True False = (=== LT)
agree False True = (=== GT)
agree False False = discard
{-# NOINLINE partialOrdLaws #-}
-------------------------------------------------------------------------------
-- Lattice
-------------------------------------------------------------------------------
-- | Lattice Kind
data LKind a where
LNotLattice :: LKind a
LNormal :: Lattice a => IsTotal a -> Distr -> LKind a
LBoundedMeet :: BoundedMeetSemiLattice a => IsTotal a -> Distr -> LKind a
LBoundedJoin :: BoundedJoinSemiLattice a => IsTotal a -> Distr -> LKind a
LBounded :: BoundedLattice a => IsTotal a -> Distr -> LKind a
LHeyting :: Heyting a => IsTotal a -> IsBoolean -> LKind a
data Distr
= NonModular
| Modular
| Distributive
deriving (Eq, Ord)
data IsBoolean
= NonBoolean
| DeMorgan
| IsBoolean
deriving (Eq, Ord)
allLatticeLaws
:: forall a. (Eq a, Show a, Arbitrary a, Typeable a, PartialOrd a)
=> LKind a
-> Proxy a
-> TestTree
allLatticeLaws ki pr = case ki of
LNotLattice -> testGroup name $
[partialOrdLaws Partial pr]
LNormal t d -> testGroup name $
partialOrdLaws t pr : allLatticeLaws' d pr
LBoundedMeet t d -> testGroup name $
partialOrdLaws t pr : allLatticeLaws' d pr ++
[ boundedMeetLaws pr ]
LBoundedJoin t d -> testGroup name $
partialOrdLaws t pr : allLatticeLaws' d pr ++
[ boundedJoinLaws pr ]
LBounded t d -> testGroup name $
partialOrdLaws t pr : allLatticeLaws' d pr ++
[ boundedMeetLaws pr
, boundedJoinLaws pr
]
LHeyting t b -> testGroup name $
partialOrdLaws t pr : allLatticeLaws' Distributive pr ++
[ boundedMeetLaws pr
, boundedJoinLaws pr
, heytingLaws pr
] ++
[ deMorganLaws pr | b >= DeMorgan ] ++
[ booleanLaws pr | b >= IsBoolean ]
where
name = show (typeOf (undefined :: a))
{-# NOINLINE allLatticeLaws #-}
allLatticeLaws'
:: forall a. (Eq a, Show a, Arbitrary a, Lattice a, PartialOrd a)
=> Distr
-> Proxy a
-> [TestTree]
allLatticeLaws' distr pr =
[ latticeLaws pr ] ++
[ modularLaws pr | distr >= Modular ] ++
[ distributiveLaws pr | distr >= Distributive ]
-------------------------------------------------------------------------------
-- Lattice laws
-------------------------------------------------------------------------------
latticeLaws
:: forall a. (Eq a, Show a, Arbitrary a, Lattice a, PartialOrd a)
=> Proxy a
-> TestTree
latticeLaws _ = testGroup "Lattice"
[ testProperty "leq = joinLeq" joinLeqProp
, testProperty "leq = meetLeq" meetLeqProp
, testProperty "meet is lower bound" meetLower
, testProperty "join is upper bound" joinUpper
, testProperty "meet commutes" meetComm
, testProperty "join commute" joinComm
, testProperty "meet associative" meetAssoc
, testProperty "join associative" joinAssoc
, testProperty "absorbtion 1" meetAbsorb
, testProperty "absorbtion 2" joinAbsorb
, testProperty "meet idempontent" meetIdemp
, testProperty "join idempontent" joinIdemp
, testProperty "comparableDef" comparableDef
]
where
joinLeqProp :: a -> a -> Property
joinLeqProp x y = leq x y === joinLeq x y
meetLeqProp :: a -> a -> Property
meetLeqProp x y = leq x y === meetLeq x y
meetLower :: a -> a -> Property
meetLower x y = (m `leq` x) QC..&&. (m `leq` y)
where
m = x /\ y
joinUpper :: a -> a -> Property
joinUpper x y = (x `leq` j) QC..&&. (y `leq` j)
where
j = x \/ y
meetComm :: a -> a -> Property
meetComm x y = x /\ y === y /\ x
joinComm :: a -> a -> Property
joinComm x y = x \/ y === y \/ x
meetAssoc :: a -> a -> a -> Property
meetAssoc x y z = x /\ (y /\ z) === (x /\ y) /\ z
joinAssoc :: a -> a -> a -> Property
joinAssoc x y z = x \/ (y \/ z) === (x \/ y) \/ z
meetAbsorb :: a -> a -> Property
meetAbsorb x y = x /\ (x \/ y) === x
joinAbsorb :: a -> a -> Property
joinAbsorb x y = x \/ (x /\ y) === x
meetIdemp :: a -> Property
meetIdemp x = x /\ x === x
joinIdemp :: a -> Property
joinIdemp x = x \/ x === x
comparableDef :: a -> a -> Property
comparableDef x y = (leq x y || leq y x) === comparable x y
{-# NOINLINE latticeLaws #-}
-------------------------------------------------------------------------------
-- Modular
-------------------------------------------------------------------------------
modularLaws
:: forall a. (Eq a, Show a, Arbitrary a, Lattice a, PartialOrd a)
=> Proxy a
-> TestTree
modularLaws _ = testGroup "Modular"
[ testProperty "(y ∧ (x ∨ z)) ∨ z = (y ∨ z) ∧ (x ∨ z)" modularProp
]
where
modularProp :: a -> a -> a -> Property
modularProp x y z = lhs === rhs where
lhs = (y /\ (x \/ z)) \/ z
rhs = (y \/ z) /\ (x \/ z)
{-# NOINLINE modularLaws #-}
-------------------------------------------------------------------------------
-- Distributive
-------------------------------------------------------------------------------
distributiveLaws
:: forall a. (Eq a, Show a, Arbitrary a, Lattice a, PartialOrd a)
=> Proxy a
-> TestTree
distributiveLaws _ = testGroup "Distributive"
[ testProperty "x ∧ (y ∨ z) = (x ∧ y) ∨ (x ∧ z)" distrProp
, testProperty "x ∨ (y ∧ z) = (x ∨ y) ∧ (x ∨ z)" distr2Prop
]
where
distrProp :: a -> a -> a -> Property
distrProp x y z = lhs === rhs where
lhs = x /\ (y \/ z)
rhs = (x /\ y) \/ (x /\ z)
distr2Prop :: a -> a -> a -> Property
distr2Prop x y z = lhs === rhs where
lhs = x \/ (y /\ z)
rhs = (x \/ y) /\ (x \/ z)
{-# NOINLINE distributiveLaws #-}
-------------------------------------------------------------------------------
-- Bounded lattice laws
-------------------------------------------------------------------------------
boundedMeetLaws
:: forall a. (Eq a, Show a, Arbitrary a, BoundedMeetSemiLattice a)
=> Proxy a
-> TestTree
boundedMeetLaws _ = testGroup "BoundedMeetSemiLattice"
[ testProperty "top /\\ x = x" identityLeftProp
, testProperty "x /\\ top = x" identityRightProp
, testProperty "top \\/ x = top" annihilationLeftProp
, testProperty "x \\/ top = top" annihilationRightProp
]
where
identityLeftProp :: a -> Property
identityLeftProp x = lhs === rhs where
lhs = top /\ x
rhs = x
identityRightProp :: a -> Property
identityRightProp x = lhs === rhs where
lhs = x /\ top
rhs = x
annihilationLeftProp :: a -> Property
annihilationLeftProp x = lhs === rhs where
lhs = top \/ x
rhs = top
annihilationRightProp :: a -> Property
annihilationRightProp x = lhs === rhs where
lhs = x \/ top
rhs = top
{-# NOINLINE boundedMeetLaws #-}
boundedJoinLaws
:: forall a. (Eq a, Show a, Arbitrary a, BoundedJoinSemiLattice a)
=> Proxy a
-> TestTree
boundedJoinLaws _ = testGroup "BoundedJoinSemiLattice"
[ testProperty "bottom \\/ x = x" identityLeftProp
, testProperty "x \\/ bottom = x" identityRightProp
, testProperty "bottom /\\ x = bottom" annihilationLeftProp
, testProperty "x /\\ bottom = bottom" annihilationRightProp
]
where
identityLeftProp :: a -> Property
identityLeftProp x = lhs === rhs where
lhs = bottom \/ x
rhs = x
identityRightProp :: a -> Property
identityRightProp x = lhs === rhs where
lhs = x \/ bottom
rhs = x
annihilationLeftProp :: a -> Property
annihilationLeftProp x = lhs === rhs where
lhs = bottom /\ x
rhs = bottom
annihilationRightProp :: a -> Property
annihilationRightProp x = lhs === rhs where
lhs = x /\ bottom
rhs = bottom
{-# NOINLINE boundedJoinLaws #-}
-------------------------------------------------------------------------------
-- Heyting laws
-------------------------------------------------------------------------------
heytingLaws
:: forall a. (Eq a, Show a, Arbitrary a, Heyting a, Typeable a)
=> Proxy a
-> TestTree
heytingLaws _ = testGroup "Heyting"
[ testProperty "neg default" negDefaultProp
, testProperty "<=> default" equivDefaultProp
, testProperty "x ==> x = top" idIsTopProp
, testProperty "a /\\ (a ==> b) = a /\\ b" andDomainProp
, testProperty "b /\\ (a ==> b) = b" andCodomainProp
, testProperty "a ==> (b /\\ c) = (a ==> b) /\\ (a ==> c)" implDistrProp
, testProperty "de Morgan 1" deMorganProp1
, testProperty "weak de Morgan 2" deMorganProp2weak
]
where
negDefaultProp :: a -> Property
negDefaultProp x = lhs === rhs where
lhs = neg x
rhs = x ==> bottom
equivDefaultProp :: a -> a -> Property
equivDefaultProp x y = lhs === rhs where
lhs = x <=> y
rhs = (x ==> y) /\ (y ==> x)
idIsTopProp :: a -> Property
idIsTopProp x = lhs === rhs where
lhs = x ==> x
rhs = top
andDomainProp :: a -> a -> Property
andDomainProp x y = lhs === rhs where
lhs = x /\ (x ==> y)
rhs = x /\ y
andCodomainProp :: a -> a -> Property
andCodomainProp x y = lhs === rhs where
lhs = y /\ (x ==> y)
rhs = y
implDistrProp :: a -> a -> a -> Property
implDistrProp x y z
| typeOf (undefined :: a) == typeOf (undefined :: HF.Free Var)
= QC.mapSize (min 16) $ implDistrProp' x y z
| otherwise
= implDistrProp' x y z
implDistrProp' :: a -> a -> a -> Property
implDistrProp' x y z = lhs === rhs where
lhs = x ==> (y /\ z)
rhs = (x ==> y) /\ (x ==> z)
deMorganProp1 :: a -> a -> Property
deMorganProp1 x y = lhs === rhs where
lhs = neg (x \/ y)
rhs = neg x /\ neg y
deMorganProp2weak :: a -> a -> Property
deMorganProp2weak x y = lhs === rhs where
lhs = neg (x /\ y)
rhs = neg (neg (neg x \/ neg y))
{-# NOINLINE heytingLaws #-}
-------------------------------------------------------------------------------
-- De morgan
-------------------------------------------------------------------------------
deMorganLaws
:: forall a. (Eq a, Show a, Arbitrary a, Heyting a)
=> Proxy a
-> TestTree
deMorganLaws _ = testGroup "de Morgan"
[ testProperty "de Morgan 2" deMorganProp2
]
where
deMorganProp2 :: a -> a -> Property
deMorganProp2 x y = lhs === rhs where
lhs = neg (x /\ y)
rhs = neg x \/ neg y
{-# NOINLINE deMorganLaws #-}
-------------------------------------------------------------------------------
-- Boolean laws
-------------------------------------------------------------------------------
booleanLaws
:: forall a. (Eq a, Show a, Arbitrary a, Heyting a)
=> Proxy a
-> TestTree
booleanLaws _ = testGroup "Boolean"
[ testProperty "LEM: neg x \\/ x = top" lemProp
, testProperty "DN: neg (neg x) = x" dnProp
]
where
lemProp :: a -> Property
lemProp x = lhs === rhs where
lhs = neg x \/ x
rhs = top
-- every element is regular, i.e. either of following equivalend conditions hold:
-- * neg (neg x) = x
-- * x = neg y, for some y in H -- I don't know example of this
dnProp :: a -> Property
dnProp x = lhs === rhs where
lhs = neg (neg x)
rhs = x
{-# NOINLINE booleanLaws #-}
-------------------------------------------------------------------------------
-- Universe / Finite laws
-------------------------------------------------------------------------------
finiteLaws
:: forall a. (Eq a, Show a, Arbitrary a, Typeable a, Finite a)
=> Proxy a
-> TestTree
finiteLaws _ = testGroup name
[ testProperty "elem x universe" elemProp
, testProperty "length pfx = length (nub pfx)" prefixProp
, testProperty "elem x universeF" elemFProp
, testProperty "length (filter (== x) universeF) = 1" singleProp
, testProperty "cardinality = Tagged (genericLength universeF)" cardinalityProp
]
where
name = show (typeOf (undefined :: a))
elemProp :: a -> Property
elemProp x = QC.property $ elem x universe
elemFProp :: a -> Property
elemFProp x = QC.property $ elem x universeF
prefixProp :: Int -> Property
prefixProp n =
let pfx = take n (universe :: [a])
in QC.counterexample (show pfx) $ length pfx === length (nub pfx)
singleProp :: a -> Property
singleProp x = length (filter (== x) universeF) === 1
cardinalityProp :: Property
cardinalityProp = cardinality === (Tagged (genericLength (universeF :: [a])) :: Tagged a Natural)
{-# NOINLINE finiteLaws #-}
-------------------------------------------------------------------------------
-- Lexicographic M2 search
-------------------------------------------------------------------------------
searchM3 :: (Eq a, PartialOrd a, Lattice a) => [a] -> Maybe (a,a,a,a,a)
searchM3 xs = listToMaybe $ do
x0 <- xs
xa <- xs
guard (xa `notElem` [x0])
guard (x0 `leq` xa)
xb <- xs
guard (xb `notElem` [x0,xa])
guard (x0 `leq` xb)
guard (not $ comparable xa xb)
xc <- xs
guard (xc `notElem` [x0,xa,xb])
guard (x0 `leq` xc)
guard (not $ comparable xa xc)
guard (not $ comparable xb xc)
x1 <- xs
guard (x1 `notElem` [x0,xa,xb,xc])
guard (x0 `leq` x1)
guard (xa `leq` x1)
guard (xb `leq` x1)
guard (xc `leq` x1)
-- homomorphism
let f M3o = x1
f M3a = xa
f M3b = xb
f M3c = xc
f M3i = x1
ma <- [minBound .. maxBound]
mb <- [minBound .. maxBound]
guard (f (ma /\ mb) == f ma /\ f mb)
guard (f (ma \/ mb) == f ma \/ f mb)
return (x0,xa,xb,xc,x1)
type L2 = LO.Lexicographic M2 M2
searchM3LexM2 :: Maybe (L2,L2,L2,L2,L2)
searchM3LexM2 = searchM3 xs
where
xs = [ LO.Lexicographic x y | x <- ys, y <- ys ]
ys = [minBound .. maxBound]
-------------------------------------------------------------------------------
-- Variable (for Free)
-------------------------------------------------------------------------------
-- | The less variables we have, the quicker tests will be :)
data Var = A | B | C | D
deriving (Eq, Ord, Show, Enum, Bounded, Typeable)
instance Arbitrary Var where
arbitrary = QC.arbitraryBoundedEnum
shrink A = []
shrink x = [ minBound .. pred x ]