module Math.Apportionment (
largestRemainder,
largestRemainderScaled,
highestAveragesScaled,
dHondtDivisors,
sainteLagueDivisors,
) where
import Control.Functor.HT (outerProduct, )
import qualified Data.Foldable as Fold
import qualified Data.List.HT as ListHT
import qualified Data.List as List
import qualified Data.Map as Map
import Data.Function.HT (compose2, )
import Data.Tuple.HT (mapSnd, )
import Data.Ord.HT (comparing, )
_largestRemainderSort :: (RealFrac a) => [a] -> [Int]
_largestRemainderSort xs =
let (d, intFracs) = fractions xs
(intUps, intDowns) =
splitAt d $ map fst $
List.sortBy (comparing (negate.snd)) intFracs
in map (1+) intUps ++ intDowns
largestRemainder :: (RealFrac a) => [a] -> [Int]
largestRemainder = largestRemainderCore . fractions
largestRemainderScaled :: (RealFrac a) => Int -> [a] -> [Int]
largestRemainderScaled s = largestRemainderCore . fractionsScaled s
type Fractions a = (Int, [(Int, a)])
largestRemainderCore :: (RealFrac a) => Fractions a -> [Int]
largestRemainderCore (d, intFracs) =
let (intUps, intDowns) =
splitAt d $ map (mapSnd fst) $
List.sortBy (comparing (negate . snd . snd)) $
zip [(0::Int) .. ] intFracs
in map snd $ List.sortBy (comparing fst) $
map (mapSnd (1+)) intUps ++ intDowns
fractions :: (RealFrac a) => [a] -> Fractions a
fractions xs =
let xsum = round $ sum xs
intFracs = map properFraction xs
isum = sum $ map fst intFracs
in (xsum-isum, intFracs)
fractionsScaled :: (RealFrac a) => Int -> [a] -> Fractions a
fractionsScaled xsum xs =
let c = fromIntegral xsum / sum xs
intFracs = map (properFraction . (* c)) xs
isum = sum $ map fst intFracs
in (xsum-isum, intFracs)
highestAveragesScaled :: (RealFrac a) => [a] -> Int -> [a] -> [Int]
highestAveragesScaled divs s xs =
let m = Map.fromList $ zip [(0::Int) ..] xs
in Map.elems $ flip Map.union (fmap (const 0) m) $
Map.fromListWith (+) $ map (mapSnd (const 1)) $
take s $ Fold.foldl (ListHT.mergeBy (compose2 (>=) snd)) [] $
Map.mapWithKey (map . (,)) $ outerProduct (/) m divs
dHondtDivisors :: Num a => [a]
dHondtDivisors = iterate (1+) 1
sainteLagueDivisors :: Num a => [a]
sainteLagueDivisors = iterate (2+) 1