{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} import Prelude import Test.HUnit hiding ((~?)) import Test.Framework.Providers.HUnit (hUnitTestToTests) import Test.Framework.Runners.Console (defaultMain) -- import Control.Applicative (Const(..)) import Control.Arrow ((***)) import Data.Data () import qualified Data.Either import Data.Function (on) -- import Data.Functor.Compose (Compose(..)) import qualified Data.List as List import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.Map.Strict as Map import Data.Maybe (fromJust) import qualified Data.Strict.Maybe as Strict import Data.Text (Text) import qualified Text.Parsec as P hiding (char, space, spaces, string) import qualified Hcompta.Account as Account import qualified Hcompta.Balance as Balance import qualified Hcompta.Filter as Filter import qualified Hcompta.Filter.Read as Filter.Read import qualified Hcompta.Lib.Foldable as Lib.Foldable import qualified Hcompta.Lib.Interval as Lib.Interval import qualified Hcompta.Lib.Interval.Sieve as Lib.Interval.Sieve import qualified Hcompta.Lib.Parsec as P import qualified Hcompta.Lib.TreeMap as TreeMap import qualified Hcompta.Polarize as Polarize import qualified Hcompta.Quantity as Quantity import qualified Hcompta.Tag as Tag main :: IO () main = defaultMain $ hUnitTestToTests test_Hcompta (~?) :: String -> Bool -> Test (~?) s b = s ~: (b ~?= True) amounts :: (Quantity.Addable q, Ord u) => [(u, q)] -> Map.Map u q amounts = Map.fromListWith Quantity.quantity_add amount_usd = (("$"::Text),) amount_eur = (("€"::Text),) amount_gbp = (("£"::Text),) test_Hcompta :: Test test_Hcompta = TestList [ "Lib" ~: TestList [ "TreeMap" ~: TestList [ "insert" ~: TestList [ "[] 0" ~: (TreeMap.insert const ((0::Int):|[]) () TreeMap.empty) ~?= (TreeMap.TreeMap $ Map.fromList [ ((0::Int), TreeMap.leaf ()) ]) , "[] 0/1" ~: (TreeMap.insert const ((0::Int):|1:[]) () TreeMap.empty) ~?= (TreeMap.TreeMap $ Map.fromList [ ((0::Int), TreeMap.Node { TreeMap.node_value = Strict.Nothing , TreeMap.node_size = 1 , TreeMap.node_descendants = TreeMap.singleton ((1::Int):|[]) () }) ]) ] , "union" ~: TestList [ ] , "map_by_depth_first" ~: TestList [ "[0, 0/1, 0/1/2, 1, 1/2/3]" ~: (TreeMap.map_by_depth_first (\descendants value -> Map.foldl' (\acc v -> (++) acc $ Strict.fromMaybe undefined $ TreeMap.node_value v ) (Strict.fromMaybe [] value) (TreeMap.nodes descendants) ) $ TreeMap.from_List const [ (((0::Integer):|[]), [0]) , ((0:|1:[]), [0,1]) , ((0:|1:2:[]), [0,1,2]) , ((1:|[]), [1]) , ((1:|2:3:[]), [1,2,3]) ] ) ~?= (TreeMap.from_List const [ ((0:|[]), [0,0,1,0,1,2]) , ((0:|1:[]), [0,1,0,1,2]) , ((0:|1:2:[]), [0,1,2]) , ((1:|[]), [1,1,2,3]) , ((1:|2:[]), [1,2,3]) , ((1:|2:3:[]), [1,2,3]) ]) , "[0/0]" ~: (TreeMap.map_by_depth_first (\descendants value -> Map.foldl' (\acc v -> (++) acc $ Strict.fromMaybe undefined $ TreeMap.node_value v ) (Strict.fromMaybe [] value) (TreeMap.nodes descendants) ) $ TreeMap.from_List const [ (((0::Integer):|0:[]), [0,0]) ] ) ~?= (TreeMap.from_List const [ ((0:|[]), [0,0]) , ((0:|0:[]), [0,0]) ]) ] , "flatten" ~: TestList [ "[0, 0/1, 0/1/2]" ~: (TreeMap.flatten id $ TreeMap.from_List const [ (((0::Integer):|[]), ()) , ((0:|1:[]), ()) , ((0:|1:2:[]), ()) ] ) ~?= (Map.fromList [ ((0:|[]), ()) , ((0:|1:[]), ()) , ((0:|1:2:[]), ()) ]) , "[1, 1/2, 1/22, 1/2/3, 1/2/33, 11, 11/2, 11/2/3, 11/2/33]" ~: (TreeMap.flatten id $ TreeMap.from_List const [ ((1:|[]), ()) , ((1:|2:[]), ()) , ((1:|22:[]), ()) , ((1:|2:3:[]), ()) , ((1:|2:33:[]), ()) , ((11:|[]), ()) , ((11:|2:[]), ()) , ((11:|2:3:[]), ()) , ((11:|2:33:[]), ()) ] ) ~?= (Map.fromList [ (((1::Integer):|[]), ()) , ((1:|2:[]), ()) , ((1:|22:[]), ()) , ((1:|2:3:[]), ()) , ((1:|2:33:[]), ()) , ((11:|[]), ()) , ((11:|2:[]), ()) , ((11:|2:3:[]), ()) , ((11:|2:33:[]), ()) ]) ] , "find_along" ~: TestList [ "0/1/2/3 [0, 0/1, 0/1/2, 0/1/2/3]" ~: (TreeMap.find_along (0:|[1,2,3]) $ TreeMap.from_List const [ (((0::Integer):|[]), [0]) , ((0:|1:[]), [0,1]) , ((0:|1:2:[]), [0,1,2]) , ((0:|1:2:3:[]), [0,1,2,3]) ] ) ~?= [ [0] , [0,1] , [0,1,2] , [0,1,2,3] ] , "0/1/2/3 [0, 0/1]" ~: (TreeMap.find_along (0:|[1,2,3]) $ TreeMap.from_List const [ (((0::Integer):|[]), [0]) , ((0:|1:[]), [0,1]) ] ) ~?= [ [0] , [0,1] ] ] ] , "Foldable" ~: TestList [ "accumLeftsAndFoldrRights" ~: TestList [ "Left" ~: (Lib.Foldable.accumLeftsAndFoldrRights (++) [""] $ [Left [0]]) ~?= (([(0::Integer)], [(""::String)])) , "repeat Left" ~: ((take 1 *** take 0) $ Lib.Foldable.accumLeftsAndFoldrRights (++) [""] $ ( repeat (Left [0]) )) ~?= ([(0::Integer)], ([]::[String])) , "Right:Left:Right:Left" ~: (Lib.Foldable.accumLeftsAndFoldrRights (++) ["0"] $ ( Right ["2"]:Left [1]:Right ["1"]:Left [0]:[] )) ~?= (([1, 0]::[Integer]), (["2", "1", "0"]::[String])) , "Right:Left:Right:repeat Left" ~: ((take 1 *** take 2) $ Lib.Foldable.accumLeftsAndFoldrRights (++) ["0"] $ ( Right ["2"]:Left [1]:Right ["1"]:repeat (Left [0]) )) ~?= (([1]::[Integer]), (["2", "1"]::[String])) ] ] , "Interval" ~: TestList [ "position" ~: TestList $ concatMap (\(mi, mj, p) -> let i = fromJust mi in let j = fromJust mj in let (le, ge) = case p of Lib.Interval.Equal -> (EQ, EQ) _ -> (LT, GT) in [ ((show . Lib.Interval.Pretty) i ++ " " ++ (show . Lib.Interval.Pretty) j) ~: Lib.Interval.position i j ~?= (p, le) , ((show . Lib.Interval.Pretty) j ++ " " ++ (show . Lib.Interval.Pretty) i) ~: Lib.Interval.position j i ~?= (p, ge) ] ) [ ( (Lib.Interval.<..<) 0 (4::Integer) , (Lib.Interval.<..<) 5 9 , Lib.Interval.Away ) , ( (Lib.Interval.<..<) 0 4 , (Lib.Interval.<=..<) 4 9 , Lib.Interval.Adjacent ) , ( (Lib.Interval.<..<) 0 5 , (Lib.Interval.<..<) 4 9 , Lib.Interval.Overlap ) , ( (Lib.Interval.<..<) 0 5 , (Lib.Interval.<..<) 0 9 , Lib.Interval.Prefix ) , ( (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 1 8 , Lib.Interval.Include ) , ( (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 5 9 , Lib.Interval.Suffixed ) , ( (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 0 9 , Lib.Interval.Equal ) , ( (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<=) 0 9 , Lib.Interval.Prefix ) , ( (Lib.Interval.<=..<) 0 9 , (Lib.Interval.<..<) 0 9 , Lib.Interval.Suffixed ) , ( (Lib.Interval.<=..<=) 0 9 , (Lib.Interval.<..<) 0 9 , Lib.Interval.Include ) ] , "intersection" ~: TestList $ concatMap (\(mi, mj, e) -> let i = fromJust mi in let j = fromJust mj in [ ((show . Lib.Interval.Pretty) i ++ " " ++ (show . Lib.Interval.Pretty) j) ~: Lib.Interval.intersection i j ~?= e , ((show . Lib.Interval.Pretty) j ++ " " ++ (show . Lib.Interval.Pretty) i) ~: Lib.Interval.intersection j i ~?= e ] ) [ ( (Lib.Interval.<..<) 0 (4::Integer) , (Lib.Interval.<..<) 5 9 , Nothing ) , ( (Lib.Interval.<..<=) 0 5 , (Lib.Interval.<=..<) 5 9 , (Lib.Interval.<=..<=) 5 5 ) , ( (Lib.Interval.<..<) 0 6 , (Lib.Interval.<..<) 4 9 , (Lib.Interval.<..<) 4 6 ) , ( (Lib.Interval.<..<=) 0 6 , (Lib.Interval.<=..<) 4 9 , (Lib.Interval.<=..<=) 4 6 ) , ( (Lib.Interval.<..<) 0 6 , (Lib.Interval.<=..<) 4 9 , (Lib.Interval.<=..<) 4 6 ) , ( (Lib.Interval.<..<=) 0 6 , (Lib.Interval.<..<) 4 9 , (Lib.Interval.<..<=) 4 6 ) , ( (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<=..<) 0 9 , (Lib.Interval.<..<=) 0 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<..<=) 0 9 , (Lib.Interval.<=..<) 0 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<=..<=) 0 9 , (Lib.Interval.<=..<=) 0 9 , (Lib.Interval.<=..<=) 0 9 ) ] , "union" ~: TestList $ concatMap (\(mi, mj, e) -> let i = fromJust mi in let j = fromJust mj in [ ((show . Lib.Interval.Pretty) i ++ " " ++ (show . Lib.Interval.Pretty) j) ~: Lib.Interval.union i j ~?= e , ((show . Lib.Interval.Pretty) j ++ " " ++ (show . Lib.Interval.Pretty) i) ~: Lib.Interval.union j i ~?= e ] ) [ ( (Lib.Interval.<..<) 0 (4::Integer) , (Lib.Interval.<..<) 5 9 , Nothing ) , ( (Lib.Interval.<..<=) 0 5 , (Lib.Interval.<..<) 5 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<..<) 0 5 , (Lib.Interval.<=..<) 5 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<..<=) 0 5 , (Lib.Interval.<=..<) 5 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<..<) 0 6 , (Lib.Interval.<..<) 4 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 0 9 , (Lib.Interval.<..<) 0 9 ) , ( (Lib.Interval.<=..<) 0 9 , (Lib.Interval.<..<=) 0 9 , (Lib.Interval.<=..<=) 0 9 ) , ( (Lib.Interval.<..<=) 0 9 , (Lib.Interval.<=..<) 0 9 , (Lib.Interval.<=..<=) 0 9 ) , ( (Lib.Interval.<=..<=) 0 9 , (Lib.Interval.<=..<=) 0 9 , (Lib.Interval.<=..<=) 0 9 ) ] , "Sieve" ~: TestList $ [ "union" ~: TestList $ List.concatMap (\(mis, me) -> let is = map (fromJust) mis in let e = map (fromJust) me in let sil = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty is in let sir = foldr (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton) Lib.Interval.Sieve.empty is in [ (List.intercalate " " $ map (show . Lib.Interval.Pretty) is) ~: Lib.Interval.Sieve.intervals sil ~?= e , (List.intercalate " " $ map (show . Lib.Interval.Pretty) $ reverse is) ~: Lib.Interval.Sieve.intervals sir ~?= e ] ) [ ( [ (Lib.Interval.<=..<) 0 (5::Integer) , (Lib.Interval.<=..<=) 5 9 ] , [ (Lib.Interval.<=..<=) 0 9 ] ) , ( [ (Lib.Interval.<=..<=) 0 5 , (Lib.Interval.<=..<=) 0 9 ] , [ (Lib.Interval.<=..<=) 0 9 ] ) , ( [ (Lib.Interval.<=..<=) 0 4 , (Lib.Interval.<=..<=) 5 9 , (Lib.Interval.<=..<=) 3 6 ] , [ (Lib.Interval.<=..<=) 0 9 ] ) , ( [ (Lib.Interval.<=..<=) 1 4 , (Lib.Interval.<=..<=) 5 8 ] , [ (Lib.Interval.<=..<=) 1 4 , (Lib.Interval.<=..<=) 5 8 ] ) , ( [ (Lib.Interval.<=..<=) 1 8 , (Lib.Interval.<=..<=) 0 9 ] , [ (Lib.Interval.<=..<=) 0 9 ] ) , ( [ (Lib.Interval.<=..<=) 1 4 , (Lib.Interval.<=..<=) 5 8 , (Lib.Interval.<=..<=) 0 9 ] , [ (Lib.Interval.<=..<=) 0 9 ] ) ] ++ List.concatMap (\(mis, mjs, me) -> let is = map fromJust mis in let js = map fromJust mjs in let e = map fromJust me in let iu = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty is in let ju = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty js in [ ((List.intercalate " " $ map (show . Lib.Interval.Pretty) is) ++ " u " ++ (List.intercalate " " $ map (show . Lib.Interval.Pretty) js)) ~: Lib.Interval.Sieve.intervals (Lib.Interval.Sieve.union iu ju) ~?= e , ((List.intercalate " " $ map (show . Lib.Interval.Pretty) $ js) ++ " u " ++ (List.intercalate " " $ map (show . Lib.Interval.Pretty) $ is)) ~: Lib.Interval.Sieve.intervals (Lib.Interval.Sieve.union ju iu) ~?= e ] ) [ ( [ (Lib.Interval.<=..<=) 0 (1::Integer) , (Lib.Interval.<=..<=) 2 4 ] , [ (Lib.Interval.<=..<=) 0 3 ] , [ (Lib.Interval.<=..<=) 0 4 ] ) , ( [ (Lib.Interval.<=..<=) 0 1 , (Lib.Interval.<=..<=) 2 3 , (Lib.Interval.<=..<=) 4 5 , (Lib.Interval.<=..<=) 6 7 ] , [ (Lib.Interval.<=..<=) 1 2 , (Lib.Interval.<=..<=) 3 4 , (Lib.Interval.<=..<=) 5 6 ] , [ (Lib.Interval.<=..<=) 0 7 ] ) , ( [ (Lib.Interval.<=..<=) 0 1 , (Lib.Interval.<=..<=) 2 3 ] , [ (Lib.Interval.<=..<=) 4 5 ] , [ (Lib.Interval.<=..<=) 0 1 , (Lib.Interval.<=..<=) 2 3 , (Lib.Interval.<=..<=) 4 5 ] ) , ( [ (Lib.Interval.<=..<=) 0 1 , (Lib.Interval.<=..<=) 4 5 ] , [ (Lib.Interval.<=..<=) 2 3 ] , [ (Lib.Interval.<=..<=) 0 1 , (Lib.Interval.<=..<=) 2 3 , (Lib.Interval.<=..<=) 4 5 ] ) ] , "intersection" ~: TestList $ List.concatMap (\(mis, mjs, me) -> let is = map (fromJust) mis in let js = map (fromJust) mjs in let e = map (fromJust) me in let iu = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty is in let ju = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty js in [ ((List.intercalate " " $ map (show . Lib.Interval.Pretty) is) ++ " n " ++ (List.intercalate " " $ map (show . Lib.Interval.Pretty) js)) ~: Lib.Interval.Sieve.intervals (Lib.Interval.Sieve.intersection iu ju) ~?= e , ((List.intercalate " " $ map (show . Lib.Interval.Pretty) $ js) ++ " n " ++ (List.intercalate " " $ map (show . Lib.Interval.Pretty) $ is)) ~: Lib.Interval.Sieve.intervals (Lib.Interval.Sieve.intersection ju iu) ~?= e ] ) [ ( [ (Lib.Interval.<=..<) 0 (5::Integer) ] , [ (Lib.Interval.<=..<=) 5 9 ] , [ ] ) , ( [ (Lib.Interval.<=..<=) 0 5 ] , [ (Lib.Interval.<=..<=) 5 9 ] , [ (Lib.Interval.<=..<=) 5 5 ] ) , ( [ (Lib.Interval.<=..<=) 0 5 ] , [ (Lib.Interval.<=..<=) 0 9 ] , [ (Lib.Interval.<=..<=) 0 5 ] ) , ( [ (Lib.Interval.<=..<=) 0 4 , (Lib.Interval.<=..<=) 5 9 ] , [ (Lib.Interval.<=..<=) 3 6 ] , [ (Lib.Interval.<=..<=) 3 4 , (Lib.Interval.<=..<=) 5 6 ] ) , ( [ (Lib.Interval.<=..<=) 1 4 , (Lib.Interval.<=..<=) 6 8 ] , [ (Lib.Interval.<=..<=) 2 3 , (Lib.Interval.<=..<=) 5 7 ] , [ (Lib.Interval.<=..<=) 2 3 , (Lib.Interval.<=..<=) 6 7 ] ) ] , "complement" ~: TestList $ List.concatMap (\(mis, me) -> let is = map fromJust mis in let e = map fromJust me in let iu = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty is in [ show (Lib.Interval.Pretty $ Lib.Interval.Sieve.fmap_interval (Lib.Interval.fmap_unsafe $ Lib.Interval.Pretty) iu) ~: Lib.Interval.Sieve.intervals (Lib.Interval.Sieve.complement iu) ~?= e ] ) [ ( [ ((Lib.Interval.<=..<) `on` Lib.Interval.Limited) 0 (5::Integer) , ((Lib.Interval.<=..<=) `on` Lib.Interval.Limited) 5 9 ] , [ Just $ (Lib.Interval...<) 0 , Just $ (Lib.Interval.<..) 9 ] ) , ( [ Just $ Lib.Interval.unlimited ] , [ ] ) , ( [ ] , [ Just $ Lib.Interval.unlimited ] ) , ( [ Just $ (Lib.Interval...<) 0 , Just $ (Lib.Interval.<..) 0 ] , [ Just $ Lib.Interval.point $ Lib.Interval.Limited 0 ] ) , ( [ ((Lib.Interval.<=..<) `on` Lib.Interval.Limited) 0 1 , ((Lib.Interval.<=..<) `on` Lib.Interval.Limited) 2 3 , ((Lib.Interval.<..<=) `on` Lib.Interval.Limited) 3 4 ] , [ Just $ (Lib.Interval...<) 0 , ((Lib.Interval.<=..<) `on` Lib.Interval.Limited) 1 2 , Just $ Lib.Interval.point $ Lib.Interval.Limited 3 , Just $ (Lib.Interval.<..) 4 ] ) ] , "complement_with" ~: TestList $ List.concatMap (\(mib, mis, me) -> let ib = fromJust mib in let is = map fromJust mis in let e = map fromJust me in let iu = foldl (flip (Lib.Interval.Sieve.union . Lib.Interval.Sieve.singleton)) Lib.Interval.Sieve.empty is in [ show (Lib.Interval.Pretty iu) ~: Lib.Interval.Sieve.intervals (Lib.Interval.Sieve.complement_with ib iu) ~?= e ] ) [ ( (Lib.Interval.<=..<=) (-10) (10::Integer) , [ (Lib.Interval.<=..<) 0 5 , (Lib.Interval.<=..<=) 5 9 ] , [ (Lib.Interval.<=..<) (-10) 0 , (Lib.Interval.<..<=) 9 10 ] ) , ( (Lib.Interval.<=..<=) (-10) 10 , [ (Lib.Interval.<=..<=) (-10) 10 ] , [ ] ) , ( (Lib.Interval.<=..<=) (-10) 10 , [ ] , [ (Lib.Interval.<=..<=) (-10) 10 ] ) , ( (Lib.Interval.<=..<=) (-10) 10 , [ (Lib.Interval.<=..<) (-10) 0 , (Lib.Interval.<..<=) 0 10 ] , [ Just $ Lib.Interval.point 0 ] ) , ( (Lib.Interval.<=..<=) (-10) 10 , [ Just $ Lib.Interval.point 0 ] , [ (Lib.Interval.<=..<) (-10) 0 , (Lib.Interval.<..<=) 0 10 ] ) , ( (Lib.Interval.<=..<=) 0 10 , [ (Lib.Interval.<..<=) 0 10 ] , [ Just $ Lib.Interval.point 0 ] ) , ( (Lib.Interval.<=..<=) 0 10 , [ (Lib.Interval.<=..<) 0 10 ] , [ Just $ Lib.Interval.point 10 ] ) , ( Just $ Lib.Interval.point 0 , [ ] , [ Just $ Lib.Interval.point 0 ] ) , ( Just $ Lib.Interval.point 0 , [ Just $ Lib.Interval.point 0 ] , [ ] ) ] ] ] ] , "Account" ~: TestList [ "foldr" ~: TestList [ "[A]" ~: (reverse $ Account.foldr ("A":|[]) (:) []) ~?= ["A":|[]] , "[A, B]" ~: (reverse $ Account.foldr ("A":|["B"]) (:) []) ~?= ["A":|[], "A":|["B"]] , "[A, B, C]" ~: (reverse $ Account.foldr ("A":|["B", "C"]) (:) []) ~?= ["A":|[], "A":|["B"], "A":|["B", "C"]] ] , "ascending" ~: TestList [ "[A]" ~: Account.ascending ("A":|[]) ~?= Nothing , "[A, B]" ~: Account.ascending ("A":|["B"]) ~?= Just ("A":|[]) , "[A, B, C]" ~: Account.ascending ("A":|["B", "C"]) ~?= Just ("A":|["B"]) ] ] , "Filter" ~: TestList [ "test" ~: TestList [ "Filter_Path" ~: TestList [ "A A" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") ]) ((("A"::Text):|[])) , "* A" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Any ]) ((("A"::Text):|[])) , ": A" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Many ]) ((("A"::Text):|[])) , ":A A" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") ]) ((("A"::Text):|[])) , "A: A" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many ]) ((("A"::Text):|[])) , "A: A:B" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many ]) ((("A"::Text):|"B":[])) , "A:B A:B" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ]) ((("A"::Text):|"B":[])) , "A::B A:B" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ]) ((("A"::Text):|"B":[])) , ":B: A:B:C" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") , Filter.Filter_Path_Section_Many ]) ((("A"::Text):|"B":"C":[])) , ":C A:B:C" ~? Filter.test (Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "C") ]) ((("A"::Text):|"B":"C":[])) , "A:B:C::D A:B:C:CC:CCC:D:E" ~? Filter.test (Filter.Filter_Path Filter.Gt [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "C") , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "D") ]) ((("A"::Text):|"B":"C":"CC":"CCC":"D":"E":[])) ] , "Filter_Bool" ~: TestList [ "Any A" ~? Filter.test (Filter.Any::Filter.Filter_Bool (Filter.Filter_Account (Tag.Tags, NonEmpty Text))) (mempty, ("A":|[])) ] , "Filter_Ord" ~: TestList [ "0 < (1, 2)" ~? Filter.test (Filter.With_Interval $ Filter.Filter_Ord Filter.Gt (0::Integer)) (fromJust $ (Lib.Interval.<=..<=) 1 2) , "(-2, -1) < 0" ~? Filter.test (Filter.With_Interval $ Filter.Filter_Ord Filter.Lt (0::Integer)) (fromJust $ (Lib.Interval.<=..<=) (-2) (-1)) , "not (1 < (0, 2))" ~? (not $ Filter.test (Filter.With_Interval $ Filter.Filter_Ord Filter.Gt (1::Integer)) (fromJust $ (Lib.Interval.<=..<=) 0 2)) ] ] , "Read" ~: TestList [ "filter_account" ~: TestList [ "*" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("*"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Any ] ] , "A" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("A"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") ] ] , "AA" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("AA"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "AA") ] ] , "::A" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("::A"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") ] ] , ":A" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" (":A"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") ] ] , "A:" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("A:"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many ] ] , "A::" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("A::"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Many ] ] , "A:B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("A:B"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ] ] , "A::B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("A::B"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ] ] , "A:::B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("A:::B"::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Many , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ] ] , "A: " ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.char ' ' <* P.eof) () "" ("A: "::Text)]) ~?= [ Filter.Filter_Path Filter.Eq [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Many ] ] , "<=A:B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" ("<=A:B"::Text)]) ~?= [ Filter.Filter_Path Filter.Le [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ] ] , ">=A:B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" (">=A:B"::Text)]) ~?= [ Filter.Filter_Path Filter.Ge [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ] ] , "A:B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_account_path <* P.eof) () "" (">A:B"::Text)]) ~?= [ Filter.Filter_Path Filter.Gt [ Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "A") , Filter.Filter_Path_Section_Text (Filter.Filter_Text_Exact "B") ] ] ] , "filter_bool" ~: TestList [ "( E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_bool [ P.char 'E' >> return (return $ Filter.Bool True) ] <* P.eof) () "" ("( E )"::Text)]) ~?= [ Filter.And (Filter.Bool True) Filter.Any ] , "( ( E ) )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_bool [ P.char 'E' >> return (return $ Filter.Bool True) ] <* P.eof) () "" ("( ( E ) )"::Text)]) ~?= [ Filter.And (Filter.And (Filter.Bool True) Filter.Any) Filter.Any ] , "( E ) & ( E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_bool [ P.char 'E' >> return (return $ Filter.Bool True) ] <* P.eof) () "" ("( E ) & ( E )"::Text)]) ~?= [ Filter.And (Filter.And (Filter.Bool True) Filter.Any) (Filter.And (Filter.Bool True) Filter.Any) ] , "( E ) + ( E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_bool [ P.char 'E' >> return (return $ Filter.Bool True) ] <* P.eof) () "" ("( E ) + ( E )"::Text)]) ~?= [ Filter.Or (Filter.And (Filter.Bool True) Filter.Any) (Filter.And (Filter.Bool True) Filter.Any) ] , "( E ) - ( E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_bool [ P.char 'E' >> return (return $ Filter.Bool True) ] <* P.eof) () "" ("( E ) - ( E )"::Text)]) ~?= [ Filter.And (Filter.And (Filter.Bool True) Filter.Any) (Filter.Not (Filter.And (Filter.Bool True) Filter.Any)) ] , "(- E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.filter_bool [ P.char 'E' >> return (return $ Filter.Bool True) ] <* P.eof) () "" ("(- E )"::Text)]) ~?= [ Filter.And (Filter.Not (Filter.Bool True)) Filter.Any ] ] ] ] , "Balance" ~: TestList [ "balance" ~: TestList [ "[A+$1] = A+$1 & $+1" ~: (Balance.cons ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ (1::Integer) ] ) Balance.empty) ~?= (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ List.map (id *** Balance.Account_Sum . Map.map Polarize.polarize) $ [ ("A":|[], amounts [ amount_usd $ 1 ]) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize $ 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] } ) , "[A+$1, A-$1] = {A+$0, $+0}" ~: (List.foldl (flip Balance.cons) Balance.empty [ ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ (1::Integer) ] ) , ( ("A":|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ -1 ] ) ]) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( "A":|[] , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.Polarized_Both (-1) ( 1) ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-1) ( 1) , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] } , "[A+$1, A-€1] = {A+$1-€1, $+1 €-1}" ~: (List.foldl (flip Balance.cons) Balance.empty [ ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ (1::Integer) ] ) , ( ("A":|[]) , Map.map Polarize.polarize $ amounts [ amount_eur $ -1 ] ) ]) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ List.map (id *** Balance.Account_Sum . Map.map Polarize.polarize) $ [ ("A":|[], amounts [ amount_usd $ 1, amount_eur $ -1 ]) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Positive 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Negative (-1) , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] } , "[A+$1, B-$1] = {A+$1 B-$1, $+0}" ~: (List.foldl (flip Balance.cons) Balance.empty [ ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ (1::Integer) ] ) , ( ("B":|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ -1 ] ) ]) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ List.map (id *** Balance.Account_Sum . Map.map Polarize.polarize) $ [ ("A":|[], amounts [ amount_usd $ 1 ]) , ("B":|[], amounts [ amount_usd $ -1 ]) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-1) 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } ] } , "[A+$1, B+$1]" ~: (List.foldl (flip Balance.cons) Balance.empty [ ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ (1::Integer) ] ) , ( ("B":|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] ) ]) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ List.map (id *** Balance.Account_Sum . Map.map Polarize.polarize) $ [ ("A":|[], amounts [ amount_usd $ 1 ]) , ("B":|[], amounts [ amount_usd $ 1 ]) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } ] } , "[A+$1+€2, A-$1-€2] = {A+$0+€0, $+0 €+0}" ~: (List.foldl (flip Balance.cons) Balance.empty [ ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ 1, amount_eur $ (2::Integer) ] ) , ( ("A":|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ -1, amount_eur $ -2 ] ) ]) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ("A":|[] , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.Polarized_Both (-1) 1 , amount_eur $ Polarize.Polarized_Both (-2) 2 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-1) 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-2) 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] } , "[A+$1+€2+£3, B-$1-2€-£3] = {A+$1+€2+£3 B-$1-€2-£3, $+0 €+0 £+0}" ~: (List.foldl (flip Balance.cons) Balance.empty [ ( (("A"::Text):|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ (1::Integer), amount_eur $ 2, amount_gbp $ 3 ] ) , ( ("B":|[]) , Map.map Polarize.polarize $ amounts [ amount_usd $ -1, amount_eur $ -2, amount_gbp $ -3 ] ) ]) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ List.map (id *** Balance.Account_Sum . Map.map Polarize.polarize) $ [ ("A":|[], amounts [ amount_usd $ 1, amount_eur $ 2, amount_gbp $ 3 ]) , ("B":|[], amounts [ amount_usd $ -1, amount_eur $ -2, amount_gbp $ -3 ]) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-1) 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-2) 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } , amount_gbp $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.Polarized_Both (-3) 3 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } ] } ] , "union" ~: TestList [ "{A+$1, $+1} {A+$1, $+1} = {A+$2, $+2}" ~: Balance.union (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( "A":|[] , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( "A":|[] , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 2 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] } , "{A+$1, $+1} {B+$1, $+1} = {A+$1 B+$1, $+2}" ~: Balance.union (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["B":|[]] } ] }) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } ] } , "{A+$1, $+1} {B+€1, €+1} = {A+$1 B+€1, $+1 €+1}" ~: Balance.union (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_eur $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["B":|[]] } ] }) ~?= Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_eur $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["B":|[]] } ] } ] , "expanded" ~: TestList [ "mempty" ~: Balance.expanded TreeMap.empty ~?= (TreeMap.empty::Balance.Expanded (NonEmpty Text) Text Integer) , "A+$1 = A+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const $ [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A/A+$1 = A+$1 A/A+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|["A"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [] }) , ("A":|["A"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A/B+$1 = A+$1 A/B+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|["B"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [] }) , ("A":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A/B/C+$1 = A+$1 A/B+$1 A/B/C+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|["B", "C"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const $ [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [] }) , ("A":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [] }) , ("A":|["B", "C"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A+$1 A/B+$1 = A+$2 A/B+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 2 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A+$1 A/B+$1 A/B/C+$1 = A+$3 A/B+$2 A/B/C+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B", "C"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 3 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 2 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B", "C"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A+$1 A/B+$1 A/B/C+$1 A/B/C/D+$1 = A+$4 A/B+$3 A/B/C+$2 A/B/C/D+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B", "C"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B", "C", "D"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 4 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 3 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B", "C"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 2 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B", "C", "D"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) , "A+$1 A/B+$1 A/BB+$1 AA/B+$1 = A+$3 A/B+$1 A/BB+$1 AA+$1 AA/B+$1" ~: Balance.expanded (TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["B"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("A":|["BB"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("AA":|["B"]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ]::Balance.Balance_by_Account Text Text (Polarize.Polarized Integer)) ~?= (TreeMap.from_List const [ ("A":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 3 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("A":|["BB"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) , ("AA":|[], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [] }) , ("AA":|["B"], Balance.Account_Sum_Expanded { Balance.inclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] , Balance.exclusive = Balance.Account_Sum $ Map.map Polarize.polarize $ amounts [ amount_usd $ 1 ] }) ]) ] , "deviation" ~: TestList [ "{A+$1, $1}" ~: (Balance.deviation $ Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Deviation (NonEmpty Text) Text (Polarize.Polarized Integer)) ~?= (Balance.Deviation $ Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["B":|[]] } ]) , "{A+$1 B+$1, $2}" ~: (Balance.deviation $ Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) [ "A":|[] , "B":|[] ] } ] }::Balance.Deviation (NonEmpty Text) Text (Polarize.Polarized Integer)) ~?= (Balance.Deviation $ Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 2 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) [ ] } ]) ] , "is_equilibrium_inferrable" ~: TestList [ "empty" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.empty::Balance.Balance (NonEmpty Text) Text Integer) , "{A+$0, $+0}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 0 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$1, $+1}" ~: TestCase $ (@=?) False $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$0+€0, $0 €+0}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 0 , amount_eur $ Polarize.polarize 0 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$1, B-$1, $+0}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize (-1) ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$1 B, $+1}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$1 B+€1, $+1 €+1}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_eur $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["B":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$1 B-$1+€1, $+0 €+1}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize (-1) , amount_eur $ Polarize.polarize 1 ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 1 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["B":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) , "{A+$1+€2+£3 B-$1-€2-£3, $+0 €+0 £+0}" ~: TestCase $ (@=?) True $ Balance.is_equilibrium_inferrable $ Balance.deviation $ (Balance.Balance { Balance.balance_by_account = TreeMap.from_List const $ [ ( ("A":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize 1 , amount_eur $ Polarize.polarize 2 , amount_gbp $ Polarize.polarize 3 ] ) , ( ("B":|[]) , Balance.Account_Sum $ Map.fromListWith const $ [ amount_usd $ Polarize.polarize (-1) , amount_eur $ Polarize.polarize (-2) , amount_gbp $ Polarize.polarize (-3) ] ) ] , Balance.balance_by_unit = Balance.Balance_by_Unit $ Map.fromList $ [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } , amount_eur $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } , amount_gbp $ Balance.Unit_Sum { Balance.unit_sum_quantity = Polarize.polarize 0 , Balance.unit_sum_accounts = Map.fromList $ List.map (,()) ["A":|[], "B":|[]] } ] }::Balance.Balance (NonEmpty Text) Text (Polarize.Polarized Integer)) ] , "infer_equilibrium" ~: TestList [ "{A+$1 B}" ~: (snd $ Balance.infer_equilibrium $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( ("A"::Text):|[] , amounts [ amount_usd $ (1::Integer) ] ) , ( "B":|[] , amounts [] ) ]) ~?= (Right $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( "A":|[] , amounts [ amount_usd $ 1 ] ) , ( "B":|[] , amounts [ amount_usd $ -1 ] ) ]) , "{A+$1 B-1€}" ~: (snd $ Balance.infer_equilibrium $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( ("A"::Text):|[] , amounts [ amount_usd $ (1::Integer) ] ) , ( "B":|[] , amounts [ amount_eur $ -1 ] ) ]) ~?= (Right $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( ("A"::Text):|[] , amounts [ amount_usd $ 1, amount_eur $ (1::Integer)] ) , ( "B":|[] , amounts [ amount_eur $ -1, amount_usd $ -1 ] ) ]) , "{A+$1 B+$1}" ~: (snd $ Balance.infer_equilibrium $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( ("A"::Text):|[] , amounts [ amount_usd $ (1::Integer) ] ) , ( "B":|[] , amounts [ amount_usd $ 1 ] ) ]) ~?= (Left [ amount_usd $ Balance.Unit_Sum { Balance.unit_sum_quantity = 2 , Balance.unit_sum_accounts = Map.fromList []} ]) , "{A+$1 B-$1 B-1€}" ~: (snd $ Balance.infer_equilibrium $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( ("A"::Text):|[] , amounts [ amount_usd $ (1::Integer) ] ) , ( "B":|[] , amounts [ amount_usd $ -1, amount_eur $ -1 ] ) ]) ~?= (Right $ Map.fromList $ List.map (\(acct, amts) -> (acct, [(acct, amts)])) $ [ ( "A":|[] , amounts [ amount_usd $ 1, amount_eur $ 1 ] ) , ( "B":|[] , amounts [ amount_usd $ -1, amount_eur $ -1 ] ) ]) ] ] ]