1 
2 module roman;
3 
4 /**
5  * Converts a roman number to an integer.
6  * Returns: the number as integer or 0 on invalid numbers.
7  */
8 uint fromRoman(string str) pure nothrow @safe @nogc {
9 	if(str.length) {
10 		immutable two = str.length > 1;
11 		if(str[0] == 'M') return 1000 + str[1..$].fromRoman;
12 		else if(two && str[0..2] == "CM") return 900 + str[2..$].fromRoman;
13 		else if(str[0] == 'D') return 500 + str[1..$].fromRoman;
14 		else if(two && str[0..2] == "CD") return 400 + str[2..$].fromRoman;
15 		else if(str[0] == 'C') return 100 + str[1..$].fromRoman;
16 		else if(two && str[0..2] == "XC") return 90 + str[2..$].fromRoman;
17 		else if(str[0] == 'L') return 50 + str[1..$].fromRoman;
18 		else if(two && str[0..2] == "XL") return 40 + str[2..$].fromRoman;
19 		else if(str[0] == 'X') return 10 + str[1..$].fromRoman;
20 		else if(two && str[0..2] == "IX") return 9 + str[2..$].fromRoman;
21 		else if(str[0] == 'V') return 5 + str[1..$].fromRoman;
22 		else if(two && str[0..2] == "IV") return 4 + str[2..$].fromRoman;
23 		else if(str[0] == 'I') return 1 + str[1..$].fromRoman;
24 	}
25 	return 0;
26 }
27 
28 ///
29 pure nothrow @safe @nogc unittest {
30 
31 	assert(fromRoman("I") == 1);
32 	assert(fromRoman("III") == 3);
33 	assert(fromRoman("IV") == 4);
34 	assert(fromRoman("V") == 5);
35 	assert(fromRoman("XLI") == 41);
36 	assert(fromRoman("MM") == 2000);
37 
38 	// invalids
39 	assert(fromRoman("A") == 0);
40 
41 }
42 
43 /**
44  * Converts an integer to a roman number.
45  * Returns: A roman number or an empty string if the given number could not be converted.
46  */
47 string toRoman(uint num) {
48 	string ret = "";
49 	while (num >= 1000) {
50 		ret ~= "M";
51 		num -= 1000;
52 	}
53 	while (num >= 900) {
54 		ret ~= "CM";
55 		num -= 900;
56 	}
57 	while (num >= 500) {
58 		ret ~= "D";
59 		num -= 500;
60 	}
61 	while (num >= 400) {
62 		ret ~= "CD";
63 		num -= 400;
64 	}
65 	while (num >= 100) {
66 		ret ~= "C";
67 		num -= 100;
68 	}
69 	while (num >= 90) {
70 		ret ~= "XC";
71 		num -= 90;
72 	}
73 	while (num >= 50) {
74 		ret ~= "L";
75 		num -= 50;
76 	}
77 	while (num >= 40) {
78 		ret ~= "XL";
79 		num -= 40;
80 	}
81 	while (num >= 10) {
82 		ret ~= "X";
83 		num -= 10;
84 	}
85 	while (num >= 9) {
86 		ret ~= "IX";
87 		num -= 9;
88 	}
89 	while (num >= 5) {
90 		ret ~= "V";
91 		num -= 5;
92 	}
93 	while (num >= 4) {
94 		ret ~= "IV";
95 		num -= 4;
96 	}
97 	while (num >= 1) {
98 		ret ~= "I";
99 		num -= 1;
100 	}    
101 	return ret;
102 }
103 
104 ///
105 unittest {
106 
107 	assert(toRoman(1) == "I");
108 	assert(toRoman(3) == "III");
109 	assert(toRoman(4) == "IV");
110 	assert(toRoman(5) == "V");
111 	assert(toRoman(9) == "IX");
112 	assert(toRoman(10) == "X");
113 	assert(toRoman(33) == "XXXIII");
114 	assert(toRoman(1111) == "MCXI");
115 	assert(toRoman(1999) == "MCMXCIX");
116 	assert(toRoman(550) == "DL");
117 	assert(toRoman(400) == "CD");
118 	assert(toRoman(40) == "XL");
119 
120 	// invalid
121 	assert(toRoman(0) == "");
122 
123 }