nri-prelude-0.1.0.0: src/Bitwise.hs
-- | Library for [bitwise operations](https://en.wikipedia.org/wiki/Bitwise_operation).
module Bitwise
( -- * Basic Operations
and,
or,
xor,
complement,
-- * Bit Shifts
shiftLeftBy,
shiftRightBy,
shiftRightZfBy,
)
where
import Basics (Int)
import Data.Bits ((.&.), (.|.))
import qualified Data.Bits
import qualified Prelude
-- | 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 == 9223372036854775792
--
-- 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