Welcome, guest | Sign In | My Account | Store | Cart
```'''
The int methods of this Roman class are overloaded to return new
Roman instances, so you can use it to perform wrap ints and
perform basic math, like this:

>>> x = Roman(3)
>>> y = Roman(30)

>>> print x, y
Roman(3, III) Roman(30, XXX)

>>> print x + y
Roman(33, XXXIII)

>>> print x * y
Roman(90, LXXXX)

>>> print y / x
Roman(10, X)

>>> print Roman(2) ** 3
Roman(8, VIII)

>>> print Roman(25) % 11
Roman(3, III)

>>> print Roman(-45)
Roman(-45, -DCCCCLV)

>>> sum(map(Roman, range(10)))
Roman(45)

>>> reduce(operator.mul, map(Roman, range(1, 5)))
Roman(24)

>>> print sum(map(Roman, range(10)))
Roman(45, XXXXV)

>>> print reduce(operator.mul, map(Roman, range(1, 5)))
Roman(24, XXIV)
'''

__author__ = "Thom"

import re

def returnthisclassfrom(methods):
'''
Class decorator by Alex Martelli.

see ('http://stackoverflow.com/questions/1242589/
subclassing-int-to-attain-a-hex-representation/1243045#1243045)
'''
def wrapit(cls, method):
return lambda *a: cls(method(*a))
def dowrap(cls):
for n in methods:
method = getattr(cls, n)
setattr(cls, n, wrapit(cls, method))
return cls

return dowrap

methods = "mul add sub div pow mod divmod".split()
methods += [c + s for s in methods for c in list('ir')]
methods = ['__%s__' % s for s in methods]
methods = set(methods) & set(dir(int))

@returnthisclassfrom(methods)
class Roman(int):

i_ones = range(10)
r_ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
ones_i2r = dict(zip(i_ones, r_ones))

i_mul = [10, 50, 100, 500, 1000]
r_mul = ["X", "L", "C", "D", "M"]
mul_i2r = dict(zip(i_mul, r_mul))

i = i_ones + i_mul
r = r_ones + r_mul
rom2int = dict(zip(r, i))
re_rom = re.compile('|'.join(sorted(r, key=len, reverse=True)))

def as_roman(self):

sign = "-" if self < 0 else ""
res = [sign]
n = int(self)
mul_i2r = self.mul_i2r

for x in reversed(self.i_mul):

div, mod = divmod(n, x)
n = mod
res.append(mul_i2r[x] * div)

res.append(self.ones_i2r[n])

return "".join(res)

def __str__(self):
return "Roman(%d, %s)" % (self, self.as_roman())

def __repr__(self):
return "Roman(%d)" % self

@classmethod
def fromstring(cls, s):
return cls(sum(map(cls.rom2int.get, cls.re_rom.findall(s))))

if __name__ == "__main__":

for x in range(1, 1001):
r = Roman(x)
assert r == Roman.fromstring(r.as_roman())
print x, r

print '\n'

import operator
x, y = Roman(20), Roman(5)
print 'x =', x
print 'y = ', y

for s in "add sub div mul".split():
print '%s(x, y) = %s' % (s, getattr(operator, s)(x, y))

print '\n'

x -= 1
print 'x % y = ', x % y

```