content: {-# LANGUAGE Rank2Types #-}
  {-# LANGUAGE FlexibleContexts #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Data.Bits.Lens
  -- Copyright   :  (C) 2012-16 Edward Kmett
  -- License     :  BSD-style (see the file LICENSE)
  -- Maintainer  :  Edward Kmett <ekmett@gmail.com>
  -- Stability   :  experimental
  -- Portability :  LiberalTypeSynonyms
  --
  ----------------------------------------------------------------------------
  module Data.Bits.Lens
    ( (.|.~), (.&.~), (<.|.~), (<.&.~), (<<.|.~), (<<.&.~)
    , (.|.=), (.&.=), (<.|.=), (<.&.=), (<<.|.=), (<<.&.=)
    , bitAt
    , bits
    , byteAt
    , bytewise
    ) where
  
  import Prelude ()
  
  import Control.Lens
  import Control.Lens.Internal.Prelude
  import Control.Monad.State
  import Data.Bits
  import Data.Word
  
  -- $setup
  -- >>> :set -XNoOverloadedStrings
  -- >>> import Control.Lens
  -- >>> import Control.Monad.State
  -- >>> import Data.Word
  
  infixr 4 .|.~, .&.~, <.|.~, <.&.~, <<.|.~, <<.&.~
  infix 4 .|.=, .&.=, <.|.=, <.&.=, <<.|.=, <<.&.=
  
  -- | Bitwise '.|.' the target(s) of a t'Lens' or t'Setter'.
  --
  -- >>> _2 .|.~ 6 $ ("hello",3)
  -- ("hello",7)
  --
  -- @
  -- ('.|.~') :: 'Bits' a             => t'Setter' s t a a    -> a -> s -> t
  -- ('.|.~') :: 'Bits' a             => t'Iso' s t a a       -> a -> s -> t
  -- ('.|.~') :: 'Bits' a             => t'Lens' s t a a      -> a -> s -> t
  -- ('.|.~') :: ('Data.Monoid.Monoid' a, 'Bits' a) => t'Traversal' s t a a -> a -> s -> t
  -- @
  (.|.~):: Bits a => ASetter s t a a -> a -> s -> t
  l .|.~ n = over l (.|. n)
  {-# INLINE (.|.~) #-}
  
  -- | Bitwise '.&.' the target(s) of a t'Lens' or t'Setter'.
  --
  -- >>> _2 .&.~ 7 $ ("hello",254)
  -- ("hello",6)
  --
  -- @
  -- ('.&.~') :: 'Bits' a             => t'Setter' s t a a    -> a -> s -> t
  -- ('.&.~') :: 'Bits' a             => t'Iso' s t a a       -> a -> s -> t
  -- ('.&.~') :: 'Bits' a             => t'Lens' s t a a      -> a -> s -> t
  -- ('.&.~') :: ('Data.Monoid.Monoid' a, 'Bits' a) => t'Traversal' s t a a -> a -> s -> t
  -- @
  (.&.~) :: Bits a => ASetter s t a a -> a -> s -> t
  l .&.~ n = over l (.&. n)
  {-# INLINE (.&.~) #-}
  
  -- | Modify the target(s) of a 'Lens'', 'Setter'' or 'Traversal'' by computing its bitwise '.&.' with another value.
  --
  -- >>> execState (do _1 .&.= 15; _2 .&.= 3) (7,7)
  -- (7,3)
  --
  -- @
  -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Setter'' s a    -> a -> m ()
  -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Iso'' s a       -> a -> m ()
  -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a      -> a -> m ()
  -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Traversal'' s a -> a -> m ()
  -- @
  (.&.=):: (MonadState s m, Bits a) => ASetter' s a -> a -> m ()
  l .&.= a = modify (l .&.~ a)
  {-# INLINE (.&.=) #-}
  
  -- | Modify the target(s) of a 'Lens'', t'Setter' or t'Traversal' by computing its bitwise '.|.' with another value.
  --
  -- >>> execState (do _1 .|.= 15; _2 .|.= 3) (7,7)
  -- (15,7)
  --
  -- @
  -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Setter'' s a    -> a -> m ()
  -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Iso'' s a       -> a -> m ()
  -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a      -> a -> m ()
  -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Traversal'' s a -> a -> m ()
  -- @
  (.|.=) :: (MonadState s m, Bits a) => ASetter' s a -> a -> m ()
  l .|.= a = modify (l .|.~ a)
  {-# INLINE (.|.=) #-}
  
  -- | Bitwise '.|.' the target(s) of a t'Lens' (or t'Traversal'), returning the result
  -- (or a monoidal summary of all of the results).
  --
  -- >>> _2 <.|.~ 6 $ ("hello",3)
  -- (7,("hello",7))
  --
  -- @
  -- ('<.|.~') :: 'Bits' a             => t'Iso' s t a a       -> a -> s -> (a, t)
  -- ('<.|.~') :: 'Bits' a             => t'Lens' s t a a      -> a -> s -> (a, t)
  -- ('<.|.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => t'Traversal' s t a a -> a -> s -> (a, t)
  -- @
  (<.|.~):: Bits a => LensLike ((,) a) s t a a -> a -> s -> (a, t)
  l <.|.~ n = l <%~ (.|. n)
  {-# INLINE (<.|.~) #-}
  
  -- | Bitwise '.&.' the target(s) of a t'Lens' or t'Traversal', returning the result
  -- (or a monoidal summary of all of the results).
  --
  -- >>> _2 <.&.~ 7 $ ("hello",254)
  -- (6,("hello",6))
  --
  -- @
  -- ('<.&.~') :: 'Bits' a             => t'Iso'       s t a a -> a -> s -> (a, t)
  -- ('<.&.~') :: 'Bits' a             => t'Lens'      s t a a -> a -> s -> (a, t)
  -- ('<.&.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => t'Traversal' s t a a -> a -> s -> (a, t)
  -- @
  (<.&.~) :: Bits a => LensLike ((,) a) s t a a -> a -> s -> (a, t)
  l <.&.~ n = l <%~ (.&. n)
  {-# INLINE (<.&.~) #-}
  
  -- | Modify the target(s) of a 'Lens'' (or 'Traversal'') by computing its bitwise '.&.' with another value,
  -- returning the result (or a monoidal summary of all of the results traversed).
  --
  -- >>> runState (_1 <.&.= 15) (31,0)
  -- (15,(15,0))
  --
  -- @
  -- ('<.&.=') :: ('MonadState' s m, 'Bits' a)           => 'Lens'' s a      -> a -> m a
  -- ('<.&.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a
  -- @
  (<.&.=):: (MonadState s m, Bits a) => LensLike' ((,)a) s a -> a -> m a
  l <.&.= b = l <%= (.&. b)
  {-# INLINE (<.&.=) #-}
  
  -- | Modify the target(s) of a 'Lens'', (or t'Traversal') by computing its bitwise '.|.' with another value,
  -- returning the result (or a monoidal summary of all of the results traversed).
  --
  -- >>> runState (_1 <.|.= 7) (28,0)
  -- (31,(31,0))
  --
  -- @
  -- ('<.|.=') :: ('MonadState' s m, 'Bits' a)           => 'Lens'' s a      -> a -> m a
  -- ('<.|.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a
  -- @
  (<.|.=) :: (MonadState s m, Bits a) => LensLike' ((,)a) s a -> a -> m a
  l <.|.= b = l <%= (.|. b)
  {-# INLINE (<.|.=) #-}
  
  -- | Bitwise '.&.' the target(s) of a t'Lens' or t'Traversal', and return the
  -- original value, or a monoidal summary of the original values.
  --
  -- When you do not need the old value, ('.&.~') is more flexible.
  --
  -- >>> _2 <<.&.~ 7 $ ("hello", 254)
  -- (254,("hello",6))
  --
  -- @
  -- ('<<.&.~') ::  'Bits' a            => t'Iso' s t a a       -> a -> s -> (a, t)
  -- ('<<.&.~') ::  'Bits' a            => t'Lens' s t a a      -> a -> s -> (a, t)
  -- ('<<.&.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => t'Traversal' s t a a -> a -> s -> (a, t)
  -- @
  (<<.&.~) :: Bits a => Optical' (->) q ((,)a) s a -> a -> q s (a, s)
  l <<.&.~ b = l $ \a -> (a, a .&. b)
  {-# INLINE (<<.&.~) #-}
  
  -- | Bitwise '.|.' the target(s) of a t'Lens' or t'Traversal', and return the
  -- original value, or a monoidal summary of the original values.
  --
  -- When you do not need the old value, ('.|.~') is more flexible.
  --
  -- >>> _2 <<.|.~ 6 $ ("hello", 3)
  -- (3,("hello",7))
  --
  -- @
  -- ('<<.|.~') ::  'Bits' a            => t'Iso' s t a a       -> a -> s -> (a, t)
  -- ('<<.|.~') ::  'Bits' a            => t'Lens' s t a a      -> a -> s -> (a, t)
  -- ('<<.|.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => t'Traversal' s t a a -> a -> s -> (a, t)
  -- @
  (<<.|.~) :: Bits a => Optical' (->) q ((,)a) s a -> a -> q s (a, s)
  l <<.|.~ b = l $ \a -> (a, a .|. b)
  {-# INLINE (<<.|.~) #-}
  
  -- | Modify the target(s) of a 'Lens'', (or 'Traversal'') by computing its
  -- bitwise '.&.' with another value, returning the original value (or a
  -- monoidal summary of all the original values).
  --
  -- When you do not need the old value, ('.&.=') is more flexible.
  --
  -- >>> runState (_1 <<.&.= 15) (31,0)
  -- (31,(15,0))
  --
  -- @
  -- ('<<.&.=') :: ('MonadState' s m, 'Bits' a)           => 'Lens'' s a      -> a -> m a
  -- ('<<.&.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a
  -- @
  (<<.&.=) :: (MonadState s m, Bits a) => LensLike' ((,) a) s a -> a -> m a
  l <<.&.= b = l %%= \a -> (a, a .&. b)
  {-# INLINE (<<.&.=) #-}
  
  -- | Modify the target(s) of a 'Lens'', (or 'Traversal'') by computing its
  -- bitwise '.|.' with another value, returning the original value (or a
  -- monoidal summary of all the original values).
  --
  -- When you do not need the old value, ('.|.=') is more flexible.
  --
  -- >>> runState (_1 <<.|.= 7) (28,0)
  -- (28,(31,0))
  --
  -- @
  -- ('<<.|.=') :: ('MonadState' s m, 'Bits' a)           => 'Lens'' s a      -> a -> m a
  -- ('<<.|.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a
  -- @
  (<<.|.=) :: (MonadState s m, Bits a) => LensLike' ((,) a) s a -> a -> m a
  l <<.|.= b = l %%= \a -> (a, a .|. b)
  {-# INLINE (<<.|.=) #-}
  
  -- | This t'Lens' can be used to access the value of the nth bit in a number.
  --
  -- @'bitAt' n@ is only a legal t'Lens' into @b@ if @0 '<=' n '<' 'bitSize' ('undefined' :: b)@.
  --
  -- >>> 16^.bitAt 4
  -- True
  --
  -- >>> 15^.bitAt 4
  -- False
  --
  -- >>> 15 & bitAt 4 .~ True
  -- 31
  --
  -- >>> 16 & bitAt 4 .~ False
  -- 0
  bitAt :: Bits b => Int -> IndexedLens' Int b Bool
  bitAt n f b = indexed f n (testBit b n) <&> \x -> if x then setBit b n else clearBit b n
  {-# INLINE bitAt #-}
  
  -- | Get the nth byte, counting from the low end.
  --
  -- @'byteAt' n@ is a legal t'Lens' into @b@ iff @0 '<=' n '<' 'div' ('bitSize' ('undefined' :: b)) 8@
  --
  -- >>> (0xff00 :: Word16)^.byteAt 0
  -- 0
  --
  -- >>> (0xff00 :: Word16)^.byteAt 1
  -- 255
  --
  -- >>> byteAt 1 .~ 0 $ 0xff00 :: Word16
  -- 0
  --
  -- >>> byteAt 0 .~ 0xff $ 0 :: Word16
  -- 255
  byteAt :: (Integral b, Bits b) => Int -> IndexedLens' Int b Word8
  byteAt i f b = back <$> indexed f i (forward b) where
    back w8 = (fromIntegral w8 `shiftL` (i * 8))
      .|. (complement (255 `shiftL` (i * 8)) .&. b)
    forward = fromIntegral . (.&.) 0xff . flip shiftR (i * 8)
  
  
  -- | Traverse over all bits in a numeric type.
  --
  -- The bit position is available as the index.
  --
  -- >>> toListOf bits (5 :: Word8)
  -- [True,False,True,False,False,False,False,False]
  --
  -- If you supply this an 'Integer', the result will be an infinite t'Traversal', which
  -- can be productively consumed, but not reassembled.
  bits :: (Num b, Bits b) => IndexedTraversal' Int b Bool
  bits f b = foldr step 0 <$> traverse g bs where
    g n      = (,) n <$> indexed f n (testBit b n)
    bs       = takeWhile hasBit [0..]
    hasBit n = complementBit b n /= b -- test to make sure that complementing this bit actually changes the value
    step (n,True) r = setBit r n
    step _        r = r
  {-# INLINE bits #-}
  
  -- | Traverse over all the bytes in an integral type, from the low end.
  --
  -- The byte position is available as the index.
  --
  -- >>> toListOf bytewise (1312301580 :: Word32)
  -- [12,34,56,78]
  --
  -- If you supply this an 'Integer', the result will be an infinite t'Traversal',
  -- which can be productively consumed, but not reassembled.
  --
  -- Why isn't this function called @bytes@ to match 'bits'? Alas, there
  -- is already a function by that name in "Data.ByteString.Lens".
  bytewise :: (Integral b, Bits b) => IndexedTraversal' Int b Word8
  bytewise f b = foldr step 0 <$> traverse g bs where
    g n = (,) n <$> indexed f n (fromIntegral $ b `shiftR` (n*8))
    bs = takeWhile hasByte [0..]
    hasByte n = complementBit b (n*8) /= b
    step (n,x) r = r .|. (fromIntegral x `shiftL` (n*8))
  {-# INLINE bytewise #-}
  