-
-
Save monkstone/b35b26384976c6e474ebf5a33d7abab7 to your computer and use it in GitHub Desktop.
roman numerals after Sandi Metz
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
# frozen_string_literal: false | |
# module that refines String and Numeric | |
module Roman | |
refine Fixnum do | |
def to_roman | |
RomanNumerals.new.to_s(self) | |
end | |
end | |
refine String do | |
def to_subtractive_roman | |
RomanNumeralConversion.new(self).to_subtractive | |
end | |
def to_additive_roman | |
RomanNumeralConversion.new(self).to_additive | |
end | |
alias_method :to_i_orig, :to_i | |
def to_i(type = nil) | |
type == :roman ? RomanNumerals.new.to_i(self) : to_i_orig | |
end | |
end | |
end | |
using Roman | |
# to Roman numeral class | |
class RomanNumerals | |
KEY = [1_000, 500, 100, 50, 10, 5, 1] | |
ROMAN = %w(M D C L X V I) | |
ROMAN_NUMERALS = KEY.zip(ROMAN).to_h | |
def to_s(num) | |
to_roman(num) | |
end | |
def to_i(str) | |
to_number(str) | |
end | |
def to_roman(number) | |
result = '' | |
ROMAN_NUMERALS.keys.reduce(number) do |to_be_converted, base_10_value| | |
num_chars_needed, remainder = to_be_converted.divmod(base_10_value) | |
result << ROMAN_NUMERALS[base_10_value] * num_chars_needed | |
remainder | |
end | |
result.to_subtractive_roman | |
end | |
def to_number(roman) | |
roman.to_additive_roman.chars.reduce(0) do |total, roman_letter| | |
total + ROMAN_NUMERALS.invert[roman_letter] | |
end | |
end | |
end | |
# from Roman numeral conversion class | |
class RomanNumeralConversion | |
LONG = %w(DCCCC CCCC LXXXX XXXX VIIII IIII) | |
SHORT = %w(CM CD XC XL IX IV) | |
LONG_TO_SHORT_MAP = LONG.zip(SHORT).to_h | |
attr_reader :roman | |
def initialize(roman) | |
@roman = roman | |
end | |
def to_additive | |
convert(LONG_TO_SHORT_MAP.invert) | |
end | |
def to_subtractive | |
convert(LONG_TO_SHORT_MAP) | |
end | |
def convert(map) | |
map.keys.reduce(roman) do |converted_roman, alternate_form| | |
converted_roman.gsub(/#{alternate_form}/, map[alternate_form]) | |
end | |
end | |
end | |
# For the Sandi Metz original sign up to her 99 bottle mailing list | |
# http://sandimetz.us3.list-manage.com/track/click?u=1090565ccff48ac602d0a84b4&id=bd938590a4&e=8144e160c6 |
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
# frozen_string_literal: false | |
#!/usr/bin/env ruby | |
gem 'minitest', '>= 5.0.0' | |
require 'minitest/autorun' | |
require 'minitest/pride' | |
require_relative 'roman_numerals' | |
using Roman | |
# the test | |
class RomanNumeralsTest < Minitest::Test | |
attr_reader :integers | |
def setup | |
integer = [1, 4, 7, 11, 19, 49, 54, 99, 899, 999] | |
roman = %w(I IV VII XI XIX XLIX LIV XCIX DCCCXCIX CMXCIX) | |
@integers = integer.zip(roman) | |
end | |
def test_to_roman | |
integers.each do |pair| | |
assert_equal pair[1], pair[0].to_roman | |
end | |
end | |
def test_to_arabic | |
integers.each do |pair| | |
assert_equal pair[0], pair[1].to_i(:roman) | |
end | |
end | |
end | |
# the long to short test | |
class RomanNumeralConversionTest < Minitest::Test | |
def test_2 | |
assert_equal 'II', RomanNumeralConversion.new('II').to_subtractive | |
assert_equal 'II', RomanNumeralConversion.new('II').to_additive | |
end | |
def test_4 | |
assert_equal 'IV', RomanNumeralConversion.new('IIII').to_subtractive | |
assert_equal 'IIII', RomanNumeralConversion.new('IV').to_additive | |
end | |
def test_9 | |
assert_equal 'IX', RomanNumeralConversion.new('VIIII').to_subtractive | |
assert_equal 'VIIII', RomanNumeralConversion.new('IX').to_additive | |
end | |
def test_49 | |
assert_equal 'XLIX', RomanNumeralConversion.new('XXXXVIIII').to_subtractive | |
assert_equal 'XXXXVIIII', RomanNumeralConversion.new('XLIX').to_additive | |
end | |
def test_99 | |
assert_equal 'XCIX', RomanNumeralConversion.new('LXXXXVIIII').to_subtractive | |
assert_equal 'LXXXXVIIII', RomanNumeralConversion.new('XCIX').to_additive | |
end | |
def test_449 | |
assert_equal 'CDXLIX', RomanNumeralConversion.new('CCCCXXXXVIIII').to_subtractive | |
assert_equal 'CCCCXXXXVIIII', RomanNumeralConversion.new('CDXLIX').to_additive | |
end | |
def test_999 | |
assert_equal 'XCIX', RomanNumeralConversion.new('LXXXXVIIII').to_subtractive | |
assert_equal 'LXXXXVIIII', RomanNumeralConversion.new('XCIX').to_additive | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment