A few days ago I attempted a Roman Numerals kata, which basically wanted me to implement a function to convert a “normal number” (I took this to mean an integer) to a Roman numeral. I chose to use Kotlin as I wanted to explore how the language worked for a problem that would probably not require many complex language features.

I captured the result after each red-green-refactor cycle in a branch of my GitHub repo tdd-template; feel free to browse the commits there but I’m going to describe them below, with some accompanying comments.

#### Setup

I used Kotlin 1.0.3 with JUnit and JUnitParams

#### The first test: 1 ⇒ I

It was nice and easy to choose what this should be!

```
import org.junit.Test
import kotlin.test.assertEquals
class DecimalToRomanConverterTest {
@Test fun `1 is written as I`() {
assertEquals("I", 1.toRoman())
}
}
```

“Hang on”, I hear you cry. “Since when did integers have a `toRoman`

method?! And how can you even call a method on a primitive int?”

Well, as you probably guessed, this is a bit of Kotlin magic.

Firstly, Kotlin provides some basic types, including `Int`

, which are treated as objects having methods (like `Integer`

in Java) but which are actually translated to the equivalent of Java’s primitive `int`

by the compiler.

Secondly, Kotlin has extension functions which, as far as I can tell, are syntactic sugar for a static method whose first argument is the target type.

Finally, note the use of backticks for the test method name, which allows a nice natural method name (and description in test runner outputs). This is arguably an abuse of the backtick, which in Kotlin is there as a way of referencing a method (say a Java method) which is named with a Kotlin reserved word.

So, I implemented this extension method. (Alt-Enter and… why, thanks IntelliJ, yes I *would* like to “Create extension function 'toRoman'”, and I think I’ll target `Int`

since you so kindly suggested it.)

```
fun Int.toRoman(): String {
return "I"
}
```

#### The second test: 2 ⇒ II

Next simplest test: I could have chosen to convert 10, or 5, as they correspond to single Roman digits. But I figured that wouldn’t teach me anything about the more general solution, so instead I moved on to writing 2 in Roman numerals.

```
@Test fun `2 is written as II`() {
assertEquals("II", 2.toRoman())
}
```

`kotlin.String`

implements `kotlin.CharSequence`

(just like their Java counterparts), and there are a whole load of neat functions defined on CharSequence. One of these is `repeat(n: Int)`

, which “Returns a string containing this char sequence repeated [n] times”. Perfect!

In an extension function, you can refer to the target instance (in this case, the integer 2) with `this`

, so the implementation was again straightforward:

```
fun Int.toRoman(): String {
return "I".repeat(this)
}
```

##### Refactoring

Just because I could, I converted the two test methods to expression bodies:

```
class DecimalToRomanConverterTest {
@Test fun `1 is written as I`() = assertEquals("I", 1.toRoman())
@Test fun `2 is written as II`() = assertEquals("II", 2.toRoman())
}
```

It seemed clear that all my tests would be this shape, so I decided to apply some JUnitParams magic. JUnitParams has many ways to define the parameter values directly in the annotation on the test method, but I thought my list might get quite long, so I created a companion method that returned the dataset:

```
import junitparams.JUnitParamsRunner
import junitparams.Parameters
import junitparams.naming.TestCaseName
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
@RunWith(JUnitParamsRunner::class)
class DecimalToRomanConverterTest {
@Test
@TestCaseName("{0} is written as {1}")
@Parameters
fun testIntToRomanConversion(number: Int,
romanRepresentation: String) {
assertEquals(romanRepresentation, number.toRoman())
}
private fun parametersForTestIntToRomanConversion() =
arrayOf(
arrayOf(1, "I"),
arrayOf(2, "II")
)
}
```

#### The third test: 10 ⇒ X

I knew a test for 3 ⇒ III would pass, but 4 ⇒ IV would fail. I could’t think how I’d deal with the “subtracting” digits (I before V = subtract I from V) yet, and for some reason I did’t want to get into the 5 scenario yet (it doesn’t follow the same addition rules as 1). So, I decided to handle 10 next.

```
private fun parametersForTestIntToRomanConversion() =
arrayOf(
arrayOf(1, "I"),
arrayOf(2, "II"),
arrayOf(10, "X")
)
```

I reckon I succeeded in keeping the implementation as simple as possible:

```
fun Int.toRoman(): String {
if (this < 10) {
return "I".repeat(this)
} else {
return "X"
}
}
```

Onward!

#### The fourth test: 20 ⇒ XX

I now knew how to add ones, and reasoned that I would be able to add tens in a similar way. I added this case to my test data function:

```
arrayOf(20, "XX")
```

and did a little bit of arithmetic to work out how many tens were in the number being converted:

```
fun Int.toRoman(): String {
if (this < 10) {
return "I".repeat(this)
} else {
return "X".repeat(this / 10)
}
}
```

I can’t remember exactly why, but thinking ahead a bit about how the numbers would later be combined, I decided I was likely to want to handle the values from larger to smaller, so I reversed the order of the conditional:

```
fun Int.toRoman(): String {
if (this >= 10) {
return "X".repeat(this / 10)
} else {
return "I".repeat(this)
}
}
```

#### Tests 5–8: converting 100, 200, 1000, 2000

By this point I had a rhythm going for the first two powers of ten and their simple repeats. I zipped through the same steps with 100, 200, 1000 and 2000:

```
fun Int.toRoman(): String {
if (this >= 1000) {
return "M".repeat(this / 1000)
} else if (this >= 100) {
return "C".repeat(this / 100)
} else if (this >= 10) {
return "X".repeat(this / 10)
} else {
return "I".repeat(this)
}
}
```

##### Refactoring

A clear pattern had emerged, a kind of duplication. It seemed wise to deal with it. First I wanted a mapping from each power of ten to its Roman digit:

```
fun symbolFor(unit: Int): String {
return when(unit) {
1000 -> "M"
100 -> "C"
10 -> "X"
1 -> "I"
else -> throw RomanNumeralException(
"No mapping to symbol for ${unit}")
}
}
class RomanNumeralException(message: String) : RuntimeException(message)
```

(This demonstrates a `when`

expression, Kotlin’s version of a `switch`

statement.)

Then I converted my previous series of `if`

conditions into a loop over the descending powers of ten from 1000 down to 1. As soon as I found a match I looked up the symbol and repeated it the correct number of times.

```
fun Int.toRoman(): String {
val powersOfTen = listOf(1000, 100, 10, 1)
powersOfTen.forEach { unit ->
if (this >= unit) {
return symbolFor(unit).repeat(this / unit)
}
}
throw RomanNumeralException(
"Can't yet convert ${this} to Roman numerals")
}
```

Tests were still green. I wasn’t entirely happy – it seemed less readable than before – but, impatient, I pressed on.

#### Place values

Still ignoring the fives and subtraction-based numerals, I decided my next task was to turn 11 into 10+1 ⇒ XI. I wrote the test and started to consider the solution.

Until now, my `toRoman`

function returned a value as soon as it found a power of ten that fit the number being converted. I could have used a local, mutable variable to build up the numeral, something like this:

```
fun Int.toRoman(): String {
val powersOfTen = listOf(1000, 100, 10, 1)
var roman = ""
var number = this
powersOfTen.forEach { unit ->
val howManyTimes = number / unit
number = number % unit
roman = roman + symbolFor(unit).repeat(howManyTimes)
}
return roman
}
```

I wanted to find a more functional style, though. I thought about the way these longer numerals are composed and realised that if I could convert a number into how many each of thousands, hundreds, tens and ones were in it, I could map across those values and join the results. For example:

```
3012 = 3000 + 10 + 2
= 3×1000 + 1×10 + 2×1
⇒ 3×M + 1×X + 2×I
⇒ MMMXII
```

This reminded me of learning about numbers at primary school, we would talk about “hundreds, tens and units”. I googled around a bit and found that this is known as positional notation or place-value notation.

So I needed to decompose an integer such as 2013 into its component “place values”. Then I could map across the list of place values and convert each one into its Roman numeral representation, then just concatenate them.

I deleted my test for 11 ⇒ XI while I worked on this new `toDecimalPlaceValues`

function.

First, then, I would need a test for this function.

```
@Test
fun testPlaceValues() {
val placeValues: List<Int> = toDecimalPlaceValues(14)
assertEquals("10+4", placeValues.joinToString("+"))
}
```

I chose to test against this `10+4`

representation because it was highly readable (actually, would be more so once I parameterised the test).

To implement `toDecimalPlaceValues`

I actually used some very similar logic to my possible solution shown earlier in this section) but applying recursion rather than mutating local variables:

```
private val powersOfTen = listOf(1000, 100, 10, 1)
fun toDecimalPlaceValues(number: Int): List<Int> {
val largestPowerNeeded = powersOfTen.find { number > it } ?: 1
if (largestPowerNeeded == 1) {
return listOf(number)
} else {
val multiplier = number / largestPowerNeeded
val remainder = number % largestPowerNeeded
val placeValueForThisPower = multiplier * largestPowerNeeded
return listOf(placeValueForThisPower) +
toDecimalPlaceValues(remainder)
}
}
```

By the time I was done I had the following tests:

```
@Test
@Parameters
fun testPlaceValues(number: Int, placeValueRepresentation: String) {
assertEquals(formatPlaceValues(toDecimalPlaceValues(number)), placeValueRepresentation)
}
private fun parametersForTestPlaceValues() =
arrayOf(
arrayOf(9, "9"),
arrayOf(14, "10+4"),
arrayOf(4025, "4000+20+5")
)
private fun formatPlaceValues(placeValues: List<Int>) =
placeValues.joinToString(separator = "+")
```

Based on my reading about place values and the terminology associated with numeric notation systems, I decided that my `symbolFor(Int)`

function name was not specific enough. It gives a Roman *digit* for an integer. So I renamed it `toRomanDigit`

and made it an extension function on `Int`

instead, which read more nicely (left-to-right):

```
return unit.toRomanDigit().repeat(this / unit)
```

#### Place Values: refinement

I reinstated the test for `toRoman`

with 11 ⇒ XI but quickly realised that having calculated the place values as integers (e.g. 3000 as the first place value in 3012) I would have to divide them again to determine how many times to repeat the digit. Perhaps I should preserve the two factors (3 and 1000) separately.

Again I deleted the test for 11 ⇒ XI and rewrote my `toDecimalPlaceValues`

function to construct a list of `PlaceValue`

classes:

```
class PlaceValue(val multiplier: Int, val unit: Int) {
override fun toString() = (multiplier * unit).toString()
}
fun toDecimalPlaceValues(number: Int): List<PlaceValue> {
val largestPowerNeeded = powersOfTen.find { number > it } ?: 1
if (largestPowerNeeded == 1) {
return listOf(PlaceValue(number, 1))
} else {
val multiplier = number / largestPowerNeeded
val remainder = number % largestPowerNeeded
return listOf(PlaceValue(multiplier, largestPowerNeeded))
+ toDecimalPlaceValues(remainder)
}
}
```

(In fact I made it a `data class`

but that wasn’t necessary.)

#### Overconfidence

I thought I was ready to plug `toDecimalPlaceValues`

into `toRoman`

and I’d finally solve 11 ⇒ XI.

```
fun Int.toRoman(): String {
val placeValues = toDecimalPlaceValues(this)
return placeValues.map {
it.unit.toRomanDigit().repeat(it.multiplier)
}.joinToString(separator = "")
}
```

I ran my tests… oh dear! Three tests failed:

```
java.lang.AssertionError: Incorrect conversion of 10 to Roman numerals. Expected <X>, actual <IIIIIIIIII>.
java.lang.AssertionError: Incorrect conversion of 100 to Roman numerals. Expected <C>, actual <XXXXXXXXXX>.
java.lang.AssertionError: Incorrect conversion of 1000 to Roman numerals. Expected <M>, actual <CCCCCCCCCC>.
```

It turned out that my `toDecimalPlaceValues`

function really didn’t like being given a simple value which should translate to 1 of a power of ten. Instead of 10 being converted to `PlaceValue(multiplier = 1, unit = 10)`

it was producing `PlaceValue(multiplier = 10, unit = 1)`

.

The way I had constructed my expected result in my tests meant that this bug hadn’t shown up.

I reverted my `toRoman`

changes again, and changed the `PlaceValue`

’s string representation to be more revealing:

```
TEST:
private fun parametersForTestPlaceValues() =
arrayOf(
arrayOf(9, "9*1"),
arrayOf(14, "1*10 + 4*1"),
arrayOf(4025, "4*1000 + 2*10 + 5*1"),
arrayOf(10, "1*10")
)
private fun formatPlaceValues(placeValues: List<PlaceValue>) =
placeValues.joinToString(separator = " + ")
IMPLEMENTATION:
data class PlaceValue(val multiplier: Int, val unit: Int) {
override fun toString() = "$multiplier*$unit"
}
```

Now I could see how `toDecimalPlaces(10)`

fails:

```
java.lang.AssertionError: Expected <1*10>, actual <10*1>.
```

Aha! When trying to work out the largest power needed to represent a number, I was finding the largest power of 10 *greater than* the number. I should have used *greater than or equal to*:

```
fun toDecimalPlaceValues(number: Int): List<PlaceValue> {
val largestPowerNeeded = powersOfTen.find { number >= it } ?: 1
...
}
```

Almost. Test still failing, but for a different reason:

```
java.lang.AssertionError: Expected <1*10>, actual <1*10 + 0*1>.
```

The recursive call for the base case (when `largestPowerNeeded == 1`

) was returning a `PlaceValue(0, 1)`

.

The simplest fix for this was just to insert a check for zero at the top of the function:

```
fun toDecimalPlaceValues(number: Int): List<PlaceValue> {
if (number == 0) {
return emptyList()
}
...
}
```

Tests all passing again! Hooray!

#### 11 ⇒ XI, at last!

I finally got to add my ninth test case for the `toRoman`

function.

My implementation was as I’d tried before, and this time it worked:

```
fun Int.toRoman(): String {
val placeValues = toDecimalPlaceValues(this)
return placeValues.map {
it.unit.toRomanDigit().repeat(it.multiplier)
}.joinToString(separator = "")
}
```

#### Testing

In TDD, you write a test that you expect to fail. In testing, you write or perform tests that you expect to pass.

I thought I’d nailed this place values stuff, at least for numbers containing (decimal) digits 0–3. I wanted to add a more complex number, though, just to reassure myself. I chose 3012 ⇒ MMMXII and was pleased to see that it passed.

#### To be continued…

I don’t think I’m consistently good at choosing the simplest thing required to make a test pass. However, I think I did introduce an appropriate domain model with the PlaceValue at a good point, which will help me complete the solution.

The key realisation was that each digit in the decimal representation of an integer could be *independently* mapped to a (possibly empty) string of Roman digits, and those strings simply concatenated in the same order to give the final Roman numeral representation. The mapping of a decimal digit works in exactly the same way but the Roman digits used vary depending upon the magnitude.

Hmm… writing that has made me think that `magnitude`

may be a better name than `unit`

in `PlaceValue`

, like this:

```
toDecimalPlaceValues(230) ==
listOf(PlaceValue(multiplier = 2, magnitude = 100),
PlaceValue(multiplier = 3, magnitude = 10))
```

Thanks, rubber ducks! Until next time… your comments are welcome!

*Image credit: photograph of Cleveland Bridge plaque, Bath by Jaggery is licensed for reuse under CC BY-SA 2.0*