    {-# LANGUAGE CPP               #-}
    {-# LANGUAGE MagicHash         #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# OPTIONS_GHC -fno-warn-orphans #-}
    {-|
    Module:      TextShow.Data.Integral
    Copyright:   (C) 2014-2017 Ryan Scott
    License:     BSD-style (see the file LICENSE)
    Maintainer:  Ryan Scott
    Stability:   Provisional
    Portability: GHC

    'TextShow' instances and monomorphic functions for integral types.

    /Since: 2/
    -}
    module TextShow.Data.Integral (
          showbIntegralPrec
        , showbIntAtBase
        , showbBin
        , showbHex
        , showbOct
        ) where

    import           Data.Char (intToDigit)
    import           Data.Int (Int8, Int16, Int32, Int64)
    import           Data.Text.Lazy.Builder (Builder, singleton)
    import           Data.Text.Lazy.Builder.Int (decimal)
    import           Data.Word (Word8, Word16, Word32, Word64)

    import           GHC.Exts (Int(I#))
    #if __GLASGOW_HASKELL__ >= 708
    import           GHC.Exts (isTrue#)
    import           GHC.Prim (Int#)
    #endif
    import           GHC.Prim ((<#), (>#))

    import           Prelude ()
    import           Prelude.Compat

    import           TextShow.Classes (TextShow(..))
    import           TextShow.Utils (toString)

    -- | Convert an 'Integral' type to a 'Builder' with the given precedence.
    --
    -- /Since: 2/
    showbIntegralPrec :: Integral a => Int -> a -> Builder
    showbIntegralPrec p = showbPrec p . toInteger
    {-# INLINE showbIntegralPrec #-}

    -- | Shows a /non-negative/ 'Integral' number using the base specified by the
    -- first argument, and the character representation specified by the second.
    --
    -- /Since: 2/
    showbIntAtBase :: (Integral a, TextShow a) => a -> (Int -> Char) -> a -> Builder
    {-# SPECIALIZE showbIntAtBase :: Int     -> (Int -> Char) -> Int     -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Int8    -> (Int -> Char) -> Int8    -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Int16   -> (Int -> Char) -> Int16   -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Int32   -> (Int -> Char) -> Int32   -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Int64   -> (Int -> Char) -> Int64   -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Integer -> (Int -> Char) -> Integer -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Word    -> (Int -> Char) -> Word    -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Word8   -> (Int -> Char) -> Word8   -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Word16  -> (Int -> Char) -> Word16  -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Word32  -> (Int -> Char) -> Word32  -> Builder #-}
    {-# SPECIALIZE showbIntAtBase :: Word64  -> (Int -> Char) -> Word64  -> Builder #-}
    showbIntAtBase base toChr n0
        | base <= 1 = error . toString $ "TextShow.Int.showbIntAtBase: applied to unsupported base" <> showb base
        | n0 < 0    = error . toString $ "TextShow.Int.showbIntAtBase: applied to negative number " <> showb n0
        | otherwise = showbIt (quotRem n0 base) mempty
      where
        showbIt (n, d) b = seq c $ -- stricter than necessary
            case n of
                 0 -> b'
                 _ -> showbIt (quotRem n base) b'
          where
            c :: Char
            c = toChr $ fromIntegral d

            b' :: Builder
            b' = singleton c <> b

    -- | Show /non-negative/ 'Integral' numbers in base 2.
    --
    -- /Since: 2/
    showbBin :: (Integral a, TextShow a) => a -> Builder
    showbBin = showbIntAtBase 2 intToDigit
    {-# INLINE showbBin #-}

    -- | Show /non-negative/ 'Integral' numbers in base 16.
    --
    -- /Since: 2/
    showbHex :: (Integral a, TextShow a) => a -> Builder
    showbHex = showbIntAtBase 16 intToDigit
    {-# INLINE showbHex #-}

    -- | Show /non-negative/ 'Integral' numbers in base 8.
    --
    -- /Since: 2/
    showbOct :: (Integral a, TextShow a) => a -> Builder
    showbOct = showbIntAtBase 8 intToDigit
    {-# INLINE showbOct #-}

    -- | /Since: 2/
    instance TextShow Int where
        showbPrec (I# p) n'@(I# n)
            | isTrue (n <# 0#) && isTrue (p ># 6#)
            = singleton '(' <> decimal n' <> singleton ')'
            | otherwise
            = decimal n'
          where
    #if __GLASGOW_HASKELL__ >= 708
            isTrue :: Int# -> Bool
            isTrue b = isTrue# b
    #else
            isTrue :: Bool -> Bool
            isTrue = id
    #endif

    -- | /Since: 2/
    instance TextShow Int8 where
        showbPrec p x = showbPrec p (fromIntegral x :: Int)
        {-# INLINE showbPrec #-}

    -- | /Since: 2/
    instance TextShow Int16 where
        showbPrec p x = showbPrec p (fromIntegral x :: Int)
        {-# INLINE showbPrec #-}

    -- | /Since: 2/
    instance TextShow Int32 where
        showbPrec p x = showbPrec p (fromIntegral x :: Int)
        {-# INLINE showbPrec #-}

    -- | /Since: 2/
    instance TextShow Int64 where
    #if WORD_SIZE_IN_BITS < 64
        showbPrec p   = showbPrec p . toInteger
    #else
        showbPrec p x = showbPrec p (fromIntegral x :: Int)
    #endif
        {-# INLINE showbPrec #-}

    -- | /Since: 2/
    instance TextShow Integer where
        showbPrec p n
            | p > 6 && n < 0 = singleton '(' <> decimal n <> singleton ')'
            | otherwise      = decimal n
        {-# INLINE showbPrec #-}

    -- | /Since: 2/
    instance TextShow Word where
        showb = decimal
        {-# INLINE showb #-}

    -- | /Since: 2/
    instance TextShow Word8 where
        showb = decimal
        {-# INLINE showb #-}

    -- | /Since: 2/
    instance TextShow Word16 where
        showb = decimal
        {-# INLINE showb #-}

    -- | /Since: 2/
    instance TextShow Word32 where
        showb = decimal
        {-# INLINE showb #-}

    -- | /Since: 2/
    instance TextShow Word64 where
        showb = decimal
        {-# INLINE showb #-}