{-# LANGUAGE UndecidableInstances, IncoherentInstances #-} {-# INCLUDE "Judy.h" #-} module Data.Array.Judy.BitSet where import Data.Typeable import Foreign.ForeignPtr import Foreign.Marshal.Alloc import Foreign.Storable import Foreign.Ptr import System.IO.Unsafe import Data.Array.Judy.Private import Data.Array.Judy.Freeze import Data.Array.Judy.HashIO newtype HashIO a => BitSet a = BitSet { judy :: ForeignPtr Judy1 } deriving (Eq, Ord, Typeable) instance Show (BitSet a) where show (BitSet bs) = "" -- | Swap contents of two sets. swapBitSets :: BitSet a -> BitSet a -> IO () swapBitSets (BitSet j1) (BitSet j2) = do withForeignPtr j1 $ \p1 -> do withForeignPtr j2 $ \p2 -> do v1 <- peek p1 v2 <- peek p2 poke p1 v2 poke p2 v1 -- | Create a set. new :: HashIO a => IO (BitSet a) new = do fp <- mallocForeignPtr addForeignPtrFinalizer judy1_free_ptr fp withForeignPtr fp $ flip poke nullPtr return $ BitSet fp -- | Add a value to the set. insert :: HashIO a => a -> BitSet a -> IO () insert v (BitSet j) = withForeignPtr j $ \j' -> do v' <- hashIO v judy1Set j' v' judyError if v' == jerr then putStrLn "HsJudy: Not enough memory." else return () -- | Delete a value in the set. delete :: HashIO a => a -> BitSet a -> IO () delete v (BitSet j) = withForeignPtr j $ \j' -> do v' <- hashIO v judy1Unset j' v' judyError if v' == jerr then error "HsJudy: Not enough memory." else return () -- | Set value in or out the set and return its old value. set :: HashIO a => BitSet a -> a -> Bool -> IO Bool set (BitSet k) v True = withForeignPtr k $ \j -> do vp <- hashIO v r <- judy1Set j vp judyError if vp == jerr then error "HsJudy: Not enough memory." else return $ r == 0 set (BitSet k) v False = withForeignPtr k $ \j -> do vp <- hashIO v r <- judy1Unset j vp judyError if vp == jerr then error "HsJudy: Not enough memory." else return $ r /= 0 -- this inline was in Meacham original BitSet -- {-# INLINE get #-} get :: HashIO a => BitSet a -> a -> IO Bool get (BitSet j) v = do jj <- withForeignPtr j peek vp <- hashIO v r <- judy1Test jj vp judyError return $ r /= 0 -- | Is the value a member of the set? member :: HashIO a => a -> BitSet a -> IO Bool member v (BitSet j) = do j' <- withForeignPtr j peek v' <- hashIO v r <- judy1Test j' v' judyError return $ r /= 0 -- | Is the set empty? null :: BitSet a -> IO Bool null (BitSet j) = do j' <- withForeignPtr j peek return $ j' == nullPtr -- | Cardinality of the set. size :: BitSet a -> IO Int size (BitSet j) = do j' <- withForeignPtr j peek r <- judy1Count j' 0 (-1) judyError return $ fromEnum r -- | Make the set empty. clear :: HashIO a => BitSet a -> IO () clear (BitSet j) = withForeignPtr j $ \j' -> judy1FreeArray j' judyError >> return () -- | Convert the set to a list of elements. toList :: (Enum a) => BitSet t -> IO [a] toList (BitSet j) = do j' <- withForeignPtr j peek alloca $ \vp -> do poke vp (-1) let f 0 xs = return xs f _ xs = do v <- peek vp v' <- unHashIO v r <- judy1Prev j' vp judyError f r (v':xs) r <- judy1Last j' vp judyError f r [] -- | Create a set from a list of elements. -- FIXME: should I create the list here maybe? fromList :: HashIO a => [a] -> BitSet a -> IO () fromList vs bs = mapM_ (\v -> insert v bs) vs -- FIXME: Is this other implementation faster than mapM_? {-setList :: [a] -> Bool -> BitSet a -> IO () setList vs True (BitSet bs) = withForeignPtr bs $ \j -> mapM_ (\v -> do vp <- newStablePtr v judy1Set j (ptrToWordPtr (castStablePtrToPtr vp)) judyError ) vs setList vs False (BitSet bs) = withForeignPtr bs $ \j -> mapM_ (\v -> do vp <- newStablePtr v judy1Unset j (ptrToWordPtr (castStablePtrToPtr vp)) judyError ) vs -} -- Pure access routines from original BitSet code instance HashIO a => Freezable (BitSet a) where freeze = freezeBitSet -- | Create a frozen, immutable version of a bitset, the original mutable version is cleared. freezeBitSet :: HashIO a => BitSet a -> IO (Frozen (BitSet a)) freezeBitSet bs = do nbs <- new swapBitSets bs nbs return (Frozen nbs) memberF :: HashIO a => a -> Frozen (BitSet a) -> Bool memberF v (Frozen bs) = unsafePerformIO $ get bs v fromListF :: HashIO a => [a] -> Frozen (BitSet a) fromListF vs = Frozen $ unsafePerformIO $ do bs <- new fromList vs bs return bs toListF :: (Enum a) => Frozen (BitSet t) -> [a] toListF (Frozen (BitSet j)) = unsafePerformIO $ do j' <- withForeignPtr j peek alloca $ \vp -> do poke vp (-1) let f 0 xs = return xs f _ xs = do v <- peek vp v' <- unHashIO v r <- judy1Prev j' vp judyError f r (v':xs) r <- judy1Last j' vp judyError f r [] -- TODO: See if ListFrom and RevList are needed -- compare my toListF with toListFrom (it have more unsafePerformIO's =P) {- toList :: Frozen (BitSet a) -> [Value] toList = toListFrom 0 toListFrom :: Value -> Frozen BitSet -> [Value] toListFrom iwp (Frozen (BitSet bs)) = unsafePerformIO $ do jj <- withForeignPtr bs peek (r,v) <- alloca $ \wp -> do poke wp iwp r <- judy1First jj wp judyError v <- peek wp return (r,v) let f 0 _ = [] f _ v = v:unsafePerformIO (g v) g v = do (r,v) <- alloca $ \wp -> do poke wp v r <- judy1Next jj wp judyError v <- peek wp touchForeignPtr bs return (r,v) return (f r v) return (f r v) toRevList :: Frozen BitSet -> [Value] toRevList = toRevListFrom (-1) toRevListFrom :: Value -> Frozen BitSet -> [Value] toRevListFrom iwp (Frozen (BitSet bs)) = unsafePerformIO $ do withForeignPtr bs $ \j -> do jj <- peek j (r,v) <- alloca $ \wp -> do poke wp iwp r <- judy1Last jj wp judyError v <- peek wp return (r,v) let f 0 _ = [] f _ v = v:unsafePerformIO (g v) g v = do (r,v) <- alloca $ \wp -> do poke wp v r <- judy1Prev jj wp judyError v <- peek wp touchForeignPtr bs return (r,v) return (f r v) return (f r v) -}