Skip to content

Commit

Permalink
Move equipartitionNatural to Numeric.Util.
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanknowles committed Mar 2, 2021
1 parent 512a118 commit 6b77d29
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 67 deletions.
Expand Up @@ -54,7 +54,6 @@ module Cardano.Wallet.Primitive.CoinSelection.MA.RoundRobin
, assignCoinsToChangeMaps

-- * Partitioning
, equipartitionNatural
, equipartitionTokenBundleWithMaxQuantity
, equipartitionTokenBundlesWithMaxQuantity
, equipartitionTokenMap
Expand Down Expand Up @@ -88,7 +87,7 @@ import Prelude
import Algebra.PartialOrd
( PartialOrd (..) )
import Cardano.Numeric.Util
( padCoalesce, partitionNatural, unsafePartitionNatural )
( equipartitionNatural, padCoalesce, partitionNatural )
import Cardano.Wallet.Primitive.Types.Coin
( Coin (..), addCoin, subtractCoin, sumCoins )
import Cardano.Wallet.Primitive.Types.TokenBundle
Expand Down Expand Up @@ -1216,22 +1215,6 @@ equipartitionCoin c =
-- value.
fmap unsafeNaturalToCoin . equipartitionNatural (coinToNatural c)

-- | Computes the equipartition of a natural number into 'n' smaller numbers.
--
equipartitionNatural
:: HasCallStack
=> Natural
-- ^ The natural number to be partitioned.
-> NonEmpty a
-- ^ Represents the number of portions in which to partition the number.
-> NonEmpty Natural
-- ^ The partitioned numbers.
equipartitionNatural n count =
-- Note: due to the behaviour of the underlying partition algorithm, a
-- simple list reversal is enough to ensure that the resultant list is
-- sorted in ascending order.
NE.reverse $ unsafePartitionNatural n (1 <$ count)

-- | Computes the equipartition of a token map into 'n' smaller maps.
--
-- Each asset is partitioned independently.
Expand Down
Expand Up @@ -35,7 +35,6 @@ import Cardano.Wallet.Primitive.CoinSelection.MA.RoundRobin
, assetSelectionLens
, assignCoinsToChangeMaps
, coinSelectionLens
, equipartitionNatural
, equipartitionTokenBundleWithMaxQuantity
, equipartitionTokenBundlesWithMaxQuantity
, equipartitionTokenMap
Expand Down Expand Up @@ -162,7 +161,6 @@ import Test.QuickCheck
, suchThat
, withMaxSuccess
, (.&&.)
, (.||.)
, (===)
, (==>)
)
Expand Down Expand Up @@ -321,17 +319,6 @@ spec = describe "Cardano.Wallet.Primitive.CoinSelection.MA.RoundRobinSpec" $
unitTests "makeChangeForUserSpecifiedAsset"
unit_makeChangeForUserSpecifiedAsset

parallel $ describe "Equipartitioning natural numbers" $ do

it "prop_equipartitionNatural_fair" $
property prop_equipartitionNatural_fair
it "prop_equipartitionNatural_length" $
property prop_equipartitionNatural_length
it "prop_equipartitionNatural_order" $
property prop_equipartitionNatural_order
it "prop_equipartitionNatural_sum" $
property prop_equipartitionNatural_sum

parallel $ describe "Equipartitioning token maps" $ do

it "prop_equipartitionTokenMap_fair" $
Expand Down Expand Up @@ -1849,40 +1836,6 @@ unit_makeChangeForUserSpecifiedAsset =
assetC :: AssetId
assetC = AssetId (UnsafeTokenPolicyId $ Hash "A") (UnsafeTokenName "2")

--------------------------------------------------------------------------------
-- Equipartitioning natural numbers
--------------------------------------------------------------------------------

-- Test that natural numbers are equipartitioned fairly:
--
-- Each portion must be within unity of the ideal portion.
--
prop_equipartitionNatural_fair
:: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_fair n count = (.||.)
(difference === 0)
(difference === 1)
where
difference :: Natural
difference = F.maximum results - F.minimum results

results :: NonEmpty Natural
results = equipartitionNatural n count

prop_equipartitionNatural_length :: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_length n count =
NE.length (equipartitionNatural n count) === NE.length count

prop_equipartitionNatural_order :: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_order n count =
NE.sort results === results
where
results = equipartitionNatural n count

prop_equipartitionNatural_sum :: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_sum n count =
F.sum (equipartitionNatural n count) === n

--------------------------------------------------------------------------------
-- Equipartitioning token maps
--------------------------------------------------------------------------------
Expand Down
29 changes: 28 additions & 1 deletion lib/numeric/src/Cardano/Numeric/Util.hs
Expand Up @@ -3,9 +3,15 @@
{-# LANGUAGE TypeApplications #-}

module Cardano.Numeric.Util
( padCoalesce
(
-- * Coalescing values
padCoalesce

-- * Partitioning natural numbers
, equipartitionNatural
, partitionNatural
, unsafePartitionNatural

) where

import Prelude hiding
Expand Down Expand Up @@ -101,6 +107,27 @@ padCoalesce sourceUnsorted target
-- Partitioning natural numbers
--------------------------------------------------------------------------------

-- | Computes the equipartition of a natural number into 'n' smaller numbers.
--
-- An /equipartition/ of a natural number 'n' is a /partition/ of that number
-- into 'n' smaller numbers whose values differ by no more than 1.
--
-- The resultant list is sorted in ascending order.
--
equipartitionNatural
:: HasCallStack
=> Natural
-- ^ The natural number to be partitioned.
-> NonEmpty a
-- ^ Represents the number of portions in which to partition the number.
-> NonEmpty Natural
-- ^ The partitioned numbers.
equipartitionNatural n count =
-- Note: due to the behaviour of the underlying partition algorithm, a
-- simple list reversal is enough to ensure that the resultant list is
-- sorted in ascending order.
NE.reverse $ unsafePartitionNatural n (1 <$ count)

-- | Partitions a natural number into a number of parts, where the size of each
-- part is proportional to the size of its corresponding element in the given
-- list of weights, and the number of parts is equal to the number of weights.
Expand Down
48 changes: 47 additions & 1 deletion lib/numeric/test/unit/Cardano/Numeric/UtilSpec.hs
Expand Up @@ -8,7 +8,7 @@ module Cardano.Numeric.UtilSpec
import Prelude

import Cardano.Numeric.Util
( padCoalesce, partitionNatural )
( equipartitionNatural, padCoalesce, partitionNatural )
import Data.List.NonEmpty
( NonEmpty (..) )
import Data.Maybe
Expand All @@ -31,6 +31,7 @@ import Test.QuickCheck
, shrinkIntegral
, withMaxSuccess
, (.&&.)
, (.||.)
, (===)
)

Expand All @@ -49,6 +50,17 @@ spec = do
it "prop_padCoalesce_sum" $
property $ prop_padCoalesce_sum @(Sum Int)

describe "equipartitionNatural" $ do

it "prop_equipartitionNatural_fair" $
property prop_equipartitionNatural_fair
it "prop_equipartitionNatural_length" $
property prop_equipartitionNatural_length
it "prop_equipartitionNatural_order" $
property prop_equipartitionNatural_order
it "prop_equipartitionNatural_sum" $
property prop_equipartitionNatural_sum

describe "partitionNatural" $ do

it "prop_partitionNatural_length" $
Expand Down Expand Up @@ -79,6 +91,40 @@ prop_padCoalesce_sum
prop_padCoalesce_sum source target =
F.fold source === F.fold (padCoalesce source target)

--------------------------------------------------------------------------------
-- Equipartitioning natural numbers
--------------------------------------------------------------------------------

-- Test that natural numbers are equipartitioned fairly:
--
-- Each portion must be within unity of the ideal portion.
--
prop_equipartitionNatural_fair
:: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_fair n count = (.||.)
(difference === 0)
(difference === 1)
where
difference :: Natural
difference = F.maximum results - F.minimum results

results :: NonEmpty Natural
results = equipartitionNatural n count

prop_equipartitionNatural_length :: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_length n count =
NE.length (equipartitionNatural n count) === NE.length count

prop_equipartitionNatural_order :: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_order n count =
NE.sort results === results
where
results = equipartitionNatural n count

prop_equipartitionNatural_sum :: Natural -> NonEmpty () -> Property
prop_equipartitionNatural_sum n count =
F.sum (equipartitionNatural n count) === n

--------------------------------------------------------------------------------
-- Partitioning natural numbers
--------------------------------------------------------------------------------
Expand Down

0 comments on commit 6b77d29

Please sign in to comment.