module Data.FastDigits
( digits
, undigits
) where
import GHC.Exts
import GHC.Integer
ti :: Integral a => a -> Integer
ti = toInteger
fi :: Num a => Integer -> a
fi = fromInteger
digitsInteger :: Integer -> Integer -> [Int]
digitsInteger base = f
where
f 0 = []
f n = let (# q, r #) = n `quotRemInteger` base in fi r : f q
digitsInt :: Int# -> Int -> [Int]
digitsInt base (I# m) = f m
where
f :: Int# -> [Int]
f 0# = []
f n = let (# q, r #) = n `quotRemInt#` base in (I# r) : f q
digitsInteger' :: Int -> Int -> Integer -> Integer -> [Int]
digitsInteger' power (I# base) poweredBase = f
where
f :: Integer -> [Int]
f n = fr ++ (if q == 0 then [] else replicate (power length fr) 0 ++ f q)
where
(# q, r #) = n `quotRemInteger` poweredBase
fr = digitsInt base (fi r)
selectPower :: Int -> (Int, Int)
selectPower base = if poweredBase > 0
then (power, poweredBase)
else (power 1, base ^ (power 1))
where
power :: Int
power = floor $ logBase (fi $ ti base) (fi $ ti $ (maxBound :: Int))
poweredBase :: Int
poweredBase = base ^ power
digits
:: Int
-> Integer
-> [Int]
digits base@(I# base') n
| base < 2 = error "Base must be > 1"
| n < 0 = error "Number must be non-negative"
| n < ti (maxBound :: Int) = digitsInt base' (fi n)
| otherwise = if power == 1
then digitsInteger (ti base) n
else digitsInteger' power base (ti poweredBase) n
where
(power, poweredBase) = selectPower base
undigits :: (Integral a, Integral b)
=> a
-> [b]
-> Integer
undigits base' = foldr (\d acc -> acc * base + ti d) 0
where
base = ti base'