{-|
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