numly 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
numly-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: numly
3
+ Version: 0.1.0
4
+ Summary: Convert numbers across numeral systems — Roman, Chinese, Greek, Egyptian, Arabic-Indic and more.
5
+ Home-page: https://github.com/TheMadrasTechie/numly
6
+ Author: TheMadrasTechie
7
+ Author-email: you@example.com
8
+ Keywords: numerals,roman,chinese,greek,egyptian,arabic-indic,number,converter
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: keywords
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ <p align="center">
25
+ <img src="numly_rounded_square.svg" alt="numly logo" width="120" height="120"/>
26
+ </p>
27
+
28
+ <h1 align="center">numly</h1>
29
+
30
+ <p align="center">
31
+ <img src="https://img.shields.io/pypi/v/numly" alt="PyPI version"/>
32
+ <img src="https://img.shields.io/pypi/pyversions/numly" alt="Python versions"/>
33
+ <img src="https://img.shields.io/github/license/yourusername/numly" alt="License"/>
34
+ </p>
35
+
36
+ <p align="center">
37
+ A Python library to work with numbers across formats — decimal, roman, words, binary, hex, and more.
38
+ </p>
39
+
40
+ ---
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install numly
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ import numly
54
+
55
+ # Convert to Roman numerals
56
+ numly.to_roman(2024) # → 'MMXXIV'
57
+
58
+ # Convert to words
59
+ numly.to_words(42) # → 'forty-two'
60
+
61
+ # Convert to binary
62
+ numly.to_binary(255) # → '11111111'
63
+
64
+ # Convert to hex
65
+ numly.to_hex(255) # → 'FF'
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Features
71
+
72
+ - 🔢 Decimal → Roman numerals
73
+ - 🔤 Decimal → Words (English)
74
+ - 💻 Decimal → Binary / Octal / Hex
75
+ - 🔁 Reverse conversions (Roman → Decimal, etc.)
76
+ - 🌍 Locale-aware number formatting *(coming soon)*
77
+
78
+ ---
79
+
80
+ ## API Reference
81
+
82
+ | Function | Input | Output |
83
+ |---|---|---|
84
+ | `to_roman(n)` | `int` | `str` — e.g. `'XIV'` |
85
+ | `from_roman(s)` | `str` | `int` — e.g. `14` |
86
+ | `to_words(n)` | `int` | `str` — e.g. `'forty-two'` |
87
+ | `to_binary(n)` | `int` | `str` — e.g. `'1110'` |
88
+ | `to_octal(n)` | `int` | `str` — e.g. `'16'` |
89
+ | `to_hex(n)` | `int` | `str` — e.g. `'E'` |
90
+
91
+ ---
92
+
93
+ ## Contributing
94
+
95
+ Contributions are welcome! Feel free to open an issue or submit a pull request.
96
+
97
+ ```bash
98
+ git clone https://github.com/yourusername/numly.git
99
+ cd numly
100
+ pip install -e .
101
+ ```
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ MIT License © 2026 TheMadrasTechie
numly-0.1.0/README.md ADDED
@@ -0,0 +1,84 @@
1
+ <p align="center">
2
+ <img src="numly_rounded_square.svg" alt="numly logo" width="120" height="120"/>
3
+ </p>
4
+
5
+ <h1 align="center">numly</h1>
6
+
7
+ <p align="center">
8
+ <img src="https://img.shields.io/pypi/v/numly" alt="PyPI version"/>
9
+ <img src="https://img.shields.io/pypi/pyversions/numly" alt="Python versions"/>
10
+ <img src="https://img.shields.io/github/license/yourusername/numly" alt="License"/>
11
+ </p>
12
+
13
+ <p align="center">
14
+ A Python library to work with numbers across formats — decimal, roman, words, binary, hex, and more.
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install numly
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Quick Start
28
+
29
+ ```python
30
+ import numly
31
+
32
+ # Convert to Roman numerals
33
+ numly.to_roman(2024) # → 'MMXXIV'
34
+
35
+ # Convert to words
36
+ numly.to_words(42) # → 'forty-two'
37
+
38
+ # Convert to binary
39
+ numly.to_binary(255) # → '11111111'
40
+
41
+ # Convert to hex
42
+ numly.to_hex(255) # → 'FF'
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Features
48
+
49
+ - 🔢 Decimal → Roman numerals
50
+ - 🔤 Decimal → Words (English)
51
+ - 💻 Decimal → Binary / Octal / Hex
52
+ - 🔁 Reverse conversions (Roman → Decimal, etc.)
53
+ - 🌍 Locale-aware number formatting *(coming soon)*
54
+
55
+ ---
56
+
57
+ ## API Reference
58
+
59
+ | Function | Input | Output |
60
+ |---|---|---|
61
+ | `to_roman(n)` | `int` | `str` — e.g. `'XIV'` |
62
+ | `from_roman(s)` | `str` | `int` — e.g. `14` |
63
+ | `to_words(n)` | `int` | `str` — e.g. `'forty-two'` |
64
+ | `to_binary(n)` | `int` | `str` — e.g. `'1110'` |
65
+ | `to_octal(n)` | `int` | `str` — e.g. `'16'` |
66
+ | `to_hex(n)` | `int` | `str` — e.g. `'E'` |
67
+
68
+ ---
69
+
70
+ ## Contributing
71
+
72
+ Contributions are welcome! Feel free to open an issue or submit a pull request.
73
+
74
+ ```bash
75
+ git clone https://github.com/yourusername/numly.git
76
+ cd numly
77
+ pip install -e .
78
+ ```
79
+
80
+ ---
81
+
82
+ ## License
83
+
84
+ MIT License © 2026 TheMadrasTechie
@@ -0,0 +1,64 @@
1
+ """
2
+ numly
3
+ ~~~~~
4
+ A Python library for working with numbers across numeral systems.
5
+
6
+ Supported systems
7
+ -----------------
8
+ decimal : Standard base-10
9
+ roman : Roman numerals (I V X L C D M)
10
+ arabic_indic : Eastern Arabic digits (٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩)
11
+ chinese : Traditional Chinese (零 一 二 三 四 … 万)
12
+ greek : Greek alphabetic (Α Β Γ … Ω + ͵ for thousands)
13
+ egyptian : Egyptian hieroglyphs (𓏺 𓎆 𓍢 𓆼 𓂭 𓆐 𓁨)
14
+
15
+ Quick start
16
+ -----------
17
+ import numly
18
+
19
+ numly.to_roman(2024) # 'MMXXIV'
20
+ numly.to_chinese(42) # '四十二'
21
+ numly.convert("MMXXIV", "roman", "greek") # '͵ΒΚΔʹ'
22
+ numly.to_all(42) # dict of every system
23
+
24
+ Individual modules
25
+ ------------------
26
+ from numly.roman import to_roman, from_roman
27
+ from numly.arabic_indic import to_arabic_indic, from_arabic_indic
28
+ from numly.chinese import to_chinese, from_chinese
29
+ from numly.greek import to_greek, from_greek
30
+ from numly.egyptian import to_egyptian, from_egyptian, symbol_breakdown
31
+ from numly.convert import convert, to_all, SYSTEMS
32
+ """
33
+
34
+ __version__ = "0.1.0"
35
+ __author__ = "numly contributors"
36
+ __license__ = "MIT"
37
+
38
+ # ── individual converters ──────────────────────────────────────────────────
39
+ from numly.roman import to_roman, from_roman, is_valid as is_valid_roman
40
+ from numly.arabic_indic import to_arabic_indic, from_arabic_indic, is_valid as is_valid_arabic_indic
41
+ from numly.chinese import to_chinese, from_chinese, is_valid as is_valid_chinese
42
+ from numly.greek import to_greek, from_greek, is_valid as is_valid_greek
43
+ from numly.egyptian import to_egyptian, from_egyptian, is_valid as is_valid_egyptian
44
+ from numly.egyptian import symbol_breakdown
45
+
46
+ # ── universal converter ────────────────────────────────────────────────────
47
+ from numly.convert import convert, to_all, SYSTEMS, supported_systems
48
+
49
+ __all__ = [
50
+ # roman
51
+ "to_roman", "from_roman", "is_valid_roman",
52
+ # arabic-indic
53
+ "to_arabic_indic", "from_arabic_indic", "is_valid_arabic_indic",
54
+ # chinese
55
+ "to_chinese", "from_chinese", "is_valid_chinese",
56
+ # greek
57
+ "to_greek", "from_greek", "is_valid_greek",
58
+ # egyptian
59
+ "to_egyptian", "from_egyptian", "is_valid_egyptian", "symbol_breakdown",
60
+ # universal
61
+ "convert", "to_all", "SYSTEMS", "supported_systems",
62
+ # meta
63
+ "__version__",
64
+ ]
@@ -0,0 +1,113 @@
1
+ """
2
+ numly.arabic_indic
3
+ ~~~~~~~~~~~~~~~~~~
4
+ Convert integers to Eastern Arabic numerals (٠١٢٣٤٥٦٧٨٩) and back.
5
+
6
+ These are the digits used in Arabic, Persian, and Urdu scripts.
7
+ Unlike Roman or Chinese, this is a pure digit-substitution system —
8
+ the positional value works exactly like decimal.
9
+
10
+ Range: 0 – unlimited
11
+
12
+ Examples:
13
+ >>> from numly.arabic_indic import to_arabic_indic, from_arabic_indic
14
+ >>> to_arabic_indic(2024)
15
+ '٢٠٢٤'
16
+ >>> from_arabic_indic('٤٢')
17
+ 42
18
+ """
19
+
20
+ # Western: 0 1 2 3 4 5 6 7 8 9
21
+ # Eastern: ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩
22
+ _DIGITS = "٠١٢٣٤٥٦٧٨٩"
23
+ _TO_ASCII = {ch: str(i) for i, ch in enumerate(_DIGITS)}
24
+
25
+ SYSTEM = "arabic_indic"
26
+ MIN = 0
27
+ MAX = None # unlimited
28
+
29
+
30
+ def to_arabic_indic(num: int) -> str:
31
+ """
32
+ Convert an integer to an Eastern Arabic numeral string.
33
+
34
+ Args:
35
+ num: Non-negative integer (no upper limit).
36
+
37
+ Returns:
38
+ Eastern Arabic numeral string, e.g. '٤٢'.
39
+
40
+ Raises:
41
+ TypeError: If num is not an int.
42
+ ValueError: If num is negative.
43
+
44
+ Examples:
45
+ >>> to_arabic_indic(0)
46
+ '٠'
47
+ >>> to_arabic_indic(42)
48
+ '٤٢'
49
+ >>> to_arabic_indic(2024)
50
+ '٢٠٢٤'
51
+ """
52
+ if not isinstance(num, int):
53
+ raise TypeError(f"Expected int, got {type(num).__name__!r}")
54
+ if num < 0:
55
+ raise ValueError("Only non-negative integers are supported")
56
+ return "".join(_DIGITS[int(d)] for d in str(num))
57
+
58
+
59
+ def from_arabic_indic(s: str) -> int:
60
+ """
61
+ Convert an Eastern Arabic numeral string to an integer.
62
+
63
+ Args:
64
+ s: Eastern Arabic numeral string, e.g. '٤٢'.
65
+ Also accepts plain ASCII digits or a mix of both.
66
+
67
+ Returns:
68
+ Integer value.
69
+
70
+ Raises:
71
+ TypeError: If s is not a str.
72
+ ValueError: If s contains non-numeric characters.
73
+
74
+ Examples:
75
+ >>> from_arabic_indic('٤٢')
76
+ 42
77
+ >>> from_arabic_indic('٢٠٢٤')
78
+ 2024
79
+ """
80
+ if not isinstance(s, str):
81
+ raise TypeError(f"Expected str, got {type(s).__name__!r}")
82
+ s = s.strip()
83
+ if not s:
84
+ raise ValueError("Empty string")
85
+
86
+ ascii_s = "".join(_TO_ASCII.get(ch, ch) for ch in s)
87
+ try:
88
+ return int(ascii_s)
89
+ except ValueError:
90
+ raise ValueError(f"Invalid Arabic-Indic numeral: {s!r}")
91
+
92
+
93
+ def is_valid(s: str) -> bool:
94
+ """
95
+ Check whether a string is a valid Eastern Arabic numeral.
96
+
97
+ Examples:
98
+ >>> is_valid('٤٢')
99
+ True
100
+ >>> is_valid('abc')
101
+ False
102
+ """
103
+ try:
104
+ from_arabic_indic(s)
105
+ return True
106
+ except (TypeError, ValueError):
107
+ return False
108
+
109
+
110
+ if __name__ == "__main__":
111
+ for n in [0, 1, 42, 100, 2024, 999_999]:
112
+ r = to_arabic_indic(n)
113
+ print(f"{n:<10} → {r:<12} → {from_arabic_indic(r)}")
@@ -0,0 +1,187 @@
1
+ """
2
+ numly.chinese
3
+ ~~~~~~~~~~~~~
4
+ Convert integers to Traditional Chinese numerals and back.
5
+
6
+ Uses the standard literary/financial positional system:
7
+ 零一二三四五六七八九 (digits)
8
+ 十 百 千 万 (place units)
9
+
10
+ Range: 0 – 99,999,999
11
+
12
+ Examples:
13
+ >>> from numly.chinese import to_chinese, from_chinese
14
+ >>> to_chinese(42)
15
+ '四十二'
16
+ >>> from_chinese('一万零一')
17
+ 10001
18
+ """
19
+
20
+ _DIGITS = "零一二三四五六七八九"
21
+ _UNIT_MAP = {"十": 10, "百": 100, "千": 1000, "万": 10_000}
22
+
23
+ SYSTEM = "chinese"
24
+ MIN, MAX = 0, 99_999_999
25
+
26
+
27
+ # ── internal helper ────────────────────────────────────────────────────────
28
+
29
+ def _encode_section(n: int) -> str:
30
+ """
31
+ Encode an integer 1–9999 to Chinese without any outer 万 context.
32
+ Handles internal zeros (e.g. 101 → 一百零一).
33
+ """
34
+ units = [(1000, "千"), (100, "百"), (10, "十"), (1, "")]
35
+ result = ""
36
+ zero_pending = False
37
+
38
+ for val, unit in units:
39
+ d = n // val
40
+ n %= val
41
+ if d:
42
+ if zero_pending:
43
+ result += "零"
44
+ zero_pending = False
45
+ result += _DIGITS[d] + unit
46
+ elif result: # non-leading zero in the middle
47
+ zero_pending = True
48
+
49
+ return result
50
+
51
+
52
+ def _decode_section(s: str) -> int:
53
+ """
54
+ Decode a Chinese string that contains no 万 character (up to 9999).
55
+ Handles leading 零 and bare 十 (e.g. '十二' → 12).
56
+ """
57
+ cn_val = {ch: i for i, ch in enumerate(_DIGITS)}
58
+ result, digit = 0, 0
59
+
60
+ for ch in s:
61
+ if ch in cn_val:
62
+ digit = cn_val[ch]
63
+ elif ch in _UNIT_MAP and ch != "万":
64
+ unit = _UNIT_MAP[ch]
65
+ if digit == 0:
66
+ digit = 1 # bare 十 → treat as 一十
67
+ result += digit * unit
68
+ digit = 0
69
+
70
+ result += digit # final ones digit (may be 0 for 零)
71
+ return result
72
+
73
+
74
+ # ── public API ─────────────────────────────────────────────────────────────
75
+
76
+ def to_chinese(num: int) -> str:
77
+ """
78
+ Convert an integer to a Traditional Chinese numeral string.
79
+
80
+ Args:
81
+ num: Integer between 0 and 99,999,999.
82
+
83
+ Returns:
84
+ Chinese numeral string, e.g. '四十二'.
85
+
86
+ Raises:
87
+ TypeError: If num is not an int.
88
+ ValueError: If num is outside 0–99,999,999.
89
+
90
+ Examples:
91
+ >>> to_chinese(0)
92
+ '零'
93
+ >>> to_chinese(42)
94
+ '四十二'
95
+ >>> to_chinese(10001)
96
+ '一万零一'
97
+ >>> to_chinese(20240)
98
+ '二万零二百四十'
99
+ >>> to_chinese(99999999)
100
+ '九千九百九十九万九千九百九十九'
101
+ """
102
+ if not isinstance(num, int):
103
+ raise TypeError(f"Expected int, got {type(num).__name__!r}")
104
+ if num == 0:
105
+ return "零"
106
+ if not MIN <= num <= MAX:
107
+ raise ValueError(f"Chinese numerals support {MIN}–{MAX:,}, got {num}")
108
+
109
+ wan = num // 10_000
110
+ rest = num % 10_000
111
+
112
+ if wan == 0:
113
+ return _encode_section(rest)
114
+
115
+ result = _encode_section(wan) + "万"
116
+
117
+ if rest == 0:
118
+ return result
119
+ # bridge 零 when rest has a missing thousands digit
120
+ if rest < 1_000 or rest // 100 == 0:
121
+ return result + "零" + _encode_section(rest)
122
+ return result + _encode_section(rest)
123
+
124
+
125
+ def from_chinese(s: str) -> int:
126
+ """
127
+ Convert a Traditional Chinese numeral string to an integer.
128
+
129
+ Args:
130
+ s: Chinese numeral string, e.g. '四十二'.
131
+
132
+ Returns:
133
+ Integer value.
134
+
135
+ Raises:
136
+ TypeError: If s is not a str.
137
+ ValueError: If s is empty or malformed.
138
+
139
+ Examples:
140
+ >>> from_chinese('零')
141
+ 0
142
+ >>> from_chinese('四十二')
143
+ 42
144
+ >>> from_chinese('一万零一')
145
+ 10001
146
+ >>> from_chinese('九千九百九十九万九千九百九十九')
147
+ 99999999
148
+ """
149
+ if not isinstance(s, str):
150
+ raise TypeError(f"Expected str, got {type(s).__name__!r}")
151
+ s = s.strip()
152
+ if not s:
153
+ raise ValueError("Empty string")
154
+ if s == "零":
155
+ return 0
156
+
157
+ if "万" in s:
158
+ idx = s.index("万")
159
+ left = s[:idx]
160
+ right = s[idx + 1:] # may start with bridge 零
161
+ return _decode_section(left) * 10_000 + _decode_section(right)
162
+
163
+ return _decode_section(s)
164
+
165
+
166
+ def is_valid(s: str) -> bool:
167
+ """
168
+ Check whether a string is a valid Chinese numeral.
169
+
170
+ Examples:
171
+ >>> is_valid('四十二')
172
+ True
173
+ >>> is_valid('hello')
174
+ False
175
+ """
176
+ try:
177
+ val = from_chinese(s)
178
+ return to_chinese(val) == s
179
+ except (TypeError, ValueError):
180
+ return False
181
+
182
+
183
+ if __name__ == "__main__":
184
+ tests = [0, 1, 10, 42, 100, 101, 1001, 10001, 20240, 99_999_999]
185
+ for n in tests:
186
+ r = to_chinese(n)
187
+ print(f"{n:<12} → {r:<28} → {from_chinese(r)}")
@@ -0,0 +1,187 @@
1
+ """
2
+ numly.convert
3
+ ~~~~~~~~~~~~~
4
+ Universal converter — convert any number between all supported systems.
5
+
6
+ Supported systems
7
+ -----------------
8
+ 'decimal' Standard base-10 range: 0 – unlimited
9
+ 'roman' Roman numerals range: 1 – 3,999
10
+ 'arabic_indic' Eastern Arabic digits range: 0 – unlimited
11
+ 'chinese' Chinese numerals range: 0 – 99,999,999
12
+ 'greek' Greek alphabetic range: 1 – 9,999
13
+ 'egyptian' Egyptian hieroglyphs range: 1 – 9,999,999
14
+
15
+ Examples:
16
+ >>> from numly.convert import convert, to_all
17
+ >>> convert(42, "decimal", "roman")
18
+ 'XLII'
19
+ >>> convert("MMXXIV", "roman", "chinese")
20
+ '二千零二十四'
21
+ >>> convert("四十二", "chinese", "greek")
22
+ 'ΜΒʹ'
23
+ >>> convert("𓎆𓎆𓎆𓎆𓏺𓏺", "egyptian", "roman")
24
+ 'XLII'
25
+ >>> to_all(42)
26
+ {'decimal': 42, 'roman': 'XLII', 'arabic_indic': '٤٢', ...}
27
+ """
28
+
29
+ from numly.roman import to_roman, from_roman
30
+ from numly.arabic_indic import to_arabic_indic, from_arabic_indic
31
+ from numly.chinese import to_chinese, from_chinese
32
+ from numly.greek import to_greek, from_greek
33
+ from numly.egyptian import to_egyptian, from_egyptian
34
+
35
+ # ── registry ───────────────────────────────────────────────────────────────
36
+
37
+ _ENCODERS: dict = {
38
+ "roman": to_roman,
39
+ "arabic_indic": to_arabic_indic,
40
+ "chinese": to_chinese,
41
+ "greek": to_greek,
42
+ "egyptian": to_egyptian,
43
+ }
44
+
45
+ _DECODERS: dict = {
46
+ "decimal": int,
47
+ "roman": from_roman,
48
+ "arabic_indic": from_arabic_indic,
49
+ "chinese": from_chinese,
50
+ "greek": from_greek,
51
+ "egyptian": from_egyptian,
52
+ }
53
+
54
+ #: Frozenset of all supported system names (including 'decimal').
55
+ SYSTEMS: frozenset = frozenset(_DECODERS.keys())
56
+
57
+
58
+ # ── public API ─────────────────────────────────────────────────────────────
59
+
60
+ def convert(value, from_system: str, to_system: str):
61
+ """
62
+ Convert a number from one numeral system to another.
63
+
64
+ Args:
65
+ value: The number to convert.
66
+ Pass an ``int`` when from_system is 'decimal',
67
+ otherwise pass a ``str``.
68
+ from_system: Source system name (see ``SYSTEMS``).
69
+ to_system: Target system name (see ``SYSTEMS``).
70
+ Pass ``'all'`` to receive a dict of every system.
71
+
72
+ Returns:
73
+ str — converted numeral string.
74
+ int — when to_system is 'decimal'.
75
+ dict — when to_system is 'all'.
76
+
77
+ Raises:
78
+ ValueError: If from_system or to_system is unrecognised.
79
+ TypeError / ValueError: Propagated from the individual modules
80
+ if the value is malformed or out of range.
81
+
82
+ Examples:
83
+ >>> convert(42, "decimal", "roman")
84
+ 'XLII'
85
+ >>> convert("XLII", "roman", "decimal")
86
+ 42
87
+ >>> convert("XLII", "roman", "chinese")
88
+ '四十二'
89
+ >>> convert("四十二", "chinese", "egyptian")
90
+ '𓎆𓎆𓎆𓎆𓏺𓏺'
91
+ >>> convert("𓎆𓎆𓎆𓎆𓏺𓏺", "egyptian", "greek")
92
+ 'ΜΒʹ'
93
+ >>> convert(42, "decimal", "all")
94
+ {'decimal': 42, 'roman': 'XLII', ...}
95
+ """
96
+ from_system = from_system.lower().strip()
97
+ if to_system != "all":
98
+ to_system = to_system.lower().strip()
99
+
100
+ if from_system not in _DECODERS:
101
+ raise ValueError(
102
+ f"Unknown source system {from_system!r}. "
103
+ f"Choose from: {sorted(SYSTEMS)}"
104
+ )
105
+
106
+ # Step 1 — decode to a plain Python int
107
+ n = _DECODERS[from_system](value)
108
+
109
+ # Step 2 — encode to target
110
+ if to_system == "all":
111
+ return to_all(n)
112
+ if to_system == "decimal":
113
+ return n
114
+ if to_system not in _ENCODERS:
115
+ raise ValueError(
116
+ f"Unknown target system {to_system!r}. "
117
+ f"Choose from: {sorted(SYSTEMS)}"
118
+ )
119
+ return _ENCODERS[to_system](n)
120
+
121
+
122
+ def to_all(value, from_system: str = "decimal") -> dict:
123
+ """
124
+ Convert a number to every supported numeral system at once.
125
+
126
+ Systems whose range does not include the value are returned as ``None``.
127
+
128
+ Args:
129
+ value: The number to convert.
130
+ from_system: Source system (default ``'decimal'``).
131
+
132
+ Returns:
133
+ dict with keys: 'decimal', 'roman', 'arabic_indic',
134
+ 'chinese', 'greek', 'egyptian'.
135
+
136
+ Examples:
137
+ >>> to_all(42)
138
+ {'decimal': 42, 'roman': 'XLII', 'arabic_indic': '٤٢',
139
+ 'chinese': '四十二', 'greek': 'ΜΒʹ', 'egyptian': '𓎆𓎆𓎆𓎆𓏺𓏺'}
140
+
141
+ >>> to_all(5000)['greek'] # out of range → None
142
+ None
143
+ """
144
+ from_system = from_system.lower().strip()
145
+ n = _DECODERS[from_system](value)
146
+
147
+ out: dict = {"decimal": n}
148
+ for name, encoder in _ENCODERS.items():
149
+ try:
150
+ out[name] = encoder(n)
151
+ except (TypeError, ValueError):
152
+ out[name] = None # value out of range for that system
153
+ return out
154
+
155
+
156
+ def supported_systems() -> list[str]:
157
+ """Return a sorted list of all supported system names."""
158
+ return sorted(SYSTEMS)
159
+
160
+
161
+ if __name__ == "__main__":
162
+ samples = [1, 42, 100, 2024, 3999]
163
+
164
+ print(f"{'n':<8} {'roman':<12} {'arabic_indic':<14} {'chinese':<22} {'greek':<10} {'egyptian'}")
165
+ print("─" * 90)
166
+ for n in samples:
167
+ r = to_all(n)
168
+ print(
169
+ f"{r['decimal']:<8}"
170
+ f"{str(r['roman']):<12}"
171
+ f"{str(r['arabic_indic']):<14}"
172
+ f"{str(r['chinese']):<22}"
173
+ f"{str(r['greek']):<10}"
174
+ f"{r['egyptian']}"
175
+ )
176
+
177
+ print()
178
+ cross = [
179
+ ("MMXXIV", "roman", "chinese"),
180
+ ("四十二", "chinese", "egyptian"),
181
+ ("ΜΒʹ", "greek", "roman"),
182
+ ("𓎆𓎆𓎆𓎆𓏺𓏺", "egyptian", "arabic_indic"),
183
+ ("٤٢", "arabic_indic", "greek"),
184
+ ]
185
+ print("Cross-system conversions:")
186
+ for val, frm, to in cross:
187
+ print(f" {val!r:<28} ({frm:<13}) → {to:<13} : {convert(val, frm, to)}")
@@ -0,0 +1,163 @@
1
+ """
2
+ numly.egyptian
3
+ ~~~~~~~~~~~~~~
4
+ Convert integers to Egyptian hieroglyphic numerals and back.
5
+
6
+ Pure additive system — no subtraction (unlike Roman).
7
+ Symbols are written largest to smallest, left to right.
8
+
9
+ 𓁨 = 1,000,000 (Heh — god with arms raised)
10
+ 𓆐 = 100,000 (tadpole / frog)
11
+ 𓂭 = 10,000 (bent finger)
12
+ 𓆼 = 1,000 (lotus flower)
13
+ 𓍢 = 100 (coil of rope)
14
+ 𓎆 = 10 (heel bone / hobble)
15
+ 𓏺 = 1 (vertical stroke / tally)
16
+
17
+ Range: 1 – 9,999,999
18
+
19
+ Note: Rendering requires a Unicode font that supports the
20
+ Egyptian Hieroglyphs block (U+13000–U+1342F), such as
21
+ Noto Sans Egyptian Hieroglyphs.
22
+
23
+ Examples:
24
+ >>> from numly.egyptian import to_egyptian, from_egyptian
25
+ >>> to_egyptian(42)
26
+ '𓎆𓎆𓎆𓎆𓏺𓏺'
27
+ >>> from_egyptian('𓎆𓎆𓎆𓎆𓏺𓏺')
28
+ 42
29
+ """
30
+
31
+ _ENC: list[tuple[int, str]] = [
32
+ (1_000_000, "𓁨"), # Heh
33
+ (100_000, "𓆐"), # tadpole
34
+ (10_000, "𓂭"), # bent finger
35
+ (1_000, "𓆼"), # lotus
36
+ (100, "𓍢"), # rope coil
37
+ (10, "𓎆"), # heel bone
38
+ (1, "𓏺"), # tally stroke
39
+ ]
40
+
41
+ _DEC: dict[str, int] = {sym: val for val, sym in _ENC}
42
+
43
+ SYSTEM = "egyptian"
44
+ MIN, MAX = 1, 9_999_999
45
+
46
+
47
+ def to_egyptian(num: int) -> str:
48
+ """
49
+ Convert an integer to an Egyptian hieroglyphic numeral string.
50
+
51
+ Args:
52
+ num: Positive integer between 1 and 9,999,999.
53
+
54
+ Returns:
55
+ Hieroglyphic numeral string, e.g. '𓎆𓎆𓎆𓎆𓏺𓏺'.
56
+
57
+ Raises:
58
+ TypeError: If num is not an int.
59
+ ValueError: If num is outside 1–9,999,999.
60
+
61
+ Examples:
62
+ >>> to_egyptian(1)
63
+ '𓏺'
64
+ >>> to_egyptian(42)
65
+ '𓎆𓎆𓎆𓎆𓏺𓏺'
66
+ >>> to_egyptian(1234)
67
+ '𓆼𓍢𓍢𓎆𓎆𓎆𓏺𓏺𓏺𓏺'
68
+ >>> to_egyptian(1000000)
69
+ '𓁨'
70
+ """
71
+ if not isinstance(num, int):
72
+ raise TypeError(f"Expected int, got {type(num).__name__!r}")
73
+ if not MIN <= num <= MAX:
74
+ raise ValueError(f"Egyptian numerals support {MIN}–{MAX:,}, got {num}")
75
+
76
+ result = ""
77
+ for val, sym in _ENC:
78
+ count, num = divmod(num, val)
79
+ result += sym * count
80
+ return result
81
+
82
+
83
+ def from_egyptian(s: str) -> int:
84
+ """
85
+ Convert an Egyptian hieroglyphic numeral string to an integer.
86
+
87
+ Args:
88
+ s: Hieroglyphic string, e.g. '𓎆𓎆𓎆𓎆𓏺𓏺'.
89
+
90
+ Returns:
91
+ Integer value.
92
+
93
+ Raises:
94
+ TypeError: If s is not a str.
95
+ ValueError: If s contains unknown hieroglyphs or is empty.
96
+
97
+ Examples:
98
+ >>> from_egyptian('𓏺')
99
+ 1
100
+ >>> from_egyptian('𓎆𓎆𓎆𓎆𓏺𓏺')
101
+ 42
102
+ >>> from_egyptian('𓆼𓍢𓍢𓎆𓎆𓎆𓏺𓏺𓏺𓏺')
103
+ 1234
104
+ """
105
+ if not isinstance(s, str):
106
+ raise TypeError(f"Expected str, got {type(s).__name__!r}")
107
+ s = s.strip()
108
+ if not s:
109
+ raise ValueError("Empty string")
110
+
111
+ result = 0
112
+ for ch in s:
113
+ if ch not in _DEC:
114
+ raise ValueError(f"Unknown Egyptian hieroglyph: {ch!r}")
115
+ result += _DEC[ch]
116
+ return result
117
+
118
+
119
+ def symbol_breakdown(num: int) -> dict[str, int]:
120
+ """
121
+ Return a breakdown of how many of each symbol are used.
122
+
123
+ Args:
124
+ num: Positive integer between 1 and 9,999,999.
125
+
126
+ Returns:
127
+ Dict mapping symbol → count (only non-zero entries).
128
+
129
+ Examples:
130
+ >>> symbol_breakdown(1234)
131
+ {'𓆼': 1, '𓍢': 2, '𓎆': 3, '𓏺': 4}
132
+ """
133
+ result = {}
134
+ for val, sym in _ENC:
135
+ count, num = divmod(num, val)
136
+ if count:
137
+ result[sym] = count
138
+ return result
139
+
140
+
141
+ def is_valid(s: str) -> bool:
142
+ """
143
+ Check whether a string is a valid Egyptian hieroglyphic numeral.
144
+
145
+ Examples:
146
+ >>> is_valid('𓎆𓎆𓏺𓏺')
147
+ True
148
+ >>> is_valid('hello')
149
+ False
150
+ """
151
+ try:
152
+ from_egyptian(s)
153
+ return True
154
+ except (TypeError, ValueError):
155
+ return False
156
+
157
+
158
+ if __name__ == "__main__":
159
+ tests = [1, 7, 10, 42, 100, 1234, 9999, 1_000_000]
160
+ for n in tests:
161
+ r = to_egyptian(n)
162
+ bd = symbol_breakdown(n)
163
+ print(f"{n:<10} → {r} breakdown: {bd}")
@@ -0,0 +1,152 @@
1
+ """
2
+ numly.greek
3
+ ~~~~~~~~~~~
4
+ Convert integers to Greek alphabetic (Milesian) numerals and back.
5
+
6
+ The Milesian system assigns numeric values to Greek letters:
7
+
8
+ Units (1–9) : Α Β Γ Δ Ε Ϛ Ζ Η Θ
9
+ Tens (10–90): Ι Κ Λ Μ Ν Ξ Ο Π Ϙ
10
+ Hundreds(100–900): Ρ Σ Τ Υ Φ Χ Ψ Ω Ϡ
11
+ Thousands (1000–9000): ͵Α ͵Β ͵Γ ͵Δ ͵Ε ͵Ϛ ͵Ζ ͵Η ͵Θ
12
+
13
+ A keraia (ʹ) follows the numeral to distinguish it from words.
14
+ The lower numeral sign ͵ (U+0375) precedes the letter for thousands.
15
+
16
+ Range: 1 – 9,999
17
+
18
+ Examples:
19
+ >>> from numly.greek import to_greek, from_greek
20
+ >>> to_greek(42)
21
+ 'ΜΒʹ'
22
+ >>> from_greek('͵ΒΚΔʹ')
23
+ 2024
24
+ """
25
+
26
+ # ── lookup tables ──────────────────────────────────────────────────────────
27
+
28
+ _UNITS = ["", "Α", "Β", "Γ", "Δ", "Ε", "Ϛ", "Ζ", "Η", "Θ"]
29
+ _TENS = ["", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ϙ"]
30
+ _HUNDS = ["", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω", "Ϡ"]
31
+ _THOU = ["", "͵Α", "͵Β", "͵Γ", "͵Δ", "͵Ε", "͵Ϛ", "͵Ζ", "͵Η", "͵Θ"]
32
+
33
+ _KERAIA = "ʹ" # U+02B9 MODIFIER LETTER PRIME
34
+ _NUMERAL_SIGN = "͵" # U+0375 GREEK LOWER NUMERAL SIGN
35
+
36
+ # Reverse map: Greek letter → integer value
37
+ _VALUE: dict[str, int] = {}
38
+ for _i, _ch in enumerate(_UNITS[1:], 1): _VALUE[_ch] = _i
39
+ for _i, _ch in enumerate(_TENS[1:], 1): _VALUE[_ch] = _i * 10
40
+ for _i, _ch in enumerate(_HUNDS[1:], 1): _VALUE[_ch] = _i * 100
41
+
42
+ SYSTEM = "greek"
43
+ MIN, MAX = 1, 9_999
44
+
45
+
46
+ # ── public API ─────────────────────────────────────────────────────────────
47
+
48
+ def to_greek(num: int) -> str:
49
+ """
50
+ Convert an integer to a Greek alphabetic numeral string.
51
+
52
+ Args:
53
+ num: Positive integer between 1 and 9,999.
54
+
55
+ Returns:
56
+ Greek numeral string with trailing keraia, e.g. 'ΜΒʹ'.
57
+
58
+ Raises:
59
+ TypeError: If num is not an int.
60
+ ValueError: If num is outside 1–9999.
61
+
62
+ Examples:
63
+ >>> to_greek(1)
64
+ 'Αʹ'
65
+ >>> to_greek(42)
66
+ 'ΜΒʹ'
67
+ >>> to_greek(999)
68
+ 'ϠϘΘʹ'
69
+ >>> to_greek(2024)
70
+ '͵ΒΚΔʹ'
71
+ >>> to_greek(9999)
72
+ '͵ΘϠϘΘʹ'
73
+ """
74
+ if not isinstance(num, int):
75
+ raise TypeError(f"Expected int, got {type(num).__name__!r}")
76
+ if not MIN <= num <= MAX:
77
+ raise ValueError(f"Greek numerals support {MIN}–{MAX}, got {num}")
78
+
79
+ t = num // 1000
80
+ h = (num % 1000) // 100
81
+ d = (num % 100) // 10
82
+ u = num % 10
83
+
84
+ return _THOU[t] + _HUNDS[h] + _TENS[d] + _UNITS[u] + _KERAIA
85
+
86
+
87
+ def from_greek(s: str) -> int:
88
+ """
89
+ Convert a Greek alphabetic numeral string to an integer.
90
+
91
+ Args:
92
+ s: Greek numeral string, with or without keraia, e.g. 'ΜΒʹ'.
93
+
94
+ Returns:
95
+ Integer value.
96
+
97
+ Raises:
98
+ TypeError: If s is not a str.
99
+ ValueError: If s contains unknown characters or is malformed.
100
+
101
+ Examples:
102
+ >>> from_greek('ΜΒʹ')
103
+ 42
104
+ >>> from_greek('͵ΒΚΔʹ')
105
+ 2024
106
+ >>> from_greek('Αʹ')
107
+ 1
108
+ """
109
+ if not isinstance(s, str):
110
+ raise TypeError(f"Expected str, got {type(s).__name__!r}")
111
+ s = s.rstrip(_KERAIA).strip()
112
+ if not s:
113
+ raise ValueError("Empty string")
114
+
115
+ result, i = 0, 0
116
+ while i < len(s):
117
+ ch = s[i]
118
+ if ch == _NUMERAL_SIGN: # thousands prefix ͵
119
+ i += 1
120
+ if i >= len(s) or s[i] not in _VALUE:
121
+ raise ValueError(f"Invalid Greek numeral: missing letter after ͵")
122
+ result += _VALUE[s[i]] * 1000
123
+ elif ch in _VALUE:
124
+ result += _VALUE[ch]
125
+ else:
126
+ raise ValueError(f"Unknown Greek numeral character: {ch!r}")
127
+ i += 1
128
+
129
+ return result
130
+
131
+
132
+ def is_valid(s: str) -> bool:
133
+ """
134
+ Check whether a string is a valid Greek alphabetic numeral.
135
+
136
+ Examples:
137
+ >>> is_valid('ΜΒʹ')
138
+ True
139
+ >>> is_valid('hello')
140
+ False
141
+ """
142
+ try:
143
+ return to_greek(from_greek(s)) == s.strip()
144
+ except (TypeError, ValueError):
145
+ return False
146
+
147
+
148
+ if __name__ == "__main__":
149
+ tests = [1, 6, 9, 10, 42, 90, 100, 399, 999, 1000, 2024, 9999]
150
+ for n in tests:
151
+ r = to_greek(n)
152
+ print(f"{n:<8} → {r:<12} → {from_greek(r)}")
@@ -0,0 +1,118 @@
1
+ """
2
+ numly.roman
3
+ ~~~~~~~~~~~
4
+ Convert integers to Roman numerals and back.
5
+
6
+ Range: 1 – 3,999
7
+
8
+ Examples:
9
+ >>> from numly.roman import to_roman, from_roman
10
+ >>> to_roman(2024)
11
+ 'MMXXIV'
12
+ >>> from_roman('XLII')
13
+ 42
14
+ """
15
+
16
+ _ENC = [
17
+ (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
18
+ (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
19
+ (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"),
20
+ ]
21
+ _DEC = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
22
+
23
+ SYSTEM = "roman"
24
+ MIN, MAX = 1, 3_999
25
+
26
+
27
+ def to_roman(num: int) -> str:
28
+ """
29
+ Convert an integer to a Roman numeral string.
30
+
31
+ Args:
32
+ num: Positive integer between 1 and 3,999.
33
+
34
+ Returns:
35
+ Roman numeral string, e.g. 'XLII'.
36
+
37
+ Raises:
38
+ TypeError: If num is not an int.
39
+ ValueError: If num is outside 1–3999.
40
+
41
+ Examples:
42
+ >>> to_roman(42)
43
+ 'XLII'
44
+ >>> to_roman(2024)
45
+ 'MMXXIV'
46
+ >>> to_roman(3999)
47
+ 'MMMCMXCIX'
48
+ """
49
+ if not isinstance(num, int):
50
+ raise TypeError(f"Expected int, got {type(num).__name__!r}")
51
+ if not MIN <= num <= MAX:
52
+ raise ValueError(f"Roman numerals support {MIN}–{MAX}, got {num}")
53
+
54
+ result = ""
55
+ for val, sym in _ENC:
56
+ while num >= val:
57
+ result += sym
58
+ num -= val
59
+ return result
60
+
61
+
62
+ def from_roman(s: str) -> int:
63
+ """
64
+ Convert a Roman numeral string to an integer.
65
+
66
+ Args:
67
+ s: Roman numeral string (case-insensitive), e.g. 'xlii'.
68
+
69
+ Returns:
70
+ Integer value.
71
+
72
+ Raises:
73
+ TypeError: If s is not a str.
74
+ ValueError: If s contains invalid characters or is empty.
75
+
76
+ Examples:
77
+ >>> from_roman('XLII')
78
+ 42
79
+ >>> from_roman('mmxxiv')
80
+ 2024
81
+ """
82
+ if not isinstance(s, str):
83
+ raise TypeError(f"Expected str, got {type(s).__name__!r}")
84
+ s = s.upper().strip()
85
+ if not s:
86
+ raise ValueError("Empty string")
87
+ for ch in s:
88
+ if ch not in _DEC:
89
+ raise ValueError(f"Invalid Roman numeral character: {ch!r}")
90
+
91
+ result, prev = 0, 0
92
+ for ch in reversed(s):
93
+ v = _DEC[ch]
94
+ result = result - v if v < prev else result + v
95
+ prev = v
96
+ return result
97
+
98
+
99
+ def is_valid(s: str) -> bool:
100
+ """
101
+ Check whether a string is a valid Roman numeral.
102
+
103
+ Examples:
104
+ >>> is_valid('XIV')
105
+ True
106
+ >>> is_valid('ABC')
107
+ False
108
+ """
109
+ try:
110
+ return to_roman(from_roman(s)).upper() == s.upper().strip()
111
+ except (TypeError, ValueError):
112
+ return False
113
+
114
+
115
+ if __name__ == "__main__":
116
+ for n in [1, 4, 9, 42, 399, 2024, 3999]:
117
+ r = to_roman(n)
118
+ print(f"{n:<8} → {r:<14} → {from_roman(r)}")
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: numly
3
+ Version: 0.1.0
4
+ Summary: Convert numbers across numeral systems — Roman, Chinese, Greek, Egyptian, Arabic-Indic and more.
5
+ Home-page: https://github.com/TheMadrasTechie/numly
6
+ Author: TheMadrasTechie
7
+ Author-email: you@example.com
8
+ Keywords: numerals,roman,chinese,greek,egyptian,arabic-indic,number,converter
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: keywords
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ <p align="center">
25
+ <img src="numly_rounded_square.svg" alt="numly logo" width="120" height="120"/>
26
+ </p>
27
+
28
+ <h1 align="center">numly</h1>
29
+
30
+ <p align="center">
31
+ <img src="https://img.shields.io/pypi/v/numly" alt="PyPI version"/>
32
+ <img src="https://img.shields.io/pypi/pyversions/numly" alt="Python versions"/>
33
+ <img src="https://img.shields.io/github/license/yourusername/numly" alt="License"/>
34
+ </p>
35
+
36
+ <p align="center">
37
+ A Python library to work with numbers across formats — decimal, roman, words, binary, hex, and more.
38
+ </p>
39
+
40
+ ---
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install numly
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ import numly
54
+
55
+ # Convert to Roman numerals
56
+ numly.to_roman(2024) # → 'MMXXIV'
57
+
58
+ # Convert to words
59
+ numly.to_words(42) # → 'forty-two'
60
+
61
+ # Convert to binary
62
+ numly.to_binary(255) # → '11111111'
63
+
64
+ # Convert to hex
65
+ numly.to_hex(255) # → 'FF'
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Features
71
+
72
+ - 🔢 Decimal → Roman numerals
73
+ - 🔤 Decimal → Words (English)
74
+ - 💻 Decimal → Binary / Octal / Hex
75
+ - 🔁 Reverse conversions (Roman → Decimal, etc.)
76
+ - 🌍 Locale-aware number formatting *(coming soon)*
77
+
78
+ ---
79
+
80
+ ## API Reference
81
+
82
+ | Function | Input | Output |
83
+ |---|---|---|
84
+ | `to_roman(n)` | `int` | `str` — e.g. `'XIV'` |
85
+ | `from_roman(s)` | `str` | `int` — e.g. `14` |
86
+ | `to_words(n)` | `int` | `str` — e.g. `'forty-two'` |
87
+ | `to_binary(n)` | `int` | `str` — e.g. `'1110'` |
88
+ | `to_octal(n)` | `int` | `str` — e.g. `'16'` |
89
+ | `to_hex(n)` | `int` | `str` — e.g. `'E'` |
90
+
91
+ ---
92
+
93
+ ## Contributing
94
+
95
+ Contributions are welcome! Feel free to open an issue or submit a pull request.
96
+
97
+ ```bash
98
+ git clone https://github.com/yourusername/numly.git
99
+ cd numly
100
+ pip install -e .
101
+ ```
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ MIT License © 2026 TheMadrasTechie
@@ -0,0 +1,14 @@
1
+ README.md
2
+ setup.cfg
3
+ setup.py
4
+ numly/__init__.py
5
+ numly/arabic_indic.py
6
+ numly/chinese.py
7
+ numly/convert.py
8
+ numly/egyptian.py
9
+ numly/greek.py
10
+ numly/roman.py
11
+ numly.egg-info/PKG-INFO
12
+ numly.egg-info/SOURCES.txt
13
+ numly.egg-info/dependency_links.txt
14
+ numly.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ numly
numly-0.1.0/setup.cfg ADDED
@@ -0,0 +1,7 @@
1
+ [metadata]
2
+ license_files =
3
+
4
+ [egg_info]
5
+ tag_build =
6
+ tag_date = 0
7
+
numly-0.1.0/setup.py ADDED
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="numly",
8
+ version="0.1.0",
9
+ packages=find_packages(),
10
+ install_requires=[],
11
+ author="TheMadrasTechie",
12
+ author_email="you@example.com",
13
+ description="Convert numbers across numeral systems — Roman, Chinese, Greek, Egyptian, Arabic-Indic and more.",
14
+ long_description=long_description,
15
+ long_description_content_type="text/markdown",
16
+ url="https://github.com/TheMadrasTechie/numly",
17
+ keywords=["numerals", "roman", "chinese", "greek", "egyptian", "arabic-indic", "number", "converter"],
18
+ classifiers=[
19
+ "Programming Language :: Python :: 3",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ ],
23
+ python_requires=">=3.8",
24
+ )