## Task

Create a RomanNumerals class that can convert a roman numeral to and from an integer value.  It should follow the API demonstrated in the examples below. Multiple roman numeral values will be tested for each helper method. 

Modern Roman numerals are written by expressing each digit separately starting with the left most digit and skipping any digit with a value of zero. In Roman numerals 1990 is rendered: 1000=M, 900=CM, 90=XC; resulting in MCMXC. 2008 is written as 2000=MM, 8=VIII; or MMVIII. 1666 uses each Roman symbol in descending order: MDCLXVI.

## Examples

```python
RomanNumerals.to_roman(1000) # should return 'M'
RomanNumerals.from_roman('M') # should return 1000
```

## Help

| Symbol | Value |
|----------------|
| I          | 1     |
| V          | 5     |
| X          | 10    |
| L          | 50    |
| C          | 100   |
| D          | 500   |
| M          | 1000  |

Difficulty: MEDIUM

Starter code:
def to_roman(n):
	

Solutions:
import string
from collections import OrderedDict

class RomanNumerals:

	@classmethod
	def to_roman(self, num):
		conversions = OrderedDict([('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)])
		out = ''
		for (key, value) in conversions.items():
			while num >= value:
				out += key
				num -= value
		return out

	@classmethod
	def from_roman(self, roman):
		conversions = OrderedDict([('CM', 900), ('CD', 400), ('XC', 90), ('XL', 40), ('IX', 9), ('IV', 4), ('M', 1000), ('D', 500), ('C', 100), ('L', 50), ('X', 10), ('V', 5), ('I', 1)])
		out = 0
		for (key, value) in conversions.items():
			out += value * roman.count(key)
			roman = string.replace(roman, key, '')
		return out


from collections import OrderedDict
import re
ROMAN_NUMERALS = OrderedDict([('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)])
DECIMAL_TO_ROMAN = [(v, k) for (k, v) in list(ROMAN_NUMERALS.items())]
ROMAN_RE = '|'.join(ROMAN_NUMERALS)

class RomanNumerals(object):

	@staticmethod
	def from_roman(roman):
		return sum((ROMAN_NUMERALS[d] for d in re.findall(ROMAN_RE, roman)))

	@staticmethod
	def to_roman(decimal):
		result = []
		for (number, roman) in DECIMAL_TO_ROMAN:
			while decimal >= number:
				decimal -= number
				result.append(roman)
		return ''.join(result)


from itertools import groupby

class RomanNumerals(object):
	letters = [('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)]

	@classmethod
	def to_roman(cls, val):
		rom = []
		for (l, v) in cls.letters:
			m = val // v
			rom += m * [l]
			val -= m * v
		return ''.join(rom)

	@classmethod
	def from_roman(cls, rom):
		cumu = 0
		for (l, v) in cls.letters:
			while rom[:len(l)] == l:
				rom = rom[len(l):]
				cumu += v
			if not rom:
				break
		return cumu


class RomanNumerals:
	READ = {1: 'I', 5: 'V', 10: 'X', 50: 'L', 100: 'C', 500: 'D', 1000: 'M'}
	INTG = {v: k for (k, v) in list(READ.items())}

	@classmethod
	def to_roman(cls, num, rom=''):
		EDGE = {0: lambda x, elem, dict, number: dict.get(elem, '') * number, 1: lambda e, i, D, l: e + D[i * (l + 1)] if l in [4, 9] else D[i * l] if l is 5 else D[i * 5] + e * (l % 5)}
		for element in list(cls.READ.keys())[0::2][::-1]:
			left = num // element
			rom += EDGE.get(left > 3, '')(cls.READ[element], element, cls.READ, left)
			num %= element
		return rom

	@classmethod
	def from_roman(cls, roma):
		cls.INTG.update({'N': 0})
		roma += 'N'
		return sum([-cls.INTG[x] if cls.INTG[x] < cls.INTG[z] else cls.INTG[x] for (x, z) in zip(roma[:-1], roma[1:])])


class RomanNumerals:

	@staticmethod
	def from_roman(s):
		X = [dict(zip('MDCLXVI', (1000.0, 500, 100, 50, 10, 5, 1)))[x] for x in s]
		return int(sum(((x, -x)[x < y] for (x, y) in zip(X, X[1:]))) + X[-1])

	@staticmethod
	def to_roman(i, o=' I II III IV V VI VII VIII IX'.split(' ')):
		r = lambda n: o[n] if n < 10 else ''.join((dict(zip('IVXLC', 'XLCDM'))[c] for c in r(n // 10))) + o[n % 10]
		return r(i)


class RomanNumerals:

	@staticmethod
	def to_roman(num):
		ints = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
		nums = ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')
		result = ''
		for i in range(len(ints)):
			count = int(num / ints[i])
			result += str(nums[i] * count)
			num -= ints[i] * count
		return result

	@staticmethod
	def from_roman(roman):
		nums = ['M', 'D', 'C', 'L', 'X', 'V', 'I']
		ints = [1000, 500, 100, 50, 10, 5, 1]
		places = []
		for i in range(len(roman)):
			c = roman[i]
			value = ints[nums.index(c)]
			try:
				next_val = ints[nums.index(roman[i + 1])]
				if next_val > value:
					value *= -1
			except:
				pass
			places.append(value)
		return sum(places)


class RomanNumerals(object):

	@classmethod
	def to_roman(self, n, result='', i=0):
		SPQR = ((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'))
		if i == len(SPQR):
			return result
		while n - SPQR[i][0] >= 0:
			result += SPQR[i][1]
			n -= SPQR[i][0]
		i += 1
		return self.to_roman(n, result, i)

	@classmethod
	def from_roman(self, roman, result=0, i=0):
		SPQR = ((900, 'CM'), (1000, 'M'), (400, 'CD'), (500, 'D'), (90, 'XC'), (100, 'C'), (50, 'L'), (40, 'XL'), (9, 'IX'), (10, 'X'), (4, 'IV'), (5, 'V'), (1, 'I'))
		if roman == '':
			return result
		while SPQR[i][1] in roman:
			result += SPQR[i][0]
			roman = roman[len(SPQR[i][1]):]
		i += 1
		return self.from_roman(roman, result, i)


from functools import reduce

class RomanNumerals:
	_map = {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'}

	@classmethod
	def to_roman(cls, n):
		_map = cls._map
		red = lambda s, e: (s[0] + _map[e] * (s[1] // e), s[1] - e * (s[1] // e))
		return reduce(red, reversed(sorted(_map)), ['', n])[0]

	@classmethod
	def from_roman(cls, s):
		_map = {v: k for (k, v) in cls._map.items()}
		red = lambda s, e: s[:-1] + [s[-1] + e] if s and s[-1] + e in _map else s + [e]
		return sum((_map[x] for x in reduce(red, s, [])))


from itertools import groupby, zip_longest
FROM_ROMAN = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1}
TO_ROMAN = ((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'))

class RomanNumerals(object):

	@staticmethod
	def to_roman(n):
		result = []
		for (num, char) in TO_ROMAN:
			(q, n) = divmod(n, num)
			result.append(q * char)
		return ''.join(result)

	@staticmethod
	def from_roman(roman):
		pairs = [sum(g) for (_, g) in groupby((FROM_ROMAN[a] for a in roman))]
		return sum((a + b if a > b else b - a for (a, b) in zip_longest(pairs[::2], pairs[1::2], fillvalue=0)))


class RomanNumerals:
	dic = [(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')]

	@staticmethod
	def to_roman(n):
		res = []
		for i in RomanNumerals.dic:
			res.extend(n / i[0] * i[1])
			n -= n / i[0] * i[0]
		return ''.join(res)

	@staticmethod
	def from_roman(n):
		res = 0
		for i in RomanNumerals.dic:
			while n.startswith(i[1]):
				res += i[0]
				n = n[len(i[1]):]
		return res