Last active
August 29, 2015 14:25
-
-
Save riverrun/7374da9721d4e7f8bb75 to your computer and use it in GitHub Desktop.
Elixir convert to and from roman numerals
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Roman do | |
@num_to_roman %{1 => "I", 2 => "II", 3 => "III", 4 => "IV", 5 => "V", | |
6 => "VI", 7 => "VII", 8 => "VIII", 9 => "IX", 10 => "X", 20 => "XX", | |
30 => "XXX", 40 => "XL", 50 => "L", 60 => "LX", 70 => "LXX", 80 => "LXXX", | |
90 => "XC", 100 => "C", 200 => "CC", 300 => "CCC", 400 => "CD", 500 => "D", | |
600 => "DC", 700 => "DCC", 800 => "DCCC", 900 => "CM", 1000 => "M", | |
2000 => "MM", 3000 => "MMM"} | |
@roman_to_num %{"I" => 1, "II" => 2, "III" => 3, "IV" => 4, "V" => 5, | |
"VI" => 6, "VII" => 7, "VIII" => 8, "IX" => 9, "X" => 10, "XX" => 20, | |
"XXX" => 30, "XL" => 40, "L" => 50, "LX" => 60, "LXX" => 70, "LXXX" => 80, | |
"XC" => 90, "C" => 100, "CC" => 200, "CCC" => 300, "CD" => 400, "D" => 500, | |
"DC" => 600, "DCC" => 700, "DCCC" => 800, "CM" => 900, "M" => 1000, | |
"MM" => 2000, "MMM" => 3000} | |
@roman Map.keys @roman_to_num | |
@invalid ["IIII", "VV", "XXXX", "LL", "CCCC", "DD", "MMMM"] | |
def translate(input) when is_integer(input) do | |
find_num(input) |> Enum.map_join(&Map.get(@num_to_roman, &1)) | |
end | |
def translate(input) do | |
if :binary.match(input, @invalid) == :nomatch and | |
Regex.match?(~r/^["I", "V", "X", "L", "C", "D", "M"]*$/, input) do | |
find_roman(input) | |
else | |
raise ArgumentError, message: "Erratum. Numerus invalidus est." | |
end | |
end | |
defp find_num(number) when number > 3999 do | |
raise ArgumentError, message: "Erratum. Numerus nimis magni est." | |
end | |
defp find_num(number) when number > 999 do | |
[1000 * div(number, 1000), 100 * div(rem(number, 1000), 100), | |
10 * div(rem(number, 100), 10), rem(number, 10)] | |
end | |
defp find_num(number) when number > 99 do | |
[100 * div(number, 100), 10 * div(rem(number, 100), 10), rem(number, 10)] | |
end | |
defp find_num(number) when number > 9 do | |
[10 * div(number, 10), rem(number, 10)] | |
end | |
defp find_num(number) when number > 0, do: [number] | |
defp find_num(_) do | |
raise ArgumentError, message: "Erratum. Numerus invalidus est." | |
end | |
defp find_roman(input) do | |
:binary.matches(input, @roman) | |
|> Enum.map(&Map.get(@roman_to_num, :binary.part(input, &1))) | |
|> Enum.sum | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Code.load_file("roman.exs") | |
ExUnit.start | |
defmodule RomanTest do | |
use ExUnit.Case, async: true | |
@roman_low ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"] | |
@roman_mid ["XXVII", "XLVIII", "LIX", "XCIII", "CXLI", "CLXIII"] | |
@roman_hi ["CDII", "DLXXV", "CMXI", "MXXIV", "MMM"] | |
test "1 to 10" do | |
assert Enum.map(1..10, &Roman.translate(&1)) == @roman_low | |
end | |
test "1 to 10 roman to number" do | |
assert Enum.map(@roman_low, &Roman.translate(&1)) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | |
end | |
test "27, 48, 59, 93, 141, 163" do | |
assert Enum.map([27, 48, 59, 93, 141, 163], &Roman.translate(&1)) == @roman_mid | |
end | |
test "27, 48, 59, 93, 141, 163 roman to number" do | |
assert Enum.map(@roman_mid, &Roman.translate(&1)) == [27, 48, 59, 93, 141, 163] | |
end | |
test "402, 575, 911, 1024, 3000" do | |
assert Enum.map([402, 575, 911, 1024, 3000], &Roman.translate(&1)) == @roman_hi | |
end | |
test "402, 575, 911, 1024, 3000 roman to number" do | |
assert Enum.map(@roman_hi, &Roman.translate(&1)) == [402, 575, 911, 1024, 3000] | |
end | |
test "too big number" do | |
assert_raise ArgumentError, "Erratum. Numerus nimis magni est.", fn -> | |
Roman.translate(4500) | |
end | |
end | |
test "invalid number" do | |
assert_raise ArgumentError, "Erratum. Numerus invalidus est.", fn -> | |
Roman.translate(-10) | |
end | |
end | |
test "invalid roman numeral" do | |
assert_raise ArgumentError, "Erratum. Numerus invalidus est.", fn -> | |
Roman.translate("MHXCI") | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment