{-| Module : Data.FastDigits Description : The fast library for integer-to-digits conversion. Copyright : (c) Andrew Lelechenko, 2015 License : GPL-3 Maintainer : andrew.lelechenko@gmail.com Stability : experimental Portability : POSIX Convert an integer to digits and back. Usually this library is twice as fast as "Data.Digits". For small bases and long numbers it may be up to 30 times faster. -} {-# LANGUAGE MagicHash #-} {-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {-# OPTIONS_GHC -O2 #-} 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 -- | Return digits of a non-negative integer in reverse order. E. g., -- -- > digits 10 123 = [3, 2, 1] -- > digits 10 0 = [] -- -- Throw an error if number is negative or base is below 2. digits :: Int -- ^ The base to use -> Integer -- ^ The integer to convert -> [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 -- | Return an integer, built from given digits in reverse order. -- Condition 0 <= digit < base is not checked. undigits :: (Integral a, Integral b) => a -- ^ The base to use -> [b] -- ^ The list of digits to convert -> Integer undigits base' = foldr (\d acc -> acc * base + ti d) 0 where base = ti base' {-# SPECIALIZE undigits :: Int -> [Int] -> Integer #-} {-# SPECIALIZE undigits :: Integer -> [Integer] -> Integer #-}