packages feed

cherry-core-alpha-0.1.0.0: src/Bitwise.hs

{-|

Module      : Bitwise
Description : Work with bits.
License     : BSD 3
Maintainer  : terezasokol@gmail.com
Stability   : experimental
Portability : POSIX

-}

module Bitwise
  ( and,
    or,
    xor,
    complement,
    shiftLeftBy,
    shiftRightBy,
    shiftRightZfBy,
  )
where

import Data.Bits ((.&.), (.|.))
import Basics (Int)
import qualified Prelude
import qualified Data.Bits


{-| Bitwise AND
-}
and :: Int -> Int -> Int
and =
  (.&.)


{-| Bitwise OR
-}
or :: Int -> Int -> Int
or =
  (.|.)


{-| Bitwise XOR
-}
xor :: Int -> Int -> Int
xor = Data.Bits.xor


{-| Flip each bit individually, often called bitwise NOT
-}
complement :: Int -> Int
complement = Data.Bits.complement


{-| Shift bits to the left by a given offset, filling new bits with zeros.
This can be used to multiply numbers by powers of two.

  >  shiftLeftBy 1 5 == 10
  >  shiftLeftBy 5 1 == 32
-}
shiftLeftBy :: Int -> Int -> Int
shiftLeftBy offset value =
  Data.Bits.shift value (Prelude.fromIntegral offset)


{-| Shift bits to the right by a given offset, filling new bits with
whatever is the topmost bit. This can be used to divide numbers by powers of two.

  >  shiftRightBy 1  32 == 16
  >  shiftRightBy 2  32 == 8
  >  shiftRightBy 1 -32 == -16

This is called an [arithmetic right shift](https://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift), often written `>>`, and
sometimes called a sign-propagating right shift because it fills empty spots
with copies of the highest bit.

-}
shiftRightBy :: Int -> Int -> Int
shiftRightBy offset value =
  Data.Bits.shiftR value (Prelude.fromIntegral offset)


{-| Shift bits to the right by a given offset, filling new bits with zeros.

  >  shiftRightZfBy 1  32 == 16
  >  shiftRightZfBy 2  32 == 8
  >  shiftRightZfBy 1 -32 == 2147483632

This is called an [logical right shift](https://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift), often written `>>>`, and
sometimes called a zero-fill right shift because it fills empty spots with
zeros.

-}
shiftRightZfBy :: Int -> Int -> Int
shiftRightZfBy offset value =
  -- For some reason Data.Bits does not implement this function. The general idea is:
  -- 1. Generate a mask that will clear the leftmost n bits when ANDed with the result.
  -- 2. Shift right by n bits.
  -- 3. Bitwise AND the mask.
  let n = Prelude.fromIntegral offset
      oneBits = Data.Bits.complement Data.Bits.zeroBits :: Int
      shiftedValue = Data.Bits.shiftR value n
      shiftedMask = Data.Bits.rotateR (Data.Bits.shiftL oneBits n) n
   in and shiftedValue shiftedMask