content: {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  -- | Like `Data.Bits` but with `Boolean` instead of `Bool`
  module Keelung.Data.Bits where
  
  import Data.Bits qualified as Bits
  import Data.Word (Word16, Word32, Word64, Word8)
  import GHC.TypeNats (KnownNat)
  import Keelung.Syntax
  
  -- | Bitwise operations on Keelung values
  class Bits a where
    -- | Bitwise \"and\"
    (.&.) :: a -> a -> a
  
    infixl 8 .&.
  
    -- | Bitwise \"or\"
    (.|.) :: a -> a -> a
  
    infixl 7 .|.
  
    -- | Bitwise \"xor\"
    (.^.) :: a -> a -> a
  
    infixl 6 .^.
  
    -- | Rotates left by @i@ bits if @i@ is positive, or right by @-i@ bits otherwise.
    rotate :: a -> Int -> a
  
    infixl 8 `rotate`
  
    -- | @'shift' x i@ shifts @x@ left by @i@ bits if @i@ is positive, or right by @-i@ bits otherwise. Vacated bits are filled with 0.
    shift :: a -> Int -> a
  
    infixl 8 `shift`
  
    -- | Retrieve the i-th bit and return it as Boolean
    --   The LSB is the 0-th bit and the MSB is the (n-1)-th bit
    --      where n is the bit width
    --   You can access the MSB with (-1) because the index is modulo n
    (!!!) :: a -> Int -> Boolean
  
    infixl 9 !!!
  
    -- | Reverse all the bits in the argument
    complement :: a -> a
  
  -- | Synonym for 'shift'
  shiftL :: Bits a => a -> Int -> a
  shiftL = shift
  
  -- | Opposite of 'shiftL'
  shiftR :: Bits a => a -> Int -> a
  shiftR x i = shiftL x (-i)
  
  -- | Infix version of 'shiftR'.
  (.>>.) :: (Bits a) => a -> Int -> a
  (.>>.) = shiftR
  
  infixl 8 .>>.
  
  -- | Infix version of 'shiftL'.
  (.<<.) :: (Bits a) => a -> Int -> a
  (.<<.) = shiftL
  
  infixl 8 .<<.
  
  -- | Synonym for 'rotate'
  rotateL :: Bits a => a -> Int -> a
  rotateL = rotate
  
  -- | Opposite of 'rotateL'
  rotateR :: Bits a => a -> Int -> a
  rotateR x i = rotateL x (-i)
  
  instance Bits Boolean where
    (.&.) = And
    (.|.) = Or
    (.^.) = Xor
    rotate x _ = x
    shift x 0 = x
    shift _ _ = false
    x !!! _ = x
    complement = Not
  
  instance KnownNat w => Bits (UInt w) where
    (.&.) = AndU
    (.|.) = OrU
    (.^.) = XorU
    rotate expr i = RoLU (widthOf expr) i expr
    shift expr i = ShLU (widthOf expr) i expr
    (!!!) = BitU
    complement = NotU
  
  -- | Make 'Word8' an instance of 'Bits'
  instance Bits Word8 where
    (.&.) = (Bits..&.)
    (.|.) = (Bits..|.)
    (.^.) = Bits.xor
    rotate = Bits.rotate
    shift = Bits.shift
    x !!! i = Boolean (Bits.testBit x i)
    complement = Bits.complement
  
  -- | Make 'Word16' an instance of 'Bits'
  instance Bits Word16 where
    (.&.) = (Bits..&.)
    (.|.) = (Bits..|.)
    (.^.) = Bits.xor
    rotate = Bits.rotate
    shift = Bits.shift
    x !!! i = Boolean (Bits.testBit x i)
    complement = Bits.complement
  
  -- | Make 'Word32' an instance of 'Bits'
  instance Bits Word32 where
    (.&.) = (Bits..&.)
    (.|.) = (Bits..|.)
    (.^.) = Bits.xor
    rotate = Bits.rotate
    shift = Bits.shift
    x !!! i = Boolean (Bits.testBit x i)
    complement = Bits.complement
  
  -- | Make 'Word64' an instance of 'Bits'
  instance Bits Word64 where
    (.&.) = (Bits..&.)
    (.|.) = (Bits..|.)
    (.^.) = Bits.xor
    rotate = Bits.rotate
    shift = Bits.shift
    x !!! i = Boolean (Bits.testBit x i)
    complement = Bits.complement
  