module TextBuilder.Domains.Digits where
import qualified Data.Text.Array as TextArray
import qualified TextBuilder.Domains.Digits.Codepoints as Codepoints
import TextBuilder.Prelude
import TextBuilderCore
{-# INLINE decimalDigit #-}
decimalDigit :: (Integral a) => a -> TextBuilder
decimalDigit :: forall a. Integral a => a -> TextBuilder
decimalDigit (a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral -> Int
n) =
Int -> TextBuilder
unicodeCodepoint (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
48)
{-# INLINE hexadecimalDigit #-}
hexadecimalDigit :: (Integral a) => a -> TextBuilder
hexadecimalDigit :: forall a. Integral a => a -> TextBuilder
hexadecimalDigit (a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral -> Int
n) =
if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
9
then Int -> TextBuilder
unicodeCodepoint (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
48)
else Int -> TextBuilder
unicodeCodepoint (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
87)
{-# INLINE customFixedNumeralSystem #-}
customFixedNumeralSystem ::
(FiniteBits a, Integral a) =>
Int ->
(a -> a) ->
a ->
TextBuilder
customFixedNumeralSystem :: forall a.
(FiniteBits a, Integral a) =>
Int -> (a -> a) -> a -> TextBuilder
customFixedNumeralSystem Int
bitsPerDigit a -> a
digitCodepoint a
val =
let size :: Int
size = Int -> Int -> Int
forall a. Integral a => a -> a -> a
div (a -> Int
forall b. FiniteBits b => b -> Int
finiteBitSize a
val Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
bitsPerDigit Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int
bitsPerDigit
in Int -> (forall s. MArray s -> Int -> ST s Int) -> TextBuilder
TextBuilder Int
size \MArray s
array Int
arrayStartIndex ->
let go :: a -> Int -> ST s Int
go a
val Int
arrayIndex =
if Int
arrayIndex Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
arrayStartIndex
then do
MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
TextArray.unsafeWrite MArray s
array Int
arrayIndex (a -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (a -> a
digitCodepoint a
val))
a -> Int -> ST s Int
go (a -> Int -> a
forall a. Bits a => a -> Int -> a
unsafeShiftR a
val Int
bitsPerDigit) (Int -> Int
forall a. Enum a => a -> a
pred Int
arrayIndex)
else Int -> ST s Int
forall a. a -> ST s a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
indexAfter
indexAfter :: Int
indexAfter =
Int
arrayStartIndex Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size
in a -> Int -> ST s Int
go a
val (Int -> Int
forall a. Enum a => a -> a
pred Int
indexAfter)
{-# INLINE binary #-}
binary :: (FiniteBits a) => a -> TextBuilder
binary :: forall a. FiniteBits a => a -> TextBuilder
binary a
val =
let size :: Int
size = a -> Int
forall b. FiniteBits b => b -> Int
finiteBitSize a
val
in Int -> (forall s. MArray s -> Int -> ST s Int) -> TextBuilder
TextBuilder Int
size \MArray s
array Int
arrayStartIndex ->
let go :: a -> Int -> ST s Int
go a
val Int
arrayIndex =
if Int
arrayIndex Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
arrayStartIndex
then do
MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
TextArray.unsafeWrite MArray s
array Int
arrayIndex if a -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit a
val Int
0 then Word8
49 else Word8
48
a -> Int -> ST s Int
go (a -> Int -> a
forall a. Bits a => a -> Int -> a
unsafeShiftR a
val Int
1) (Int -> Int
forall a. Enum a => a -> a
pred Int
arrayIndex)
else Int -> ST s Int
forall a. a -> ST s a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
indexAfter
indexAfter :: Int
indexAfter =
Int
arrayStartIndex Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size
in a -> Int -> ST s Int
go a
val (Int -> Int
forall a. Enum a => a -> a
pred Int
indexAfter)
{-# INLINE prefixedBinary #-}
prefixedBinary :: (FiniteBits a) => a -> TextBuilder
prefixedBinary :: forall a. FiniteBits a => a -> TextBuilder
prefixedBinary = TextBuilder -> TextBuilder -> TextBuilder
forall a. Monoid a => a -> a -> a
mappend TextBuilder
"0b" (TextBuilder -> TextBuilder)
-> (a -> TextBuilder) -> a -> TextBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> TextBuilder
forall a. FiniteBits a => a -> TextBuilder
binary
{-# INLINE octal #-}
octal :: (FiniteBits a, Integral a) => a -> TextBuilder
octal :: forall a. (FiniteBits a, Integral a) => a -> TextBuilder
octal = Int -> (a -> a) -> a -> TextBuilder
forall a.
(FiniteBits a, Integral a) =>
Int -> (a -> a) -> a -> TextBuilder
customFixedNumeralSystem Int
3 (a -> a
forall a. (Bits a, Num a) => a -> a
Codepoints.octalDigit (a -> a) -> (a -> a) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral)
{-# INLINE prefixedOctal #-}
prefixedOctal :: (FiniteBits a, Integral a) => a -> TextBuilder
prefixedOctal :: forall a. (FiniteBits a, Integral a) => a -> TextBuilder
prefixedOctal = TextBuilder -> TextBuilder -> TextBuilder
forall a. Monoid a => a -> a -> a
mappend TextBuilder
"0o" (TextBuilder -> TextBuilder)
-> (a -> TextBuilder) -> a -> TextBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> TextBuilder
forall a. (FiniteBits a, Integral a) => a -> TextBuilder
octal
{-# INLINE hexadecimal #-}
hexadecimal :: (FiniteBits a, Integral a) => a -> TextBuilder
hexadecimal :: forall a. (FiniteBits a, Integral a) => a -> TextBuilder
hexadecimal = Int -> (a -> a) -> a -> TextBuilder
forall a.
(FiniteBits a, Integral a) =>
Int -> (a -> a) -> a -> TextBuilder
customFixedNumeralSystem Int
4 (a -> a
forall a. (Bits a, Num a, Ord a) => a -> a
Codepoints.hexDigit (a -> a) -> (a -> a) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral)
{-# INLINE prefixedHexadecimal #-}
prefixedHexadecimal :: (FiniteBits a, Integral a) => a -> TextBuilder
prefixedHexadecimal :: forall a. (FiniteBits a, Integral a) => a -> TextBuilder
prefixedHexadecimal = TextBuilder -> TextBuilder -> TextBuilder
forall a. Monoid a => a -> a -> a
mappend TextBuilder
"0x" (TextBuilder -> TextBuilder)
-> (a -> TextBuilder) -> a -> TextBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> TextBuilder
forall a. (FiniteBits a, Integral a) => a -> TextBuilder
hexadecimal
{-# INLINE signed #-}
signed :: (Ord a, Num a) => (a -> TextBuilder) -> a -> TextBuilder
signed :: forall a. (Ord a, Num a) => (a -> TextBuilder) -> a -> TextBuilder
signed a -> TextBuilder
onUnsigned a
i =
if a
i a -> a -> Bool
forall a. Ord a => a -> a -> Bool
>= a
0
then a -> TextBuilder
onUnsigned a
i
else Int -> TextBuilder
unicodeCodepoint Int
45 TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> a -> TextBuilder
onUnsigned (a -> a
forall a. Num a => a -> a
negate a
i)
{-# INLINEABLE decimal #-}
decimal :: (Integral a) => a -> TextBuilder
decimal :: forall a. Integral a => a -> TextBuilder
decimal = (a -> TextBuilder) -> a -> TextBuilder
forall a. (Ord a, Num a) => (a -> TextBuilder) -> a -> TextBuilder
signed a -> TextBuilder
forall a. Integral a => a -> TextBuilder
unsignedDecimal
{-# INLINE digitsByRadix #-}
digitsByRadix :: (Integral a) => a -> (a -> a) -> a -> TextBuilder
digitsByRadix :: forall a. Integral a => a -> (a -> a) -> a -> TextBuilder
digitsByRadix a
radix a -> a
digitCodepoint =
Int -> [a] -> a -> TextBuilder
go Int
0 []
where
go :: Int -> [a] -> a -> TextBuilder
go !Int
offset ![a]
digits a
x = case a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
divMod a
x a
radix of
(a
next, a
digit) ->
if a
next a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= a
0
then Int -> [a] -> TextBuilder
finish (Int -> Int
forall a. Enum a => a -> a
succ Int
offset) (a
digit a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
digits)
else Int -> [a] -> a -> TextBuilder
go (Int -> Int
forall a. Enum a => a -> a
succ Int
offset) (a
digit a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
digits) a
next
finish :: Int -> [a] -> TextBuilder
finish Int
size [a]
digits =
Int -> (forall s. MArray s -> Int -> ST s Int) -> TextBuilder
TextBuilder Int
size MArray s -> Int -> ST s Int
forall s. MArray s -> Int -> ST s Int
action
where
action :: TextArray.MArray s -> Int -> ST s Int
action :: forall s. MArray s -> Int -> ST s Int
action MArray s
array =
[a] -> Int -> ST s Int
go [a]
digits
where
go :: [a] -> Int -> ST s Int
go [a]
digits Int
offset = case [a]
digits of
[] -> Int -> ST s Int
forall a. a -> ST s a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
offset
(a
digit : [a]
digits) -> do
MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
TextArray.unsafeWrite MArray s
array Int
offset (a -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (a -> a
digitCodepoint a
digit))
[a] -> Int -> ST s Int
go [a]
digits (Int -> Int
forall a. Enum a => a -> a
succ Int
offset)
{-# INLINE unsignedOctal #-}
unsignedOctal :: (Integral a) => a -> TextBuilder
unsignedOctal :: forall a. Integral a => a -> TextBuilder
unsignedOctal =
a -> (a -> a) -> a -> TextBuilder
forall a. Integral a => a -> (a -> a) -> a -> TextBuilder
digitsByRadix a
8 (a -> a -> a
forall a. Num a => a -> a -> a
+ a
48)
{-# INLINE unsignedDecimal #-}
unsignedDecimal :: (Integral a) => a -> TextBuilder
unsignedDecimal :: forall a. Integral a => a -> TextBuilder
unsignedDecimal =
a -> (a -> a) -> a -> TextBuilder
forall a. Integral a => a -> (a -> a) -> a -> TextBuilder
digitsByRadix a
10 (a -> a -> a
forall a. Num a => a -> a -> a
+ a
48)
{-# INLINE unsignedHexadecimal #-}
unsignedHexadecimal :: (Integral a) => a -> TextBuilder
unsignedHexadecimal :: forall a. Integral a => a -> TextBuilder
unsignedHexadecimal =
a -> (a -> a) -> a -> TextBuilder
forall a. Integral a => a -> (a -> a) -> a -> TextBuilder
digitsByRadix a
16 (\a
digit -> if a
digit a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= a
9 then a
digit a -> a -> a
forall a. Num a => a -> a -> a
+ a
48 else a
digit a -> a -> a
forall a. Num a => a -> a -> a
+ a
87)
{-# INLINEABLE fixedLengthDecimal #-}
fixedLengthDecimal :: (Integral a) => Int -> a -> TextBuilder
fixedLengthDecimal :: forall a. Integral a => Int -> a -> TextBuilder
fixedLengthDecimal (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 -> Int
size) (a -> a
forall a. Num a => a -> a
abs -> a
val) =
Int -> (forall s. MArray s -> Int -> ST s Int) -> TextBuilder
TextBuilder Int
size ((forall s. MArray s -> Int -> ST s Int) -> TextBuilder)
-> (forall s. MArray s -> Int -> ST s Int) -> TextBuilder
forall a b. (a -> b) -> a -> b
$ \MArray s
array Int
startOffset ->
let offsetAfter :: Int
offsetAfter = Int
startOffset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size
writeValue :: a -> Int -> ST s Int
writeValue a
val Int
offset =
if Int
offset Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
startOffset
then
if a
val a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
0
then case a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
divMod a
val a
10 of
(a
val, a
digit) -> do
MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
TextArray.unsafeWrite MArray s
array Int
offset (Word8 -> ST s ()) -> Word8 -> ST s ()
forall a b. (a -> b) -> a -> b
$ Word8
48 Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ a -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
digit
a -> Int -> ST s Int
writeValue a
val (Int -> Int
forall a. Enum a => a -> a
pred Int
offset)
else Int -> ST s Int
writePadding Int
offset
else Int -> ST s Int
forall a. a -> ST s a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
offsetAfter
writePadding :: Int -> ST s Int
writePadding Int
offset =
if Int
offset Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
startOffset
then do
MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
TextArray.unsafeWrite MArray s
array Int
offset Word8
48
Int -> ST s Int
writePadding (Int -> Int
forall a. Enum a => a -> a
pred Int
offset)
else Int -> ST s Int
forall a. a -> ST s a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
offsetAfter
in a -> Int -> ST s Int
writeValue a
val (Int -> Int
forall a. Enum a => a -> a
pred Int
offsetAfter)
{-# INLINEABLE thousandSeparatedDecimal #-}
thousandSeparatedDecimal :: (Integral a) => Char -> a -> TextBuilder
thousandSeparatedDecimal :: forall a. Integral a => Char -> a -> TextBuilder
thousandSeparatedDecimal Char
separatorChar =
(a -> TextBuilder) -> a -> TextBuilder
forall a. (Ord a, Num a) => (a -> TextBuilder) -> a -> TextBuilder
signed (Char -> a -> TextBuilder
forall a. Integral a => Char -> a -> TextBuilder
unsignedThousandSeparatedDecimal Char
separatorChar)
{-# INLINEABLE unsignedThousandSeparatedDecimal #-}
unsignedThousandSeparatedDecimal :: (Integral a) => Char -> a -> TextBuilder
unsignedThousandSeparatedDecimal :: forall a. Integral a => Char -> a -> TextBuilder
unsignedThousandSeparatedDecimal Char
separatorChar =
a -> TextBuilder
processRightmostDigit
where
processRightmostDigit :: a -> TextBuilder
processRightmostDigit a
value =
case a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
divMod a
value a
10 of
(a
value, a
digit) ->
[TextBuilder] -> Int -> a -> TextBuilder
processAnotherDigit [a -> TextBuilder
forall a. Integral a => a -> TextBuilder
decimalDigit a
digit] (Int
1 :: Int) a
value
processAnotherDigit :: [TextBuilder] -> Int -> a -> TextBuilder
processAnotherDigit [TextBuilder]
builders Int
index a
value =
if a
value a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
0
then [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat [TextBuilder]
builders
else case a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
divMod a
value a
10 of
(a
value, a
digit) ->
if Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod Int
index Int
3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0
then
[TextBuilder] -> Int -> a -> TextBuilder
processAnotherDigit
(a -> TextBuilder
forall a. Integral a => a -> TextBuilder
decimalDigit a
digit TextBuilder -> [TextBuilder] -> [TextBuilder]
forall a. a -> [a] -> [a]
: Char -> TextBuilder
char Char
separatorChar TextBuilder -> [TextBuilder] -> [TextBuilder]
forall a. a -> [a] -> [a]
: [TextBuilder]
builders)
(Int -> Int
forall a. Enum a => a -> a
succ Int
index)
a
value
else
[TextBuilder] -> Int -> a -> TextBuilder
processAnotherDigit
(a -> TextBuilder
forall a. Integral a => a -> TextBuilder
decimalDigit a
digit TextBuilder -> [TextBuilder] -> [TextBuilder]
forall a. a -> [a] -> [a]
: [TextBuilder]
builders)
(Int -> Int
forall a. Enum a => a -> a
succ Int
index)
a
value