Packages

Elixir-inspired standard library modules for Erlang

Current section

Files

Jump to
ex_stdlib src bitwise.erl
Raw

src/bitwise.erl

%%% @doc
%%% Bitwise operations module inspired by Elixir's Bitwise module.
%%%
%%% This module provides a set of functions for performing bitwise operations
%%% on integers. All functions work only on integers, otherwise a badarg
%%% error is raised.
%%%
%%% Examples:
%%% ```
%%% % Bitwise AND
%%% 1 = bitwise:bit_and(9, 3),
%%%
%%% % Bitwise OR
%%% 11 = bitwise:bit_or(9, 3),
%%%
%%% % Bitwise XOR
%%% 10 = bitwise:bit_xor(9, 3),
%%%
%%% % Bitwise NOT
%%% -3 = bitwise:bit_not(2),
%%%
%%% % Left shift
%%% 4 = bitwise:bit_shift_left(1, 2),
%%%
%%% % Right shift
%%% 0 = bitwise:bit_shift_right(1, 2).
%%% '''
%%% @end
-module(bitwise).
%% API
-export([bit_not/1, bit_and/2, bit_or/2, bit_xor/2, bit_shift_left/2, bit_shift_right/2]).
-export([count_ones/1, count_zeros/1, count_leading_zeros/1, count_trailing_zeros/1]).
-export([set_bit/2, clear_bit/2, toggle_bit/2, test_bit/2]).
-export([rotate_left/2, rotate_right/2]).
%% Type specifications
-type integer_type() :: integer().
-export_type([integer_type/0]).
%%% @doc
%%% Calculates the bitwise NOT of the argument.
%%%
%%% Equivalent to Erlang's bnot/1. The result is the bitwise complement
%%% of the input integer.
%%%
%%% Examples:
%%% ```
%%% -3 = bitwise:bit_not(2),
%%% 1 = bitwise:bit_and(bitwise:bit_not(2), 3).
%%% '''
%%% @end
-spec bit_not(integer()) -> integer().
bit_not(Int) when is_integer(Int) ->
bnot Int;
bit_not(_) ->
error(badarg).
%%% @doc
%%% Calculates the bitwise AND of its arguments.
%%%
%%% Equivalent to Erlang's band/2. Returns a new integer with bits set
%%% only where both arguments have the corresponding bit set.
%%%
%%% Examples:
%%% ```
%%% 1 = bitwise:bit_and(9, 3), % 1001 & 0011 = 0001
%%% 0 = bitwise:bit_and(5, 2). % 0101 & 0010 = 0000
%%% '''
%%% @end
-spec bit_and(integer(), integer()) -> integer().
bit_and(Left, Right) when is_integer(Left), is_integer(Right) ->
Left band Right;
bit_and(_, _) ->
error(badarg).
%%% @doc
%%% Calculates the bitwise OR of its arguments.
%%%
%%% Equivalent to Erlang's bor/2. Returns a new integer with bits set
%%% where either argument has the corresponding bit set.
%%%
%%% Examples:
%%% ```
%%% 11 = bitwise:bit_or(9, 3), % 1001 | 0011 = 1011
%%% 7 = bitwise:bit_or(5, 2). % 0101 | 0010 = 0111
%%% '''
%%% @end
-spec bit_or(integer(), integer()) -> integer().
bit_or(Left, Right) when is_integer(Left), is_integer(Right) ->
Left bor Right;
bit_or(_, _) ->
error(badarg).
%%% @doc
%%% Calculates the bitwise XOR of its arguments.
%%%
%%% Equivalent to Erlang's bxor/2. Returns a new integer with bits set
%%% where exactly one of the arguments has the corresponding bit set.
%%%
%%% Examples:
%%% ```
%%% 10 = bitwise:bit_xor(9, 3), % 1001 ^ 0011 = 1010
%%% 7 = bitwise:bit_xor(5, 2). % 0101 ^ 0010 = 0111
%%% '''
%%% @end
-spec bit_xor(integer(), integer()) -> integer().
bit_xor(Left, Right) when is_integer(Left), is_integer(Right) ->
Left bxor Right;
bit_xor(_, _) ->
error(badarg).
%%% @doc
%%% Calculates the result of an arithmetic left bitshift.
%%%
%%% Equivalent to Erlang's bsl/2. Shifts the bits of the first argument
%%% to the left by the number of positions specified by the second argument.
%%% A negative shift amount results in a right shift.
%%%
%%% Examples:
%%% ```
%%% 4 = bitwise:bit_shift_left(1, 2), % 0001 << 2 = 0100
%%% 0 = bitwise:bit_shift_left(1, -2), % 0001 >> 2 = 0000
%%% -4 = bitwise:bit_shift_left(-1, 2), % Arithmetic shift preserves sign
%%% -1 = bitwise:bit_shift_left(-1, -2).
%%% '''
%%% @end
-spec bit_shift_left(integer(), integer()) -> integer().
bit_shift_left(Left, Right) when is_integer(Left), is_integer(Right) ->
Left bsl Right;
bit_shift_left(_, _) ->
error(badarg).
%%% @doc
%%% Calculates the result of an arithmetic right bitshift.
%%%
%%% Equivalent to Erlang's bsr/2. Shifts the bits of the first argument
%%% to the right by the number of positions specified by the second argument.
%%% A negative shift amount results in a left shift.
%%%
%%% Examples:
%%% ```
%%% 0 = bitwise:bit_shift_right(1, 2), % 0001 >> 2 = 0000
%%% 4 = bitwise:bit_shift_right(1, -2), % 0001 << 2 = 0100
%%% -1 = bitwise:bit_shift_right(-1, 2), % Arithmetic shift preserves sign
%%% -4 = bitwise:bit_shift_right(-1, -2).
%%% '''
%%% @end
-spec bit_shift_right(integer(), integer()) -> integer().
bit_shift_right(Left, Right) when is_integer(Left), is_integer(Right) ->
Left bsr Right;
bit_shift_right(_, _) ->
error(badarg).
%%% @doc
%%% Counts the number of set bits (1s) in an integer.
%%%
%%% Uses a population count algorithm to efficiently count the number
%%% of 1 bits in the binary representation of the integer.
%%%
%%% Examples:
%%% ```
%%% 2 = bitwise:count_ones(3), % 0011 has 2 ones
%%% 4 = bitwise:count_ones(15), % 1111 has 4 ones
%%% 0 = bitwise:count_ones(0). % 0000 has 0 ones
%%% '''
%%% @end
-spec count_ones(integer()) -> non_neg_integer().
count_ones(Int) when is_integer(Int), Int >= 0 ->
count_ones_impl(Int);
count_ones(Int) when is_integer(Int), Int < 0 ->
% For negative numbers, count ones in two's complement representation
% This is a simplified approach - in practice you'd need to consider word size
count_ones_impl(bnot Int) + 1;
count_ones(_) ->
error(badarg).
%%% @doc
%%% Counts the number of zero bits in the lower bits of an integer.
%%%
%%% For positive integers, counts zeros in the binary representation.
%%% The exact behavior for negative numbers depends on the word size.
%%%
%%% Examples:
%%% ```
%%% 2 = bitwise:count_zeros(12), % 1100 has 2 trailing zeros
%%% 0 = bitwise:count_zeros(15), % 1111 has 0 zeros
%%% '''
%%% @end
-spec count_zeros(integer()) -> non_neg_integer().
count_zeros(Int) when is_integer(Int), Int >= 0 ->
% Count zeros in a reasonable bit width (32 bits for example)
BitWidth = 32,
Mask = (1 bsl BitWidth) - 1,
MaskedInt = Int band Mask,
BitWidth - count_ones_impl(MaskedInt);
count_zeros(Int) when is_integer(Int) ->
% For negative numbers, use complement
count_zeros(bnot Int);
count_zeros(_) ->
error(badarg).
%%% @doc
%%% Counts the number of leading zero bits in an integer.
%%%
%%% Counts the number of consecutive zero bits from the most significant bit.
%%% For zero, returns the word size (32 bits).
%%%
%%% Examples:
%%% ```
%%% 28 = bitwise:count_leading_zeros(15), % 0000...01111 has 28 leading zeros
%%% 31 = bitwise:count_leading_zeros(1), % 0000...00001 has 31 leading zeros
%%% 32 = bitwise:count_leading_zeros(0). % all zeros
%%% '''
%%% @end
-spec count_leading_zeros(integer()) -> non_neg_integer().
count_leading_zeros(0) -> 32;
count_leading_zeros(Int) when is_integer(Int), Int > 0 ->
count_leading_zeros_impl(Int, 0);
count_leading_zeros(Int) when is_integer(Int), Int < 0 ->
0; % Negative numbers have no leading zeros in two's complement
count_leading_zeros(_) ->
error(badarg).
%%% @doc
%%% Counts the number of trailing zero bits in an integer.
%%%
%%% Counts the number of consecutive zero bits from the least significant bit.
%%% For zero, returns the word size (32 bits).
%%%
%%% Examples:
%%% ```
%%% 2 = bitwise:count_trailing_zeros(12), % 1100 has 2 trailing zeros
%%% 0 = bitwise:count_trailing_zeros(15), % 1111 has 0 trailing zeros
%%% 32 = bitwise:count_trailing_zeros(0). % all zeros
%%% '''
%%% @end
-spec count_trailing_zeros(integer()) -> non_neg_integer().
count_trailing_zeros(0) -> 32;
count_trailing_zeros(Int) when is_integer(Int), Int /= 0 ->
count_trailing_zeros_impl(Int, 0);
count_trailing_zeros(_) ->
error(badarg).
%%% @doc
%%% Sets a specific bit to 1 in an integer.
%%%
%%% Sets the bit at the specified position (0-indexed from the right) to 1.
%%% Position 0 is the least significant bit.
%%%
%%% Examples:
%%% ```
%%% 9 = bitwise:set_bit(8, 0), % 1000 -> 1001
%%% 12 = bitwise:set_bit(8, 2), % 1000 -> 1100
%%% 8 = bitwise:set_bit(8, 3). % 1000 -> 1000 (already set)
%%% '''
%%% @end
-spec set_bit(integer(), non_neg_integer()) -> integer().
set_bit(Int, Pos) when is_integer(Int), is_integer(Pos), Pos >= 0 ->
Int bor (1 bsl Pos);
set_bit(_, _) ->
error(badarg).
%%% @doc
%%% Clears a specific bit to 0 in an integer.
%%%
%%% Sets the bit at the specified position (0-indexed from the right) to 0.
%%% Position 0 is the least significant bit.
%%%
%%% Examples:
%%% ```
%%% 8 = bitwise:clear_bit(9, 0), % 1001 -> 1000
%%% 8 = bitwise:clear_bit(12, 2), % 1100 -> 1000
%%% 8 = bitwise:clear_bit(8, 0). % 1000 -> 1000 (already clear)
%%% '''
%%% @end
-spec clear_bit(integer(), non_neg_integer()) -> integer().
clear_bit(Int, Pos) when is_integer(Int), is_integer(Pos), Pos >= 0 ->
Int band (bnot(1 bsl Pos));
clear_bit(_, _) ->
error(badarg).
%%% @doc
%%% Toggles a specific bit in an integer.
%%%
%%% Flips the bit at the specified position (0-indexed from the right).
%%% Position 0 is the least significant bit.
%%%
%%% Examples:
%%% ```
%%% 9 = bitwise:toggle_bit(8, 0), % 1000 -> 1001
%%% 8 = bitwise:toggle_bit(12, 2), % 1100 -> 1000
%%% 0 = bitwise:toggle_bit(8, 3). % 1000 -> 0000
%%% '''
%%% @end
-spec toggle_bit(integer(), non_neg_integer()) -> integer().
toggle_bit(Int, Pos) when is_integer(Int), is_integer(Pos), Pos >= 0 ->
Int bxor (1 bsl Pos);
toggle_bit(_, _) ->
error(badarg).
%%% @doc
%%% Tests if a specific bit is set in an integer.
%%%
%%% Returns true if the bit at the specified position (0-indexed from the right)
%%% is set to 1, false otherwise. Position 0 is the least significant bit.
%%%
%%% Examples:
%%% ```
%%% false = bitwise:test_bit(8, 0), % 1000 bit 0 is 0
%%% true = bitwise:test_bit(9, 0), % 1001 bit 0 is 1
%%% true = bitwise:test_bit(8, 3). % 1000 bit 3 is 1
%%% '''
%%% @end
-spec test_bit(integer(), non_neg_integer()) -> boolean().
test_bit(Int, Pos) when is_integer(Int), is_integer(Pos), Pos >= 0 ->
(Int band (1 bsl Pos)) =/= 0;
test_bit(_, _) ->
error(badarg).
%%% @doc
%%% Rotates bits to the left by a specified number of positions.
%%%
%%% Performs a circular left shift within a 32-bit word. Bits that are shifted
%%% out from the left are rotated back in from the right.
%%%
%%% Examples:
%%% ```
%%% 24 = bitwise:rotate_left(3, 3), % 0011 rotated left 3 -> 11000 (24)
%%% 6 = bitwise:rotate_left(3, 1). % 0011 rotated left 1 -> 0110 (6)
%%% '''
%%% @end
-spec rotate_left(integer(), non_neg_integer()) -> integer().
rotate_left(Int, Positions) when is_integer(Int), is_integer(Positions), Positions >= 0 ->
WordSize = 32,
Mask = (1 bsl WordSize) - 1,
MaskedInt = Int band Mask,
EffectivePos = Positions rem WordSize,
LeftPart = (MaskedInt bsl EffectivePos) band Mask,
RightPart = MaskedInt bsr (WordSize - EffectivePos),
LeftPart bor RightPart;
rotate_left(_, _) ->
error(badarg).
%%% @doc
%%% Rotates bits to the right by a specified number of positions.
%%%
%%% Performs a circular right shift within a 32-bit word. Bits that are shifted
%%% out from the right are rotated back in from the left.
%%%
%%% Examples:
%%% ```
%%% 6 = bitwise:rotate_right(12, 1), % 1100 rotated right 1 -> 0110 (6)
%%% 3 = bitwise:rotate_right(12, 2). % 1100 rotated right 2 -> 0011 (3)
%%% '''
%%% @end
-spec rotate_right(integer(), non_neg_integer()) -> integer().
rotate_right(Int, Positions) when is_integer(Int), is_integer(Positions), Positions >= 0 ->
WordSize = 32,
Mask = (1 bsl WordSize) - 1,
MaskedInt = Int band Mask,
EffectivePos = Positions rem WordSize,
RightPart = MaskedInt bsr EffectivePos,
LeftPart = (MaskedInt bsl (WordSize - EffectivePos)) band Mask,
LeftPart bor RightPart;
rotate_right(_, _) ->
error(badarg).
%%%=============================================================================
%%% Internal functions
%%%=============================================================================
%% @private
count_ones_impl(0) -> 0;
count_ones_impl(Int) ->
(Int band 1) + count_ones_impl(Int bsr 1).
%% @private
count_leading_zeros_impl(Int, Count) when Int >= (1 bsl 31) ->
Count;
count_leading_zeros_impl(Int, Count) ->
count_leading_zeros_impl(Int bsl 1, Count + 1).
%% @private
count_trailing_zeros_impl(Int, Count) when (Int band 1) =:= 1 ->
Count;
count_trailing_zeros_impl(Int, Count) ->
count_trailing_zeros_impl(Int bsr 1, Count + 1).