defmodule Integer do @moduledoc """ Functions for working with integers. """ import Bitwise @doc """ Determines if an integer is odd. Returns `true` if `n` is an odd number, otherwise `false`. Allowed in guard clauses. """ defmacro is_odd(n) do quote do: (unquote(n) &&& 1) == 1 end @doc """ Determines if an integer is even. Returns `true` if `n` is an even number, otherwise `false`. Allowed in guard clauses. """ defmacro is_even(n) do quote do: (unquote(n) &&& 1) == 0 end @doc """ Returns the ordered digits for the given non-negative integer. An optional base value may be provided representing the radix for the returned digits. ## Examples iex> Integer.digits(101) [1, 0, 1] iex> Integer.digits(58127, 2) [1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1] """ @spec digits(non_neg_integer, pos_integer) :: [non_neg_integer] def digits(n, base \\ 10) when is_integer(n) and n >= 0 and is_integer(base) and base >= 2 do do_digits(n, base, []) end defp do_digits(0, _base, []), do: [0] defp do_digits(0, _base, acc), do: acc defp do_digits(n, base, acc) do do_digits div(n, base), base, [rem(n, base) | acc] end @doc """ Returns the integer represented by the ordered digits. An optional base value may be provided representing the radix for the digits. ## Examples iex> Integer.undigits([1, 0, 1]) 101 iex> Integer.undigits([1, 4], 16) 20 """ @spec undigits([integer], integer) :: integer def undigits(digits, base \\ 10) when is_integer(base) do do_undigits(digits, base, 0) end defp do_undigits([], _base, acc), do: acc defp do_undigits([digit | tail], base, acc) do do_undigits(tail, base, acc * base + digit) end @doc """ Converts a binary from a text representation of an integer in an optional base `base` to the corresponding integer. If the base `base` is not given, base 10 will be used. If successful, returns a tuple of the form `{integer, remainder_of_binary}`. Otherwise `:error`. Raises an error if `base` is less than 2 or more than 36. ## Examples iex> Integer.parse("34") {34, ""} iex> Integer.parse("34.5") {34, ".5"} iex> Integer.parse("three") :error iex> Integer.parse("34", 10) {34, ""} iex> Integer.parse("f4", 16) {244, ""} iex> Integer.parse("Awww++", 36) {509216, "++"} iex> Integer.parse("fab", 10) :error iex> Integer.parse("a2", 38) ** (ArgumentError) invalid base 38 """ @spec parse(binary, 2..36) :: {integer, binary} | :error | no_return def parse(binary, base \\ 10) def parse(binary, base) when is_integer(base) and base in 2..36 do parse_in_base(binary, base) end def parse(_, base) do raise ArgumentError, "invalid base #{base}" end defp parse_in_base(<< ?-, bin :: binary >>, base) do case do_parse(bin, base) do :error -> :error {number, remainder} -> {-number, remainder} end end defp parse_in_base(<< ?+, bin :: binary >>, base) do do_parse(bin, base) end defp parse_in_base(bin, base) when is_binary(bin) do do_parse(bin, base) end defp do_parse(<< char, bin :: binary >>, base) do if valid_digit_in_base?(char, base) do do_parse(bin, base, parse_digit(char, base)) else :error end end defp do_parse(_, _), do: :error defp do_parse(<< char, rest :: binary >>, base, acc) do if valid_digit_in_base?(char, base) do do_parse(rest, base, base * acc + parse_digit(char, base)) else {acc, << char, rest :: binary >>} end end defp do_parse(bitstring, _, acc) do {acc, bitstring} end defp parse_digit(char, _) do cond do char in ?0..?9 -> char - ?0 char in ?A..?Z -> char - ?A + 10 true -> char - ?a + 10 end end defp valid_digit_in_base?(char, base) do if base <= 10 do char in ?0..(?0 + base - 1) else char in ?0..?9 or char in ?A..(?A + base - 11) or char in ?a..(?a + base - 11) end end @doc """ Returns a binary which corresponds to the text representation of `some_integer`. Inlined by the compiler. ## Examples iex> Integer.to_string(123) "123" """ @spec to_string(integer) :: String.t def to_string(some_integer) do :erlang.integer_to_binary(some_integer) end @doc """ Returns a binary which corresponds to the text representation of `some_integer` in base `base`. Inlined by the compiler. ## Examples iex> Integer.to_string(100, 16) "64" """ @spec to_string(integer, 2..36) :: String.t def to_string(some_integer, base) do :erlang.integer_to_binary(some_integer, base) end @doc """ Returns a char list which corresponds to the text representation of the given integer. Inlined by the compiler. ## Examples iex> Integer.to_char_list(7) '7' """ @spec to_char_list(integer) :: char_list def to_char_list(number) do :erlang.integer_to_list(number) end @doc """ Returns a char list which corresponds to the text representation of the given integer in the given base. Inlined by the compiler. ## Examples iex> Integer.to_char_list(1023, 16) '3FF' """ @spec to_char_list(integer, 2..36) :: char_list def to_char_list(number, base) do :erlang.integer_to_list(number, base) end end