Update to new symantic and draft Modules rendition.
[comptalang.git] / lib / Hcompta / Balance.hs
index fe19a14831a813d09ed831de48358b014cf77b16..43ce15e05996e426a20d538132ca28b4b83a72ac 100644 (file)
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE NamedFieldPuns #-}
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# OPTIONS_GHC -fno-warn-deprecations #-}
- -- FIXME: to be removed when dropping GHC-7.6 support
-
--- | Balance
+{-# OPTIONS_GHC -fno-warn-orphans #-}
 module Hcompta.Balance where
 
--- import           Control.Applicative (Const(..))
--- import           Control.Arrow (second)
 import           Control.DeepSeq (NFData(..))
 import           Control.Exception (assert)
 import           Data.Bool
 import           Data.Data
 import           Data.Either (Either(..))
 import           Data.Eq (Eq(..))
-import           Data.Foldable (Foldable(..))
 import qualified Data.Foldable as Foldable
 import           Data.Function (($), (.), const, flip)
--- import           Data.Functor.Identity (Identity(..))
 import           Data.Map.Strict (Map)
 import qualified Data.Map.Strict as Map
--- import qualified Data.MonoTraversable as MT
 import           Data.Monoid (Monoid(..))
+import qualified Data.MonoTraversable as MT
 import           Data.Ord (Ord(..))
+import           Data.Semigroup (Semigroup(..))
+import qualified Data.Sequences as Seqs
 import qualified Data.Strict.Maybe as Strict
 import           Data.TreeMap.Strict (TreeMap(..))
 import qualified Data.TreeMap.Strict as TreeMap
 import           Data.Tuple (curry, fst, snd)
 import           Data.Typeable ()
-import           Prelude (undefined)
+import           Prelude (seq, undefined)
 import           Text.Show (Show(..))
 
-import           Hcompta.Account
-import           Hcompta.Amount
+import           Hcompta.Has
 import qualified Hcompta.Lib.Foldable as Foldable
 import qualified Hcompta.Lib.Strict as Strict
-import           Hcompta.Posting
 import           Hcompta.Quantity
 
--- * Class 'Balance_Posting'
-
--- | A 'posting' used to produce a 'Balance'
---   must be an instance of this class.
-class Posting p => Balance_Posting p where
-       -- | 'Balance_Posting_Quantity'
-       -- enables to build a 'Balance' with some quantity
-       -- other than: 'Amount_Quantity' ('Posting_Amount' @p@);
-       -- it's useful to 'polarize' it.
-       type Balance_Posting_Quantity p
-       balance_posting_amounts
-        :: p -> Balance_Posting_Amounts p
-       balance_posting_amounts_set
-        :: Balance_Posting_Amounts p
-        -> p -> p
-
--- ** Type 'Balance_Posting_Amounts'
-
--- | 'Balance' operations works on this type of 'Amount's.
-type Balance_Posting_Amounts p
- = Map (Amount_Unit (Posting_Amount p))
-       (Balance_Posting_Quantity p)
-
-instance -- (account, Map unit quantity)
- ( Account account
- , Amount (unit, quantity)
- -- , Amount (MT.Element amounts)
- -- , MT.MonoFoldable amounts
- ) => Balance_Posting (account, Map unit quantity) where
-       type Balance_Posting_Quantity (account, Map unit quantity) = quantity
-       balance_posting_amounts          (_, amts) = amts
-       balance_posting_amounts_set amts (acct, _) = (acct, amts)
+-- * Type 'Balance_Account'
+-- | 'Balance' operations works on this type of 'Account'.
+type Balance_Account = TreeMap.Path
+instance Get (Balance_Account acct_sect)
+             (Balance_Account acct_sect
+             ,Balance_Amounts unit qty) where get = fst
+
+-- * Type 'Balance_Amounts'
+-- | 'Balance' operations works on this type of 'Amounts'.
+type Balance_Amounts = Map
+instance Get (Balance_Amounts unit qty)
+             (Balance_Account acct_sect
+             ,Balance_Amounts unit qty) where get = snd
+instance Set (Balance_Amounts unit qty)
+             (Balance_Account acct_sect
+             ,Balance_Amounts unit qty) where set x (a, _) = (a, x)
 
 -- * Type 'Balance'
 
--- | 'Balance_Account' and 'Balance_by_Unit' of some 'Balance_Posting's.
+-- | 'Balance_Account' and 'BalByUnit' of some @post@s.
 --
 -- NOTE: to reduce memory consumption
 --       when applying 'balance_cons' incrementally,
 --       the fields are explicitely stricts.
-data Balance account_section unit quantity
+data Balance acct_sect unit qty
  =   Balance
- {   balance_by_account :: !(Balance_by_Account account_section unit quantity)
- ,   balance_by_unit    :: !(Balance_by_Unit    account_section unit quantity)
- }
- --deriving (Data, Eq, Show, Typeable)
-deriving instance -- Data
- ( Data    account_section
- , Data     unit
- , Data     quantity
- , Ord      unit
- , Ord account_section
- , Typeable unit
- , Typeable quantity
- ) => Data (Balance account_section unit quantity)
-deriving instance -- Eq
- ( Eq      account_section
- , Eq      unit
- , Eq      quantity
- ) => Eq (Balance account_section unit quantity)
-deriving instance -- Show
- ( Show    account_section
- , Show    unit
- , Show    quantity
- ) => Show (Balance account_section unit quantity)
-deriving instance -- Typeable
- Typeable3 Balance
- -- FIXME: use 'Typeable' when dropping GHC-7.6 support
-
-instance -- Monoid
- ( Addable quantity
- , Ord     unit
- , Ord     account_section
- ) => Monoid (Balance account_section unit quantity) where
-       mempty  = balance_empty
-       mappend = balance_union
-
--- ** Type 'Balance_by_Account'
-type Balance_by_Account account_section unit quantity
- = TreeMap account_section
-           (Balance_by_Account_Sum unit quantity)
-
--- *** Type 'Balance_by_Account_Sum'
--- | A sum of 'quantity's, concerning a single 'account'.
-newtype Balance_by_Account_Sum unit quantity
- =      Balance_by_Account_Sum (Map unit quantity)
- deriving (Data, Eq, Foldable, Show, Typeable)
-unBalance_by_Account_Sum
- :: Balance_by_Account_Sum unit quantity
- -> Map unit quantity
-unBalance_by_Account_Sum (Balance_by_Account_Sum m) = m
-instance -- Monoid
- ( Addable quantity
- , Ord unit
- ) => Monoid (Balance_by_Account_Sum unit quantity) where
-       mempty = Balance_by_Account_Sum mempty
-       mappend (Balance_by_Account_Sum x) (Balance_by_Account_Sum y) =
-               Balance_by_Account_Sum $ Map.unionWith quantity_add x y
+ {   balByAccount :: !(BalByAccount acct_sect unit qty)
+ ,   balByUnit    :: !(BalByUnit    acct_sect unit qty)
+ } deriving (Data, Eq, Show, Typeable)
 instance -- NFData
- ( NFData unit
- , NFData quantity
- ) => NFData (Balance_by_Account_Sum unit quantity) where
-       rnf (Balance_by_Account_Sum m) = rnf m
-instance -- Posting
- ( Account account
- , Amount (unit, quantity)
- ) => Posting          (account, Balance_by_Account_Sum unit quantity) where
-       type Posting_Account (account, Balance_by_Account_Sum unit quantity) = account
-       type Posting_Amount  (account, Balance_by_Account_Sum unit quantity) = (unit, quantity)
-       type Posting_Amounts (account, Balance_by_Account_Sum unit quantity) = Map unit quantity
-       posting_account = fst
-       posting_amounts = unBalance_by_Account_Sum . snd
-instance -- Balance_Posting
- ( Account account
- , Amount (unit, quantity)
- ) => Balance_Posting (account, Balance_by_Account_Sum unit quantity) where
-       type Balance_Posting_Quantity (account, Balance_by_Account_Sum unit quantity) = quantity
-       balance_posting_amounts (_, Balance_by_Account_Sum x) = x
-       balance_posting_amounts_set amounts (acct, _) = (acct, Balance_by_Account_Sum amounts)
-
--- ** Type 'Balance_by_Unit'
-newtype Balance_by_Unit account_section unit quantity
- =      Balance_by_Unit (Map unit (Balance_by_Unit_Sum account_section quantity))
-deriving instance -- Data
- ( Data     account_section
- , Data     unit
- , Data     quantity
- , Ord      unit
- , Ord      account_section
- , Typeable unit
- , Typeable quantity
- ) => Data (Balance_by_Unit account_section unit quantity)
-deriving instance -- Eq
- ( Eq account_section
- , Eq unit
- , Eq quantity
- ) => Eq (Balance_by_Unit account_section unit quantity)
+ ( NFData acct_sect
+ , NFData unit
+ , NFData qty
+ , Ord acct_sect
+ ) => NFData (Balance acct_sect unit qty) where
+       rnf (Balance a u) = rnf a `seq` rnf u
+instance -- Semigroup
+ ( Addable qty
+ , Ord     unit
+ , Ord     acct_sect
+ ) => Semigroup (Balance acct_sect unit qty) where
+       (<>) = balance_union
 instance -- Monoid
- ( Addable quantity
+ ( Addable qty
  , Ord     unit
- , Ord     account_section
- ) => Monoid (Balance_by_Unit account_section unit quantity) where
-       mempty  = Balance_by_Unit mempty
-       mappend = balance_by_unit_union
-deriving instance -- Show
- ( Show account_section
- , Show unit
- , Show quantity
- ) => Show (Balance_by_Unit account_section unit quantity)
-deriving instance -- Typeable
- Typeable3 Balance_by_Unit
- -- FIXME: use 'Typeable' when dropping GHC-7.6 support
-
--- *** Type 'Balance_by_Unit_Sum'
-
--- | A sum of 'quantity's with their 'Account's involved,
--- concerning a single 'unit'.
-data Balance_by_Unit_Sum account_section quantity
- =   Balance_by_Unit_Sum
- {   balance_by_unit_sum_quantity :: !quantity
-     -- ^ The sum of 'quantity's for a single 'unit'.
- ,   balance_by_unit_sum_accounts :: !(Map (Account_Path account_section) ())
-     -- ^ The 'account's involved to build 'balance_by_unit_sum_quantity'.
- }
-deriving instance -- Data
- ( Data account_section
- , Data quantity
- , Ord  account_section
- ) => Data (Balance_by_Unit_Sum account_section quantity)
-deriving instance -- Eq
- ( Eq account_section
- , Eq quantity
- ) => Eq (Balance_by_Unit_Sum account_section quantity)
-deriving instance -- Show
- ( Show account_section
- , Show quantity
- ) => Show (Balance_by_Unit_Sum account_section quantity)
-deriving instance -- Typeable
- Typeable2 Balance_by_Unit_Sum
- -- FIXME: use 'Typeable' when dropping GHC-7.6 support
-
--- ** Constructors
+ , Ord     acct_sect
+ ) => Monoid (Balance acct_sect unit qty) where
+       mempty  = balance_empty
+       mappend = (<>)
 
-balance_empty
- :: ( Addable quantity
-    , Ord     unit
-    , Ord     account_section
-    )
- => Balance account_section unit quantity
+balance_empty :: Balance acct_sect unit qty
 balance_empty =
        Balance
-        { balance_by_account = mempty
-        , balance_by_unit    = mempty
+        { balByAccount = TreeMap.empty
+        , balByUnit    = BalByUnit Map.empty
         }
 
--- | Return the given 'Balance'
---   updated by the given 'Balance_Posting'.
-balance_cons
- :: ( Balance_Posting posting
-    , balance ~ Balance (Account_Section (Posting_Account posting))
-                        (Amount_Unit     (Posting_Amount  posting))
-                        (Balance_Posting_Quantity posting)
-    , Addable (Balance_Posting_Quantity posting)
-    , Ord     (Amount_Unit (Posting_Amount posting))
-    )
- => posting -> balance -> balance
-balance_cons post bal =
-       bal
-        { balance_by_account = balance_by_account_cons post (balance_by_account bal)
-        , balance_by_unit    = balance_by_unit_cons    post (balance_by_unit    bal)
-        }
-
--- | Return the given 'Balance'
---   updated by the given 'Balance_Posting's.
-balance_postings
- :: ( Balance_Posting posting
-    , balance ~ Balance (Account_Section (Posting_Account posting))
-                        (Amount_Unit     (Posting_Amount  posting))
-                        (Balance_Posting_Quantity posting)
-    , Foldable foldable
-    , Addable (Balance_Posting_Quantity posting)
-    , Ord     (Amount_Unit (Posting_Amount posting))
-    , Posting posting
-    )
- => foldable posting -> balance -> balance
-balance_postings = flip (Foldable.foldr balance_cons)
-
 -- | Return the first given 'Balance'
 --   updated by the second given 'Balance'.
-balance_union
- :: ( Addable quantity
-    , Ord unit
-    , Ord account_section
-    , balance ~ Balance account_section unit quantity
-    )
- => balance -> balance -> balance
+balance_union ::
+ (Addable qty, Ord acct_sect, Ord unit)
+ => Balance acct_sect unit qty
+ -> Balance acct_sect unit qty
+ -> Balance acct_sect unit qty
 balance_union
  (Balance b0a b0u)
  (Balance b1a b1u) =
        Balance
-        { balance_by_account = balance_by_account_union b0a b1a
-        , balance_by_unit    = balance_by_unit_union    b0u b1u
+        { balByAccount = balByAccount_union b0a b1a
+        , balByUnit    = balByUnit_union    b0u b1u
         }
 
--- | Return the given 'Balance_by_Account'
---   updated by the given 'Balance_Posting'.
-balance_by_account_cons
- :: ( Posting posting
-    , Balance_Posting posting
-    , account  ~ Posting_Account  posting
-    , quantity ~ Balance_Posting_Quantity posting
-    , unit     ~ Amount_Unit     (Posting_Amount posting)
-    , Addable quantity
-    , Ord unit
-    )
- => posting
- -> Balance_by_Account (Account_Section account) unit quantity
- -> Balance_by_Account (Account_Section account) unit quantity
-balance_by_account_cons post =
-       TreeMap.insert mappend
-        (account_path $ posting_account post)
-        (Balance_by_Account_Sum $ balance_posting_amounts post)
-
--- | Return the given 'Balance_by_Unit'
---   updated by the given 'Balance_Posting'.
-balance_by_unit_cons
- :: ( Balance_Posting posting
-    , account_section ~ Account_Section (Posting_Account posting)
-    , quantity ~ Balance_Posting_Quantity posting
-    , unit ~ Amount_Unit (Posting_Amount  posting)
-    , Addable quantity
-    , Ord unit
-    )
- => posting
- -> Balance_by_Unit account_section unit quantity
- -> Balance_by_Unit account_section unit quantity
-balance_by_unit_cons post =
-       balance_by_unit_union $
-       Balance_by_Unit $
-       (`Map.map` balance_posting_amounts post) $
-        \quantity -> Balance_by_Unit_Sum
-                { balance_by_unit_sum_quantity = quantity
-                , balance_by_unit_sum_accounts = Map.singleton (account_path $ posting_account post) ()
-                }
-
--- | Return a 'Balance_by_Unit'
---   derived from the given 'Balance_by_Account'.
-balance_by_unit_of_by_account ::
- ( Amount (unit, quantity)
- , Addable quantity
- , Data    account_section
- , NFData  account_section
- , Ord     account_section
- , Ord     unit
- , Show    account_section
- )
- => Balance_by_Account account_section unit quantity
- -> Balance_by_Unit account_section unit quantity
- -> Balance_by_Unit account_section unit quantity
-balance_by_unit_of_by_account =
-       flip $ TreeMap.foldr_with_Path $ curry balance_by_unit_cons
-
--- | Return the first given 'Balance_by_Account'
---   updated by the second given 'Balance_by_Account'.
-balance_by_account_union
- :: ( Addable quantity
-    , Ord account_section
-    , Ord unit
-    )
- => Balance_by_Account account_section unit quantity
- -> Balance_by_Account account_section unit quantity
- -> Balance_by_Account account_section unit quantity
-balance_by_account_union = TreeMap.union mappend
+-- | Return the given 'Balance'
+--   updated by the given @post@.
+balance_cons ::
+ ( Get (Balance_Account acct_sect) post
+ , Get (Balance_Amounts unit qty)  post
+ , Addable qty, Ord acct_sect, Ord unit )
+ => post
+ -> Balance acct_sect unit qty
+ -> Balance acct_sect unit qty
+balance_cons post bal =
+       bal
+        { balByAccount = balByAccount_cons post (balByAccount bal)
+        , balByUnit    = balByUnit_cons    post (balByUnit    bal)
+        }
 
--- | Return the first given 'Balance_by_Unit'
---   updated by the second given 'Balance_by_Unit'.
-balance_by_unit_union
- :: ( Addable quantity
-    , Ord unit
-    , Ord account_section
-    )
- => Balance_by_Unit account_section unit quantity
- -> Balance_by_Unit account_section unit quantity
- -> Balance_by_Unit account_section unit quantity
-balance_by_unit_union
- (Balance_by_Unit a0)
- (Balance_by_Unit a1) =
-       Balance_by_Unit $
+-- | Return the given 'Balance'
+--   updated by the given @post@s.
+balance_postings ::
+ ( post ~ MT.Element posts
+ , MT.MonoFoldable posts
+ , Get (Balance_Account acct_sect) post
+ , Get (Balance_Amounts unit qty)  post
+ , Addable qty, Ord acct_sect, Ord unit )
+ => posts
+ -> Balance acct_sect unit qty
+ -> Balance acct_sect unit qty
+balance_postings = flip (MT.ofoldr balance_cons)
+
+-- ** Type 'BalByAccount'
+type BalByAccount acct_sect unit qty
+ = TreeMap acct_sect (SumByAccount unit qty)
+
+-- | Return the first given 'BalByAccount'
+--   updated by the second given 'BalByAccount'.
+balByAccount_union ::
+ ( Addable qty
+ , Ord acct_sect
+ , Ord unit )
+ => BalByAccount acct_sect unit qty
+ -> BalByAccount acct_sect unit qty
+ -> BalByAccount acct_sect unit qty
+balByAccount_union = TreeMap.union (<>)
+
+-- | Return the given 'BalByAccount'
+--   updated by the given @post@.
+balByAccount_cons ::
+ ( Get (Balance_Account acct_sect) post
+ , Get (Balance_Amounts unit qty)  post
+ , Ord acct_sect
+ , Ord unit
+ , Addable qty
+ ) => post
+ -> BalByAccount acct_sect unit qty
+ -> BalByAccount acct_sect unit qty
+balByAccount_cons post =
+       TreeMap.insert (<>) (get post) (SumByAccount $ get post)
+
+-- *** Type 'SumByAccount'
+-- | A sum of @qty@s, concerning a single 'Balance_Account'.
+newtype SumByAccount unit qty
+ =      SumByAccount (Balance_Amounts unit qty)
+ deriving (Data, Eq, NFData, Show, Typeable)
+instance -- Semigroup
+ (Addable qty, Ord unit) =>
+ Semigroup (SumByAccount unit qty) where
+       SumByAccount x <> SumByAccount y =
+               SumByAccount $ Map.unionWith (flip quantity_add) x y
+instance -- Monoid
+ (Addable qty, Ord unit) =>
+ Monoid (SumByAccount unit qty) where
+       mempty  = SumByAccount mempty
+       mappend = (<>)
+
+unSumByAccount
+ :: SumByAccount unit qty
+ -> Map unit qty
+unSumByAccount (SumByAccount m) = m
+
+-- ** Type 'BalByUnit'
+newtype BalByUnit acct_sect unit qty
+ =      BalByUnit (Map unit (SumByUnit (Balance_Account acct_sect) qty))
+ deriving (Data, Eq, NFData, Show, Typeable)
+instance -- Semigroup
+ (Addable qty, Ord acct_sect, Ord unit) =>
+ Semigroup (BalByUnit acct_sect unit qty) where
+       (<>) = balByUnit_union
+instance -- Monoid
+ (Addable qty, Ord acct_sect, Ord unit) =>
+ Monoid (BalByUnit acct_sect unit qty) where
+       mempty  = BalByUnit mempty
+       mappend = (<>)
+
+-- | Return the first given 'BalByUnit'
+--   updated by the second given 'BalByUnit'.
+balByUnit_union
+ :: (Addable qty, Ord acct_sect, Ord unit)
+ => BalByUnit acct_sect unit qty
+ -> BalByUnit acct_sect unit qty
+ -> BalByUnit acct_sect unit qty
+balByUnit_union
+ (BalByUnit a0)
+ (BalByUnit a1) =
+       BalByUnit $
        Map.unionWith
-        (\new old -> Balance_by_Unit_Sum
-                { balance_by_unit_sum_quantity = quantity_add
-                        (balance_by_unit_sum_quantity old)
-                        (balance_by_unit_sum_quantity new)
-                , balance_by_unit_sum_accounts = Map.unionWith
+        (\new old -> SumByUnit
+                { sumByUnit_quantity = quantity_add
+                        (sumByUnit_quantity old)
+                        (sumByUnit_quantity new)
+                , sumByUnit_accounts = Map.unionWith
                         (const::()->()->())
-                        (balance_by_unit_sum_accounts old)
-                        (balance_by_unit_sum_accounts new)
+                        (sumByUnit_accounts old)
+                        (sumByUnit_accounts new)
                 })
         a0 a1
 
--- * Type 'Balance_Deviation'
-
--- | The 'Balance_by_Unit' whose 'balance_by_unit_sum_quantity'
---   is not zero and possible 'account' to 'balance_infer_equilibrium'.
-newtype Balance_Deviation                  account_section unit quantity
- =      Balance_Deviation (Balance_by_Unit account_section unit quantity)
-deriving instance -- Data
- ( Data     account_section
- , Data     unit
- , Data     quantity
- , Ord      unit
- , Ord      account_section
- , Typeable unit
- , Typeable quantity
- ) => Data (Balance_Deviation account_section unit quantity)
-deriving instance -- Eq
- ( Eq      account_section
- , Eq      unit
- , Eq      quantity
- ) => Eq (Balance_Deviation account_section unit quantity)
-deriving instance -- Show
- ( Show    account_section
- , Show    unit
- , Show    quantity
- ) => Show (Balance_Deviation account_section unit quantity)
-deriving instance -- Typeable
- Typeable3 Balance_Deviation
- -- FIXME: use 'Typeable' when dropping GHC-7.6 support
+-- | Return the given 'BalByUnit'
+--   updated by the given @post@.
+balByUnit_cons ::
+ ( Get (Balance_Account acct_sect) post
+ , Get (Balance_Amounts unit qty)  post
+ , Addable qty
+ , Ord acct_sect
+ , Ord unit
+ ) => post
+ -> BalByUnit acct_sect unit qty
+ -> BalByUnit acct_sect unit qty
+balByUnit_cons post =
+       balByUnit_union $
+       BalByUnit $
+       (`Map.map` get post) $
+        \qty -> SumByUnit
+                { sumByUnit_quantity = qty
+                , sumByUnit_accounts = Map.singleton (get post) ()
+                }
 
--- | Return the 'balance_by_unit' of the given 'Balance' with:
+-- | Return the given 'BalByUnit'
+--   updated by the given 'BalByAccount'.
+balByUnit_of_BalByAccount
+ :: (Addable qty, Ord acct_sect, Ord unit)
+ => BalByAccount acct_sect unit qty
+ -> BalByUnit    acct_sect unit qty
+ -> BalByUnit    acct_sect unit qty
+balByUnit_of_BalByAccount =
+       flip $ TreeMap.foldr_with_Path $ curry balByUnit_cons
+
+instance Get ( Balance_Account acct_sect )
+             ( Balance_Account acct_sect
+             , SumByAccount unit qty )
+ where get = fst
+instance Get ( Balance_Amounts unit qty )
+             ( Balance_Account acct_sect
+             , SumByAccount unit qty )
+ where get = unSumByAccount . snd
+
+-- *** Type 'SumByUnit'
+-- | A sum of @qty@s with their 'Account's involved,
+-- concerning a single @unit@.
+data SumByUnit acct qty
+ =   SumByUnit
+ {   sumByUnit_quantity :: !qty
+     -- ^ The sum of @qty@s for a single @unit@.
+ ,   sumByUnit_accounts :: !(Map acct ())
+     -- ^ The 'Balance_Account's involved to build 'sumByUnit_quantity'.
+ } deriving (Data, Eq, Ord, Show, Typeable)
+instance -- NFData
+ ( NFData acct
+ , NFData qty
+ ) => NFData (SumByUnit acct qty) where
+       rnf (SumByUnit q a) = rnf q `seq` rnf a
+
+-- * Type 'DeviationByUnit'
+-- | The 'BalByUnit' whose 'sumByUnit_quantity'
+--   is not zero and possible 'Balance_Account' to 'equilibrium'.
+newtype DeviationByUnit            acct_sect unit qty
+ =      DeviationByUnit (BalByUnit acct_sect unit qty)
+ deriving (Data, Eq, NFData, Show, Typeable)
+
+-- | Return the 'balByUnit' of the given 'Balance' with:
 --
--- * 'unit's whose 'balance_by_unit_sum_quantity' is verifying 'quantity_null' removed,
+-- * @unit@s whose 'sumByUnit_quantity' verifying 'quantity_null' are removed,
 --
--- * and remaining 'unit's having their 'balance_by_unit_sum_accounts'
---   complemented with the 'balance_by_account' of the given 'Balance'
---   (i.e. now mapping to 'account's __not__ involved to build the 'Balance_by_Unit_Sum').
-balance_deviation
- :: ( Zero    quantity
-    , Addable quantity
-    , Ord     account_section
-    , Ord     unit
-    )
- => Balance           account_section unit quantity
- -> Balance_Deviation account_section unit quantity
-balance_deviation Balance
- { balance_by_account
- , balance_by_unit=Balance_by_Unit balance_by_unit
+-- * and remaining @unit@s have their 'sumByUnit_accounts'
+--   complemented with the 'balByAccount' of the given 'Balance'
+--   (i.e. now mapping to the 'Balance_Account's __not__ involved to build the 'SumByUnit').
+deviationByUnit
+ :: (Zero qty, Addable qty, Ord acct_sect, Ord unit)
+ => Balance         acct_sect unit qty
+ -> DeviationByUnit acct_sect unit qty
+deviationByUnit Balance
+ { balByAccount
+ , balByUnit=BalByUnit balByUnit
  } =
-       let all_accounts = TreeMap.flatten (const ()) balance_by_account in
+       let all_accounts = TreeMap.flatten (const ()) balByAccount in
        let max_accounts = Map.size all_accounts in
-       Balance_Deviation $
+       DeviationByUnit $
                Map.foldlWithKey
-                (\(Balance_by_Unit m) unit Balance_by_Unit_Sum{..} ->
-                       Balance_by_Unit $
-                       if quantity_null balance_by_unit_sum_quantity
+                (\(BalByUnit m) unit SumByUnit{..} ->
+                       BalByUnit $
+                       if quantity_null sumByUnit_quantity
                        then m
                        else
-                               case Map.size balance_by_unit_sum_accounts of
+                               case Map.size sumByUnit_accounts of
                                 n | n == max_accounts ->
-                                       Map.insert unit Balance_by_Unit_Sum
-                                        { balance_by_unit_sum_quantity
-                                        , balance_by_unit_sum_accounts = Map.empty
+                                       Map.insert unit SumByUnit
+                                        { sumByUnit_quantity
+                                        , sumByUnit_accounts = Map.empty
                                         } m
                                 _ ->
-                                       let diff = Map.difference all_accounts balance_by_unit_sum_accounts in
-                                       Map.insert unit Balance_by_Unit_Sum
-                                        { balance_by_unit_sum_quantity
-                                        , balance_by_unit_sum_accounts = diff
+                                       let diff = Map.difference all_accounts sumByUnit_accounts in
+                                       Map.insert unit SumByUnit
+                                        { sumByUnit_quantity
+                                        , sumByUnit_accounts = diff
                                         } m
                 )
                 mempty
-                balance_by_unit
+                balByUnit
 
 -- ** Balance equilibrium
-
--- | Return the 'Balance' (adjusted by inferred 'quantity's)
---   of the given 'Balance_Posting's and either:
+-- | Return the 'Balance' (adjusted by inferred @qty@s)
+--   of the given @post@s and either:
 --
---   * 'Left': the 'Balance_Posting's that cannot be inferred.
---   * 'Right': the given 'Balance_Posting's with inferred 'quantity's inserted.
-balance_infer_equilibrium
- :: ( Balance_Posting posting
-    , account  ~ Posting_Account  posting
-    , unit     ~ Amount_Unit     (Posting_Amount posting)
-    , quantity ~ Balance_Posting_Quantity posting
-    , account  ~ Account_Path (Account_Section account)
-    , account_section ~ Account_Section account
-    , Addable quantity
-    , Eq      quantity
-    , Negable quantity
-    , Zero    quantity
-    , Ord     account_section
-    , Ord     unit
-    )
- => Map account [posting]
- -> ( Balance account_section unit quantity
-    , Either [(unit, Balance_by_Unit_Sum account_section quantity)]
-             (Map account [posting])
-    )
-balance_infer_equilibrium posts =
-       let bal_initial = Foldable.foldr balance_postings balance_empty posts in
-       let Balance_Deviation (Balance_by_Unit dev) = balance_deviation bal_initial in
+--   * 'Left': the @unit@s which have a non null 'SumByUnit'
+--             and for which no equibrating 'Balance_Account' can be inferred.
+--   * 'Right': the given @post@s with inferred @qty@s inserted.
+equilibrium ::
+ ( post ~ MT.Element posts
+ , Seqs.IsSequence posts
+ , Get (Balance_Account acct_sect) post
+ , Has (Balance_Amounts unit qty)  post
+ , Zero qty, Addable qty, Negable qty
+ , Ord acct, Ord acct_sect, Ord unit
+ , Get acct (Balance_Account acct_sect)
+ ) => Map acct posts
+   -> ( Balance acct_sect unit qty
+      , Either [(unit, SumByUnit (Balance_Account acct_sect) qty)]
+               (Map acct posts) )
+equilibrium posts =
+       let bal_initial = MT.ofoldr balance_postings balance_empty posts in
+       let DeviationByUnit (BalByUnit dev) = deviationByUnit bal_initial in
        let (bal_adjusted, eithers) =
                Map.foldrWithKey
-                (\unit unit_sum@Balance_by_Unit_Sum{..} (bal, lr) ->
-                       case Map.size balance_by_unit_sum_accounts of
+                (\unit unit_sum@SumByUnit{..} (bal, lr) ->
+                       case Map.size sumByUnit_accounts of
                         1 ->
-                               let acct = fst $ Map.elemAt 0 balance_by_unit_sum_accounts in
-                               let qty  = quantity_neg balance_by_unit_sum_quantity in
+                               let acct = fst $ Map.elemAt 0 sumByUnit_accounts in
+                               let qty  = quantity_neg sumByUnit_quantity in
                                let amts = Map.singleton unit qty in
-                               ( balance_cons (acct, Balance_by_Account_Sum amts) bal
+                               ( balance_cons (acct, SumByAccount amts) bal
                                , Right (acct, unit, qty) : lr
                                )
                         _ -> (bal, Left [(unit, unit_sum)] : lr))
@@ -503,88 +357,68 @@ balance_infer_equilibrium posts =
        let (l, r) = Foldable.accumLeftsAndFoldrRights
                 (\(acct, unit, qty) ->
                        Map.insertWith
-                        (\_new_ps -> insert_amount (unit, qty))
-                        acct (assert False []))
+                        (\_new_ps -> Seqs.fromList . insert_amount (unit, qty) . MT.otoList)
+                        (get acct) (assert False undefined))
+                        -- NOTE: acct is within bal_initial,
+                        -- hence posts already has a mapping for acct.
                 posts eithers in
        case l of
         [] -> (bal_adjusted, Right r)
         _  -> (bal_adjusted, Left  l)
        where
                insert_amount
-                :: Balance_Posting posting
-                => ( Amount_Unit     (Posting_Amount posting)
-                   , Balance_Posting_Quantity posting
-                   )
-                -> [posting]
-                -> [posting]
-               insert_amount p@(unit, qty) ps =
-                       case ps of
+                :: forall post unit qty.
+                ( Ord unit
+                , Has (Balance_Amounts unit qty) post
+                ) => (unit, qty) -> [post] -> [post]
+               insert_amount amt@(unit, qty) l =
+                       case l of
                         [] -> assert False []
-                        (x:xs) | Map.null (balance_posting_amounts x) ->
-                               balance_posting_amounts_set (Map.singleton unit qty) x:xs
-                               | Map.notMember unit (balance_posting_amounts x) ->
-                               let amts = Map.insertWith
-                                        (assert False undefined)
-                                        unit qty (balance_posting_amounts x) in
-                               balance_posting_amounts_set amts x:xs
-                        (x:xs) -> x:insert_amount p xs
-
--- | Return 'True' if and only if the given 'Balance_Deviation' maps no 'unit'.
-is_balance_at_equilibrium
- :: Balance_Deviation account_section unit quantity
- -> Bool
-is_balance_at_equilibrium (Balance_Deviation (Balance_by_Unit dev)) = Map.null dev
-
--- | Return 'True' if and only if the given 'Balance_Deviation'
--- maps only to 'Balance_by_Unit_Sum's whose 'balance_by_unit_sum_accounts'
--- maps exactly one 'account'.
-is_balance_equilibrium_inferrable
- :: Balance_Deviation account_section unit quantity
- -> Bool
-is_balance_equilibrium_inferrable (Balance_Deviation (Balance_by_Unit dev)) =
-       Foldable.all
-        (\s -> Map.size (balance_by_unit_sum_accounts s) == 1)
-        dev
-
--- | Return 'True' if and only if the given 'Balance_Deviation'
--- maps to at least one 'Balance_by_Unit_Sum' whose 'balance_by_unit_sum_accounts'
--- maps more than one 'Account'.
-is_balance_equilibrium_non_inferrable
- :: Balance_Deviation account_section unit quantity
- -> Bool
-is_balance_equilibrium_non_inferrable (Balance_Deviation (Balance_by_Unit dev)) =
-       Foldable.any
-        (\s -> Map.size (balance_by_unit_sum_accounts s) > 1)
-        dev
-
--- * Type 'Balance_Expanded'
-
--- | Descending propagation of 'quantity's accross 'Account's.
-type Balance_Expanded account_section unit quantity
- = TreeMap account_section (Balance_by_Account_Sum_Expanded unit quantity)
-
--- ** Type 'Balance_by_Account_Sum_Expanded'
-
+                               -- NOTE: the acct being in bal_initial,
+                               -- hence there was at least one post for this acct.
+                        p:ps ->
+                               let amts :: Balance_Amounts unit qty = get p in
+                               if Map.notMember unit amts
+                               then set (Map.insert unit qty amts) p:ps
+                               else p:insert_amount amt ps
+
+-- | Return 'True' if and only if the given 'DeviationByUnit' maps no @unit@.
+is_equilibrium :: DeviationByUnit acct_sect unit qty -> Bool
+is_equilibrium (DeviationByUnit (BalByUnit dev)) = Map.null dev
+
+-- | Return 'True' if and only if the given 'DeviationByUnit'
+-- maps only to 'SumByUnit's whose 'sumByUnit_accounts'
+-- maps exactly one 'Balance_Account'.
+is_equilibrium_inferrable :: DeviationByUnit acct_sect unit qty -> Bool
+is_equilibrium_inferrable (DeviationByUnit (BalByUnit dev)) =
+       Foldable.all ((== 1) . Map.size . sumByUnit_accounts) dev
+
+-- * Type 'ClusiveBalByAccount'
+
+-- | {Ex,In}clusive 'BalByAccount':
+-- descending propagation of @qty@s accross 'Account's.
+type ClusiveBalByAccount acct_sect unit qty
+ = TreeMap acct_sect (ClusiveSumByAccount unit qty)
+
+-- ** Type 'ClusiveSumByAccount'
 -- |
--- * 'Strict.exclusive': contains the original 'Balance_by_Account_Sum'.
+-- * 'Strict.exclusive': contains the original 'SumByAccount'.
 -- * 'Strict.inclusive': contains 'quantity_add' folded
 --   over 'Strict.exclusive' and 'Strict.inclusive'
 --   of 'TreeMap.node_descendants'
-type Balance_by_Account_Sum_Expanded unit quantity
- = Strict.Clusive (Balance_by_Account_Sum unit quantity)
+type ClusiveSumByAccount unit qty
+ = Strict.Clusive (SumByAccount unit qty)
 
--- | Return the given 'Balance_by_Account' with:
+-- | Return the given 'BalByAccount' with:
 --
 -- * all missing 'Account.parent' 'Account's inserted;
--- * and every mapped 'quantity' added with any 'quantity'
+-- * and every mapped @qty@ added with any @qty@
 --   of the 'Account's for which it is 'Account.parent'.
-balance_expanded ::
- ( Addable quantity
- , Ord account_section
- , Ord unit
- ) => Balance_by_Account account_section unit quantity
- -> Balance_Expanded account_section unit quantity
-balance_expanded =
+clusiveBalByAccount
+ :: (Addable qty, Ord acct_sect, Ord unit)
+ => BalByAccount    acct_sect unit qty
+ -> ClusiveBalByAccount acct_sect unit qty
+clusiveBalByAccount =
        TreeMap.map_by_depth_first
         (\descendants value ->
                let exclusive = Strict.fromMaybe mempty value in
@@ -592,38 +426,31 @@ balance_expanded =
                 { Strict.exclusive
                 , Strict.inclusive =
                        Map.foldl'
-                        ( flip $ mappend . Strict.inclusive
+                        ( flip $ (<>) . Strict.inclusive
                         . Strict.fromMaybe (assert False undefined)
                         . TreeMap.node_value )
                         exclusive $
                        TreeMap.nodes descendants
                 })
 
--- | Return a 'Balance_by_Unit'
---   derived from the given 'Balance_Expanded' balance.
+-- | Return a 'BalByUnit'
+--   derived from the given 'ClusiveBalByAccount' balance.
 --
---   NOTE: also correct if the 'Balance_Expanded' has been filtered.
-balance_by_unit_of_expanded
- :: ( Amount (unit, quantity)
-    , Addable quantity
-    , Data    account_section
-    , NFData  account_section
-    , Ord     account_section
-    , Ord     unit
-    , Show    account_section
-    )
- => Balance_Expanded account_section unit quantity
- -> Balance_by_Unit  account_section unit quantity
- -> Balance_by_Unit  account_section unit quantity
-balance_by_unit_of_expanded =
+--   NOTE: also correct if the 'ClusiveBalByAccount' has been filtered.
+balByUnit_of_ClusiveBalByAccount
+ :: (Addable qty, Ord acct_sect, Ord unit)
+ => ClusiveBalByAccount acct_sect unit qty
+ -> BalByUnit acct_sect unit qty
+ -> BalByUnit acct_sect unit qty
+balByUnit_of_ClusiveBalByAccount =
        go []
        where
-               go p (TreeMap nodes) bal =
-                       Map.foldrWithKey
-                        (\k TreeMap.Node{TreeMap.node_value, TreeMap.node_descendants} acc ->
-                               case node_value of
-                                Strict.Nothing -> go (k:p) node_descendants acc
-                                Strict.Just a  ->
-                                       let acct = TreeMap.reverse $ TreeMap.path k p in
-                                       balance_by_unit_cons (acct, Strict.inclusive a) acc)
-                        bal nodes
+       go p (TreeMap nodes) bal =
+               Map.foldrWithKey
+                (\k TreeMap.Node{TreeMap.node_value, TreeMap.node_descendants} acc ->
+                       case node_value of
+                        Strict.Nothing -> go (k:p) node_descendants acc
+                        Strict.Just a  ->
+                               let acct = TreeMap.reverse $ TreeMap.path k p in
+                               balByUnit_cons (acct, Strict.inclusive a) acc)
+                bal nodes