-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCheckBoundedSet.hs
More file actions
102 lines (82 loc) · 3.16 KB
/
CheckBoundedSet.hs
File metadata and controls
102 lines (82 loc) · 3.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
module CheckBoundedSet where
import Control.Applicative
import Control.Monad
import Data.Maybe
import qualified Data.Vector as V
import Test.QuickCheck
import BoundedSet
forAllMembers :: Testable prop => BoundedSet -> (Int -> prop) -> Property
forAllMembers s p =
case boundedSetToList s
of [] -> property True
xs -> forAll (elements xs) p
-- | Generate a random function from bounded integers to bounded sets.
-- The function's output is biased toward sets of size @(1 - eps)/bound@.
--
-- From graph theory, interpreting the function as giving the edges of a
-- directed graph, this size is on the border of where the graph has
-- one giant component.
genSmallSetFunction :: Int -> Gen (Int -> BoundedSet)
genSmallSetFunction bound = do
-- Precalculate the entire range of the function.
range <- replicateM (bound + 1) $ boundedSetFromList <$> gen_set
let rangev = V.fromList range
-- Look up values from the range
let f i | i >= 0 && i <= bound = rangev V.! i
| otherwise = error "Index out of bounds"
rangev `seq` return f
where
prob :: Double
prob = if bound == 0
then 0
else (1 - 0.002) / fromIntegral bound
gen_set :: Gen [Int]
gen_set = do
liftM catMaybes $ forM [0..bound] $ \i -> do
r <- choose (0, 1)
return $! if r < prob then Just i else Nothing
genMonotonicBoundedFunction :: Gen (BoundedSet -> BoundedSet)
genMonotonicBoundedFunction = sized $ \sz -> do
f <- genSmallSetFunction sz
return $ bsUnions . map f . boundedSetToList
-- Properties that should be tautologies
singleton_contains x = x `bsMember` bsSingleton x
singleton_excludes x y = x /= y ==> y `bsNotMember` bsSingleton x
union_subsumes s t = forAllMembers (bsUnion s t) $ \x ->
x `bsMember` s || x `bsMember` t
union_excludes s t = forAll arbitrary $ \x ->
x `bsNotMember` s && x `bsNotMember` t ==> x `bsNotMember` bsUnion s t
intersection_includes s t = forAllMembers (bsUnion s t) $ \x ->
x `bsMember` bsIntersection s t
intersection_excludes s t = forAll arbitrary $ \x ->
x `bsNotMember` bsIntersection s t ==> x `bsNotMember` s || x `bsNotMember` t
difference_excludes s t = forAllMembers t $ \x ->
x `bsNotMember` bsDifference s t
difference_includes s t = forAllMembers (bsDifference s t) $ \x ->
x `bsMember` s
difference_union s t = forAll arbitrary $ \x ->
x `bsNotMember` bsUnion s t ==> x `bsNotMember` bsDifference s t
closure_contains_f =
forAll (fmap Blind genMonotonicBoundedFunction) $ \(Blind f) ->
f bsEmpty `bsDifference` bsTransitiveClosure f `bsEqual` bsEmpty
closure_closed =
forAll (fmap Blind genMonotonicBoundedFunction) $ \(Blind f) ->
let tc = bsTransitiveClosure f
in tc `bsEqual` bsUnion tc (f (tc))
main = do
putStrLn "singleton"
quickCheck singleton_contains
quickCheck singleton_excludes
putStrLn "union"
quickCheck union_subsumes
quickCheck union_excludes
putStrLn "intersection"
quickCheck intersection_includes
quickCheck intersection_excludes
putStrLn "difference"
quickCheck difference_includes
quickCheck difference_excludes
quickCheck difference_union
putStrLn "closure"
quickCheck closure_contains_f
quickCheck closure_closed