Browse | Submit A New Snippet | Create A Package

 

Roman Numerals

Type:
Class
Category:
Math Functions
License:
GNU General Public License
Language:
Ruby
 
Description:
Completely pointless class that converts integers to Roman numerals and vice versa, also extends the integer class with a "to_roman" method. Using method missing as syntactic sugar for converting numerals to numbers.

Versions Of This Snippet::

Snippet ID Download Version Date Posted Author Delete
1861.02006-11-18 22:11

Download a raw-text version of this code by clicking on "Download Version"

 


Latest Snippet Version: :1.0

##########################################################
# Roman Numeral converter -- By Sam Kellett
#
# Examples:
# r = Roman.new
# r.to_int "viii" # 8
# r.to_int "mcmxcix" # 1999
# r.viii # 8
# r.mcmxcix # 1999
# r.to_roman 2550 # "mmdl"
# r.to_roman 9 # "ix"
# 2550.to_roman # "mmdl"
#
# Supports: 
#  Rules regarding Roman numerals often state that a symbol representing 10x may not
#  precede any symbol larger than 10x+1. For example, C cannot be preceded by I or V, 
#  only by X (or, of course, by a symbol representing a value equal to or larger than 
#  C). Thus, one should represent the number "ninety-nine" as XCIX, not as the 
#  "shortcut" IC.
#    -- Wikipedia (http://en.wikipedia.org/wiki/Roman_numerals#XCIX_or_IC.3F)
#
#
# To-do:
#  - Basic arithmetic with roman numerals
#  - Integration with the Time and Date objects

class Roman
	VALUES = ["i", "v", "x", "l", "c", "d", "m"]
	NUMERAL = {
		"i" => 1,
		"v" => 5,
		"x" => 10,
		"l" => 50,
		"c" => 100,
		"d" => 500,
		"m" => 1000
	}
	SHORTCUTS = {
		"cm" => "dcccc",
		"ld" => "ccccl",
		"xc" => "lxxxx",
		"vl" => "xxxxv",
		"ix" => "viiii",
		"iv" => "iiii"
	}
	def to_int(str)
		total = 0
		str.downcase!
		return unless str =~ /^[mcdlxvi]+$/
		
		str.gsub! Regexp.compile(SHORTCUTS.keys.join("|")) do
			SHORTCUTS[$&]
		end

		str.split("").each do |char|
			total += NUMERAL[char]
		end
		total
	end
	
	def to_roman(int)
		numbers = reverse_hash NUMERAL
		longcuts = reverse_hash SHORTCUTS
		#return "bad" unless int =~ /^[0-9]+$/
		roman = ""
		
		mknum = []
		remainder = int.to_i
		numbers.keys.sort.reverse.each do |num|
			tmp = remainder.divmod num
			mknum.push tmp[0]
			remainder = tmp[1]
		end
		values = VALUES.reverse
		mknum.each_with_index do |num, i|
			roman << values[i]*num
		end
		roman.gsub! Regexp.compile(longcuts.keys.join("|")) do
			longcuts[$&]
		end
		roman
	end
	
	private
		def reverse_hash(hsh)
			tmp = {}
			hsh.each do |key, value|
				tmp[value] = key
			end
			tmp
		end
		def method_missing(method_id)
			to_int(method_id.id2name)
		end
end

class Integer
	def to_roman
		r = Roman.new
		r.to_roman(self)
	end
end
		

Submit a new version

You can submit a new version of this snippet if you have modified it and you feel it is appropriate to share with others..