Skip to content

Instantly share code, notes, and snippets.

@somoza
Created May 19, 2023 16:32
Show Gist options
  • Save somoza/17dbde7b09c95573a1350662fafb7e5d to your computer and use it in GitHub Desktop.
Save somoza/17dbde7b09c95573a1350662fafb7e5d to your computer and use it in GitHub Desktop.
Base number conversion using Elixir
defmodule BaseConverter do
def convert(_digits, _input_base, output_base) when output_base < 2,
do: {:error, "output base must be >= 2"}
def convert(_digits, input_base, _output_base) when input_base < 2,
do: {:error, "input base must be >= 2"}
def convert(digits, input_base, 10) do
maybe_convert_to_base_10(digits, input_base, is_valid_list_of_ints?(digits, input_base))
end
def convert(digits, 10, output_base) do
maybe_convert_to_base_x(digits, output_base, is_valid_list_of_ints?(digits, 10))
end
def convert(digits, input_base, output_base) do
with true <- is_valid_list_of_ints?(digits, input_base),
base_10_list <- to_base_10(digits, input_base),
base_10_string <- Enum.join(base_10_list),
base_10_int <- String.to_integer(base_10_string) do
{:ok, to_base_x(base_10_int, output_base)}
else
error -> error
end
end
defp to_base_10(digits, base) do
{_, decimal} =
Enum.reduce(digits, {length(digits) - 1, 0}, fn digit, {length_digits, result} ->
{length_digits - 1, result + digit * :math.pow(base, length_digits)}
end)
trunc(decimal) |> Integer.digits()
end
defp to_base_x(number, base, acc \\ []) do
division = div(number, base)
reminder = rem(number, base)
if number >= base do
to_base_x(division, base, acc ++ [Kernel.to_string(reminder)])
else
(acc ++ [Kernel.to_string(reminder)])
|> Enum.map(fn digit ->
{int, _} = Integer.parse(digit)
int
end)
|> Enum.reverse()
end
end
defp is_valid_list_of_ints?(digits, base),
do: digits == Enum.filter(digits, &(&1 >= 0 and &1 < base))
defp maybe_convert_to_base_10(digits, input_base, true),
do: {:ok, to_base_10(digits, input_base)}
defp maybe_convert_to_base_10(_, _, false),
do: {:error, "all digits must be >= 0 and < input base"}
defp maybe_convert_to_base_x(digits, output_base, true),
do: {:ok, to_base_x(digits |> Enum.join() |> String.to_integer(), output_base)}
defp maybe_convert_to_base_x(_, _, false),
do: {:error, "all digits must be >= 0 and < input base"}
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment