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.
Files changed (37) hide show
  1. numclassify-0.3.0/.github/workflows/docs.yml +30 -0
  2. {numclassify-0.2.1 → numclassify-0.3.0}/CHANGELOG.md +11 -0
  3. {numclassify-0.2.1 → numclassify-0.3.0}/PKG-INFO +15 -1
  4. {numclassify-0.2.1 → numclassify-0.3.0}/README.md +14 -0
  5. {numclassify-0.2.1 → numclassify-0.3.0}/docs/api.md +3 -0
  6. numclassify-0.3.0/docs/playground.html +995 -0
  7. {numclassify-0.2.1 → numclassify-0.3.0}/mkdocs.yml +1 -0
  8. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/__init__.py +5 -1
  9. {numclassify-0.2.1 → numclassify-0.3.0}/pyproject.toml +1 -1
  10. {numclassify-0.2.1 → numclassify-0.3.0}/.github/workflows/ci.yml +0 -0
  11. {numclassify-0.2.1 → numclassify-0.3.0}/.github/workflows/publish.yml +0 -0
  12. {numclassify-0.2.1 → numclassify-0.3.0}/.gitignore +0 -0
  13. {numclassify-0.2.1 → numclassify-0.3.0}/CONTRIBUTING.md +0 -0
  14. {numclassify-0.2.1 → numclassify-0.3.0}/LICENSE +0 -0
  15. {numclassify-0.2.1 → numclassify-0.3.0}/docs/changelog.md +0 -0
  16. {numclassify-0.2.1 → numclassify-0.3.0}/docs/cli.md +0 -0
  17. {numclassify-0.2.1 → numclassify-0.3.0}/docs/index.md +0 -0
  18. {numclassify-0.2.1 → numclassify-0.3.0}/examples/basic_usage.py +0 -0
  19. {numclassify-0.2.1 → numclassify-0.3.0}/examples/custom_type.py +0 -0
  20. {numclassify-0.2.1 → numclassify-0.3.0}/examples/find_perfect_numbers.py +0 -0
  21. {numclassify-0.2.1 → numclassify-0.3.0}/examples/random_classify.py +0 -0
  22. {numclassify-0.2.1 → numclassify-0.3.0}/examples/stream_large_range.py +0 -0
  23. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/__main__.py +0 -0
  24. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/__init__.py +0 -0
  25. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/combinatorial.py +0 -0
  26. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/digital.py +0 -0
  27. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/divisors.py +0 -0
  28. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/figurate.py +0 -0
  29. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/number_theory.py +0 -0
  30. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/powers.py +0 -0
  31. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/primes.py +0 -0
  32. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/recreational.py +0 -0
  33. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_core/sequences.py +0 -0
  34. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/_registry.py +0 -0
  35. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/cli.py +0 -0
  36. {numclassify-0.2.1 → numclassify-0.3.0}/numclassify/py.typed +0 -0
  37. {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.2.1
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
+ [![PyPI version](https://img.shields.io/pypi/v/numclassify.svg?color=FF9933&style=flat-square)](https://pypi.org/project/numclassify/)
32
+ [![PyPI downloads](https://img.shields.io/pypi/dm/numclassify.svg?color=FF9933&style=flat-square)](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
  [![PyPI version](https://img.shields.io/pypi/v/numclassify)](https://pypi.org/project/numclassify/)
@@ -35,6 +38,9 @@ The most comprehensive Python library for number classification — 3000+ number
35
38
  [![Python versions](https://img.shields.io/pypi/pyversions/numclassify)](https://pypi.org/project/numclassify/)
36
39
  [![License MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/LICENSE)
37
40
  [![Tests](https://img.shields.io/github/actions/workflow/status/aratrikghosh2011-tech/numclassify/ci.yml?label=tests)](https://github.com/aratrikghosh2011-tech/numclassify/actions/workflows/ci.yml)
41
+ [![PyPI](https://img.shields.io/pypi/v/numclassify?label=version)](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
+ [![PyPI version](https://img.shields.io/pypi/v/numclassify.svg?color=FF9933&style=flat-square)](https://pypi.org/project/numclassify/)
4
+ [![PyPI downloads](https://img.shields.io/pypi/dm/numclassify.svg?color=FF9933&style=flat-square)](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
  [![PyPI version](https://img.shields.io/pypi/v/numclassify)](https://pypi.org/project/numclassify/)
@@ -7,6 +10,9 @@ The most comprehensive Python library for number classification — 3000+ number
7
10
  [![Python versions](https://img.shields.io/pypi/pyversions/numclassify)](https://pypi.org/project/numclassify/)
8
11
  [![License MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/aratrikghosh2011-tech/numclassify/blob/main/LICENSE)
9
12
  [![Tests](https://img.shields.io/github/actions/workflow/status/aratrikghosh2011-tech/numclassify/ci.yml?label=tests)](https://github.com/aratrikghosh2011-tech/numclassify/actions/workflows/ci.yml)
13
+ [![PyPI](https://img.shields.io/pypi/v/numclassify?label=version)](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>
@@ -19,6 +19,7 @@ nav:
19
19
  - API Reference: api.md
20
20
  - CLI: cli.md
21
21
  - Changelog: changelog.md
22
+ - Playground: playground.html
22
23
 
23
24
  markdown_extensions:
24
25
  - pymdownx.highlight
@@ -28,7 +28,11 @@ Public API
28
28
  from __future__ import annotations
29
29
  del annotations
30
30
 
31
- __version__ = "0.2.1"
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.2.1"
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