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.
@@ -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
+ ]
@@ -0,0 +1,4 @@
1
+ """Allow ``python -m numclassify`` to invoke the CLI."""
2
+ from numclassify.cli import main
3
+
4
+ main()
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