{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE PolyKinds                  #-}
{-# LANGUAGE ScopedTypeVariables        #-}
module Data.Avro.Schema.Decimal
( Decimal
, fromUnderlyingValue
, underlyingValue )
where

import qualified Data.BigDecimal as D
import           Data.Proxy
import           GHC.TypeLits

newtype Decimal (p :: Nat) (s :: Nat)
  = Decimal { forall (p :: Natural) (s :: Natural). Decimal p s -> BigDecimal
unDecimal :: D.BigDecimal }
  deriving (Decimal p s -> Decimal p s -> Bool
(Decimal p s -> Decimal p s -> Bool)
-> (Decimal p s -> Decimal p s -> Bool) -> Eq (Decimal p s)
forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
== :: Decimal p s -> Decimal p s -> Bool
$c/= :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
/= :: Decimal p s -> Decimal p s -> Bool
Eq, Eq (Decimal p s)
Eq (Decimal p s) =>
(Decimal p s -> Decimal p s -> Ordering)
-> (Decimal p s -> Decimal p s -> Bool)
-> (Decimal p s -> Decimal p s -> Bool)
-> (Decimal p s -> Decimal p s -> Bool)
-> (Decimal p s -> Decimal p s -> Bool)
-> (Decimal p s -> Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s -> Decimal p s)
-> Ord (Decimal p s)
Decimal p s -> Decimal p s -> Bool
Decimal p s -> Decimal p s -> Ordering
Decimal p s -> Decimal p s -> Decimal p s
forall (p :: Natural) (s :: Natural). Eq (Decimal p s)
forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Ordering
forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
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 :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Ordering
compare :: Decimal p s -> Decimal p s -> Ordering
$c< :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
< :: Decimal p s -> Decimal p s -> Bool
$c<= :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
<= :: Decimal p s -> Decimal p s -> Bool
$c> :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
> :: Decimal p s -> Decimal p s -> Bool
$c>= :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Bool
>= :: Decimal p s -> Decimal p s -> Bool
$cmax :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
max :: Decimal p s -> Decimal p s -> Decimal p s
$cmin :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
min :: Decimal p s -> Decimal p s -> Decimal p s
Ord, Int -> Decimal p s -> ShowS
[Decimal p s] -> ShowS
Decimal p s -> String
(Int -> Decimal p s -> ShowS)
-> (Decimal p s -> String)
-> ([Decimal p s] -> ShowS)
-> Show (Decimal p s)
forall (p :: Natural) (s :: Natural). Int -> Decimal p s -> ShowS
forall (p :: Natural) (s :: Natural). [Decimal p s] -> ShowS
forall (p :: Natural) (s :: Natural). Decimal p s -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall (p :: Natural) (s :: Natural). Int -> Decimal p s -> ShowS
showsPrec :: Int -> Decimal p s -> ShowS
$cshow :: forall (p :: Natural) (s :: Natural). Decimal p s -> String
show :: Decimal p s -> String
$cshowList :: forall (p :: Natural) (s :: Natural). [Decimal p s] -> ShowS
showList :: [Decimal p s] -> ShowS
Show, ReadPrec [Decimal p s]
ReadPrec (Decimal p s)
Int -> ReadS (Decimal p s)
ReadS [Decimal p s]
(Int -> ReadS (Decimal p s))
-> ReadS [Decimal p s]
-> ReadPrec (Decimal p s)
-> ReadPrec [Decimal p s]
-> Read (Decimal p s)
forall (p :: Natural) (s :: Natural). ReadPrec [Decimal p s]
forall (p :: Natural) (s :: Natural). ReadPrec (Decimal p s)
forall (p :: Natural) (s :: Natural). Int -> ReadS (Decimal p s)
forall (p :: Natural) (s :: Natural). ReadS [Decimal p s]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: forall (p :: Natural) (s :: Natural). Int -> ReadS (Decimal p s)
readsPrec :: Int -> ReadS (Decimal p s)
$creadList :: forall (p :: Natural) (s :: Natural). ReadS [Decimal p s]
readList :: ReadS [Decimal p s]
$creadPrec :: forall (p :: Natural) (s :: Natural). ReadPrec (Decimal p s)
readPrec :: ReadPrec (Decimal p s)
$creadListPrec :: forall (p :: Natural) (s :: Natural). ReadPrec [Decimal p s]
readListPrec :: ReadPrec [Decimal p s]
Read, Integer -> Decimal p s
Decimal p s -> Decimal p s
Decimal p s -> Decimal p s -> Decimal p s
(Decimal p s -> Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s)
-> (Integer -> Decimal p s)
-> Num (Decimal p s)
forall (p :: Natural) (s :: Natural). Integer -> Decimal p s
forall (p :: Natural) (s :: Natural). Decimal p s -> Decimal p s
forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
$c+ :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
+ :: Decimal p s -> Decimal p s -> Decimal p s
$c- :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
- :: Decimal p s -> Decimal p s -> Decimal p s
$c* :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
* :: Decimal p s -> Decimal p s -> Decimal p s
$cnegate :: forall (p :: Natural) (s :: Natural). Decimal p s -> Decimal p s
negate :: Decimal p s -> Decimal p s
$cabs :: forall (p :: Natural) (s :: Natural). Decimal p s -> Decimal p s
abs :: Decimal p s -> Decimal p s
$csignum :: forall (p :: Natural) (s :: Natural). Decimal p s -> Decimal p s
signum :: Decimal p s -> Decimal p s
$cfromInteger :: forall (p :: Natural) (s :: Natural). Integer -> Decimal p s
fromInteger :: Integer -> Decimal p s
Num, Num (Decimal p s)
Num (Decimal p s) =>
(Decimal p s -> Decimal p s -> Decimal p s)
-> (Decimal p s -> Decimal p s)
-> (Rational -> Decimal p s)
-> Fractional (Decimal p s)
Rational -> Decimal p s
Decimal p s -> Decimal p s
Decimal p s -> Decimal p s -> Decimal p s
forall (p :: Natural) (s :: Natural). Num (Decimal p s)
forall (p :: Natural) (s :: Natural). Rational -> Decimal p s
forall (p :: Natural) (s :: Natural). Decimal p s -> Decimal p s
forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
forall a.
Num a =>
(a -> a -> a) -> (a -> a) -> (Rational -> a) -> Fractional a
$c/ :: forall (p :: Natural) (s :: Natural).
Decimal p s -> Decimal p s -> Decimal p s
/ :: Decimal p s -> Decimal p s -> Decimal p s
$crecip :: forall (p :: Natural) (s :: Natural). Decimal p s -> Decimal p s
recip :: Decimal p s -> Decimal p s
$cfromRational :: forall (p :: Natural) (s :: Natural). Rational -> Decimal p s
fromRational :: Rational -> Decimal p s
Fractional, Num (Decimal p s)
Ord (Decimal p s)
(Num (Decimal p s), Ord (Decimal p s)) =>
(Decimal p s -> Rational) -> Real (Decimal p s)
Decimal p s -> Rational
forall (p :: Natural) (s :: Natural). Num (Decimal p s)
forall (p :: Natural) (s :: Natural). Ord (Decimal p s)
forall (p :: Natural) (s :: Natural). Decimal p s -> Rational
forall a. (Num a, Ord a) => (a -> Rational) -> Real a
$ctoRational :: forall (p :: Natural) (s :: Natural). Decimal p s -> Rational
toRational :: Decimal p s -> Rational
Real)

intScale :: D.BigDecimal -> Integer
intScale :: BigDecimal -> Integer
intScale = Natural -> Integer
forall a. Integral a => a -> Integer
toInteger (Natural -> Integer)
-> (BigDecimal -> Natural) -> BigDecimal -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BigDecimal -> Natural
D.scale

fromUnderlyingValue
  :: forall p s. KnownNat s
  => Integer -> Decimal p s
fromUnderlyingValue :: forall (p :: Natural) (s :: Natural).
KnownNat s =>
Integer -> Decimal p s
fromUnderlyingValue Integer
n
  = BigDecimal -> Decimal p s
forall (p :: Natural) (s :: Natural). BigDecimal -> Decimal p s
Decimal (BigDecimal -> Decimal p s) -> BigDecimal -> Decimal p s
forall a b. (a -> b) -> a -> b
$ Integer -> Natural -> BigDecimal
D.BigDecimal Integer
n (Integer -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Natural) -> Integer -> Natural
forall a b. (a -> b) -> a -> b
$ Proxy s -> Integer
forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy s
forall {k} (t :: k). Proxy t
Proxy :: Proxy s))

underlyingValue
  :: forall s p. (KnownNat p, KnownNat s)
  => Decimal p s -> Maybe Int
underlyingValue :: forall (s :: Natural) (p :: Natural).
(KnownNat p, KnownNat s) =>
Decimal p s -> Maybe Int
underlyingValue (Decimal BigDecimal
d)
  = let ss :: Integer
ss = Proxy s -> Integer
forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy s
forall {k} (t :: k). Proxy t
Proxy :: Proxy s)
        pp :: Integer
pp = Proxy p -> Integer
forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy p
forall {k} (t :: k). Proxy t
Proxy :: Proxy p)
        new :: BigDecimal
new = if Integer
ss Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> BigDecimal -> Integer
intScale BigDecimal
d
                 then Integer -> Natural -> BigDecimal
D.BigDecimal (BigDecimal -> Integer
D.value BigDecimal
d Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
10 Integer -> Integer -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ (Integer
ss Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- BigDecimal -> Integer
intScale BigDecimal
d)) (Integer -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
ss)
                 else BigDecimal -> RoundingAdvice -> BigDecimal
D.roundBD BigDecimal
d (Natural -> RoundingAdvice
D.halfUp (Integer -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
ss))
    in if BigDecimal -> Natural
D.precision BigDecimal
new Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Integer -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
pp
          then Maybe Int
forall a. Maybe a
Nothing
          else Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ BigDecimal -> Integer
D.value BigDecimal
new