diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2017-06-26 09:09:20 +0300 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2017-06-26 09:09:20 +0300 |
commit | c82a64701ff64283e95efbbd6df614cd1e775e52 (patch) | |
tree | 771140e4a38137796e1bbd9717274b86691a2ba7 /lib | |
parent | 6f18125faaf7afa6d543b074eca65836a13a372b (diff) | |
download | molodivo-c82a64701ff64283e95efbbd6df614cd1e775e52.tar.gz |
Refactor
Mainly because further development will not preserve
bill order in lists and we'll need to maintain mapping
internally.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Malodivo/Budget.hs | 69 | ||||
-rw-r--r-- | lib/Malodivo/Types/District.hs | 34 | ||||
-rw-r--r-- | lib/Malodivo/Types/Ministry.hs | 25 |
3 files changed, 80 insertions, 48 deletions
diff --git a/lib/Malodivo/Budget.hs b/lib/Malodivo/Budget.hs index a707cec..657cf14 100644 --- a/lib/Malodivo/Budget.hs +++ b/lib/Malodivo/Budget.hs @@ -3,20 +3,32 @@ Budget planning in the Kingdom of Malodivo. -} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} + module Malodivo.Budget - ( DistrictFunds - , normalizeDown - , manyToOne + ( BillBudget , manyToMany + , manyToOne + , normalizeDown ) where +import GHC.Generics (Generic) + +import Data.Aeson (ToJSON) import qualified Data.HashMap.Strict as HM import qualified Malodivo.Types.Bill as B import qualified Malodivo.Types.District as D --- | Convenient type. Describes available or allocated funds per 'District'. -type DistrictFunds = HM.HashMap D.District Integer +-- | This is output type. It describes contribution of each district into a bill. +data BillBudget = BillBudget + { bill :: B.Bill + , districts :: [D.DistrictInfo] + } deriving (Generic, ToJSON) + +zipBills :: [B.Bill] -> [D.DistrictFunds] -> [BillBudget] +zipBills = zipWith $ \b df -> BillBudget {bill = b, districts = D.df2di df} {-| Normalize list of integers, i. e. proportionally decrease each list element @@ -125,52 +137,29 @@ True -} manyToOne :: - DistrictFunds -- ^ Amounts of available funds per district. + D.DistrictFunds -- ^ Amounts of available funds per district. -> B.Bill -- ^ A bill requiring funding. - -> DistrictFunds -- ^ Contribution of each district. -manyToOne funds bill = fundRaising (B.amount bill) funds + -> D.DistrictFunds -- ^ Contribution of each district. +manyToOne df b = fundRaising (B.amount b) df {-| Districts funding multiple bills. No constraints. - ->>> :set -XOverloadedStrings ->>> import Data.Maybe (fromJust) ->>> import qualified Data.HashMap.Strict as HM ->>> import qualified Malodivo.Types.Bill as B ->>> import qualified Malodivo.Types.District as D ->>> import qualified Malodivo.Types.Ministry as M - ->>> let billA = B.Bill { B.amount = 30, B.name = "Bill A", B.ministry = M.Science } ->>> let billB = B.Bill { B.amount = 10, B.name = "Bill B", B.ministry = M.Welfare } ->>> let equalAmountBills = take 3 (repeat billB) ->>> let nonEqualAmountBills = [billA, billB] - - -If all bills requires the same amount, funds of a single district are -allocated evenly: - ->>> let funds = HM.fromList [(D.Palolene, 100)] ->>> let contribution = manyToMany funds equalAmountBills ->>> all (== B.amount (head equalAmountBills)) (fromJust . HM.lookup D.Palolene <$> contribution) -True - -} manyToMany :: - DistrictFunds -- ^ Amounts of available funds per district. + D.DistrictFunds -- ^ Amounts of available funds per district. -> [B.Bill] -- ^ Bills requiring funding. - -> [DistrictFunds] -- ^ Contribution of each district to each bill. -manyToMany funds bills = - flip fundRaising funds <$> normalizeDown fundsTotal billAmounts + -> [BillBudget] -- ^ Contribution of each district to each bill. +manyToMany df bills = zipBills bills allocated where billAmounts = B.amount <$> bills - fundsTotal = sum $ HM.elems funds + fundsTotal = sum $ HM.elems df + allocated = flip fundRaising df <$> normalizeDown fundsTotal billAmounts -- | Helper function to maintain DRY and backward compatibility. fundRaising :: Integer -- ^ Amount to be raised. - -> DistrictFunds -- ^ Amounts of available funds per district. - -> DistrictFunds -- ^ Contribution of each district. -fundRaising needed df = HM.fromList $ zip districts funds' + -> D.DistrictFunds -- ^ Amounts of available funds per district. + -> D.DistrictFunds -- ^ Contribution of each district. +fundRaising needed df = HM.fromList $ zip ds (normalizeDown needed fs) where - (districts, funds) = unzip $ HM.toList df - funds' = normalizeDown needed funds + (ds, fs) = unzip $ HM.toList df diff --git a/lib/Malodivo/Types/District.hs b/lib/Malodivo/Types/District.hs index 058377b..2e78634 100644 --- a/lib/Malodivo/Types/District.hs +++ b/lib/Malodivo/Types/District.hs @@ -18,11 +18,17 @@ Just [Lakos] module Malodivo.Types.District ( District(..) + , DistrictFunds + , DistrictInfo(..) + , di2df + , df2di ) where +import Control.Arrow ((&&&)) import GHC.Generics (Generic) import Data.Aeson (FromJSON, ToJSON) +import qualified Data.HashMap.Strict as HM import Data.Hashable (Hashable) -- | District of the Kindom of Malodivo. @@ -30,10 +36,24 @@ data District = Palolene | SouthernPalolene | Lakos - deriving ( Eq - , Hashable - , Show - , Generic - , FromJSON - , ToJSON - ) + deriving (Eq, Hashable, Show, Generic, FromJSON, ToJSON) + +-- | Convenient type. +type DistrictFunds = HM.HashMap District Integer + +{-| +We use this data type instead of 'DistrictFunds' for JSON, +because maps other than @HashMap Text _@ are hard to decode/encode see +<https://github.com/bos/aeson/issues/79>. Anyway, we still have type-checked +typos-proof structure. +-} +data DistrictInfo = DistrictInfo + { name :: District + , amount :: Integer + } deriving (Generic, FromJSON, ToJSON) + +di2df :: [DistrictInfo] -> DistrictFunds +di2df = HM.fromListWith (+) . map (name &&& amount) + +df2di :: DistrictFunds -> [DistrictInfo] +df2di = map (\(n, a) -> DistrictInfo {name = n, amount = a}) . HM.toList diff --git a/lib/Malodivo/Types/Ministry.hs b/lib/Malodivo/Types/Ministry.hs index c3e315b..f3878d1 100644 --- a/lib/Malodivo/Types/Ministry.hs +++ b/lib/Malodivo/Types/Ministry.hs @@ -18,15 +18,38 @@ Just [Science] module Malodivo.Types.Ministry ( Ministry(..) + , MinistryInfo + , MinistryLimits + , mi2ml ) where +import Control.Arrow ((&&&)) import GHC.Generics (Generic) import Data.Aeson (FromJSON, ToJSON) +import qualified Data.HashMap.Strict as HM +import Data.Hashable (Hashable) -- | Ministry of the Kingdom of Malodivo. data Ministry = Defense | Science | Welfare - deriving (Show, Generic, FromJSON, ToJSON) + deriving (Eq, Hashable, Show, Generic, FromJSON, ToJSON) + +-- | Convenient type. Describes maximum money a ministry can get. +type MinistryLimits = HM.HashMap Ministry Integer + +{-| +We use this data type instead of 'MinistryLimits' for JSON, +because maps other than @HashMap Text _@ are hard to decode/encode see +<https://github.com/bos/aeson/issues/79>. Anyway, we still have type-checked +typos-proof structure. +-} +data MinistryInfo = MinistryInfo + { name :: Ministry + , amount :: Integer + } deriving (Generic, FromJSON) + +mi2ml :: [MinistryInfo] -> MinistryLimits +mi2ml = HM.fromListWith (+) . map (name &&& amount) |