bitset 1.1 → 1.2
raw patch · 9 files changed
+453/−262 lines, 9 filesdep +bitsetdep +criteriondep +deepseqdep ~QuickCheckdep ~basesetup-changednew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: bitset, criterion, deepseq, test-framework, test-framework-quickcheck2
Dependency ranges changed: QuickCheck, base
API changes (from Hackage documentation)
+ Data.BitSet: (\\) :: Enum a => BitSet a -> BitSet a -> BitSet a
+ Data.BitSet: difference :: Enum a => BitSet a -> BitSet a -> BitSet a
+ Data.BitSet: elems :: Enum a => BitSet a -> [a]
+ Data.BitSet: instance Enum a => Monoid (BitSet a)
+ Data.BitSet: instance NFData (BitSet a)
+ Data.BitSet: intersection :: Enum a => BitSet a -> BitSet a -> BitSet a
+ Data.BitSet: isProperSubsetOf :: Enum a => BitSet a -> BitSet a -> Bool
+ Data.BitSet: isSubsetOf :: Enum a => BitSet a -> BitSet a -> Bool
+ Data.BitSet: notMember :: Enum a => a -> BitSet a -> Bool
+ Data.BitSet: singleton :: Enum a => a -> BitSet a
+ Data.BitSet: toList :: Enum a => BitSet a -> [a]
+ Data.BitSet: union :: Enum a => BitSet a -> BitSet a -> BitSet a
+ Data.BitSet: unions :: Enum a => [BitSet a] -> BitSet a
Files
- CHANGES +6/−8
- Data/BitSet.hs +0/−108
- LICENSE +13/−27
- Setup.hs +5/−1
- benchmarks/Benchmarks.hs +14/−0
- bitset.cabal +58/−21
- src/Data/BitSet.hs +184/−0
- tests/Properties.hs +0/−97
- tests/Tests.hs +173/−0
CHANGES view
@@ -1,11 +1,9 @@--*- mode: outline -*-+bitset Changelog+================ -* 1.1-Added conversion to and from the underlying Integral value (thanks to Mathieu-Boespflug).+Here you can see the full list of changes between each bitset release. -* 1.0-Instead of a custom Hash typeclass, we now use Enum. It's standard and it gives-us exactly what we need, so it's a win-win.+Version 1.2+----------- -Also, use unboxed tuples to represent the bitset.+Initial release, released on March 22th, 2013
− Data/BitSet.hs
@@ -1,108 +0,0 @@--- | A /bit set/ maintains a record of members from a type that can be mapped--- into (non-negative) @Int@s. The maximum number of elements that can be--- stored is @maxbound :: Int@. Supports insertion, deletion, size, and--- membership testing, and is completely pure (functional).------ To use this library, simply supply a `Enum' instance for your data type or--- have it derived. It is important that the values you intend to keep track--- of start from 0 and go up. A value for which @fromEnum x@ is @n@--- corresponds to bit location @n@ in an @Integer@, and thus requires that--- @Integer@ to have at least @n@ bits.------ The implementation is quite simple: we rely on the @Bits Integer@ instance--- from @Data.Bits@. An advantage of this library over simply using that--- @Bits@ instance is the phantom type parameter used in the `BitSet' type.--- The interface we expose ensures client code will not typecheck if it--- confuses two bit sets intended to keep track of different types.-module Data.BitSet- ( BitSet- , empty- , null- , insert- , fromList- , delete- , member- , size- , toIntegral- , unsafeFromIntegral- ) where--import Prelude hiding ( null )-import Data.Bits-import Data.Data---data BitSet a = BS {-# UNPACK #-} !Int {-# UNPACK #-} !Integer- deriving (Eq, Ord, Data, Typeable)--instance (Enum a, Show a) => Show (BitSet a) where- show (BS _ i :: BitSet a) = "fromList " ++ show (f 0 i)- where f _ 0 = []- f n x = if testBit x 0- then (toEnum n :: a) : f (n+1) (shiftR x 1)- else f (n+1) (shiftR x 1)---- | The empty bit set.-empty :: BitSet a---- | Is the bit set empty?-null :: BitSet a -> Bool---- | /O(setBit on Integer)/ Insert an item into the bit set.-insert :: Enum a => a -> BitSet a -> BitSet a---- | /O(n * setBit on Integer)/ Make a @BitSet@ from a list of items.-fromList :: Enum a => [a] -> BitSet a---- | /O(clearBit on Integer)/ Delete an item from the bit set.-delete :: Enum a => a -> BitSet a -> BitSet a---- | /O(testBit on Integer)/ Ask whether the item is in the bit set.-member :: Enum a => a -> BitSet a -> Bool---- | /O(1)/ The number of elements in the bit set.-size :: BitSet a -> Int---- | /O(1)/ Project a bit set to an integer.-toIntegral :: Integral b => BitSet a -> b---- | /O(n)/ Make a bit set of type @BitSet a@ from an integer. This is unsafe--- because it is not checked whether the bits set in the integer correspond to--- values of type @a@. This is only useful as a more efficient alternative to--- fromList.-unsafeFromIntegral :: Integral b => b -> BitSet a---- * Implementation--{-# INLINE empty #-}-empty = BS 0 0--{-# INLINE null #-}-null (BS n _) = n == 0--{-# INLINE insert #-}-insert x (BS count i) = BS count' (setBit i e)- where count' = if testBit i e then count else count+1- e = fromEnum x--fromList xs = BS (length xs) (foldl (\i x -> setBit i (fromEnum x)) 0 xs)--{-# INLINE delete #-}-delete x (BS count i) = BS count' (clearBit i e)- where count' = if testBit i e then count-1 else count- e = fromEnum x--{-# INLINE member #-}-member x (BS _ i) = testBit i (fromEnum x)--{-# INLINE size #-}-size (BS count _) = count--{-# INLINE toIntegral #-}-toIntegral (BS _ i) = fromIntegral i--{-# INLINE unsafeFromIntegral #-}-unsafeFromIntegral x = let i = fromIntegral x in BS (count i) i- where count 0 = 0- count z | z `mod` 2 == 0 = 1 + count (shiftR z 1)- | otherwise = count (shiftR z 1)
LICENSE view
@@ -1,30 +1,16 @@-Copyright (c) 2009 Denis Bueno-All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.+Copyright (c) 2013 Sergei Lebedev - * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and+associated documentation files (the "Software"), to deal in the Software without restriction,+including without limitation the rights to use, copy, modify, merge, publish, distribute,+sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions: - * Neither the name of Denis Bueno nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.+The above copyright notice and this permission notice shall be included in all copies or substantial+portions of the Software. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT+NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES+OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Setup.hs view
@@ -1,2 +1,6 @@-import Distribution.Simple+#!/usr/bin/env runhaskell++import Distribution.Simple (defaultMain)++main :: IO () main = defaultMain
+ benchmarks/Benchmarks.hs view
@@ -0,0 +1,14 @@+module Main (main) where++import Criterion.Main (defaultMain, bench, nf)++import qualified Data.BitSet as BitSet++main :: IO ()+main = do+ defaultMain+ [ bench "fromList" (nf BitSet.fromList [1..n])+ ]+ where+ n :: Int+ n = 1024
bitset.cabal view
@@ -1,24 +1,61 @@-Name: bitset-Version: 1.1-Synopsis: A functional data structure for efficient membership testing.+Name: bitset+Version: 1.2+Synopsis: A compact functional set data structure. Description:- A /bit set/ maintains a record of members from a type that can be mapped- into (non-negative) @Int@s. Supports insertion, deletion, size, and- membership testing, and is completely pure (functional).-Category: Data Structures-License: BSD3-License-file: LICENSE-Data-files: CHANGES-Author: Denis Bueno-Maintainer: Denis Bueno <dbueno@gmail.com>-Stability: provisional-Cabal-Version: >= 1.2.3-Build-type: Simple-Extra-source-files: tests/Properties.hs+ A /bit set/ is a compact data structure, which maintains a set of members+ from a type that can be enumerated (i. e. has an `Enum' instance). Current+ implementations uses @Integer@ for as bit storage and provides most of the+ expected set operations: insertion, deletion, intersection, membership+ testing etc.+Category: Data Structures+License: MIT+License-file: LICENSE+Data-files: CHANGES+Author: Sergei Lebedev <superbobry@gmail.com>+Maintainer: Sergei Lebedev <superbobry@gmail.com>+Stability: Alpha+Cabal-Version: >= 1.12+Build-type: Simple+Tested-with: GHC >= 7.4.2 Library- Exposed-modules: Data.BitSet- Build-depends: base >= 4.0.0 && < 5, QuickCheck >= 2 && < 3- Extensions: ScopedTypeVariables, DeriveDataTypeable- Ghc-options: -Wall -O2- Ghc-prof-options: -prof -auto-all+ Hs-source-dirs: src+ Ghc-options: -Wall+ Default-language: Haskell2010++ Build-depends: base >= 4.5.1 && < 4.7+ , deepseq == 1.3.*++ Exposed-modules: Data.BitSet+++Test-suite bitset-tests+ Hs-source-dirs: tests+ Ghc-options: -Wall -O2 -fno-warn-orphans+ Default-language: Haskell2010++ Type: exitcode-stdio-1.0+ Main-is: Tests.hs++ Build-depends: base >= 4.5.1 && < 4.7+ , QuickCheck == 2.5.*+ , test-framework == 0.6.*+ , test-framework-quickcheck2 == 0.2.*+ , bitset++Benchmark bitset-benchmarks+ Main-is: Benchmarks.hs+ Hs-source-dirs: tests, benchmarks+ Default-language: Haskell2010+ Type: exitcode-stdio-1.0++ Build-depends: base >= 4.5.1 || < 4.7+ , deepseq == 1.3.*+ , bitset++ , criterion == 0.6.*+++Source-repository head+ Type: git+ Location: https://github.com/superbobry/bitset
+ src/Data/BitSet.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE DeriveDataTypeable #-}++-- | A /bit set/ maintains a record of members from a type that can be+-- enumerated (i. e. has and `Enum' instance). The maximum number of elements+-- that can be stored is @maxBound :: Int@.+--+-- To use this library, define a `Enum' instance for your data type or+-- have it derived. It is important that the values you intend to store+-- in a bit set start from 0 and go up. A value for which @fromEnum x@ is @n@+-- corresponds to bit location @n@ in an @Integer@, and thus requires that+-- @Integer@ to have at least @n@ bits.+--+-- /Note/: The idea of using `Integer' as bit storage for a bit set was+-- borrowed from an unsupported `Data.BitSet' implementation by Denis Bueno.+module Data.BitSet+ (+ -- * Bit set type+ BitSet++ -- * Operators+ , (\\)++ -- * Query+ , null+ , size+ , member+ , notMember+ , isSubsetOf+ , isProperSubsetOf++ -- * Construction+ , empty+ , singleton+ , insert+ , delete++ -- * Combine+ , union+ , unions+ , difference+ , intersection++ -- * Conversion+ -- ** List+ , elems+ , toList+ , fromList+ -- ** Arbitraty integral type+ , toIntegral+ , unsafeFromIntegral+ ) where++import Prelude hiding (null)++import Data.Bits (Bits, (.|.), (.&.), complement,+ testBit, setBit, clearBit, shiftR, popCount)+import Data.Data (Data, Typeable)+import Data.List (foldl')+import Data.Monoid (Monoid(..))++import Control.DeepSeq (NFData(..))++data BitSet a = BitSet {-# UNPACK #-} !Int !Integer+ deriving (Eq, Ord, Data, Typeable)++instance Enum a => Monoid (BitSet a) where+ mempty = empty+ mappend = union+ mconcat = unions++instance (Enum a, Show a) => Show (BitSet a) where+ show bs = "fromList " ++ show (elems bs)++instance NFData (BitSet a) where+ rnf (BitSet count i) = rnf count `seq` rnf i `seq` ()+++-- | /O(1)/. Is the bit set empty?+null :: BitSet a -> Bool+null (BitSet _n i) = i == 0+{-# INLINE null #-}++-- | /O(1)/. The number of elements in the bit set.+size :: BitSet a -> Int+size (BitSet n _i) = n+{-# INLINE size #-}++-- | /O(testBit on Integer)/. Ask whether the item is in the bit set.+member :: Enum a => a -> BitSet a -> Bool+member x (BitSet _n i) = testBit i (fromEnum x)+{-# INLINE member #-}++-- | /O(testBit on Integer)/. Ask whether the item is in the bit set.+notMember :: Enum a => a -> BitSet a -> Bool+notMember bs = not . member bs+{-# INLINE notMember #-}++-- | /O(max(n, m))/.. Is this a subset? (@s1 isSubsetOf s2@) tells whether+-- @s1@ is a subset of @s2@.+isSubsetOf :: Enum a => BitSet a -> BitSet a -> Bool+isSubsetOf (BitSet n1 i1) (BitSet n2 i2) = n2 >= n1 && i2 .|. i1 == i2++-- | /O(max(n, m)/.. Is this a proper subset? (ie. a subset but not equal).+isProperSubsetOf :: Enum a => BitSet a -> BitSet a -> Bool+isProperSubsetOf bs1 bs2 = bs1 `isSubsetOf` bs2 && bs1 /= bs2++-- | The empty bit set.+empty :: BitSet a+empty = BitSet 0 0+{-# INLINE empty #-}++-- | O(setBit on Integer). Create a singleton set.+singleton :: Enum a => a -> BitSet a+singleton x = insert x empty+{-# INLINE singleton #-}++-- | /O(setBit on Integer)/. Insert an item into the bit set.+insert :: Enum a => a -> BitSet a -> BitSet a+insert x (BitSet n i) = BitSet n' $ setBit i e where+ n' = if testBit i e then n else n + 1+ e = fromEnum x+{-# INLINE insert #-}++-- | /O(clearBit on Integer)/. Delete an item from the bit set.+delete :: Enum a => a -> BitSet a -> BitSet a+delete x (BitSet n i) = BitSet n' $ clearBit i e where+ n' = if testBit i e then n - 1 else n+ e = fromEnum x+{-# INLINE delete #-}++-- | /O(max(m, n))/. The union of two bit sets.+union :: Enum a => BitSet a -> BitSet a -> BitSet a+union (BitSet _n1 i1) (BitSet _n2 i2) = BitSet (popCount i) i where+ i = i1 .|. i2+{-# INLINE union #-}++-- | /O(max(m, n))/. The union of a list of bit sets.+unions :: Enum a => [BitSet a] -> BitSet a+unions = foldl' union empty+{-# INLINE unions #-}++-- | /O(max(m, n))/. Difference of two bit sets.+difference :: Enum a => BitSet a -> BitSet a -> BitSet a+difference (BitSet _n1 i1) (BitSet _n2 i2) = BitSet (popCount i) i where+ i = i1 .&. complement i2++-- | /O(max(m, n))/. See `difference'.+(\\) :: Enum a => BitSet a -> BitSet a -> BitSet a+(\\) = difference++-- | /O(max(m, n))/. The intersection of two bit sets.+intersection :: Enum a => BitSet a -> BitSet a -> BitSet a+intersection (BitSet _n1 i1) (BitSet _n2 i2) = BitSet (popCount i) i where+ i = i1 .&. i2++-- | /O(n * shiftR on Integer)/. An alias to @toList@.+elems :: Enum a => BitSet a -> [a]+elems = toList++-- | /O(n * shiftR on Integer)/. Convert a bit set to a list of elements.+toList :: Enum a => BitSet a -> [a]+toList (BitSet _i n0) = go 0 n0 [] where+ go _i 0 acc = reverse acc+ go i n acc = if n `testBit` 0+ then go (i + 1) (shiftR n 1) (toEnum i : acc)+ else go (i + 1) (shiftR n 1) acc++-- | /O(n * setBit on Integer)/. Make a bit set from a list of elements.+fromList :: Enum a => [a] -> BitSet a+fromList xs = BitSet (popCount i) i where+ i = foldl' (\b x -> setBit b (fromEnum x)) 0 xs++-- | /O(1)/. Project a bit set to an integral type.+toIntegral :: Integral b => BitSet a -> b+toIntegral (BitSet _n i) = fromIntegral i+{-# INLINE toIntegral #-}++-- | /O(n)/. Make a bit set from an integral. Unsafe because we don't+-- checked whether the bits set in a given value correspond to values+-- of type @a@. This is only useful as a more efficient alternative to+-- fromList.+unsafeFromIntegral :: Integral b => b -> BitSet a+unsafeFromIntegral x = let i = fromIntegral x in BitSet (popCount i) i+{-# INLINE unsafeFromIntegral #-}
− tests/Properties.hs
@@ -1,97 +0,0 @@-module Properties where--import Control.Monad( liftM )-import Prelude hiding ( null )-import Data.BitSet-import Data.Foldable ( foldl' )-import Data.List ( nub )-import Test.QuickCheck-import System.IO--import qualified Data.List as List----main :: IO ()-main = do- dbgMsg "prop_size: " >> quickCheckWith myargs prop_size- dbgMsg "prop_size_insert: " >> quickCheckWith myargs prop_size_insert- dbgMsg "prop_size_delete: " >> quickCheckWith myargs prop_size_delete- dbgMsg "prop_insert: " >> quickCheckWith myargs prop_insert- dbgMsg "prop_delete: " >> quickCheckWith myargs prop_delete- dbgMsg "prop_insDelIdempotent: " >> quickCheckWith myargs prop_insDelIdempotent- dbgMsg "prop_delDelIdempotent: " >> quickCheckWith myargs prop_delDelIdempotent- dbgMsg "prop_insInsIdempotent: " >> quickCheckWith myargs prop_insInsIdempotent- dbgMsg "prop_extensional: " >> quickCheckWith myargs prop_extensional- dbgMsg "prop_fromList: " >> quickCheckWith myargs prop_fromList- dbgMsg "prop_empty: " >> quickCheckWith myargs prop_empty- dbgMsg "prop_integral: " >> quickCheckWith (myargs{ maxSize = 40 }) prop_integral--dbgMsg = hPutStr stderr--myargs = stdArgs{ maxSize = 64 }---- * Quickcheck properties--trivial test = classify test "trivial"--prop_size xs =- trivial (List.null uxs) $- length uxs == size (foldr insert empty uxs)- where uxs = nub (map abs xs) :: [Int]--prop_size_insert x s =- trivial (null s) $- if xa `member` s then size s == size s'- else size s + 1 == size s'- where s' = insert xa s- xa = abs x :: Int--prop_size_delete x s =- trivial (null s) $- if xa `member` s then size s - 1 == size s'- else size s == size s'- where s' = delete xa s- xa = abs x :: Int--prop_insert x s = xa `member` insert xa s- where xa = abs x :: Int--prop_delete x s = not $ xa `member` delete xa s- where xa = abs x :: Int--prop_insDelIdempotent x s =- classify (not (xa `member` s)) "passed guard" $- not (xa `member` s) ==>- s == (delete xa . insert xa) s- where xa = abs x :: Int--prop_delDelIdempotent x s =- classify (xa `member` s) "x in s" $- classify (not (xa `member` s)) "x not in s" $- delete xa s == (delete xa . delete xa $ s)- where xa = abs x :: Int--prop_insInsIdempotent x s = insert xa s == (insert xa . insert xa) s- where xa = abs x :: Int--prop_extensional xs = and $ map (`member` s) xsa- where s = foldr insert empty xsa- xsa = map abs xs :: [Int]--prop_fromList xs = all (`member` s) xsa- where s = fromList xsa- xsa = map abs xs :: [Int]--prop_empty x = not $ xa `member` empty- where xa = abs x :: Int--prop_integral x s = trivial (null s) $ xa `member` reconstructed- where reconstructed = unsafeFromIntegral (toIntegral xs)- xa = abs x :: Int- xs = xa `insert` s----instance (Arbitrary a, Enum a) => Arbitrary (BitSet a) where- arbitrary = sized $ liftM fromList . vector
+ tests/Tests.hs view
@@ -0,0 +1,173 @@+module Main (main) where++import Control.Applicative ((<$>))+import Data.List ((\\), intersect, union, nub, sort)+import Data.Monoid (mempty, mappend)+import Data.Word (Word16)++import Test.Framework (Test, defaultMain)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Property, Arbitrary(..), (==>), classify)++import Data.BitSet (BitSet)+import qualified Data.BitSet as BitSet++instance (Arbitrary a, Enum a) => Arbitrary (BitSet a) where+ arbitrary = BitSet.fromList <$> arbitrary+++propSize :: [Word16] -> Bool+propSize = go . nub where+ go xs = length xs == BitSet.size (BitSet.fromList xs)++propSizeAfterInsert :: Word16 -> BitSet Word16 -> Bool+propSizeAfterInsert x bs =+ BitSet.size (BitSet.insert x bs) == BitSet.size bs + diff+ where+ diff :: Int+ diff = if x `BitSet.member` bs then 0 else 1++propSizeAfterDelete :: Word16 -> BitSet Word16 -> Bool+propSizeAfterDelete x bs =+ BitSet.size (BitSet.delete x bs) == BitSet.size bs - diff+ where+ diff :: Int+ diff = if x `BitSet.member` bs then 1 else 0++propInsertMember :: Word16 -> BitSet Word16 -> Bool+propInsertMember x bs = x `BitSet.member` BitSet.insert x bs++propDeleteMember :: Word16 -> BitSet Word16 -> Bool+propDeleteMember x bs = x `BitSet.notMember` BitSet.delete x bs++propInsertDeleteIdempotent :: Word16 -> BitSet Word16 -> Property+propInsertDeleteIdempotent x bs =+ x `BitSet.notMember` bs ==> bs == BitSet.delete x (BitSet.insert x bs)++propDeleteIdempotent :: Word16 -> BitSet Word16 -> Property+propDeleteIdempotent x bs =+ classify (x `BitSet.member` bs) "x in bs" $+ classify (x `BitSet.notMember` bs) "x not in bs" $+ BitSet.delete x bs == BitSet.delete x (BitSet.delete x bs)++propInsertIdempotent :: Word16 -> BitSet Word16 -> Bool+propInsertIdempotent x bs =+ BitSet.insert x bs == BitSet.insert x (BitSet.insert x bs)++propToList :: [Word16] -> Bool+propToList xs = nub (sort xs) == BitSet.toList bs where+ bs :: BitSet Word16+ bs = BitSet.fromList xs++propFromList :: [Word16] -> Bool+propFromList xs = all (`BitSet.member` bs) xs where+ bs :: BitSet Word16+ bs = BitSet.fromList xs++propEmpty :: Word16 -> Bool+propEmpty x = x `BitSet.notMember` BitSet.empty++propUnsafeFromIntegral :: Word16 -> Bool+propUnsafeFromIntegral x =+ bs == BitSet.unsafeFromIntegral (BitSet.toIntegral bs :: Integer)+ where+ bs :: BitSet Word16+ bs = BitSet.singleton x++propUnions :: [Word16] -> Bool+propUnions xs = all (`BitSet.member` bs) xs where+ n = length xs+ (l, r) = splitAt (n `div` 2) xs++ bs :: BitSet Word16+ bs = BitSet.unions $ map BitSet.fromList [l, r, l, r, l]++propIntersectionWithSelf :: [Word16] -> Bool+propIntersectionWithSelf xs = all (`BitSet.member` bs) xs+ where+ bs :: BitSet Word16+ bs = let bs0 = BitSet.fromList xs in bs0 `BitSet.intersection` bs0++propIntersection :: [Word16] -> Bool+propIntersection xs =+ all (`BitSet.member` bs) (l `intersect` r) &&+ all (`BitSet.notMember` bs) (dl `union` dr)+ where+ n = length xs+ (l, r) = splitAt (n `div` 2) $ nub xs++ dl = l \\ r+ dr = r \\ l++ bs :: BitSet Word16+ bs = let bs1 = BitSet.fromList l+ bs2 = BitSet.fromList r+ in bs1 `BitSet.intersection` bs2++propDifferenceWithSelf :: [Word16] -> Bool+propDifferenceWithSelf xs = bs == BitSet.empty where+ bs :: BitSet Word16+ bs = let bs0 = BitSet.fromList xs in bs0 `BitSet.difference` bs0++propDifference :: [Word16] -> Property+propDifference xs = n > 0 ==>+ all (`BitSet.member` bs) (l \\ r) &&+ all (`BitSet.notMember` bs) (l `intersect` r)+ where+ n = length xs+ (l, r) = splitAt (n `div` 2) $ nub xs++ bs :: BitSet Word16+ bs = let bs1 = BitSet.fromList l+ bs2 = BitSet.fromList r+ in bs1 `BitSet.difference` bs2++propMonoidLaws :: BitSet Word16 -> BitSet Word16 -> BitSet Word16 -> Bool+propMonoidLaws bs1 bs2 bs3 =+ bs1 `mappend` mempty == bs1 &&+ mempty `mappend` bs1 == bs1 &&+ mappend bs1 (bs2 `mappend` bs3) == (bs1 `mappend` bs2) `mappend` bs3++propIsSubsetOfSelf :: BitSet Word16 -> Bool+propIsSubsetOfSelf bs = bs `BitSet.isSubsetOf` bs &&+ not (bs `BitSet.isProperSubsetOf` bs)++propIsSubsetOf :: [Word16] -> Bool+propIsSubsetOf xs =+ bs1 `BitSet.isSubsetOf` bs && bs2 `BitSet.isSubsetOf` bs+ where+ n = length xs++ bs :: BitSet Word16+ bs = BitSet.fromList xs++ bs1 :: BitSet Word16+ bs1 = BitSet.fromList $ take (n `div` 2) xs++ bs2 :: BitSet Word16+ bs2 = BitSet.fromList $ drop (n `div` 2) xs++main :: IO ()+main = defaultMain tests where+ tests :: [Test]+ tests = [ testProperty "size" propSize+ , testProperty "size after insert" propSizeAfterInsert+ , testProperty "size after delete" propSizeAfterDelete+ , testProperty "insert" propInsertMember+ , testProperty "delete" propDeleteMember+ , testProperty "insert and delete are idempotent" propInsertDeleteIdempotent+ , testProperty "delete is idempotent" propDeleteIdempotent+ , testProperty "insert is idempotent" propInsertIdempotent+ , testProperty "toList" propToList+ , testProperty "fromList" propFromList+ , testProperty "empty" propEmpty+ , testProperty "unsafe construction from integral" propUnsafeFromIntegral+ , testProperty "unions" propUnions+ , testProperty "intersection with self" propIntersectionWithSelf+ , testProperty "intersection" propIntersection+ , testProperty "difference with self" propDifferenceWithSelf+ , testProperty "difference" propDifference+ , testProperty "monoid laws" propMonoidLaws+ , testProperty "is subset of self" propIsSubsetOfSelf+ , testProperty "is subset of" propIsSubsetOf+ ]