packages feed

bv-little-1.3.0: src/keys/Data/BitVector/LittleEndian/MonoKeyed.hs

{-|

Copyright   : © 2020 Alex Washburn
License     : BSD-3-Clause
Maintainer  : github@recursion.ninja
Stability   : Stable

Exposes the following instances for 'BitVector':

  * 'MonoAdjustable'
  * 'MonoFoldableWithKey'
  * 'MonoIndexable'
  * 'MonoKeyed'
  * 'MonoLookup'
  * 'MonoTraversableWithKey'
  * 'MonoZip'
  * 'MonoZipWithKey'

-}

{-# Language BangPatterns #-}
{-# Language TypeFamilies #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}

module Data.BitVector.LittleEndian.MonoKeyed
    (
    ) where

import Data.BitVector.LittleEndian (BitVector)
import Data.BitVector.LittleEndian.Internal hiding (BitVector)
import Data.BitVector.LittleEndian.MonoTraversable ()
import Data.Bits
import Data.Foldable (fold)
import Data.Key
import Data.Maybe (fromMaybe)
import Data.MonoTraversable ()
import Data.MonoTraversable.Keys


{-| @since 1.0.0 -}
type instance MonoKey BitVector
    = Word


{-| @since 1.0.0 -}
instance MonoAdjustable BitVector where

    -- | /O(1)/
    {-# INLINE oadjust #-}
    oadjust f k bv@(BV w n)
        | k >= w    = bv
        | v == b    = bv
        | otherwise = bv `complementBit` i
        where
            !i = fromEnum k
            !v = n `testBit` i
            !b = f v

    -- | /O(1)/
    {-# INLINE oreplace #-}
    oreplace k v bv@(BV w _)
        | k >= w    = bv
        | v         = bv `setBit` i
        | otherwise = bv `clearBit` i
        where !i = fromEnum k


{-| @since 1.0.0 -}
instance MonoFoldableWithKey BitVector where

    -- | /O(n)/
    {-# INLINE otoKeyedList #-}
    otoKeyedList (BV w n) =
        let go 0 = []
            go c =
                let !k = w - c
                    !v = n `testBit` fromEnum k
                    !i = c - 1
                in  (k, v) : go i
        in  go w

    -- | /O(n)/
    {-# INLINE ofoldMapWithKey #-}
    ofoldMapWithKey f (BV w n) =
        let go 0 = mempty
            go c =
                let !k = w - c
                    !v = n `testBit` fromEnum k
                    !i = c - 1
                    !m = f k v
                in  m `mappend` go i
        in  go w

    -- | /O(n)/
    {-# INLINE ofoldrWithKey #-}
    ofoldrWithKey f e (BV w n) =
        let go 0 acc = acc
            go c acc =
                let !k = w - c
                    !i = c - 1
                    !b = n `testBit` fromEnum k
                in  f k b $ go i acc
        in  go w e

    -- | /O(n)/
    {-# INLINE ofoldlWithKey #-}
    ofoldlWithKey f e (BV w n) = go w e
        where
            go 0 acc = acc
            go c acc =
                let !k = w - c
                    !i = c - 1
                    !b = n `testBit` fromEnum k
                    !a = f acc k b
                in  go i a


{-| @since 1.0.0 -}
instance MonoIndexable BitVector where

    -- | /O(1)/
    {-# INLINE oindex #-}
    oindex bv@(BV w _) i = fromMaybe errorMessage $ i `olookup` bv
        where
            errorMessage = error $ fold
                [ "Data.BitVector.LittleEndian.oindex: "
                , "The index "
                , show i
                , " was greater than or equal to the length of the bit vector "
                , show w
                ]


{-| @since 1.0.0 -}
instance MonoKeyed BitVector where

    -- | /O(n)/
    {-# INLINE omapWithKey #-}
    omapWithKey f (BV w n) =
        let go 0 acc = acc
            go c acc =
                let !k = w - c
                    !i = fromEnum k
                    !j = c - 1
                    !b = n `testBit` i
                    !a
                        | f k b     = acc `setBit` i
                        | otherwise = acc
                in  go j a
        in  go w $ BV w 0


{-| @since 1.0.0 -}
instance MonoLookup BitVector where

    -- | /O(1)/
    {-# INLINE olookup #-}
    olookup k (BV w n)
        | k <= w    = Nothing
        | otherwise = Just $ n `testBit` fromEnum k


{-| @since 1.0.0 -}
instance MonoTraversableWithKey BitVector where

    -- | /O(n)/
    {-# INLINE otraverseWithKey #-}
    otraverseWithKey f = fmap fromBits . traverseWithKey (f . toEnum) . toBits


{-| @since 1.0.0 -}
instance MonoZip BitVector where

    -- | /O(1)/
    {-# INLINE ozipWith #-}
    ozipWith f lhs@(BV w1 p) rhs@(BV w2 q) =
        let !w0   = min w1 w2
            !mask = bit (fromEnum w0) - 1
            bv    = BV w0 . (mask .&.)
            not'  = nat . complement
        in  case (f True True, f True False, f False True, f False False) of
              -- Contradiction (Const False)
            (False, False, False, False) -> bv 0
            -- Logical NOR
            (False, False, False, True ) -> bv $ not' lhs .&. not' rhs
            -- Converse non-implication
            (False, False, True , False) -> bv $ not' lhs .&. q
            -- NOT p
            (False, False, True , True ) -> bv $ not' lhs
            -- Logical non-implication
            (False, True , False, False) -> bv $ p .&. not' rhs
            -- NOT q
            (False, True , False, True ) -> bv $ not' rhs
            -- Logical XOR
            (False, True , True , False) -> bv $ p `xor` q
            -- Logical NAND
            (False, True , True , True ) -> bv $ not' lhs .|. not' rhs
            -- Logical AND
            (True , False, False, False) -> bv $ p .&. q
            -- Logical XNOR
            (True , False, False, True ) -> bv $ (p .&. q) .|. (not' lhs .&. not' rhs)
            -- Const q
            (True , False, True , False) -> bv q
            -- Logical implication
            (True , False, True , True ) -> bv $ not' lhs .|. q
            -- Const p
            (True , True , False, False) -> bv p
            -- Converse implication
            (True , True , False, True ) -> bv $ p .|. not' rhs
            -- Logical OR
            (True , True , True , False) -> bv $ p .|. q
            -- Constant True
            (True , True , True , True ) -> bv $ bit (fromEnum w0) - 1
    -- See the following entry for explanation:
    -- https://en.wikipedia.org/wiki/Truth_table#Truth_table_for_all_binary_logical_operators
    --
    -- cases of f p q


{-| @since 1.0.0 -}
instance MonoZipWithKey BitVector where

    {-# INLINE ozipWithKey #-}
    ozipWithKey f (BV w1 n) (BV w2 m) =
        let w0 = min w1 w2
            go 0 _ = 0
            go c e =
                let !k = w0 - c
                    !i = fromEnum k
                    !j = c - 1
                    !b = f k (n `testBit` i) (m `testBit` i)
                    !a = e `shiftL` 1
                    !v = if b then e else 0
                in  v + go j a
        in  BV w0 $ go w0 1