summaryrefslogtreecommitdiff
path: root/libraries/integer-simple/GHC
diff options
context:
space:
mode:
authorIan Lynagh <igloo@earth.li>2011-04-22 15:21:21 +0100
committerIan Lynagh <igloo@earth.li>2011-04-22 15:21:21 +0100
commit17f1654367b96b9dfeb44b91ab4d1214d38b2ad1 (patch)
tree38156a0a7de02a50151ce640d3a4ebddc4b6a042 /libraries/integer-simple/GHC
parent6ec3bbca8ae20b01a1a8e39d322e2fd6d5f82ef5 (diff)
downloadhaskell-17f1654367b96b9dfeb44b91ab4d1214d38b2ad1.tar.gz
Part of #5122 "Faster conversion between Rational and Double/Float" fix
From daniel.is.fischer.
Diffstat (limited to 'libraries/integer-simple/GHC')
-rw-r--r--libraries/integer-simple/GHC/Integer/Logarithms.hs43
-rw-r--r--libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs166
2 files changed, 209 insertions, 0 deletions
diff --git a/libraries/integer-simple/GHC/Integer/Logarithms.hs b/libraries/integer-simple/GHC/Integer/Logarithms.hs
new file mode 100644
index 0000000000..cfafe14226
--- /dev/null
+++ b/libraries/integer-simple/GHC/Integer/Logarithms.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE MagicHash, UnboxedTuples, NoImplicitPrelude #-}
+module GHC.Integer.Logarithms
+ ( integerLogBase#
+ , integerLog2#
+ , wordLog2#
+ ) where
+
+import GHC.Prim
+import GHC.Integer
+import qualified GHC.Integer.Logarithms.Internals as I
+
+-- | Calculate the integer logarithm for an arbitrary base.
+-- The base must be greater than 1, the second argument, the number
+-- whose logarithm is sought, should be positive, otherwise the
+-- result is meaningless.
+--
+-- > base ^ integerLogBase# base m <= m < base ^ (integerLogBase# base m + 1)
+--
+-- for @base > 1@ and @m > 0@.
+integerLogBase# :: Integer -> Integer -> Int#
+integerLogBase# b m = case step b of
+ (# _, e #) -> e
+ where
+ step pw =
+ if m `ltInteger` pw
+ then (# m, 0# #)
+ else case step (pw `timesInteger` pw) of
+ (# q, e #) ->
+ if q `ltInteger` pw
+ then (# q, 2# *# e #)
+ else (# q `quotInteger` pw, 2# *# e +# 1# #)
+
+-- | Calculate the integer base 2 logarithm of an 'Integer'.
+-- The calculation is more efficient than for the general case,
+-- on platforms with 32- or 64-bit words much more efficient.
+--
+-- The argument must be strictly positive, that condition is /not/ checked.
+integerLog2# :: Integer -> Int#
+integerLog2# = I.integerLog2#
+
+-- | This function calculates the integer base 2 logarithm of a 'Word#'.
+wordLog2# :: Word# -> Int#
+wordLog2# = I.wordLog2#
diff --git a/libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs b/libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs
new file mode 100644
index 0000000000..c7aab33e53
--- /dev/null
+++ b/libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs
@@ -0,0 +1,166 @@
+{-# LANGUAGE CPP, MagicHash, UnboxedTuples, NoImplicitPrelude #-}
+{-# OPTIONS_HADDOCK hide #-}
+
+#include "MachDeps.h"
+
+-- (Hopefully) Fast integer logarithms to base 2.
+-- integerLog2# and wordLog2# are of general usefulness,
+-- the others are only needed for a fast implementation of
+-- fromRational.
+-- Since they are needed in GHC.Float, we must expose this
+-- module, but it should not show up in the docs.
+
+module GHC.Integer.Logarithms.Internals
+ ( integerLog2#
+ , integerLog2IsPowerOf2#
+ , wordLog2#
+ , roundingMode#
+ ) where
+
+import GHC.Prim
+import GHC.Integer.Type
+import GHC.Integer
+
+default ()
+
+-- When larger word sizes become common, add support for those,
+-- it's not hard, just tedious.
+#if (WORD_SIZE_IN_BITS != 32) && (WORD_SIZE_IN_BITS != 64)
+
+-- We don't know whether the word has 30 bits or 128 or even more,
+-- so we can't start from the top, although that would be much more
+-- efficient.
+wordLog2# :: Word# -> Int#
+wordLog2# w = go 8# w
+ where
+ go acc u = case u `uncheckedShiftRL#` 8# of
+ 0## -> case leadingZeros of
+ BA ba -> acc -# indexInt8Array# ba (word2Int# u)
+ v -> go (acc +# 8#) v
+
+#else
+
+-- This one at least can also be done efficiently.
+-- wordLog2# 0## = -1#
+{-# INLINE wordLog2# #-}
+wordLog2# :: Word# -> Int#
+wordLog2# w =
+ case leadingZeros of
+ BA lz ->
+ let zeros u = indexInt8Array# lz (word2Int# u) in
+#if WORD_SIZE_IN_BITS == 64
+ case uncheckedShiftRL# w 56# of
+ a ->
+ if a `neWord#` 0##
+ then 64# -# zeros a
+ else
+ case uncheckedShiftRL# w 48# of
+ b ->
+ if b `neWord#` 0##
+ then 56# -# zeros b
+ else
+ case uncheckedShiftRL# w 40# of
+ c ->
+ if c `neWord#` 0##
+ then 48# -# zeros c
+ else
+ case uncheckedShiftRL# w 32# of
+ d ->
+ if d `neWord#` 0##
+ then 40# -# zeros d
+ else
+#endif
+ case uncheckedShiftRL# w 24# of
+ e ->
+ if e `neWord#` 0##
+ then 32# -# zeros e
+ else
+ case uncheckedShiftRL# w 16# of
+ f ->
+ if f `neWord#` 0##
+ then 24# -# zeros f
+ else
+ case uncheckedShiftRL# w 8# of
+ g ->
+ if g `neWord#` 0##
+ then 16# -# zeros g
+ else 8# -# zeros w
+
+#endif
+
+-- Assumption: Integer is strictly positive,
+-- otherwise return -1# arbitrarily
+-- Going up in word-sized steps should not be too bad.
+integerLog2# :: Integer -> Int#
+integerLog2# (Positive digits) = step 0# digits
+ where
+ step acc (Some dig None) = acc +# wordLog2# dig
+ step acc (Some _ digs) =
+ step (acc +# WORD_SIZE_IN_BITS#) digs
+ step acc None = acc -- should be impossible, throw error?
+integerLog2# _ = negateInt# 1#
+
+-- Again, integer should be strictly positive
+integerLog2IsPowerOf2# :: Integer -> (# Int#, Int# #)
+integerLog2IsPowerOf2# (Positive digits) = couldBe 0# digits
+ where
+ couldBe acc (Some dig None) =
+ (# acc +# wordLog2# dig, word2Int# (and# dig (minusWord# dig 1##)) #)
+ couldBe acc (Some dig digs) =
+ if eqWord# dig 0##
+ then couldBe (acc +# WORD_SIZE_IN_BITS#) digs
+ else noPower (acc +# WORD_SIZE_IN_BITS#) digs
+ couldBe acc None = (# acc, 1# #) -- should be impossible, error?
+ noPower acc (Some dig None) =
+ (# acc +# wordLog2# dig, 1# #)
+ noPower acc (Some _ digs) =
+ noPower (acc +# WORD_SIZE_IN_BITS#) digs
+ noPower acc None = (# acc, 1# #) -- should be impossible, error?
+integerLog2IsPowerOf2# _ = (# negateInt# 1#, 1# #)
+
+-- Assumption: Integer and Int# are strictly positive, Int# is less
+-- than logBase 2 of Integer, otherwise havoc ensues.
+-- Used only for the numerator in fromRational when the denominator
+-- is a power of 2.
+-- The Int# argument is log2 n minus the number of bits in the mantissa
+-- of the target type, i.e. the index of the first non-integral bit in
+-- the quotient.
+--
+-- 0# means round down (towards zero)
+-- 1# means we have a half-integer, round to even
+-- 2# means round up (away from zero)
+-- This function should probably be improved.
+roundingMode# :: Integer -> Int# -> Int#
+roundingMode# m h =
+ case smallInteger 1# `shiftLInteger` h of
+ c -> case m `andInteger`
+ ((c `plusInteger` c) `minusInteger` smallInteger 1#) of
+ r ->
+ if c `ltInteger` r
+ then 2#
+ else if c `gtInteger` r
+ then 0#
+ else 1#
+
+-- Lookup table
+data BA = BA ByteArray#
+
+leadingZeros :: BA
+leadingZeros =
+ let mkArr s =
+ case newByteArray# 256# s of
+ (# s1, mba #) ->
+ case writeInt8Array# mba 0# 9# s1 of
+ s2 ->
+ let fillA lim val idx st =
+ if idx ==# 256#
+ then st
+ else if idx <# lim
+ then case writeInt8Array# mba idx val st of
+ nx -> fillA lim val (idx +# 1#) nx
+ else fillA (2# *# lim) (val -# 1#) idx st
+ in case fillA 2# 8# 1# s2 of
+ s3 -> case unsafeFreezeByteArray# mba s3 of
+ (# _, ba #) -> ba
+ in case mkArr realWorld# of
+ b -> BA b