packages feed

bitset 1.2 → 1.3.0

raw patch · 7 files changed

+674/−259 lines, 7 filesdep +containersdep +ghc-primdep +integer-gmpdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: containers, ghc-prim, integer-gmp, random, random-shuffle

Dependency ranges changed: base

API changes (from Hackage documentation)

- Data.BitSet: (\\) :: Enum a => BitSet a -> BitSet a -> BitSet a
- Data.BitSet: data BitSet a
- Data.BitSet: delete :: Enum a => 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: empty :: BitSet a
- Data.BitSet: fromList :: Enum a => [a] -> BitSet a
- Data.BitSet: insert :: Enum a => a -> BitSet a -> BitSet a
- Data.BitSet: instance (Enum a, Show a) => Show (BitSet a)
- Data.BitSet: instance Data a => Data (BitSet a)
- Data.BitSet: instance Enum a => Monoid (BitSet a)
- Data.BitSet: instance Eq (BitSet a)
- Data.BitSet: instance NFData (BitSet a)
- Data.BitSet: instance Ord (BitSet a)
- Data.BitSet: instance Typeable1 BitSet
- 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: member :: Enum a => a -> BitSet a -> Bool
- Data.BitSet: notMember :: Enum a => a -> BitSet a -> Bool
- Data.BitSet: null :: BitSet a -> Bool
- Data.BitSet: singleton :: Enum a => a -> BitSet a
- Data.BitSet: size :: BitSet a -> Int
- Data.BitSet: toIntegral :: Integral b => BitSet a -> b
- 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
- Data.BitSet: unsafeFromIntegral :: Integral b => b -> BitSet a
+ Data.BitSet.Dynamic: (\\) :: BitSet a -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: data FasterInteger
+ Data.BitSet.Dynamic: delete :: a -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: difference :: BitSet a -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: empty :: Enum a => BitSet a
+ Data.BitSet.Dynamic: filter :: Enum a => (a -> Bool) -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: fromList :: Enum a => [a] -> BitSet a
+ Data.BitSet.Dynamic: insert :: a -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: instance Bits FasterInteger
+ Data.BitSet.Dynamic: instance Enum FasterInteger
+ Data.BitSet.Dynamic: instance Eq FasterInteger
+ Data.BitSet.Dynamic: instance Integral FasterInteger
+ Data.BitSet.Dynamic: instance NFData FasterInteger
+ Data.BitSet.Dynamic: instance Num FasterInteger
+ Data.BitSet.Dynamic: instance Ord FasterInteger
+ Data.BitSet.Dynamic: instance Read FasterInteger
+ Data.BitSet.Dynamic: instance Real FasterInteger
+ Data.BitSet.Dynamic: instance Show FasterInteger
+ Data.BitSet.Dynamic: intersection :: BitSet a -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: isProperSubsetOf :: BitSet a -> BitSet a -> Bool
+ Data.BitSet.Dynamic: isSubsetOf :: BitSet a -> BitSet a -> Bool
+ Data.BitSet.Dynamic: map :: (Enum a, Enum b) => (a -> b) -> BitSet a -> BitSet b
+ Data.BitSet.Dynamic: member :: a -> BitSet a -> Bool
+ Data.BitSet.Dynamic: notMember :: a -> BitSet a -> Bool
+ Data.BitSet.Dynamic: null :: BitSet a -> Bool
+ Data.BitSet.Dynamic: singleton :: Enum a => a -> BitSet a
+ Data.BitSet.Dynamic: size :: BitSet a -> Int
+ Data.BitSet.Dynamic: toList :: BitSet a -> [a]
+ Data.BitSet.Dynamic: type BitSet = GBitSet FasterInteger
+ Data.BitSet.Dynamic: union :: BitSet a -> BitSet a -> BitSet a
+ Data.BitSet.Dynamic: unions :: Enum a => [BitSet a] -> BitSet a
+ Data.BitSet.Generic: (\\) :: GBitSet c a -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: BitSet :: Int -> c -> GBitSet c a
+ Data.BitSet.Generic: _bits :: GBitSet c a -> c
+ Data.BitSet.Generic: _n :: GBitSet c a -> Int
+ Data.BitSet.Generic: data GBitSet c a
+ Data.BitSet.Generic: delete :: a -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: difference :: GBitSet c a -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: empty :: (Enum a, Bits c, Num c) => GBitSet c a
+ Data.BitSet.Generic: filter :: (Enum a, Bits c, Num c) => (a -> Bool) -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: fromList :: (Enum a, Bits c, Num c) => [a] -> GBitSet c a
+ Data.BitSet.Generic: insert :: a -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: instance (Enum a, Bits c, Num c) => Monoid (GBitSet c a)
+ Data.BitSet.Generic: instance (Enum a, Read a, Bits c, Num c) => Read (GBitSet c a)
+ Data.BitSet.Generic: instance (Show a, Num c) => Show (GBitSet c a)
+ Data.BitSet.Generic: instance Eq (GBitSet c a)
+ Data.BitSet.Generic: instance NFData c => NFData (GBitSet c a)
+ Data.BitSet.Generic: instance Num c => Foldable (GBitSet c)
+ Data.BitSet.Generic: instance Ord (GBitSet c a)
+ Data.BitSet.Generic: instance Typeable2 GBitSet
+ Data.BitSet.Generic: intersection :: GBitSet c a -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: isProperSubsetOf :: Eq c => GBitSet c a -> GBitSet c a -> Bool
+ Data.BitSet.Generic: isSubsetOf :: GBitSet c a -> GBitSet c a -> Bool
+ Data.BitSet.Generic: map :: (Enum a, Enum b, Bits c, Num c) => (a -> b) -> GBitSet c a -> GBitSet c b
+ Data.BitSet.Generic: member :: a -> GBitSet c a -> Bool
+ Data.BitSet.Generic: notMember :: a -> GBitSet c a -> Bool
+ Data.BitSet.Generic: null :: GBitSet c a -> Bool
+ Data.BitSet.Generic: singleton :: (Enum a, Bits c, Num c) => a -> GBitSet c a
+ Data.BitSet.Generic: size :: GBitSet c a -> Int
+ Data.BitSet.Generic: toList :: Num c => GBitSet c a -> [a]
+ Data.BitSet.Generic: union :: GBitSet c a -> GBitSet c a -> GBitSet c a
+ Data.BitSet.Generic: unions :: (Enum a, Bits c, Num c) => [GBitSet c a] -> GBitSet c a

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2013 Sergei Lebedev+Copyright (c) 2013 Sergei Lebedev, Aleksey Kladov  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,
benchmarks/Benchmarks.hs view
@@ -1,14 +1,96 @@+{-# LANGUAGE ExistentialQuantification #-}+ module Main (main) where -import Criterion.Main (defaultMain, bench, nf)+import Data.List (foldl') -import qualified Data.BitSet as BitSet+import Control.DeepSeq (NFData(..))+import Criterion.Main (defaultMain, bench, bgroup, nf)+import Data.Set (Set)+import System.Random (mkStdGen)+import System.Random.Shuffle (shuffle')+import qualified Data.Set as Set +import Data.BitSet (BitSet)+import qualified Data.BitSet as BS++data B = forall a. NFData a => B a++instance NFData B where+    rnf (B b) = rnf b+ main :: IO () main = do+    let bs1 = BS.fromList elems1+        bs2 = BS.fromList elems2+        s1  = Set.fromList elems1+        s2  = Set.fromList elems2+        r   = mkStdGen 42+        shuffledElems1 = shuffle' elems1 n r+        shuffledElems2 = shuffle' elems2 (n `div` 2) r+    return $ rnf [B bs1, B bs2, B s1, B s2, B shuffledElems1, B shuffledElems2]     defaultMain-        [ bench "fromList" (nf BitSet.fromList [1..n])+        [ bgroup "Set"+          [ bench "fromList" (nf Set.fromList shuffledElems1)+          , bench "toList" (nf Set.toList s1)+          , bench "singleton" (nf Set.singleton n)+          , bench "insert" (nf (insertS elems1) Set.empty)+          , bench "delete" (nf (deleteS elems1) s1)+          , bench "member" (nf (memberS shuffledElems1) s1)+          , bench "notMember" (nf (notMemberS shuffledElems2) s1)+          , bench "isSubsetOf" (nf (Set.isSubsetOf s2) s1)+          , bench "isProperSubsetOf" (nf (Set.isProperSubsetOf s2) s1)+          , bench "intersection" (nf (Set.intersection s2) s1)+          , bench "difference" (nf (Set.difference s2) s1)+          , bench "union" (nf (Set.union s2) s1)+          , bench "map" (nf (Set.map id) s1)+          , bench "filter" (nf (Set.filter $ const True) s1)+          ]++        , bgroup "BitSet"+          [ bench "fromList" (nf BS.fromList shuffledElems1)+          , bench "toList" (nf BS.toList bs1)+          , bench "singleton" (nf BS.singleton n)+          , bench "insert" (nf (insertBS elems1) BS.empty)+          , bench "delete" (nf (deleteBS elems1) bs1)+          , bench "member" (nf (memberBS shuffledElems1) bs1)+          , bench "notMember" (nf (notMemberBS shuffledElems2) bs1)+          , bench "isSubsetOf" (nf (BS.isSubsetOf bs2) bs1)+          , bench "isProperSubsetOf" (nf (BS.isProperSubsetOf bs2) bs1)+          , bench "intersection" (nf (BS.intersection bs2) bs1)+          , bench "difference" (nf (BS.difference bs2) bs1)+          , bench "union" (nf (BS.union bs2) bs1)+          , bench "map" (nf (BS.map id) bs1)+          , bench "filter" (nf (BS.filter $ const True) bs1)+          ]         ]   where     n :: Int-    n = 1024+    n = 128++    elems1 = [1..n]+    elems2 = [1..n `div` 2]++memberS :: [Int] -> Set Int -> Bool+memberS xs s = all (\x -> Set.member x s) xs++memberBS :: [Int] -> BitSet Int -> Bool+memberBS xs bs = all (\x -> BS.member x bs) xs++notMemberS :: [Int] -> Set Int -> Bool+notMemberS xs s = all (\x -> Set.notMember x s) xs++notMemberBS :: [Int] -> BitSet Int -> Bool+notMemberBS xs bs = all (\x -> BS.notMember x bs) xs++insertS :: [Int] -> Set Int -> Set Int+insertS xs s0 = foldl' (\s x -> Set.insert x s) s0 xs++insertBS :: [Int] -> BitSet Int -> BitSet Int+insertBS xs bs0 = foldl' (\bs x -> BS.insert x bs) bs0 xs++deleteS :: [Int] -> Set Int -> Set Int+deleteS xs s0 = foldl' (\s x -> Set.delete x s) s0 xs++deleteBS :: [Int] -> BitSet Int -> BitSet Int+deleteBS xs bs0 = foldl' (\bs x -> BS.delete x bs) bs0 xs
bitset.cabal view
@@ -1,33 +1,40 @@ Name:                 bitset-Version:              1.2-Synopsis:             A compact functional set data structure.+Version:              1.3.0+Synopsis:             A space-efficient set data structure. Description:   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.+  from a type that can be enumerated (i. e. has an `Enum' instance). 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+                    , Aleksey Kladov <aleksey.kladov@gmail.com>+                    , Fedor Gogolev <knsd@knsd.net>+Maintainer:           superbobry@gmail.com+Bug-reports:          http://github.com/superbobry/bitset/issues+Stability:            Experimental Cabal-Version:        >= 1.12 Build-type:           Simple Tested-with:          GHC >= 7.4.2 +Source-repository head+  Type:     git+  Location: https://github.com/superbobry/bitset+ Library   Hs-source-dirs:     src-  Ghc-options:        -Wall+  Ghc-options:        -Wall -fno-warn-orphans   Default-language:   Haskell2010 -  Build-depends:      base                    >= 4.5.1 && < 4.7+  Build-depends:      base                    >= 4.4.0 && < 4.7                     , deepseq                 == 1.3.*+                    , integer-gmp+                    , ghc-prim    Exposed-modules:    Data.BitSet-+                    , Data.BitSet.Dynamic+                    , Data.BitSet.Generic  Test-suite bitset-tests   Hs-source-dirs:     tests@@ -37,25 +44,26 @@   Type:               exitcode-stdio-1.0   Main-is:            Tests.hs -  Build-depends:      base                       >= 4.5.1 && < 4.7+  Build-depends:      base                       >= 4.4.0 && < 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+  Hs-source-dirs:     src benchmarks+  Ghc-options:        -Wall -O2 -optc-O3 -optc-msse4.1 -fno-warn-orphans   Default-language:   Haskell2010+   Type:               exitcode-stdio-1.0+  Main-is:            Benchmarks.hs -  Build-depends:      base                        >= 4.5.1 || < 4.7+  Build-depends:      base                        >= 4.4.0 || < 4.7                     , deepseq                     == 1.3.*-                    , bitset+                    , integer-gmp+                    , ghc-prim                      , criterion                   == 0.6.*---Source-repository head-  Type:     git-  Location: https://github.com/superbobry/bitset+                    , containers                  >= 0.4.2+                    , random                      == 1.0.*+                    , random-shuffle              == 0.0.3
src/Data/BitSet.hs view
@@ -1,184 +1,28 @@-{-# 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@.+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.BitSet.Dynamic+-- Copyright   :  (c) Sergei Lebedev, Aleksey Kladov, Fedor Gogolev 2013+--                Based on Data.BitSet (c) Denis Bueno 2008-2009+-- License     :  MIT+-- Maintainer  :  superbobry@gmail.com+-- Stability   :  experimental+-- Portability :  GHC ----- 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.+-- A space-efficient implementation of set data structure enumerated+-- data types. ----- /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+-- /Note/: Read below the synopsis for important notes on the use of+-- this module.+--+-- This module is intended to be imported @qualified@, to avoid name+-- clashes with "Prelude" functions, e.g.+--+-- > import Data.BitSet (BitSet)+-- > import qualified Data.BitSet as BS+--+-- This module re-exports 'Dynamic' implementation that uses+-- 'Integer' as underlying container. --- | /O(1)/. Project a bit set to an integral type.-toIntegral :: Integral b => BitSet a -> b-toIntegral (BitSet _n i) = fromIntegral i-{-# INLINE toIntegral #-}+module Data.BitSet ( module Data.BitSet.Dynamic ) where --- | /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 #-}+import Data.BitSet.Dynamic
+ src/Data/BitSet/Dynamic.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Data.BitSet.Dynamic+-- Copyright   :  (c) Sergei Lebedev, Aleksey Kladov, Fedor Gogolev 2013+--                Based on Data.BitSet (c) Denis Bueno 2008-2009+-- License     :  MIT+-- Maintainer  :  superbobry@gmail.com+-- Stability   :  experimental+-- Portability :  GHC+--+-- A space-efficient implementation of set data structure enumerated+-- data types.+--+-- /Note/: Read below the synopsis for important notes on the use of+-- this module.+--+-- This module is intended to be imported @qualified@, to avoid name+-- clashes with "Prelude" functions, e.g.+--+-- > import Data.BitSet.Dynamic (BitSet)+-- > import qualified Data.BitSet.Dynamic as BS+--+-- The implementation uses 'Integer' as underlying container, thus it+-- grows automatically when more elements are inserted into the bit set.++module Data.BitSet.Dynamic+    (+    -- * Bit set type+      FasterInteger+    , BitSet+    -- * Operators+    , (\\)++    -- * Construction+    , empty+    , singleton+    , insert+    , delete++    -- * Query+    , null+    , size+    , member+    , notMember+    , isSubsetOf+    , isProperSubsetOf++    -- * Combine+    , union+    , unions+    , difference+    , intersection++    -- * Transformations+    , map++    -- * Filter+    , filter++    -- * Lists+    , toList+    , fromList+    ) where++import Prelude hiding (null, map, filter)++import Data.Bits (Bits(..))+import GHC.Base (Int(..), divInt#, modInt#)+import GHC.Exts (popCnt#)+import GHC.Integer.GMP.Internals (Integer(..))+import GHC.Prim (Int#, Word#, (+#), (==#), (>=#),+                 word2Int#, int2Word#, plusWord#, indexWordArray#)+import GHC.Word (Word(..))++import Control.DeepSeq (NFData(..))++import Data.BitSet.Generic (GBitSet(..))+import qualified Data.BitSet.Generic as BS++-- | A wrapper around 'Integer' which provides faster bit-level operations.+newtype FasterInteger = FasterInteger { unFI :: Integer }+    deriving (Read, Show, Eq, Ord, Enum, Integral, Num, Real, NFData)++instance Bits FasterInteger where+    FasterInteger x .&. FasterInteger y = FasterInteger $ x .&. y+    {-# INLINE (.&.) #-}++    FasterInteger x .|. FasterInteger y = FasterInteger $ x .|. y+    {-# INLINE (.|.) #-}++    FasterInteger x `xor` FasterInteger y = FasterInteger $ x `xor` y+    {-# INLINE xor #-}++    complement = FasterInteger . complement . unFI+    {-# INLINE complement #-}++    shift (FasterInteger x) i = FasterInteger $ shift x i+    {-# INLINE shift #-}++    rotate (FasterInteger x) i = FasterInteger $ rotate x i+    {-# INLINE rotate #-}++    bit = FasterInteger . bit+    {-# INLINE bit #-}++    testBit (FasterInteger x) i = testBitInteger x i+    {-# INLINE testBit #-}++    popCount (FasterInteger x) = I# (word2Int# (popCountInteger x))++    bitSize = bitSize . unFI+    {-# INLINE bitSize #-}++    isSigned = isSigned . unFI+    {-# INLINE isSigned #-}++type BitSet = GBitSet FasterInteger++-- | /O(1)/. Is the bit set empty?+null :: BitSet a -> Bool+null = BS.null+{-# INLINE null #-}++-- | /O(1)/. The number of elements in the bit set.+size :: BitSet a -> Int+size = BS.size+{-# INLINE size #-}++-- | /O(1)/. Ask whether the item is in the bit set.+member :: a -> BitSet a -> Bool+member = BS.member+{-# INLINE member #-}++-- | /O(1)/. Ask whether the item is in the bit set.+notMember :: a -> BitSet a -> Bool+notMember = BS.notMember+{-# INLINE notMember #-}++-- | /O(max(n, m))/. Is this a subset? (@s1 isSubsetOf s2@) tells whether+-- @s1@ is a subset of @s2@.+isSubsetOf :: BitSet a -> BitSet a -> Bool+isSubsetOf = BS.isSubsetOf++-- | /O(max(n, m)/. Is this a proper subset? (ie. a subset but not equal).+isProperSubsetOf :: BitSet a -> BitSet a -> Bool+isProperSubsetOf = BS.isProperSubsetOf++-- | The empty bit set.+empty :: Enum a => BitSet a+empty = BS.empty+{-# INLINE empty #-}++-- | O(1). Create a singleton set.+singleton :: Enum a => a -> BitSet a+singleton = BS.singleton+{-# INLINE singleton #-}++-- | /O(1)/. Insert an item into the bit set.+insert :: a -> BitSet a -> BitSet a+insert = BS.insert+{-# INLINE insert #-}++-- | /O(1)/. Delete an item from the bit set.+delete :: a -> BitSet a -> BitSet a+delete = BS.delete+{-# INLINE delete #-}++-- | /O(max(m, n))/. The union of two bit sets.+union :: BitSet a -> BitSet a -> BitSet a+union = BS.union+{-# INLINE union #-}++-- | /O(max(m, n))/. The union of a list of bit sets.+unions :: Enum a => [BitSet a] -> BitSet a+unions = BS.unions+{-# INLINE unions #-}++-- | /O(max(m, n))/. Difference of two bit sets.+difference :: BitSet a -> BitSet a -> BitSet a+difference = BS.difference+{-# INLINE difference #-}++-- | /O(max(m, n))/. See `difference'.+(\\) :: BitSet a -> BitSet a -> BitSet a+(\\) = difference++-- | /O(max(m, n))/. The intersection of two bit sets.+intersection :: BitSet a -> BitSet a -> BitSet a+intersection = BS.intersection+{-# INLINE intersection #-}++-- | /O(n)/ Transform this bit set by applying a function to every value.+-- Resulting bit set may be smaller then the original.+map :: (Enum a, Enum b) => (a -> b) -> BitSet a -> BitSet b+map = BS.map++-- | /O(n)/ Filter this bit set by retaining only elements satisfying a+-- predicate.+filter :: Enum a => (a -> Bool) -> BitSet a -> BitSet a+filter = BS.filter++-- | /O(n)/. Convert the bit set set to a list of elements.+toList :: BitSet a -> [a]+toList = BS.toList++-- | /O(n)/. Make a bit set from a list of elements.+fromList :: Enum a => [a] -> BitSet a+fromList = BS.fromList+{-# INLINE fromList #-}++popCountInteger :: Integer -> Word#+popCountInteger (S# i#)    = popCnt# (int2Word# i#)+popCountInteger (J# s# d#) = go 0# (int2Word# 0#) where+  go i acc =+      if i ==# s#+      then acc+      else go (i +# 1#) $ acc `plusWord#` popCnt# (indexWordArray# d# i)+{-# INLINE popCountInteger #-}++#include "MachDeps.h"+#ifndef WORD_SIZE_IN_BITS+#error WORD_SIZE_IN_BITS not defined!+#endif++-- Note(superbobry): this will be irrelevant after the new GHC release.+testBitInteger :: Integer -> Int -> Bool+testBitInteger (S# i#) b = I# i# `testBit` b+testBitInteger (J# s# d#) (I# b#) =+    if block# >=# s#+    then False+    else W# (indexWordArray# d# block#) `testBit` I# offset#+  where+    n# :: Int#+    n# = WORD_SIZE_IN_BITS#++    block# :: Int#+    !block# = b# `divInt#` n#++    offset# :: Int#+    !offset# = b# `modInt#` n#+{-# INLINE testBitInteger #-}
+ src/Data/BitSet/Generic.hs view
@@ -0,0 +1,221 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.BitSet.Generic+-- Copyright   :  (c) Sergei Lebedev, Aleksey Kladov, Fedor Gogolev 2013+--                Based on Data.BitSet (c) Denis Bueno 2008-2009+-- License     :  MIT+-- Maintainer  :  superbobry@gmail.com+-- Stability   :  experimental+-- Portability :  GHC+--+-- A space-efficient implementation of set data structure for enumerated+-- data types.+--+-- /Note/: Read below the synopsis for important notes on the use of+-- this module.+--+-- This module is intended to be imported @qualified@, to avoid name+-- clashes with "Prelude" functions, e.g.+--+-- > import Data.BitSet.Generic (BitSet)+-- > import qualified Data.BitSet.Generic as BS+--+-- The implementation is abstract with respect to conatiner type, so any+-- numeric type with 'Bits' instance can be used as a container. However,+-- independent of container choice, the maximum number of elements in a+-- bit set is bounded by @maxBound :: Int@.++{-# LANGUAGE GADTs #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE DeriveDataTypeable #-}++module Data.BitSet.Generic+    (+    -- * Bit set type+      GBitSet(..)+    -- * Operators+    , (\\)++    -- * Construction+    , empty+    , singleton+    , insert+    , delete++    -- * Query+    , null+    , size+    , member+    , notMember+    , isSubsetOf+    , isProperSubsetOf++    -- * Combine+    , union+    , unions+    , difference+    , intersection++    -- * Transformations+    , map++    -- * Filter+    , filter++    -- * Lists+    , toList+    , fromList+    ) where++import Prelude hiding (null, map, filter)++import Control.Applicative ((<$>))+import Control.DeepSeq (NFData(..))+import Data.Bits (Bits, (.|.), (.&.), complement, bit,+                  testBit, setBit, clearBit, popCount)+import Data.Data (Typeable)+import Data.Function (on)+import Data.Monoid (Monoid(..), (<>))+import Text.Read (Read(..), Lexeme(..), lexP, prec, parens)+import qualified Data.Foldable as Foldable+import qualified Data.List as List++-- | A bit set with unspecified container type.+data GBitSet c a = (Enum a, Bits c, Num c) =>+                   BitSet { _n    :: Int  -- ^ Number of elements in the bit set.+                          , _bits :: c    -- ^ Bit container.+                          }+    deriving Typeable++instance Eq (GBitSet c a) where+    (==) = (==) `on` _n++instance Ord (GBitSet c a) where+    compare = compare `on` _n++instance (Enum a, Read a, Bits c, Num c) => Read (GBitSet c a) where+    readPrec = parens . prec 10 $ do+        Ident "fromList" <- lexP+        fromList <$> readPrec++instance (Show a, Num c) => Show (GBitSet c a) where+    showsPrec p bs = showParen (p > 10) $+                     showString "fromList " . shows (toList bs)++instance (Enum a, Bits c, Num c) => Monoid (GBitSet c a) where+    mempty  = empty+    mappend = union+    mconcat = unions++instance NFData c => NFData (GBitSet c a) where+    rnf (BitSet { _n, _bits }) = rnf _n `seq` rnf _bits `seq` ()++instance Num c => Foldable.Foldable (GBitSet c) where+    foldMap f (BitSet { _n, _bits }) = go _n 0 where+        go 0 _b = mempty+        go !n b = if _bits `testBit` b+                  then f (toEnum b) <> go (pred n) (succ b)+                  else go n (succ b)++-- | /O(1)/. Is the bit set empty?+null :: GBitSet c a -> Bool+null (BitSet { _bits }) = _bits == 0+{-# INLINE null #-}++-- | /O(1)/. The number of elements in the bit set.+size :: GBitSet c a -> Int+size = _n+{-# INLINE size #-}++-- | /O(d)/. Ask whether the item is in the bit set.+member :: a -> GBitSet c a -> Bool+member x (BitSet { _bits }) = _bits `testBit` fromEnum x+{-# INLINE member #-}++-- | /O(d)/. Ask whether the item is in the bit set.+notMember :: a -> GBitSet c a -> Bool+notMember x = not . member x+{-# INLINE notMember #-}++-- | /O(max(n, m))/. Is this a subset? (@s1 isSubsetOf s2@) tells whether+-- @s1@ is a subset of @s2@.+isSubsetOf :: GBitSet c a -> GBitSet c a -> Bool+isSubsetOf (BitSet { _n = n1, _bits = b1 }) (BitSet { _n = n2, _bits = b2 }) =+    n2 >= n1 && b2 .|. b1 == b2++-- | /O(max(n, m)/. Is this a proper subset? (ie. a subset but not equal).+isProperSubsetOf :: Eq c => GBitSet c a -> GBitSet c a -> Bool+isProperSubsetOf bs1 bs2 = bs1 `isSubsetOf` bs2 && bs1 /= bs2++-- | The empty bit set.+empty :: (Enum a, Bits c, Num c) => GBitSet c a+empty = BitSet { _n = 0, _bits = 0 }+{-# INLINE empty #-}++-- | O(1). Create a singleton set.+singleton :: (Enum a, Bits c, Num c) => a -> GBitSet c a+singleton x = BitSet { _n = 1, _bits = bit $! fromEnum x }+{-# INLINE singleton #-}++-- | /O(d)/. Insert an item into the bit set.+insert :: a -> GBitSet c a -> GBitSet c a+insert x bs@(BitSet { _bits }) =+    let b = _bits `setBit` fromEnum x in bs { _n = popCount b, _bits = b }+{-# INLINE insert #-}++-- | /O(d)/. Delete an item from the bit set.+delete :: a -> GBitSet c a -> GBitSet c a+delete x bs@(BitSet { _bits }) =+    let b = _bits `clearBit` fromEnum x in bs { _n = popCount b, _bits = b }+{-# INLINE delete #-}++-- | /O(max(m, n))/. The union of two bit sets.+union :: GBitSet c a -> GBitSet c a -> GBitSet c a+union (BitSet { _bits = b1 }) (BitSet { _bits = b2 }) =+    let b = b1 .|. b2 in BitSet { _n = popCount b, _bits = b }++{-# INLINE union #-}++-- | /O(max(m, n))/. The union of a list of bit sets.+unions :: (Enum a, Bits c, Num c) => [GBitSet c a] -> GBitSet c a+unions = List.foldl' union empty+{-# INLINE unions #-}++-- | /O(max(m, n))/. Difference of two bit sets.+difference :: GBitSet c a -> GBitSet c a -> GBitSet c a+difference (BitSet { _bits = b1 }) (BitSet { _bits = b2 }) =+    let b = b1 .&. complement b2 in BitSet { _n = popCount b, _bits = b }+{-# INLINE difference #-}++-- | /O(max(m, n))/. See 'difference'.+(\\) :: GBitSet c a -> GBitSet c a -> GBitSet c a+(\\) = difference++-- | /O(max(m, n))/. The intersection of two bit sets.+intersection :: GBitSet c a -> GBitSet c a -> GBitSet c a+intersection (BitSet { _bits = b1 }) (BitSet { _bits = b2 }) =+    BitSet { _n = popCount b, _bits = b }+  where+    b = b1 .&. b2+{-# INLINE intersection #-}++-- | /O(d * n)/ Transform this bit set by applying a function to every+-- value. Resulting bit set may be smaller then the original.+map :: (Enum a, Enum b, Bits c, Num c) => (a -> b) -> GBitSet c a -> GBitSet c b+map f = fromList . List.map f . toList++-- | /O(d * n)/ Filter this bit set by retaining only elements satisfying+-- predicate.+filter :: (Enum a, Bits c, Num c) => (a -> Bool) -> GBitSet c a -> GBitSet c a+filter f = fromList . List.filter f . toList++-- | /O(d * n)/. Convert the bit set set to a list of elements.+toList :: Num c => GBitSet c a -> [a]+toList = Foldable.toList++-- | /O(d * n)/. Make a bit set from a list of elements.+fromList :: (Enum a, Bits c, Num c) => [a] -> GBitSet c a+fromList xs = BitSet { _n = popCount b, _bits = b } where+  b = List.foldl' (\i x -> setBit i (fromEnum x)) 0 xs+{-# INLINE fromList #-}
tests/Tests.hs view
@@ -1,8 +1,11 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+ module Main (main) where  import Control.Applicative ((<$>)) import Data.List ((\\), intersect, union, nub, sort)-import Data.Monoid (mempty, mappend)+import Data.Monoid ((<>), mempty) import Data.Word (Word16)  import Test.Framework (Test, defaultMain)@@ -10,88 +13,85 @@ import Test.QuickCheck (Property, Arbitrary(..), (==>), classify)  import Data.BitSet (BitSet)-import qualified Data.BitSet as BitSet+import qualified Data.BitSet as BS  instance (Arbitrary a, Enum a) => Arbitrary (BitSet a) where-    arbitrary = BitSet.fromList <$> arbitrary+    arbitrary = BS.fromList <$> arbitrary +instance Show (Word16 -> a) where+    show = const "FUNCTION"  propSize :: [Word16] -> Bool propSize = go . nub where-  go xs = length xs == BitSet.size (BitSet.fromList xs)+  go xs = length xs == BS.size (BS.fromList xs)  propSizeAfterInsert :: Word16 -> BitSet Word16 -> Bool propSizeAfterInsert x bs =-    BitSet.size (BitSet.insert x bs) == BitSet.size bs + diff+    BS.size (BS.insert x bs) == BS.size bs + diff   where     diff :: Int-    diff = if x `BitSet.member` bs then 0 else 1+    diff = if x `BS.member` bs then 0 else 1  propSizeAfterDelete :: Word16 -> BitSet Word16 -> Bool propSizeAfterDelete x bs =-    BitSet.size (BitSet.delete x bs) == BitSet.size bs - diff+    BS.size (BS.delete x bs) == BS.size bs - diff   where     diff :: Int-    diff = if x `BitSet.member` bs then 1 else 0+    diff = if x `BS.member` bs then 1 else 0  propInsertMember :: Word16 -> BitSet Word16 -> Bool-propInsertMember x bs = x `BitSet.member` BitSet.insert x bs+propInsertMember x bs = x `BS.member` BS.insert x bs  propDeleteMember :: Word16 -> BitSet Word16 -> Bool-propDeleteMember x bs = x `BitSet.notMember` BitSet.delete x bs+propDeleteMember x bs = x `BS.notMember` BS.delete x bs  propInsertDeleteIdempotent :: Word16 -> BitSet Word16 -> Property propInsertDeleteIdempotent x bs =-    x `BitSet.notMember` bs ==> bs == BitSet.delete x (BitSet.insert x bs)+    x `BS.notMember` bs ==>+    bs == BS.delete x (BS.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)+    classify (x `BS.member` bs) "x in bs" $+    classify (x `BS.notMember` bs) "x not in bs" $+    BS.delete x bs == BS.delete x (BS.delete x bs)  propInsertIdempotent :: Word16 -> BitSet Word16 -> Bool propInsertIdempotent x bs =-    BitSet.insert x bs == BitSet.insert x (BitSet.insert x bs)+    BS.insert x bs == BS.insert x (BS.insert x bs)  propToList :: [Word16] -> Bool-propToList xs = nub (sort xs) == BitSet.toList bs where+propToList xs = nub (sort xs) == BS.toList bs where   bs :: BitSet Word16-  bs = BitSet.fromList xs+  bs = BS.fromList xs  propFromList :: [Word16] -> Bool-propFromList xs = all (`BitSet.member` bs) xs where+propFromList xs = all (`BS.member` bs) xs where   bs :: BitSet Word16-  bs = BitSet.fromList xs+  bs = BS.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+propEmpty x = x `BS.notMember` BS.empty  propUnions :: [Word16] -> Bool-propUnions xs = all (`BitSet.member` bs) xs where+propUnions xs = all (`BS.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]+  bs = BS.unions $ map BS.fromList [l, r, l, r, l]  propIntersectionWithSelf :: [Word16] -> Bool-propIntersectionWithSelf xs = all (`BitSet.member` bs) xs+propIntersectionWithSelf xs = all (`BS.member` bs) xs   where     bs :: BitSet Word16-    bs = let bs0 = BitSet.fromList xs in bs0 `BitSet.intersection` bs0+    bs = let bs0 = BS.fromList xs in+         bs0 `BS.intersection` bs0  propIntersection :: [Word16] -> Bool propIntersection xs =-    all (`BitSet.member` bs) (l `intersect` r) &&-    all (`BitSet.notMember` bs) (dl `union` dr)+    all (`BS.member` bs) (l `intersect` r) &&+    all (`BS.notMember` bs) (dl `union` dr)   where     n      = length xs     (l, r) = splitAt (n `div` 2) $ nub xs@@ -100,53 +100,64 @@     dr = r \\ l      bs :: BitSet Word16-    bs = let bs1 = BitSet.fromList l-             bs2 = BitSet.fromList r-         in bs1 `BitSet.intersection` bs2+    bs = let bs1 = BS.fromList l+             bs2 = BS.fromList r+         in bs1 `BS.intersection` bs2  propDifferenceWithSelf :: [Word16] -> Bool-propDifferenceWithSelf xs = bs == BitSet.empty where+propDifferenceWithSelf xs = bs == BS.empty where   bs :: BitSet Word16-  bs = let bs0 = BitSet.fromList xs in bs0 `BitSet.difference` bs0+  bs = let bs0 = BS.fromList xs in+       bs0 `BS.difference` bs0  propDifference :: [Word16] -> Property propDifference xs = n > 0 ==>-                    all (`BitSet.member` bs) (l \\ r) &&-                    all (`BitSet.notMember` bs) (l `intersect` r)+                    all (`BS.member` bs) (l \\ r) &&+                    all (`BS.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+    bs = let bs1 = BS.fromList l+             bs2 = BS.fromList r+         in bs1 `BS.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+    bs1 <> mempty == bs1 &&+    mempty <> bs1 == bs1 &&+    bs1 <> (bs2 <> bs3) == (bs1 <> bs2) <> bs3  propIsSubsetOfSelf :: BitSet Word16 -> Bool-propIsSubsetOfSelf bs = bs `BitSet.isSubsetOf` bs &&-                        not (bs `BitSet.isProperSubsetOf` bs)+propIsSubsetOfSelf bs = bs `BS.isSubsetOf` bs &&+                        not (bs `BS.isProperSubsetOf` bs)  propIsSubsetOf :: [Word16] -> Bool propIsSubsetOf xs =-    bs1 `BitSet.isSubsetOf` bs && bs2 `BitSet.isSubsetOf` bs+    bs1 `BS.isSubsetOf` bs &&+    bs2 `BS.isSubsetOf` bs   where     n = length xs      bs :: BitSet Word16-    bs = BitSet.fromList xs+    bs = BS.fromList xs      bs1 :: BitSet Word16-    bs1 = BitSet.fromList $ take (n `div` 2) xs+    bs1 = BS.fromList $ take (n `div` 2) xs      bs2 :: BitSet Word16-    bs2 = BitSet.fromList $ drop (n `div` 2) xs+    bs2 = BS.fromList $ drop (n `div` 2) xs +propShowRead :: BitSet Word16 -> Bool+propShowRead bs = bs == (read $ show bs)++propMap :: BitSet Word16 -> (Word16 -> Word16) -> Bool+propMap bs f = BS.map f bs == (BS.fromList $ map f $ BS.toList bs)++propFilter :: BitSet Word16 -> (Word16 -> Bool) -> Bool+propFilter bs f = BS.filter f bs == (BS.fromList $ filter f $ BS.toList bs)+ main :: IO () main = defaultMain tests where   tests :: [Test]@@ -161,7 +172,6 @@           , 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@@ -170,4 +180,7 @@           , testProperty "monoid laws" propMonoidLaws           , testProperty "is subset of self" propIsSubsetOfSelf           , testProperty "is subset of" propIsSubsetOf+          , testProperty "show read" propShowRead+          , testProperty "map" propMap+          , testProperty "filter" propFilter           ]