numclassify 0.1.1__tar.gz → 0.2.1__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.
- {numclassify-0.1.1 → numclassify-0.2.1}/.gitignore +2 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/CHANGELOG.md +13 -0
- numclassify-0.2.1/CONTRIBUTING.md +71 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/PKG-INFO +73 -45
- {numclassify-0.1.1 → numclassify-0.2.1}/README.md +68 -42
- numclassify-0.2.1/docs/api.md +25 -0
- numclassify-0.2.1/docs/changelog.md +3 -0
- numclassify-0.2.1/docs/cli.md +22 -0
- numclassify-0.2.1/docs/index.md +23 -0
- numclassify-0.2.1/examples/basic_usage.py +6 -0
- numclassify-0.2.1/examples/custom_type.py +12 -0
- numclassify-0.2.1/examples/find_perfect_numbers.py +9 -0
- numclassify-0.2.1/examples/random_classify.py +6 -0
- numclassify-0.2.1/examples/stream_large_range.py +7 -0
- numclassify-0.2.1/mkdocs.yml +26 -0
- numclassify-0.2.1/numclassify/__init__.py +181 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_registry.py +3 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/cli.py +79 -3
- numclassify-0.2.1/numclassify/py.typed +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/pyproject.toml +10 -4
- numclassify-0.1.1/numclassify/__init__.py +0 -63
- {numclassify-0.1.1 → numclassify-0.2.1}/.github/workflows/ci.yml +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/.github/workflows/publish.yml +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/LICENSE +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/__main__.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/__init__.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/combinatorial.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/digital.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/divisors.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/figurate.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/number_theory.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/powers.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/primes.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/recreational.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/numclassify/_core/sequences.py +0 -0
- {numclassify-0.1.1 → numclassify-0.2.1}/tests/test_registry.py +0 -0
|
@@ -6,6 +6,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.2.0] - 2026-05-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `classify(n)` — returns `{"number": n, "true_properties": [...], "score": int}`
|
|
13
|
+
- `classify_batch(numbers)` — classify a list of numbers in one call
|
|
14
|
+
- `random_number(max_n=10000)` — classify a random number
|
|
15
|
+
- `find_by_property(start, end, **filters)` — query numbers by property name/value
|
|
16
|
+
- `stream(start, end)` — memory-safe generator yielding classify results
|
|
17
|
+
- `numclassify compare <a> <b> [--json]` — new CLI command showing shared/exclusive properties
|
|
18
|
+
- Windows ANSI color fix via `SetConsoleMode` VT100 flag
|
|
19
|
+
- `py.typed` marker added (PEP 561 compliant)
|
|
20
|
+
- Full type hints on all public API functions
|
|
21
|
+
|
|
9
22
|
## [0.1.1] - 2026-05-06
|
|
10
23
|
### Fixed
|
|
11
24
|
- cli.py: Fixed imports to use numclassify._core instead of numclassify
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Contributing to numclassify
|
|
2
|
+
|
|
3
|
+
## Adding a New Number Type
|
|
4
|
+
|
|
5
|
+
The fastest way to contribute is adding a new number type using the `@register` decorator.
|
|
6
|
+
|
|
7
|
+
### Step 1 \u2014 Write your function
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from numclassify import register
|
|
11
|
+
|
|
12
|
+
@register(name="My Type", category="recreational")
|
|
13
|
+
def is_my_type(n: int) -> bool:
|
|
14
|
+
return n > 0 and n % 7 == 0
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
That's it. Your type is now available via:
|
|
18
|
+
- `nc.get_all_properties(n)`
|
|
19
|
+
- `nc.get_true_properties(n)`
|
|
20
|
+
- `nc.find_in_range(nc.is_my_type, 1, 1000)`
|
|
21
|
+
- `numclassify find my_type` in the CLI
|
|
22
|
+
|
|
23
|
+
### Step 2 \u2014 Add it to the right module
|
|
24
|
+
|
|
25
|
+
Place your function in the appropriate file under `numclassify/_core/`:
|
|
26
|
+
|
|
27
|
+
| Module | Types |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `primes.py` | Prime families |
|
|
30
|
+
| `figurate.py` | Polygonal/figurate numbers |
|
|
31
|
+
| `divisors.py` | Divisor-based (perfect, abundant...) |
|
|
32
|
+
| `digital.py` | Digit-based (Armstrong, Harshad...) |
|
|
33
|
+
| `sequences.py` | Number sequences (Fibonacci, Lucas...) |
|
|
34
|
+
| `powers.py` | Powers and sums |
|
|
35
|
+
| `number_theory.py` | Number theory properties |
|
|
36
|
+
| `combinatorial.py` | Combinatorial numbers |
|
|
37
|
+
| `recreational.py` | Recreational/fun types |
|
|
38
|
+
|
|
39
|
+
### Step 3 \u2014 Add a test
|
|
40
|
+
|
|
41
|
+
Add a test in `tests/` following the existing pattern:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
def test_is_my_type():
|
|
45
|
+
assert nc.is_my_type(7) == True
|
|
46
|
+
assert nc.is_my_type(1) == False
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 4 \u2014 Submit a PR
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git checkout -b add-my-type
|
|
53
|
+
git add .
|
|
54
|
+
git commit -m "Add My Type number classification"
|
|
55
|
+
git push origin add-my-type
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then open a pull request on GitHub.
|
|
59
|
+
|
|
60
|
+
## Running Tests
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd numclassify
|
|
64
|
+
python -m pytest tests/ -v
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Code Style
|
|
68
|
+
|
|
69
|
+
- Pure Python, no external dependencies
|
|
70
|
+
- Type hints required on all functions
|
|
71
|
+
- Function name must start with `is_` for boolean classifiers
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: numclassify
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: The most comprehensive Python library for number classification
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: The most comprehensive Python library for number classification - 3000+ number types
|
|
5
5
|
Project-URL: Homepage, https://github.com/aratrikghosh2011-tech/numclassify
|
|
6
6
|
Project-URL: Repository, https://github.com/aratrikghosh2011-tech/numclassify
|
|
7
|
+
Project-URL: Documentation, https://aratrikghosh2011-tech.github.io/numclassify/
|
|
7
8
|
Project-URL: Issues, https://github.com/aratrikghosh2011-tech/numclassify/issues
|
|
8
9
|
Author-email: Aratrik Ghosh <aratrikghosh2011@gmail.com>
|
|
9
10
|
License: MIT
|
|
10
11
|
License-File: LICENSE
|
|
11
12
|
Keywords: armstrong,classification,figurate,mathematics,number-theory,prime
|
|
12
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
14
|
Classifier: Intended Audience :: Education
|
|
14
15
|
Classifier: Intended Audience :: Science/Research
|
|
15
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -20,32 +21,34 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
20
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
25
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
24
26
|
Requires-Python: >=3.8
|
|
25
27
|
Description-Content-Type: text/markdown
|
|
26
28
|
|
|
27
29
|
# numclassify
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
The most comprehensive Python library for number classification — 3000+ number types, zero dependencies.
|
|
30
32
|
|
|
31
33
|
[](https://pypi.org/project/numclassify/)
|
|
34
|
+
[](https://pypi.org/project/numclassify/)
|
|
32
35
|
[](https://pypi.org/project/numclassify/)
|
|
33
36
|
[](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/LICENSE)
|
|
34
37
|
[](https://github.com/aratrikghosh2011-tech/numclassify/actions/workflows/ci.yml)
|
|
35
38
|
|
|
36
39
|
---
|
|
37
40
|
|
|
38
|
-
##
|
|
41
|
+
## Overview
|
|
39
42
|
|
|
40
|
-
Most number-theory libraries — `labmath`, `eulerlib`, `pyntlib` —
|
|
43
|
+
Most number-theory libraries — `labmath`, `eulerlib`, `pyntlib` — are built around computation: factoring integers, finding GCDs, generating primes. `numclassify` solves a different problem: **given a number, what is it?**
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
```
|
|
46
|
+
153 → Armstrong, Harshad, Triangular, Abundant ...
|
|
47
|
+
1729 → Taxicab (Hardy-Ramanujan), Carmichael, Zeisel ...
|
|
48
|
+
28 → Perfect, Triangular, Hexagonal, Semiprime ...
|
|
49
|
+
```
|
|
47
50
|
|
|
48
|
-
Over **3000 named number types
|
|
51
|
+
Over **3000 named number types** are supported, with instant lookup, no external dependencies, and a fully typed API.
|
|
49
52
|
|
|
50
53
|
---
|
|
51
54
|
|
|
@@ -55,7 +58,7 @@ Over **3000 named number types**, instant lookup, no external dependencies.
|
|
|
55
58
|
pip install numclassify
|
|
56
59
|
```
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
To install from source in editable mode:
|
|
59
62
|
|
|
60
63
|
```bash
|
|
61
64
|
git clone https://github.com/aratrikghosh2011-tech/numclassify.git
|
|
@@ -71,20 +74,32 @@ pip install -e .
|
|
|
71
74
|
import numclassify as nc
|
|
72
75
|
|
|
73
76
|
# Boolean checks
|
|
74
|
-
nc.is_prime(17)
|
|
75
|
-
nc.
|
|
76
|
-
|
|
77
|
+
nc.is_prime(17) # True
|
|
78
|
+
nc.is_perfect(28) # True
|
|
79
|
+
|
|
80
|
+
# Classify a single number
|
|
81
|
+
nc.classify(1729)
|
|
82
|
+
# {'number': 1729, 'true_properties': ['Taxicab', 'Carmichael', ...], 'score': 22}
|
|
83
|
+
|
|
84
|
+
# Classify multiple numbers at once
|
|
85
|
+
nc.classify_batch([6, 28, 496])
|
|
86
|
+
|
|
87
|
+
# Query by property
|
|
88
|
+
nc.find_by_property(start=1, end=1000, Perfect=True)
|
|
89
|
+
# [6, 28, 496]
|
|
90
|
+
|
|
91
|
+
# Memory-safe streaming over large ranges
|
|
92
|
+
for result in nc.stream(1, 1_000_000):
|
|
93
|
+
if result['score'] > 30:
|
|
94
|
+
print(result)
|
|
95
|
+
|
|
96
|
+
# Random number classification
|
|
97
|
+
nc.random_number()
|
|
77
98
|
|
|
78
99
|
# All true properties of a number
|
|
79
100
|
nc.get_true_properties(1729)
|
|
80
|
-
# ['taxicab', 'zeisel', 'carmichael', 'odd', 'composite',
|
|
81
|
-
# 'deficient', 'squarefree', 'cubefree', 'powerful_not_perfect_power']
|
|
82
|
-
|
|
83
|
-
# Search a range
|
|
84
|
-
nc.find_in_range(nc.is_armstrong, 1, 10000)
|
|
85
|
-
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 8208, 9474]
|
|
86
101
|
|
|
87
|
-
# Pretty-print everything
|
|
102
|
+
# Pretty-print everything
|
|
88
103
|
nc.print_properties(153)
|
|
89
104
|
# ┌─────────────────────────────────────────┐
|
|
90
105
|
# │ Properties of 153 │
|
|
@@ -132,6 +147,16 @@ $ numclassify range 1 20 --filter prime
|
|
|
132
147
|
2, 3, 5, 7, 11, 13, 17, 19
|
|
133
148
|
```
|
|
134
149
|
|
|
150
|
+
**Compare two numbers:**
|
|
151
|
+
```bash
|
|
152
|
+
$ numclassify compare 6 28
|
|
153
|
+
Comparing 6 and 28
|
|
154
|
+
──────────────────
|
|
155
|
+
Shared (13): Perfect, Triangular, Hexagonal, ...
|
|
156
|
+
Only in 6 (21): Armstrong, Factorial, Palindrome, ...
|
|
157
|
+
Only in 28 (8): Happy, Keith, Padovan, ...
|
|
158
|
+
```
|
|
159
|
+
|
|
135
160
|
**List all registered types in a category:**
|
|
136
161
|
```bash
|
|
137
162
|
$ numclassify list --category primes
|
|
@@ -156,23 +181,23 @@ Examples: 1, 2, 3, 153, 370, 371, 407, 1634, 8208, 9474
|
|
|
156
181
|
|
|
157
182
|
| Category | Count | Examples |
|
|
158
183
|
|---|---|---|
|
|
159
|
-
| Polygonal (figurate) | 998 | Triangular, Square, Pentagonal
|
|
160
|
-
| Centered Polygonal | 998 | Centered Triangular, Centered Hexagonal
|
|
161
|
-
| Prime families | 41 | Twin, Mersenne, Sophie Germain, Wilson, Safe
|
|
162
|
-
| Digital invariants | 10 | Armstrong, Spy, Harshad, Disarium, Happy, Neon
|
|
163
|
-
| Divisor-based | 27 | Perfect, Abundant, Weird, Amicable, Practical
|
|
164
|
-
| Sequences | 15 | Fibonacci, Lucas, Catalan, Bell, Padovan
|
|
165
|
-
| Powers | 13 | Perfect Square, Taxicab, Sum of Two Squares
|
|
166
|
-
| Number theory | 14 | Evil, Carmichael, Keith, Autobiographical
|
|
167
|
-
| Combinatorial | 10 | Factorial, Primorial, Subfactorial, Catalan
|
|
168
|
-
| Recreational | 5 | Kaprekar, Automorphic, Palindrome
|
|
184
|
+
| Polygonal (figurate) | 998 | Triangular, Square, Pentagonal, Chiliagonal |
|
|
185
|
+
| Centered Polygonal | 998 | Centered Triangular, Centered Hexagonal |
|
|
186
|
+
| Prime families | 41 | Twin, Mersenne, Sophie Germain, Wilson, Safe |
|
|
187
|
+
| Digital invariants | 10 | Armstrong, Spy, Harshad, Disarium, Happy, Neon |
|
|
188
|
+
| Divisor-based | 27 | Perfect, Abundant, Weird, Amicable, Practical |
|
|
189
|
+
| Sequences | 15 | Fibonacci, Lucas, Catalan, Bell, Padovan |
|
|
190
|
+
| Powers | 13 | Perfect Square, Taxicab, Sum of Two Squares |
|
|
191
|
+
| Number theory | 14 | Evil, Carmichael, Keith, Autobiographical |
|
|
192
|
+
| Combinatorial | 10 | Factorial, Primorial, Subfactorial, Catalan |
|
|
193
|
+
| Recreational | 5 | Kaprekar, Automorphic, Palindrome |
|
|
169
194
|
| **Total** | **3000+** | |
|
|
170
195
|
|
|
171
196
|
---
|
|
172
197
|
|
|
173
|
-
## Adding
|
|
198
|
+
## Adding Custom Number Types
|
|
174
199
|
|
|
175
|
-
The `@register` decorator lets you
|
|
200
|
+
The `@register` decorator lets you define and integrate your own number types in a few lines. Once registered, the type is automatically available through the full API and CLI.
|
|
176
201
|
|
|
177
202
|
```python
|
|
178
203
|
from numclassify import register
|
|
@@ -181,7 +206,6 @@ from numclassify import register
|
|
|
181
206
|
def is_my_type(n: int) -> bool:
|
|
182
207
|
return n > 0 and n % 7 == 0 and str(n)[0] == "4"
|
|
183
208
|
|
|
184
|
-
# Now works everywhere
|
|
185
209
|
import numclassify as nc
|
|
186
210
|
nc.is_my_type(49) # False (doesn't start with 4)
|
|
187
211
|
nc.is_my_type(42) # True
|
|
@@ -194,24 +218,28 @@ nc.get_true_properties(42) # [..., 'my_type', ...]
|
|
|
194
218
|
|
|
195
219
|
| Function | Description |
|
|
196
220
|
|---|---|
|
|
197
|
-
| `
|
|
198
|
-
| `
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
221
|
+
| `classify(n)` | Returns a dict with the number, its true properties, and a score |
|
|
222
|
+
| `classify_batch(numbers)` | Classify a list of numbers; returns a list of dicts |
|
|
223
|
+
| `random_number(max_n)` | Classify a randomly selected number up to `max_n` |
|
|
224
|
+
| `find_by_property(start, end, **filters)` | Find numbers in a range matching given property filters |
|
|
225
|
+
| `stream(start, end)` | Generator yielding classify results one at a time; memory-safe |
|
|
226
|
+
| `is_prime(n)` | Returns `True` if `n` is prime |
|
|
227
|
+
| `is_armstrong(n)` | Returns `True` if `n` is an Armstrong (narcissistic) number |
|
|
228
|
+
| `get_all_properties(n)` | Dict mapping every registered type to `True` or `False` |
|
|
229
|
+
| `get_true_properties(n)` | List of only the properties that hold for `n` |
|
|
201
230
|
| `print_properties(n)` | Pretty-prints a formatted property table to stdout |
|
|
202
231
|
| `find_in_range(fn, lo, hi)` | All integers in `[lo, hi]` where `fn` returns `True` |
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `most_special_in_range(lo, hi)` | The number in `[lo, hi]` with the most true properties |
|
|
232
|
+
| `count_properties(n)` | Number of types that apply to `n` |
|
|
233
|
+
| `most_special_in_range(lo, hi)` | Number in `[lo, hi]` with the greatest count of true properties |
|
|
206
234
|
|
|
207
|
-
Full API
|
|
235
|
+
Full API documentation: [github.com/aratrikghosh2011-tech/numclassify](https://github.com/aratrikghosh2011-tech/numclassify)
|
|
208
236
|
|
|
209
237
|
---
|
|
210
238
|
|
|
211
239
|
## Requirements
|
|
212
240
|
|
|
213
241
|
- Python 3.8 or higher
|
|
214
|
-
-
|
|
242
|
+
- No external dependencies
|
|
215
243
|
|
|
216
244
|
---
|
|
217
245
|
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
# numclassify
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The most comprehensive Python library for number classification — 3000+ number types, zero dependencies.
|
|
4
4
|
|
|
5
5
|
[](https://pypi.org/project/numclassify/)
|
|
6
|
+
[](https://pypi.org/project/numclassify/)
|
|
6
7
|
[](https://pypi.org/project/numclassify/)
|
|
7
8
|
[](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/LICENSE)
|
|
8
9
|
[](https://github.com/aratrikghosh2011-tech/numclassify/actions/workflows/ci.yml)
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## Overview
|
|
13
14
|
|
|
14
|
-
Most number-theory libraries — `labmath`, `eulerlib`, `pyntlib` —
|
|
15
|
+
Most number-theory libraries — `labmath`, `eulerlib`, `pyntlib` — are built around computation: factoring integers, finding GCDs, generating primes. `numclassify` solves a different problem: **given a number, what is it?**
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
```
|
|
18
|
+
153 → Armstrong, Harshad, Triangular, Abundant ...
|
|
19
|
+
1729 → Taxicab (Hardy-Ramanujan), Carmichael, Zeisel ...
|
|
20
|
+
28 → Perfect, Triangular, Hexagonal, Semiprime ...
|
|
21
|
+
```
|
|
21
22
|
|
|
22
|
-
Over **3000 named number types
|
|
23
|
+
Over **3000 named number types** are supported, with instant lookup, no external dependencies, and a fully typed API.
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -29,7 +30,7 @@ Over **3000 named number types**, instant lookup, no external dependencies.
|
|
|
29
30
|
pip install numclassify
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
To install from source in editable mode:
|
|
33
34
|
|
|
34
35
|
```bash
|
|
35
36
|
git clone https://github.com/aratrikghosh2011-tech/numclassify.git
|
|
@@ -45,20 +46,32 @@ pip install -e .
|
|
|
45
46
|
import numclassify as nc
|
|
46
47
|
|
|
47
48
|
# Boolean checks
|
|
48
|
-
nc.is_prime(17)
|
|
49
|
-
nc.
|
|
50
|
-
|
|
49
|
+
nc.is_prime(17) # True
|
|
50
|
+
nc.is_perfect(28) # True
|
|
51
|
+
|
|
52
|
+
# Classify a single number
|
|
53
|
+
nc.classify(1729)
|
|
54
|
+
# {'number': 1729, 'true_properties': ['Taxicab', 'Carmichael', ...], 'score': 22}
|
|
55
|
+
|
|
56
|
+
# Classify multiple numbers at once
|
|
57
|
+
nc.classify_batch([6, 28, 496])
|
|
58
|
+
|
|
59
|
+
# Query by property
|
|
60
|
+
nc.find_by_property(start=1, end=1000, Perfect=True)
|
|
61
|
+
# [6, 28, 496]
|
|
62
|
+
|
|
63
|
+
# Memory-safe streaming over large ranges
|
|
64
|
+
for result in nc.stream(1, 1_000_000):
|
|
65
|
+
if result['score'] > 30:
|
|
66
|
+
print(result)
|
|
67
|
+
|
|
68
|
+
# Random number classification
|
|
69
|
+
nc.random_number()
|
|
51
70
|
|
|
52
71
|
# All true properties of a number
|
|
53
72
|
nc.get_true_properties(1729)
|
|
54
|
-
# ['taxicab', 'zeisel', 'carmichael', 'odd', 'composite',
|
|
55
|
-
# 'deficient', 'squarefree', 'cubefree', 'powerful_not_perfect_power']
|
|
56
|
-
|
|
57
|
-
# Search a range
|
|
58
|
-
nc.find_in_range(nc.is_armstrong, 1, 10000)
|
|
59
|
-
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 8208, 9474]
|
|
60
73
|
|
|
61
|
-
# Pretty-print everything
|
|
74
|
+
# Pretty-print everything
|
|
62
75
|
nc.print_properties(153)
|
|
63
76
|
# ┌─────────────────────────────────────────┐
|
|
64
77
|
# │ Properties of 153 │
|
|
@@ -106,6 +119,16 @@ $ numclassify range 1 20 --filter prime
|
|
|
106
119
|
2, 3, 5, 7, 11, 13, 17, 19
|
|
107
120
|
```
|
|
108
121
|
|
|
122
|
+
**Compare two numbers:**
|
|
123
|
+
```bash
|
|
124
|
+
$ numclassify compare 6 28
|
|
125
|
+
Comparing 6 and 28
|
|
126
|
+
──────────────────
|
|
127
|
+
Shared (13): Perfect, Triangular, Hexagonal, ...
|
|
128
|
+
Only in 6 (21): Armstrong, Factorial, Palindrome, ...
|
|
129
|
+
Only in 28 (8): Happy, Keith, Padovan, ...
|
|
130
|
+
```
|
|
131
|
+
|
|
109
132
|
**List all registered types in a category:**
|
|
110
133
|
```bash
|
|
111
134
|
$ numclassify list --category primes
|
|
@@ -130,23 +153,23 @@ Examples: 1, 2, 3, 153, 370, 371, 407, 1634, 8208, 9474
|
|
|
130
153
|
|
|
131
154
|
| Category | Count | Examples |
|
|
132
155
|
|---|---|---|
|
|
133
|
-
| Polygonal (figurate) | 998 | Triangular, Square, Pentagonal
|
|
134
|
-
| Centered Polygonal | 998 | Centered Triangular, Centered Hexagonal
|
|
135
|
-
| Prime families | 41 | Twin, Mersenne, Sophie Germain, Wilson, Safe
|
|
136
|
-
| Digital invariants | 10 | Armstrong, Spy, Harshad, Disarium, Happy, Neon
|
|
137
|
-
| Divisor-based | 27 | Perfect, Abundant, Weird, Amicable, Practical
|
|
138
|
-
| Sequences | 15 | Fibonacci, Lucas, Catalan, Bell, Padovan
|
|
139
|
-
| Powers | 13 | Perfect Square, Taxicab, Sum of Two Squares
|
|
140
|
-
| Number theory | 14 | Evil, Carmichael, Keith, Autobiographical
|
|
141
|
-
| Combinatorial | 10 | Factorial, Primorial, Subfactorial, Catalan
|
|
142
|
-
| Recreational | 5 | Kaprekar, Automorphic, Palindrome
|
|
156
|
+
| Polygonal (figurate) | 998 | Triangular, Square, Pentagonal, Chiliagonal |
|
|
157
|
+
| Centered Polygonal | 998 | Centered Triangular, Centered Hexagonal |
|
|
158
|
+
| Prime families | 41 | Twin, Mersenne, Sophie Germain, Wilson, Safe |
|
|
159
|
+
| Digital invariants | 10 | Armstrong, Spy, Harshad, Disarium, Happy, Neon |
|
|
160
|
+
| Divisor-based | 27 | Perfect, Abundant, Weird, Amicable, Practical |
|
|
161
|
+
| Sequences | 15 | Fibonacci, Lucas, Catalan, Bell, Padovan |
|
|
162
|
+
| Powers | 13 | Perfect Square, Taxicab, Sum of Two Squares |
|
|
163
|
+
| Number theory | 14 | Evil, Carmichael, Keith, Autobiographical |
|
|
164
|
+
| Combinatorial | 10 | Factorial, Primorial, Subfactorial, Catalan |
|
|
165
|
+
| Recreational | 5 | Kaprekar, Automorphic, Palindrome |
|
|
143
166
|
| **Total** | **3000+** | |
|
|
144
167
|
|
|
145
168
|
---
|
|
146
169
|
|
|
147
|
-
## Adding
|
|
170
|
+
## Adding Custom Number Types
|
|
148
171
|
|
|
149
|
-
The `@register` decorator lets you
|
|
172
|
+
The `@register` decorator lets you define and integrate your own number types in a few lines. Once registered, the type is automatically available through the full API and CLI.
|
|
150
173
|
|
|
151
174
|
```python
|
|
152
175
|
from numclassify import register
|
|
@@ -155,7 +178,6 @@ from numclassify import register
|
|
|
155
178
|
def is_my_type(n: int) -> bool:
|
|
156
179
|
return n > 0 and n % 7 == 0 and str(n)[0] == "4"
|
|
157
180
|
|
|
158
|
-
# Now works everywhere
|
|
159
181
|
import numclassify as nc
|
|
160
182
|
nc.is_my_type(49) # False (doesn't start with 4)
|
|
161
183
|
nc.is_my_type(42) # True
|
|
@@ -168,24 +190,28 @@ nc.get_true_properties(42) # [..., 'my_type', ...]
|
|
|
168
190
|
|
|
169
191
|
| Function | Description |
|
|
170
192
|
|---|---|
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
193
|
+
| `classify(n)` | Returns a dict with the number, its true properties, and a score |
|
|
194
|
+
| `classify_batch(numbers)` | Classify a list of numbers; returns a list of dicts |
|
|
195
|
+
| `random_number(max_n)` | Classify a randomly selected number up to `max_n` |
|
|
196
|
+
| `find_by_property(start, end, **filters)` | Find numbers in a range matching given property filters |
|
|
197
|
+
| `stream(start, end)` | Generator yielding classify results one at a time; memory-safe |
|
|
198
|
+
| `is_prime(n)` | Returns `True` if `n` is prime |
|
|
199
|
+
| `is_armstrong(n)` | Returns `True` if `n` is an Armstrong (narcissistic) number |
|
|
200
|
+
| `get_all_properties(n)` | Dict mapping every registered type to `True` or `False` |
|
|
201
|
+
| `get_true_properties(n)` | List of only the properties that hold for `n` |
|
|
175
202
|
| `print_properties(n)` | Pretty-prints a formatted property table to stdout |
|
|
176
203
|
| `find_in_range(fn, lo, hi)` | All integers in `[lo, hi]` where `fn` returns `True` |
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `most_special_in_range(lo, hi)` | The number in `[lo, hi]` with the most true properties |
|
|
204
|
+
| `count_properties(n)` | Number of types that apply to `n` |
|
|
205
|
+
| `most_special_in_range(lo, hi)` | Number in `[lo, hi]` with the greatest count of true properties |
|
|
180
206
|
|
|
181
|
-
Full API
|
|
207
|
+
Full API documentation: [github.com/aratrikghosh2011-tech/numclassify](https://github.com/aratrikghosh2011-tech/numclassify)
|
|
182
208
|
|
|
183
209
|
---
|
|
184
210
|
|
|
185
211
|
## Requirements
|
|
186
212
|
|
|
187
213
|
- Python 3.8 or higher
|
|
188
|
-
-
|
|
214
|
+
- No external dependencies
|
|
189
215
|
|
|
190
216
|
---
|
|
191
217
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## classify(n)
|
|
4
|
+
Returns dict with number, true_properties list, and score.
|
|
5
|
+
|
|
6
|
+
## classify_batch(numbers)
|
|
7
|
+
Classify a list of integers.
|
|
8
|
+
|
|
9
|
+
## random_number(max_n=10000)
|
|
10
|
+
Classify a random integer up to max_n.
|
|
11
|
+
|
|
12
|
+
## find_by_property(start, end, **filters)
|
|
13
|
+
Find numbers matching property filters.
|
|
14
|
+
|
|
15
|
+
## stream(start, end)
|
|
16
|
+
Memory-safe generator over large ranges.
|
|
17
|
+
|
|
18
|
+
## Utilities
|
|
19
|
+
- get_all_properties(n)
|
|
20
|
+
- get_true_properties(n)
|
|
21
|
+
- print_properties(n)
|
|
22
|
+
- count_properties(n)
|
|
23
|
+
- most_special_in_range(lo, hi)
|
|
24
|
+
- find_in_range(fn, lo, hi)
|
|
25
|
+
- is_prime(n), is_armstrong(n), is_perfect(n)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# CLI Reference
|
|
2
|
+
|
|
3
|
+
## check
|
|
4
|
+
numclassify check 1729
|
|
5
|
+
numclassify check 1729 --json
|
|
6
|
+
|
|
7
|
+
## compare
|
|
8
|
+
numclassify compare 6 28
|
|
9
|
+
numclassify compare 6 28 --json
|
|
10
|
+
|
|
11
|
+
## find
|
|
12
|
+
numclassify find armstrong --limit 10
|
|
13
|
+
|
|
14
|
+
## range
|
|
15
|
+
numclassify range 1 100 --filter prime
|
|
16
|
+
|
|
17
|
+
## info
|
|
18
|
+
numclassify info armstrong
|
|
19
|
+
|
|
20
|
+
## list
|
|
21
|
+
numclassify list
|
|
22
|
+
numclassify list --category primes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# numclassify
|
|
2
|
+
|
|
3
|
+
The most comprehensive Python number classification library - 3000+ types, zero dependencies.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
pip install numclassify
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
import numclassify as nc
|
|
12
|
+
nc.classify(1729)
|
|
13
|
+
nc.classify_batch([6, 28, 496])
|
|
14
|
+
nc.find_by_property(start=1, end=1000, Perfect=True)
|
|
15
|
+
nc.random_number()
|
|
16
|
+
|
|
17
|
+
## Why numclassify?
|
|
18
|
+
|
|
19
|
+
- 153: Armstrong, Narcissistic, Harshad, Triangular...
|
|
20
|
+
- 1729: Taxicab, Carmichael, Dodecagonal...
|
|
21
|
+
- 28: Perfect, Triangular, Hexagonal, Keith...
|
|
22
|
+
|
|
23
|
+
3000+ named number types. Instant lookup. Zero dependencies.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from numclassify import register
|
|
2
|
+
import numclassify as nc
|
|
3
|
+
|
|
4
|
+
# Add a custom number type using the @register decorator
|
|
5
|
+
@register(name="Lucky Seven Multiple", category="recreational")
|
|
6
|
+
def is_lucky_seven_multiple(n: int) -> bool:
|
|
7
|
+
return n > 0 and n % 7 == 0 and "7" in str(n)
|
|
8
|
+
|
|
9
|
+
# Now works across the entire API
|
|
10
|
+
print(nc.is_lucky_seven_multiple(77)) # True
|
|
11
|
+
print(nc.is_lucky_seven_multiple(14)) # False
|
|
12
|
+
print("Lucky Seven Multiples under 200:", nc.find_in_range(nc.is_lucky_seven_multiple, 1, 200))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import numclassify as nc
|
|
2
|
+
|
|
3
|
+
# Find all perfect numbers under 10000
|
|
4
|
+
perfect = nc.find_by_property(start=1, end=10000, Perfect=True)
|
|
5
|
+
print("Perfect numbers:", perfect)
|
|
6
|
+
|
|
7
|
+
# Find numbers that are both perfect and odious
|
|
8
|
+
perfect_odious = nc.find_by_property(start=1, end=10000, Perfect=True, Odious=True)
|
|
9
|
+
print("Perfect and Odious:", perfect_odious)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
site_name: numclassify
|
|
2
|
+
site_description: The most comprehensive Python number classification library - 3000+ types
|
|
3
|
+
site_url: https://aratrikghosh2011-tech.github.io/numclassify/
|
|
4
|
+
repo_url: https://github.com/aratrikghosh2011-tech/numclassify
|
|
5
|
+
repo_name: aratrikghosh2011-tech/numclassify
|
|
6
|
+
|
|
7
|
+
theme:
|
|
8
|
+
name: material
|
|
9
|
+
palette:
|
|
10
|
+
primary: indigo
|
|
11
|
+
accent: indigo
|
|
12
|
+
features:
|
|
13
|
+
- navigation.tabs
|
|
14
|
+
- navigation.top
|
|
15
|
+
- content.code.copy
|
|
16
|
+
|
|
17
|
+
nav:
|
|
18
|
+
- Home: index.md
|
|
19
|
+
- API Reference: api.md
|
|
20
|
+
- CLI: cli.md
|
|
21
|
+
- Changelog: changelog.md
|
|
22
|
+
|
|
23
|
+
markdown_extensions:
|
|
24
|
+
- pymdownx.highlight
|
|
25
|
+
- pymdownx.superfences
|
|
26
|
+
- tables
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numclassify
|
|
3
|
+
~~~~~~~~~~~
|
|
4
|
+
The most comprehensive Python library for number classification.
|
|
5
|
+
Importing this package triggers registration of all built-in classification
|
|
6
|
+
functions via their ``@register`` decorators.
|
|
7
|
+
|
|
8
|
+
Public API
|
|
9
|
+
----------
|
|
10
|
+
.. autosummary::
|
|
11
|
+
is_prime
|
|
12
|
+
is_armstrong
|
|
13
|
+
is_perfect
|
|
14
|
+
get_all_properties
|
|
15
|
+
get_true_properties
|
|
16
|
+
print_properties
|
|
17
|
+
find_in_range
|
|
18
|
+
find_all_in_range
|
|
19
|
+
count_properties
|
|
20
|
+
most_special_in_range
|
|
21
|
+
classify
|
|
22
|
+
classify_batch
|
|
23
|
+
random_number
|
|
24
|
+
find_by_property
|
|
25
|
+
stream
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
del annotations
|
|
30
|
+
|
|
31
|
+
__version__ = "0.2.1"
|
|
32
|
+
|
|
33
|
+
# --- Import all _core submodules so @register decorators fire at import time ---
|
|
34
|
+
from numclassify._core import primes as _primes # noqa: F401
|
|
35
|
+
from numclassify._core import figurate as _figurate # noqa: F401
|
|
36
|
+
from numclassify._core import digital as _digital # noqa: F401
|
|
37
|
+
from numclassify._core import recreational as _recreational # noqa: F401
|
|
38
|
+
from numclassify._core import divisors as _divisors # noqa: F401
|
|
39
|
+
from numclassify._core import sequences as _sequences # noqa: F401
|
|
40
|
+
from numclassify._core import powers as _powers # noqa: F401
|
|
41
|
+
from numclassify._core import number_theory as _number_theory # noqa: F401
|
|
42
|
+
from numclassify._core import combinatorial as _combinatorial # noqa: F401
|
|
43
|
+
|
|
44
|
+
# --- Re-export key functions at top level ---
|
|
45
|
+
from numclassify._core.primes import is_prime # noqa: F401
|
|
46
|
+
from numclassify._core.digital import is_armstrong # noqa: F401
|
|
47
|
+
from numclassify._core.divisors import is_perfect # noqa: F401
|
|
48
|
+
from numclassify._registry import register # noqa: F401
|
|
49
|
+
from numclassify._registry import ( # noqa: F401
|
|
50
|
+
get_all_properties,
|
|
51
|
+
get_true_properties,
|
|
52
|
+
print_properties,
|
|
53
|
+
find_in_range,
|
|
54
|
+
find_all_in_range,
|
|
55
|
+
count_properties,
|
|
56
|
+
most_special_in_range,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Dynamic attribute fallback — allows nc.<any_registered_type>() to work
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
def __getattr__(name: str):
|
|
64
|
+
"""Fallback: look up *name* in the global registry as a normalised key."""
|
|
65
|
+
from numclassify._registry import REGISTRY, _normalize
|
|
66
|
+
|
|
67
|
+
key = _normalize(name)
|
|
68
|
+
if key in REGISTRY:
|
|
69
|
+
return REGISTRY[key].func
|
|
70
|
+
raise AttributeError(f"module 'numclassify' has no attribute {name!r}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# New features
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
def classify(n: int) -> dict:
|
|
78
|
+
"""
|
|
79
|
+
Returns a summary dict for a single integer.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
{
|
|
84
|
+
"number": n,
|
|
85
|
+
"true_properties": [list of property names that are True],
|
|
86
|
+
"score": int, # count of True properties
|
|
87
|
+
}
|
|
88
|
+
"""
|
|
89
|
+
raw = get_true_properties(n)
|
|
90
|
+
if isinstance(raw, dict):
|
|
91
|
+
true_props = [k for k, v in raw.items() if v]
|
|
92
|
+
else:
|
|
93
|
+
true_props = list(raw)
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
"number": n,
|
|
97
|
+
"true_properties": true_props,
|
|
98
|
+
"score": len(true_props),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def classify_batch(numbers: list) -> list:
|
|
103
|
+
"""Returns a list of classify(n) dicts, one per number, same order."""
|
|
104
|
+
return [classify(n) for n in numbers]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def random_number(max_n: int = 10000) -> dict:
|
|
108
|
+
"""Picks a random int between 1 and max_n inclusive, returns classify(n)."""
|
|
109
|
+
import random as _random
|
|
110
|
+
n = _random.randint(1, max_n)
|
|
111
|
+
return classify(n)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def find_by_property(start: int = 1, end: int = 1000,
|
|
115
|
+
limit: int | None = None, **filters: bool) -> list:
|
|
116
|
+
"""
|
|
117
|
+
Query numbers by property values within [start, end].
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
start, end : int
|
|
122
|
+
Inclusive search range.
|
|
123
|
+
limit : int, optional
|
|
124
|
+
Stop after finding this many matches.
|
|
125
|
+
**filters : bool
|
|
126
|
+
Property name to required bool value mapping.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
list of int
|
|
131
|
+
|
|
132
|
+
Example
|
|
133
|
+
-------
|
|
134
|
+
find_by_property(start=1, end=1000, Perfect=True, Odious=True)
|
|
135
|
+
"""
|
|
136
|
+
results = []
|
|
137
|
+
for n in range(start, end + 1):
|
|
138
|
+
if not filters:
|
|
139
|
+
results.append(n)
|
|
140
|
+
else:
|
|
141
|
+
props = get_all_properties(n)
|
|
142
|
+
if all(props.get(k) == v for k, v in filters.items()):
|
|
143
|
+
results.append(n)
|
|
144
|
+
if limit is not None and len(results) >= limit:
|
|
145
|
+
break
|
|
146
|
+
return results
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def stream(start: int, end: int):
|
|
150
|
+
"""Generator. Yields classify(n) for each n in [start, end]. Memory-safe for large ranges."""
|
|
151
|
+
for n in range(start, end + 1):
|
|
152
|
+
yield classify(n)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
# Public API
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
__all__ = [
|
|
160
|
+
"__version__",
|
|
161
|
+
"is_prime",
|
|
162
|
+
"is_armstrong",
|
|
163
|
+
"is_perfect",
|
|
164
|
+
"get_all_properties",
|
|
165
|
+
"get_true_properties",
|
|
166
|
+
"print_properties",
|
|
167
|
+
"find_in_range",
|
|
168
|
+
"find_all_in_range",
|
|
169
|
+
"count_properties",
|
|
170
|
+
"most_special_in_range",
|
|
171
|
+
"classify",
|
|
172
|
+
"classify_batch",
|
|
173
|
+
"random_number",
|
|
174
|
+
"find_by_property",
|
|
175
|
+
"stream",
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
# Clean up internal names that leak into dir(nc)
|
|
179
|
+
del (_primes, _figurate, _digital, _recreational,
|
|
180
|
+
_divisors, _sequences, _powers, _number_theory,
|
|
181
|
+
_combinatorial, _core, _registry)
|
|
@@ -123,6 +123,9 @@ def register(
|
|
|
123
123
|
key = _normalize(name)
|
|
124
124
|
with _REGISTRY_LOCK:
|
|
125
125
|
REGISTRY[key] = entry
|
|
126
|
+
# Also register under the function's __name__ so
|
|
127
|
+
# nc.<func_name>() works via __getattr__ fallback
|
|
128
|
+
REGISTRY[_normalize(func.__name__)] = entry
|
|
126
129
|
for alias in _aliases:
|
|
127
130
|
REGISTRY[_normalize(alias)] = entry
|
|
128
131
|
return func
|
|
@@ -21,6 +21,9 @@ info <type_name>
|
|
|
21
21
|
|
|
22
22
|
list [--category <cat>]
|
|
23
23
|
List all registered types, optionally filtered by category.
|
|
24
|
+
|
|
25
|
+
compare <a> <b> [--json]
|
|
26
|
+
Compare two numbers: show shared and exclusive properties.
|
|
24
27
|
"""
|
|
25
28
|
|
|
26
29
|
from __future__ import annotations
|
|
@@ -345,6 +348,46 @@ def cmd_list(args: argparse.Namespace) -> None:
|
|
|
345
348
|
print()
|
|
346
349
|
|
|
347
350
|
|
|
351
|
+
def cmd_compare(args: argparse.Namespace) -> None:
|
|
352
|
+
"""Handle: numclassify compare <a> <b> [--json]."""
|
|
353
|
+
nc, _, _ = _lazy_import()
|
|
354
|
+
|
|
355
|
+
try:
|
|
356
|
+
a = int(args.a)
|
|
357
|
+
b = int(args.b)
|
|
358
|
+
except ValueError:
|
|
359
|
+
_die("a and b must be integers.")
|
|
360
|
+
|
|
361
|
+
props_a = set(nc.get_true_properties(a))
|
|
362
|
+
props_b = set(nc.get_true_properties(b))
|
|
363
|
+
|
|
364
|
+
shared = sorted(props_a & props_b)
|
|
365
|
+
only_a = sorted(props_a - props_b)
|
|
366
|
+
only_b = sorted(props_b - props_a)
|
|
367
|
+
|
|
368
|
+
if args.json:
|
|
369
|
+
payload = {
|
|
370
|
+
"a": a,
|
|
371
|
+
"b": b,
|
|
372
|
+
"shared": shared,
|
|
373
|
+
"only_a": only_a,
|
|
374
|
+
"only_b": only_b,
|
|
375
|
+
}
|
|
376
|
+
print(json.dumps(payload, indent=2))
|
|
377
|
+
return
|
|
378
|
+
|
|
379
|
+
header = f"Comparing {a} and {b}"
|
|
380
|
+
print(_bold(header))
|
|
381
|
+
print(_rule(len(header)))
|
|
382
|
+
|
|
383
|
+
def _fmt_list(items: List[str]) -> str:
|
|
384
|
+
return ", ".join(items) if items else "(none)"
|
|
385
|
+
|
|
386
|
+
print(f"Shared ({len(shared)}): {_fmt_list(shared)}")
|
|
387
|
+
print(f"Only in {a} ({len(only_a)}): {_fmt_list(only_a)}")
|
|
388
|
+
print(f"Only in {b} ({len(only_b)}): {_fmt_list(only_b)}")
|
|
389
|
+
|
|
390
|
+
|
|
348
391
|
# ---------------------------------------------------------------------------
|
|
349
392
|
# Argument parser
|
|
350
393
|
# ---------------------------------------------------------------------------
|
|
@@ -475,8 +518,28 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
475
518
|
)
|
|
476
519
|
p_list.set_defaults(func=cmd_list)
|
|
477
520
|
|
|
478
|
-
|
|
521
|
+
# -- compare -------------------------------------------------------------
|
|
522
|
+
p_compare = sub.add_parser(
|
|
523
|
+
"compare",
|
|
524
|
+
help="Compare properties of two numbers side by side.",
|
|
525
|
+
description=(
|
|
526
|
+
"Show shared and exclusive properties between two integers.\n\n"
|
|
527
|
+
"Examples:\n"
|
|
528
|
+
" numclassify compare 6 28\n"
|
|
529
|
+
" numclassify compare 6 28 --json"
|
|
530
|
+
),
|
|
531
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
532
|
+
)
|
|
533
|
+
p_compare.add_argument("a", help="First integer.")
|
|
534
|
+
p_compare.add_argument("b", help="Second integer.")
|
|
535
|
+
p_compare.add_argument(
|
|
536
|
+
"--json",
|
|
537
|
+
action="store_true",
|
|
538
|
+
help="Output results as JSON with keys a, b, shared, only_a, only_b.",
|
|
539
|
+
)
|
|
540
|
+
p_compare.set_defaults(func=cmd_compare)
|
|
479
541
|
|
|
542
|
+
return parser
|
|
480
543
|
|
|
481
544
|
# ---------------------------------------------------------------------------
|
|
482
545
|
# Entry point
|
|
@@ -500,7 +563,20 @@ def main() -> None:
|
|
|
500
563
|
)
|
|
501
564
|
|
|
502
565
|
# ------------------------------------------------------------------
|
|
503
|
-
# 2. Enable
|
|
566
|
+
# 2. Enable VT100 ANSI escape processing on Windows consoles.
|
|
567
|
+
# On older Windows 10 builds and Wine, ANSI codes are printed
|
|
568
|
+
# literally unless the console mode flag is set explicitly.
|
|
569
|
+
# ------------------------------------------------------------------
|
|
570
|
+
if sys.platform == "win32":
|
|
571
|
+
import ctypes
|
|
572
|
+
try:
|
|
573
|
+
kernel32 = ctypes.windll.kernel32
|
|
574
|
+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
575
|
+
except Exception:
|
|
576
|
+
pass
|
|
577
|
+
|
|
578
|
+
# ------------------------------------------------------------------
|
|
579
|
+
# 3. Enable colour + Unicode only when writing to a real terminal.
|
|
504
580
|
# Subprocesses/pipes get plain ASCII — no ✓, no ─, no ANSI codes.
|
|
505
581
|
# ------------------------------------------------------------------
|
|
506
582
|
if sys.stdout.isatty():
|
|
@@ -522,4 +598,4 @@ def main() -> None:
|
|
|
522
598
|
|
|
523
599
|
|
|
524
600
|
if __name__ == "__main__":
|
|
525
|
-
main()
|
|
601
|
+
main()
|
|
File without changes
|
|
@@ -4,15 +4,15 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "numclassify"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "The most comprehensive Python library for number classification
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "The most comprehensive Python library for number classification - 3000+ number types"
|
|
9
9
|
authors = [{name = "Aratrik Ghosh", email = "aratrikghosh2011@gmail.com"}]
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.8"
|
|
12
12
|
license = {text = "MIT"}
|
|
13
13
|
keywords = ["number-theory", "mathematics", "classification", "armstrong", "prime", "figurate"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
16
|
"Intended Audience :: Education",
|
|
17
17
|
"Intended Audience :: Science/Research",
|
|
18
18
|
"Topic :: Scientific/Engineering :: Mathematics",
|
|
@@ -22,6 +22,7 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.10",
|
|
23
23
|
"Programming Language :: Python :: 3.11",
|
|
24
24
|
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
25
26
|
"License :: OSI Approved :: MIT License",
|
|
26
27
|
"Operating System :: OS Independent",
|
|
27
28
|
]
|
|
@@ -29,7 +30,12 @@ classifiers = [
|
|
|
29
30
|
[project.urls]
|
|
30
31
|
Homepage = "https://github.com/aratrikghosh2011-tech/numclassify"
|
|
31
32
|
Repository = "https://github.com/aratrikghosh2011-tech/numclassify"
|
|
33
|
+
Documentation = "https://aratrikghosh2011-tech.github.io/numclassify/"
|
|
32
34
|
Issues = "https://github.com/aratrikghosh2011-tech/numclassify/issues"
|
|
33
35
|
|
|
34
36
|
[project.scripts]
|
|
35
|
-
numclassify = "numclassify.cli:main"
|
|
37
|
+
numclassify = "numclassify.cli:main"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["numclassify"]
|
|
41
|
+
include = ["numclassify/py.typed"]
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
numclassify
|
|
3
|
-
~~~~~~~~~~~
|
|
4
|
-
The most comprehensive Python library for number classification.
|
|
5
|
-
Importing this package triggers registration of all built-in classification
|
|
6
|
-
functions via their ``@register`` decorators.
|
|
7
|
-
|
|
8
|
-
Public API
|
|
9
|
-
----------
|
|
10
|
-
.. autosummary::
|
|
11
|
-
is_prime
|
|
12
|
-
is_armstrong
|
|
13
|
-
is_perfect
|
|
14
|
-
get_all_properties
|
|
15
|
-
get_true_properties
|
|
16
|
-
print_properties
|
|
17
|
-
find_in_range
|
|
18
|
-
find_all_in_range
|
|
19
|
-
count_properties
|
|
20
|
-
most_special_in_range
|
|
21
|
-
"""
|
|
22
|
-
from __future__ import annotations
|
|
23
|
-
|
|
24
|
-
__version__ = "0.1.0"
|
|
25
|
-
|
|
26
|
-
# --- Import all _core submodules so @register decorators fire at import time ---
|
|
27
|
-
from numclassify._core import primes # noqa: F401
|
|
28
|
-
from numclassify._core import figurate # noqa: F401
|
|
29
|
-
from numclassify._core import digital # noqa: F401
|
|
30
|
-
from numclassify._core import recreational # noqa: F401
|
|
31
|
-
from numclassify._core import divisors # noqa: F401
|
|
32
|
-
from numclassify._core import sequences # noqa: F401
|
|
33
|
-
from numclassify._core import powers # noqa: F401
|
|
34
|
-
from numclassify._core import number_theory # noqa: F401
|
|
35
|
-
from numclassify._core import combinatorial # noqa: F401
|
|
36
|
-
|
|
37
|
-
# --- Re-export key functions at top level ---
|
|
38
|
-
from numclassify._core.primes import is_prime # noqa: F401
|
|
39
|
-
from numclassify._core.digital import is_armstrong # noqa: F401
|
|
40
|
-
from numclassify._core.divisors import is_perfect # noqa: F401
|
|
41
|
-
from numclassify._registry import ( # noqa: F401
|
|
42
|
-
get_all_properties,
|
|
43
|
-
get_true_properties,
|
|
44
|
-
print_properties,
|
|
45
|
-
find_in_range,
|
|
46
|
-
find_all_in_range,
|
|
47
|
-
count_properties,
|
|
48
|
-
most_special_in_range,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
__all__ = [
|
|
52
|
-
"__version__",
|
|
53
|
-
"is_prime",
|
|
54
|
-
"is_armstrong",
|
|
55
|
-
"is_perfect",
|
|
56
|
-
"get_all_properties",
|
|
57
|
-
"get_true_properties",
|
|
58
|
-
"print_properties",
|
|
59
|
-
"find_in_range",
|
|
60
|
-
"find_all_in_range",
|
|
61
|
-
"count_properties",
|
|
62
|
-
"most_special_in_range",
|
|
63
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|