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)