From 61f57d70afacb126fb48090b1b0f8e6189e957ed Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Sun, 18 Apr 2021 02:41:08 +0530 Subject: [PATCH 01/17] Implemented parse_roman() function --- number_parser/__init__.py | 2 +- number_parser/parser.py | 37 +++++++++++++++++++++++++++++++++ tests/data/test_nemral_roman.py | 13 ++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/data/test_nemral_roman.py diff --git a/number_parser/__init__.py b/number_parser/__init__.py index 472179e..bd57de9 100644 --- a/number_parser/__init__.py +++ b/number_parser/__init__.py @@ -1 +1 @@ -from number_parser.parser import parse, parse_number, parse_ordinal, parse_fraction +from number_parser.parser import parse, parse_number, parse_ordinal, parse_fraction, parse_roman diff --git a/number_parser/parser.py b/number_parser/parser.py index e0d67c2..df3d831 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -1,6 +1,7 @@ import re from importlib import import_module import unicodedata + SENTENCE_SEPARATORS = [".", ","] SUPPORTED_LANGUAGES = ['en', 'es', 'hi', 'ru'] RE_BUG_LANGUAGES = ['hi'] @@ -298,6 +299,42 @@ def parse_fraction(input_string, language=None): return None +def parse_roman(input_string): + tokens = _tokenize(input_string, None) + + def build_roman(roman_number): + roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} + num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) + num_tokens = [item for item in num_tokens if item != ''] + built_num = 0 + for num_token in num_tokens: + if len(num_token) == 1: + built_num += roman[num_token] + elif len(num_token) == 2: + if (roman[num_token[0]]) > (roman[num_token[1]]): + built_num += roman[num_token[0]] + roman[num_token[1]] + elif (roman[num_token[0]]) < (roman[num_token[1]]): + built_num += roman[num_token[1]] - roman[num_token[0]] + else: + built_num += (roman[num_token[0]]) * 2 + elif len(num_token) == 3: + if (roman[num_token[0]]) > (roman[num_token[1]]): + built_num += roman[num_token[0]] + (2 * roman[num_token[1]]) + else: + built_num += (roman[num_token[0]]) * 3 + elif len(num_token) == 4: + built_num += roman[num_token[0]] + (3 * roman[num_token[1]]) + + return built_num + + for token in tokens: + if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): + tokens[tokens.index(token)] = str(build_roman(token)) + final_sentence = ''.join(tokens) + + return final_sentence + + def parse(input_string, language=None): """ Converts all the numbers in a sentence written in natural language to their numeric type while keeping diff --git a/tests/data/test_nemral_roman.py b/tests/data/test_nemral_roman.py new file mode 100644 index 0000000..88322b9 --- /dev/null +++ b/tests/data/test_nemral_roman.py @@ -0,0 +1,13 @@ +import pytest +from number_parser import parse_roman + +@pytest.mark.parametrize( + "test_string, expected", + [ + ('CDXX', '420'), + ('lxix', '69'), + ('Built in MMLXXVII', 'Built in 2077'), + ] +) +def test_parse_roman(test_string, expected): + assert parse_roman(test_string) == expected From 6664b6fab4ab7b17196b811419b89103d22b7006 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Sun, 18 Apr 2021 02:49:25 +0530 Subject: [PATCH 02/17] Moved test_numeral_roman.py --- tests/{data => }/test_nemral_roman.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{data => }/test_nemral_roman.py (100%) diff --git a/tests/data/test_nemral_roman.py b/tests/test_nemral_roman.py similarity index 100% rename from tests/data/test_nemral_roman.py rename to tests/test_nemral_roman.py From 376db4043a2c5d2a1c90388b03db1c774b433bd0 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Sun, 18 Apr 2021 09:44:43 +0530 Subject: [PATCH 03/17] Implemented roman_numera()l function I have implemented roman_numeral() function with the use of regex to build the number. --- number_parser/parser.py | 22 ++++++---------------- tests/test_nemral_roman.py | 1 + 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/number_parser/parser.py b/number_parser/parser.py index df3d831..473ca5a 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -308,22 +308,12 @@ def build_roman(roman_number): num_tokens = [item for item in num_tokens if item != ''] built_num = 0 for num_token in num_tokens: - if len(num_token) == 1: - built_num += roman[num_token] - elif len(num_token) == 2: - if (roman[num_token[0]]) > (roman[num_token[1]]): - built_num += roman[num_token[0]] + roman[num_token[1]] - elif (roman[num_token[0]]) < (roman[num_token[1]]): - built_num += roman[num_token[1]] - roman[num_token[0]] - else: - built_num += (roman[num_token[0]]) * 2 - elif len(num_token) == 3: - if (roman[num_token[0]]) > (roman[num_token[1]]): - built_num += roman[num_token[0]] + (2 * roman[num_token[1]]) - else: - built_num += (roman[num_token[0]]) * 3 - elif len(num_token) == 4: - built_num += roman[num_token[0]] + (3 * roman[num_token[1]]) + if re.search('iv|ix|xl|xc|cd|cm', num_token): + built_num += roman[num_token[1]] - roman[num_token[0]] + elif re.search('[vld][ixc]{1,3}', num_token): + built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) + else: + built_num += roman[num_token[0]] * len(num_token) return built_num diff --git a/tests/test_nemral_roman.py b/tests/test_nemral_roman.py index 88322b9..dd80d3d 100644 --- a/tests/test_nemral_roman.py +++ b/tests/test_nemral_roman.py @@ -1,6 +1,7 @@ import pytest from number_parser import parse_roman + @pytest.mark.parametrize( "test_string, expected", [ From ce4e5c65df448c5777afac3ce9c0878741718756 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Sun, 25 Apr 2021 03:00:30 +0530 Subject: [PATCH 04/17] Adding numeral_system as a parameter to functions --- number_parser/__init__.py | 2 +- number_parser/parser.py | 204 +- test.py | 3 + tests/data/all_roman_numbers.csv | 3999 ++++++++++++++++++++++++++++++ tests/test_nemral_roman.py | 14 - tests/test_numeral_roman.py | 20 + 6 files changed, 4128 insertions(+), 114 deletions(-) create mode 100644 test.py create mode 100644 tests/data/all_roman_numbers.csv delete mode 100644 tests/test_nemral_roman.py create mode 100644 tests/test_numeral_roman.py diff --git a/number_parser/__init__.py b/number_parser/__init__.py index bd57de9..472179e 100644 --- a/number_parser/__init__.py +++ b/number_parser/__init__.py @@ -1 +1 @@ -from number_parser.parser import parse, parse_number, parse_ordinal, parse_fraction, parse_roman +from number_parser.parser import parse, parse_number, parse_ordinal, parse_fraction diff --git a/number_parser/parser.py b/number_parser/parser.py index 473ca5a..c4ab6c5 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -6,7 +6,6 @@ SUPPORTED_LANGUAGES = ['en', 'es', 'hi', 'ru'] RE_BUG_LANGUAGES = ['hi'] - class LanguageData: """Main language class to populate the requisite language-specific variables.""" unit_numbers = {} @@ -228,21 +227,46 @@ def _valid_tokens_by_language(input_string): return 'en' return best_language +def _parse_roman(input_string): + tokens = _tokenize(input_string, None) + + def build_roman(roman_number): + roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} + num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) + num_tokens = [item for item in num_tokens if item != ''] + built_num = 0 + for num_token in num_tokens: + if re.search('iv|ix|xl|xc|cd|cm', num_token): + built_num += roman[num_token[1]] - roman[num_token[0]] + elif re.search('[vld][ixc]{1,3}', num_token): + built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) + else: + built_num += roman[num_token[0]] * len(num_token) + + return built_num + + for token in tokens: + if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): + tokens[tokens.index(token)] = str(build_roman(token)) + final_sentence = ''.join(tokens) + + return final_sentence -def parse_ordinal(input_string, language=None): +def parse_ordinal(input_string, language=None, numeral_system = 'decimal'): """Converts a single number in ordinal or cardinal form to it's numeric equivalent""" - if language is None: - language = _valid_tokens_by_language(input_string) + if numeral_system == 'decimal': + if language is None: + language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) - tokens = _tokenize(input_string, language) - normalized_tokens = _normalize_tokens(tokens) - processed_tokens = [_apply_cardinal_conversion(token, lang_data) for token in normalized_tokens] - output_string = ' '.join(processed_tokens) - return parse_number(output_string, language) + lang_data = LanguageData(language) + tokens = _tokenize(input_string, language) + normalized_tokens = _normalize_tokens(tokens) + processed_tokens = [_apply_cardinal_conversion(token, lang_data) for token in normalized_tokens] + output_string = ' '.join(processed_tokens) + return parse_number(output_string, language) -def parse_number(input_string, language=None): +def parse_number(input_string, language=None, numeral_system = 'decimal'): """Converts a single number written in natural language to a numeric type""" if not input_string.strip(): return None @@ -250,23 +274,27 @@ def parse_number(input_string, language=None): if input_string.strip().isnumeric(): return int(input_string) - if language is None: - language = _valid_tokens_by_language(input_string) + if numeral_system == 'decimal': + if language is None: + language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) + lang_data = LanguageData(language) - tokens = _tokenize(input_string, language) - normalized_tokens = _normalize_tokens(tokens) - for index, token in enumerate(normalized_tokens): - if _is_cardinal_token(token, lang_data) or not token.strip(): - continue - if _is_skip_token(token, lang_data) and index != 0: - continue + tokens = _tokenize(input_string, language) + normalized_tokens = _normalize_tokens(tokens) + for index, token in enumerate(normalized_tokens): + if _is_cardinal_token(token, lang_data) or not token.strip(): + continue + if _is_skip_token(token, lang_data) and index != 0: + continue + return None + number_built = _build_number(normalized_tokens, lang_data) + if len(number_built) == 1: + return int(number_built[0]) return None - number_built = _build_number(normalized_tokens, lang_data) - if len(number_built) == 1: - return int(number_built[0]) - return None + + elif numeral_system == 'roman': + return int(_parse_roman(input_string)) def parse_fraction(input_string, language=None): @@ -299,95 +327,73 @@ def parse_fraction(input_string, language=None): return None -def parse_roman(input_string): - tokens = _tokenize(input_string, None) - - def build_roman(roman_number): - roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} - num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) - num_tokens = [item for item in num_tokens if item != ''] - built_num = 0 - for num_token in num_tokens: - if re.search('iv|ix|xl|xc|cd|cm', num_token): - built_num += roman[num_token[1]] - roman[num_token[0]] - elif re.search('[vld][ixc]{1,3}', num_token): - built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) - else: - built_num += roman[num_token[0]] * len(num_token) - - return built_num - - for token in tokens: - if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): - tokens[tokens.index(token)] = str(build_roman(token)) - final_sentence = ''.join(tokens) - - return final_sentence - - -def parse(input_string, language=None): +def parse(input_string, language=None, numeral_system = 'decimal'): """ Converts all the numbers in a sentence written in natural language to their numeric type while keeping the other words unchanged. Returns the transformed string. """ - if language is None: - language = _valid_tokens_by_language(input_string) + if numeral_system == 'decimal': + if language is None: + language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) + lang_data = LanguageData(language) - tokens = _tokenize(input_string, language) + tokens = _tokenize(input_string, language) - final_sentence = [] - current_sentence = [] - tokens_taken = [] + final_sentence = [] + current_sentence = [] + tokens_taken = [] - def _build_and_add_number(pop_last_space=False): - if tokens_taken: - result = _build_number(tokens_taken, lang_data) - tokens_taken.clear() + def _build_and_add_number(pop_last_space=False): + if tokens_taken: + result = _build_number(tokens_taken, lang_data) + tokens_taken.clear() - for number in result: - current_sentence.extend([number, " "]) + for number in result: + current_sentence.extend([number, " "]) - if pop_last_space: - current_sentence.pop() + if pop_last_space: + current_sentence.pop() - for token in tokens: - compare_token = _strip_accents(token.lower()) - ordinal_number = _is_ordinal_token(compare_token, lang_data) + for token in tokens: + compare_token = _strip_accents(token.lower()) + ordinal_number = _is_ordinal_token(compare_token, lang_data) - if not compare_token.strip(): - if not tokens_taken: - current_sentence.append(token) - continue + if not compare_token.strip(): + if not tokens_taken: + current_sentence.append(token) + continue - if compare_token in SENTENCE_SEPARATORS: - _build_and_add_number(pop_last_space=True) - current_sentence.append(token) - final_sentence.extend(current_sentence) - current_sentence = [] - continue + if compare_token in SENTENCE_SEPARATORS: + _build_and_add_number(pop_last_space=True) + current_sentence.append(token) + final_sentence.extend(current_sentence) + current_sentence = [] + continue + + if ordinal_number: + tokens_taken.append(ordinal_number) + _build_and_add_number(pop_last_space=True) + elif ( + _is_cardinal_token(compare_token, lang_data) + or (_is_skip_token(compare_token, lang_data) and len(tokens_taken) != 0) + ): + tokens_taken.append(compare_token) + else: + if tokens_taken and _is_skip_token(tokens_taken[-1], lang_data): + # when finishing with a skip_token --> keep it + skip_token = tokens_taken[-1] + tokens_taken.pop() + _build_and_add_number() + current_sentence.extend([skip_token, " "]) - if ordinal_number: - tokens_taken.append(ordinal_number) - _build_and_add_number(pop_last_space=True) - elif ( - _is_cardinal_token(compare_token, lang_data) - or (_is_skip_token(compare_token, lang_data) and len(tokens_taken) != 0) - ): - tokens_taken.append(compare_token) - else: - if tokens_taken and _is_skip_token(tokens_taken[-1], lang_data): - # when finishing with a skip_token --> keep it - skip_token = tokens_taken[-1] - tokens_taken.pop() _build_and_add_number() - current_sentence.extend([skip_token, " "]) + current_sentence.append(token) - _build_and_add_number() - current_sentence.append(token) + _build_and_add_number() - _build_and_add_number() + final_sentence.extend(current_sentence) + return ''.join(final_sentence).strip() - final_sentence.extend(current_sentence) - return ''.join(final_sentence).strip() + elif numeral_system == 'roman': + return _parse_roman(input_string) diff --git a/test.py b/test.py new file mode 100644 index 0000000..b84c884 --- /dev/null +++ b/test.py @@ -0,0 +1,3 @@ +from number_parser import parse ,parse_number + +print(parse_number('built in CDXX', numeral_system='roman')) diff --git a/tests/data/all_roman_numbers.csv b/tests/data/all_roman_numbers.csv new file mode 100644 index 0000000..ba85178 --- /dev/null +++ b/tests/data/all_roman_numbers.csv @@ -0,0 +1,3999 @@ +number,text +1,I +2,II +3,III +4,IV +5,V +6,VI +7,VII +8,VIII +9,IX +10,X +11,XI +12,XII +13,XIII +14,XIV +15,XV +16,XVI +17,XVII +18,XVIII +19,XIX +20,XX +21,XXI +22,XXII +23,XXIII +24,XXIV +25,XXV +26,XXVI +27,XXVII +28,XXVIII +29,XXIX +30,XXX +31,XXXI +32,XXXII +33,XXXIII +34,XXXIV +35,XXXV +36,XXXVI +37,XXXVII +38,XXXVIII +39,XXXIX +40,XL +41,XLI +42,XLII +43,XLIII +44,XLIV +45,XLV +46,XLVI +47,XLVII +48,XLVIII +49,XLIX +50,L +51,LI +52,LII +53,LIII +54,LIV +55,LV +56,LVI +57,LVII +58,LVIII +59,LIX +60,LX +61,LXI +62,LXII +63,LXIII +64,LXIV +65,LXV +66,LXVI +67,LXVII +68,LXVIII +69,LXIX +70,LXX +71,LXXI +72,LXXII +73,LXXIII +74,LXXIV +75,LXXV +76,LXXVI +77,LXXVII +78,LXXVIII +79,LXXIX +80,LXXX +81,LXXXI +82,LXXXII +83,LXXXIII +84,LXXXIV +85,LXXXV +86,LXXXVI +87,LXXXVII +88,LXXXVIII +89,LXXXIX +90,XC +91,XCI +92,XCII +93,XCIII +94,XCIV +95,XCV +96,XCVI +97,XCVII +98,XCVIII +99,XCIX +100,C +101,CI +102,CII +103,CIII +104,CIV +105,CV +106,CVI +107,CVII +108,CVIII +109,CIX +110,CX +111,CXI +112,CXII +113,CXIII +114,CXIV +115,CXV +116,CXVI +117,CXVII +118,CXVIII +119,CXIX +120,CXX +121,CXXI +122,CXXII +123,CXXIII +124,CXXIV +125,CXXV +126,CXXVI +127,CXXVII +128,CXXVIII +129,CXXIX +130,CXXX +131,CXXXI +132,CXXXII +133,CXXXIII +134,CXXXIV +135,CXXXV +136,CXXXVI +137,CXXXVII +138,CXXXVIII +139,CXXXIX +140,CXL +141,CXLI +142,CXLII +143,CXLIII +144,CXLIV +145,CXLV +146,CXLVI +147,CXLVII +148,CXLVIII +149,CXLIX +150,CL +151,CLI +152,CLII +153,CLIII +154,CLIV +155,CLV +156,CLVI +157,CLVII +158,CLVIII +159,CLIX +160,CLX +161,CLXI +162,CLXII +163,CLXIII +164,CLXIV +165,CLXV +166,CLXVI +167,CLXVII +168,CLXVIII +169,CLXIX +170,CLXX +171,CLXXI +172,CLXXII +173,CLXXIII +174,CLXXIV +175,CLXXV +176,CLXXVI +177,CLXXVII +178,CLXXVIII +179,CLXXIX +180,CLXXX +181,CLXXXI +182,CLXXXII +183,CLXXXIII +184,CLXXXIV +185,CLXXXV +186,CLXXXVI +187,CLXXXVII +188,CLXXXVIII +189,CLXXXIX +190,CXC +191,CXCI +192,CXCII +193,CXCIII +194,CXCIV +195,CXCV +196,CXCVI +197,CXCVII +198,CXCVIII +199,CXCIX +200,CC +201,CCI +202,CCII +203,CCIII +204,CCIV +205,CCV +206,CCVI +207,CCVII +208,CCVIII +209,CCIX +210,CCX +211,CCXI +212,CCXII +213,CCXIII +214,CCXIV +215,CCXV +216,CCXVI +217,CCXVII +218,CCXVIII +219,CCXIX +220,CCXX +221,CCXXI +222,CCXXII +223,CCXXIII +224,CCXXIV +225,CCXXV +226,CCXXVI +227,CCXXVII +228,CCXXVIII +229,CCXXIX +230,CCXXX +231,CCXXXI +232,CCXXXII +233,CCXXXIII +234,CCXXXIV +235,CCXXXV +236,CCXXXVI +237,CCXXXVII +238,CCXXXVIII +239,CCXXXIX +240,CCXL +241,CCXLI +242,CCXLII +243,CCXLIII +244,CCXLIV +245,CCXLV +246,CCXLVI +247,CCXLVII +248,CCXLVIII +249,CCXLIX +250,CCL +251,CCLI +252,CCLII +253,CCLIII +254,CCLIV +255,CCLV +256,CCLVI +257,CCLVII +258,CCLVIII +259,CCLIX +260,CCLX +261,CCLXI +262,CCLXII +263,CCLXIII +264,CCLXIV +265,CCLXV +266,CCLXVI +267,CCLXVII +268,CCLXVIII +269,CCLXIX +270,CCLXX +271,CCLXXI +272,CCLXXII +273,CCLXXIII +274,CCLXXIV +275,CCLXXV +276,CCLXXVI +277,CCLXXVII +278,CCLXXVIII +279,CCLXXIX +280,CCLXXX +281,CCLXXXI +282,CCLXXXII +283,CCLXXXIII +284,CCLXXXIV +285,CCLXXXV +286,CCLXXXVI +287,CCLXXXVII +288,CCLXXXVIII +289,CCLXXXIX +290,CCXC +291,CCXCI +292,CCXCII +293,CCXCIII +294,CCXCIV +295,CCXCV +296,CCXCVI +297,CCXCVII +298,CCXCVIII +299,CCXCIX +300,CCC +301,CCCI +302,CCCII +303,CCCIII +304,CCCIV +305,CCCV +306,CCCVI +307,CCCVII +308,CCCVIII +309,CCCIX +310,CCCX +311,CCCXI +312,CCCXII +313,CCCXIII +314,CCCXIV +315,CCCXV +316,CCCXVI +317,CCCXVII +318,CCCXVIII +319,CCCXIX +320,CCCXX +321,CCCXXI +322,CCCXXII +323,CCCXXIII +324,CCCXXIV +325,CCCXXV +326,CCCXXVI +327,CCCXXVII +328,CCCXXVIII +329,CCCXXIX +330,CCCXXX +331,CCCXXXI +332,CCCXXXII +333,CCCXXXIII +334,CCCXXXIV +335,CCCXXXV +336,CCCXXXVI +337,CCCXXXVII +338,CCCXXXVIII +339,CCCXXXIX +340,CCCXL +341,CCCXLI +342,CCCXLII +343,CCCXLIII +344,CCCXLIV +345,CCCXLV +346,CCCXLVI +347,CCCXLVII +348,CCCXLVIII +349,CCCXLIX +350,CCCL +351,CCCLI +352,CCCLII +353,CCCLIII +354,CCCLIV +355,CCCLV +356,CCCLVI +357,CCCLVII +358,CCCLVIII +359,CCCLIX +360,CCCLX +361,CCCLXI +362,CCCLXII +363,CCCLXIII +364,CCCLXIV +365,CCCLXV +366,CCCLXVI +367,CCCLXVII +368,CCCLXVIII +369,CCCLXIX +370,CCCLXX +371,CCCLXXI +372,CCCLXXII +373,CCCLXXIII +374,CCCLXXIV +375,CCCLXXV +376,CCCLXXVI +377,CCCLXXVII +378,CCCLXXVIII +379,CCCLXXIX +380,CCCLXXX +381,CCCLXXXI +382,CCCLXXXII +383,CCCLXXXIII +384,CCCLXXXIV +385,CCCLXXXV +386,CCCLXXXVI +387,CCCLXXXVII +388,CCCLXXXVIII +389,CCCLXXXIX +390,CCCXC +391,CCCXCI +392,CCCXCII +393,CCCXCIII +394,CCCXCIV +395,CCCXCV +396,CCCXCVI +397,CCCXCVII +398,CCCXCVIII +399,CCCXCIX +400,CD +401,CDI +402,CDII +403,CDIII +404,CDIV +405,CDV +406,CDVI +407,CDVII +408,CDVIII +409,CDIX +410,CDX +411,CDXI +412,CDXII +413,CDXIII +414,CDXIV +415,CDXV +416,CDXVI +417,CDXVII +418,CDXVIII +419,CDXIX +420,CDXX +421,CDXXI +422,CDXXII +423,CDXXIII +424,CDXXIV +425,CDXXV +426,CDXXVI +427,CDXXVII +428,CDXXVIII +429,CDXXIX +430,CDXXX +431,CDXXXI +432,CDXXXII +433,CDXXXIII +434,CDXXXIV +435,CDXXXV +436,CDXXXVI +437,CDXXXVII +438,CDXXXVIII +439,CDXXXIX +440,CDXL +441,CDXLI +442,CDXLII +443,CDXLIII +444,CDXLIV +445,CDXLV +446,CDXLVI +447,CDXLVII +448,CDXLVIII +449,CDXLIX +450,CDL +451,CDLI +452,CDLII +453,CDLIII +454,CDLIV +455,CDLV +456,CDLVI +457,CDLVII +458,CDLVIII +459,CDLIX +460,CDLX +461,CDLXI +462,CDLXII +463,CDLXIII +464,CDLXIV +465,CDLXV +466,CDLXVI +467,CDLXVII +468,CDLXVIII +469,CDLXIX +470,CDLXX +471,CDLXXI +472,CDLXXII +473,CDLXXIII +474,CDLXXIV +475,CDLXXV +476,CDLXXVI +477,CDLXXVII +478,CDLXXVIII +479,CDLXXIX +480,CDLXXX +481,CDLXXXI +482,CDLXXXII +483,CDLXXXIII +484,CDLXXXIV +485,CDLXXXV +486,CDLXXXVI +487,CDLXXXVII +488,CDLXXXVIII +489,CDLXXXIX +490,CDXC +491,CDXCI +492,CDXCII +493,CDXCIII +494,CDXCIV +495,CDXCV +496,CDXCVI +497,CDXCVII +498,CDXCVIII +499,CDXCIX +500,D +501,DI +502,DII +503,DIII +504,DIV +505,DV +506,DVI +507,DVII +508,DVIII +509,DIX +510,DX +511,DXI +512,DXII +513,DXIII +514,DXIV +515,DXV +516,DXVI +517,DXVII +518,DXVIII +519,DXIX +520,DXX +521,DXXI +522,DXXII +523,DXXIII +524,DXXIV +525,DXXV +526,DXXVI +527,DXXVII +528,DXXVIII +529,DXXIX +530,DXXX +531,DXXXI +532,DXXXII +533,DXXXIII +534,DXXXIV +535,DXXXV +536,DXXXVI +537,DXXXVII +538,DXXXVIII +539,DXXXIX +540,DXL +541,DXLI +542,DXLII +543,DXLIII +544,DXLIV +545,DXLV +546,DXLVI +547,DXLVII +548,DXLVIII +549,DXLIX +550,DL +551,DLI +552,DLII +553,DLIII +554,DLIV +555,DLV +556,DLVI +557,DLVII +558,DLVIII +559,DLIX +560,DLX +561,DLXI +562,DLXII +563,DLXIII +564,DLXIV +565,DLXV +566,DLXVI +567,DLXVII +568,DLXVIII +569,DLXIX +570,DLXX +571,DLXXI +572,DLXXII +573,DLXXIII +574,DLXXIV +575,DLXXV +576,DLXXVI +577,DLXXVII +578,DLXXVIII +579,DLXXIX +580,DLXXX +581,DLXXXI +582,DLXXXII +583,DLXXXIII +584,DLXXXIV +585,DLXXXV +586,DLXXXVI +587,DLXXXVII +588,DLXXXVIII +589,DLXXXIX +590,DXC +591,DXCI +592,DXCII +593,DXCIII +594,DXCIV +595,DXCV +596,DXCVI +597,DXCVII +598,DXCVIII +599,DXCIX +600,DC +601,DCI +602,DCII +603,DCIII +604,DCIV +605,DCV +606,DCVI +607,DCVII +608,DCVIII +609,DCIX +610,DCX +611,DCXI +612,DCXII +613,DCXIII +614,DCXIV +615,DCXV +616,DCXVI +617,DCXVII +618,DCXVIII +619,DCXIX +620,DCXX +621,DCXXI +622,DCXXII +623,DCXXIII +624,DCXXIV +625,DCXXV +626,DCXXVI +627,DCXXVII +628,DCXXVIII +629,DCXXIX +630,DCXXX +631,DCXXXI +632,DCXXXII +633,DCXXXIII +634,DCXXXIV +635,DCXXXV +636,DCXXXVI +637,DCXXXVII +638,DCXXXVIII +639,DCXXXIX +640,DCXL +641,DCXLI +642,DCXLII +643,DCXLIII +644,DCXLIV +645,DCXLV +646,DCXLVI +647,DCXLVII +648,DCXLVIII +649,DCXLIX +650,DCL +651,DCLI +652,DCLII +653,DCLIII +654,DCLIV +655,DCLV +656,DCLVI +657,DCLVII +658,DCLVIII +659,DCLIX +660,DCLX +661,DCLXI +662,DCLXII +663,DCLXIII +664,DCLXIV +665,DCLXV +666,DCLXVI +667,DCLXVII +668,DCLXVIII +669,DCLXIX +670,DCLXX +671,DCLXXI +672,DCLXXII +673,DCLXXIII +674,DCLXXIV +675,DCLXXV +676,DCLXXVI +677,DCLXXVII +678,DCLXXVIII +679,DCLXXIX +680,DCLXXX +681,DCLXXXI +682,DCLXXXII +683,DCLXXXIII +684,DCLXXXIV +685,DCLXXXV +686,DCLXXXVI +687,DCLXXXVII +688,DCLXXXVIII +689,DCLXXXIX +690,DCXC +691,DCXCI +692,DCXCII +693,DCXCIII +694,DCXCIV +695,DCXCV +696,DCXCVI +697,DCXCVII +698,DCXCVIII +699,DCXCIX +700,DCC +701,DCCI +702,DCCII +703,DCCIII +704,DCCIV +705,DCCV +706,DCCVI +707,DCCVII +708,DCCVIII +709,DCCIX +710,DCCX +711,DCCXI +712,DCCXII +713,DCCXIII +714,DCCXIV +715,DCCXV +716,DCCXVI +717,DCCXVII +718,DCCXVIII +719,DCCXIX +720,DCCXX +721,DCCXXI +722,DCCXXII +723,DCCXXIII +724,DCCXXIV +725,DCCXXV +726,DCCXXVI +727,DCCXXVII +728,DCCXXVIII +729,DCCXXIX +730,DCCXXX +731,DCCXXXI +732,DCCXXXII +733,DCCXXXIII +734,DCCXXXIV +735,DCCXXXV +736,DCCXXXVI +737,DCCXXXVII +738,DCCXXXVIII +739,DCCXXXIX +740,DCCXL +741,DCCXLI +742,DCCXLII +743,DCCXLIII +744,DCCXLIV +745,DCCXLV +746,DCCXLVI +747,DCCXLVII +748,DCCXLVIII +749,DCCXLIX +750,DCCL +751,DCCLI +752,DCCLII +753,DCCLIII +754,DCCLIV +755,DCCLV +756,DCCLVI +757,DCCLVII +758,DCCLVIII +759,DCCLIX +760,DCCLX +761,DCCLXI +762,DCCLXII +763,DCCLXIII +764,DCCLXIV +765,DCCLXV +766,DCCLXVI +767,DCCLXVII +768,DCCLXVIII +769,DCCLXIX +770,DCCLXX +771,DCCLXXI +772,DCCLXXII +773,DCCLXXIII +774,DCCLXXIV +775,DCCLXXV +776,DCCLXXVI +777,DCCLXXVII +778,DCCLXXVIII +779,DCCLXXIX +780,DCCLXXX +781,DCCLXXXI +782,DCCLXXXII +783,DCCLXXXIII +784,DCCLXXXIV +785,DCCLXXXV +786,DCCLXXXVI +787,DCCLXXXVII +788,DCCLXXXVIII +789,DCCLXXXIX +790,DCCXC +791,DCCXCI +792,DCCXCII +793,DCCXCIII +794,DCCXCIV +795,DCCXCV +796,DCCXCVI +797,DCCXCVII +798,DCCXCVIII +799,DCCXCIX +800,DCCC +801,DCCCI +802,DCCCII +803,DCCCIII +804,DCCCIV +805,DCCCV +806,DCCCVI +807,DCCCVII +808,DCCCVIII +809,DCCCIX +810,DCCCX +811,DCCCXI +812,DCCCXII +813,DCCCXIII +814,DCCCXIV +815,DCCCXV +816,DCCCXVI +817,DCCCXVII +818,DCCCXVIII +819,DCCCXIX +820,DCCCXX +821,DCCCXXI +822,DCCCXXII +823,DCCCXXIII +824,DCCCXXIV +825,DCCCXXV +826,DCCCXXVI +827,DCCCXXVII +828,DCCCXXVIII +829,DCCCXXIX +830,DCCCXXX +831,DCCCXXXI +832,DCCCXXXII +833,DCCCXXXIII +834,DCCCXXXIV +835,DCCCXXXV +836,DCCCXXXVI +837,DCCCXXXVII +838,DCCCXXXVIII +839,DCCCXXXIX +840,DCCCXL +841,DCCCXLI +842,DCCCXLII +843,DCCCXLIII +844,DCCCXLIV +845,DCCCXLV +846,DCCCXLVI +847,DCCCXLVII +848,DCCCXLVIII +849,DCCCXLIX +850,DCCCL +851,DCCCLI +852,DCCCLII +853,DCCCLIII +854,DCCCLIV +855,DCCCLV +856,DCCCLVI +857,DCCCLVII +858,DCCCLVIII +859,DCCCLIX +860,DCCCLX +861,DCCCLXI +862,DCCCLXII +863,DCCCLXIII +864,DCCCLXIV +865,DCCCLXV +866,DCCCLXVI +867,DCCCLXVII +868,DCCCLXVIII +869,DCCCLXIX +870,DCCCLXX +871,DCCCLXXI +872,DCCCLXXII +873,DCCCLXXIII +874,DCCCLXXIV +875,DCCCLXXV +876,DCCCLXXVI +877,DCCCLXXVII +878,DCCCLXXVIII +879,DCCCLXXIX +880,DCCCLXXX +881,DCCCLXXXI +882,DCCCLXXXII +883,DCCCLXXXIII +884,DCCCLXXXIV +885,DCCCLXXXV +886,DCCCLXXXVI +887,DCCCLXXXVII +888,DCCCLXXXVIII +889,DCCCLXXXIX +890,DCCCXC +891,DCCCXCI +892,DCCCXCII +893,DCCCXCIII +894,DCCCXCIV +895,DCCCXCV +896,DCCCXCVI +897,DCCCXCVII +898,DCCCXCVIII +899,DCCCXCIX +900,CM +901,CMI +902,CMII +903,CMIII +904,CMIV +905,CMV +906,CMVI +907,CMVII +908,CMVIII +909,CMIX +910,CMX +911,CMXI +912,CMXII +913,CMXIII +914,CMXIV +915,CMXV +916,CMXVI +917,CMXVII +918,CMXVIII +919,CMXIX +920,CMXX +921,CMXXI +922,CMXXII +923,CMXXIII +924,CMXXIV +925,CMXXV +926,CMXXVI +927,CMXXVII +928,CMXXVIII +929,CMXXIX +930,CMXXX +931,CMXXXI +932,CMXXXII +933,CMXXXIII +934,CMXXXIV +935,CMXXXV +936,CMXXXVI +937,CMXXXVII +938,CMXXXVIII +939,CMXXXIX +940,CMXL +941,CMXLI +942,CMXLII +943,CMXLIII +944,CMXLIV +945,CMXLV +946,CMXLVI +947,CMXLVII +948,CMXLVIII +949,CMXLIX +950,CML +951,CMLI +952,CMLII +953,CMLIII +954,CMLIV +955,CMLV +956,CMLVI +957,CMLVII +958,CMLVIII +959,CMLIX +960,CMLX +961,CMLXI +962,CMLXII +963,CMLXIII +964,CMLXIV +965,CMLXV +966,CMLXVI +967,CMLXVII +968,CMLXVIII +969,CMLXIX +970,CMLXX +971,CMLXXI +972,CMLXXII +973,CMLXXIII +974,CMLXXIV +975,CMLXXV +976,CMLXXVI +977,CMLXXVII +978,CMLXXVIII +979,CMLXXIX +980,CMLXXX +981,CMLXXXI +982,CMLXXXII +983,CMLXXXIII +984,CMLXXXIV +985,CMLXXXV +986,CMLXXXVI +987,CMLXXXVII +988,CMLXXXVIII +989,CMLXXXIX +990,CMXC +991,CMXCI +992,CMXCII +993,CMXCIII +994,CMXCIV +995,CMXCV +996,CMXCVI +997,CMXCVII +998,CMXCVIII +999,CMXCIX +1000,M +1001,MI +1002,MII +1003,MIII +1004,MIV +1005,MV +1006,MVI +1007,MVII +1008,MVIII +1009,MIX +1010,MX +1011,MXI +1012,MXII +1013,MXIII +1014,MXIV +1015,MXV +1016,MXVI +1017,MXVII +1018,MXVIII +1019,MXIX +1020,MXX +1021,MXXI +1022,MXXII +1023,MXXIII +1024,MXXIV +1025,MXXV +1026,MXXVI +1027,MXXVII +1028,MXXVIII +1029,MXXIX +1030,MXXX +1031,MXXXI +1032,MXXXII +1033,MXXXIII +1034,MXXXIV +1035,MXXXV +1036,MXXXVI +1037,MXXXVII +1038,MXXXVIII +1039,MXXXIX +1040,MXL +1041,MXLI +1042,MXLII +1043,MXLIII +1044,MXLIV +1045,MXLV +1046,MXLVI +1047,MXLVII +1048,MXLVIII +1049,MXLIX +1050,ML +1051,MLI +1052,MLII +1053,MLIII +1054,MLIV +1055,MLV +1056,MLVI +1057,MLVII +1058,MLVIII +1059,MLIX +1060,MLX +1061,MLXI +1062,MLXII +1063,MLXIII +1064,MLXIV +1065,MLXV +1066,MLXVI +1067,MLXVII +1068,MLXVIII +1069,MLXIX +1070,MLXX +1071,MLXXI +1072,MLXXII +1073,MLXXIII +1074,MLXXIV +1075,MLXXV +1076,MLXXVI +1077,MLXXVII +1078,MLXXVIII +1079,MLXXIX +1080,MLXXX +1081,MLXXXI +1082,MLXXXII +1083,MLXXXIII +1084,MLXXXIV +1085,MLXXXV +1086,MLXXXVI +1087,MLXXXVII +1088,MLXXXVIII +1089,MLXXXIX +1090,MXC +1091,MXCI +1092,MXCII +1093,MXCIII +1094,MXCIV +1095,MXCV +1096,MXCVI +1097,MXCVII +1098,MXCVIII +1099,MXCIX +1100,MC +1101,MCI +1102,MCII +1103,MCIII +1104,MCIV +1105,MCV +1106,MCVI +1107,MCVII +1108,MCVIII +1109,MCIX +1110,MCX +1111,MCXI +1112,MCXII +1113,MCXIII +1114,MCXIV +1115,MCXV +1116,MCXVI +1117,MCXVII +1118,MCXVIII +1119,MCXIX +1120,MCXX +1121,MCXXI +1122,MCXXII +1123,MCXXIII +1124,MCXXIV +1125,MCXXV +1126,MCXXVI +1127,MCXXVII +1128,MCXXVIII +1129,MCXXIX +1130,MCXXX +1131,MCXXXI +1132,MCXXXII +1133,MCXXXIII +1134,MCXXXIV +1135,MCXXXV +1136,MCXXXVI +1137,MCXXXVII +1138,MCXXXVIII +1139,MCXXXIX +1140,MCXL +1141,MCXLI +1142,MCXLII +1143,MCXLIII +1144,MCXLIV +1145,MCXLV +1146,MCXLVI +1147,MCXLVII +1148,MCXLVIII +1149,MCXLIX +1150,MCL +1151,MCLI +1152,MCLII +1153,MCLIII +1154,MCLIV +1155,MCLV +1156,MCLVI +1157,MCLVII +1158,MCLVIII +1159,MCLIX +1160,MCLX +1161,MCLXI +1162,MCLXII +1163,MCLXIII +1164,MCLXIV +1165,MCLXV +1166,MCLXVI +1167,MCLXVII +1168,MCLXVIII +1169,MCLXIX +1170,MCLXX +1171,MCLXXI +1172,MCLXXII +1173,MCLXXIII +1174,MCLXXIV +1175,MCLXXV +1176,MCLXXVI +1177,MCLXXVII +1178,MCLXXVIII +1179,MCLXXIX +1180,MCLXXX +1181,MCLXXXI +1182,MCLXXXII +1183,MCLXXXIII +1184,MCLXXXIV +1185,MCLXXXV +1186,MCLXXXVI +1187,MCLXXXVII +1188,MCLXXXVIII +1189,MCLXXXIX +1190,MCXC +1191,MCXCI +1192,MCXCII +1193,MCXCIII +1194,MCXCIV +1195,MCXCV +1196,MCXCVI +1197,MCXCVII +1198,MCXCVIII +1199,MCXCIX +1200,MCC +1201,MCCI +1202,MCCII +1203,MCCIII +1204,MCCIV +1205,MCCV +1206,MCCVI +1207,MCCVII +1208,MCCVIII +1209,MCCIX +1210,MCCX +1211,MCCXI +1212,MCCXII +1213,MCCXIII +1214,MCCXIV +1215,MCCXV +1216,MCCXVI +1217,MCCXVII +1218,MCCXVIII +1219,MCCXIX +1220,MCCXX +1221,MCCXXI +1222,MCCXXII +1223,MCCXXIII +1224,MCCXXIV +1225,MCCXXV +1226,MCCXXVI +1227,MCCXXVII +1228,MCCXXVIII +1229,MCCXXIX +1230,MCCXXX +1231,MCCXXXI +1232,MCCXXXII +1233,MCCXXXIII +1234,MCCXXXIV +1235,MCCXXXV +1236,MCCXXXVI +1237,MCCXXXVII +1238,MCCXXXVIII +1239,MCCXXXIX +1240,MCCXL +1241,MCCXLI +1242,MCCXLII +1243,MCCXLIII +1244,MCCXLIV +1245,MCCXLV +1246,MCCXLVI +1247,MCCXLVII +1248,MCCXLVIII +1249,MCCXLIX +1250,MCCL +1251,MCCLI +1252,MCCLII +1253,MCCLIII +1254,MCCLIV +1255,MCCLV +1256,MCCLVI +1257,MCCLVII +1258,MCCLVIII +1259,MCCLIX +1260,MCCLX +1261,MCCLXI +1262,MCCLXII +1263,MCCLXIII +1264,MCCLXIV +1265,MCCLXV +1266,MCCLXVI +1267,MCCLXVII +1268,MCCLXVIII +1269,MCCLXIX +1270,MCCLXX +1271,MCCLXXI +1272,MCCLXXII +1273,MCCLXXIII +1274,MCCLXXIV +1275,MCCLXXV +1276,MCCLXXVI +1277,MCCLXXVII +1278,MCCLXXVIII +1279,MCCLXXIX +1280,MCCLXXX +1281,MCCLXXXI +1282,MCCLXXXII +1283,MCCLXXXIII +1284,MCCLXXXIV +1285,MCCLXXXV +1286,MCCLXXXVI +1287,MCCLXXXVII +1288,MCCLXXXVIII +1289,MCCLXXXIX +1290,MCCXC +1291,MCCXCI +1292,MCCXCII +1293,MCCXCIII +1294,MCCXCIV +1295,MCCXCV +1296,MCCXCVI +1297,MCCXCVII +1298,MCCXCVIII +1299,MCCXCIX +1300,MCCC +1301,MCCCI +1302,MCCCII +1303,MCCCIII +1304,MCCCIV +1305,MCCCV +1306,MCCCVI +1307,MCCCVII +1308,MCCCVIII +1309,MCCCIX +1310,MCCCX +1311,MCCCXI +1312,MCCCXII +1313,MCCCXIII +1314,MCCCXIV +1315,MCCCXV +1316,MCCCXVI +1317,MCCCXVII +1318,MCCCXVIII +1319,MCCCXIX +1320,MCCCXX +1321,MCCCXXI +1322,MCCCXXII +1323,MCCCXXIII +1324,MCCCXXIV +1325,MCCCXXV +1326,MCCCXXVI +1327,MCCCXXVII +1328,MCCCXXVIII +1329,MCCCXXIX +1330,MCCCXXX +1331,MCCCXXXI +1332,MCCCXXXII +1333,MCCCXXXIII +1334,MCCCXXXIV +1335,MCCCXXXV +1336,MCCCXXXVI +1337,MCCCXXXVII +1338,MCCCXXXVIII +1339,MCCCXXXIX +1340,MCCCXL +1341,MCCCXLI +1342,MCCCXLII +1343,MCCCXLIII +1344,MCCCXLIV +1345,MCCCXLV +1346,MCCCXLVI +1347,MCCCXLVII +1348,MCCCXLVIII +1349,MCCCXLIX +1350,MCCCL +1351,MCCCLI +1352,MCCCLII +1353,MCCCLIII +1354,MCCCLIV +1355,MCCCLV +1356,MCCCLVI +1357,MCCCLVII +1358,MCCCLVIII +1359,MCCCLIX +1360,MCCCLX +1361,MCCCLXI +1362,MCCCLXII +1363,MCCCLXIII +1364,MCCCLXIV +1365,MCCCLXV +1366,MCCCLXVI +1367,MCCCLXVII +1368,MCCCLXVIII +1369,MCCCLXIX +1370,MCCCLXX +1371,MCCCLXXI +1372,MCCCLXXII +1373,MCCCLXXIII +1374,MCCCLXXIV +1375,MCCCLXXV +1376,MCCCLXXVI +1377,MCCCLXXVII +1378,MCCCLXXVIII +1379,MCCCLXXIX +1380,MCCCLXXX +1381,MCCCLXXXI +1382,MCCCLXXXII +1383,MCCCLXXXIII +1384,MCCCLXXXIV +1385,MCCCLXXXV +1386,MCCCLXXXVI +1387,MCCCLXXXVII +1388,MCCCLXXXVIII +1389,MCCCLXXXIX +1390,MCCCXC +1391,MCCCXCI +1392,MCCCXCII +1393,MCCCXCIII +1394,MCCCXCIV +1395,MCCCXCV +1396,MCCCXCVI +1397,MCCCXCVII +1398,MCCCXCVIII +1399,MCCCXCIX +1400,MCD +1401,MCDI +1402,MCDII +1403,MCDIII +1404,MCDIV +1405,MCDV +1406,MCDVI +1407,MCDVII +1408,MCDVIII +1409,MCDIX +1410,MCDX +1411,MCDXI +1412,MCDXII +1413,MCDXIII +1414,MCDXIV +1415,MCDXV +1416,MCDXVI +1417,MCDXVII +1418,MCDXVIII +1419,MCDXIX +1420,MCDXX +1421,MCDXXI +1422,MCDXXII +1423,MCDXXIII +1424,MCDXXIV +1425,MCDXXV +1426,MCDXXVI +1427,MCDXXVII +1428,MCDXXVIII +1429,MCDXXIX +1430,MCDXXX +1431,MCDXXXI +1432,MCDXXXII +1433,MCDXXXIII +1434,MCDXXXIV +1435,MCDXXXV +1436,MCDXXXVI +1437,MCDXXXVII +1438,MCDXXXVIII +1439,MCDXXXIX +1440,MCDXL +1441,MCDXLI +1442,MCDXLII +1443,MCDXLIII +1444,MCDXLIV +1445,MCDXLV +1446,MCDXLVI +1447,MCDXLVII +1448,MCDXLVIII +1449,MCDXLIX +1450,MCDL +1451,MCDLI +1452,MCDLII +1453,MCDLIII +1454,MCDLIV +1455,MCDLV +1456,MCDLVI +1457,MCDLVII +1458,MCDLVIII +1459,MCDLIX +1460,MCDLX +1461,MCDLXI +1462,MCDLXII +1463,MCDLXIII +1464,MCDLXIV +1465,MCDLXV +1466,MCDLXVI +1467,MCDLXVII +1468,MCDLXVIII +1469,MCDLXIX +1470,MCDLXX +1471,MCDLXXI +1472,MCDLXXII +1473,MCDLXXIII +1474,MCDLXXIV +1475,MCDLXXV +1476,MCDLXXVI +1477,MCDLXXVII +1478,MCDLXXVIII +1479,MCDLXXIX +1480,MCDLXXX +1481,MCDLXXXI +1482,MCDLXXXII +1483,MCDLXXXIII +1484,MCDLXXXIV +1485,MCDLXXXV +1486,MCDLXXXVI +1487,MCDLXXXVII +1488,MCDLXXXVIII +1489,MCDLXXXIX +1490,MCDXC +1491,MCDXCI +1492,MCDXCII +1493,MCDXCIII +1494,MCDXCIV +1495,MCDXCV +1496,MCDXCVI +1497,MCDXCVII +1498,MCDXCVIII +1499,MCDXCIX +1500,MD +1501,MDI +1502,MDII +1503,MDIII +1504,MDIV +1505,MDV +1506,MDVI +1507,MDVII +1508,MDVIII +1509,MDIX +1510,MDX +1511,MDXI +1512,MDXII +1513,MDXIII +1514,MDXIV +1515,MDXV +1516,MDXVI +1517,MDXVII +1518,MDXVIII +1519,MDXIX +1520,MDXX +1521,MDXXI +1522,MDXXII +1523,MDXXIII +1524,MDXXIV +1525,MDXXV +1526,MDXXVI +1527,MDXXVII +1528,MDXXVIII +1529,MDXXIX +1530,MDXXX +1531,MDXXXI +1532,MDXXXII +1533,MDXXXIII +1534,MDXXXIV +1535,MDXXXV +1536,MDXXXVI +1537,MDXXXVII +1538,MDXXXVIII +1539,MDXXXIX +1540,MDXL +1541,MDXLI +1542,MDXLII +1543,MDXLIII +1544,MDXLIV +1545,MDXLV +1546,MDXLVI +1547,MDXLVII +1548,MDXLVIII +1549,MDXLIX +1550,MDL +1551,MDLI +1552,MDLII +1553,MDLIII +1554,MDLIV +1555,MDLV +1556,MDLVI +1557,MDLVII +1558,MDLVIII +1559,MDLIX +1560,MDLX +1561,MDLXI +1562,MDLXII +1563,MDLXIII +1564,MDLXIV +1565,MDLXV +1566,MDLXVI +1567,MDLXVII +1568,MDLXVIII +1569,MDLXIX +1570,MDLXX +1571,MDLXXI +1572,MDLXXII +1573,MDLXXIII +1574,MDLXXIV +1575,MDLXXV +1576,MDLXXVI +1577,MDLXXVII +1578,MDLXXVIII +1579,MDLXXIX +1580,MDLXXX +1581,MDLXXXI +1582,MDLXXXII +1583,MDLXXXIII +1584,MDLXXXIV +1585,MDLXXXV +1586,MDLXXXVI +1587,MDLXXXVII +1588,MDLXXXVIII +1589,MDLXXXIX +1590,MDXC +1591,MDXCI +1592,MDXCII +1593,MDXCIII +1594,MDXCIV +1595,MDXCV +1596,MDXCVI +1597,MDXCVII +1598,MDXCVIII +1599,MDXCIX +1600,MDC +1601,MDCI +1602,MDCII +1603,MDCIII +1604,MDCIV +1605,MDCV +1606,MDCVI +1607,MDCVII +1608,MDCVIII +1609,MDCIX +1610,MDCX +1611,MDCXI +1612,MDCXII +1613,MDCXIII +1614,MDCXIV +1615,MDCXV +1616,MDCXVI +1617,MDCXVII +1618,MDCXVIII +1619,MDCXIX +1620,MDCXX +1621,MDCXXI +1622,MDCXXII +1623,MDCXXIII +1624,MDCXXIV +1625,MDCXXV +1626,MDCXXVI +1627,MDCXXVII +1628,MDCXXVIII +1629,MDCXXIX +1630,MDCXXX +1631,MDCXXXI +1632,MDCXXXII +1633,MDCXXXIII +1634,MDCXXXIV +1635,MDCXXXV +1636,MDCXXXVI +1637,MDCXXXVII +1638,MDCXXXVIII +1639,MDCXXXIX +1640,MDCXL +1641,MDCXLI +1642,MDCXLII +1643,MDCXLIII +1644,MDCXLIV +1645,MDCXLV +1646,MDCXLVI +1647,MDCXLVII +1648,MDCXLVIII +1649,MDCXLIX +1650,MDCL +1651,MDCLI +1652,MDCLII +1653,MDCLIII +1654,MDCLIV +1655,MDCLV +1656,MDCLVI +1657,MDCLVII +1658,MDCLVIII +1659,MDCLIX +1660,MDCLX +1661,MDCLXI +1662,MDCLXII +1663,MDCLXIII +1664,MDCLXIV +1665,MDCLXV +1666,MDCLXVI +1667,MDCLXVII +1668,MDCLXVIII +1669,MDCLXIX +1670,MDCLXX +1671,MDCLXXI +1672,MDCLXXII +1673,MDCLXXIII +1674,MDCLXXIV +1675,MDCLXXV +1676,MDCLXXVI +1677,MDCLXXVII +1678,MDCLXXVIII +1679,MDCLXXIX +1680,MDCLXXX +1681,MDCLXXXI +1682,MDCLXXXII +1683,MDCLXXXIII +1684,MDCLXXXIV +1685,MDCLXXXV +1686,MDCLXXXVI +1687,MDCLXXXVII +1688,MDCLXXXVIII +1689,MDCLXXXIX +1690,MDCXC +1691,MDCXCI +1692,MDCXCII +1693,MDCXCIII +1694,MDCXCIV +1695,MDCXCV +1696,MDCXCVI +1697,MDCXCVII +1698,MDCXCVIII +1699,MDCXCIX +1700,MDCC +1701,MDCCI +1702,MDCCII +1703,MDCCIII +1704,MDCCIV +1705,MDCCV +1706,MDCCVI +1707,MDCCVII +1708,MDCCVIII +1709,MDCCIX +1710,MDCCX +1711,MDCCXI +1712,MDCCXII +1713,MDCCXIII +1714,MDCCXIV +1715,MDCCXV +1716,MDCCXVI +1717,MDCCXVII +1718,MDCCXVIII +1719,MDCCXIX +1720,MDCCXX +1721,MDCCXXI +1722,MDCCXXII +1723,MDCCXXIII +1724,MDCCXXIV +1725,MDCCXXV +1726,MDCCXXVI +1727,MDCCXXVII +1728,MDCCXXVIII +1729,MDCCXXIX +1730,MDCCXXX +1731,MDCCXXXI +1732,MDCCXXXII +1733,MDCCXXXIII +1734,MDCCXXXIV +1735,MDCCXXXV +1736,MDCCXXXVI +1737,MDCCXXXVII +1738,MDCCXXXVIII +1739,MDCCXXXIX +1740,MDCCXL +1741,MDCCXLI +1742,MDCCXLII +1743,MDCCXLIII +1744,MDCCXLIV +1745,MDCCXLV +1746,MDCCXLVI +1747,MDCCXLVII +1748,MDCCXLVIII +1749,MDCCXLIX +1750,MDCCL +1751,MDCCLI +1752,MDCCLII +1753,MDCCLIII +1754,MDCCLIV +1755,MDCCLV +1756,MDCCLVI +1757,MDCCLVII +1758,MDCCLVIII +1759,MDCCLIX +1760,MDCCLX +1761,MDCCLXI +1762,MDCCLXII +1763,MDCCLXIII +1764,MDCCLXIV +1765,MDCCLXV +1766,MDCCLXVI +1767,MDCCLXVII +1768,MDCCLXVIII +1769,MDCCLXIX +1770,MDCCLXX +1771,MDCCLXXI +1772,MDCCLXXII +1773,MDCCLXXIII +1774,MDCCLXXIV +1775,MDCCLXXV +1776,MDCCLXXVI +1777,MDCCLXXVII +1778,MDCCLXXVIII +1779,MDCCLXXIX +1780,MDCCLXXX +1781,MDCCLXXXI +1782,MDCCLXXXII +1783,MDCCLXXXIII +1784,MDCCLXXXIV +1785,MDCCLXXXV +1786,MDCCLXXXVI +1787,MDCCLXXXVII +1788,MDCCLXXXVIII +1789,MDCCLXXXIX +1790,MDCCXC +1791,MDCCXCI +1792,MDCCXCII +1793,MDCCXCIII +1794,MDCCXCIV +1795,MDCCXCV +1796,MDCCXCVI +1797,MDCCXCVII +1798,MDCCXCVIII +1799,MDCCXCIX +1800,MDCCC +1801,MDCCCI +1802,MDCCCII +1803,MDCCCIII +1804,MDCCCIV +1805,MDCCCV +1806,MDCCCVI +1807,MDCCCVII +1808,MDCCCVIII +1809,MDCCCIX +1810,MDCCCX +1811,MDCCCXI +1812,MDCCCXII +1813,MDCCCXIII +1814,MDCCCXIV +1815,MDCCCXV +1816,MDCCCXVI +1817,MDCCCXVII +1818,MDCCCXVIII +1819,MDCCCXIX +1820,MDCCCXX +1821,MDCCCXXI +1822,MDCCCXXII +1823,MDCCCXXIII +1824,MDCCCXXIV +1825,MDCCCXXV +1826,MDCCCXXVI +1827,MDCCCXXVII +1828,MDCCCXXVIII +1829,MDCCCXXIX +1830,MDCCCXXX +1831,MDCCCXXXI +1832,MDCCCXXXII +1833,MDCCCXXXIII +1834,MDCCCXXXIV +1835,MDCCCXXXV +1836,MDCCCXXXVI +1837,MDCCCXXXVII +1838,MDCCCXXXVIII +1839,MDCCCXXXIX +1840,MDCCCXL +1841,MDCCCXLI +1842,MDCCCXLII +1843,MDCCCXLIII +1844,MDCCCXLIV +1845,MDCCCXLV +1846,MDCCCXLVI +1847,MDCCCXLVII +1848,MDCCCXLVIII +1849,MDCCCXLIX +1850,MDCCCL +1851,MDCCCLI +1852,MDCCCLII +1853,MDCCCLIII +1854,MDCCCLIV +1855,MDCCCLV +1856,MDCCCLVI +1857,MDCCCLVII +1858,MDCCCLVIII +1859,MDCCCLIX +1860,MDCCCLX +1861,MDCCCLXI +1862,MDCCCLXII +1863,MDCCCLXIII +1864,MDCCCLXIV +1865,MDCCCLXV +1866,MDCCCLXVI +1867,MDCCCLXVII +1868,MDCCCLXVIII +1869,MDCCCLXIX +1870,MDCCCLXX +1871,MDCCCLXXI +1872,MDCCCLXXII +1873,MDCCCLXXIII +1874,MDCCCLXXIV +1875,MDCCCLXXV +1876,MDCCCLXXVI +1877,MDCCCLXXVII +1878,MDCCCLXXVIII +1879,MDCCCLXXIX +1880,MDCCCLXXX +1881,MDCCCLXXXI +1882,MDCCCLXXXII +1883,MDCCCLXXXIII +1884,MDCCCLXXXIV +1885,MDCCCLXXXV +1886,MDCCCLXXXVI +1887,MDCCCLXXXVII +1888,MDCCCLXXXVIII +1889,MDCCCLXXXIX +1890,MDCCCXC +1891,MDCCCXCI +1892,MDCCCXCII +1893,MDCCCXCIII +1894,MDCCCXCIV +1895,MDCCCXCV +1896,MDCCCXCVI +1897,MDCCCXCVII +1898,MDCCCXCVIII +1899,MDCCCXCIX +1900,MCM +1901,MCMI +1902,MCMII +1903,MCMIII +1904,MCMIV +1905,MCMV +1906,MCMVI +1907,MCMVII +1908,MCMVIII +1909,MCMIX +1910,MCMX +1911,MCMXI +1912,MCMXII +1913,MCMXIII +1914,MCMXIV +1915,MCMXV +1916,MCMXVI +1917,MCMXVII +1918,MCMXVIII +1919,MCMXIX +1920,MCMXX +1921,MCMXXI +1922,MCMXXII +1923,MCMXXIII +1924,MCMXXIV +1925,MCMXXV +1926,MCMXXVI +1927,MCMXXVII +1928,MCMXXVIII +1929,MCMXXIX +1930,MCMXXX +1931,MCMXXXI +1932,MCMXXXII +1933,MCMXXXIII +1934,MCMXXXIV +1935,MCMXXXV +1936,MCMXXXVI +1937,MCMXXXVII +1938,MCMXXXVIII +1939,MCMXXXIX +1940,MCMXL +1941,MCMXLI +1942,MCMXLII +1943,MCMXLIII +1944,MCMXLIV +1945,MCMXLV +1946,MCMXLVI +1947,MCMXLVII +1948,MCMXLVIII +1949,MCMXLIX +1950,MCML +1951,MCMLI +1952,MCMLII +1953,MCMLIII +1954,MCMLIV +1955,MCMLV +1956,MCMLVI +1957,MCMLVII +1958,MCMLVIII +1959,MCMLIX +1960,MCMLX +1961,MCMLXI +1962,MCMLXII +1963,MCMLXIII +1964,MCMLXIV +1965,MCMLXV +1966,MCMLXVI +1967,MCMLXVII +1968,MCMLXVIII +1969,MCMLXIX +1970,MCMLXX +1971,MCMLXXI +1972,MCMLXXII +1973,MCMLXXIII +1974,MCMLXXIV +1975,MCMLXXV +1976,MCMLXXVI +1977,MCMLXXVII +1978,MCMLXXVIII +1979,MCMLXXIX +1980,MCMLXXX +1981,MCMLXXXI +1982,MCMLXXXII +1983,MCMLXXXIII +1984,MCMLXXXIV +1985,MCMLXXXV +1986,MCMLXXXVI +1987,MCMLXXXVII +1988,MCMLXXXVIII +1989,MCMLXXXIX +1990,MCMXC +1991,MCMXCI +1992,MCMXCII +1993,MCMXCIII +1994,MCMXCIV +1995,MCMXCV +1996,MCMXCVI +1997,MCMXCVII +1998,MCMXCVIII +1999,MCMXCIX +2000,MM +2001,MMI +2002,MMII +2003,MMIII +2004,MMIV +2005,MMV +2006,MMVI +2007,MMVII +2008,MMVIII +2009,MMIX +2010,MMX +2011,MMXI +2012,MMXII +2013,MMXIII +2014,MMXIV +2015,MMXV +2016,MMXVI +2017,MMXVII +2018,MMXVIII +2019,MMXIX +2020,MMXX +2021,MMXXI +2022,MMXXII +2023,MMXXIII +2024,MMXXIV +2025,MMXXV +2026,MMXXVI +2027,MMXXVII +2028,MMXXVIII +2029,MMXXIX +2030,MMXXX +2031,MMXXXI +2032,MMXXXII +2033,MMXXXIII +2034,MMXXXIV +2035,MMXXXV +2036,MMXXXVI +2037,MMXXXVII +2038,MMXXXVIII +2039,MMXXXIX +2040,MMXL +2041,MMXLI +2042,MMXLII +2043,MMXLIII +2044,MMXLIV +2045,MMXLV +2046,MMXLVI +2047,MMXLVII +2048,MMXLVIII +2049,MMXLIX +2050,MML +2051,MMLI +2052,MMLII +2053,MMLIII +2054,MMLIV +2055,MMLV +2056,MMLVI +2057,MMLVII +2058,MMLVIII +2059,MMLIX +2060,MMLX +2061,MMLXI +2062,MMLXII +2063,MMLXIII +2064,MMLXIV +2065,MMLXV +2066,MMLXVI +2067,MMLXVII +2068,MMLXVIII +2069,MMLXIX +2070,MMLXX +2071,MMLXXI +2072,MMLXXII +2073,MMLXXIII +2074,MMLXXIV +2075,MMLXXV +2076,MMLXXVI +2077,MMLXXVII +2078,MMLXXVIII +2079,MMLXXIX +2080,MMLXXX +2081,MMLXXXI +2082,MMLXXXII +2083,MMLXXXIII +2084,MMLXXXIV +2085,MMLXXXV +2086,MMLXXXVI +2087,MMLXXXVII +2088,MMLXXXVIII +2089,MMLXXXIX +2090,MMXC +2091,MMXCI +2092,MMXCII +2093,MMXCIII +2094,MMXCIV +2095,MMXCV +2096,MMXCVI +2097,MMXCVII +2098,MMXCVIII +2099,MMXCIX +2100,MMC +2101,MMCI +2102,MMCII +2103,MMCIII +2104,MMCIV +2105,MMCV +2106,MMCVI +2107,MMCVII +2108,MMCVIII +2109,MMCIX +2110,MMCX +2111,MMCXI +2112,MMCXII +2113,MMCXIII +2114,MMCXIV +2115,MMCXV +2116,MMCXVI +2117,MMCXVII +2118,MMCXVIII +2119,MMCXIX +2120,MMCXX +2121,MMCXXI +2122,MMCXXII +2123,MMCXXIII +2124,MMCXXIV +2125,MMCXXV +2126,MMCXXVI +2127,MMCXXVII +2128,MMCXXVIII +2129,MMCXXIX +2130,MMCXXX +2131,MMCXXXI +2132,MMCXXXII +2133,MMCXXXIII +2134,MMCXXXIV +2135,MMCXXXV +2136,MMCXXXVI +2137,MMCXXXVII +2138,MMCXXXVIII +2139,MMCXXXIX +2140,MMCXL +2141,MMCXLI +2142,MMCXLII +2143,MMCXLIII +2144,MMCXLIV +2145,MMCXLV +2146,MMCXLVI +2147,MMCXLVII +2148,MMCXLVIII +2149,MMCXLIX +2150,MMCL +2151,MMCLI +2152,MMCLII +2153,MMCLIII +2154,MMCLIV +2155,MMCLV +2156,MMCLVI +2157,MMCLVII +2158,MMCLVIII +2159,MMCLIX +2160,MMCLX +2161,MMCLXI +2162,MMCLXII +2163,MMCLXIII +2164,MMCLXIV +2165,MMCLXV +2166,MMCLXVI +2167,MMCLXVII +2168,MMCLXVIII +2169,MMCLXIX +2170,MMCLXX +2171,MMCLXXI +2172,MMCLXXII +2173,MMCLXXIII +2174,MMCLXXIV +2175,MMCLXXV +2176,MMCLXXVI +2177,MMCLXXVII +2178,MMCLXXVIII +2179,MMCLXXIX +2180,MMCLXXX +2181,MMCLXXXI +2182,MMCLXXXII +2183,MMCLXXXIII +2184,MMCLXXXIV +2185,MMCLXXXV +2186,MMCLXXXVI +2187,MMCLXXXVII +2188,MMCLXXXVIII +2189,MMCLXXXIX +2190,MMCXC +2191,MMCXCI +2192,MMCXCII +2193,MMCXCIII +2194,MMCXCIV +2195,MMCXCV +2196,MMCXCVI +2197,MMCXCVII +2198,MMCXCVIII +2199,MMCXCIX +2200,MMCC +2201,MMCCI +2202,MMCCII +2203,MMCCIII +2204,MMCCIV +2205,MMCCV +2206,MMCCVI +2207,MMCCVII +2208,MMCCVIII +2209,MMCCIX +2210,MMCCX +2211,MMCCXI +2212,MMCCXII +2213,MMCCXIII +2214,MMCCXIV +2215,MMCCXV +2216,MMCCXVI +2217,MMCCXVII +2218,MMCCXVIII +2219,MMCCXIX +2220,MMCCXX +2221,MMCCXXI +2222,MMCCXXII +2223,MMCCXXIII +2224,MMCCXXIV +2225,MMCCXXV +2226,MMCCXXVI +2227,MMCCXXVII +2228,MMCCXXVIII +2229,MMCCXXIX +2230,MMCCXXX +2231,MMCCXXXI +2232,MMCCXXXII +2233,MMCCXXXIII +2234,MMCCXXXIV +2235,MMCCXXXV +2236,MMCCXXXVI +2237,MMCCXXXVII +2238,MMCCXXXVIII +2239,MMCCXXXIX +2240,MMCCXL +2241,MMCCXLI +2242,MMCCXLII +2243,MMCCXLIII +2244,MMCCXLIV +2245,MMCCXLV +2246,MMCCXLVI +2247,MMCCXLVII +2248,MMCCXLVIII +2249,MMCCXLIX +2250,MMCCL +2251,MMCCLI +2252,MMCCLII +2253,MMCCLIII +2254,MMCCLIV +2255,MMCCLV +2256,MMCCLVI +2257,MMCCLVII +2258,MMCCLVIII +2259,MMCCLIX +2260,MMCCLX +2261,MMCCLXI +2262,MMCCLXII +2263,MMCCLXIII +2264,MMCCLXIV +2265,MMCCLXV +2266,MMCCLXVI +2267,MMCCLXVII +2268,MMCCLXVIII +2269,MMCCLXIX +2270,MMCCLXX +2271,MMCCLXXI +2272,MMCCLXXII +2273,MMCCLXXIII +2274,MMCCLXXIV +2275,MMCCLXXV +2276,MMCCLXXVI +2277,MMCCLXXVII +2278,MMCCLXXVIII +2279,MMCCLXXIX +2280,MMCCLXXX +2281,MMCCLXXXI +2282,MMCCLXXXII +2283,MMCCLXXXIII +2284,MMCCLXXXIV +2285,MMCCLXXXV +2286,MMCCLXXXVI +2287,MMCCLXXXVII +2288,MMCCLXXXVIII +2289,MMCCLXXXIX +2290,MMCCXC +2291,MMCCXCI +2292,MMCCXCII +2293,MMCCXCIII +2294,MMCCXCIV +2295,MMCCXCV +2296,MMCCXCVI +2297,MMCCXCVII +2298,MMCCXCVIII +2299,MMCCXCIX +2300,MMCCC +2301,MMCCCI +2302,MMCCCII +2303,MMCCCIII +2304,MMCCCIV +2305,MMCCCV +2306,MMCCCVI +2307,MMCCCVII +2308,MMCCCVIII +2309,MMCCCIX +2310,MMCCCX +2311,MMCCCXI +2312,MMCCCXII +2313,MMCCCXIII +2314,MMCCCXIV +2315,MMCCCXV +2316,MMCCCXVI +2317,MMCCCXVII +2318,MMCCCXVIII +2319,MMCCCXIX +2320,MMCCCXX +2321,MMCCCXXI +2322,MMCCCXXII +2323,MMCCCXXIII +2324,MMCCCXXIV +2325,MMCCCXXV +2326,MMCCCXXVI +2327,MMCCCXXVII +2328,MMCCCXXVIII +2329,MMCCCXXIX +2330,MMCCCXXX +2331,MMCCCXXXI +2332,MMCCCXXXII +2333,MMCCCXXXIII +2334,MMCCCXXXIV +2335,MMCCCXXXV +2336,MMCCCXXXVI +2337,MMCCCXXXVII +2338,MMCCCXXXVIII +2339,MMCCCXXXIX +2340,MMCCCXL +2341,MMCCCXLI +2342,MMCCCXLII +2343,MMCCCXLIII +2344,MMCCCXLIV +2345,MMCCCXLV +2346,MMCCCXLVI +2347,MMCCCXLVII +2348,MMCCCXLVIII +2349,MMCCCXLIX +2350,MMCCCL +2351,MMCCCLI +2352,MMCCCLII +2353,MMCCCLIII +2354,MMCCCLIV +2355,MMCCCLV +2356,MMCCCLVI +2357,MMCCCLVII +2358,MMCCCLVIII +2359,MMCCCLIX +2360,MMCCCLX +2361,MMCCCLXI +2362,MMCCCLXII +2363,MMCCCLXIII +2364,MMCCCLXIV +2365,MMCCCLXV +2366,MMCCCLXVI +2367,MMCCCLXVII +2368,MMCCCLXVIII +2369,MMCCCLXIX +2370,MMCCCLXX +2371,MMCCCLXXI +2372,MMCCCLXXII +2373,MMCCCLXXIII +2374,MMCCCLXXIV +2375,MMCCCLXXV +2376,MMCCCLXXVI +2377,MMCCCLXXVII +2378,MMCCCLXXVIII +2379,MMCCCLXXIX +2380,MMCCCLXXX +2381,MMCCCLXXXI +2382,MMCCCLXXXII +2383,MMCCCLXXXIII +2384,MMCCCLXXXIV +2385,MMCCCLXXXV +2386,MMCCCLXXXVI +2387,MMCCCLXXXVII +2388,MMCCCLXXXVIII +2389,MMCCCLXXXIX +2390,MMCCCXC +2391,MMCCCXCI +2392,MMCCCXCII +2393,MMCCCXCIII +2394,MMCCCXCIV +2395,MMCCCXCV +2396,MMCCCXCVI +2397,MMCCCXCVII +2398,MMCCCXCVIII +2399,MMCCCXCIX +2400,MMCD +2401,MMCDI +2402,MMCDII +2403,MMCDIII +2404,MMCDIV +2405,MMCDV +2406,MMCDVI +2407,MMCDVII +2408,MMCDVIII +2409,MMCDIX +2410,MMCDX +2411,MMCDXI +2412,MMCDXII +2413,MMCDXIII +2414,MMCDXIV +2415,MMCDXV +2416,MMCDXVI +2417,MMCDXVII +2418,MMCDXVIII +2419,MMCDXIX +2420,MMCDXX +2421,MMCDXXI +2422,MMCDXXII +2423,MMCDXXIII +2424,MMCDXXIV +2425,MMCDXXV +2426,MMCDXXVI +2427,MMCDXXVII +2428,MMCDXXVIII +2429,MMCDXXIX +2430,MMCDXXX +2431,MMCDXXXI +2432,MMCDXXXII +2433,MMCDXXXIII +2434,MMCDXXXIV +2435,MMCDXXXV +2436,MMCDXXXVI +2437,MMCDXXXVII +2438,MMCDXXXVIII +2439,MMCDXXXIX +2440,MMCDXL +2441,MMCDXLI +2442,MMCDXLII +2443,MMCDXLIII +2444,MMCDXLIV +2445,MMCDXLV +2446,MMCDXLVI +2447,MMCDXLVII +2448,MMCDXLVIII +2449,MMCDXLIX +2450,MMCDL +2451,MMCDLI +2452,MMCDLII +2453,MMCDLIII +2454,MMCDLIV +2455,MMCDLV +2456,MMCDLVI +2457,MMCDLVII +2458,MMCDLVIII +2459,MMCDLIX +2460,MMCDLX +2461,MMCDLXI +2462,MMCDLXII +2463,MMCDLXIII +2464,MMCDLXIV +2465,MMCDLXV +2466,MMCDLXVI +2467,MMCDLXVII +2468,MMCDLXVIII +2469,MMCDLXIX +2470,MMCDLXX +2471,MMCDLXXI +2472,MMCDLXXII +2473,MMCDLXXIII +2474,MMCDLXXIV +2475,MMCDLXXV +2476,MMCDLXXVI +2477,MMCDLXXVII +2478,MMCDLXXVIII +2479,MMCDLXXIX +2480,MMCDLXXX +2481,MMCDLXXXI +2482,MMCDLXXXII +2483,MMCDLXXXIII +2484,MMCDLXXXIV +2485,MMCDLXXXV +2486,MMCDLXXXVI +2487,MMCDLXXXVII +2488,MMCDLXXXVIII +2489,MMCDLXXXIX +2490,MMCDXC +2491,MMCDXCI +2492,MMCDXCII +2493,MMCDXCIII +2494,MMCDXCIV +2495,MMCDXCV +2496,MMCDXCVI +2497,MMCDXCVII +2498,MMCDXCVIII +2499,MMCDXCIX +2500,MMD +2501,MMDI +2502,MMDII +2503,MMDIII +2504,MMDIV +2505,MMDV +2506,MMDVI +2507,MMDVII +2508,MMDVIII +2509,MMDIX +2510,MMDX +2511,MMDXI +2512,MMDXII +2513,MMDXIII +2514,MMDXIV +2515,MMDXV +2516,MMDXVI +2517,MMDXVII +2518,MMDXVIII +2519,MMDXIX +2520,MMDXX +2521,MMDXXI +2522,MMDXXII +2523,MMDXXIII +2524,MMDXXIV +2525,MMDXXV +2526,MMDXXVI +2527,MMDXXVII +2528,MMDXXVIII +2529,MMDXXIX +2530,MMDXXX +2531,MMDXXXI +2532,MMDXXXII +2533,MMDXXXIII +2534,MMDXXXIV +2535,MMDXXXV +2536,MMDXXXVI +2537,MMDXXXVII +2538,MMDXXXVIII +2539,MMDXXXIX +2540,MMDXL +2541,MMDXLI +2542,MMDXLII +2543,MMDXLIII +2544,MMDXLIV +2545,MMDXLV +2546,MMDXLVI +2547,MMDXLVII +2548,MMDXLVIII +2549,MMDXLIX +2550,MMDL +2551,MMDLI +2552,MMDLII +2553,MMDLIII +2554,MMDLIV +2555,MMDLV +2556,MMDLVI +2557,MMDLVII +2558,MMDLVIII +2559,MMDLIX +2560,MMDLX +2561,MMDLXI +2562,MMDLXII +2563,MMDLXIII +2564,MMDLXIV +2565,MMDLXV +2566,MMDLXVI +2567,MMDLXVII +2568,MMDLXVIII +2569,MMDLXIX +2570,MMDLXX +2571,MMDLXXI +2572,MMDLXXII +2573,MMDLXXIII +2574,MMDLXXIV +2575,MMDLXXV +2576,MMDLXXVI +2577,MMDLXXVII +2578,MMDLXXVIII +2579,MMDLXXIX +2580,MMDLXXX +2581,MMDLXXXI +2582,MMDLXXXII +2583,MMDLXXXIII +2584,MMDLXXXIV +2585,MMDLXXXV +2586,MMDLXXXVI +2587,MMDLXXXVII +2588,MMDLXXXVIII +2589,MMDLXXXIX +2590,MMDXC +2591,MMDXCI +2592,MMDXCII +2593,MMDXCIII +2594,MMDXCIV +2595,MMDXCV +2596,MMDXCVI +2597,MMDXCVII +2598,MMDXCVIII +2599,MMDXCIX +2600,MMDC +2601,MMDCI +2602,MMDCII +2603,MMDCIII +2604,MMDCIV +2605,MMDCV +2606,MMDCVI +2607,MMDCVII +2608,MMDCVIII +2609,MMDCIX +2610,MMDCX +2611,MMDCXI +2612,MMDCXII +2613,MMDCXIII +2614,MMDCXIV +2615,MMDCXV +2616,MMDCXVI +2617,MMDCXVII +2618,MMDCXVIII +2619,MMDCXIX +2620,MMDCXX +2621,MMDCXXI +2622,MMDCXXII +2623,MMDCXXIII +2624,MMDCXXIV +2625,MMDCXXV +2626,MMDCXXVI +2627,MMDCXXVII +2628,MMDCXXVIII +2629,MMDCXXIX +2630,MMDCXXX +2631,MMDCXXXI +2632,MMDCXXXII +2633,MMDCXXXIII +2634,MMDCXXXIV +2635,MMDCXXXV +2636,MMDCXXXVI +2637,MMDCXXXVII +2638,MMDCXXXVIII +2639,MMDCXXXIX +2640,MMDCXL +2641,MMDCXLI +2642,MMDCXLII +2643,MMDCXLIII +2644,MMDCXLIV +2645,MMDCXLV +2646,MMDCXLVI +2647,MMDCXLVII +2648,MMDCXLVIII +2649,MMDCXLIX +2650,MMDCL +2651,MMDCLI +2652,MMDCLII +2653,MMDCLIII +2654,MMDCLIV +2655,MMDCLV +2656,MMDCLVI +2657,MMDCLVII +2658,MMDCLVIII +2659,MMDCLIX +2660,MMDCLX +2661,MMDCLXI +2662,MMDCLXII +2663,MMDCLXIII +2664,MMDCLXIV +2665,MMDCLXV +2666,MMDCLXVI +2667,MMDCLXVII +2668,MMDCLXVIII +2669,MMDCLXIX +2670,MMDCLXX +2671,MMDCLXXI +2672,MMDCLXXII +2673,MMDCLXXIII +2674,MMDCLXXIV +2675,MMDCLXXV +2676,MMDCLXXVI +2677,MMDCLXXVII +2678,MMDCLXXVIII +2679,MMDCLXXIX +2680,MMDCLXXX +2681,MMDCLXXXI +2682,MMDCLXXXII +2683,MMDCLXXXIII +2684,MMDCLXXXIV +2685,MMDCLXXXV +2686,MMDCLXXXVI +2687,MMDCLXXXVII +2688,MMDCLXXXVIII +2689,MMDCLXXXIX +2690,MMDCXC +2691,MMDCXCI +2692,MMDCXCII +2693,MMDCXCIII +2694,MMDCXCIV +2695,MMDCXCV +2696,MMDCXCVI +2697,MMDCXCVII +2698,MMDCXCVIII +2699,MMDCXCIX +2700,MMDCC +2701,MMDCCI +2702,MMDCCII +2703,MMDCCIII +2704,MMDCCIV +2705,MMDCCV +2706,MMDCCVI +2707,MMDCCVII +2708,MMDCCVIII +2709,MMDCCIX +2710,MMDCCX +2711,MMDCCXI +2712,MMDCCXII +2713,MMDCCXIII +2714,MMDCCXIV +2715,MMDCCXV +2716,MMDCCXVI +2717,MMDCCXVII +2718,MMDCCXVIII +2719,MMDCCXIX +2720,MMDCCXX +2721,MMDCCXXI +2722,MMDCCXXII +2723,MMDCCXXIII +2724,MMDCCXXIV +2725,MMDCCXXV +2726,MMDCCXXVI +2727,MMDCCXXVII +2728,MMDCCXXVIII +2729,MMDCCXXIX +2730,MMDCCXXX +2731,MMDCCXXXI +2732,MMDCCXXXII +2733,MMDCCXXXIII +2734,MMDCCXXXIV +2735,MMDCCXXXV +2736,MMDCCXXXVI +2737,MMDCCXXXVII +2738,MMDCCXXXVIII +2739,MMDCCXXXIX +2740,MMDCCXL +2741,MMDCCXLI +2742,MMDCCXLII +2743,MMDCCXLIII +2744,MMDCCXLIV +2745,MMDCCXLV +2746,MMDCCXLVI +2747,MMDCCXLVII +2748,MMDCCXLVIII +2749,MMDCCXLIX +2750,MMDCCL +2751,MMDCCLI +2752,MMDCCLII +2753,MMDCCLIII +2754,MMDCCLIV +2755,MMDCCLV +2756,MMDCCLVI +2757,MMDCCLVII +2758,MMDCCLVIII +2759,MMDCCLIX +2760,MMDCCLX +2761,MMDCCLXI +2762,MMDCCLXII +2763,MMDCCLXIII +2764,MMDCCLXIV +2765,MMDCCLXV +2766,MMDCCLXVI +2767,MMDCCLXVII +2768,MMDCCLXVIII +2769,MMDCCLXIX +2770,MMDCCLXX +2771,MMDCCLXXI +2772,MMDCCLXXII +2773,MMDCCLXXIII +2774,MMDCCLXXIV +2775,MMDCCLXXV +2776,MMDCCLXXVI +2777,MMDCCLXXVII +2778,MMDCCLXXVIII +2779,MMDCCLXXIX +2780,MMDCCLXXX +2781,MMDCCLXXXI +2782,MMDCCLXXXII +2783,MMDCCLXXXIII +2784,MMDCCLXXXIV +2785,MMDCCLXXXV +2786,MMDCCLXXXVI +2787,MMDCCLXXXVII +2788,MMDCCLXXXVIII +2789,MMDCCLXXXIX +2790,MMDCCXC +2791,MMDCCXCI +2792,MMDCCXCII +2793,MMDCCXCIII +2794,MMDCCXCIV +2795,MMDCCXCV +2796,MMDCCXCVI +2797,MMDCCXCVII +2798,MMDCCXCVIII +2799,MMDCCXCIX +2800,MMDCCC +2801,MMDCCCI +2802,MMDCCCII +2803,MMDCCCIII +2804,MMDCCCIV +2805,MMDCCCV +2806,MMDCCCVI +2807,MMDCCCVII +2808,MMDCCCVIII +2809,MMDCCCIX +2810,MMDCCCX +2811,MMDCCCXI +2812,MMDCCCXII +2813,MMDCCCXIII +2814,MMDCCCXIV +2815,MMDCCCXV +2816,MMDCCCXVI +2817,MMDCCCXVII +2818,MMDCCCXVIII +2819,MMDCCCXIX +2820,MMDCCCXX +2821,MMDCCCXXI +2822,MMDCCCXXII +2823,MMDCCCXXIII +2824,MMDCCCXXIV +2825,MMDCCCXXV +2826,MMDCCCXXVI +2827,MMDCCCXXVII +2828,MMDCCCXXVIII +2829,MMDCCCXXIX +2830,MMDCCCXXX +2831,MMDCCCXXXI +2832,MMDCCCXXXII +2833,MMDCCCXXXIII +2834,MMDCCCXXXIV +2835,MMDCCCXXXV +2836,MMDCCCXXXVI +2837,MMDCCCXXXVII +2838,MMDCCCXXXVIII +2839,MMDCCCXXXIX +2840,MMDCCCXL +2841,MMDCCCXLI +2842,MMDCCCXLII +2843,MMDCCCXLIII +2844,MMDCCCXLIV +2845,MMDCCCXLV +2846,MMDCCCXLVI +2847,MMDCCCXLVII +2848,MMDCCCXLVIII +2849,MMDCCCXLIX +2850,MMDCCCL +2851,MMDCCCLI +2852,MMDCCCLII +2853,MMDCCCLIII +2854,MMDCCCLIV +2855,MMDCCCLV +2856,MMDCCCLVI +2857,MMDCCCLVII +2858,MMDCCCLVIII +2859,MMDCCCLIX +2860,MMDCCCLX +2861,MMDCCCLXI +2862,MMDCCCLXII +2863,MMDCCCLXIII +2864,MMDCCCLXIV +2865,MMDCCCLXV +2866,MMDCCCLXVI +2867,MMDCCCLXVII +2868,MMDCCCLXVIII +2869,MMDCCCLXIX +2870,MMDCCCLXX +2871,MMDCCCLXXI +2872,MMDCCCLXXII +2873,MMDCCCLXXIII +2874,MMDCCCLXXIV +2875,MMDCCCLXXV +2876,MMDCCCLXXVI +2877,MMDCCCLXXVII +2878,MMDCCCLXXVIII +2879,MMDCCCLXXIX +2880,MMDCCCLXXX +2881,MMDCCCLXXXI +2882,MMDCCCLXXXII +2883,MMDCCCLXXXIII +2884,MMDCCCLXXXIV +2885,MMDCCCLXXXV +2886,MMDCCCLXXXVI +2887,MMDCCCLXXXVII +2888,MMDCCCLXXXVIII +2889,MMDCCCLXXXIX +2890,MMDCCCXC +2891,MMDCCCXCI +2892,MMDCCCXCII +2893,MMDCCCXCIII +2894,MMDCCCXCIV +2895,MMDCCCXCV +2896,MMDCCCXCVI +2897,MMDCCCXCVII +2898,MMDCCCXCVIII +2899,MMDCCCXCIX +2900,MMCM +2901,MMCMI +2902,MMCMII +2903,MMCMIII +2904,MMCMIV +2905,MMCMV +2906,MMCMVI +2907,MMCMVII +2908,MMCMVIII +2909,MMCMIX +2910,MMCMX +2911,MMCMXI +2912,MMCMXII +2913,MMCMXIII +2914,MMCMXIV +2915,MMCMXV +2916,MMCMXVI +2917,MMCMXVII +2918,MMCMXVIII +2919,MMCMXIX +2920,MMCMXX +2921,MMCMXXI +2922,MMCMXXII +2923,MMCMXXIII +2924,MMCMXXIV +2925,MMCMXXV +2926,MMCMXXVI +2927,MMCMXXVII +2928,MMCMXXVIII +2929,MMCMXXIX +2930,MMCMXXX +2931,MMCMXXXI +2932,MMCMXXXII +2933,MMCMXXXIII +2934,MMCMXXXIV +2935,MMCMXXXV +2936,MMCMXXXVI +2937,MMCMXXXVII +2938,MMCMXXXVIII +2939,MMCMXXXIX +2940,MMCMXL +2941,MMCMXLI +2942,MMCMXLII +2943,MMCMXLIII +2944,MMCMXLIV +2945,MMCMXLV +2946,MMCMXLVI +2947,MMCMXLVII +2948,MMCMXLVIII +2949,MMCMXLIX +2950,MMCML +2951,MMCMLI +2952,MMCMLII +2953,MMCMLIII +2954,MMCMLIV +2955,MMCMLV +2956,MMCMLVI +2957,MMCMLVII +2958,MMCMLVIII +2959,MMCMLIX +2960,MMCMLX +2961,MMCMLXI +2962,MMCMLXII +2963,MMCMLXIII +2964,MMCMLXIV +2965,MMCMLXV +2966,MMCMLXVI +2967,MMCMLXVII +2968,MMCMLXVIII +2969,MMCMLXIX +2970,MMCMLXX +2971,MMCMLXXI +2972,MMCMLXXII +2973,MMCMLXXIII +2974,MMCMLXXIV +2975,MMCMLXXV +2976,MMCMLXXVI +2977,MMCMLXXVII +2978,MMCMLXXVIII +2979,MMCMLXXIX +2980,MMCMLXXX +2981,MMCMLXXXI +2982,MMCMLXXXII +2983,MMCMLXXXIII +2984,MMCMLXXXIV +2985,MMCMLXXXV +2986,MMCMLXXXVI +2987,MMCMLXXXVII +2988,MMCMLXXXVIII +2989,MMCMLXXXIX +2990,MMCMXC +2991,MMCMXCI +2992,MMCMXCII +2993,MMCMXCIII +2994,MMCMXCIV +2995,MMCMXCV +2996,MMCMXCVI +2997,MMCMXCVII +2998,MMCMXCVIII +2999,MMCMXCIX +3000,MMM +3001,MMMI +3002,MMMII +3003,MMMIII +3004,MMMIV +3005,MMMV +3006,MMMVI +3007,MMMVII +3008,MMMVIII +3009,MMMIX +3010,MMMX +3011,MMMXI +3012,MMMXII +3013,MMMXIII +3014,MMMXIV +3015,MMMXV +3016,MMMXVI +3017,MMMXVII +3018,MMMXVIII +3019,MMMXIX +3020,MMMXX +3021,MMMXXI +3022,MMMXXII +3023,MMMXXIII +3024,MMMXXIV +3025,MMMXXV +3026,MMMXXVI +3027,MMMXXVII +3028,MMMXXVIII +3029,MMMXXIX +3030,MMMXXX +3031,MMMXXXI +3032,MMMXXXII +3033,MMMXXXIII +3034,MMMXXXIV +3035,MMMXXXV +3036,MMMXXXVI +3037,MMMXXXVII +3038,MMMXXXVIII +3039,MMMXXXIX +3040,MMMXL +3041,MMMXLI +3042,MMMXLII +3043,MMMXLIII +3044,MMMXLIV +3045,MMMXLV +3046,MMMXLVI +3047,MMMXLVII +3048,MMMXLVIII +3049,MMMXLIX +3050,MMML +3051,MMMLI +3052,MMMLII +3053,MMMLIII +3054,MMMLIV +3055,MMMLV +3056,MMMLVI +3057,MMMLVII +3058,MMMLVIII +3059,MMMLIX +3060,MMMLX +3061,MMMLXI +3062,MMMLXII +3063,MMMLXIII +3064,MMMLXIV +3065,MMMLXV +3066,MMMLXVI +3067,MMMLXVII +3068,MMMLXVIII +3069,MMMLXIX +3070,MMMLXX +3071,MMMLXXI +3072,MMMLXXII +3073,MMMLXXIII +3074,MMMLXXIV +3075,MMMLXXV +3076,MMMLXXVI +3077,MMMLXXVII +3078,MMMLXXVIII +3079,MMMLXXIX +3080,MMMLXXX +3081,MMMLXXXI +3082,MMMLXXXII +3083,MMMLXXXIII +3084,MMMLXXXIV +3085,MMMLXXXV +3086,MMMLXXXVI +3087,MMMLXXXVII +3088,MMMLXXXVIII +3089,MMMLXXXIX +3090,MMMXC +3091,MMMXCI +3092,MMMXCII +3093,MMMXCIII +3094,MMMXCIV +3095,MMMXCV +3096,MMMXCVI +3097,MMMXCVII +3098,MMMXCVIII +3099,MMMXCIX +3100,MMMC +3101,MMMCI +3102,MMMCII +3103,MMMCIII +3104,MMMCIV +3105,MMMCV +3106,MMMCVI +3107,MMMCVII +3108,MMMCVIII +3109,MMMCIX +3110,MMMCX +3111,MMMCXI +3112,MMMCXII +3113,MMMCXIII +3114,MMMCXIV +3115,MMMCXV +3116,MMMCXVI +3117,MMMCXVII +3118,MMMCXVIII +3119,MMMCXIX +3120,MMMCXX +3121,MMMCXXI +3122,MMMCXXII +3123,MMMCXXIII +3124,MMMCXXIV +3125,MMMCXXV +3126,MMMCXXVI +3127,MMMCXXVII +3128,MMMCXXVIII +3129,MMMCXXIX +3130,MMMCXXX +3131,MMMCXXXI +3132,MMMCXXXII +3133,MMMCXXXIII +3134,MMMCXXXIV +3135,MMMCXXXV +3136,MMMCXXXVI +3137,MMMCXXXVII +3138,MMMCXXXVIII +3139,MMMCXXXIX +3140,MMMCXL +3141,MMMCXLI +3142,MMMCXLII +3143,MMMCXLIII +3144,MMMCXLIV +3145,MMMCXLV +3146,MMMCXLVI +3147,MMMCXLVII +3148,MMMCXLVIII +3149,MMMCXLIX +3150,MMMCL +3151,MMMCLI +3152,MMMCLII +3153,MMMCLIII +3154,MMMCLIV +3155,MMMCLV +3156,MMMCLVI +3157,MMMCLVII +3158,MMMCLVIII +3159,MMMCLIX +3160,MMMCLX +3161,MMMCLXI +3162,MMMCLXII +3163,MMMCLXIII +3164,MMMCLXIV +3165,MMMCLXV +3166,MMMCLXVI +3167,MMMCLXVII +3168,MMMCLXVIII +3169,MMMCLXIX +3170,MMMCLXX +3171,MMMCLXXI +3172,MMMCLXXII +3173,MMMCLXXIII +3174,MMMCLXXIV +3175,MMMCLXXV +3176,MMMCLXXVI +3177,MMMCLXXVII +3178,MMMCLXXVIII +3179,MMMCLXXIX +3180,MMMCLXXX +3181,MMMCLXXXI +3182,MMMCLXXXII +3183,MMMCLXXXIII +3184,MMMCLXXXIV +3185,MMMCLXXXV +3186,MMMCLXXXVI +3187,MMMCLXXXVII +3188,MMMCLXXXVIII +3189,MMMCLXXXIX +3190,MMMCXC +3191,MMMCXCI +3192,MMMCXCII +3193,MMMCXCIII +3194,MMMCXCIV +3195,MMMCXCV +3196,MMMCXCVI +3197,MMMCXCVII +3198,MMMCXCVIII +3199,MMMCXCIX +3200,MMMCC +3201,MMMCCI +3202,MMMCCII +3203,MMMCCIII +3204,MMMCCIV +3205,MMMCCV +3206,MMMCCVI +3207,MMMCCVII +3208,MMMCCVIII +3209,MMMCCIX +3210,MMMCCX +3211,MMMCCXI +3212,MMMCCXII +3213,MMMCCXIII +3214,MMMCCXIV +3215,MMMCCXV +3216,MMMCCXVI +3217,MMMCCXVII +3218,MMMCCXVIII +3219,MMMCCXIX +3220,MMMCCXX +3221,MMMCCXXI +3222,MMMCCXXII +3223,MMMCCXXIII +3224,MMMCCXXIV +3225,MMMCCXXV +3226,MMMCCXXVI +3227,MMMCCXXVII +3228,MMMCCXXVIII +3229,MMMCCXXIX +3230,MMMCCXXX +3231,MMMCCXXXI +3232,MMMCCXXXII +3233,MMMCCXXXIII +3234,MMMCCXXXIV +3235,MMMCCXXXV +3236,MMMCCXXXVI +3237,MMMCCXXXVII +3238,MMMCCXXXVIII +3239,MMMCCXXXIX +3240,MMMCCXL +3241,MMMCCXLI +3242,MMMCCXLII +3243,MMMCCXLIII +3244,MMMCCXLIV +3245,MMMCCXLV +3246,MMMCCXLVI +3247,MMMCCXLVII +3248,MMMCCXLVIII +3249,MMMCCXLIX +3250,MMMCCL +3251,MMMCCLI +3252,MMMCCLII +3253,MMMCCLIII +3254,MMMCCLIV +3255,MMMCCLV +3256,MMMCCLVI +3257,MMMCCLVII +3258,MMMCCLVIII +3259,MMMCCLIX +3260,MMMCCLX +3261,MMMCCLXI +3262,MMMCCLXII +3263,MMMCCLXIII +3264,MMMCCLXIV +3265,MMMCCLXV +3266,MMMCCLXVI +3267,MMMCCLXVII +3268,MMMCCLXVIII +3269,MMMCCLXIX +3270,MMMCCLXX +3271,MMMCCLXXI +3272,MMMCCLXXII +3273,MMMCCLXXIII +3274,MMMCCLXXIV +3275,MMMCCLXXV +3276,MMMCCLXXVI +3277,MMMCCLXXVII +3278,MMMCCLXXVIII +3279,MMMCCLXXIX +3280,MMMCCLXXX +3281,MMMCCLXXXI +3282,MMMCCLXXXII +3283,MMMCCLXXXIII +3284,MMMCCLXXXIV +3285,MMMCCLXXXV +3286,MMMCCLXXXVI +3287,MMMCCLXXXVII +3288,MMMCCLXXXVIII +3289,MMMCCLXXXIX +3290,MMMCCXC +3291,MMMCCXCI +3292,MMMCCXCII +3293,MMMCCXCIII +3294,MMMCCXCIV +3295,MMMCCXCV +3296,MMMCCXCVI +3297,MMMCCXCVII +3298,MMMCCXCVIII +3299,MMMCCXCIX +3300,MMMCCC +3301,MMMCCCI +3302,MMMCCCII +3303,MMMCCCIII +3304,MMMCCCIV +3305,MMMCCCV +3306,MMMCCCVI +3307,MMMCCCVII +3308,MMMCCCVIII +3309,MMMCCCIX +3310,MMMCCCX +3311,MMMCCCXI +3312,MMMCCCXII +3313,MMMCCCXIII +3314,MMMCCCXIV +3315,MMMCCCXV +3316,MMMCCCXVI +3317,MMMCCCXVII +3318,MMMCCCXVIII +3319,MMMCCCXIX +3320,MMMCCCXX +3321,MMMCCCXXI +3322,MMMCCCXXII +3323,MMMCCCXXIII +3324,MMMCCCXXIV +3325,MMMCCCXXV +3326,MMMCCCXXVI +3327,MMMCCCXXVII +3328,MMMCCCXXVIII +3329,MMMCCCXXIX +3330,MMMCCCXXX +3331,MMMCCCXXXI +3332,MMMCCCXXXII +3333,MMMCCCXXXIII +3334,MMMCCCXXXIV +3335,MMMCCCXXXV +3336,MMMCCCXXXVI +3337,MMMCCCXXXVII +3338,MMMCCCXXXVIII +3339,MMMCCCXXXIX +3340,MMMCCCXL +3341,MMMCCCXLI +3342,MMMCCCXLII +3343,MMMCCCXLIII +3344,MMMCCCXLIV +3345,MMMCCCXLV +3346,MMMCCCXLVI +3347,MMMCCCXLVII +3348,MMMCCCXLVIII +3349,MMMCCCXLIX +3350,MMMCCCL +3351,MMMCCCLI +3352,MMMCCCLII +3353,MMMCCCLIII +3354,MMMCCCLIV +3355,MMMCCCLV +3356,MMMCCCLVI +3357,MMMCCCLVII +3358,MMMCCCLVIII +3359,MMMCCCLIX +3360,MMMCCCLX +3361,MMMCCCLXI +3362,MMMCCCLXII +3363,MMMCCCLXIII +3364,MMMCCCLXIV +3365,MMMCCCLXV +3366,MMMCCCLXVI +3367,MMMCCCLXVII +3368,MMMCCCLXVIII +3369,MMMCCCLXIX +3370,MMMCCCLXX +3371,MMMCCCLXXI +3372,MMMCCCLXXII +3373,MMMCCCLXXIII +3374,MMMCCCLXXIV +3375,MMMCCCLXXV +3376,MMMCCCLXXVI +3377,MMMCCCLXXVII +3378,MMMCCCLXXVIII +3379,MMMCCCLXXIX +3380,MMMCCCLXXX +3381,MMMCCCLXXXI +3382,MMMCCCLXXXII +3383,MMMCCCLXXXIII +3384,MMMCCCLXXXIV +3385,MMMCCCLXXXV +3386,MMMCCCLXXXVI +3387,MMMCCCLXXXVII +3388,MMMCCCLXXXVIII +3389,MMMCCCLXXXIX +3390,MMMCCCXC +3391,MMMCCCXCI +3392,MMMCCCXCII +3393,MMMCCCXCIII +3394,MMMCCCXCIV +3395,MMMCCCXCV +3396,MMMCCCXCVI +3397,MMMCCCXCVII +3398,MMMCCCXCVIII +3399,MMMCCCXCIX +3400,MMMCD +3401,MMMCDI +3402,MMMCDII +3403,MMMCDIII +3404,MMMCDIV +3405,MMMCDV +3406,MMMCDVI +3407,MMMCDVII +3408,MMMCDVIII +3409,MMMCDIX +3410,MMMCDX +3411,MMMCDXI +3412,MMMCDXII +3413,MMMCDXIII +3414,MMMCDXIV +3415,MMMCDXV +3416,MMMCDXVI +3417,MMMCDXVII +3418,MMMCDXVIII +3419,MMMCDXIX +3420,MMMCDXX +3421,MMMCDXXI +3422,MMMCDXXII +3423,MMMCDXXIII +3424,MMMCDXXIV +3425,MMMCDXXV +3426,MMMCDXXVI +3427,MMMCDXXVII +3428,MMMCDXXVIII +3429,MMMCDXXIX +3430,MMMCDXXX +3431,MMMCDXXXI +3432,MMMCDXXXII +3433,MMMCDXXXIII +3434,MMMCDXXXIV +3435,MMMCDXXXV +3436,MMMCDXXXVI +3437,MMMCDXXXVII +3438,MMMCDXXXVIII +3439,MMMCDXXXIX +3440,MMMCDXL +3441,MMMCDXLI +3442,MMMCDXLII +3443,MMMCDXLIII +3444,MMMCDXLIV +3445,MMMCDXLV +3446,MMMCDXLVI +3447,MMMCDXLVII +3448,MMMCDXLVIII +3449,MMMCDXLIX +3450,MMMCDL +3451,MMMCDLI +3452,MMMCDLII +3453,MMMCDLIII +3454,MMMCDLIV +3455,MMMCDLV +3456,MMMCDLVI +3457,MMMCDLVII +3458,MMMCDLVIII +3459,MMMCDLIX +3460,MMMCDLX +3461,MMMCDLXI +3462,MMMCDLXII +3463,MMMCDLXIII +3464,MMMCDLXIV +3465,MMMCDLXV +3466,MMMCDLXVI +3467,MMMCDLXVII +3468,MMMCDLXVIII +3469,MMMCDLXIX +3470,MMMCDLXX +3471,MMMCDLXXI +3472,MMMCDLXXII +3473,MMMCDLXXIII +3474,MMMCDLXXIV +3475,MMMCDLXXV +3476,MMMCDLXXVI +3477,MMMCDLXXVII +3478,MMMCDLXXVIII +3479,MMMCDLXXIX +3480,MMMCDLXXX +3481,MMMCDLXXXI +3482,MMMCDLXXXII +3483,MMMCDLXXXIII +3484,MMMCDLXXXIV +3485,MMMCDLXXXV +3486,MMMCDLXXXVI +3487,MMMCDLXXXVII +3488,MMMCDLXXXVIII +3489,MMMCDLXXXIX +3490,MMMCDXC +3491,MMMCDXCI +3492,MMMCDXCII +3493,MMMCDXCIII +3494,MMMCDXCIV +3495,MMMCDXCV +3496,MMMCDXCVI +3497,MMMCDXCVII +3498,MMMCDXCVIII +3499,MMMCDXCIX +3500,MMMD +3501,MMMDI +3502,MMMDII +3503,MMMDIII +3504,MMMDIV +3505,MMMDV +3506,MMMDVI +3507,MMMDVII +3508,MMMDVIII +3509,MMMDIX +3510,MMMDX +3511,MMMDXI +3512,MMMDXII +3513,MMMDXIII +3514,MMMDXIV +3515,MMMDXV +3516,MMMDXVI +3517,MMMDXVII +3518,MMMDXVIII +3519,MMMDXIX +3520,MMMDXX +3521,MMMDXXI +3522,MMMDXXII +3523,MMMDXXIII +3524,MMMDXXIV +3525,MMMDXXV +3526,MMMDXXVI +3527,MMMDXXVII +3528,MMMDXXVIII +3529,MMMDXXIX +3530,MMMDXXX +3531,MMMDXXXI +3532,MMMDXXXII +3533,MMMDXXXIII +3534,MMMDXXXIV +3535,MMMDXXXV +3536,MMMDXXXVI +3537,MMMDXXXVII +3538,MMMDXXXVIII +3539,MMMDXXXIX +3540,MMMDXL +3541,MMMDXLI +3542,MMMDXLII +3543,MMMDXLIII +3544,MMMDXLIV +3545,MMMDXLV +3546,MMMDXLVI +3547,MMMDXLVII +3548,MMMDXLVIII +3549,MMMDXLIX +3550,MMMDL +3551,MMMDLI +3552,MMMDLII +3553,MMMDLIII +3554,MMMDLIV +3555,MMMDLV +3556,MMMDLVI +3557,MMMDLVII +3558,MMMDLVIII +3559,MMMDLIX +3560,MMMDLX +3561,MMMDLXI +3562,MMMDLXII +3563,MMMDLXIII +3564,MMMDLXIV +3565,MMMDLXV +3566,MMMDLXVI +3567,MMMDLXVII +3568,MMMDLXVIII +3569,MMMDLXIX +3570,MMMDLXX +3571,MMMDLXXI +3572,MMMDLXXII +3573,MMMDLXXIII +3574,MMMDLXXIV +3575,MMMDLXXV +3576,MMMDLXXVI +3577,MMMDLXXVII +3578,MMMDLXXVIII +3579,MMMDLXXIX +3580,MMMDLXXX +3581,MMMDLXXXI +3582,MMMDLXXXII +3583,MMMDLXXXIII +3584,MMMDLXXXIV +3585,MMMDLXXXV +3586,MMMDLXXXVI +3587,MMMDLXXXVII +3588,MMMDLXXXVIII +3589,MMMDLXXXIX +3590,MMMDXC +3591,MMMDXCI +3592,MMMDXCII +3593,MMMDXCIII +3594,MMMDXCIV +3595,MMMDXCV +3596,MMMDXCVI +3597,MMMDXCVII +3598,MMMDXCVIII +3599,MMMDXCIX +3600,MMMDC +3601,MMMDCI +3602,MMMDCII +3603,MMMDCIII +3604,MMMDCIV +3605,MMMDCV +3606,MMMDCVI +3607,MMMDCVII +3608,MMMDCVIII +3609,MMMDCIX +3610,MMMDCX +3611,MMMDCXI +3612,MMMDCXII +3613,MMMDCXIII +3614,MMMDCXIV +3615,MMMDCXV +3616,MMMDCXVI +3617,MMMDCXVII +3618,MMMDCXVIII +3619,MMMDCXIX +3620,MMMDCXX +3621,MMMDCXXI +3622,MMMDCXXII +3623,MMMDCXXIII +3624,MMMDCXXIV +3625,MMMDCXXV +3626,MMMDCXXVI +3627,MMMDCXXVII +3628,MMMDCXXVIII +3629,MMMDCXXIX +3630,MMMDCXXX +3631,MMMDCXXXI +3632,MMMDCXXXII +3633,MMMDCXXXIII +3634,MMMDCXXXIV +3635,MMMDCXXXV +3636,MMMDCXXXVI +3637,MMMDCXXXVII +3638,MMMDCXXXVIII +3639,MMMDCXXXIX +3640,MMMDCXL +3641,MMMDCXLI +3642,MMMDCXLII +3643,MMMDCXLIII +3644,MMMDCXLIV +3645,MMMDCXLV +3646,MMMDCXLVI +3647,MMMDCXLVII +3648,MMMDCXLVIII +3649,MMMDCXLIX +3650,MMMDCL +3651,MMMDCLI +3652,MMMDCLII +3653,MMMDCLIII +3654,MMMDCLIV +3655,MMMDCLV +3656,MMMDCLVI +3657,MMMDCLVII +3658,MMMDCLVIII +3659,MMMDCLIX +3660,MMMDCLX +3661,MMMDCLXI +3662,MMMDCLXII +3663,MMMDCLXIII +3664,MMMDCLXIV +3665,MMMDCLXV +3666,MMMDCLXVI +3667,MMMDCLXVII +3668,MMMDCLXVIII +3669,MMMDCLXIX +3670,MMMDCLXX +3671,MMMDCLXXI +3672,MMMDCLXXII +3673,MMMDCLXXIII +3674,MMMDCLXXIV +3675,MMMDCLXXV +3676,MMMDCLXXVI +3677,MMMDCLXXVII +3678,MMMDCLXXVIII +3679,MMMDCLXXIX +3680,MMMDCLXXX +3681,MMMDCLXXXI +3682,MMMDCLXXXII +3683,MMMDCLXXXIII +3684,MMMDCLXXXIV +3685,MMMDCLXXXV +3686,MMMDCLXXXVI +3687,MMMDCLXXXVII +3688,MMMDCLXXXVIII +3689,MMMDCLXXXIX +3690,MMMDCXC +3691,MMMDCXCI +3692,MMMDCXCII +3693,MMMDCXCIII +3694,MMMDCXCIV +3695,MMMDCXCV +3696,MMMDCXCVI +3697,MMMDCXCVII +3698,MMMDCXCVIII +3699,MMMDCXCIX +3700,MMMDCC +3701,MMMDCCI +3702,MMMDCCII +3703,MMMDCCIII +3704,MMMDCCIV +3705,MMMDCCV +3706,MMMDCCVI +3707,MMMDCCVII +3708,MMMDCCVIII +3709,MMMDCCIX +3710,MMMDCCX +3711,MMMDCCXI +3712,MMMDCCXII +3713,MMMDCCXIII +3714,MMMDCCXIV +3715,MMMDCCXV +3716,MMMDCCXVI +3717,MMMDCCXVII +3718,MMMDCCXVIII +3719,MMMDCCXIX +3720,MMMDCCXX +3721,MMMDCCXXI +3722,MMMDCCXXII +3723,MMMDCCXXIII +3724,MMMDCCXXIV +3725,MMMDCCXXV +3726,MMMDCCXXVI +3727,MMMDCCXXVII +3728,MMMDCCXXVIII +3729,MMMDCCXXIX +3730,MMMDCCXXX +3731,MMMDCCXXXI +3732,MMMDCCXXXII +3733,MMMDCCXXXIII +3734,MMMDCCXXXIV +3735,MMMDCCXXXV +3736,MMMDCCXXXVI +3737,MMMDCCXXXVII +3738,MMMDCCXXXVIII +3739,MMMDCCXXXIX +3740,MMMDCCXL +3741,MMMDCCXLI +3742,MMMDCCXLII +3743,MMMDCCXLIII +3744,MMMDCCXLIV +3745,MMMDCCXLV +3746,MMMDCCXLVI +3747,MMMDCCXLVII +3748,MMMDCCXLVIII +3749,MMMDCCXLIX +3750,MMMDCCL +3751,MMMDCCLI +3752,MMMDCCLII +3753,MMMDCCLIII +3754,MMMDCCLIV +3755,MMMDCCLV +3756,MMMDCCLVI +3757,MMMDCCLVII +3758,MMMDCCLVIII +3759,MMMDCCLIX +3760,MMMDCCLX +3761,MMMDCCLXI +3762,MMMDCCLXII +3763,MMMDCCLXIII +3764,MMMDCCLXIV +3765,MMMDCCLXV +3766,MMMDCCLXVI +3767,MMMDCCLXVII +3768,MMMDCCLXVIII +3769,MMMDCCLXIX +3770,MMMDCCLXX +3771,MMMDCCLXXI +3772,MMMDCCLXXII +3773,MMMDCCLXXIII +3774,MMMDCCLXXIV +3775,MMMDCCLXXV +3776,MMMDCCLXXVI +3777,MMMDCCLXXVII +3778,MMMDCCLXXVIII +3779,MMMDCCLXXIX +3780,MMMDCCLXXX +3781,MMMDCCLXXXI +3782,MMMDCCLXXXII +3783,MMMDCCLXXXIII +3784,MMMDCCLXXXIV +3785,MMMDCCLXXXV +3786,MMMDCCLXXXVI +3787,MMMDCCLXXXVII +3788,MMMDCCLXXXVIII +3789,MMMDCCLXXXIX +3790,MMMDCCXC +3791,MMMDCCXCI +3792,MMMDCCXCII +3793,MMMDCCXCIII +3794,MMMDCCXCIV +3795,MMMDCCXCV +3796,MMMDCCXCVI +3797,MMMDCCXCVII +3798,MMMDCCXCVIII +3799,MMMDCCXCIX +3800,MMMDCCC +3801,MMMDCCCI +3802,MMMDCCCII +3803,MMMDCCCIII +3804,MMMDCCCIV +3805,MMMDCCCV +3806,MMMDCCCVI +3807,MMMDCCCVII +3808,MMMDCCCVIII +3809,MMMDCCCIX +3810,MMMDCCCX +3811,MMMDCCCXI +3812,MMMDCCCXII +3813,MMMDCCCXIII +3814,MMMDCCCXIV +3815,MMMDCCCXV +3816,MMMDCCCXVI +3817,MMMDCCCXVII +3818,MMMDCCCXVIII +3819,MMMDCCCXIX +3820,MMMDCCCXX +3821,MMMDCCCXXI +3822,MMMDCCCXXII +3823,MMMDCCCXXIII +3824,MMMDCCCXXIV +3825,MMMDCCCXXV +3826,MMMDCCCXXVI +3827,MMMDCCCXXVII +3828,MMMDCCCXXVIII +3829,MMMDCCCXXIX +3830,MMMDCCCXXX +3831,MMMDCCCXXXI +3832,MMMDCCCXXXII +3833,MMMDCCCXXXIII +3834,MMMDCCCXXXIV +3835,MMMDCCCXXXV +3836,MMMDCCCXXXVI +3837,MMMDCCCXXXVII +3838,MMMDCCCXXXVIII +3839,MMMDCCCXXXIX +3840,MMMDCCCXL +3841,MMMDCCCXLI +3842,MMMDCCCXLII +3843,MMMDCCCXLIII +3844,MMMDCCCXLIV +3845,MMMDCCCXLV +3846,MMMDCCCXLVI +3847,MMMDCCCXLVII +3848,MMMDCCCXLVIII +3849,MMMDCCCXLIX +3850,MMMDCCCL +3851,MMMDCCCLI +3852,MMMDCCCLII +3853,MMMDCCCLIII +3854,MMMDCCCLIV +3855,MMMDCCCLV +3856,MMMDCCCLVI +3857,MMMDCCCLVII +3858,MMMDCCCLVIII +3859,MMMDCCCLIX +3860,MMMDCCCLX +3861,MMMDCCCLXI +3862,MMMDCCCLXII +3863,MMMDCCCLXIII +3864,MMMDCCCLXIV +3865,MMMDCCCLXV +3866,MMMDCCCLXVI +3867,MMMDCCCLXVII +3868,MMMDCCCLXVIII +3869,MMMDCCCLXIX +3870,MMMDCCCLXX +3871,MMMDCCCLXXI +3872,MMMDCCCLXXII +3873,MMMDCCCLXXIII +3874,MMMDCCCLXXIV +3875,MMMDCCCLXXV +3876,MMMDCCCLXXVI +3877,MMMDCCCLXXVII +3878,MMMDCCCLXXVIII +3879,MMMDCCCLXXIX +3880,MMMDCCCLXXX +3881,MMMDCCCLXXXI +3882,MMMDCCCLXXXII +3883,MMMDCCCLXXXIII +3884,MMMDCCCLXXXIV +3885,MMMDCCCLXXXV +3886,MMMDCCCLXXXVI +3887,MMMDCCCLXXXVII +3888,MMMDCCCLXXXVIII +3889,MMMDCCCLXXXIX +3890,MMMDCCCXC +3891,MMMDCCCXCI +3892,MMMDCCCXCII +3893,MMMDCCCXCIII +3894,MMMDCCCXCIV +3895,MMMDCCCXCV +3896,MMMDCCCXCVI +3897,MMMDCCCXCVII +3898,MMMDCCCXCVIII +3899,MMMDCCCXCIX +3900,MMMCM +3901,MMMCMI +3902,MMMCMII +3903,MMMCMIII +3904,MMMCMIV +3905,MMMCMV +3906,MMMCMVI +3907,MMMCMVII +3908,MMMCMVIII +3909,MMMCMIX +3910,MMMCMX +3911,MMMCMXI +3912,MMMCMXII +3913,MMMCMXIII +3914,MMMCMXIV +3915,MMMCMXV +3916,MMMCMXVI +3917,MMMCMXVII +3918,MMMCMXVIII +3919,MMMCMXIX +3920,MMMCMXX +3921,MMMCMXXI +3922,MMMCMXXII +3923,MMMCMXXIII +3924,MMMCMXXIV +3925,MMMCMXXV +3926,MMMCMXXVI +3927,MMMCMXXVII +3928,MMMCMXXVIII +3929,MMMCMXXIX +3930,MMMCMXXX +3931,MMMCMXXXI +3932,MMMCMXXXII +3933,MMMCMXXXIII +3934,MMMCMXXXIV +3935,MMMCMXXXV +3936,MMMCMXXXVI +3937,MMMCMXXXVII +3938,MMMCMXXXVIII +3939,MMMCMXXXIX +3940,MMMCMXL +3941,MMMCMXLI +3942,MMMCMXLII +3943,MMMCMXLIII +3944,MMMCMXLIV +3945,MMMCMXLV +3946,MMMCMXLVI +3947,MMMCMXLVII +3948,MMMCMXLVIII +3949,MMMCMXLIX +3950,MMMCML +3951,MMMCMLI +3952,MMMCMLII +3953,MMMCMLIII +3954,MMMCMLIV +3955,MMMCMLV +3956,MMMCMLVI +3957,MMMCMLVII +3958,MMMCMLVIII +3959,MMMCMLIX +3960,MMMCMLX +3961,MMMCMLXI +3962,MMMCMLXII +3963,MMMCMLXIII +3964,MMMCMLXIV +3965,MMMCMLXV +3966,MMMCMLXVI +3967,MMMCMLXVII +3968,MMMCMLXVIII +3969,MMMCMLXIX +3970,MMMCMLXX +3971,MMMCMLXXI +3972,MMMCMLXXII +3973,MMMCMLXXIII +3974,MMMCMLXXIV +3975,MMMCMLXXV +3976,MMMCMLXXVI +3977,MMMCMLXXVII +3978,MMMCMLXXVIII +3979,MMMCMLXXIX +3980,MMMCMLXXX +3981,MMMCMLXXXI +3982,MMMCMLXXXII +3983,MMMCMLXXXIII +3984,MMMCMLXXXIV +3985,MMMCMLXXXV +3986,MMMCMLXXXVI +3987,MMMCMLXXXVII +3988,MMMCMLXXXVIII +3989,MMMCMLXXXIX +3990,MMMCMXC +3991,MMMCMXCI +3992,MMMCMXCII +3993,MMMCMXCIII +3994,MMMCMXCIV +3995,MMMCMXCV +3996,MMMCMXCVI +3997,MMMCMXCVII +3998,MMMCMXCVIII diff --git a/tests/test_nemral_roman.py b/tests/test_nemral_roman.py deleted file mode 100644 index dd80d3d..0000000 --- a/tests/test_nemral_roman.py +++ /dev/null @@ -1,14 +0,0 @@ -import pytest -from number_parser import parse_roman - - -@pytest.mark.parametrize( - "test_string, expected", - [ - ('CDXX', '420'), - ('lxix', '69'), - ('Built in MMLXXVII', 'Built in 2077'), - ] -) -def test_parse_roman(test_string, expected): - assert parse_roman(test_string) == expected diff --git a/tests/test_numeral_roman.py b/tests/test_numeral_roman.py new file mode 100644 index 0000000..98125e4 --- /dev/null +++ b/tests/test_numeral_roman.py @@ -0,0 +1,20 @@ +import pytest +import csv +from number_parser import parse, parse_number +@pytest.mark.parametrize( + "test_string, expected", + [ + ('Built in MMLXXVII', 'Built in 2077') + ] +) +def test_parse_roman(test_string, expected): + assert parse(test_string, numeral_system = 'roman') == expected + +def test_parse_all_roman_numbers(): + with open('data/all_roman_numbers.csv', "r") as csv_file: + csv_reader = csv.DictReader(csv_file) + for row in csv_reader: + try: + assert parse_number(row['text'], numeral_system='roman') == int(row['number']) + except AssertionError as e: + raise AssertionError(F"Failed execution of {row['number']},{row['text']}") From 6d420a7941c060a47e5f3e9fd442a6fc817c26c0 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Wed, 28 Apr 2021 18:15:43 +0530 Subject: [PATCH 05/17] added _valid_input_by_numeral_system() Added the _valid_input_by_numeral_system(), it will decide the numeral system based on the input string if not given by the user. --- number_parser/parser.py | 20 ++++++++++++++++++-- tests/test_numeral_roman.py | 4 ++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/number_parser/parser.py b/number_parser/parser.py index c4ab6c5..0aec6dc 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -5,6 +5,7 @@ SENTENCE_SEPARATORS = [".", ","] SUPPORTED_LANGUAGES = ['en', 'es', 'hi', 'ru'] RE_BUG_LANGUAGES = ['hi'] +NUMERAL_SYSTEM = ['decimal', 'roman'] class LanguageData: """Main language class to populate the requisite language-specific variables.""" @@ -227,6 +228,15 @@ def _valid_tokens_by_language(input_string): return 'en' return best_language +def _valid_input_by_numeral_system(input_string): + language = _valid_tokens_by_language(input_string) + tokens = _tokenize(input_string, language) + best_numeral_system = 'decimal' + for token in tokens: + if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): + best_numeral_system = 'roman' + return best_numeral_system + def _parse_roman(input_string): tokens = _tokenize(input_string, None) @@ -266,7 +276,7 @@ def parse_ordinal(input_string, language=None, numeral_system = 'decimal'): return parse_number(output_string, language) -def parse_number(input_string, language=None, numeral_system = 'decimal'): +def parse_number(input_string, language=None, numeral_system = None): """Converts a single number written in natural language to a numeric type""" if not input_string.strip(): return None @@ -274,6 +284,9 @@ def parse_number(input_string, language=None, numeral_system = 'decimal'): if input_string.strip().isnumeric(): return int(input_string) + if numeral_system == None: + numeral_system = _valid_input_by_numeral_system(input_string) + if numeral_system == 'decimal': if language is None: language = _valid_tokens_by_language(input_string) @@ -327,11 +340,14 @@ def parse_fraction(input_string, language=None): return None -def parse(input_string, language=None, numeral_system = 'decimal'): +def parse(input_string, language=None, numeral_system = None): """ Converts all the numbers in a sentence written in natural language to their numeric type while keeping the other words unchanged. Returns the transformed string. """ + if numeral_system == None: + numeral_system = _valid_input_by_numeral_system(input_string) + if numeral_system == 'decimal': if language is None: language = _valid_tokens_by_language(input_string) diff --git a/tests/test_numeral_roman.py b/tests/test_numeral_roman.py index 98125e4..755e96a 100644 --- a/tests/test_numeral_roman.py +++ b/tests/test_numeral_roman.py @@ -8,13 +8,13 @@ ] ) def test_parse_roman(test_string, expected): - assert parse(test_string, numeral_system = 'roman') == expected + assert parse(test_string) == expected def test_parse_all_roman_numbers(): with open('data/all_roman_numbers.csv', "r") as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: try: - assert parse_number(row['text'], numeral_system='roman') == int(row['number']) + assert parse_number(row['text']) == int(row['number']) except AssertionError as e: raise AssertionError(F"Failed execution of {row['number']},{row['text']}") From bf7d6e3a7cf6ccded0461a11136b443248a7d8e0 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Mon, 3 May 2021 12:59:30 +0530 Subject: [PATCH 06/17] Added NUMERAL_SYSTEMS added NUMERAL_SYSTEM list where you decide which numeral system should be used to parse. --- number_parser/__init__.py | 2 +- number_parser/parser.py | 234 ++++++++++++++++++------------------ tests/test_numeral_roman.py | 2 +- 3 files changed, 118 insertions(+), 120 deletions(-) diff --git a/number_parser/__init__.py b/number_parser/__init__.py index 472179e..af957fa 100644 --- a/number_parser/__init__.py +++ b/number_parser/__init__.py @@ -1 +1 @@ -from number_parser.parser import parse, parse_number, parse_ordinal, parse_fraction +from number_parser.parser import parse, parse_number, parse_ordinal, parse_fraction, NUMERAL_SYSTEMS diff --git a/number_parser/parser.py b/number_parser/parser.py index 0aec6dc..e6960cb 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -5,7 +5,8 @@ SENTENCE_SEPARATORS = [".", ","] SUPPORTED_LANGUAGES = ['en', 'es', 'hi', 'ru'] RE_BUG_LANGUAGES = ['hi'] -NUMERAL_SYSTEM = ['decimal', 'roman'] +NUMERAL_SYSTEMS = ['decimal', 'roman'] + class LanguageData: """Main language class to populate the requisite language-specific variables.""" @@ -228,55 +229,21 @@ def _valid_tokens_by_language(input_string): return 'en' return best_language -def _valid_input_by_numeral_system(input_string): - language = _valid_tokens_by_language(input_string) - tokens = _tokenize(input_string, language) - best_numeral_system = 'decimal' - for token in tokens: - if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): - best_numeral_system = 'roman' - return best_numeral_system - -def _parse_roman(input_string): - tokens = _tokenize(input_string, None) - - def build_roman(roman_number): - roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} - num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) - num_tokens = [item for item in num_tokens if item != ''] - built_num = 0 - for num_token in num_tokens: - if re.search('iv|ix|xl|xc|cd|cm', num_token): - built_num += roman[num_token[1]] - roman[num_token[0]] - elif re.search('[vld][ixc]{1,3}', num_token): - built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) - else: - built_num += roman[num_token[0]] * len(num_token) - - return built_num - - for token in tokens: - if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): - tokens[tokens.index(token)] = str(build_roman(token)) - final_sentence = ''.join(tokens) - - return final_sentence -def parse_ordinal(input_string, language=None, numeral_system = 'decimal'): +def parse_ordinal(input_string, language=None): """Converts a single number in ordinal or cardinal form to it's numeric equivalent""" - if numeral_system == 'decimal': - if language is None: - language = _valid_tokens_by_language(input_string) + if language is None: + language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) - tokens = _tokenize(input_string, language) - normalized_tokens = _normalize_tokens(tokens) - processed_tokens = [_apply_cardinal_conversion(token, lang_data) for token in normalized_tokens] - output_string = ' '.join(processed_tokens) - return parse_number(output_string, language) + lang_data = LanguageData(language) + tokens = _tokenize(input_string, language) + normalized_tokens = _normalize_tokens(tokens) + processed_tokens = [_apply_cardinal_conversion(token, lang_data) for token in normalized_tokens] + output_string = ' '.join(processed_tokens) + return parse_number(output_string, language) -def parse_number(input_string, language=None, numeral_system = None): +def parse_number(input_string, language=None): """Converts a single number written in natural language to a numeric type""" if not input_string.strip(): return None @@ -284,30 +251,26 @@ def parse_number(input_string, language=None, numeral_system = None): if input_string.strip().isnumeric(): return int(input_string) - if numeral_system == None: - numeral_system = _valid_input_by_numeral_system(input_string) + if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", input_string.lower()): + return int(_parse_roman(input_string)) - if numeral_system == 'decimal': - if language is None: - language = _valid_tokens_by_language(input_string) + if language is None: + language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) + lang_data = LanguageData(language) - tokens = _tokenize(input_string, language) - normalized_tokens = _normalize_tokens(tokens) - for index, token in enumerate(normalized_tokens): - if _is_cardinal_token(token, lang_data) or not token.strip(): - continue - if _is_skip_token(token, lang_data) and index != 0: - continue - return None - number_built = _build_number(normalized_tokens, lang_data) - if len(number_built) == 1: - return int(number_built[0]) + tokens = _tokenize(input_string, language) + normalized_tokens = _normalize_tokens(tokens) + for index, token in enumerate(normalized_tokens): + if _is_cardinal_token(token, lang_data) or not token.strip(): + continue + if _is_skip_token(token, lang_data) and index != 0: + continue return None - - elif numeral_system == 'roman': - return int(_parse_roman(input_string)) + number_built = _build_number(normalized_tokens, lang_data) + if len(number_built) == 1: + return int(number_built[0]) + return None def parse_fraction(input_string, language=None): @@ -340,76 +303,111 @@ def parse_fraction(input_string, language=None): return None -def parse(input_string, language=None, numeral_system = None): +def parse(input_string, language=None, numeral_systems=None): """ Converts all the numbers in a sentence written in natural language to their numeric type while keeping the other words unchanged. Returns the transformed string. """ - if numeral_system == None: - numeral_system = _valid_input_by_numeral_system(input_string) + if numeral_systems is None: + numeral_systems = ['decimal'] - if numeral_system == 'decimal': - if language is None: - language = _valid_tokens_by_language(input_string) + if language is None: + language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) + if 'decimal' in numeral_systems: + final_sentence = _parse_decimal(input_string, language) + input_string = final_sentence + return final_sentence - tokens = _tokenize(input_string, language) + if 'roman' in numeral_systems: + final_sentence = _parse_roman(input_string) + return final_sentence - final_sentence = [] - current_sentence = [] - tokens_taken = [] +def _parse_decimal(input_string, language): - def _build_and_add_number(pop_last_space=False): - if tokens_taken: - result = _build_number(tokens_taken, lang_data) - tokens_taken.clear() + lang_data = LanguageData(language) - for number in result: - current_sentence.extend([number, " "]) + tokens = _tokenize(input_string, language) + + final_sentence = [] + current_sentence = [] + tokens_taken = [] - if pop_last_space: - current_sentence.pop() + def _build_and_add_number(pop_last_space=False): + if tokens_taken: + result = _build_number(tokens_taken, lang_data) + tokens_taken.clear() - for token in tokens: - compare_token = _strip_accents(token.lower()) - ordinal_number = _is_ordinal_token(compare_token, lang_data) + for number in result: + current_sentence.extend([number, " "]) - if not compare_token.strip(): - if not tokens_taken: - current_sentence.append(token) - continue + if pop_last_space: + current_sentence.pop() + + for token in tokens: + compare_token = _strip_accents(token.lower()) + ordinal_number = _is_ordinal_token(compare_token, lang_data) - if compare_token in SENTENCE_SEPARATORS: - _build_and_add_number(pop_last_space=True) + if not compare_token.strip(): + if not tokens_taken: current_sentence.append(token) - final_sentence.extend(current_sentence) - current_sentence = [] - continue - - if ordinal_number: - tokens_taken.append(ordinal_number) - _build_and_add_number(pop_last_space=True) - elif ( - _is_cardinal_token(compare_token, lang_data) - or (_is_skip_token(compare_token, lang_data) and len(tokens_taken) != 0) - ): - tokens_taken.append(compare_token) - else: - if tokens_taken and _is_skip_token(tokens_taken[-1], lang_data): - # when finishing with a skip_token --> keep it - skip_token = tokens_taken[-1] - tokens_taken.pop() - _build_and_add_number() - current_sentence.extend([skip_token, " "]) + continue + + if compare_token in SENTENCE_SEPARATORS: + _build_and_add_number(pop_last_space=True) + current_sentence.append(token) + final_sentence.extend(current_sentence) + current_sentence = [] + continue + if ordinal_number: + tokens_taken.append(ordinal_number) + _build_and_add_number(pop_last_space=True) + elif ( + _is_cardinal_token(compare_token, lang_data) + or (_is_skip_token(compare_token, lang_data) and len(tokens_taken) != 0) + ): + tokens_taken.append(compare_token) + else: + if tokens_taken and _is_skip_token(tokens_taken[-1], lang_data): + # when finishing with a skip_token --> keep it + skip_token = tokens_taken[-1] + tokens_taken.pop() _build_and_add_number() - current_sentence.append(token) + current_sentence.extend([skip_token, " "]) + + _build_and_add_number() + current_sentence.append(token) + + _build_and_add_number() + + final_sentence.extend(current_sentence) + return ''.join(final_sentence).strip() + + +def _parse_roman(input_string): + global final_sentence + tokens = _tokenize(input_string, None) - _build_and_add_number() + for token in tokens: + if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): + tokens[tokens.index(token)] = str(build_roman(token)) + final_sentence = ''.join(tokens) + + return final_sentence - final_sentence.extend(current_sentence) - return ''.join(final_sentence).strip() - elif numeral_system == 'roman': - return _parse_roman(input_string) +def build_roman(roman_number): + roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} + num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) + num_tokens = [item for item in num_tokens if item != ''] + built_num = 0 + for num_token in num_tokens: + if re.search('iv|ix|xl|xc|cd|cm', num_token): + built_num += roman[num_token[1]] - roman[num_token[0]] + elif re.search('[vld][ixc]{1,3}', num_token): + built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) + else: + built_num += roman[num_token[0]] * len(num_token) + + return built_num diff --git a/tests/test_numeral_roman.py b/tests/test_numeral_roman.py index 755e96a..dc94d9e 100644 --- a/tests/test_numeral_roman.py +++ b/tests/test_numeral_roman.py @@ -8,7 +8,7 @@ ] ) def test_parse_roman(test_string, expected): - assert parse(test_string) == expected + assert parse(test_string, numeral_systems=['roman']) == expected def test_parse_all_roman_numbers(): with open('data/all_roman_numbers.csv', "r") as csv_file: From 5104b76d07bada0b8e308882ff6adc67b857c2f7 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Mon, 3 May 2021 23:44:02 +0530 Subject: [PATCH 07/17] fixed Unicode issue for Hindi, Spanish and Russian --- number_parser/parser.py | 34 ++++++++++--------- tests/__init__.py | 2 +- .../{ => permutations}/all_roman_numbers.csv | 0 tests/test_language_en.py | 2 +- tests/test_numeral_roman.py | 10 +++--- 5 files changed, 25 insertions(+), 23 deletions(-) rename tests/data/{ => permutations}/all_roman_numbers.csv (100%) diff --git a/number_parser/parser.py b/number_parser/parser.py index e6960cb..3c6bf04 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -308,23 +308,28 @@ def parse(input_string, language=None, numeral_systems=None): Converts all the numbers in a sentence written in natural language to their numeric type while keeping the other words unchanged. Returns the transformed string. """ + global complete_sentence + if numeral_systems is None: - numeral_systems = ['decimal'] + numeral_systems = ['decimal', 'roman'] if language is None: language = _valid_tokens_by_language(input_string) - if 'decimal' in numeral_systems: - final_sentence = _parse_decimal(input_string, language) - input_string = final_sentence - return final_sentence + for numeral_system in numeral_systems: - if 'roman' in numeral_systems: - final_sentence = _parse_roman(input_string) - return final_sentence + if numeral_system == 'decimal': + complete_sentence = _parse_decimal(input_string, language) + input_string = complete_sentence + + if numeral_system == 'roman': + complete_sentence = _parse_roman(input_string) + input_string = complete_sentence + + return complete_sentence -def _parse_decimal(input_string, language): +def _parse_decimal(input_string, language): lang_data = LanguageData(language) tokens = _tokenize(input_string, language) @@ -378,7 +383,6 @@ def _build_and_add_number(pop_last_space=False): _build_and_add_number() current_sentence.append(token) - _build_and_add_number() final_sentence.extend(current_sentence) @@ -386,18 +390,17 @@ def _build_and_add_number(pop_last_space=False): def _parse_roman(input_string): - global final_sentence tokens = _tokenize(input_string, None) - for token in tokens: if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): - tokens[tokens.index(token)] = str(build_roman(token)) - final_sentence = ''.join(tokens) + if _build_roman(token) != 0: + tokens[tokens.index(token)] = str(_build_roman(token)) + final_sentence = ''.join(tokens) return final_sentence -def build_roman(roman_number): +def _build_roman(roman_number): roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) num_tokens = [item for item in num_tokens if item != ''] @@ -409,5 +412,4 @@ def build_roman(roman_number): built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) else: built_num += roman[num_token[0]] * len(num_token) - return built_num diff --git a/tests/__init__.py b/tests/__init__.py index 31eac93..d0dc4c5 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -20,7 +20,7 @@ def get_test_files(path, prefix): def _test_files(path, language, is_ordinal=True): fnx = parse_ordinal if is_ordinal else parse_number for filename in get_test_files(path, f'{language}_'): - with open(filename, "r") as csv_file: + with open(filename, "r", encoding="utf8") as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: try: diff --git a/tests/data/all_roman_numbers.csv b/tests/data/permutations/all_roman_numbers.csv similarity index 100% rename from tests/data/all_roman_numbers.csv rename to tests/data/permutations/all_roman_numbers.csv diff --git a/tests/test_language_en.py b/tests/test_language_en.py index b91850c..cb2ce7f 100644 --- a/tests/test_language_en.py +++ b/tests/test_language_en.py @@ -61,7 +61,7 @@ def test_parse_number_digits(expected, test_input): ] ) def test_parse_basic_sentences(expected, test_input): - assert parse(test_input, LANG) == expected + assert parse(test_input, LANG, numeral_systems=['decimal']) == expected @pytest.mark.parametrize( diff --git a/tests/test_numeral_roman.py b/tests/test_numeral_roman.py index dc94d9e..b5e811d 100644 --- a/tests/test_numeral_roman.py +++ b/tests/test_numeral_roman.py @@ -2,16 +2,16 @@ import csv from number_parser import parse, parse_number @pytest.mark.parametrize( - "test_string, expected", + "test_string, numeral_systems, expected", [ - ('Built in MMLXXVII', 'Built in 2077') + ('Built in MMLXXVII', None, 'Built in 2077') ] ) -def test_parse_roman(test_string, expected): - assert parse(test_string, numeral_systems=['roman']) == expected +def test_parse_roman(test_string, numeral_systems, expected): + assert parse(test_string, numeral_systems=numeral_systems) == expected def test_parse_all_roman_numbers(): - with open('data/all_roman_numbers.csv', "r") as csv_file: + with open('data/permutations/all_roman_numbers.csv', "r") as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: try: From 94d944118e7e728bd524b75b67df0c557888fc1e Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Tue, 4 May 2021 02:12:48 +0530 Subject: [PATCH 08/17] Update test_numeral_roman.py Added more test cases to test the numeral system --- tests/test_numeral_roman.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/test_numeral_roman.py b/tests/test_numeral_roman.py index b5e811d..18b1f1e 100644 --- a/tests/test_numeral_roman.py +++ b/tests/test_numeral_roman.py @@ -1,17 +1,31 @@ import pytest import csv -from number_parser import parse, parse_number +import os + +from number_parser import parse, parse_number, NUMERAL_SYSTEMS +from tests import PERMUTATION_DIRECTORY + +all_numeral_systems_but_roman = [system for system in NUMERAL_SYSTEMS if system != 'roman'] +all_numeral_systems_but_decimal = [system for system in NUMERAL_SYSTEMS if system != 'decimal'] + + @pytest.mark.parametrize( "test_string, numeral_systems, expected", [ - ('Built in MMLXXVII', None, 'Built in 2077') + ('Built in MMLXXVII.', None, 'Built in 2077.'), + ('Built in MMLXXVII.', ['decimal'], 'Built in MMLXXVII.'), + ('I was given two IV injections.', all_numeral_systems_but_roman, 'I was given 2 IV injections.'), + ('I was given two IV injections.', all_numeral_systems_but_decimal, '1 was given two 4 injections.'), + ('I was given two IV injections.', None, '1 was given 2 4 injections.'), + ('I have three apples.', all_numeral_systems_but_roman, 'I have 3 apples.') ] ) def test_parse_roman(test_string, numeral_systems, expected): assert parse(test_string, numeral_systems=numeral_systems) == expected + def test_parse_all_roman_numbers(): - with open('data/permutations/all_roman_numbers.csv', "r") as csv_file: + with open(os.path.join(PERMUTATION_DIRECTORY, 'all_roman_numbers.csv'), "r") as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: try: From 475ad04523d5ec27816d901071bd5f45d085cb8c Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Tue, 4 May 2021 02:13:12 +0530 Subject: [PATCH 09/17] Update test_number_parsing.py --- tests/test_number_parsing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_number_parsing.py b/tests/test_number_parsing.py index 2de6d09..427530b 100644 --- a/tests/test_number_parsing.py +++ b/tests/test_number_parsing.py @@ -60,7 +60,7 @@ def test_parse_number(expected, test_input, lang): ] ) def test_parse_basic_sentences(expected, test_input, lang): - assert parse(test_input, lang) == expected + assert parse(test_input, lang, numeral_systems=['decimal']) == expected @pytest.mark.parametrize( From 45bfdc5c1cf51ca944d523671b5ea4e5aee1df8f Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Tue, 4 May 2021 18:14:46 +0530 Subject: [PATCH 10/17] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1f1f62e..fb03ccb 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,7 @@ Identifying the numbers in a text string, converting them to corresponding numer This also supports ordinal number conversion (for English only). >>> from number_parser import parse ->>> parse("I have two hats and thirty seven coats") +>>> parse("I have two hats and thirty seven coats", numeral_systems=['decimal']) 'I have 2 hats and 37 coats' >>> parse("One, Two, Three go") '1, 2, 3 go' From 973664ba8bbf183019b364a2413ef2e0654c5007 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Tue, 4 May 2021 19:49:35 +0530 Subject: [PATCH 11/17] Delete test.py --- test.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 test.py diff --git a/test.py b/test.py deleted file mode 100644 index b84c884..0000000 --- a/test.py +++ /dev/null @@ -1,3 +0,0 @@ -from number_parser import parse ,parse_number - -print(parse_number('built in CDXX', numeral_system='roman')) From 0e896801f9e8a2aa415a4854b9259ab62bd9c0c7 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Thu, 6 May 2021 18:51:32 +0530 Subject: [PATCH 12/17] Made all the changes and added support for incorrect roman numbers Made all changes except .lower() --- number_parser/parser.py | 65 ++++++++++++------- tests/data/permutations/all_roman_numbers.csv | 1 + .../permutations/incorrect_roman_numbers.csv | 7 ++ ...meral_roman.py => test_numeral_systems.py} | 17 +---- tests/test_roman.py | 28 ++++++++ 5 files changed, 79 insertions(+), 39 deletions(-) create mode 100644 tests/data/permutations/incorrect_roman_numbers.csv rename tests/{test_numeral_roman.py => test_numeral_systems.py} (57%) create mode 100644 tests/test_roman.py diff --git a/number_parser/parser.py b/number_parser/parser.py index 3c6bf04..36e7925 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -6,7 +6,7 @@ SUPPORTED_LANGUAGES = ['en', 'es', 'hi', 'ru'] RE_BUG_LANGUAGES = ['hi'] NUMERAL_SYSTEMS = ['decimal', 'roman'] - +ROMAN_REGEX_EXPRESSION = "^(m{0,3})(cm|cd|d?c{0,4})(xc|xl|l?x{0,4})(ix|iv|v?i{0,4})$" class LanguageData: """Main language class to populate the requisite language-specific variables.""" @@ -243,7 +243,7 @@ def parse_ordinal(input_string, language=None): return parse_number(output_string, language) -def parse_number(input_string, language=None): +def parse_number(input_string, language=None, numeral_systems=None): """Converts a single number written in natural language to a numeric type""" if not input_string.strip(): return None @@ -251,26 +251,36 @@ def parse_number(input_string, language=None): if input_string.strip().isnumeric(): return int(input_string) - if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", input_string.lower()): - return int(_parse_roman(input_string)) - if language is None: language = _valid_tokens_by_language(input_string) - lang_data = LanguageData(language) + if numeral_systems is None: + numeral_systems = NUMERAL_SYSTEMS - tokens = _tokenize(input_string, language) - normalized_tokens = _normalize_tokens(tokens) - for index, token in enumerate(normalized_tokens): - if _is_cardinal_token(token, lang_data) or not token.strip(): - continue - if _is_skip_token(token, lang_data) and index != 0: - continue - return None - number_built = _build_number(normalized_tokens, lang_data) - if len(number_built) == 1: - return int(number_built[0]) - return None + if re.search(ROMAN_REGEX_EXPRESSION, input_string.lower()): + numeral_systems = ['roman'] + else: + numeral_systems = ['decimal'] + + for numeral_system in numeral_systems: + if numeral_system == 'decimal': + lang_data = LanguageData(language) + + tokens = _tokenize(input_string, language) + normalized_tokens = _normalize_tokens(tokens) + for index, token in enumerate(normalized_tokens): + if _is_cardinal_token(token, lang_data) or not token.strip(): + continue + if _is_skip_token(token, lang_data) and index != 0: + continue + return None + number_built = _build_number(normalized_tokens, lang_data) + if len(number_built) == 1: + return int(number_built[0]) + return None + + elif numeral_system == 'roman': + return int(_parse_roman(input_string)) def parse_fraction(input_string, language=None): @@ -308,7 +318,7 @@ def parse(input_string, language=None, numeral_systems=None): Converts all the numbers in a sentence written in natural language to their numeric type while keeping the other words unchanged. Returns the transformed string. """ - global complete_sentence + complete_sentence = None if numeral_systems is None: numeral_systems = ['decimal', 'roman'] @@ -391,10 +401,10 @@ def _build_and_add_number(pop_last_space=False): def _parse_roman(input_string): tokens = _tokenize(input_string, None) + tokens = [item for item in tokens if item != ''] for token in tokens: - if re.search("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", token.lower()): - if _build_roman(token) != 0: - tokens[tokens.index(token)] = str(_build_roman(token)) + if re.search(ROMAN_REGEX_EXPRESSION, token.lower()): + tokens[tokens.index(token)] = str(_build_roman(token)) final_sentence = ''.join(tokens) return final_sentence @@ -402,14 +412,21 @@ def _parse_roman(input_string): def _build_roman(roman_number): roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} - num_tokens = re.split("^(m{0,3})(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$", roman_number.lower()) + + num_tokens = re.split(ROMAN_REGEX_EXPRESSION, roman_number.lower()) num_tokens = [item for item in num_tokens if item != ''] + built_num = 0 + for num_token in num_tokens: + if re.search('iv|ix|xl|xc|cd|cm', num_token): built_num += roman[num_token[1]] - roman[num_token[0]] - elif re.search('[vld][ixc]{1,3}', num_token): + + elif re.search('[vld][ixc]{1,4}', num_token): built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) + else: built_num += roman[num_token[0]] * len(num_token) + return built_num diff --git a/tests/data/permutations/all_roman_numbers.csv b/tests/data/permutations/all_roman_numbers.csv index ba85178..0a9b436 100644 --- a/tests/data/permutations/all_roman_numbers.csv +++ b/tests/data/permutations/all_roman_numbers.csv @@ -3997,3 +3997,4 @@ number,text 3996,MMMCMXCVI 3997,MMMCMXCVII 3998,MMMCMXCVIII +3999,MMMCMXCIX \ No newline at end of file diff --git a/tests/data/permutations/incorrect_roman_numbers.csv b/tests/data/permutations/incorrect_roman_numbers.csv new file mode 100644 index 0000000..274e805 --- /dev/null +++ b/tests/data/permutations/incorrect_roman_numbers.csv @@ -0,0 +1,7 @@ +number,text +4,IIII +9,VIIII +40,XXXX +90,LXXXX +400,CCCC +900,DCCCC diff --git a/tests/test_numeral_roman.py b/tests/test_numeral_systems.py similarity index 57% rename from tests/test_numeral_roman.py rename to tests/test_numeral_systems.py index 18b1f1e..f24b0dc 100644 --- a/tests/test_numeral_roman.py +++ b/tests/test_numeral_systems.py @@ -1,9 +1,6 @@ import pytest -import csv -import os -from number_parser import parse, parse_number, NUMERAL_SYSTEMS -from tests import PERMUTATION_DIRECTORY +from number_parser import parse, NUMERAL_SYSTEMS all_numeral_systems_but_roman = [system for system in NUMERAL_SYSTEMS if system != 'roman'] all_numeral_systems_but_decimal = [system for system in NUMERAL_SYSTEMS if system != 'decimal'] @@ -16,19 +13,9 @@ ('Built in MMLXXVII.', ['decimal'], 'Built in MMLXXVII.'), ('I was given two IV injections.', all_numeral_systems_but_roman, 'I was given 2 IV injections.'), ('I was given two IV injections.', all_numeral_systems_but_decimal, '1 was given two 4 injections.'), - ('I was given two IV injections.', None, '1 was given 2 4 injections.'), ('I have three apples.', all_numeral_systems_but_roman, 'I have 3 apples.') ] ) def test_parse_roman(test_string, numeral_systems, expected): - assert parse(test_string, numeral_systems=numeral_systems) == expected - -def test_parse_all_roman_numbers(): - with open(os.path.join(PERMUTATION_DIRECTORY, 'all_roman_numbers.csv'), "r") as csv_file: - csv_reader = csv.DictReader(csv_file) - for row in csv_reader: - try: - assert parse_number(row['text']) == int(row['number']) - except AssertionError as e: - raise AssertionError(F"Failed execution of {row['number']},{row['text']}") + assert parse(test_string, numeral_systems=numeral_systems) == expected diff --git a/tests/test_roman.py b/tests/test_roman.py new file mode 100644 index 0000000..d6da1b6 --- /dev/null +++ b/tests/test_roman.py @@ -0,0 +1,28 @@ +import csv +import os + +from number_parser import parse_number, NUMERAL_SYSTEMS +from tests import PERMUTATION_DIRECTORY + +all_numeral_systems_but_roman = [system for system in NUMERAL_SYSTEMS if system != 'roman'] +all_numeral_systems_but_decimal = [system for system in NUMERAL_SYSTEMS if system != 'decimal'] + + +def test_parse_all_roman_numbers(): + with open(os.path.join(PERMUTATION_DIRECTORY, 'all_roman_numbers.csv'), "r") as csv_file: + csv_reader = csv.DictReader(csv_file) + for row in csv_reader: + try: + assert parse_number(row['text']) == int(row['number']) + except AssertionError as e: + raise AssertionError(F"Failed execution of {row['number']},{row['text']}") + + +def test_incorrect_roman_numbers(): + with open(os.path.join(PERMUTATION_DIRECTORY, 'incorrect_roman_numbers.csv'), "r") as csv_file: + csv_reader = csv.DictReader(csv_file) + for row in csv_reader: + try: + assert parse_number(row['text']) == int(row['number']) + except AssertionError as e: + raise AssertionError(F"Failed execution of {row['number']},{row['text']}") From 9704eff97b70d4804a222d27f891d3b6f81b215e Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Thu, 6 May 2021 21:26:09 +0530 Subject: [PATCH 13/17] Removed .lower() from roman regex expressions --- number_parser/parser.py | 45 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/number_parser/parser.py b/number_parser/parser.py index 36e7925..bdb801d 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -5,8 +5,9 @@ SENTENCE_SEPARATORS = [".", ","] SUPPORTED_LANGUAGES = ['en', 'es', 'hi', 'ru'] RE_BUG_LANGUAGES = ['hi'] -NUMERAL_SYSTEMS = ['decimal', 'roman'] -ROMAN_REGEX_EXPRESSION = "^(m{0,3})(cm|cd|d?c{0,4})(xc|xl|l?x{0,4})(ix|iv|v?i{0,4})$" +NUMERAL_SYSTEMS = ('decimal', 'roman') +ROMAN_REGEX_EXPRESSION = "(?i)^(m{0,3})(cm|cd|d?c{0,4})(xc|xl|l?x{0,4})(ix|iv|v?i{0,4})$" + class LanguageData: """Main language class to populate the requisite language-specific variables.""" @@ -243,6 +244,10 @@ def parse_ordinal(input_string, language=None): return parse_number(output_string, language) +def _search_roman(search_string): + return re.search(ROMAN_REGEX_EXPRESSION, search_string, re.IGNORECASE) + + def parse_number(input_string, language=None, numeral_systems=None): """Converts a single number written in natural language to a numeric type""" if not input_string.strip(): @@ -257,8 +262,9 @@ def parse_number(input_string, language=None, numeral_systems=None): if numeral_systems is None: numeral_systems = NUMERAL_SYSTEMS - if re.search(ROMAN_REGEX_EXPRESSION, input_string.lower()): + if _search_roman(input_string): numeral_systems = ['roman'] + else: numeral_systems = ['decimal'] @@ -282,6 +288,9 @@ def parse_number(input_string, language=None, numeral_systems=None): elif numeral_system == 'roman': return int(_parse_roman(input_string)) + else: + raise ValueError(f'"{numeral_system}" is not a supported numeral system') + def parse_fraction(input_string, language=None): """Converts a single number written in fraction to a numeric type""" @@ -321,20 +330,24 @@ def parse(input_string, language=None, numeral_systems=None): complete_sentence = None if numeral_systems is None: - numeral_systems = ['decimal', 'roman'] + numeral_systems = NUMERAL_SYSTEMS if language is None: language = _valid_tokens_by_language(input_string) + temporary_sentence = input_string for numeral_system in numeral_systems: if numeral_system == 'decimal': - complete_sentence = _parse_decimal(input_string, language) - input_string = complete_sentence + complete_sentence = _parse_decimal(temporary_sentence, language) + temporary_sentence = complete_sentence - if numeral_system == 'roman': - complete_sentence = _parse_roman(input_string) - input_string = complete_sentence + elif numeral_system == 'roman': + complete_sentence = _parse_roman(temporary_sentence) + temporary_sentence = complete_sentence + + else: + raise ValueError(f'"{numeral_system}" is not a supported numeral system') return complete_sentence @@ -403,7 +416,7 @@ def _parse_roman(input_string): tokens = _tokenize(input_string, None) tokens = [item for item in tokens if item != ''] for token in tokens: - if re.search(ROMAN_REGEX_EXPRESSION, token.lower()): + if _search_roman(token): tokens[tokens.index(token)] = str(_build_roman(token)) final_sentence = ''.join(tokens) @@ -413,20 +426,20 @@ def _parse_roman(input_string): def _build_roman(roman_number): roman = {'i': 1, 'v': 5, 'x': 10, 'l': 50, 'c': 100, 'd': 500, 'm': 1000} - num_tokens = re.split(ROMAN_REGEX_EXPRESSION, roman_number.lower()) + num_tokens = re.split(ROMAN_REGEX_EXPRESSION, roman_number, re.IGNORECASE) num_tokens = [item for item in num_tokens if item != ''] built_num = 0 for num_token in num_tokens: - if re.search('iv|ix|xl|xc|cd|cm', num_token): - built_num += roman[num_token[1]] - roman[num_token[0]] + if re.search('iv|ix|xl|xc|cd|cm', num_token, re.IGNORECASE): + built_num += roman[num_token[1].lower()] - roman[num_token[0].lower()] - elif re.search('[vld][ixc]{1,4}', num_token): - built_num += roman[num_token[0]] + (roman[num_token[1]] * (len(num_token) - 1)) + elif re.search('[XLVD][IXC]{1,4}', num_token, re.IGNORECASE): + built_num += roman[num_token[0].lower()] + (roman[num_token[1].lower()] * (len(num_token) - 1)) else: - built_num += roman[num_token[0]] * len(num_token) + built_num += roman[num_token[0].lower()] * len(num_token) return built_num From 554d5537768b37d1c65a57f061d3e86384071255 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Thu, 6 May 2021 21:28:31 +0530 Subject: [PATCH 14/17] Update test_numeral_systems.py --- tests/test_numeral_systems.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_numeral_systems.py b/tests/test_numeral_systems.py index f24b0dc..5a4570a 100644 --- a/tests/test_numeral_systems.py +++ b/tests/test_numeral_systems.py @@ -10,10 +10,10 @@ "test_string, numeral_systems, expected", [ ('Built in MMLXXVII.', None, 'Built in 2077.'), - ('Built in MMLXXVII.', ['decimal'], 'Built in MMLXXVII.'), + ('Built in MMLXXVII.', ['roman'], 'Built in 2077.'), ('I was given two IV injections.', all_numeral_systems_but_roman, 'I was given 2 IV injections.'), ('I was given two IV injections.', all_numeral_systems_but_decimal, '1 was given two 4 injections.'), - ('I have three apples.', all_numeral_systems_but_roman, 'I have 3 apples.') + ('I have three apples.', None, '1 have 3 apples.') ] ) def test_parse_roman(test_string, numeral_systems, expected): From fb9bff70d89c368eb556d6fa5d223d617933eee2 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Sat, 8 May 2021 17:11:15 +0530 Subject: [PATCH 15/17] Update number_parser/parser.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adrián Chaves --- number_parser/parser.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/number_parser/parser.py b/number_parser/parser.py index bdb801d..66f194d 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -335,21 +335,19 @@ def parse(input_string, language=None, numeral_systems=None): if language is None: language = _valid_tokens_by_language(input_string) - temporary_sentence = input_string + result = input_string for numeral_system in numeral_systems: if numeral_system == 'decimal': - complete_sentence = _parse_decimal(temporary_sentence, language) - temporary_sentence = complete_sentence + result = _parse_decimal(result, language) elif numeral_system == 'roman': - complete_sentence = _parse_roman(temporary_sentence) - temporary_sentence = complete_sentence + result = _parse_roman(result) else: raise ValueError(f'"{numeral_system}" is not a supported numeral system') - return complete_sentence + return result def _parse_decimal(input_string, language): From 480cf7c8d2c2284e72fb0e8ec177382318570b37 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Sat, 8 May 2021 17:11:24 +0530 Subject: [PATCH 16/17] Update number_parser/parser.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adrián Chaves --- number_parser/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/number_parser/parser.py b/number_parser/parser.py index 66f194d..e16d44f 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -289,7 +289,7 @@ def parse_number(input_string, language=None, numeral_systems=None): return int(_parse_roman(input_string)) else: - raise ValueError(f'"{numeral_system}" is not a supported numeral system') + raise ValueError(f'{numeral_system!r} is not a supported numeral system') def parse_fraction(input_string, language=None): From cbc4661c6712b54d8ad215d2faa5dc485383d469 Mon Sep 17 00:00:00 2001 From: AmPhIbIaN26 <43638430+AmPhIbIaN26@users.noreply.github.com> Date: Wed, 12 May 2021 16:21:37 +0530 Subject: [PATCH 17/17] Added more test cases for better code coverage --- number_parser/parser.py | 19 ++++++++----------- tests/test_numeral_systems.py | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/number_parser/parser.py b/number_parser/parser.py index e16d44f..f463f14 100644 --- a/number_parser/parser.py +++ b/number_parser/parser.py @@ -244,7 +244,7 @@ def parse_ordinal(input_string, language=None): return parse_number(output_string, language) -def _search_roman(search_string): +def _is_roman(search_string): return re.search(ROMAN_REGEX_EXPRESSION, search_string, re.IGNORECASE) @@ -260,13 +260,11 @@ def parse_number(input_string, language=None, numeral_systems=None): language = _valid_tokens_by_language(input_string) if numeral_systems is None: - numeral_systems = NUMERAL_SYSTEMS - - if _search_roman(input_string): - numeral_systems = ['roman'] + if _is_roman(input_string): + numeral_systems = ['roman'] - else: - numeral_systems = ['decimal'] + elif language in SUPPORTED_LANGUAGES and not _is_roman(input_string): + numeral_systems = ['decimal'] for numeral_system in numeral_systems: if numeral_system == 'decimal': @@ -327,8 +325,6 @@ def parse(input_string, language=None, numeral_systems=None): Converts all the numbers in a sentence written in natural language to their numeric type while keeping the other words unchanged. Returns the transformed string. """ - complete_sentence = None - if numeral_systems is None: numeral_systems = NUMERAL_SYSTEMS @@ -414,7 +410,7 @@ def _parse_roman(input_string): tokens = _tokenize(input_string, None) tokens = [item for item in tokens if item != ''] for token in tokens: - if _search_roman(token): + if _is_roman(token): tokens[tokens.index(token)] = str(_build_roman(token)) final_sentence = ''.join(tokens) @@ -437,7 +433,8 @@ def _build_roman(roman_number): elif re.search('[XLVD][IXC]{1,4}', num_token, re.IGNORECASE): built_num += roman[num_token[0].lower()] + (roman[num_token[1].lower()] * (len(num_token) - 1)) - else: + elif re.search('[ixcm]{1,4}|[vld]{1}', num_token, re.IGNORECASE): built_num += roman[num_token[0].lower()] * len(num_token) return built_num + diff --git a/tests/test_numeral_systems.py b/tests/test_numeral_systems.py index 5a4570a..9a6799e 100644 --- a/tests/test_numeral_systems.py +++ b/tests/test_numeral_systems.py @@ -1,6 +1,6 @@ import pytest -from number_parser import parse, NUMERAL_SYSTEMS +from number_parser import parse, parse_number, NUMERAL_SYSTEMS all_numeral_systems_but_roman = [system for system in NUMERAL_SYSTEMS if system != 'roman'] all_numeral_systems_but_decimal = [system for system in NUMERAL_SYSTEMS if system != 'decimal'] @@ -16,6 +16,15 @@ ('I have three apples.', None, '1 have 3 apples.') ] ) -def test_parse_roman(test_string, numeral_systems, expected): - +def test_supported_numerals(test_string, numeral_systems, expected): assert parse(test_string, numeral_systems=numeral_systems) == expected + + +def test_parse_unsupported_numerals(): + with pytest.raises(ValueError): + parse('I have six apples.', numeral_systems=['suzhou']) + + +def test_parse_number_unsupported_numerals(): + with pytest.raises(ValueError): + parse_number('Two thousand', numeral_systems=['suzhou'])