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,349 @@
1
+ """
2
+ numclassify/_core/powers.py
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ Power-based number classification functions.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import math
9
+ from typing import Set
10
+
11
+ from numclassify._registry import register
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Helper (NOT registered)
15
+ # ---------------------------------------------------------------------------
16
+
17
+ def is_perfect_power_check(n: int, exp: int) -> bool:
18
+ """Return True if n is a perfect exp-th power (n = k^exp for integer k > 0).
19
+
20
+ Parameters
21
+ ----------
22
+ n : int
23
+ exp : int
24
+
25
+ Returns
26
+ -------
27
+ bool
28
+ """
29
+ if n < 0 or exp < 2:
30
+ return False
31
+ if n == 0 or n == 1:
32
+ return True
33
+ root = round(n ** (1.0 / exp))
34
+ for r in range(max(1, root - 2), root + 3):
35
+ if r ** exp == n:
36
+ return True
37
+ return False
38
+
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Registered classifiers
42
+ # ---------------------------------------------------------------------------
43
+
44
+ @register(name="Perfect Square", category="powers", oeis="A000290",
45
+ description="n = k^2 for some non-negative integer k.")
46
+ def is_perfect_square(n: int) -> bool:
47
+ """Return True if n is a perfect square.
48
+
49
+ Parameters
50
+ ----------
51
+ n : int
52
+
53
+ Returns
54
+ -------
55
+ bool
56
+
57
+ Examples
58
+ --------
59
+ >>> is_perfect_square(16)
60
+ True
61
+ >>> is_perfect_square(15)
62
+ False
63
+ """
64
+ if n < 0:
65
+ return False
66
+ r = math.isqrt(n)
67
+ return r * r == n
68
+
69
+
70
+ @register(name="Perfect Cube", category="powers", oeis="A000578",
71
+ description="n = k^3 for some positive integer k.")
72
+ def is_perfect_cube(n: int) -> bool:
73
+ """Return True if n is a perfect cube.
74
+
75
+ Parameters
76
+ ----------
77
+ n : int
78
+
79
+ Returns
80
+ -------
81
+ bool
82
+
83
+ Examples
84
+ --------
85
+ >>> is_perfect_cube(27)
86
+ True
87
+ >>> is_perfect_cube(26)
88
+ False
89
+ """
90
+ if n < 0:
91
+ return False
92
+ return is_perfect_power_check(n, 3)
93
+
94
+
95
+ @register(name="Perfect Fourth Power", category="powers", oeis="A000583",
96
+ description="n = k^4 for some positive integer k.")
97
+ def is_perfect_fourth(n: int) -> bool:
98
+ """Return True if n is a perfect fourth power.
99
+
100
+ Parameters
101
+ ----------
102
+ n : int
103
+
104
+ Returns
105
+ -------
106
+ bool
107
+ """
108
+ if n < 0:
109
+ return False
110
+ return is_perfect_power_check(n, 4)
111
+
112
+
113
+ @register(name="Perfect Fifth Power", category="powers", oeis="A000584",
114
+ description="n = k^5 for some positive integer k.")
115
+ def is_perfect_fifth(n: int) -> bool:
116
+ """Return True if n is a perfect fifth power.
117
+
118
+ Parameters
119
+ ----------
120
+ n : int
121
+
122
+ Returns
123
+ -------
124
+ bool
125
+ """
126
+ if n < 0:
127
+ return False
128
+ return is_perfect_power_check(n, 5)
129
+
130
+
131
+ @register(name="Perfect Power", category="powers", oeis="A001597",
132
+ description="n = k^m for some integers k > 1, m > 1.")
133
+ def is_perfect_power(n: int) -> bool:
134
+ """Return True if n is a perfect power (k^m, k>1, m>1).
135
+
136
+ Parameters
137
+ ----------
138
+ n : int
139
+
140
+ Returns
141
+ -------
142
+ bool
143
+ """
144
+ if n <= 1:
145
+ return False
146
+ for exp in range(2, n.bit_length() + 1):
147
+ if is_perfect_power_check(n, exp):
148
+ return True
149
+ return False
150
+
151
+
152
+ @register(name="Sum of Two Squares", category="powers", oeis="A001481",
153
+ description="n = a^2 + b^2 for non-negative integers a, b.")
154
+ def is_sum_of_two_squares(n: int) -> bool:
155
+ """Return True if n can be expressed as the sum of two squares.
156
+
157
+ Parameters
158
+ ----------
159
+ n : int
160
+
161
+ Returns
162
+ -------
163
+ bool
164
+
165
+ Examples
166
+ --------
167
+ >>> is_sum_of_two_squares(5)
168
+ True
169
+ >>> is_sum_of_two_squares(3)
170
+ False
171
+ """
172
+ if n < 0:
173
+ return False
174
+ a = 0
175
+ while a * a <= n:
176
+ remainder = n - a * a
177
+ b = math.isqrt(remainder)
178
+ if b * b == remainder:
179
+ return True
180
+ a += 1
181
+ return False
182
+
183
+
184
+ @register(name="Sum of Two Cubes", category="powers", oeis="A003325",
185
+ description="n = a^3 + b^3 for positive integers a, b.")
186
+ def is_sum_of_two_cubes(n: int) -> bool:
187
+ """Return True if n can be expressed as the sum of two positive cubes.
188
+
189
+ Parameters
190
+ ----------
191
+ n : int
192
+
193
+ Returns
194
+ -------
195
+ bool
196
+ """
197
+ if n < 2:
198
+ return False
199
+ a = 1
200
+ while a ** 3 < n:
201
+ remainder = n - a ** 3
202
+ b = round(remainder ** (1.0 / 3))
203
+ for r in range(max(1, b - 1), b + 2):
204
+ if r ** 3 == remainder:
205
+ return True
206
+ a += 1
207
+ return False
208
+
209
+
210
+ @register(name="Sum of Three Squares", category="powers", oeis="A000443",
211
+ description="n = a^2 + b^2 + c^2 (by Legendre's three-square theorem).")
212
+ def is_sum_of_three_squares(n: int) -> bool:
213
+ """Return True if n can be expressed as the sum of three squares.
214
+
215
+ Uses Legendre's three-square theorem: n is NOT expressible iff
216
+ n = 4^a * (8b + 7) for non-negative integers a, b.
217
+
218
+ Parameters
219
+ ----------
220
+ n : int
221
+
222
+ Returns
223
+ -------
224
+ bool
225
+ """
226
+ if n < 0:
227
+ return False
228
+ # Remove factors of 4
229
+ while n % 4 == 0:
230
+ n //= 4
231
+ return n % 8 != 7
232
+
233
+
234
+ @register(name="Taxicab", category="powers", oeis="A001235",
235
+ description="Expressible as sum of two cubes in at least 2 different ways.")
236
+ def is_taxicab(n: int) -> bool:
237
+ """Return True if n can be expressed as the sum of two positive cubes in ≥ 2 ways.
238
+
239
+ Parameters
240
+ ----------
241
+ n : int
242
+
243
+ Returns
244
+ -------
245
+ bool
246
+
247
+ Examples
248
+ --------
249
+ >>> is_taxicab(1729)
250
+ True
251
+ """
252
+ if n < 2:
253
+ return False
254
+ ways = 0
255
+ a = 1
256
+ while a ** 3 < n:
257
+ remainder = n - a ** 3
258
+ b = round(remainder ** (1.0 / 3))
259
+ for r in range(max(1, b - 1), b + 2):
260
+ if r >= a and r ** 3 == remainder:
261
+ ways += 1
262
+ break
263
+ a += 1
264
+ return ways >= 2
265
+
266
+
267
+ @register(name="Power of 2", category="powers", oeis="A000079",
268
+ description="n = 2^k for some non-negative integer k.")
269
+ def is_power_of_2(n: int) -> bool:
270
+ """Return True if n is a power of 2.
271
+
272
+ Parameters
273
+ ----------
274
+ n : int
275
+
276
+ Returns
277
+ -------
278
+ bool
279
+ """
280
+ return n > 0 and (n & (n - 1)) == 0
281
+
282
+
283
+ @register(name="Power of 3", category="powers", oeis="A000244",
284
+ description="n = 3^k for some non-negative integer k.")
285
+ def is_power_of_3(n: int) -> bool:
286
+ """Return True if n is a power of 3.
287
+
288
+ Parameters
289
+ ----------
290
+ n : int
291
+
292
+ Returns
293
+ -------
294
+ bool
295
+ """
296
+ if n < 1:
297
+ return False
298
+ while n % 3 == 0:
299
+ n //= 3
300
+ return n == 1
301
+
302
+
303
+ @register(name="Power of 10", category="powers", oeis="A011557",
304
+ description="n = 10^k for some non-negative integer k.")
305
+ def is_power_of_10(n: int) -> bool:
306
+ """Return True if n is a power of 10.
307
+
308
+ Parameters
309
+ ----------
310
+ n : int
311
+
312
+ Returns
313
+ -------
314
+ bool
315
+ """
316
+ if n < 1:
317
+ return False
318
+ while n % 10 == 0:
319
+ n //= 10
320
+ return n == 1
321
+
322
+
323
+ @register(name="Sum of Squares of Primes", category="powers",
324
+ description="n = p1^2 + p2^2 for primes p1, p2.")
325
+ def is_sum_of_squares_of_primes(n: int) -> bool:
326
+ """Return True if n = p1^2 + p2^2 for primes p1, p2.
327
+
328
+ Parameters
329
+ ----------
330
+ n : int
331
+
332
+ Returns
333
+ -------
334
+ bool
335
+ """
336
+ if n < 4:
337
+ return False
338
+ from numclassify._core.primes import is_prime
339
+ limit = math.isqrt(n)
340
+ p1 = 2
341
+ while p1 <= limit:
342
+ if is_prime(p1):
343
+ remainder = n - p1 * p1
344
+ if remainder > 0:
345
+ p2 = math.isqrt(remainder)
346
+ if p2 * p2 == remainder and is_prime(p2):
347
+ return True
348
+ p1 += 1 if p1 == 2 else 2
349
+ return False