    {- |
        Copyright  : Copyright (C) 2006-2018 Bjorn Buckwalter
        License    : BSD3

        Maintainer : bjorn@buckwalter.se
        Stability  : Stable

    Defines convenience functions for inspecting and manipulating quantities with 'RealFloat'
    floating-point representations.

    The dimensionally-typed versions of functions from Patrick Perry's [ieee754](https://hackage.haskell.org/package/ieee754) package
    copy that packages API as closely as possible, by permission. In turn they are based on
    the @tango@ math library for the D language.

    -}

    {-# LANGUAGE ScopedTypeVariables #-}

    module Numeric.Units.Dimensional.Float
    (
      -- * Lifted Predicates from 'RealFloat'
      isDenormalized, isInfinite, isNaN, isNegativeZero
      -- * Convenience Functions
    , isFiniteNumber, scaleFloat
      -- * Lifted Functions from "Numeric.IEEE"
      -- ** Values
    , infinity, minNormal, maxFinite, epsilon, nan
      -- ** Arithmetic
    , predIEEE, succIEEE, bisectIEEE, copySign
      -- ** NaN with Payload
    , nanWithPayload, nanPayload, F.maxNaNPayload
      -- ** Comparisons
    , identicalIEEE, minNum, maxNum, minNaN, maxNaN
    )
    where

    import Control.Applicative
    import Data.Word (Word64)
    import Prelude (RealFloat)
    import qualified Prelude as P
    import Numeric.IEEE (IEEE)
    import qualified Numeric.IEEE as F
    import Numeric.Units.Dimensional.Internal (liftQ, liftQ2)
    import Numeric.Units.Dimensional.Prelude hiding (RealFloat(..))
    import Numeric.Units.Dimensional.Coercion

    -- $setup
    -- >>> :set -fno-warn-type-defaults
    -- >>> :set -XExtendedDefaultRules
    -- >>> :set -XNegativeLiterals

    -- | 'True' if the representation of the argument is too small to be represented in normalized format.
    isDenormalized :: RealFloat a => Quantity d a -> Bool
    isDenormalized :: forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isDenormalized = a -> Bool
    forall a. RealFloat a => a -> Bool
    P.isDenormalized (a -> Bool) -> (Quantity d a -> a) -> Quantity d a -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Quantity d a -> a
    forall (s :: ExactPi') (d :: Dimension) a. SQuantity s d a -> a
    unQuantity

    -- | 'True' if the representation of the argument is a number and is not infinite.
    --
    -- >>> isFiniteNumber (_1 / _0)
    -- False
    --
    -- >>> isFiniteNumber (_0 / _0)
    -- False
    --
    -- >>> isFiniteNumber (_3 / _2)
    -- True
    isFiniteNumber :: RealFloat a => Quantity d a -> Bool
    isFiniteNumber :: forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isFiniteNumber = Bool -> Bool
    not (Bool -> Bool) -> (Quantity d a -> Bool) -> Quantity d a -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . (Bool -> Bool -> Bool)
    -> (Quantity d a -> Bool)
    -> (Quantity d a -> Bool)
    -> Quantity d a
    -> Bool
    forall a b c.
    (a -> b -> c)
    -> (Quantity d a -> a) -> (Quantity d a -> b) -> Quantity d a -> c
    forall (f :: Type -> Type) a b c.
    Applicative f =>
    (a -> b -> c) -> f a -> f b -> f c
    liftA2 Bool -> Bool -> Bool
    (||) Quantity d a -> Bool
    forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isNaN Quantity d a -> Bool
    forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isInfinite

    -- | 'True' if the representation of the argument is an IEEE infinity or negative infinity.
    --
    -- >>> isInfinite (_1 / _0)
    -- True
    --
    -- >>> isInfinite (42 *~ micro farad)
    -- False
    isInfinite :: RealFloat a => Quantity d a -> Bool
    isInfinite :: forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isInfinite = a -> Bool
    forall a. RealFloat a => a -> Bool
    P.isInfinite (a -> Bool) -> (Quantity d a -> a) -> Quantity d a -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Quantity d a -> a
    forall (s :: ExactPi') (d :: Dimension) a. SQuantity s d a -> a
    unQuantity

    -- | 'True' if the representation of the argument is an IEEE "not-a-number" (NaN) value.
    --
    -- >>> isNaN _3
    -- False
    --
    -- >>> isNaN (_1 / _0)
    -- False
    --
    -- >>> isNaN (asin _4)
    -- True
    isNaN :: RealFloat a => Quantity d a -> Bool
    isNaN :: forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isNaN = a -> Bool
    forall a. RealFloat a => a -> Bool
    P.isNaN (a -> Bool) -> (Quantity d a -> a) -> Quantity d a -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Quantity d a -> a
    forall (s :: ExactPi') (d :: Dimension) a. SQuantity s d a -> a
    unQuantity

    -- | 'True' if the representation of the argument is an IEEE negative zero.
    --
    -- >>> isNegativeZero _0
    -- False
    --
    -- >>> isNegativeZero $ (-1e-200 *~ one) * (1e-200 *~ one)
    -- True
    isNegativeZero :: RealFloat a => Quantity d a -> Bool
    isNegativeZero :: forall a (d :: Dimension). RealFloat a => Quantity d a -> Bool
    isNegativeZero = a -> Bool
    forall a. RealFloat a => a -> Bool
    P.isNegativeZero (a -> Bool) -> (Quantity d a -> a) -> Quantity d a -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Quantity d a -> a
    forall (s :: ExactPi') (d :: Dimension) a. SQuantity s d a -> a
    unQuantity

    -- | Multiplies a floating-point quantity by an integer power of the radix of the representation type.
    --
    -- Use 'P.floatRadix' to determine the radix.
    --
    -- >>> let x = 3 *~ meter
    -- >>> scaleFloat 3 x
    -- 24.0 m
    scaleFloat :: RealFloat a => Int -> Quantity d a -> Quantity d a
    scaleFloat :: forall a (d :: Dimension).
    RealFloat a =>
    Int -> Quantity d a -> Quantity d a
    scaleFloat Int
    x = a -> Quantity d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity (a -> Quantity d a)
    -> (Quantity d a -> a) -> Quantity d a -> Quantity d a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Int -> a -> a
    forall a. RealFloat a => Int -> a -> a
    P.scaleFloat Int
    x (a -> a) -> (Quantity d a -> a) -> Quantity d a -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Quantity d a -> a
    forall (s :: ExactPi') (d :: Dimension) a. SQuantity s d a -> a
    unQuantity

    -- | An infinite floating-point quantity.
    infinity :: IEEE a => Quantity d a
    infinity :: forall a (d :: Dimension). IEEE a => Quantity d a
    infinity = a -> Dimensional ('DQuantity One) d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity a
    forall a. IEEE a => a
    F.infinity

    -- | The smallest representable positive quantity whose representation is normalized.
    minNormal :: IEEE a => Quantity d a
    minNormal :: forall a (d :: Dimension). IEEE a => Quantity d a
    minNormal = a -> Dimensional ('DQuantity One) d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity a
    forall a. IEEE a => a
    F.minNormal

    -- | The largest representable finite floating-point quantity.
    maxFinite :: IEEE a => Quantity d a
    maxFinite :: forall a (d :: Dimension). IEEE a => Quantity d a
    maxFinite = a -> Dimensional ('DQuantity One) d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity a
    forall a. IEEE a => a
    F.maxFinite

    -- | The smallest positive value @x@ such that @_1 + x@ is representable.
    epsilon :: IEEE a => Dimensionless a
    epsilon :: forall a. IEEE a => Dimensionless a
    epsilon = a -> Dimensional ('DQuantity One) DOne a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity a
    forall a. IEEE a => a
    F.epsilon

    -- | @copySign x y@ returns the quantity @x@ with its sign changed to match that of @y@.
    copySign :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a
    copySign :: forall a (d :: Dimension).
    IEEE a =>
    Quantity d a -> Quantity d a -> Quantity d a
    copySign = (a -> a -> a)
    -> SQuantity One d a -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension) (s3 :: ExactPi') (d3 :: Dimension).
    (a -> a -> a)
    -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a
    liftQ2 a -> a -> a
    forall a. IEEE a => a -> a -> a
    F.copySign

    -- | Return 'True' if two floating-point quantities are /exactly/ (bitwise) equal.
    identicalIEEE :: IEEE a => Quantity d a -> Quantity d a -> Bool
    identicalIEEE :: forall a (d :: Dimension).
    IEEE a =>
    Quantity d a -> Quantity d a -> Bool
    identicalIEEE (Quantity a
    x) (Quantity a
    y) = a -> a -> Bool
    forall a. IEEE a => a -> a -> Bool
    F.identicalIEEE a
    x a
    y

    -- | Return the next largest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged).
    succIEEE :: IEEE a => Quantity d a -> Quantity d a
    succIEEE :: forall a (d :: Dimension). IEEE a => Quantity d a -> Quantity d a
    succIEEE = (a -> a) -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension).
    (a -> a) -> SQuantity s1 d1 a -> SQuantity s2 d2 a
    liftQ a -> a
    forall a. IEEE a => a -> a
    F.succIEEE

    -- | Return the next smallest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged).
    predIEEE :: IEEE a => Quantity d a -> Quantity d a
    predIEEE :: forall a (d :: Dimension). IEEE a => Quantity d a -> Quantity d a
    predIEEE = (a -> a) -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension).
    (a -> a) -> SQuantity s1 d1 a -> SQuantity s2 d2 a
    liftQ a -> a
    forall a. IEEE a => a -> a
    F.predIEEE

    -- | Given two floating-point quantities with the same sign, return the quantity whose representation is halfway
    -- between their representations on the IEEE number line. If the signs of the values differ or either is @NaN@,
    -- the value is undefined.
    bisectIEEE :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a
    bisectIEEE :: forall a (d :: Dimension).
    IEEE a =>
    Quantity d a -> Quantity d a -> Quantity d a
    bisectIEEE (Quantity a
    x) (Quantity a
    y) = a -> Quantity d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity (a -> Quantity d a) -> a -> Quantity d a
    forall a b. (a -> b) -> a -> b
    $ a -> a -> a
    forall a. IEEE a => a -> a -> a
    F.bisectIEEE a
    x a
    y

    -- | Default @NaN@ quantity.
    nan :: IEEE a => Quantity d a
    nan :: forall a (d :: Dimension). IEEE a => Quantity d a
    nan = a -> Dimensional ('DQuantity One) d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity a
    forall a. IEEE a => a
    F.nan

    -- | Quiet @NaN@ quantity with a positive integer payload.
    -- Payload must be less than 'maxNaNPayload' of the representation type.
    --
    -- Beware that while some platforms allow using 0 as a payload, this behavior is not portable.
    nanWithPayload :: IEEE a => Word64 -> Quantity d a
    nanWithPayload :: forall a (d :: Dimension). IEEE a => Word64 -> Quantity d a
    nanWithPayload = a -> Quantity d a
    forall (s :: ExactPi') (d :: Dimension) a.
    a -> Dimensional ('DQuantity s) d a
    Quantity (a -> Quantity d a) -> (Word64 -> a) -> Word64 -> Quantity d a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Word64 -> a
    forall a. IEEE a => Word64 -> a
    F.nanWithPayload

    -- | The payload stored in a @NaN@ quantity. Undefined if the argument is not @NaN@.
    nanPayload :: IEEE a => Quantity d a -> Word64
    nanPayload :: forall a (d :: Dimension). IEEE a => Quantity d a -> Word64
    nanPayload = a -> Word64
    forall a. IEEE a => a -> Word64
    F.nanPayload (a -> Word64) -> (Quantity d a -> a) -> Quantity d a -> Word64
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . Quantity d a -> a
    forall (s :: ExactPi') (d :: Dimension) a. SQuantity s d a -> a
    unQuantity

    -- | Return the minimum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@.
    minNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a
    minNum :: forall a (d :: Dimension).
    RealFloat a =>
    Quantity d a -> Quantity d a -> Quantity d a
    minNum = (a -> a -> a)
    -> SQuantity One d a -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension) (s3 :: ExactPi') (d3 :: Dimension).
    (a -> a -> a)
    -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a
    liftQ2 a -> a -> a
    forall a. RealFloat a => a -> a -> a
    F.minNum

    -- | Return the maximum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@.
    maxNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a
    maxNum :: forall a (d :: Dimension).
    RealFloat a =>
    Quantity d a -> Quantity d a -> Quantity d a
    maxNum = (a -> a -> a)
    -> SQuantity One d a -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension) (s3 :: ExactPi') (d3 :: Dimension).
    (a -> a -> a)
    -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a
    liftQ2 a -> a -> a
    forall a. RealFloat a => a -> a -> a
    F.maxNum

    -- | Return the minimum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@.
    minNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a
    minNaN :: forall a (d :: Dimension).
    RealFloat a =>
    Quantity d a -> Quantity d a -> Quantity d a
    minNaN = (a -> a -> a)
    -> SQuantity One d a -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension) (s3 :: ExactPi') (d3 :: Dimension).
    (a -> a -> a)
    -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a
    liftQ2 a -> a -> a
    forall a. RealFloat a => a -> a -> a
    F.minNaN

    -- | Return the maximum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@.
    maxNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a
    maxNaN :: forall a (d :: Dimension).
    RealFloat a =>
    Quantity d a -> Quantity d a -> Quantity d a
    maxNaN = (a -> a -> a)
    -> SQuantity One d a -> SQuantity One d a -> SQuantity One d a
    forall a (s1 :: ExactPi') (d1 :: Dimension) (s2 :: ExactPi')
           (d2 :: Dimension) (s3 :: ExactPi') (d3 :: Dimension).
    (a -> a -> a)
    -> SQuantity s1 d1 a -> SQuantity s2 d2 a -> SQuantity s3 d3 a
    liftQ2 a -> a -> a
    forall a. RealFloat a => a -> a -> a
    F.maxNaN