module Main where import Test.Framework (Test, defaultMain, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.Framework.Providers.QuickCheck2 (testProperty) import Data.List import Data.Ord import qualified Data.Set as S import qualified Data.PartialOrd as PO import Test.HUnit ((@?=)) main :: IO () main = defaultMain tests tests :: [Test.Framework.Test] tests = [ testGroup "Number Properties" [ testProperty "Int" (prop_num :: Integer -> Integer -> Bool) , testProperty "Integer" (prop_num :: Int -> Int -> Bool) , testProperty "Double" (prop_num :: Double -> Double -> Bool) , testProperty "Float" (prop_num :: Float -> Float -> Bool) ] , testGroup "List Properties" [ testProperty "==" (\ a b -> compareBinFuns (equal isSublistOf) (PO.==) (a :: [Int]) (b :: [Int])) , testProperty "== (sort)" (\ a -> let a' = sort a :: [Int] in compareBinFuns (equal isSublistOf) (PO.==) (reverse a') a') , testProperty "/=" (\ a b -> compareBinFuns (notEqual isSublistOf) (PO./=) (a :: [Int]) (b :: [Int])) , testProperty "<=" (\ a b -> compareBinFuns isSublistOf (PO.<=) (a :: [Int]) (b :: [Int])) , testProperty ">=" (\ a b -> compareBinFuns (geq isSublistOf) (PO.>=) (a :: [Int]) (b :: [Int])) , testProperty "<" (\ a b -> compareBinFuns (less isSublistOf) (PO.<) (a :: [Int]) (b :: [Int])) , testProperty ">" (\ a b -> compareBinFuns (greater isSublistOf) (PO.>) (a :: [Int]) (b :: [Int])) , testProperty "transitivity" (\ a b c -> prop_trans (a :: [Int]) (b :: [Int]) (c :: [Int])) , testProperty "antisymmetry" (\ a b -> prop_antisymmetry (a :: [Int]) (b :: [Int])) ] , testGroup "Set Properties" [ testProperty "==" (\ a b -> compareBinFuns (equal S.isSubsetOf) (PO.==) (a :: S.Set Int) (b :: S.Set Int)) , testProperty "/=" (\ a b -> compareBinFuns (notEqual S.isSubsetOf) (PO./=) (a :: S.Set Int) (b :: S.Set Int)) , testProperty "<=" (\ a b -> compareBinFuns S.isSubsetOf (PO.<=) (a :: S.Set Int) (b :: S.Set Int)) , testProperty ">=" (\ a b -> compareBinFuns (geq S.isSubsetOf) (PO.>=) (a :: S.Set Int) (b :: S.Set Int)) , testProperty "<" (\ a b -> compareBinFuns (less S.isSubsetOf) (PO.<) (a :: S.Set Int) (b :: S.Set Int)) , testProperty ">" (\ a b -> compareBinFuns (greater S.isSubsetOf) (PO.>) (a :: S.Set Int) (b :: S.Set Int)) , testProperty "transitivity" (\ a b c -> prop_trans (a :: S.Set Int) (b :: S.Set Int) (c :: S.Set Int)) , testProperty "antisymmetry" (\ a b -> prop_antisymmetry (a :: S.Set Int) (b :: S.Set Int)) ] , testGroup "Maxima & Minima" [ testProperty "maxima exist" (prop_extrema_exist (PO.maxima :: [Int] -> [Int])) , testProperty "minima exist" (prop_extrema_exist (PO.minima :: [Int] -> [Int])) , testProperty "minima are minimal" (prop_extrema_extremal (PO.minima :: [[Int]] -> [[Int]]) isSuplistOf) , testProperty "maxima are maximal" (prop_extrema_extremal (PO.maxima :: [[Int]] -> [[Int]]) isSublistOf) , testProperty "Unique maximum for Ord types" (prop_unique_extremum (PO.maxima :: [Int] -> [Int]) maximum) , testProperty "Unique minimum for Ord types" (prop_unique_extremum (PO.minima :: [Int] -> [Int]) minimum) ] , testGroup "Known Extrema" (map (\ (idx, extrema) -> let label = "extremal cases (" ++ show idx ++ ")" in testCase label (test_known_extrema extrema)) (zip [1..] knownExtrema)) ] test_known_extrema :: PO.PartialOrd a => ([a], [a], [a]) -> IO () test_known_extrema (as, asMax, asMin) = ((equal isSublistOf) (PO.maxima as) asMax && (equal isSublistOf) (PO.minima as) asMin) @?= True knownExtrema :: [([[Int]], [[Int]], [[Int]])] knownExtrema = [ ( [ [], [1, 2, 3], [4, 5], [], [4, 5] , [3, 4], [0], [0, 1, 2, 3, 4, 6] ] , [ [ 0, 1, 2, 3, 4, 6], [4, 5] ] , [ [] ] ) , ( [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ] , [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ] , [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ] ) , ( [] , [] , [] ) , ( [ [ 1, 2 ], [ 2, 1 ] ] , [ [ 1, 2 ] ] , [ [ 1, 2 ] ] ) , ( [ [ 1, 2 ], [ 2, 3 ] ] , [ [ 1, 2 ], [ 2, 3 ] ] , [ [ 1, 2 ], [ 2, 3 ] ] ) , ( [ [ 0 ], [ 0, 0 ] ] , [ [ 0 ] ] , [ [ 0 ] ] ) , ( [ [ 1 ], [ 1 ], [ 1 ] ] , [ [ 1 ] ] , [ [ 1 ] ] ) , ( [ [ ], [ -1, -2, -3 ] ] , [ [ -3, -2, -1 ] ] , [ [ ] ] ) ] prop_trans :: PO.PartialOrd a => a -> a -> a -> Bool prop_trans a b c = case (a PO.<= b, b PO.<= c) of (True, True) -> a PO.<= c _ -> True prop_antisymmetry :: PO.PartialOrd a => a -> a -> Bool prop_antisymmetry a b = if a PO.<= b then a PO.== b || not (a PO.>= b) else True prop_unique_extremum :: Ord a => ([a] -> [a]) -> ([a] -> a) -> [a] -> Bool prop_unique_extremum _ _ [] = True prop_unique_extremum computeExtrema computeExtremum as = case computeExtrema as of [extremum] -> extremum == computeExtremum as _ -> False prop_extrema_extremal :: Eq a => ([a] -> [a]) -> (a -> a -> Bool) -> [a] -> Bool prop_extrema_extremal computeExtrema relation as = let extrema = computeExtrema as extrema' = filter (isBiggerExtremum extrema) as in null extrema' where -- Returns True if a < a' where a' is in extrema. isBiggerExtremum extrema a = notNull $ filter (\ e -> (less relation) e a) extrema notNull = not . null prop_extrema_exist :: Eq a => ([a] -> [a]) -> [a] -> Bool prop_extrema_exist f as = null as || (not . null) (f as) prop_num :: (Num a, Ord a) => a -> a -> Bool prop_num x y = case x `compare` y of LT -> x < y GT -> x > y EQ -> x == y -- Check if two given binary functions agree on the given input. compareBinFuns :: Eq c => (a -> b -> c) -> (a -> b -> c) -> a -> b -> Bool compareBinFuns f g a b = (a `f` b) == (a `g` b) -- Implement equality given less-or-equal relation. equal :: (a -> a -> Bool) -> a -> a -> Bool equal leq a b = a `leq` b && b `leq` a -- Implement inequality given less-or-equal relation. notEqual :: (a -> a -> Bool) -> a -> a -> Bool notEqual leq a b = not $ equal leq a b -- Implement greater-or-equal given a less-or-equal relation. geq :: (a -> a -> Bool) -> a -> a -> Bool geq = flip -- Implement strictly-less given a less or-equal relation. less :: (a -> a -> Bool) -> a -> a -> Bool less leq a b = (a `leq` b) && notEqual leq a b -- Implement strictly-greater given less-or-equal relation. greater :: (a -> a -> Bool) -> a -> a -> Bool greater leq a b = less leq b a -- Check if each element of the first list is also an element of the -- second list. isSublistOf :: PO.PartialOrd a => [a] -> [a] -> Bool isSublistOf [] bs = True isSublistOf (a:as) bs = a `PO.elem` bs && as `isSublistOf` bs -- Check if each element of the second list is also an element of the -- first list. isSuplistOf :: PO.PartialOrd a => [a] -> [a] -> Bool isSuplistOf = flip isSublistOf