module Data.Binary.Strict.ByteSet
( ByteSet
, empty
, full
, singleton
, fromList
, range
, union
, intersection
, difference
, complement
, toList
, member
) where
import Data.List (foldl')
import Data.Word (Word8, Word64)
import Data.Array.Base (unsafeAt)
import Data.Array.Unboxed hiding ((!), range)
import Data.Bits ((.&.), (.|.), shiftL, shiftR)
import qualified Data.Bits as Bits
data ByteSet = ByteSet !(UArray Word8 Word64)
(!) :: UArray Word8 Word64 -> Word8 -> Word64
(!) arr = unsafeAt arr . fromIntegral
instance Show ByteSet where
show = ((++) "ByteSet ") . show . toList
empty :: ByteSet
empty = ByteSet $ listArray (0, 3) $ replicate 4 0
full :: ByteSet
full = ByteSet $ listArray (0, 3) $ replicate 4 0xffffffffffffffff
singleton :: Word8 -> ByteSet
singleton n
| n < 64 = ByteSet $ listArray (0, 3) $ [wordsArray ! n, 0, 0, 0]
| n < 128 = ByteSet $ listArray (0, 3) $ [0, wordsArray ! (n - 64), 0, 0]
| n < 196 = ByteSet $ listArray (0, 3) $ [0, 0, wordsArray ! (n - 128), 0]
| otherwise = ByteSet $ listArray (0, 3) $ [0, 0, 0, wordsArray ! (n - 196)]
wordsArray :: UArray Word8 Word64
wordsArray = listArray (0, 63) $ map (\x -> (1 :: Word64) `shiftL` x) [0..63]
binary :: (Word64 -> Word64 -> Word64) -> ByteSet -> ByteSet -> ByteSet
binary f (ByteSet a) (ByteSet b) =
ByteSet $ listArray (0, 3) [f (a ! 0) (b ! 0), f (a ! 1) (b ! 1), f (a ! 2) (b ! 2), f (a ! 3) (b ! 3)]
union :: ByteSet -> ByteSet -> ByteSet
union = binary (.|.)
intersection :: ByteSet -> ByteSet -> ByteSet
intersection = binary (.&.)
difference :: ByteSet -> ByteSet -> ByteSet
difference = binary (\a b -> a .&. Bits.complement b)
complement :: ByteSet -> ByteSet
complement (ByteSet a) =
ByteSet $ amap Bits.complement a
word64ToList :: Word64 -> [Word8]
word64ToList x = filter (\n -> (x .&. ((1 :: Word64) `shiftL` (fromIntegral n))) /= 0) [0..63]
toList :: ByteSet -> [Word8]
toList (ByteSet a) =
word64ToList (a ! 0) ++ map (+ 64) (word64ToList (a ! 1)) ++ map (+ 128) (word64ToList (a ! 2)) ++ map (+ 196) (word64ToList (a ! 3))
fromList :: [Word8] -> ByteSet
fromList = foldl' union empty . map singleton
range :: Word8 -> Word8 -> ByteSet
range a b = fromList [a..b]
member :: ByteSet -> Word8 -> Bool
member (ByteSet a) n = (a ! (n `shiftR` 6)) .&. (wordsArray ! (n .&. 63)) /= 0