Java  1.0
roman.java
Go to the documentation of this file.
1 import java.io.BufferedReader;
2 import java.io.IOException;
3 import java.io.InputStreamReader;
4 import java.util.HashMap;
5 import java.util.Map;
6 
29 public class roman {
30 
34  private static String[][] symbols = {
35  {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }, // units
36  {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }, // tens
37  {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }, // hundreds
38  {"", "M", "MM", "MMM", "(IV)", "(V)", "(VI)", "(VII)", "(VIII)", "(IX)"}, // thousands
39  {"", "(X)", "(XX)", "(XXX)", "(XL)", "(L)", "(LX)", "(LXX)", "(LXXX)", "(XC)"}, // ten thousands
40  {"", "(C)", "(CC)", "(CCC)", "(CD)", "(D)", "(DC)", "(DCC)", "(DCCC)", "(CM)"}, // hundred thousands
41  {"", "(M)", "(MM)", "(MMM)", "", "", "", "", "", ""} // millions
42  };
43 
47  private static Map<Character, Integer> d = null;
48  static {
49  // Roman numerals, as used today, are based on seven symbols:
50  d = new HashMap<Character, Integer>();
51  d.put ('M', 1000);
52  d.put ('D', 500);
53  d.put ('C', 100);
54  d.put ('L', 50);
55  d.put ('X', 10);
56  d.put ('V', 5);
57  d.put ('I', 1);
58  }
59 
63  private static int[] l = null;
64  static {
65  l = new int [100]; // initialized with zeroes by default
66 
67  // An array must be addressed by positive integers.
68  // In Java or C, a char is an integer in the ASCII table.
69  // Since 'Z' is 90, and we reserved 100
70  // positions in the list, we are fine.
71 
72  l['M'] = 1000;
73  l['D'] = 500;
74  l['C'] = 100;
75  l['L'] = 50;
76  l['X'] = 10;
77  l['V'] = 5;
78  l['I'] = 1;
79  }
80 
84  private static String[] rSymbols = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
88  private static int[] decimals = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
89 
93  private String rnum = null;
94 
98  private int inum = 0;
99 
104  public roman () {
105  }
106 
114  public roman ( String r ) throws ValueError {
115  String val=validateRoman(r);
116  if ( val.length() > 0 ) {
117  throw new ValueError(String.format ("Exception %s : Invalid roman \"%s\"", val, r));
118  }
119 
120  rnum = r;
121  inum = roman2int ( r );
122  }
123 
130  public roman ( int i ) {
131  inum = i;
132  rnum = int2roman ( i );
133  }
134 
141  public String toString ( ) {
142  return String.format( "%d in roman = %s", inum, rnum );
143  }
144 
151  public int toInt ( ) {
152  return inum;
153  }
154 
168  public static String int2roman ( int num ) {
169 
170  StringBuilder roman = new StringBuilder(); // Construct an empty string builder with 16 characters.
171  if ( (num >= 4000000) || (num < 1) )
172  roman.append("N/A");
173  else {
174  String cnum = baseConverter.itoa ( num, 10 ); // Convert num to String.
175  int scnum = cnum.length();
176  int strlen = scnum-1; // Find out how many digits are in
177  // the input number (magnitude).
178 /*
179  The loop is executed at most 7 times, and
180  it uses no division or multiplication.
181 */
182  int dig;
183  for (int i=0; i<scnum; i++) { // Loop forward (0,1,...,len-1).
184  dig = cnum.charAt(i) - '0'; // dig between [0,9].
185  roman.append (symbols[strlen][dig]); // Loop backward (len-1,len-2,len-3,...,0).
186  strlen -= 1;
187  }
188  }
189 
190  return roman.toString().replace ( ")(", "" );
191  }
192 
199 public static String int2Roman ( int num ) {
200  String roman = ""; // Empty string
201  if ((num >= 4000000) || (num < 1))
202  roman = "N/A";
203  else {
204  int ptr = 0;
205  int temp, excess;
206 
207  if (num >= 4000) { // The part of the number above 4000
208  excess = num/1000; // should be between parentheses.
209  if (excess%10 < 4) // What is less than 4000 should be
210  excess = excess/10*10; // outside the parentheses.
211  roman = "(" + int2Roman (excess) + ")"; // recursive call
212  num -= excess*1000;
213  }
214 
215  while (num > 0) { // Check to see if "num" still has any value left in it.
216  temp = num / decimals[ptr]; // See how many of the currently selected value can
217  // fit in the remaining input.
218  for ( int i = 0; i < temp; ++i )
219  roman += rSymbols[ptr]; // Append a number of Roman characters depending
220  // on the value of "temp".
221 
222  num -= temp * decimals[ptr]; // Subtract the value of the characters that were
223  // appended to output from the input.
224  ptr += 1; // Move the pointer to the next cell of the arrays.
225  }
226  }
227  return roman;
228 }
229 
233  public static class ValueError extends Exception {
234  private static final long serialVersionUID = 8236264803123918032L;
235 
236  public ValueError(String message){
237  super(message);
238  }
239  }
240 
248  public static int roman2int ( String roman ) throws ValueError {
249  int total = 0, pptr = 0, cptr = 0;
250  boolean m = false;
251  int len = roman.length();
252  char c;
253  String validc = "MDCLXVImdclxvi()";
254  for ( int i = 0; i < len; i++ ) {
255  c = roman.charAt ( len-i-1 ); // traverse the string backward
256  if ( validc.indexOf(c) >= 0 ) {
257  if ( c == ')' )
258  m = true;
259  else if ( c == '(' )
260  m = false;
261  else {
262  cptr = l[Character.toUpperCase(c)];
263  if ( m ) cptr *= 1000;
264  if (cptr < pptr)
265  total -= cptr; // IV = 5 - 1, IX = 10 - 1, XL = 50 - 10
266  else
267  total += cptr;
268  pptr = cptr;
269  }
270  }
271  else
272  throw new ValueError(String.format ("Invalid character \"%c\"", c));
273  }
274  return total;
275  }
276 
283  public static String validateRoman (String romano) {
284  romano = romano.toUpperCase();
285  String[] s = {romano,""};
286 
287  int position = romano.indexOf( "(" ); // find first occurrence
288  if ( position >= 0 ) {
289  int position2 = romano.indexOf( ")" );
290  s[0] = romano.substring (position+1, position2);
291  s[1] = romano.substring (position2+1, romano.length());
292  }
293 
294  for ( int j = 0; j < 2; ++j ) {
295  String r = s[j];
296  int lr = r.length();
297  if ( lr == 0 ) continue;
298  if ( "MDCLXVI".indexOf(r.charAt(lr-1)) < 0 ) return Character.toString(r.charAt(lr-1));
299  for ( int i=0; i < lr-1; ++i ) {
300  char c = r.charAt(i);
301  char c1 = r.charAt(i+1);
302 
303  if ( "MDCLXVI".indexOf(c) < 0) return Character.toString(c);
304  if ( (i+2) < lr ) {
305  char c2 = r.charAt(i+2);
306  if ( (i+3) < lr ) {
307  char c3 = r.charAt(i+3);
308  if ( c == c1 && c == c2 && c == c3 )
309  return "0: " + r.substring (i,i+4);
310  }
311  if ( d.get(c2)!=null && (d.get(c) < d.get(c2)) )
312  return "1: " + r.substring (i,i+3);
313  if ( "VLD".indexOf(c) >= 0 && c == c2 )
314  return "2: " + r.substring (i,i+3);
315  if ( c == 'I' && c2 == 'I' && c1 != 'I' )
316  return "3: " + r.substring (i,i+3);
317  if ( c == 'X' && c2 == 'X' && "LC".indexOf(c1) >= 0 )
318  return "4: " + r.substring (i,i+3);
319  }
320 
321  if ( d.get(c1)!=null && (d.get(c) < d.get(c1)) ) {
322  if ( "IXC".indexOf(c) < 0 )
323  return "5: " + r.substring (i,i+2);
324  if ( c == 'I' && "VX".indexOf(c1) < 0 )
325  return "6: " + r.substring (i,i+2);
326  if ( c == 'X' && "LC".indexOf(c1) < 0 )
327  return "7: " + r.substring (i,i+2);
328  if ( c == 'C' && "DM".indexOf(c1) < 0 )
329  return "8: " + r.substring (i,i+2);
330  }
331  if ( "VLD".indexOf(c) >= 0 && c == c1 )
332  return "9: " + r.substring (i,i+2);
333  }
334  }
335  return "";
336  }
337 
343  public static void main(String[] args) {
344  String jVersion = System.getProperty("java.version");
345  jVersion = jVersion.substring(0, 3);
346  Float f = Float.valueOf(jVersion);
347  if (f.floatValue() < (float)1.5) {
348  System.out.println(String.format("Java version %s is too low ....", jVersion));
349  System.exit(1);
350  }
351 
352  int r = 0;
353  System.out.print ( "Type an integer: " );
354  try {
355  r = Integer.parseInt(System.console().readLine());
356  }
357  // catch (NullPointerException | NumberFormatException e) {
358  catch (NumberFormatException e) {
359  System.out.println ( "A decimal number should have been typed\n" );
360  }
361  catch (NullPointerException e) {
362  // The reason this occurs is because eclipse runs your app as a background process
363  // and not as a top-level process with a system console.
364  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
365  try {
366  r = Integer.parseInt(bufferedReader.readLine());
367  } catch (IOException e2) {
368  System.out.println (e2);
369  System.out.println ( "A decimal number should have been typed\n" );
370  }
371  }
372  roman rm = new roman(r);
373  String rs = roman.int2Roman(r);
374  System.out.println ( rm );
375  System.out.println ( String.format( "%d in roman = %s", r, rs ) );
376 
377  try {
378  rm = new roman ( rs );
379  System.out.println ( String.format( "%s in decimal = %d", rs, rm.toInt() ) );
380  }
381  catch (ValueError e) {
382  System.out.println (e.getMessage());
383  }
384 
385  String invalidRoman[] = { rs, "XXXX", "ivLdi", "IIV", "LXL", "IVI", "XLX", "DM", "iM", "mxm", "CmC", "VV", "va", "xvibc" };
386 
387  for ( String s : invalidRoman ) {
388  String val = roman.validateRoman(s);
389  System.out.println ( String.format( "%s is %s%s", s, (val.length()==0)?"valid":"not valid -> ", val ) );
390  }
391 
392  // http://math.hws.edu/eck/cs124/javanotes3/c9/ex-9-3-answer.html
393  TextIO.putln("Enter a Roman numeral and I will convert it to an ordinary");
394  TextIO.putln("arabic integer. Enter an integer in the range 1 to 3,999,999");
395  TextIO.putln("and I will convert it to a Roman numeral. Press return when");
396  TextIO.putln("you want to quit.");
397  while (true) {
398  TextIO.putln();
399  TextIO.put("? ");
400  while (TextIO.peek() == ' ' || TextIO.peek() == '\t')
401  TextIO.getAnyChar();
402  if ( TextIO.peek() == '\n' )
403  break;
404  /*
405  If the first non-blank character is a digit, read an arabic
406  numeral and convert it to a Roman numeral. Otherwise, read
407  a Roman numeral and convert it to an arabic numeral.
408  */
409  if ( Character.isDigit(TextIO.peek()) ) {
410  int arabic = TextIO.getlnInt();
411  rm = new roman(arabic);
412  TextIO.putln ( rm.toString() );
413  }
414  else {
415  String str = TextIO.getln();
416  try {
417  rm = new roman (str);
418  TextIO.putln (str + " in decimal = " + rm.toInt());
419  }
420  catch (ValueError e) {
421  TextIO.putln (e.getMessage());
422  }
423  }
424  }
425  TextIO.putln("OK. Bye for now.");
426  }
427 }
roman.validateRoman
static String validateRoman(String romano)
Validates a roman numeral.
Definition: roman.java:283
baseConverter
Class for converting between different numerical bases.
Definition: baseConverter.java:64
roman.roman
roman(int i)
Constructor from an integer.
Definition: roman.java:130
roman
Class for manipulating roman numerals.
Definition: roman.java:29
TextIO.put
static void put(Object x)
Write a single value to the current output destination, using the default format and no extra spaces.
Definition: TextIO.java:361
roman.roman2int
static int roman2int(String roman)
Converts a roman numeral to decimal.
Definition: roman.java:248
roman.symbols
static String[][] symbols
table for mapping [decimal power, digit] to roman symbols
Definition: roman.java:34
roman.int2Roman
static String int2Roman(int num)
Converts an integer to roman.
Definition: roman.java:199
roman.l
static int[] l
list for mapping roman symbols to decimals
Definition: roman.java:63
roman.main
static void main(String[] args)
main function for testing.
Definition: roman.java:343
roman.ValueError
Exception class.
Definition: roman.java:233
roman.decimals
static int[] decimals
table for relating decimals to roman symbols: an ordered list of the decimal representation of roman ...
Definition: roman.java:88
TextIO
TextIO provides a set of static methods for reading and writing text.
Definition: TextIO.java:34
roman.inum
int inum
hold the integer representation of the roman numeral of this object
Definition: roman.java:98
roman.d
static Map< Character, Integer > d
dictionary for mapping roman symbols to decimals
Definition: roman.java:47
TextIO.putln
static void putln(Object x)
This is equivalent to put(x), followed by an end-of-line.
Definition: TextIO.java:392
roman.toInt
int toInt()
Used to convert a roman object to integer.
Definition: roman.java:151
TextIO.peek
static char peek()
Returns the next character in the current input source, without actually removing that character from...
Definition: TextIO.java:482
roman.ValueError.serialVersionUID
static final long serialVersionUID
Definition: roman.java:234
roman.ValueError.ValueError
ValueError(String message)
Definition: roman.java:236
roman.toString
String toString()
Used to print a roman object.
Definition: roman.java:141
baseConverter.itoa
static String itoa(int num, int base)
Converts an integer value to a string using the specified base and returns it.
Definition: baseConverter.java:299
TextIO.getlnInt
static int getlnInt()
Skips whitespace characters and then reads a value of type int from input, discarding the rest of the...
Definition: TextIO.java:548
roman.roman
roman(String r)
Constructor from a roman numeral.
Definition: roman.java:114
roman.rnum
String rnum
hold the roman numeral of this object
Definition: roman.java:93
TextIO.getAnyChar
static char getAnyChar()
Reads the next character from the current input source.
Definition: TextIO.java:471
roman.roman
roman()
Empty constructor.
Definition: roman.java:104
roman.rSymbols
static String[] rSymbols
table for relating decimals to roman symbols: an ordered list of roman numerals
Definition: roman.java:84
roman.int2roman
static String int2roman(int num)
Converts an integer number to roman.
Definition: roman.java:168
TextIO.getln
static String getln()
Reads all the characters from the current input source, up to the next end-of-line.
Definition: TextIO.java:645