defmodule BitFlagger do
@moduledoc """
A set of functions that manipulate bit flags.
"""
use Bitwise, only_operators: true
@type state :: non_neg_integer | binary
@type index :: non_neg_integer
@doc """
Converts state to a list of boolean values.
## Examples
iex> parse(0b1010, 4)
[false, true, false, true]
iex> parse(<<0b1010>>, 4)
[false, true, false, true]
"""
@spec parse(state, pos_integer) :: list(boolean)
def parse(state, size) when (is_integer(state) or is_binary(state)) and is_integer(size) do
for index <- 0..(size - 1), do: on?(state, index)
end
@doc """
Checks if the flag is turned on at a specified index.
## Examples
iex> on?(0b0010, 1)
true
iex> on?(0b0010, 3)
false
iex> on?(<<0b0010>>, 1)
true
iex> on?(<<0b0010>>, 3)
false
"""
@spec on?(state, index) :: boolean
def on?(state, index) when is_integer(state) and is_integer(index) do
(state >>> index &&& 0x01) == 1
end
def on?(state, index) when is_binary(state) do
state
|> :binary.decode_unsigned()
|> on?(index)
end
@doc """
Checks if the flag is turned off at a specified index.
## Examples
iex> off?(0b0001, 1)
true
iex> off?(0b0001, 0)
false
iex> off?(<<0b0001>>, 1)
true
iex> off?(<<0b0001>>, 0)
false
"""
@spec off?(state, index) :: boolean
def off?(state, index), do: !on?(state, index)
@doc """
Turns on the flag at a specified index.
## Examples
iex> on(0b0000, 2)
0b0100
iex> on(<<0b0000>>, 2)
<<0b0100>>
"""
@spec on(state, index) :: state
def on(state, index) when is_integer(state) and is_integer(index) do
state ||| 0x01 <<< index
end
def on(state, index) when is_binary(state) do
state
|> :binary.decode_unsigned()
|> on(index)
|> :binary.encode_unsigned()
end
@doc """
Turns off the flag at a specified index.
## Examples
iex> off(0b1111, 2)
0b1011
iex> off(<<0b1111>>, 2)
<<0b1011>>
"""
@spec off(state, index) :: state
def off(state, index) when is_integer(state) and is_integer(index) do
state &&& ~~~(0x01 <<< index)
end
def off(state, index) when is_binary(state) do
state
|> :binary.decode_unsigned()
|> off(index)
|> :binary.encode_unsigned()
end
end