roman_numerals from bluefruit competition. http://www.bluefruit.co.uk/careers/win/

This commit is contained in:
2015-01-13 08:53:25 +04:00
parent c0b2c2f12d
commit f24d1e5a07

View File

@@ -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 <iostream>
#include <exception>
/*
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;
}}