{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_HADDOCK hide #-}

-- |
-- Copyright: © 2018-2024 Intersect MBO
-- License: Apache-2.0
--
module Internal.Coin
    (
      -- * Types
      Coin

      -- Construction and Deconstruction
    , coinFromIntegral
    , coinFromNatural
    , coinToIntegral
    , coinToNatural

      -- * Unary Operations
    , pred
    , succ

      -- * Binary Operations
    , add
    , sub
    , mul
    , div
    , mod

      -- * Calculating Distances
    , distance

      -- * Value Tests
    , isZero

      -- * Special Values
    , zero
    , one

    ) where

import Prelude hiding ( div, fromIntegral, mod, pred, succ )

import GHC.Generics ( Generic )
import Numeric.Natural ( Natural )
import Quiet ( Quiet (Quiet) )

import qualified Prelude

-- | Represents a non-negative integral amount of currency.
--
-- Use 'coinFromNatural' to create a coin from a natural number.
--
-- Use 'coinToNatural' to convert a coin into a natural number.
--
-- @since 1.0.0
newtype Coin = Coin { Coin -> Natural
unCoin :: Natural }
    deriving stock (Coin -> Coin -> Bool
(Coin -> Coin -> Bool) -> (Coin -> Coin -> Bool) -> Eq Coin
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Coin -> Coin -> Bool
== :: Coin -> Coin -> Bool
$c/= :: Coin -> Coin -> Bool
/= :: Coin -> Coin -> Bool
Eq, (forall x. Coin -> Rep Coin x)
-> (forall x. Rep Coin x -> Coin) -> Generic Coin
forall x. Rep Coin x -> Coin
forall x. Coin -> Rep Coin x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Coin -> Rep Coin x
from :: forall x. Coin -> Rep Coin x
$cto :: forall x. Rep Coin x -> Coin
to :: forall x. Rep Coin x -> Coin
Generic, Eq Coin
Eq Coin =>
(Coin -> Coin -> Ordering)
-> (Coin -> Coin -> Bool)
-> (Coin -> Coin -> Bool)
-> (Coin -> Coin -> Bool)
-> (Coin -> Coin -> Bool)
-> (Coin -> Coin -> Coin)
-> (Coin -> Coin -> Coin)
-> Ord Coin
Coin -> Coin -> Bool
Coin -> Coin -> Ordering
Coin -> Coin -> Coin
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Coin -> Coin -> Ordering
compare :: Coin -> Coin -> Ordering
$c< :: Coin -> Coin -> Bool
< :: Coin -> Coin -> Bool
$c<= :: Coin -> Coin -> Bool
<= :: Coin -> Coin -> Bool
$c> :: Coin -> Coin -> Bool
> :: Coin -> Coin -> Bool
$c>= :: Coin -> Coin -> Bool
>= :: Coin -> Coin -> Bool
$cmax :: Coin -> Coin -> Coin
max :: Coin -> Coin -> Coin
$cmin :: Coin -> Coin -> Coin
min :: Coin -> Coin -> Coin
Ord)
    deriving Int -> Coin -> ShowS
[Coin] -> ShowS
Coin -> String
(Int -> Coin -> ShowS)
-> (Coin -> String) -> ([Coin] -> ShowS) -> Show Coin
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Coin -> ShowS
showsPrec :: Int -> Coin -> ShowS
$cshow :: Coin -> String
show :: Coin -> String
$cshowList :: [Coin] -> ShowS
showList :: [Coin] -> ShowS
Show via (Quiet Coin)

-- | Creates a coin from an integral number.
--
-- Returns a coin if (and only if) the given input is not negative.
--
coinFromIntegral :: Integral i => i -> Maybe Coin
coinFromIntegral :: forall i. Integral i => i -> Maybe Coin
coinFromIntegral i
i
    | i
i i -> i -> Bool
forall a. Ord a => a -> a -> Bool
>= i
0    = Coin -> Maybe Coin
forall a. a -> Maybe a
Just (Coin -> Maybe Coin) -> Coin -> Maybe Coin
forall a b. (a -> b) -> a -> b
$ Natural -> Coin
Coin (Natural -> Coin) -> Natural -> Coin
forall a b. (a -> b) -> a -> b
$ i -> Natural
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral i
i
    | Bool
otherwise = Maybe Coin
forall a. Maybe a
Nothing

-- | Creates a coin from a natural number.
--
-- @since 1.0.0
coinFromNatural :: Natural -> Coin
coinFromNatural :: Natural -> Coin
coinFromNatural = Natural -> Coin
Coin

-- | Converts the given coin into an integral number.
--
coinToIntegral :: Integral i => Coin -> i
coinToIntegral :: forall i. Integral i => Coin -> i
coinToIntegral (Coin Natural
i) = Natural -> i
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral Natural
i

-- | Converts the given coin into a natural number.
--
-- @since 1.0.0
coinToNatural :: Coin -> Natural
coinToNatural :: Coin -> Natural
coinToNatural = Coin -> Natural
unCoin

add :: Coin -> Coin -> Coin
add :: Coin -> Coin -> Coin
add (Coin Natural
x) (Coin Natural
y) = Natural -> Coin
Coin (Natural -> Coin) -> Natural -> Coin
forall a b. (a -> b) -> a -> b
$ Natural
x Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
+ Natural
y

sub :: Coin -> Coin -> Maybe Coin
sub :: Coin -> Coin -> Maybe Coin
sub (Coin Natural
x) (Coin Natural
y) = Integer -> Maybe Coin
forall i. Integral i => i -> Maybe Coin
coinFromIntegral (Integer -> Maybe Coin) -> Integer -> Maybe Coin
forall a b. (a -> b) -> a -> b
$ Natural -> Integer
forall a. Integral a => a -> Integer
toInteger Natural
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Natural -> Integer
forall a. Integral a => a -> Integer
toInteger Natural
y

mul :: Integral i => Coin -> i -> Maybe Coin
mul :: forall i. Integral i => Coin -> i -> Maybe Coin
mul (Coin Natural
x) i
y = Integer -> Maybe Coin
forall i. Integral i => i -> Maybe Coin
coinFromIntegral (Integer -> Maybe Coin) -> Integer -> Maybe Coin
forall a b. (a -> b) -> a -> b
$ Natural -> Integer
forall a. Integral a => a -> Integer
toInteger Natural
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* i -> Integer
forall a. Integral a => a -> Integer
toInteger i
y

div :: Integral i => Coin -> i -> Maybe Coin
div :: forall i. Integral i => Coin -> i -> Maybe Coin
div (Coin Natural
x) i
y
    | i
y i -> i -> Bool
forall a. Ord a => a -> a -> Bool
<= i
0    = Maybe Coin
forall a. Maybe a
Nothing
    | Bool
otherwise = Integer -> Maybe Coin
forall i. Integral i => i -> Maybe Coin
coinFromIntegral (Integer -> Maybe Coin) -> Integer -> Maybe Coin
forall a b. (a -> b) -> a -> b
$ Natural -> Integer
forall a. Integral a => a -> Integer
toInteger Natural
x Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`Prelude.div` i -> Integer
forall a. Integral a => a -> Integer
toInteger i
y

mod :: Integral i => Coin -> i -> Maybe Coin
mod :: forall i. Integral i => Coin -> i -> Maybe Coin
mod (Coin Natural
x) i
y
    | i
y i -> i -> Bool
forall a. Ord a => a -> a -> Bool
<= i
0    = Maybe Coin
forall a. Maybe a
Nothing
    | Bool
otherwise = Integer -> Maybe Coin
forall i. Integral i => i -> Maybe Coin
coinFromIntegral (Integer -> Maybe Coin) -> Integer -> Maybe Coin
forall a b. (a -> b) -> a -> b
$ Natural -> Integer
forall a. Integral a => a -> Integer
toInteger Natural
x Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`Prelude.mod` i -> Integer
forall a. Integral a => a -> Integer
toInteger i
y

distance :: Coin -> Coin -> Coin
distance :: Coin -> Coin -> Coin
distance (Coin Natural
x) (Coin Natural
y)
    | Natural
x Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
>= Natural
y    = Natural -> Coin
Coin (Natural -> Coin) -> Natural -> Coin
forall a b. (a -> b) -> a -> b
$ Natural
x Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
y
    | Bool
otherwise = Natural -> Coin
Coin (Natural -> Coin) -> Natural -> Coin
forall a b. (a -> b) -> a -> b
$ Natural
y Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
x

pred :: Coin -> Maybe Coin
pred :: Coin -> Maybe Coin
pred Coin
x = Coin
x Coin -> Coin -> Maybe Coin
`sub` Coin
one

succ :: Coin -> Coin
succ :: Coin -> Coin
succ Coin
x = Coin
x Coin -> Coin -> Coin
`add` Coin
one

isZero :: Coin -> Bool
isZero :: Coin -> Bool
isZero = (Coin -> Coin -> Bool
forall a. Eq a => a -> a -> Bool
== Coin
zero)

zero :: Coin
zero :: Coin
zero = Natural -> Coin
Coin Natural
0

one :: Coin
one :: Coin
one = Natural -> Coin
Coin Natural
1

newtype Sum a = Sum { forall a. Sum a -> a
getSum :: a }
    deriving stock (Sum a -> Sum a -> Bool
(Sum a -> Sum a -> Bool) -> (Sum a -> Sum a -> Bool) -> Eq (Sum a)
forall a. Eq a => Sum a -> Sum a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => Sum a -> Sum a -> Bool
== :: Sum a -> Sum a -> Bool
$c/= :: forall a. Eq a => Sum a -> Sum a -> Bool
/= :: Sum a -> Sum a -> Bool
Eq, (forall x. Sum a -> Rep (Sum a) x)
-> (forall x. Rep (Sum a) x -> Sum a) -> Generic (Sum a)
forall x. Rep (Sum a) x -> Sum a
forall x. Sum a -> Rep (Sum a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (Sum a) x -> Sum a
forall a x. Sum a -> Rep (Sum a) x
$cfrom :: forall a x. Sum a -> Rep (Sum a) x
from :: forall x. Sum a -> Rep (Sum a) x
$cto :: forall a x. Rep (Sum a) x -> Sum a
to :: forall x. Rep (Sum a) x -> Sum a
Generic, Eq (Sum a)
Eq (Sum a) =>
(Sum a -> Sum a -> Ordering)
-> (Sum a -> Sum a -> Bool)
-> (Sum a -> Sum a -> Bool)
-> (Sum a -> Sum a -> Bool)
-> (Sum a -> Sum a -> Bool)
-> (Sum a -> Sum a -> Sum a)
-> (Sum a -> Sum a -> Sum a)
-> Ord (Sum a)
Sum a -> Sum a -> Bool
Sum a -> Sum a -> Ordering
Sum a -> Sum a -> Sum a
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall a. Ord a => Eq (Sum a)
forall a. Ord a => Sum a -> Sum a -> Bool
forall a. Ord a => Sum a -> Sum a -> Ordering
forall a. Ord a => Sum a -> Sum a -> Sum a
$ccompare :: forall a. Ord a => Sum a -> Sum a -> Ordering
compare :: Sum a -> Sum a -> Ordering
$c< :: forall a. Ord a => Sum a -> Sum a -> Bool
< :: Sum a -> Sum a -> Bool
$c<= :: forall a. Ord a => Sum a -> Sum a -> Bool
<= :: Sum a -> Sum a -> Bool
$c> :: forall a. Ord a => Sum a -> Sum a -> Bool
> :: Sum a -> Sum a -> Bool
$c>= :: forall a. Ord a => Sum a -> Sum a -> Bool
>= :: Sum a -> Sum a -> Bool
$cmax :: forall a. Ord a => Sum a -> Sum a -> Sum a
max :: Sum a -> Sum a -> Sum a
$cmin :: forall a. Ord a => Sum a -> Sum a -> Sum a
min :: Sum a -> Sum a -> Sum a
Ord)
    deriving Int -> Sum a -> ShowS
[Sum a] -> ShowS
Sum a -> String
(Int -> Sum a -> ShowS)
-> (Sum a -> String) -> ([Sum a] -> ShowS) -> Show (Sum a)
forall a. Show a => Int -> Sum a -> ShowS
forall a. Show a => [Sum a] -> ShowS
forall a. Show a => Sum a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> Sum a -> ShowS
showsPrec :: Int -> Sum a -> ShowS
$cshow :: forall a. Show a => Sum a -> String
show :: Sum a -> String
$cshowList :: forall a. Show a => [Sum a] -> ShowS
showList :: [Sum a] -> ShowS
Show via (Quiet (Sum a))

instance Monoid Coin where
    mempty :: Coin
mempty = Coin
zero

instance Semigroup Coin where
    <> :: Coin -> Coin -> Coin
(<>) = Coin -> Coin -> Coin
add