{-# LANGUAGE BangPatterns, DeriveDataTypeable, DeriveGeneric, MagicHash #-}
{-# LANGUAGE Trustworthy, TypeFamilies #-}
module Data.BitVector.LittleEndian
( BitVector()
, fromBits
, toBits
, fromNumber
, toSignedNumber
, toUnsignedNumber
, dimension
, isZeroVector
, subRange
) where
import Control.DeepSeq
import Data.Bits
import Data.Data
import Data.Foldable
import Data.Hashable
import Data.List.NonEmpty (NonEmpty(..))
import Data.Monoid ()
import Data.MonoTraversable
import Data.Ord
import Data.Primitive.ByteArray
import Data.Semigroup
import Data.Word
import GHC.Exts
import GHC.Generics
import GHC.Integer.GMP.Internals
import GHC.Integer.Logarithms
import GHC.Natural
import Test.QuickCheck (Arbitrary(..), CoArbitrary(..), NonNegative(..), suchThat, variant)
data BitVector
= BV
{ dim :: {-# UNPACK #-} !Word
, nat :: !Natural
} deriving ( Data
, Generic
, Typeable
)
type instance Element BitVector = Bool
instance Arbitrary BitVector where
arbitrary = do
dimVal <- getNonNegative <$> arbitrary
let upperBound = shiftL 1 dimVal
intVal <- (getNonNegative <$> arbitrary) `suchThat` (< upperBound)
pure . BV (toEnum dimVal) $ intToNat intVal
instance Bits BitVector where
{-# INLINE (.&.) #-}
(BV w1 a) .&. (BV w2 b) = BV (max w1 w2) $ a .&. b
{-# INLINE (.|.) #-}
(BV w1 a) .|. (BV w2 b) = BV (max w1 w2) $ a .|. b
{-# INLINE xor #-}
(BV w1 a) `xor` (BV w2 b) = BV (max w1 w2) $ a `xor` b
{-# INLINE complement #-}
complement (BV w n) = BV w $ (shiftL 1 (fromEnum w)) - 1 - n
{-# INLINE zeroBits #-}
zeroBits = BV 0 0
{-# INLINE bit #-}
bit i = BV (succ $ toEnum i) (shiftL 1 i)
{-# INLINE clearBit #-}
clearBit bv@(BV w n) i
| i < 0 || toEnum i >= w = bv
| otherwise =
let !allBits = pred . shiftL 1 $ fromEnum w
!mask = bit i `xor` allBits
in BV w $ n .&. mask
{-# INLINE testBit #-}
testBit (BV w n) i = i >= 0 && toEnum i < w && n `testBit` i
bitSize (BV w _) = fromEnum w
{-# INLINE bitSizeMaybe #-}
bitSizeMaybe (BV w _) = Just $ fromEnum w
{-# INLINE isSigned #-}
isSigned = const False
{-# INLINE shiftL #-}
shiftL (BV w n) k
| toEnum k > w = BV w 0
| otherwise = BV w $ shiftL n k .&. pred (shiftL 1 (fromEnum w))
{-# INLINE shiftR #-}
shiftR (BV w n) k
| toEnum k > w = BV w 0
| otherwise = BV w $ shiftR n k
{-# INLINE rotateL #-}
rotateL bv 0 = bv
rotateL bv@(BV 0 _) _ = bv
rotateL bv@(BV 1 _) _ = bv
rotateL bv@(BV w n) k
| k < 0 = bv
| j >= w = go . fromEnum $ j `mod` w
| otherwise = go k
where
!j = toEnum k
go 0 = bv
go !i = BV w $ h + l
where
!v = fromEnum w
!d = v - i
!m = pred $ shiftL 1 d
!l = n `shiftR` d
!h = (n .&. m) `shiftL` i
{-# INLINE rotateR #-}
rotateR bv 0 = bv
rotateR bv@(BV 0 _) _ = bv
rotateR bv@(BV 1 _) _ = bv
rotateR bv@(BV w n) k
| k < 0 = bv
| j >= w = go . fromEnum $ j `mod` w
| otherwise = go k
where
!j = toEnum k
go 0 = bv
go !i = BV w $ h + l
where
!v = fromEnum w
!d = v - i
!m = pred $ shiftL 1 i
!l = n `shiftR` i
!h = (n .&. m) `shiftL` d
{-# INLINE popCount #-}
popCount = popCount . nat
instance CoArbitrary BitVector where
coarbitrary bv = variant (dimension bv)
instance Eq BitVector where
{-# INLINE (==) #-}
(==) (BV w1 m) (BV w2 n) = w1 == w2 && m == n
instance FiniteBits BitVector where
{-# INLINE finiteBitSize #-}
finiteBitSize = fromEnum . dim
{-# INLINE countTrailingZeros #-}
countTrailingZeros (BV w n) = max 0 $ fromEnum w - lastSetBit - 1
where
lastSetBit = I# (integerLog2# (toInteger n))
{-# INLINE countLeadingZeros #-}
countLeadingZeros (BV w 0) = fromEnum w
countLeadingZeros (BV w natVal) =
case natVal of
NatS# v -> countTrailingZeros $ iMask .|. W# v
NatJ# (BN# v) -> f $ ByteArray v
where
iMask = complement zeroBits `xor` (2 ^ w - 1)
!x = fromEnum w
f :: ByteArray -> Int
f byteArr = g 0
where
(q, r) = x `quotRem` 64
wMask = complement zeroBits `xor` (2 ^ r - 1) :: Word64
g :: Int -> Int
g !i
| i >= q = countTrailingZeros $ wMask .|. value
| otherwise =
case countTrailingZeros value of
64 -> 64 + g (i+1)
v -> v
where
value :: Word64
value = byteArr `indexByteArray` i
instance Hashable BitVector where
hash (BV w n) = fromEnum w `hashWithSalt` hash n
hashWithSalt salt bv = salt `hashWithSalt` hash bv
instance Monoid BitVector where
{-# INLINE mappend #-}
mappend = (<>)
{-# INLINE mconcat #-}
mconcat bs =
case bs of
[] -> mempty
x:xs -> sconcat $ x:|xs
{-# INLINE mempty #-}
mempty = BV 0 0
instance MonoFoldable BitVector where
{-# INLINE ofoldMap #-}
ofoldMap f = mconcat . fmap f. toBits
{-# INLINE ofoldr #-}
ofoldr f e = foldr f e . toBits
{-# INLINE ofoldl' #-}
ofoldl' f e = foldl' f e . toBits
{-# INLINE ofoldr1Ex #-}
ofoldr1Ex f = foldr1 f . toBits
{-# INLINE ofoldl1Ex' #-}
ofoldl1Ex' f = foldl1 f . toBits
{-# INLINE onull #-}
onull = (== 0) . dim
{-# INLINE olength #-}
olength = fromEnum . dim
instance MonoFunctor BitVector where
omap f (BV w n) = BV w . go (fromEnum w) $ n `xor` n
where
go 0 !acc = acc
go !i !acc = go i' acc'
where
i' = i - 1
acc'
| f (testBit n i') = acc `setBit` i'
| otherwise = acc
instance MonoTraversable BitVector where
{-# INLINE otraverse #-}
otraverse f = fmap fromBits . traverse f . toBits
{-# INLINE omapM #-}
omapM = otraverse
instance NFData BitVector where
{-# INLINE rnf #-}
rnf = const ()
instance Ord BitVector where
{-# INLINE compare #-}
compare lhs rhs =
case comparing dim lhs rhs of
EQ -> comparing nat lhs rhs
v -> v
instance Semigroup BitVector where
{-# INLINE (<>) #-}
(<>) (BV x m) (BV y n) = BV (x + y) $ (n `shiftL` fromEnum x) + m
{-# INLINABLE sconcat #-}
sconcat xs = BV w' n'
where
(w', _, n') = foldl' f (0, 0, 0) xs
f (bitCountW, bitCountI, natVal) (BV w n) =
(bitCountW + w, bitCountI + fromEnum w, natVal + (n `shiftL` bitCountI))
{-# INLINE stimes #-}
stimes 0 _ = mempty
stimes e (BV w n) = BV limit $ go start n
where
!x = fromEnum w
!start = fromEnum $ limit - w
!limit = (toEnum . fromEnum) e * w
go 0 !acc = acc
go !k !acc = go (k-x) $ (n `shiftL` k) + acc
instance Show BitVector where
show (BV w n) = mconcat [ "[", show w, "]", show n ]
{-# INLINE fromBits #-}
fromBits :: Foldable f => f Bool -> BitVector
fromBits bs = BV (toEnum n) k
where
(!n, !k) = foldl' go (0, 0) bs
go (!i, !v) b
| b = (i+1, setBit v i)
| otherwise = (i+1, v)
{-# INLINE toBits #-}
toBits :: BitVector -> [Bool]
toBits (BV w n) = testBit n <$> [ 0 .. fromEnum w - 1 ]
{-# INLINE fromNumber #-}
fromNumber
:: Integral v
=> Word
-> v
-> BitVector
fromNumber !dimValue !intValue = BV dimValue . intToNat $ mask .&. v
where
!v | signum int < 0 = negate $ (shiftL 1 intBits) - int
| otherwise = int
!int = toInteger intValue
!intBits = I# (integerLog2# int)
!mask = 2 ^ dimValue - 1
{-# INLINE toSignedNumber #-}
toSignedNumber :: Num a => BitVector -> a
toSignedNumber (BV w n) = fromInteger v
where
!i = toInteger n
!v | n `testBit` (fromEnum w - 1) = negate $ (shiftL 1 (fromEnum w)) - i
| otherwise = i
{-# INLINE toUnsignedNumber #-}
toUnsignedNumber :: Num a => BitVector -> a
toUnsignedNumber = fromInteger . toInteger . nat
{-# INLINE dimension #-}
dimension :: BitVector -> Word
dimension = dim
{-# INLINE isZeroVector #-}
isZeroVector :: BitVector -> Bool
isZeroVector = (0 ==) . nat
{-# INLINE subRange #-}
subRange :: (Word, Word) -> BitVector -> BitVector
subRange (!lower, !upper) (BV _ n)
| lower > upper = zeroBits
| otherwise =
case toInt lower of
Nothing -> zeroBits
Just i ->
let b = n `shiftR` i
in case toInt upper of
Nothing ->
let m = toEnum $ maxBound - i + 1
in BV m $ n `shiftR` i
Just j ->
let x = j - i
m | x == maxBound = x
| otherwise = x + 1
in BV (toEnum m) $ b .&. pred (1 `shiftL` m)
toInt :: Word -> Maybe Int
toInt w
| w > maxInt = Nothing
| otherwise = Just $ fromEnum w
where
maxInt = toEnum (maxBound :: Int)
{-# INLINE intToNat #-}
intToNat :: Integer -> Natural
intToNat (S# i#) | I# i# >= 0 = NatS# (int2Word# i#)
intToNat (Jp# bn) = NatJ# bn
intToNat _ = NatS# (int2Word# 0#)