{-# LANGUAGE DataKinds        #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MagicHash        #-}
{-# LANGUAGE TypeOperators    #-}
{-# LANGUAGE TypeFamilies     #-}

{-# OPTIONS_HADDOCK show-extensions #-}

{-|
Copyright  :  (C) 2013-2015, University of Twente
License    :  BSD2 (see the file LICENSE)
Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>
-}
module CLaSH.Prelude.BitIndex where

import GHC.TypeLits                   (KnownNat, type (+), type (-))

import CLaSH.Class.BitPack            (BitPack (..))
import CLaSH.Promoted.Nat             (SNat)
import CLaSH.Sized.Internal.BitVector (BitVector, Bit, index#, lsb#, msb#,
                                       replaceBit#, setSlice#, slice#, split#)

-- $setup
-- >>> :set -XDataKinds
-- >>> import CLaSH.Prelude

{-# INLINE (!) #-}
-- | Get the bit at the specified bit index.
--
-- __NB:__ Bit indices are __DESCENDING__.
--
-- >>> pack (7 :: Unsigned 6)
-- 000111
-- >>> (7 :: Unsigned 6) ! 1
-- 1
-- >>> (7 :: Unsigned 6) ! 5
-- 0
-- >>> (7 :: Unsigned 6) ! 6
-- *** Exception: (!): 6 is out of range [5..0]
(!) :: (BitPack a, KnownNat (BitSize a), Integral i) => a -> i -> Bit
(!) v i = index# (pack v) (fromIntegral i)

{-# INLINE slice #-}
-- | Get a slice between bit index @m@ and and bit index @n@.
--
-- __NB:__ Bit indices are __DESCENDING__.
--
-- >>> pack (7 :: Unsigned 6)
-- 000111
-- >>> slice (7 :: Unsigned 6) d4 d2
-- 001
-- >>> slice (7 :: Unsigned 6) d6 d4
-- <BLANKLINE>
-- <interactive>:...
--     Couldn't match type ‘7 + i0’ with ‘6’
--     The type variable ‘i0’ is ambiguous
--     Expected type: (6 + 1) + i0
--       Actual type: BitSize (Unsigned 6)
--     In the expression: slice (7 :: Unsigned 6) d6 d4
--     In an equation for ‘it’: it = slice (7 :: Unsigned 6) d6 d4
slice :: (BitPack a, BitSize a ~ ((m + 1) + i)) => a -> SNat m -> SNat n
      -> BitVector (m + 1 - n)
slice v m n = slice# (pack v) m n

{-# INLINE split #-}
-- | Split a value of a bit size @m + n@ into a tuple of values with size @m@
-- and size @n@.
--
-- >>> pack (7 :: Unsigned 6)
-- 000111
-- >>> split (7 :: Unsigned 6) :: (BitVector 2, BitVector 4)
-- (00,0111)
split :: (BitPack a, BitSize a ~ (m + n), KnownNat n) => a
      -> (BitVector m, BitVector n)
split v = split# (pack v)

{-# INLINE replaceBit #-}
-- | Set the bit at the specified index
--
-- __NB:__ Bit indices are __DESCENDING__.
--
-- >>> pack (-5 :: Signed 6)
-- 111011
-- >>> replaceBit (-5 :: Signed 6) 4 0
-- -21
-- >>> pack (-21 :: Signed 6)
-- 101011
-- >>> replaceBit (-5 :: Signed 6) 5 0
-- 27
-- >>> pack (27 :: Signed 6)
-- 011011
-- >>> replaceBit (-5 :: Signed 6) 6 0
-- *** Exception: replaceBit: 6 is out of range [5..0]
replaceBit :: (BitPack a, KnownNat (BitSize a), Integral i) => a -> i -> Bit
           -> a
replaceBit v i b = unpack (replaceBit# (pack v) (fromIntegral i) b)

{-# INLINE setSlice #-}
-- | Set the bits between bit index @m@ and bit index @n@.
--
-- __NB:__ Bit indices are __DESCENDING__.
--
-- >>> pack (-5 :: Signed 6)
-- 111011
-- >>> setSlice (-5 :: Signed 6) d4 d3 0
-- -29
-- >>> pack (-29 :: Signed 6)
-- 100011
-- >>> setSlice (-5 :: Signed 6) d6 d5 0
-- <BLANKLINE>
-- <interactive>:...
--     Couldn't match type ‘7 + i0’ with ‘6’
--     The type variable ‘i0’ is ambiguous
--     Expected type: (6 + 1) + i0
--       Actual type: BitSize (Signed 6)
--     In the expression: setSlice (- 5 :: Signed 6) d6 d5 0
--     In an equation for ‘it’: it = setSlice (- 5 :: Signed 6) d6 d5 0
setSlice :: (BitPack a, BitSize a ~ ((m + 1) + i)) => a -> SNat m -> SNat n
         -> BitVector (m + 1 - n) -> a
setSlice v m n w = unpack (setSlice# (pack v) m n w)

{-# INLINE msb #-}
-- | Get the most significant bit.
--
-- >>> pack (-4 :: Signed 6)
-- 111100
-- >>> msb (-4 :: Signed 6)
-- 1
-- >>> pack (4 :: Signed 6)
-- 000100
-- >>> msb (4 :: Signed 6)
-- 0
msb :: (BitPack a, KnownNat (BitSize a)) => a -> Bit
msb v = msb# (pack v)

{-# INLINE lsb #-}
-- | Get the least significant bit.
--
-- >>> pack (-9 :: Signed 6)
-- 110111
-- >>> lsb (-9 :: Signed 6)
-- 1
-- >>> pack (-8 :: Signed 6)
-- 111000
-- >>> lsb (-8 :: Signed 6)
-- 0
lsb :: BitPack a => a -> Bit
lsb v = lsb# (pack v)