hjugement-2.0.1.20190208: test/HUnit/Rank.hs
module HUnit.Rank where
import Data.Bool
import Data.Eq (Eq(..))
import Data.Foldable (Foldable(..))
import Data.Function (($), (.))
import Data.Functor ((<$>))
import Data.List
import Data.Ord (Ord(..))
import Data.Semigroup (Semigroup(..))
import Data.Ratio
import GHC.Exts (IsList(..))
import Majority.Judgment
import Prelude (Integer, Num(..), fromIntegral)
import Test.Tasty
import Test.Tasty.HUnit
import Text.Show (Show(..))
import QuickCheck.Merit ()
import QuickCheck.Value ()
hunit :: TestTree
hunit = testGroup "Rank"
[ testGroup "lexicographic"
[ testLexRank 1 1
, testLexRank 5 4
, testLexRank 5 5
, testLexRank 10 5
, testLexRank 15 5
]
, testGroup "majority"
[ testMajRank 1 1
, testMajRank 5 4
, testMajRank 5 5
, testMajRank 10 5
, testMajRank 15 5
, testMajRank 25 4
]
]
testLexRank :: JS -> GS -> TestTree
testLexRank js gs =
testGroup ("js="<>show js<>" gs="<>show gs)
[ testCase "rankOfMerit" $
rankOfMerit gs <$> merits js gs
@?= [0..lastRank js gs]
, testCase "Rank -> Merit -> Rank" $
let ranks = [0..lastRank js gs] in
rankOfMerit gs .
meritOfRank js gs
<$> ranks @?= ranks
, testCase "Merit -> Rank -> Merit" $
let dists = merits js gs in
meritOfRank js gs .
rankOfMerit gs
<$> dists @?= dists
]
testMajRank :: JS -> GS -> TestTree
testMajRank js gs =
testGroup ("js="<>show js<>" gs="<>show gs)
[ testCase "rankOfMajorityValue" $
rankOfMajorityValue gs <$> majorityValues js gs
@?= [0..lastRank js gs]
]
-- | Generate all distributions possible, in lexicographic order.
merits :: JS -> GS -> [[G]]
merits js0 gs = go 0 js0
where
go g js
| g == gs - 1 = [replicate (fromIntegral js) g]
| otherwise = concat
[ (replicate (fromIntegral r) g <>) <$> go (g+1) (js-r)
| r <- reverse [0..js]
]
-- | Generate all distributions possible, in majority order.
majorityValues :: JS -> GS -> [MajorityValue (Ranked ())]
majorityValues js0 gs = sort $ majorityValue . fromList <$> go 0 js0
where
go g js
| g == gs - 1 = [[(Ranked (g, ()), js%1)]]
| otherwise = concat
[ ((Ranked (g, ()), r%1) :) <$> go (g+1) (js-r)
| r <- reverse [0..js]
]
rankOfMerit :: GS -> [Integer] -> Integer
rankOfMerit gsI dist = go 0 ranks dist
where
js = fromIntegral $ length dist
gs = fromIntegral gsI
ranks = reverse $ reverse . take gs <$> take js pascalDiagonals
go g0 (p:ps) (d:ds) =
sum (take dI p) +
go d (drop dI <$> ps) ds
where dI = fromIntegral (d - g0)
go _ _ _ = 0
meritOfRank :: JS -> GS -> Integer -> [Integer]
meritOfRank jsI gsI = go 0 ranks
where
js = fromIntegral jsI
gs = fromIntegral gsI
ranks = reverse $ reverse . take gs <$> take js pascalDiagonals
go _g0 [] _r = []
go g0 (p:ps) r = g : go g (drop s <$> ps) (r-dr)
where
skip = takeWhile (<= r) $ scanl1 (+) p
s = length skip
g = g0 + fromIntegral s
dr = if null skip then 0 else last skip
-- | Diagonals of Pascal's triangle.
pascalDiagonals :: [[Integer]]
pascalDiagonals = repeat 1 : (scanl1 (+) <$> pascalDiagonals)