numclassify 0.2.1__tar.gz → 0.3.0__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.3.0/.github/workflows/docs.yml +30 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/CHANGELOG.md +11 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/PKG-INFO +15 -1
- {numclassify-0.2.1 → numclassify-0.3.0}/README.md +14 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/docs/api.md +3 -0
- numclassify-0.3.0/docs/playground.html +995 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/mkdocs.yml +1 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/__init__.py +5 -1
- {numclassify-0.2.1 → numclassify-0.3.0}/pyproject.toml +1 -1
- {numclassify-0.2.1 → numclassify-0.3.0}/.github/workflows/ci.yml +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/.github/workflows/publish.yml +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/.gitignore +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/CONTRIBUTING.md +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/LICENSE +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/docs/changelog.md +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/docs/cli.md +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/docs/index.md +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/examples/basic_usage.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/examples/custom_type.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/examples/find_perfect_numbers.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/examples/random_classify.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/examples/stream_large_range.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/__main__.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/__init__.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/combinatorial.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/digital.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/divisors.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/figurate.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/number_theory.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/powers.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/primes.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/recreational.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/sequences.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_registry.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/cli.py +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/py.typed +0 -0
- {numclassify-0.2.1 → numclassify-0.3.0}/tests/test_registry.py +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Deploy Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
deploy:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
pip install mkdocs-material
|
|
28
|
+
|
|
29
|
+
- name: Deploy to GitHub Pages
|
|
30
|
+
run: mkdocs gh-deploy --force
|
|
@@ -6,6 +6,17 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-06-12
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Interactive Playground page (docs/playground.html) powered by Pyodide
|
|
13
|
+
- Classifier, property search, number comparison, random number, and Number of the Day features
|
|
14
|
+
- Shareable URLs via ?n=<number> query parameter
|
|
15
|
+
- Copy results to clipboard button
|
|
16
|
+
- Auto-deploy GitHub Pages workflow on every push to main and on version tags
|
|
17
|
+
- Version number now dynamically read from package metadata (single source of truth)
|
|
18
|
+
- PyPI version and downloads badges in README
|
|
19
|
+
|
|
9
20
|
## [0.2.0] - 2026-05-11
|
|
10
21
|
|
|
11
22
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: numclassify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
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
|
|
@@ -28,6 +28,9 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
|
|
29
29
|
# numclassify
|
|
30
30
|
|
|
31
|
+
[](https://pypi.org/project/numclassify/)
|
|
32
|
+
[](https://pypi.org/project/numclassify/)
|
|
33
|
+
|
|
31
34
|
The most comprehensive Python library for number classification — 3000+ number types, zero dependencies.
|
|
32
35
|
|
|
33
36
|
[](https://pypi.org/project/numclassify/)
|
|
@@ -35,6 +38,9 @@ The most comprehensive Python library for number classification — 3000+ number
|
|
|
35
38
|
[](https://pypi.org/project/numclassify/)
|
|
36
39
|
[](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/LICENSE)
|
|
37
40
|
[](https://github.com/aratrikghosh2011-tech/numclassify/actions/workflows/ci.yml)
|
|
41
|
+
[](https://pypi.org/project/numclassify/)
|
|
42
|
+
|
|
43
|
+
> **Latest version: 0.2.1** — See [CHANGELOG](CHANGELOG.md) for what's new.
|
|
38
44
|
|
|
39
45
|
---
|
|
40
46
|
|
|
@@ -212,12 +218,20 @@ nc.is_my_type(42) # True
|
|
|
212
218
|
nc.get_true_properties(42) # [..., 'my_type', ...]
|
|
213
219
|
```
|
|
214
220
|
|
|
221
|
+
See the [`examples/`](examples/) folder for runnable scripts demonstrating all major features:
|
|
222
|
+
- [`basic_usage.py`](examples/basic_usage.py) — classify, batch, streaming
|
|
223
|
+
- [`custom_type.py`](examples/custom_type.py) — registering custom types
|
|
224
|
+
- [`find_perfect_numbers.py`](examples/find_perfect_numbers.py) — property-based search
|
|
225
|
+
- [`stream_large_range.py`](examples/stream_large_range.py) — memory-safe range streaming
|
|
226
|
+
- [`random_classify.py`](examples/random_classify.py) — random number classification
|
|
227
|
+
|
|
215
228
|
---
|
|
216
229
|
|
|
217
230
|
## API Reference
|
|
218
231
|
|
|
219
232
|
| Function | Description |
|
|
220
233
|
|---|---|
|
|
234
|
+
| `register(name, category, ...)` | Decorator to add custom number types to the full API |
|
|
221
235
|
| `classify(n)` | Returns a dict with the number, its true properties, and a score |
|
|
222
236
|
| `classify_batch(numbers)` | Classify a list of numbers; returns a list of dicts |
|
|
223
237
|
| `random_number(max_n)` | Classify a randomly selected number up to `max_n` |
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# numclassify
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/numclassify/)
|
|
4
|
+
[](https://pypi.org/project/numclassify/)
|
|
5
|
+
|
|
3
6
|
The most comprehensive Python library for number classification — 3000+ number types, zero dependencies.
|
|
4
7
|
|
|
5
8
|
[](https://pypi.org/project/numclassify/)
|
|
@@ -7,6 +10,9 @@ The most comprehensive Python library for number classification — 3000+ number
|
|
|
7
10
|
[](https://pypi.org/project/numclassify/)
|
|
8
11
|
[](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/LICENSE)
|
|
9
12
|
[](https://github.com/aratrikghosh2011-tech/numclassify/actions/workflows/ci.yml)
|
|
13
|
+
[](https://pypi.org/project/numclassify/)
|
|
14
|
+
|
|
15
|
+
> **Latest version: 0.2.1** — See [CHANGELOG](CHANGELOG.md) for what's new.
|
|
10
16
|
|
|
11
17
|
---
|
|
12
18
|
|
|
@@ -184,12 +190,20 @@ nc.is_my_type(42) # True
|
|
|
184
190
|
nc.get_true_properties(42) # [..., 'my_type', ...]
|
|
185
191
|
```
|
|
186
192
|
|
|
193
|
+
See the [`examples/`](examples/) folder for runnable scripts demonstrating all major features:
|
|
194
|
+
- [`basic_usage.py`](examples/basic_usage.py) — classify, batch, streaming
|
|
195
|
+
- [`custom_type.py`](examples/custom_type.py) — registering custom types
|
|
196
|
+
- [`find_perfect_numbers.py`](examples/find_perfect_numbers.py) — property-based search
|
|
197
|
+
- [`stream_large_range.py`](examples/stream_large_range.py) — memory-safe range streaming
|
|
198
|
+
- [`random_classify.py`](examples/random_classify.py) — random number classification
|
|
199
|
+
|
|
187
200
|
---
|
|
188
201
|
|
|
189
202
|
## API Reference
|
|
190
203
|
|
|
191
204
|
| Function | Description |
|
|
192
205
|
|---|---|
|
|
206
|
+
| `register(name, category, ...)` | Decorator to add custom number types to the full API |
|
|
193
207
|
| `classify(n)` | Returns a dict with the number, its true properties, and a score |
|
|
194
208
|
| `classify_batch(numbers)` | Classify a list of numbers; returns a list of dicts |
|
|
195
209
|
| `random_number(max_n)` | Classify a randomly selected number up to `max_n` |
|
|
@@ -15,6 +15,9 @@ Find numbers matching property filters.
|
|
|
15
15
|
## stream(start, end)
|
|
16
16
|
Memory-safe generator over large ranges.
|
|
17
17
|
|
|
18
|
+
## register(name, category, ...)
|
|
19
|
+
Decorator to register custom number types. See [examples/custom_type.py](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/examples/custom_type.py).
|
|
20
|
+
|
|
18
21
|
## Utilities
|
|
19
22
|
- get_all_properties(n)
|
|
20
23
|
- get_true_properties(n)
|
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>numclassify Playground</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--saffron: #FF9933;
|
|
13
|
+
--saffron-light: #FFB366;
|
|
14
|
+
--saffron-dark: #E6821A;
|
|
15
|
+
--saffron-glow: rgba(255, 153, 51, 0.3);
|
|
16
|
+
--saffron-faint: rgba(255, 153, 51, 0.08);
|
|
17
|
+
--bg: #0A0A0F;
|
|
18
|
+
--bg-card: #12121A;
|
|
19
|
+
--bg-input: #1A1A26;
|
|
20
|
+
--text: #F0F0F5;
|
|
21
|
+
--text-muted: #8888AA;
|
|
22
|
+
--border: rgba(255, 153, 51, 0.2);
|
|
23
|
+
--success: #4CAF7D;
|
|
24
|
+
--radius: 12px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
28
|
+
|
|
29
|
+
body {
|
|
30
|
+
font-family: 'Inter', sans-serif;
|
|
31
|
+
background: var(--bg);
|
|
32
|
+
color: var(--text);
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
overflow-x: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
body::before {
|
|
38
|
+
content: '';
|
|
39
|
+
position: fixed;
|
|
40
|
+
inset: 0;
|
|
41
|
+
background-image:
|
|
42
|
+
linear-gradient(rgba(255,153,51,0.04) 1px, transparent 1px),
|
|
43
|
+
linear-gradient(90deg, rgba(255,153,51,0.04) 1px, transparent 1px);
|
|
44
|
+
background-size: 48px 48px;
|
|
45
|
+
animation: gridPulse 8s ease-in-out infinite;
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
z-index: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@keyframes gridPulse {
|
|
51
|
+
0%, 100% { opacity: 0.5; }
|
|
52
|
+
50% { opacity: 1; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.orb {
|
|
56
|
+
position: fixed;
|
|
57
|
+
border-radius: 50%;
|
|
58
|
+
filter: blur(80px);
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
z-index: 0;
|
|
61
|
+
animation: orbFloat 12s ease-in-out infinite;
|
|
62
|
+
}
|
|
63
|
+
.orb-1 {
|
|
64
|
+
width: 400px; height: 400px;
|
|
65
|
+
background: radial-gradient(circle, rgba(255,153,51,0.15), transparent 70%);
|
|
66
|
+
top: -100px; right: -100px;
|
|
67
|
+
animation-delay: 0s;
|
|
68
|
+
}
|
|
69
|
+
.orb-2 {
|
|
70
|
+
width: 300px; height: 300px;
|
|
71
|
+
background: radial-gradient(circle, rgba(255,100,0,0.1), transparent 70%);
|
|
72
|
+
bottom: 100px; left: -80px;
|
|
73
|
+
animation-delay: -4s;
|
|
74
|
+
}
|
|
75
|
+
.orb-3 {
|
|
76
|
+
width: 200px; height: 200px;
|
|
77
|
+
background: radial-gradient(circle, rgba(255,200,51,0.08), transparent 70%);
|
|
78
|
+
top: 50%; left: 50%;
|
|
79
|
+
animation-delay: -8s;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@keyframes orbFloat {
|
|
83
|
+
0%, 100% { transform: translate(0, 0) scale(1); }
|
|
84
|
+
33% { transform: translate(30px, -20px) scale(1.05); }
|
|
85
|
+
66% { transform: translate(-20px, 15px) scale(0.95); }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.container {
|
|
89
|
+
position: relative;
|
|
90
|
+
z-index: 1;
|
|
91
|
+
max-width: 900px;
|
|
92
|
+
margin: 0 auto;
|
|
93
|
+
padding: 40px 24px 80px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
header {
|
|
97
|
+
text-align: center;
|
|
98
|
+
margin-bottom: 48px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.logo-badge {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 8px;
|
|
105
|
+
background: var(--saffron-faint);
|
|
106
|
+
border: 1px solid var(--border);
|
|
107
|
+
border-radius: 100px;
|
|
108
|
+
padding: 6px 16px;
|
|
109
|
+
font-size: 12px;
|
|
110
|
+
font-weight: 600;
|
|
111
|
+
color: var(--saffron);
|
|
112
|
+
letter-spacing: 0.1em;
|
|
113
|
+
text-transform: uppercase;
|
|
114
|
+
margin-bottom: 20px;
|
|
115
|
+
animation: fadeInDown 0.6s ease;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.logo-badge::before {
|
|
119
|
+
content: '●';
|
|
120
|
+
font-size: 8px;
|
|
121
|
+
animation: blink 2s ease-in-out infinite;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@keyframes blink {
|
|
125
|
+
0%, 100% { opacity: 1; }
|
|
126
|
+
50% { opacity: 0.3; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
h1 {
|
|
130
|
+
font-size: clamp(2rem, 5vw, 3.5rem);
|
|
131
|
+
font-weight: 700;
|
|
132
|
+
background: linear-gradient(135deg, var(--saffron), var(--saffron-light), #FFD700);
|
|
133
|
+
-webkit-background-clip: text;
|
|
134
|
+
-webkit-text-fill-color: transparent;
|
|
135
|
+
background-clip: text;
|
|
136
|
+
line-height: 1.1;
|
|
137
|
+
margin-bottom: 12px;
|
|
138
|
+
animation: fadeInDown 0.6s ease 0.1s both;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.subtitle {
|
|
142
|
+
color: var(--text-muted);
|
|
143
|
+
font-size: 16px;
|
|
144
|
+
font-weight: 400;
|
|
145
|
+
animation: fadeInDown 0.6s ease 0.2s both;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@keyframes fadeInDown {
|
|
149
|
+
from { opacity: 0; transform: translateY(-16px); }
|
|
150
|
+
to { opacity: 1; transform: translateY(0); }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#loader {
|
|
154
|
+
background: var(--bg-card);
|
|
155
|
+
border: 1px solid var(--border);
|
|
156
|
+
border-radius: var(--radius);
|
|
157
|
+
padding: 28px;
|
|
158
|
+
text-align: center;
|
|
159
|
+
margin-bottom: 32px;
|
|
160
|
+
animation: fadeIn 0.4s ease;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.loader-text {
|
|
164
|
+
color: var(--saffron);
|
|
165
|
+
font-family: 'JetBrains Mono', monospace;
|
|
166
|
+
font-size: 14px;
|
|
167
|
+
margin-bottom: 16px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.progress-bar-wrap {
|
|
171
|
+
background: rgba(255,153,51,0.1);
|
|
172
|
+
border-radius: 100px;
|
|
173
|
+
height: 6px;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.progress-bar {
|
|
178
|
+
height: 100%;
|
|
179
|
+
background: linear-gradient(90deg, var(--saffron-dark), var(--saffron), var(--saffron-light));
|
|
180
|
+
border-radius: 100px;
|
|
181
|
+
width: 0%;
|
|
182
|
+
transition: width 0.4s ease;
|
|
183
|
+
box-shadow: 0 0 12px var(--saffron-glow);
|
|
184
|
+
animation: shimmer 1.5s linear infinite;
|
|
185
|
+
background-size: 200% 100%;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@keyframes shimmer {
|
|
189
|
+
0% { background-position: -200% 0; }
|
|
190
|
+
100% { background-position: 200% 0; }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.card {
|
|
194
|
+
background: var(--bg-card);
|
|
195
|
+
border: 1px solid var(--border);
|
|
196
|
+
border-radius: var(--radius);
|
|
197
|
+
padding: 28px;
|
|
198
|
+
margin-bottom: 20px;
|
|
199
|
+
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
200
|
+
animation: fadeInUp 0.5s ease both;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.card:hover {
|
|
204
|
+
border-color: var(--saffron);
|
|
205
|
+
box-shadow: 0 0 24px var(--saffron-glow);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@keyframes fadeInUp {
|
|
209
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
210
|
+
to { opacity: 1; transform: translateY(0); }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.card-title {
|
|
214
|
+
font-size: 13px;
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
color: var(--saffron);
|
|
217
|
+
text-transform: uppercase;
|
|
218
|
+
letter-spacing: 0.1em;
|
|
219
|
+
margin-bottom: 16px;
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 8px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.card-title::before {
|
|
226
|
+
content: '';
|
|
227
|
+
display: block;
|
|
228
|
+
width: 3px;
|
|
229
|
+
height: 14px;
|
|
230
|
+
background: var(--saffron);
|
|
231
|
+
border-radius: 2px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.input-row {
|
|
235
|
+
display: flex;
|
|
236
|
+
gap: 12px;
|
|
237
|
+
flex-wrap: wrap;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
input[type="number"], input[type="text"] {
|
|
241
|
+
flex: 1;
|
|
242
|
+
min-width: 180px;
|
|
243
|
+
background: var(--bg-input);
|
|
244
|
+
border: 1px solid var(--border);
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
color: var(--text);
|
|
247
|
+
font-family: 'JetBrains Mono', monospace;
|
|
248
|
+
font-size: 16px;
|
|
249
|
+
padding: 12px 16px;
|
|
250
|
+
outline: none;
|
|
251
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
252
|
+
-moz-appearance: textfield;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
input::-webkit-outer-spin-button,
|
|
256
|
+
input::-webkit-inner-spin-button { -webkit-appearance: none; }
|
|
257
|
+
|
|
258
|
+
input:focus {
|
|
259
|
+
border-color: var(--saffron);
|
|
260
|
+
box-shadow: 0 0 0 3px var(--saffron-glow);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.btn {
|
|
264
|
+
background: linear-gradient(135deg, var(--saffron-dark), var(--saffron));
|
|
265
|
+
color: #0A0A0F;
|
|
266
|
+
border: none;
|
|
267
|
+
border-radius: 8px;
|
|
268
|
+
font-family: 'Inter', sans-serif;
|
|
269
|
+
font-size: 14px;
|
|
270
|
+
font-weight: 700;
|
|
271
|
+
padding: 12px 24px;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
|
|
274
|
+
white-space: nowrap;
|
|
275
|
+
letter-spacing: 0.02em;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.btn:hover:not(:disabled) {
|
|
279
|
+
transform: translateY(-2px);
|
|
280
|
+
box-shadow: 0 6px 24px var(--saffron-glow);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.btn:active:not(:disabled) {
|
|
284
|
+
transform: translateY(0);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.btn:disabled {
|
|
288
|
+
opacity: 0.5;
|
|
289
|
+
cursor: not-allowed;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.btn-ghost {
|
|
293
|
+
background: transparent;
|
|
294
|
+
border: 1px solid var(--border);
|
|
295
|
+
color: var(--text-muted);
|
|
296
|
+
font-size: 13px;
|
|
297
|
+
padding: 10px 18px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.btn-ghost:hover:not(:disabled) {
|
|
301
|
+
border-color: var(--saffron);
|
|
302
|
+
color: var(--saffron);
|
|
303
|
+
box-shadow: 0 0 16px var(--saffron-glow);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.btn-icon {
|
|
307
|
+
background: var(--saffron-faint);
|
|
308
|
+
border: 1px solid var(--border);
|
|
309
|
+
color: var(--saffron);
|
|
310
|
+
font-size: 13px;
|
|
311
|
+
padding: 8px 14px;
|
|
312
|
+
border-radius: 6px;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.btn-row {
|
|
316
|
+
display: flex;
|
|
317
|
+
gap: 10px;
|
|
318
|
+
flex-wrap: wrap;
|
|
319
|
+
margin-top: 14px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#result-classify, #result-search, #result-compare {
|
|
323
|
+
margin-top: 20px;
|
|
324
|
+
display: none;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.result-header {
|
|
328
|
+
display: flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
justify-content: space-between;
|
|
331
|
+
margin-bottom: 14px;
|
|
332
|
+
flex-wrap: wrap;
|
|
333
|
+
gap: 8px;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.number-display {
|
|
337
|
+
font-family: 'JetBrains Mono', monospace;
|
|
338
|
+
font-size: 48px;
|
|
339
|
+
font-weight: 700;
|
|
340
|
+
background: linear-gradient(135deg, var(--saffron), #FFD700);
|
|
341
|
+
-webkit-background-clip: text;
|
|
342
|
+
-webkit-text-fill-color: transparent;
|
|
343
|
+
background-clip: text;
|
|
344
|
+
line-height: 1;
|
|
345
|
+
animation: popIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@keyframes popIn {
|
|
349
|
+
from { transform: scale(0.8); opacity: 0; }
|
|
350
|
+
to { transform: scale(1); opacity: 1; }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.score-badge {
|
|
354
|
+
background: var(--saffron-faint);
|
|
355
|
+
border: 1px solid var(--border);
|
|
356
|
+
border-radius: 100px;
|
|
357
|
+
padding: 4px 14px;
|
|
358
|
+
font-size: 13px;
|
|
359
|
+
color: var(--saffron);
|
|
360
|
+
font-family: 'JetBrains Mono', monospace;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.tags-wrap {
|
|
364
|
+
display: flex;
|
|
365
|
+
flex-wrap: wrap;
|
|
366
|
+
gap: 8px;
|
|
367
|
+
margin-top: 4px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.tag {
|
|
371
|
+
background: var(--saffron-faint);
|
|
372
|
+
border: 1px solid rgba(255,153,51,0.25);
|
|
373
|
+
color: var(--saffron-light);
|
|
374
|
+
border-radius: 6px;
|
|
375
|
+
padding: 4px 12px;
|
|
376
|
+
font-size: 12px;
|
|
377
|
+
font-family: 'JetBrains Mono', monospace;
|
|
378
|
+
transition: all 0.2s ease;
|
|
379
|
+
animation: tagIn 0.3s ease both;
|
|
380
|
+
cursor: default;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.tag:hover {
|
|
384
|
+
background: rgba(255,153,51,0.2);
|
|
385
|
+
border-color: var(--saffron);
|
|
386
|
+
transform: translateY(-1px);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
@keyframes tagIn {
|
|
390
|
+
from { opacity: 0; transform: scale(0.9) translateY(4px); }
|
|
391
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.empty-state {
|
|
395
|
+
color: var(--text-muted);
|
|
396
|
+
font-size: 14px;
|
|
397
|
+
padding: 12px 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.divider {
|
|
401
|
+
height: 1px;
|
|
402
|
+
background: var(--border);
|
|
403
|
+
margin: 20px 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.compare-grid {
|
|
407
|
+
display: grid;
|
|
408
|
+
grid-template-columns: 1fr 1fr;
|
|
409
|
+
gap: 16px;
|
|
410
|
+
margin-top: 16px;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@media (max-width: 560px) {
|
|
414
|
+
.compare-grid { grid-template-columns: 1fr; }
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.compare-col {
|
|
418
|
+
background: var(--bg-input);
|
|
419
|
+
border: 1px solid var(--border);
|
|
420
|
+
border-radius: 8px;
|
|
421
|
+
padding: 16px;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.compare-col-title {
|
|
425
|
+
font-family: 'JetBrains Mono', monospace;
|
|
426
|
+
font-size: 22px;
|
|
427
|
+
font-weight: 700;
|
|
428
|
+
color: var(--saffron);
|
|
429
|
+
margin-bottom: 8px;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.shared-section {
|
|
433
|
+
margin-top: 16px;
|
|
434
|
+
background: rgba(76, 175, 125, 0.05);
|
|
435
|
+
border: 1px solid rgba(76, 175, 125, 0.2);
|
|
436
|
+
border-radius: 8px;
|
|
437
|
+
padding: 16px;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.shared-title {
|
|
441
|
+
color: var(--success);
|
|
442
|
+
font-size: 12px;
|
|
443
|
+
font-weight: 600;
|
|
444
|
+
text-transform: uppercase;
|
|
445
|
+
letter-spacing: 0.1em;
|
|
446
|
+
margin-bottom: 10px;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.notd-number {
|
|
450
|
+
font-family: 'JetBrains Mono', monospace;
|
|
451
|
+
font-size: 64px;
|
|
452
|
+
font-weight: 700;
|
|
453
|
+
background: linear-gradient(135deg, var(--saffron), #FFD700, var(--saffron-light));
|
|
454
|
+
-webkit-background-clip: text;
|
|
455
|
+
-webkit-text-fill-color: transparent;
|
|
456
|
+
background-clip: text;
|
|
457
|
+
text-align: center;
|
|
458
|
+
line-height: 1;
|
|
459
|
+
margin: 12px 0;
|
|
460
|
+
animation: glow 3s ease-in-out infinite;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
@keyframes glow {
|
|
464
|
+
0%, 100% { filter: brightness(1); }
|
|
465
|
+
50% { filter: brightness(1.2); }
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.search-result-list {
|
|
469
|
+
display: flex;
|
|
470
|
+
flex-wrap: wrap;
|
|
471
|
+
gap: 8px;
|
|
472
|
+
margin-top: 8px;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.search-num {
|
|
476
|
+
background: var(--saffron-faint);
|
|
477
|
+
border: 1px solid var(--border);
|
|
478
|
+
color: var(--saffron);
|
|
479
|
+
border-radius: 6px;
|
|
480
|
+
padding: 6px 14px;
|
|
481
|
+
font-family: 'JetBrains Mono', monospace;
|
|
482
|
+
font-size: 14px;
|
|
483
|
+
cursor: pointer;
|
|
484
|
+
transition: all 0.2s ease;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.search-num:hover {
|
|
488
|
+
background: var(--saffron);
|
|
489
|
+
color: #0A0A0F;
|
|
490
|
+
transform: translateY(-2px);
|
|
491
|
+
box-shadow: 0 4px 12px var(--saffron-glow);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
#toast {
|
|
495
|
+
position: fixed;
|
|
496
|
+
bottom: 32px;
|
|
497
|
+
right: 32px;
|
|
498
|
+
background: var(--saffron);
|
|
499
|
+
color: #0A0A0F;
|
|
500
|
+
padding: 12px 20px;
|
|
501
|
+
border-radius: 8px;
|
|
502
|
+
font-size: 14px;
|
|
503
|
+
font-weight: 600;
|
|
504
|
+
opacity: 0;
|
|
505
|
+
transform: translateY(10px);
|
|
506
|
+
transition: all 0.3s ease;
|
|
507
|
+
z-index: 1000;
|
|
508
|
+
pointer-events: none;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
#toast.show {
|
|
512
|
+
opacity: 1;
|
|
513
|
+
transform: translateY(0);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.spinner {
|
|
517
|
+
display: inline-block;
|
|
518
|
+
width: 14px;
|
|
519
|
+
height: 14px;
|
|
520
|
+
border: 2px solid rgba(255,153,51,0.3);
|
|
521
|
+
border-top-color: var(--saffron);
|
|
522
|
+
border-radius: 50%;
|
|
523
|
+
animation: spin 0.7s linear infinite;
|
|
524
|
+
vertical-align: middle;
|
|
525
|
+
margin-right: 6px;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
@keyframes spin {
|
|
529
|
+
to { transform: rotate(360deg); }
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.tabs {
|
|
533
|
+
display: flex;
|
|
534
|
+
gap: 4px;
|
|
535
|
+
background: var(--bg-input);
|
|
536
|
+
border-radius: 10px;
|
|
537
|
+
padding: 4px;
|
|
538
|
+
margin-bottom: 24px;
|
|
539
|
+
border: 1px solid var(--border);
|
|
540
|
+
animation: fadeInDown 0.5s ease 0.3s both;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.tab {
|
|
544
|
+
flex: 1;
|
|
545
|
+
background: transparent;
|
|
546
|
+
border: none;
|
|
547
|
+
color: var(--text-muted);
|
|
548
|
+
font-family: 'Inter', sans-serif;
|
|
549
|
+
font-size: 13px;
|
|
550
|
+
font-weight: 500;
|
|
551
|
+
padding: 10px;
|
|
552
|
+
border-radius: 7px;
|
|
553
|
+
cursor: pointer;
|
|
554
|
+
transition: all 0.2s ease;
|
|
555
|
+
white-space: nowrap;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.tab.active {
|
|
559
|
+
background: linear-gradient(135deg, var(--saffron-dark), var(--saffron));
|
|
560
|
+
color: #0A0A0F;
|
|
561
|
+
font-weight: 700;
|
|
562
|
+
box-shadow: 0 2px 8px var(--saffron-glow);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.tab:hover:not(.active) {
|
|
566
|
+
color: var(--saffron);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.tab-panel { display: none; }
|
|
570
|
+
.tab-panel.active { display: block; }
|
|
571
|
+
|
|
572
|
+
footer {
|
|
573
|
+
text-align: center;
|
|
574
|
+
color: var(--text-muted);
|
|
575
|
+
font-size: 13px;
|
|
576
|
+
margin-top: 48px;
|
|
577
|
+
padding-top: 24px;
|
|
578
|
+
border-top: 1px solid var(--border);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
footer a {
|
|
582
|
+
color: var(--saffron);
|
|
583
|
+
text-decoration: none;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.url-hint {
|
|
587
|
+
font-family: 'JetBrains Mono', monospace;
|
|
588
|
+
font-size: 12px;
|
|
589
|
+
color: var(--text-muted);
|
|
590
|
+
background: var(--bg-input);
|
|
591
|
+
border: 1px solid var(--border);
|
|
592
|
+
border-radius: 6px;
|
|
593
|
+
padding: 8px 12px;
|
|
594
|
+
margin-top: 12px;
|
|
595
|
+
display: inline-block;
|
|
596
|
+
}
|
|
597
|
+
</style>
|
|
598
|
+
</head>
|
|
599
|
+
<body>
|
|
600
|
+
|
|
601
|
+
<div class="orb orb-1"></div>
|
|
602
|
+
<div class="orb orb-2"></div>
|
|
603
|
+
<div class="orb orb-3"></div>
|
|
604
|
+
|
|
605
|
+
<div class="container">
|
|
606
|
+
<header>
|
|
607
|
+
<div class="logo-badge">numclassify playground</div>
|
|
608
|
+
<h1>Number Intelligence</h1>
|
|
609
|
+
<p class="subtitle">Classify any integer into 3000+ named mathematical types — powered by real Python in your browser.</p>
|
|
610
|
+
</header>
|
|
611
|
+
|
|
612
|
+
<div id="loader">
|
|
613
|
+
<p class="loader-text" id="loader-text">⏳ Loading Python runtime...</p>
|
|
614
|
+
<div class="progress-bar-wrap">
|
|
615
|
+
<div class="progress-bar" id="progress-bar"></div>
|
|
616
|
+
</div>
|
|
617
|
+
</div>
|
|
618
|
+
|
|
619
|
+
<div id="app" style="display:none;">
|
|
620
|
+
|
|
621
|
+
<div class="tabs">
|
|
622
|
+
<button class="tab active" onclick="switchTab('classify')">Classify</button>
|
|
623
|
+
<button class="tab" onclick="switchTab('search')">Search</button>
|
|
624
|
+
<button class="tab" onclick="switchTab('compare')">Compare</button>
|
|
625
|
+
<button class="tab" onclick="switchTab('notd')">Today's Number</button>
|
|
626
|
+
</div>
|
|
627
|
+
|
|
628
|
+
<div class="tab-panel active" id="tab-classify">
|
|
629
|
+
<div class="card">
|
|
630
|
+
<div class="card-title">Classify a Number</div>
|
|
631
|
+
<div class="input-row">
|
|
632
|
+
<input type="number" id="input-classify" placeholder="Enter any integer..." min="-999999" max="999999" />
|
|
633
|
+
<button class="btn" id="btn-classify" onclick="doClassify()">Classify</button>
|
|
634
|
+
<button class="btn btn-ghost" onclick="doRandom()">Random</button>
|
|
635
|
+
</div>
|
|
636
|
+
|
|
637
|
+
<div id="result-classify">
|
|
638
|
+
<div class="divider"></div>
|
|
639
|
+
<div class="result-header">
|
|
640
|
+
<div class="number-display" id="classify-number"></div>
|
|
641
|
+
<div>
|
|
642
|
+
<span class="score-badge" id="classify-score"></span>
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
<div class="tags-wrap" id="classify-tags"></div>
|
|
646
|
+
<div class="btn-row">
|
|
647
|
+
<button class="btn btn-icon" onclick="copyResults()">📋 Copy</button>
|
|
648
|
+
<button class="btn btn-icon" onclick="shareURL()">🔗 Share</button>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
|
|
653
|
+
<div class="url-hint" id="share-hint" style="display:none;">
|
|
654
|
+
URL copied! Share: <span id="share-url-text"></span>
|
|
655
|
+
</div>
|
|
656
|
+
</div>
|
|
657
|
+
|
|
658
|
+
<div class="tab-panel" id="tab-search">
|
|
659
|
+
<div class="card">
|
|
660
|
+
<div class="card-title">Search by Property</div>
|
|
661
|
+
<div class="input-row">
|
|
662
|
+
<input type="text" id="input-property" placeholder="Property name (e.g. prime, perfect, triangular)..." />
|
|
663
|
+
</div>
|
|
664
|
+
<div class="input-row" style="margin-top:10px;">
|
|
665
|
+
<input type="number" id="input-range-start" placeholder="From" value="1" />
|
|
666
|
+
<input type="number" id="input-range-end" placeholder="To" value="1000" />
|
|
667
|
+
<button class="btn" onclick="doSearch()">Search</button>
|
|
668
|
+
</div>
|
|
669
|
+
<div id="result-search">
|
|
670
|
+
<div class="divider"></div>
|
|
671
|
+
<div id="search-count" style="color: var(--text-muted); font-size:13px; margin-bottom:10px;"></div>
|
|
672
|
+
<div class="search-result-list" id="search-results"></div>
|
|
673
|
+
</div>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
|
|
677
|
+
<div class="tab-panel" id="tab-compare">
|
|
678
|
+
<div class="card">
|
|
679
|
+
<div class="card-title">Compare Two Numbers</div>
|
|
680
|
+
<div class="input-row">
|
|
681
|
+
<input type="number" id="input-compare-a" placeholder="First number..." />
|
|
682
|
+
<input type="number" id="input-compare-b" placeholder="Second number..." />
|
|
683
|
+
<button class="btn" onclick="doCompare()">Compare</button>
|
|
684
|
+
</div>
|
|
685
|
+
<div id="result-compare">
|
|
686
|
+
<div class="divider"></div>
|
|
687
|
+
<div class="compare-grid">
|
|
688
|
+
<div class="compare-col">
|
|
689
|
+
<div class="compare-col-title" id="compare-title-a"></div>
|
|
690
|
+
<div class="tags-wrap" id="compare-only-a"></div>
|
|
691
|
+
</div>
|
|
692
|
+
<div class="compare-col">
|
|
693
|
+
<div class="compare-col-title" id="compare-title-b"></div>
|
|
694
|
+
<div class="tags-wrap" id="compare-only-b"></div>
|
|
695
|
+
</div>
|
|
696
|
+
</div>
|
|
697
|
+
<div class="shared-section">
|
|
698
|
+
<div class="shared-title">Shared Properties</div>
|
|
699
|
+
<div class="tags-wrap" id="compare-shared"></div>
|
|
700
|
+
</div>
|
|
701
|
+
</div>
|
|
702
|
+
</div>
|
|
703
|
+
</div>
|
|
704
|
+
|
|
705
|
+
<div class="tab-panel" id="tab-notd">
|
|
706
|
+
<div class="card">
|
|
707
|
+
<div class="card-title">Number of the Day</div>
|
|
708
|
+
<p style="color: var(--text-muted); font-size: 14px; margin-bottom: 16px;">
|
|
709
|
+
A new number every day, seeded by today's date.
|
|
710
|
+
</p>
|
|
711
|
+
<div id="notd-loading" style="color: var(--saffron); font-family: 'JetBrains Mono', monospace; font-size: 14px;">
|
|
712
|
+
<span class="spinner"></span> Computing...
|
|
713
|
+
</div>
|
|
714
|
+
<div id="notd-content" style="display:none;">
|
|
715
|
+
<div class="notd-number" id="notd-number"></div>
|
|
716
|
+
<div class="tags-wrap" id="notd-tags" style="justify-content: center;"></div>
|
|
717
|
+
<div class="btn-row" style="justify-content: center; margin-top: 16px;">
|
|
718
|
+
<button class="btn btn-ghost" onclick="classifyNotd()">Full Classify</button>
|
|
719
|
+
</div>
|
|
720
|
+
</div>
|
|
721
|
+
</div>
|
|
722
|
+
</div>
|
|
723
|
+
|
|
724
|
+
</div>
|
|
725
|
+
|
|
726
|
+
<footer>
|
|
727
|
+
<p>Built with <a href="https://pypi.org/project/numclassify/" target="_blank">numclassify</a> + <a href="https://pyodide.org" target="_blank">Pyodide</a> — Python running natively in your browser.</p>
|
|
728
|
+
<p style="margin-top: 8px;"><a href="https://github.com/aratrikghosh2011-tech/numclassify" target="_blank">GitHub</a> · <a href="." target="_blank">Docs</a></p>
|
|
729
|
+
</footer>
|
|
730
|
+
</div>
|
|
731
|
+
|
|
732
|
+
<div id="toast"></div>
|
|
733
|
+
|
|
734
|
+
<script>
|
|
735
|
+
let pyodide = null;
|
|
736
|
+
let pyReady = false;
|
|
737
|
+
|
|
738
|
+
function switchTab(name) {
|
|
739
|
+
document.querySelectorAll('.tab').forEach((t, i) => {
|
|
740
|
+
const names = ['classify', 'search', 'compare', 'notd'];
|
|
741
|
+
t.classList.toggle('active', names[i] === name);
|
|
742
|
+
});
|
|
743
|
+
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
|
|
744
|
+
document.getElementById('tab-' + name).classList.add('active');
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function toast(msg, duration = 2500) {
|
|
748
|
+
const el = document.getElementById('toast');
|
|
749
|
+
el.textContent = msg;
|
|
750
|
+
el.classList.add('show');
|
|
751
|
+
setTimeout(() => el.classList.remove('show'), duration);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function setProgress(pct, text) {
|
|
755
|
+
document.getElementById('progress-bar').style.width = pct + '%';
|
|
756
|
+
document.getElementById('loader-text').textContent = text;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
async function initPyodide() {
|
|
760
|
+
setProgress(10, '⏳ Loading Python runtime (one-time, ~10MB)...');
|
|
761
|
+
pyodide = await loadPyodide();
|
|
762
|
+
|
|
763
|
+
setProgress(50, '📦 Loading micropip...');
|
|
764
|
+
await pyodide.loadPackage('micropip');
|
|
765
|
+
|
|
766
|
+
setProgress(70, '🔢 Installing numclassify...');
|
|
767
|
+
await pyodide.runPythonAsync(`
|
|
768
|
+
import micropip
|
|
769
|
+
await micropip.install('numclassify')
|
|
770
|
+
import numclassify as nc
|
|
771
|
+
import json, random, math
|
|
772
|
+
`);
|
|
773
|
+
|
|
774
|
+
setProgress(100, '✅ Ready!');
|
|
775
|
+
await new Promise(r => setTimeout(r, 400));
|
|
776
|
+
|
|
777
|
+
document.getElementById('loader').style.display = 'none';
|
|
778
|
+
document.getElementById('app').style.display = 'block';
|
|
779
|
+
pyReady = true;
|
|
780
|
+
|
|
781
|
+
const params = new URLSearchParams(window.location.search);
|
|
782
|
+
const n = params.get('n');
|
|
783
|
+
if (n !== null && !isNaN(parseInt(n))) {
|
|
784
|
+
document.getElementById('input-classify').value = n;
|
|
785
|
+
doClassify();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
computeNOTD();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function requireReady() {
|
|
792
|
+
if (!pyReady) { toast('Python still loading...'); return false; }
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function makeTags(props, container, delayBase = 0) {
|
|
797
|
+
container.innerHTML = '';
|
|
798
|
+
if (!props.length) {
|
|
799
|
+
container.innerHTML = '<span class="empty-state">No matching properties found.</span>';
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
props.forEach((p, i) => {
|
|
803
|
+
const tag = document.createElement('span');
|
|
804
|
+
tag.className = 'tag';
|
|
805
|
+
tag.textContent = p.replace(/_/g, ' ');
|
|
806
|
+
tag.style.animationDelay = (delayBase + i * 18) + 'ms';
|
|
807
|
+
container.appendChild(tag);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
async function doClassify(n = null) {
|
|
812
|
+
if (!requireReady()) return;
|
|
813
|
+
const val = n !== null ? n : parseInt(document.getElementById('input-classify').value);
|
|
814
|
+
if (isNaN(val)) { toast('Enter a valid integer.'); return; }
|
|
815
|
+
|
|
816
|
+
const btn = document.getElementById('btn-classify');
|
|
817
|
+
btn.disabled = true;
|
|
818
|
+
btn.innerHTML = '<span class="spinner"></span> Classifying...';
|
|
819
|
+
|
|
820
|
+
try {
|
|
821
|
+
const result = await pyodide.runPythonAsync(`
|
|
822
|
+
import json
|
|
823
|
+
r = nc.classify(${val})
|
|
824
|
+
json.dumps({"number": r["number"], "score": r["score"], "props": r["true_properties"]})
|
|
825
|
+
`);
|
|
826
|
+
const data = JSON.parse(result);
|
|
827
|
+
|
|
828
|
+
document.getElementById('classify-number').textContent = data.number;
|
|
829
|
+
document.getElementById('classify-score').textContent = `${data.score} properties`;
|
|
830
|
+
makeTags(data.props, document.getElementById('classify-tags'));
|
|
831
|
+
|
|
832
|
+
const res = document.getElementById('result-classify');
|
|
833
|
+
res.style.display = 'block';
|
|
834
|
+
|
|
835
|
+
const url = new URL(window.location);
|
|
836
|
+
url.searchParams.set('n', val);
|
|
837
|
+
window.history.replaceState({}, '', url);
|
|
838
|
+
|
|
839
|
+
} catch(e) {
|
|
840
|
+
toast('Error: ' + e.message.slice(0, 80));
|
|
841
|
+
} finally {
|
|
842
|
+
btn.disabled = false;
|
|
843
|
+
btn.textContent = 'Classify';
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
async function doRandom() {
|
|
848
|
+
if (!requireReady()) return;
|
|
849
|
+
const n = await pyodide.runPythonAsync(`nc.random_number()["number"]`);
|
|
850
|
+
document.getElementById('input-classify').value = n;
|
|
851
|
+
await doClassify(n);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
async function doSearch() {
|
|
855
|
+
if (!requireReady()) return;
|
|
856
|
+
const prop = document.getElementById('input-property').value.trim();
|
|
857
|
+
const start = parseInt(document.getElementById('input-range-start').value) || 1;
|
|
858
|
+
const end = parseInt(document.getElementById('input-range-end').value) || 1000;
|
|
859
|
+
|
|
860
|
+
if (!prop) { toast('Enter a property name.'); return; }
|
|
861
|
+
if (end - start > 10000) { toast('Range too large. Max 10000.'); return; }
|
|
862
|
+
|
|
863
|
+
document.getElementById('search-count').innerHTML = '<span class="spinner"></span> Searching...';
|
|
864
|
+
document.getElementById('result-search').style.display = 'block';
|
|
865
|
+
document.getElementById('search-results').innerHTML = '';
|
|
866
|
+
|
|
867
|
+
try {
|
|
868
|
+
const result = await pyodide.runPythonAsync(`
|
|
869
|
+
import json
|
|
870
|
+
nums = [n for n in range(${start}, ${end}+1) if nc.get_all_properties(n).get("${prop.replace(/"/g, '')}", False)]
|
|
871
|
+
json.dumps(nums[:200])
|
|
872
|
+
`);
|
|
873
|
+
const nums = JSON.parse(result);
|
|
874
|
+
document.getElementById('search-count').textContent =
|
|
875
|
+
`Found ${nums.length} numbers${nums.length === 200 ? ' (showing first 200)' : ''} with property "${prop}"`;
|
|
876
|
+
|
|
877
|
+
const container = document.getElementById('search-results');
|
|
878
|
+
if (!nums.length) {
|
|
879
|
+
container.innerHTML = '<span class="empty-state">No numbers found. Check the property name (use underscores, e.g. "triangular_number").</span>';
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
nums.forEach(n => {
|
|
883
|
+
const el = document.createElement('div');
|
|
884
|
+
el.className = 'search-num';
|
|
885
|
+
el.textContent = n;
|
|
886
|
+
el.onclick = () => {
|
|
887
|
+
document.getElementById('input-classify').value = n;
|
|
888
|
+
switchTab('classify');
|
|
889
|
+
doClassify(n);
|
|
890
|
+
};
|
|
891
|
+
container.appendChild(el);
|
|
892
|
+
});
|
|
893
|
+
} catch(e) {
|
|
894
|
+
toast('Error: ' + e.message.slice(0, 80));
|
|
895
|
+
document.getElementById('search-count').textContent = 'Error during search.';
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async function doCompare() {
|
|
900
|
+
if (!requireReady()) return;
|
|
901
|
+
const a = parseInt(document.getElementById('input-compare-a').value);
|
|
902
|
+
const b = parseInt(document.getElementById('input-compare-b').value);
|
|
903
|
+
if (isNaN(a) || isNaN(b)) { toast('Enter two valid integers.'); return; }
|
|
904
|
+
|
|
905
|
+
try {
|
|
906
|
+
const result = await pyodide.runPythonAsync(`
|
|
907
|
+
import json
|
|
908
|
+
pa = set(nc.get_true_properties(${a}))
|
|
909
|
+
pb = set(nc.get_true_properties(${b}))
|
|
910
|
+
shared = sorted(pa & pb)
|
|
911
|
+
only_a = sorted(pa - pb)
|
|
912
|
+
only_b = sorted(pb - pa)
|
|
913
|
+
json.dumps({"only_a": only_a, "only_b": only_b, "shared": shared})
|
|
914
|
+
`);
|
|
915
|
+
const data = JSON.parse(result);
|
|
916
|
+
|
|
917
|
+
document.getElementById('compare-title-a').textContent = a;
|
|
918
|
+
document.getElementById('compare-title-b').textContent = b;
|
|
919
|
+
makeTags(data.only_a, document.getElementById('compare-only-a'));
|
|
920
|
+
makeTags(data.only_b, document.getElementById('compare-only-b'));
|
|
921
|
+
makeTags(data.shared, document.getElementById('compare-shared'));
|
|
922
|
+
|
|
923
|
+
document.getElementById('result-compare').style.display = 'block';
|
|
924
|
+
} catch(e) {
|
|
925
|
+
toast('Error: ' + e.message.slice(0, 80));
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
async function computeNOTD() {
|
|
930
|
+
try {
|
|
931
|
+
const result = await pyodide.runPythonAsync(`
|
|
932
|
+
import json, math
|
|
933
|
+
today = __import__('datetime').date.today()
|
|
934
|
+
seed = today.year * 10000 + today.month * 100 + today.day
|
|
935
|
+
__import__('random').seed(seed)
|
|
936
|
+
n = __import__('random').randint(1, 9999)
|
|
937
|
+
r = nc.classify(n)
|
|
938
|
+
json.dumps({"number": r["number"], "score": r["score"], "props": r["true_properties"][:12]})
|
|
939
|
+
`);
|
|
940
|
+
const data = JSON.parse(result);
|
|
941
|
+
document.getElementById('notd-number').textContent = data.number;
|
|
942
|
+
makeTags(data.props, document.getElementById('notd-tags'));
|
|
943
|
+
document.getElementById('notd-loading').style.display = 'none';
|
|
944
|
+
document.getElementById('notd-content').style.display = 'block';
|
|
945
|
+
} catch(e) {
|
|
946
|
+
document.getElementById('notd-loading').textContent = 'Could not compute today\'s number.';
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function classifyNotd() {
|
|
951
|
+
const n = parseInt(document.getElementById('notd-number').textContent);
|
|
952
|
+
document.getElementById('input-classify').value = n;
|
|
953
|
+
switchTab('classify');
|
|
954
|
+
doClassify(n);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
async function copyResults() {
|
|
958
|
+
const num = document.getElementById('classify-number').textContent;
|
|
959
|
+
const score = document.getElementById('classify-score').textContent;
|
|
960
|
+
const tags = [...document.getElementById('classify-tags').querySelectorAll('.tag')].map(t => t.textContent).join(', ');
|
|
961
|
+
const text = `numclassify: ${num}\n${score}\n\n${tags}`;
|
|
962
|
+
try {
|
|
963
|
+
await navigator.clipboard.writeText(text);
|
|
964
|
+
toast('Copied to clipboard!');
|
|
965
|
+
} catch {
|
|
966
|
+
toast('Copy failed — try selecting manually.');
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function shareURL() {
|
|
971
|
+
const url = window.location.href;
|
|
972
|
+
navigator.clipboard.writeText(url).then(() => {
|
|
973
|
+
const hint = document.getElementById('share-hint');
|
|
974
|
+
document.getElementById('share-url-text').textContent = url;
|
|
975
|
+
hint.style.display = 'inline-block';
|
|
976
|
+
toast('Share URL copied!');
|
|
977
|
+
setTimeout(() => hint.style.display = 'none', 5000);
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
982
|
+
document.getElementById('input-classify')?.addEventListener('keydown', e => {
|
|
983
|
+
if (e.key === 'Enter') doClassify();
|
|
984
|
+
});
|
|
985
|
+
document.getElementById('input-property')?.addEventListener('keydown', e => {
|
|
986
|
+
if (e.key === 'Enter') doSearch();
|
|
987
|
+
});
|
|
988
|
+
document.getElementById('input-compare-b')?.addEventListener('keydown', e => {
|
|
989
|
+
if (e.key === 'Enter') doCompare();
|
|
990
|
+
});
|
|
991
|
+
initPyodide();
|
|
992
|
+
});
|
|
993
|
+
</script>
|
|
994
|
+
</body>
|
|
995
|
+
</html>
|
|
@@ -28,7 +28,11 @@ Public API
|
|
|
28
28
|
from __future__ import annotations
|
|
29
29
|
del annotations
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
from importlib.metadata import version as _version, PackageNotFoundError as _PackageNotFoundError
|
|
32
|
+
try:
|
|
33
|
+
__version__ = _version("numclassify")
|
|
34
|
+
except _PackageNotFoundError:
|
|
35
|
+
__version__ = "0.0.0"
|
|
32
36
|
|
|
33
37
|
# --- Import all _core submodules so @register decorators fire at import time ---
|
|
34
38
|
from numclassify._core import primes as _primes # noqa: F401
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "numclassify"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
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"
|
|
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
|
|
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
|