From f24d1e5a0726c555a14287650fa3f6ee4fce2fbd Mon Sep 17 00:00:00 2001 From: Vahagn Khachatryan Date: Tue, 13 Jan 2015 08:53:25 +0400 Subject: [PATCH] roman_numerals from bluefruit competition. http://www.bluefruit.co.uk/careers/win/ --- puzzles/bluefruit/roman_numerals.cpp | 271 +++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 puzzles/bluefruit/roman_numerals.cpp diff --git a/puzzles/bluefruit/roman_numerals.cpp b/puzzles/bluefruit/roman_numerals.cpp new file mode 100644 index 0000000..a85fcac --- /dev/null +++ b/puzzles/bluefruit/roman_numerals.cpp @@ -0,0 +1,271 @@ +/* +VIM: let g:lcppflags="-std=c++11 -O2 -pthread" +VIM: let g:wcppflags="/O2 /EHsc /DWIN32" +VIM: let g:argv="" + +ID 971852 +*/ +#include +#include + +/* +Given a Roman number as a string (eg "XX") determine +its integer value (eg 20). + +You cannot write numerals like IM for 999. +Wikipedia states "Modern Roman numerals are written by +expressing each digit separately starting with the +leftmost digit and skipping any digit with a value of zero." + +Examples: + + "I" -> 1 | "X" -> 10 | "C" -> 100 | "M" -> 1000 + "II" -> 2 | "XX" -> 20 | "CC" -> 200 | "MM" -> 2000 + "III" -> 3 | "XXX" -> 30 | "CCC" -> 300 | "MMM" -> 3000 + "IV" -> 4 | "XL" -> 40 | "CD" -> 400 | "MMMM" -> 4000 + "V" -> 5 | "L" -> 50 | "D" -> 500 | + "VI" -> 6 | "LX" -> 60 | "DC" -> 600 | + "VII" -> 7 | "LXX" -> 70 | "DCC" -> 700 | +"VIII" -> 8 | "LXXX" -> 80 | "DCCC" -> 800 | + "IX" -> 9 | "XC" -> 90 | "CM" -> 900 | + + "MCMXC" -> 1990 ("M" -> 1000 + "CM" -> 900 + "XC" -> 90) +"MMVIII" -> 2008 ("MM" -> 2000 + "VIII" -> 8) + "XCIX" -> 99 ("XC" -> 90 + "IX" -> 9) + "XLVII" -> 47 ("XL" -> 40 + "VII" -> 7) +*/ +int roman2num( const char * roman_num ) { + // + // Possible digits. + // + enum digits { D_I = 0, D_V, D_X, D_L, D_C, D_D, D_M, D_ERR }; + // + // The states of parser. The names of states are quite intuitive. The + // vI, lX, dC are the states corresponding to patterns VI+, LX+, DC+ + // when the first I,X,C letters are seen. + // + enum states { + S = 0, /* START state. */ + I, vI, II, III, IV, V, IX, + X, lX, XX, XXX, XL, L, XC, + C, dC, CC, CCC, CD, D, CM, + M, MM, MMM, MMMM, + E /* ERROR state */ + }; + // + // This table defines state transition for each character seen. + // + static const char state_table[][8] = { + /* I, V, X, L, C, D, M, D_ERR */ + { I, V, X, L, C, D, M, E}, //S + + { II, IV, IX, E, E, E, E, E}, //I + { II, E, E, E, E, E, E, E}, //vI + {III, E, E, E, E, E, E, E}, //II + { E, E, E, E, E, E, E, E}, //III + { E, E, E, E, E, E, E, E}, //IV + { vI, E, E, E, E, E, E, E}, //V + { E, E, E, E, E, E, E, E}, //IX + + { I, V, XX, XL, XC, E, E, E}, //X + { I, V, XX, E, E, E, E, E}, //lX + { I, V,XXX, E, E, E, E, E}, //XX + { I, V, E, E, E, E, E, E}, //XXX + { I, V, E, E, E, E, E, E}, //XL + { I, V, lX, E, E, E, E, E}, //L + { I, V, E, E, E, E, E, E}, //XC + + { I, V, X, L, CC, CD, CM, E}, //C + { I, V, X, L, CC, E, E, E}, //dC + { I, V, X, L,CCC, E, E, E}, //CC + { I, V, X, L, E, E, E, E}, //CCC + { I, V, X, L, E, E, E, E}, //CD + { I, V, X, L, dC, E, E, E}, //D + { I, V, X, L, E, E, E, E}, //CM + + { I, V, X, L, C, D, MM, E}, //M + { I, V, X, L, C, D, MMM, E}, //MM + { I, V, X, L, C, D, MMMM, E}, //MMM + { I, V, X, L, C, D, E, E}, //MMMM + + { E, E, E, E, E, E, E, E} //E + }; + // + // Each state transition causes an increment of the number with + // certain value defined in this table. + // A state transition happens for each character we read, hence when + // defining the increment value of complex states such as II,III,IV,... + // please take into consideration what amount increment we already have. + // For example the state XL is defined as 30 since we already added 10 when + // we see X. + // + static const short increment[] = { + //S This is here to preserve alignment only. + 0, + // I, vI, II, III, IV, V, IX + 1, 1, 1, 1, 3, 5, 8, + // X, lX, XX, XXX, XL, L, XC + 10, 10, 10, 10, 30, 50, 80, + // C, dC, CC, CCC, CD, D, CM, + 100, 100, 100, 100, 300, 500, 800, + // M, MM, MMM, MMMM */ + 1000, 1000, 1000, 1000, + //E This is the error condition. The value doesn't really matter. + 0 + }; + // + // Ideally this had to be a table based lookup. + // + auto chr2digit = []( char ch ) { + switch (ch) { + case 'I' : return D_I; + case 'V' : return D_V; + case 'X' : return D_X; + case 'L' : return D_L; + case 'C' : return D_C; + case 'D' : return D_D; + case 'M' : return D_M; + default : return D_ERR; + } + }; + // + // Here the algorithm begins. + // + if ( !roman_num ) + return -1; + + int num = 0; + int state = S; + for ( const char * e = roman_num; *e && state < E; ++e ) { + state = state_table[state][chr2digit(*e)]; + num += increment[state]; + } + if ( state >= E ) + return -1; + return num; +} + +void ASSERT_EQ( int golden, int ret ) { + if ( ret != golden ) + std::cout << "FAIL: Expected " << golden << " got " << ret << std::endl; +} + +int main ( void ) +{try{ + ASSERT_EQ(0, roman2num("")); + ASSERT_EQ(1, roman2num("I")); + ASSERT_EQ(2, roman2num("II")); + ASSERT_EQ(3, roman2num("III")); + ASSERT_EQ(4, roman2num("IV")); + ASSERT_EQ(5, roman2num("V")); + ASSERT_EQ(6, roman2num("VI")); + ASSERT_EQ(7, roman2num("VII")); + ASSERT_EQ(8, roman2num("VIII")); + ASSERT_EQ(9, roman2num("IX")); + + ASSERT_EQ(10, roman2num("X")); + ASSERT_EQ(20, roman2num("XX")); + ASSERT_EQ(30, roman2num("XXX")); + ASSERT_EQ(40, roman2num("XL")); + ASSERT_EQ(50, roman2num("L")); + ASSERT_EQ(60, roman2num("LX")); + ASSERT_EQ(70, roman2num("LXX")); + ASSERT_EQ(80, roman2num("LXXX")); + ASSERT_EQ(90, roman2num("XC")); + + ASSERT_EQ(100, roman2num("C")); + ASSERT_EQ(200, roman2num("CC")); + ASSERT_EQ(300, roman2num("CCC")); + ASSERT_EQ(400, roman2num("CD")); + ASSERT_EQ(500, roman2num("D")); + ASSERT_EQ(600, roman2num("DC")); + ASSERT_EQ(700, roman2num("DCC")); + ASSERT_EQ(800, roman2num("DCCC")); + ASSERT_EQ(900, roman2num("CM")); + + ASSERT_EQ(1000, roman2num("M")); + ASSERT_EQ(2000, roman2num("MM")); + ASSERT_EQ(3000, roman2num("MMM")); + ASSERT_EQ(4000, roman2num("MMMM")); + + ASSERT_EQ(1990, roman2num("MCMXC")); + ASSERT_EQ(2008, roman2num("MMVIII")); + ASSERT_EQ(91, roman2num("XCI")); + ASSERT_EQ(99, roman2num("XCIX")); + ASSERT_EQ(47, roman2num("XLVII")); + ASSERT_EQ(3888, roman2num("MMMDCCCLXXXVIII")); + ASSERT_EQ(4888, roman2num("MMMMDCCCLXXXVIII")); + ASSERT_EQ(4999, roman2num("MMMMCMXCIX")); + + static const char * num [] = { + "","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" + }; + for ( int i = 0; i < 5000; ++i ) { + int d4 = i/1000; + int d3 = i%1000/100; + int d2 = i%100/10; + int d1 = i%10; + + std::string test = num[30+d4]; + test +=num[20+d3]; + test +=num[10+d2]; + test +=num[d1]; + + ASSERT_EQ(i, roman2num(test.c_str())); + } + + // + // Negative cases. + // + ASSERT_EQ(-1, roman2num(0)); + ASSERT_EQ(-1, roman2num("IXLVII")); + ASSERT_EQ(-1, roman2num("IIII")); + ASSERT_EQ(-1, roman2num("VV")); + ASSERT_EQ(-1, roman2num("XXXX")); + ASSERT_EQ(-1, roman2num("LL")); + ASSERT_EQ(-1, roman2num("CCCC")); + ASSERT_EQ(-1, roman2num("DD")); + ASSERT_EQ(-1, roman2num("MMMMM")); + ASSERT_EQ(-1, roman2num("IIV")); + ASSERT_EQ(-1, roman2num("IVI")); + ASSERT_EQ(-1, roman2num("IIX")); + ASSERT_EQ(-1, roman2num("IXI")); + ASSERT_EQ(-1, roman2num("XXC")); + ASSERT_EQ(-1, roman2num("XCX")); + ASSERT_EQ(-1, roman2num("XXL")); + ASSERT_EQ(-1, roman2num("XLX")); + ASSERT_EQ(-1, roman2num("CCM")); + ASSERT_EQ(-1, roman2num("CMC")); + ASSERT_EQ(-1, roman2num("CCD")); + ASSERT_EQ(-1, roman2num("CDC")); + + ASSERT_EQ(-1, roman2num("c")); + ASSERT_EQ(-1, roman2num("l")); + ASSERT_EQ(-1, roman2num("x")); + ASSERT_EQ(-1, roman2num("v")); + ASSERT_EQ(-1, roman2num("v")); + ASSERT_EQ(-1, roman2num("v")); + ASSERT_EQ(-1, roman2num("v")); + ASSERT_EQ(-1, roman2num("1")); + ASSERT_EQ(-1, roman2num("2")); + ASSERT_EQ(-1, roman2num("a")); + ASSERT_EQ(-1, roman2num(".")); + + return 0; +} +catch ( const std::exception& e ) +{ + std::cerr << std::endl + << "std::exception(\"" << e.what() << "\")." << std::endl; + return 2; +} +catch ( ... ) +{ + std::cerr << std::endl + << "unknown exception." << std::endl; + return 1; +}} +