# PercyGrunwald's solution

## to All Your Base in the Elixir Track

Published at Jan 04 2019 · 0 comments
Instructions
Test suite
Solution

Convert a number, represented as a sequence of digits in one base, to any other base.

Implement general base conversion. Given a number in base a, represented as a sequence of digits, convert it to base b.

## Note

• Try to implement the conversion yourself. Do not use something else to perform the conversion for you.

In positional notation, a number in base b can be understood as a linear combination of powers of b.

The number 42, in base 10, means:

(4 * 10^1) + (2 * 10^0)

The number 101010, in base 2, means:

(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)

The number 1120, in base 3, means:

(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)

I think you got the idea!

Yes. Those three numbers above are exactly the same. Congratulations!

## Running tests

Execute the tests with:

``````\$ elixir all_your_base_test.exs
``````

### Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

``````# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
``````

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the test suite.

``````# ExUnit.configure exclude: :pending, trace: true
``````

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### all_your_base_test.exs

``````if !System.get_env("EXERCISM_TEST_EXAMPLES") do
end

ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)

defmodule AllYourBaseTest do
use ExUnit.Case

test "convert single bit one to decimal" do
assert AllYourBase.convert([1], 2, 10) == [1]
end

@tag :pending
test "convert binary to single decimal" do
assert AllYourBase.convert([1, 0, 1], 2, 10) == [5]
end

@tag :pending
test "convert single decimal to binary" do
assert AllYourBase.convert([5], 10, 2) == [1, 0, 1]
end

@tag :pending
test "convert binary to multiple decimal" do
assert AllYourBase.convert([1, 0, 1, 0, 1, 0], 2, 10) == [4, 2]
end

@tag :pending
test "convert decimal to binary" do
assert AllYourBase.convert([4, 2], 10, 2) == [1, 0, 1, 0, 1, 0]
end

@tag :pending
test "convert trinary to hexadecimal" do
assert AllYourBase.convert([1, 1, 2, 0], 3, 16) == [2, 10]
end

@tag :pending
test "convert hexadecimal to trinary" do
assert AllYourBase.convert([2, 10], 16, 3) == [1, 1, 2, 0]
end

@tag :pending
test "convert 15-bit integer" do
assert AllYourBase.convert([3, 46, 60], 97, 73) == [6, 10, 45]
end

@tag :pending
test "convert empty list" do
assert AllYourBase.convert([], 2, 10) == nil
end

@tag :pending
test "convert single zero" do
assert AllYourBase.convert([0], 10, 2) == [0]
end

@tag :pending
test "convert multiple zeros" do
assert AllYourBase.convert([0, 0, 0], 10, 2) == [0]
end

@tag :pending
assert AllYourBase.convert([0, 6, 0], 7, 10) == [4, 2]
end

@tag :pending
test "convert negative digit" do
assert AllYourBase.convert([1, -1, 1, 0, 1, 0], 2, 10) == nil
end

@tag :pending
test "convert invalid positive digit" do
assert AllYourBase.convert([1, 2, 1, 0, 1, 0], 2, 10) == nil
end

@tag :pending
test "convert first base is one" do
assert AllYourBase.convert([0], 1, 10) == nil
end

@tag :pending
test "convert second base is one" do
assert AllYourBase.convert([1, 0, 1, 0, 1, 0], 2, 1) == nil
end

@tag :pending
test "convert first base is zero" do
assert AllYourBase.convert([], 0, 10) == nil
end

@tag :pending
test "convert second base is zero" do
assert AllYourBase.convert([7], 10, 0) == nil
end

@tag :pending
test "convert first base is negative" do
assert AllYourBase.convert([1], -2, 10) == nil
end

@tag :pending
test "convert second base is negative" do
assert AllYourBase.convert([1], 2, -7) == nil
end

@tag :pending
test "convert both bases are negative" do
assert AllYourBase.convert([1], -2, -7) == nil
end
end``````
``````defmodule AllYourBase do
@doc """
Given a number in base a, represented as a sequence of digits, converts it to base b,
or returns nil if either of the bases are less than 2
"""
@spec convert(list, integer, integer) :: list
def convert(digits, base_a, base_b) do
if is_valid?(digits, base_a, base_b) do
digits
|> convert_to_decimal(base_a)
|> convert_to_base(base_b)
else
nil
end
end

defp is_valid?(digits, base_a, base_b) do
all_digits_between_zero_and_base_a? =
Enum.all?(digits, fn digit ->
0 <= digit and digit < base_a
end)

length(digits) > 0 and is_integer(base_a) and base_a > 1 and is_integer(base_b) and base_b > 1 and
all_digits_between_zero_and_base_a?
end

defp convert_to_decimal(digits, base) do
digits
|> Enum.with_index(1)
|> Enum.map(fn {digit, index} ->
factor = pow(base, length(digits) - index)

digit * factor
end)
|> Enum.sum()
end

defp convert_to_base(int, base), do: convert_to_base(int, base, {[], nil})

defp convert_to_base(0, _base, _acc), do: [0]

defp convert_to_base(int, base, acc) when is_integer(int) and int > 0 do
{acc_list, previous_power} = acc

power =
if is_nil(previous_power) do
get_highest_power(int, base)
else
previous_power - 1
end

highest_factor = pow(base, power)

int_quotient = div(int, highest_factor)
int_remainder = rem(int, highest_factor)

cond do
power == 0 ->
acc_list ++ [int_quotient]

power == 1 ->
acc_list ++ [int_quotient, int_remainder]

true ->
convert_to_base(int_remainder, base, {acc_list ++ [int_quotient], power})
end
end

defp convert_to_base(_int, _base, _acc), do: nil

defp get_highest_power(int, base, power \\ 0) do
factor = pow(base, power)

cond do
int == factor ->
power

int > factor ->
get_highest_power(int, base, power + 1)

true ->
power - 1
end
end

defp pow(number, power) do
:math.pow(number, power) |> :erlang.floor()
end
end``````