I’ve been programming Perl for a while now, but I’ve seen lot of people around me (even Perl enthusiasts) getting very excited about Python.

In Newbay it’s used mainly for automated and performance tests, but some times also for some system engineering automation.

I can understand them, Python seems to be a pretty cool language, very clear and rigorous when compared to Perl, which tend to be messy (depending on the coder).

Python seems also to have more stuff into the default distribution which comes pre-installed on every Linux/Unix box. You already have http and xml libraries without the necessity of installing stuff (with Perl for example you don’t have LWP or any XML dom library by default and you have to install them through CPAN or your packaging system).

Also, with Jython is easier to interface with Java based stuff like JMS, etc. In the pat I’ve used some awful hacks using Inline::Java , they were working but, you know, they looked really scary.

I still do like Perl though, for doing simple and quick hacks, one-liners, etc.And I will probably continue to use it.

I’m currently reading this Diving into Python book, which is free to download or view online.

My colleague Brano has always tried to evangelize me, with no success so far (because of my chronic laziness), but today he gave me a nice puzzle which took me a couple of hours to solve 😛

I’m sharing it with you just in case you fancy the idea to solve it too. Feel free to send me the result at
pallotron (at) freaknet (dot) org 🙂

It’s called roman.py, it contains two functions called roman2dec and dec2roman, they should convert integers into roman numbers and viceversa.

Brano gave it to me with the excuse that, because I’m Italian, I should be pretty used with romans numbers and it wouldn’t take too much to solve it 😛

I started in the afternoon while at work, it took a 1 hour and half to me to figure out the algo for the dec2roman function, I don’t consider myself a math genius so excuse me if I was slow 😛

The roman2dec was a bit of a challenge, but after 3 glasses of nice white wine (Corvo Glicine) I started seen the pattern and I’ve nailed it 🙂

In vino veritas! ahaha!

You have to write the code of the two functions so that the all unit tests pass (there are also negatives tests that you should take care of), here is the code. Just copy paste in a file called roman.py and execute it with python roman.py, enjoy!

I’ll publish my script in a week or so (just to give you the time to play with it, PLEASE DO NOT CHEAT 🙂 ).

import unittest

# your global variables can go here, too ;)

def roman2dec(roman):
    assert isinstance(roman, str), 'Expected string argument'
    # your code goes here

    return None

def dec2roman(dec):
    assert isinstance(dec, int), 'Expected integer argument'
    # your code goes here

    return None

class RomanTests(unittest.TestCase):

    def testSimple(self):
        self.assertEqual(roman2dec('XXIV'), 24)
        self.assertEqual(roman2dec('mcmxcix'), 1999)
        self.assertEqual(dec2roman(13), 'XIII')
        self.assertEqual(dec2roman(78), 'LXXVIII')

    def testInvalid(self):
        self.assertRaises(ValueError, roman2dec, 'IIII')
        self.assertRaises(ValueError, roman2dec, 'VX')
        self.assertRaises(ValueError, roman2dec, 'VV')
        self.assertRaises(ValueError, roman2dec, 'IC')
        self.assertRaises(ValueError, dec2roman, -1)

    def testExhaustive(self):
        for i in range(4000):
            self.assertEqual(roman2dec(dec2roman(i)), i)

    def testExhaustive2(self):
        letters = ('I', 'V', 'X', 'L', 'C', 'D', 'M')
        first_example = None
        count = 0
        for i in xrange(8*8*8*8*8):
            rom = ''
            while True:             # do { ... } while i>0
                rom += letters[i % 7]
                i /= 7
                if i<=0: break

                dec = roman2dec(rom)
                roman = dec2roman(dec)
                if rom <> roman:
                    if first_example is None:
                        first_example = '%s != %s (decimal %d)' % (rom, roman, dec)
                    count += 1
            except ValueError, e:

        self.assertEqual(count, 0, 'Accepted %d invalid roman numbers (e.g. %s)' % (count, first_example))

if __name__ == '__main__':


There much more invalid roman numbers that you have to check, the quiz has been updated above :P

16 thoughts on “SPQR”

  1. 🙂 what do you mean by “atomic bombs?” Do you refer to regex? I encoded most of the knowledge of the conversion in the regex and in the table, in order to make the code clear and rid of special cases.

    • Did you try to execute your code using the last version of the test unit? (i’ve modified the script attached to the article) 🙂

      Does it pass testExhaustive2?

  2. Here is my new version, to deal with all the possible invalid roman numbers, probably the solution is just stupid, i’ve put this at the end of the roman2dec function:

    if roman <> dec2roman(result): raise ValueError

    i just do a cross-check, because i know 100% that the dec2roman is correct, if the two results are different there is something wrong in the input of roman2dec. but again this is probably cheating 😛

    new solution here:

    http://paste.debian.net/102793/ (never expires)

  3. Hey lookee,

    i’ve updated the test with some more checks… there are lot of invalid roman numbers than i thought 😛
    run your solution against it 🙂
    I had to modify mine to pass the new tests 🙂


Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.