content: module Main where
  
  ------------------------
  --  DECODER FUNCTION  --
  ------------------------
  
  decodeDigit :: Char -> Int
  decodeDigit 'I' = 1
  decodeDigit 'V' = 5
  decodeDigit 'X' = 10
  decodeDigit 'L' = 50
  decodeDigit 'C' = 100
  decodeDigit 'D' = 500
  decodeDigit 'M' = 1000
  decodeDigit _ = error "invalid digit"
  
  --  We process a Roman numeral from right to left, digit by digit, adding the value.
  --  If a digit is lower than the previous then its value is negative.
  --  The first digit is always positive.
  
  decode roman = decodeRoman startValue startValue rest
    where
      (first:rest) = reverse roman
      startValue = decodeDigit first
  
  decodeRoman :: Int -> Int -> [Char] -> Int
  decodeRoman lastSum _ [] = lastSum
  decodeRoman lastSum lastValue (digit:rest) = decodeRoman updatedSum digitValue rest
    where
      digitValue = decodeDigit digit
      updatedSum = (if digitValue < lastValue then (-) else (+)) lastSum digitValue
  
  ------------------
  --  TEST SUITE  --
  ------------------
  
  main = do
    test "MCMXC" 1990
    test "MMVIII" 2008
    test "MDCLXVI" 1666
  
  test roman expected = putStrLn (roman ++ " = " ++ (show (arabic)) ++ remark)
    where
      arabic = decode roman
      remark = " (" ++ (if arabic == expected then "PASS" else ("FAIL, expected " ++ (show expected))) ++ ")"
  