|         |      1 abstract class RomanDigit  | 
|         |      2 case object I extends RomanDigit  | 
|         |      3 case object V extends RomanDigit  | 
|         |      4 case object X extends RomanDigit  | 
|         |      5 case object L extends RomanDigit  | 
|         |      6 case object C extends RomanDigit  | 
|         |      7 case object D extends RomanDigit  | 
|         |      8 case object M extends RomanDigit  | 
|         |      9  | 
|         |     10 type RomanNumeral = List[RomanDigit]  | 
|         |     11  | 
|         |     12 def optionlist[A](xs: List[Option[A]]): Option[List[A]] = xs match { | 
|         |     13   case Nil => Some(Nil) | 
|         |     14   case x::xs => (x, optionlist(xs)) match { | 
|         |     15     case (None, _) => None | 
|         |     16     case (_, None) => None | 
|         |     17     case (Some(y), Some(ys)) => Some(y::ys)   | 
|         |     18   } | 
|         |     19 } | 
|         |     20  | 
|         |     21 def char2romandigit(c: Char): Option[RomanDigit] = c match { | 
|         |     22   case 'I' => Some(I) | 
|         |     23   case 'V' => Some(V) | 
|         |     24   case 'X' => Some(X) | 
|         |     25   case 'L' => Some(L) | 
|         |     26   case 'C' => Some(C) | 
|         |     27   case 'D' => Some(D) | 
|         |     28   case 'M' => Some(M) | 
|         |     29   case _ => None | 
|         |     30 } | 
|         |     31  | 
|         |     32 def String2RomanNumeral(s: String) : Option[RomanNumeral] =  | 
|         |     33   optionlist(s.toList.map(char2romandigit)) | 
|         |     34  | 
|         |     35 String2RomanNumeral("IIII") | 
|         |     36 String2RomanNumeral("IV") | 
|         |     37 String2RomanNumeral("VI") | 
|         |     38 String2RomanNumeral("IX") | 
|         |     39 String2RomanNumeral("MCMLXXIX") | 
|         |     40 String2RomanNumeral("MCMXLIV")  | 
|         |     41 String2RomanNumeral("M C M X L I V")  // None | 
|         |     42  | 
|         |     43 //////////////// | 
|         |     44  | 
|         |     45 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match {  | 
|         |     46   case Nil => 0 | 
|         |     47   case M::r    => 1000 + RomanNumeral2Int(r)   | 
|         |     48   case C::M::r => 900 + RomanNumeral2Int(r) | 
|         |     49   case D::r    => 500 + RomanNumeral2Int(r) | 
|         |     50   case C::D::r => 400 + RomanNumeral2Int(r) | 
|         |     51   case C::r    => 100 + RomanNumeral2Int(r) | 
|         |     52   case X::C::r => 90 + RomanNumeral2Int(r) | 
|         |     53   case L::r    => 50 + RomanNumeral2Int(r) | 
|         |     54   case X::L::r => 40 + RomanNumeral2Int(r) | 
|         |     55   case X::r    => 10 + RomanNumeral2Int(r) | 
|         |     56   case I::X::r => 9 + RomanNumeral2Int(r) | 
|         |     57   case V::r    => 5 + RomanNumeral2Int(r) | 
|         |     58   case I::V::r => 4 + RomanNumeral2Int(r) | 
|         |     59   case I::r    => 1 + RomanNumeral2Int(r) | 
|         |     60 } | 
|         |     61  | 
|         |     62 RomanNumeral2Int(List(I,I,I,I))         // 4 | 
|         |     63 RomanNumeral2Int(List(I,V))             // 4 | 
|         |     64 RomanNumeral2Int(List(V,I))             // 6 | 
|         |     65 RomanNumeral2Int(List(I,X))             // 9 | 
|         |     66 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 | 
|         |     67 RomanNumeral2Int(List(M,C,M,X,L,I,V))   // 1944 | 
|         |     68  | 
|         |     69  | 
|         |     70 def String2Int(s: String): Option[Int] =  | 
|         |     71   String2RomanNumeral(s).map(RomanNumeral2Int(_)) | 
|         |     72  | 
|         |     73  | 
|         |     74 String2Int("IIII")      // 4 invalid  | 
|         |     75 String2Int("IV")        // 4 | 
|         |     76 String2Int("VI")        // 6 | 
|         |     77 String2Int("IX")        // 9 | 
|         |     78 String2Int("MCMLXXIX")  // 1979 | 
|         |     79 String2Int("MCMXLIV")   // 1944 | 
|         |     80 String2Int("")          // 0 | 
|         |     81 String2Int("MDCLXI")    // 1661 | 
|         |     82 String2Int("MMMCMXCIX") // 3999 | 
|         |     83 String2Int("XLVIII")    // 48 | 
|         |     84 String2Int("MMVIII")    // 2008 | 
|         |     85  | 
|         |     86 String2Int("MMXI")      // 2011  | 
|         |     87 String2Int("MIM")       // 1999 | 
|         |     88 String2Int("MCMLVI")    // 1956  | 
|         |     89 String2Int("XXCIII")    // ?? 103 / 83 | 
|         |     90 String2Int("LXXIIX")    // ?? 80 / 78 | 
|         |     91 String2Int("IIIIX")     // ?? 12 invalid   | 
|         |     92 String2Int("IIXX")      // ?? 20 / 18 invalid | 
|         |     93  | 
|         |     94 String2Int("III") 	// 3 | 
|         |     95 String2Int("XXX") 	// 30 | 
|         |     96 String2Int("CCC") 	// 300 | 
|         |     97 String2Int("MMM") 	// 3000 | 
|         |     98 String2Int("VII") 	// 7 | 
|         |     99 String2Int("LXVI") 	// 66 | 
|         |    100 String2Int("CL") 	// 150 | 
|         |    101 String2Int("MCC") 	// 1200 | 
|         |    102 String2Int("IV") 	// 4 | 
|         |    103 String2Int("IX") 	// 9 | 
|         |    104 String2Int("XC") 	// 90 | 
|         |    105 String2Int("ICM") 	// ?? 901 | 
|         |    106 String2Int("CIM") 	// ?? 899 | 
|         |    107 String2Int("MDCLXVI")	// 1666 | 
|         |    108  | 
|         |    109 String2Int("VIV")       //9, but should be written as IX  | 
|         |    110 String2Int("IVX")       // 14 invalid | 
|         |    111  | 
|         |    112 // error cases | 
|         |    113 String2Int("MC?I") | 
|         |    114 String2Int("abc") | 
|         |    115  | 
|         |    116  | 
|         |    117 def runsAllowed(r: RomanDigit) = r match { | 
|         |    118   case I | X | C | M => true | 
|         |    119   case V | L | D => false | 
|         |    120 } | 
|         |    121  | 
|         |    122 def norunsAllowed(r: RomanDigit) = !runsAllowed(r) | 
|         |    123  | 
|         |    124  | 
|         |    125 def isValidNumeral(digitList: RomanNumeral): Bool = digitList match { | 
|         |    126  | 
|         |    127     // empty list is valid | 
|         |    128   case Nil => true | 
|         |    129  | 
|         |    130     // a following digit that is equal or larger is an error | 
|         |    131   case d1::d2::_ if (d1 <= d2)  => false  | 
|         |    132  | 
|         |    133     // A single digit is always allowed | 
|         |    134   case _::ds => isValidNumeral(ds)  | 
|         |    135  | 
|         |    136 } |