apportionment-0.0.0.4: Round a set of numbers while maintaining its sum
Safe HaskellSafe-Inferred
LanguageHaskell2010

Math.Apportionment

Synopsis

Documentation

largestRemainder :: RealFrac a => [a] -> [Int] Source #

This function rounds values such that the sum of the rounded values matches the rounded sum of the original values.

Also known as Hare-Niemeyer method. https://en.wikipedia.org/wiki/Largest_remainder_method

Input values must be non-negative, otherwise properFraction bites us.

>>> largestRemainder [1,2,3::Rational]
[1,2,3]
>>> largestRemainder [1.1,2.2,3.3,4.4::Rational]
[1,2,3,5]
\xs -> xs == largestRemainder (map fromIntegral xs :: [Rational])
forAllNonNegatives $ \xs -> round (sum xs) == sum (largestRemainder (xs :: [Rational]))

largestRemainderScaled :: RealFrac a => Int -> [a] -> [Int] Source #

largestRemainderScaled s xs scales and rounds the values in xs such that their sum becomes s.

>>> largestRemainderScaled 100 [1,2,3::Rational]
[17,33,50]

That is, it returns integral percentages almost proportional to 1:2:3.

>>> largestRemainderScaled 100 [1,10,100::Rational]
[1,9,90]
forAllNonNegatives $ \xs -> xs == largestRemainderScaled (sum xs) (map fromIntegral xs :: [Rational])
\(QC.Positive s) -> forAllNonNegatives $ \xs -> s == sum (largestRemainderScaled s (xs :: [Rational]))

highestAveragesScaled :: RealFrac a => [a] -> Int -> [a] -> [Int] Source #

https://en.wikipedia.org/wiki/Highest_averages_method

In highestAveragesScaled divs s xs, divs must be an infinite list of strictly increasing positive numbers. E.g. highestAveragesScaled dHondtDivisors s xs runs the d'Hondt method.

>>> highestAveragesScaled dHondtDivisors 100 [1,2,3::Rational]
[17,33,50]
>>> highestAveragesScaled dHondtDivisors 100 [1,10,100::Rational]
[0,9,91]
>>> highestAveragesScaled sainteLagueDivisors 100 [1,2,3::Rational]
[17,33,50]
>>> highestAveragesScaled sainteLagueDivisors 100 [1,10,100::Rational]
[1,9,90]
forAllNonNegatives $ \xs -> xs == highestAveragesScaled dHondtDivisors (sum xs) (map fromIntegral xs :: [Rational])
forAllNonNegatives $ \xs -> xs == highestAveragesScaled sainteLagueDivisors (sum xs) (map fromIntegral xs :: [Rational])
\(QC.Positive s) -> forAllNonNegatives $ \xs -> s == sum (highestAveragesScaled dHondtDivisors s (xs :: [Rational]))
\(QC.Positive s) -> forAllNonNegatives $ \xs -> s == sum (highestAveragesScaled sainteLagueDivisors s (xs :: [Rational]))