package com.mindprod.inwords;

/**
 * <pre>
 *       com.mindprod.inwords.Roman.java - convert long integer into Roman
 * numerals e.g.
 *       12345 -&gt; &quot; MMMMMMMMMMMMCCCXLV&quot;
 * <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 &quot;Oliver
 * Borchert&quot; &lt;oliver.borchert@nist.gov&gt;
 *       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
    {
    // ------------------------------ FIELDS ------------------------------

    /**
     * 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 = {
            /*
            * HowToProcess many of this group is needed to form one of the succeeding group.
            */
            /* 1 10 100 1000 */
            10, 10, 10, 1000000 };
    // -------------------------- PUBLIC STATIC METHODS --------------------------

    /**
     * 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 = "";
        // Work least significant digit to most, right to left.
        // until high order part is all 0s.
        for ( int i = 0; num > 0; i++ )
            {
            int remdr = ( int ) ( num % divisor[ i ] );
            num = num / divisor[ i ];
            StringBuilder t;
            if ( i == 3 )
                {
                /* M is as big as it gets, just repeat */
                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;
                    }// end switch
                }// end else
            s = t + s;
            }// end for
        return s;
        }// end inWords

    // -------------------------- PUBLIC INSTANCE  METHODS --------------------------
    /**
     * @inheritDoc
     */
    public String toWords( long num )
        {
        return Roman.inWords( num );
        }

    // --------------------------- main() method ---------------------------

    /**
     * test harness
     *
     * @param args not used
     */
    public static void main( String[] args )
        {
        Test.test( new Roman() );
        }// end main
    }// end Roman