{-# LANGUAGE NoImplicitPrelude #-}
-- |
-- Module:       $HEADER$
-- Description:  Get number of digits of an Integer.
-- Copyright:    (c) 2016, Peter Trško
-- License:      BSD3
--
-- Stability:    experimental
-- Portability:  NoImplicitPrelude
--
-- Get number of digits of an 'Integer'.
--
-- /Since 0.2.0.0/
module Data.NumberLength.Integer
    (
    -- * Decimal (base 10)
      lengthInteger

    -- * Hexadecimal (base 16)
    , lengthIntegerHex
    )
  where

import Prelude
    ( Bounded(maxBound)
    , Integer
    , Integral(quot)
    , Num((+), fromInteger, negate)
    , (^)
    , fromIntegral
    )

import Data.Bool (otherwise)
import Data.Eq (Eq((==)))
import Data.Int (Int)
import Data.Ord (Ord((<)))

import Data.NumberLength.Int (lengthInt, lengthIntHex)
import Data.NumberLength.Internal (either32or64)


-- | Number of digits in a @number :: 'Integer'@ in base 10.
--
-- /Since 0.2.0.0/
lengthInteger :: Integer -> Int
lengthInteger n
  | n < 0     = go (negate (fromIntegral n))
  | otherwise = go (fromIntegral n)
  where
    go :: Integer -> Int
    go m
      | m < maxInt = lengthInt (fromInteger m)
      | otherwise  =
        let r = m `quot` (10 ^ maxIntDigits)
        in maxIntDigits + if r == 0 then 0 else lengthInteger r

    maxIntDigits :: Int
    maxIntDigits = 10 `either32or64` 19
{-# INLINE lengthInteger #-}

-- | Number of digits in a @number :: 'Integer'@ in base 16.
--
-- /Since 0.2.0.0/
lengthIntegerHex :: Integer -> Int
lengthIntegerHex n
  | n < 0     = go (negate n)
  | otherwise = go n
  where
    go :: Integer -> Int
    go m
      | m < maxInt = lengthIntHex (fromInteger m)
      | otherwise  =
        let r = m `quot` (16 ^ maxIntDigits)
        in maxIntDigits + if r == 0 then 0 else lengthIntegerHex r

    maxIntDigits :: Int
    maxIntDigits = 8 `either32or64` 16
{-# INLINE lengthIntegerHex #-}

-- | Maximum value of type 'Int' cast in to 'Integral'.
--
-- /Do not export this function!/
maxInt :: Integer
maxInt = fromIntegral (maxBound :: Int)