numclassify 0.1.0__py3-none-any.whl
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/__init__.py +63 -0
- numclassify/__main__.py +4 -0
- numclassify/_core/__init__.py +0 -0
- numclassify/_core/combinatorial.py +392 -0
- numclassify/_core/digital.py +403 -0
- numclassify/_core/divisors.py +756 -0
- numclassify/_core/figurate.py +357 -0
- numclassify/_core/number_theory.py +533 -0
- numclassify/_core/powers.py +349 -0
- numclassify/_core/primes.py +2100 -0
- numclassify/_core/recreational.py +245 -0
- numclassify/_core/sequences.py +488 -0
- numclassify/_registry.py +417 -0
- numclassify/cli.py +525 -0
- numclassify-0.1.0.dist-info/METADATA +220 -0
- numclassify-0.1.0.dist-info/RECORD +19 -0
- numclassify-0.1.0.dist-info/WHEEL +4 -0
- numclassify-0.1.0.dist-info/entry_points.txt +2 -0
- numclassify-0.1.0.dist-info/licenses/LICENSE +21 -0
numclassify/__init__.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
]
|
numclassify/__main__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numclassify/_core/combinatorial.py
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Combinatorial number classification functions.
|
|
5
|
+
|
|
6
|
+
All membership sets are precomputed at module load time up to 10^9
|
|
7
|
+
(or 50 terms, whichever comes first) for O(1) lookup.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Set
|
|
12
|
+
|
|
13
|
+
from numclassify._registry import register
|
|
14
|
+
|
|
15
|
+
_LIMIT = 10 ** 9
|
|
16
|
+
_MAX_TERMS = 50
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Precomputed sets
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
def _gen_factorials() -> Set[int]:
|
|
23
|
+
s: Set[int] = set()
|
|
24
|
+
k, val = 0, 1
|
|
25
|
+
while val <= _LIMIT and k <= _MAX_TERMS:
|
|
26
|
+
s.add(val)
|
|
27
|
+
k += 1
|
|
28
|
+
val *= k
|
|
29
|
+
return s
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _gen_double_factorials() -> Set[int]:
|
|
33
|
+
s: Set[int] = set()
|
|
34
|
+
# k!! = k*(k-2)*...
|
|
35
|
+
for k in range(0, 200):
|
|
36
|
+
val = 1
|
|
37
|
+
i = k
|
|
38
|
+
while i > 0:
|
|
39
|
+
val *= i
|
|
40
|
+
i -= 2
|
|
41
|
+
if val > _LIMIT:
|
|
42
|
+
break
|
|
43
|
+
s.add(val)
|
|
44
|
+
return s
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _gen_subfactorials() -> Set[int]:
|
|
48
|
+
s: Set[int] = set()
|
|
49
|
+
# !0=1, !1=0, !n=(n-1)*(!{n-1}+!{n-2})
|
|
50
|
+
a, b = 1, 0 # !0, !1
|
|
51
|
+
s.add(a)
|
|
52
|
+
s.add(b)
|
|
53
|
+
for n in range(2, _MAX_TERMS + 1):
|
|
54
|
+
c = (n - 1) * (a + b)
|
|
55
|
+
if c > _LIMIT:
|
|
56
|
+
break
|
|
57
|
+
s.add(c)
|
|
58
|
+
a, b = b, c
|
|
59
|
+
return s
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _is_prime_simple(n: int) -> bool:
|
|
63
|
+
if n < 2:
|
|
64
|
+
return False
|
|
65
|
+
if n < 4:
|
|
66
|
+
return True
|
|
67
|
+
if n % 2 == 0 or n % 3 == 0:
|
|
68
|
+
return False
|
|
69
|
+
f = 5
|
|
70
|
+
while f * f <= n:
|
|
71
|
+
if n % f == 0 or n % (f + 2) == 0:
|
|
72
|
+
return False
|
|
73
|
+
f += 6
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _gen_primorials() -> Set[int]:
|
|
78
|
+
s: Set[int] = set()
|
|
79
|
+
val = 1
|
|
80
|
+
n = 2
|
|
81
|
+
while True:
|
|
82
|
+
if _is_prime_simple(n):
|
|
83
|
+
val *= n
|
|
84
|
+
if val > _LIMIT:
|
|
85
|
+
break
|
|
86
|
+
s.add(val)
|
|
87
|
+
n += 1
|
|
88
|
+
return s
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _gen_central_binomials() -> Set[int]:
|
|
92
|
+
s: Set[int] = set()
|
|
93
|
+
# C(2k, k) for k = 0, 1, 2, ...
|
|
94
|
+
val = 1
|
|
95
|
+
k = 0
|
|
96
|
+
while val <= _LIMIT and k <= _MAX_TERMS:
|
|
97
|
+
s.add(val)
|
|
98
|
+
k += 1
|
|
99
|
+
# C(2k,k) = C(2(k-1),k-1) * 2*(2k-1)/k
|
|
100
|
+
val = val * 2 * (2 * k - 1) // k
|
|
101
|
+
return s
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _gen_binomial_coefficients() -> Set[int]:
|
|
105
|
+
s: Set[int] = set()
|
|
106
|
+
# Generate Pascal's triangle rows until values exceed _LIMIT
|
|
107
|
+
row = [1]
|
|
108
|
+
while True:
|
|
109
|
+
for v in row:
|
|
110
|
+
if v <= _LIMIT:
|
|
111
|
+
s.add(v)
|
|
112
|
+
next_row = [1]
|
|
113
|
+
overflow = True
|
|
114
|
+
for i in range(len(row) - 1):
|
|
115
|
+
nv = row[i] + row[i + 1]
|
|
116
|
+
next_row.append(nv)
|
|
117
|
+
if nv <= _LIMIT:
|
|
118
|
+
overflow = False
|
|
119
|
+
next_row.append(1)
|
|
120
|
+
if overflow and len(row) > 5:
|
|
121
|
+
break
|
|
122
|
+
row = next_row
|
|
123
|
+
if len(row) > 1000:
|
|
124
|
+
break
|
|
125
|
+
return s
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _gen_partition_numbers() -> Set[int]:
|
|
129
|
+
s: Set[int] = set()
|
|
130
|
+
# Use Euler's pentagonal number theorem
|
|
131
|
+
# p(n) = sum over k != 0 of (-1)^(k+1) * p(n - k*(3k-1)/2)
|
|
132
|
+
# Build iteratively
|
|
133
|
+
partitions = [1] # p(0) = 1
|
|
134
|
+
s.add(1)
|
|
135
|
+
n = 1
|
|
136
|
+
while True:
|
|
137
|
+
total = 0
|
|
138
|
+
k = 1
|
|
139
|
+
while True:
|
|
140
|
+
pent1 = k * (3 * k - 1) // 2
|
|
141
|
+
pent2 = k * (3 * k + 1) // 2
|
|
142
|
+
if pent1 > n:
|
|
143
|
+
break
|
|
144
|
+
sign = (-1) ** (k + 1)
|
|
145
|
+
total += sign * partitions[n - pent1]
|
|
146
|
+
if pent2 <= n:
|
|
147
|
+
total += sign * partitions[n - pent2]
|
|
148
|
+
k += 1
|
|
149
|
+
partitions.append(total)
|
|
150
|
+
if total > _LIMIT:
|
|
151
|
+
break
|
|
152
|
+
s.add(total)
|
|
153
|
+
n += 1
|
|
154
|
+
if n > 10000:
|
|
155
|
+
break
|
|
156
|
+
return s
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _gen_euler_numbers() -> Set[int]:
|
|
160
|
+
# Euler numbers E_0=1, E_2=-1, E_4=5, E_6=-61, ... (all odd-indexed = 0)
|
|
161
|
+
# We store absolute values of non-zero ones: 1, 1, 5, 61, 1385, ...
|
|
162
|
+
s: Set[int] = set()
|
|
163
|
+
# Use the recurrence via the Euler number triangle
|
|
164
|
+
# E_n = 0 for odd n; for even n use: sum formula
|
|
165
|
+
# Simple approach: build the alternating permutation triangle
|
|
166
|
+
T = [[0] * 100 for _ in range(100)]
|
|
167
|
+
T[1][1] = 1
|
|
168
|
+
s.add(1)
|
|
169
|
+
for i in range(2, 30):
|
|
170
|
+
if i % 2 == 0:
|
|
171
|
+
T[i][1] = T[i - 1][1]
|
|
172
|
+
for j in range(2, i + 1):
|
|
173
|
+
T[i][j] = T[i][j - 1] + T[i - 1][i - j + 1]
|
|
174
|
+
else:
|
|
175
|
+
T[i][i] = T[i - 1][i - 1]
|
|
176
|
+
for j in range(i - 1, 0, -1):
|
|
177
|
+
T[i][j] = T[i][j + 1] + T[i - 1][j]
|
|
178
|
+
val = T[i][1] if i % 2 == 0 else T[i][i]
|
|
179
|
+
if val > _LIMIT:
|
|
180
|
+
break
|
|
181
|
+
if val > 0:
|
|
182
|
+
s.add(val)
|
|
183
|
+
return s
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# Known Bernoulli numerators (absolute values) — partial list
|
|
187
|
+
_BERNOULLI_NUMERATORS: Set[int] = {
|
|
188
|
+
1, 1, 1, 1, 1, 5, 691, 7, 3617, 43867, 174611, 854513,
|
|
189
|
+
236364091, 8553103, 23749461029, 8615841276005, 7709321041217,
|
|
190
|
+
2577687858367
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _gen_catalan_triangle() -> Set[int]:
|
|
195
|
+
s: Set[int] = set()
|
|
196
|
+
# T(n,k) = C(n+k, k) - C(n+k, k-1) for 0 <= k <= n
|
|
197
|
+
# T(n,0)=1 for all n; T(n,n) = Catalan(n)
|
|
198
|
+
for n in range(0, 60):
|
|
199
|
+
for k in range(0, n + 1):
|
|
200
|
+
from math import comb
|
|
201
|
+
val = comb(n + k, k) - (comb(n + k, k - 1) if k > 0 else 0)
|
|
202
|
+
if val > _LIMIT:
|
|
203
|
+
break
|
|
204
|
+
s.add(val)
|
|
205
|
+
if n > 0:
|
|
206
|
+
from math import comb
|
|
207
|
+
check = comb(2 * n, n) - comb(2 * n, n - 1) if n > 0 else 1
|
|
208
|
+
if check > _LIMIT:
|
|
209
|
+
break
|
|
210
|
+
return s
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# Build at module load time
|
|
214
|
+
_FACTORIALS: Set[int] = _gen_factorials()
|
|
215
|
+
_DOUBLE_FACTORIALS: Set[int] = _gen_double_factorials()
|
|
216
|
+
_SUBFACTORIALS: Set[int] = _gen_subfactorials()
|
|
217
|
+
_PRIMORIALS: Set[int] = _gen_primorials()
|
|
218
|
+
_CENTRAL_BINOMIALS: Set[int] = _gen_central_binomials()
|
|
219
|
+
_BINOMIAL_COEFFICIENTS: Set[int] = _gen_binomial_coefficients()
|
|
220
|
+
_PARTITION_NUMBERS: Set[int] = _gen_partition_numbers()
|
|
221
|
+
_EULER_NUMBERS: Set[int] = _gen_euler_numbers()
|
|
222
|
+
_CATALAN_TRIANGLE: Set[int] = _gen_catalan_triangle()
|
|
223
|
+
|
|
224
|
+
# ---------------------------------------------------------------------------
|
|
225
|
+
# Registered classifiers
|
|
226
|
+
# ---------------------------------------------------------------------------
|
|
227
|
+
|
|
228
|
+
@register(name="Factorial", category="combinatorial", oeis="A000142",
|
|
229
|
+
description="n = k! for some non-negative integer k.")
|
|
230
|
+
def is_factorial(n: int) -> bool:
|
|
231
|
+
"""Return True if n is a factorial number.
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
n : int
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
bool
|
|
240
|
+
|
|
241
|
+
Examples
|
|
242
|
+
--------
|
|
243
|
+
>>> is_factorial(24)
|
|
244
|
+
True
|
|
245
|
+
>>> is_factorial(25)
|
|
246
|
+
False
|
|
247
|
+
"""
|
|
248
|
+
return n >= 0 and n in _FACTORIALS
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@register(name="Double Factorial", category="combinatorial", oeis="A006882",
|
|
252
|
+
description="n = k!! for some non-negative integer k.")
|
|
253
|
+
def is_double_factorial(n: int) -> bool:
|
|
254
|
+
"""Return True if n is a double factorial number.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
n : int
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
bool
|
|
263
|
+
"""
|
|
264
|
+
return n >= 0 and n in _DOUBLE_FACTORIALS
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@register(name="Subfactorial", category="combinatorial", oeis="A000166",
|
|
268
|
+
description="n = !k (number of derangements of k elements).")
|
|
269
|
+
def is_subfactorial(n: int) -> bool:
|
|
270
|
+
"""Return True if n is a subfactorial (derangement number).
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
n : int
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
bool
|
|
279
|
+
"""
|
|
280
|
+
return n >= 0 and n in _SUBFACTORIALS
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@register(name="Primorial", category="combinatorial", oeis="A002110",
|
|
284
|
+
description="n = p# (product of all primes up to prime p).")
|
|
285
|
+
def is_primorial(n: int) -> bool:
|
|
286
|
+
"""Return True if n is a primorial.
|
|
287
|
+
|
|
288
|
+
Parameters
|
|
289
|
+
----------
|
|
290
|
+
n : int
|
|
291
|
+
|
|
292
|
+
Returns
|
|
293
|
+
-------
|
|
294
|
+
bool
|
|
295
|
+
"""
|
|
296
|
+
return n >= 1 and n in _PRIMORIALS
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@register(name="Central Binomial Coefficient", category="combinatorial", oeis="A000984",
|
|
300
|
+
description="n = C(2k, k) for some non-negative integer k.")
|
|
301
|
+
def is_central_binomial(n: int) -> bool:
|
|
302
|
+
"""Return True if n is a central binomial coefficient C(2k, k).
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
n : int
|
|
307
|
+
|
|
308
|
+
Returns
|
|
309
|
+
-------
|
|
310
|
+
bool
|
|
311
|
+
"""
|
|
312
|
+
return n >= 0 and n in _CENTRAL_BINOMIALS
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@register(name="Binomial Coefficient", category="combinatorial", oeis="A007318",
|
|
316
|
+
description="n = C(a, b) for some integers a >= b >= 0.")
|
|
317
|
+
def is_binomial_coefficient(n: int) -> bool:
|
|
318
|
+
"""Return True if n appears in Pascal's triangle.
|
|
319
|
+
|
|
320
|
+
Parameters
|
|
321
|
+
----------
|
|
322
|
+
n : int
|
|
323
|
+
|
|
324
|
+
Returns
|
|
325
|
+
-------
|
|
326
|
+
bool
|
|
327
|
+
"""
|
|
328
|
+
return n >= 0 and n in _BINOMIAL_COEFFICIENTS
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@register(name="Partition Number", category="combinatorial", oeis="A000041",
|
|
332
|
+
description="n = p(k), the number of integer partitions of k.")
|
|
333
|
+
def is_partition_number(n: int) -> bool:
|
|
334
|
+
"""Return True if n is an integer partition number.
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
n : int
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
bool
|
|
343
|
+
"""
|
|
344
|
+
return n >= 0 and n in _PARTITION_NUMBERS
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
@register(name="Euler Number", category="combinatorial", oeis="A122045",
|
|
348
|
+
description="n is an absolute value of an Euler number.")
|
|
349
|
+
def is_euler_number(n: int) -> bool:
|
|
350
|
+
"""Return True if n is (the absolute value of) an Euler number.
|
|
351
|
+
|
|
352
|
+
Parameters
|
|
353
|
+
----------
|
|
354
|
+
n : int
|
|
355
|
+
|
|
356
|
+
Returns
|
|
357
|
+
-------
|
|
358
|
+
bool
|
|
359
|
+
"""
|
|
360
|
+
return n >= 0 and n in _EULER_NUMBERS
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@register(name="Bernoulli Numerator", category="combinatorial",
|
|
364
|
+
description="n appears as the numerator of a Bernoulli number (partial list).")
|
|
365
|
+
def is_bernoulli_numerator(n: int) -> bool:
|
|
366
|
+
"""Return True if n appears as a Bernoulli number numerator (partial known list).
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
n : int
|
|
371
|
+
|
|
372
|
+
Returns
|
|
373
|
+
-------
|
|
374
|
+
bool
|
|
375
|
+
"""
|
|
376
|
+
return n >= 0 and n in _BERNOULLI_NUMERATORS
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
@register(name="Catalan Triangle", category="combinatorial", oeis="A009766",
|
|
380
|
+
description="n appears in Catalan's triangle T(n,k) = C(n+k,k) - C(n+k,k-1).")
|
|
381
|
+
def is_catalan_triangle(n: int) -> bool:
|
|
382
|
+
"""Return True if n appears in Catalan's triangle.
|
|
383
|
+
|
|
384
|
+
Parameters
|
|
385
|
+
----------
|
|
386
|
+
n : int
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
bool
|
|
391
|
+
"""
|
|
392
|
+
return n >= 0 and n in _CATALAN_TRIANGLE
|