gotdd/roman_numerals.go

91 lines
1.7 KiB
Go
Raw Normal View History

2022-01-20 04:20:12 +00:00
package main
import "strings"
// ConvertToArabic converts a Roman Numeral to an Arabic number.
func ConvertToArabic(roman string) (total uint16) {
for _, symbols := range windowedRoman(roman).Symbols() {
total += allRomanNumerals.ValueOf(symbols...)
}
return
}
// ConvertToRoman converts an Arabic number to a Roman Numeral.
func ConvertToRoman(arabic uint16) string {
var result strings.Builder
for _, numeral := range allRomanNumerals {
for arabic >= numeral.Value {
result.WriteString(numeral.Symbol)
arabic -= numeral.Value
}
}
return result.String()
}
type romanNumeral struct {
Value uint16
Symbol string
}
type romanNumerals []romanNumeral
func (r romanNumerals) ValueOf(symbols ...byte) uint16 {
symbol := string(symbols)
for _, s := range r {
if s.Symbol == symbol {
return s.Value
}
}
return 0
}
func (r romanNumerals) Exists(symbols ...byte) bool {
symbol := string(symbols)
for _, s := range r {
if s.Symbol == symbol {
return true
}
}
return false
}
var allRomanNumerals = romanNumerals{
{1000, "M"},
{900, "CM"},
{500, "D"},
{400, "CD"},
{100, "C"},
{90, "XC"},
{50, "L"},
{40, "XL"},
{10, "X"},
{9, "IX"},
{5, "V"},
{4, "IV"},
{1, "I"},
}
type windowedRoman string
func (w windowedRoman) Symbols() (symbols [][]byte) {
for i := 0; i < len(w); i++ {
symbol := w[i]
notAtEnd := i+1 < len(w)
if notAtEnd && isSubtractive(symbol) && allRomanNumerals.Exists(symbol, w[i+1]) {
symbols = append(symbols, []byte{symbol, w[i+1]})
i++
} else {
symbols = append(symbols, []byte{symbol})
}
}
return
}
func isSubtractive(symbol uint8) bool {
return symbol == 'I' || symbol == 'X' || symbol == 'C'
}