-- | Shared utility functions used by other modules
module Tax.Util where

import Data.Fixed (Centi)
import Data.List.NonEmpty (nonEmpty)
import Data.Maybe (fromMaybe, mapMaybe)

-- | Repeatedly apply the function to the argument until it reaches the fixed point.
fixEq :: Eq a => (a -> a) -> a -> a
fixEq :: forall a. Eq a => (a -> a) -> a -> a
fixEq a -> a
f a
a
   | a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
a' = a
a
   | Bool
otherwise = (a -> a) -> a -> a
forall a. Eq a => (a -> a) -> a -> a
fixEq a -> a
f a
a'
   where a' :: a
a' = a -> a
f a
a

-- | Sum the list of arguments; return 'Nothing' iff all items are 'Nothing'.
totalOf :: Num a => [Maybe a] -> Maybe a
totalOf :: forall a. Num a => [Maybe a] -> Maybe a
totalOf = (NonEmpty a -> a) -> Maybe (NonEmpty a) -> Maybe a
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap NonEmpty a -> a
forall a. Num a => NonEmpty a -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (Maybe (NonEmpty a) -> Maybe a)
-> ([Maybe a] -> Maybe (NonEmpty a)) -> [Maybe a] -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Maybe (NonEmpty a)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty ([a] -> Maybe (NonEmpty a))
-> ([Maybe a] -> [a]) -> [Maybe a] -> Maybe (NonEmpty a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe a -> Maybe a) -> [Maybe a] -> [a]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Maybe a -> Maybe a
forall a. a -> a
id

-- | Subtraction under 'Maybe'
difference :: Maybe Centi -> Maybe Centi -> Maybe Centi
difference :: Maybe Centi -> Maybe Centi -> Maybe Centi
difference Maybe Centi
Nothing Maybe Centi
Nothing = Maybe Centi
forall a. Maybe a
Nothing
difference Maybe Centi
a Maybe Centi
b = Centi -> Maybe Centi
forall a. a -> Maybe a
Just (Centi -> Maybe Centi -> Centi
forall a. a -> Maybe a -> a
fromMaybe Centi
0 Maybe Centi
a Centi -> Centi -> Centi
forall a. Num a => a -> a -> a
- Centi -> Maybe Centi -> Centi
forall a. a -> Maybe a -> a
fromMaybe Centi
0 Maybe Centi
b)

-- | Non-negative subtraction under 'Maybe', returning @Just 0@ instead of negative results
nonNegativeDifference :: Maybe Centi -> Maybe Centi -> Maybe Centi
nonNegativeDifference :: Maybe Centi -> Maybe Centi -> Maybe Centi
nonNegativeDifference Maybe Centi
Nothing Maybe Centi
Nothing = Maybe Centi
forall a. Maybe a
Nothing
nonNegativeDifference Maybe Centi
a Maybe Centi
b = Centi -> Maybe Centi
forall a. a -> Maybe a
Just (Centi -> Centi -> Centi
forall a. Ord a => a -> a -> a
max Centi
0 (Centi -> Centi) -> Centi -> Centi
forall a b. (a -> b) -> a -> b
$ Centi -> Maybe Centi -> Centi
forall a. a -> Maybe a -> a
fromMaybe Centi
0 Maybe Centi
a Centi -> Centi -> Centi
forall a. Num a => a -> a -> a
- Centi -> Maybe Centi -> Centi
forall a. a -> Maybe a -> a
fromMaybe Centi
0 Maybe Centi
b)

-- | Multiplication under 'Maybe'
fractionOf :: Maybe Rational -> Maybe Centi -> Maybe Centi
fractionOf :: Maybe Rational -> Maybe Centi -> Maybe Centi
fractionOf (Just Rational
x) (Just Centi
amt) = Centi -> Maybe Centi
forall a. a -> Maybe a
Just (Centi -> Maybe Centi) -> Centi -> Maybe Centi
forall a b. (a -> b) -> a -> b
$ Rational -> Centi
forall a. Fractional a => Rational -> a
fromRational (Rational
x Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Centi -> Rational
forall a. Real a => a -> Rational
toRational Centi
amt)
fractionOf Maybe Rational
_ Maybe Centi
_ = Maybe Centi
forall a. Maybe a
Nothing