bitvec-0.1: src/Data/Bit/Internal.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702
{-# LANGUAGE Safe #-}
#else
#define safe
#endif
module Data.Bit.Internal where
import safe Data.Bits
import safe Data.List
import safe Data.Typeable
import safe Data.Word
#if !MIN_VERSION_base(4,3,0)
import safe Control.Monad
mfilter :: MonadPlus m => (a -> Bool) -> m a -> m a
mfilter p xs = do x <- xs; guard (p x); return x
#endif
newtype Bit = Bit Bool
deriving (Bounded, Eq, Ord, Typeable)
fromBool b = Bit b
toBool (Bit b) = b
instance Enum Bit where
toEnum = fromBool . toEnum
fromEnum = fromEnum . toBool
-- various internal utility functions and constants
lg2 :: Int -> Int
lg2 n = i
where Just i = findIndex (>= toInteger n) (iterate (`shiftL` 1) 1)
-- |The number of 'Bit's in a 'Word'. A handy constant to have around when defining 'Word'-based bulk operations on bit vectors.
wordSize :: Int
wordSize = bitSize (0 :: Word)
lgWordSize, wordSizeMask, wordSizeMaskC :: Int
lgWordSize = lg2 wordSize
wordSizeMask = wordSize - 1
wordSizeMaskC = complement wordSizeMask
divWordSize x = shiftR x lgWordSize
modWordSize x = x .&. (wordSize - 1)
mulWordSize x = shiftL x lgWordSize
-- number of words needed to store n bits
nWords nBits = divWordSize (nBits + wordSize - 1)
-- number of bits storable in n words
nBits nWords = mulWordSize nWords
aligned x = (x .&. wordSizeMask == 0)
notAligned x = x /= alignDown x
-- round a number of bits up to the nearest multiple of word size
alignUp x
| x == x' = x'
| otherwise = x' + wordSize
where x' = alignDown x
-- round a number of bits down to the nearest multiple of word size
alignDown x = x .&. wordSizeMaskC
readBit :: Int -> Word -> Bit
readBit i w = fromBool (testBit w i)
extendToWord :: Bit -> Word
extendToWord (Bit False) = 0
extendToWord (Bit True) = complement 0
-- create a mask consisting of the lower n bits
mask :: Int -> Word
mask b = m
where
m | b >= bitSize m = complement 0
| b < 0 = 0
| otherwise = bit b - 1
masked b x = x .&. mask b
isMasked b x = (masked b x == x)
-- meld 2 words by taking the low 'b' bits from 'lo' and the rest from 'hi'
meld b lo hi = (lo .&. m) .|. (hi .&. complement m)
where m = mask b
-- given a bit offset 'k' and 2 words, extract a word by taking the 'k' highest bits of the first word and the 'wordSize - k' lowest bits of the second word.
{-# INLINE extractWord #-}
extractWord :: Int -> Word -> Word -> Word
extractWord k lo hi = (lo `shiftR` k) .|. (hi `shiftL` (wordSize - k))
-- given a bit offset 'k', 2 words 'lo' and 'hi' and a word 'x', overlay 'x' onto 'lo' and 'hi' at the position such that (k `elem` [0..wordSize] ==> uncurry (extractWord k) (spliceWord k lo hi x) == x) and (k `elem` [0..wordSize] ==> spliceWord k lo hi (extractWord k lo hi) == (lo,hi))
{-# INLINE spliceWord #-}
spliceWord :: Int -> Word -> Word -> Word -> (Word, Word)
spliceWord k lo hi x =
( meld k lo (x `shiftL` k)
, meld k (x `shiftR` (wordSize - k)) hi
)
-- this could be given a more general type, but it would be wrong; it works for any fixed word size, but only for unsigned types
reverseWord :: Word -> Word
reverseWord x = foldr swap x masks
where
nextMask (d, x) = (d', x `xor` shift x d')
where !d' = d `shiftR` 1
!(_:masks) =
takeWhile ((0 /=) . snd)
(iterate nextMask (bitSize x, maxBound))
swap (n, m) x = ((x .&. m) `shiftL` n) .|. ((x .&. complement m) `shiftR` n)
-- TODO: is an unrolled version like "loop lgWordSize" faster than the generic implementation above? If so, can that be fixed?
-- loop 0 x = x
-- loop 1 x = loop 0 (((x .&. 0x5555555555555555) `shiftL` 1) .|. ((x .&. 0xAAAAAAAAAAAAAAAA) `shiftR` 1))
-- loop 2 x = loop 1 (((x .&. 0x3333333333333333) `shiftL` 2) .|. ((x .&. 0xCCCCCCCCCCCCCCCC) `shiftR` 2))
-- loop 3 x = loop 2 (((x .&. 0x0F0F0F0F0F0F0F0F) `shiftL` 4) .|. ((x .&. 0xF0F0F0F0F0F0F0F0) `shiftR` 4))
-- loop 4 x = loop 3 (((x .&. 0x00FF00FF00FF00FF) `shiftL` 8) .|. ((x .&. 0xFF00FF00FF00FF00) `shiftR` 8))
-- loop 5 x = loop 4 (((x .&. 0x0000FFFF0000FFFF) `shiftL` 16) .|. ((x .&. 0xFFFF0000FFFF0000) `shiftR` 16))
-- loop 6 x = loop 5 (((x .&. 0x00000000FFFFFFFF) `shiftL` 32) .|. ((x .&. 0xFFFFFFFF00000000) `shiftR` 32))
-- loop _ _ = error "reverseWord only implemented for up to 64 bit words!"
reversePartialWord n w
| n >= wordSize = reverseWord w
| otherwise = reverseWord w `shiftR` (wordSize - n)
diff :: Word -> Word -> Word
diff w1 w2 = w1 .&. complement w2
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 704
popCount :: Bits a => a -> Int
popCount = loop 0
where
loop !n 0 = n
loop !n x = loop (n+1) (x .&. (x - 1))
#endif
ffs :: Word -> Maybe Int
ffs 0 = Nothing
ffs x = Just $! (popCount (x `xor` complement (-x)) - 1)
-- TODO: this can probably be faster
-- the interface is very specialized here; 'j' is an offset to add to every bit index and the result is a difference list
bitsInWord :: Int -> Word -> [Int] -> [Int]
bitsInWord j = loop id
where
loop is !w = case ffs w of
Nothing -> is
Just i -> loop (is . (j + i :)) (clearBit w i)
-- TODO: faster!
selectWord :: Word -> Word -> (Int, Word)
selectWord m x = loop 0 0 0
where
loop !i !ct !y
| i >= wordSize = (ct, y)
| testBit m i = loop (i+1) (ct+1) (if testBit x i then setBit y ct else y)
| otherwise = loop (i+1) ct y