packages feed

fast-digits 0.1.0.0 → 0.2.0.0

raw patch · 4 files changed

+159/−69 lines, 4 filesdep ~basedep ~integer-gmpPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, integer-gmp

API changes (from Hackage documentation)

+ Data.FastDigits: digitsUnsigned :: Word -> Natural -> [Word]
+ Data.FastDigits.Internal: selectPower :: Word# -> (# Word#, Word# #)
+ Data.FastDigits.Internal: selectPower' :: Word -> (Word, Word)

Files

fast-digits.cabal view
@@ -1,9 +1,9 @@ name:                fast-digits-version:             0.1.0.0+version:             0.2.0.0 synopsis:            The fast library for integer-to-digits conversion. description:         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.+                     For small bases and long numbers it may be up to 40 times faster. homepage:            https://github.com/Bodigrim/fast-digits license:             GPL-3 license-file:        LICENSE@@ -18,9 +18,10 @@   location: git://github.com/Bodigrim/fast-digits.git  library-  exposed-modules:     Data.FastDigits-  build-depends:         base < 5-                       , integer-gmp+  exposed-modules:       Data.FastDigits+                       , Data.FastDigits.Internal+  build-depends:         base >=4.8 && < 5+                       , integer-gmp >=1.0   hs-source-dirs:      src   default-language:    Haskell2010   ghc-options:         -Wall
src/Data/FastDigits.hs view
@@ -5,11 +5,10 @@ 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.+For small bases and long numbers it may be up to 40 times faster. -}  {-# LANGUAGE MagicHash     #-}@@ -17,84 +16,106 @@  {-# OPTIONS_GHC -fno-warn-type-defaults  #-} {-# OPTIONS_GHC -O2                      #-}+{-# OPTIONS_GHC -optc-O3                 #-}  module Data.FastDigits   ( digits   , undigits+  , digitsUnsigned   ) where  import GHC.Exts-import GHC.Integer--ti :: Integral a => a -> Integer-ti = toInteger--fi :: Num a => Integer -> a-fi = fromInteger-+import GHC.Integer.GMP.Internals+import GHC.Natural+import Unsafe.Coerce+import Data.FastDigits.Internal -digitsInteger :: Integer -> Integer -> [Int]-digitsInteger base = f+digitsNatural :: GmpLimb# -> BigNat -> [Word]+digitsNatural base = f   where-    f 0 = []-    f n = let (# q, r #) = n `quotRemInteger` base in fi r : f q+    f n = if n == zeroBigNat+      then []+      else let (# q, r #) = n `quotRemBigNatWord` base in W# r : f q -digitsInt :: Int# -> Int -> [Int]-digitsInt base (I# m) = f m+digitsWord :: Word# -> Word# -> [Word]+digitsWord 2## = g   where-    f :: Int# -> [Int]-    f 0# = []-    f n = let (# q, r #) = n `quotRemInt#` base in (I# r) : f q-+    g :: Word# -> [Word]+    g 0## = []+    g n = W# (n `and#` 1##) : g (n `uncheckedShiftRL#` 1#)+digitsWord base = f+  where+    f :: Word# -> [Word]+    f 0## = []+    f n = let (# q, r #) = n `quotRemWord#` base in W# r : f q -digitsInteger' :: Int -> Int -> Integer -> Integer -> [Int]-digitsInteger' power (I# base) poweredBase = f+-- | For a given base and expected length of list of digits+--   return the list of digits and padding till expected length.+digitsWordL :: Word# -> Word# -> Word# -> (# [Word], Word# #)+digitsWordL 2## power = g   where-    f :: Integer -> [Int]-    f n = fr ++ (if q == 0 then [] else replicate (power - length fr) 0 ++ f q)+    g :: Word# -> (# [Word], Word# #)+    g 0## = (# [], power #)+    g n = (# W# (n `and#` 1##) : fq, lq `minusWord#` 1## #)       where-        (# q, r #) = n `quotRemInteger` poweredBase-        fr = digitsInt base (fi r)+        (# fq, lq #) = g (n `uncheckedShiftRL#` 1#)+digitsWordL base power = f+  where+    f :: Word# -> (# [Word], Word# #)+    f 0## = (# [], power #)+    f n = (# W# r : fq, lq `minusWord#` 1## #)+      where+        (# q, r #) = n `quotRemWord#` base+        (# fq, lq #) = f q +-- | For a given base, power and precalculated base^power+--   take an integer and return the list of its digits.+digitsNatural' :: Word# -> Word# -> Word# -> BigNat -> [Word]+digitsNatural' base power poweredBase = f+  where+    f :: BigNat -> [Word]+    f n = let (# q, r #) = n `quotRemBigNatWord` poweredBase in+      if q == zeroBigNat+        then digitsWord base r+        else let (# fr, lr #) = digitsWordL base power r in+          fr ++ replicate (I# (unsafeCoerce# lr)) 0 ++ f q -selectPower :: Int -> (Int, Int)-selectPower base = if poweredBase > 0-    then (power, poweredBase)-    else (power - 1, base ^ (power - 1))+-- | Return digits of a non-negative number in reverse order.+digitsUnsigned+  :: Word    -- ^ Precondition that base is ≥2 is not checked+  -> Natural+  -> [Word]+digitsUnsigned (W# base) (NatS# n) = digitsWord base n+digitsUnsigned (W# base) (NatJ# n) = case power of+  1## -> digitsNatural base n+  _   -> digitsNatural' base power poweredBase n   where-    power :: Int-    power = floor $ logBase (fi $ ti base) (fi $ ti $ (maxBound :: Int))-    poweredBase :: Int-    poweredBase = base ^ power+    (# power, poweredBase #) = selectPower base --- | Return digits of a non-negative integer in reverse order. E. g.,+-- | Return digits of a non-negative number in reverse order.+--   Throw an error if number is negative or base is below 2. -- --   > 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+  -> Integer -- ^ The number to convert+  -> [Int]   -- ^ Digits in reverse order+digits 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+  | otherwise = unsafeCoerce+              $ digitsUnsigned (unsafeCoerce base) (unsafeCoerce n)  -- | Return an integer, built from given digits in reverse order.---   Condition 0 <= digit < base is not checked.+--   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+undigits base' = foldr (\d acc -> acc * base + toInteger d) 0   where-    base = ti base'-{-# SPECIALIZE undigits :: Int -> [Int] -> Integer #-}+    base = toInteger base'+{-# SPECIALIZE undigits :: Word    -> [Word]    -> Integer #-}+{-# SPECIALIZE undigits :: Int     -> [Int]     -> Integer #-} {-# SPECIALIZE undigits :: Integer -> [Integer] -> Integer #-}
+ src/Data/FastDigits/Internal.hs view
@@ -0,0 +1,51 @@+{-|+Module      : Data.FastDigits.Internal+Copyright   : (c) Andrew Lelechenko, 2015+License     : GPL-3+Maintainer  : andrew.lelechenko@gmail.com+Stability   : experimental++-}++{-# LANGUAGE CPP           #-}+{-# LANGUAGE MagicHash     #-}+{-# LANGUAGE UnboxedTuples #-}++{-# OPTIONS_GHC -fno-warn-type-defaults  #-}+{-# OPTIONS_GHC -O2                      #-}+{-# OPTIONS_GHC -optc-O3                 #-}++module Data.FastDigits.Internal+  ( selectPower+  , selectPower'+  ) where++import GHC.Exts++-- | Take an integer base and return (pow, base^pow),+--   where base^pow <= maxBound and pow is as large as possible.+selectPower :: Word# -> (# Word#, Word# #)+#if WORD_SIZE_IN_BITS == 31+selectPower 2## = (# 31##, 2147483648## #)+#elif WORD_SIZE_IN_BITS == 32+selectPower 2## = (# 31##, 2147483648## #)+#else+selectPower 2## = (# 63##, 9223372036854775808## #)+#endif++selectPower base = go base+  where+    go pw = case timesWord2# pw pw of+        (# 0##, pw2 #)+          -> let (# n, pw2n #) = go pw2 in+            case timesWord2# pw pw2n of+              (# 0##, pw2n1 #) -> (#n `timesWord#` 2## `plusWord#` 1##, pw2n1 #)+              _ -> (# n `timesWord#` 2##, pw2n #)+        _           -> (# 1##, pw #)++-- | Take an integer base and return (pow, base^pow),+--   where base^pow <= maxBound and pow is as large as possible.+selectPower' :: Word -> (Word, Word)+selectPower' (W# base) = (W# power, W# poweredBase)+  where+    (# power, poweredBase #) = selectPower base
tests/Tests.hs view
@@ -13,6 +13,7 @@ import qualified Data.Digits as D  import Data.FastDigits+import Data.FastDigits.Internal  instance (Num a, Ord a, Arbitrary a) => Arbitrary (Positive a) where   arbitrary =@@ -102,22 +103,38 @@   where     n = toInteger a * toInteger (maxBound :: Int) + toInteger b +qProperty8 :: Positive Int -> QC.Property+qProperty8 (Positive base') = base > 1 QC.==>+  base ^ power == poweredBase && poweredBase > maxBound `div` base+  where+    base = fromIntegral $ toInteger base'+    (power, poweredBase) = selectPower' base++sProperty8 :: Positive Int -> Bool+sProperty8 (Positive base') = base <= 1 ||+  base ^ power == poweredBase && poweredBase > maxBound `div` base+  where+    base = fromIntegral $ toInteger base'+    (power, poweredBase) = selectPower' base+ testSuite :: TestTree testSuite = testGroup "digits"-  [ QC.testProperty "undigits base . digits base == id" qProperty1-  , SC.testProperty "undigits base . digits base == id" sProperty1-  , QC.testProperty "digits == digitsD" qProperty2-  , SC.testProperty "digits == digitsD" sProperty2-  , QC.testProperty "digits 10 == digits10" qProperty3-  , SC.testProperty "digits 10 == digits10" sProperty3-  , QC.testProperty "All digits are between 0 and base - 1" qProperty4-  , SC.testProperty "All digits are between 0 and base - 1" sProperty4-  , QC.testProperty "Last digit is not 0" qProperty5-  , SC.testProperty "Last digit is not 0" sProperty5-  , QC.testProperty "digits 2 == digitsD 2" qProperty6-  , SC.testProperty "digits 2 == digitsD 2" sProperty6-  , QC.testProperty "digits 2 == digitsD 2 on integers of special form" qProperty7-  , SC.testProperty "digits 2 == digitsD 2 on integers of special form" sProperty7+  [ SC.testProperty "S undigits base . digits base == id" sProperty1+  , QC.testProperty "Q undigits base . digits base == id" qProperty1+  , SC.testProperty "S digits == digitsD" sProperty2+  , QC.testProperty "Q digits == digitsD" qProperty2+  , SC.testProperty "S digits 10 == digits10" sProperty3+  , QC.testProperty "Q digits 10 == digits10" qProperty3+  , SC.testProperty "S All digits are between 0 and base - 1" sProperty4+  , QC.testProperty "Q All digits are between 0 and base - 1" qProperty4+  , SC.testProperty "S Last digit is not 0" sProperty5+  , QC.testProperty "Q Last digit is not 0" qProperty5+  , SC.testProperty "S digits 2 == digitsD 2" sProperty6+  , QC.testProperty "Q digits 2 == digitsD 2" qProperty6+  , SC.testProperty "S digits 2 == digitsD 2 on integers of special form" sProperty7+  , QC.testProperty "Q digits 2 == digitsD 2 on integers of special form" qProperty7+  , SC.testProperty "S selectPower is correct" sProperty8+  , QC.testProperty "Q selectPower is correct" qProperty8   ]  main :: IO ()