# Roman Numerals Kata

In the Ruby world TDD is often demonstrated with a kata of converting decimal numbers into roman numerals. Here’s my take on it with a gentle slope in introducing complexity, with a stepwise discovery of the concepts of…

*repetition**transliteration**concatenation**prefixing*

We start with the minimal implementation for 1.

```
class Integer
def to_roman
"I"
end
end
```

For 2 we discover our first concept: *repetition*.

```
class Integer
def to_roman
"I" * self
end
end
```

That works for 3 as well, then we add a special case for 4. We decide, we can live with that.

```
class Integer
def to_roman
return "IV" if self == 4
"I" * self
end
end
```

This will break again, at 5. To go green asap, we quickly add yet another special case.

```
class Integer
def to_roman
return "V" if self == 5
return "IV" if self == 4
"I" * self
end
end
```

And then we refactor, thereby discovering the second core concept: *transliteration*.

```
class Integer
NUMERALS = [[5, "V"], [4, "IV"], [1, "I"]]
def to_roman
NUMERALS.each do |decimal, roman|
count = self / decimal
return roman * count if count > 0
end
end
end
```

For 6, again, we simply add a special case and move on.

```
class Integer
NUMERALS = [[6, "VI"], [5, "V"], [4, "IV"], [1, "I"]]
def to_roman
NUMERALS.each do |decimal, roman|
count = self / decimal
return roman * count if count > 0
end
end
end
```

At 7, breaking again, we must get green quickly, even if it comes at the price of a second special case.

```
class Integer
NUMERALS = [[7, "VII"], [6, "VI"], [5, "V"], [4, "IV"], [1, "I"]]
def to_roman
NUMERALS.each do |decimal, roman|
count = self / decimal
return roman * count if count > 0
end
end
end
```

And because this is now ripe to refactor, we introduce the third concept: *concatenation*.

```
class Integer
NUMERALS = [[5, "V"], [4, "IV"], [1, "I"]]
def to_roman
result, remainder = "", self
NUMERALS.each do |decimal, roman|
count, remainder = remainder.divmod decimal
result << roman * count
end
result
end
end
```

Working with the translation table we can complete the converter, also adding error handling.

```
class Integer
NUMERALS = [
[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"]
]
def to_roman
raise "There's no such a roman number" if self < 1 || self > 3999
result, remainder = "", self
NUMERALS.each do |decimal, roman|
count, remainder = remainder.divmod decimal
result << roman * count
end
result
end
end
```

The TDD approach guided us separating the discovery of the core concepts – *repetition*, *transliteration* and *concatenation* – into distinct steps. Nice.

However.

There’s still quite some fugly duplication going on in the translation table, indicating that we’ve ignored the fourth concept lurking behind the remaining double-letter values: *prefixing*. Whether the additional algorithmic complexity is worth the elimination of conceptual duplication is yet to be seen…