sasha-0: src/Sasha/Internal/Word8Set.hs
{-# LANGUAGE TemplateHaskellQuotes #-}
module Sasha.Internal.Word8Set (
-- * Set type
Word8Set,
Key,
-- * Construction
empty,
full,
singleton,
range,
fromList,
-- * Insertion
insert,
-- * Deletion
delete,
-- * Query
member,
memberCode,
isSubsetOf,
null,
isFull,
isSingleRange,
size,
-- * Combine
union,
intersection,
complement,
-- * Min\/Max
findMin,
findMax,
-- * Conversion to List
elems,
toList,
) where
import Prelude
(Bool (..), Eq ((==)), Int, Monoid (..), Ord, Semigroup (..),
Show (showsPrec), fromIntegral, negate, otherwise, showParen, showString,
($), (&&), (+), (-), (.), (<), (<=), (>), (||), return)
import Data.Bits ((.&.), (.|.))
import Data.Foldable (foldl')
import Data.WideWord.Word256 (Word256 (..))
import Data.Word (Word64, Word8)
import Test.QuickCheck (Arbitrary (..))
import Algebra.Lattice
(BoundedJoinSemiLattice (..), BoundedMeetSemiLattice (..), Lattice (..))
import Language.Haskell.TH.Syntax
import qualified Data.Bits as Bits
-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------
newtype Word8Set = W8S Word256
deriving (Eq, Ord)
type Key = Word8
-------------------------------------------------------------------------------
-- Instances
-------------------------------------------------------------------------------
instance Show Word8Set where
showsPrec d xs = showParen (d > 10) $ showString "fromList " . showsPrec 11 (toList xs)
instance Lift Word8Set where
liftTyped (W8S (Word256 a b c d)) =
[|| W8S (Word256 a b c d) ||]
instance Semigroup Word8Set where
(<>) = union
instance Monoid Word8Set where
mempty = empty
instance Arbitrary Word8Set where
arbitrary = do
a <- arbitrary
b <- arbitrary
c <- arbitrary
d <- arbitrary
return (W8S (Word256 a b c d))
instance Lattice Word8Set where
(\/) = union
(/\) = intersection
instance BoundedJoinSemiLattice Word8Set where
bottom = empty
instance BoundedMeetSemiLattice Word8Set where
top = full
-------------------------------------------------------------------------------
-- Construction
-------------------------------------------------------------------------------
empty :: Word8Set
empty = W8S Bits.zeroBits
full :: Word8Set
full = W8S ones
ones :: Word256
ones = Bits.complement Bits.zeroBits
singleton :: Word8 -> Word8Set
singleton x = W8S (Bits.bit (fromIntegral x))
range :: Word8 -> Word8 -> Word8Set
range mi ma
| mi <= ma = W8S $ Bits.shiftL (Bits.shiftR ones (fromIntegral (negate (1 + ma - mi)))) (fromIntegral mi)
| otherwise = empty
-------------------------------------------------------------------------------
-- Insertion
-------------------------------------------------------------------------------
insert :: Word8 -> Word8Set -> Word8Set
insert x (W8S xs) = W8S (Bits.setBit xs (fromIntegral x))
-------------------------------------------------------------------------------
-- Deletion
-------------------------------------------------------------------------------
delete :: Word8 -> Word8Set -> Word8Set
delete x (W8S xs) = W8S (Bits.clearBit xs (fromIntegral x))
-------------------------------------------------------------------------------
-- Query
-------------------------------------------------------------------------------
null :: Word8Set -> Bool
null (W8S xs) = xs == Bits.zeroBits
isFull :: Word8Set -> Bool
isFull (W8S xs) = xs == ones
size :: Word8Set -> Int
size (W8S xs) = Bits.popCount xs
member :: Word8 -> Word8Set -> Bool
member x (W8S xs) = Bits.testBit xs (fromIntegral x)
-- | Optimized routing to check membership when 'Word8Set' is statically known.
--
-- @
-- 'memberCode' c ws = [||'member' $$c $$(liftTyped ws) ||]
-- @
--
memberCode :: Code Q Word8 -> Word8Set -> Code Q Bool
memberCode c ws
-- simple cases
| null ws = [|| False ||]
| isFull ws = [|| True ||]
| size ws == 1 = [|| $$c == $$(liftTyped (findMin ws)) ||]
| size ws == 2 = [|| $$c == $$(liftTyped (findMin ws)) || $$c == $$(liftTyped (findMax ws)) ||]
-- continuos range
| isSingleRange ws = [|| $$(liftTyped (findMin ws)) <= $$c && $$c <= $$(liftTyped (findMax ws)) ||]
-- low chars
| W8S (Word256 0 0 0 x) <- ws = [|| $$c < 64 && Bits.testBit ($$(liftTyped x) :: Word64) (fromIntegral ($$c :: Word8)) ||]
-- fallback
| otherwise = [|| member $$c $$(liftTyped ws) ||]
isSubsetOf :: Word8Set -> Word8Set -> Bool
isSubsetOf a b = b == union a b
isSingleRange :: Word8Set -> Bool
isSingleRange (W8S 0) = True
isSingleRange (W8S ws) = Bits.popCount ws + Bits.countLeadingZeros ws + Bits.countTrailingZeros ws == 256
-------------------------------------------------------------------------------
-- Combine
-------------------------------------------------------------------------------
complement :: Word8Set -> Word8Set
complement (W8S xs) = W8S (Bits.complement xs)
union :: Word8Set -> Word8Set -> Word8Set
union (W8S xs) (W8S ys) = W8S (xs .|. ys)
intersection :: Word8Set -> Word8Set -> Word8Set
intersection (W8S xs) (W8S ys) = W8S (xs .&. ys)
-------------------------------------------------------------------------------
-- Min/Max
-------------------------------------------------------------------------------
findMin :: Word8Set -> Word8
findMin (W8S xs) = fromIntegral (Bits.countTrailingZeros xs)
findMax :: Word8Set -> Word8
findMax (W8S xs) = fromIntegral (255 - Bits.countLeadingZeros xs)
-------------------------------------------------------------------------------
-- List
-------------------------------------------------------------------------------
elems :: Word8Set -> [Word8]
elems = toList
toList :: Word8Set -> [Word8]
toList xs = [ w8 | w8 <- [0x00..0xff], member w8 xs]
fromList :: [Word8] -> Word8Set
fromList w8s = W8S $ foldl' (\acc w8 -> Bits.setBit acc (fromIntegral w8)) Bits.zeroBits w8s