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