{-# LANGUAGE DeriveLift #-}

module Argo.Internal.Type.Decimal where

import Data.Ratio ((%))

import qualified Argo.Vendor.DeepSeq as DeepSeq
import qualified Argo.Vendor.TemplateHaskell as TH
import qualified Data.List as List
import qualified Data.Ratio as Ratio
import qualified Numeric

data Decimal = Decimal Integer Integer
    deriving (Decimal -> Decimal -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Decimal -> Decimal -> Bool
$c/= :: Decimal -> Decimal -> Bool
== :: Decimal -> Decimal -> Bool
$c== :: Decimal -> Decimal -> Bool
Eq, forall t.
(forall (m :: * -> *). Quote m => t -> m Exp)
-> (forall (m :: * -> *). Quote m => t -> Code m t) -> Lift t
forall (m :: * -> *). Quote m => Decimal -> m Exp
forall (m :: * -> *). Quote m => Decimal -> Code m Decimal
liftTyped :: forall (m :: * -> *). Quote m => Decimal -> Code m Decimal
$cliftTyped :: forall (m :: * -> *). Quote m => Decimal -> Code m Decimal
lift :: forall (m :: * -> *). Quote m => Decimal -> m Exp
$clift :: forall (m :: * -> *). Quote m => Decimal -> m Exp
TH.Lift, Int -> Decimal -> ShowS
[Decimal] -> ShowS
Decimal -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Decimal] -> ShowS
$cshowList :: [Decimal] -> ShowS
show :: Decimal -> String
$cshow :: Decimal -> String
showsPrec :: Int -> Decimal -> ShowS
$cshowsPrec :: Int -> Decimal -> ShowS
Show)

instance DeepSeq.NFData Decimal where
    rnf :: Decimal -> ()
rnf (Decimal Integer
s Integer
e) = forall a. NFData a => a -> ()
DeepSeq.rnf (Integer
s, Integer
e)

negate :: Decimal -> Decimal
negate :: Decimal -> Decimal
negate (Decimal Integer
s Integer
e) = Integer -> Integer -> Decimal
Decimal (-Integer
s) Integer
e

decimal :: Integer -> Integer -> Decimal
decimal :: Integer -> Integer -> Decimal
decimal Integer
s = Decimal -> Decimal
normalize forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Integer -> Decimal
Decimal Integer
s

normalize :: Decimal -> Decimal
normalize :: Decimal -> Decimal
normalize (Decimal Integer
s Integer
e) = if Integer
s forall a. Eq a => a -> a -> Bool
== Integer
0
    then Integer -> Integer -> Decimal
Decimal Integer
0 Integer
0
    else
        let (Integer
q, Integer
r) = forall a. Integral a => a -> a -> (a, a)
quotRem Integer
s Integer
10
        in if Integer
r forall a. Eq a => a -> a -> Bool
== Integer
0 then Decimal -> Decimal
normalize forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Decimal
Decimal Integer
q (Integer
e forall a. Num a => a -> a -> a
+ Integer
1) else Integer -> Integer -> Decimal
Decimal Integer
s Integer
e

fromInteger :: Integer -> Decimal
fromInteger :: Integer -> Decimal
fromInteger = forall a b c. (a -> b -> c) -> b -> a -> c
flip Integer -> Integer -> Decimal
decimal Integer
0

toInteger :: Decimal -> Maybe Integer
toInteger :: Decimal -> Maybe Integer
toInteger (Decimal Integer
s Integer
e) = if Integer
e forall a. Ord a => a -> a -> Bool
< Integer
0 then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Integer
s forall a. Num a => a -> a -> a
* Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Integer
e

fromRealFloat :: RealFloat a => a -> Maybe Decimal
fromRealFloat :: forall a. RealFloat a => a -> Maybe Decimal
fromRealFloat a
x = if forall a. RealFloat a => a -> Bool
isNaN a
x Bool -> Bool -> Bool
|| forall a. RealFloat a => a -> Bool
isInfinite a
x
    then forall a. Maybe a
Nothing
    else
        forall a. a -> Maybe a
Just
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. (if a
x forall a. Ord a => a -> a -> Bool
< a
0 then Decimal -> Decimal
Argo.Internal.Type.Decimal.negate else forall a. a -> a
id)
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry [Int] -> Int -> Decimal
fromDigits
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. RealFloat a => Integer -> a -> ([Int], Int)
Numeric.floatToDigits Integer
10
        forall a b. (a -> b) -> a -> b
$ forall a. Num a => a -> a
abs a
x

toRealFloat :: RealFloat a => Decimal -> a
toRealFloat :: forall a. RealFloat a => Decimal -> a
toRealFloat = forall a. Fractional a => Rational -> a
Prelude.fromRational forall b c a. (b -> c) -> (a -> b) -> a -> c
. Decimal -> Rational
Argo.Internal.Type.Decimal.toRational

fromDigits :: [Int] -> Int -> Decimal
fromDigits :: [Int] -> Int -> Decimal
fromDigits [Int]
ds Int
e = forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Integer -> Integer -> Decimal
decimal forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
List.foldl'
    (\(Integer
a, Integer
n) Int
d -> (Integer
a forall a. Num a => a -> a -> a
* Integer
10 forall a. Num a => a -> a -> a
+ forall a. Integral a => a -> Integer
Prelude.toInteger Int
d, Integer
n forall a. Num a => a -> a -> a
- Integer
1))
    (Integer
0, forall a. Integral a => a -> Integer
Prelude.toInteger Int
e)
    [Int]
ds

toRational :: Decimal -> Rational
toRational :: Decimal -> Rational
toRational d :: Decimal
d@(Decimal Integer
s Integer
e) = forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Integer
s forall a. Integral a => a -> a -> Ratio a
% (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ (-Integer
e))) forall a b. (Integral a, Num b) => a -> b
fromIntegral
    forall a b. (a -> b) -> a -> b
$ Decimal -> Maybe Integer
Argo.Internal.Type.Decimal.toInteger Decimal
d

fromRational :: Rational -> Maybe Decimal
fromRational :: Rational -> Maybe Decimal
fromRational Rational
r =
    let
        n :: Integer
n = forall a. Ratio a -> a
Ratio.numerator Rational
r
        d1 :: Integer
d1 = forall a. Ratio a -> a
Ratio.denominator Rational
r
        (Integer
t, Integer
d2) = forall a b. (Num a, Integral b) => b -> a -> b -> (a, b)
factor Integer
2 (Integer
0 :: Integer) Integer
d1
        (Integer
f, Integer
d3) = forall a b. (Num a, Integral b) => b -> a -> b -> (a, b)
factor Integer
5 (Integer
0 :: Integer) Integer
d2
        p :: Integer
p = forall a. Ord a => a -> a -> a
max Integer
t Integer
f
    in if Integer
d3 forall a. Eq a => a -> a -> Bool
== Integer
1
        then forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Decimal
decimal (Integer
n forall a. Num a => a -> a -> a
* Integer
2 forall a b. (Num a, Integral b) => a -> b -> a
^ (Integer
p forall a. Num a => a -> a -> a
- Integer
t) forall a. Num a => a -> a -> a
* Integer
5 forall a b. (Num a, Integral b) => a -> b -> a
^ (Integer
p forall a. Num a => a -> a -> a
- Integer
f)) (-Integer
p)
        else forall a. Maybe a
Nothing

-- factor d 0 x = (p, y) <=> x = (d ^ p) * y
factor :: (Num a, Integral b) => b -> a -> b -> (a, b)
factor :: forall a b. (Num a, Integral b) => b -> a -> b -> (a, b)
factor b
d a
n b
x =
    let (b
q, b
r) = forall a. Integral a => a -> a -> (a, a)
quotRem b
x b
d
    in if b
x forall a. Eq a => a -> a -> Bool
/= b
0 Bool -> Bool -> Bool
&& b
r forall a. Eq a => a -> a -> Bool
== b
0 then forall a b. (Num a, Integral b) => b -> a -> b -> (a, b)
factor b
d (a
n forall a. Num a => a -> a -> a
+ a
1) b
q else (a
n, b
x)