84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
var m = map[rune]int{
|
|
'I': 1,
|
|
'V': 5,
|
|
'X': 10,
|
|
'L': 50,
|
|
'C': 100,
|
|
'D': 500,
|
|
'M': 1000,
|
|
}
|
|
|
|
func parseRoman(s string) (r int, err error) {
|
|
if s == "" {
|
|
return 0, errors.New("Empty string")
|
|
}
|
|
is := []rune(s) // easier to convert string up front
|
|
var c0 rune // c0: roman character last read
|
|
var cv0 int // cv0: value of cv
|
|
|
|
// the key to the algorithm is to process digits from right to left
|
|
for i := len(is) - 1; i >= 0; i-- {
|
|
// read roman digit
|
|
c := is[i]
|
|
k := c == '\u0305' // unicode overbar combining character
|
|
if k {
|
|
if i == 0 {
|
|
return 0, errors.New(
|
|
"Overbar combining character invalid at position 0")
|
|
}
|
|
i--
|
|
c = is[i]
|
|
}
|
|
cv := m[c]
|
|
if cv == 0 {
|
|
if c == 0x0305 {
|
|
return 0, fmt.Errorf(
|
|
"Overbar combining character invalid at position %d", i)
|
|
} else {
|
|
return 0, fmt.Errorf(
|
|
"Character unrecognized as Roman digit: %c", c)
|
|
}
|
|
}
|
|
if k {
|
|
c = -c // convention indicating overbar
|
|
cv *= 1000
|
|
}
|
|
|
|
// handle cases of new, same, subtractive, changed, in that order.
|
|
switch {
|
|
default: // case 4: digit change
|
|
fallthrough
|
|
case c0 == 0: // case 1: no previous digit
|
|
c0 = c
|
|
cv0 = cv
|
|
case c == c0: // case 2: same digit
|
|
case cv*5 == cv0 || cv*10 == cv0: // case 3: subtractive
|
|
// handle next digit as new.
|
|
// a subtractive digit doesn't count as a previous digit.
|
|
c0 = 0
|
|
r -= cv // subtract...
|
|
continue // ...instead of adding
|
|
}
|
|
r += cv // add, in all cases except subtractive
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
func main() {
|
|
// parse three numbers mentioned in task description
|
|
for _, r := range []string{"MCMXC", "MMVIII", "MDCLXVI"} {
|
|
v, err := parseRoman(r)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
} else {
|
|
fmt.Println(r, "==", v)
|
|
}
|
|
}
|
|
}
|