packages feed

moonlight-core-0.1.0.0: src-solver/Moonlight/Core/Fixpoint/Internal/Solver/BitSet.hs

-- | A mutable @Word64@-packed bit set over a bounded non-negative key range.
module Moonlight.Core.Fixpoint.Internal.Solver.BitSet
  ( MutableBitSet (..),
    newBitSet,
    bitSetMember,
    bitSetInsert,
    bitSetDelete,
  )
where

import Control.Monad.ST (ST)
import Data.Bits (clearBit, setBit, testBit)
import Data.Vector.Unboxed.Mutable qualified as UMVector
import Data.Word (Word64)
import Prelude

data MutableBitSet state = MutableBitSet
  { size :: !Int,
    bitSetWords :: !(UMVector.MVector state Word64)
  }

newBitSet :: Int -> ST state (MutableBitSet state)
newBitSet size = do
  wordsVector <- UMVector.replicate wordCount 0
  pure
    MutableBitSet
      { size = max 0 size,
        bitSetWords = wordsVector
      }
  where
    wordCount =
      (max 0 size + bitSetWordBits - 1) `quot` bitSetWordBits

bitSetMember :: Int -> MutableBitSet state -> ST state Bool
bitSetMember key bitSet
  | not (bitSetInBounds key bitSet) = pure False
  | otherwise = do
      word <- UMVector.read (bitSetWords bitSet) (bitSetWordIndex key)
      pure (testBit word (bitSetBitIndex key))

bitSetInsert :: Int -> MutableBitSet state -> ST state ()
bitSetInsert key bitSet
  | not (bitSetInBounds key bitSet) = pure ()
  | otherwise =
      modifyBitSetWord key (`setBit` bitSetBitIndex key) bitSet

bitSetDelete :: Int -> MutableBitSet state -> ST state ()
bitSetDelete key bitSet
  | not (bitSetInBounds key bitSet) = pure ()
  | otherwise =
      modifyBitSetWord key (`clearBit` bitSetBitIndex key) bitSet

modifyBitSetWord :: Int -> (Word64 -> Word64) -> MutableBitSet state -> ST state ()
modifyBitSetWord key update bitSet = do
  word <- UMVector.read (bitSetWords bitSet) wordIndex
  UMVector.write (bitSetWords bitSet) wordIndex (update word)
  where
    wordIndex =
      bitSetWordIndex key

bitSetInBounds :: Int -> MutableBitSet state -> Bool
bitSetInBounds key bitSet =
  key >= 0 && key < size bitSet

bitSetWordIndex :: Int -> Int
bitSetWordIndex key =
  key `quot` bitSetWordBits

bitSetBitIndex :: Int -> Int
bitSetBitIndex key =
  key `rem` bitSetWordBits

bitSetWordBits :: Int
bitSetWordBits =
  64