{-|
Module      : Data.FastDigits.Internal
Copyright   : (c) Andrew Lelechenko, 2015-2016
License     : GPL-3
Maintainer  : andrew.lelechenko@gmail.com

-}

{-# LANGUAGE BangPatterns  #-}
{-# LANGUAGE CPP           #-}
{-# LANGUAGE MagicHash     #-}
{-# LANGUAGE UnboxedTuples #-}

module Data.FastDigits.Internal
  ( selectPower
  , selectPower'
  ) where

import Data.Bits (finiteBitSize)
import GHC.Exts (Word#, Word(..), timesWord2#, plusWord#, timesWord#)

-- | Take an integer base and return (pow, base^pow),
--   where base^pow <= maxBound and pow is as large as possible.
selectPower :: Word# -> (# Word#, Word# #)
selectPower :: Word# -> (# Word#, Word# #)
selectPower Word#
2##
  | forall b. FiniteBits b => b -> Int
finiteBitSize (Word
0 :: Word) forall a. Eq a => a -> a -> Bool
== Int
64
  = (# Word#
63##, Word#
9223372036854775808## #)
selectPower Word#
10##
  | forall b. FiniteBits b => b -> Int
finiteBitSize (Word
0 :: Word) forall a. Eq a => a -> a -> Bool
== Int
64
  = (# Word#
19##, Word#
10000000000000000000## #)

selectPower Word#
base = Word# -> (# Word#, Word# #)
go Word#
base
  where
    go :: Word# -> (# Word#, Word# #)
go Word#
pw = case Word# -> Word# -> (# Word#, Word# #)
timesWord2# Word#
pw Word#
pw of
        (# Word#
0##, Word#
pw2 #)
          -> let !(# Word#
n, Word#
pw2n #) = Word# -> (# Word#, Word# #)
go Word#
pw2 in
            case Word# -> Word# -> (# Word#, Word# #)
timesWord2# Word#
pw Word#
pw2n of
              (# Word#
0##, Word#
pw2n1 #) -> (#Word#
n Word# -> Word# -> Word#
`timesWord#` Word#
2## Word# -> Word# -> Word#
`plusWord#` Word#
1##, Word#
pw2n1 #)
              (# Word#, Word# #)
_ -> (# Word#
n Word# -> Word# -> Word#
`timesWord#` Word#
2##, Word#
pw2n #)
        (# Word#, Word# #)
_           -> (# Word#
1##, Word#
pw #)

-- | Take an integer base and return (pow, base^pow),
--   where base^pow <= maxBound and pow is as large as possible.
selectPower' :: Word -> (Word, Word)
selectPower' :: Word -> (Word, Word)
selectPower' (W# Word#
base) = (Word# -> Word
W# Word#
power, Word# -> Word
W# Word#
poweredBase)
  where
    !(# Word#
power, Word#
poweredBase #) = Word# -> (# Word#, Word# #)
selectPower Word#
base