{-
 - Copyright (C) 2009-2010 Nick Bowler.
 -
 - License BSD2:  2-clause BSD license.  See LICENSE for full terms.
 - This is free software: you are free to change and redistribute it.
 - There is NO WARRANTY, to the extent permitted by law.
 -}

-- | Definition of the core floating point types and basic manipulation of
-- them.
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, OverlappingInstances #-}
{-# LANGUAGE MagicHash #-}
module Data.Floating.Types (
    Double(..), Float(..), FloatConvert(..)
) where

import Prelude hiding (Double, Float)
import qualified GHC.Exts as GHC
import GHC.Integer
import GHC.Prim
import Foreign.C

import Data.Ratio

import Unsafe.Coerce

-- | The Double type.  This is expected to be an identical declaration to
-- the one found in GHC.Prim.  We avoid simply using GHC's type because we need 
-- to define our own class instances.
data Double = D# Double#

-- | The Float type. 
data Float  = F# Float#

-- | Coercion to floating point types.
class FloatConvert a b where
    -- | Convert to a floating point type.  Conversions from integers and real
    -- types are provided, as well as conversions between floating point types.
    -- Conversions between floating point types preserve infinities, negative
    -- zeros and NaNs.
    toFloating :: a -> b

instance FloatConvert Double CDouble where
    toFloating = unsafeCoerce

instance FloatConvert CDouble Double where
    toFloating = unsafeCoerce

instance FloatConvert Float CFloat where
    toFloating = unsafeCoerce

instance FloatConvert CFloat Float where
    toFloating = unsafeCoerce

instance FloatConvert Double Float where
    toFloating (D# x) = F# (double2Float# x)

instance FloatConvert Float Double where
    toFloating (F# x) = D# (float2Double# x)

instance FloatConvert Integer Double where
    toFloating x = D# (doubleFromInteger x)

instance FloatConvert Integer Float where
    toFloating x = F# (floatFromInteger x)

instance Real a => FloatConvert a Double where
    toFloating x = D# (num /## denom) where
        !(D# num)   = toFloating . numerator   . toRational $ x
        !(D# denom) = toFloating . denominator . toRational $ x

instance Real a => FloatConvert a Float where
    toFloating x = F# (divideFloat# num denom) where
        !(F# num)   = toFloating . numerator   . toRational $ x
        !(F# denom) = toFloating . denominator . toRational $ x

instance FloatConvert a a where
    toFloating = id