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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numclassify._core.recreational
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Recreational / curiosity number classifications.
|
|
5
|
+
|
|
6
|
+
All functions are registered via ``@register`` and exposed as plain
|
|
7
|
+
module-level callables.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from numclassify._registry import register
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@register(
|
|
16
|
+
name="Kaprekar",
|
|
17
|
+
category="recreational",
|
|
18
|
+
oeis="A006886",
|
|
19
|
+
description=(
|
|
20
|
+
"A number n such that n² can be split into two parts (no leading zero "
|
|
21
|
+
"on the right part) that sum to n. n=1 is included by convention."
|
|
22
|
+
),
|
|
23
|
+
aliases=["kaprekar number"],
|
|
24
|
+
)
|
|
25
|
+
def is_kaprekar(n: int) -> bool:
|
|
26
|
+
"""Return ``True`` if *n* is a Kaprekar number.
|
|
27
|
+
|
|
28
|
+
A positive integer *n* is Kaprekar if its square ``n²`` can be partitioned
|
|
29
|
+
at some point into a left part *A* and a right part *B* (where *B* > 0 and
|
|
30
|
+
has no leading zero) such that ``A + B == n``.
|
|
31
|
+
|
|
32
|
+
Special case: *n* = 1 is Kaprekar by convention (1² = 1, split as 0 + 1).
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
n:
|
|
37
|
+
Positive integer.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
bool
|
|
42
|
+
|
|
43
|
+
Example
|
|
44
|
+
-------
|
|
45
|
+
>>> is_kaprekar(45) # 45²=2025; split (20, 25) → 20+25=45
|
|
46
|
+
True
|
|
47
|
+
>>> is_kaprekar(9) # 9²=81; split (8, 1) → 8+1=9
|
|
48
|
+
True
|
|
49
|
+
>>> is_kaprekar(1)
|
|
50
|
+
True
|
|
51
|
+
>>> is_kaprekar(100)
|
|
52
|
+
False
|
|
53
|
+
"""
|
|
54
|
+
if n <= 0:
|
|
55
|
+
return False
|
|
56
|
+
if n == 1:
|
|
57
|
+
return True
|
|
58
|
+
sq = n * n
|
|
59
|
+
sq_str = str(sq)
|
|
60
|
+
length = len(sq_str)
|
|
61
|
+
for split in range(1, length):
|
|
62
|
+
left_str = sq_str[:length - split]
|
|
63
|
+
right_str = sq_str[length - split:]
|
|
64
|
+
# Right part must not start with '0' (leading zero check)
|
|
65
|
+
if right_str[0] == "0":
|
|
66
|
+
continue
|
|
67
|
+
left = int(left_str) if left_str else 0
|
|
68
|
+
right = int(right_str)
|
|
69
|
+
if right > 0 and left + right == n:
|
|
70
|
+
return True
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@register(
|
|
75
|
+
name="Automorphic",
|
|
76
|
+
category="recreational",
|
|
77
|
+
oeis="A003226",
|
|
78
|
+
description="A number whose square ends in the number itself.",
|
|
79
|
+
aliases=["automorphic number"],
|
|
80
|
+
)
|
|
81
|
+
def is_automorphic(n: int) -> bool:
|
|
82
|
+
"""Return ``True`` if *n* is an automorphic number.
|
|
83
|
+
|
|
84
|
+
An automorphic number satisfies: the last *k* digits of *n²* equal *n*,
|
|
85
|
+
where *k* is the number of digits in *n*.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
n:
|
|
90
|
+
Non-negative integer.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
bool
|
|
95
|
+
|
|
96
|
+
Example
|
|
97
|
+
-------
|
|
98
|
+
>>> is_automorphic(5) # 5²=25, ends in 5
|
|
99
|
+
True
|
|
100
|
+
>>> is_automorphic(6) # 6²=36, ends in 6
|
|
101
|
+
True
|
|
102
|
+
>>> is_automorphic(76) # 76²=5776, ends in 76
|
|
103
|
+
True
|
|
104
|
+
>>> is_automorphic(7) # 7²=49, does not end in 7
|
|
105
|
+
False
|
|
106
|
+
"""
|
|
107
|
+
if n < 0:
|
|
108
|
+
return False
|
|
109
|
+
sq = n * n
|
|
110
|
+
n_str = str(n)
|
|
111
|
+
sq_str = str(sq)
|
|
112
|
+
return sq_str.endswith(n_str)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@register(
|
|
116
|
+
name="Palindrome",
|
|
117
|
+
category="recreational",
|
|
118
|
+
oeis="A002113",
|
|
119
|
+
description=(
|
|
120
|
+
"A number whose decimal representation reads the same forwards and "
|
|
121
|
+
"backwards."
|
|
122
|
+
),
|
|
123
|
+
aliases=["palindromic", "palindromic number"],
|
|
124
|
+
)
|
|
125
|
+
def is_palindrome(n: int) -> bool:
|
|
126
|
+
"""Return ``True`` if *n* is a palindromic number.
|
|
127
|
+
|
|
128
|
+
A palindromic number reads the same in both directions in base-10.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
n:
|
|
133
|
+
Non-negative integer.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
bool
|
|
138
|
+
|
|
139
|
+
Example
|
|
140
|
+
-------
|
|
141
|
+
>>> is_palindrome(121)
|
|
142
|
+
True
|
|
143
|
+
>>> is_palindrome(1221)
|
|
144
|
+
True
|
|
145
|
+
>>> is_palindrome(123)
|
|
146
|
+
False
|
|
147
|
+
"""
|
|
148
|
+
if n < 0:
|
|
149
|
+
return False
|
|
150
|
+
s = str(n)
|
|
151
|
+
return s == s[::-1]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@register(
|
|
155
|
+
name="Strobogrammatic",
|
|
156
|
+
category="recreational",
|
|
157
|
+
oeis="",
|
|
158
|
+
description=(
|
|
159
|
+
"A number that looks the same when rotated 180°. "
|
|
160
|
+
"Valid digits are 0, 1, 6, 8, 9 with map 0→0, 1→1, 6→9, 8→8, 9→6."
|
|
161
|
+
),
|
|
162
|
+
aliases=["strobogrammatic number"],
|
|
163
|
+
)
|
|
164
|
+
def is_strobogrammatic(n: int) -> bool:
|
|
165
|
+
"""Return ``True`` if *n* is a strobogrammatic number.
|
|
166
|
+
|
|
167
|
+
A strobogrammatic number appears the same when rotated 180°. Only the
|
|
168
|
+
digits 0, 1, 6, 8, 9 are valid; their rotated equivalents are
|
|
169
|
+
0→0, 1→1, 6→9, 8→8, 9→6. The rotated string is the reverse of the
|
|
170
|
+
mapped digits.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
n:
|
|
175
|
+
Non-negative integer.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
bool
|
|
180
|
+
|
|
181
|
+
Example
|
|
182
|
+
-------
|
|
183
|
+
>>> is_strobogrammatic(69) # 69 rotated → 96... wait: 6→9, 9→6; reversed: '96' → same as '96' ✓
|
|
184
|
+
True
|
|
185
|
+
>>> is_strobogrammatic(88)
|
|
186
|
+
True
|
|
187
|
+
>>> is_strobogrammatic(1)
|
|
188
|
+
True
|
|
189
|
+
>>> is_strobogrammatic(6) # 6 alone rotated → '9' ≠ '6'
|
|
190
|
+
False
|
|
191
|
+
"""
|
|
192
|
+
if n < 0:
|
|
193
|
+
return False
|
|
194
|
+
rotate_map = {"0": "0", "1": "1", "6": "9", "8": "8", "9": "6"}
|
|
195
|
+
s = str(n)
|
|
196
|
+
for ch in s:
|
|
197
|
+
if ch not in rotate_map:
|
|
198
|
+
return False
|
|
199
|
+
rotated = "".join(rotate_map[ch] for ch in reversed(s))
|
|
200
|
+
return rotated == s
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@register(
|
|
204
|
+
name="Bouncy",
|
|
205
|
+
category="recreational",
|
|
206
|
+
oeis="A152054",
|
|
207
|
+
description=(
|
|
208
|
+
"A positive integer whose digits are neither all non-decreasing nor "
|
|
209
|
+
"all non-increasing."
|
|
210
|
+
),
|
|
211
|
+
aliases=["bouncy number"],
|
|
212
|
+
)
|
|
213
|
+
def is_bouncy(n: int) -> bool:
|
|
214
|
+
"""Return ``True`` if *n* is a bouncy number.
|
|
215
|
+
|
|
216
|
+
A bouncy number has digits that are neither monotonically non-decreasing
|
|
217
|
+
nor monotonically non-increasing. Numbers with fewer than three digits
|
|
218
|
+
can never be bouncy.
|
|
219
|
+
|
|
220
|
+
Parameters
|
|
221
|
+
----------
|
|
222
|
+
n:
|
|
223
|
+
Positive integer.
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
bool
|
|
228
|
+
|
|
229
|
+
Example
|
|
230
|
+
-------
|
|
231
|
+
>>> is_bouncy(155349)
|
|
232
|
+
True
|
|
233
|
+
>>> is_bouncy(134468) # non-decreasing → not bouncy
|
|
234
|
+
False
|
|
235
|
+
>>> is_bouncy(66420) # non-increasing → not bouncy
|
|
236
|
+
False
|
|
237
|
+
"""
|
|
238
|
+
if n <= 0:
|
|
239
|
+
return False
|
|
240
|
+
digits = [int(d) for d in str(n)]
|
|
241
|
+
if len(digits) < 3:
|
|
242
|
+
return False
|
|
243
|
+
increasing = any(digits[i] < digits[i + 1] for i in range(len(digits) - 1))
|
|
244
|
+
decreasing = any(digits[i] > digits[i + 1] for i in range(len(digits) - 1))
|
|
245
|
+
return increasing and decreasing
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numclassify/_core/sequences.py
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Sequence-membership number classification functions.
|
|
5
|
+
|
|
6
|
+
All sets are precomputed at module load time up to 10^9 for O(1) lookup.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Set
|
|
11
|
+
|
|
12
|
+
from numclassify._registry import register
|
|
13
|
+
|
|
14
|
+
_LIMIT = 10 ** 9
|
|
15
|
+
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# Precomputed sets
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
def _gen_fibonacci() -> Set[int]:
|
|
21
|
+
s: Set[int] = set()
|
|
22
|
+
a, b = 0, 1
|
|
23
|
+
while a <= _LIMIT:
|
|
24
|
+
s.add(a)
|
|
25
|
+
a, b = b, a + b
|
|
26
|
+
return s
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _gen_lucas() -> Set[int]:
|
|
30
|
+
s: Set[int] = set()
|
|
31
|
+
a, b = 2, 1
|
|
32
|
+
while a <= _LIMIT:
|
|
33
|
+
s.add(a)
|
|
34
|
+
a, b = b, a + b
|
|
35
|
+
return s
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _gen_tribonacci() -> Set[int]:
|
|
39
|
+
s: Set[int] = set()
|
|
40
|
+
a, b, c = 0, 0, 1
|
|
41
|
+
while a <= _LIMIT:
|
|
42
|
+
s.add(a)
|
|
43
|
+
a, b, c = b, c, a + b + c
|
|
44
|
+
return s
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _gen_tetranacci() -> Set[int]:
|
|
48
|
+
s: Set[int] = set()
|
|
49
|
+
a, b, c, d = 0, 0, 0, 1
|
|
50
|
+
while a <= _LIMIT:
|
|
51
|
+
s.add(a)
|
|
52
|
+
a, b, c, d = b, c, d, a + b + c + d
|
|
53
|
+
return s
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _gen_pell() -> Set[int]:
|
|
57
|
+
s: Set[int] = set()
|
|
58
|
+
a, b = 0, 1
|
|
59
|
+
while a <= _LIMIT:
|
|
60
|
+
s.add(a)
|
|
61
|
+
a, b = b, 2 * b + a
|
|
62
|
+
return s
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _gen_jacobsthal() -> Set[int]:
|
|
66
|
+
s: Set[int] = set()
|
|
67
|
+
a, b = 0, 1
|
|
68
|
+
while a <= _LIMIT:
|
|
69
|
+
s.add(a)
|
|
70
|
+
a, b = b, b + 2 * a
|
|
71
|
+
return s
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _gen_padovan() -> Set[int]:
|
|
75
|
+
s: Set[int] = set()
|
|
76
|
+
# P(0)=1, P(1)=1, P(2)=1, P(n)=P(n-2)+P(n-3)
|
|
77
|
+
seq = [1, 1, 1]
|
|
78
|
+
for v in seq:
|
|
79
|
+
s.add(v)
|
|
80
|
+
while True:
|
|
81
|
+
nxt = seq[-2] + seq[-3]
|
|
82
|
+
if nxt > _LIMIT:
|
|
83
|
+
break
|
|
84
|
+
s.add(nxt)
|
|
85
|
+
seq.append(nxt)
|
|
86
|
+
return s
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _gen_perrin() -> Set[int]:
|
|
90
|
+
s: Set[int] = set()
|
|
91
|
+
# P(0)=3,P(1)=0,P(2)=2, P(n)=P(n-2)+P(n-3)
|
|
92
|
+
seq = [3, 0, 2]
|
|
93
|
+
for v in seq:
|
|
94
|
+
s.add(v)
|
|
95
|
+
while True:
|
|
96
|
+
nxt = seq[-2] + seq[-3]
|
|
97
|
+
if nxt > _LIMIT:
|
|
98
|
+
break
|
|
99
|
+
s.add(nxt)
|
|
100
|
+
seq.append(nxt)
|
|
101
|
+
return s
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _gen_catalan() -> Set[int]:
|
|
105
|
+
s: Set[int] = set()
|
|
106
|
+
# C(n) = C(2n,n)/(n+1); iterative: C(0)=1, C(n+1)=C(n)*2*(2n+1)/(n+2)
|
|
107
|
+
from fractions import Fraction
|
|
108
|
+
c = Fraction(1)
|
|
109
|
+
n = 0
|
|
110
|
+
while int(c) <= _LIMIT:
|
|
111
|
+
s.add(int(c))
|
|
112
|
+
c = c * 2 * (2 * n + 1) // (n + 2)
|
|
113
|
+
n += 1
|
|
114
|
+
return s
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _gen_bell() -> Set[int]:
|
|
118
|
+
s: Set[int] = set()
|
|
119
|
+
# Bell triangle
|
|
120
|
+
row = [1]
|
|
121
|
+
while row[0] <= _LIMIT:
|
|
122
|
+
s.add(row[0])
|
|
123
|
+
new_row = [row[-1]]
|
|
124
|
+
for i in range(len(row)):
|
|
125
|
+
new_row.append(new_row[-1] + row[i])
|
|
126
|
+
row = new_row
|
|
127
|
+
return s
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _gen_motzkin() -> Set[int]:
|
|
131
|
+
s: Set[int] = set()
|
|
132
|
+
# M(n+1) = M(n) + sum_{k=0}^{n-1} M(k)*M(n-1-k)
|
|
133
|
+
# Simpler recurrence: M(n) = ((2n+2)*M(n-1) + (3n-3)*M(n-2)) / (n+3) — but fractions needed
|
|
134
|
+
# Use: M(0)=1, M(1)=1, M(n)= M(n-1) + sum(M(k)*M(n-2-k) for k=0..n-2)
|
|
135
|
+
# Cleaner: (n+3)*M(n) = (2n+2)*M(n-1) + (3n-3)*M(n-2) [valid for n>=2, using n->n shifted]
|
|
136
|
+
# Actually standard: (n+2)*M(n) = (2n)*M(n-1) + 3*M(n-2) -- let's verify small:
|
|
137
|
+
# M(0)=1,M(1)=1,M(2)=2: (4)*2=(2*2)*1+3*1=4+3=7 WRONG
|
|
138
|
+
# Use direct: M(n+1) = M(n) + sum_{k=0}^{n-1} M(k)*M(n-1-k)
|
|
139
|
+
memo = [1, 1]
|
|
140
|
+
s.update(memo)
|
|
141
|
+
while True:
|
|
142
|
+
n = len(memo)
|
|
143
|
+
nxt = memo[n - 1] + sum(memo[k] * memo[n - 2 - k] for k in range(n - 1))
|
|
144
|
+
if nxt > _LIMIT:
|
|
145
|
+
break
|
|
146
|
+
s.add(nxt)
|
|
147
|
+
memo.append(nxt)
|
|
148
|
+
return s
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _gen_recaman() -> Set[int]:
|
|
152
|
+
s: Set[int] = set()
|
|
153
|
+
# a(0)=0; a(n) = a(n-1)-n if positive and not already in sequence, else a(n-1)+n
|
|
154
|
+
# Generate enough terms; sequence is non-decreasing in range so stop at _LIMIT
|
|
155
|
+
a = [0]
|
|
156
|
+
s.add(0)
|
|
157
|
+
seen = {0}
|
|
158
|
+
n = 1
|
|
159
|
+
while True:
|
|
160
|
+
prev = a[n - 1]
|
|
161
|
+
candidate = prev - n
|
|
162
|
+
if candidate > 0 and candidate not in seen:
|
|
163
|
+
val = candidate
|
|
164
|
+
else:
|
|
165
|
+
val = prev + n
|
|
166
|
+
if val > _LIMIT:
|
|
167
|
+
break
|
|
168
|
+
a.append(val)
|
|
169
|
+
seen.add(val)
|
|
170
|
+
s.add(val)
|
|
171
|
+
n += 1
|
|
172
|
+
if n > 100000: # safety cap
|
|
173
|
+
break
|
|
174
|
+
return s
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _gen_look_and_say() -> Set[int]:
|
|
178
|
+
s: Set[int] = set()
|
|
179
|
+
term = "1"
|
|
180
|
+
while int(term) <= _LIMIT:
|
|
181
|
+
s.add(int(term))
|
|
182
|
+
# generate next term
|
|
183
|
+
new_term = []
|
|
184
|
+
i = 0
|
|
185
|
+
while i < len(term):
|
|
186
|
+
ch = term[i]
|
|
187
|
+
count = 1
|
|
188
|
+
while i + count < len(term) and term[i + count] == ch:
|
|
189
|
+
count += 1
|
|
190
|
+
new_term.append(str(count) + ch)
|
|
191
|
+
i += count
|
|
192
|
+
term = "".join(new_term)
|
|
193
|
+
if len(term) > 10: # terms grow fast, int would exceed _LIMIT
|
|
194
|
+
break
|
|
195
|
+
return s
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _gen_kolakoski() -> Set[int]:
|
|
199
|
+
s: Set[int] = set()
|
|
200
|
+
# Kolakoski sequence: self-describing run-length encoding over {1,2}
|
|
201
|
+
# a(1)=1, a(2)=2, a(3)=2, ...
|
|
202
|
+
seq = [1, 2, 2]
|
|
203
|
+
for v in seq:
|
|
204
|
+
s.add(v)
|
|
205
|
+
write = 2 # index of element being "written" (0-based)
|
|
206
|
+
read = 2 # index of element being "read" to determine run length
|
|
207
|
+
# Only 1 and 2 are in this sequence
|
|
208
|
+
s.update({1, 2})
|
|
209
|
+
return s # Kolakoski only contains 1 and 2
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _gen_sylvester() -> Set[int]:
|
|
213
|
+
s: Set[int] = set()
|
|
214
|
+
a = 2
|
|
215
|
+
while a <= _LIMIT:
|
|
216
|
+
s.add(a)
|
|
217
|
+
a = a * (a - 1) + 1
|
|
218
|
+
return s
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# Build all sets at module load
|
|
222
|
+
_FIBONACCI: Set[int] = _gen_fibonacci()
|
|
223
|
+
_LUCAS: Set[int] = _gen_lucas()
|
|
224
|
+
_TRIBONACCI: Set[int] = _gen_tribonacci()
|
|
225
|
+
_TETRANACCI: Set[int] = _gen_tetranacci()
|
|
226
|
+
_PELL: Set[int] = _gen_pell()
|
|
227
|
+
_JACOBSTHAL: Set[int] = _gen_jacobsthal()
|
|
228
|
+
_PADOVAN: Set[int] = _gen_padovan()
|
|
229
|
+
_PERRIN: Set[int] = _gen_perrin()
|
|
230
|
+
_CATALAN: Set[int] = _gen_catalan()
|
|
231
|
+
_BELL: Set[int] = _gen_bell()
|
|
232
|
+
_MOTZKIN: Set[int] = _gen_motzkin()
|
|
233
|
+
_RECAMAN: Set[int] = _gen_recaman()
|
|
234
|
+
_LOOK_AND_SAY: Set[int] = _gen_look_and_say()
|
|
235
|
+
_KOLAKOSKI: Set[int] = _gen_kolakoski()
|
|
236
|
+
_SYLVESTER: Set[int] = _gen_sylvester()
|
|
237
|
+
|
|
238
|
+
# ---------------------------------------------------------------------------
|
|
239
|
+
# Registered classifiers
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
@register(name="Fibonacci", category="sequences", oeis="A000045",
|
|
243
|
+
description="Member of the Fibonacci sequence.")
|
|
244
|
+
def is_fibonacci(n: int) -> bool:
|
|
245
|
+
"""Return True if n is a Fibonacci number.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
n : int
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
bool
|
|
254
|
+
"""
|
|
255
|
+
return n >= 0 and n in _FIBONACCI
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@register(name="Lucas", category="sequences", oeis="A000032",
|
|
259
|
+
description="Member of the Lucas sequence.")
|
|
260
|
+
def is_lucas(n: int) -> bool:
|
|
261
|
+
"""Return True if n is a Lucas number.
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
n : int
|
|
266
|
+
|
|
267
|
+
Returns
|
|
268
|
+
-------
|
|
269
|
+
bool
|
|
270
|
+
"""
|
|
271
|
+
return n >= 0 and n in _LUCAS
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@register(name="Tribonacci", category="sequences", oeis="A000073",
|
|
275
|
+
description="Member of the Tribonacci sequence.")
|
|
276
|
+
def is_tribonacci(n: int) -> bool:
|
|
277
|
+
"""Return True if n is a Tribonacci number.
|
|
278
|
+
|
|
279
|
+
Parameters
|
|
280
|
+
----------
|
|
281
|
+
n : int
|
|
282
|
+
|
|
283
|
+
Returns
|
|
284
|
+
-------
|
|
285
|
+
bool
|
|
286
|
+
"""
|
|
287
|
+
return n >= 0 and n in _TRIBONACCI
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@register(name="Tetranacci", category="sequences", oeis="A000288",
|
|
291
|
+
description="Member of the Tetranacci sequence.")
|
|
292
|
+
def is_tetranacci(n: int) -> bool:
|
|
293
|
+
"""Return True if n is a Tetranacci number.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
n : int
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
bool
|
|
302
|
+
"""
|
|
303
|
+
return n >= 0 and n in _TETRANACCI
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@register(name="Pell", category="sequences", oeis="A000129",
|
|
307
|
+
description="Member of the Pell sequence.")
|
|
308
|
+
def is_pell(n: int) -> bool:
|
|
309
|
+
"""Return True if n is a Pell number.
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
n : int
|
|
314
|
+
|
|
315
|
+
Returns
|
|
316
|
+
-------
|
|
317
|
+
bool
|
|
318
|
+
"""
|
|
319
|
+
return n >= 0 and n in _PELL
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@register(name="Jacobsthal", category="sequences", oeis="A001045",
|
|
323
|
+
description="Member of the Jacobsthal sequence.")
|
|
324
|
+
def is_jacobsthal(n: int) -> bool:
|
|
325
|
+
"""Return True if n is a Jacobsthal number.
|
|
326
|
+
|
|
327
|
+
Parameters
|
|
328
|
+
----------
|
|
329
|
+
n : int
|
|
330
|
+
|
|
331
|
+
Returns
|
|
332
|
+
-------
|
|
333
|
+
bool
|
|
334
|
+
"""
|
|
335
|
+
return n >= 0 and n in _JACOBSTHAL
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@register(name="Padovan", category="sequences", oeis="A000931",
|
|
339
|
+
description="Member of the Padovan sequence.")
|
|
340
|
+
def is_padovan(n: int) -> bool:
|
|
341
|
+
"""Return True if n is a Padovan number.
|
|
342
|
+
|
|
343
|
+
Parameters
|
|
344
|
+
----------
|
|
345
|
+
n : int
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
bool
|
|
350
|
+
"""
|
|
351
|
+
return n >= 0 and n in _PADOVAN
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@register(name="Perrin", category="sequences", oeis="A001608",
|
|
355
|
+
description="Member of the Perrin sequence.")
|
|
356
|
+
def is_perrin(n: int) -> bool:
|
|
357
|
+
"""Return True if n is a Perrin number.
|
|
358
|
+
|
|
359
|
+
Parameters
|
|
360
|
+
----------
|
|
361
|
+
n : int
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
-------
|
|
365
|
+
bool
|
|
366
|
+
"""
|
|
367
|
+
return n >= 0 and n in _PERRIN
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@register(name="Catalan", category="sequences", oeis="A000108",
|
|
371
|
+
description="Member of the Catalan number sequence.")
|
|
372
|
+
def is_catalan(n: int) -> bool:
|
|
373
|
+
"""Return True if n is a Catalan number.
|
|
374
|
+
|
|
375
|
+
Parameters
|
|
376
|
+
----------
|
|
377
|
+
n : int
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
bool
|
|
382
|
+
|
|
383
|
+
Examples
|
|
384
|
+
--------
|
|
385
|
+
>>> is_catalan(14)
|
|
386
|
+
True
|
|
387
|
+
>>> is_catalan(13)
|
|
388
|
+
False
|
|
389
|
+
"""
|
|
390
|
+
return n >= 0 and n in _CATALAN
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
@register(name="Bell", category="sequences", oeis="A000110",
|
|
394
|
+
description="Member of the Bell number sequence.")
|
|
395
|
+
def is_bell(n: int) -> bool:
|
|
396
|
+
"""Return True if n is a Bell number.
|
|
397
|
+
|
|
398
|
+
Parameters
|
|
399
|
+
----------
|
|
400
|
+
n : int
|
|
401
|
+
|
|
402
|
+
Returns
|
|
403
|
+
-------
|
|
404
|
+
bool
|
|
405
|
+
"""
|
|
406
|
+
return n >= 0 and n in _BELL
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@register(name="Motzkin", category="sequences", oeis="A001006",
|
|
410
|
+
description="Member of the Motzkin sequence.")
|
|
411
|
+
def is_motzkin(n: int) -> bool:
|
|
412
|
+
"""Return True if n is a Motzkin number.
|
|
413
|
+
|
|
414
|
+
Parameters
|
|
415
|
+
----------
|
|
416
|
+
n : int
|
|
417
|
+
|
|
418
|
+
Returns
|
|
419
|
+
-------
|
|
420
|
+
bool
|
|
421
|
+
"""
|
|
422
|
+
return n >= 0 and n in _MOTZKIN
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@register(name="Recaman", category="sequences", oeis="A005132",
|
|
426
|
+
description="Member of the Recaman sequence.")
|
|
427
|
+
def is_recaman(n: int) -> bool:
|
|
428
|
+
"""Return True if n appears in the Recaman sequence.
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
n : int
|
|
433
|
+
|
|
434
|
+
Returns
|
|
435
|
+
-------
|
|
436
|
+
bool
|
|
437
|
+
"""
|
|
438
|
+
return n >= 0 and n in _RECAMAN
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
@register(name="Look and Say", category="sequences", oeis="A005150",
|
|
442
|
+
description="Member of the look-and-say sequence.")
|
|
443
|
+
def is_look_and_say(n: int) -> bool:
|
|
444
|
+
"""Return True if n is a term in the look-and-say sequence.
|
|
445
|
+
|
|
446
|
+
Parameters
|
|
447
|
+
----------
|
|
448
|
+
n : int
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
bool
|
|
453
|
+
"""
|
|
454
|
+
return n >= 0 and n in _LOOK_AND_SAY
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@register(name="Kolakoski", category="sequences", oeis="A000002",
|
|
458
|
+
description="Member of the Kolakoski sequence (only 1 and 2).")
|
|
459
|
+
def is_kolakoski(n: int) -> bool:
|
|
460
|
+
"""Return True if n appears in the Kolakoski sequence.
|
|
461
|
+
|
|
462
|
+
The Kolakoski sequence only contains 1 and 2.
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
n : int
|
|
467
|
+
|
|
468
|
+
Returns
|
|
469
|
+
-------
|
|
470
|
+
bool
|
|
471
|
+
"""
|
|
472
|
+
return n in _KOLAKOSKI
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
@register(name="Sylvester", category="sequences", oeis="A000058",
|
|
476
|
+
description="Member of the Sylvester sequence.")
|
|
477
|
+
def is_sylvester(n: int) -> bool:
|
|
478
|
+
"""Return True if n is a term in the Sylvester sequence.
|
|
479
|
+
|
|
480
|
+
Parameters
|
|
481
|
+
----------
|
|
482
|
+
n : int
|
|
483
|
+
|
|
484
|
+
Returns
|
|
485
|
+
-------
|
|
486
|
+
bool
|
|
487
|
+
"""
|
|
488
|
+
return n >= 0 and n in _SYLVESTER
|