1 module HUnit.Rank where
4 import Data.Eq (Eq(..))
5 import Data.Foldable (Foldable(..))
6 import Data.Function (($), (.))
7 import Data.Functor ((<$>))
9 import Data.Ord (Ord(..))
11 import Data.Semigroup (Semigroup(..))
12 import GHC.Exts (IsList(..))
13 import Majority.Judgment
14 import Prelude (Integer, Num(..), fromIntegral)
16 import Test.Tasty.HUnit
17 import Text.Show (Show(..))
19 import QuickCheck.Merit ()
20 import QuickCheck.Value ()
23 hunit = testGroup "Rank"
24 [ testGroup "lexicographic"
31 , testGroup "majority"
54 testLexRank :: JS -> GS -> TestTree
56 testGroup ("js="<>show js<>" gs="<>show gs)
57 [ testCase "lexicographicRankOfMerit" $
58 lexicographicRankOfMerit gs <$> merits js gs
59 @?= [0..lastRank js gs]
60 , testCase "lexRankOfMerit . meritOfLexRank == id" $
61 let ranks = [0..lastRank js gs] in
62 lexicographicRankOfMerit gs . meritOfLexicographicRank js gs
64 , testCase "meritOfLexRank . lexRankOfMerit == id" $
65 let dists = merits js gs in
66 meritOfLexicographicRank js gs . lexicographicRankOfMerit gs
70 testMajRank :: JS -> GS -> TestTree
72 let mvs = majorityValues js gs in
73 testGroup ("js="<>show js<>" gs="<>show gs<>" ("<>show (countMerits js gs)<>" merits)")
74 [ testCase "listMediansBefore" $
75 sum (countMedian js gs <$> listMediansBefore js gs 0 (Median (gs,gs)))
77 , testCase "majorityValueOfRank" $
78 majorityValueOfRank js gs <$> [0..lastRank js gs] @?= mvs
79 , testCase "rankOfMajorityValue" $
80 rankOfMajorityValue gs <$> mvs @?= [0..lastRank js gs]
81 {- NOTE: already implied by the previous tests.
82 , testCase "rankOfMV . mvOfRank == id" $
83 rankOfMajorityValue gs . majorityValueOfRank js gs
84 <$> [0..lastRank js gs] @?= [0..lastRank js gs]
85 , testCase "mvOfRank . rankOfMV == id" $
86 majorityValueOfRank js gs . rankOfMajorityValue gs
91 -- | Generate all distributions possible, in lexicographic order.
92 merits :: JS -> GS -> [[G]]
93 merits js0 gs = go 0 js0
96 | g == gs - 1 = [replicate (fromIntegral js) g]
98 [ (replicate (fromIntegral r) g <>) <$> go (g+1) (js-r)
99 | r <- reverse [0..js]
102 -- | Generate all distributions possible, in majority order.
103 majorityValues :: JS -> GS -> [MajorityValue (Ranked ())]
104 majorityValues js0 gs = sort $ majorityValue . fromList <$> go 0 js0
107 | g == gs - 1 = [[(Ranked (g, ()), js%1)]]
109 [ ((Ranked (g, ()), r%1) :) <$> go (g+1) (js-r)
110 | r <- reverse [0..js]
113 lexicographicRankOfMerit :: GS -> [Integer] -> Integer
114 lexicographicRankOfMerit gsI dist = go 0 ranks dist
116 js = fromIntegral $ length dist
117 gs = fromIntegral gsI
118 ranks = reverse $ reverse . take gs <$> take js pascalDiagonals
119 go g0 (p:ps) (d:ds) =
121 go d (drop dI <$> ps) ds
122 where dI = fromIntegral (d - g0)
125 meritOfLexicographicRank :: JS -> GS -> Integer -> [Integer]
126 meritOfLexicographicRank jsI gsI = go 0 ranks
128 js = fromIntegral jsI
129 gs = fromIntegral gsI
130 ranks = reverse $ reverse . take gs <$> take js pascalDiagonals
132 go g0 (p:ps) r = g : go g (drop s <$> ps) (r-dr)
134 skip = takeWhile (<= r) $ scanl1 (+) p
136 g = g0 + fromIntegral s
137 dr = if null skip then 0 else last skip
139 -- | Diagonals of Pascal's triangle.
140 pascalDiagonals :: [[Integer]]
141 pascalDiagonals = repeat 1 : (scanl1 (+) <$> pascalDiagonals)