-- | 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 :: (Foldable f, Num a) => f (Maybe a) -> Maybe a
totalOf :: forall (f :: * -> *) a.
(Foldable f, Num a) =>
f (Maybe a) -> Maybe a
totalOf = (Maybe a -> Maybe a -> Maybe a)
-> Maybe a -> f (Maybe a) -> Maybe a
forall b a. (b -> a -> b) -> b -> f a -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Maybe a -> Maybe a -> Maybe a
forall {a}. Num a => Maybe a -> Maybe a -> Maybe a
add Maybe a
forall a. Maybe a
Nothing
  where add :: Maybe a -> Maybe a -> Maybe a
add Maybe a
Nothing Maybe a
x = Maybe a
x
        add Maybe a
x Maybe a
Nothing = Maybe a
x
        add (Just a
x) (Just a
y) = a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$! a
xa -> a -> a
forall a. Num a => a -> a -> a
+a
y

-- | 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