Last active
November 19, 2019 08:45
-
-
Save zeitnot/a289c6fe3c96f12d3e1f1c47d478b54c to your computer and use it in GitHub Desktop.
Ruby Roman numeral converter.
This file contains 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
require "test/unit" | |
InvalidRomanChars = Class.new(StandardError) | |
OutOfRange = Class.new(StandardError) | |
InvalidInteger = Class.new(StandardError) | |
class Roman | |
I,IV,V,IX,X,XL,L,XC,C,CD,D,CM,M = 1,4,5,9,10,40,50,90,100,400,500,900,1000 | |
LETTERS = %w(IV V IX I XL L XC X CD D CM C M) | |
def initialize(value) | |
@value = value | |
end | |
def to_roman | |
@value = @value.to_i | |
raise InvalidInteger.new('Given value should be integer and greater than zero.') if @value.zero? | |
raise OutOfRange.new('Given value is greater than 9999') if @value > 9999 | |
roman = '' | |
until @value.zero? | |
int = highest_integer | |
roman << correspondind_roman_letter(int).to_s | |
@value -= int | |
end | |
roman | |
end | |
def to_number | |
@value.strip! | |
return 0 if @value.size.zero? | |
@value.upcase! | |
sum = 0 | |
raise InvalidRomanChars if (@value.chars - LETTERS).any? | |
until @value.empty? | |
LETTERS.each do |letter| | |
if @value.start_with?(letter) | |
sum += self.class.const_get(letter) | |
@value[0...letter.size] = '' | |
break | |
else | |
end | |
end | |
end | |
sum | |
end | |
private | |
def highest_integer | |
digits = @value.digits | |
digit = digits.last | |
2.upto(digits.size) do | |
digit *= 10 | |
end | |
if correspondind_roman_letter(digit) | |
digit | |
else | |
found = false | |
digit.downto(1) do |i| | |
if correspondind_roman_letter(i) | |
digit = i | |
found = true | |
break | |
end | |
end | |
found ? digit : digit.digits.last | |
end | |
end | |
def correspondind_roman_letter(int) | |
self.class.constants.detect do |cons| | |
self.class.const_get(cons) == int | |
end | |
end | |
class << self | |
def to_number(roman_str) | |
Roman.new(roman_str).to_number | |
end | |
def to_roman(int) | |
Roman.new(int).to_roman | |
end | |
end | |
end | |
class TestRomanClass < Test::Unit::TestCase | |
def test_to_roman | |
assert_equal('I', Roman.to_roman(1)) | |
assert_equal('X', Roman.to_roman(10)) | |
assert_equal('XX', Roman.to_roman(20)) | |
assert_equal('XIX', Roman.to_roman(19)) | |
assert_equal('MCMXC', Roman.to_roman(1990)) | |
assert_equal('MMVIII', Roman.to_roman(2008)) | |
assert_equal('MDCLXVI', Roman.to_roman(1666)) | |
assert_equal('MMMMMMMI', Roman.to_roman(7001)) | |
end | |
def test_to_roman_with_string_integer | |
assert_equal('I', Roman.to_roman('1')) | |
assert_equal('X', Roman.to_roman('10')) | |
assert_equal('XX', Roman.to_roman('20')) | |
assert_equal('XIX', Roman.to_roman('19')) | |
assert_equal('MCMXC', Roman.to_roman('1990')) | |
assert_equal('MMVIII', Roman.to_roman('2008')) | |
assert_equal('MDCLXVI', Roman.to_roman('1666')) | |
assert_equal('MMMMMMMI', Roman.to_roman('7001')) | |
end | |
def test_to_roman_with_zero | |
assert_raise(InvalidInteger) do | |
Roman.to_roman('0') | |
end | |
end | |
def test_to_roman_with_string_litterals | |
assert_raise(InvalidInteger) do | |
Roman.to_roman('test') | |
end | |
end | |
def test_to_roman_out_of_range | |
assert_raise(OutOfRange) do | |
Roman.to_roman(10_000_00) | |
end | |
end | |
def test_to_number | |
assert_equal(0, Roman.to_number('')) | |
assert_equal(10, Roman.to_number('X')) | |
assert_equal(20, Roman.to_number('XX')) | |
assert_equal(19, Roman.to_number('XIX')) | |
assert_equal(1990, Roman.to_number('MCMXC')) | |
assert_equal(2008, Roman.to_number('MMVIII')) | |
assert_equal(1666, Roman.to_number('MDCLXVI')) | |
end | |
def test_to_number_when_invalid_roman_chars | |
assert_raise(InvalidRomanChars) do | |
Roman.to_number('invalid') | |
Roman.to_number('1234') | |
Roman.to_number('xxxa') | |
end | |
end | |
def test_to_numeber_with_lowercase | |
assert_equal(10, Roman.to_number('x')) | |
assert_equal(20, Roman.to_number('xx')) | |
assert_equal(19, Roman.to_number('xix')) | |
assert_equal(1990, Roman.to_number('mcmxc')) | |
assert_equal(2008, Roman.to_number('mmviii')) | |
assert_equal(1666, Roman.to_number('mdclxvi')) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ruby Roman numeral converter either from Roman letters to integer or from integers to Roman letters.
Usage: