Skip to content

Instantly share code, notes, and snippets.

@bil-bas
Created June 12, 2011 14:12
Show Gist options
  • Save bil-bas/1021586 to your computer and use it in GitHub Desktop.
Save bil-bas/1021586 to your computer and use it in GitHub Desktop.
Spatzerny roman to int and int to roman
class Integer
INT_TO_ROMAN = {
1000 => 'M',
500 => 'D',
100 => 'C',
50 => 'L',
10 => 'X',
5 => 'V',
1 => 'I',
}
public
def to_roman
str = ""
mod = roman_digit(str, self, 1000, 100)
mod = roman_digit(str, mod, 500, 100)
mod = roman_digit(str, mod, 100, 10)
mod = roman_digit(str, mod, 50, 10)
mod = roman_digit(str, mod, 10, 1)
mod = roman_digit(str, mod, 5, 1)
roman_digit(str, mod, 1)
str
end
protected
def roman_digit(str, n, value, sub_value = nil)
div, mod = n.divmod value
str << INT_TO_ROMAN[value] * div
# Add MC or IX or whatever.
if sub_value
div, mod = mod.divmod(value - sub_value)
str << "#{INT_TO_ROMAN[sub_value]}#{INT_TO_ROMAN[value]}" * div
end
mod
end
end
class String
# Invert the conversion above
ROMAN_TO_INT = {}
Integer::INT_TO_ROMAN.each_pair {|value, letter| ROMAN_TO_INT[letter] = value }
def to_i(base = 10)
if base == :roman
total = 0
strip.each_char.with_index do |letter, i|
value = ROMAN_TO_INT[letter.upcase] || raise("Character '#{letter}' not valid in a Roman numeral!")
next_value = ROMAN_TO_INT[self[i + 1]] || 0
total += ((next_value > value) ? -value : +value)
end
total
else
super(base)
end
end
end
puts 1989.to_roman # => MCMLXXXIX
puts 2004.to_roman # => MMIV
puts 2012.to_roman # => MMXII
puts (1..20).map(&:to_roman)
puts "III".to_i(:roman) # => 3
puts "XCIX".to_i(:roman) # => 99
puts "IIJ".to_i(:roman) # => error J not a roman numeral
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment