    {-# LANGUAGE CPP, NoImplicitPrelude #-}
    module Numeric.Compat (
      module Base
    , showBin
    , showFFloatAlt
    , showGFloatAlt
    , showHFloat
    , readBin
    ) where

    import Numeric as Base

    #if !(MIN_VERSION_base(4,16,0))
    import Data.Char (intToDigit)
    import Prelude
    import Text.ParserCombinators.ReadP (readP_to_S)
    import qualified Text.Read.Lex.Compat as L
    #endif

    #if !(MIN_VERSION_base(4,11,0))
    {- | Show a floating-point value in the hexadecimal format,
    similar to the @%a@ specifier in C's printf.

      >>> showHFloat (212.21 :: Double) ""
      "0x1.a86b851eb851fp7"
      >>> showHFloat (-12.76 :: Float) ""
      "-0x1.9851ecp3"
      >>> showHFloat (-0 :: Double) ""
      "-0x0p+0"
    -}
    showHFloat :: RealFloat a => a -> ShowS
    showHFloat = showString . fmt
      where
      fmt x
        | isNaN x                   = "NaN"
        | isInfinite x              = (if x < 0 then "-" else "") ++ "Infinity"
        | x < 0 || isNegativeZero x = '-' : cvt (-x)
        | otherwise                 = cvt x

      cvt x
        | x == 0 = "0x0p+0"
        | otherwise =
          case floatToDigits 2 x of
            r@([], _) -> error $ "Impossible happened: showHFloat: " ++ show r
            (d:ds, e) -> "0x" ++ show d ++ frac ds ++ "p" ++ show (e-1)

      -- Given binary digits, convert them to hex in blocks of 4
      -- Special case: If all 0's, just drop it.
      frac digits
        | allZ digits = ""
        | otherwise   = "." ++ hex digits
        where
        hex ds =
          case ds of
            []                -> ""
            [a]               -> hexDigit a 0 0 0 ""
            [a,b]             -> hexDigit a b 0 0 ""
            [a,b,c]           -> hexDigit a b c 0 ""
            a : b : c : d : r -> hexDigit a b c d (hex r)

      hexDigit a b c d = showHex (8*a + 4*b + 2*c + d)

      allZ xs = case xs of
                  x : more -> x == 0 && allZ more
                  []       -> True
    #endif

    #if !(MIN_VERSION_base(4,16,0))
    -- | Read an unsigned number in binary notation.
    --
    -- >>> readBin "10011"
    -- [(19,"")]
    readBin :: (Eq a, Num a) => ReadS a
    readBin = readP_to_S L.readBinP

    -- | Show /non-negative/ 'Integral' numbers in base 2.
    showBin :: (Integral a, Show a) => a -> ShowS
    showBin = showIntAtBase 2  intToDigit
    #endif