-- |
-- Copyright:   (c) 2024 Pierre Le Marre
-- Licence:     BSD3
-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
module Data.Text.Builder.Linear.Dec.Unbounded (
  (|>$$),
  ($$<|),
  -- prependUnboundedDecimal,
  -- Strategy (..),
)
where

import Data.Bits (Bits (..), FiniteBits (..))
import Data.Text.Array qualified as A
import Data.Word (Word64)
import GHC.Exts (
  Int (..),
  Int#,
  State#,
  Word (..),
  Word#,
  word2Int#,
  (-#),
 )
import GHC.Num.BigNat qualified as BN
import GHC.Num.Integer qualified as I
import GHC.Num.Natural qualified as N
import GHC.Ptr (plusPtr)
import GHC.ST (ST (..))

import Data.Text.Builder.Linear.Array (unsafeReplicate)
import Data.Text.Builder.Linear.Core (Buffer)
import Data.Text.Builder.Linear.Dec.Bounded (digits, maxDecLen, quotRem100)
import Data.Text.Builder.Linear.Dec.Bounded qualified as Bounded
import Data.Text.Builder.Linear.Internal (appendBounded', prependBounded')

--------------------------------------------------------------------------------
-- Append
--------------------------------------------------------------------------------

-- | Append the decimal representation of an /unbounded/ integral number.
--
-- @since 0.1.3
(|>$$)  Integral a  Buffer  a  Buffer

infixl 6 |>$$
Buffer
buffer |>$$ :: forall a. Integral a => Buffer %1 -> a -> Buffer
|>$$ a
n = case a -> Integer
forall a. Integral a => a -> Integer
toInteger a
n of
  !Integer
n' 
    Int
-> (forall s x.
    ((MArray s -> Int -> ST s Int) -> ST s x)
    -> ((MArray s -> Int -> ST s Int) -> ST s x) -> ST s x)
-> Buffer
%1 -> Buffer
appendBounded'
      (Integer -> Int
maxIntegerDecLen Integer
n')
      (Integer
-> ((MArray s -> Int -> ST s Int) -> ST s x)
-> ((MArray s -> Int -> ST s Int) -> ST s x)
-> ST s x
forall s x.
Integer
-> ((MArray s -> Int -> ST s Int) -> ST s x)
-> ((MArray s -> Int -> ST s Int) -> ST s x)
-> ST s x
unsafeAppendDec Integer
n')
      Buffer
buffer
{-# INLINEABLE (|>$$) #-}

-- • For small 'Integers', `unsafeAppendDec`
-- • For 'BigNat's, use a buffer with `unsafePrependUnboundedDec`, then copy it.
--
-- For *bounded* integers we used the exact size of the decimal representation to
-- compute the offset from which we can use the prepend action to actually append.
--
-- But the exact size of an (unbounded) 'Integer' could be expensive to compute.
-- So it is faster to use a buffer and then copy it.
unsafeAppendDec
    s x
   . Integer
   ((A.MArray s  Int  ST s Int)  ST s x)
   ((A.MArray s  Int  ST s Int)  ST s x)
   ST s x
unsafeAppendDec :: forall s x.
Integer
-> ((MArray s -> Int -> ST s Int) -> ST s x)
-> ((MArray s -> Int -> ST s Int) -> ST s x)
-> ST s x
unsafeAppendDec Integer
n = case Integer
n of
  I.IS Int#
i#  \(MArray s -> Int -> ST s Int) -> ST s x
append (MArray s -> Int -> ST s Int) -> ST s x
_  (MArray s -> Int -> ST s Int) -> ST s x
append (\MArray s
marr Int
off  MArray s -> Int -> Int -> ST s Int
forall a s.
(Integral a, FiniteBits a) =>
MArray s -> Int -> a -> ST s Int
Bounded.unsafeAppendDec MArray s
marr Int
off (Int# -> Int
I# Int#
i#))
  Integer
_  \(MArray s -> Int -> ST s Int) -> ST s x
_ (MArray s -> Int -> ST s Int) -> ST s x
prepend  (MArray s -> Int -> ST s Int) -> ST s x
prepend (\MArray s
marr Int
off  MArray s -> Int -> Integer -> ST s Int
forall s. MArray s -> Int -> Integer -> ST s Int
unsafePrependDec MArray s
marr Int
off Integer
n)
{-# INLINEABLE unsafeAppendDec #-}

--------------------------------------------------------------------------------
-- Prepend
--------------------------------------------------------------------------------

-- | Prepend the decimal representation of an /unbounded/ integral number.
--
-- @since 0.1.3
($$<|)  Integral a  a  Buffer  Buffer

infixr 6 $$<|
a
n $$<| :: forall a. Integral a => a -> Buffer %1 -> Buffer
$$<| Buffer
buffer = case a -> Integer
forall a. Integral a => a -> Integer
toInteger a
n of
  !Integer
n' 
    Int
-> (forall s. MArray s -> Int -> ST s Int) -> Buffer %1 -> Buffer
prependBounded'
      (Integer -> Int
maxIntegerDecLen Integer
n')
      (\MArray s
dst Int
dstOff  MArray s -> Int -> Integer -> ST s Int
forall s. MArray s -> Int -> Integer -> ST s Int
unsafePrependDec MArray s
dst Int
dstOff Integer
n')
      Buffer
buffer
{-# INLINEABLE ($$<|) #-}

unsafePrependDec   s. A.MArray s  Int  Integer  ST s Int
unsafePrependDec :: forall s. MArray s -> Int -> Integer -> ST s Int
unsafePrependDec MArray s
marr off :: Int
off@(I# Int#
off#) Integer
n = case Integer
n of
  I.IS Int#
i#  MArray s -> Int -> Int -> ST s Int
forall s a.
(Integral a, FiniteBits a) =>
MArray s -> Int -> a -> ST s Int
Bounded.unsafePrependDec MArray s
marr Int
off (Int# -> Int
I# Int#
i#)
  Integer
_  MArray s -> DigitsWriter s
forall s. MArray s -> DigitsWriter s
unsafePrependBigNatDec MArray s
marr (Int#
off# Int# -> Int# -> Int#
-# Int#
1#) (Integer -> BigNat#
integerToBigNat# Integer
n) ST s Int -> (Int -> ST s Int) -> ST s Int
forall a b. ST s a -> (a -> ST s b) -> ST s b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Int -> ST s Int
prependSign
    where
      prependSign :: Int -> ST s Int
prependSign !Int
off' =
        if Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0
          then do
            MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Word8
0x2d -- '-'
            Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
off' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
          else Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
off')
{-# INLINEABLE unsafePrependDec #-}

type DigitsWriter s = Int#  BN.BigNat#  ST s Int

-- Use the fastest writer depending on the BigNat size
unsafePrependBigNatDec   s. A.MArray s  DigitsWriter s
unsafePrependBigNatDec :: forall s. MArray s -> DigitsWriter s
unsafePrependBigNatDec MArray s
marr !Int#
off0 !BigNat#
n0
  | BigNat# -> Word
BN.bigNatSize BigNat#
n0 Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
< Word
hugeSizeThreshold = MArray s -> DigitsWriter s
forall s. MArray s -> DigitsWriter s
prependSmallNat MArray s
marr Int#
off0 BigNat#
n0
  | Bool
otherwise = MArray s -> DigitsWriter s
forall s. MArray s -> DigitsWriter s
prependHugeNat MArray s
marr Int#
off0 BigNat#
n0
  where
    hugeSizeThreshold  Word
    hugeSizeThreshold :: Word
hugeSizeThreshold = Word
80

-- Writer for “small” 'BigNat's.
--
-- Divide repeatedly by poweredBase.
prependSmallNat   s. A.MArray s  DigitsWriter s
prependSmallNat :: forall s. MArray s -> DigitsWriter s
prependSmallNat MArray s
marr = DigitsWriter s
go
  where
    !(# Word#
power, Word#
poweredBase, BigNat#
_poweredBase² #) = (# #) -> (# Word#, Word#, BigNat# #)
selectPower (# #)

    go  DigitsWriter s
    go :: DigitsWriter s
go !Int#
o1 !BigNat#
n = case BigNat#
n BigNat# -> Word# -> (# BigNat#, Word# #)
`BN.bigNatQuotRemWord#` Word#
poweredBase of
      (# BigNat#
q, Word#
r #)  do
        !Int
o2  MArray s -> Int -> Word -> ST s Int
forall s. MArray s -> Int -> Word -> ST s Int
unsafePrependWordDec MArray s
marr (Int# -> Int
I# Int#
o1) (Word# -> Word
W# Word#
r)
        if BigNat# -> Bool
BN.bigNatIsZero BigNat#
q
          then Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
o2
          else do
            let !o3 :: Int#
o3 = Int#
o1 Int# -> Int# -> Int#
-# (Word# -> Int#
word2Int# Word#
power Int# -> Int# -> Int#
-# Int#
1#)
            MArray s -> Int -> Int -> ST s ()
forall s. MArray s -> Int -> Int -> ST s ()
padWithZeros MArray s
marr (Int# -> Int
I# Int#
o3) (Int
o2 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int# -> Int
I# Int#
o3)
            DigitsWriter s
go (Int#
o3 Int# -> Int# -> Int#
-# Int#
1#) BigNat#
q

-- Use the raw state in order to avoid boxed Int in `scaleWriter`
type DigitsWriter# s = Int#  BN.BigNat#  State# s  (# State# s, Int# #)

-- Writer for “huge” 'BigNat's.
--
-- Algorithm used in bytestring-0.12.1 (simplified):
--
-- 1. Find k0 = min k such that pow10 ^ (2 ^ (k + 1)) > n0
-- 2. Set k to k0 and n to n0
-- 3. Set (q, r) = n `quotRem` (pow10 ^ (2 ^ k))
-- 4. if k = 0, then write decimal representation of q and r
--    else repeat recursively 3 and 4 with n = {q,r} and k = k - 1
prependHugeNat   s. A.MArray s  DigitsWriter s
prependHugeNat :: forall s. MArray s -> DigitsWriter s
prependHugeNat MArray s
marr Int#
off BigNat#
n = STRep s Int -> ST s Int
forall s a. STRep s a -> ST s a
ST (STRep s Int -> ST s Int) -> STRep s Int -> ST s Int
forall a b. (a -> b) -> a -> b
$ \State# s
s1 
  case (Bool -> DigitsWriter# s) -> BigNat# -> DigitsWriter# s
go Bool -> DigitsWriter# s
prependTiny# BigNat#
poweredBase² Int#
off BigNat#
n State# s
s1 of
    (# State# s
s2, Int#
off'# #)  (# State# s
s2, Int# -> Int
I# Int#
off'# #)
  where
    !(# Word#
power, Word#
poweredBase, BigNat#
poweredBase² #) = (# #) -> (# Word#, Word#, BigNat# #)
selectPower (# #)

    go  (Bool  DigitsWriter# s)  BN.BigNat#  DigitsWriter# s
    go :: (Bool -> DigitsWriter# s) -> BigNat# -> DigitsWriter# s
go !Bool -> DigitsWriter# s
write !BigNat#
pow10 !Int#
o !BigNat#
n# =
      if BigNat# -> BigNat# -> Bool
BN.bigNatLt BigNat#
n# BigNat#
pow10
        then Bool -> DigitsWriter# s
write Bool
True Int#
o BigNat#
n#
        else (Bool -> DigitsWriter# s) -> BigNat# -> DigitsWriter# s
go ((Bool -> DigitsWriter# s) -> BigNat# -> Bool -> DigitsWriter# s
scaleWriter Bool -> DigitsWriter# s
write BigNat#
pow10) (BigNat# -> BigNat# -> BigNat#
BN.bigNatMul BigNat#
pow10 BigNat#
pow10) Int#
o BigNat#
n#

    scaleWriter  (Bool  DigitsWriter# s)  BN.BigNat#  Bool  DigitsWriter# s
    scaleWriter :: (Bool -> DigitsWriter# s) -> BigNat# -> Bool -> DigitsWriter# s
scaleWriter !Bool -> DigitsWriter# s
write !BigNat#
pow10 = \ !Bool
high !Int#
o1 !BigNat#
n# State# s
s1 
      case BigNat# -> BigNat# -> (# BigNat#, BigNat# #)
BN.bigNatQuotRem# BigNat#
n# BigNat#
pow10 of
        (# BigNat#
q, BigNat#
r #)
          | Bool
high Bool -> Bool -> Bool
&& BigNat# -> Bool
BN.bigNatIsZero BigNat#
q  Bool -> DigitsWriter# s
write Bool
high Int#
o1 BigNat#
r State# s
s1
          | Bool
otherwise  case Bool -> DigitsWriter# s
write Bool
False Int#
o1 BigNat#
r State# s
s1 of
              (# State# s
s2, Int#
o2 #)  Bool -> DigitsWriter# s
write Bool
high (Int#
o2 Int# -> Int# -> Int#
-# Int#
1#) BigNat#
q State# s
s2

    prependTiny#  Bool  DigitsWriter# s
    prependTiny# :: Bool -> DigitsWriter# s
prependTiny# !Bool
high !Int#
o1 !BigNat#
n# = case Bool -> DigitsWriter s
prependTiny Bool
high Int#
o1 BigNat#
n# of
      ST STRep s Int
f  \State# s
s1  case STRep s Int
f State# s
s1 of
        (# State# s
s2, I# Int#
o2 #)  (# State# s
s2, Int#
o2 #)

    -- Use ST instead of raw state as the utils functions do.
    -- `prependTiny` must inline to leave no boxing/unboxing roundtrip.
    {-# INLINE prependTiny #-}
    prependTiny  Bool  DigitsWriter s
    prependTiny :: Bool -> DigitsWriter s
prependTiny !Bool
high !Int#
o1 !BigNat#
n# =
      case BigNat# -> Word# -> (# BigNat#, Word# #)
BN.bigNatQuotRemWord# BigNat#
n# Word#
poweredBase of
        (# BigNat#
q, Word#
r #)  do
          !Int
o2  MArray s -> Int -> Word -> ST s Int
forall s. MArray s -> Int -> Word -> ST s Int
unsafePrependWordDec MArray s
marr (Int# -> Int
I# Int#
o1) (Word# -> Word
W# Word#
r)
          if Bool
high Bool -> Bool -> Bool
&& BigNat# -> Bool
BN.bigNatIsZero BigNat#
q
            then Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
o2
            else do
              let !o3 :: Int
o3 = Int# -> Int
I# Int#
o1 Int -> Int -> Int
forall a. Num a => a -> a -> a
- (Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word# -> Word
W# Word#
power) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
              MArray s -> Int -> Int -> ST s ()
forall s. MArray s -> Int -> Int -> ST s ()
padWithZeros MArray s
marr Int
o3 (Int
o2 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
o3)
              !Int
o4  MArray s -> Int -> Word -> ST s Int
forall s. MArray s -> Int -> Word -> ST s Int
unsafePrependWordDec MArray s
marr (Int
o3 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (BigNat# -> Word
BN.bigNatToWord BigNat#
q)
              if Bool
high
                then Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
o4
                else do
                  let !o5 :: Int
o5 = Int
o3 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word# -> Word
W# Word#
power)
                  MArray s -> Int -> Int -> ST s ()
forall s. MArray s -> Int -> Int -> ST s ()
padWithZeros MArray s
marr Int
o5 (Int
o4 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
o5)
                  Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
o5

--------------------------------------------------------------------------------
-- Prepend word
--------------------------------------------------------------------------------

unsafePrependWordDec   s. A.MArray s  Int  Word  ST s Int
unsafePrependWordDec :: forall s. MArray s -> Int -> Word -> ST s Int
unsafePrependWordDec = MArray s -> Int -> Word -> ST s Int
forall a s.
(Integral a, FiniteBits a) =>
MArray s -> Int -> a -> ST s Int
f
  where
    f :: MArray s -> Int -> a -> ST s Int
f MArray s
marr !Int
o !a
k
      | a
k a -> a -> Bool
forall a. Ord a => a -> a -> Bool
>= a
10 = do
          let (a
q, a
r) = a -> (a, a)
forall a. (Integral a, FiniteBits a) => a -> (a, a)
quotRem100 a
k
          MArray s -> Int -> Ptr Word8 -> Int -> ST s ()
forall s. MArray s -> Int -> Ptr Word8 -> Int -> ST s ()
A.copyFromPointer MArray s
marr (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (CString
digits CString -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` (a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
r Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftL` Int
1)) Int
2
          if a
k a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
100 then Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) else MArray s -> Int -> a -> ST s Int
f MArray s
marr (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) a
q
      | Bool
otherwise = do
          MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr Int
o (a -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (a
0x30 a -> a -> a
forall a. Num a => a -> a -> a
+ a
k))
          Int -> ST s Int
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
o

--------------------------------------------------------------------------------
-- Utils
--------------------------------------------------------------------------------

maxIntegerDecLen  Integer  Int
maxIntegerDecLen :: Integer -> Int
maxIntegerDecLen Integer
a = case Integer
a of
  I.IS Int#
i#  Int -> Int
forall a. FiniteBits a => a -> Int
maxDecLen (Int# -> Int
I# Int#
i#)
  I.IP BigNat#
n#  BigNat# -> Int
maxBitNatDecLen BigNat#
n#
  I.IN BigNat#
n#  Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ BigNat# -> Int
maxBitNatDecLen BigNat#
n#
{-# INLINEABLE maxIntegerDecLen #-}

-- | ceiling (fbs a * logBase 10 2) < ceiling (fbs a * 5 / 16) < 1 + floor (fbs a * 5 / 16)
--
-- We approximate @fbs a@ to @bigNatSize a * word_size@.
maxBitNatDecLen  BN.BigNat#  Int
maxBitNatDecLen :: BigNat# -> Int
maxBitNatDecLen BigNat#
n#
  -- This can overflow in theory, but in practice it would overflow for a BigNat#
  -- of at least:
  --
  -- • On 32 bits platform: 6.4 GiB, out of max 4 GiB RAM
  --   → BN.bigNatSize n# = 214748364 =
  --       (maxBound @Int32 - 1) `div` fromIntegral (shiftR (finiteBitSize @Word32 0 * 5) 4)
  -- • On 64 bits platform: 3276 PiB
  --   → BN.bigNatSize n# = 461168601842738790 =
  --       (maxBound @Int64 - 1) `div` fromIntegral (shiftR (finiteBitSize @Word64 0 * 5) 4)
  --
  -- These thresholds are too big to be realistic (32 bits: more than available RAM, 64
  -- bits: integer size in petabytes), so it is perfectly reasonable to have no
  -- special handling of overflow here.

  -- Word bit size is multiple of 16 (e.g. 32 and 64 bits arch)
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
rem (forall a. FiniteBits a => a -> Int
finiteBitSize @Word Word
0) Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
      Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (BigNat# -> Word
BN.bigNatSize BigNat#
n# Word -> Word -> Word
forall a. Num a => a -> a -> a
* Word -> Int -> Word
forall a. Bits a => a -> Int -> a
shiftR (Int -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. FiniteBits a => a -> Int
finiteBitSize @Word Word
0) Word -> Word -> Word
forall a. Num a => a -> a -> a
* Word
5) Int
4)
  -- Other cases (non-standard arch)
  | Bool
otherwise =
      Int
1
        Int -> Int -> Int
forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral @Word64
          ( (Word -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (BigNat# -> Word
BN.bigNatSize BigNat#
n#) Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
* Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. FiniteBits a => a -> Int
finiteBitSize @Word Word
0) Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
* Word64
5)
              Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
4
          )
{-# INLINEABLE maxBitNatDecLen #-}

integerToBigNat#  Integer  BN.BigNat#
integerToBigNat# :: Integer -> BigNat#
integerToBigNat# Integer
n = case Integer -> (# Int#, BigNat# #)
I.integerToBigNatSign# Integer
n of
  (# Int#
_, BigNat#
n# #)  BigNat#
n#
{-# INLINE integerToBigNat# #-}

-- Maximal power of 10 fitting into a 'Word':
-- • 10 ^ 9  for 32 bit words  (32 * log 2 / log 10 ≈  9.63)
-- • 10 ^ 19 for 64 bit words  (64 * log 2 / log 10 ≈ 19.27)
--
-- Why (# #)? We can't have top-level unlifted bindings
-- (see: https://gitlab.haskell.org/ghc/ghc/-/issues/17521). So we use a function
-- that take an empty argument (# #) that will be discarded at compile time.
selectPower  (# #)  (# Word#, Word#, BN.BigNat# #)
selectPower :: (# #) -> (# Word#, Word#, BigNat# #)
selectPower (# #)
_ = case forall a. FiniteBits a => a -> Int
finiteBitSize @Word Word
0 of
  Int
64  (# Word#
19##, Word#
10000000000000000000##, Natural -> BigNat#
N.naturalToBigNat# Natural
tenPower38 #)
  -- Not 64 bits: assume 32 bits
  Int
_  (# Word#
9##, Word#
1000000000##, Natural -> BigNat#
N.naturalToBigNat# Natural
tenPower18 #)

-- NOTE: ensure to not inline the following numbers, in order to avoid allocations.

tenPower18  N.Natural
tenPower18 :: Natural
tenPower18 = Natural
1e18
{-# NOINLINE tenPower18 #-}

tenPower38  N.Natural
tenPower38 :: Natural
tenPower38 = Natural
1e38
{-# NOINLINE tenPower38 #-}

padWithZeros   s. A.MArray s  Int  Int  ST s ()
padWithZeros :: forall s. MArray s -> Int -> Int -> ST s ()
padWithZeros MArray s
marr Int
off Int
count = MArray s -> Int -> Int -> Int -> ST s ()
forall s. MArray s -> Int -> Int -> Int -> ST s ()
unsafeReplicate MArray s
marr Int
off Int
count Int
0x30
{-# INLINE padWithZeros #-}

--------------------------------------------------------------------------------
-- For testing purpose only
--------------------------------------------------------------------------------

-- data Strategy = SmallOnly | HugeOnly

-- prependUnboundedDecimal ∷ Integral a ⇒ Strategy → a → Buffer ⊸ Buffer
-- prependUnboundedDecimal strategy n buffer = case toInteger n of
--   !n' →
--     prependBounded'
--       (maxIntegerDecLen n')
--       (\dst dstOff → unsafePrependDec' strategy dst dstOff n')
--       buffer

-- unsafePrependDec' ∷ ∀ s. Strategy → A.MArray s → Int → Integer → ST s Int
-- unsafePrependDec' s marr off@(I# off#) n' = case n' of
--   I.IS i# → Bounded.unsafePrependDec marr off (I# i#)
--   _ → unsafePrependBigNatDec' s marr (off# -# 1#) (integerToBigNat# n') >>= prependSign
--     where
--       prependSign !off' =
--         if n' < 0
--           then do
--             A.unsafeWrite marr (off' - 1) 0x2d -- '-'
--             pure (off - off' + 1)
--           else pure (off - off')
-- {-# INLINEABLE unsafePrependDec' #-}

-- unsafePrependBigNatDec' ∷ ∀ s. Strategy → A.MArray s → DigitsWriter s
-- unsafePrependBigNatDec' strategy marr !off0 !n0 = case strategy of
--   SmallOnly → prependSmallNat marr off0 n0
--   HugeOnly → prependHugeNat marr off0 n0