Exercism Elixir Easy Part 1
Mix.install([
{:benchee_dsl, "~> 0.5"},
{:benchee_markdown, "~> 0.3"}
])
require Integer
import ExUnit.Assertions
Resistor Color
https://exercism.org/tracks/elixir/exercises/resistor-color
defmodule ResistorColor do
@type color ::
:black | :brown | :red | :orange | :yellow | :green | :blue | :violet | :grey | :white
@doc """
Return the value of a color band.
## Examples
iex> ResistorColor.code(:blue)
6
"""
@spec code(color()) :: integer()
def code(:black), do: 0
def code(:brown), do: 1
def code(:red), do: 2
def code(:orange), do: 3
def code(:yellow), do: 4
def code(:green), do: 5
def code(:blue), do: 6
def code(:violet), do: 7
def code(:grey), do: 8
def code(:white), do: 9
end
Resistor Color: Tests
assert ResistorColor.code(:black) == 0
assert ResistorColor.code(:brown) == 1
assert ResistorColor.code(:red) == 2
assert ResistorColor.code(:orange) == 3
assert ResistorColor.code(:yellow) == 4
assert ResistorColor.code(:green) == 5
assert ResistorColor.code(:blue) == 6
assert ResistorColor.code(:violet) == 7
assert ResistorColor.code(:grey) == 8
assert ResistorColor.code(:white) == 9
:passed
Two Fer
https://exercism.org/tracks/elixir/exercises/two-fer
defmodule TwoFer do
@doc """
Two-fer or 2-fer is short for two for one. One for you and one for me.
## Examples
iex> TwoFer.two_fer()
"One for you, one for me."
iex> TwoFer.two_fer("John")
"One for John, one for me."
"""
@spec two_fer(String.t()) :: String.t()
def two_fer(name \\ "you") when is_binary(name),
do: "One for #{name}, one for me."
end
Two Fer: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/two-fer/test/two_fer_test.exs
assert TwoFer.two_fer() == "One for you, one for me."
assert TwoFer.two_fer("Alice") == "One for Alice, one for me."
assert TwoFer.two_fer("Bob") == "One for Bob, one for me."
assert_raise FunctionClauseError, fn ->
TwoFer.two_fer(10)
end
assert_raise FunctionClauseError, fn ->
TwoFer.two_fer(:bob)
end
assert_raise FunctionClauseError, fn ->
refute TwoFer.two_fer('Jon Snow')
end
:passed
Accumulate
https://exercism.org/tracks/elixir/exercises/accumulate
defmodule Accumulate do
@doc """
Given a list and a function, apply the function to each list item and
replace it with the function's return value.
Returns a list.
## Examples
iex> Accumulate.accumulate([], fn(x) -> x * 2 end)
[]
iex> Accumulate.accumulate([1, 2, 3], fn(x) -> x * 2 end)
[2, 4, 6]
"""
@spec accumulate(list(), (any() -> any())) :: list()
def accumulate([], _fun), do: []
def accumulate([item | tail], fun), do: [fun.(item) | accumulate(tail, fun)]
end
Accumulate: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/accumulate/test/accumulate_test.exs
assert Accumulate.accumulate([], fn n -> n * n end) == []
assert Accumulate.accumulate([1, 2, 3], fn n -> n * n end) == [1, 4, 9]
fun = fn w -> String.upcase(w) end
assert Accumulate.accumulate(["hello", "world"], fun) == ["HELLO", "WORLD"]
fun = fn w -> String.reverse(w) end
words = ~w(the quick brown fox etc)
expected = ["eht", "kciuq", "nworb", "xof", "cte"]
assert Accumulate.accumulate(words, fun) == expected
chars = ~w(a b c)
nums = ~w(1 2 3)
fun = fn c -> Accumulate.accumulate(nums, &(c <> &1)) end
expected = [["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]]
assert Accumulate.accumulate(chars, fun) == expected
:passed
Acronym
https://exercism.org/tracks/elixir/exercises/acronym
defmodule Acronym do
@doc """
Generate an acronym from a string.
## Examples
iex> Acronym.abbreviate("This is a string")
"TIAS"
"""
@spec abbreviate(String.t()) :: String.t()
def abbreviate(string) do
String.split(string, ~r/\p{Z}|-|_/, trim: true)
|> Enum.map_join("", &String.at(&1, 0))
|> String.upcase()
end
end
Acronym: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/acronym/test/acronym_test.exs
assert Acronym.abbreviate("Portable Networks Graphic") === "PNG"
assert Acronym.abbreviate("Ruby on Rails") === "ROR"
assert Acronym.abbreviate("First in, First out") === "FIFO"
assert Acronym.abbreviate("GNU Image Manipulation Program") === "GIMP"
assert Acronym.abbreviate("Complementary Metal-Oxide semiconductor") === "CMOS"
assert Acronym.abbreviate(
"Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me"
) === "ROTFLSHTMDCOALM"
assert Acronym.abbreviate("Something - I made up from thin air") === "SIMUFTA"
assert Acronym.abbreviate("Halley's Comet") === "HC"
assert Acronym.abbreviate("The Road _Not_ Taken") === "TRNT"
:passed
All Your Base (TCO)
https://exercism.org/tracks/elixir/exercises/all-your-base
defmodule AllYourBase.TCO do
@typep digits :: [non_neg_integer()]
@doc """
Given a number in input base, represented as a sequence of digits, converts it to output base,
or returns an error tuple if either of the bases are less than 2.
## Examples
iex> AllYourBase.TCO.convert([1, 0, 1], 2, 10)
{:ok, [5]}
iex> AllYourBase.TCO.convert([15, 15], 16, 2)
{:ok, [1, 1, 1, 1, 1, 1, 1, 1]}
"""
@spec convert(digits(), input_base :: pos_integer(), output_base :: pos_integer()) ::
{:ok, digits()} | {:error, String.t()}
def convert(digits, input_base, output_base)
def convert(_, _, output_base) when output_base < 2,
do: {:error, "output base must be >= 2"}
def convert(_, input_base, _) when input_base < 2,
do: {:error, "input base must be >= 2"}
def convert([], _, _), do: {:ok, [0]}
def convert(digits, input_base, output_base) do
case do_convert_from(digits, input_base, 0) do
{:ok, 0} -> {:ok, [0]}
{:ok, number} -> do_convert_to(number, output_base, [])
{:error, _reason} = err -> err
end
end
defp do_convert_from(digits, base, acc)
defp do_convert_from([], _, acc), do: {:ok, acc}
defp do_convert_from([digit | _rest], base, _) when digit < 0 or digit >= base,
do: {:error, "all digits must be >= 0 and < input base"}
defp do_convert_from([digit | rest], base, acc),
do: do_convert_from(rest, base, acc * base + digit)
defp do_convert_to(number, base, acc)
defp do_convert_to(0, _, acc), do: {:ok, acc}
defp do_convert_to(number, base, acc),
do: do_convert_to(div(number, base), base, [rem(number, base) | acc])
end
All Your Base (Enum.all?/2, Enum.zip/2)
defmodule AllYourBase.EnumFuncs do
@typep digits :: [non_neg_integer()]
@doc """
Given a number in input base, represented as a sequence of digits, converts it to output base,
or returns an error tuple if either of the bases are less than 2.
## Examples
iex> AllYourBase.EnumFuncs.convert([1, 0, 1], 2, 10)
{:ok, [5]}
iex> AllYourBase.EnumFuncs.convert([15, 15], 16, 2)
{:ok, [1, 1, 1, 1, 1, 1, 1, 1]}
"""
@spec convert(digits(), input_base :: pos_integer(), output_base :: pos_integer()) ::
{:ok, digits()} | {:error, String.t()}
def convert(digits, input_base, output_base)
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([], _input_base, _output_base), do: {:ok, [0]}
def convert(digits, input_base, output_base) when input_base >= 2 and output_base >= 2 do
cond do
all_zeros?(digits) ->
{:ok, [0]}
not all_valid?(digits, input_base) ->
{:error, "all digits must be >= 0 and < input base"}
true ->
number =
digits
|> convert_decimal_from_base(input_base)
|> do_convert_to_base(output_base, [])
{:ok, number}
end
end
defp all_zeros?(digits), do: Enum.all?(digits, &(&1 == 0))
defp all_valid?(digits, input_base), do: Enum.all?(digits, &(&1 >= 0 and &1 < input_base))
defp convert_decimal_from_base(digits, base) do
exponent = Enum.count(digits)
for {digit, position} <- Enum.zip(digits, (exponent - 1)..0), reduce: 0 do
acc -> acc + digit * Integer.pow(base, position)
end
end
defp do_convert_to_base(number, _base, acc) when number <= 0, do: acc
defp do_convert_to_base(number, base, acc) do
acc = [rem(number, base) | acc]
do_convert_to_base(div(number, base), base, acc)
end
end
All Your Base: Tests
for module <- [AllYourBase.TCO, AllYourBase.EnumFuncs] do
assert module.convert([1], 2, 10) == {:ok, [1]}
assert module.convert([1, 0, 1], 2, 10) == {:ok, [5]}
assert module.convert([5], 10, 2) == {:ok, [1, 0, 1]}
assert module.convert([1, 0, 1, 0, 1, 0], 2, 10) == {:ok, [4, 2]}
assert module.convert([4, 2], 10, 2) == {:ok, [1, 0, 1, 0, 1, 0]}
assert module.convert([1, 1, 2, 0], 3, 16) == {:ok, [2, 10]}
assert module.convert([2, 10], 16, 3) == {:ok, [1, 1, 2, 0]}
assert module.convert([3, 46, 60], 97, 73) == {:ok, [6, 10, 45]}
assert module.convert([], 2, 10) == {:ok, [0]}
assert module.convert([0], 10, 2) == {:ok, [0]}
assert module.convert([0, 0, 0], 10, 2) == {:ok, [0]}
assert module.convert([0, 6, 0], 7, 10) == {:ok, [4, 2]}
assert module.convert([0], 1, 10) == {:error, "input base must be >= 2"}
assert module.convert([], 0, 10) == {:error, "input base must be >= 2"}
assert module.convert([1], -2, 10) == {:error, "input base must be >= 2"}
assert module.convert([1, -1, 1, 0, 1, 0], 2, 10) ==
{:error, "all digits must be >= 0 and < input base"}
assert module.convert([1, 2, 1, 0, 1, 0], 2, 10) ==
{:error, "all digits must be >= 0 and < input base"}
assert module.convert([1, 0, 1, 0, 1, 0], 2, 1) == {:error, "output base must be >= 2"}
assert module.convert([7], 10, 0) == {:error, "output base must be >= 2"}
assert module.convert([1], 2, -7) == {:error, "output base must be >= 2"}
assert module.convert([1], -2, -7) == {:error, "output base must be >= 2"}
end
:passed
All Your Base: Benchmark
{:module, name, _binary, _bindings} =
defmodule AllYourBase.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{"Small" => [15, 15], "Bigger" => [15, 15, 15, 15, 15, 15, 15, 15]})
job(tco(input)) do
AllYourBase.TCO.convert(input, 16, 2)
end
job(enum_funcs(input)) do
AllYourBase.EnumFuncs.convert(input, 16, 2)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Anagram
https://exercism.org/tracks/elixir/exercises/anagram
defmodule Anagram do
@doc """
Returns all candidates that are anagrams of, but not equal to, 'base'.
## Examples
iex> Anagram.match("solemn", ~w(lemons cherry melons))
~w(lemons melons)
"""
@spec match(String.t(), [String.t()]) :: [String.t()]
def match(base, candidates) do
for candidate <- candidates,
anagram?(String.downcase(base), String.downcase(candidate)),
into: [],
do: candidate
end
defp anagram?(word, word), do: false
defp anagram?(word_a, word_b), do: sorted(word_a) == sorted(word_b)
defp sorted(word), do: to_charlist(word) |> Enum.sort()
end
Anagram: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/anagram/test/anagram_test.exs
assert Anagram.match("diaper", ~w(hello world zombies pants)) == []
assert Anagram.match(
"solemn",
~w(lemons cherry melons)
) == ~w(lemons melons)
assert Anagram.match("good", ~w(dog goody)) == []
assert Anagram.match(
"listen",
~w(enlists google inlets banana)
) == ~w(inlets)
assert Anagram.match(
"allergy",
~w(gallery ballerina regally clergy largely leading)
) ==
~w(gallery regally largely)
assert Anagram.match("nose", ~w(Eons ONES)) == ~w(Eons ONES)
assert Anagram.match("mass", ~w(last)) == []
assert Anagram.match(
"orchestra",
~w(cashregister Carthorse radishes)
) ==
~w(Carthorse)
assert Anagram.match(
"Orchestra",
~w(cashregister carthorse radishes)
) == ~w(carthorse)
assert Anagram.match("orchestra", ~w(cashregister Carthorse radishes)) ==
~w(Carthorse)
assert Anagram.match("go", ~w(go Go GO)) == []
assert Anagram.match("tapper", ~w(patter)) == []
assert Anagram.match("BANANA", ~w(BANANA)) == []
assert Anagram.match("BANANA", ~w(Banana)) == []
assert Anagram.match("BANANA", ~w(banana)) == []
assert Anagram.match("LISTEN", ~w(Silent LISTEN)) == ~w(Silent)
:passed
Armstrong Numbers (List Comprehension)
https://exercism.org/tracks/elixir/exercises/armstrong-numbers
defmodule ArmstrongNumber.ListComprehension do
@doc """
Checks if a given number is an Armstrong number.
## Examples
iex> ArmstrongNumber.ListComprehension.valid?(9474)
true
iex> ArmstrongNumber.ListComprehension.valid?(9475)
false
"""
@spec valid?(integer()) :: boolean()
def valid?(0), do: true
def valid?(number) do
digits = Integer.digits(number)
exponent = Enum.count(digits)
number ==
for digit <- digits, reduce: 0 do
acc -> acc + Integer.pow(digit, exponent)
end
end
end
Armstrong Numbers (Optimized integer exponentiation)
defmodule ArmstrongNumber.ListComprehensionOIE do
import Bitwise
@doc """
Checks if a given number is an Armstrong number.
## Examples
iex> ArmstrongNumber.ListComprehensionOIE.valid?(9474)
true
iex> ArmstrongNumber.ListComprehensionOIE.valid?(9475)
false
"""
@spec valid?(integer()) :: boolean()
def valid?(0), do: true
def valid?(number) do
digits = Integer.digits(number)
exponent = Enum.count(digits)
number ==
for digit <- digits, reduce: 0 do
acc -> acc + pow(digit, exponent)
end
end
# Optimized integer exponentiation
# https://en.wikipedia.org/wiki/Exponentiation_by_squaring
defp pow(base, exponent), do: pow(base, exponent, 1)
defp pow(_base, 0, result), do: result
defp pow(base, 1, result), do: result * base
defp pow(2, exponent, result), do: result <<< exponent
defp pow(base, exponent, result) when rem(exponent, 2) == 0,
do: pow(base * base, exponent >>> 1, result)
defp pow(base, exponent, result),
do: pow(base * base, exponent >>> 1, result * base)
end
Armstrong Numbers (Enum.reduce/3)
defmodule ArmstrongNumber.EnumReduce do
@doc """
Checks if a given number is an Armstrong number.
## Examples
iex> ArmstrongNumber.EnumReduce.valid?(9474)
true
iex> ArmstrongNumber.EnumReduce.valid?(9475)
false
"""
@spec valid?(integer()) :: boolean()
def valid?(0), do: true
def valid?(number) do
digits = Integer.digits(number)
exponent = Enum.count(digits)
number ==
Enum.reduce(digits, 0, fn digit, acc ->
acc + Integer.pow(digit, exponent)
end)
end
end
Armstrong Numbers (Enum.reduce/3, Optimized integer exponentiation)
defmodule ArmstrongNumber.EnumReduceOIE do
import Bitwise
@doc """
Checks if a given number is an Armstrong number.
## Examples
iex> ArmstrongNumber.EnumReduceOIE.valid?(9474)
true
iex> ArmstrongNumber.EnumReduceOIE.valid?(9475)
false
"""
@spec valid?(integer()) :: boolean()
def valid?(0), do: true
def valid?(number) do
digits = Integer.digits(number)
exponent = Enum.count(digits)
number ==
Enum.reduce(digits, 0, fn digit, acc ->
acc + pow(digit, exponent)
end)
end
# Optimized integer exponentiation
# https://en.wikipedia.org/wiki/Exponentiation_by_squaring
defp pow(base, exponent), do: pow(base, exponent, 1)
defp pow(_base, 0, result), do: result
defp pow(base, 1, result), do: result * base
defp pow(2, exponent, result), do: result <<< exponent
defp pow(base, exponent, result) when rem(exponent, 2) == 0,
do: pow(base * base, exponent >>> 1, result)
defp pow(base, exponent, result),
do: pow(base * base, exponent >>> 1, result * base)
end
Armstrong Numbers (Naive, Enum.reduce/3)
defmodule ArmstrongNumber.NaiveEnumReduce do
@doc """
Checks if a given number is an Armstrong number.
## Examples
iex> ArmstrongNumber.NaiveEnumReduce.valid?(9474)
true
iex> ArmstrongNumber.NaiveEnumReduce.valid?(9475)
false
"""
@spec valid?(integer()) :: boolean()
def valid?(0), do: true
def valid?(number) do
digits =
to_string(number)
|> String.split("", trim: true)
|> Enum.map(&String.to_integer/1)
number ==
Enum.reduce(digits, 0, fn digit, acc ->
acc + Integer.pow(digit, length(digits))
end)
end
end
Armstrong Numbers (Naive, Enum.reduce/3, Optimized integer exponentiation)
defmodule ArmstrongNumber.NaiveEnumReduceOIE do
import Bitwise
@doc """
Checks if a given number is an Armstrong number.
## Examples
iex> ArmstrongNumber.NaiveEnumReduceOIE.valid?(9474)
true
iex> ArmstrongNumber.NaiveEnumReduceOIE.valid?(9475)
false
"""
@spec valid?(integer()) :: boolean()
def valid?(0), do: true
def valid?(number) do
digits =
to_string(number)
|> String.split("", trim: true)
|> Enum.map(&String.to_integer/1)
number ==
Enum.reduce(digits, 0, fn digit, acc ->
acc + pow(digit, length(digits))
end)
end
# Optimized integer exponentiation
# https://en.wikipedia.org/wiki/Exponentiation_by_squaring
defp pow(base, exponent), do: pow(base, exponent, 1)
defp pow(_base, 0, result), do: result
defp pow(base, 1, result), do: result * base
defp pow(2, exponent, result), do: result <<< exponent
defp pow(base, exponent, result) when rem(exponent, 2) == 0,
do: pow(base * base, exponent >>> 1, result)
defp pow(base, exponent, result),
do: pow(base * base, exponent >>> 1, result * base)
end
Armstrong Numbers: Tests
modules = [
ArmstrongNumber.ListComprehension,
ArmstrongNumber.ListComprehensionOIE,
ArmstrongNumber.EnumReduce,
ArmstrongNumber.EnumReduceOIE,
ArmstrongNumber.NaiveEnumReduce,
ArmstrongNumber.NaiveEnumReduceOIE
]
for module <- modules do
assert module.valid?(0)
assert module.valid?(5)
refute module.valid?(10)
assert module.valid?(153)
refute module.valid?(100)
assert module.valid?(9474)
refute module.valid?(9475)
assert module.valid?(9_926_315)
refute module.valid?(9_926_134)
end
:passed
Armstrong Numbers: Benchmark
{:module, name, _binary, _bindings} =
defmodule ArmstrongNumber.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{"Small" => 9474, "Bigger" => 947_494_749_474})
job(list_comphrehension(input)) do
ArmstrongNumber.ListComprehension.valid?(input)
end
job(list_comphrehension_oie(input)) do
ArmstrongNumber.ListComprehensionOIE.valid?(input)
end
job(enum_reduce(input)) do
ArmstrongNumber.EnumReduce.valid?(input)
end
job(enum_reduce_oie(input)) do
ArmstrongNumber.EnumReduceOIE.valid?(input)
end
job(naive_enum_reduce(input)) do
ArmstrongNumber.NaiveEnumReduce.valid?(input)
end
job(naive_enum_reduce_oie(input)) do
ArmstrongNumber.NaiveEnumReduceOIE.valid?(input)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Collatz Conjecture (Stream.iterate/2, Stream.find_index/2)
https://exercism.org/tracks/elixir/exercises/collatz-conjecture
defmodule CollatzConjecture.StreamIterateFindIndex do
import Integer, only: [is_even: 1]
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
## Examples
iex> CollatzConjecture.StreamIterateFindIndex.calc(16)
4
iex> CollatzConjecture.StreamIterateFindIndex.calc(21)
7
"""
@spec calc(input :: pos_integer()) :: non_neg_integer()
def calc(input) when is_integer(input) and input > 0 do
input
|> Stream.iterate(fn
i when is_even(i) -> div(i, 2)
i -> i * 3 + 1
end)
|> Enum.find_index(&(&1 == 1))
end
end
Collatz Conjecture (Stream.unfold/2)
defmodule CollatzConjecture.StreamUnfold do
import Integer, only: [is_even: 1]
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
## Examples
iex> CollatzConjecture.StreamUnfold.calc(16)
4
iex> CollatzConjecture.StreamUnfold.calc(21)
7
"""
@spec calc(input :: pos_integer()) :: non_neg_integer()
def calc(input) when is_integer(input) and input > 0 do
input
|> Stream.unfold(fn
1 -> nil
i when is_even(i) -> {i, div(i, 2)}
i -> {i, i * 3 + 1}
end)
|> Enum.count()
end
end
Collatz Conjecture (Stream.iterate/2, Stream.take_while/2)
defmodule CollatzConjecture.StreamIterateTakeWhile do
import Integer, only: [is_even: 1]
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
## Examples
iex> CollatzConjecture.StreamIterateTakeWhile.calc(16)
4
iex> CollatzConjecture.StreamIterateTakeWhile.calc(21)
7
"""
@spec calc(input :: pos_integer()) :: non_neg_integer()
def calc(input) when is_integer(input) and input > 0 do
input
|> Stream.iterate(fn
i when is_even(i) -> div(i, 2)
i -> i * 3 + 1
end)
|> Stream.take_while(&(&1 != 1))
|> Enum.count()
end
end
Collatz Conjecture (Recursion)
defmodule CollatzConjecture.Recursion do
import Integer, only: [is_even: 1, is_odd: 1]
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
## Examples
iex> CollatzConjecture.Recursion.calc(16)
4
iex> CollatzConjecture.Recursion.calc(21)
7
"""
@spec calc(input :: pos_integer()) :: non_neg_integer()
def calc(1), do: 0
def calc(input) when input > 1 and is_even(input), do: calc(div(input, 2)) + 1
def calc(input) when input > 1 and is_odd(input), do: calc(input * 3 + 1) + 1
end
Collatz Conjecture (TCO)
defmodule CollatzConjecture.TCO do
import Integer, only: [is_even: 1]
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
## Examples
iex> CollatzConjecture.TCO.calc(16)
4
iex> CollatzConjecture.TCO.calc(21)
7
"""
@spec calc(input :: pos_integer()) :: non_neg_integer()
def calc(input) when is_integer(input) and input > 0, do: do_calc(input, 0)
defp do_calc(1, count), do: count
defp do_calc(number, count) when is_even(number), do: do_calc(div(number, 2), count + 1)
defp do_calc(number, count), do: do_calc(number * 3 + 1, count + 1)
end
Collatz Conjecture (TCO, Case)
defmodule CollatzConjecture.TCOCase do
@doc """
calc/1 takes an integer and returns the number of steps required to get the
number to 1 when following the rules:
- if number is odd, multiply with 3 and add 1
- if number is even, divide by 2
## Examples
iex> CollatzConjecture.TCOCase.calc(16)
4
iex> CollatzConjecture.TCOCase.calc(21)
7
"""
@spec calc(input :: pos_integer()) :: non_neg_integer()
def calc(input) when is_integer(input) and input > 0, do: do_calc(input, 0)
defp do_calc(1, count), do: count
defp do_calc(number, count) do
number =
case rem(number, 2) do
0 -> div(number, 2)
_ -> number * 3 + 1
end
do_calc(number, count + 1)
end
end
Collatz Conjecture: Tests
modules = [
CollatzConjecture.StreamIterateFindIndex,
CollatzConjecture.StreamUnfold,
CollatzConjecture.StreamIterateTakeWhile,
CollatzConjecture.Recursion,
CollatzConjecture.TCO,
CollatzConjecture.TCOCase
]
for module <- modules do
assert module.calc(1) == 0
assert module.calc(16) == 4
assert module.calc(12) == 9
assert module.calc(1_000_000) == 152
assert module.calc(21) == 7
assert module.calc(7) == 16
assert_raise FunctionClauseError,
fn -> module.calc(0) end
assert_raise FunctionClauseError,
fn -> module.calc(-15) end
assert_raise FunctionClauseError,
fn -> module.calc("fubar") end
end
:passed
Collatz Conjecture: Benchmark
{:module, name, _binary, _bindings} =
defmodule CollatzConjecture.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{"Small" => 16, "Bigger" => 16_161_616_161_616})
job(stream_iterate_find_index(input)) do
CollatzConjecture.StreamIterateFindIndex.calc(input)
end
job(stream_unfold(input)) do
CollatzConjecture.StreamUnfold.calc(input)
end
job(stream_iterate_take_while(input)) do
CollatzConjecture.StreamIterateTakeWhile.calc(input)
end
job(recursion(input)) do
CollatzConjecture.Recursion.calc(input)
end
job(tco(input)) do
CollatzConjecture.TCO.calc(input)
end
job(tco_case(input)) do
CollatzConjecture.TCOCase.calc(input)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Darts
https://exercism.org/tracks/elixir/exercises/darts
ETL
https://exercism.org/tracks/elixir/exercises/etl
defmodule ETL do
@moduledoc """
This module defines `Transform` step of Extract-Transform-Load which allows to
extract some Scrabble scores from legacy system and transform them to format
which is understood by new Scrabble system.
"""
@doc """
Transforms an old Scrabble score system to a new one.
## Examples
iex> ETL.transform(%{1 => ["A", "E"], 2 => ["D", "G"]})
%{"a" => 1, "d" => 2, "e" => 1, "g" => 2}
"""
@spec transform(map()) :: map()
def transform(input) do
for {points, letters} <- input, letter <- letters, into: %{} do
{String.downcase(letter), points}
end
end
end
ETL: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/etl/test/etl_test.exs
old = %{1 => ["A"]}
expected = %{"a" => 1}
assert ETL.transform(old) == expected
old = %{1 => ~W(A E I O U)}
expected = %{"a" => 1, "e" => 1, "i" => 1, "o" => 1, "u" => 1}
assert ETL.transform(old) == expected
old = %{1 => ["A", "E"], 2 => ["D", "G"]}
expected = %{
"a" => 1,
"d" => 2,
"e" => 1,
"g" => 2
}
assert ETL.transform(old) == expected
old = %{
1 => ~W(A E I O U L N R S T),
2 => ~W(D G),
3 => ~W(B C M P),
4 => ~W(F H V W Y),
5 => ~W(K),
8 => ~W(J X),
10 => ~W(Q Z)
}
expected = %{
"a" => 1,
"b" => 3,
"c" => 3,
"d" => 2,
"e" => 1,
"f" => 4,
"g" => 2,
"h" => 4,
"i" => 1,
"j" => 8,
"k" => 5,
"l" => 1,
"m" => 3,
"n" => 1,
"o" => 1,
"p" => 3,
"q" => 10,
"r" => 1,
"s" => 1,
"t" => 1,
"u" => 1,
"v" => 4,
"w" => 4,
"x" => 8,
"y" => 4,
"z" => 10
}
assert ETL.transform(old) == expected
:passed
Hamming (TCO)
https://exercism.org/tracks/elixir/exercises/hamming
defmodule Hamming.TCO do
@doc """
Returns number of differences between two strands of DNA, known as the Hamming Distance.
## Examples
iex> Hamming.TCO.hamming_distance('AAGTCATA', 'TAGCGATC')
{:ok, 4}
"""
@spec hamming_distance([char()], [char()]) :: {:ok, non_neg_integer()} | {:error, String.t()}
def hamming_distance(strand1, strand2) when length(strand1) == length(strand2),
do: {:ok, do_hamming_distance(strand1, strand2, 0)}
def hamming_distance(_starnd1, _strand2), do: {:error, "strands must be of equal length"}
defp do_hamming_distance([], [], distance), do: distance
defp do_hamming_distance([gene1 | tail1], [gene2 | tail2], distance) do
distance = if gene1 == gene2, do: distance, else: distance + 1
do_hamming_distance(tail1, tail2, distance)
end
end
Hamming (Stream.zip/2)
defmodule Hamming.StreamZip do
@doc """
Returns number of differences between two strands of DNA, known as the Hamming Distance.
## Examples
iex> Hamming.StreamZip.hamming_distance('AAGTCATA', 'TAGCGATC')
{:ok, 4}
"""
@spec hamming_distance([char()], [char()]) :: {:ok, non_neg_integer()} | {:error, String.t()}
def hamming_distance(strand1, strand2) when length(strand1) == length(strand2) do
{:ok, Stream.zip(strand1, strand2) |> Enum.count(fn {gene1, gene2} -> gene1 != gene2 end)}
end
def hamming_distance(_starnd1, _strand2), do: {:error, "strands must be of equal length"}
end
Hamming: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/hamming/test/hamming_test.exs
for module <- [Hamming.TCO, Hamming.StreamZip] do
assert module.hamming_distance('', '') == {:ok, 0}
assert module.hamming_distance('A', 'A') == {:ok, 0}
assert module.hamming_distance('G', 'T') == {:ok, 1}
assert module.hamming_distance('GGACTGAAATCTG', 'GGACTGAAATCTG') == {:ok, 0}
assert module.hamming_distance('GGACGGATTCTG', 'AGGACGGATTCT') == {:ok, 9}
assert {:error, "strands must be of equal length"} = module.hamming_distance('AATG', 'AAA')
assert {:error, "strands must be of equal length"} = module.hamming_distance('ATA', 'AGTG')
assert {:error, "strands must be of equal length"} = module.hamming_distance('', 'G')
assert {:error, "strands must be of equal length"} = module.hamming_distance('G', '')
end
:passed
Hamming: Benchmark
{:module, name, _binary, _bindings} =
defmodule Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{
"Small" => {'AAGTCATA', 'TAGCGATC'},
"Bigger" =>
{'AAGTCATAAAGTCATAAAGTCATAAAGTCATAAAGTCATAAAGTCATA',
'TAGCGATCTAGCGATCTAGCGATCTAGCGATCTAGCGATCTAGCGATC'}
})
job(tco({a, b})) do
Hamming.TCO.hamming_distance(a, b)
end
job(stream_zip({a, b})) do
Hamming.StreamZip.hamming_distance(a, b)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Nth Prime
https://exercism.org/tracks/elixir/exercises/nth-prime
defmodule Prime do
@doc """
Generates the nth prime number.
## Examples
iex> Prime.nth(6)
13
"""
@spec nth(non_neg_integer) :: non_neg_integer
def nth(count) when is_integer(count) and count > 0, do: do_nth(count, 2, [])
defp do_nth(0, _, primes), do: hd(primes)
defp do_nth(count, value, primes) do
case prime?(value, primes) do
true -> do_nth(count - 1, value + 1, [value | primes])
false -> do_nth(count, value + 1, primes)
end
end
defp prime?(number, primes), do: Enum.all?(primes, &(rem(number, &1) != 0))
end
Nth Prime: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/nth-prime/test/prime_test.exs
assert Prime.nth(1) == 2
assert Prime.nth(2) == 3
assert Prime.nth(6) == 13
assert Prime.nth(100) == 541
assert Prime.nth(10001) == 104_743
catch_error(Prime.nth(0))
:passed
Nucleotide Count
https://exercism.org/tracks/elixir/exercises/nucleotide-count
defmodule NucleotideCount do
@doc """
Counts individual nucleotides in a DNA strand.
## Examples
iex> NucleotideCount.count('AATAA', ?A)
4
iex> NucleotideCount.count('AATAA', ?T)
1
"""
@spec count(charlist(), char()) :: non_neg_integer()
def count(strand, nucleotide),
do: Enum.count(strand, &(&1 == nucleotide))
@histogram %{?A => 0, ?T => 0, ?C => 0, ?G => 0}
@doc """
Returns a summary of counts by nucleotide.
## Examples
iex> NucleotideCount.histogram('AATAA')
%{?A => 4, ?T => 1, ?C => 0, ?G => 0}
"""
@spec histogram(charlist()) :: map()
def histogram(strand) do
for nucleotide <- strand, reduce: @histogram do
acc -> Map.update(acc, nucleotide, 1, &(&1 + 1))
end
end
end
Nucleotide Count: Tests
assert NucleotideCount.count('', ?A) == 0
assert NucleotideCount.count('G', ?G) == 1
assert NucleotideCount.count('CCCCC', ?C) == 5
assert NucleotideCount.count('GGGGGTAACCCGG', ?T) == 1
expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 0}
assert NucleotideCount.histogram('') == expected
expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 1}
assert NucleotideCount.histogram('G') == expected
expected = %{?A => 0, ?T => 0, ?C => 0, ?G => 8}
assert NucleotideCount.histogram('GGGGGGGG') == expected
s = 'AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC'
expected = %{?A => 20, ?T => 21, ?C => 12, ?G => 17}
assert NucleotideCount.histogram(s) == expected
:passed
Pangram (List Comprehension Uniq)
https://exercism.org/tracks/elixir/exercises/pangram
defmodule Pangram.ListComprehensionUniq do
@ascii_alphabet Enum.to_list(?a..?z)
@ascii_alphabet_size length(@ascii_alphabet)
@doc """
Determines if a word or sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
Returns a boolean.
## Examples
iex> Pangram.ListComprehensionUniq.pangram?("the quick brown fox jumps over the lazy dog")
true
iex> Pangram.ListComprehensionUniq.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
false
"""
@spec pangram?(String.t()) :: boolean()
def pangram?(sentence) do
chars =
sentence
|> String.downcase()
|> to_charlist()
chars = for char <- chars, char in @ascii_alphabet, uniq: true, into: [], do: char
length(chars) == @ascii_alphabet_size
end
end
Pangram (TCO)
defmodule Pangram.TCO do
@ascii_alphabet Enum.to_list(?a..?z)
@doc """
Determines if a word or sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
Returns a boolean.
## Examples
iex> Pangram.TCO.pangram?("the quick brown fox jumps over the lazy dog")
true
iex> Pangram.TCO.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
false
"""
@spec pangram?(String.t()) :: boolean()
def pangram?(sentence) do
chars =
sentence
|> String.downcase()
|> to_charlist()
do_pangram(chars, @ascii_alphabet)
end
defp do_pangram(_chars, []), do: true
defp do_pangram([], _letters), do: false
defp do_pangram([char | tail], letters), do: do_pangram(tail, List.delete(letters, char))
end
Pangram (Enum.all?/2)
defmodule Pangram.EnumAll do
@ascii_alphabet ?a..?z
@doc """
Determines if a word or sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
Returns a boolean.
## Examples
iex> Pangram.EnumAll.pangram?("the quick brown fox jumps over the lazy dog")
true
iex> Pangram.EnumAll.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
false
"""
@spec pangram?(String.t()) :: boolean()
def pangram?(sentence) do
chars =
sentence
|> String.downcase()
|> to_charlist()
Enum.all?(@ascii_alphabet, &(&1 in chars))
end
end
Pangram (List Substraction)
defmodule Pangram.ListSubstraction do
@ascii_alphabet ?a..?z
@doc """
Determines if a word or sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
Returns a boolean.
## Examples
iex> Pangram.ListSubstraction.pangram?("the quick brown fox jumps over the lazy dog")
true
iex> Pangram.ListSubstraction.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
false
"""
@spec pangram?(String.t()) :: boolean()
def pangram?(sentence) do
Enum.empty?(Enum.to_list(@ascii_alphabet) -- to_charlist(String.downcase(sentence)))
end
end
Pangram (List Comprehension Reduce)
defmodule Pangram.ListComprehensionReduce do
@doc """
Determines if a word or sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
Returns a boolean.
## Examples
iex> Pangram.ListComprehensionReduce.pangram?("the quick brown fox jumps over the lazy dog")
true
iex> Pangram.ListComprehensionReduce.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
false
"""
@spec pangram?(String.t()) :: boolean()
def pangram?(sentence) do
sentence = String.downcase(sentence) |> to_charlist()
for char <- sentence, char in ?a..?z, reduce: %{} do
acc -> Map.put(acc, char, true)
end
|> Map.keys()
|> Enum.count() == 26
end
end
Pangram: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/pangram/test/pangram_test.exs
modules = [
Pangram.ListComprehensionUniq,
Pangram.TCO,
Pangram.EnumAll,
Pangram.ListSubstraction,
Pangram.ListComprehensionReduce
]
for module <- modules do
refute module.pangram?("")
assert module.pangram?("abcdefghijklmnopqrstuvwxyz")
assert module.pangram?("the quick brown fox jumps over the lazy dog")
refute module.pangram?("a quick movement of the enemy will jeopardize five gunboats")
refute module.pangram?("the quick brown fish jumps over the lazy dog")
refute module.pangram?("five boxing wizards jump quickly at it")
assert module.pangram?("the_quick_brown_fox_jumps_over_the_lazy_dog")
assert module.pangram?("the 1 quick brown fox jumps over the 2 lazy dogs")
refute module.pangram?("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")
assert module.pangram?("\"Five quacking Zephyrs jolt my wax bed.\"")
assert module.pangram?("the quick brown fox jumps over the lazy DOG")
refute module.pangram?("abcdefghijklm ABCDEFGHIJKLM")
assert module.pangram?("Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich.")
refute module.pangram?(
"Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства."
)
end
:passed
Pangram: Benchmark
{:module, name, _binary, _bindings} =
defmodule Pangram.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{
"Small" => "the quick brown fox jumps over the lazy dog",
"Bigger" =>
"the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog"
})
job(list_comprehension_uniq(input)) do
Pangram.ListComprehensionUniq.pangram?(input)
end
job(tco(input)) do
Pangram.TCO.pangram?(input)
end
job(enum_all(input)) do
Pangram.EnumAll.pangram?(input)
end
job(list_substraction(input)) do
Pangram.ListSubstraction.pangram?(input)
end
job(list_comprehension_reduce(input)) do
Pangram.ListComprehensionReduce.pangram?(input)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Pig Latin (Binary Patter Matching)
https://exercism.org/tracks/elixir/exercises/pig-latin
defmodule PigLatin.Binary do
@vowels [?a, ?e, ?i, ?o, ?u]
defguardp is_vowel(c) when c in @vowels
defguardp is_consonant(c) when not is_vowel(c)
@doc """
Given a `phrase`, translate it a word at a time to Pig Latin.
## Examples
iex> PigLatin.translate("fox")
"oxfay"
iex> PigLatin.translate("throat")
"oatthray"
"""
@spec translate(phrase :: String.t()) :: String.t()
def translate(phrase) do
phrase
|> String.split()
|> Enum.map_join(" ", &word/1)
end
defp word(<> = word) when is_vowel(c),
do: <>
defp word(<<"qu", rest::binary>>),
do: <>
defp word(<>) when is_consonant(c),
do: <>
defp word(<> = word) when xy in [?x, ?y] and is_consonant(c),
do: <>
defp word(<c::utf8,rest::binary>) when is_vowel(c),
do: <>
defp word(word),
do: cluster(word, [[] | ""])
defp cluster(<>, [_ | c2] = acc) when is_consonant(c) and c2 != ~c"y",
do: cluster(rest, [acc | [c]])
defp cluster(word, [acc | [?y]]),
do: <word::binary,io.iodata_to_binary(acc)::binary,"ay">
defp cluster(word, acc),
do: <>
end
Pig Latin (Regex)
defmodule PigLatin.Regex do
@regex ~r/^(?:y([aeiou][a-z]+))|((?:xr)?(?:xb)?[aeiouy][a-z]+)|(?:([^aeioyqu]*qu|[^aeiouy]+)([a-z]+))/
@doc """
Given a `phrase`, translate it a word at a time to Pig Latin.
## Examples
iex> PigLatin.translate("fox")
"oxfay"
iex> PigLatin.translate("throat")
"oatthray"
"""
@spec translate(phrase :: String.t()) :: String.t()
def translate(phrase) do
phrase
|> String.split()
|> Enum.map_join(" ", &do_translate/1)
end
defp do_translate(word) do
case Regex.run(@regex, word) do
[_, m1] -> m1 <> "yay"
[_, _, m2] -> m2 <> "ay"
[_, _, _, m3, m4] -> IO.iodata_to_binary([m4, m3, "ay"])
end
end
end
Pig Latin: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/pig-latin/test/pig_latin_test.exs
for module <- [PigLatin.Binary, PigLatin.Regex] do
assert module.translate("apple") == "appleay"
assert module.translate("ear") == "earay"
assert module.translate("igloo") == "iglooay"
assert module.translate("object") == "objectay"
assert module.translate("under") == "underay"
assert module.translate("equal") == "equalay"
assert module.translate("pig") == "igpay"
assert module.translate("koala") == "oalakay"
assert module.translate("xenon") == "enonxay"
assert module.translate("qat") == "atqay"
assert module.translate("pleasure") == "easureplay"
assert module.translate("stringify") == "ingifystray"
assert module.translate("zkrrkrkrkrzzzkewk") == "ewkzkrrkrkrkrzzzkay"
assert module.translate("chair") == "airchay"
assert module.translate("queen") == "eenquay"
assert module.translate("square") == "aresquay"
assert module.translate("therapy") == "erapythay"
assert module.translate("thrush") == "ushthray"
assert module.translate("school") == "oolschay"
assert module.translate("yttria") == "yttriaay"
assert module.translate("yddria") == "yddriaay"
assert module.translate("xray") == "xrayay"
assert module.translate("xbot") == "xbotay"
assert module.translate("yellow") == "ellowyay"
assert module.translate("rhythm") == "ythmrhay"
assert module.translate("my") == "ymay"
assert module.translate("quick fast run") == "ickquay astfay unray"
end
:passed
Pig Latin: Benchmark
{:module, name, _binary, _bindings} =
defmodule PigLatin.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{
"Small" => "the quick brown fox jumps over the lazy dog",
"Bigger" =>
"the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog the quick brown fox jumps over the lazy dog"
})
job(binary_patter_matching(input)) do
PigLatin.Binary.translate(input)
end
job(regex(input)) do
PigLatin.Regex.translate(input)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Protein Translation (TCO)
https://exercism.org/tracks/elixir/exercises/protein-translation
defmodule ProteinTranslation.TCO do
@doc """
Given an RNA string, return a list of proteins specified by codons, in order.
## Examples
iex> ProteinTranslation.TCO.of_rna("UUUUUU")
{:ok, ~w(Phenylalanine Phenylalanine)}
"""
@spec of_rna(String.t()) :: {:ok, list(String.t())} | {:error, String.t()}
def of_rna(rna), do: do_of_rna(rna, [])
defp do_of_rna(<>, proteins) do
case of_codon(codon) do
{:ok, "STOP"} -> do_of_rna(<<>>, proteins)
{:ok, protein} -> do_of_rna(rest, proteins ++ [protein])
{:error, _reason} -> {:error, "invalid RNA"}
end
end
defp do_of_rna(<<>>, proteins), do: {:ok, proteins}
defp do_of_rna(_rna, _proteins), do: {:error, "invalid RNA"}
@codon_to_protein %{
"UGU" => "Cysteine",
"UGC" => "Cysteine",
"UUA" => "Leucine",
"UUG" => "Leucine",
"AUG" => "Methionine",
"UUU" => "Phenylalanine",
"UUC" => "Phenylalanine",
"UCU" => "Serine",
"UCC" => "Serine",
"UCA" => "Serine",
"UCG" => "Serine",
"UGG" => "Tryptophan",
"UAU" => "Tyrosine",
"UAC" => "Tyrosine",
"UAA" => "STOP",
"UAG" => "STOP",
"UGA" => "STOP"
}
@doc """
Given a codon, return the corresponding protein
UGU -> Cysteine
UGC -> Cysteine
UUA -> Leucine
UUG -> Leucine
AUG -> Methionine
UUU -> Phenylalanine
UUC -> Phenylalanine
UCU -> Serine
UCC -> Serine
UCA -> Serine
UCG -> Serine
UGG -> Tryptophan
UAU -> Tyrosine
UAC -> Tyrosine
UAA -> STOP
UAG -> STOP
UGA -> STOP
"""
@spec of_codon(String.t()) :: {:ok, String.t()} | {:error, String.t()}
for {codon, protein} <- @codon_to_protein do
def of_codon(unquote(codon)), do: {:ok, unquote(protein)}
end
def of_codon(_codon), do: {:error, "invalid codon"}
end
Protein Translation (TCO, Case)
defmodule ProteinTranslation.TCOCase do
@doc """
Given an RNA string, return a list of proteins specified by codons, in order.
## Examples
iex> ProteinTranslation.TCOCase.of_rna("UUUUUU")
{:ok, ~w(Phenylalanine Phenylalanine)}
"""
@spec of_rna(String.t()) :: {:ok, list(String.t())} | {:error, String.t()}
def of_rna(rna), do: do_of_rna(rna, [])
defp do_of_rna(<>, proteins) do
case of_codon(codon) do
{:ok, "STOP"} -> do_of_rna(<<>>, proteins)
{:ok, protein} -> do_of_rna(rest, proteins ++ [protein])
{:error, _reason} -> {:error, "invalid RNA"}
end
end
defp do_of_rna(<<>>, proteins), do: {:ok, proteins}
defp do_of_rna(_rna, _proteins), do: {:error, "invalid RNA"}
@doc """
Given a codon, return the corresponding protein
UGU -> Cysteine
UGC -> Cysteine
UUA -> Leucine
UUG -> Leucine
AUG -> Methionine
UUU -> Phenylalanine
UUC -> Phenylalanine
UCU -> Serine
UCC -> Serine
UCA -> Serine
UCG -> Serine
UGG -> Tryptophan
UAU -> Tyrosine
UAC -> Tyrosine
UAA -> STOP
UAG -> STOP
UGA -> STOP
"""
@spec of_codon(String.t()) :: {:ok, String.t()} | {:error, String.t()}
def of_codon(codon) do
case codon do
"UGU" -> {:ok, "Cysteine"}
"UGC" -> {:ok, "Cysteine"}
"UUA" -> {:ok, "Leucine"}
"UUG" -> {:ok, "Leucine"}
"AUG" -> {:ok, "Methionine"}
"UUU" -> {:ok, "Phenylalanine"}
"UUC" -> {:ok, "Phenylalanine"}
"UCU" -> {:ok, "Serine"}
"UCC" -> {:ok, "Serine"}
"UCA" -> {:ok, "Serine"}
"UCG" -> {:ok, "Serine"}
"UGG" -> {:ok, "Tryptophan"}
"UAU" -> {:ok, "Tyrosine"}
"UAC" -> {:ok, "Tyrosine"}
"UAA" -> {:ok, "STOP"}
"UAG" -> {:ok, "STOP"}
"UGA" -> {:ok, "STOP"}
_ -> {:error, "invalid codon"}
end
end
end
Protein Translation: Tests
for module <- [ProteinTranslation.TCO, ProteinTranslation.TCOCase] do
assert module.of_codon("AUG") == {:ok, "Methionine"}
assert module.of_codon("UUU") == {:ok, "Phenylalanine"}
assert module.of_codon("UUC") == {:ok, "Phenylalanine"}
assert module.of_codon("UUA") == {:ok, "Leucine"}
assert module.of_codon("UUG") == {:ok, "Leucine"}
assert module.of_codon("UCU") == {:ok, "Serine"}
assert module.of_codon("UCC") == {:ok, "Serine"}
assert module.of_codon("UCA") == {:ok, "Serine"}
assert module.of_codon("UCG") == {:ok, "Serine"}
assert module.of_codon("UAU") == {:ok, "Tyrosine"}
assert module.of_codon("UAC") == {:ok, "Tyrosine"}
assert module.of_codon("UGU") == {:ok, "Cysteine"}
assert module.of_codon("UGC") == {:ok, "Cysteine"}
assert module.of_codon("UGG") == {:ok, "Tryptophan"}
assert module.of_codon("UAA") == {:ok, "STOP"}
assert module.of_codon("UAG") == {:ok, "STOP"}
assert module.of_codon("UGA") == {:ok, "STOP"}
assert module.of_codon("UG") == {:error, "invalid codon"}
assert module.of_codon("UGGG") == {:error, "invalid codon"}
assert module.of_codon("AAA") == {:error, "invalid codon"}
assert module.of_codon("XYZ") == {:error, "invalid codon"}
assert module.of_rna("") == {:ok, []}
assert module.of_rna("AUGUUUUGG") ==
{:ok, ~w(Methionine Phenylalanine Tryptophan)}
assert module.of_rna("UUUUUU") ==
{:ok, ~w(Phenylalanine Phenylalanine)}
assert module.of_rna("UUAUUG") == {:ok, ~w(Leucine Leucine)}
assert module.of_rna("UAGUGG") == {:ok, ~w()}
assert module.of_rna("UGGUAG") == {:ok, ~w(Tryptophan)}
assert module.of_rna("AUGUUUUAA") == {:ok, ~w(Methionine Phenylalanine)}
assert module.of_rna("UGGUAGUGG") == {:ok, ~w(Tryptophan)}
assert module.of_rna("UGGUGUUAUUAAUGGUUU") ==
{:ok, ~w(Tryptophan Cysteine Tyrosine)}
assert module.of_rna("UG") == {:error, "invalid RNA"}
assert module.of_rna("AAA") == {:error, "invalid RNA"}
assert module.of_rna("XYZ") == {:error, "invalid RNA"}
assert module.of_rna("UUUROT") == {:error, "invalid RNA"}
assert module.of_rna("AUGU") == {:error, "invalid RNA"}
assert module.of_rna("UUCUUCUAAUGGU") ==
{:ok, ~w(Phenylalanine Phenylalanine)}
end
:passed
Protein Translation: Benchmark
{:module, name, _binary, _bindings} =
defmodule ProteinTranslation.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{
"Small" => "UUUUUU",
"Bigger" => "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"
})
job(tco(input)) do
ProteinTranslation.TCO.of_rna(input)
end
job(tco_case(input)) do
ProteinTranslation.TCOCase.of_rna(input)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Raindrops
https://exercism.org/tracks/elixir/exercises/raindrops
defmodule Raindrops do
@drops Pling: 3, Plang: 5, Plong: 7
@doc """
Returns a string based on raindrop factors.
- If the number contains 3 as a prime factor, output 'Pling'.
- If the number contains 5 as a prime factor, output 'Plang'.
- If the number contains 7 as a prime factor, output 'Plong'.
- If the number does not contain 3, 5, or 7 as a prime factor,
just pass the number's digits straight through.
## Examples
iex> Raindrops.convert(105)
"PlingPlangPlong"
"""
@spec convert(pos_integer()) :: String.t()
def convert(number) do
@drops
|> Enum.filter(&factor?(number, elem(&1, 1)))
|> Enum.map(&elem(&1, 0))
|> join(number)
end
defp factor?(number, factor) do
rem(number, factor) == 0
end
defp join([], number), do: to_string(number)
defp join(list, _number), do: Enum.join(list, "")
end
Raindrops: Tests
https://github.com/exercism/elixir/blob/main/exercises/practice/raindrops/test/raindrops_test.exs
assert Raindrops.convert(1) == "1"
assert Raindrops.convert(3) == "Pling"
assert Raindrops.convert(5) == "Plang"
assert Raindrops.convert(7) == "Plong"
assert Raindrops.convert(6) == "Pling"
assert Raindrops.convert(8) == "8"
assert Raindrops.convert(9) == "Pling"
assert Raindrops.convert(10) == "Plang"
assert Raindrops.convert(14) == "Plong"
assert Raindrops.convert(15) == "PlingPlang"
assert Raindrops.convert(21) == "PlingPlong"
assert Raindrops.convert(25) == "Plang"
assert Raindrops.convert(35) == "PlangPlong"
assert Raindrops.convert(49) == "Plong"
assert Raindrops.convert(52) == "52"
assert Raindrops.convert(105) == "PlingPlangPlong"
assert Raindrops.convert(12121) == "12121"
:passed
Resistor Color Duo
https://exercism.org/tracks/elixir/exercises/resistor-color-duo
defmodule ResistorColorDuo do
@type color ::
:black | :brown | :red | :orange | :yellow | :green | :blue | :violet | :grey | :white
@spec code(color :: color()) :: non_neg_integer()
defp code(:black), do: 0
defp code(:brown), do: 1
defp code(:red), do: 2
defp code(:orange), do: 3
defp code(:yellow), do: 4
defp code(:green), do: 5
defp code(:blue), do: 6
defp code(:violet), do: 7
defp code(:grey), do: 8
defp code(:white), do: 9
@doc """
Calculate a resistance value from two colors.
## Examples
iex> ResistorColorDuo.value([:yellow, :red])
42
"""
@spec value(colors :: [color()]) :: non_neg_integer()
def value(colors) when length(colors) >= 2 do
[color1, color2 | _tail] = colors
code(color1) * 10 + code(color2)
end
end
Resistor Color Duo: Tests
colors = [:brown, :black]
output = ResistorColorDuo.value(colors)
expected = 10
assert output == expected
colors = [:blue, :grey]
output = ResistorColorDuo.value(colors)
expected = 68
assert output == expected
colors = [:yellow, :violet]
output = ResistorColorDuo.value(colors)
expected = 47
assert output == expected
colors = [:white, :red]
output = ResistorColorDuo.value(colors)
expected = 92
assert output == expected
colors = [:orange, :orange]
output = ResistorColorDuo.value(colors)
expected = 33
assert output == expected
colors = [:green, :brown, :orange]
output = ResistorColorDuo.value(colors)
expected = 51
assert output == expected
colors = [:black, :brown]
output = ResistorColorDuo.value(colors)
expected = 1
assert output == expected
:passed
RNA Transcription (TCO)
https://exercism.org/tracks/elixir/exercises/rna-transcription
defmodule RnaTranscription.TCO do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.TCO.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna), do: do_to_rna(dna, [])
defp do_to_rna([], acc), do: Enum.reverse(acc)
defp do_to_rna([?G | dna], acc), do: do_to_rna(dna, [?C | acc])
defp do_to_rna([?C | dna], acc), do: do_to_rna(dna, [?G | acc])
defp do_to_rna([?T | dna], acc), do: do_to_rna(dna, [?A | acc])
defp do_to_rna([?A | dna], acc), do: do_to_rna(dna, [?U | acc])
end
RNA Transcription (Recursion)
defmodule RnaTranscription.Recursion do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.Recursion.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna([]), do: []
def to_rna([?G | dna]), do: [?C | to_rna(dna)]
def to_rna([?C | dna]), do: [?G | to_rna(dna)]
def to_rna([?T | dna]), do: [?A | to_rna(dna)]
def to_rna([?A | dna]), do: [?U | to_rna(dna)]
end
RNA Transcription (Enum.map/2, Multi Clause)
defmodule RnaTranscription.EnumMapMultiClause do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.EnumMapMultiClause.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
Enum.map(dna, fn
?G -> ?C
?C -> ?G
?T -> ?A
?A -> ?U
end)
end
end
RNA Transcription (Enum.map/2, Case)
defmodule RnaTranscription.EnumMapCase do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.EnumMapCase.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
Enum.map(dna, fn nucleotide ->
case nucleotide do
?G -> ?C
?C -> ?G
?T -> ?A
?A -> ?U
end
end)
end
end
RNA Transcription (List Comprehension)
defmodule RnaTranscription.ListComprehension do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.ListComprehension.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
for nucleotide <- dna, into: [] do
transcribe(nucleotide)
end
end
defp transcribe(?G), do: ?C
defp transcribe(?C), do: ?G
defp transcribe(?T), do: ?A
defp transcribe(?A), do: ?U
end
RNA Transcription (Enum.map/2)
defmodule RnaTranscription.EnumMap do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.EnumMap.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
Enum.map(dna, &transcribe/1)
end
defp transcribe(?G), do: ?C
defp transcribe(?C), do: ?G
defp transcribe(?T), do: ?A
defp transcribe(?A), do: ?U
end
RNA Transcription (Enum.map/2, Lookup List)
defmodule RnaTranscription.EnumMapLookupDict do
@rna_complements %{
?G => ?C,
?C => ?G,
?T => ?A,
?A => ?U
}
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.EnumMapLookupDict.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
Enum.map(dna, &@rna_complements[&1])
end
end
RNA Transcription (List Comprehension, Lookup Dict)
defmodule RnaTranscription.ListComprehensionLookupDict do
@rna_complements %{
?G => ?C,
?C => ?G,
?T => ?A,
?A => ?U
}
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.ListComprehensionLookupDict.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
for nucleotide <- dna, into: [] do
@rna_complements[nucleotide]
end
end
end
RNA Transcription (List Comprehension, Case)
defmodule RnaTranscription.ListComprehensionCase do
@doc """
Transcribes a character list representing DNA nucleotides to RNA.
## Examples
iex> RnaTranscription.ListComprehensionCase.to_rna('ACTG')
'UGAC'
"""
@spec to_rna([char()]) :: [char()]
def to_rna(dna) do
for nucleotide <- dna, into: [] do
case nucleotide do
?G -> ?C
?C -> ?G
?T -> ?A
?A -> ?U
end
end
end
end
RNA Transcription: Tests
modules = [
RnaTranscription.TCO,
RnaTranscription.Recursion,
RnaTranscription.EnumMapMultiClause,
RnaTranscription.EnumMapCase,
RnaTranscription.ListComprehension,
RnaTranscription.EnumMap,
RnaTranscription.EnumMapLookupDict,
RnaTranscription.ListComprehensionLookupDict,
RnaTranscription.ListComprehensionCase
]
for module <- modules do
assert module.to_rna('') == ''
assert module.to_rna('G') == 'C'
assert module.to_rna('C') == 'G'
assert module.to_rna('T') == 'A'
assert module.to_rna('A') == 'U'
assert module.to_rna('ACGTGGTCTTAA') == 'UGCACCAGAAUU'
end
:passed
RNA Transcription: Benchmark
{:module, name, _binary, _bindings} =
defmodule RnaTranscription.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{
"Small" => 'ACTG',
"Bigger" => 'ACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTG'
})
job(tco(input)) do
RnaTranscription.TCO.to_rna(input)
end
job(recursion(input)) do
RnaTranscription.Recursion.to_rna(input)
end
job(enum_map_multi_clause(input)) do
RnaTranscription.EnumMapMultiClause.to_rna(input)
end
job(enum_map_case(input)) do
RnaTranscription.EnumMapCase.to_rna(input)
end
job(list_comprehension(input)) do
RnaTranscription.ListComprehension.to_rna(input)
end
job(enum_map(input)) do
RnaTranscription.EnumMap.to_rna(input)
end
job(enum_map_lookup_dict(input)) do
RnaTranscription.EnumMapLookupDict.to_rna(input)
end
job(list_comprehension_lookup_dict(input)) do
RnaTranscription.ListComprehensionLookupDict.to_rna(input)
end
job(list_comprehension_case(input)) do
RnaTranscription.ListComprehensionCase.to_rna(input)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()
Roman Numerals
https://exercism.org/tracks/elixir/exercises/roman-numerals
defmodule RomanNumerals do
@roman_numerals [
{1000, "M"},
{900, "CM"},
{500, "D"},
{400, "CD"},
{100, "C"},
{90, "XC"},
{50, "L"},
{40, "XL"},
{10, "X"},
{9, "IX"},
{5, "V"},
{4, "IV"},
{1, "I"}
]
@doc """
Convert the number to a roman number.
## Examples
iex> RomanNumerals.numeral(420)
"CDXX"
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) when number > 0 do
{part, letter} = hd(Enum.filter(@roman_numerals, fn {p, _} -> p <= number end))
letter <> numeral(number - part)
end
def numeral(_), do: ""
end
Roman Numerals: Tests
assert RomanNumerals.numeral(1) == "I"
assert RomanNumerals.numeral(2) == "II"
assert RomanNumerals.numeral(3) == "III"
assert RomanNumerals.numeral(4) == "IV"
assert RomanNumerals.numeral(5) == "V"
assert RomanNumerals.numeral(6) == "VI"
assert RomanNumerals.numeral(9) == "IX"
assert RomanNumerals.numeral(27) == "XXVII"
assert RomanNumerals.numeral(48) == "XLVIII"
assert RomanNumerals.numeral(59) == "LIX"
assert RomanNumerals.numeral(93) == "XCIII"
assert RomanNumerals.numeral(141) == "CXLI"
assert RomanNumerals.numeral(163) == "CLXIII"
assert RomanNumerals.numeral(402) == "CDII"
assert RomanNumerals.numeral(575) == "DLXXV"
assert RomanNumerals.numeral(911) == "CMXI"
assert RomanNumerals.numeral(1024) == "MXXIV"
assert RomanNumerals.numeral(3000) == "MMM"
assert RomanNumerals.numeral(16) == "XVI"
assert RomanNumerals.numeral(66) == "LXVI"
assert RomanNumerals.numeral(166) == "CLXVI"
assert RomanNumerals.numeral(666) == "DCLXVI"
assert RomanNumerals.numeral(1666) == "MDCLXVI"
assert RomanNumerals.numeral(3001) == "MMMI"
assert RomanNumerals.numeral(3999) == "MMMCMXCIX"
:passed
Rotational Cipher
https://exercism.org/tracks/elixir/exercises/rotational-cipher
defmodule RotationalCipher.EnumMap do
@doc """
Given a plaintext and amount to shift by, return a rotated string.
## Examples
iex> RotationalCipher.EnumMap.rotate("Attack at dawn", 13)
"Nggnpx ng qnja"
"""
@spec rotate(text :: String.t(), shift :: integer) :: String.t()
def rotate(text, shift) do
text
|> to_charlist()
|> Enum.map(&translate(&1, shift))
|> to_string()
end
defp translate(letter, shift) when letter in ?a..?z,
do: rem(letter + shift - ?a, 26) + ?a
defp translate(letter, shift) when letter in ?A..?Z,
do: rem(letter + shift - ?A, 26) + ?A
defp translate(other_character, _), do: other_character
end
Rotational Cipher (Macro)
defmodule RotationalCipher.Macro do
@alphabet "abcdefghijklmnopqrstuvwxyz"
@alphabet_size String.length(@alphabet)
for shift <- 0..25 do
plain = String.split(@alphabet, "", trim: true)
cipher =
@alphabet
|> Kernel.<>(@alphabet)
|> String.split("", trim: true)
|> Enum.drop(shift)
|> Enum.take(@alphabet_size)
for {plain_letter, cipher_letter} <- Enum.zip(plain, cipher) do
defp translate(unquote(plain_letter), unquote(shift)),
do: unquote(cipher_letter)
defp translate(unquote(plain_letter |> String.upcase()), unquote(shift)),
do: unquote(cipher_letter |> String.upcase())
end
end
defp translate(other_character, _), do: other_character
@doc """
Given a plaintext and amount to shift by, return a rotated string.
## Examples
iex> RotationalCipher.Macro.rotate("Attack at dawn", 13)
"Nggnpx ng qnja"
"""
@spec rotate(text :: String.t(), shift :: integer) :: String.t()
def rotate(text, shift) do
text
|> String.split("", trim: true)
|> Enum.map_join(&translate(&1, shift))
end
end
Rotational Cipher (List Comprehension)
defmodule RotationalCipher.ListComprehension do
@doc """
Given a plaintext and amount to shift by, return a rotated string.
## Examples
iex> RotationalCipher.ListComprehension.rotate("Attack at dawn", 13)
"Nggnpx ng qnja"
"""
@spec rotate(text :: String.t(), shift :: integer) :: String.t()
def rotate(text, shift) do
for char <- to_charlist(text), into: [] do
cond do
char >= ?a and char <= ?z ->
char = char + shift
if char > ?z do
?a + (char - ?z) - 1
else
char
end
char >= ?A and char <= ?Z ->
char = char + shift
if char > ?Z do
?A + (char - ?Z) - 1
else
char
end
true ->
char
end
end
|> to_string()
end
end
Rotational Cipher: Tests
modules = [
RotationalCipher.EnumMap,
RotationalCipher.Macro,
RotationalCipher.ListComprehension
]
for module <- modules do
plaintext = "a"
shift = 1
assert module.rotate(plaintext, shift) == "b"
plaintext = "a"
shift = 26
assert module.rotate(plaintext, shift) == "a"
plaintext = "a"
shift = 0
assert module.rotate(plaintext, shift) == "a"
plaintext = "m"
shift = 13
assert module.rotate(plaintext, shift) == "z"
plaintext = "n"
shift = 13
assert module.rotate(plaintext, shift) == "a"
plaintext = "OMG"
shift = 5
assert module.rotate(plaintext, shift) == "TRL"
plaintext = "O M G"
shift = 5
assert module.rotate(plaintext, shift) == "T R L"
plaintext = "Testing 1 2 3 testing"
shift = 4
assert module.rotate(plaintext, shift) == "Xiwxmrk 1 2 3 xiwxmrk"
plaintext = "Let's eat, Grandma!"
shift = 21
assert module.rotate(plaintext, shift) == "Gzo'n zvo, Bmviyhv!"
plaintext = "The quick brown fox jumps over the lazy dog."
shift = 13
assert module.rotate(plaintext, shift) ==
"Gur dhvpx oebja sbk whzcf bire gur ynml qbt."
end
:passed
Rotational Cipher: Benchmark
{:module, name, _binary, _bindings} =
defmodule RotationalCipher.Benchmark do
use BencheeDsl.Benchmark
config(
warmup: 1,
time: 3,
memory_time: 1,
reduction_time: 1,
pre_check: true,
print: [configuration: false]
)
inputs(%{
"Small" => "Attack at dawn",
"Bigger" =>
"Attack at dawn Attack at dawn Attack at dawn Attack at dawn Attack at dawn Attack at dawn"
})
job(enum_map(input)) do
RotationalCipher.EnumMap.rotate(input, 13)
end
job(macro(input)) do
RotationalCipher.Macro.rotate(input, 13)
end
job(list_comprehension(input)) do
RotationalCipher.ListComprehension.rotate(input, 13)
end
end
BencheeDsl.Livebook.benchee_config() |> name.run() |> BencheeDsl.Livebook.render()