bitvec-1.0.0.0: src/Data/Bit/Immutable.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
#ifndef BITVEC_THREADSAFE
module Data.Bit.Immutable
#else
module Data.Bit.ImmutableTS
#endif
( castFromWords
, castToWords
, cloneToWords
, zipBits
, selectBits
, excludeBits
, bitIndex
) where
import Control.Monad
import Control.Monad.ST
import Data.Bits
#ifndef BITVEC_THREADSAFE
import Data.Bit.Internal
import qualified Data.Bit.Mutable as B
#else
import Data.Bit.InternalTS
import qualified Data.Bit.MutableTS as B
#endif
import Data.Bit.Utils
import qualified Data.Vector.Generic.Mutable as MV
import qualified Data.Vector.Generic as V
import Data.Vector.Unboxed as U
hiding (and, or, any, all, reverse, findIndex)
import qualified Data.Vector.Unboxed as Unsafe
import Data.Word
import Prelude as P
hiding (and, or, any, all, reverse)
-- | Cast a vector of words to a vector of bits.
-- Cf. 'Data.Bit.castFromWordsM'.
--
-- >>> castFromWords (Data.Vector.Unboxed.singleton 123)
-- [1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
castFromWords
:: U.Vector Word
-> U.Vector Bit
castFromWords ws = BitVec 0 (nBits (V.length ws)) ws
-- | Try to cast a vector of bits to a vector of words.
-- It succeeds if a vector of bits is aligned.
-- Use 'cloneToWords' otherwise.
-- Cf. 'Data.Bit.castToWordsM'.
--
-- prop> castToWords (castFromWords v) == Just v
castToWords
:: U.Vector Bit
-> Maybe (U.Vector Word)
castToWords (BitVec s n ws)
| aligned s
, aligned n
= Just $ V.slice (divWordSize s) (nWords n) ws
| otherwise
= Nothing
-- | Clone a vector of bits to a new unboxed vector of words.
-- If the bits don't completely fill the words, the last word will be zero-padded.
-- Cf. 'Data.Bit.cloneToWordsM'.
--
-- >>> cloneToWords (read "[1,1,0,1,1,1,1,0]")
-- [123]
cloneToWords
:: U.Vector Bit
-> U.Vector Word
cloneToWords v@(BitVec _ n _) = runST $ do
ws <- MV.new (nWords n)
let loop !i !j
| i >= n = return ()
| otherwise = do
MV.write ws j (indexWord v i)
loop (i + wordSize) (j + 1)
loop 0 0
V.unsafeFreeze ws
{-# INLINE cloneToWords #-}
-- | Zip two vectors with the given function.
-- Similar to 'Data.Vector.Unboxed.zipWith', but much faster.
--
-- >>> import Data.Bits
-- >>> zipBits (.&.) (read "[1,1,0]") (read "[0,1,1]") -- intersection
-- [0,1,0]
-- >>> zipBits (.|.) (read "[1,1,0]") (read "[0,1,1]") -- union
-- [1,1,1]
-- >>> zipBits (\x y -> x .&. complement y) (read "[1,1,0]") (read "[0,1,1]") -- difference
-- [1,0,0]
-- >>> zipBits xor (read "[1,1,0]") (read "[0,1,1]") -- symmetric difference
-- [1,0,1]
zipBits
:: (forall a. Bits a => a -> a -> a)
-> U.Vector Bit
-> U.Vector Bit
-> U.Vector Bit
zipBits f xs ys
| U.length xs >= U.length ys = zs
| otherwise = U.slice 0 (U.length xs) zs
where
zs = U.modify (B.zipInPlace f xs) ys
-- | For each set bit of the first argument, deposit
-- the corresponding bit of the second argument
-- to the result. Similar to the parallel deposit instruction (PDEP).
--
-- >>> selectBits (read "[0,1,0,1,1]") (read "[1,1,0,0,1]")
-- [1,0,1]
--
-- Here is a reference (but slow) implementation:
--
-- > import qualified Data.Vector.Unboxed as U
-- > selectBits mask ws == U.map snd (U.filter (unBit . fst) (U.zip mask ws))
selectBits :: U.Vector Bit -> U.Vector Bit -> U.Vector Bit
selectBits is xs = runST $ do
xs1 <- U.thaw xs
n <- B.selectBitsInPlace is xs1
Unsafe.unsafeFreeze (MV.take n xs1)
-- | For each unset bit of the first argument, deposit
-- the corresponding bit of the second argument
-- to the result.
--
-- >>> excludeBits (read "[0,1,0,1,1]") (read "[1,1,0,0,1]")
-- [1,0]
--
-- Here is a reference (but slow) implementation:
--
-- > import qualified Data.Vector.Unboxed as U
-- > excludeBits mask ws == U.map snd (U.filter (not . unBit . fst) (U.zip mask ws))
excludeBits :: U.Vector Bit -> U.Vector Bit -> U.Vector Bit
excludeBits is xs = runST $ do
xs1 <- U.thaw xs
n <- B.excludeBitsInPlace is xs1
Unsafe.unsafeFreeze (MV.take n xs1)
-- | Return the index of the first bit in the vector
-- with the specified value, if any.
-- Similar to 'Data.Vector.Unboxed.elemIndex', but much faster.
--
-- >>> bitIndex (Bit True) (read "[0,0,1,0,1]")
-- Just 2
-- >>> bitIndex (Bit True) (read "[0,0,0,0,0]")
-- Nothing
--
-- prop> bitIndex bit == nthBitIndex bit 1
--
-- One can also use it to reduce a vector with disjunction or conjunction:
--
-- >>> import Data.Maybe
-- >>> isAnyBitSet = isJust . bitIndex (Bit True)
-- >>> areAllBitsSet = isNothing . bitIndex (Bit False)
bitIndex :: Bit -> U.Vector Bit -> Maybe Int
bitIndex b xs = mfilter (< n) (loop 0)
where
!n = V.length xs
!ff | unBit b = ffs
| otherwise = ffs . complement
loop !i
| i >= n = Nothing
| otherwise = fmap (i +) (ff (indexWord xs i)) `mplus` loop (i + wordSize)