Monday, April 20, 2009

Roman numbers in Java

Surely this has several implementations around, but... someone may still wants to copy-paste this. It only works up to 3999, as after that non-US-ASCII characters are needed. Trivial to extend it for that however... Public Domain:

private static final char[] UPPER_ROMAN_DIGITS = new char[] {
    'I', 'V',
    'X', 'L',
    'C', 'D',
    'M'
}; 

private static final char[] LOWER_ROMAN_DIGITS = new char[] {
    'i', 'v',
    'x', 'l',
    'c', 'd',
    'm'
}; 

/**
 * Converts a number to upper-case Roman number, like XVI; up to 3999.
 * @throws IllegalArgumentException if the number is not in the [1..3999]
 *    range.
 */
public static String toUpperRomanNumber(int n) {
    return toRomanNumber(n, UPPER_ROMAN_DIGITS);
}

/**
 * Converts a number to lower-case Roman number, like xvi; up to 3999.
 * @throws IllegalArgumentException if the number is not in the [1..3999]
 *    range.
 */
public static String toLowerRomanNumber(int n) {
    return toRomanNumber(n, LOWER_ROMAN_DIGITS);
}

private static String toRomanNumber(int n, char[] romanDigits) {
    // We fetch the decimal digits from right to left.
    // The res buffer will contain the Roman number *backwards*, and thus it
    // also will contain the Roman "digits" backwards, like 7 will be "IIV".
    // At the very end the buffer is reversed.
    
    if (n > 3999) {
        throw new IllegalArgumentException("toRomanNumber only supports "
                + "numbers in the [1..3999] range, but the number was "
                + n + ".");
    }
    
    StringBuilder res = new StringBuilder();
    int base = 0;
    while (n != 0) {
        int digit = n % 10;
        n /= 10;
        if (digit != 0) {
            switch (digit) {
            case 3:
                res.append(romanDigits[base]);
                // falls through
            case 2:
                res.append(romanDigits[base]);
                // falls through
            case 1:
                res.append(romanDigits[base]);
                break;
                
            case 4:
                res.append(romanDigits[base + 1])
                        .append(romanDigits[base]);
                break;
                
            case 8:
                res.append(romanDigits[base]);
                // falls through
            case 7:
                res.append(romanDigits[base]);
                // falls through
            case 6:
                res.append(romanDigits[base]);
                // falls through
            case 5:
                res.append(romanDigits[base + 1]);
                break;
                
            case 9: 
                res.append(romanDigits[base + 2]);
                res.append(romanDigits[base]);
                break;
                
            default:
                throw new BugException("Unexpected branch");
            }
        }
        base += 2;
    }
    return res.reverse().toString();
}

No comments:

Post a Comment