import Test.Tasty import Test.Tasty.QuickCheck as QC import Data.Set (Set) import qualified Data.Set as Set import Data.RangeSet.List (RSet) import qualified Data.RangeSet.List as RSet import Control.Applicative import Data.Int import Data.Monoid main :: IO () main = defaultMain tests tests :: TestTree tests = testGroup "Tests" [qcProps] data SetAction a = AEmpty | ASingleton a | AFromList [a] | AInsert a (SetAction a) | ADelete a (SetAction a) | AUnion (SetAction a) (SetAction a) | ADifference (SetAction a) (SetAction a) | AIntersection (SetAction a) (SetAction a) deriving (Show) instance Arbitrary a => Arbitrary (SetAction a) where arbitrary = sized arbitrary' where arbitrary' n | n <= 0 = oneof [pure AEmpty, ASingleton <$> arbitrary] | otherwise = oneof [ pure AEmpty , ASingleton <$> arbitrary , AFromList <$> arbitrary , AInsert <$> arbitrary <*> arbitrary1 , ADelete <$> arbitrary <*> arbitrary1 , AUnion <$> arbitrary2 <*> arbitrary2 , ADifference <$> arbitrary2 <*> arbitrary2 , AIntersection <$> arbitrary2 <*> arbitrary2 ] where arbitrary1 = arbitrary' $ n - 1 arbitrary2 = arbitrary' $ n `div` 2 toSet :: (Ord a) => SetAction a -> Set a toSet AEmpty = Set.empty toSet (ASingleton a) = Set.singleton a toSet (AFromList l) = Set.fromList l toSet (AInsert a set) = Set.insert a $ toSet set toSet (ADelete a set) = Set.delete a $ toSet set toSet (AUnion a b) = Set.union (toSet a) (toSet b) toSet (ADifference a b) = Set.difference (toSet a) (toSet b) toSet (AIntersection a b) = Set.intersection (toSet a) (toSet b) toRSet :: (Enum a, Ord a) => SetAction a -> RSet a toRSet AEmpty = RSet.empty toRSet (ASingleton a) = RSet.singleton a toRSet (AFromList l) = RSet.fromList l toRSet (AInsert a set) = RSet.insert a $ toRSet set toRSet (ADelete a set) = RSet.delete a $ toRSet set toRSet (AUnion a b) = RSet.union (toRSet a) (toRSet b) toRSet (ADifference a b) = RSet.difference (toRSet a) (toRSet b) toRSet (AIntersection a b) = RSet.intersection (toRSet a) (toRSet b) elementsProp :: SetAction Int -> Property elementsProp seta = Set.elems (toSet seta) === RSet.elems (toRSet seta) nullProp :: SetAction Int -> Property nullProp seta = Set.null (toSet seta) === RSet.null (toRSet seta) memberProp :: Int -> SetAction Int -> Property memberProp x seta = Set.member x (toSet seta) === RSet.member x (toRSet seta) notMemberProp :: Int -> SetAction Int -> Property notMemberProp x seta = Set.notMember x (toSet seta) === RSet.notMember x (toRSet seta) data RSetAction a = RAEmpty | RASingleton (a, a) | RAFromList [(a, a)] | RAInsert (a, a) (RSetAction a) | RADelete (a, a) (RSetAction a) | RAUnion (RSetAction a) (RSetAction a) | RADifference (RSetAction a) (RSetAction a) | RAIntersection (RSetAction a) (RSetAction a) deriving (Show) instance Arbitrary a => Arbitrary (RSetAction a) where arbitrary = sized arbitrary' where arbitrary' n | n <= 0 = oneof [pure RAEmpty, RASingleton <$> arbitrary] | otherwise = oneof [ pure RAEmpty , RASingleton <$> arbitrary , RAFromList <$> arbitrary , RAInsert <$> arbitrary <*> arbitrary1 , RADelete <$> arbitrary <*> arbitrary1 , RAUnion <$> arbitrary2 <*> arbitrary2 , RADifference <$> arbitrary2 <*> arbitrary2 , RAIntersection <$> arbitrary2 <*> arbitrary2 ] where arbitrary1 = arbitrary' $ n - 1 arbitrary2 = arbitrary' $ n `div` 2 rangeToSet :: (Enum a, Ord a) => RSetAction a -> Set a rangeToSet RAEmpty = Set.empty rangeToSet (RASingleton a) = Set.fromList $ uncurry enumFromTo a rangeToSet (RAFromList l) = Set.fromList $ concatMap (uncurry enumFromTo) l rangeToSet (RAInsert a set) = foldr Set.insert (rangeToSet set) $ uncurry enumFromTo a rangeToSet (RADelete a set) = foldr Set.delete (rangeToSet set) $ uncurry enumFromTo a rangeToSet (RAUnion a b) = Set.union (rangeToSet a) (rangeToSet b) rangeToSet (RADifference a b) = Set.difference (rangeToSet a) (rangeToSet b) rangeToSet (RAIntersection a b) = Set.intersection (rangeToSet a) (rangeToSet b) rangeToRSet :: (Enum a, Ord a) => RSetAction a -> RSet a rangeToRSet RAEmpty = RSet.empty rangeToRSet (RASingleton a) = RSet.singletonRange a rangeToRSet (RAFromList l) = RSet.fromRangeList l rangeToRSet (RAInsert a set) = RSet.insertRange a $ rangeToRSet set rangeToRSet (RADelete a set) = RSet.deleteRange a $ rangeToRSet set rangeToRSet (RAUnion a b) = RSet.union (rangeToRSet a) (rangeToRSet b) rangeToRSet (RADifference a b) = RSet.difference (rangeToRSet a) (rangeToRSet b) rangeToRSet (RAIntersection a b) = RSet.intersection (rangeToRSet a) (rangeToRSet b) rangeProp :: RSetAction Int8 -> Property rangeProp seta = Set.elems (rangeToSet seta) === RSet.elems (rangeToRSet seta) ordered :: Ord a => [(a,a)] -> Bool ordered rs = all lt $ zip rs (tail rs) where lt :: Ord a => ((a,a),(a,a)) -> Bool lt ((_,y),(u,_)) = y < u pairOrdered :: Ord a => [(a, a)] -> Bool pairOrdered = all (uncurry (<=)) orderedProp :: RSetAction Int8 -> Bool orderedProp setAction = ordered rs && pairOrdered rs where rs = RSet.toRangeList . rangeToRSet $ setAction -- Complement laws complementProps :: TestTree complementProps = testGroup "complement" [ QC.testProperty "definition" (\a e -> RSet.member e (rs a) === RSet.notMember e (RSet.complement (rs a))) , QC.testProperty "involutive" (\a -> rs a === RSet.complement (RSet.complement (rs a))) ] where rs = rangeToRSet :: RSetAction Int -> RSet Int -- Monoid laws monoidLaws :: TestTree monoidLaws = testGroup "MonoidLaws" [ QC.testProperty "left identity" (\a -> rs a === mempty <> rs a) , QC.testProperty "right identity" (\a -> rs a === rs a <> mempty) , QC.testProperty "associativity" (\a b c -> rs a <> (rs b <> rs c) === (rs a <> rs b) <> rs c) ] where rs = rangeToRSet :: RSetAction Int -> RSet Int -- All QuickCheck properties qcProps :: TestTree qcProps = testGroup "QuickCheck properties" [ QC.testProperty "element operations similar" elementsProp , QC.testProperty "null operation similar" nullProp , QC.testProperty "member operation similar" memberProp , QC.testProperty "notMember operation similar" notMemberProp , QC.testProperty "range operations similar" rangeProp , QC.testProperty "ranges remain ordered" orderedProp , complementProps , monoidLaws ]