    {-# OPTIONS_GHC -XNoImplicitPrelude #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Numeric
    -- Copyright   :  (c) The University of Glasgow 2002
    -- License     :  BSD-style (see the file libraries/base/LICENSE)
    --
    -- Maintainer  :  libraries@haskell.org
    -- Stability   :  provisional
    -- Portability :  portable
    --
    -- Odds and ends, mostly functions for reading and showing
    -- 'RealFloat'-like kind of values.
    --
    -----------------------------------------------------------------------------

    module Numeric (

            -- * Showing

            showSigned,       -- :: (Real a) => (a -> ShowS) -> Int -> a -> ShowS

            showIntAtBase,    -- :: Integral a => a -> (a -> Char) -> a -> ShowS
            showInt,          -- :: Integral a => a -> ShowS
            showHex,          -- :: Integral a => a -> ShowS
            showOct,          -- :: Integral a => a -> ShowS

            showEFloat,       -- :: (RealFloat a) => Maybe Int -> a -> ShowS
            showFFloat,       -- :: (RealFloat a) => Maybe Int -> a -> ShowS
            showGFloat,       -- :: (RealFloat a) => Maybe Int -> a -> ShowS
            showFloat,        -- :: (RealFloat a) => a -> ShowS

            floatToDigits,    -- :: (RealFloat a) => Integer -> a -> ([Int], Int)

            -- * Reading

            -- | /NB:/ 'readInt' is the \'dual\' of 'showIntAtBase',
            -- and 'readDec' is the \`dual\' of 'showInt'.
            -- The inconsistent naming is a historical accident.

            readSigned,       -- :: (Real a) => ReadS a -> ReadS a

            readInt,          -- :: (Integral a) => a -> (Char -> Bool)
                              --         -> (Char -> Int) -> ReadS a
            readDec,          -- :: (Integral a) => ReadS a
            readOct,          -- :: (Integral a) => ReadS a
            readHex,          -- :: (Integral a) => ReadS a

            readFloat,        -- :: (RealFloat a) => ReadS a

            lexDigits,        -- :: ReadS String

            -- * Miscellaneous

            fromRat,          -- :: (RealFloat a) => Rational -> a

            ) where

    #ifdef __GLASGOW_HASKELL__
    import GHC.Base
    import GHC.Read
    import GHC.Real
    import GHC.Float
    import GHC.Num
    import GHC.Show
    import Data.Maybe
    import Text.ParserCombinators.ReadP( ReadP, readP_to_S, pfail )
    import qualified Text.Read.Lex as L
    #else
    import Data.Char
    #endif

    #ifdef __HUGS__
    import Hugs.Prelude
    import Hugs.Numeric
    #endif

    #ifdef __GLASGOW_HASKELL__
    -- -----------------------------------------------------------------------------
    -- Reading

    -- | Reads an /unsigned/ 'Integral' value in an arbitrary base.
    readInt :: Num a
      => a                  -- ^ the base
      -> (Char -> Bool)     -- ^ a predicate distinguishing valid digits in this base
      -> (Char -> Int)      -- ^ a function converting a valid digit character to an 'Int'
      -> ReadS a
    readInt base isDigit valDigit = readP_to_S (L.readIntP base isDigit valDigit)

    -- | Read an unsigned number in octal notation.
    readOct :: Num a => ReadS a
    readOct = readP_to_S L.readOctP

    -- | Read an unsigned number in decimal notation.
    readDec :: Num a => ReadS a
    readDec = readP_to_S L.readDecP

    -- | Read an unsigned number in hexadecimal notation.
    -- Both upper or lower case letters are allowed.
    readHex :: Num a => ReadS a
    readHex = readP_to_S L.readHexP

    -- | Reads an /unsigned/ 'RealFrac' value,
    -- expressed in decimal scientific notation.
    readFloat :: RealFrac a => ReadS a
    readFloat = readP_to_S readFloatP

    readFloatP :: RealFrac a => ReadP a
    readFloatP =
      do tok <- L.lex
         case tok of
           L.Rat y  -> return (fromRational y)
           L.Int i  -> return (fromInteger i)
           _        -> pfail

    -- It's turgid to have readSigned work using list comprehensions,
    -- but it's specified as a ReadS to ReadS transformer
    -- With a bit of luck no one will use it.

    -- | Reads a /signed/ 'Real' value, given a reader for an unsigned value.
    readSigned :: (Real a) => ReadS a -> ReadS a
    readSigned readPos = readParen False read'
                         where read' r  = read'' r ++
                                          (do
                                            ("-",s) <- lex r
                                            (x,t)   <- read'' s
                                            return (-x,t))
                               read'' r = do
                                   (str,s) <- lex r
                                   (n,"")  <- readPos str
                                   return (n,s)

    -- -----------------------------------------------------------------------------
    -- Showing

    -- | Show /non-negative/ 'Integral' numbers in base 10.
    showInt :: Integral a => a -> ShowS
    showInt n0 cs0
        | n0 < 0    = error "Numeric.showInt: can't show negative numbers"
        | otherwise = go n0 cs0
        where
        go n cs
            | n < 10    = case unsafeChr (ord '0' + fromIntegral n) of
                c@(C# _) -> c:cs
            | otherwise = case unsafeChr (ord '0' + fromIntegral r) of
                c@(C# _) -> go q (c:cs)
            where
            (q,r) = n `quotRem` 10

    -- Controlling the format and precision of floats. The code that
    -- implements the formatting itself is in @PrelNum@ to avoid
    -- mutual module deps.

    {-# SPECIALIZE showEFloat ::
            Maybe Int -> Float  -> ShowS,
            Maybe Int -> Double -> ShowS #-}
    {-# SPECIALIZE showFFloat ::
            Maybe Int -> Float  -> ShowS,
            Maybe Int -> Double -> ShowS #-}
    {-# SPECIALIZE showGFloat ::
            Maybe Int -> Float  -> ShowS,
            Maybe Int -> Double -> ShowS #-}

    -- | Show a signed 'RealFloat' value
    -- using scientific (exponential) notation (e.g. @2.45e2@, @1.5e-3@).
    --
    -- In the call @'showEFloat' digs val@, if @digs@ is 'Nothing',
    -- the value is shown to full precision; if @digs@ is @'Just' d@,
    -- then at most @d@ digits after the decimal point are shown.
    showEFloat    :: (RealFloat a) => Maybe Int -> a -> ShowS

    -- | Show a signed 'RealFloat' value
    -- using standard decimal notation (e.g. @245000@, @0.0015@).
    --
    -- In the call @'showFFloat' digs val@, if @digs@ is 'Nothing',
    -- the value is shown to full precision; if @digs@ is @'Just' d@,
    -- then at most @d@ digits after the decimal point are shown.
    showFFloat    :: (RealFloat a) => Maybe Int -> a -> ShowS

    -- | Show a signed 'RealFloat' value
    -- using standard decimal notation for arguments whose absolute value lies
    -- between @0.1@ and @9,999,999@, and scientific notation otherwise.
    --
    -- In the call @'showGFloat' digs val@, if @digs@ is 'Nothing',
    -- the value is shown to full precision; if @digs@ is @'Just' d@,
    -- then at most @d@ digits after the decimal point are shown.
    showGFloat    :: (RealFloat a) => Maybe Int -> a -> ShowS

    showEFloat d x =  showString (formatRealFloat FFExponent d x)
    showFFloat d x =  showString (formatRealFloat FFFixed d x)
    showGFloat d x =  showString (formatRealFloat FFGeneric d x)
    #endif  /* __GLASGOW_HASKELL__ */

    -- ---------------------------------------------------------------------------
    -- Integer printing functions

    -- | Shows a /non-negative/ 'Integral' number using the base specified by the
    -- first argument, and the character representation specified by the second.
    showIntAtBase :: Integral a => a -> (Int -> Char) -> a -> ShowS
    showIntAtBase base toChr n0 r0
      | base <= 1 = error ("Numeric.showIntAtBase: applied to unsupported base " ++ show base)
      | n0 <  0   = error ("Numeric.showIntAtBase: applied to negative number " ++ show n0)
      | otherwise = showIt (quotRem n0 base) r0
       where
        showIt (n,d) r = seq c $ -- stricter than necessary
          case n of
            0 -> r'
            _ -> showIt (quotRem n base) r'
         where
          c  = toChr (fromIntegral d)
          r' = c : r

    -- | Show /non-negative/ 'Integral' numbers in base 16.
    showHex :: Integral a => a -> ShowS
    showHex = showIntAtBase 16 intToDigit

    -- | Show /non-negative/ 'Integral' numbers in base 8.
    showOct :: Integral a => a -> ShowS
    showOct = showIntAtBase 8  intToDigit