Current section
Files
Jump to
Current section
Files
lib/custom_base.ex
defmodule CustomBase do
@moduledoc false
defmodule Pow do
@moduledoc false
# This is an integer-only implementation of pow
# It avoids issues where the erlang :math.pow overflows on a float
# but yet doesn't raise any errors
require Integer
def pow(_, 0), do: 1
def pow(x, n) when Integer.is_odd(n), do: x * pow(x, n - 1)
def pow(x, n) do
result = pow(x, div(n, 2))
result * result
end
end
defmacro __using__(alphabet) do
quote bind_quoted: [alphabet: alphabet] do
for { digit, idx } <- Enum.with_index(alphabet) do
def encode(unquote(idx)), do: unquote(<< digit >>)
end
@spec encode(integer) :: binary
def encode(number) do
encode(div(number, unquote(length(alphabet)))) <> encode(rem(number, unquote(length(alphabet))))
end
@spec decode(binary) :: {:ok, integer} | :error
def decode(binary) do
{ :ok, do_decode(binary) }
rescue
ArgumentError -> :error
end
@spec decode!(binary) :: integer
def decode!(binary), do: do_decode(binary)
defp do_decode(binary) do
binary
|> String.split("", trim: true)
|> Enum.reverse
|> decode(0)
end
for { digit, idx } <- Enum.with_index(alphabet) do
defp decode_char(unquote(<< digit >>)), do: unquote(idx)
end
defp decode_char(char) do
raise(ArgumentError, "non-alphabet digit found: #{char}")
end
defp decode([last], step) do
decode_char(last) * Pow.pow(unquote(length(alphabet)), step)
end
defp decode([head|tail], step) do
decode_char(head) * Pow.pow(unquote(length(alphabet)), step) + decode(tail, step + 1)
end
end
end
end