Skip to content

Instantly share code, notes, and snippets.

@anandabhishek73
Last active November 1, 2017 20:00
Show Gist options
  • Save anandabhishek73/c4b7d661aa463806bc6dd381f58ec8b8 to your computer and use it in GitHub Desktop.
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

Console output:

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
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();
}
}
}
@anandabhishek73
Copy link
Author

Runtime console output of following program is -

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'
Algo positive test case successful

Process finished with exit code 0

@anandabhishek73
Copy link
Author

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