packages feed

decimal-arithmetic 0.4.0.0 → 0.5.0.0

raw patch · 21 files changed

+3316/−2254 lines, 21 filesdep +binarydep +binary-bitsdep +deepseqdep ~basedep ~mtlPVP ok

version bump matches the API change (PVP)

Dependencies added: binary, binary-bits, deepseq, hspec

Dependency ranges changed: base, mtl

API changes (from Hackage documentation)

- Numeric.Decimal: precision :: Precision p => p -> Maybe Int
- Numeric.Decimal.Operation: QNaNClass :: NaNClass
- Numeric.Decimal.Operation: SNaNClass :: NaNClass
- Numeric.Decimal.Operation: class_ :: Precision a => Decimal a b -> Arith p r Class
+ Numeric.Decimal: fromOrdering :: Ordering -> Decimal p r
+ Numeric.Decimal: type Decimal128 = ExtendedDecimal Pdecimal128
+ Numeric.Decimal: type Decimal32 = ExtendedDecimal Pdecimal32
+ Numeric.Decimal: type Decimal64 = ExtendedDecimal Pdecimal64
+ Numeric.Decimal: type Pdecimal128 = Format K128 DecimalCoefficient
+ Numeric.Decimal: type Pdecimal32 = Format K32 DecimalCoefficient
+ Numeric.Decimal: type Pdecimal64 = Format K64 DecimalCoefficient
+ Numeric.Decimal.Encoding: class Parameters k
+ Numeric.Decimal.Encoding: data BinaryCoefficient
+ Numeric.Decimal.Encoding: data DecimalCoefficient
+ Numeric.Decimal.Encoding: data Format k c
+ Numeric.Decimal.Encoding: data K32
+ Numeric.Decimal.Encoding: data KPlus32 k
+ Numeric.Decimal.Encoding: data KTimes2 k
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.CoefficientEncoding Numeric.Decimal.Encoding.BinaryCoefficient
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.CoefficientEncoding Numeric.Decimal.Encoding.DecimalCoefficient
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.Parameters Numeric.Decimal.Encoding.K32
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.Parameters k => Data.Binary.Class.Binary (Numeric.Decimal.Number.Decimal (Numeric.Decimal.Encoding.Format k Numeric.Decimal.Encoding.DecimalCoefficient) r)
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.Parameters k => Numeric.Decimal.Encoding.Parameters (Numeric.Decimal.Encoding.KPlus32 k)
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.Parameters k => Numeric.Decimal.Encoding.Parameters (Numeric.Decimal.Encoding.KTimes2 k)
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.Parameters k => Numeric.Decimal.Precision.FinitePrecision (Numeric.Decimal.Encoding.Format k c)
+ Numeric.Decimal.Encoding: instance Numeric.Decimal.Encoding.Parameters k => Numeric.Decimal.Precision.Precision (Numeric.Decimal.Encoding.Format k c)
+ Numeric.Decimal.Encoding: type Decimal128 = ExtendedDecimal Pdecimal128
+ Numeric.Decimal.Encoding: type Decimal32 = ExtendedDecimal Pdecimal32
+ Numeric.Decimal.Encoding: type Decimal64 = ExtendedDecimal Pdecimal64
+ Numeric.Decimal.Encoding: type K128 = KTimes2 K64
+ Numeric.Decimal.Encoding: type K64 = KPlus32 K32
+ Numeric.Decimal.Encoding: type Pdecimal128 = Format K128 DecimalCoefficient
+ Numeric.Decimal.Encoding: type Pdecimal32 = Format K32 DecimalCoefficient
+ Numeric.Decimal.Encoding: type Pdecimal64 = Format K64 DecimalCoefficient
+ Numeric.Decimal.Operation: QuietClass :: NaNClass
+ Numeric.Decimal.Operation: SignalingClass :: NaNClass
+ Numeric.Decimal.Operation: class' :: Precision a => Decimal a b -> Arith p r Class
+ Numeric.Decimal.Operation: compareTotal :: Decimal a b -> Decimal c d -> Arith p r Ordering
+ Numeric.Decimal.Operation: compareTotalMagnitude :: Decimal a b -> Decimal c d -> Arith p r Ordering
+ Numeric.Decimal.Operation: instance GHC.Enum.Enum Numeric.Decimal.Operation.NaNClass
+ Numeric.Decimal.Operation: roundToIntegralExact :: (Precision a, Rounding r) => Decimal a b -> Arith p r (Decimal a r)
+ Numeric.Decimal.Operation: roundToIntegralValue :: (Precision a, Rounding r) => Decimal a b -> Arith p r (Decimal a r)
+ Numeric.Decimal.Operation: scaleb :: Decimal a b -> Decimal c d -> Arith p r (Decimal a b)
- Numeric.Decimal: class Precision p
+ Numeric.Decimal: class Precision p where eMax n = subtract 1 . (10 ^) . numDigits <$> base where mlength = precision n :: Maybe Int base = (10 *) . fromIntegral <$> mlength :: Maybe Coefficient eMin = fmap (1 -) . eMax
- Numeric.Decimal.Operation: compare :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: compare :: Decimal a b -> Decimal c d -> Arith p r (Either (Decimal p r) Ordering)
- Numeric.Decimal.Operation: compareSignal :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: compareSignal :: Decimal a b -> Decimal c d -> Arith p r (Either (Decimal p r) Ordering)
- Numeric.Decimal.Operation: max :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: max :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
- Numeric.Decimal.Operation: maxMagnitude :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: maxMagnitude :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
- Numeric.Decimal.Operation: min :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: min :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
- Numeric.Decimal.Operation: minMagnitude :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: minMagnitude :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
- Numeric.Decimal.Operation: quantize :: (Precision p, Rounding r) => Decimal p r -> Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: quantize :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2016, Robert Leslie+Copyright (c) 2016-2017, Robert Leslie  All rights reserved. 
TODO view
@@ -1,5 +1,4 @@ -*- Outline -*-  * To Do-** instance Floating (Decimal p r) ** instance PrintfArg (Decimal p r)
decimal-arithmetic.cabal view
@@ -1,6 +1,6 @@  name:                decimal-arithmetic-version:             0.4.0.0+version:             0.5.0.0  synopsis:            An implementation of the General Decimal Arithmetic                      Specification@@ -16,7 +16,7 @@ license:             BSD3 license-file:        LICENSE -copyright:           © 2016 Robert Leslie+copyright:           © 2016–2017 Robert Leslie author:              Rob Leslie <rob@mars.org> maintainer:          Rob Leslie <rob@mars.org> @@ -38,20 +38,41 @@   hs-source-dirs:      src    exposed-modules:     Numeric.Decimal-                       Numeric.Decimal.Conversion                        Numeric.Decimal.Arithmetic+                       Numeric.Decimal.Conversion+                       Numeric.Decimal.Encoding                        Numeric.Decimal.Operation-  other-modules:       Numeric.Decimal.Number+  other-modules:       Numeric.Decimal.Exception+                       Numeric.Decimal.Number                        Numeric.Decimal.Precision                        Numeric.Decimal.Rounding -  build-depends:       base >= 4.7 && < 5-                     , mtl+  build-depends:       base >= 4.8 && < 5+                     , binary >= 0.8 && < 0.9+                     , binary-bits >= 0.5 && < 0.6+                     , deepseq >= 1.4 && < 1.5+                     , mtl >= 2.2 && < 2.3   default-language:    Haskell2010   default-extensions:  Trustworthy   other-extensions:    FlexibleInstances                        MultiParamTypeClasses                        RoleAnnotations++test-suite spec+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:       Arbitrary+                       Numeric.Decimal.EncodingSpec+                       Numeric.Decimal.NumberSpec+                       Numeric.Decimal.OperationSpec+  build-depends:       base+                     , binary+                     , decimal-arithmetic+                     , hspec+                     , QuickCheck+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010  test-suite doctests   type:                exitcode-stdio-1.0
src/Numeric/Decimal.hs view
@@ -1,7 +1,7 @@ {-| Module      : Numeric.Decimal Description : General arbitrary-precision decimal floating-point number type-Copyright   : © 2016 Robert Leslie+Copyright   : © 2016–2017 Robert Leslie License     : BSD3 Maintainer  : rob@mars.org Stability   : experimental@@ -38,20 +38,49 @@ representation via 'Show' and 'Read' instances. Note that there may be multiple representations of values that are numerically equal (e.g. 1 and 1.00) which are preserved by this conversion.++Some decimal numbers also support encoding and decoding specific IEEE 754+interchange formats via a 'Data.Binary.Binary' instance. -} module Numeric.Decimal        ( -- * Usage          -- $usage +         -- ** Advanced usage+         -- $advanced-usage+          -- * Arbitrary-precision decimal numbers          Decimal        , BasicDecimal        , ExtendedDecimal        , GeneralDecimal +         -- ** Number types with defined encodings+         -- $encodings+       , Decimal32+       , Decimal64+       , Decimal128+          -- ** Precision types-       , module Numeric.Decimal.Precision+       , Precision+       , FinitePrecision +       , P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10+       , P11, P12, P13, P14, P15, P16, P17, P18, P19, P20+       , P21, P22, P23, P24, P25, P26, P27, P28, P29, P30+       , P31, P32, P33, P34, P35, P36, P37, P38, P39, P40+       , P41, P42, P43, P44, P45, P46, P47, P48, P49, P50++       , P75, P100, P150, P200, P250, P300, P400, P500, P1000, P2000++       , PPlus1, PTimes2++       , PInfinite++       , Pdecimal32+       , Pdecimal64+       , Pdecimal128+          -- ** Rounding types        , Rounding @@ -67,23 +96,14 @@          -- * Functions        , cast        , fromBool+       , fromOrdering        ) where +import Numeric.Decimal.Encoding import Numeric.Decimal.Number import Numeric.Decimal.Precision import Numeric.Decimal.Rounding --- | A decimal floating point number with 9 digits of precision, rounding half--- up-type BasicDecimal = Decimal P9 RoundHalfUp---- | A decimal floating point number with selectable precision, rounding half--- even-type ExtendedDecimal p = Decimal p RoundHalfEven---- | A decimal floating point number with infinite precision-type GeneralDecimal = ExtendedDecimal PInfinite- {- $usage  You should choose a decimal number type with appropriate precision and@@ -93,35 +113,52 @@ rounds half up.  * 'ExtendedDecimal' is a number type constructor with selectable precision-that rounds half even. For example, @'ExtendedDecimal' 'P34'@ is a number type-with 34 decimal digits of precision. There is a range of ready-made precisions-available, including 'P1' through 'P50' on up to 'P2000' (the IEEE 754-smallest and basic formats correspond to precisions 'P7', 'P16', or 'P34').-Alternatively, an arbitrary precision can be constructed through type-application of 'PPlus1' and/or 'PTimes2' to any existing precision.+that rounds half even. For example, @'ExtendedDecimal' 'P15'@ is a number type+with 15 decimal digits of precision. There is a range of ready-made precisions+available, including 'P1' through 'P50' on up to 'P2000'.  * 'GeneralDecimal' is a number type with infinite precision. Note that not all operations support numbers with infinite precision. +* 'Decimal32', 'Decimal64', and 'Decimal128' are specialized number types with+'Data.Binary.Binary' instances that implement the /decimal32/, /decimal64/,+and /decimal128/ interchange format encodings described in IEEE 754-2008.+These types have precisions of 7, 16, and 34 decimal digits, respectively, and+round half even.+ * The most versatile 'Decimal' type constructor is parameterized by both a precision and a rounding algorithm. For example, @'Decimal' 'P20' 'RoundDown'@ is a number type with 20 decimal digits of precision that rounds down (truncates). Several 'Rounding' algorithms are available to choose from. -It is suggested to create an alias for the type of numbers you wish to support-in your application. For example:--> type Number = ExtendedDecimal P16--A decimal number type may be used in a @default@ declaration, possibly-replacing 'Double' and/or 'Integer'. For example:+A decimal number type may be used in a @default@ declaration, for example+replacing 'Double':  > default (Integer, BasicDecimal)+-} -== Advanced usage+{- $advanced-usage -Additional operations and control beyond what is provided by the basic numeric-type classes are available through the use of "Numeric.Decimal.Arithmetic" and+Additional operations and control beyond what is provided by the standard type+classes are available through the use of "Numeric.Decimal.Arithmetic" and "Numeric.Decimal.Operation". Advanced string conversion is also available through "Numeric.Decimal.Conversion".++Arbitrary precisions can be constructed through type application of 'PPlus1'+and/or 'PTimes2' to any existing precision.++It is possible to create arbitrary width interchange format encodings with the+help of "Numeric.Decimal.Encoding".+-}++{- $encodings++These decimal number types have a 'Data.Binary.Binary' instance that+implements a specific interchange format encoding described in IEEE+754-2008. See "Numeric.Decimal.Encoding" for further details, including the+ability to create additional formats of arbitrary width.++Alternative rounding algorithms can be used through the more general 'Decimal'+type constructor and the special precision types 'Pdecimal32', 'Pdecimal64',+or 'Pdecimal128', e.g. @'Decimal' 'Pdecimal64' 'RoundCeiling'@. -}
src/Numeric/Decimal/Arithmetic.hs view
@@ -71,8 +71,8 @@ -- arithmetic computation and manipulate its 'Context'.  -- | A context for decimal arithmetic, carrying signal flags, trap enabler--- state, and a trap handler, parameterized by precision @p@ and rounding--- algorithm @r@+-- state, and a trap handler, parameterized by precision @p@ and rounding mode+-- @r@ data Context p r =   Context { flags        :: Signals                             -- ^ The current signal flags of the context@@ -105,9 +105,8 @@         disabled = [Inexact, Rounded, Subnormal]  -- | Return a new context with all signal flags cleared, all traps disabled--- (IEEE 854 §7), using selectable precision (the IEEE 754 smallest and basic--- formats correspond to precisions 'P7', 'P16', or 'P34'), and rounding half--- even (IEEE 754 §4.3.3).+-- (IEEE 854 §7), using selectable precision, and rounding half even (IEEE 754+-- §4.3.3). extendedDefaultContext :: Context p RoundHalfEven extendedDefaultContext = newContext @@ -123,7 +122,7 @@   deriving Show  -- | A decimal arithmetic monad parameterized by the precision @p@ and--- rounding algorithm @r@+-- rounding mode @r@ newtype Arith p r a = Arith (ExceptT (Exception p r)                              (State (Context p r)) a) @@ -164,8 +163,8 @@ evalArith (Arith e) = evalState (runExceptT e)  -- | Perform a subcomputation using a different precision and/or rounding--- algorithm. The subcomputation is evaluated within a new context with all--- flags cleared and all traps disabled. Any flags set in the context of the+-- mode. The subcomputation is evaluated within a new context with all flags+-- cleared and all traps disabled. Any flags set in the context of the -- subcomputation are ignored, but if an exception is returned it will be -- re-raised within the current context. subArith :: Arith a b (Decimal a b) -> Arith p r (Decimal a b)@@ -181,7 +180,7 @@   where getPrecision' :: Precision p => p -> Arith p r (Maybe Int)         getPrecision' = return . precision --- | Return the rounding algorithm of the arithmetic context.+-- | Return the rounding mode of the arithmetic context. getRounding :: Rounding r => Arith p r RoundingAlgorithm getRounding = getRounding' undefined   where getRounding' :: Rounding r => r -> Arith p r RoundingAlgorithm
src/Numeric/Decimal/Conversion.hs view
@@ -244,15 +244,18 @@               showString (replicate (fromIntegral $ -e - cl) '0') .               showString cs -  Inf  {             } -> showString "Infinity"-  QNaN { payload = p } -> showString  "NaN" . diag p-  SNaN { payload = p } -> showString "sNaN" . diag p+  Inf {                            } -> showString "Infinity"+  NaN { signaling = s, payload = p } -> sig s . showString  "NaN" . diag p    where signStr :: ShowS         signStr = case sign num of           Pos -> id           Neg -> showChar '-' +        sig :: Bool -> ShowS+        sig False = id+        sig True  = showChar 's'+         diag :: Payload -> ShowS         diag 0 = id         diag d = shows d@@ -295,34 +298,26 @@                         , exponent    = e - fromIntegral (length fracDigits)                         } -                digitsWithOptionalPoint = fractionalDigits <|> wholeDigits--                fractionalDigits = do-                  char '.'-                  fracDigits <- many1 parseDigit-                  return $ \e ->-                    Num { sign        = Pos-                        , coefficient = readDigits fracDigits-                        , exponent    = e - fromIntegral (length fracDigits)-                        }--                wholeDigits = do+                digitsWithOptionalPoint = do+                  fractional <- option False (char '.' *> pure True)                   digits <- many1 parseDigit+                  let offset | fractional = fromIntegral (length digits)+                             | otherwise  = 0                   return $ \e -> Num { sign        = Pos                                      , coefficient = readDigits digits-                                     , exponent    = e+                                     , exponent    = e - offset                                      }          parseExponentPart :: ReadP Exponent         parseExponentPart = do           parseString "E"-          parseSign negate <*> (readDigits <$> many1 parseDigit)+          parseSign negate <*> (fromIntegral . readDigits <$> many1 parseDigit)          parseInfinity :: ReadP (Decimal p r)         parseInfinity = do           parseString "Inf"           optional $ parseString "inity"-          return Inf { sign = Pos }+          return infinity          parseNaN :: ReadP (Decimal p r)         parseNaN = parseQNaN <|> parseSNaN@@ -330,13 +325,13 @@         parseQNaN :: ReadP (Decimal p r)         parseQNaN = do           p <- parseNaNPayload-          return QNaN { sign = Pos, payload = p }+          return qNaN { payload = p }          parseSNaN :: ReadP (Decimal p r)         parseSNaN = do           parseString "s"           p <- parseNaNPayload-          return SNaN { sign = Pos, payload = p }+          return sNaN { payload = p }          parseNaNPayload :: ReadP Payload         parseNaNPayload = do@@ -349,7 +344,7 @@         parseString :: String -> ReadP ()         parseString = mapM_ $ \c -> char (toLower c) <|> char (toUpper c) -        readDigits :: Num c => [Int] -> c+        readDigits :: [Int] -> Coefficient         readDigits = foldl' (\a b -> a * 10 + fromIntegral b) 0  {- $doctest-toNumber
+ src/Numeric/Decimal/Encoding.hs view
@@ -0,0 +1,330 @@++{-# LANGUAGE FlexibleInstances #-}++-- | This module implements the decimal interchange format encodings described+-- in IEEE 754-2008, including the /decimal32/, /decimal64/, and /decimal128/+-- formats, as well as arbitrary width /decimal{k}/ formats through the use of+-- 'Format' with 'KPlus32' and\/or 'KTimes2'. For example, to use a+-- /decimal96/ format:+--+-- > type Decimal96 = ExtendedDecimal (Format (KPlus32 K64) DecimalCoefficient)+--+-- Currently only a decimal encoding of coefficients is implemented, but a+-- binary encoding may be added in the future.+module Numeric.Decimal.Encoding (+    -- * Primary convenience types+    Decimal32+  , Decimal64+  , Decimal128++    -- ** Precision types+  , Pdecimal32+  , Pdecimal64+  , Pdecimal128++    -- * Interchange format types+  , Format++    -- ** Format parameters+  , Parameters+  , K32+  , K64+  , K128+  , KPlus32+  , KTimes2++    -- ** Coefficient encodings+  -- , CoefficientEncoding+  , DecimalCoefficient+  , BinaryCoefficient+  ) where++import Prelude hiding (exponent)++import Data.Binary (Binary(get, put), Get)+import Data.Binary.Bits.Get (BitGet, getBool, getWord8, getWord16be, runBitGet)+import Data.Binary.Bits.Put (BitPut, putBool, putWord8, putWord16be, runBitPut)+import Data.Bits (bit, shiftL, shiftR, testBit, (.&.), (.|.))+import Data.Word (Word8, Word16)++import Numeric.Decimal.Number+import Numeric.Decimal.Precision++-- Decimal number types++-- | A decimal floating point number with 7 digits of precision, rounding half+-- even, and a 32-bit encoded representation using the /decimal32/ interchange+-- format (with a decimal encoding for the coefficient)+type Decimal32 = ExtendedDecimal Pdecimal32++-- | A decimal floating point number with 16 digits of precision, rounding+-- half even, and a 64-bit encoded representation using the /decimal64/+-- interchange format (with a decimal encoding for the coefficient)+type Decimal64 = ExtendedDecimal Pdecimal64++-- | A decimal floating point number with 34 digits of precision, rounding+-- half even, and a 128-bit encoded representation using the /decimal128/+-- interchange format (with a decimal encoding for the coefficient)+type Decimal128 = ExtendedDecimal Pdecimal128++-- Precision types++-- | A type with 'Precision' instance specifying /decimal32/ interchange+-- format parameters (using a decimal encoding for the coefficient) having an+-- effective precision of 7 decimal digits+type Pdecimal32 = Format K32 DecimalCoefficient++-- | A type with 'Precision' instance specifying /decimal64/ interchange+-- format parameters (using a decimal encoding for the coefficient) having an+-- effective precision of 16 decimal digits+type Pdecimal64 = Format K64 DecimalCoefficient++-- | A type with 'Precision' instance specifying /decimal128/ interchange+-- format parameters (using a decimal encoding for the coefficient) having an+-- effective precision of 34 decimal digits+type Pdecimal128 = Format K128 DecimalCoefficient++-- Format parameters++-- | Interchange format parameters used to define an encoding and derive the+-- format's /precision/ and E/max/+class Parameters k where+  -- | /k//32, the primary format parameter defining the encoding width as a+  -- multiple of 32 bits+  paramK32 :: k -> Int++-- | /p/, precision in digits+paramP :: Parameters k => k -> Int+paramP k = 9 * paramK32 k - 2++-- | /emax/+paramEmax :: Parameters k => k -> Exponent+paramEmax k = 3 * 2^(paramK32 k * 2 + 3)++-- | /bias/, /E/ − /q/+paramBias :: Parameters k => k -> Exponent+paramBias k = paramEmax k + fromIntegral (paramP k - 2)++-- | /w/, combination field width in bits − 5+paramW :: Parameters k => k -> Int+paramW k = paramK32 k * 2 + 4++-- | /t//10, trailing significand field width in 10-bit multiples+paramT10 :: Parameters k => k -> Int+paramT10 k = 3 * paramK32 k - 1++-- | Parameters for the /decimal32/ interchange format+data K32+instance Parameters K32 where+  paramK32 _ = 1++-- | Parameters for the /decimal64/ interchange format+type K64 = KPlus32 K32++-- | Parameters for the /decimal128/ interchange format+type K128 = KTimes2 K64++-- | Parameters for a /decimal{@k@ + 32}/ interchange format+data KPlus32 k+instance Parameters k => Parameters (KPlus32 k) where+  paramK32 t = paramK32 (minus32 t) + 1+    where minus32 :: KPlus32 k -> k+          minus32 = undefined++-- | Parameters for a /decimal{@k@ × 2}/ interchange format+data KTimes2 k+instance Parameters k => Parameters (KTimes2 k) where+  paramK32 t = paramK32 (div2 t) * 2+    where div2 :: KTimes2 k -> k+          div2 = undefined++-- | A class encapsulating coefficient encodings+class CoefficientEncoding c++-- | Specify a decimal encoding for the coefficient.+data DecimalCoefficient+instance CoefficientEncoding DecimalCoefficient++-- | Specify a binary encoding for the coefficient (currently unimplemented).+data BinaryCoefficient+instance CoefficientEncoding BinaryCoefficient++-- | A type (with a 'Precision' instance) for specifying interchange format+-- parameters @k@ and coefficient encoding @c@+data Format k c++formatK :: Format k c -> k+formatK = undefined++-- | This 'Precision' instance automatically computes the /precision/ and+-- E/max/ of decimal numbers that use this format.+instance Parameters k => Precision (Format k c) where+  precision = Just . paramP    . formatK+  eMax      = Just . paramEmax . formatK++instance Parameters k => FinitePrecision (Format k c)++-- | A 'Binary' instance is defined for interchange formats for which a+-- 'Parameters' instance exists, and covers particularly the 'Decimal32',+-- 'Decimal64', and 'Decimal128' types.+instance Parameters k => Binary (Decimal (Format k DecimalCoefficient) r) where+  put d = runBitPut $ putDecimal (paramW k) (paramT10 k) (paramBias k) d+    where k = formatK (decimalFormat d)++          decimalFormat :: Decimal (Format k c) r -> Format k c+          decimalFormat = undefined++  get = result+    where result = runBitGet $ getDecimal (paramW k) (paramT10 k) (paramBias k)+          k = formatK (getDecimalFormat result)++          getDecimalFormat :: Get (Decimal (Format k c) r) -> Format k c+          getDecimalFormat = undefined++-- Densely Packed Decimal++dpd2bcd :: Word16 -> (Word8, Word8, Word8)+dpd2bcd dpd = case mask 0 0xe of+  0xe -> case mask 4 0x6 of+    0x6 -> (    c7,     f4,     i0)+    0x4 -> (a9b8c7,     f4,     i0)+    0x2 -> (    c7, d9e8f4,     i0)+    _   -> (    c7,     f4, g9h8i0)+  0xc ->   (    c7, d6e5f4, g9h8i0)+  0xa ->   (a9b8c7,     f4, g6h5i0)+  0x8 ->   (a9b8c7, d6e5f4,     i0)+  _   ->   (a9b8c7, d6e5f4, g2h1i0)++  where a9b8c7 =              mask 7 7+        d6e5f4 =              mask 4 7+        d9e8f4 = mask 7 6 .|. mask 4 1+        g2h1i0 =              mask 0 7+        g6h5i0 = mask 4 6 .|. mask 0 1+        g9h8i0 = mask 7 6 .|. mask 0 1+        i0     = 8        .|. mask 0 1+        f4     = 8        .|. mask 4 1+        c7     = 8        .|. mask 7 1++        mask :: Int -> Word8 -> Word8+        mask s m = fromIntegral (shiftR dpd s) .&. m++bcd2dpd :: Word8 -> Word8 -> Word8 -> Word16+bcd2dpd d2 d1 d0 = case (d2 < 8, d1 < 8, d0 < 8) of+  (True , True , True ) ->      a9b8c7 .|.      d6e5f4          .|. g2h1i0+  (True , True , False) ->      a9b8c7 .|.      d6e5f4 .|. 0x08 .|.     i0+  (True , False, True ) ->      a9b8c7 .|. g6h5 .|. f4 .|. 0x0a .|.     i0+  (False, True , True ) -> g9h8 .|. c7 .|.      d6e5f4 .|. 0x0c .|.     i0+  (False, False, True ) -> g9h8 .|. c7 .|.          f4 .|. 0x0e .|.     i0+  (False, True , False) -> d9e8 .|. c7 .|.          f4 .|. 0x2e .|.     i0+  (True , False, False) ->      a9b8c7 .|.          f4 .|. 0x4e .|.     i0+  (False, False, False) ->          c7 .|.          f4 .|. 0x6e .|.     i0++  where a9b8c7 = isolate d2 7 7+        c7     = isolate d2 1 7+        d6e5f4 = isolate d1 7 4+        d9e8   = isolate d1 6 7+        f4     = isolate d1 1 4+        g2h1i0 = isolate d0 7 0+        g6h5   = isolate d0 6 4+        g9h8   = isolate d0 6 7+        i0     = isolate d0 1 0++        isolate :: Word8 -> Word8 -> Int -> Word16+        isolate d m = shiftL (fromIntegral $ d .&. m)++-- Low-level encoding/decoding++data CombinationField = Finite { exponentMSBs   :: Word8+                               , coefficientMSD :: Word8 }+                      | Infinity+                      | NotANumber++getCommon :: BitGet (Sign, CombinationField)+getCommon = do+  sign <- toEnum . fromEnum <$> getBool+  bits <- getWord8 5+  let cf = case bits of+        0x1e -> Infinity+        0x1f -> NotANumber+        _    -> let ab = shiftR bits 3 in case ab of+          0x03 -> Finite { exponentMSBs   = shiftR bits 1 .&. 0x03+                         , coefficientMSD = 0x08 .|. (bits .&. 0x01)+                         }+          _    -> Finite { exponentMSBs   = ab+                         , coefficientMSD = bits .&. 0x07+                         }+  return (sign, cf)++putCommon :: Sign -> CombinationField -> BitPut ()+putCommon sign cf = do+  putBool (toEnum . fromEnum $ sign)+  let bits = case cf of+        Finite { exponentMSBs = msbs, coefficientMSD = msd }+          | msd < 8   ->          shiftL msbs 3 .|.  msd+          | otherwise -> 0x18 .|. shiftL msbs 1 .|. (msd .&. 0x01)+        Infinity      -> 0x1e+        NotANumber    -> 0x1f+  putWord8 5 bits++getCoefficient :: CombinationField -> Int -> BitGet Coefficient+getCoefficient = getCoefficient' . getMSD++  where getCoefficient' :: Coefficient -> Int -> BitGet Coefficient+        getCoefficient' ic 0 = return ic+        getCoefficient' ic n = do+          (a, b, c) <- dpd2bcd <$> getWord16be 10+          let v = fromIntegral a * 100 + fromIntegral b * 10 + fromIntegral c+          getCoefficient' (ic * 1000 + v) (pred n)++        getMSD :: CombinationField -> Coefficient+        getMSD Finite { coefficientMSD = msd } = fromIntegral msd+        getMSD _                               = 0++getDecimal :: Int -> Int -> Exponent -> BitGet (Decimal p r)+getDecimal ecbits cclen bias = do+  (sign, cf) <- getCommon+  ec <- getWord16be ecbits+  coefficient <- getCoefficient cf cclen+  return $ case cf of+    Finite { exponentMSBs = msbs } ->+      let ee = shiftL (fromIntegral msbs) ecbits .|. fromIntegral ec+      in Num { sign = sign, coefficient = coefficient, exponent = ee - bias }+    Infinity -> Inf { sign = sign }+    NotANumber ->+      let s = testBit ec (ecbits - 1)+      in NaN { sign = sign, signaling = s, payload = coefficient }++putDecimal :: Int -> Int -> Exponent -> Decimal p r -> BitPut ()+putDecimal ecbits cclen bias x = do+  let msd : cc = digits x+      (cf, ee) = case x of+        Num { exponent = e } ->+          let cf = Finite { exponentMSBs   = fromIntegral (shiftR ee ecbits)+                          , coefficientMSD = msd+                          }+          in (cf, fromIntegral $ e + bias)+        Inf{} -> (Infinity, 0)+        NaN { signaling = s } -> (NotANumber, if s then bit (ecbits - 1) else 0)+  putCommon (sign x) cf+  putWord16be ecbits (ee .&. (bit ecbits - 1))+  putDigits cc++  where digits :: Decimal p r -> [Word8]+        digits x = let ds = case x of+                         Num { coefficient = c } -> digits' c+                         NaN { payload     = p } -> digits' p+                         Inf {                 } -> []+                   in replicate (1 + cclen * 3 - length ds) 0 ++ ds++        digits' :: Coefficient -> [Word8]+        digits' = go []+          where go ds 0 = ds+                go ds c = let (q, r) = c `quotRem` 10+                          in go (fromIntegral r : ds) q++        putDigits :: [Word8] -> BitPut ()+        putDigits (a : b : c : rest) = do+          putWord16be 10 (bcd2dpd a b c)+          putDigits rest+        putDigits [] = return ()+        putDigits _ = error "putDigits: invalid # digits"
+ src/Numeric/Decimal/Exception.hs view
@@ -0,0 +1,242 @@++module Numeric.Decimal.Exception (+  -- * Exceptional conditions+    clamped+  , conversionSyntax+  , divisionByZero+  , divisionImpossible+  , divisionUndefined+  , inexact+  , insufficientStorage+  , invalidContext+  , invalidOperation+  , overflow+  , rounded+  , subnormal+  , underflow+  ) where++import Prelude hiding (exponent)++import Numeric.Decimal.Arithmetic+import Numeric.Decimal.Number+import Numeric.Decimal.Precision+import Numeric.Decimal.Rounding++-- | This occurs and signals 'Clamped' if the exponent of a result has been+-- altered in order to fit the constraints of a specific concrete+-- representation. This may occur when the exponent of a zero result would be+-- outside the bounds of a representation, or (in the IEEE 754 interchange+-- formats) when a large normal number would have an encoded exponent that+-- cannot be represented. In this latter case, the exponent is reduced to fit+-- and the corresponding number of zero digits are appended to the coefficient+-- (“fold-down”). The condition always occurs when a subnormal value rounds to+-- zero.+clamped :: Decimal p r -> Arith p r (Decimal p r)+clamped = raiseSignal Clamped++-- | This occurs and signals 'InvalidOperation' if a string is being converted+-- to a number and it does not conform to the numeric string syntax. The+-- result is @[0,qNaN]@.+conversionSyntax :: Arith p r (Decimal p r)+conversionSyntax = raiseSignal InvalidOperation qNaN++-- | This occurs and signals 'DivisionByZero' if division of a finite number+-- by zero was attempted (during a 'Numeric.Decimal.Operation.divideInteger'+-- or 'Numeric.Decimal.Operation.divide' operation, or a+-- 'Numeric.Decimal.Operation.power' operation with negative right-hand+-- operand), and the dividend was not zero.+--+-- The result of the operation is @[@/sign/@,inf]@, where /sign/ is the+-- exclusive or of the signs of the operands for divide, or is 1 for an odd+-- power of −0, for power.+divisionByZero :: Decimal p r -> Arith p r (Decimal p r)+divisionByZero = raiseSignal DivisionByZero++-- | This occurs and signals 'InvalidOperation' if the integer result of a+-- 'Numeric.Decimal.Operation.divideInteger' or+-- 'Numeric.Decimal.Operation.remainder' operation had too many digits (would+-- be longer than /precision/). The result is @[0,qNaN]@.+divisionImpossible :: Arith p r (Decimal p r)+divisionImpossible = raiseSignal InvalidOperation qNaN++-- | This occurs and signals 'InvalidOperation' if division by zero was+-- attempted (during a 'Numeric.Decimal.Operation.divideInteger',+-- 'Numeric.Decimal.Operation.divide', or+-- 'Numeric.Decimal.Operation.remainder' operation), and the dividend is also+-- zero. The result is @[0,qNaN]@.+divisionUndefined :: Arith p r (Decimal p r)+divisionUndefined = raiseSignal InvalidOperation qNaN++-- | This occurs and signals 'Inexact' whenever the result of an operation is+-- not exact (that is, it needed to be rounded and any discarded digits were+-- non-zero), or if an overflow or underflow condition occurs. The result in+-- all cases is unchanged.+--+-- The 'Inexact' signal may be tested (or trapped) to determine if a given+-- operation (or sequence of operations) was inexact.+inexact :: Decimal p r -> Arith p r (Decimal p r)+inexact = raiseSignal Inexact++-- | For many implementations, storage is needed for calculations and+-- intermediate results, and on occasion an arithmetic operation may fail due+-- to lack of storage. This is considered an operating environment error,+-- which can be either be handled as appropriate for the environment, or+-- treated as an Invalid operation condition. The result is @[0,qNaN]@.+insufficientStorage :: Arith p r (Decimal p r)+insufficientStorage = invalidOperation qNaN++-- | This occurs and signals 'InvalidOperation' if an invalid context was+-- detected during an operation. This can occur if contexts are not checked on+-- creation and either the /precision/ exceeds the capability of the+-- underlying concrete representation or an unknown or unsupported /rounding/+-- was specified. These aspects of the context need only be checked when the+-- values are required to be used. The result is @[0,qNaN]@.+invalidContext :: Arith p r (Decimal p r)+invalidContext = raiseSignal InvalidOperation qNaN++-- | This occurs and signals 'InvalidOperation' if:+--+-- * an operand to an operation is @[s,sNaN]@ or @[s,sNaN,d]@ (any /signaling/+-- NaN)+--+-- * an attempt is made to add @[0,inf]@ to @[1,inf]@ during an addition or+-- subtraction operation+--+-- * an attempt is made to multiply 0 by @[0,inf]@ or @[1,inf]@+--+-- * an attempt is made to divide either @[0,inf]@ or @[1,inf]@ by either+-- @[0,inf]@ or @[1,inf]@+--+-- * the divisor for a remainder operation is zero+--+-- * the dividend for a remainder operation is either @[0,inf]@ or @[1,inf]@+--+-- * either operand of the 'Numeric.Decimal.Operation.quantize' operation is+-- infinite, or the result of a 'Numeric.Decimal.Operation.quantize' operation+-- would require greater precision than is available+--+-- * the operand of the 'Numeric.Decimal.Operation.ln' or the+-- 'Numeric.Decimal.Operation.log10' operation is less than zero+--+-- * the operand of the 'Numeric.Decimal.Operation.squareRoot' operation has a+-- /sign/ of 1 and a non-zero /coefficient/+--+-- * both operands of the 'Numeric.Decimal.Operation.power' operation are+-- zero, or if the left-hand operand is less than zero and the right-hand+-- operand does not have an integral value or is infinite+--+-- * an operand is invalid; for example, certain values of concrete+-- representations may not correspond to numbers — an implementation is+-- permitted (but is not required) to detect these invalid values and raise+-- this condition.+--+-- The result of the operation after any of these invalid operations is+-- @[0,qNaN]@ except when the cause is a signaling NaN, in which case the+-- result is @[s,qNaN]@ or @[s,qNaN,d]@ where the sign and diagnostic are+-- copied from the signaling NaN.+invalidOperation :: Decimal a b -> Arith p r (Decimal p r)+invalidOperation n = raiseSignal InvalidOperation $ case n of+  NaN { signaling = True } -> n { signaling = False }+  _                        -> qNaN++-- | This occurs and signals 'Overflow' if the /adjusted exponent/ of a result+-- (from a conversion or from an operation that is not an attempt to divide by+-- zero), after rounding, would be greater than the largest value that can be+-- handled by the implementation (the value E/max/).+--+-- The result depends on the rounding mode:+--+-- * For 'RoundHalfUp' and 'RoundHalfEven' (and for 'RoundHalfDown' and+-- 'RoundUp', if implemented), the result of the operation is [sign,@inf@],+-- where /sign/ is the sign of the intermediate result.+--+-- * For 'RoundDown', (and 'Round05Up', if implemented), the result is the+-- largest finite number that can be represented in the current /precision/,+-- with the sign of the intermediate result.+--+-- * For 'RoundCeiling', the result is the same as for 'RoundDown' if the sign+-- of the intermediate result is 1, or is @[0,inf]@ otherwise.+--+-- * For 'RoundFloor', the result is the same as for 'RoundDown' if the sign+-- of the intermediate result is 0, or is @[1,inf]@ otherwise.+--+-- In all cases, 'inexact' and 'rounded' will also be raised.+--+-- Note: IEEE 854 §7.3 requires that the result delivered to a trap handler be+-- different, depending on whether the overflow was the result of a conversion+-- or of an arithmetic operation. This specification deviates from IEEE 854 in+-- this respect; however, an implementation could comply with IEEE 854 by+-- providing a separate mechanism for the special result to a trap+-- handler. IEEE 754 has no such requirement.+overflow :: (Precision p, Rounding r) => Decimal p r -> Arith p r (Decimal p r)+overflow ir = result >>= raiseSignal Overflow >>= inexact >>= rounded++  where result :: (Precision p, Rounding r) => Arith p r (Decimal p r)+        result = getRounding >>= \r -> case r of+          RoundHalfUp   -> signedInfinity+          RoundHalfEven -> signedInfinity+          RoundHalfDown -> signedInfinity+          RoundUp       -> signedInfinity++          RoundDown     -> largestFinite+          Round05Up     -> largestFinite++          RoundCeiling  -> case sign ir of+            Neg -> largestFinite+            Pos -> return infinity++          RoundFloor    -> case sign ir of+            Pos -> largestFinite+            Neg -> return infinity { sign = Neg }++        signedInfinity :: Arith p r (Decimal p r)+        signedInfinity = return infinity { sign = sign ir }++        largestFinite :: Precision p => Arith p r (Decimal p r)+        largestFinite = getPrecision >>= \p ->+          let x = Num { sign = sign ir+                      , coefficient = undefined -- 10^p - 1+                      , exponent = undefined -- eMax x+                      }+          in return x++-- | This occurs and signals 'Rounded' whenever the result of an operation is+-- rounded (that is, some zero or non-zero digits were discarded from the+-- coefficient), or if an overflow or underflow condition occurs. The result+-- in all cases is unchanged.+--+-- The 'Rounded' signal may be tested (or trapped) to determine if a given+-- operation (or sequence of operations) caused a loss of precision.+rounded :: Decimal p r -> Arith p r (Decimal p r)+rounded = raiseSignal Rounded++-- | This occurs and signals 'Subnormal' whenever the result of a conversion+-- or operation is subnormal (that is, its adjusted exponent is less than+-- E/min/, before any rounding). The result in all cases is unchanged.+--+-- The 'Subnormal' signal may be tested (or trapped) to determine if a given+-- or operation (or sequence of operations) yielded a subnormal result.+subnormal :: Decimal p r -> Arith p r (Decimal p r)+subnormal = raiseSignal Subnormal++-- | This occurs and signals 'Underflow' if a result is inexact and the+-- /adjusted exponent/ of the result would be smaller (more negative) than the+-- smallest value that can be handled by the implementation (the value+-- E/min/). That is, the result is both inexact and subnormal.+--+-- The result after an underflow will be a subnormal number rounded, if+-- necessary, so that its exponent is not less than E/tiny/. This may result+-- in 0 with the sign of the intermediate result and an exponent of E/tiny/.+--+-- In all cases, 'inexact', 'rounded', and 'subnormal' will also be raised.+--+-- Note: IEEE 854 §7.4 requires that the result delivered to a trap handler be+-- different, depending on whether the underflow was the result of a+-- conversion or of an arithmetic operation. This specification deviates from+-- IEEE 854 in this respect; however, an implementation could comply with IEEE+-- 854 by providing a separate mechanism for the result to a trap+-- handler. IEEE 754 has no such requirement.+underflow :: Decimal p r -> Arith p r (Decimal p r)+underflow x = undefined >>= raiseSignal Underflow >>=+  subnormal >>= inexact >>= rounded
+ src/Numeric/Decimal/Exception.hs-boot view
@@ -0,0 +1,11 @@++module Numeric.Decimal.Exception (+    inexact+  , rounded+  ) where++import {-# SOURCE #-} Numeric.Decimal.Arithmetic+import {-# SOURCE #-} Numeric.Decimal.Number++inexact :: Decimal p r -> Arith p r (Decimal p r)+rounded :: Decimal p r -> Arith p r (Decimal p r)
src/Numeric/Decimal/Number.hs view
@@ -11,6 +11,10 @@        , Payload         , Decimal(..)+       , BasicDecimal+       , ExtendedDecimal+       , GeneralDecimal+        , zero        , oneHalf        , one@@ -23,7 +27,9 @@         , flipSign        , cast+        , fromBool+       , fromOrdering         , isPositive        , isNegative@@ -38,6 +44,7 @@  import Prelude hiding (exponent) +import Control.DeepSeq (NFData(..)) import Control.Monad (join) import Data.Bits (Bits(..), FiniteBits(..)) import Data.Char (isSpace)@@ -55,14 +62,13 @@  import qualified GHC.Real -{- $setup->>> :load Harness--}- data Sign = Pos  -- ^ Positive or non-negative           | Neg  -- ^ Negative           deriving (Eq, Enum) +instance NFData Sign where+  rnf s = s `seq` ()+ negateSign :: Sign -> Sign negateSign Pos = Neg negateSign Neg = Pos@@ -83,25 +89,34 @@   _  -> Pos  type Coefficient = Natural-type Exponent    = Int+type Exponent    = Integer type Payload     = Coefficient  -- | A decimal floating point number with selectable precision and rounding -- algorithm data Decimal p r-  = Num  { sign        :: Sign-         , coefficient :: Coefficient-         , exponent    :: Exponent-         }-  | Inf  { sign        :: Sign-         }-  | QNaN { sign        :: Sign-         , payload     :: Payload-         }-  | SNaN { sign        :: Sign-         , payload     :: Payload-         }+  = Num { sign        :: Sign+        , coefficient :: Coefficient+        , exponent    :: Exponent+        }+  | Inf { sign        :: Sign+        }+  | NaN { sign        :: Sign+        , signaling   :: Bool+        , payload     :: Payload+        } +-- | A decimal floating point number with 9 digits of precision, rounding half+-- up+type BasicDecimal = Decimal P9 RoundHalfUp++-- | A decimal floating point number with selectable precision, rounding half+-- even+type ExtendedDecimal p = Decimal p RoundHalfEven++-- | A decimal floating point number with infinite precision+type GeneralDecimal = ExtendedDecimal PInfinite+ -- | The 'Show' instance uses the 'toScientificString' operation from -- "Numeric.Decimal.Conversion". instance Show (Decimal p r) where@@ -115,67 +130,61 @@                     | (n, s) <- readParen False                       (readP_to_S toNumber . dropWhile isSpace) str ] -{- $doctest-Read->>> fmap toRep (read "Just 123" :: Maybe GeneralDecimal)-Just (N (0,123,0))-->>> fmap toRep (read "Just (-12.0)" :: Maybe GeneralDecimal)-Just (N (1,120,-1))--}+decimalPrecision :: Decimal p r -> p+decimalPrecision = undefined  instance Precision p => Precision (Decimal p r) where   precision = precision . decimalPrecision-    where decimalPrecision :: Decimal p r -> p-          decimalPrecision = undefined+  eMax      = eMax      . decimalPrecision+  eMin      = eMin      . decimalPrecision -evalOp :: Arith p r (Decimal p r) -> Decimal p r-evalOp op = either exceptionResult id $ evalArith op newContext+-- This assumes the arithmetic operation does not trap any signals, which+-- could result in an exception being thrown (and returned in a Left value).+evalOp :: Arith p r a -> a+evalOp op = let Right r = evalArith op newContext in r -type GeneralDecimal = Decimal PInfinite RoundHalfEven+evalOp' :: Arith p RoundHalfEven a -> a+evalOp' = evalOp +compareDecimal :: Decimal a b -> Decimal c d -> Either GeneralDecimal Ordering+compareDecimal x y = evalOp (x `Op.compare` y)++-- | Note that NaN values are not equal to any value, including other NaNs. instance Eq (Decimal p r) where-  x == y = case evalOp (x `Op.compare` y) :: GeneralDecimal of-    Num { coefficient = 0 } -> True-    _                       -> False+  x == y = case compareDecimal x y of+    Right EQ -> True+    _        -> False -instance (Precision p, Rounding r) => Ord (Decimal p r) where-  x `compare` y = case evalOp (x `Op.compare` y) :: GeneralDecimal of-    Num { coefficient = 0 } -> EQ-    Num { sign = Neg      } -> LT-    Num { sign = Pos      } -> GT-    _                       -> GT  -- match Prelude behavior for NaN+-- | Unlike the instances for 'Float' and 'Double', the 'compare' method in+-- this instance uses a total ordering over all possible values. Note that+-- @'compare' x y == 'EQ'@ does not imply @x == y@ (and similarly for 'LT' and+-- 'GT') in the cases where @x@ or @y@ are NaN values.+instance Ord (Decimal p r) where+  compare x y = case compareDecimal x y of+    Right o -> o+    Left _  -> evalOp (x `Op.compareTotal` y) -  x < y = case evalOp (x `Op.compare` y) :: GeneralDecimal of-    Num { sign = Neg      } -> True-    _                       -> False+  x < y = case compareDecimal x y of+    Right LT -> True+    _        -> False -  x <= y = case evalOp (x `Op.compare` y) :: GeneralDecimal of-    Num { sign = Neg      } -> True-    Num { coefficient = 0 } -> True-    _                       -> False+  x <= y = case compareDecimal x y of+    Right LT -> True+    Right EQ -> True+    _        -> False -  x > y = case evalOp (x `Op.compare` y) :: GeneralDecimal of-    Num { coefficient = 0 } -> False-    Num { sign = Pos      } -> True-    _                       -> False+  x > y = case compareDecimal x y of+    Right GT -> True+    _        -> False -  x >= y = case evalOp (x `Op.compare` y) :: GeneralDecimal of-    Num { sign = Pos      } -> True-    _                       -> False+  x >= y = case compareDecimal x y of+    Right GT -> True+    Right EQ -> True+    _        -> False    max x y = evalOp (Op.max x y)   min x y = evalOp (Op.min x y) -{- $doctest-Ord-prop> x > y ==> max x y == x && max y x == (x :: BasicDecimal)-prop> x < y ==> min x y == x && min y x == (x :: BasicDecimal)--prop> max x y == x ==> x >= y-prop> max x y == y ==> y >= x-prop> min x y == x ==> x <= y-prop> min x y == y ==> y <= x--}- -- | Unlike the instances for 'Float' and 'Double', the lists returned by the -- 'enumFromTo' and 'enumFromThenTo' methods in this instance terminate with -- the last element strictly less than (greater than in the case of a negative@@ -204,17 +213,6 @@              => Decimal p r -> Decimal p r -> [Decimal p r] enumFromWith x i = x : enumFromWith (x + i) i -{- $doctest-Enum->>> [0, 0.1 .. 2] :: [BasicDecimal]-[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0]-->>> [2, 1.9 .. 0] :: [BasicDecimal]-[2,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1,1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0]-->>> [1.7 .. 5.7] :: [BasicDecimal]-[1.7,2.7,3.7,4.7,5.7]--}- instance (Precision p, Rounding r) => Num (Decimal p r) where   x + y = evalOp (x `Op.add`      y)   x - y = evalOp (x `Op.subtract` y)@@ -224,10 +222,11 @@   abs    = evalOp . Op.abs    signum n = case n of-    Num { coefficient = 0 } -> zero-    Num { sign = s        } -> one { sign = s }-    Inf { sign = s        } -> one { sign = s }-    _                       -> n+    Num { sign = s, coefficient = c }+      | c == 0       -> zero { sign = s }+      | otherwise    -> one  { sign = s }+    Inf { sign = s } -> one  { sign = s }+    _                -> qNaN    fromInteger x = cast     Num { sign        = signMatch x@@ -235,15 +234,6 @@         , exponent    = 0         } -{- $doctest-Num-prop> x + x == x * (2 :: GeneralDecimal)-prop> isFinite x ==> x - x == (0 :: GeneralDecimal)-prop> isFinite x ==> x + negate x == (0 :: GeneralDecimal)-prop> abs x >= (0 :: GeneralDecimal)--prop> abs x * signum x == (x :: GeneralDecimal)--}- instance (Precision p, Rounding r) => Real (Decimal p r) where   toRational Num { sign = s, coefficient = c, exponent = e }     | e >= 0    = fromInteger $ signFunc s (fromIntegral c * 10^e)@@ -258,16 +248,6 @@                        d = fromInteger (denominator r) :: GeneralDecimal                    in evalOp (n `Op.divide` d) -{- $doctest-Fractional-prop> (4.14 :: Decimal P2 RoundHalfUp)   == 4.1-prop> (4.15 :: Decimal P2 RoundHalfUp)   == 4.2-prop> (4.15 :: Decimal P2 RoundHalfDown) == 4.1-prop> (4.15 :: Decimal P2 RoundHalfEven) == 4.2-prop> (4.25 :: Decimal P2 RoundHalfEven) == 4.2-prop> (4.35 :: Decimal P2 RoundHalfEven) == 4.4-prop> (4.45 :: Decimal P2 RoundHalfEven) == 4.4--}- instance (FinitePrecision p, Rounding r) => RealFrac (Decimal p r) where   properFraction x@Num { sign = s, coefficient = c, exponent = e }     | e < 0     = (n, f)@@ -277,95 +257,254 @@           (q, r) = c `quotRem` (10^(-e))   properFraction nan = (0, nan) -{- $doctest-RealFrac-prop> let (n,f) = properFraction (x :: BasicDecimal) in x == fromIntegral n + f-prop> let (n,f) = properFraction (x :: BasicDecimal) in (x < 0 && n <= 0) || (x >= 0 && n >= 0)-prop> let (n,f) = properFraction (x :: BasicDecimal) in (x < 0 && f <= 0) || (x >= 0 && f >= 0)-prop> let (n,f) = properFraction (x :: BasicDecimal) in isFinite f ==> abs f < 1--}+-- | Compute a generalized continued fraction to maximum precision. A hint is+-- used to indicate the minimum number of terms that should be generated+-- before (expensively) examining the results for convergence.+continuedFraction :: FinitePrecision p+                  => Int -> Integer -> [(Integer, Integer)] -> ExtendedDecimal p+continuedFraction m b0 ((a1, b1) : ps) = convergent (max 0 $ m - 2) x0 x1 x1' ps+  where x0  = (aa0, bb0)+        x1  = (aa1, bb1)+        x1' = fromRational (aa1 % bb1)+        aa0 = b0+        bb0 = 1+        aa1 = b1 * b0 + a1+        bb1 = b1 +        convergent m (aa0, bb0) x1@(aa1, bb1) x1' ((a2, b2) : ps)+          | m == 0 && x2' == x1' = x2'+          | otherwise            = convergent (max 0 $ m - 1) x1 x2 x2' ps+          where x2  = (aa2, bb2)+                x2' = fromRational (aa2 % bb2)+                aa2 = b2 * aa1 + a2 * aa0+                bb2 = b2 * bb1 + a2 * bb0+        convergent _ _ _ x [] = x++continuedFraction _ b0 [] = fromInteger b0+ -- | Compute an infinite series to maximum precision.-infiniteSeries :: (FinitePrecision p, Rounding r)-               => [Decimal p r] -> Decimal p r-infiniteSeries = series zero+infiniteSeries :: FinitePrecision p+               => (ExtendedDecimal p -> ExtendedDecimal p -> ExtendedDecimal p)+               -> [ExtendedDecimal p] -> ExtendedDecimal p+infiniteSeries op ~(x:xs) = series x xs   where series n (x:xs)           | n' == n   = n'           | otherwise = series n' xs-          where n' = n + x+          where n' = n `op` x         series n []   = n --- | Compute the arcsine of the argument to maximum precision using series--- expansion.-arcsine :: (FinitePrecision p, Rounding r) => Decimal p r -> Decimal p r-arcsine x = infiniteSeries (x : series 1 2 x 3)-  where series n d x i =-          let x' = x * x2-          in (n * x') / (d * i) : series (n * i) (d * (i + one)) x' (i + two)+-- | Compute the inverse tangent of the argument to maximum precision using+-- series expansion.+seriesArctan :: FinitePrecision p => Decimal p r -> ExtendedDecimal p+seriesArctan z = infiniteSeries (+) (z' : series True three z')+  where series neg d z =+          let z' = z * z2+              n | neg       = flipSign z'+                | otherwise = z'+          in (n / d) : series (not neg) (d + two) z'+        z' = castRounding z+        z2 = z' * z'++-- | Compute the inverse sine of the argument to maximum precision using+-- series expansion.+seriesArcsin :: FinitePrecision p => Decimal p r -> ExtendedDecimal p+seriesArcsin z = infiniteSeries (+) (z' : series one two z' three)+  where series n d z i =+          let z' = z * z2+          in (n * z') / (d * i) : series (n * i) (d * (i + one)) z' (i + two)+        z' = castRounding z+        z2 = z' * z'++-- | Compute the inverse tangent of the (decimal) argument to maximum+-- precision.+arctan :: (FinitePrecision p, Rounding r) => Decimal p r -> ExtendedDecimal p+arctan z@Num {          } = arctan' (toRational z)+arctan   Inf { sign = s } = signFunc s halfPi+arctan   _                = qNaN++-- | Compute the inverse tangent of the (rational) argument to maximum+-- precision using a generalized continued fraction.+arctan' :: FinitePrecision p => Rational -> ExtendedDecimal p+arctan' z = continuedFraction m 0 $ (x, y) : partials 1 0 y+  where x = numerator   z+        y = denominator z+        m = fromInteger $ (42 * abs x) `div` y  -- estimated minimum # terms+         x2 = x * x+        ty = 2 * y --- | Compute π to maximum precision using the arcsine series expansion.-seriesPi :: FinitePrecision p => Decimal p RoundHalfEven-seriesPi = 6 * arcsine oneHalf+        -- [ (nx * nx, (n * 2 + 1) * y) | n <- [1..], let nx = n * x ]+        partials n a b =+          let a' = a + n * x2+              b' = b +     ty+          in (a', b') : partials (n + 2) a' b' +-- | Compute the inverse sine of the argument to maximum precision.+arcsin :: FinitePrecision p => Decimal p r -> ExtendedDecimal p+arcsin z = let z' = castUp z+           in castDown' $ two * arctan (z' / (one + sqrt (one - z' * z')))++-- | Compute the inverse cosine of the argument to maximum precision.+arccos :: FinitePrecision p => Decimal p r -> ExtendedDecimal p+arccos = castDown' . (halfPi -) . arcsin . castUp++-- | Compute π to maximum precision using the inverse sine series expansion.+seriesPi :: FinitePrecision p => ExtendedDecimal p+seriesPi = castDown' $ 6 * arcsin oneHalf++-- | Compute π to maximum precision using the best-known Machin-like formula.+machinPi :: FinitePrecision p => ExtendedDecimal p+machinPi = castDown' $ 16 * arctan' (1 % 5) - 4 * arctan' (1 % 239)++-- | Compute π to maximum precision using a generalized continued fraction+-- that converges linearly, adding at least three decimal digits of precision+-- per four terms.+cfPi :: FinitePrecision p => ExtendedDecimal p+cfPi = pi'+  where pi'    = continuedFraction m+                 0 $ (4, 1) : [ (n * n, n * 2 + 1) | n <- [1..] ]+        Just p = precision pi'+        m      = (p `div` 3) * 4++-- | Precomputed π to a precision of 50 digits+fastPi :: FinitePrecision p => ExtendedDecimal p+fastPi = 3.1415926535897932384626433832795028841971693993751++-- | Compute π/2 to maximum precision.+halfPi :: FinitePrecision p => ExtendedDecimal p+halfPi = castDown' $ pi * oneHalf++-- | Compute π/4 to maximum precision.+quarterPi :: FinitePrecision p => ExtendedDecimal p+quarterPi = castDown' $ pi * oneQuarter++-- | Compute (cos 𝛽, sin 𝛽) to maximum precision using Volder's algorithm+-- (CORDIC).+cordic :: FinitePrecision p+       => ExtendedDecimal p -> (ExtendedDecimal p, ExtendedDecimal p)+cordic beta@Num{}+  | beta >          halfPi = negatePair $ cordic (beta - pi)+  | beta < flipSign halfPi = negatePair $ cordic (beta + pi)+  | isZero beta            = (one, zero)+  | otherwise              = cordic' beta (one, zero) one angles++  where negatePair (x, y) = (flipSign x, flipSign y)++        angles = quarterPi : [ arctan' z | let half = 1 % 2+                                         , z <- iterate (* half) half ]++        cordic' beta v@(x, y) powerOfTwo ~(angle:angles)+          | v' == v   = (k * x, k * y)+          | otherwise = cordic' beta' v' powerOfTwo' angles+          where isNegBeta = isNegative beta+                beta'  | isNegBeta = beta + angle+                       | otherwise = beta - angle+                factor | isNegBeta = powerOfTwo { sign = Neg }+                       | otherwise = powerOfTwo+                v' = (x - factor * y, factor * x + y)+                powerOfTwo' = powerOfTwo * oneHalf++        -- K = lim {n→∞} K(n)+        -- K(n) = prod {i=0..n-1} 1 / sqrt (1 + 2^(-2 * i))+        k | p <= 50   = fastK+          | otherwise = seriesK+          where Just p  = precision k+                fastK   = 0.60725293500888125616944675250492826311239085215009+                seriesK = infiniteSeries (*)+                  [ recip $ sqrt (one + x) | x <- iterate (* oneQuarter) one ]++cordic _ = (qNaN, qNaN)++-- | Cast a number to a number with two additional digits of precision and+-- rounding half even.+castUp :: Precision p => Decimal p r -> ExtendedDecimal (PPlus1 (PPlus1 p))+castUp = coerce+ -- | Cast a number with two additional digits of precision down to a number--- with the desired precision.-castDown :: (Precision p, Rounding r)-         => Decimal (PPlus1 (PPlus1 p)) a -> Decimal p r-castDown = cast+-- with the desired precision, rounding half even.+castDown' :: Precision p+          => ExtendedDecimal (PPlus1 (PPlus1 p)) -> ExtendedDecimal p+castDown' = cast -notyet :: String -> a-notyet = error . (++ ": not yet implemented")+-- | Cast a number with two additional digits of precision down to a number+-- with the desired precision, rounding half even, but returning a number type+-- with arbitrary rounding.+castDown :: (Precision p, Rounding r)+         => ExtendedDecimal (PPlus1 (PPlus1 p)) -> Decimal p r+castDown = castRounding . castDown' --- | The trigonometric and hyperbolic 'Floating' methods (other than the--- precision-dependent constant 'pi') are not yet implemented.+-- | The constant 'pi' is precision-dependent. instance (FinitePrecision p, Rounding r) => Floating (Decimal p r) where-  pi = castDown seriesPi+  pi = castRounding pi'+    where pi' | p <= 50   = fastPi+              | otherwise = cfPi+          Just p = precision pi'    exp = castRounding . evalOp . Op.exp   log = castRounding . evalOp . Op.ln -  logBase 10 x = castRounding $ evalOp (Op.log10 x)-  logBase _  1 = zero-  logBase b  x = evalOp (join $ Op.divide <$> Op.ln x <*> Op.ln b)+  logBase b@Num{} x+    | b == ten = castRounding $ evalOp (Op.log10 x)+    | x == one = case b `compare` one of+        LT -> zero { sign = Neg }+        EQ -> qNaN+        GT -> zero+    | x == b && not (isZero b) = one+  logBase b x = evalOp (join $ Op.divide <$> Op.ln x <*> Op.ln b)    x ** y = evalOp (x `Op.power` y)    sqrt = castRounding . evalOp . Op.squareRoot -  sin   = notyet "sin"-  cos   = notyet "cos"--  asin  = notyet "asin"-  acos  = notyet "acos"-  atan  = notyet "atan"--  sinh  = notyet "sinh"-  cosh  = notyet "cosh"--  asinh = notyet "asinh"-  acosh = notyet "acosh"-  atanh = notyet "atanh"+  sin  = castDown . snd                . cordic . castUp+  cos  = castDown . fst                . cordic . castUp+  tan  = castDown . uncurry (flip (/)) . cordic . castUp -{- $doctest-Floating-prop> realToFrac (pi :: ExtendedDecimal P16) == (pi :: Double)+  asin = castRounding . arcsin+  acos = castRounding . arccos+  atan = castRounding . arctan -prop> y >= 0 ==> (x :: BasicDecimal) ** fromInteger y == x ^ y+  -- sinh x = let ex = exp x in (ex^2 - 1) / (2 * ex)+  sinh x = castDown . evalOp' $+    Op.exp x >>= \ex -> two `Op.multiply` ex >>= \tex ->+    ex `Op.multiply` ex >>= (`Op.subtract` one) >>= (`Op.divide` tex)+  -- cosh x = let ex = exp x in (ex^2 + 1) / (2 * ex)+  cosh x = castDown . evalOp' $+    Op.exp x >>= \ex -> two `Op.multiply` ex >>= \tex ->+    ex `Op.multiply` ex >>= (`Op.add` one) >>= (`Op.divide` tex)+  -- tanh x = let e2x = exp (2 * x) in (e2x - 1) / (e2x + 1)+  tanh x = castDown . evalOp' $+    two `Op.multiply` x >>= Op.exp >>= \e2x ->+    e2x `Op.subtract` one >>= \e2xm1 -> e2x `Op.add` one >>= (e2xm1 `Op.divide`) -prop> isFinite x && x >= 0 ==> coefficient (sqrt (x * x) - (x :: ExtendedDecimal P16)) <= 1--}+  -- asinh x = log (x + sqrt (x^2 + 1))+  asinh x = castDown . evalOp' $ x `Op.multiply` x >>=+    (`Op.add` one) >>= Op.squareRoot >>= (x `Op.add`) >>= Op.ln+  -- acosh x = log (x + sqrt (x^2 - 1))+  acosh x = castDown . evalOp' $ x `Op.multiply` x >>=+    (`Op.subtract` one) >>= Op.squareRoot >>= (x `Op.add`) >>= Op.ln+  -- atanh x = log ((1 + x) / (1 - x)) / 2+  atanh x = castDown . evalOp' $ one `Op.add` x >>= \xp1 ->+    one `Op.subtract` x >>= (xp1 `Op.divide`) >>= Op.ln >>= Op.multiply oneHalf  instance (FinitePrecision p, Rounding r) => RealFloat (Decimal p r) where   floatRadix  _ = 10   floatDigits x = let Just p = precision x in p-  floatRange  _ = (minBound, maxBound)  -- ?+  floatRange  x = let Just emin = eMin x+                      Just emax = eMax x+                  in (fromIntegral emin, fromIntegral emax)    decodeFloat x = case x of-    Num  { sign = s, coefficient = c, exponent = e } -> (m, n)-      where m = signFunc s (fromIntegral c)-            n = fromIntegral e-    Inf  { sign = s              } -> (special s 0, maxBound    )-    QNaN { sign = s, payload = p } -> (special s p, minBound    )-    SNaN { sign = s, payload = p } -> (special s p, minBound + 1)+    Num { sign = s, coefficient = c, exponent = e }+      | c == 0    -> (0, 0)+      | otherwise -> (m, n)+      where m = signFunc s (fromIntegral $ c * 10^d)+            n = fromIntegral e - d+            d = floatDigits x - numDigits c+    Inf { sign = s                                } -> (special s 0, maxBound)+    NaN { sign = s, signaling = sig, payload = p  } -> (special s p, n)+      where n = minBound + fromEnum sig+     where special :: Sign -> Coefficient -> Integer           special s v = signFunc s (pp + fromIntegral v)           pp = 10 ^ floatDigits x :: Integer@@ -378,16 +517,16 @@                                    }           special             | n == maxBound     = Inf  { sign = signMatch m }-            | n == minBound     = QNaN { sign = signMatch m, payload = p }-            | otherwise         = SNaN { sign = signMatch m, payload = p }+            | n == minBound     = qNaN { sign = signMatch m, payload = p }+            | otherwise         = sNaN { sign = signMatch m, payload = p }             where p = fromInteger (am - pp)+           am = abs m              :: Integer           pp = 10 ^ floatDigits x :: Integer    isNaN x = case x of-    QNaN{} -> True-    SNaN{} -> True-    _      -> False+    NaN{} -> True+    _     -> False    isInfinite x = case x of     Inf{} -> True@@ -401,17 +540,6 @@    isIEEE _ = True -{- $doctest-RealFloat-prop> uncurry encodeFloat (decodeFloat x) == (x :: BasicDecimal)-prop> isFinite x ==> significand x * fromInteger (floatRadix x) ^^ Prelude.exponent x == (x :: BasicDecimal)-prop> Prelude.exponent (0 :: BasicDecimal) == 0-prop> isFinite x && x /= 0 ==> Prelude.exponent (x :: BasicDecimal) == snd (decodeFloat x) + floatDigits x--prop> isNegativeZero (read "-0" :: BasicDecimal) == True-prop> isNegativeZero (read "+0" :: BasicDecimal) == False-prop> x /= 0 ==> isNegativeZero (x :: BasicDecimal) == False--}- -- | The 'Bits' instance makes use of the logical operations from the -- /General Decimal Arithmetic Specification/ using a /digit-wise/ -- representation of bits where the /sign/ is non-negative, the /exponent/ is@@ -454,6 +582,13 @@ instance FinitePrecision p => FiniteBits (Decimal p r) where   finiteBitSize x = let Just p = precision x in p +instance NFData (Decimal p r) where+  rnf Num { sign = s, coefficient = c, exponent = e } =+    rnf s `seq` rnf c `seq` rnf e+  rnf Inf { sign = s } = rnf s+  rnf NaN { sign = s, signaling = sig, payload = p } =+    rnf s `seq` rnf sig `seq` rnf p+ -- | A 'Decimal' representing the value zero zero :: Decimal p r zero = Num { sign        = Pos@@ -465,6 +600,10 @@ oneHalf :: Decimal p r oneHalf = zero { coefficient = 5, exponent = -1 } +-- | A 'Decimal' representing the value ¼+oneQuarter :: Decimal p r+oneQuarter = zero { coefficient = 25, exponent = -2 }+ -- | A 'Decimal' representing the value one one :: Decimal p r one = zero { coefficient = 1 }@@ -473,6 +612,10 @@ two :: Decimal p r two = zero { coefficient = 2 } +-- | A 'Decimal' representing the value three+three :: Decimal p r+three = zero { coefficient = 3 }+ -- | A 'Decimal' representing the value ten ten :: Decimal p r ten = zero { coefficient = 10 }@@ -487,11 +630,11 @@  -- | A 'Decimal' representing undefined results qNaN :: Decimal p r-qNaN = QNaN { sign = Pos, payload = 0 }+qNaN = NaN { sign = Pos, signaling = False, payload = 0 }  -- | A signaling 'Decimal' representing undefined results sNaN :: Decimal p r-sNaN = SNaN { sign = Pos, payload = 0 }+sNaN = qNaN { signaling = True }  -- | Negate the given 'Decimal' by directly flipping its sign. flipSign :: Decimal p r -> Decimal p r@@ -559,35 +702,31 @@   | isFinite n && not (isZero n) = maybe False (adjustedExponent n <) (eMin n)   | otherwise                    = False --- | If the argument is 'False', return a 'Decimal' value zero; if 'True',--- return the value one. This is basically an optimized @toEnum . fromEnum@ to--- support an all-decimal usage of the operations from--- "Numeric.Decimal.Operation" that return a 'Bool'.+-- | Return @0@ or @1@ if the argument is 'False' or 'True', respectively.+-- This is basically an optimized @'toEnum' . 'fromEnum'@ and allows an+-- all-decimal usage of the operations from "Numeric.Decimal.Operation" that+-- return a 'Bool'. fromBool :: Bool -> Decimal p r fromBool False = zero fromBool True  = one  -- | Return 'False' if the argument is zero or NaN, and 'True' otherwise. toBool :: Decimal p r -> Bool-toBool Num { coefficient = c }-  | c == 0    = False-  | otherwise = True-toBool Inf{}  = True-toBool _      = False+toBool Num { coefficient = c } = c /= 0+toBool NaN{}                   = False+toBool _                       = True +-- | Return @-1@, @0@, or @1@ if the argument is 'LT', 'EQ', or 'GT',+-- respectively. This allows an all-decimal usage of the operations from+-- "Numeric.Decimal.Operation" that return an 'Ordering'.+fromOrdering :: Ordering -> Decimal p r+fromOrdering LT = negativeOne+fromOrdering EQ = zero+fromOrdering GT = one+ -- | Upper limit on the absolute value of the exponent eLimit :: Precision p => p -> Maybe Exponent eLimit = eMax -- ?---- | Minimum value of the adjusted exponent-eMin :: Precision p => p -> Maybe Exponent-eMin n = (1 -) <$> eMax n---- | Maximum value of the adjusted exponent-eMax :: Precision p => p -> Maybe Exponent-eMax n = subtract 1 . (10 ^) . numDigits <$> base-  where mlength = precision n                    :: Maybe Int-        base = (10 *) . fromIntegral <$> mlength :: Maybe Coefficient  -- | Minimum value of the exponent for subnormal results eTiny :: Precision p => p -> Maybe Exponent
src/Numeric/Decimal/Number.hs-boot view
@@ -4,37 +4,31 @@  module Numeric.Decimal.Number        ( Sign(..)-       , Decimal(..)        , Coefficient+       , Exponent+       , Decimal(..)        , numDigits        ) where  import Numeric.Natural (Natural) -import Numeric.Decimal.Precision- data Sign = Pos | Neg-instance Eq Sign  type Coefficient = Natural-type Exponent    = Int+type Exponent    = Integer type Payload     = Coefficient  type role Decimal phantom phantom data Decimal p r-  = Num  { sign        :: Sign-         , coefficient :: Coefficient-         , exponent    :: Exponent-         }-  | Inf  { sign        :: Sign-         }-  | QNaN { sign        :: Sign-         , payload     :: Payload-         }-  | SNaN { sign        :: Sign-         , payload     :: Payload-         }--instance Precision p => Precision (Decimal p r)+  = Num { sign        :: Sign+        , coefficient :: Coefficient+        , exponent    :: Exponent+        }+  | Inf { sign        :: Sign+        }+  | NaN { sign        :: Sign+        , signaling   :: Bool+        , payload     :: Payload+        }  numDigits :: Coefficient -> Int
src/Numeric/Decimal/Operation.hs view
@@ -16,1950 +16,1383 @@        ( -- * Arithmetic operations          -- $arithmetic-operations -         abs-       , add-       , subtract-       , compare-       , compareSignal-       , divide-         -- divideInteger-       , exp-       , fusedMultiplyAdd-       , ln-       , log10-       , max-       , maxMagnitude-       , min-       , minMagnitude-       , minus-       , plus-       , multiply-         -- nextMinus-         -- nextPlus-         -- nextToward-       , power-       , quantize-       , reduce-         -- remainder-         -- remainderNear-         -- roundToIntegralExact-         -- roundToIntegralValue-       , squareRoot--         -- * Miscellaneous operations-         -- $miscellaneous-operations--       , and-       , canonical-       , class_, Class(..), Sign(..), NumberClass(..), NaNClass(..)-         -- compareTotal-         -- compareTotalMagnitude-       , copy-       , copyAbs-       , copyNegate-       , copySign-       , invert-       , isCanonical-       , isFinite-       , isInfinite-       , isNaN-       , isNormal-       , isQNaN-       , isSigned-       , isSNaN-       , isSubnormal-       , isZero-       , logb-       , or-       , radix-       , rotate-       , sameQuantum-         -- scaleb-       , shift-       , xor-       ) where--import Prelude hiding (abs, and, compare, exp, exponent, isInfinite, isNaN,-                       max, min, or, subtract)-import qualified Prelude--import Control.Monad (join)-import Data.Bits (complement, setBit, testBit, zeroBits, (.&.), (.|.))-import Data.Coerce (coerce)-import Data.List (find)-import Data.Maybe (fromMaybe)--import qualified Data.Bits as Bits--import Numeric.Decimal.Arithmetic-import Numeric.Decimal.Number hiding (isFinite, isNormal, isSubnormal, isZero)-import Numeric.Decimal.Precision-import Numeric.Decimal.Rounding--import qualified Numeric.Decimal.Number as Number--{- $setup->>> :load Harness--}--finitePrecision :: FinitePrecision p => Decimal p r -> Int-finitePrecision n = let Just p = precision n in p--roundingAlg :: Rounding r => Arith p r a -> RoundingAlgorithm-roundingAlg = rounding . arithRounding-  where arithRounding :: Arith p r a -> r-        arithRounding = undefined--result :: (Precision p, Rounding r) => Decimal p r -> Arith p r (Decimal p r)-result = roundDecimal  -- ...---  | maybe False (numDigits c >) (precision r) = undefined--invalidOperation :: Decimal a b -> Arith p r (Decimal p r)-invalidOperation n = raiseSignal InvalidOperation qNaN--toQNaN :: Decimal a b -> Decimal p r-toQNaN SNaN { sign = s, payload = p } = QNaN { sign = s, payload = p }-toQNaN n@QNaN{}                       = coerce n-toQNaN n                              = qNaN { sign = sign n }--toQNaN2 :: Decimal a b -> Decimal c d -> Decimal p r-toQNaN2 nan@SNaN{} _ = toQNaN nan-toQNaN2 _ nan@SNaN{} = toQNaN nan-toQNaN2 nan@QNaN{} _ = coerce nan-toQNaN2 _ nan@QNaN{} = coerce nan-toQNaN2 n _          = toQNaN n--quietToSignal :: Decimal p r -> Decimal p r-quietToSignal QNaN { sign = s, payload = p } = SNaN { sign = s, payload = p }-quietToSignal x = x---- $arithmetic-operations------ This section describes the arithmetic operations on, and some other--- functions of, numbers, including subnormal numbers, negative zeros, and--- special values (see also IEEE 754 §5 and §6).--{- $doctest-special-values->>> op2 Op.add "Infinity" "1"-Infinity-->>> op2 Op.add "NaN" "1"-NaN-->>> op2 Op.add "NaN" "Infinity"-NaN-->>> op2 Op.subtract "1" "Infinity"--Infinity-->>> op2 Op.multiply "-1" "Infinity"--Infinity-->>> op2 Op.subtract "-0" "0"--0-->>> op2 Op.multiply "-1" "0"--0-->>> op2 Op.divide "1" "0"-Infinity-->>> op2 Op.divide "1" "-0"--Infinity-->>> op2 Op.divide "-1" "0"--Infinity--}---- | 'add' takes two operands. If either operand is a /special value/ then the--- general rules apply.------ Otherwise, the operands are added.------ The result is then rounded to /precision/ digits if necessary, counting--- from the most significant digit of the result.-add :: (Precision p, Rounding r)-    => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-add Num { sign = xs, coefficient = xc, exponent = xe }-    Num { sign = ys, coefficient = yc, exponent = ye } = sum--  where sum = result Num { sign = rs, coefficient = rc, exponent = re }-        rs | rc /= 0                       = if xac > yac then xs else ys-           | xs == Neg && ys == Neg        = Neg-           | xs /= ys &&-             roundingAlg sum == RoundFloor = Neg-           | otherwise                     = Pos-        rc | xs == ys  = xac + yac-           | xac > yac = xac - yac-           | otherwise = yac - xac-        re = Prelude.min xe ye-        (xac, yac) | xe == ye  = (xc, yc)-                   | xe >  ye  = (xc * 10^n, yc)-                   | otherwise = (xc, yc * 10^n)-          where n = Prelude.abs (xe - ye)--add inf@Inf { sign = xs } Inf { sign = ys }-  | xs == ys  = return (coerce inf)-  | otherwise = invalidOperation inf-add inf@Inf{} Num{} = return (coerce inf)-add Num{} inf@Inf{} = return (coerce inf)-add x y             = return (toQNaN2 x y)--{- $doctest-add->>> op2 Op.add "12" "7.00"-19.00-->>> op2 Op.add "1E+2" "1E+4"-1.01E+4--}---- | 'subtract' takes two operands. If either operand is a /special value/--- then the general rules apply.------ Otherwise, the operands are added after inverting the /sign/ used for the--- second operand.------ The result is then rounded to /precision/ digits if necessary, counting--- from the most significant digit of the result.-subtract :: (Precision p, Rounding r)-         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-subtract x = add x . flipSign--{- $doctest-subtract->>> op2 Op.subtract "1.3" "1.07"-0.23-->>> op2 Op.subtract "1.3" "1.30"-0.00-->>> op2 Op.subtract "1.3" "2.07"--0.77--}---- | 'minus' takes one operand, and corresponds to the prefix minus operator--- in programming languages.------ Note that the result of this operation is affected by context and may set--- /flags/. The 'copyNegate' operation may be used instead of 'minus' if this--- is not desired.-minus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)-minus x = zero { exponent = exponent x } `subtract` x--{- $doctest-minus->>> op1 Op.minus "1.3"--1.3-->>> op1 Op.minus "-1.3"-1.3--}---- | 'plus' takes one operand, and corresponds to the prefix plus operator in--- programming languages.------ Note that the result of this operation is affected by context and may set--- /flags/.-plus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)-plus x = zero { exponent = exponent x } `add` x--{- $doctest-plus->>> op1 Op.plus "1.3"-1.3-->>> op1 Op.plus "-1.3"--1.3--}---- | 'multiply' takes two operands. If either operand is a /special value/--- then the general rules apply. Otherwise, the operands are multiplied--- together (“long multiplication”), resulting in a number which may be as--- long as the sum of the lengths of the two operands.------ The result is then rounded to /precision/ digits if necessary, counting--- from the most significant digit of the result.-multiply :: (Precision p, Rounding r)-         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-multiply Num { sign = xs, coefficient = xc, exponent = xe }-         Num { sign = ys, coefficient = yc, exponent = ye } = result rn--  where rn = Num { sign = rs, coefficient = rc, exponent = re }-        rs = xorSigns xs ys-        rc = xc * yc-        re = xe + ye--multiply Inf { sign = xs } Inf { sign = ys } =-  return Inf { sign = xorSigns xs ys }-multiply Inf { sign = xs } Num { sign = ys, coefficient = yc }-  | yc == 0   = invalidOperation qNaN-  | otherwise = return Inf { sign = xorSigns xs ys }-multiply Num { sign = xs, coefficient = xc } Inf { sign = ys }-  | xc == 0   = invalidOperation qNaN-  | otherwise = return Inf { sign = xorSigns xs ys }-multiply nan@SNaN{} _ = invalidOperation nan-multiply _ nan@SNaN{} = invalidOperation nan-multiply x y = return (toQNaN2 x y)--{- $doctest-multiply->>> op2 Op.multiply "1.20" "3"-3.60-->>> op2 Op.multiply "7" "3"-21-->>> op2 Op.multiply "0.9" "0.8"-0.72-->>> op2 Op.multiply "0.9" "-0"--0.0-->>> op2 Op.multiply "654321" "654321"-4.28135971E+11--}---- | 'exp' takes one operand. If the operand is a NaN then the general rules--- for special values apply.------ Otherwise, the result is /e/ raised to the power of the operand, with the--- following cases:------ * If the operand is −Infinity, the result is 0 and exact.------ * If the operand is a zero, the result is 1 and exact.------ * If the operand is +Infinity, the result is +Infinity and exact.------ * Otherwise the result is inexact and will be rounded using the--- /round-half-even/ algorithm. The coefficient will have exactly /precision/--- digits (unless the result is subnormal). These inexact results should be--- correctly rounded, but may be up to 1 ulp (unit in last place) in error.-exp :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p RoundHalfEven)-exp x@Num { sign = s, coefficient = c }-  | c == 0    = return one-  | s == Neg  = subArith (maclaurin x { sign = Pos } >>= reciprocal) >>=-                subRounded >>= result-  | otherwise = subArith (maclaurin x) >>= subRounded >>= result--  where multiplyExact :: Decimal a b -> Decimal c d-                      -> Arith PInfinite RoundHalfEven-                         (Decimal PInfinite RoundHalfEven)-        multiplyExact = multiply--        maclaurin :: FinitePrecision p => Decimal a b-                  -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-        maclaurin x-          | adjustedExponent x >= 0 = subArith (subMaclaurin x) >>= subRounded-          | otherwise = sum one one one one-          where sum :: FinitePrecision p-                    => Decimal p RoundHalfEven-                    -> Decimal PInfinite RoundHalfEven-                    -> Decimal PInfinite RoundHalfEven-                    -> Decimal PInfinite RoundHalfEven-                    -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-                sum s num den n = do-                  num' <- subArith (multiplyExact num x)-                  den' <- subArith (multiplyExact den n)-                  s' <- add s =<< divide num' den'-                  if s' == s then return s'-                    else sum s' num' den' =<< subArith (add n one)--        subMaclaurin :: FinitePrecision p => Decimal a b-                     -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-        subMaclaurin x = subArith (multiplyExact x oneHalf) >>= maclaurin >>=-          \r -> multiply r r--        subRounded :: Precision p-                   => Decimal (PPlus1 (PPlus1 p)) a-                   -> Arith p r (Decimal p RoundHalfEven)-        subRounded = subArith . roundDecimal--        result :: Decimal p a -> Arith p r (Decimal p a)-        result r = coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')-          where r' = coerce r--exp n@Inf { sign = s }-  | s == Pos  = return (coerce n)-  | otherwise = return zero-exp n@QNaN{}  = return (coerce n)-exp n@SNaN{}  = coerce <$> invalidOperation n--{- $doctest-exp->>> op1 Op.exp "-Infinity"-0-->>> op1 Op.exp "-1"-0.367879441-->>> op1 Op.exp "0"-1-->>> op1 Op.exp "1"-2.71828183-->>> op1 Op.exp "0.693147181"-2.00000000-->>> op1 Op.exp "+Infinity"-Infinity--}---- | 'fusedMultiplyAdd' takes three operands; the first two are multiplied--- together, using 'multiply', with sufficient precision and exponent range--- that the result is exact and unrounded. No /flags/ are set by the--- multiplication unless one of the first two operands is a signaling NaN or--- one is a zero and the other is an infinity.------ Unless the multiplication failed, the third operand is then added to the--- result of that multiplication, using 'add', under the current context.------ In other words, @fusedMultiplyAdd x y z@ delivers a result which is @(x ×--- y) + z@ with only the one, final, rounding.-fusedMultiplyAdd :: (Precision p, Rounding r)-                 => Decimal a b -> Decimal c d -> Decimal e f-                 -> Arith p r (Decimal p r)-fusedMultiplyAdd x y z =-  either raise (return . coerce) (exactMult x y) >>= add z--  where exactMult :: Rounding r => Decimal a b -> Decimal c d-                  -> Either (Exception PInfinite r) (Decimal PInfinite r)-        exactMult x y = evalArith (multiply x y) newContext--        raise :: Exception a r -> Arith p r (Decimal p r)-        raise e = raiseSignal (exceptionSignal e) (coerce $ exceptionResult e)--{- $doctest-fusedMultiplyAdd->>> op3 Op.fusedMultiplyAdd "3" "5" "7"-22-->>> op3 Op.fusedMultiplyAdd "3" "-5" "7"--8-->>> op3 Op.fusedMultiplyAdd "888565290" "1557.96930" "-86087.7578"-1.38435736E+12--}---- | 'ln' takes one operand. If the operand is a NaN then the general rules--- for special values apply.------ Otherwise, the operand must be a zero or positive, and the result is the--- natural (base /e/) logarithm of the operand, with the following cases:------ * If the operand is a zero, the result is −Infinity and exact.------ * If the operand is +Infinity, the result is +Infinity and exact.------ * If the operand equals one, the result is 0 and exact.------ * Otherwise the result is inexact and will be rounded using the--- /round-half-even/ algorithm. The coefficient will have exactly /precision/--- digits (unless the result is subnormal). These inexact results should be--- correctly rounded, but may be up to 1 ulp (unit in last place) in error.-ln :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p RoundHalfEven)-ln x@Num { sign = s, coefficient = c, exponent = e }-  | c == 0   = return infinity { sign = Neg }-  | s == Pos = if e <= 0 && c == 10^(-e) then return zero-               else subArith (subLn x) >>= subRounded >>= result--  where subLn :: FinitePrecision p => Decimal a b-              -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-        subLn x = do-          let fe = fromIntegral (-(numDigits c - 1)) :: Exponent-              r  = fromIntegral (e - fe) :: Decimal PInfinite RoundHalfEven-          lnf <- taylorLn x { exponent = fe }-          add lnf =<< multiply r =<< ln10--        subRounded :: Precision p => Decimal (PPlus1 (PPlus1 p)) a-                   -> Arith p r (Decimal p RoundHalfEven)-        subRounded = subArith . roundDecimal--        result :: Decimal p a -> Arith p r (Decimal p a)-        result r = coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')-          where r' = coerce r--ln n@Inf { sign = Pos } = return (coerce n)-ln n@QNaN{} = return (coerce n)-ln n = coerce <$> invalidOperation n--taylorLn :: FinitePrecision p => Decimal a b-         -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-taylorLn x = do-  num <- x `subtract` one-  den <- x `add`      one-  multiply two =<< sum =<< num `divide` den--    where sum :: FinitePrecision p => Decimal p RoundHalfEven-              -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-          sum b = multiply b b >>= \b2 -> sum' b b b2 one--            where sum' :: FinitePrecision p-                       => Decimal p RoundHalfEven-                       -> Decimal p RoundHalfEven-                       -> Decimal p RoundHalfEven-                       -> Decimal PInfinite RoundHalfEven-                       -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-                  sum' s m b n = do-                    m' <- multiply m b-                    n' <- subArith (add n two)-                    s' <- add s =<< divide m' n'-                    if s' == s then return s' else sum' s' m' b n'--ln10 :: FinitePrecision p => Arith p r (Decimal p RoundHalfEven)-ln10 = getPrecision >>= \(Just p) ->-  if p <= 50 then return fastLn10 else slowLn10--  where fastLn10 :: FinitePrecision p => Decimal p RoundHalfEven-        fastLn10 = 2.3025850929940456840179914546843642076011014886288--        slowLn10 :: FinitePrecision p => Arith p r (Decimal p RoundHalfEven)-        slowLn10 = subArith (taylorLn ten) >>= subRound--          where subRound :: Precision p => Decimal (PPlus1 (PPlus1 p)) a-                         -> Arith p r (Decimal p RoundHalfEven)-                subRound = subArith . roundDecimal--{- $doctest-ln->>> op1 Op.ln "0"--Infinity-->>> op1 Op.ln "1.000"-0-->>> op1 Op.ln "2.71828183"-1.00000000-->>> op1 Op.ln "10"-2.30258509-->>> op1 Op.ln "+Infinity"-Infinity--}---- | 'log10' takes one operand. If the operand is a NaN then the general rules--- for special values apply.------ Otherwise, the operand must be a zero or positive, and the result is the--- base 10 logarithm of the operand, with the following cases:------ * If the operand is a zero, the result is −Infinity and exact.------ * If the operand is +Infinity, the result is +Infinity and exact.------ * If the operand equals an integral power of ten (including 10^0 and--- negative powers) and there is sufficient /precision/ to hold the integral--- part of the result, the result is an integer (with an exponent of 0) and--- exact.------ * Otherwise the result is inexact and will be rounded using the--- /round-half-even/ algorithm. The coefficient will have exactly /precision/--- digits (unless the result is subnormal). These inexact results should be--- correctly rounded, but may be up to 1 ulp (unit in last place) in error.-log10 :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p RoundHalfEven)-log10 x@Num { sign = s, coefficient = c, exponent = e }-  | c == 0   = return infinity { sign = Neg }-  | s == Pos = getPrecision >>= \prec -> case powerOfTen c e of-      Just p | maybe True (numDigits pc <=) prec -> return (fromInteger p)-        where pc = fromInteger (Prelude.abs p) :: Coefficient-      _ -> subArith (join $ divide <$> ln x <*> ln10) >>= result--  where powerOfTen :: Coefficient -> Exponent -> Maybe Integer-        powerOfTen c e-          | c == 10^d = Just (fromIntegral e + fromIntegral d)-          | otherwise = Nothing-          where d = numDigits c - 1 :: Int--        result :: Decimal p a -> Arith p r (Decimal p a)-        result r = coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')-          where r' = coerce r--log10 n@Inf { sign = Pos } = return (coerce n)-log10 n@QNaN{} = return (coerce n)-log10 n = coerce <$> invalidOperation n--{- $doctest-log10->>> op1 Op.log10 "0"--Infinity-->>> op1 Op.log10 "0.001"--3-->>> op1 Op.log10 "1.000"-0-->>> op1 Op.log10 "2"-0.301029996-->>> op1 Op.log10 "10"-1-->>> op1 Op.log10 "70"-1.84509804-->>> op1 Op.log10 "+Infinity"-Infinity--}---- | 'divide' takes two operands. If either operand is a /special value/ then--- the general rules apply.------ Otherwise, if the divisor is zero then either the Division undefined--- condition is raised (if the dividend is zero) and the result is NaN, or the--- Division by zero condition is raised and the result is an Infinity with a--- sign which is the exclusive or of the signs of the operands.------ Otherwise, a “long division” is effected.------ The result is then rounded to /precision/ digits, if necessary, according--- to the /rounding/ algorithm and taking into account the remainder from the--- division.-divide :: (FinitePrecision p, Rounding r)-       => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-divide dividend@Num{ sign = xs } Num { coefficient = 0, sign = ys }-  | Number.isZero dividend = invalidOperation qNaN-  | otherwise              = raiseSignal DivisionByZero-                             infinity { sign = xorSigns xs ys }-divide Num { sign = xs, coefficient = xc, exponent = xe }-       Num { sign = ys, coefficient = yc, exponent = ye } = quotient--  where quotient = result =<< answer-        rn = Num { sign = rs, coefficient = rc, exponent = re }-        rs = xorSigns xs ys-        (rc, rem, dv, adjust) = longDivision xc yc (finitePrecision rn)-        re = xe - (ye + adjust)-        answer-          | rem == 0  = return rn-          | otherwise = roundDecimal $ case (rem * 2) `Prelude.compare` dv of-              LT -> rn { coefficient = rc * 10 + 1, exponent = re - 1 }-              EQ -> rn { coefficient = rc * 10 + 5, exponent = re - 1 }-              GT -> rn { coefficient = rc * 10 + 9, exponent = re - 1 }--divide Inf{} Inf{} = invalidOperation qNaN-divide Inf { sign = xs } Num { sign = ys } =-  return Inf { sign = xorSigns xs ys }-divide Num { sign = xs } Inf { sign = ys } =-  return zero { sign = xorSigns xs ys }-divide x y = return (toQNaN2 x y)--{- $doctest-divide->>> op2 Op.divide "1" "3"-0.333333333-->>> op2 Op.divide "2" "3"-0.666666667-->>> op2 Op.divide "5" "2"-2.5-->>> op2 Op.divide "1" "10"-0.1-->>> op2 Op.divide "12" "12"-1-->>> op2 Op.divide "8.00" "2"-4.00-->>> op2 Op.divide "2.400" "2.0"-1.20-->>> op2 Op.divide "1000" "100"-10-->>> op2 Op.divide "1000" "1"-1000-->>> op2 Op.divide "2.40E+6" "2"-1.20E+6--}--type Dividend  = Coefficient-type Divisor   = Coefficient-type Quotient  = Coefficient-type Remainder = Dividend--longDivision :: Dividend -> Divisor -> Int-             -> (Quotient, Remainder, Divisor, Exponent)-longDivision 0  dv _ = (0, 0, dv, 0)-longDivision dd dv p = step1 dd dv 0--  where step1 :: Dividend -> Divisor -> Exponent-              -> (Quotient, Remainder, Divisor, Exponent)-        step1 dd dv adjust-          | dd <       dv = step1 (dd * 10)  dv       (adjust + 1)-          | dd >= 10 * dv = step1  dd       (dv * 10) (adjust - 1)-          | otherwise     = step2  dd        dv        adjust--        step2 :: Dividend -> Divisor -> Exponent-              -> (Quotient, Remainder, Divisor, Exponent)-        step2 = step3 0--        step3 :: Quotient -> Dividend -> Divisor -> Exponent-              -> (Quotient, Remainder, Divisor, Exponent)-        step3 r dd dv adjust-          | dv <= dd                 = step3 (r +  1) (dd - dv) dv  adjust-          | (dd == 0 && adjust >= 0) ||-            numDigits r == p         = step4  r        dd       dv  adjust-          | otherwise                = step3 (r * 10) (dd * 10) dv (adjust + 1)--        step4 :: Quotient -> Remainder -> Divisor -> Exponent-              -> (Quotient, Remainder, Divisor, Exponent)-        step4 = (,,,)--reciprocal :: (FinitePrecision p, Rounding r)-           => Decimal a b -> Arith p r (Decimal p r)-reciprocal = divide one---- | 'abs' takes one operand. If the operand is negative, the result is the--- same as using the 'minus' operation on the operand. Otherwise, the result--- is the same as using the 'plus' operation on the operand.------ Note that the result of this operation is affected by context and may set--- /flags/. The 'copyAbs' operation may be used if this is not desired.-abs :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)-abs x-  | isNegative x = minus x-  | otherwise    = plus  x--{- $doctest-abs->>> op1 Op.abs "2.1"-2.1-->>> op1 Op.abs "-100"-100-->>> op1 Op.abs "101.5"-101.5-->>> op1 Op.abs "-101.5"-101.5--}---- | 'compare' takes two operands and compares their values numerically. If--- either operand is a /special value/ then the general rules apply. No flags--- are set unless an operand is a signaling NaN.------ Otherwise, the operands are compared, returning @−1@ if the first is less--- than the second, @0@ if they are equal, or @1@ if the first is greater than--- the second.-compare :: (Precision p, Rounding r)-        => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-compare x@Num{} y@Num{} = nzp <$> (xn `subtract` yn)--  where (xn, yn) | sign x /= sign y = (nzp x, nzp y)-                 | otherwise        = (x, y)--        nzp :: Decimal p r -> Decimal p r-        nzp Num { sign = s, coefficient = c }-          | c == 0    = zero-          | s == Pos  = one-          | otherwise = negativeOne-        nzp Inf { sign = s }-          | s == Pos  = one-          | otherwise = negativeOne-        nzp n = toQNaN n--compare Inf { sign = xs } Inf { sign = ys }-  | xs == ys  = return zero-  | xs == Neg = return negativeOne-  | otherwise = return one-compare Inf { sign = xs } Num { }-  | xs == Neg = return negativeOne-  | otherwise = return one-compare Num { } Inf { sign = ys }-  | ys == Pos = return negativeOne-  | otherwise = return one-compare nan@SNaN{} _ = invalidOperation nan-compare _ nan@SNaN{} = invalidOperation nan-compare x y          = return (toQNaN2 x y)--{- $doctest-compare->>> op2 Op.compare "2.1" "3"--1-->>> op2 Op.compare "2.1" "2.1"-0-->>> op2 Op.compare "2.1" "2.10"-0-->>> op2 Op.compare "3" "2.1"-1-->>> op2 Op.compare "2.1" "-3"-1-->>> op2 Op.compare "-3" "2.1"--1--}---- | 'compareSignal' takes two operands and compares their values--- numerically. This operation is identical to 'compare', except that if--- neither operand is a signaling NaN then any quiet NaN operand is treated as--- though it were a signaling NaN. (That is, all NaNs signal, with signaling--- NaNs taking precedence over quiet NaNs.)-compareSignal :: (Precision p, Rounding r)-              => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-compareSignal x@SNaN{} y        =               x `compare`               y-compareSignal x        y@SNaN{} =               x `compare`               y-compareSignal x        y        = quietToSignal x `compare` quietToSignal y---- | 'max' takes two operands, compares their values numerically, and returns--- the maximum. If either operand is a NaN then the general rules apply,--- unless one is a quiet NaN and the other is numeric, in which case the--- numeric operand is returned.-max :: (Precision p, Rounding r)-    => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)-max x y = snd <$> minMax id x y--{- $doctest-max->>> op2 Op.max "3" "2"-3-->>> op2 Op.max "-10" "3"-3-->>> op2 Op.max "1.0" "1"-1-->>> op2 Op.max "7" "NaN"-7--}---- | 'maxMagnitude' takes two operands and compares their values numerically--- with their /sign/ ignored and assumed to be 0.------ If, without signs, the first operand is the larger then the original first--- operand is returned (that is, with the original sign). If, without signs,--- the second operand is the larger then the original second operand is--- returned. Otherwise the result is the same as from the 'max' operation.-maxMagnitude :: (Precision p, Rounding r)-             => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)-maxMagnitude x y = snd <$> minMax withoutSign x y---- | 'min' takes two operands, compares their values numerically, and returns--- the minimum. If either operand is a NaN then the general rules apply,--- unless one is a quiet NaN and the other is numeric, in which case the--- numeric operand is returned.-min :: (Precision p, Rounding r)-    => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)-min x y = fst <$> minMax id x y--{- $doctest-min->>> op2 Op.min "3" "2"-2-->>> op2 Op.min "-10" "3"--10-->>> op2 Op.min "1.0" "1"-1.0-->>> op2 Op.min "7" "NaN"-7--}---- | 'minMagnitude' takes two operands and compares their values numerically--- with their /sign/ ignored and assumed to be 0.------ If, without signs, the first operand is the smaller then the original first--- operand is returned (that is, with the original sign). If, without signs,--- the second operand is the smaller then the original second operand is--- returned. Otherwise the result is the same as from the 'min' operation.-minMagnitude :: (Precision p, Rounding r)-             => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)-minMagnitude x y = fst <$> minMax withoutSign x y---- | Ordering function for 'min', 'minMagnitude', 'max', and 'maxMagnitude':--- returns the original arguments as (smaller, larger) when the given function--- is applied to them.-minMax :: (Precision p, Rounding r)-       => (Decimal a b -> Decimal a b) -> Decimal a b -> Decimal a b-       -> Arith p r (Decimal a b, Decimal a b)-minMax _ x@Num{}  QNaN{} = return (x, x)-minMax _ x@Inf{}  QNaN{} = return (x, x)-minMax _  QNaN{} y@Num{} = return (y, y)-minMax _  QNaN{} y@Inf{} = return (y, y)--minMax f x y = do-  c <- f x `compare` f y-  return $ case c of-    Num { coefficient = 0 } -> case (sign x, sign y) of-      (Neg, Pos) -> (x, y)-      (Pos, Neg) -> (y, x)-      (Pos, Pos) -> case (x, y) of-        (Num { exponent = xe }, Num { exponent = ye }) | xe > ye -> (y, x)-        _ -> (x, y)-      (Neg, Neg) -> case (x, y) of-        (Num { exponent = xe }, Num { exponent = ye }) | xe < ye -> (y, x)-        _ -> (x, y)-    Num { sign = Pos } -> (y, x)-    Num { sign = Neg } -> (x, y)-    nan -> let nan' = coerce nan in (nan', nan')--withoutSign :: Decimal p r -> Decimal p r-withoutSign n = n { sign = Pos }---- | 'power' takes two operands, and raises a number (the left-hand operand)--- to a power (the right-hand operand). If either operand is a /special value/--- then the general rules apply, except in certain cases.-power :: (FinitePrecision p, Rounding r)-      => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-power x@Num { coefficient = 0 } y@Num{}-  | Number.isZero y     = invalidOperation qNaN-  | Number.isNegative y = return infinity { sign = powerSign x y }-  | otherwise           = return zero     { sign = powerSign x y }-power x@Num{} y@Num{} = case integralValue y of-  Just i  | i < 0               -> reciprocal x >>= \rx -> integralPower rx (-i)-          | otherwise           ->                         integralPower  x   i-  Nothing | Number.isPositive x -> ln x >>= multiply y >>= fmap coerce . exp-          | otherwise           -> invalidOperation qNaN-power x@Num{} y@Inf{}-  | Number.isPositive x = return $ case sign y of-      Pos -> infinity-      Neg -> zero-  | otherwise           = invalidOperation qNaN-power x@Inf{} y@Num{}-  | Number.isZero y     = return one-  | Number.isPositive y = return infinity { sign = powerSign x y }-  | otherwise           = return zero     { sign = powerSign x y }-power Inf{} Inf { sign = s }-  | s == Pos            = return infinity-  | otherwise           = return zero-power x@SNaN{} _        = invalidOperation x-power _        y@SNaN{} = invalidOperation y-power x@QNaN{} _        = return (coerce x)-power _        y@QNaN{} = return (coerce y)--powerSign :: Decimal a b -> Decimal c d -> Sign-powerSign x y-  | Number.isNegative x && fromMaybe False (odd <$> integralValue y) = Neg-  | otherwise                                                        = Pos--integralPower :: (Precision p, Rounding r)-              => Decimal a b -> Integer -> Arith p r (Decimal p r)-integralPower b e = integralPower' (return b) e one-  where integralPower' :: (Precision p, Rounding r)-                       => Arith p r (Decimal a b) -> Integer -> Decimal p r-                       -> Arith p r (Decimal p r)-        integralPower' _  0 r = return r-        integralPower' mb e r-          | odd e     = mb >>= \b -> multiply r b >>=-                        integralPower'              (multiply b b) e'-          | otherwise = integralPower' (mb >>= \b -> multiply b b) e' r-          where e' = e `div` 2--{- $doctest-power->>> op2 Op.power "2" "3"-8-->>> op2 Op.power "-2" "3"--8-->>> op2 Op.power "2" "-3"-0.125-->>> op2 Op.power "1.7" "8"-69.7575744-->>> op2 Op.power "10" "0.301029996"-2.00000000-->>> op2 Op.power "Infinity" "-1"-0-->>> op2 Op.power "Infinity" "0"-1-->>> op2 Op.power "Infinity" "1"-Infinity-->>> op2 Op.power "-Infinity" "-1"--0-->>> op2 Op.power "-Infinity" "0"-1-->>> op2 Op.power "-Infinity" "1"--Infinity-->>> op2 Op.power "-Infinity" "2"-Infinity-->>> op2 Op.power "0" "0"-NaN--}---- | 'quantize' takes two operands. If either operand is a /special value/--- then the general rules apply, except that if either operand is infinite and--- the other is finite an Invalid operation condition is raised and the result--- is NaN, or if both are infinite then the result is the first operand.------ Otherwise (both operands are finite), 'quantize' returns the number which--- is equal in value (except for any rounding) and sign to the first--- (left-hand) operand and which has an /exponent/ set to be equal to the--- exponent of the second (right-hand) operand.------ The /coefficient/ of the result is derived from that of the left-hand--- operand. It may be rounded using the current /rounding/ setting (if the--- /exponent/ is being increased), multiplied by a positive power of ten (if--- the /exponent/ is being decreased), or is unchanged (if the /exponent/ is--- already equal to that of the right-hand operand).------ Unlike other operations, if the length of the /coefficient/ after the--- quantize operation would be greater than /precision/ then an Invalid--- operation condition is raised. This guarantees that, unless there is an--- error condition, the /exponent/ of the result of a quantize is always equal--- to that of the right-hand operand.------ Also unlike other operations, quantize will never raise Underflow, even if--- the result is subnormal and inexact.-quantize :: (Precision p, Rounding r)-         => Decimal p r -> Decimal a b -> Arith p r (Decimal p r)-quantize x@Num { coefficient = xc, exponent = xe } Num { exponent = ye }-  | xe > ye   = result x { coefficient = xc * 10^(xe - ye), exponent = ye }-  | xe < ye   = rc >>= \c -> return x { coefficient = c, exponent = ye }-  | otherwise = return x--  where result :: Precision p => Decimal p r -> Arith p r (Decimal p r)-        result x = getPrecision >>= \p -> case numDigits (coefficient x) of-          n | maybe False (n >) p -> invalidOperation x-          _                       -> return x--        rc :: Rounding r => Arith p r Coefficient-        rc = let b      = 10^(ye - xe)-                 (q, r) = xc `quotRem` b-             in getRounder >>= \rounder -> return (rounder (sign x) r b q)--quantize Num{}      Inf{}    = invalidOperation qNaN-quantize Inf{}      Num{}    = invalidOperation qNaN-quantize n@Inf{}    Inf{}    = return n-quantize n@SNaN{}   _        = invalidOperation n-quantize _          n@SNaN{} = invalidOperation n-quantize n@QNaN{}   _        = return         n-quantize _          n@QNaN{} = return (coerce n)--{- $doctest-quantize->>> op2 Op.quantize "2.17" "0.001"-2.170-->>> op2 Op.quantize "2.17" "0.01"-2.17-->>> op2 Op.quantize "2.17" "0.1"-2.2-->>> op2 Op.quantize "2.17" "1e+0"-2-->>> op2 Op.quantize "2.17" "1e+1"-0E+1-->>> op2 Op.quantize "-Inf" "Infinity"--Infinity-->>> op2 Op.quantize "2" "Infinity"-NaN-->>> op2 Op.quantize "-0.1" "1"--0-->>> op2 Op.quantize "-0" "1e+5"--0E+5-->>> op2 Op.quantize "+35236450.6" "1e-2"-NaN-->>> op2 Op.quantize "-35236450.6" "1e-2"-NaN-->>> op2 Op.quantize "217" "1e-1"-217.0-->>> op2 Op.quantize "217" "1e+0"-217-->>> op2 Op.quantize "217" "1e+1"-2.2E+2-->>> op2 Op.quantize "217" "1e+2"-2E+2--}---- | 'reduce' takes one operand. It has the same semantics as the 'plus'--- operation, except that if the final result is finite it is reduced to its--- simplest form, with all trailing zeros removed and its sign preserved.-reduce :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)-reduce n = reduce' <$> plus n-  where reduce' n@Num { coefficient = c, exponent = e }-          | c == 0 =         n {                  exponent = 0     }-          | r == 0 = reduce' n { coefficient = q, exponent = e + 1 }-          where (q, r) = c `quotRem` 10-        reduce' n = n--{- $doctest-reduce->>> op1 Op.reduce "2.1"-2.1-->>> op1 Op.reduce "-2.0"--2-->>> op1 Op.reduce "1.200"-1.2-->>> op1 Op.reduce "-120"--1.2E+2-->>> op1 Op.reduce "120.00"-1.2E+2-->>> op1 Op.reduce "0.00"-0--}---- | 'squareRoot' takes one operand. If the operand is a /special value/ then--- the general rules apply.------ Otherwise, the ideal exponent of the result is defined to be half the--- exponent of the operand (rounded to an integer, towards −Infinity, if--- necessary) and then:------ If the operand is less than zero an Invalid operation condition is raised.------ If the operand is greater than zero, the result is the square root of the--- operand. If no rounding is necessary (the exact result requires /precision/--- digits or fewer) then the coefficient and exponent giving the correct value--- and with the exponent closest to the ideal exponent is used. If the result--- must be inexact, it is rounded using the /round-half-even/ algorithm and--- the coefficient will have exactly /precision/ digits (unless the result is--- subnormal), and the exponent will be set to maintain the correct value.------ Otherwise (the operand is equal to zero), the result will be the zero with--- the same sign as the operand and with the ideal exponent.-squareRoot :: FinitePrecision p-           => Decimal a b -> Arith p r (Decimal p RoundHalfEven)-squareRoot n@Num { sign = s, coefficient = c, exponent = e }-  | c == 0   = return n { exponent = idealExp }-  | s == Pos = subResult >>= subRounded >>= result--  where idealExp = e `div` 2 :: Exponent--        reduced :: Decimal p r -> Decimal p r-        reduced n@Num { coefficient = c, exponent = e }-          | e < idealExp = case bd of-              Just (b, (q, _)) -> n { coefficient = q, exponent = e + b }-              Nothing          -> n-          | e > idealExp = n { coefficient = c * 10^d, exponent = idealExp }-          where d  = Prelude.abs (e - idealExp)-                bd = find (\(_, (_, r)) -> r == 0) ds-                ds = map (\d -> (d, c `quotRem` (10^d))) [d, d - 1 .. 1]-        reduced n = n--        subResult :: FinitePrecision p-                  => Arith p r (Decimal (PPlus1 (PPlus1 p)) RoundHalfEven)-        subResult = subArith (babylonian approx)--        subRounded :: Precision p-                   => Decimal a b -> Arith p r (Decimal p RoundHalfEven)-        subRounded = subArith . roundDecimal--        exactness :: Decimal a b -> Arith p r (Decimal PInfinite RoundHalfEven)-        exactness r = subArith (multiply r r >>= compare n)--        result :: Decimal p a -> Arith p r (Decimal p a)-        result r = do-          e <- exactness r-          if Number.isZero e-            then return (reduced r)-            else let r' = coerce r-                 in coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')--        approx :: Decimal p r-        approx | even ae   = n { coefficient = 2, exponent =  ae      `quot` 2 }-               | otherwise = n { coefficient = 6, exponent = (ae - 1) `quot` 2 }-          where ae = adjustedExponent n--        babylonian :: FinitePrecision p => Decimal p RoundHalfEven-                   -> Arith p RoundHalfEven (Decimal p RoundHalfEven)-        babylonian x = do-          x' <- multiply oneHalf =<< add x =<< n `divide` x-          if x' == x then return x' else babylonian x'--squareRoot n@Inf { sign = Pos } = return (coerce n)-squareRoot n@QNaN{}             = return (coerce n)-squareRoot n                    = coerce <$> invalidOperation n--{- $doctest-squareRoot->>> op1 Op.squareRoot "0"-0-->>> op1 Op.squareRoot "-0"--0--This example appears to contradict the specification that the resulting-coefficient will have exactly /precision/ digits; awaiting clarification.-<<< op1 Op.squareRoot "0.39"-0.62449980-->>> op1 Op.squareRoot "100"-10-->>> op1 Op.squareRoot "1"-1-->>> op1 Op.squareRoot "1.0"-1.0-->>> op1 Op.squareRoot "1.00"-1.0-->>> op1 Op.squareRoot "7"-2.64575131-->>> op1 Op.squareRoot "10"-3.16227766--}---- $miscellaneous-operations------ This section describes miscellaneous operations on decimal numbers,--- including non-numeric comparisons, sign and other manipulations, and--- logical operations.------ The logical operations ('and', 'invert', 'or', and 'xor') take--- /logical operands/, which are finite numbers with a /sign/ of 0, an--- /exponent/ of 0, and a /coefficient/ whose digits must all be either 0 or--- 1. The length of the result will be at most /precision/ digits (all of--- which will be either 0 or 1); operands are truncated on the left or padded--- with zeros on the left as necessary. The result of a logical operation is--- never rounded and the only /flag/ that might be set is /invalid-operation/--- (set if an operand is not a valid logical operand).------ Some operations return a boolean value that is described as 0 or 1 in the--- documentation below. For reasons of efficiency, and as permitted by the--- /General Decimal Arithmetic Specification/, these operations return a--- 'Bool' in this implementation, but can be converted to 'Decimal' via--- 'fromBool'.--data Logical = Logical { bits :: Integer, bitLength :: Int }--toLogical :: Decimal a b -> Maybe Logical-toLogical Num { sign = Pos, coefficient = c, exponent = 0 } =-  getBits c Logical { bits = zeroBits, bitLength = 0 }--  where getBits :: Coefficient -> Logical -> Maybe Logical-        getBits 0 g = return g-        getBits c g@Logical { bits = b, bitLength = l } = case d of-          0 -> getBits c' g {                    bitLength = succ l }-          1 -> getBits c' g { bits = setBit b l, bitLength = succ l }-          _ -> Nothing-          where (c', d) = c `quotRem` 10--toLogical _ = Nothing--fromLogical :: Logical -> Decimal a b-fromLogical Logical { bits = b, bitLength = l } =-  Num { sign = Pos, coefficient = fromBits 0 1 0, exponent = 0 }--  where fromBits :: Int -> Coefficient -> Coefficient -> Coefficient-        fromBits i r c-          | i == l      = c-          | testBit b i = fromBits i' r' (c + r)-          | otherwise   = fromBits i' r'  c-          where i' = succ i-                r' = r * 10---- | 'and' is a logical operation which takes two logical operands. The result--- is the digit-wise /and/ of the two operands; each digit of the result is--- the logical and of the corresponding digits of the operands, aligned at the--- least-significant digit. A result digit is 1 if both of the corresponding--- operand digits are 1; otherwise it is 0.-and :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-and x y = case (toLogical x, toLogical y) of-  (Just lx, Just ly) -> getPrecision >>= \p ->-    let m = Prelude.min (bitLength lx) (bitLength ly)-        z = Logical { bits = bits lx .&. bits ly-                    , bitLength = maybe m (Prelude.min m) p }-    in return (fromLogical z)-  _ -> invalidOperation qNaN--{- $doctest-and->>> op2 Op.and "0" "0"-0-->>> op2 Op.and "0" "1"-0-->>> op2 Op.and "1" "0"-0-->>> op2 Op.and "1" "1"-1-->>> op2 Op.and "1100" "1010"-1000-->>> op2 Op.and "1111" "10"-10--}---- | 'or' is a logical operation which takes two logical operands. The result--- is the digit-wise /inclusive or/ of the two operands; each digit of the--- result is the logical or of the corresponding digits of the operands,--- aligned at the least-significant digit. A result digit is 1 if either or--- both of the corresponding operand digits is 1; otherwise it is 0.-or :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-or x y = case (toLogical x, toLogical y) of-  (Just lx, Just ly) -> getPrecision >>= \p ->-    let m = Prelude.max (bitLength lx) (bitLength ly)-        z = Logical { bits = bits lx .|. bits ly-                    , bitLength = maybe m (Prelude.min m) p }-    in return (fromLogical z)-  _ -> invalidOperation qNaN--{- $doctest-or->>> op2 Op.or "0" "0"-0-->>> op2 Op.or "0" "1"-1-->>> op2 Op.or "1" "0"-1-->>> op2 Op.or "1" "1"-1-->>> op2 Op.or "1100" "1010"-1110-->>> op2 Op.or "1110" "10"-1110--}---- | 'xor' is a logical operation which takes two logical operands. The result--- is the digit-wise /exclusive or/ of the two operands; each digit of the--- result is the logical exclusive-or of the corresponding digits of the--- operands, aligned at the least-significant digit. A result digit is 1 if--- one of the corresponding operand digits is 1 and the other is 0; otherwise--- it is 0.-xor :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-xor x y = case (toLogical x, toLogical y) of-  (Just lx, Just ly) -> getPrecision >>= \p ->-    let m = Prelude.max (bitLength lx) (bitLength ly)-        z = Logical { bits = bits lx `Bits.xor` bits ly-                    , bitLength = maybe m (Prelude.min m) p }-    in return (fromLogical z)-  _ -> invalidOperation qNaN--{- $doctest-xor->>> op2 Op.xor "0" "0"-0-->>> op2 Op.xor "0" "1"-1-->>> op2 Op.xor "1" "0"-1-->>> op2 Op.xor "1" "1"-0-->>> op2 Op.xor "1100" "1010"-110-->>> op2 Op.xor "1111" "10"-1101--}---- | 'invert' is a logical operation which takes one logical operand. The--- result is the digit-wise /inversion/ of the operand; each digit of the--- result is the inverse of the corresponding digit of the operand. A result--- digit is 1 if the corresponding operand digit is 0; otherwise it is 0.-invert :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p r)-invert x = case toLogical x of-  Just lx -> getPrecision >>= \(Just p) ->-    let z = Logical { bits = complement (bits lx), bitLength = p }-    in return (fromLogical z)-  _ -> invalidOperation qNaN--{- $doctest-invert->>> op1 Op.invert "0"-111111111-->>> op1 Op.invert "1"-111111110-->>> op1 Op.invert "111111111"-0-->>> op1 Op.invert "101010101"-10101010--}---- | 'canonical' takes one operand. The result has the same value as the--- operand but always uses a /canonical/ encoding. The definition of--- /canonical/ is implementation-defined; if more than one internal encoding--- for a given NaN, Infinity, or finite number is possible then one--- “preferred” encoding is deemed canonical. This operation then returns the--- value using that preferred encoding.------ If all possible operands have just one internal encoding each, then--- 'canonical' always returns the operand unchanged (that is, it has the same--- effect as 'copy'). This operation is unaffected by context and is quiet —--- no /flags/ are changed in the context.-canonical :: Decimal a b -> Arith p r (Decimal a b)-canonical = return--{- $doctest-canonical->>> op1 Op.canonical "2.50"-2.50--}---- | 'class_' takes one operand. The result is an indication of the /class/ of--- the operand, where the class is one of ten possibilities, corresponding to--- one of the strings @"sNaN"@ (signaling NaN), @\"NaN"@ (quiet NaN),--- @"-Infinity"@ (negative infinity), @"-Normal"@ (negative normal finite--- number), @"-Subnormal"@ (negative subnormal finite number), @"-Zero"@--- (negative zero), @"+Zero"@ (non-negative zero), @"+Subnormal"@ (positive--- subnormal finite number), @"+Normal"@ (positive normal finite number), or--- @"+Infinity"@ (positive infinity). This operation is quiet; no /flags/ are--- changed in the context.------ Note that unlike the special values in the model, the sign of any NaN is--- ignored in the classification, as required by IEEE 754.-class_ :: Precision a => Decimal a b -> Arith p r Class-class_ n = return $ case n of-  Num {} | Number.isZero n      -> NumberClass (sign n) ZeroClass-         | Number.isSubnormal n -> NumberClass (sign n) SubnormalClass-         | otherwise            -> NumberClass (sign n) NormalClass-  Inf {}                        -> NumberClass (sign n) InfinityClass-  QNaN{}                        -> NaNClass QNaNClass-  SNaN{}                        -> NaNClass SNaNClass--data Class = NumberClass Sign NumberClass -- ^ Number (finite or infinite)-           | NaNClass NaNClass            -- ^ Not a number (quiet or signaling)-           deriving Eq--data NumberClass = ZeroClass       -- ^ Zero-                 | SubnormalClass  -- ^ Subnormal finite number-                 | NormalClass     -- ^ Normal finite number-                 | InfinityClass   -- ^ Infinity-                 deriving Eq--data NaNClass = QNaNClass  -- ^ Not a number (quiet)-              | SNaNClass  -- ^ Not a number (signaling)-              deriving Eq--instance Show Class where-  show c = case c of-    NumberClass s nc   -> signChar s : showNumberClass nc-    NaNClass QNaNClass ->       nan-    NaNClass SNaNClass -> 's' : nan--    where signChar :: Sign -> Char-          signChar Pos = '+'-          signChar Neg = '-'--          showNumberClass :: NumberClass -> String-          showNumberClass s = case s of-            ZeroClass      -> "Zero"-            SubnormalClass -> "Subnormal"-            NormalClass    -> "Normal"-            InfinityClass  -> "Infinity"--          nan :: String-          nan = "NaN"--{- $doctest-class_->>> op1 Op.class_ "Infinity"-+Infinity-->>> op1 Op.class_ "1E-10"-+Normal-->>> op1 Op.class_ "2.50"-+Normal-->>> op1 Op.class_ "0.1E-999"-+Subnormal-->>> op1 Op.class_ "0"-+Zero-->>> op1 Op.class_ "-0"--Zero-->>> op1 Op.class_ "-0.1E-999"--Subnormal-->>> op1 Op.class_ "-1E-10"--Normal-->>> op1 Op.class_ "-2.50"--Normal-->>> op1 Op.class_ "-Infinity"--Infinity-->>> op1 Op.class_ "NaN"-NaN-->>> op1 Op.class_ "-NaN"-NaN-->>> op1 Op.class_ "sNaN"-sNaN--}---- | 'copy' takes one operand. The result is a copy of the operand. This--- operation is unaffected by context and is quiet — no /flags/ are changed in--- the context.-copy :: Decimal a b -> Arith p r (Decimal a b)-copy = return--{- $doctest-copy->>> op1 Op.copy "2.1"-2.1-->>> op1 Op.copy "-1.00"--1.00--}---- | 'copyAbs' takes one operand. The result is a copy of the operand with the--- /sign/ set to 0. Unlike the 'abs' operation, this operation is unaffected--- by context and is quiet — no /flags/ are changed in the context.-copyAbs :: Decimal a b -> Arith p r (Decimal a b)-copyAbs n = return n { sign = Pos }--{- $doctest-copyAbs->>> op1 Op.copyAbs "2.1"-2.1-->>> op1 Op.copyAbs "-100"-100--}---- | 'copyNegate' takes one operand. The result is a copy of the operand with--- the /sign/ inverted (a /sign/ of 0 becomes 1 and vice versa). Unlike the--- 'minus' operation, this operation is unaffected by context and is quiet —--- no /flags/ are changed in the context.-copyNegate :: Decimal a b -> Arith p r (Decimal a b)-copyNegate n = return n { sign = negateSign (sign n) }--{- $doctest-copyNegate->>> op1 Op.copyNegate "101.5"--101.5-->>> op1 Op.copyNegate "-101.5"-101.5--}---- | 'copySign' takes two operands. The result is a copy of the first operand--- with the /sign/ set to be the same as the /sign/ of the second--- operand. This operation is unaffected by context and is quiet — no /flags/--- are changed in the context.-copySign :: Decimal a b -> Decimal c d -> Arith p r (Decimal a b)-copySign n m = return n { sign = sign m }--{- $doctest-copySign->>> op2 Op.copySign  "1.50"  "7.33"-1.50-->>> op2 Op.copySign "-1.50"  "7.33"-1.50-->>> op2 Op.copySign  "1.50" "-7.33"--1.50-->>> op2 Op.copySign "-1.50" "-7.33"--1.50--}---- | 'isCanonical' takes one operand. The result is 1 if the operand is--- /canonical/; otherwise it is 0. The definition of /canonical/ is--- implementation-defined; if more than one internal encoding for a given NaN,--- Infinity, or finite number is possible then one “preferred” encoding is--- deemed canonical. This operation then tests whether the internal encoding--- is that preferred encoding.------ If all possible operands have just one internal encoding each, then--- 'isCanonical' always returns 1. This operation is unaffected by context and--- is quiet — no /flags/ are changed in the context.-isCanonical :: Decimal a b -> Arith p r Bool-isCanonical _ = return True--{- $doctest-isCanonical->>> fromBool $ op1 Op.isCanonical "2.50"-1--}---- | 'isFinite' takes one operand. The result is 1 if the operand is neither--- infinite nor a NaN (that is, it is a normal number, a subnormal number, or--- a zero); otherwise it is 0. This operation is unaffected by context and is--- quiet — no /flags/ are changed in the context.-isFinite :: Decimal a b -> Arith p r Bool-isFinite = return . Number.isFinite--{- $doctest-isFinite->>> fromBool $ op1 Op.isFinite "2.50"-1-->>> fromBool $ op1 Op.isFinite "-0.3"-1-->>> fromBool $ op1 Op.isFinite "0"-1-->>> fromBool $ op1 Op.isFinite "Inf"-0-->>> fromBool $ op1 Op.isFinite "NaN"-0--}---- | 'isInfinite' takes one operand. The result is 1 if the operand is an--- Infinity; otherwise it is 0. This operation is unaffected by context and is--- quiet — no /flags/ are changed in the context.-isInfinite :: Decimal a b -> Arith p r Bool-isInfinite n = return $ case n of-  Inf{} -> True-  _     -> False--{- $doctest-isInfinite->>> fromBool $ op1 Op.isInfinite "2.50"-0-->>> fromBool $ op1 Op.isInfinite "-Inf"-1-->>> fromBool $ op1 Op.isInfinite "NaN"-0--}---- | 'isNaN' takes one operand. The result is 1 if the operand is a NaN (quiet--- or signaling); otherwise it is 0. This operation is unaffected by context--- and is quiet — no /flags/ are changed in the context.-isNaN :: Decimal a b -> Arith p r Bool-isNaN n = return $ case n of-  QNaN{} -> True-  SNaN{} -> True-  _      -> False--{- $doctest-isNaN->>> fromBool $ op1 Op.isNaN "2.50"-0-->>> fromBool $ op1 Op.isNaN "NaN"-1-->>> fromBool $ op1 Op.isNaN "-sNaN"-1--}---- | 'isNormal' takes one operand. The result is 1 if the operand is a--- positive or negative /normal number/; otherwise it is 0. This operation is--- quiet; no /flags/ are changed in the context.-isNormal :: Precision a => Decimal a b -> Arith p r Bool-isNormal = return . Number.isNormal--{- $doctest-isNormal->>> fromBool $ op1 Op.isNormal "2.50"-1-->>> fromBool $ op1 Op.isNormal "0.1E-999"-0-->>> fromBool $ op1 Op.isNormal "0.00"-0-->>> fromBool $ op1 Op.isNormal "-Inf"-0-->>> fromBool $ op1 Op.isNormal "NaN"-0--}---- | 'isQNaN' takes one operand. The result is 1 if the operand is a quiet--- NaN; otherwise it is 0. This operation is unaffected by context and is--- quiet — no /flags/ are changed in the context.-isQNaN :: Decimal a b -> Arith p r Bool-isQNaN n = return $ case n of-  QNaN{} -> True-  _      -> False--{- $doctest-isQNaN->>> fromBool $ op1 Op.isQNaN "2.50"-0-->>> fromBool $ op1 Op.isQNaN "NaN"-1-->>> fromBool $ op1 Op.isQNaN "sNaN"-0--}---- | 'isSigned' takes one operand. The result is 1 if the /sign/ of the--- operand is 1; otherwise it is 0. This operation is unaffected by context--- and is quiet — no /flags/ are changed in the context.-isSigned :: Decimal a b -> Arith p r Bool-isSigned = return . Number.isNegative--{- $doctest-isSigned->>> fromBool $ op1 Op.isSigned "2.50"-0-->>> fromBool $ op1 Op.isSigned "-12"-1-->>> fromBool $ op1 Op.isSigned "-0"-1--}---- | 'isSNaN' takes one operand. The result is 1 if the operand is a signaling--- NaN; otherwise it is 0. This operation is unaffected by context and is--- quiet — no /flags/ are changed in the context.-isSNaN :: Decimal a b -> Arith p r Bool-isSNaN n = return $ case n of-  SNaN{} -> True-  _      -> False--{- $doctest-isSNaN->>> fromBool $ op1 Op.isSNaN "2.50"-0-->>> fromBool $ op1 Op.isSNaN "NaN"-0-->>> fromBool $ op1 Op.isSNaN "sNaN"-1--}---- | 'isSubnormal' takes one operand. The result is 1 if the operand is a--- positive or negative /subnormal number/; otherwise it is 0. This operation--- is quiet; no /flags/ are changed in the context.-isSubnormal :: Precision a => Decimal a b -> Arith p r Bool-isSubnormal = return . Number.isSubnormal--{- $doctest-isSubnormal->>> fromBool $ op1 Op.isSubnormal "2.50"-0-->>> fromBool $ op1 Op.isSubnormal "0.1E-999"-1-->>> fromBool $ op1 Op.isSubnormal "0.00"-0-->>> fromBool $ op1 Op.isSubnormal "-Inf"-0-->>> fromBool $ op1 Op.isSubnormal "NaN"-0--}---- | 'isZero' takes one operand. The result is 1 if the operand is a zero;--- otherwise it is 0. This operation is unaffected by context and is quiet —--- no /flags/ are changed in the context.-isZero :: Decimal a b -> Arith p r Bool-isZero = return . Number.isZero--{- $doctest-isZero->>> fromBool $ op1 Op.isZero "0"-1-->>> fromBool $ op1 Op.isZero "2.50"-0-->>> fromBool $ op1 Op.isZero "-0E+2"-1--}---- | 'logb' takes one operand. If the operand is a NaN then the general--- arithmetic rules apply. If the operand is infinite then +Infinity is--- returned. If the operand is a zero, then −Infinity is returned and the--- Division by zero exceptional condition is raised.------ Otherwise, the result is the integer which is the exponent of the magnitude--- of the most significant digit of the operand (as though the operand were--- truncated to a single digit while maintaining the value of that digit and--- without limiting the resulting exponent). All results are exact unless an--- integer result does not fit in the available /precision/.-logb :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)-logb Num { coefficient = c, exponent = e }-  | c == 0    = raiseSignal DivisionByZero Inf { sign = Neg }-  | otherwise = roundDecimal (fromInteger r :: Decimal PInfinite RoundHalfEven)-  where r = fromIntegral (numDigits c) - 1 + fromIntegral e :: Integer-logb Inf{} = return Inf { sign = Pos }-logb n@QNaN{} = return (coerce n)-logb n@SNaN{} = invalidOperation n--{- $doctest-logb->>> op1 Op.logb "250"-2-->>> op1 Op.logb "2.50"-0-->>> op1 Op.logb "0.03"--2-->>> op1 Op.logb "0"--Infinity--}---- | 'radix' takes no operands. The result is the radix (base) in which--- arithmetic is effected; for this specification the result will have the--- value 10.-radix :: Precision p => Arith p r (Decimal p r)-radix = return radix'-  where radix' = case precision radix' of-          Just 1 -> one { exponent    =  1 }-          _      -> one { coefficient = 10 }--{- $doctest-radix->>> op0 Op.radix-10--}---- | 'sameQuantum' takes two operands, and returns 1 if the two operands have--- the same /exponent/ or 0 otherwise. The result is never affected by either--- the sign or the coefficient of either operand.------ If either operand is a /special value/, 1 is returned only if both operands--- are NaNs or both are infinities.------ 'sameQuantum' does not change any /flags/ in the context.-sameQuantum :: Decimal a b -> Decimal c d -> Arith p r Bool-sameQuantum Num { exponent = e1 } Num { exponent = e2 }-  | e1 == e2  = return True-  | otherwise = return False-sameQuantum Inf {} Inf {} = return True-sameQuantum QNaN{} QNaN{} = return True-sameQuantum SNaN{} SNaN{} = return True-sameQuantum QNaN{} SNaN{} = return True-sameQuantum SNaN{} QNaN{} = return True-sameQuantum _      _      = return False--{- $doctest-sameQuantum->>> fromBool $ op2 Op.sameQuantum "2.17" "0.001"-0-->>> fromBool $ op2 Op.sameQuantum "2.17" "0.01"-1-->>> fromBool $ op2 Op.sameQuantum "2.17" "0.1"-0-->>> fromBool $ op2 Op.sameQuantum "2.17" "1"-0-->>> fromBool $ op2 Op.sameQuantum "Inf" "-Inf"-1-->>> fromBool $ op2 Op.sameQuantum "NaN" "NaN"-1--}---- | 'shift' takes two operands. The second operand must be an integer (with--- an /exponent/ of 0) in the range /−precision/ through /precision/. If the--- first operand is a NaN then the general arithmetic rules apply, and if it--- is infinite then the result is the Infinity unchanged.------ Otherwise (the first operand is finite) the result has the same /sign/ and--- /exponent/ as the first operand, and a /coefficient/ which is a shifted--- copy of the digits in the coefficient of the first operand. The number of--- places to shift is taken from the absolute value of the second operand,--- with the shift being to the left if the second operand is positive or to--- the right otherwise. Digits shifted into the coefficient are zeros.------ The only /flag/ that might be set is /invalid-operation/ (set if the first--- operand is an sNaN or the second is not valid).------ The 'rotate' operation can be used to rotate rather than shift a--- coefficient.-shift :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-shift n@Num { coefficient = c } s@Num { sign = d, coefficient = sc }-  | validShift z s = return z-  where z = case precision z of-          Just p  -> y { coefficient = coefficient y `rem` 10 ^ p }-          Nothing -> y-        y = case d of-          Pos -> n { coefficient =  c  *     10 ^ sc }-          Neg -> n { coefficient =  c `quot` 10 ^ sc }-shift n@Inf{}  s | validShift z s = return z where z = coerce n-shift n@QNaN{} s | validShift z s = return z where z = coerce n-shift n        _                  = invalidOperation n--validShift :: Precision p => p -> Decimal a b -> Bool-validShift px Num { coefficient = c, exponent = 0 } =-  let p = fromIntegral <$> precision px in maybe True (c <=) p-validShift _ _ = False--{- $doctest-shift->>> op2 Op.shift "34" "8"-400000000-->>> op2 Op.shift "12" "9"-0-->>> op2 Op.shift "123456789" "-2"-1234567-->>> op2 Op.shift "123456789" "0"-123456789-->>> op2 Op.shift "123456789" "+2"-345678900--}---- | 'rotate' takes two operands. The second operand must be an integer (with--- an /exponent/ of 0) in the range /−precision/ through /precision/. If the--- first operand is a NaN then the general arithmetic rules apply, and if it--- is infinite then the result is the Infinity unchanged.------ Otherwise (the first operand is finite) the result has the same /sign/ and--- /exponent/ as the first operand, and a /coefficient/ which is a rotated--- copy of the digits in the coefficient of the first operand. The number of--- places of rotation is taken from the absolute value of the second operand,--- with the rotation being to the left if the second operand is positive or to--- the right otherwise.------ If the coefficient of the first operand has fewer than /precision/ digits,--- it is treated as though it were padded on the left with zeros to length--- /precision/ before the rotation. Similarly, if the coefficient of the first--- operand has more than /precision/ digits, it is truncated on the left--- before use.------ The only /flag/ that might be set is /invalid-operation/ (set if the first--- operand is an sNaN or the second is not valid).------ The 'shift' operation can be used to shift rather than rotate a--- coefficient.-rotate :: FinitePrecision p-       => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-rotate n@Num { coefficient = c } s@Num { sign = d, coefficient = sc }-  | validShift z s = return z-  where z = n { coefficient = rc * b + (lc `rem` b) }-        (lc, rc) = c `quotRem` b'-        (b , b') = case d of-          Pos -> (10^sc , 10^sc')-          Neg -> (10^sc', 10^sc )-        Just p = precision z-        sc'    = p - fromIntegral sc-rotate n@Inf{}  s | validShift z s = return z where z = coerce n-rotate n@QNaN{} s | validShift z s = return z where z = coerce n-rotate n        _                  = invalidOperation n--{- $doctest-rotate->>> op2 Op.rotate "34" "8"-400000003-->>> op2 Op.rotate "12" "9"-12-->>> op2 Op.rotate "123456789" "-2"-891234567-->>> op2 Op.rotate "123456789" "0"-123456789-->>> op2 Op.rotate "123456789" "+2"-345678912--}+         -- ** General arithmetic+         add+       , subtract+       , multiply+       , divide+         -- divideInteger+         -- remainder+         -- remainderNear+       , power+       , squareRoot+       , fusedMultiplyAdd++         -- ** Exponential and logarithmic+       , exp+       , ln+       , log10++         -- ** Unary sign+       , plus+       , minus+       , abs++         -- ** Comparison+       , compare+       , compareSignal++       , min+       , max+       , minMagnitude+       , maxMagnitude++         -- ** Rounding and quantization++       , roundToIntegralValue+       , roundToIntegralExact+       , quantize+       , reduce++         -- nextMinus+         -- nextPlus+         -- nextToward++         -- * Miscellaneous operations+         -- $miscellaneous-operations++         -- ** Logic and shifting+         -- $logical-operations+       , and+       , or+       , xor+       , invert++       , shift+       , rotate++         -- ** Predicates+       , isZero+       , isSigned+       , isFinite+       , isInfinite+       , isNormal+       , isSubnormal+       , isNaN+       , isQNaN+       , isSNaN+       , isCanonical++         -- ** Total comparison and classification+       , compareTotal+       , compareTotalMagnitude++       , class', Class(..), Sign(..), NumberClass(..), NaNClass(..)++         -- ** Exponent manipulation+       , logb+       , scaleb+       , sameQuantum+       , radix++         -- ** Sign manipulation and conversion+       , copyAbs+       , copyNegate+       , copySign+       , copy++       , canonical+       ) where++import Prelude hiding (abs, and, compare, exp, exponent, isInfinite, isNaN,+                       max, min, or, subtract)+import qualified Prelude++import Control.Monad (join)+import Data.Bits (complement, setBit, testBit, zeroBits, (.&.), (.|.))+import Data.Coerce (coerce)+import Data.List (find)+import Data.Maybe (fromMaybe)++import qualified Data.Bits as Bits++import Numeric.Decimal.Arithmetic+import Numeric.Decimal.Exception+import Numeric.Decimal.Number hiding (isFinite, isNormal, isSubnormal, isZero)+import Numeric.Decimal.Precision+import Numeric.Decimal.Rounding++import qualified Numeric.Decimal.Number as Number++finitePrecision :: FinitePrecision p => Decimal p r -> Int+finitePrecision n = let Just p = precision n in p++roundingAlg :: Rounding r => Arith p r a -> RoundingAlgorithm+roundingAlg = rounding . arithRounding+  where arithRounding :: Arith p r a -> r+        arithRounding = undefined++result :: (Precision p, Rounding r) => Decimal p r -> Arith p r (Decimal p r)+result = roundDecimal  -- ...+--  | maybe False (numDigits c >) (precision r) = undefined++generalRules1 :: Decimal a b -> Arith p r (Decimal p r)+generalRules1 nan@NaN { signaling = False } = return (coerce nan)+generalRules1 x                             = invalidOperation x++generalRules2 :: Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+generalRules2 nan@NaN { signaling = True } _ = invalidOperation nan+generalRules2 _ nan@NaN { signaling = True } = invalidOperation nan+generalRules2 nan@NaN{} _                    = return (coerce nan)+generalRules2 _         nan@NaN{}            = return (coerce nan)+generalRules2 x         _                    = invalidOperation x++-- $arithmetic-operations+--+-- This section describes the arithmetic operations on, and some other+-- functions of, numbers, including subnormal numbers, negative zeros, and+-- special values (see also IEEE 754 §5 and §6).++-- | 'add' takes two operands. If either operand is a /special value/ then the+-- general rules apply.+--+-- Otherwise, the operands are added.+--+-- The result is then rounded to /precision/ digits if necessary, counting+-- from the most significant digit of the result.+add :: (Precision p, Rounding r)+    => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+add Num { sign = xs, coefficient = xc, exponent = xe }+    Num { sign = ys, coefficient = yc, exponent = ye } = sum++  where sum = result Num { sign = rs, coefficient = rc, exponent = re }+        rs | rc /= 0                       = if xac > yac then xs else ys+           | xs == Neg && ys == Neg        = Neg+           | xs /= ys &&+             roundingAlg sum == RoundFloor = Neg+           | otherwise                     = Pos+        rc | xs == ys  = xac + yac+           | xac > yac = xac - yac+           | otherwise = yac - xac+        re = Prelude.min xe ye+        (xac, yac) | xe == ye  = (xc, yc)+                   | xe >  ye  = (xc * 10^n, yc)+                   | otherwise = (xc, yc * 10^n)+          where n = Prelude.abs (xe - ye)++add inf@Inf{} Num{} = return (coerce inf)+add Num{} inf@Inf{} = return (coerce inf)+add inf@Inf { sign = xs } Inf { sign = ys }+  | xs == ys  = return (coerce inf)+  | otherwise = invalidOperation qNaN+add x y = generalRules2 x y++-- | 'subtract' takes two operands. If either operand is a /special value/+-- then the general rules apply.+--+-- Otherwise, the operands are added after inverting the /sign/ used for the+-- second operand.+--+-- The result is then rounded to /precision/ digits if necessary, counting+-- from the most significant digit of the result.+subtract :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+subtract x@Num{} y@Num{} = add x (flipSign y)+subtract x@Inf{} y@Num{} = add x (flipSign y)+subtract x@Num{} y@Inf{} = add x (flipSign y)+subtract x@Inf{} y@Inf{} = add x (flipSign y)+subtract x y = generalRules2 x y++-- | 'minus' takes one operand, and corresponds to the prefix minus operator+-- in programming languages.+--+-- Note that the result of this operation is affected by context and may set+-- /flags/. The 'copyNegate' operation may be used instead of 'minus' if this+-- is not desired.+minus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+minus x = zero { exponent = exponent x } `subtract` x++-- | 'plus' takes one operand, and corresponds to the prefix plus operator in+-- programming languages.+--+-- Note that the result of this operation is affected by context and may set+-- /flags/.+plus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+plus x = zero { exponent = exponent x } `add` x++-- | 'multiply' takes two operands. If either operand is a /special value/+-- then the general rules apply. Otherwise, the operands are multiplied+-- together (“long multiplication”), resulting in a number which may be as+-- long as the sum of the lengths of the two operands.+--+-- The result is then rounded to /precision/ digits if necessary, counting+-- from the most significant digit of the result.+multiply :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+multiply Num { sign = xs, coefficient = xc, exponent = xe }+         Num { sign = ys, coefficient = yc, exponent = ye } = result rn++  where rn = Num { sign = rs, coefficient = rc, exponent = re }+        rs = xorSigns xs ys+        rc = xc * yc+        re = xe + ye++multiply Inf { sign = xs } Inf { sign = ys } =+  return Inf { sign = xorSigns xs ys }+multiply Inf { sign = xs } Num { sign = ys, coefficient = yc }+  | yc == 0   = invalidOperation qNaN+  | otherwise = return Inf { sign = xorSigns xs ys }+multiply Num { sign = xs, coefficient = xc } Inf { sign = ys }+  | xc == 0   = invalidOperation qNaN+  | otherwise = return Inf { sign = xorSigns xs ys }+multiply x y = generalRules2 x y++-- | 'exp' takes one operand. If the operand is a NaN then the general rules+-- for special values apply.+--+-- Otherwise, the result is /e/ raised to the power of the operand, with the+-- following cases:+--+-- * If the operand is −Infinity, the result is 0 and exact.+--+-- * If the operand is a zero, the result is 1 and exact.+--+-- * If the operand is +Infinity, the result is +Infinity and exact.+--+-- * Otherwise the result is inexact and will be rounded using the+-- 'RoundHalfEven' algorithm. The coefficient will have exactly /precision/+-- digits (unless the result is subnormal). These inexact results should be+-- correctly rounded, but may be up to 1 ulp (unit in last place) in error.+exp :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p RoundHalfEven)+exp x@Num { sign = s, coefficient = c }+  | c == 0    = return one+  | s == Neg  = subArith (maclaurin x { sign = Pos } >>= reciprocal) >>=+                subRounded >>= result+  | otherwise = subArith (maclaurin x) >>= subRounded >>= result++  where multiplyExact :: Decimal a b -> Decimal c d+                      -> Arith PInfinite RoundHalfEven+                         (Decimal PInfinite RoundHalfEven)+        multiplyExact = multiply++        maclaurin :: FinitePrecision p => Decimal a b+                  -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+        maclaurin x+          | adjustedExponent x >= 0 = subArith (subMaclaurin x) >>= subRounded+          | otherwise = sum one one one one+          where sum :: FinitePrecision p+                    => Decimal p RoundHalfEven+                    -> Decimal PInfinite RoundHalfEven+                    -> Decimal PInfinite RoundHalfEven+                    -> Decimal PInfinite RoundHalfEven+                    -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+                sum s num den n = do+                  num' <- subArith (multiplyExact num x)+                  den' <- subArith (multiplyExact den n)+                  s' <- add s =<< divide num' den'+                  if s' == s then return s'+                    else sum s' num' den' =<< subArith (add n one)++        subMaclaurin :: FinitePrecision p => Decimal a b+                     -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+        subMaclaurin x = subArith (multiplyExact x oneHalf) >>= maclaurin >>=+          \r -> multiply r r++        subRounded :: Precision p+                   => Decimal (PPlus1 (PPlus1 p)) a+                   -> Arith p r (Decimal p RoundHalfEven)+        subRounded = subArith . roundDecimal++        result :: Decimal p a -> Arith p r (Decimal p a)+        result r = coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')+          where r' = coerce r++exp n@Inf { sign = s }+  | s == Pos  = return (coerce n)+  | otherwise = return zero+exp x = coerce <$> generalRules1 x++-- | 'fusedMultiplyAdd' takes three operands; the first two are multiplied+-- together, using 'multiply', with sufficient precision and exponent range+-- that the result is exact and unrounded. No /flags/ are set by the+-- multiplication unless one of the first two operands is a signaling NaN or+-- one is a zero and the other is an infinity.+--+-- Unless the multiplication failed, the third operand is then added to the+-- result of that multiplication, using 'add', under the current context.+--+-- In other words, @fusedMultiplyAdd x y z@ delivers a result which is @(x ×+-- y) + z@ with only the one, final, rounding.+fusedMultiplyAdd :: (Precision p, Rounding r)+                 => Decimal a b -> Decimal c d -> Decimal e f+                 -> Arith p r (Decimal p r)+fusedMultiplyAdd x y z =+  either raise (return . coerce) (exactMult x y) >>= add z++  where exactMult :: Rounding r => Decimal a b -> Decimal c d+                  -> Either (Exception PInfinite r) (Decimal PInfinite r)+        exactMult x y = evalArith (multiply x y) newContext++        raise :: Exception a r -> Arith p r (Decimal p r)+        raise e = raiseSignal (exceptionSignal e) (coerce $ exceptionResult e)++-- | 'ln' takes one operand. If the operand is a NaN then the general rules+-- for special values apply.+--+-- Otherwise, the operand must be a zero or positive, and the result is the+-- natural (base /e/) logarithm of the operand, with the following cases:+--+-- * If the operand is a zero, the result is −Infinity and exact.+--+-- * If the operand is +Infinity, the result is +Infinity and exact.+--+-- * If the operand equals one, the result is 0 and exact.+--+-- * Otherwise the result is inexact and will be rounded using the+-- 'RoundHalfEven' algorithm. The coefficient will have exactly /precision/+-- digits (unless the result is subnormal). These inexact results should be+-- correctly rounded, but may be up to 1 ulp (unit in last place) in error.+ln :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p RoundHalfEven)+ln x@Num { sign = s, coefficient = c, exponent = e }+  | c == 0   = return infinity { sign = Neg }+  | s == Pos = if e <= 0 && c == 10^(-e) then return zero+               else subArith (subLn x) >>= subRounded >>= result++  where subLn :: FinitePrecision p => Decimal a b+              -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+        subLn x = do+          let fe = fromIntegral (-(numDigits c - 1)) :: Exponent+              r  = fromIntegral (e - fe) :: Decimal PInfinite RoundHalfEven+          lnf <- taylorLn x { exponent = fe }+          add lnf =<< multiply r =<< ln10++        subRounded :: Precision p => Decimal (PPlus1 (PPlus1 p)) a+                   -> Arith p r (Decimal p RoundHalfEven)+        subRounded = subArith . roundDecimal++        result :: Decimal p a -> Arith p r (Decimal p a)+        result r = coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')+          where r' = coerce r++ln n@Inf { sign = Pos } = return (coerce n)+ln x = coerce <$> generalRules1 x++taylorLn :: FinitePrecision p => Decimal a b+         -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+taylorLn x = do+  num <- x `subtract` one+  den <- x `add`      one+  multiply two =<< sum =<< num `divide` den++    where sum :: FinitePrecision p => Decimal p RoundHalfEven+              -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+          sum b = multiply b b >>= \b2 -> sum' b b b2 one++            where sum' :: FinitePrecision p+                       => Decimal p RoundHalfEven+                       -> Decimal p RoundHalfEven+                       -> Decimal p RoundHalfEven+                       -> Decimal PInfinite RoundHalfEven+                       -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+                  sum' s m b n = do+                    m' <- multiply m b+                    n' <- subArith (add n two)+                    s' <- add s =<< divide m' n'+                    if s' == s then return s' else sum' s' m' b n'++ln10 :: FinitePrecision p => Arith p r (Decimal p RoundHalfEven)+ln10 = getPrecision >>= \(Just p) ->+  if p <= 50 then return fastLn10 else slowLn10++  where fastLn10 :: FinitePrecision p => Decimal p RoundHalfEven+        fastLn10 = 2.3025850929940456840179914546843642076011014886288++        slowLn10 :: FinitePrecision p => Arith p r (Decimal p RoundHalfEven)+        slowLn10 = subArith (taylorLn ten) >>= subRound++          where subRound :: Precision p => Decimal (PPlus1 (PPlus1 p)) a+                         -> Arith p r (Decimal p RoundHalfEven)+                subRound = subArith . roundDecimal++-- | 'log10' takes one operand. If the operand is a NaN then the general rules+-- for special values apply.+--+-- Otherwise, the operand must be a zero or positive, and the result is the+-- base 10 logarithm of the operand, with the following cases:+--+-- * If the operand is a zero, the result is −Infinity and exact.+--+-- * If the operand is +Infinity, the result is +Infinity and exact.+--+-- * If the operand equals an integral power of ten (including 10^0 and+-- negative powers) and there is sufficient /precision/ to hold the integral+-- part of the result, the result is an integer (with an exponent of 0) and+-- exact.+--+-- * Otherwise the result is inexact and will be rounded using the+-- 'RoundHalfEven' algorithm. The coefficient will have exactly /precision/+-- digits (unless the result is subnormal). These inexact results should be+-- correctly rounded, but may be up to 1 ulp (unit in last place) in error.+log10 :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p RoundHalfEven)+log10 x@Num { sign = s, coefficient = c, exponent = e }+  | c == 0   = return infinity { sign = Neg }+  | s == Pos = getPrecision >>= \prec -> case powerOfTen c e of+      Just p | maybe True (numDigits pc <=) prec -> return (fromInteger p)+        where pc = fromInteger (Prelude.abs p) :: Coefficient+      _ -> subArith (join $ divide <$> ln x <*> ln10) >>= result++  where powerOfTen :: Coefficient -> Exponent -> Maybe Integer+        powerOfTen c e+          | c == 10^d = Just (fromIntegral e + fromIntegral d)+          | otherwise = Nothing+          where d = numDigits c - 1 :: Int++        result :: Decimal p a -> Arith p r (Decimal p a)+        result r = coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')+          where r' = coerce r++log10 n@Inf { sign = Pos } = return (coerce n)+log10 x = coerce <$> generalRules1 x++-- | 'divide' takes two operands. If either operand is a /special value/ then+-- the general rules apply.+--+-- Otherwise, if the divisor is zero then either the Division undefined+-- condition is raised (if the dividend is zero) and the result is NaN, or the+-- Division by zero condition is raised and the result is an Infinity with a+-- sign which is the /exclusive or/ of the signs of the operands.+--+-- Otherwise, a “long division” is effected.+--+-- The result is then rounded to /precision/ digits, if necessary, according+-- to the /rounding/ algorithm and taking into account the remainder from the+-- division.+divide :: (FinitePrecision p, Rounding r)+       => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+divide dividend@Num{ sign = xs } Num { coefficient = 0, sign = ys }+  | Number.isZero dividend = divisionUndefined+  | otherwise              = divisionByZero infinity { sign = xorSigns xs ys }+divide Num { sign = xs, coefficient = xc, exponent = xe }+       Num { sign = ys, coefficient = yc, exponent = ye } = quotient++  where quotient = result =<< answer+        rn = Num { sign = rs, coefficient = rc, exponent = re }+        rs = xorSigns xs ys+        (rc, rem, dv, adjust) = longDivision xc yc (finitePrecision rn)+        re = xe - (ye + adjust)+        answer+          | rem == 0  = return rn+          | otherwise = roundDecimal $ case (rem * 2) `Prelude.compare` dv of+              LT -> rn { coefficient = rc * 10 + 1, exponent = re - 1 }+              EQ -> rn { coefficient = rc * 10 + 5, exponent = re - 1 }+              GT -> rn { coefficient = rc * 10 + 9, exponent = re - 1 }++divide Inf{} Inf{} = invalidOperation qNaN+divide Inf { sign = xs } Num { sign = ys } =+  return Inf { sign = xorSigns xs ys }+divide Num { sign = xs } Inf { sign = ys } =+  return zero { sign = xorSigns xs ys }+divide x y = generalRules2 x y++type Dividend  = Coefficient+type Divisor   = Coefficient+type Quotient  = Coefficient+type Remainder = Dividend++longDivision :: Dividend -> Divisor -> Int+             -> (Quotient, Remainder, Divisor, Exponent)+longDivision 0  dv _ = (0, 0, dv, 0)+longDivision dd dv p = step1 dd dv 0++  where step1 :: Dividend -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step1 dd dv adjust+          | dd <       dv = step1 (dd * 10)  dv       (adjust + 1)+          | dd >= 10 * dv = step1  dd       (dv * 10) (adjust - 1)+          | otherwise     = step2  dd        dv        adjust++        step2 :: Dividend -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step2 = step3 0++        step3 :: Quotient -> Dividend -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step3 r dd dv adjust+          | dv <= dd                 = step3 (r +  1) (dd - dv) dv  adjust+          | (dd == 0 && adjust >= 0) ||+            numDigits r == p         = step4  r        dd       dv  adjust+          | otherwise                = step3 (r * 10) (dd * 10) dv (adjust + 1)++        step4 :: Quotient -> Remainder -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step4 = (,,,)++reciprocal :: (FinitePrecision p, Rounding r)+           => Decimal a b -> Arith p r (Decimal p r)+reciprocal = divide one++-- | 'abs' takes one operand. If the operand is negative, the result is the+-- same as using the 'minus' operation on the operand. Otherwise, the result+-- is the same as using the 'plus' operation on the operand.+--+-- Note that the result of this operation is affected by context and may set+-- /flags/. The 'copyAbs' operation may be used if this is not desired.+abs :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+abs x+  | isNegative x = minus x+  | otherwise    = plus  x++-- | 'compare' takes two operands and compares their values numerically. If+-- either operand is a /special value/ then the general rules apply. No flags+-- are set unless an operand is a signaling NaN.+--+-- Otherwise, the operands are compared, returning @'Right' 'LT'@ if the first+-- is less than the second, @'Right' 'EQ'@ if they are equal, or @'Right'+-- 'GT'@ if the first is greater than the second.+--+-- A 'Left' value is returned if the result is NaN, indicating an “unordered”+-- comparison (see IEEE 754 §5.11).+compare :: Decimal a b -> Decimal c d+        -> Arith p r (Either (Decimal p r) Ordering)+compare x@Num{} y@Num{} = nzp <$> subArith (subtract' xn yn)++  where subtract' :: Decimal a b -> Decimal c d+                  -> Arith PInfinite RoundHalfEven+                     (Decimal PInfinite RoundHalfEven)+        subtract' = subtract++        (xn, yn) | sign x /= sign y = (either id fromOrdering $ nzp x,+                                       either id fromOrdering $ nzp y)+                 | otherwise        = (x, y)++        nzp :: Decimal a b -> Either (Decimal p r) Ordering+        nzp Num { sign = s, coefficient = c }+          | c == 0    = Right EQ+          | s == Pos  = Right GT+          | otherwise = Right LT+        nzp Inf { sign = s } = case s of+          Pos -> Right GT+          Neg -> Right LT+        nzp n = Left (coerce n)++compare Inf { sign = xs } Inf { sign = ys } = return $ case (xs, ys) of+  (Pos, Neg) -> Right GT+  (Neg, Pos) -> Right LT+  _          -> Right EQ+compare Inf { sign = xs } Num { } = return $ case xs of+  Pos -> Right GT+  Neg -> Right LT+compare Num { } Inf { sign = ys } = return $ case ys of+  Pos -> Right LT+  Neg -> Right GT+compare x y = Left <$> generalRules2 x y++-- | 'compareSignal' takes two operands and compares their values+-- numerically. This operation is identical to 'compare', except that if+-- neither operand is a signaling NaN then any quiet NaN operand is treated as+-- though it were a signaling NaN. (That is, all NaNs signal, with signaling+-- NaNs taking precedence over quiet NaNs.)+compareSignal :: Decimal a b -> Decimal c d+              -> Arith p r (Either (Decimal p r) Ordering)+compareSignal x@NaN { signaling = True } y =     x `compare`     y+compareSignal x y@NaN { signaling = True } =     x `compare`     y+compareSignal x y                          = q2s x `compare` q2s y++  where q2s :: Decimal p r -> Decimal p r+        q2s nan@NaN{} = nan { signaling = True }+        q2s x         = x++-- | 'max' takes two operands, compares their values numerically, and returns+-- the maximum. If either operand is a NaN then the general rules apply,+-- unless one is a quiet NaN and the other is numeric, in which case the+-- numeric operand is returned.+max :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+max x y = snd <$> minMax id x y++-- | 'maxMagnitude' takes two operands and compares their values numerically+-- with their /sign/ ignored and assumed to be 0.+--+-- If, without signs, the first operand is the larger then the original first+-- operand is returned (that is, with the original sign). If, without signs,+-- the second operand is the larger then the original second operand is+-- returned. Otherwise the result is the same as from the 'max' operation.+maxMagnitude :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+maxMagnitude x y = snd <$> minMax withoutSign x y++-- | 'min' takes two operands, compares their values numerically, and returns+-- the minimum. If either operand is a NaN then the general rules apply,+-- unless one is a quiet NaN and the other is numeric, in which case the+-- numeric operand is returned.+min :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+min x y = fst <$> minMax id x y++-- | 'minMagnitude' takes two operands and compares their values numerically+-- with their /sign/ ignored and assumed to be 0.+--+-- If, without signs, the first operand is the smaller then the original first+-- operand is returned (that is, with the original sign). If, without signs,+-- the second operand is the smaller then the original second operand is+-- returned. Otherwise the result is the same as from the 'min' operation.+minMagnitude :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+minMagnitude x y = fst <$> minMax withoutSign x y++-- | Ordering function for 'min', 'minMagnitude', 'max', and 'maxMagnitude':+-- returns the original arguments as (smaller, larger) when the given function+-- is applied to them.+minMax :: (Decimal a b -> Decimal a b) -> Decimal a b -> Decimal a b+       -> Arith p r (Decimal a b, Decimal a b)+minMax _ x@Num{}                       NaN { signaling = False } = return (x, x)+minMax _ x@Inf{}                       NaN { signaling = False } = return (x, x)+minMax _   NaN { signaling = False } y@Num{}                     = return (y, y)+minMax _   NaN { signaling = False } y@Inf{}                     = return (y, y)++minMax f x y = f x `compare` f y >>= \c -> return $ case c of+  Right LT -> (x, y)+  Right GT -> (y, x)+  Right EQ -> case (sign x, sign y) of+    (Neg, Pos) -> (x, y)+    (Pos, Neg) -> (y, x)+    (Pos, Pos) -> case (x, y) of+      (Num { exponent = xe }, Num { exponent = ye }) | xe > ye -> (y, x)+      _ -> (x, y)+    (Neg, Neg) -> case (x, y) of+      (Num { exponent = xe }, Num { exponent = ye }) | xe < ye -> (y, x)+      _ -> (x, y)+  Left nan -> let nan' = coerce nan in (nan', nan')++withoutSign :: Decimal p r -> Decimal p r+withoutSign n = n { sign = Pos }++-- | 'power' takes two operands, and raises a number (the left-hand operand)+-- to a power (the right-hand operand). If either operand is a /special value/+-- then the general rules apply, except as stated below.+--+-- The following rules apply:+--+-- * If both operands are zero, or if the left-hand operand is less than zero+-- and the right-hand operand does not have an integral value or is infinite,+-- an Invalid operation condition is raised, the result is NaN, and the+-- following rules do not apply.+--+-- * If the left-hand operand is infinite, the result will be exact and will+-- be infinite if the right-hand side is positive, 1 if the right-hand side is+-- a zero, and 0 if the right-hand side is negative.+--+-- * If the left-hand operand is a zero, the result will be exact and will be+-- infinite if the right-hand side is negative or 0 if the right-hand side is+-- positive.+--+-- * If the right-hand operand is a zero, the result will be 1 and exact.+--+-- * In cases not covered above, the result will be inexact unless the+-- right-hand side has an integral value and the result is finite and can be+-- expressed exactly within /precision/ digits. In this latter case, if the+-- result is unrounded then its exponent will be that which would result if+-- the operation were calculated by repeated multiplication (if the second+-- operand is negative then the reciprocal of the first operand is used, with+-- the absolute value of the second operand determining the multiplications).+--+-- * Inexact finite results should be correctly rounded, but may be up to 1+-- ulp (unit in last place) in error.+--+-- * The /sign/ of the result will be 1 only if the right-hand side has an+-- integral value and is odd (and is not infinite) and also the /sign/ of the+-- left-hand side is 1. In all other cases, the /sign/ of the result will be+-- 0.+power :: (FinitePrecision p, Rounding r)+      => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+power x@Num { coefficient = 0 } y@Num{}+  | Number.isZero y     = invalidOperation qNaN+  | Number.isNegative y = divisionByZero infinity { sign = powerSign x y }+  | otherwise           = return zero     { sign = powerSign x y }+power x@Num{} y@Num{} = case integralValue y of+  Just i  | i < 0               -> reciprocal x >>= \rx -> integralPower rx (-i)+          | otherwise           ->                         integralPower  x   i+  Nothing | Number.isPositive x -> ln x >>= multiply y >>= fmap coerce . exp+          | otherwise           -> invalidOperation qNaN+power x@Num{} y@Inf{}+  | Number.isPositive x = return $ case sign y of+      Pos -> infinity+      Neg -> zero+  | otherwise           = invalidOperation qNaN+power x@Inf{} y@Num{}+  | Number.isZero y     = return one+  | Number.isPositive y = return infinity { sign = powerSign x y }+  | otherwise           = return zero     { sign = powerSign x y }+power Inf{} Inf { sign = s }+  | s == Pos            = return infinity+  | otherwise           = return zero+power x y = generalRules2 x y++powerSign :: Decimal a b -> Decimal c d -> Sign+powerSign x y+  | Number.isNegative x && fromMaybe False (odd <$> integralValue y) = Neg+  | otherwise                                                        = Pos++integralPower :: (Precision p, Rounding r)+              => Decimal a b -> Integer -> Arith p r (Decimal p r)+integralPower b e = integralPower' (return b) e one+  where integralPower' :: (Precision p, Rounding r)+                       => Arith p r (Decimal a b) -> Integer -> Decimal p r+                       -> Arith p r (Decimal p r)+        integralPower' _  0 r = return r+        integralPower' mb e r+          | odd e     = mb >>= \b -> multiply r b >>=+                        integralPower'              (multiply b b) e'+          | otherwise = integralPower' (mb >>= \b -> multiply b b) e' r+          where e' = e `div` 2++-- | 'quantize' takes two operands. If either operand is a /special value/+-- then the general rules apply, except that if either operand is infinite and+-- the other is finite an Invalid operation condition is raised and the result+-- is NaN, or if both are infinite then the result is the first operand.+--+-- Otherwise (both operands are finite), 'quantize' returns the number which+-- is equal in value (except for any rounding) and sign to the first+-- (left-hand) operand and which has an /exponent/ set to be equal to the+-- exponent of the second (right-hand) operand.+--+-- The /coefficient/ of the result is derived from that of the left-hand+-- operand. It may be rounded using the current /rounding/ setting (if the+-- /exponent/ is being increased), multiplied by a positive power of ten (if+-- the /exponent/ is being decreased), or is unchanged (if the /exponent/ is+-- already equal to that of the right-hand operand).+--+-- Unlike other operations, if the length of the /coefficient/ after the+-- quantize operation would be greater than /precision/ then an Invalid+-- operation condition is raised. This guarantees that, unless there is an+-- error condition, the /exponent/ of the result of a quantize is always equal+-- to that of the right-hand operand.+--+-- Also unlike other operations, quantize will never raise Underflow, even if+-- the result is subnormal and inexact.+quantize :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+quantize x@Num { coefficient = xc, exponent = xe } Num { exponent = ye }+  | xe > ye   = result x { coefficient = xc * 10^(xe - ye), exponent = ye }+  | xe < ye   = rc >>= \c -> result x { coefficient = c, exponent = ye }+  | otherwise = result x++  where result :: Precision p => Decimal a b -> Arith p r (Decimal p r)+        result x = getPrecision >>= \p -> case numDigits (coefficient x) of+          n | maybe False (n >) p -> invalidOperation qNaN+          _                       -> return (coerce x)++        rc :: Rounding r => Arith p r Coefficient+        rc = let b      = 10^(ye - xe)+                 (q, r) = xc `quotRem` b+             in getRounder >>= \rounder -> return (rounder (sign x) r b q)++quantize Num{}   Inf{} = invalidOperation qNaN+quantize Inf{}   Num{} = invalidOperation qNaN+quantize x@Inf{} Inf{} = return (coerce x)+quantize x y = generalRules2 x y++-- | 'reduce' takes one operand. It has the same semantics as the 'plus'+-- operation, except that if the final result is finite it is reduced to its+-- simplest form, with all trailing zeros removed and its sign preserved.+reduce :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+reduce n = reduce' <$> plus n+  where reduce' n@Num { coefficient = c, exponent = e }+          | c == 0 =         n {                  exponent = 0     }+          | r == 0 = reduce' n { coefficient = q, exponent = e + 1 }+          where (q, r) = c `quotRem` 10+        reduce' n = n++-- | 'roundToIntegralExact' takes one operand. If the operand is a+-- /special value/, or the exponent of the operand is non-negative, then the+-- result is the same as the operand (unless the operand is a signaling NaN,+-- as usual).+--+-- Otherwise (the operand has a negative exponent) the result is the same as+-- using the 'quantize' operation using the given operand as the+-- left-hand-operand, 1E+0 as the right-hand-operand, and the precision of the+-- operand as the /precision/ setting. The rounding mode is taken from the+-- context, as usual.+roundToIntegralExact :: (Precision a, Rounding r)+                     => Decimal a b -> Arith p r (Decimal a r)+roundToIntegralExact x@Num { exponent = e }+  | e >= 0    = return (coerce x)+  | otherwise =+      let (Right r, context) = runArith (quantize x one) newContext+          quantizeFlags = flags context++          maybeRaise :: Signal -> Decimal a r -> Arith p r (Decimal a r)+          maybeRaise sig+            | sig `signalMember` quantizeFlags =+                fmap coerce . raiseSignal sig . coerce+            | otherwise = return++      in maybeRaise Inexact r >> maybeRaise Rounded r++roundToIntegralExact x@Inf{} = return (coerce x)+roundToIntegralExact x = coerce <$> generalRules1 x++-- | 'roundToIntegralValue' takes one operand. It is identical to the+-- 'roundToIntegralExact' operation except that the 'Inexact' and 'Rounded'+-- flags are never set even if the operand is rounded (that is, the operation+-- is quiet unless the operand is a signaling NaN).+roundToIntegralValue :: (Precision a, Rounding r)+                     => Decimal a b -> Arith p r (Decimal a r)+roundToIntegralValue x@Num { exponent = e }+  | e >= 0    = return (coerce x)+  | otherwise = subArith (quantize x one)+roundToIntegralValue x@Inf{} = return (coerce x)+roundToIntegralValue x = coerce <$> generalRules1 x++-- | 'squareRoot' takes one operand. If the operand is a /special value/ then+-- the general rules apply.+--+-- Otherwise, the ideal exponent of the result is defined to be half the+-- exponent of the operand (rounded to an integer, towards −Infinity, if+-- necessary) and then:+--+-- If the operand is less than zero an Invalid operation condition is raised.+--+-- If the operand is greater than zero, the result is the square root of the+-- operand. If no rounding is necessary (the exact result requires /precision/+-- digits or fewer) then the coefficient and exponent giving the correct value+-- and with the exponent closest to the ideal exponent is used. If the result+-- must be inexact, it is rounded using the 'RoundHalfEven' algorithm and the+-- coefficient will have exactly /precision/ digits (unless the result is+-- subnormal), and the exponent will be set to maintain the correct value.+--+-- Otherwise (the operand is equal to zero), the result will be the zero with+-- the same sign as the operand and with the ideal exponent.+squareRoot :: FinitePrecision p+           => Decimal a b -> Arith p r (Decimal p RoundHalfEven)+squareRoot n@Num { sign = s, coefficient = c, exponent = e }+  | c == 0   = return n { exponent = idealExp }+  | s == Pos = subResult >>= subRounded >>= result++  where idealExp = e `div` 2 :: Exponent++        reduced :: Decimal p r -> Decimal p r+        reduced n@Num { coefficient = c, exponent = e }+          | e < idealExp = case bd of+              Just (b, (q, _)) -> n { coefficient = q, exponent = e + b }+              Nothing          -> n+          | e > idealExp = n { coefficient = c * 10^d, exponent = idealExp }+          where d  = Prelude.abs (e - idealExp)+                bd = find (\(_, (_, r)) -> r == 0) ds+                ds = map (\d -> (d, c `quotRem` (10^d))) [d, d - 1 .. 1]+        reduced n = n++        subResult :: FinitePrecision p+                  => Arith p r (Decimal (PPlus1 (PPlus1 p)) RoundHalfEven)+        subResult = subArith (babylonian approx)++        subRounded :: Precision p+                   => Decimal a b -> Arith p r (Decimal p RoundHalfEven)+        subRounded = subArith . roundDecimal++        exactness :: Decimal a b -> Arith p r+                     (Either (Decimal p r) Ordering)+        exactness r = subArith (multiply' r r) >>= compare n+          where multiply' :: Decimal a b -> Decimal c d+                          -> Arith PInfinite RoundHalfEven+                             (Decimal PInfinite RoundHalfEven)+                multiply' = multiply++        result :: Decimal p a -> Arith p r (Decimal p a)+        result r = exactness r >>= \e -> case e of+          Right EQ -> return (reduced r)+          _ -> let r' = coerce r+               in coerce <$> (raiseSignal Rounded =<< raiseSignal Inexact r')++        approx :: Decimal p r+        approx | even ae   = n { coefficient = 2, exponent =  ae      `quot` 2 }+               | otherwise = n { coefficient = 6, exponent = (ae - 1) `quot` 2 }+          where ae = adjustedExponent n++        babylonian :: FinitePrecision p => Decimal p RoundHalfEven+                   -> Arith p RoundHalfEven (Decimal p RoundHalfEven)+        babylonian x = do+          x' <- multiply oneHalf =<< add x =<< n `divide` x+          if x' == x then return x' else babylonian x'++squareRoot n@Inf { sign = Pos } = return (coerce n)+squareRoot x = coerce <$> generalRules1 x++-- $miscellaneous-operations+--+-- This section describes miscellaneous operations on decimal numbers,+-- including non-numeric comparisons, sign and other manipulations, and+-- logical operations.+--+-- Some operations return a boolean value described as 0 or 1 in the+-- /General Decimal Arithmetic Specification/, but which is returned as a+-- 'Bool' in this implementation. These values can be converted to 'Decimal'+-- via 'fromBool'.+--+-- Similarly, the total ordering operations return an 'Ordering' value in this+-- implementation, but can be converted to 'Decimal' via 'fromOrdering'.++data Logical = Logical { bits :: Integer, bitLength :: Int }++toLogical :: Decimal a b -> Maybe Logical+toLogical Num { sign = Pos, coefficient = c, exponent = 0 } =+  getBits c Logical { bits = zeroBits, bitLength = 0 }++  where getBits :: Coefficient -> Logical -> Maybe Logical+        getBits 0 g = return g+        getBits c g@Logical { bits = b, bitLength = l } = case d of+          0 -> getBits c' g {                    bitLength = succ l }+          1 -> getBits c' g { bits = setBit b l, bitLength = succ l }+          _ -> Nothing+          where (c', d) = c `quotRem` 10++toLogical _ = Nothing++fromLogical :: Logical -> Decimal a b+fromLogical Logical { bits = b, bitLength = l } =+  Num { sign = Pos, coefficient = fromBits 0 1 0, exponent = 0 }++  where fromBits :: Int -> Coefficient -> Coefficient -> Coefficient+        fromBits i r c+          | i == l      = c+          | testBit b i = fromBits i' r' (c + r)+          | otherwise   = fromBits i' r'  c+          where i' = succ i+                r' = r * 10++-- $logical-operations+--+-- The logical operations ('and', 'or', 'xor', and 'invert') take+-- /logical operands/, which are finite numbers with a /sign/ of 0, an+-- /exponent/ of 0, and a /coefficient/ whose digits must all be either 0 or+-- 1. The length of the result will be at most /precision/ digits (all of+-- which will be either 0 or 1); operands are truncated on the left or padded+-- with zeros on the left as necessary. The result of a logical operation is+-- never rounded and the only /flag/ that might be set is 'InvalidOperation'+-- (set if an operand is not a valid logical operand).++-- | 'and' is a logical operation which takes two logical operands. The result+-- is the digit-wise /and/ of the two operands; each digit of the result is+-- the logical and of the corresponding digits of the operands, aligned at the+-- least-significant digit. A result digit is 1 if both of the corresponding+-- operand digits are 1; otherwise it is 0.+and :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+and x@Num{} y@Num{} = case (toLogical x, toLogical y) of+  (Just lx, Just ly) -> getPrecision >>= \p ->+    let m = Prelude.min (bitLength lx) (bitLength ly)+        z = Logical { bits = bits lx .&. bits ly+                    , bitLength = maybe m (Prelude.min m) p }+    in return (fromLogical z)+  _ -> invalidOperation qNaN+and x y = generalRules2 x y++-- | 'or' is a logical operation which takes two logical operands. The result+-- is the digit-wise /inclusive or/ of the two operands; each digit of the+-- result is the logical or of the corresponding digits of the operands,+-- aligned at the least-significant digit. A result digit is 1 if either or+-- both of the corresponding operand digits is 1; otherwise it is 0.+or :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+or x@Num{} y@Num{} = case (toLogical x, toLogical y) of+  (Just lx, Just ly) -> getPrecision >>= \p ->+    let m = Prelude.max (bitLength lx) (bitLength ly)+        z = Logical { bits = bits lx .|. bits ly+                    , bitLength = maybe m (Prelude.min m) p }+    in return (fromLogical z)+  _ -> invalidOperation qNaN+or x y = generalRules2 x y++-- | 'xor' is a logical operation which takes two logical operands. The result+-- is the digit-wise /exclusive or/ of the two operands; each digit of the+-- result is the logical exclusive-or of the corresponding digits of the+-- operands, aligned at the least-significant digit. A result digit is 1 if+-- one of the corresponding operand digits is 1 and the other is 0; otherwise+-- it is 0.+xor :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+xor x@Num{} y@Num{} = case (toLogical x, toLogical y) of+  (Just lx, Just ly) -> getPrecision >>= \p ->+    let m = Prelude.max (bitLength lx) (bitLength ly)+        z = Logical { bits = bits lx `Bits.xor` bits ly+                    , bitLength = maybe m (Prelude.min m) p }+    in return (fromLogical z)+  _ -> invalidOperation qNaN+xor x y = generalRules2 x y++-- | 'invert' is a logical operation which takes one logical operand. The+-- result is the digit-wise /inversion/ of the operand; each digit of the+-- result is the inverse of the corresponding digit of the operand. A result+-- digit is 1 if the corresponding operand digit is 0; otherwise it is 0.+invert :: FinitePrecision p => Decimal a b -> Arith p r (Decimal p r)+invert x@Num{} = case toLogical x of+  Just lx -> getPrecision >>= \(Just p) ->+    let z = Logical { bits = complement (bits lx), bitLength = p }+    in return (fromLogical z)+  _ -> invalidOperation qNaN+invert x = generalRules1 x++-- | 'canonical' takes one operand. The result has the same value as the+-- operand but always uses a /canonical/ encoding. The definition of+-- /canonical/ is implementation-defined; if more than one internal encoding+-- for a given NaN, Infinity, or finite number is possible then one+-- “preferred” encoding is deemed canonical. This operation then returns the+-- value using that preferred encoding.+--+-- If all possible operands have just one internal encoding each, then+-- 'canonical' always returns the operand unchanged (that is, it has the same+-- effect as 'copy'). This operation is unaffected by context and is quiet —+-- no /flags/ are changed in the context.+canonical :: Decimal a b -> Arith p r (Decimal a b)+canonical = copy++-- | 'class'' takes one operand. The result is an indication of the /class/ of+-- the operand, where the class is one of ten possibilities, corresponding to+-- one of the strings @"sNaN"@ (signaling NaN), @\"NaN"@ (quiet NaN),+-- @"-Infinity"@ (negative infinity), @"-Normal"@ (negative normal finite+-- number), @"-Subnormal"@ (negative subnormal finite number), @"-Zero"@+-- (negative zero), @"+Zero"@ (non-negative zero), @"+Subnormal"@ (positive+-- subnormal finite number), @"+Normal"@ (positive normal finite number), or+-- @"+Infinity"@ (positive infinity). This operation is quiet; no /flags/ are+-- changed in the context.+--+-- Note that unlike the special values in the model, the sign of any NaN is+-- ignored in the classification, as required by IEEE 754.+class' :: Precision a => Decimal a b -> Arith p r Class+class' n = return $ case n of+  Num {} | Number.isZero n      -> NumberClass (sign n) ZeroClass+         | Number.isSubnormal n -> NumberClass (sign n) SubnormalClass+         | otherwise            -> NumberClass (sign n) NormalClass+  Inf {}                        -> NumberClass (sign n) InfinityClass+  NaN { signaling = s }         -> NaNClass (toEnum . fromEnum $ s)++data Class = NumberClass Sign NumberClass -- ^ Number (finite or infinite)+           | NaNClass NaNClass            -- ^ Not a number (quiet or signaling)+           deriving Eq++data NumberClass = ZeroClass       -- ^ Zero+                 | SubnormalClass  -- ^ Subnormal finite number+                 | NormalClass     -- ^ Normal finite number+                 | InfinityClass   -- ^ Infinity+                 deriving Eq++data NaNClass = QuietClass      -- ^ Quiet NaN+              | SignalingClass  -- ^ Signaling NaN+              deriving (Eq, Enum)++instance Show Class where+  show c = case c of+    NumberClass s nc        -> signChar s : showNumberClass nc+    NaNClass QuietClass     ->       nan+    NaNClass SignalingClass -> 's' : nan++    where signChar :: Sign -> Char+          signChar Pos = '+'+          signChar Neg = '-'++          showNumberClass :: NumberClass -> String+          showNumberClass nc = case nc of+            ZeroClass      -> "Zero"+            SubnormalClass -> "Subnormal"+            NormalClass    -> "Normal"+            InfinityClass  -> "Infinity"++          nan :: String+          nan = "NaN"++-- | 'compareTotal' takes two operands and compares them using their abstract+-- representation rather than their numerical value. A /total ordering/ is+-- defined for all possible abstract representations, as described below. If+-- the first operand is lower in the total order than the second operand then+-- the result is 'LT', if the operands have the same abstract representation+-- then the result is 'EQ', and if the first operand is higher in the total+-- order than the second operand then the result is 'GT'. The total ordering+-- is defined as follows.+--+-- 1. The following items describe the ordering for representations whose+-- /sign/ is 0. If the /sign/ is 1, the order is reversed. A representation+-- with a /sign/ of 1 is always lower in the ordering than one with a /sign/+-- of 0.+--+-- 2. Numbers (representations which are not NaNs) are ordered such that a+-- larger numerical value is higher in the ordering. If two representations+-- have the same numerical value then the exponent is taken into account;+-- larger (more positive) exponents are higher in the ordering.+--+-- 3. All quiet NaNs are higher in the total ordering than all signaling NaNs.+--+-- 4. Quiet NaNs and signaling NaNs are ordered according to their /payload/;+-- a larger payload is higher in the ordering.+--+-- For example, the following values are ordered from lowest to highest: @-NaN+-- -sNaN -Infinity -127 -1 -1.00 -0 -0.000 0 1.2300 1.23 1E+9 Infinity sNaN+-- NaN NaN456@.+compareTotal :: Decimal a b -> Decimal c d -> Arith p r Ordering+compareTotal x y = return $ case (sign x, sign y) of+  (Pos, Pos) -> compareAbs x y+  (Neg, Neg) -> compareAbs y x+  (Neg, Pos) -> LT+  (Pos, Neg) -> GT++  where compareAbs :: Decimal a b -> Decimal c d -> Ordering+        compareAbs Num { coefficient = xc, exponent = xe }+                   Num { coefficient = yc, exponent = ye } =+          let (xac, yac) | xe == ye  = (xc, yc)+                         | xe >  ye  = (xc * 10^n, yc)+                         | otherwise = (xc, yc * 10^n)+              n = Prelude.abs (xe - ye)+          in Prelude.compare xac yac `mappend` Prelude.compare xe ye+        compareAbs Num{} Inf{} = LT+        compareAbs Inf{} Num{} = GT+        compareAbs Inf{} Inf{} = EQ+        compareAbs NaN { signaling = xs, payload = xp }+                   NaN { signaling = ys, payload = yp } =+          Prelude.compare ys xs `mappend` Prelude.compare xp yp+        compareAbs NaN{} _     = GT+        compareAbs _     NaN{} = LT++-- | 'compareTotalMagnitude' takes two operands and compares them using their+-- abstract representation rather than their numerical value and with their+-- /sign/ ignored and assumed to be 0. The result is identical to that+-- obtained by using 'compareTotal' on two operands which are the 'copyAbs'+-- copies of the operands to 'compareTotalMagnitude'.+compareTotalMagnitude :: Decimal a b -> Decimal c d -> Arith p r Ordering+compareTotalMagnitude x y = compareTotal x { sign = Pos } y { sign = Pos }++-- | 'copy' takes one operand. The result is a copy of the operand. This+-- operation is unaffected by context and is quiet — no /flags/ are changed in+-- the context.+copy :: Decimal a b -> Arith p r (Decimal a b)+copy = return++-- | 'copyAbs' takes one operand. The result is a copy of the operand with the+-- /sign/ set to 0. Unlike the 'abs' operation, this operation is unaffected+-- by context and is quiet — no /flags/ are changed in the context.+copyAbs :: Decimal a b -> Arith p r (Decimal a b)+copyAbs n = return n { sign = Pos }++-- | 'copyNegate' takes one operand. The result is a copy of the operand with+-- the /sign/ inverted (a /sign/ of 0 becomes 1 and vice versa). Unlike the+-- 'minus' operation, this operation is unaffected by context and is quiet —+-- no /flags/ are changed in the context.+copyNegate :: Decimal a b -> Arith p r (Decimal a b)+copyNegate n = return n { sign = negateSign (sign n) }++-- | 'copySign' takes two operands. The result is a copy of the first operand+-- with the /sign/ set to be the same as the /sign/ of the second+-- operand. This operation is unaffected by context and is quiet — no /flags/+-- are changed in the context.+copySign :: Decimal a b -> Decimal c d -> Arith p r (Decimal a b)+copySign n m = return n { sign = sign m }++-- | 'isCanonical' takes one operand. The result is 'True' if the operand is+-- /canonical/; otherwise it is 'False'. The definition of /canonical/ is+-- implementation-defined; if more than one internal encoding for a given NaN,+-- Infinity, or finite number is possible then one “preferred” encoding is+-- deemed canonical. This operation then tests whether the internal encoding+-- is that preferred encoding.+--+-- If all possible operands have just one internal encoding each, then+-- 'isCanonical' always returns 'True'. This operation is unaffected by+-- context and is quiet — no /flags/ are changed in the context.+isCanonical :: Decimal a b -> Arith p r Bool+isCanonical _ = return True++-- | 'isFinite' takes one operand. The result is 'True' if the operand is+-- neither infinite nor a NaN (that is, it is a normal number, a subnormal+-- number, or a zero); otherwise it is 'False'. This operation is unaffected+-- by context and is quiet — no /flags/ are changed in the context.+isFinite :: Decimal a b -> Arith p r Bool+isFinite = return . Number.isFinite++-- | 'isInfinite' takes one operand. The result is 'True' if the operand is an+-- Infinity; otherwise it is 'False'. This operation is unaffected by context+-- and is quiet — no /flags/ are changed in the context.+isInfinite :: Decimal a b -> Arith p r Bool+isInfinite n = return $ case n of+  Inf{} -> True+  _     -> False++-- | 'isNaN' takes one operand. The result is 'True' if the operand is a NaN+-- (quiet or signaling); otherwise it is 'False'. This operation is unaffected+-- by context and is quiet — no /flags/ are changed in the context.+isNaN :: Decimal a b -> Arith p r Bool+isNaN n = return $ case n of+  NaN{} -> True+  _     -> False++-- | 'isNormal' takes one operand. The result is 'True' if the operand is a+-- positive or negative /normal number/; otherwise it is 'False'. This+-- operation is quiet; no /flags/ are changed in the context.+isNormal :: Precision a => Decimal a b -> Arith p r Bool+isNormal = return . Number.isNormal++-- | 'isQNaN' takes one operand. The result is 'True' if the operand is a+-- quiet NaN; otherwise it is 'False'. This operation is unaffected by context+-- and is quiet — no /flags/ are changed in the context.+isQNaN :: Decimal a b -> Arith p r Bool+isQNaN n = return $ case n of+  NaN { signaling = False } -> True+  _                         -> False++-- | 'isSigned' takes one operand. The result is 'True' if the /sign/ of the+-- operand is 1; otherwise it is 'False'. This operation is unaffected by+-- context and is quiet — no /flags/ are changed in the context.+isSigned :: Decimal a b -> Arith p r Bool+isSigned = return . Number.isNegative++-- | 'isSNaN' takes one operand. The result is 'True' if the operand is a+-- signaling NaN; otherwise it is 'False'. This operation is unaffected by+-- context and is quiet — no /flags/ are changed in the context.+isSNaN :: Decimal a b -> Arith p r Bool+isSNaN n = return $ case n of+  NaN { signaling = True } -> True+  _                        -> False++-- | 'isSubnormal' takes one operand. The result is 'True' if the operand is a+-- positive or negative /subnormal number/; otherwise it is 'False'. This+-- operation is quiet; no /flags/ are changed in the context.+isSubnormal :: Precision a => Decimal a b -> Arith p r Bool+isSubnormal = return . Number.isSubnormal++-- | 'isZero' takes one operand. The result is 'True' if the operand is a+-- zero; otherwise it is 'False'. This operation is unaffected by context and+-- is quiet — no /flags/ are changed in the context.+isZero :: Decimal a b -> Arith p r Bool+isZero = return . Number.isZero++-- | 'logb' takes one operand. If the operand is a NaN then the general+-- arithmetic rules apply. If the operand is infinite then +Infinity is+-- returned. If the operand is a zero, then −Infinity is returned and the+-- Division by zero exceptional condition is raised.+--+-- Otherwise, the result is the integer which is the exponent of the magnitude+-- of the most significant digit of the operand (as though the operand were+-- truncated to a single digit while maintaining the value of that digit and+-- without limiting the resulting exponent). All results are exact unless an+-- integer result does not fit in the available /precision/.+logb :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+logb Num { coefficient = c, exponent = e }+  | c == 0    = raiseSignal DivisionByZero Inf { sign = Neg }+  | otherwise = roundDecimal (fromInteger r :: Decimal PInfinite RoundHalfEven)+  where r = fromIntegral (numDigits c) - 1 + fromIntegral e :: Integer+logb Inf{} = return Inf { sign = Pos }+logb x = generalRules1 x++-- | 'scaleb' takes two operands. If either operand is a NaN then the general+-- arithmetic rules apply. Otherwise, the second operand must be a finite+-- integer with an exponent of zero and in the range ±2 × (E/max/ ++-- /precision/) inclusive, where E/max/ is the largest value that can be+-- returned by the 'logb' operation at the same /precision/ setting. (If is is+-- not, the Invalid Operation condition is raised and the result is NaN.)+--+-- If the first operand is infinite then that Infinity is returned, otherwise+-- the result is the first operand modified by adding the value of the second+-- operand to its /exponent/. The result may Overflow or Underflow.+scaleb :: Decimal a b -> Decimal c d -> Arith p r (Decimal a b)+scaleb x@Num { exponent = e } s+  | validScale s = let Just i = integralValue s+                   in return x { exponent = e + fromInteger i }+                      -- XXX check for Overflow and Underflow+scaleb x@Inf{} s | validScale s = return x+scaleb x y = coerce <$> generalRules2 x y++validScale :: Decimal a b -> Bool+validScale Num { exponent = 0 } = True  -- XXX+validScale _                    = False++-- | 'radix' takes no operands. The result is the radix (base) in which+-- arithmetic is effected; for this specification the result will have the+-- value 10.+radix :: Precision p => Arith p r (Decimal p r)+radix = return radix'+  where radix' = case precision radix' of+          Just 1 -> one { exponent    =  1 }+          _      -> one { coefficient = 10 }++-- | 'sameQuantum' takes two operands, and returns 'True' if the two operands+-- have the same /exponent/ or 'False' otherwise. The result is never affected+-- by either the sign or the coefficient of either operand.+--+-- If either operand is a /special value/, 'True' is returned only if both+-- operands are NaNs or both are infinities.+--+-- 'sameQuantum' does not change any /flags/ in the context.+sameQuantum :: Decimal a b -> Decimal c d -> Arith p r Bool+sameQuantum Num { exponent = xe } Num { exponent = ye } = return (xe == ye)+sameQuantum Inf {               } Inf {               } = return True+sameQuantum NaN {               } NaN {               } = return True+sameQuantum _                     _                     = return False++-- | 'shift' takes two operands. The second operand must be an integer (with+-- an /exponent/ of 0) in the range /−precision/ through /precision/. If the+-- first operand is a NaN then the general arithmetic rules apply, and if it+-- is infinite then the result is the Infinity unchanged.+--+-- Otherwise (the first operand is finite) the result has the same /sign/ and+-- /exponent/ as the first operand, and a /coefficient/ which is a shifted+-- copy of the digits in the coefficient of the first operand. The number of+-- places to shift is taken from the absolute value of the second operand,+-- with the shift being to the left if the second operand is positive or to+-- the right otherwise. Digits shifted into the coefficient are zeros.+--+-- The only /flag/ that might be set is 'InvalidOperation' (set if the first+-- operand is an sNaN or the second is not valid).+--+-- The 'rotate' operation can be used to rotate rather than shift a+-- coefficient.+shift :: Precision p => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+shift n@Num { coefficient = c } s@Num { sign = d, coefficient = sc }+  | validShift z s = return z+  where z = case precision z of+          Just p  -> y { coefficient = coefficient y `rem` 10 ^ p }+          Nothing -> y+        y = case d of+          Pos -> n { coefficient =  c  *     10 ^ sc }+          Neg -> n { coefficient =  c `quot` 10 ^ sc }++shift n@Inf {                   } s | validShift z s = return z+  where z = coerce n+shift n@NaN { signaling = False } s | validShift z s = return z+  where z = coerce n+shift n@NaN { signaling = True  } _                  = invalidOperation n+shift _                           s                  = invalidOperation s++validShift :: Precision p => p -> Decimal a b -> Bool+validShift px Num { coefficient = c, exponent = 0 } =+  let p = fromIntegral <$> precision px in maybe True (c <=) p+validShift _ _ = False++-- | 'rotate' takes two operands. The second operand must be an integer (with+-- an /exponent/ of 0) in the range /−precision/ through /precision/. If the+-- first operand is a NaN then the general arithmetic rules apply, and if it+-- is infinite then the result is the Infinity unchanged.+--+-- Otherwise (the first operand is finite) the result has the same /sign/ and+-- /exponent/ as the first operand, and a /coefficient/ which is a rotated+-- copy of the digits in the coefficient of the first operand. The number of+-- places of rotation is taken from the absolute value of the second operand,+-- with the rotation being to the left if the second operand is positive or to+-- the right otherwise.+--+-- If the coefficient of the first operand has fewer than /precision/ digits,+-- it is treated as though it were padded on the left with zeros to length+-- /precision/ before the rotation. Similarly, if the coefficient of the first+-- operand has more than /precision/ digits, it is truncated on the left+-- before use.+--+-- The only /flag/ that might be set is 'InvalidOperation' (set if the first+-- operand is an sNaN or the second is not valid).+--+-- The 'shift' operation can be used to shift rather than rotate a+-- coefficient.+rotate :: FinitePrecision p+       => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+rotate n@Num { coefficient = c } s@Num { sign = d, coefficient = sc }+  | validShift z s = return z+  where z = n { coefficient = rc * b + (lc `rem` b) }+        (lc, rc) = c `quotRem` b'+        (b , b') = case d of+          Pos -> (10^sc , 10^sc')+          Neg -> (10^sc', 10^sc )+        sc' = finitePrecision z - fromIntegral sc++rotate n@Inf {                   } s | validShift z s = return z+  where z = coerce n+rotate n@NaN { signaling = False } s | validShift z s = return z+  where z = coerce n+rotate n@NaN { signaling = True  } _                  = invalidOperation n+rotate _                           s                  = invalidOperation s
src/Numeric/Decimal/Operation.hs-boot view
@@ -11,6 +11,7 @@        , minus        , abs        , compare+       , compareTotal        , min        , max        , power@@ -48,12 +49,11 @@          => Decimal a b -> Arith p r (Decimal p r) abs      :: (Precision p, Rounding r)          => Decimal a b -> Arith p r (Decimal p r)-compare  :: (Precision p, Rounding r)-         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)-min      :: (Precision p, Rounding r)-         => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)-max      :: (Precision p, Rounding r)-         => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+compare  :: Decimal a b -> Decimal c d+         -> Arith p r (Either (Decimal p r) Ordering)+compareTotal :: Decimal a b -> Decimal c d -> Arith p r Ordering+min      :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+max      :: Decimal a b -> Decimal a b -> Arith p r (Decimal a b) power    :: (FinitePrecision p, Rounding r)          => Decimal a b -> Decimal c d -> Arith p r (Decimal p r) squareRoot :: FinitePrecision p
src/Numeric/Decimal/Precision.hs view
@@ -16,12 +16,27 @@        , PInfinite        ) where +import {-# SOURCE #-} Numeric.Decimal.Number+ -- | Precision indicates the maximum number of significant decimal digits a -- number may have. class Precision p where-  -- | Return the precision of the argument, or 'Nothing' if the precision is infinite.+  -- | Return the precision of the argument, or 'Nothing' if the precision is+  -- infinite.   precision :: p -> Maybe Int +  -- | Return the maximum exponent for a number in scientific notation with+  -- the given precision, or 'Nothing' if the exponent has no limit.+  eMax :: p -> Maybe Exponent+  eMax n = subtract 1 . (10 ^) . numDigits <$> base+    where mlength = precision n                       :: Maybe Int+          base    = (10 *) . fromIntegral <$> mlength :: Maybe Coefficient++  -- | Return the minimum exponent for a number in scientific notation with+  -- the given precision, or 'Nothing' if the exponent has no limit.+  eMin :: p -> Maybe Exponent+  eMin = fmap (1 -) . eMax+ -- | A subclass of precisions that are finite class Precision p => FinitePrecision p @@ -29,6 +44,7 @@ data PInfinite instance Precision PInfinite where   precision _ = Nothing+  eMax      _ = Nothing  -- | A precision of 1 significant digit data P1
src/Numeric/Decimal/Rounding.hs view
@@ -1,7 +1,7 @@  module Numeric.Decimal.Rounding        ( RoundingAlgorithm(..)-       , Rounding(..)+       , Rounding(rounding)         , RoundDown        , RoundHalfUp@@ -14,6 +14,8 @@        , Round05Up         , getRounder+       , Rounder+        , roundDecimal        ) where @@ -21,9 +23,10 @@  import Data.Coerce (coerce) +import {-# SOURCE #-} Numeric.Decimal.Arithmetic+import {-# SOURCE #-} Numeric.Decimal.Exception import {-# SOURCE #-} Numeric.Decimal.Number import                Numeric.Decimal.Precision-import {-# SOURCE #-} Numeric.Decimal.Arithmetic  -- | A value representation of a rounding algorithm (cf. 'Rounding'). data RoundingAlgorithm = RoundDown@@ -53,7 +56,7 @@         getRounder' = return roundCoefficient  -- | Round a 'Decimal' to the precision of the arithmetic context using the--- rounding algorithm of the arithmetic context.+-- rounding mode of the arithmetic context. roundDecimal :: (Precision p, Rounding r)              => Decimal a b -> Arith p r (Decimal p r) roundDecimal n@Num { sign = s, coefficient = c, exponent = e } = do@@ -68,20 +71,23 @@           n'     = case excessDigits c' =<< p of             Nothing -> n { coefficient = c'          , exponent =      e' }             _       -> n { coefficient = c' `quot` 10, exponent = succ e' }-          rounded :: Decimal p r -> Arith p r (Decimal p r)-          rounded-            | r /= 0    = raiseSignal Inexact-            | otherwise = return-      raiseSignal Rounded =<< rounded n'  -- XXX check for overflow+      rounded =<< (if r /= 0 then inexact else return) n'+      -- XXX check for overflow      Nothing -> return (coerce n) -  where excessDigits :: Coefficient -> Int -> Maybe Int-        excessDigits c p | d > p     = Just (d - p)-                         | otherwise = Nothing-          where d = numDigits c :: Int+roundDecimal n@NaN { payload = p } = do+  prec <- getPrecision+  case excessDigits p =<< (pred <$> prec) of+    Just _  -> return n { payload = 0 }+    Nothing -> return (coerce n)  roundDecimal n = return (coerce n)++excessDigits :: Coefficient -> Int -> Maybe Int+excessDigits c p | d > p     = Just (d - p)+                 | otherwise = Nothing+  where d = numDigits c :: Int  -- Required algorithms... 
stack.yaml view
@@ -1,13 +1,13 @@ # This file was automatically generated by 'stack init'-# +# # Some commonly used options have been documented as comments in this file. # For advanced use and comprehensive documentation of the format, please see:-# http://docs.haskellstack.org/en/stable/yaml_configuration/+# https://docs.haskellstack.org/en/stable/yaml_configuration/  # Resolver to choose a 'specific' stackage snapshot or a compiler version. # A snapshot resolver dictates the compiler version and the set of packages # to be used for project dependencies. For example:-# +# # resolver: lts-3.5 # resolver: nightly-2015-09-21 # resolver: ghc-7.10.2@@ -15,11 +15,11 @@ # resolver: #  name: custom-snapshot #  location: "./custom-snapshot.yaml"-resolver: lts-6.9+resolver: lts-9.1  # User packages to be built. # Various formats can be used as shown in the example below.-# +# # packages: # - some-directory # - https://example.com/foo/bar/baz-0.0.2.tar.gz@@ -31,12 +31,12 @@ #  subdirs: #  - auto-update #  - wai-# +# # A package marked 'extra-dep: true' will only be built if demanded by a # non-dependency (i.e. a user package), and its test suites and benchmarks # will not be run. This is useful for tweaking upstream packages. packages:-- '.'+- . # Dependency packages to be pulled from upstream that are not in the resolver # (e.g., acme-missiles-0.3) extra-deps: []@@ -49,18 +49,18 @@  # Control whether we use the GHC we find on the path # system-ghc: true-# +# # Require a specific version of stack, using version ranges # require-stack-version: -any # Default-# require-stack-version: ">=1.1"-# +# require-stack-version: ">=1.5"+# # Override the architecture used by stack, especially useful on Windows # arch: i386 # arch: x86_64-# +# # Extra directories used by stack for building # extra-include-dirs: [/path/to/dir] # extra-lib-dirs: [/path/to/dir]-# +# # Allow a newer minor version of GHC than the snapshot specifies # compiler-check: newer-minor
+ test/Arbitrary.hs view
@@ -0,0 +1,31 @@++module Arbitrary+       ( Arbitrary+       ) where++import Numeric.Decimal+import Test.QuickCheck++infinity :: (Precision p, Rounding r) => Decimal p r+infinity = read "Infinity"++instance (Precision p, Rounding r) => Arbitrary (Decimal p r) where+  arbitrary = frequency [(85, genNum), (10, genInf)]++genNum :: (Precision p, Rounding r) => Gen (Decimal p r)+genNum = do+  c <- choose (-(10^10), 10^10) :: Gen Integer+  e <- choose (-99, 99)         :: Gen Integer+  return $ read (show c ++ 'E' : show e)++genInf :: (Precision p, Rounding r) => Gen (Decimal p r)+genInf = do+  s <- elements [-1, 1]+  return (s * infinity)++genNaN :: (Precision p, Rounding r) => Gen (Decimal p r)+genNaN = oneof [nan "", nan "s"]+  where nan kind = do+          s <- elements ["", "-"]+          p <- choose (0, 10000) :: Gen Integer+          return $ read (s ++ kind ++ "NaN" ++ show p)
+ test/Numeric/Decimal/EncodingSpec.hs view
@@ -0,0 +1,25 @@++{-# LANGUAGE OverloadedStrings #-}++module Numeric.Decimal.EncodingSpec (spec) where++import Test.Hspec+import Data.Binary++import Numeric.Decimal.Encoding++spec :: Spec+spec = do+  it "encodes (-7.50) correctly" $+    encode (read "-7.50" :: Decimal64) `shouldBe`+    "\xA2\x30\x00\x00\x00\x00\x03\xD0"+  it "decodes (-7.50) correctly" $+    (decode "\xA2\x30\x00\x00\x00\x00\x03\xD0" :: Decimal64) `shouldBe` (-7.50)++  it "decodes Infinity correctly" $+    (decode "\x78\xFF\xFF\xFF\xFF\xFF\xFF\xFF" :: Decimal64) `shouldSatisfy`+    \x -> isInfinite x && signum x == 1++-- prop> decode (encode x) == (x :: Decimal32)+-- prop> decode (encode x) == (x :: Decimal64)+-- prop> decode (encode x) == (x :: Decimal128)
+ test/Numeric/Decimal/NumberSpec.hs view
@@ -0,0 +1,165 @@++module Numeric.Decimal.NumberSpec (spec) where++import Arbitrary ()++import Test.Hspec+import Test.QuickCheck+import Numeric.Decimal++spec :: Spec+spec = do+  describe "Read" $ do+    it "reads with Just correctly (positive)" $+      (read "Just 123" :: Maybe GeneralDecimal) `shouldBe` Just 123+    it "reads with Just correctly (negative)" $+      (read "Just (-12.0)" :: Maybe GeneralDecimal) `shouldBe` Just (-12)++  describe "Ord" $ do+    it "satisfies (>) invariant" $+      property $ \x y ->+      x > y ==> max x y == x && max y x == (x :: BasicDecimal)+    it "satisfies (<) invariant" $+      property $ \x y ->+      x < y ==> min x y == x && min y x == (x :: BasicDecimal)++    it "satisfies `max` invariant with respect to 1st arg" $+      property $ \x y ->+      max x y == x ==> x >= (y :: BasicDecimal)+    it "satisfies `max` invariant with respect to 2nd arg" $+      property $ \x y ->+      max x y == y ==> y >= (x :: BasicDecimal)+    it "satisfies `min` invariant with respect to 1st arg" $+      property $ \x y ->+      min x y == x ==> x <= (y :: BasicDecimal)+    it "satisfies `min` invariant with respect to 2nd arg" $+      property $ \x y ->+      min x y == y ==> y <= (x :: BasicDecimal)++  describe "Enum" $ do+    it "enumerates `enumFromTo` precisely" $+      ([1.7 .. 5.7] :: [BasicDecimal]) `shouldBe` [1.7, 2.7, 3.7, 4.7, 5.7]++    it "enumerates `enumFromThenTo` precisely (ascending)" $+      ([0, 0.1 .. 2] :: [BasicDecimal]) `shouldBe`+      [  0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9,+       1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]+    it "enumerates `enumFromThenTo` precisely (descending)" $+      ([2, 1.9 .. 0] :: [BasicDecimal]) `shouldBe`+      [  2, 1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1,+       1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]++  describe "Num" $ do+    it "satisfies relation between (+) and (*)" $+      property $ \x ->+      x + x == x * (2 :: GeneralDecimal)+    it "satisfies relation between (-) and 0" $+      property $ \x ->+      isFinite x ==> x - x == (0 :: BasicDecimal)+    it "satisfies relation between (+) and `negate`" $+      property $ \x ->+      isFinite x ==> x + negate x == (0 :: BasicDecimal)+    it "satisfies relation between `abs` and 0" $+      property $ \x ->+      abs x >= (0 :: GeneralDecimal)+    it "satisfies relation between `abs` and `signum`" $+      property $ \x ->+      abs x * signum x == (x :: GeneralDecimal)++  describe "Fractional" $ do+    context "RoundHalfUp" $ do+      it "properly rounds (down)" $+        (4.14 :: Decimal P2 RoundHalfUp)   `shouldBe` 4.1+      it "properly rounds (up)" $+        (4.15 :: Decimal P2 RoundHalfUp)   `shouldBe` 4.2+    context "RoundHalfDown" $+      it "properly rounds" $+        (4.15 :: Decimal P2 RoundHalfDown) `shouldBe` 4.1+    context "RoundHalfEven" $ do+      it "properly rounds 1 (up)" $+        (4.15 :: Decimal P2 RoundHalfEven) `shouldBe` 4.2+      it "properly rounds 1 (down)" $+        (4.25 :: Decimal P2 RoundHalfEven) `shouldBe` 4.2+      it "properly rounds 2 (up)" $+        (4.35 :: Decimal P2 RoundHalfEven) `shouldBe` 4.4+      it "properly rounds 2 (down)" $+        (4.45 :: Decimal P2 RoundHalfEven) `shouldBe` 4.4++  describe "RealFrac" $ do+    it "satisfies `properFraction` invariant 1" $+      property $ \x ->+      let (n,f) = properFraction (x :: BasicDecimal) :: (Integer, BasicDecimal)+      in x == fromIntegral n + f+    it "satisfies `properFraction` invariant 2" $+      property $ \x ->+      let (n,_) = properFraction (x :: BasicDecimal) :: (Integer, BasicDecimal)+      in (x < 0 && n <= 0) || (x >= 0 && n >= 0)+    it "satisfies `properFraction` invariant 3" $+      property $ \x ->+      let (_,f) = properFraction (x :: BasicDecimal) :: (Integer, BasicDecimal)+      in (x < 0 && f <= 0) || (x >= 0 && f >= 0)+    it "satisfies `properFraction` invariant 4" $+      property $ \x ->+      let (_,f) = properFraction (x :: BasicDecimal) :: (Integer, BasicDecimal)+      in isFinite f ==> abs f < 1++  describe "Floating" $ do+    it "produces same `pi` as Double" $+      realToFrac (pi :: ExtendedDecimal P16) `shouldBe` (pi :: Double)+    it "satisfies relation between (**) and (^)" $+      property $ \x y ->+      y >= 0 ==> (x :: BasicDecimal) ** fromInteger y == x ^ y+    it "computes `sqrt` correctly" $+      property $ \x ->+      isFinite x && x >= 0 ==>+      sqrt (x * x) `shouldBe` (x :: ExtendedDecimal P32)+      -- coefficient (sqrt (x * x) - (x :: ExtendedDecimal P16)) <= 1++  describe "RealFloat" $ do+    it "satisfies `decodeFloat` invariant 1" $+      property $ \x ->+      isFinite x ==> let b      = floatRadix (x :: BasicDecimal)+                         (m, n) = decodeFloat x+                     in x == fromInteger m * fromInteger b ^^ n+    it "satisfies `decodeFloat` invariant 2 (zero)" $+      decodeFloat (0 :: BasicDecimal) `shouldBe` (0,0)+    it "satisfies `decodeFloat` invariant 2 (negative zero)" $+      decodeFloat (read "-0" :: BasicDecimal) `shouldBe` (0,0)+    it "satisfies `decodeFloat` invariant 2 (nonzero)" $+      property $ \x ->+      isFinite x && x /= 0 ==> let b      = floatRadix (x :: BasicDecimal)+                                   (m, _) = decodeFloat x+                                   d      = floatDigits x+                                   am     = abs m+                               in b^(d-1) <= am && am < b^d+    it "satisfies relation between `encodeFloat` and `decodeFloat`" $+      property $ \x ->+      not (isNegativeZero x) ==>+      uncurry encodeFloat (decodeFloat x) == (x :: BasicDecimal)+    it "satisfies `exponent` invariant 1 (zero)" $+      exponent (0 :: BasicDecimal) `shouldBe` 0+    it "satisfies `exponent` invariant 1 (nonzero)" $+      property $ \x ->+      isFinite x && x /= 0 ==>+      exponent (x :: BasicDecimal) == snd (decodeFloat x) + floatDigits x+    it "satisfies `exponent` invariant 2" $+      property $ \x ->+      isFinite x ==> let b = floatRadix (x :: BasicDecimal)+                     in x == significand x * fromInteger b ^^ exponent x+    it "satisfies `significand` invariant" $+      property $ \x ->+      isFinite x ==> let s = significand (x :: BasicDecimal)+                         b = floatRadix x+                     in s == 0 ||+                        (s > -1 && s < 1 && abs s >= 1 / fromInteger b)++    it "detects negative zero" $+      isNegativeZero (read "-0" :: BasicDecimal) `shouldBe` True+    it "does not detect normal zero as negative" $+      isNegativeZero (read "+0" :: BasicDecimal) `shouldBe` False+    it "does not detect nonzero numbers as negative zero" $+      property $ \x ->+      x /= 0 ==> isNegativeZero (x :: BasicDecimal) == False++isFinite :: (FinitePrecision p, Rounding r) => Decimal p r -> Bool+isFinite x = not (isNaN x || isInfinite x)
+ test/Numeric/Decimal/OperationSpec.hs view
@@ -0,0 +1,618 @@++module Numeric.Decimal.OperationSpec (spec) where++import Test.Hspec++import Numeric.Decimal+import Numeric.Decimal.Arithmetic++import qualified Numeric.Decimal.Operation as Op++spec :: Spec+spec = do+  describe "Special values" $ do+    it "Infinity + 1 = Infinity" $+      op2 Op.add "Infinity" "1" `shouldBe` "Infinity"+    it "NaN + 1 = NaN" $+      op2 Op.add "NaN" "1" `shouldBe` "NaN"+    it "NaN + Infinity = NaN" $+      op2 Op.add "NaN" "Infinity" `shouldBe` "NaN"+    it "1 - Infinity = -Infinity" $+      op2 Op.subtract "1" "Infinity" `shouldBe` "-Infinity"+    it "-1 * Infinity = -Infinity" $+      op2 Op.multiply "-1" "Infinity" `shouldBe` "-Infinity"+    it "-0 - 0 = -0" $+      op2 Op.subtract "-0" "0" `shouldBe` "-0"+    it "-1 * 0 = -0" $+      op2 Op.multiply "-1" "0" `shouldBe` "-0"+    it "1 / 0 = Infinity" $+      op2 Op.divide "1" "0" `shouldBe` "Infinity"+    it "1 / -0 = -Infinity" $+      op2 Op.divide "1" "-0" `shouldBe` "-Infinity"+    it "-1 / 0 = -Infinity" $+      op2 Op.divide "-1" "0" `shouldBe` "-Infinity"++  describe "add" $ do+    it "add('12', '7.00')        ==>  '19.00'" $+      op2 Op.add "12" "7.00" `shouldBe` "19.00"+    it "add('1E+2', '1E+4')      ==>  '1.01E+4'" $+      op2 Op.add "1E+2" "1E+4" `shouldBe` "1.01E+4"++  describe "subtract" $ do+    it "subtract('1.3', '1.07')  ==>  '0.23'" $+      op2 Op.subtract "1.3" "1.07" `shouldBe` "0.23"+    it "subtract('1.3', '1.30')  ==>  '0.00'" $+      op2 Op.subtract "1.3" "1.30" `shouldBe` "0.00"+    it "subtract('1.3', '2.07')  ==>  '-0.77'" $+      op2 Op.subtract "1.3" "2.07" `shouldBe` "-0.77"++  describe "minus" $ do+    it "minus('1.3')   ==>  '-1.3'" $+      op1 Op.minus "1.3" `shouldBe` "-1.3"+    it "minus('-1.3')  ==>  '1.3'" $+      op1 Op.minus "-1.3" `shouldBe` "1.3"++  describe "plus" $ do+    it "plus('1.3')    ==>  '1.3'" $+      op1 Op.plus "1.3" `shouldBe` "1.3"+    it "plus('-1.3')   ==>  '-1.3'" $+      op1 Op.plus "-1.3" `shouldBe` "-1.3"++  describe "multiply" $ do+    it "multiply('1.20', '3')         ==>  '3.60'" $+      op2 Op.multiply "1.20" "3" `shouldBe` "3.60"+    it "multiply('7', '3')            ==>  '21'" $+      op2 Op.multiply "7" "3" `shouldBe` "21"+    it "multiply('0.9', '0.8')        ==> '0.72'" $+      op2 Op.multiply "0.9" "0.8" `shouldBe` "0.72"+    it "multiply('0.9', '-0')         ==> '-0.0'" $+      op2 Op.multiply "0.9" "-0" `shouldBe` "-0.0"+    it "multiply('654321', '654321')  ==>  '4.28135971E+11'" $+      op2 Op.multiply "654321" "654321" `shouldBe` "4.28135971E+11"++  describe "exp" $ do+    it "exp('-Infinity')    ==> '0'" $+      op1 Op.exp "-Infinity" `shouldBe` "0"+    it "exp('-1')           ==> '0.367879441'" $+      op1 Op.exp "-1" `shouldBe` "0.367879441"+    it "exp('0')            ==> '1'" $+      op1 Op.exp "0" `shouldBe` "1"+    it "exp('1')            ==> '2.71828183'" $+      op1 Op.exp "1" `shouldBe` "2.71828183"+    it "exp('0.693147181')  ==> '2.00000000'" $+      op1 Op.exp "0.693147181" `shouldBe` "2.00000000"+    it "exp('+Infinity')    ==> 'Infinity'" $+      op1 Op.exp "+Infinity" `shouldBe` "Infinity"++  describe "fusedMultiplyAdd" $ do+    it ("fused-multiply-add('3', '5', '7')                             ==>  " +++        "'22'") $+      op3 Op.fusedMultiplyAdd "3" "5" "7" `shouldBe` "22"+    it ("fused-multiply-add('3', '-5', '7')                            ==>  " +++        "'-8'") $+      op3 Op.fusedMultiplyAdd "3" "-5" "7" `shouldBe` "-8"+    it ("fused-multiply-add('888565290', '1557.96930', '-86087.7578')  ==>  " +++        "'1.38435736E+12'") $+      op3 Op.fusedMultiplyAdd "888565290" "1557.96930" "-86087.7578" `shouldBe`+      "1.38435736E+12"++  describe "ln" $ do+    it "ln('0')           ==> '-Infinity'" $+      op1 Op.ln "0" `shouldBe` "-Infinity"+    it "ln('1.000')       ==> '0'" $+      op1 Op.ln "1.000" `shouldBe` "0"+    it "ln('2.71828183')  ==> '1.00000000'" $+      op1 Op.ln "2.71828183" `shouldBe` "1.00000000"+    it "ln('10')          ==> '2.30258509'" $+      op1 Op.ln "10" `shouldBe` "2.30258509"+    it "ln('+Infinity')   ==> 'Infinity'" $+      op1 Op.ln "+Infinity" `shouldBe` "Infinity"++  describe "log10" $ do+    it "log10('0')          ==>  '-Infinity'" $+      op1 Op.log10 "0" `shouldBe` "-Infinity"+    it "log10('0.001')      ==>  '-3'" $+      op1 Op.log10 "0.001" `shouldBe` "-3"+    it "log10('1.000')      ==>  '0'" $+      op1 Op.log10 "1.000" `shouldBe` "0"+    it "log10('2')          ==>  '0.301029996'" $+      op1 Op.log10 "2" `shouldBe` "0.301029996"+    it "log10('10')         ==>  '1'" $+      op1 Op.log10 "10" `shouldBe` "1"+    it "log10('70')         ==>  '1.84509804'" $+      op1 Op.log10 "70" `shouldBe` "1.84509804"+    it "log10('+Infinity')  ==>  'Infinity'" $+      op1 Op.log10 "+Infinity" `shouldBe` "Infinity"++  describe "divide" $ do+    it "divide('1', '3'  )      ==>  '0.333333333'" $+      op2 Op.divide "1" "3" `shouldBe` "0.333333333"+    it "divide('2', '3'  )      ==>  '0.666666667'" $+      op2 Op.divide "2" "3" `shouldBe` "0.666666667"+    it "divide('5', '2'  )      ==>  '2.5'" $+      op2 Op.divide "5" "2" `shouldBe` "2.5"+    it "divide('1', '10' )      ==>  '0.1'" $+      op2 Op.divide "1" "10" `shouldBe` "0.1"+    it "divide('12', '12')      ==>  '1'" $+      op2 Op.divide "12" "12" `shouldBe` "1"+    it "divide('8.00', '2')     ==>  '4.00'" $+      op2 Op.divide "8.00" "2" `shouldBe` "4.00"+    it "divide('2.400', '2.0')  ==>  '1.20'" $+      op2 Op.divide "2.400" "2.0" `shouldBe` "1.20"+    it "divide('1000', '100')   ==>  '10'" $+      op2 Op.divide "1000" "100" `shouldBe` "10"+    it "divide('1000', '1')     ==>  '1000'" $+      op2 Op.divide "1000" "1" `shouldBe` "1000"+    it "divide('2.40E+6', '2')  ==>  '1.20E+6'" $+      op2 Op.divide "2.40E+6" "2" `shouldBe` "1.20E+6"++  describe "abs" $ do+    it "abs('2.1')    ==>  '2.1'" $+      op1 Op.abs "2.1" `shouldBe` "2.1"+    it "abs('-100')   ==>  '100'" $+      op1 Op.abs "-100" `shouldBe` "100"+    it "abs('101.5')  ==>  '101.5'" $+      op1 Op.abs "101.5" `shouldBe` "101.5"+    it "abs('-101.5') ==>  '101.5'" $+      op1 Op.abs "-101.5" `shouldBe` "101.5"++  describe "compare" $ do+    it "compare('2.1', '3')     ==>  '-1'" $+      op2 compare' "2.1" "3" `shouldBe` "-1"+    it "compare('2.1', '2.1')   ==>  '0'" $+      op2 compare' "2.1" "2.1" `shouldBe` "0"+    it "compare('2.1', '2.10')  ==>  '0'" $+      op2 compare' "2.1" "2.10" `shouldBe` "0"+    it "compare('3', '2.1')     ==>  '1'" $+      op2 compare' "3" "2.1" `shouldBe` "1"+    it "compare('2.1', '-3')    ==>  '1'" $+      op2 compare' "2.1" "-3" `shouldBe` "1"+    it "compare('-3', '2.1')    ==>  '-1'" $+      op2 compare' "-3" "2.1" `shouldBe` "-1"++  describe "max" $ do+    it "max('3', '2')    ==>  '3'" $+      op2 Op.max "3" "2" `shouldBe` "3"+    it "max('-10', '3')  ==>  '3'" $+      op2 Op.max "-10" "3" `shouldBe` "3"+    it "max('1.0', '1')  ==>  '1'" $+      op2 Op.max "1.0" "1" `shouldBe` "1"+    it "max('7', 'NaN')  ==>  '7'" $+      op2 Op.max "7" "NaN" `shouldBe` "7"++  describe "min" $ do+    it "min('3', '2')    ==>  '2'" $+      op2 Op.min "3" "2" `shouldBe` "2"+    it "min('-10', '3')  ==>  '-10'" $+      op2 Op.min "-10" "3" `shouldBe` "-10"+    it "min('1.0', '1')  ==>  '1.0'" $+      op2 Op.min "1.0" "1" `shouldBe` "1.0"+    it "min('7', 'NaN')  ==>  '7'" $+      op2 Op.min "7" "NaN" `shouldBe` "7"++  describe "power" $ do+    it "power('2', '3')             ==>  '8'" $+      op2 Op.power "2" "3" `shouldBe` "8"+    it "power('-2', '3')            ==>  '-8'" $+      op2 Op.power "-2" "3" `shouldBe` "-8"+    it "power('2', '-3')            ==>  '0.125'" $+      op2 Op.power "2" "-3" `shouldBe` "0.125"+    it "power('1.7', '8')           ==>  '69.7575744'" $+      op2 Op.power "1.7" "8" `shouldBe` "69.7575744"+    it "power('10', '0.301029996')  ==>  '2.00000000'" $+      op2 Op.power "10" "0.301029996" `shouldBe` "2.00000000"+    it "power('Infinity', '-1')     ==>  '0'" $+      op2 Op.power "Infinity" "-1" `shouldBe` "0"+    it "power('Infinity', '0')      ==>  '1'" $+      op2 Op.power "Infinity" "0" `shouldBe` "1"+    it "power('Infinity', '1')      ==>  'Infinity'" $+      op2 Op.power "Infinity" "1" `shouldBe` "Infinity"+    it "power('-Infinity', '-1')    ==>  '-0'" $+      op2 Op.power "-Infinity" "-1" `shouldBe` "-0"+    it "power('-Infinity', '0')     ==>  '1'" $+      op2 Op.power "-Infinity" "0" `shouldBe` "1"+    it "power('-Infinity', '1')     ==>  '-Infinity'" $+      op2 Op.power "-Infinity" "1" `shouldBe` "-Infinity"+    it "power('-Infinity', '2')     ==>  'Infinity'" $+      op2 Op.power "-Infinity" "2" `shouldBe` "Infinity"+    it "power('0', '0')             ==>  'NaN'" $+      op2 Op.power "0" "0" `shouldBe` "NaN"++  describe "quantize" $ do+    it "quantize('2.17', '0.001')        ==>  '2.170'" $+      op2 Op.quantize "2.17" "0.001" `shouldBe` "2.170"+    it "quantize('2.17', '0.01')         ==>  '2.17'" $+      op2 Op.quantize "2.17" "0.01" `shouldBe` "2.17"+    it "quantize('2.17', '0.1')          ==>  '2.2'" $+      op2 Op.quantize "2.17" "0.1" `shouldBe` "2.2"+    it "quantize('2.17', '1e+0')         ==>  '2'" $+      op2 Op.quantize "2.17" "1e+0" `shouldBe` "2"+    it "quantize('2.17', '1e+1')         ==>  '0E+1'" $+      op2 Op.quantize "2.17" "1e+1" `shouldBe` "0E+1"+    it "quantize('-Inf'  'Infinity')     ==>  '-Infinity'" $+      op2 Op.quantize "-Inf" "Infinity" `shouldBe` "-Infinity"+    it "quantize('2',    'Infinity')     ==>  'NaN'" $+      op2 Op.quantize "2" "Infinity" `shouldBe` "NaN"+    it "quantize('-0.1', '1'  )          ==>  '-0'" $+      op2 Op.quantize "-0.1" "1" `shouldBe` "-0"+    it "quantize('-0',   '1e+5')         ==>  '-0E+5'" $+      op2 Op.quantize "-0" "1e+5" `shouldBe` "-0E+5"+    it "quantize('+35236450.6', '1e-2')  ==>  'NaN'" $+      op2 Op.quantize "+35236450.6" "1e-2" `shouldBe` "NaN"+    it "quantize('-35236450.6', '1e-2')  ==>  'NaN'" $+      op2 Op.quantize "-35236450.6" "1e-2" `shouldBe` "NaN"+    it "quantize('217',  '1e-1')         ==>  '217.0'" $+      op2 Op.quantize "217" "1e-1" `shouldBe` "217.0"+    it "quantize('217',  '1e+0')         ==>  '217'" $+      op2 Op.quantize "217" "1e+0" `shouldBe` "217"+    it "quantize('217',  '1e+1')         ==>  '2.2E+2'" $+      op2 Op.quantize "217" "1e+1" `shouldBe` "2.2E+2"+    it "quantize('217',  '1e+2')         ==>  '2E+2'" $+      op2 Op.quantize "217" "1e+2" `shouldBe` "2E+2"++  describe "reduce" $ do+    it "reduce('2.1')     ==>  '2.1'" $+      op1 Op.reduce "2.1" `shouldBe` "2.1"+    it "reduce('-2.0')    ==>  '-2'" $+      op1 Op.reduce "-2.0" `shouldBe` "-2"+    it "reduce('1.200')   ==>  '1.2'" $+      op1 Op.reduce "1.200" `shouldBe` "1.2"+    it "reduce('-120')    ==>  '-1.2E+2'" $+      op1 Op.reduce "-120" `shouldBe` "-1.2E+2"+    it "reduce('120.00')  ==>  '1.2E+2'" $+      op1 Op.reduce "120.00" `shouldBe` "1.2E+2"+    it "reduce('0.00')    ==>  '0'" $+      op1 Op.reduce "0.00" `shouldBe` "0"++  describe "roundToIntegralExact" $ do+    it "round-to-integral-exact('2.1')      ==>  '2'" $+      op1 Op.roundToIntegralExact "2.1" `shouldBe` "2"+    it "round-to-integral-exact('100')      ==>  '100'" $+      op1 Op.roundToIntegralExact "100" `shouldBe` "100"+    it "round-to-integral-exact('100.0')    ==>  '100'" $+      op1 Op.roundToIntegralExact "100.0" `shouldBe` "100"+    it "round-to-integral-exact('101.5')    ==>  '102'" $+      op1 Op.roundToIntegralExact "101.5" `shouldBe` "102"+    it "round-to-integral-exact('-101.5')   ==>  '-102'" $+      op1 Op.roundToIntegralExact "-101.5" `shouldBe` "-102"+    it "round-to-integral-exact('10E+5')    ==>  '1.0E+6'" $+      op1 Op.roundToIntegralExact "10E+5" `shouldBe` "1.0E+6"+    it "round-to-integral-exact('7.89E+77') ==>  '7.89E+77'" $+      op1 Op.roundToIntegralExact "7.89E+77" `shouldBe` "7.89E+77"+    it "round-to-integral-exact('-Inf')     ==>  '-Infinity'" $+      op1 Op.roundToIntegralExact "-Inf" `shouldBe` "-Infinity"++  describe "squareRoot" $ do+    it "square-root('0')     ==> '0'" $+      op1 Op.squareRoot "0" `shouldBe` "0"+    it "square-root('-0')    ==> '-0'" $+      op1 Op.squareRoot "-0" `shouldBe` "-0"+    -- The following example is a corrected version of that found in the+    -- specification; confirmed with Mike Cowlishaw on 2016-08-02.+    it "square-root('0.39')  ==> '0.624499800'" $+      op1 Op.squareRoot "0.39" `shouldBe` "0.624499800"+    it "square-root('100')   ==> '10'" $+      op1 Op.squareRoot "100" `shouldBe` "10"+    it "square-root('1')     ==> '1'" $+      op1 Op.squareRoot "1" `shouldBe` "1"+    it "square-root('1.0')   ==> '1.0'" $+      op1 Op.squareRoot "1.0" `shouldBe` "1.0"+    it "square-root('1.00')  ==> '1.0'" $+      op1 Op.squareRoot "1.00" `shouldBe` "1.0"+    it "square-root('7')     ==> '2.64575131'" $+      op1 Op.squareRoot "7" `shouldBe` "2.64575131"+    it "square-root('10')    ==> '3.16227766'" $+      op1 Op.squareRoot "10" `shouldBe` "3.16227766"++  describe "and" $ do+    it "and('0', '0')        ==>  '0'" $+      op2 Op.and "0" "0" `shouldBe` "0"+    it "and('0', '1')        ==>  '0'" $+      op2 Op.and "0" "1" `shouldBe` "0"+    it "and('1', '0')        ==>  '0'" $+      op2 Op.and "1" "0" `shouldBe` "0"+    it "and('1', '1')        ==>  '1'" $+      op2 Op.and "1" "1" `shouldBe` "1"+    it "and('1100', '1010')  ==>  '1000'" $+      op2 Op.and "1100" "1010" `shouldBe` "1000"+    it "and('1111', '10')    ==>  '10'" $+      op2 Op.and "1111" "10" `shouldBe` "10"++  describe "or" $ do+    it "or('0', '0')        ==>  '0'" $+      op2 Op.or "0" "0" `shouldBe` "0"+    it "or('0', '1')        ==>  '1'" $+      op2 Op.or "0" "1" `shouldBe` "1"+    it "or('1', '0')        ==>  '1'" $+      op2 Op.or "1" "0" `shouldBe` "1"+    it "or('1', '1')        ==>  '1'" $+      op2 Op.or "1" "1" `shouldBe` "1"+    it "or('1100', '1010')  ==>  '1110'" $+      op2 Op.or "1100" "1010" `shouldBe` "1110"+    it "or('1110', '10')    ==>  '1110'" $+      op2 Op.or "1110" "10" `shouldBe` "1110"++  describe "xor" $ do+    it "xor('0', '0')        ==>  '0'" $+      op2 Op.xor "0" "0" `shouldBe` "0"+    it "xor('0', '1')        ==>  '1'" $+      op2 Op.xor "0" "1" `shouldBe` "1"+    it "xor('1', '0')        ==>  '1'" $+      op2 Op.xor "1" "0" `shouldBe` "1"+    it "xor('1', '1')        ==>  '0'" $+      op2 Op.xor "1" "1" `shouldBe` "0"+    it "xor('1100', '1010')  ==>  '110'" $+      op2 Op.xor "1100" "1010" `shouldBe` "110"+    it "xor('1111', '10')    ==>  '1101'" $+      op2 Op.xor "1111" "10" `shouldBe` "1101"++  describe "invert" $ do+    it "invert('0')          ==>  '111111111'" $+      op1 Op.invert "0" `shouldBe` "111111111"+    it "invert('1')          ==>  '111111110'" $+      op1 Op.invert "1" `shouldBe` "111111110"+    it "invert('111111111')  ==>  '0'" $+      op1 Op.invert "111111111" `shouldBe` "0"+    it "invert('101010101')  ==>  '10101010'" $+      op1 Op.invert "101010101" `shouldBe` "10101010"++  describe "canonical" $+    it "canonical('2.50')  ==>  '2.50'" $+      op1 Op.canonical "2.50" `shouldBe` "2.50"++  describe "class'" $ do+    it "class('Infinity')   ==>  \"+Infinity\"" $+      op1 Op.class' "Infinity" `shouldBe` "+Infinity"+    it "class('1E-10')      ==>  \"+Normal\"" $+      op1 Op.class' "1E-10" `shouldBe` "+Normal"+    it "class('2.50')       ==>  \"+Normal\"" $+      op1 Op.class' "2.50" `shouldBe` "+Normal"+    it "class('0.1E-999')   ==>  \"+Subnormal\"" $+      op1 Op.class' "0.1E-999" `shouldBe` "+Subnormal"+    it "class('0')          ==>  \"+Zero\"" $+      op1 Op.class' "0" `shouldBe` "+Zero"+    it "class('-0')         ==>  \"-Zero\"" $+      op1 Op.class' "-0" `shouldBe` "-Zero"+    it "class('-0.1E-999')  ==>  \"-Subnormal\"" $+      op1 Op.class' "-0.1E-999" `shouldBe` "-Subnormal"+    it "class('-1E-10')     ==>  \"-Normal\"" $+      op1 Op.class' "-1E-10" `shouldBe` "-Normal"+    it "class('-2.50')      ==>  \"-Normal\"" $+      op1 Op.class' "-2.50" `shouldBe` "-Normal"+    it "class('-Infinity')  ==>  \"-Infinity\"" $+      op1 Op.class' "-Infinity" `shouldBe` "-Infinity"+    it "class('NaN')        ==>  \"NaN\"" $+      op1 Op.class' "NaN" `shouldBe` "NaN"+    it "class('-NaN')       ==>  \"NaN\"" $+      op1 Op.class' "-NaN" `shouldBe` "NaN"+    it "class('sNaN')       ==>  \"sNaN\"" $+      op1 Op.class' "sNaN" `shouldBe` "sNaN"++  describe "compareTotal" $ do+    it "compare-total('12.73', '127.9')   ==>  '-1'" $+      op2 compareTotal' "12.73" "127.9" `shouldBe` "-1"+    it "compare-total('-127',  '12')      ==>  '-1'" $+      op2 compareTotal' "-127" "12" `shouldBe` "-1"+    it "compare-total('12.30', '12.3')    ==>  '-1'" $+      op2 compareTotal' "12.30" "12.3" `shouldBe` "-1"+    it "compare-total('12.30', '12.30')   ==>  '0'" $+      op2 compareTotal' "12.30" "12.30" `shouldBe` "0"+    it "compare-total('12.3',  '12.300')  ==>  '1'" $+      op2 compareTotal' "12.3" "12.300" `shouldBe` "1"+    it "compare-total('12.3',  'NaN')     ==>  '-1'" $+      op2 compareTotal' "12.3" "NaN" `shouldBe` "-1"++  describe "copy" $ do+    it "copy('2.1')    ==>  '2.1'" $+      op1 Op.copy "2.1" `shouldBe` "2.1"+    it "copy('-1.00')  ==>  '-1.00'" $+      op1 Op.copy "-1.00" `shouldBe` "-1.00"++  describe "copyAbs" $ do+    it "copy-abs('2.1')   ==>  '2.1'" $+      op1 Op.copyAbs "2.1" `shouldBe` "2.1"+    it "copy-abs('-100')  ==>  '100'" $+      op1 Op.copyAbs "-100" `shouldBe` "100"++  describe "copyNegate" $ do+    it "copy-negate('101.5')   ==>  '-101.5'" $+      op1 Op.copyNegate "101.5" `shouldBe` "-101.5"+    it "copy-negate('-101.5')  ==>  '101.5'" $+      op1 Op.copyNegate "-101.5" `shouldBe` "101.5"++  describe "copySign" $ do+    it "copy-sign( '1.50',  '7.33')  ==>  '1.50'" $+      op2 Op.copySign  "1.50"  "7.33" `shouldBe` "1.50"+    it "copy-sign('-1.50',  '7.33')  ==>  '1.50'" $+      op2 Op.copySign "-1.50"  "7.33" `shouldBe` "1.50"+    it "copy-sign( '1.50', '-7.33')  ==>  '-1.50'" $+      op2 Op.copySign  "1.50" "-7.33" `shouldBe` "-1.50"+    it "copy-sign('-1.50', '-7.33')  ==>  '-1.50'" $+      op2 Op.copySign "-1.50" "-7.33" `shouldBe` "-1.50"++  describe "isCanonical" $+    it "is-canonical('2.50')  ==>  '1'" $+      pred1 Op.isCanonical "2.50" `shouldBe` "1"++  describe "isFinite" $ do+    it "is-finite('2.50')  ==>  '1'" $+      pred1 Op.isFinite "2.50" `shouldBe` "1"+    it "is-finite('-0.3')  ==>  '1'" $+      pred1 Op.isFinite "-0.3" `shouldBe` "1"+    it "is-finite('0')     ==>  '1'" $+      pred1 Op.isFinite "0" `shouldBe` "1"+    it "is-finite('Inf')   ==>  '0'" $+      pred1 Op.isFinite "Inf" `shouldBe` "0"+    it "is-finite('NaN')   ==>  '0'" $+      pred1 Op.isFinite "NaN" `shouldBe` "0"++  describe "isInfinite" $ do+    it "is-infinite('2.50')  ==>  '0'" $+      pred1 Op.isInfinite "2.50" `shouldBe` "0"+    it "is-infinite('-Inf')  ==>  '1'" $+      pred1 Op.isInfinite "-Inf" `shouldBe` "1"+    it "is-infinite('NaN')   ==>  '0'" $+      pred1 Op.isInfinite "NaN" `shouldBe` "0"++  describe "isNaN" $ do+    it "is-NaN('2.50')   ==>  '0'" $+      pred1 Op.isNaN "2.50" `shouldBe` "0"+    it "is-NaN('NaN')    ==>  '1'" $+      pred1 Op.isNaN "NaN" `shouldBe` "1"+    it "is-NaN('-sNaN')  ==>  '1'" $+      pred1 Op.isNaN "-sNaN" `shouldBe` "1"++  describe "isNormal" $ do+    it "is-normal('2.50')      ==>  '1'" $+      pred1 Op.isNormal "2.50" `shouldBe` "1"+    it "is-normal('0.1E-999')  ==>  '0'" $+      pred1 Op.isNormal "0.1E-999" `shouldBe` "0"+    it "is-normal('0.00')      ==>  '0'" $+      pred1 Op.isNormal "0.00" `shouldBe` "0"+    it "is-normal('-Inf')      ==>  '0'" $+      pred1 Op.isNormal "-Inf" `shouldBe` "0"+    it "is-normal('NaN')       ==>  '0'" $+      pred1 Op.isNormal "NaN" `shouldBe` "0"++  describe "isQNaN" $ do+    it "is-qNaN('2.50')  ==>  '0'" $+      pred1 Op.isQNaN "2.50" `shouldBe` "0"+    it "is-qNaN('NaN')   ==>  '1'" $+      pred1 Op.isQNaN "NaN" `shouldBe` "1"+    it "is-qNaN('sNaN')  ==>  '0'" $+      pred1 Op.isQNaN "sNaN" `shouldBe` "0"++  describe "isSigned" $ do+    it "is-signed('2.50')  ==>  '0'" $+      pred1 Op.isSigned "2.50" `shouldBe` "0"+    it "is-signed('-12')   ==>  '1'" $+      pred1 Op.isSigned "-12" `shouldBe` "1"+    it "is-signed('-0')    ==>  '1'" $+      pred1 Op.isSigned "-0" `shouldBe` "1"++  describe "isSNaN" $ do+    it "is-sNaN('2.50')  ==>  '0'" $+      pred1 Op.isSNaN "2.50" `shouldBe` "0"+    it "is-sNaN('NaN')   ==>  '0'" $+      pred1 Op.isSNaN "NaN" `shouldBe` "0"+    it "is-sNaN('sNaN')  ==>  '1'" $+      pred1 Op.isSNaN "sNaN" `shouldBe` "1"++  describe "isSubnormal" $ do+    it "is-subnormal('2.50')      ==>  '0'" $+      pred1 Op.isSubnormal "2.50" `shouldBe` "0"+    it "is-subnormal('0.1E-999')  ==>  '1'" $+      pred1 Op.isSubnormal "0.1E-999" `shouldBe` "1"+    it "is-subnormal('0.00')      ==>  '0'" $+      pred1 Op.isSubnormal "0.00" `shouldBe` "0"+    it "is-subnormal('-Inf')      ==>  '0'" $+      pred1 Op.isSubnormal "-Inf" `shouldBe` "0"+    it "is-subnormal('NaN')       ==>  '0'" $+      pred1 Op.isSubnormal "NaN" `shouldBe` "0"++  describe "isZero" $ do+    it "is-zero('0')      ==>  '1'" $+      pred1 Op.isZero "0" `shouldBe` "1"+    it "is-zero('2.50')   ==>  '0'" $+      pred1 Op.isZero "2.50" `shouldBe` "0"+    it "is-zero('-0E+2')  ==>  '1'" $+      pred1 Op.isZero "-0E+2" `shouldBe` "1"++  describe "logb" $ do+    it "logb('250')   ==>  '2'" $+      op1 Op.logb "250" `shouldBe` "2"+    it "logb('2.50')  ==>  '0'" $+      op1 Op.logb "2.50" `shouldBe` "0"+    it "logb('0.03')  ==>  '-2'" $+      op1 Op.logb "0.03" `shouldBe` "-2"+    it "logb('0')     ==>  '-Infinity'" $+      op1 Op.logb "0" `shouldBe` "-Infinity"++  describe "scaleb" $ do+    it "scaleb('7.50', '-2')  ==>  '0.0750'" $+      op2 Op.scaleb "7.50" "-2" `shouldBe` "0.0750"+    it "scaleb('7.50', '0')   ==>  '7.50'" $+      op2 Op.scaleb "7.50" "0" `shouldBe` "7.50"+    it "scaleb('7.50', '3')   ==>  '7.50E+3'" $+      op2 Op.scaleb "7.50" "3" `shouldBe` "7.50E+3"++  describe "radix" $+    it "radix()  ==>  '10'" $+      op0 Op.radix `shouldBe` "10"++  describe "sameQuantum" $ do+    it "samequantum('2.17', '0.001')  ==>  '0'" $+      pred2 Op.sameQuantum "2.17" "0.001" `shouldBe` "0"+    it "samequantum('2.17', '0.01')   ==>  '1'" $+      pred2 Op.sameQuantum "2.17" "0.01" `shouldBe` "1"+    it "samequantum('2.17', '0.1')    ==>  '0'" $+      pred2 Op.sameQuantum "2.17" "0.1" `shouldBe` "0"+    it "samequantum('2.17', '1')      ==>  '0'" $+      pred2 Op.sameQuantum "2.17" "1" `shouldBe` "0"+    it "samequantum('Inf', '-Inf')    ==>  '1'" $+      pred2 Op.sameQuantum "Inf" "-Inf" `shouldBe` "1"+    it "samequantum('NaN', 'NaN')     ==>  '1'" $+      pred2 Op.sameQuantum "NaN" "NaN" `shouldBe` "1"++  describe "shift" $ do+    it "shift('34', '8')          ==>  '400000000'" $+      op2 Op.shift "34" "8" `shouldBe` "400000000"+    it "shift('12', '9')          ==>  '0'" $+      op2 Op.shift "12" "9" `shouldBe` "0"+    it "shift('123456789', '-2')  ==>  '1234567'" $+      op2 Op.shift "123456789" "-2" `shouldBe` "1234567"+    it "shift('123456789', '0')   ==>  '123456789'" $+      op2 Op.shift "123456789" "0" `shouldBe` "123456789"+    it "shift('123456789', '+2')  ==>  '345678900'" $+      op2 Op.shift "123456789" "+2" `shouldBe` "345678900"++  describe "rotate" $ do+    it "rotate('34', '8')          ==>  '400000003'" $+      op2 Op.rotate "34" "8" `shouldBe` "400000003"+    it "rotate('12', '9')          ==>  '12'" $+      op2 Op.rotate "12" "9" `shouldBe` "12"+    it "rotate('123456789', '-2')  ==>  '891234567'" $+      op2 Op.rotate "123456789" "-2" `shouldBe` "891234567"+    it "rotate('123456789', '0')   ==>  '123456789'" $+      op2 Op.rotate "123456789" "0" `shouldBe` "123456789"+    it "rotate('123456789', '+2')  ==>  '345678912'" $+      op2 Op.rotate "123456789" "+2" `shouldBe` "345678912"++exceptionError :: Exception p r -> a+exceptionError = error . show . exceptionSignal++type BasicArith = Arith P9 RoundHalfUp++pred1 :: (BasicDecimal -> BasicArith Bool) -> String -> String+pred1 op x = either exceptionError (show . fromBool) $+  evalArith arith newContext+  where arith = op (read x)++pred2 :: (BasicDecimal -> BasicDecimal -> BasicArith Bool) -> String -> String+      -> String+pred2 op x y = either exceptionError (show . fromBool) $+  evalArith arith newContext+  where arith = op (read x) (read y)++op0 :: Show a => BasicArith a -> String+op0 op = either exceptionError show $ evalArith op newContext++op1 :: Show a => (BasicDecimal -> BasicArith a) -> String -> String+op1 op x = either exceptionError show $ evalArith arith newContext+  where arith = op (read x)++op2 :: Show a => (BasicDecimal -> BasicDecimal -> BasicArith a) -> String+    -> String -> String+op2 op x y = either exceptionError show $ evalArith arith newContext+  where arith = read x `op` read y++op3 :: Show a => (BasicDecimal -> BasicDecimal -> BasicDecimal -> BasicArith a)+    -> String -> String -> String -> String+op3 op x y z = either exceptionError show $ evalArith arith newContext+  where arith = op (read x) (read y) (read z)++compare' :: Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+compare' x y = either id fromOrdering <$> Op.compare x y++compareTotal' :: Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+compareTotal' x y = fromOrdering <$> Op.compareTotal x y
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}