Roman of '123' is : CXXIII
Roman of '17' is : XVII
Roman of '3259' is : MMMCCLIX
Roman of '3999' is : MMMCMXCIX
Decimal of 'XVIII' is : 18
Decimal of 'MMMDCCCLXXXVIII' is : 3888
Decimal of 'MMMCMXCIX' is : 3999
Decimal of 'DLV' is : 555
java.lang.IllegalArgumentException: number must range from 1 - 3999. Input : '4000'
java.lang.IllegalArgumentException: number must range from 1 - 3999. Input : '-1'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'LLV'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'IL'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'XLXX'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'LXXXX'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'viic'
java.lang.IllegalArgumentException: Invalid Roman Character in String : 't' Input : 'tiic'
Algo positive test case successful
Process finished with exit code 0
Last active
November 1, 2017 20:00
-
-
Save anandabhishek73/c4b7d661aa463806bc6dd381f58ec8b8 to your computer and use it in GitHub Desktop.
Java utility to convert Roman and Decimal numbers to each other. Also has a utility function to check validity of a Roman numeral. Includes positive and negative use-cases
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
import java.util.HashMap; | |
import java.util.TreeMap; | |
/** | |
* Created by Abhishek Anand on 01-Nov-17. | |
*/ | |
public class RomanNumeral { | |
private static final int D2R_METHOD_FLAG = 2; | |
private static final char[][] D2R_MAP = new char[][]{ | |
{'I', 'V'}, | |
{'X', 'L'}, | |
{'C', 'D'}, | |
{'M', '?'} | |
}; | |
private final static TreeMap<Integer, String> D2R_MAP2 = new TreeMap<Integer, String>(); | |
static { | |
D2R_MAP2.put(1000, "M"); | |
D2R_MAP2.put(900, "CM"); | |
D2R_MAP2.put(500, "D"); | |
D2R_MAP2.put(400, "CD"); | |
D2R_MAP2.put(100, "C"); | |
D2R_MAP2.put(90, "XC"); | |
D2R_MAP2.put(50, "L"); | |
D2R_MAP2.put(40, "XL"); | |
D2R_MAP2.put(10, "X"); | |
D2R_MAP2.put(9, "IX"); | |
D2R_MAP2.put(5, "V"); | |
D2R_MAP2.put(4, "IV"); | |
D2R_MAP2.put(1, "I"); | |
} | |
private static final HashMap<Character, Integer> R2D_MAP = new HashMap<Character, Integer>(); | |
static { | |
R2D_MAP.put('I', 1); | |
R2D_MAP.put('V', 5); | |
R2D_MAP.put('X', 10); | |
R2D_MAP.put('L', 50); | |
R2D_MAP.put('C', 100); | |
R2D_MAP.put('D', 500); | |
R2D_MAP.put('M', 1000); | |
} | |
public static String convertDecimalToRoman(String number) { | |
int no = Integer.getInteger(number); | |
return convertDecimalToRoman(no); | |
} | |
public static String convertDecimalToRoman(int number) { | |
if (number >= 4000 || number <= 0) | |
throw new IllegalArgumentException("number must range from 1 - 3999."); | |
StringBuilder roman = new StringBuilder(); | |
int remaining = number; | |
switch (D2R_METHOD_FLAG) { | |
case 1: // Hash map algorithm | |
int digit, i = 0; // used to reference D2R_MAP array | |
while (remaining > 0) { | |
digit = remaining % 10; | |
switch (digit % 5) { // for digits 1,2,3,6,7,8 | |
case 3: | |
roman.insert(0, D2R_MAP[i][0]); | |
case 2: | |
roman.insert(0, D2R_MAP[i][0]); | |
case 1: | |
roman.insert(0, D2R_MAP[i][0]); | |
default: | |
break; | |
} | |
switch ((digit + 1) / 5) { | |
case 1: | |
roman.insert(0, D2R_MAP[i][1]); | |
break; | |
case 2: | |
roman.insert(0, D2R_MAP[i + 1][0]); | |
break; | |
default: | |
break; | |
} | |
if (digit % 5 == 4) { | |
roman.insert(0, D2R_MAP[i][0]); | |
} | |
remaining /= 10; // remove processed digit from decimal no | |
i++; | |
} | |
break; | |
case 2: // Tree map algorithm | |
int key ; | |
while( remaining != 0) { | |
key = D2R_MAP2.floorKey(remaining); | |
roman.append(D2R_MAP2.get(key)); | |
remaining -= key; | |
} | |
break; | |
} | |
return roman.toString(); | |
} | |
/** | |
* Converts a valid Roman numeral string to its corresponding Decimal representation. | |
* | |
* @param roman Roman Numeral String. | |
* @return Corresponding | |
*/ | |
public static int convertRomanToDecimal(String roman) { | |
String upperRoman = roman.toUpperCase(); | |
for (int i = 0; i < upperRoman.length(); i++) { | |
if (R2D_MAP.get(upperRoman.charAt(i)) == null) { | |
throw new IllegalArgumentException("Invalid Roman Character in String : '" + roman.charAt(i) + "'"); | |
} | |
} | |
int decimal = convertRomanToDecimalUnchecked(upperRoman); | |
String validRoman = convertDecimalToRoman(decimal); | |
if (!validRoman.equals(upperRoman)) | |
throw new IllegalArgumentException("Invalid Roman Numeral"); | |
return decimal; | |
} | |
private static int convertRomanToDecimalUnchecked(String roman) { | |
int decimal = 0; | |
int max = 1; | |
int currentVal; | |
for (int i = roman.length() - 1; i >= 0; i--) { | |
currentVal = R2D_MAP.get(roman.charAt(i)); | |
if (currentVal < max) { // subtractive case | |
decimal -= currentVal; | |
} else { // additive case | |
max = currentVal; | |
decimal += currentVal; | |
} | |
} | |
return decimal; | |
} | |
public static boolean isValidRoman(String roman) { | |
try { | |
convertDecimalToRoman(convertRomanToDecimalUnchecked(roman)); | |
return true; | |
} catch (IllegalArgumentException e) { | |
return false; | |
} | |
/* | |
int max = 0; | |
char current; | |
int currentVal; | |
boolean wasLastSubtracted = false; | |
String rm = roman.toUpperCase(); | |
int groupingCount = 0; | |
for (int i = rm.length() - 1; i >= 0; i--) { | |
current = rm.charAt(i); | |
currentVal = R2D_MAP.get(current)[VAL]; | |
if (currentVal < max) { // subtractive case | |
if (wasLastSubtracted) // can have at max 1 subtraction numeral in left of bigger numeral. Ex= IX, IV, XL | |
return false; | |
if (max / currentVal > 10) // Example - IL or IC is not a valid roman numeral | |
return false; | |
wasLastSubtracted = true; | |
groupingCount = 0; // subtractive roman numerals cannot be clubbed, therefore 0 | |
} else { // additive case | |
if (currentVal == max) { // same as previous | |
groupingCount++; | |
if (groupingCount > R2D_MAP.get(current)[GROUP]) { | |
return false; | |
} | |
} else { // bigger than previous | |
groupingCount = 1; // new non-subtractive roman numeral in series | |
} | |
max = currentVal; | |
wasLastSubtracted = false; | |
} | |
} | |
return true; | |
*/ | |
} | |
private static void testingAlgo() throws Exception { | |
for (int i = 1; i < 4000; i++) { | |
if (i != convertRomanToDecimal(convertDecimalToRoman(i))) { | |
throw new Exception("Algorithm failed for decimal : " + i); | |
} | |
} | |
} | |
public static void main(String srgs[]) { | |
int no; | |
String roman; | |
no = 123; | |
System.out.println("Roman of '" + no + "' is : " + convertDecimalToRoman(no)); | |
no = 17; | |
System.out.println("Roman of '" + no + "' is : " + convertDecimalToRoman(no)); | |
no = 3259; | |
System.out.println("Roman of '" + no + "' is : " + convertDecimalToRoman(no)); | |
no = 3999; | |
System.out.println("Roman of '" + no + "' is : " + convertDecimalToRoman(no)); | |
roman = "XVIII"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
roman = "MMMDCCCLXXXVIII"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
roman = "MMMCMXCIX"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
roman = "DLV"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
// Negative test cases | |
try { | |
no = 4000; | |
System.out.println("Roman of '" + no + "' is : " + convertDecimalToRoman(no)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + no + "'"); | |
} | |
try { | |
no = -1; | |
System.out.println("Roman of '" + no + "' is : " + convertDecimalToRoman(no)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + no + "'"); | |
} | |
try { | |
roman = "LLV"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + roman + "'"); | |
} | |
try { | |
roman = "IL"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + roman + "'"); | |
} | |
try { | |
roman = "XLXX"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + roman + "'"); | |
} | |
try { | |
roman = "LXXXX"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + roman + "'"); | |
} | |
try { | |
roman = "viic"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + roman + "'"); | |
} | |
try { | |
roman = "tiic"; | |
System.out.println("Decimal of '" + roman + "' is : " + convertRomanToDecimal(roman)); | |
} catch (IllegalArgumentException e) { | |
System.out.println(e + " Input : '" + roman + "'"); | |
} | |
try { | |
testingAlgo(); | |
System.out.println("Algo positive test case successful"); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
Updated console output:
Roman of '123' is : CXXIII
Roman of '17' is : XVII
Roman of '3259' is : MMMCCLIX
Roman of '3999' is : MMMCMXCIX
Roman of 'XVIII' is : 18
Roman of 'MMMDCCCLXXXVIII' is : 3888
Roman of 'MMMCMXCIX' is : 3999
Roman of 'DLV' is : 555
java.lang.IllegalArgumentException: number must range from 1 - 3999. Input : '4000'
java.lang.IllegalArgumentException: number must range from 1 - 3999. Input : '-1'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'LLV'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'IL'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'XLXX'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'LXXXX'
java.lang.IllegalArgumentException: Invalid Roman Numeral Input : 'viic'
java.lang.IllegalArgumentException: Invalid Roman Character in String : 't' Input : 'tiic'
Algo positive test case successful
Process finished with exit code 0
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Runtime console output of following program is -