1 {-# LANGUAGE DefaultSignatures #-}
 
   3 module Literate.Accounting.Math where
 
   6 import Data.Decimal (Decimal, DecimalRaw (..), roundTo)
 
   7 import Data.Eq (Eq (..))
 
   8 import Data.Function (flip, (.))
 
   9 import Data.Functor ((<$>))
 
  11 import Data.Map.Strict (Map)
 
  12 import Data.Map.Strict qualified as Map
 
  13 import Data.Maybe (Maybe (..))
 
  14 import Data.Monoid (Endo (..))
 
  15 import Data.Ord (Ord (..), Ordering (..))
 
  16 import Data.String (String)
 
  17 import Data.Word (Word8)
 
  18 import Prelude (Integer, Integral, fromIntegral)
 
  19 import Prelude qualified
 
  22 class Zeroable a where
 
  24 instance Zeroable String where
 
  26 instance Zeroable Decimal where
 
  28 instance Zeroable (Map.Map k a) where
 
  32 instance Zeroable Decimal where
 
  34 instance Zeroable (Map k a) where
 
  39 class Nullable a where
 
  41   default null :: Zeroable a => Eq a => a -> Bool
 
  43 instance Nullable String
 
  44 instance Nullable Decimal
 
  47 instance Nullable Decimal where
 
  49 instance Nullable a => Nullable (Map k a) where
 
  50   null = Foldable.all null
 
  54 class Signable a where
 
  56   default sign :: Zeroable a => Nullable a => Ord a => a -> Ordering
 
  63 -- instance Signable Decimal
 
  71   default (+) :: Prelude.Num a => a -> a -> a
 
  74 -- | For @'Addable' ('Map' k ())@.
 
  75 instance Addable () where
 
  78 instance (Ord k, Addable a) => Addable (Map k a) where
 
  79   (+) = Map.unionWith (flip (+))
 
  80 instance Addable a => Addable (Maybe a) where
 
  81   Nothing + Nothing = Nothing
 
  82   Just x + Nothing = Just x
 
  83   Nothing + Just y = Just y
 
  84   Just x + Just y = Just (x + y)
 
  85 instance Addable Decimal where
 
  86   (+) x y = Decimal e (fromIntegral (nx Prelude.+ ny))
 
  88     (e, nx, ny) = roundMinDecimal x y
 
  90 -- | Round the two 'DecimalRaw' values to the smallest exponent.
 
  91 roundMinDecimal :: Integral i => DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
 
  92 roundMinDecimal d1@(Decimal e1 _) d2@(Decimal e2 _) = (e, n1, n2)
 
  95   Decimal _ n1 = roundTo e d1
 
  96   Decimal _ n2 = roundTo e d2
 
 101   default negate :: Prelude.Num a => a -> a
 
 102   negate = Prelude.negate
 
 104 -- | For @'Negable' ('Map' k ())@.
 
 105 instance Negable () where
 
 109 instance Negable Integer
 
 110 instance Negable Decimal
 
 111 instance Negable a => Negable (Map k a) where
 
 112   negate = Map.map negate
 
 113 instance Negable a => Negable (Endo a) where
 
 114   negate (Endo f) = Endo (f . negate)
 
 115 instance Negable a => Negable [a] where
 
 116   negate = (negate <$>)
 
 119 class Subable a where
 
 122   default (-) :: Prelude.Num a => a -> a -> a
 
 125 -- | For @'Subable' ('Map' k ())@.
 
 126 instance Subable () where
 
 130 instance Subable Integer
 
 131 instance Subable Decimal
 
 132 instance (Ord k, Addable a, Negable a) => Subable (Map k a) where
 
 133   (-) x y = Map.unionWith (flip (+)) x (negate y)