chiranjeev-toolkit 1.0.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.
- chiranjeev_toolkit-1.0.0/LICENSE +21 -0
- chiranjeev_toolkit-1.0.0/PKG-INFO +91 -0
- chiranjeev_toolkit-1.0.0/README.md +75 -0
- chiranjeev_toolkit-1.0.0/pyproject.toml +25 -0
- chiranjeev_toolkit-1.0.0/setup.cfg +4 -0
- chiranjeev_toolkit-1.0.0/src/chiranjeev_toolkit.egg-info/PKG-INFO +91 -0
- chiranjeev_toolkit-1.0.0/src/chiranjeev_toolkit.egg-info/SOURCES.txt +16 -0
- chiranjeev_toolkit-1.0.0/src/chiranjeev_toolkit.egg-info/dependency_links.txt +1 -0
- chiranjeev_toolkit-1.0.0/src/chiranjeev_toolkit.egg-info/entry_points.txt +2 -0
- chiranjeev_toolkit-1.0.0/src/chiranjeev_toolkit.egg-info/top_level.txt +1 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/__init__.py +38 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/__main__.py +3 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/cli.py +86 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/display.py +80 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/mathx.py +101 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/stats.py +59 -0
- chiranjeev_toolkit-1.0.0/src/ctoolkit/textx.py +42 -0
- chiranjeev_toolkit-1.0.0/tests/test_ctoolkit.py +75 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chiranjeev (Roll No. 2432709)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chiranjeev-toolkit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A tiny, dependency-free Python toolkit with math, text and statistics helpers, plus a beautiful terminal UI.
|
|
5
|
+
Author: Chiranjeev
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: toolkit,math,text,statistics,cli,assignment
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
<div align="center">
|
|
18
|
+
|
|
19
|
+
# ๐งฐ chiranjeev-toolkit
|
|
20
|
+
|
|
21
|
+
**A tiny, dependency-free Python toolkit โ math, text & statistics helpers with a beautiful terminal UI.**
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+

|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
Made by **Chiranjeev** ยท Roll No. **2432709**
|
|
28
|
+
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## โจ Features
|
|
34
|
+
|
|
35
|
+
| Module | What it gives you |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `ctoolkit.mathx` | `is_prime`, `primes_upto`, `prime_factors`, `fibonacci`, `factorial`, `gcd`, `lcm`, `collatz_steps` |
|
|
38
|
+
| `ctoolkit.textx` | `word_count`, `vowel_count`, `is_palindrome`, `reverse_words`, `caesar`, `slugify` |
|
|
39
|
+
| `ctoolkit.stats` | `mean`, `median`, `mode`, `variance`, `std_dev`, `describe` |
|
|
40
|
+
| `ctoolkit.display` | `box`, `table`, `banner`, `paint` โ ANSI colours that respect `NO_COLOR` |
|
|
41
|
+
|
|
42
|
+
## ๐ฆ Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install chiranjeev-toolkit
|
|
46
|
+
# or from source
|
|
47
|
+
pip install .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## ๐ Quick start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import ctoolkit as ct
|
|
54
|
+
|
|
55
|
+
ct.is_prime(97) # True
|
|
56
|
+
ct.prime_factors(360) # [2, 2, 2, 3, 3, 5]
|
|
57
|
+
ct.fibonacci(8) # [0, 1, 1, 2, 3, 5, 8, 13]
|
|
58
|
+
ct.caesar("Hello", 3) # 'Khoor'
|
|
59
|
+
ct.describe([2, 4, 4, 4, 5, 5, 7, 9])["std_dev"] # 2.0
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## ๐ฅ๏ธ Command line
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ctoolkit info # banner + author details
|
|
66
|
+
ctoolkit prime 97 # primality + factorisation
|
|
67
|
+
ctoolkit fib 10 # first 10 Fibonacci numbers
|
|
68
|
+
ctoolkit primes 50 # all primes up to 50
|
|
69
|
+
ctoolkit stats 2 4 4 4 5 5 7 9 # descriptive statistics table
|
|
70
|
+
ctoolkit text "Never odd or even"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
(`python -m ctoolkit ...` works too.)
|
|
74
|
+
|
|
75
|
+
## ๐งช Run the tests
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m unittest discover -s tests
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## ๐ค Publishing to PyPI
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install build twine
|
|
85
|
+
python -m build
|
|
86
|
+
twine upload dist/*
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## ๐ License
|
|
90
|
+
|
|
91
|
+
MIT ยฉ 2026 Chiranjeev (Roll No. 2432709)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# ๐งฐ chiranjeev-toolkit
|
|
4
|
+
|
|
5
|
+
**A tiny, dependency-free Python toolkit โ math, text & statistics helpers with a beautiful terminal UI.**
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
Made by **Chiranjeev** ยท Roll No. **2432709**
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## โจ Features
|
|
18
|
+
|
|
19
|
+
| Module | What it gives you |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `ctoolkit.mathx` | `is_prime`, `primes_upto`, `prime_factors`, `fibonacci`, `factorial`, `gcd`, `lcm`, `collatz_steps` |
|
|
22
|
+
| `ctoolkit.textx` | `word_count`, `vowel_count`, `is_palindrome`, `reverse_words`, `caesar`, `slugify` |
|
|
23
|
+
| `ctoolkit.stats` | `mean`, `median`, `mode`, `variance`, `std_dev`, `describe` |
|
|
24
|
+
| `ctoolkit.display` | `box`, `table`, `banner`, `paint` โ ANSI colours that respect `NO_COLOR` |
|
|
25
|
+
|
|
26
|
+
## ๐ฆ Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install chiranjeev-toolkit
|
|
30
|
+
# or from source
|
|
31
|
+
pip install .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## ๐ Quick start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import ctoolkit as ct
|
|
38
|
+
|
|
39
|
+
ct.is_prime(97) # True
|
|
40
|
+
ct.prime_factors(360) # [2, 2, 2, 3, 3, 5]
|
|
41
|
+
ct.fibonacci(8) # [0, 1, 1, 2, 3, 5, 8, 13]
|
|
42
|
+
ct.caesar("Hello", 3) # 'Khoor'
|
|
43
|
+
ct.describe([2, 4, 4, 4, 5, 5, 7, 9])["std_dev"] # 2.0
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## ๐ฅ๏ธ Command line
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
ctoolkit info # banner + author details
|
|
50
|
+
ctoolkit prime 97 # primality + factorisation
|
|
51
|
+
ctoolkit fib 10 # first 10 Fibonacci numbers
|
|
52
|
+
ctoolkit primes 50 # all primes up to 50
|
|
53
|
+
ctoolkit stats 2 4 4 4 5 5 7 9 # descriptive statistics table
|
|
54
|
+
ctoolkit text "Never odd or even"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
(`python -m ctoolkit ...` works too.)
|
|
58
|
+
|
|
59
|
+
## ๐งช Run the tests
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
python -m unittest discover -s tests
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## ๐ค Publishing to PyPI
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install build twine
|
|
69
|
+
python -m build
|
|
70
|
+
twine upload dist/*
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## ๐ License
|
|
74
|
+
|
|
75
|
+
MIT ยฉ 2026 Chiranjeev (Roll No. 2432709)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "chiranjeev-toolkit"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A tiny, dependency-free Python toolkit with math, text and statistics helpers, plus a beautiful terminal UI."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Chiranjeev" }]
|
|
13
|
+
keywords = ["toolkit", "math", "text", "statistics", "cli", "assignment"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Intended Audience :: Education",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
ctoolkit = "ctoolkit.cli:main"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["src"]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chiranjeev-toolkit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A tiny, dependency-free Python toolkit with math, text and statistics helpers, plus a beautiful terminal UI.
|
|
5
|
+
Author: Chiranjeev
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: toolkit,math,text,statistics,cli,assignment
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
<div align="center">
|
|
18
|
+
|
|
19
|
+
# ๐งฐ chiranjeev-toolkit
|
|
20
|
+
|
|
21
|
+
**A tiny, dependency-free Python toolkit โ math, text & statistics helpers with a beautiful terminal UI.**
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+

|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
Made by **Chiranjeev** ยท Roll No. **2432709**
|
|
28
|
+
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## โจ Features
|
|
34
|
+
|
|
35
|
+
| Module | What it gives you |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `ctoolkit.mathx` | `is_prime`, `primes_upto`, `prime_factors`, `fibonacci`, `factorial`, `gcd`, `lcm`, `collatz_steps` |
|
|
38
|
+
| `ctoolkit.textx` | `word_count`, `vowel_count`, `is_palindrome`, `reverse_words`, `caesar`, `slugify` |
|
|
39
|
+
| `ctoolkit.stats` | `mean`, `median`, `mode`, `variance`, `std_dev`, `describe` |
|
|
40
|
+
| `ctoolkit.display` | `box`, `table`, `banner`, `paint` โ ANSI colours that respect `NO_COLOR` |
|
|
41
|
+
|
|
42
|
+
## ๐ฆ Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install chiranjeev-toolkit
|
|
46
|
+
# or from source
|
|
47
|
+
pip install .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## ๐ Quick start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import ctoolkit as ct
|
|
54
|
+
|
|
55
|
+
ct.is_prime(97) # True
|
|
56
|
+
ct.prime_factors(360) # [2, 2, 2, 3, 3, 5]
|
|
57
|
+
ct.fibonacci(8) # [0, 1, 1, 2, 3, 5, 8, 13]
|
|
58
|
+
ct.caesar("Hello", 3) # 'Khoor'
|
|
59
|
+
ct.describe([2, 4, 4, 4, 5, 5, 7, 9])["std_dev"] # 2.0
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## ๐ฅ๏ธ Command line
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ctoolkit info # banner + author details
|
|
66
|
+
ctoolkit prime 97 # primality + factorisation
|
|
67
|
+
ctoolkit fib 10 # first 10 Fibonacci numbers
|
|
68
|
+
ctoolkit primes 50 # all primes up to 50
|
|
69
|
+
ctoolkit stats 2 4 4 4 5 5 7 9 # descriptive statistics table
|
|
70
|
+
ctoolkit text "Never odd or even"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
(`python -m ctoolkit ...` works too.)
|
|
74
|
+
|
|
75
|
+
## ๐งช Run the tests
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m unittest discover -s tests
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## ๐ค Publishing to PyPI
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install build twine
|
|
85
|
+
python -m build
|
|
86
|
+
twine upload dist/*
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## ๐ License
|
|
90
|
+
|
|
91
|
+
MIT ยฉ 2026 Chiranjeev (Roll No. 2432709)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/chiranjeev_toolkit.egg-info/PKG-INFO
|
|
5
|
+
src/chiranjeev_toolkit.egg-info/SOURCES.txt
|
|
6
|
+
src/chiranjeev_toolkit.egg-info/dependency_links.txt
|
|
7
|
+
src/chiranjeev_toolkit.egg-info/entry_points.txt
|
|
8
|
+
src/chiranjeev_toolkit.egg-info/top_level.txt
|
|
9
|
+
src/ctoolkit/__init__.py
|
|
10
|
+
src/ctoolkit/__main__.py
|
|
11
|
+
src/ctoolkit/cli.py
|
|
12
|
+
src/ctoolkit/display.py
|
|
13
|
+
src/ctoolkit/mathx.py
|
|
14
|
+
src/ctoolkit/stats.py
|
|
15
|
+
src/ctoolkit/textx.py
|
|
16
|
+
tests/test_ctoolkit.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ctoolkit
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""ctoolkit - a tiny toolkit of math, text and statistics helpers.
|
|
2
|
+
|
|
3
|
+
Author : Chiranjeev
|
|
4
|
+
Roll No: 2432709
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .mathx import (
|
|
8
|
+
collatz_steps,
|
|
9
|
+
factorial,
|
|
10
|
+
fibonacci,
|
|
11
|
+
gcd,
|
|
12
|
+
is_prime,
|
|
13
|
+
lcm,
|
|
14
|
+
prime_factors,
|
|
15
|
+
primes_upto,
|
|
16
|
+
)
|
|
17
|
+
from .stats import describe, mean, median, mode, std_dev, variance
|
|
18
|
+
from .textx import (
|
|
19
|
+
caesar,
|
|
20
|
+
is_palindrome,
|
|
21
|
+
reverse_words,
|
|
22
|
+
slugify,
|
|
23
|
+
vowel_count,
|
|
24
|
+
word_count,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__version__ = "1.0.0"
|
|
28
|
+
__author__ = "Chiranjeev"
|
|
29
|
+
__roll_no__ = "2432709"
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"__version__", "__author__", "__roll_no__",
|
|
33
|
+
"collatz_steps", "factorial", "fibonacci", "gcd", "is_prime", "lcm",
|
|
34
|
+
"prime_factors", "primes_upto",
|
|
35
|
+
"describe", "mean", "median", "mode", "std_dev", "variance",
|
|
36
|
+
"caesar", "is_palindrome", "reverse_words", "slugify", "vowel_count",
|
|
37
|
+
"word_count",
|
|
38
|
+
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Command-line interface: ``ctoolkit <command>`` or ``python -m ctoolkit``."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from . import __version__, mathx, stats, textx
|
|
7
|
+
from .display import banner, box, paint, table
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
11
|
+
p = argparse.ArgumentParser(
|
|
12
|
+
prog="ctoolkit",
|
|
13
|
+
description="Math, text and statistics helpers with a pretty terminal UI.",
|
|
14
|
+
)
|
|
15
|
+
p.add_argument("-V", "--version", action="version", version=f"ctoolkit {__version__}")
|
|
16
|
+
sub = p.add_subparsers(dest="command")
|
|
17
|
+
|
|
18
|
+
sub.add_parser("info", help="show the banner and author details")
|
|
19
|
+
|
|
20
|
+
s = sub.add_parser("prime", help="primality check and factorisation")
|
|
21
|
+
s.add_argument("n", type=int)
|
|
22
|
+
|
|
23
|
+
s = sub.add_parser("fib", help="first N Fibonacci numbers")
|
|
24
|
+
s.add_argument("count", type=int)
|
|
25
|
+
|
|
26
|
+
s = sub.add_parser("primes", help="all primes up to N")
|
|
27
|
+
s.add_argument("n", type=int)
|
|
28
|
+
|
|
29
|
+
s = sub.add_parser("stats", help="descriptive statistics for numbers")
|
|
30
|
+
s.add_argument("numbers", type=float, nargs="+")
|
|
31
|
+
|
|
32
|
+
s = sub.add_parser("text", help="analyse a piece of text")
|
|
33
|
+
s.add_argument("text", nargs="+")
|
|
34
|
+
|
|
35
|
+
return p
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _fmt(x: float) -> str:
|
|
39
|
+
return f"{x:.4g}" if isinstance(x, float) else str(x)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
43
|
+
parser = _build_parser()
|
|
44
|
+
args = parser.parse_args(argv)
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
if args.command in (None, "info"):
|
|
48
|
+
print(banner())
|
|
49
|
+
if args.command is None:
|
|
50
|
+
print("\nRun " + paint("ctoolkit --help", "yellow") + " to see all commands.")
|
|
51
|
+
elif args.command == "prime":
|
|
52
|
+
ok = mathx.is_prime(args.n)
|
|
53
|
+
verdict = paint("PRIME โ", "green", "bold") if ok else paint("NOT PRIME โ", "red", "bold")
|
|
54
|
+
lines = [f"{args.n} is {verdict}"]
|
|
55
|
+
if args.n >= 2:
|
|
56
|
+
lines.append("Factors: " + " ร ".join(map(str, mathx.prime_factors(args.n))))
|
|
57
|
+
print(box(lines, "green" if ok else "red"))
|
|
58
|
+
elif args.command == "fib":
|
|
59
|
+
seq = mathx.fibonacci(args.count)
|
|
60
|
+
print(box([", ".join(map(str, seq)) or "(empty)"], "cyan"))
|
|
61
|
+
elif args.command == "primes":
|
|
62
|
+
ps = mathx.primes_upto(args.n)
|
|
63
|
+
print(box([f"{len(ps)} primes up to {args.n}", ", ".join(map(str, ps))], "blue"))
|
|
64
|
+
elif args.command == "stats":
|
|
65
|
+
d = stats.describe(args.numbers)
|
|
66
|
+
rows = [(k, ", ".join(map(_fmt, v)) if isinstance(v, list) else _fmt(v)) for k, v in d.items()]
|
|
67
|
+
print(table(["Metric", "Value"], rows, "magenta"))
|
|
68
|
+
elif args.command == "text":
|
|
69
|
+
t = " ".join(args.text)
|
|
70
|
+
rows = [
|
|
71
|
+
("Words", textx.word_count(t)),
|
|
72
|
+
("Vowels", textx.vowel_count(t)),
|
|
73
|
+
("Palindrome", "yes" if textx.is_palindrome(t) else "no"),
|
|
74
|
+
("Reversed words", textx.reverse_words(t)),
|
|
75
|
+
("Slug", textx.slugify(t)),
|
|
76
|
+
("Caesar +3", textx.caesar(t, 3)),
|
|
77
|
+
]
|
|
78
|
+
print(table(["Property", "Result"], rows, "cyan"))
|
|
79
|
+
except (ValueError, TypeError) as exc:
|
|
80
|
+
print(paint(f"Error: {exc}", "red"))
|
|
81
|
+
return 1
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Tiny ANSI helpers for a beautiful terminal UI (respects NO_COLOR)."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Iterable, List, Sequence
|
|
6
|
+
|
|
7
|
+
_CODES = {
|
|
8
|
+
"reset": "0", "bold": "1", "dim": "2",
|
|
9
|
+
"red": "91", "green": "92", "yellow": "93",
|
|
10
|
+
"blue": "94", "magenta": "95", "cyan": "96",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _use_color() -> bool:
|
|
15
|
+
return sys.stdout.isatty() and "NO_COLOR" not in os.environ
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def paint(text: str, *styles: str) -> str:
|
|
19
|
+
"""Wrap ``text`` in ANSI styles (no-op when colour is unavailable)."""
|
|
20
|
+
if not _use_color():
|
|
21
|
+
return text
|
|
22
|
+
start = "".join(f"\033[{_CODES[s]}m" for s in styles)
|
|
23
|
+
return f"{start}{text}\033[0m"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def box(lines: Iterable[str], color: str = "cyan") -> str:
|
|
27
|
+
"""Draw a rounded box around ``lines``."""
|
|
28
|
+
rows = list(lines)
|
|
29
|
+
width = max((len(r) for r in rows), default=0)
|
|
30
|
+
top = paint("โญ" + "โ" * (width + 2) + "โฎ", color)
|
|
31
|
+
bottom = paint("โฐ" + "โ" * (width + 2) + "โฏ", color)
|
|
32
|
+
side = paint("โ", color)
|
|
33
|
+
body = [f"{side} {r.ljust(width)} {side}" for r in rows]
|
|
34
|
+
return "\n".join([top, *body, bottom])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def table(headers: Sequence[str], rows: Sequence[Sequence[object]], color: str = "cyan") -> str:
|
|
38
|
+
"""Render a neat unicode table."""
|
|
39
|
+
str_rows: List[List[str]] = [[str(c) for c in r] for r in rows]
|
|
40
|
+
widths = [
|
|
41
|
+
max(len(h), *(len(r[i]) for r in str_rows)) if str_rows else len(h)
|
|
42
|
+
for i, h in enumerate(headers)
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
def line(left: str, mid: str, right: str) -> str:
|
|
46
|
+
return paint(left + mid.join("โ" * (w + 2) for w in widths) + right, color)
|
|
47
|
+
|
|
48
|
+
def row(cells: Sequence[str], style: str = "") -> str:
|
|
49
|
+
bar = paint("โ", color)
|
|
50
|
+
parts = [f" {paint(c.ljust(w), style) if style else c.ljust(w)} " for c, w in zip(cells, widths)]
|
|
51
|
+
return bar + bar.join(parts) + bar
|
|
52
|
+
|
|
53
|
+
out = [line("โ", "โฌ", "โ"), row(list(headers), "bold"), line("โ", "โผ", "โค")]
|
|
54
|
+
out += [row(r) for r in str_rows]
|
|
55
|
+
out.append(line("โ", "โด", "โ"))
|
|
56
|
+
return "\n".join(out)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def banner() -> str:
|
|
60
|
+
"""The package banner."""
|
|
61
|
+
from . import __author__, __roll_no__, __version__
|
|
62
|
+
|
|
63
|
+
art = [
|
|
64
|
+
" ____ _____ ___ ___ _ _ _____ _____ ",
|
|
65
|
+
" / ___|_ _/ _ \\ / _ \\| | | |/ /_ _|_ _|",
|
|
66
|
+
"| | | || | | | | | | | | ' / | | | | ",
|
|
67
|
+
"| |___ | || |_| | |_| | |___| . \\ | | | | ",
|
|
68
|
+
" \\____| |_| \\___/ \\___/|_____|_|\\_\\___| |_| ",
|
|
69
|
+
]
|
|
70
|
+
colors = ["cyan", "cyan", "blue", "blue", "magenta"]
|
|
71
|
+
art_block = "\n".join(paint(l, c, "bold") for l, c in zip(art, colors))
|
|
72
|
+
info = box(
|
|
73
|
+
[
|
|
74
|
+
f"Author : {__author__}",
|
|
75
|
+
f"Roll No : {__roll_no__}",
|
|
76
|
+
f"Version : {__version__}",
|
|
77
|
+
],
|
|
78
|
+
"magenta",
|
|
79
|
+
)
|
|
80
|
+
return f"{art_block}\n{info}"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Number-theory and math helpers."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _check_int(n: int, name: str = "n", minimum: int = 0) -> None:
|
|
7
|
+
if not isinstance(n, int) or isinstance(n, bool):
|
|
8
|
+
raise TypeError(f"{name} must be an int, got {type(n).__name__}")
|
|
9
|
+
if n < minimum:
|
|
10
|
+
raise ValueError(f"{name} must be >= {minimum}, got {n}")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_prime(n: int) -> bool:
|
|
14
|
+
"""Return True if ``n`` is a prime number."""
|
|
15
|
+
_check_int(n, minimum=-(10**18))
|
|
16
|
+
if n < 2:
|
|
17
|
+
return False
|
|
18
|
+
if n < 4:
|
|
19
|
+
return True
|
|
20
|
+
if n % 2 == 0 or n % 3 == 0:
|
|
21
|
+
return False
|
|
22
|
+
i = 5
|
|
23
|
+
while i * i <= n:
|
|
24
|
+
if n % i == 0 or n % (i + 2) == 0:
|
|
25
|
+
return False
|
|
26
|
+
i += 6
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def primes_upto(n: int) -> List[int]:
|
|
31
|
+
"""Return all primes <= n using the Sieve of Eratosthenes."""
|
|
32
|
+
_check_int(n)
|
|
33
|
+
if n < 2:
|
|
34
|
+
return []
|
|
35
|
+
sieve = bytearray([1]) * (n + 1)
|
|
36
|
+
sieve[0:2] = b"\x00\x00"
|
|
37
|
+
for i in range(2, int(n ** 0.5) + 1):
|
|
38
|
+
if sieve[i]:
|
|
39
|
+
sieve[i * i :: i] = bytearray(len(range(i * i, n + 1, i)))
|
|
40
|
+
return [i for i, flag in enumerate(sieve) if flag]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def prime_factors(n: int) -> List[int]:
|
|
44
|
+
"""Return the prime factorisation of ``n`` (with repetition)."""
|
|
45
|
+
_check_int(n, minimum=2)
|
|
46
|
+
factors, d = [], 2
|
|
47
|
+
while d * d <= n:
|
|
48
|
+
while n % d == 0:
|
|
49
|
+
factors.append(d)
|
|
50
|
+
n //= d
|
|
51
|
+
d += 1 if d == 2 else 2
|
|
52
|
+
if n > 1:
|
|
53
|
+
factors.append(n)
|
|
54
|
+
return factors
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def fibonacci(count: int) -> List[int]:
|
|
58
|
+
"""Return the first ``count`` Fibonacci numbers."""
|
|
59
|
+
_check_int(count, "count")
|
|
60
|
+
seq: List[int] = []
|
|
61
|
+
a, b = 0, 1
|
|
62
|
+
for _ in range(count):
|
|
63
|
+
seq.append(a)
|
|
64
|
+
a, b = b, a + b
|
|
65
|
+
return seq
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def factorial(n: int) -> int:
|
|
69
|
+
"""Return n! (iteratively, so no recursion limit)."""
|
|
70
|
+
_check_int(n)
|
|
71
|
+
result = 1
|
|
72
|
+
for i in range(2, n + 1):
|
|
73
|
+
result *= i
|
|
74
|
+
return result
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def gcd(a: int, b: int) -> int:
|
|
78
|
+
"""Greatest common divisor (Euclid's algorithm)."""
|
|
79
|
+
_check_int(a, "a", minimum=-(10**18))
|
|
80
|
+
_check_int(b, "b", minimum=-(10**18))
|
|
81
|
+
a, b = abs(a), abs(b)
|
|
82
|
+
while b:
|
|
83
|
+
a, b = b, a % b
|
|
84
|
+
return a
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def lcm(a: int, b: int) -> int:
|
|
88
|
+
"""Least common multiple."""
|
|
89
|
+
if a == 0 or b == 0:
|
|
90
|
+
return 0
|
|
91
|
+
return abs(a * b) // gcd(a, b)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def collatz_steps(n: int) -> int:
|
|
95
|
+
"""Number of steps for ``n`` to reach 1 in the Collatz sequence."""
|
|
96
|
+
_check_int(n, minimum=1)
|
|
97
|
+
steps = 0
|
|
98
|
+
while n != 1:
|
|
99
|
+
n = n // 2 if n % 2 == 0 else 3 * n + 1
|
|
100
|
+
steps += 1
|
|
101
|
+
return steps
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Descriptive statistics without any third-party dependency."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from collections import Counter
|
|
5
|
+
from typing import Dict, List, Sequence
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _prepare(data: Sequence[float]) -> List[float]:
|
|
9
|
+
values = list(data)
|
|
10
|
+
if not values:
|
|
11
|
+
raise ValueError("data must not be empty")
|
|
12
|
+
return values
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def mean(data: Sequence[float]) -> float:
|
|
16
|
+
values = _prepare(data)
|
|
17
|
+
return sum(values) / len(values)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def median(data: Sequence[float]) -> float:
|
|
21
|
+
values = sorted(_prepare(data))
|
|
22
|
+
mid = len(values) // 2
|
|
23
|
+
if len(values) % 2:
|
|
24
|
+
return float(values[mid])
|
|
25
|
+
return (values[mid - 1] + values[mid]) / 2
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def mode(data: Sequence[float]) -> List[float]:
|
|
29
|
+
"""Return every most-frequent value (sorted)."""
|
|
30
|
+
counts = Counter(_prepare(data))
|
|
31
|
+
top = max(counts.values())
|
|
32
|
+
return sorted(v for v, c in counts.items() if c == top)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def variance(data: Sequence[float], sample: bool = False) -> float:
|
|
36
|
+
values = _prepare(data)
|
|
37
|
+
if sample and len(values) < 2:
|
|
38
|
+
raise ValueError("sample variance needs at least two values")
|
|
39
|
+
m = mean(values)
|
|
40
|
+
ss = sum((x - m) ** 2 for x in values)
|
|
41
|
+
return ss / (len(values) - 1 if sample else len(values))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def std_dev(data: Sequence[float], sample: bool = False) -> float:
|
|
45
|
+
return math.sqrt(variance(data, sample))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def describe(data: Sequence[float]) -> Dict[str, object]:
|
|
49
|
+
"""Return a summary dictionary of the data."""
|
|
50
|
+
values = _prepare(data)
|
|
51
|
+
return {
|
|
52
|
+
"count": len(values),
|
|
53
|
+
"min": min(values),
|
|
54
|
+
"max": max(values),
|
|
55
|
+
"mean": mean(values),
|
|
56
|
+
"median": median(values),
|
|
57
|
+
"mode": mode(values),
|
|
58
|
+
"std_dev": std_dev(values),
|
|
59
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Text-processing helpers."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import string
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def word_count(text: str) -> int:
|
|
8
|
+
"""Count the words in ``text``."""
|
|
9
|
+
return len(re.findall(r"\b[\w']+\b", text))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def vowel_count(text: str) -> int:
|
|
13
|
+
"""Count the vowels (a, e, i, o, u) in ``text``."""
|
|
14
|
+
return sum(1 for ch in text.lower() if ch in "aeiou")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_palindrome(text: str) -> bool:
|
|
18
|
+
"""Case-, space- and punctuation-insensitive palindrome check."""
|
|
19
|
+
cleaned = [ch.lower() for ch in text if ch.isalnum()]
|
|
20
|
+
return cleaned == cleaned[::-1]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def reverse_words(text: str) -> str:
|
|
24
|
+
"""Reverse the order of the words in ``text``."""
|
|
25
|
+
return " ".join(reversed(text.split()))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def caesar(text: str, shift: int) -> str:
|
|
29
|
+
"""Apply a Caesar cipher. Use a negative shift to decrypt."""
|
|
30
|
+
shift %= 26
|
|
31
|
+
lower, upper = string.ascii_lowercase, string.ascii_uppercase
|
|
32
|
+
table = str.maketrans(
|
|
33
|
+
lower + upper,
|
|
34
|
+
lower[shift:] + lower[:shift] + upper[shift:] + upper[:shift],
|
|
35
|
+
)
|
|
36
|
+
return text.translate(table)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def slugify(text: str) -> str:
|
|
40
|
+
"""Turn ``text`` into a URL-friendly slug."""
|
|
41
|
+
text = re.sub(r"[^\w\s-]", "", text.lower())
|
|
42
|
+
return re.sub(r"[\s_-]+", "-", text).strip("-")
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from ctoolkit import (
|
|
4
|
+
caesar, collatz_steps, describe, factorial, fibonacci, gcd, is_palindrome,
|
|
5
|
+
is_prime, lcm, mean, median, mode, prime_factors, primes_upto, reverse_words,
|
|
6
|
+
slugify, std_dev, vowel_count, word_count,
|
|
7
|
+
)
|
|
8
|
+
import ctoolkit
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestMath(unittest.TestCase):
|
|
12
|
+
def test_identity(self):
|
|
13
|
+
self.assertEqual(ctoolkit.__author__, "Chiranjeev")
|
|
14
|
+
self.assertEqual(ctoolkit.__roll_no__, "2432709")
|
|
15
|
+
|
|
16
|
+
def test_is_prime(self):
|
|
17
|
+
self.assertEqual([n for n in range(20) if is_prime(n)], [2, 3, 5, 7, 11, 13, 17, 19])
|
|
18
|
+
self.assertFalse(is_prime(-7))
|
|
19
|
+
|
|
20
|
+
def test_primes_upto(self):
|
|
21
|
+
self.assertEqual(primes_upto(30), [2, 3, 5, 7, 11, 13, 17, 19, 23, 29])
|
|
22
|
+
self.assertEqual(primes_upto(1), [])
|
|
23
|
+
|
|
24
|
+
def test_prime_factors(self):
|
|
25
|
+
self.assertEqual(prime_factors(360), [2, 2, 2, 3, 3, 5])
|
|
26
|
+
self.assertEqual(prime_factors(97), [97])
|
|
27
|
+
|
|
28
|
+
def test_fibonacci(self):
|
|
29
|
+
self.assertEqual(fibonacci(8), [0, 1, 1, 2, 3, 5, 8, 13])
|
|
30
|
+
self.assertEqual(fibonacci(0), [])
|
|
31
|
+
|
|
32
|
+
def test_factorial_gcd_lcm(self):
|
|
33
|
+
self.assertEqual(factorial(5), 120)
|
|
34
|
+
self.assertEqual(gcd(48, 18), 6)
|
|
35
|
+
self.assertEqual(lcm(4, 6), 12)
|
|
36
|
+
|
|
37
|
+
def test_collatz(self):
|
|
38
|
+
self.assertEqual(collatz_steps(27), 111)
|
|
39
|
+
|
|
40
|
+
def test_errors(self):
|
|
41
|
+
with self.assertRaises(ValueError):
|
|
42
|
+
factorial(-1)
|
|
43
|
+
with self.assertRaises(TypeError):
|
|
44
|
+
fibonacci("3")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestText(unittest.TestCase):
|
|
48
|
+
def test_basic(self):
|
|
49
|
+
self.assertEqual(word_count("Hello, world! It's me."), 4)
|
|
50
|
+
self.assertEqual(vowel_count("Chiranjeev"), 4)
|
|
51
|
+
self.assertTrue(is_palindrome("A man, a plan, a canal: Panama"))
|
|
52
|
+
self.assertEqual(reverse_words("one two three"), "three two one")
|
|
53
|
+
self.assertEqual(slugify("Hello, World! 2026"), "hello-world-2026")
|
|
54
|
+
|
|
55
|
+
def test_caesar_roundtrip(self):
|
|
56
|
+
self.assertEqual(caesar("Hello, World!", 3), "Khoor, Zruog!")
|
|
57
|
+
self.assertEqual(caesar(caesar("Hello", 7), -7), "Hello")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestStats(unittest.TestCase):
|
|
61
|
+
def test_stats(self):
|
|
62
|
+
data = [2, 4, 4, 4, 5, 5, 7, 9]
|
|
63
|
+
self.assertEqual(mean(data), 5)
|
|
64
|
+
self.assertEqual(median(data), 4.5)
|
|
65
|
+
self.assertEqual(mode(data), [4])
|
|
66
|
+
self.assertEqual(std_dev(data), 2)
|
|
67
|
+
self.assertEqual(describe(data)["count"], 8)
|
|
68
|
+
|
|
69
|
+
def test_empty(self):
|
|
70
|
+
with self.assertRaises(ValueError):
|
|
71
|
+
mean([])
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
unittest.main()
|