aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/docutils/utils/roman.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/docutils/utils/roman.py')
-rw-r--r--.venv/lib/python3.12/site-packages/docutils/utils/roman.py154
1 files changed, 154 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/docutils/utils/roman.py b/.venv/lib/python3.12/site-packages/docutils/utils/roman.py
new file mode 100644
index 00000000..df0c5b33
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docutils/utils/roman.py
@@ -0,0 +1,154 @@
+##############################################################################
+#
+# Copyright (c) 2001 Mark Pilgrim and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Convert to and from Roman numerals"""
+
+__author__ = "Mark Pilgrim (f8dy@diveintopython.org)"
+__version__ = "1.4"
+__date__ = "8 August 2001"
+__copyright__ = """Copyright (c) 2001 Mark Pilgrim
+
+This program is part of "Dive Into Python", a free Python tutorial for
+experienced programmers. Visit http://diveintopython.org/ for the
+latest version.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the Python 2.1.1 license, available at
+http://www.python.org/2.1.1/license.html
+"""
+
+import argparse
+import re
+import sys
+
+
+# Define exceptions
+class RomanError(Exception):
+ pass
+
+
+class OutOfRangeError(RomanError):
+ pass
+
+
+class NotIntegerError(RomanError):
+ pass
+
+
+class InvalidRomanNumeralError(RomanError):
+ pass
+
+
+# Define digit mapping
+romanNumeralMap = (('M', 1000),
+ ('CM', 900),
+ ('D', 500),
+ ('CD', 400),
+ ('C', 100),
+ ('XC', 90),
+ ('L', 50),
+ ('XL', 40),
+ ('X', 10),
+ ('IX', 9),
+ ('V', 5),
+ ('IV', 4),
+ ('I', 1))
+
+
+def toRoman(n):
+ """convert integer to Roman numeral"""
+ if not isinstance(n, int):
+ raise NotIntegerError("decimals can not be converted")
+ if not (-1 < n < 5000):
+ raise OutOfRangeError("number out of range (must be 0..4999)")
+
+ # special case
+ if n == 0:
+ return 'N'
+
+ result = ""
+ for numeral, integer in romanNumeralMap:
+ while n >= integer:
+ result += numeral
+ n -= integer
+ return result
+
+
+# Define pattern to detect valid Roman numerals
+romanNumeralPattern = re.compile("""
+ ^ # beginning of string
+ M{0,4} # thousands - 0 to 4 M's
+ (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
+ # or 500-800 (D, followed by 0 to 3 C's)
+ (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
+ # or 50-80 (L, followed by 0 to 3 X's)
+ (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
+ # or 5-8 (V, followed by 0 to 3 I's)
+ $ # end of string
+ """, re.VERBOSE)
+
+
+def fromRoman(s):
+ """convert Roman numeral to integer"""
+ if not s:
+ raise InvalidRomanNumeralError('Input can not be blank')
+
+ # special case
+ if s == 'N':
+ return 0
+
+ if not romanNumeralPattern.search(s):
+ raise InvalidRomanNumeralError('Invalid Roman numeral: %s' % s)
+
+ result = 0
+ index = 0
+ for numeral, integer in romanNumeralMap:
+ while s[index:index + len(numeral)] == numeral:
+ result += integer
+ index += len(numeral)
+ return result
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(
+ prog='roman',
+ description='convert between roman and arabic numerals'
+ )
+ parser.add_argument('number', help='the value to convert')
+ parser.add_argument(
+ '-r', '--reverse',
+ action='store_true',
+ default=False,
+ help='convert roman to numeral (case insensitive) [default: False]')
+
+ args = parser.parse_args()
+ args.number = args.number
+ return args
+
+
+def main():
+ args = parse_args()
+ if args.reverse:
+ u = args.number.upper()
+ r = fromRoman(u)
+ print(r)
+ else:
+ i = int(args.number)
+ n = toRoman(i)
+ print(n)
+
+ return 0
+
+
+if __name__ == "__main__": # pragma: no cover
+ sys.exit(main()) # pragma: no cover