{-# LANGUAGE DeriveDataTypeable #-}

-- | Decimal numbers are represented as @m*10^(-e)@ where
-- @m@ and @e@ are integers.  The exponent @e@ is an unsigned Word8.  Hence
-- the smallest value that can be represented is @10^-255@.
--
-- Unary arithmetic results have the exponent of the argument.
-- Addition and subtraction results have an exponent equal to the
-- maximum of the exponents of the arguments. Other operators have
-- exponents sufficient to show the exact result, up to a limit of
-- 255:
--
-- > 0.15 * 0.15 :: Decimal    = 0.0225
-- > (1/3) :: Decimal          = 0.33333333333333...
-- > decimalPlaces (1/3)       = 255
--
-- While @(/)@ is defined, you don't normally want to use it. Instead
-- The functions "divide" and "allocate" will split a decimal amount
-- into lists of results which are guaranteed to sum to the original
-- number.  This is a useful property when doing financial arithmetic.
--
-- The arithmetic on mantissas is always done using @Integer@, regardless of
-- the type of @DecimalRaw@ being manipulated.  In practice it is strongly
-- recommended that @Decimal@ be used, with other types being used only where
-- necessary (e.g. to conform to a network protocol). For instance
-- @(1/3) :: DecimalRaw Int@ does not give the right answer.
--
-- Care must be taken with literal values of type Decimal. As per the Haskell
-- Report, the literal @10.00@ will be converted into @fromRational 10.00@, which
-- in a @Decimal@ context will be converted into @10@ with zero decimal places.
-- Likewise @10.10@ will be converted into @10.1@ with one decimal place. If
-- you mean @10.00@ with 2 decimal places then you have to write @roundTo 2 10@.


module Data.Decimal (
   -- ** Decimal Values
   DecimalRaw (..),
   Decimal,
   realFracToDecimal,
   decimalConvert,
   unsafeDecimalConvert,
   roundTo,
   roundTo',
   (*.),
   divide,
   allocate,
   eitherFromRational,
   normalizeDecimal
) where


import Control.DeepSeq
import Data.Char
import Data.Ratio
import Data.Word
import Data.Typeable
import Text.ParserCombinators.ReadP

-- | Raw decimal arithmetic type constructor.  A decimal value consists of an
-- integer mantissa and a negative exponent which is interpreted as the number
-- of decimal places.  The value stored in a @Decimal d@ is therefore equal to:
--
-- > decimalMantissa d / (10 ^ decimalPlaces d)
--
-- The "Show" instance will add trailing zeros, so @show $ Decimal 3 1500@
-- will return \"1.500\".  Conversely the "Read" instance will use the decimal
-- places to determine the precision.
data DecimalRaw i = Decimal {
      forall i. DecimalRaw i -> Word8
decimalPlaces :: !Word8,
      forall i. DecimalRaw i -> i
decimalMantissa :: !i}
                                  deriving (Typeable)


-- | Arbitrary precision decimal type.  Programs should do decimal
-- arithmetic with this type and only convert to other instances of
-- "DecimalRaw" where required by an external interface. This will avoid
-- issues with integer overflows.
--
-- Using this type is also faster because it avoids repeated conversions
-- to and from @Integer@.
type Decimal = DecimalRaw Integer

instance (NFData i) => NFData (DecimalRaw i) where
    rnf :: DecimalRaw i -> ()
rnf (Decimal Word8
_ i
i) = forall a. NFData a => a -> ()
rnf i
i

instance (Integral i) => Enum (DecimalRaw i) where
   succ :: DecimalRaw i -> DecimalRaw i
succ DecimalRaw i
x = DecimalRaw i
x forall a. Num a => a -> a -> a
+ DecimalRaw i
1
   pred :: DecimalRaw i -> DecimalRaw i
pred DecimalRaw i
x = DecimalRaw i
x forall a. Num a => a -> a -> a
- DecimalRaw i
1
   toEnum :: Int -> DecimalRaw i
toEnum = forall a b. (Integral a, Num b) => a -> b
fromIntegral
   fromEnum :: DecimalRaw i -> Int
fromEnum = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall i. DecimalRaw i -> i
decimalMantissa forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo Word8
0
   enumFrom :: DecimalRaw i -> [DecimalRaw i]
enumFrom = forall a. (a -> a) -> a -> [a]
iterate (forall a. Num a => a -> a -> a
+DecimalRaw i
1)
   enumFromThen :: DecimalRaw i -> DecimalRaw i -> [DecimalRaw i]
enumFromThen DecimalRaw i
x1 DecimalRaw i
x2 = let dx :: DecimalRaw i
dx = DecimalRaw i
x2 forall a. Num a => a -> a -> a
- DecimalRaw i
x1 in forall a. (a -> a) -> a -> [a]
iterate (forall a. Num a => a -> a -> a
+DecimalRaw i
dx) DecimalRaw i
x1
   enumFromTo :: DecimalRaw i -> DecimalRaw i -> [DecimalRaw i]
enumFromTo DecimalRaw i
x1 DecimalRaw i
x2 = forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Ord a => a -> a -> Bool
<= DecimalRaw i
x2) forall a b. (a -> b) -> a -> b
$ forall a. (a -> a) -> a -> [a]
iterate (forall a. Num a => a -> a -> a
+DecimalRaw i
1) DecimalRaw i
x1
   enumFromThenTo :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i -> [DecimalRaw i]
enumFromThenTo DecimalRaw i
x1 DecimalRaw i
x2 DecimalRaw i
x3 = forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Ord a => a -> a -> Bool
<= DecimalRaw i
x3) forall a b. (a -> b) -> a -> b
$ forall a. Enum a => a -> a -> [a]
enumFromThen DecimalRaw i
x1 DecimalRaw i
x2


-- | Convert a real fractional value into a Decimal of the appropriate
-- precision.
realFracToDecimal :: (Integral i, RealFrac r) => Word8 -> r -> DecimalRaw i
realFracToDecimal :: forall i r. (Integral i, RealFrac r) => Word8 -> r -> DecimalRaw i
realFracToDecimal Word8
e r
r = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e forall a b. (a -> b) -> a -> b
$ forall a b. (RealFrac a, Integral b) => a -> b
round (r
r forall a. Num a => a -> a -> a
* (r
10forall a b. (Num a, Integral b) => a -> b -> a
^Word8
e))


-- Internal function to divide and return the nearest integer. Implements Bankers' Rounding in
-- which 0.5 is rounded to the nearest even value. This follows the practice of "Prelude.round".
divRound :: (Integral a) => a -> a -> a
divRound :: forall a. Integral a => a -> a -> a
divRound a
n1 a
n2 = a
n forall a. Num a => a -> a -> a
+ a
bankers
    where
      (a
n, a
r) = a
n1 forall a. Integral a => a -> a -> (a, a)
`quotRem` a
n2
      bankers :: a
bankers = case forall a. Ord a => a -> a -> Ordering
compare (forall a. Num a => a -> a
abs a
r forall a. Num a => a -> a -> a
* a
2) (forall a. Num a => a -> a
abs a
n2) of
         Ordering
LT -> a
0
         Ordering
GT -> forall a. Num a => a -> a
signum a
n1
         Ordering
EQ -> if forall a. Integral a => a -> Bool
odd a
n then forall a. Num a => a -> a
signum a
n1 else a
0


-- | Convert a @DecimalRaw@ from one base representation to another.  Does
-- not check for overflow in the new representation. Only use after
-- using "roundTo" to put an upper value on the exponent, or to convert
-- to a larger representation.
unsafeDecimalConvert :: (Integral a, Integral b) => DecimalRaw a -> DecimalRaw b
unsafeDecimalConvert :: forall a b.
(Integral a, Integral b) =>
DecimalRaw a -> DecimalRaw b
unsafeDecimalConvert (Decimal Word8
e a
n) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral a
n


-- | Convert a @DecimalRaw@ from one base to another. Returns @Nothing@ if
-- this would cause arithmetic overflow.
decimalConvert :: (Integral a, Integral b, Bounded b) =>
   DecimalRaw a -> Maybe (DecimalRaw b)
decimalConvert :: forall a b.
(Integral a, Integral b, Bounded b) =>
DecimalRaw a -> Maybe (DecimalRaw b)
decimalConvert (Decimal Word8
e a
n) =
   let n1 :: Integer
       n1 :: Integer
n1 = forall a b. (Integral a, Num b) => a -> b
fromIntegral a
n
       n2 :: b
n2 = forall a b. (Integral a, Num b) => a -> b
fromIntegral a
n   -- Of type b.
       ub :: Integer
ub = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ forall a. Ord a => a -> a -> a
max forall a. Bounded a => a
maxBound b
n2  -- Can't say "maxBound :: b", so do this instead.
       lb :: Integer
lb = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ forall a. Ord a => a -> a -> a
min forall a. Bounded a => a
minBound b
n2
   in if Integer
lb forall a. Ord a => a -> a -> Bool
<= Integer
n1 Bool -> Bool -> Bool
&& Integer
n1 forall a. Ord a => a -> a -> Bool
<= Integer
ub then forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e b
n2 else forall a. Maybe a
Nothing


-- | Round a @DecimalRaw@ to a specified number of decimal places.
-- If the value ends in @5@ then it is rounded to the nearest even value (Banker's Rounding)
roundTo :: (Integral i) => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo :: forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo Word8
d (Decimal Word8
_ i
0) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
d i
0
roundTo Word8
d (Decimal Word8
e i
n) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
d forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral i
n1
    where
      n1 :: i
n1 = case forall a. Ord a => a -> a -> Ordering
compare Word8
d Word8
e of
             Ordering
LT -> i
n forall a. Integral a => a -> a -> a
`divRound` i
divisor
             Ordering
EQ -> i
n
             Ordering
GT -> i
n forall a. Num a => a -> a -> a
* i
multiplier
      divisor :: i
divisor = i
10 forall a b. (Num a, Integral b) => a -> b -> a
^ (Word8
eforall a. Num a => a -> a -> a
-Word8
d)
      multiplier :: i
multiplier = i
10 forall a b. (Num a, Integral b) => a -> b -> a
^ (Word8
dforall a. Num a => a -> a -> a
-Word8
e)

-- | Round a @DecimalRaw@ to a specified number of decimal places using the specified
-- rounding function. Typically this will be one of @floor@, @ceiling@, @truncate@ or @round@.
-- Note that @roundTo == roundTo\' round@
roundTo' :: (Integral i) => (Rational -> i) -> Word8 -> DecimalRaw i -> DecimalRaw i
roundTo' :: forall i.
Integral i =>
(Rational -> i) -> Word8 -> DecimalRaw i -> DecimalRaw i
roundTo' Rational -> i
_ Word8
d (Decimal Word8
_  i
0) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
d i
0
roundTo' Rational -> i
f Word8
d (Decimal Word8
e i
n) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
d forall a b. (a -> b) -> a -> b
$ Rational -> i
f Rational
n1
   where
      divisor :: Rational
divisor = Rational
10 forall a b. (Num a, Integral b) => a -> b -> a
^ (Word8
eforall a. Num a => a -> a -> a
-Word8
d)
      multiplier :: Rational
multiplier = Rational
10 forall a b. (Num a, Integral b) => a -> b -> a
^ (Word8
dforall a. Num a => a -> a -> a
-Word8
e)
      n1 :: Rational
n1 = case forall a. Ord a => a -> a -> Ordering
compare Word8
d Word8
e of
         Ordering
LT -> forall a. Real a => a -> Rational
toRational i
n forall a. Fractional a => a -> a -> a
/ Rational
divisor
         Ordering
EQ -> forall a. Real a => a -> Rational
toRational i
n
         Ordering
GT -> forall a. Real a => a -> Rational
toRational i
n forall a. Num a => a -> a -> a
* Rational
multiplier

-- Round the two DecimalRaw values to the largest exponent.
roundMax :: (Integral i) => DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
roundMax :: forall i.
Integral i =>
DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
roundMax (Decimal Word8
_  i
0)   (Decimal Word8
_  i
0)  = (Word8
0,i
0,i
0)
roundMax (Decimal Word8
e1 i
n1)  (Decimal Word8
_  i
0)  = (Word8
e1,i
n1,i
0)
roundMax (Decimal Word8
_  i
0)   (Decimal Word8
e2 i
n2) = (Word8
e2,i
0,i
n2)
roundMax d1 :: DecimalRaw i
d1@(Decimal Word8
e1 i
n1) d2 :: DecimalRaw i
d2@(Decimal Word8
e2 i
n2)
  | Word8
e1 forall a. Eq a => a -> a -> Bool
== Word8
e2  = (Word8
e1, i
n1, i
n2)
  | Bool
otherwise = (Word8
e, i
n1', i
n2')
    where
      e :: Word8
e = forall a. Ord a => a -> a -> a
max Word8
e1 Word8
e2
      (Decimal Word8
_ i
n1') = forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo Word8
e DecimalRaw i
d1
      (Decimal Word8
_ i
n2') = forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo Word8
e DecimalRaw i
d2


instance (Integral i, Show i) => Show (DecimalRaw i) where
   showsPrec :: Int -> DecimalRaw i -> ShowS
showsPrec Int
_ (Decimal Word8
e i
n)
       | Word8
e forall a. Eq a => a -> a -> Bool
== Word8
0     = ((String
signStr forall a. [a] -> [a] -> [a]
++ String
strN) forall a. [a] -> [a] -> [a]
++)
       | Bool
otherwise  = (forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String
signStr, String
intPart, String
".", String
fracPart] forall a. [a] -> [a] -> [a]
++)
       where
         strN :: String
strN = forall a. Show a => a -> String
show forall a b. (a -> b) -> a -> b
$ forall a. Num a => a -> a
abs i
n
         signStr :: String
signStr = if i
n forall a. Ord a => a -> a -> Bool
< i
0 then String
"-" else String
""
         len :: Int
len = forall (t :: * -> *) a. Foldable t => t a -> Int
length String
strN
         padded :: String
padded = forall a. Int -> a -> [a]
replicate (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
e forall a. Num a => a -> a -> a
+ Int
1 forall a. Num a => a -> a -> a
- Int
len) Char
'0' forall a. [a] -> [a] -> [a]
++ String
strN
         (String
intPart, String
fracPart) = forall a. Int -> [a] -> ([a], [a])
splitAt (forall a. Ord a => a -> a -> a
max Int
1 (Int
len forall a. Num a => a -> a -> a
- forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
e)) String
padded

instance (Integral i, Read i) => Read (DecimalRaw i) where
    readsPrec :: Int -> ReadS (DecimalRaw i)
readsPrec Int
_ = forall a. ReadP a -> ReadS a
readP_to_S forall i. (Integral i, Read i) => ReadP (DecimalRaw i)
readDecimalP


-- | Parse a Decimal value. Used for the Read instance.
readDecimalP :: (Integral i, Read i) => ReadP (DecimalRaw i)
readDecimalP :: forall i. (Integral i, Read i) => ReadP (DecimalRaw i)
readDecimalP = do
          ReadP ()
skipSpaces
          Char
s1           <- forall {a}. a -> ReadP a -> ReadP a
myOpt Char
'+' forall a b. (a -> b) -> a -> b
$ Char -> ReadP Char
char Char
'-' forall a. ReadP a -> ReadP a -> ReadP a
+++ Char -> ReadP Char
char Char
'+'
          String
intPart      <- (Char -> Bool) -> ReadP String
munch1 Char -> Bool
isDigit
          String
fractPart    <- forall {a}. a -> ReadP a -> ReadP a
myOpt String
"" forall a b. (a -> b) -> a -> b
$ do
                            Char
_ <- Char -> ReadP Char
char Char
'.'
                            (Char -> Bool) -> ReadP String
munch1 Char -> Bool
isDigit
          Int
expPart <- forall {a}. a -> ReadP a -> ReadP a
myOpt Int
0 forall a b. (a -> b) -> a -> b
$ do
                            Char
_  <- Char -> ReadP Char
char Char
'e' forall a. ReadP a -> ReadP a -> ReadP a
+++ Char -> ReadP Char
char Char
'E'
                            Char
s2 <- forall {a}. a -> ReadP a -> ReadP a
myOpt Char
'+' forall a b. (a -> b) -> a -> b
$ Char -> ReadP Char
char Char
'-' forall a. ReadP a -> ReadP a -> ReadP a
+++ Char -> ReadP Char
char Char
'+'
                            forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall {a}. Num a => Char -> a -> a
applySign Char
s2 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. Integral n => String -> n
strToInt) forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> ReadP String
munch1 Char -> Bool
isDigit
          let n :: i
n = forall {a}. Num a => Char -> a -> a
applySign Char
s1 forall a b. (a -> b) -> a -> b
$ forall n. Integral n => String -> n
strToInt forall a b. (a -> b) -> a -> b
$ String
intPart forall a. [a] -> [a] -> [a]
++ String
fractPart
              e :: Int
e = forall (t :: * -> *) a. Foldable t => t a -> Int
length String
fractPart forall a. Num a => a -> a -> a
- Int
expPart
          if Int
e forall a. Ord a => a -> a -> Bool
< Int
0
             then forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
0 forall a b. (a -> b) -> a -> b
$ i
n forall a. Num a => a -> a -> a
* i
10 forall a b. (Num a, Integral b) => a -> b -> a
^ forall a. Num a => a -> a
negate Int
e
             else if Int
e forall a. Ord a => a -> a -> Bool
< Int
256
                then forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall i. Word8 -> i -> DecimalRaw i
Decimal (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
e) i
n
                else forall a. ReadP a
pfail
    where
       strToInt :: (Integral n) => String -> n
       strToInt :: forall n. Integral n => String -> n
strToInt = forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\n
t n
v -> n
10 forall a. Num a => a -> a -> a
* n
t forall a. Num a => a -> a -> a
+ n
v) n
0 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => a -> a -> a
subtract (Char -> Int
ord Char
'0') forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Int
ord)
       applySign :: Char -> a -> a
applySign Char
'-' a
v = forall a. Num a => a -> a
negate a
v
       applySign Char
_   a
v = a
v
       myOpt :: a -> ReadP a -> ReadP a
myOpt a
d ReadP a
p = ReadP a
p forall a. ReadP a -> ReadP a -> ReadP a
<++ forall (m :: * -> *) a. Monad m => a -> m a
return a
d


instance (Integral i) => Eq (DecimalRaw i) where
   DecimalRaw i
d1 == :: DecimalRaw i -> DecimalRaw i -> Bool
== DecimalRaw i
d2   =   i
n1 forall a. Eq a => a -> a -> Bool
== i
n2 where (Word8
_, i
n1, i
n2) = forall i.
Integral i =>
DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
roundMax DecimalRaw i
d1 DecimalRaw i
d2


instance (Integral i) => Ord (DecimalRaw i) where
    compare :: DecimalRaw i -> DecimalRaw i -> Ordering
compare DecimalRaw i
d1 DecimalRaw i
d2 = forall a. Ord a => a -> a -> Ordering
compare i
n1 i
n2 where (Word8
_, i
n1, i
n2) = forall i.
Integral i =>
DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
roundMax DecimalRaw i
d1 DecimalRaw i
d2


instance (Integral i) => Num (DecimalRaw i) where
    (Decimal Word8
_ i
0) + :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i
+ DecimalRaw i
d = DecimalRaw i
d
    DecimalRaw i
d + (Decimal Word8
_ i
0) = DecimalRaw i
d
    DecimalRaw i
d1 + DecimalRaw i
d2 = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral (i
n1 forall a. Num a => a -> a -> a
+ i
n2)
        where (Word8
e, i
n1, i
n2) = forall i.
Integral i =>
DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
roundMax DecimalRaw i
d1 DecimalRaw i
d2
    (Decimal Word8
_ i
0) - :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i
- (Decimal Word8
e i
n) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e (-i
n)
    DecimalRaw i
d - (Decimal Word8
_ i
0) = DecimalRaw i
d
    DecimalRaw i
d1 - DecimalRaw i
d2 = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral (i
n1 forall a. Num a => a -> a -> a
- i
n2)
        where (Word8
e, i
n1, i
n2) = forall i.
Integral i =>
DecimalRaw i -> DecimalRaw i -> (Word8, i, i)
roundMax DecimalRaw i
d1 DecimalRaw i
d2
    (Decimal Word8
_ i
0) * :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i
* DecimalRaw i
_ = DecimalRaw i
0
    DecimalRaw i
_ * (Decimal Word8
_ i
0) = DecimalRaw i
0
    DecimalRaw i
d1 * DecimalRaw i
d2 = forall i. Integral i => DecimalRaw i -> DecimalRaw i
normalizeDecimal forall a b. (a -> b) -> a -> b
$ forall i r. (Integral i, RealFrac r) => Word8 -> r -> DecimalRaw i
realFracToDecimal forall a. Bounded a => a
maxBound forall a b. (a -> b) -> a -> b
$ forall a. Real a => a -> Rational
toRational DecimalRaw i
d1 forall a. Num a => a -> a -> a
* forall a. Real a => a -> Rational
toRational DecimalRaw i
d2
    abs :: DecimalRaw i -> DecimalRaw i
abs (Decimal Word8
e i
n) = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e forall a b. (a -> b) -> a -> b
$ forall a. Num a => a -> a
abs i
n
    signum :: DecimalRaw i -> DecimalRaw i
signum (Decimal Word8
_ i
n) = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ forall a. Num a => a -> a
signum i
n
    fromInteger :: Integer -> DecimalRaw i
fromInteger Integer
n = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
0 forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
n

instance (Integral i) => Real (DecimalRaw i) where
    toRational :: DecimalRaw i -> Rational
toRational (Decimal Word8
e i
n) = forall a b. (Integral a, Num b) => a -> b
fromIntegral i
n forall a. Integral a => a -> a -> Ratio a
% (Integer
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Word8
e)

instance (Integral i) => Fractional (DecimalRaw i) where
  fromRational :: Rational -> DecimalRaw i
fromRational Rational
r =
     let
        v :: Decimal
        v :: Decimal
v = forall i. Integral i => DecimalRaw i -> DecimalRaw i
normalizeDecimal forall a b. (a -> b) -> a -> b
$ forall i r. (Integral i, RealFrac r) => Word8 -> r -> DecimalRaw i
realFracToDecimal forall a. Bounded a => a
maxBound Rational
r
     in forall a b.
(Integral a, Integral b) =>
DecimalRaw a -> DecimalRaw b
unsafeDecimalConvert Decimal
v
  DecimalRaw i
a / :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i
/ DecimalRaw i
b = forall a. Fractional a => Rational -> a
fromRational forall a b. (a -> b) -> a -> b
$ forall a. Real a => a -> Rational
toRational DecimalRaw i
a forall a. Fractional a => a -> a -> a
/ forall a. Real a => a -> Rational
toRational DecimalRaw i
b

instance (Integral i) => RealFrac (DecimalRaw i) where
  properFraction :: forall b. Integral b => DecimalRaw i -> (b, DecimalRaw i)
properFraction DecimalRaw i
a = (b
rnd, forall a. Fractional a => Rational -> a
fromRational Rational
rep)
    where
      (b
rnd, Rational
rep) = forall a b. (RealFrac a, Integral b) => a -> (b, a)
properFraction forall a b. (a -> b) -> a -> b
$ forall a. Real a => a -> Rational
toRational DecimalRaw i
a



-- | Divide a @DecimalRaw@ value into one or more portions.  The portions
-- will be approximately equal, and the sum of the portions is guaranteed to
-- be the original value.
--
-- The portions are represented as a list of pairs.  The first part of each
-- pair is the number of portions, and the second part is the portion value.
-- Hence 10 dollars divided 3 ways will produce @[(2, 3.33), (1, 3.34)]@.
divide :: Decimal -> Int -> [(Int, Decimal)]
divide :: Decimal -> Int -> [(Int, Decimal)]
divide (Decimal Word8
e Integer
n) Int
d
    | Int
d forall a. Ord a => a -> a -> Bool
> Int
0 =
        case Integer
n forall a. Integral a => a -> a -> (a, a)
`divMod` forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
d of
          (Integer
result, Integer
0) -> [(Int
d, forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e Integer
result)]
          (Integer
result, Integer
r) -> [(Int
d forall a. Num a => a -> a -> a
- forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
r,
                           forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e Integer
result),
                          (forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
r, forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e (Integer
resultforall a. Num a => a -> a -> a
+Integer
1))]
    | Bool
otherwise = forall a. HasCallStack => String -> a
error String
"Data.Decimal.divide: Divisor must be > 0."



-- | Allocate a @DecimalRaw@ value proportionately with the values in a list.
-- The allocated portions are guaranteed to add up to the original value.
--
-- Some of the allocations may be zero or negative, but the sum of the list
-- must not be zero.  The allocation is intended to be as close as possible
-- to the following:
--
-- > let result = allocate d parts
-- > in all (== d / sum parts) $ zipWith (/) result parts
allocate :: Decimal -> [Integer] -> [Decimal]
allocate :: Decimal -> [Integer] -> [Decimal]
allocate (Decimal Word8
e Integer
n) [Integer]
ps
    | Integer
total forall a. Eq a => a -> a -> Bool
== Integer
0  =
        forall a. HasCallStack => String -> a
error String
"Data.Decimal.allocate: allocation list must not sum to zero."
    | Bool
otherwise   = forall a b. (a -> b) -> [a] -> [b]
map (forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e) forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (-) [Integer]
ts (forall a. [a] -> [a]
tail [Integer]
ts)
    where
      ts :: [Integer]
ts = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst forall a b. (a -> b) -> a -> b
$ forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl forall {b}. Integral b => (b, b) -> b -> (b, b)
nxt (Integer
n, Integer
total) [Integer]
ps
      nxt :: (b, b) -> b -> (b, b)
nxt (b
n1, b
t1) b
p1 = (b
n1 forall a. Num a => a -> a -> a
- (b
n1 forall a. Num a => a -> a -> a
* b
p1) forall a. Integral a => a -> a -> a
`zdiv` b
t1, b
t1 forall a. Num a => a -> a -> a
- b
p1)
      zdiv :: a -> a -> a
zdiv a
0 a
0 = a
0
      zdiv a
x a
y = a
x forall a. Integral a => a -> a -> a
`divRound` a
y
      total :: Integer
total = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [Integer]
ps


-- | Multiply a @DecimalRaw@ by a @RealFrac@ value.
(*.) :: (Integral i, RealFrac r) => DecimalRaw i -> r -> DecimalRaw i
(Decimal Word8
e i
m) *. :: forall i r.
(Integral i, RealFrac r) =>
DecimalRaw i -> r -> DecimalRaw i
*. r
d = forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
e forall a b. (a -> b) -> a -> b
$ forall a b. (RealFrac a, Integral b) => a -> b
round forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral i
m forall a. Num a => a -> a -> a
* r
d

-- | Count the divisors, i.e. the count of 2 divisors in 18 is 1 because 18 = 2 * 3 * 3
factorN :: (Integral a)
           => a                  -- ^ Denominator base
           -> a                  -- ^ dividing value
           -> (a, a)             -- ^ The count of divisors and the result of division
factorN :: forall a. Integral a => a -> a -> (a, a)
factorN a
d a
val = forall {t}. Num t => a -> t -> (t, a)
factorN' a
val a
0
  where
    factorN' :: a -> t -> (t, a)
factorN' a
1 t
acc = (t
acc, a
1)
    factorN' a
v t
acc = if a
md forall a. Eq a => a -> a -> Bool
== a
0
                     then a -> t -> (t, a)
factorN' a
vd (t
acc forall a. Num a => a -> a -> a
+ t
1)
                     else (t
acc, a
v)
      where
        (a
vd, a
md) = a
v forall a. Integral a => a -> a -> (a, a)
`divMod` a
d

-- | Try to convert Rational to Decimal with absolute precision
-- return string with fail description if not converted
eitherFromRational :: (Integral i) => Rational -> Either String (DecimalRaw i)
eitherFromRational :: forall i. Integral i => Rational -> Either String (DecimalRaw i)
eitherFromRational Rational
r = if Integer
done forall a. Eq a => a -> a -> Bool
== Integer
1
                       then do
                         Word8
wres <- Either String Word8
we
                         forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall i. Word8 -> i -> DecimalRaw i
Decimal Word8
wres (forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
m)
                       else forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show Rational
r forall a. [a] -> [a] -> [a]
++ String
" has no decimal denominator"
  where
    den :: Integer
den = forall a. Ratio a -> a
denominator Rational
r
    num :: Integer
num = forall a. Ratio a -> a
numerator Rational
r
    (Integer
f2, Integer
rest) = forall a. Integral a => a -> a -> (a, a)
factorN Integer
2 Integer
den
    (Integer
f5, Integer
done) = forall a. Integral a => a -> a -> (a, a)
factorN Integer
5 Integer
rest
    e :: Integer
e = forall a. Ord a => a -> a -> a
max Integer
f2 Integer
f5
    m :: Integer
m = Integer
num forall a. Num a => a -> a -> a
* ((Integer
10forall a b. (Num a, Integral b) => a -> b -> a
^Integer
e) forall a. Integral a => a -> a -> a
`div` Integer
den)
    we :: Either String Word8
we = if Integer
e forall a. Ord a => a -> a -> Bool
> forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. Bounded a => a
maxBound :: Word8)
         then forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show Integer
e forall a. [a] -> [a] -> [a]
++ String
" is too big ten power to represent as Decimal"
         else forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
e

-- | Reduce the exponent of the decimal number to the minimal possible value
normalizeDecimal :: (Integral i) => DecimalRaw i -> DecimalRaw i
normalizeDecimal :: forall i. Integral i => DecimalRaw i -> DecimalRaw i
normalizeDecimal DecimalRaw i
r = case forall i. Integral i => Rational -> Either String (DecimalRaw i)
eitherFromRational forall a b. (a -> b) -> a -> b
$ forall a. Real a => a -> Rational
toRational DecimalRaw i
r of
  Right DecimalRaw i
x -> DecimalRaw i
x
  Left String
e -> forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"Impossible happened: " forall a. [a] -> [a] -> [a]
++ String
e