And, who in their right mind would bother to write code to deal with it, since it's already been done about 5 million times? Well, me for one. OK, yes, I am a turbodork. I can no longer deny it. Here's the code:
/**
* Represent Roman numerals between 1 and 4999 inclusive.
*/
public class Roman implements Comparable {
private int value;
private String roman;
private final static String[][] digits = {
{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
{"", "M", "MM", "MMM", "MMMM"}
};
private final static Pattern regex = Pattern.compile("M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})");
public Roman(int n) {
value = n;
roman = intToRoman(n);
}
public Roman(String string) {
if (isRoman(string)) {
value = romanToInt(string);
roman = string;
}
else
throw new RuntimeException("\""+string+"\" is not a valid roman numeral.");
}
public String toString() {
return roman;
}
public int toInt() {
return value;
}
/**
* Test whether the input string is a valid roman numeral (between 1 and 4999).
*/
public static boolean isRoman(String s) {
return s!=null && !"".equals(s) && regex.matcher(s).matches();
}
/**
* Convert an integer between 1 and 4999 (inclusive) to a Roman numeral.
*/
public static String intToRoman(int n) {
if (n>=5000 || n <= 0)
throw new RuntimeException("Roman equivalents exist only for numbers between 1 and 4999.");
StringBuilder sb = new StringBuilder(16);
int place = 0;
while (n>0) {
int qoutient = n/10;
int remainder = n - (qoutient * 10);
sb.insert(0, digits[place][remainder]);
place++;
n = qoutient;
}
return sb.toString();
}
/**
* Convert a roman numeral to an integer. This method isn't strict about
* the rules of what makes a valid roman numeral. In particular, it will
* happily convert IIIIIIIIII to 10 or IL to 49 even though neither of
* these are valid roman numerals. To check whether a String is a valid
* roman numeral use isRoman().
*/
public static int romanToInt(String roman) {
int value = 0;
int prev = 1000;
for (int i=0; i prev) {
value -= (prev << 1);
}
value += v;
prev = v;
}
return value;
}
/**
* @return return the integer value of a single roman character.
* @throws RuntimeException if the character isn't a roman numeral.
*/
private static int charToValue(char c) {
if (c=='I') return 1;
if (c=='V') return 5;
if (c=='X') return 10;
if (c=='L') return 50;
if (c=='C') return 100;
if (c=='D') return 500;
if (c=='M') return 1000;
throw new RuntimeException("Illegal character in roman numeral: " + c);
}
public int compareTo(Roman other) {
return this.value - other.value;
}
@Override
public int hashCode() {
return value;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Roman other = (Roman) obj;
return this.value == other.value;
}
}