package com.mindprod.inwords;
/**
* <pre>
* com.mindprod.inwords.Roman.java - convert long integer into Roman
* numerals e.g.
* 12345 -> " MMMMMMMMMMMMCCCXLV"
* <p/>
* <p/>
* @author copyright (c) 1999-2008 Roedy Green, Canadian Mind Products
* may be copied and used freely for any purpose but military.
* <p/>
* Freeware
* <p/>
* Roedy Green
* Canadian Mind Products
* #101 - 2536 Wark Street
* Victoria, BC Canada V8T 4G8
* tel: (250) 361-9093
* roedy g at mindprod dotcom
* http://mindprod.com
* <p/>
* version 1.2 1999-08-11
* - correct code for 8 to VIII not VII.
* - thanks to bug report from "Oliver
* Borchert" <oliver.borchert@nist.gov>
* version 1.1 1999-01-20
* - remove 0 and negative number support.
* version 1.0 1999-01-19
* <p/>
* Why is this code written the way it is?
* <p/>
* 1. It works right to left so that it can avoid divisions
* for high order parts when there aren't any.
* <p/>
* 2. It accentuates all asymmetry by using a common loop body for each
* part of
* the number. Part of the reason for wrting these classes was
* to understand the structure of human language and unravelling the
* loops
* would mask those quirks, even if it made the code simpler.
* <p/>
* 3. It is written hopefully so that a non-native speaker could
* understand
* the code. The use of temporary boolean variables in particular was
* done for clarity at the expense of program size/speed.
* <p/>
* 4. It is table driven as much as possible so that classes for new
* lanuages
* can be written easily by taking an existing driver, changing the
* tables, then
* tweaking just a little logic, perhaps cobbling together bits of
* logic from other language classes, that all work with the same
* table-driven
* pattern.
* <p/>
* There are terser algorithms, such as the Pascal version posted at
* http://www.merlyn.demon.co.uk/programs/cvt_rome.pas
* but they don't show the parallelism with other numbering systems
* and I think they are harder to understand.
* </pre>
*/
public final class Roman implements ToWords
{
/**
* undisplayed copyright notice
*
* @noinspection UnusedDeclaration
*/
public static final String EMBEDDED_COPYRIGHT =
"copyright (c) 1999-2008 Roedy Green, Canadian Mind Products, http://mindprod.com";
private static final char[] fiveLetter = { 'V', 'L', 'D' };
private static final char[] unitLetter = { 'I', 'X', 'C', 'M' };
private static final int[] divisor = {
10, 10, 10, 1000000 };
/**
* convert long integer into Roman Numerals. e.g. -12345 -> "minus MMMMMMMMMMMMCCCXLV" Handles negative and positive
* integers on range -Long.MAX_VALUE .. Long.MAX_VALUE; It cannot handle Long.MIN_VALUE;
*
* @param num number to convert to words
* @return words
*/
@SuppressWarnings( { "WeakerAccess" } )
public static String inWords( long num )
{
if ( num < 0 )
{
return "The Romans had no negative numbers.";
}
if ( num == 0 )
{
return "The Romans had no zero.";
}
if ( num > 100000 )
{
return "too unwieldy for Roman numerals";
}
String s = "";
for ( int i = 0; num > 0; i++ )
{
int remdr = ( int ) ( num % divisor[ i ] );
num = num / divisor[ i ];
StringBuilder t;
if ( i == 3 )
{
t = new StringBuilder( remdr );
for ( int j = 0; j < remdr; j++ )
{
t.append( unitLetter[ i ] );
}
}
else
{
t = new StringBuilder( 3 );
switch ( remdr )
{
case 0:
break;
case 1:
t.append( unitLetter[ i ] );
break;
case 2:
t.append( unitLetter[ i ] );
t.append( unitLetter[ i ] );
break;
case 3:
t.append( unitLetter[ i ] );
t.append( unitLetter[ i ] );
t.append( unitLetter[ i ] );
break;
case 4:
t.append( unitLetter[ i ] );
t.append( fiveLetter[ i ] );
break;
case 5:
t.append( fiveLetter[ i ] );
break;
case 6:
t.append( fiveLetter[ i ] );
t.append( unitLetter[ i ] );
break;
case 7:
t.append( fiveLetter[ i ] );
t.append( unitLetter[ i ] );
t.append( unitLetter[ i ] );
break;
case 8:
t.append( fiveLetter[ i ] );
t.append( unitLetter[ i ] );
t.append( unitLetter[ i ] );
t.append( unitLetter[ i ] );
break;
case 9:
t.append( unitLetter[ i ] );
t.append( unitLetter[ i + 1 ] );
break;
}
}
s = t + s;
}
return s;
}
/**
* @inheritDoc
*/
public String toWords( long num )
{
return Roman.inWords( num );
}
/**
* test harness
*
* @param args not used
*/
public static void main( String[] args )
{
Test.test( new Roman() );
}
}