1 module HUnit.Rank where
3 import Data.Eq (Eq(..))
4 import Data.Foldable (Foldable(..))
5 import Data.Function (($), (.))
6 import Data.Functor ((<$>))
8 import Data.Ord (Ord(..))
9 import Data.Semigroup (Semigroup(..))
11 import GHC.Exts (IsList(..))
12 import Majority.Judgment
13 import Prelude (Integer, Num(..), fromIntegral)
15 import Test.Tasty.HUnit
16 import Text.Show (Show(..))
18 import QuickCheck.Merit ()
19 import QuickCheck.Value ()
22 hunit = testGroup "Rank"
23 [ testGroup "lexicographic"
30 , testGroup "majority"
40 testLexRank :: JS -> GS -> TestTree
42 testGroup ("js="<>show js<>" gs="<>show gs)
43 [ testCase "rankOfMerit" $
44 rankOfMerit gs <$> merits js gs
45 @?= [0..lastRank js gs]
46 , testCase "Rank -> Merit -> Rank" $
47 let ranks = [0..lastRank js gs] in
51 , testCase "Merit -> Rank -> Merit" $
52 let dists = merits js gs in
58 testMajRank :: JS -> GS -> TestTree
60 testGroup ("js="<>show js<>" gs="<>show gs)
61 [ testCase "rankOfMajorityValue" $
62 rankOfMajorityValue gs <$> majorityValues js gs
63 @?= [0..lastRank js gs]
66 -- | Generate all distributions possible, in lexicographic order.
67 merits :: JS -> GS -> [[G]]
68 merits js0 gs = go 0 js0
71 | g == gs - 1 = [replicate (fromIntegral js) g]
73 [ (replicate (fromIntegral r) g <>) <$> go (g+1) (js-r)
74 | r <- reverse [0..js]
77 -- | Generate all distributions possible, in majority order.
78 majorityValues :: JS -> GS -> [MajorityValue (Ranked ())]
79 majorityValues js0 gs = sort $ majorityValue . fromList <$> go 0 js0
82 | g == gs - 1 = [[(Ranked (g, ()), js%1)]]
84 [ ((Ranked (g, ()), r%1) :) <$> go (g+1) (js-r)
85 | r <- reverse [0..js]
88 rankOfMerit :: GS -> [Integer] -> Integer
89 rankOfMerit gsI dist = go 0 ranks dist
91 js = fromIntegral $ length dist
93 ranks = reverse $ reverse . take gs <$> take js pascalDiagonals
96 go d (drop dI <$> ps) ds
97 where dI = fromIntegral (d - g0)
100 meritOfRank :: JS -> GS -> Integer -> [Integer]
101 meritOfRank jsI gsI = go 0 ranks
103 js = fromIntegral jsI
104 gs = fromIntegral gsI
105 ranks = reverse $ reverse . take gs <$> take js pascalDiagonals
107 go g0 (p:ps) r = g : go g (drop s <$> ps) (r-dr)
109 skip = takeWhile (<= r) $ scanl1 (+) p
111 g = g0 + fromIntegral s
112 dr = if null skip then 0 else last skip
114 -- | Diagonals of Pascal's triangle.
115 pascalDiagonals :: [[Integer]]
116 pascalDiagonals = repeat 1 : (scanl1 (+) <$> pascalDiagonals)