    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    {-# LANGUAGE LambdaCase #-}

    -- | Double datatype with saner instances
    module GHC.Types.SaneDouble
      ( SaneDouble (..)
      )
    where

    import GHC.Prelude
    import GHC.Utils.Binary
    import GHC.Float (castDoubleToWord64, castWord64ToDouble)

    -- | A newtype wrapper around 'Double' to ensure we never generate a 'Double'
    -- that becomes a 'NaN', see instances for details on sanity.
    newtype SaneDouble = SaneDouble
      { SaneDouble -> Double
    unSaneDouble :: Double
      }
      deriving (Num SaneDouble
    Num SaneDouble =>
    (SaneDouble -> SaneDouble -> SaneDouble)
    -> (SaneDouble -> SaneDouble)
    -> (Rational -> SaneDouble)
    -> Fractional SaneDouble
    Rational -> SaneDouble
    SaneDouble -> SaneDouble
    SaneDouble -> SaneDouble -> SaneDouble
    forall a.
    Num a =>
    (a -> a -> a) -> (a -> a) -> (Rational -> a) -> Fractional a
    $c/ :: SaneDouble -> SaneDouble -> SaneDouble
    / :: SaneDouble -> SaneDouble -> SaneDouble
    $crecip :: SaneDouble -> SaneDouble
    recip :: SaneDouble -> SaneDouble
    $cfromRational :: Rational -> SaneDouble
    fromRational :: Rational -> SaneDouble
    Fractional, Integer -> SaneDouble
    SaneDouble -> SaneDouble
    SaneDouble -> SaneDouble -> SaneDouble
    (SaneDouble -> SaneDouble -> SaneDouble)
    -> (SaneDouble -> SaneDouble -> SaneDouble)
    -> (SaneDouble -> SaneDouble -> SaneDouble)
    -> (SaneDouble -> SaneDouble)
    -> (SaneDouble -> SaneDouble)
    -> (SaneDouble -> SaneDouble)
    -> (Integer -> SaneDouble)
    -> Num SaneDouble
    forall a.
    (a -> a -> a)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> (a -> a)
    -> (a -> a)
    -> (a -> a)
    -> (Integer -> a)
    -> Num a
    $c+ :: SaneDouble -> SaneDouble -> SaneDouble
    + :: SaneDouble -> SaneDouble -> SaneDouble
    $c- :: SaneDouble -> SaneDouble -> SaneDouble
    - :: SaneDouble -> SaneDouble -> SaneDouble
    $c* :: SaneDouble -> SaneDouble -> SaneDouble
    * :: SaneDouble -> SaneDouble -> SaneDouble
    $cnegate :: SaneDouble -> SaneDouble
    negate :: SaneDouble -> SaneDouble
    $cabs :: SaneDouble -> SaneDouble
    abs :: SaneDouble -> SaneDouble
    $csignum :: SaneDouble -> SaneDouble
    signum :: SaneDouble -> SaneDouble
    $cfromInteger :: Integer -> SaneDouble
    fromInteger :: Integer -> SaneDouble
    Num)

    instance Eq SaneDouble where
        (SaneDouble Double
    x) == :: SaneDouble -> SaneDouble -> Bool
    == (SaneDouble Double
    y) = Double
    x Double -> Double -> Bool
    forall a. Eq a => a -> a -> Bool
    == Double
    y Bool -> Bool -> Bool
    || (Double -> Bool
    forall a. RealFloat a => a -> Bool
    isNaN Double
    x Bool -> Bool -> Bool
    && Double -> Bool
    forall a. RealFloat a => a -> Bool
    isNaN Double
    y)

    instance Ord SaneDouble where
        compare :: SaneDouble -> SaneDouble -> Ordering
    compare (SaneDouble Double
    x) (SaneDouble Double
    y) = Maybe Double -> Maybe Double -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare (Double -> Maybe Double
    forall {a}. RealFloat a => a -> Maybe a
    fromNaN Double
    x) (Double -> Maybe Double
    forall {a}. RealFloat a => a -> Maybe a
    fromNaN Double
    y)
            where fromNaN :: a -> Maybe a
    fromNaN a
    z | a -> Bool
    forall a. RealFloat a => a -> Bool
    isNaN a
    z = Maybe a
    forall a. Maybe a
    Nothing
                            | Bool
    otherwise = a -> Maybe a
    forall a. a -> Maybe a
    Just a
    z

    instance Show SaneDouble where
        show :: SaneDouble -> String
    show (SaneDouble Double
    x) = Double -> String
    forall a. Show a => a -> String
    show Double
    x

    -- we need to preserve NaN and infinities, unfortunately the Binary instance for
    -- Double does not do this
    instance Binary SaneDouble where
      put_ :: BinHandle -> SaneDouble -> IO ()
    put_ BinHandle
    bh (SaneDouble Double
    d)
        | Double -> Bool
    forall a. RealFloat a => a -> Bool
    isNaN Double
    d               = BinHandle -> Word8 -> IO ()
    putByte BinHandle
    bh Word8
    1
        | Double -> Bool
    forall a. RealFloat a => a -> Bool
    isInfinite Double
    d Bool -> Bool -> Bool
    && Double
    d Double -> Double -> Bool
    forall a. Ord a => a -> a -> Bool
    > Double
    0 = BinHandle -> Word8 -> IO ()
    putByte BinHandle
    bh Word8
    2
        | Double -> Bool
    forall a. RealFloat a => a -> Bool
    isInfinite Double
    d Bool -> Bool -> Bool
    && Double
    d Double -> Double -> Bool
    forall a. Ord a => a -> a -> Bool
    < Double
    0 = BinHandle -> Word8 -> IO ()
    putByte BinHandle
    bh Word8
    3
        | Double -> Bool
    forall a. RealFloat a => a -> Bool
    isNegativeZero Double
    d      = BinHandle -> Word8 -> IO ()
    putByte BinHandle
    bh Word8
    4
        | Bool
    otherwise             = BinHandle -> Word8 -> IO ()
    putByte BinHandle
    bh Word8
    5 IO () -> IO () -> IO ()
    forall a b. IO a -> IO b -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> BinHandle -> Word64 -> IO ()
    forall a. Binary a => BinHandle -> a -> IO ()
    put_ BinHandle
    bh (Double -> Word64
    castDoubleToWord64 Double
    d)
      get :: BinHandle -> IO SaneDouble
    get BinHandle
    bh = BinHandle -> IO Word8
    getByte BinHandle
    bh IO Word8 -> (Word8 -> IO SaneDouble) -> IO SaneDouble
    forall a b. IO a -> (a -> IO b) -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
        Word8
    1 -> SaneDouble -> IO SaneDouble
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (SaneDouble -> IO SaneDouble) -> SaneDouble -> IO SaneDouble
    forall a b. (a -> b) -> a -> b
    $ Double -> SaneDouble
    SaneDouble (Double
    0    Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    0)
        Word8
    2 -> SaneDouble -> IO SaneDouble
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (SaneDouble -> IO SaneDouble) -> SaneDouble -> IO SaneDouble
    forall a b. (a -> b) -> a -> b
    $ Double -> SaneDouble
    SaneDouble (Double
    1    Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    0)
        Word8
    3 -> SaneDouble -> IO SaneDouble
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (SaneDouble -> IO SaneDouble) -> SaneDouble -> IO SaneDouble
    forall a b. (a -> b) -> a -> b
    $ Double -> SaneDouble
    SaneDouble ((-Double
    1) Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    0)
        Word8
    4 -> SaneDouble -> IO SaneDouble
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (SaneDouble -> IO SaneDouble) -> SaneDouble -> IO SaneDouble
    forall a b. (a -> b) -> a -> b
    $ Double -> SaneDouble
    SaneDouble (-Double
    0)
        Word8
    5 -> Double -> SaneDouble
    SaneDouble (Double -> SaneDouble)
    -> (Word64 -> Double) -> Word64 -> SaneDouble
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word64 -> Double
    castWord64ToDouble (Word64 -> SaneDouble) -> IO Word64 -> IO SaneDouble
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> BinHandle -> IO Word64
    forall a. Binary a => BinHandle -> IO a
    get BinHandle
    bh
        Word8
    n -> String -> IO SaneDouble
    forall a. HasCallStack => String -> a
    error (String
    "Binary get bh SaneDouble: invalid tag: " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Word8 -> String
    forall a. Show a => a -> String
    show Word8
    n)