module Chronos.Internal where
import Data.Attoparsec.Text (Parser)
import Data.Vector (Vector)
import Data.Text.Lazy.Builder (Builder)
import Data.Word
import Data.Int
import qualified Data.ByteString.Builder as BBuilder
import qualified Data.ByteString.Char8 as BC8
import qualified Data.Attoparsec.ByteString.Char8 as AB
import qualified Data.Vector.Unboxed as UVector
import qualified Data.Attoparsec.Text as Atto
import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Text.Lazy.Builder.Int as Builder
import qualified Data.Text.Read as Text
import qualified Data.Text as Text
import qualified Data.Vector as Vector
parseFixedDigits :: Int -> Parser Int
parseFixedDigits n = do
t <- Atto.take n
case Text.decimal t of
Left err -> fail err
Right (i,r) -> if Text.null r
then return i
else fail "datetime decoding could not parse integral text"
parseFixedDigitsIntBS :: Int -> AB.Parser Int
parseFixedDigitsIntBS n = do
t <- AB.take n
case BC8.readInt t of
Nothing -> fail "datetime decoding could not parse integral bytestring (a)"
Just (i,r) -> if BC8.null r
then return i
else fail "datetime decoding could not parse integral bytestring (b)"
raiseTenTo :: Int -> Int64
raiseTenTo i = if i > 15
then 10 ^ i
else UVector.unsafeIndex tenRaisedToSmallPowers i
tenRaisedToSmallPowers :: UVector.Vector Int64
tenRaisedToSmallPowers = UVector.fromList $ map (10 ^) [0 :: Int ..15]
indexTwoDigitTextBuilder :: Int -> Builder
indexTwoDigitTextBuilder i = if i < 100
then Vector.unsafeIndex twoDigitTextBuilder (fromIntegral i)
else Builder.decimal i
indexTwoDigitByteStringBuilder :: Int -> BBuilder.Builder
indexTwoDigitByteStringBuilder i = if i < 100
then Vector.unsafeIndex twoDigitByteStringBuilder (fromIntegral i)
else BBuilder.intDec i
twoDigitByteStringBuilder :: Vector BBuilder.Builder
twoDigitByteStringBuilder = Vector.fromList
$ map (BBuilder.byteString . BC8.pack) twoDigitStrings
twoDigitTextBuilder :: Vector Builder
twoDigitTextBuilder = Vector.fromList
$ map (Builder.fromText . Text.pack) twoDigitStrings
twoDigitStrings :: [String]
twoDigitStrings =
[ "00","01","02","03","04","05","06","07","08","09"
, "10","11","12","13","14","15","16","17","18","19"
, "20","21","22","23","24","25","26","27","28","29"
, "30","31","32","33","34","35","36","37","38","39"
, "40","41","42","43","44","45","46","47","48","49"
, "50","51","52","53","54","55","56","57","58","59"
, "60","61","62","63","64","65","66","67","68","69"
, "70","71","72","73","74","75","76","77","78","79"
, "80","81","82","83","84","85","86","87","88","89"
, "90","91","92","93","94","95","96","97","98","99"
]
countDigits :: (Integral a) => a -> Int
countDigits v0
| fromIntegral v64 == v0 = go 1 v64
| otherwise = goBig 1 (fromIntegral v0)
where v64 = fromIntegral v0
goBig !k (v :: Integer)
| v > big = goBig (k + 19) (v `quot` big)
| otherwise = go k (fromIntegral v)
big = 10000000000000000000
go !k (v :: Word64)
| v < 10 = k
| v < 100 = k + 1
| v < 1000 = k + 2
| v < 1000000000000 =
k + if v < 100000000
then if v < 1000000
then if v < 10000
then 3
else 4 + fin v 100000
else 6 + fin v 10000000
else if v < 10000000000
then 8 + fin v 1000000000
else 10 + fin v 100000000000
| otherwise = go (k + 12) (v `quot` 1000000000000)
fin v n = if v >= n then 1 else 0
clip :: (Ord t) => t -> t -> t -> t
clip a _ x | x < a = a
clip _ b x | x > b = b
clip _ _ x = x