packages feed

bv-little-1.3.0: src/libs/Data/BitVector/LittleEndian/MonoTraversable.hs

{-|

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

Exposes the following instances for 'BitVector':

  * 'MonoFoldable'
  * 'MonoFunctor'
  * 'MonoTraversable'

-}

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

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

module Data.BitVector.LittleEndian.MonoTraversable
    (
    ) where

import Data.BitVector.LittleEndian (BitVector)
import Data.BitVector.LittleEndian.Internal hiding (BitVector)
import Data.Bits
import Data.Foldable
import Data.MonoTraversable
import Data.Monoid ()


{-| @since 0.1.0 -}
type instance Element BitVector
    = Bool


{-| @since 0.1.0 -}
instance MonoFoldable BitVector where

    {-# INLINE ofoldMap #-}
    ofoldMap f (BV w n) = go m
        where
            !m = fromEnum w
            go 0 = mempty
            go c =
                let !i = m - c
                    !j = c - 1
                    !b = n `testBit` i
                in  f b `mappend` go j

    {-# INLINE ofoldr #-}
    ofoldr f e (BV w n) =
        let !m = fromEnum w
            go 0 acc = acc
            go c acc =
                let !i = m - c
                    !j = c - 1
                    !b = n `testBit` i
                in  f b $ go j acc
        in  go m e

    {-# INLINE ofoldl' #-}
    ofoldl' f e (BV w n) = go m e
        where
            !m = fromEnum w
            go 0 acc = acc
            go c acc =
                let !i = m - c
                    !j = c - 1
                    !b = n `testBit` i
                    !a = f acc b
                in  go j a

    {-# INLINE otoList #-}
    otoList = toBits

    -- | /O(1)/
    {-# INLINE oall #-}
    oall _ (BV 0 _) = True
    oall f (BV w n) = case (f False, f True) of
        (False, False) -> False
        (True , True ) -> True
        (False, True ) -> n == bit (fromEnum w) - 1
        (True , False) -> n == 0

    -- | /O(1)/
    {-# INLINE oany #-}
    oany _ (BV 0 _) = False
    oany f (BV w n) = case (f False, f True) of
        (False, False) -> False
        (True , True ) -> True
        (False, True ) -> n > 0
        (True , False) -> n < bit (fromEnum w) - 1

    -- | /O(1)/
    {-# INLINE onull #-}
    onull = (== 0) . dim

    -- | /O(1)/
    {-# INLINE olength #-}
    olength = fromEnum . dim

    -- | /O(1)/
    {-# INLINE olength64 #-}
    olength64 = toEnum . olength

    {-# INLINE otraverse_ #-}
    otraverse_ f (BV w n) = go (fromEnum w)
        where
            go 0 = pure ()
            go c =
                let !j = c - 1
                    !a = f (n `testBit` j)
                in  a *> go j

    {-# INLINE ofoldlM #-}
    ofoldlM f e (BV w n) = go (fromEnum w) e
        where
            go 0 acc = pure acc
            go c acc =
                let !j = c - 1
                    !x = f acc (n `testBit` j)
                in  x >>= go j

    {-# INLINE ofoldMap1Ex #-}
    ofoldMap1Ex _ (BV 0 _) = Prelude.error "Data.MonoTraversable.ofoldMap1Ex on an empty BitVector!"
    ofoldMap1Ex f (BV w n) = go 0
        where
            !m = fromEnum w
            go !c
                | c >= m - 1
                = f $ n `testBit` c
                | otherwise
                = let
                      !j = c + 1
                      !b = n `testBit` c
                  in  f b <> go j

    -- | /O(1)/
    {-# INLINE ofoldr1Ex #-}
    ofoldr1Ex _ (   BV 0 _) = Prelude.error "Data.MonoTraversable.ofoldr1Ex on an empty BitVector!"
    ofoldr1Ex _ (   BV 1 n) = n > 0
    ofoldr1Ex f bv@(BV w n) =
        -- 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
                              case (f True True, f True False, f False True, f False False) of
          -- Contradiction (Const False)
        (False, False, False, False) -> False
        -- Logical NOR
        (False, False, False, True) ->
            let !zeros = toEnum $ countLeadingZeros bv
            in  if (w - zeros) == 1 || n == 0 then even zeros else odd zeros
        -- Converse non-implication
        --   Only True when of the form <0+1>
        (False, False, True , False) -> n == bit (fromEnum w - 1)
        -- NOT p
        (False, False, True , True ) -> not (n `testBit` 0)
        -- Logical non-implication
        --   Only True when the number of leading ones is even
        (False, True , False, False) -> let !los = countLeadingZeros $ complement bv in odd los
        -- NOT q
        (False, True , False, True ) -> let !v = n `testBit` (fromEnum w - 1) in if even w then not v else v
        -- Logical XOR
        (False, True , True , False) -> odd $ popCount n
        -- Logical NAND
        (False, True, True, True) ->
            let !los = countLeadingZeros $ complement bv
                !x   = bit (fromEnum w - 1) - 1
                !y   = bit (fromEnum w) - 1
            in  if n == x || n == y then odd los else even los
        -- Logical AND
        (True, False, False, False) -> n == bit (fromEnum w) - 1
        -- Logical XNOR
        (True, False, False, True ) -> let !pc = popCount n in if even w then even pc else odd pc
        -- Const q
        (True, False, True , False) -> n `testBit` (fromEnum w - 1)
        -- Logical implication
        --   only False when of the form <1+0>
        (True, False, True , True ) -> let !i = fromEnum w - 1 in n /= bit i - 1
        -- Const p
        (True, True , False, False) -> n `testBit` 0
        -- Converse implication
        (True, True , False, True ) -> even $ countLeadingZeros bv
        -- Logical OR
        (True, True , True , False) -> n > 0
        -- Constant True
        (True, True , True , True ) -> True

    -- | /O(n)/
    {-# INLINE ofoldl1Ex' #-}
    ofoldl1Ex' _ (   BV 0 _) = Prelude.error "Data.MonoTraversable.ofoldl1Ex' on an empty BitVector!"
    ofoldl1Ex' _ (   BV 1 n) = n > 0
    ofoldl1Ex' f bv@(BV w n) =
        -- 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
                               case (f True True, f True False, f False True, f False False) of
          -- Contradiction (Const False)
        (False, False, False, False) -> False
        -- Logical NOR
        (False, False, False, True) ->
            let !zeros = toEnum $ countTrailingZeros bv
            in  if (w - zeros) == 1 || n == 0 then even zeros else odd zeros
        -- Converse non-implication
        (False, False, True , False) -> let !zeros = countTrailingZeros $ complement bv in odd zeros
        -- NOT p
        (False, False, True , True ) -> even w == even n
        -- Logical non-implication
        (False, True , False, False) -> n == 1
        -- NOT q
        (False, True , False, True ) -> not $ n `testBit` (fromEnum w - 1)
        -- Logical XOR
        (False, True , True , False) -> odd $ popCount n
        -- Logical NAND
        (False, True, True, True) ->
            let !zeros = countTrailingZeros $ complement bv
                !x     = bit (fromEnum w) - 1
                !y     = bit (fromEnum w) - 2
            in  if n == x || n == y then odd zeros else even zeros
        -- Logical AND
        (True, False, False, False) -> n == bit (fromEnum w) - 1
        -- Logical XNOR
        (True, False, False, True ) -> let !count = popCount n in if even w then even count else odd count
        -- Const q
        (True, False, True , False) -> n `testBit` (fromEnum w - 1)
        -- Logical implication
        (True, False, True , True ) -> even $ countTrailingZeros bv
        -- Const p
        (True, True , False, False) -> n `testBit` 0
        -- Converse implication
        --    only False when of the form <01+>
        (True, True , False, True ) -> n /= bit (fromEnum w) - 2
        -- Logical OR
        (True, True , True , False) -> n > 0
        -- Constant True
        (True, True , True , True ) -> True

    -- | /O(1)/
    {-# INLINE headEx #-}
    headEx (BV 0 _) = error "Call to Data.MonoFoldable.headEx on an empty BitVector!"
    headEx (BV _ n) = n `testBit` 0

    -- | /O(1)/
    {-# INLINE lastEx #-}
    lastEx (BV 0 _) = error "Call to Data.MonoFoldable.lastEx on an empty BitVector!"
    lastEx (BV w n) = n `testBit` (fromEnum w - 1)

    -- | /O(n)/
    {-# INLINE maximumByEx #-}
    maximumByEx _ (BV 0 _) = error "Call to Data.MonoFoldable.maximumByEx on an empty BitVector!"
    maximumByEx _ (BV 1 n) = n /= 0
    maximumByEx f bv       = maximumBy f $ toBits bv

    -- | /O(n)/
    {-# INLINE minimumByEx #-}
    minimumByEx _ (BV 0 _) = error "Call to Data.MonoFoldable.minimumByEx on an empty BitVector!"
    minimumByEx _ (BV 1 n) = n /= 0
    minimumByEx f bv       = minimumBy f $ toBits bv

    -- | /O(1)/
    {-# INLINE oelem #-}
    oelem _     (BV 0 _) = False
    oelem True  (BV _ n) = n > 0
    oelem False (BV w n) = n < bit (fromEnum w) - 1

    -- | /O(1)/
    {-# INLINE onotElem #-}
    onotElem e = not . oelem e


{-| @since 0.1.0 -}
instance MonoFunctor BitVector where

    -- | /O(1)/
    {-# INLINE omap #-}
    omap f bv@(BV w n) = case (f False, f True) of
        (False, False) -> BV w 0
        (True , True ) -> BV w $ bit (fromEnum w) - 1
        (False, True ) -> bv
        (True , False) -> let !allOnes = bit (fromEnum w) - 1 in BV w $ n `xor` allOnes


{-| @since 0.1.0 -}
instance MonoTraversable BitVector where

    -- | /O(n)/
    {-# INLINE otraverse #-}
    otraverse f = fmap fromBits . traverse f . toBits

    -- | /O(n)/
    {-# INLINE omapM #-}
    omapM = otraverse