Conversion class:
package com.russellglasser.romannumeral;
Unit test class:import java.util.HashMap; import java.util.Map; class RomanNumeralConverter { private final String[] romanDigits; private final Long[] arabicDigits; private final MaptoArabicTable; private final Map toRomanTable; RomanNumeralConverter() { romanDigits = new String[]{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; arabicDigits = new Long[]{1000L, 900L, 500L, 400L, 100L, 90L, 50L, 40L, 10L, 9L, 5L, 4L, 1L}; toArabicTable = new HashMap<>(); toRomanTable = new HashMap<>(); for (int i = 0; i < romanDigits.length; i++) { toArabicTable.put(romanDigits[i], arabicDigits[i]); toRomanTable.put(arabicDigits[i], romanDigits[i]); } } String toRoman(Long arabic) { StringBuilder builder = new StringBuilder(); Long remaining = arabic; for (Long digit : arabicDigits) { while (remaining >= digit) { builder.append(toRomanTable.get(digit)); remaining -= digit; } } return builder.toString(); } Long toArabic(String roman) { String remaining = roman; Long result = 0L; for (String digit : romanDigits) { while (remaining.startsWith(digit)) { result += toArabicTable.get(digit); remaining = remaining.replaceFirst(digit, ""); } } return result; } }
package com.russellglasser.romannumeral; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class RomanNumeralConverterTest { private final RomanNumeralConverter converter = new RomanNumeralConverter(); @Test void toRoman1() { check(1L, "I"); } @Test void toRoman3() { check(3L, "III"); } @Test void toRoman4() { check(4L, "IV"); } @Test void toRoman8() { check(8L, "VIII"); } @Test void toRoman14() { check(14L, "XIV"); } @Test void toRoman18() { check(18L, "XVIII"); } @Test void toRoman1979() { check(1979L, "MCMLXXIX"); } @Test void toRoman2018() { check(2018L, "MMXVIII"); } @Test void toRoman9999() { check(9999L, "MMMMMMMMMCMXCIX"); } private void check(Long arabic, String roman) { assertEquals(roman, converter.toRoman(arabic)); assertEquals(arabic, converter.toArabic(roman)); } }
That is a nice solution where the subtractive notation is treated as a single token.
ReplyDeleteI wonder how the performance would differ using StringBuilder methods instead of String.sartsWith() and String.replaceFirst().
Things I should modify:
ReplyDelete1. Should use Integers instead of Longs, because let's face it, there is no conceivable situation where you want to convert more than a 32 bit number to a Roman numeral.
2. toRoman should throw an exception if it is passed a number <= 0
3. toArabic should throw an exception if it can't parse any more digits but the remaining string is not empty