{-# LANGUAGE InstanceSigs #-} -- In order to use quickcheck library, -- you will need to do: cabal install QuickCheck --lib -- in the command line. import Test.QuickCheck distance :: Int -> Int -> Int distance x y = abs (y-x) -- The distance between any number and itself is always 0 prop_dist_self :: Int -> Bool prop_dist_self x = distance x x == 0 -- The distance between x and y is equal to the distance between y and x prop_dist_symmetric :: Int -> Int -> Bool prop_dist_symmetric x y = distance x y == distance y x -- quickCheck :: Testable prop => prop -> IO () -- verboseCheck :: Testable prop => prop -> IO () -- withMaxSuccess :: Testable prop => Int -> prop -> Property -- QuickCheck's Testable class. -- what if we forget abs when defining distance. qsort :: Ord a => [a] -> [a] qsort [] = [] qsort (x:xs) = qsort lhs ++ [x] ++ qsort rhs where lhs = filter (< x) xs rhs = filter (>= x) xs prop_idempotent :: [Int] -> Bool prop_idempotent xs = qsort (qsort xs) == qsort xs prop_idempotent1 :: [Float] -> Bool prop_idempotent1 xs = qsort (qsort xs) == qsort xs prop_first_elem :: [Int] -> Bool prop_first_elem xs = if null xs then True else let ys = qsort xs in head ys == minimum xs prop_first_elem' :: [Int] -> Property prop_first_elem' xs = (not (null xs)) ==> head (qsort xs) == minimum xs sorted :: Ord a => [a] -> Bool sorted [] = True sorted [x] = True sorted (x:xs) = x <= head xs && sorted xs prop_sorted :: [Int] -> Bool prop_sorted xs = sorted $ qsort xs prop_quick_sort :: [Int] -> Property prop_quick_sort xs = undefined -- How to find properties to test? append :: [a] -> [a] -> [a] append [] xs = xs append (y:ys) xs = y : append ys xs -- 1. Algebraic laws. prop_app_unit :: [Int] -> Bool prop_app_unit xs = append xs [] == xs prop_app_unit' :: [Int] -> Bool prop_app_unit' xs = xs == append [] xs prop_app_assoc :: [Int] -> [Int] -> [Int] -> Bool prop_app_assoc xs ys zs = append (append xs ys) zs == append xs (append ys zs) -- 2. Equivalent of implementations. prop_qsort_sort :: [Int] -> Bool prop_qsort_sort xs = undefined -- qsort xs == bubbleSort xs -- Some other way to build testable properties. -- Let's test the 'replicate' function. prop_replicate :: Int -> Int -> Int -> Bool prop_replicate n x i = replicate n x !! i == x -- A better property using '==>' prop_replicate' :: Int -> Int -> Int -> Property prop_replicate' n x i = (i < n && 0 <= i) ==> replicate n x !! i == x prop_replicate2 :: Char -> Property prop_replicate2 x = forAll (choose (1, 1000)) $ \ n -> forAll (choose (0, n-1)) (\ i -> replicate n x !! i == x) -- Quickcheck's Arbitrary class and the 'generate' function. -- Some useful combinators for generators -- generate $ choose (0,1) -- generate $ elements ['a','e','i','o','u'] -- generate $ (vector 100 :: Gen [Int]) -- generate $ frequency [(99, elements [True]), (1, elements [False])] -- generate $ shuffle [0 .. 10] data Color = Red | Blue | Green deriving (Show) instance Arbitrary Color where arbitrary :: Gen Color arbitrary = elements [Red, Blue, Green] data List a = Nil | Cons a (List a) deriving (Show) instance (Arbitrary a) => Arbitrary (List a) where arbitrary :: Gen (List a) arbitrary = frequency [(1, elements [Nil]) , (2, do{x <- arbitrary; xs <- arbitrary; return (Cons x xs)})] data BinaryTree a = Empty | Node a (BinaryTree a) (BinaryTree a) deriving (Show) instance (Arbitrary a) => Arbitrary (BinaryTree a) where arbitrary :: Gen (BinaryTree a) arbitrary = resize 32 $ sized binary_sized' -- frequency [(2, elements [Empty]) , -- (4, do{x <- arbitrary; l <- arbitrary; r <- arbitrary; return (Node x l r)})] binary_sized :: (Arbitrary a) => Int -> Gen (BinaryTree a) binary_sized x = if x == 0 then elements [Empty] else frequency [(2, elements [Empty]) , (4, do{y <- arbitrary; l <- binary_sized (x `div` 2); r <- binary_sized (x `div` 2); return (Node y l r)})] binary_sized' :: (Arbitrary a) => Int -> Gen (BinaryTree a) binary_sized' x = if x == 0 then elements [Empty] else frequency [(2, elements [Empty]) , (x, do{y <- arbitrary; l <- binary_sized' (x `div` 2); r <- binary_sized' (x `div` 2); return (Node y l r)})] height :: BinaryTree a -> Int height Empty = 0 height (Node x l r) = maximum [height l, height r] + 1 maxTree :: (Ord a) => BinaryTree a -> Maybe a maxTree Empty = Nothing maxTree (Node x l r) = case (maxTree l, maxTree r) of (Nothing, Nothing) -> Just x (Just ln, Nothing) -> Just $ max ln x (Nothing, Just rn) -> Just $ max rn x (Just ln, Just rn) -> Just $ maximum [x, ln, rn] minTree :: (Ord a) => BinaryTree a -> Maybe a minTree Empty = Nothing minTree (Node x l r) = case (minTree l, minTree r) of (Nothing, Nothing) -> Just x (Just ln, Nothing) -> Just $ min ln x (Nothing, Just rn) -> Just $ min rn x (Just ln, Just rn) -> Just $ minimum [x, ln, rn] bst :: (Ord a) => BinaryTree a -> Bool bst Empty = True bst (Node x l r) = let ln = maxTree l rn = minTree r in case (ln, rn) of (Nothing, Nothing) -> True (Nothing, Just rn') -> x < rn' && bst r (Just ln', Nothing) -> ln' <= x && bst l (Just ln', Just rn') -> ln' <= x && x < rn' && bst l && bst r bst_sized :: Int -> Int -> Int -> Gen (BinaryTree Int) bst_sized x lower upper = if x == 0 then elements [Empty] else frequency [(2, elements [Empty]), (4, if abs (lower - upper) >= 2 then do{ y <- choose (lower+1, upper-1); l <- bst_sized (x `div` 2) lower (y-1); r <- bst_sized (x `div` 2) y upper; return (Node y l r)} else elements [Empty] ) ] prop_bst = forAll (bst_sized 32 0 100) (\ x -> bst x)