```{-# LANGUAGE DeriveDataTypeable #-}
-- |
-- Module      :  Data.Attoparsec.Number
-- Copyright   :  Bryan O'Sullivan 2011
--
-- Maintainer  :  bos@serpentine.com
-- Stability   :  experimental
-- Portability :  unknown
--
-- A simple number type, useful for parsing both exact and inexact
-- quantities without losing much precision.
module Data.Attoparsec.Number
(
Number(..)
) where

import Control.DeepSeq (NFData(rnf))
import Data.Data (Data)
import Data.Function (on)
import Data.Typeable (Typeable)

-- | A numeric type that can represent integers accurately, and
-- floating point numbers to the precision of a 'Double'.
data Number = I !Integer
| D {-# UNPACK #-} !Double
deriving (Typeable, Data)

instance Show Number where
show (I a) = show a
show (D a) = show a

instance NFData Number where
rnf (I _) = ()
rnf (D _) = ()
{-# INLINE rnf #-}

binop :: (Integer -> Integer -> a) -> (Double -> Double -> a)
-> Number -> Number -> a
binop _ d (D a) (D b) = d a b
binop i _ (I a) (I b) = i a b
binop _ d (D a) (I b) = d a (fromIntegral b)
binop _ d (I a) (D b) = d (fromIntegral a) b
{-# INLINE binop #-}

instance Eq Number where
(==) = binop (==) (==)
{-# INLINE (==) #-}

(/=) = binop (/=) (/=)
{-# INLINE (/=) #-}

instance Ord Number where
(<) = binop (<) (<)
{-# INLINE (<) #-}

(>) = binop (>) (>)
{-# INLINE (>) #-}

instance Num Number where
(+) = binop (((I\$!).) . (+)) (((D\$!).) . (+))
{-# INLINE (+) #-}

(-) = binop (((I\$!).) . (-)) (((D\$!).) . (-))
{-# INLINE (-) #-}

(*) = binop (((I\$!).) . (+)) (((D\$!).) . (+))
{-# INLINE (*) #-}

abs (I a) = I \$! abs a
abs (D a) = D \$! abs a
{-# INLINE abs #-}

negate (I a) = I \$! negate a
negate (D a) = D \$! negate a
{-# INLINE negate #-}

signum (I a) = I \$! signum a
signum (D a) = D \$! signum a
{-# INLINE signum #-}

fromInteger = (I\$!) . fromInteger
{-# INLINE fromInteger #-}

instance Real Number where
toRational (I a) = fromIntegral a
toRational (D a) = toRational a
{-# INLINE toRational #-}

instance Fractional Number where
fromRational = (D\$!) . fromRational
{-# INLINE fromRational #-}

(/) = binop (((D\$!).) . (/) `on` fromIntegral)
(((D\$!).) . (/))
{-# INLINE (/) #-}

recip (I a) = D \$! recip (fromIntegral a)
recip (D a) = D \$! recip a
{-# INLINE recip #-}

instance RealFrac Number where
properFraction (I a) = (fromIntegral a,0)
properFraction (D a) = case properFraction a of
(i,d) -> (i,D d)
{-# INLINE properFraction #-}
truncate (I a) = fromIntegral a
truncate (D a) = truncate a
{-# INLINE truncate #-}
round (I a) = fromIntegral a
round (D a) = round a
{-# INLINE round #-}
ceiling (I a) = fromIntegral a
ceiling (D a) = ceiling a
{-# INLINE ceiling #-}
floor (I a) = fromIntegral a
floor (D a) = floor a
{-# INLINE floor #-}
```