bitvec 1.0.1.2 → 1.0.2.0
raw patch · 13 files changed
+291/−110 lines, 13 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Bit: gcdExt :: F2Poly -> F2Poly -> (F2Poly, F2Poly)
+ Data.Bit.ThreadSafe: gcdExt :: F2Poly -> F2Poly -> (F2Poly, F2Poly)
Files
- bench/Bench.hs +2/−0
- bench/Bench/GCD.hs +30/−0
- bitvec.cabal +4/−1
- changelog.md +15/−8
- src/Data/Bit.hs +1/−0
- src/Data/Bit/F2Poly.hs +25/−2
- src/Data/Bit/Internal.hs +64/−85
- src/Data/Bit/Mutable.hs +1/−2
- src/Data/Bit/Utils.hs +1/−1
- test/Main.hs +16/−0
- test/Tests/Conc.hs +64/−0
- test/Tests/SetOps.hs +64/−11
- test/Tests/SetOpsTS.hs +4/−0
bench/Bench.hs view
@@ -3,6 +3,7 @@ import Gauge.Main import Bench.BitIndex+import Bench.GCD import Bench.Intersection import Bench.Invert import Bench.Product@@ -18,6 +19,7 @@ main = defaultMain [ bgroup "bitIndex" $ map benchBitIndex [5..14] , bgroup "invert" $ map benchInvert [5..14]+ , bgroup "gcdExt" $ map benchGCD [5..14] , bgroup "intersection" $ map benchIntersection [5..14] , bgroup "product" $ map benchProduct [5..14] , bgroup "randomWrite" $ map benchRandomWrite [5..14]
+ bench/Bench/GCD.hs view
@@ -0,0 +1,30 @@+module Bench.GCD+ ( benchGCD+ ) where++import Data.Bit+import qualified Data.Bit.ThreadSafe as TS+import Data.Bits+import qualified Data.Vector.Unboxed as U+import qualified Data.Vector.Unboxed.Mutable as MU+import Gauge.Main+import System.Random++randomBools :: [Bool]+randomBools = map (> (0 :: Int)) $ randoms $ mkStdGen $ 42++randomVec :: MU.Unbox a => (Bool -> a) -> Int -> U.Vector a+randomVec f k = U.fromList $ map f $ take n $ randomBools+ where+ n = 1 `shiftL` k++randomVec' :: MU.Unbox a => (Bool -> a) -> Int -> U.Vector a+randomVec' f k = U.fromList $ map f $ take n $ drop n $ randomBools+ where+ n = 1 `shiftL` k++benchGCD :: Int -> Benchmark+benchGCD k = bgroup (show (1 `shiftL` k :: Int))+ [ bench "Bit/gcdExt" $ nf (uncurry gcdExt) ( toF2Poly $ randomVec Bit k, toF2Poly $ randomVec' Bit k)+ , bench "Bit.TS/gcdExt" $ nf (uncurry TS.gcdExt) (TS.toF2Poly $ randomVec TS.Bit k, TS.toF2Poly $ randomVec' TS.Bit k)+ ]
bitvec.cabal view
@@ -1,5 +1,5 @@ name: bitvec-version: 1.0.1.2+version: 1.0.2.0 cabal-version: >=1.10 build-type: Simple license: BSD3@@ -129,9 +129,11 @@ hs-source-dirs: test other-modules: Support+ Tests.Conc Tests.MVector Tests.MVectorTS Tests.SetOps+ Tests.SetOpsTS Tests.Vector ghc-options: -Wall -threaded -rtsopts include-dirs: test@@ -151,6 +153,7 @@ hs-source-dirs: bench other-modules: Bench.BitIndex+ Bench.GCD Bench.Invert Bench.Intersection Bench.Product
changelog.md view
@@ -1,10 +1,17 @@+# 1.0.2.0++* Fix out-of-bounds writes in mutable interface.+* Improve thread-safety of mutable interface.+* Add extended GCD for `F2Poly`.+* Change `Show` instance of `F2Poly`.+ # 1.0.1.2 -* Fix more bugs in 'F2Poly' multiplication.+* Fix more bugs in `F2Poly` multiplication. # 1.0.1.1 -* Fix bugs in 'F2Poly' multiplication.+* Fix bugs in `F2Poly` multiplication. * Performance improvements. # 1.0.1.0@@ -22,18 +29,18 @@ * Redesign API from the scratch. * Add a thread-safe implementation.-* Add 'nthBitIndex' function.+* Add `nthBitIndex` function. # 0.2.0.1 -* Fix 'Read' instance.+* Fix `Read` instance. # 0.2.0.0 -* Remove hand-written 'Num', 'Real', 'Integral', 'Bits' instances.-* Derive 'Bits' and 'FiniteBits' instances.-* Expose 'Bit' constructor directly and remove 'fromBool' function.-* Rename 'toBool' to 'unBit'.+* Remove hand-written `Num`, `Real`, `Integral`, `Bits` instances.+* Derive `Bits` and `FiniteBits` instances.+* Expose `Bit` constructor directly and remove `fromBool` function.+* Rename `toBool` to `unBit`. # 0.1.1.0
src/Data/Bit.hs view
@@ -58,6 +58,7 @@ , F2Poly , unF2Poly , toF2Poly+ , gcdExt ) where import Prelude hiding (and, or)
src/Data/Bit/F2Poly.hs view
@@ -16,6 +16,7 @@ ( F2Poly , unF2Poly , toF2Poly+ , gcdExt ) where import Control.DeepSeq@@ -33,12 +34,14 @@ #endif import Data.Bit.Utils import Data.Bits+import Data.Char import Data.Coerce import Data.Primitive.ByteArray import Data.Typeable import qualified Data.Vector.Unboxed as U import qualified Data.Vector.Unboxed.Mutable as MU import GHC.Generics+import Numeric #if UseIntegerGmp import qualified Data.Vector.Primitive as P@@ -59,13 +62,13 @@ -- >>> :set -XBinaryLiterals -- >>> -- (1 + x) (1 + x + x^2) = 1 + x^3 (mod 2) -- >>> 0b11 * 0b111 :: F2Poly--- F2Poly {unF2Poly = [1,0,0,1]}+-- 0b1001 newtype F2Poly = F2Poly { unF2Poly :: U.Vector Bit -- ^ Convert 'F2Poly' to a vector of coefficients -- (first element corresponds to a constant term). }- deriving (Eq, Ord, Show, Typeable, Generic, NFData)+ deriving (Eq, Ord, Typeable, Generic, NFData) -- | Make 'F2Poly' from a list of coefficients -- (first element corresponds to a constant term).@@ -131,6 +134,9 @@ divMod = quotRem mod = rem +instance Show F2Poly where+ show = (:) '0' . (:) 'b' . flip (showIntAtBase 2 intToDigit) "" . toInteger+ -- | Inputs must be valid for wrapping into F2Poly: no trailing garbage is allowed. xorBits :: U.Vector Bit@@ -309,3 +315,20 @@ bitsToInteger = U.ifoldl' (\acc i (Bit b) -> if b then acc `setBit` i else acc) 0 #endif++-- | Execute the extended Euclidean algorithm.+-- For polynomials @a@ and @b@, compute their unique greatest common divisor @g@+-- and the unique coefficient polynomial @s@ satisfying @as + bt = g@.+--+-- >>> :set -XBinaryLiterals+-- >>> gcdExt 0b101 0b0101+-- (0b101,0b0)+-- >>> gcdExt 0b11 0b111+-- (0b1,0b10)+gcdExt :: F2Poly -> F2Poly -> (F2Poly, F2Poly)+gcdExt = go 1 0+ where+ go s s' r r'+ | r' == 0 = (r, s)+ | otherwise = case quotRem r r' of+ (q, r'') -> go s' (s - q * s') r' r''
src/Data/Bit/Internal.hs view
@@ -25,6 +25,7 @@ , unsafeFlipBit , flipBit , WithInternals(..)+ , modifyByteArray ) where #include "vector.h"@@ -182,45 +183,63 @@ #endif {-# INLINE readWord #-} +modifyByteArray+ :: PrimMonad m+ => MutableByteArray (PrimState m)+ -> Int+ -> Word+ -> Word+ -> m ()+#ifndef BITVEC_THREADSAFE+modifyByteArray arr ix msk new = do+ old <- readByteArray arr ix+ writeByteArray arr ix (old .&. msk .|. new)+{-# INLINE modifyByteArray #-}+#else+modifyByteArray (MutableByteArray mba) (I# ix) (W# msk) (W# new) = do+ primitive $ \state ->+ let !(# state', _ #) = fetchAndIntArray# mba ix (word2Int# msk) state in+ let !(# state'', _ #) = fetchOrIntArray# mba ix (word2Int# new) state' in+ (# state'', () #)++-- https://gitlab.haskell.org/ghc/ghc/issues/17334+#if __GLASGOW_HASKELL__ == 808 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1+{-# NOINLINE modifyByteArray #-}+#else+{-# INLINE modifyByteArray #-}+#endif++#endif+ -- | write a word at the given bit offset in little-endian order (i.e., the LSB will correspond to the bit at the given address, the 2's bit will correspond to the address + 1, etc.). If the offset is such that the word extends past the end of the vector, the word is truncated and as many low-order bits as possible are written. writeWord :: PrimMonad m => U.MVector (PrimState m) Bit -> Int -> Word -> m () writeWord !(BitMVec _ 0 _) _ _ = pure ()-writeWord !(BitMVec off len' arr) !i' !x = do- let len = off + len'- lenMod = modWordSize len- i = off + i'- nMod = modWordSize i- loIx = divWordSize i+writeWord !(BitMVec off len' arr) !i' !x+ | iMod == 0+ = if len >= i + wordSize+ then writeByteArray arr iDiv x+ else modifyByteArray arr iDiv (hiMask lenMod) (x .&. loMask lenMod)+ | iDiv == divWordSize (len - 1)+ = if lenMod == 0+ then modifyByteArray arr iDiv (loMask iMod) (x `unsafeShiftL` iMod)+ else modifyByteArray arr iDiv (loMask iMod .|. hiMask lenMod) ((x `unsafeShiftL` iMod) .&. loMask lenMod)+ | iDiv + 1 == divWordSize (len - 1)+ = do+ modifyByteArray arr iDiv (loMask iMod) (x `unsafeShiftL` iMod)+ if lenMod == 0+ then modifyByteArray arr (iDiv + 1) (hiMask iMod) (x `unsafeShiftR` (wordSize - iMod))+ else modifyByteArray arr (iDiv + 1) (hiMask iMod .|. hiMask lenMod) (x `unsafeShiftR` (wordSize - iMod) .&. loMask lenMod)+ | otherwise+ = do+ modifyByteArray arr iDiv (loMask iMod) (x `unsafeShiftL` iMod)+ modifyByteArray arr (iDiv + 1) (hiMask iMod) (x `unsafeShiftR` (wordSize - iMod))+ where+ len = off + len'+ lenMod = modWordSize len+ i = off + i'+ iMod = modWordSize i+ iDiv = divWordSize i - if nMod == 0- then if len >= i + wordSize- then writeByteArray arr loIx x- else do- loWord <- readByteArray arr loIx- writeByteArray arr loIx- $ (loWord .&. hiMask lenMod)- .|. (x .&. loMask lenMod)- else if loIx == divWordSize (len - 1)- then do- loWord <- readByteArray arr loIx- if lenMod == 0- then- writeByteArray arr loIx- $ (loWord .&. loMask nMod)- .|. (x `unsafeShiftL` nMod)- else- writeByteArray arr loIx- $ (loWord .&. (loMask nMod .|. hiMask lenMod))- .|. ((x `unsafeShiftL` nMod) .&. loMask lenMod)- else do- loWord <- readByteArray arr loIx- writeByteArray arr loIx- $ (loWord .&. loMask nMod)- .|. (x `unsafeShiftL` nMod)- hiWord <- readByteArray arr (loIx + 1)- writeByteArray arr (loIx + 1)- $ (hiWord .&. hiMask nMod)- .|. (x `unsafeShiftR` (wordSize - nMod)) #if __GLASGOW_HASKELL__ >= 800 {-# SPECIALIZE writeWord :: U.MVector s Bit -> Int -> Word -> ST s () #-} #endif@@ -300,9 +319,7 @@ 0 -> setByteArray arr offWords lWords (x :: Word) nMod -> do setByteArray arr offWords (lWords - 1) (x :: Word)- lastWord <- readByteArray arr (offWords + lWords - 1)- let lastWord' = lastWord .&. hiMask nMod .|. x .&. loMask nMod- writeByteArray arr (offWords + lWords - 1) lastWord'+ modifyByteArray arr (offWords + lWords - 1) (hiMask nMod) (x .&. loMask nMod) where offBits = modWordSize off offWords = divWordSize off@@ -310,27 +327,16 @@ basicSet (BitMVec off len arr) (extendToWord -> x) = case modWordSize (off + len) of 0 -> do- firstWord <- readByteArray arr offWords- let firstWord' = firstWord .&. loMask offBits .|. x .&. hiMask offBits- writeByteArray arr offWords firstWord'+ modifyByteArray arr offWords (loMask offBits) (x .&. hiMask offBits) setByteArray arr (offWords + 1) (lWords - 1) (x :: Word) nMod -> if lWords == 1 then do- theOnlyWord <- readByteArray arr offWords let lohiMask = loMask offBits .|. hiMask nMod- theOnlyWord' =- theOnlyWord .&. lohiMask .|. x .&. complement lohiMask- writeByteArray arr offWords theOnlyWord'+ modifyByteArray arr offWords lohiMask (x .&. complement lohiMask) else do- firstWord <- readByteArray arr offWords- let firstWord' = firstWord .&. loMask offBits .|. x .&. hiMask offBits- writeByteArray arr offWords firstWord'-+ modifyByteArray arr offWords (loMask offBits) (x .&. hiMask offBits) setByteArray arr (offWords + 1) (lWords - 2) (x :: Word)-- lastWord <- readByteArray arr (offWords + lWords - 1)- let lastWord' = lastWord .&. hiMask nMod .|. x .&. loMask nMod- writeByteArray arr (offWords + lWords - 1) lastWord'+ modifyByteArray arr (offWords + lWords - 1) (hiMask nMod) (x .&. loMask nMod) where offBits = modWordSize off offWords = divWordSize off@@ -353,10 +359,7 @@ (wordsToBytes $ lDstWords - 1) lastWordSrc <- readByteArray src (offSrcWords + lDstWords - 1)- lastWordDst <- readByteArray dst (offDstWords + lDstWords - 1)- let lastWordDst' =- lastWordDst .&. hiMask nMod .|. lastWordSrc .&. loMask nMod- writeByteArray dst (offDstWords + lDstWords - 1) lastWordDst'+ modifyByteArray dst (offDstWords + lDstWords - 1) (hiMask nMod) (lastWordSrc .&. loMask nMod) where offDstBits = modWordSize offDst offDstWords = divWordSize offDst@@ -367,14 +370,7 @@ | offDstBits == offSrcBits = case modWordSize (offSrc + lenDst) of 0 -> do firstWordSrc <- readByteArray src offSrcWords- firstWordDst <- readByteArray dst offDstWords- let firstWordDst' =- firstWordDst- .&. loMask offSrcBits- .|. firstWordSrc- .&. hiMask offSrcBits- writeByteArray dst offDstWords firstWordDst'-+ modifyByteArray dst offDstWords (loMask offSrcBits) (firstWordSrc .&. hiMask offSrcBits) copyMutableByteArray dst (wordsToBytes $ offDstWords + 1) src@@ -384,34 +380,17 @@ then do let lohiMask = loMask offSrcBits .|. hiMask nMod theOnlyWordSrc <- readByteArray src offSrcWords- theOnlyWordDst <- readByteArray dst offDstWords- let theOnlyWordDst' =- theOnlyWordDst- .&. lohiMask- .|. theOnlyWordSrc- .&. complement lohiMask- writeByteArray dst offDstWords theOnlyWordDst'+ modifyByteArray dst offDstWords lohiMask (theOnlyWordSrc .&. complement lohiMask) else do firstWordSrc <- readByteArray src offSrcWords- firstWordDst <- readByteArray dst offDstWords- let firstWordDst' =- firstWordDst- .&. loMask offSrcBits- .|. firstWordSrc- .&. hiMask offSrcBits- writeByteArray dst offDstWords firstWordDst'-+ modifyByteArray dst offDstWords (loMask offSrcBits) (firstWordSrc .&. hiMask offSrcBits) copyMutableByteArray dst (wordsToBytes $ offDstWords + 1) src (wordsToBytes $ offSrcWords + 1) (wordsToBytes $ lDstWords - 2)- lastWordSrc <- readByteArray src (offSrcWords + lDstWords - 1)- lastWordDst <- readByteArray dst (offDstWords + lDstWords - 1)- let lastWordDst' =- lastWordDst .&. hiMask nMod .|. lastWordSrc .&. loMask nMod- writeByteArray dst (offDstWords + lDstWords - 1) lastWordDst'+ modifyByteArray dst (offDstWords + lDstWords - 1) (hiMask nMod) (lastWordSrc .&. loMask nMod) where offDstBits = modWordSize offDst offDstWords = divWordSize offDst
src/Data/Bit/Mutable.hs view
@@ -104,8 +104,7 @@ writeWord vecYs 0 (f x y) | otherwise = do y <- readByteArray ys base- writeByteArray ys base $- (y .&. loMask shft) .|. (f (x `unsafeShiftL` shft) y .&. hiMask shft)+ modifyByteArray ys base (loMask shft) (f (x `unsafeShiftL` shft) y .&. hiMask shft) go' (len - wordSize + shft) (offXs + wordSize - shft) (base + 1) where vecXs = BitVec offXs len xs
src/Data/Bit/Utils.hs view
@@ -39,7 +39,7 @@ lgWordSize = case wordSize of 32 -> 5 64 -> 6- _ -> error "wordsToBytes: unknown architecture"+ _ -> error "lgWordSize: unknown architecture" wordSizeMask = wordSize - 1 wordSizeMaskC = complement wordSizeMask
test/Main.hs view
@@ -14,9 +14,11 @@ import Test.Tasty.QuickCheck import Support+import Tests.Conc (concTests) import Tests.MVector (mvectorTests) import qualified Tests.MVectorTS as TS (mvectorTests) import Tests.SetOps (setOpTests)+import qualified Tests.SetOpsTS as TS (setOpTests) import Tests.Vector (vectorTests) main :: IO ()@@ -26,7 +28,9 @@ , mvectorTests , TS.mvectorTests , setOpTests+ , TS.setOpTests , vectorTests+ , concTests ] lawsTests :: TestTree@@ -53,6 +57,7 @@ , tenTimesLess $ testProperty "Multiplication long" prop_f2polyMulLong , tenTimesLess $ testProperty "Square long" prop_f2polySqrLong , testProperty "Remainder" prop_f2polyRem+ , testProperty "GCD" prop_f2polyGCD , tenTimesLess $ lawsToTest $ showLaws (Proxy :: Proxy F2Poly) #if MIN_VERSION_quickcheck_classes(0,6,3)@@ -85,6 +90,17 @@ prop_f2polyRem :: F2Poly -> F2Poly -> Property prop_f2polyRem x y = y /= 0 ==> x `rem` y === fromInteger (toInteger x `binRem` toInteger y)++-- For polynomials @x@ and @y@, @gcdExt@ computes their unique greatest common+-- divisor @g@ and the unique coefficient polynomial @s@ satisfying @xs + yt = g@.+--+-- Thus it is sufficient to check @gcd == fst . gcdExt@ and @xs == g (mod y)@,+-- except if @y@ divides @x@, then @gcdExt x y@ is @(y, 0)@ and @xs `rem` y@ is zero,+-- so that it is then necessary to check @xs `rem` y == g `rem` y == 0@.+prop_f2polyGCD :: F2Poly -> F2Poly -> Property+prop_f2polyGCD x y = g === x `gcd` y .&&. (y /= 0 ==> (x * s) `rem` y === g `rem` y)+ where+ (g, s) = x `gcdExt` y binMul :: Integer -> Integer -> Integer binMul = go 0
+ test/Tests/Conc.hs view
@@ -0,0 +1,64 @@+module Tests.Conc where++import Control.Concurrent+import Control.Monad+import Data.Bit.ThreadSafe+import Data.Bits+import qualified Data.Vector.Generic as V+import qualified Data.Vector.Generic.Mutable as M+import qualified Data.Vector.Unboxed as U+import Test.Tasty+import Test.Tasty.HUnit++concTests :: TestTree+concTests = testGroup "Concurrency"+ [ testCase "invertInPlace" case_conc_invert+ , testCase "reverseInPlace" case_conc_reverse+ , testCase "zipInPlace" case_conc_zip+ ]++runConcurrently :: IO () -> IO () -> IO ()+runConcurrently action1 action2 = do+ m <- newEmptyMVar+ _ <- forkIO $ do+ action1+ putMVar m ()+ action2+ takeMVar m++case_conc_invert :: IO ()+case_conc_invert = replicateM_ 1000 $ do+ let len = 64+ len' = 37+ vec <- M.replicate len (Bit True)+ ref <- V.freeze vec :: IO (U.Vector Bit)+ runConcurrently+ (replicateM_ 1000 $ invertInPlace (M.take len' vec))+ (replicateM_ 1000 $ invertInPlace (M.drop len' vec))+ wec <- V.unsafeFreeze vec+ assertEqual "should be equal" ref wec++case_conc_reverse :: IO ()+case_conc_reverse = replicateM_ 1000 $ do+ let len = 128+ len' = 66+ vec <- M.new len+ forM_ [0 .. len - 1] $ \i -> M.write vec i (Bit $ odd i)+ ref <- V.freeze vec :: IO (U.Vector Bit)+ runConcurrently+ (replicateM_ 1000 $ reverseInPlace (M.take len' vec))+ (replicateM_ 1000 $ reverseInPlace (M.drop len' vec))+ wec <- V.unsafeFreeze vec+ assertEqual "should be equal" ref wec++case_conc_zip :: IO ()+case_conc_zip = replicateM_ 1000 $ do+ let len = 128+ len' = 37+ vec <- M.replicate len (Bit True)+ let ref = V.replicate len (Bit False)+ runConcurrently+ (replicateM_ 1001 $ zipInPlace (const complement) ref (M.take len' vec))+ (replicateM_ 1001 $ zipInPlace (const complement) ref (M.drop len' vec))+ wec <- V.unsafeFreeze vec+ assertEqual "should be equal" ref wec
test/Tests/SetOps.hs view
@@ -1,29 +1,40 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} +#ifndef BITVEC_THREADSAFE module Tests.SetOps where+#else+module Tests.SetOpsTS where+#endif import Support () +import Control.Monad+import Control.Monad.ST import Data.Bit import Data.Bits import qualified Data.Vector.Unboxed as U+import qualified Data.Vector.Unboxed.Mutable as MU import Test.Tasty import Test.Tasty.QuickCheck hiding ((.&.)) setOpTests :: TestTree setOpTests = testGroup "Set operations"- [ testProperty "generalize" prop_generalize- , testProperty "zipBits" prop_zipBits- , testProperty "zipInPlace" prop_zipInPlace- , testProperty "invertBits" prop_invertBits- , testProperty "invertBitsWords" prop_invertBitsWords- , testProperty "invertInPlace" prop_invertInPlace- , testProperty "reverseBits" prop_reverseBits- , testProperty "reverseInPlace" prop_reverseInPlace- , testProperty "selectBits" prop_selectBits_def- , testProperty "excludeBits" prop_excludeBits_def- , testProperty "countBits" prop_countBits_def+ [ testProperty "generalize" prop_generalize+ , testProperty "zipBits" prop_zipBits+ , testProperty "zipInPlace" prop_zipInPlace+ , testProperty "invertBits" prop_invertBits+ , testProperty "invertBitsWords" prop_invertBitsWords+ , testProperty "invertInPlace" prop_invertInPlace+ , testProperty "invertInPlace middle" prop_invertInPlace_middle+ , testProperty "reverseBits" prop_reverseBits+ , testProperty "reverseBitsWords" prop_reverseBitsWords+ , testProperty "reverseInPlace" prop_reverseInPlace+ , testProperty "reverseInPlace middle" prop_reverseInPlace_middle+ , testProperty "selectBits" prop_selectBits_def+ , testProperty "excludeBits" prop_excludeBits_def+ , testProperty "countBits" prop_countBits_def ] prop_generalize :: Fun (Bit, Bit) Bit -> Bit -> Bit -> Property@@ -73,13 +84,55 @@ prop_invertInPlace xs = U.map complement xs === U.modify invertInPlace xs +prop_invertInPlace_middle :: NonNegative Int -> NonNegative Int -> NonNegative Int -> Property+prop_invertInPlace_middle (NonNegative from) (NonNegative len) (NonNegative excess) = runST $ do+ let totalLen = from + len + excess+ vec <- MU.new totalLen+ forM_ [0 .. totalLen - 1] $ \i ->+ MU.write vec i (Bit (odd i))+ ref <- U.freeze vec++ let middle = MU.slice from len vec+ invertInPlace middle+ wec <- U.unsafeFreeze vec++ let refLeft = U.take from ref+ wecLeft = U.take from wec+ refRight = U.drop (from + len) ref+ wecRight = U.drop (from + len) wec+ pure $ refLeft === wecLeft .&&. refRight === wecRight+ prop_reverseBits :: U.Vector Bit -> Property prop_reverseBits xs = U.reverse xs === reverseBits xs +prop_reverseBitsWords :: U.Vector Word -> Property+prop_reverseBitsWords ws =+ U.reverse xs === reverseBits xs+ where+ xs = castFromWords ws+ prop_reverseInPlace :: U.Vector Bit -> Property prop_reverseInPlace xs = U.reverse xs === U.modify reverseInPlace xs++prop_reverseInPlace_middle :: NonNegative Int -> NonNegative Int -> NonNegative Int -> Property+prop_reverseInPlace_middle (NonNegative from) (NonNegative len) (NonNegative excess) = runST $ do+ let totalLen = from + len + excess+ vec <- MU.new totalLen+ forM_ [0 .. totalLen - 1] $ \i ->+ MU.write vec i (Bit (odd i))+ ref <- U.freeze vec++ let middle = MU.slice from len vec+ reverseInPlace middle+ wec <- U.unsafeFreeze vec++ let refLeft = U.take from ref+ wecLeft = U.take from wec+ refRight = U.drop (from + len) ref+ wecRight = U.drop (from + len) wec+ pure $ refLeft === wecLeft .&&. refRight === wecRight select :: U.Unbox a => U.Vector Bit -> U.Vector a -> U.Vector a select mask ws = U.map snd (U.filter (unBit . fst) (U.zip mask ws))
+ test/Tests/SetOpsTS.hs view
@@ -0,0 +1,4 @@+{-# LANGUAGE CPP #-}++#define BITVEC_THREADSAFE+#include "Tests/SetOps.hs"