%%% @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).