1 {-# LANGUAGE FlexibleInstances #-}
2 {-# LANGUAGE ScopedTypeVariables #-}
3 {-# OPTIONS_GHC -fno-warn-orphans #-}
4 module QuickCheck where
8 import Test.Tasty.QuickCheck
10 import Control.Arrow (second)
11 import Control.Monad (replicateM)
12 import Data.Hashable (Hashable)
14 import GHC.Exts (IsList(..))
16 import qualified Data.Set as Set
21 quickchecks :: TestTree
23 testGroup "QuickCheck"
24 [ testProperty "arbitraryJudgments" $ \(SameLength (x::[(G6,Share)],y)) ->
25 let (gx, cx) = unzip x in
26 let (gy, cy) = unzip y in
27 gx == gy && sum cx == sum cy
28 , testGroup "MajorityValue"
29 [ testProperty "compare" $ \(SameLength (x::MajorityValue G6,y)) ->
30 expandValue x`compare` expandValue y == x`compare`y
33 , testProperty "majorityGauge and majorityValue consistency" $
34 \(SameLength (x@(Merit xs)::Merit G6,y@(Merit ys))) ->
35 not (all (==0) xs || all (==0) ys) ==>
36 case majorityGauge x`compare`majorityGauge y of
37 LT -> majorityValue x < majorityValue y
38 GT -> majorityValue x > majorityValue y
43 -- | Decompress a 'MajorityValue'.
44 expandValue :: MajorityValue a -> [a]
45 expandValue (MajorityValue as) =
46 let m = foldr lcm 1 (denominator . snd <$> as) in
47 go $ second (\s -> numerator s * m) <$> as
50 go ((x,c):xs) = replicate (fromIntegral c) x ++ go xs
52 -- | @arbitraryJudgments n@ arbitrarily generates 'n' lists of pairs of grade and 'Share'
53 -- for the same arbitrary grades,
54 -- and with the same total 'Share' of individual judgments.
55 arbitraryJudgments :: forall g. (Bounded g, Enum g) => Int -> Gen [[(g, Share)]]
56 arbitraryJudgments n = sized $ \s -> do
57 minG <- choose (fromEnum(minBound::g), fromEnum(maxBound::g))
58 maxG <- choose (minG, fromEnum(maxBound::g))
59 let gs::[g] = toEnum minG`enumFromTo`toEnum maxG
60 let lg = maxG - minG + 1
62 cs <- resize s $ arbitrarySizedNaturalSum lg
63 cs' <- arbitraryPad (lg - length cs) (return 0) cs
64 return $ zip gs $ fromIntegral <$> cs'
66 -- | @arbitrarySizedNaturalSum maxLen@
67 -- arbitrarily chooses a list of 'length' at most 'maxLen',
68 -- containing 'Int's summing up to 'sized'.
69 arbitrarySizedNaturalSum :: Int -> Gen [Int]
70 arbitrarySizedNaturalSum maxLen = sized (go maxLen)
72 go :: Int -> Int -> Gen [Int]
73 go len tot | len <= 0 = return []
74 | len == 1 = return [tot]
75 | tot <= 0 = return [tot]
78 (d:) <$> go (len-1) (tot - d)
80 -- | @arbitraryPad n pad xs@
81 -- arbitrarily grows list 'xs' with 'pad' elements
83 arbitraryPad :: (Num i, Integral i) => i -> Gen a -> [a] -> Gen [a]
84 arbitraryPad n pad [] = replicateM (fromIntegral n) pad
85 arbitraryPad n pad xs = do
88 then arbitraryPad r pad xs'
91 go r xs' | r <= 0 = return (0,xs')
92 go r [] = arbitrary >>= \b ->
93 if b then pad >>= \p -> ((p:)<$>) <$> go (r-1) []
95 go r (x:xs') = arbitrary >>= \b ->
96 if b then pad >>= \p -> (([p,x]++)<$>) <$> go (r-1) xs'
97 else ((x:)<$>) <$> go r xs'
99 -- | Like 'nub', but O(n * log n).
100 nubList :: Ord a => [a] -> [a]
101 nubList = go Set.empty where
103 go s (x:xs) | x`Set.member`s = go s xs
104 | otherwise = x:go (Set.insert x s) xs
106 instance Arbitrary G6 where
107 arbitrary = arbitraryBoundedEnum
108 instance (Arbitrary g, Bounded g, Enum g, Ord g, Show g) => Arbitrary (Merit g) where
109 arbitrary = fromList . head <$> arbitraryJudgments 1
110 shrink (Merit m) = Merit <$> shrink m
112 ( Arbitrary c, Bounded c, Enum c, Eq c, Hashable c, Show c
113 , Arbitrary g, Bounded g, Enum g, Ord g, Show g
114 ) => Arbitrary (MeritByChoice c g) where
116 minP <- choose (fromEnum(minBound::c), fromEnum(maxBound::c))
117 maxP <- choose (minP, fromEnum(maxBound::c))
118 let ps = toEnum minP`enumFromTo`toEnum maxP
119 let ms = (fromList <$>) <$> arbitraryJudgments (maxP - minP + 1)
120 fromList . zip ps <$> ms
121 instance (Bounded g, Eq g, Integral g, Arbitrary g) => Arbitrary (MajorityValue g) where
122 arbitrary = head . (MajorityValue <$>) <$> arbitraryJudgments 1
123 shrink (MajorityValue vs) = MajorityValue <$> shrink vs
125 -- * Type 'SameLength'
126 newtype SameLength a = SameLength a
128 instance Functor SameLength where
129 fmap f (SameLength x) = SameLength (f x)
130 instance (Arbitrary g, Bounded g, Enum g) => Arbitrary (SameLength (MajorityValue g, MajorityValue g)) where
132 SameLength (x,y) <- arbitrary
133 return $ SameLength (MajorityValue x, MajorityValue y)
134 instance (Arbitrary g, Bounded g, Enum g, Ord g, Show g) => Arbitrary (SameLength (Merit g, Merit g)) where
136 SameLength (x,y) <- arbitrary
137 return $ SameLength (fromList x, fromList y)
138 instance (Arbitrary g, Bounded g, Enum g) => Arbitrary (SameLength ([(g,Share)], [(g,Share)])) where
140 vs <- arbitraryJudgments 2
142 [x,y] -> return $ SameLength (x,y)