packages feed

symplectic-chp-0.1.0.1: src/SymplecticCHP/BitVec.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}

-- | Simple BitVec implementation for arbitrary-qubit support.
-- This is a pragmatic alternative to the full type-family approach.
module SymplecticCHP.BitVec
  ( BitVec
  , bvEmpty
  , bvFromWord64
  , bvToWord64
  , bvXor
  , bvAnd
  , bvTestBit
  , bvSetBit
  , bvClearBit
  , bvPopCount
  , bvShow
    -- * Re-export for compatibility
  , Word64
  ) where

import Data.Bits (Bits(..), popCount, xor)
import Data.Word (Word64)
import qualified Data.Vector.Unboxed as V

-- | Arbitrary-length bit vector
data BitVec = BitVec
  { bvBits :: !(V.Vector Word64)  -- ^ Chunked storage
  , bvSize :: !Int                -- ^ Number of bits
  }
  deriving (Eq)

instance Show BitVec where
  show = bvShow

-- | Create empty BitVec for n bits
bvEmpty :: Int -> BitVec
bvEmpty n
  | n <= 0 = error "bvEmpty: size must be positive"
  | otherwise = 
      let chunks = (n + 63) `div` 64
      in BitVec (V.replicate chunks 0) n

-- | Create from Word64 (for n <= 64)
bvFromWord64 :: Word64 -> BitVec
bvFromWord64 w = BitVec (V.singleton w) 64

-- | Convert to Word64 (only valid for n <= 64)
bvToWord64 :: BitVec -> Word64
bvToWord64 (BitVec v n)
  | n <= 64 && V.length v >= 1 = V.head v
  | otherwise = error "bvToWord64: BitVec too large"

-- | XOR operation
bvXor :: BitVec -> BitVec -> BitVec
bvXor (BitVec a sa) (BitVec b sb)
  | sa /= sb = error "bvXor: size mismatch"
  | otherwise = BitVec (V.zipWith xor a b) sa

-- | AND operation  
bvAnd :: BitVec -> BitVec -> BitVec
bvAnd (BitVec a sa) (BitVec b sb)
  | sa /= sb = error "bvAnd: size mismatch"
  | otherwise = BitVec (V.zipWith (.&.) a b) sa

-- | Test bit
bvTestBit :: BitVec -> Int -> Bool
bvTestBit (BitVec v n) i
  | i < 0 || i >= n = False  -- Out of bounds
  | otherwise = 
      let (word, bit) = i `divMod` 64
      in (V.unsafeIndex v word) `testBit` bit

-- | Set bit
bvSetBit :: BitVec -> Int -> BitVec
bvSetBit bv@(BitVec v n) i
  | i < 0 || i >= n = bv  -- Out of bounds: no change
  | otherwise =
      let (word, bit) = i `divMod` 64
          oldVal = V.unsafeIndex v word
          newVal = oldVal `setBit` bit
      in BitVec (V.unsafeUpd v [(word, newVal)]) n

-- | Clear bit
bvClearBit :: BitVec -> Int -> BitVec
bvClearBit bv@(BitVec v n) i
  | i < 0 || i >= n = bv
  | otherwise =
      let (word, bit) = i `divMod` 64
          oldVal = V.unsafeIndex v word
          newVal = oldVal `clearBit` bit
      in BitVec (V.unsafeUpd v [(word, newVal)]) n

-- | Population count
bvPopCount :: BitVec -> Int
bvPopCount (BitVec v _) = V.sum (V.map popCount v)

-- | Show as string of bits
bvShow :: BitVec -> String
bvShow (BitVec v n) = 
  "BitVec[" ++ show n ++ "] " ++ 
  concatMap showChunk (V.toList v)
  where
    showChunk w = [if testBit w i then '1' else '0' | i <- [0..63]]