math-step-to-step 0.2.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.
- math_step_to_step-0.2.0/LICENSE +21 -0
- math_step_to_step-0.2.0/PKG-INFO +165 -0
- math_step_to_step-0.2.0/README.md +138 -0
- math_step_to_step-0.2.0/pyproject.toml +45 -0
- math_step_to_step-0.2.0/setup.cfg +4 -0
- math_step_to_step-0.2.0/src/math_step_to_step/__init__.py +10 -0
- math_step_to_step-0.2.0/src/math_step_to_step/__main__.py +3 -0
- math_step_to_step-0.2.0/src/math_step_to_step/cli.py +41 -0
- math_step_to_step-0.2.0/src/math_step_to_step/context.py +23 -0
- math_step_to_step-0.2.0/src/math_step_to_step/engine.py +413 -0
- math_step_to_step-0.2.0/src/math_step_to_step/errors.py +74 -0
- math_step_to_step-0.2.0/src/math_step_to_step/fmt.py +183 -0
- math_step_to_step-0.2.0/src/math_step_to_step/fonts/LICENSE_STIX +124 -0
- math_step_to_step-0.2.0/src/math_step_to_step/fonts/STIXGeneral.ttf +0 -0
- math_step_to_step-0.2.0/src/math_step_to_step/fonts/STIXGeneralItalic.ttf +0 -0
- math_step_to_step-0.2.0/src/math_step_to_step/mathlabel.py +127 -0
- math_step_to_step-0.2.0/src/math_step_to_step/mathrender.py +876 -0
- math_step_to_step-0.2.0/src/math_step_to_step/nodes.py +156 -0
- math_step_to_step-0.2.0/src/math_step_to_step/parser.py +257 -0
- math_step_to_step-0.2.0/src/math_step_to_step/poly.py +91 -0
- math_step_to_step-0.2.0/src/math_step_to_step/py.typed +0 -0
- math_step_to_step-0.2.0/src/math_step_to_step/solver.py +241 -0
- math_step_to_step-0.2.0/src/math_step_to_step/symbols.py +166 -0
- math_step_to_step-0.2.0/src/math_step_to_step.egg-info/PKG-INFO +165 -0
- math_step_to_step-0.2.0/src/math_step_to_step.egg-info/SOURCES.txt +31 -0
- math_step_to_step-0.2.0/src/math_step_to_step.egg-info/dependency_links.txt +1 -0
- math_step_to_step-0.2.0/src/math_step_to_step.egg-info/entry_points.txt +2 -0
- math_step_to_step-0.2.0/src/math_step_to_step.egg-info/requires.txt +4 -0
- math_step_to_step-0.2.0/src/math_step_to_step.egg-info/top_level.txt +1 -0
- math_step_to_step-0.2.0/tests/test_fuzz.py +58 -0
- math_step_to_step-0.2.0/tests/test_mathrender.py +61 -0
- math_step_to_step-0.2.0/tests/test_new.py +55 -0
- math_step_to_step-0.2.0/tests/test_steps.py +133 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VVT
|
|
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,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: math-step-to-step
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Step-by-step solutions for math expressions and equations (1 + 2 x 3 -> ['1 + 6', '7']), plus text-to-symbol helpers (6^6 -> 6⁶).
|
|
5
|
+
Author: VVT
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: math,steps,solver,equation,calculator,superscript,education
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Education
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Education
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: gui
|
|
24
|
+
Requires-Dist: pillow>=10.1; extra == "gui"
|
|
25
|
+
Requires-Dist: customtkinter>=5.2; extra == "gui"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# math-step-to-step
|
|
29
|
+
|
|
30
|
+
Giải biểu thức / phương trình **từng bước** và trả về danh sách các bước.
|
|
31
|
+
Không phụ thuộc thư viện ngoài, dùng phân số chính xác (`fractions.Fraction`) nên không bị sai số.
|
|
32
|
+
|
|
33
|
+
*Step-by-step solver for math expressions and equations. Pure Python, zero dependencies, exact arithmetic.*
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install math-step-to-step
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Dùng nhanh
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from math_step_to_step import steps, solve, evaluate, prettify
|
|
43
|
+
|
|
44
|
+
steps("1 + 2 x 3") # ['1 + 6', '7']
|
|
45
|
+
steps("(1 + 2) x 3 + 4 x 5") # ['3 × 3 + 4 × 5', '9 + 20', '29']
|
|
46
|
+
steps("2 + 3 x (4 - 1)^2") # ['2 + 3 × 3²', '2 + 3 × 9', '2 + 27', '29']
|
|
47
|
+
steps("1/2 + 1/3") # ['1/2 + 1/3', '5/6']
|
|
48
|
+
|
|
49
|
+
# tìm x
|
|
50
|
+
steps("2x + 3 = 7") # ['2x = 7 - 3', '2x = 4', 'x = 4 ÷ 2', 'x = 2']
|
|
51
|
+
steps("2(x + 3) = 10") # ['2x + 6 = 10', '2x = 10 - 6', '2x = 4', 'x = 4 ÷ 2', 'x = 2']
|
|
52
|
+
steps("x^2 - 3x + 2 = 0") # a, b, c -> Δ -> x₁, x₂ (từng bước)
|
|
53
|
+
|
|
54
|
+
# kết quả có cấu trúc
|
|
55
|
+
sol = solve("x^2 - 3x + 2 = 0")
|
|
56
|
+
sol.kind, sol.values # ('roots', (2, 1))
|
|
57
|
+
|
|
58
|
+
evaluate("2 + 3 x 4") # 14 (nhanh, không tạo chuỗi bước)
|
|
59
|
+
|
|
60
|
+
# đổi chuỗi thành ký hiệu
|
|
61
|
+
prettify("6^6") # '6⁶'
|
|
62
|
+
prettify("x^2 + y^(n+1) <= sqrt(2)*pi") # 'x² + yⁿ⁺¹ ≤ √2×π'
|
|
63
|
+
prettify("a//b") # 'ᵃ√b' (căn bậc a của b)
|
|
64
|
+
prettify("(a/b)") # 'a⁄b' (a phần b); prettify("(3/7)") -> '³⁄₇'
|
|
65
|
+
prettify("a^b^c") # 'aᵇᶜ' (lũy thừa chồng lũy thừa)
|
|
66
|
+
prettify("a**") # 'a▫' (thiếu số mũ -> ô vuông rỗng)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Cú pháp nhập
|
|
70
|
+
|
|
71
|
+
| Viết | Ý nghĩa |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `+ - * x × · / ÷ :` | cộng, trừ, nhân, chia |
|
|
74
|
+
| `x` giữa hai toán hạng, vd `2 x 3` | **phép nhân**; còn `2x`, `x + 1`, `x²` thì `x` là **biến** |
|
|
75
|
+
| `^`, `**`, `²` `³` | lũy thừa (số mũ nguyên) |
|
|
76
|
+
| `sqrt(…)`, `√9`, `//9` | căn bậc hai |
|
|
77
|
+
| `a//b` | căn bậc **a** của **b** (`3//27` = 3) |
|
|
78
|
+
| `(a/b)` | phân số "a phần b" (hai vế là một số/biến/lũy thừa/nhóm trong ngoặc, vd `((1+2)/(3+4))`) |
|
|
79
|
+
| `pi`, `π` | số π = 3.14159265359 (`2pi`, `a*pi`, `api` = a × π) |
|
|
80
|
+
| `( )`, `[ ]`, `{ }` | ngoặc |
|
|
81
|
+
| `2x`, `3(x+1)`, `(a)(b)` | nhân ngầm |
|
|
82
|
+
| `1,5` hoặc `1.5` | số thập phân |
|
|
83
|
+
| `=` | phương trình (1 biến, bậc 1 hoặc 2) |
|
|
84
|
+
|
|
85
|
+
## Tùy chọn của `steps()` / `solve()`
|
|
86
|
+
|
|
87
|
+
| Tham số | Mặc định | Ý nghĩa |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `include_original` | `False` | thêm biểu thức gốc làm bước đầu |
|
|
90
|
+
| `detailed` | `False` | mỗi bước chỉ làm 1 phép (`1+2+3` → `3 + 3` → `6`) thay vì gộp |
|
|
91
|
+
| `pretty` | `True` | dùng `× ÷ ² √`; `False` ra ASCII (`* / ^ sqrt()`), có thể đưa lại vào thư viện |
|
|
92
|
+
| `style` | `"auto"` | `"fraction"`, `"decimal"` hoặc `"auto"` (thập phân nếu đầu vào có số thập phân) |
|
|
93
|
+
| `lang` | `"vi"` | ngôn ngữ thông báo: `"vi"` / `"en"` |
|
|
94
|
+
|
|
95
|
+
Cách chọn bước: ngoặc trước → lũy thừa/căn → nhân chia → cộng trừ; các phép độc lập được làm **cùng một bước**
|
|
96
|
+
và chuỗi cộng/nhân liên tiếp được gộp để lời giải ngắn nhất. Với phương trình: rút gọn → chuyển vế → chia hệ số;
|
|
97
|
+
bậc 2 dùng Δ.
|
|
98
|
+
|
|
99
|
+
## Ký hiệu
|
|
100
|
+
|
|
101
|
+
`prettify()` đổi `^`→mũ trên, `a//b`→`ᵃ√b`, `(a/b)`→`a⁄b`, thiếu phần nào thì hiện `▫`
|
|
102
|
+
(đổi được bằng `placeholder="□"`). Mũ chồng mũ `a^b^c` hiện `aᵇᶜ`; với số như `2^3^2`
|
|
103
|
+
sẽ hiện `2³²` (dễ đọc nhầm), nên khi cần rõ ràng hãy dùng `steps("2^3^2", include_original=True)`
|
|
104
|
+
→ `2^(3²)`.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from math_step_to_step import superscript, subscript, prettify
|
|
108
|
+
superscript("123") # '¹²³'
|
|
109
|
+
subscript("12") # '₁₂'
|
|
110
|
+
prettify("H_2O, 6^6, x^(n+1), 1/2, a<=b, a!=b, +-3, alpha")
|
|
111
|
+
# 'H₂O, 6⁶, xⁿ⁺¹, ½, a≤b, a≠b, ±3, α'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Hiển thị như trong sách giáo khoa (CTkLabel)
|
|
115
|
+
|
|
116
|
+
`prettify()` chỉ đổi sang ký tự Unicode nên mũ/phân số/căn bị giới hạn bởi font. Muốn đúng ký hiệu
|
|
117
|
+
lớp học (phân số có gạch ngang, mũ & chỉ số thật, căn có gạch trên đầu, ngoặc co giãn theo chiều cao,
|
|
118
|
+
biến in nghiêng) thì dùng `MathLabel` — bản thân nó là một `CTkLabel` (biểu thức được vẽ thành ảnh
|
|
119
|
+
rồi gán vào `image=`, tự đổi màu sáng/tối, nét sắc trên màn hình HiDPI, kèm sẵn font STIX nên máy nào cũng giống nhau):
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install "math-step-to-step[gui]" # pillow + customtkinter
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import customtkinter as ctk
|
|
127
|
+
from math_step_to_step import steps
|
|
128
|
+
from math_step_to_step.mathlabel import MathLabel
|
|
129
|
+
|
|
130
|
+
root = ctk.CTk()
|
|
131
|
+
MathLabel(root, "x_1 = (-b +- sqrt(b^2 - 4ac)) / (2a)", font_size=26).pack(padx=20, pady=20)
|
|
132
|
+
for st in steps("2 + 3 x (4 - 1)^2", pretty=False): # pretty=False: ASCII, dễ vẽ nhất
|
|
133
|
+
MathLabel(root, st).pack(anchor="w", padx=20)
|
|
134
|
+
root.mainloop()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Cú pháp giống `prettify` (và nhận cả đầu ra Unicode của `steps()` như `x²`, `√3`, `³√8`):
|
|
138
|
+
|
|
139
|
+
| Viết | Hiện |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `(a/b)`, `a/b`, `(x+1)/(x-1)`, `(3+1) / 2` | phân số xếp chồng (không có chuỗi `a/b/c`) |
|
|
142
|
+
| `(1/2)^2`, `3(1/2)` | giữ ngoặc, ngoặc tự cao theo phân số |
|
|
143
|
+
| `x^2`, `x^(n+1)`, `a^b^c` | mũ thật; `a^b^c` = tháp mũ a^(b^c) |
|
|
144
|
+
| `x_1`, `x_(n+1)`, `x_1^2` | chỉ số dưới / cả hai |
|
|
145
|
+
| `sqrt(x)`, `//x`, `√x`, `a//b` | căn có gạch trên đầu; `a//b` có chỉ số căn |
|
|
146
|
+
| `*`, `-`, `<=`, `>=`, `!=`, `+-`, `=>`, `pi` | `×`, `−`, `≤`, `≥`, `≠`, `±`, `⇒`, `π` |
|
|
147
|
+
| `a^`, `a**`, `a//`, `(a/)` | thiếu phần nào hiện ô vuông rỗng □ |
|
|
148
|
+
|
|
149
|
+
Đổi nội dung: `label.set_math("...")` hoặc `label.configure(math=..., font_size=..., text_color=("#000", "#fff"))`.
|
|
150
|
+
Chỉ cần ảnh (PIL) thì dùng `from math_step_to_step.mathrender import render_math`.
|
|
151
|
+
Chạy thử: `python examples/gui_demo.py`.
|
|
152
|
+
|
|
153
|
+
## Giới hạn (cố ý, để ít lỗi)
|
|
154
|
+
Một biến; phương trình bậc ≤ 2; số mũ nguyên; không có số phức; không rút gọn căn phức tạp
|
|
155
|
+
(căn không chính phương được rút gọn dạng `2 × √3` và cho giá trị gần đúng). Đầu vào sai/không hỗ trợ
|
|
156
|
+
luôn ném `MathStepError` (kế thừa `ValueError`) với thông báo rõ ràng.
|
|
157
|
+
|
|
158
|
+
## Dòng lệnh
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
math-step-to-step "(1 + 2) x 3 + 4 x 5"
|
|
162
|
+
python -m math_step_to_step "2x + 3 = 7" --en
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
MIT License.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# math-step-to-step
|
|
2
|
+
|
|
3
|
+
Giải biểu thức / phương trình **từng bước** và trả về danh sách các bước.
|
|
4
|
+
Không phụ thuộc thư viện ngoài, dùng phân số chính xác (`fractions.Fraction`) nên không bị sai số.
|
|
5
|
+
|
|
6
|
+
*Step-by-step solver for math expressions and equations. Pure Python, zero dependencies, exact arithmetic.*
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install math-step-to-step
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Dùng nhanh
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from math_step_to_step import steps, solve, evaluate, prettify
|
|
16
|
+
|
|
17
|
+
steps("1 + 2 x 3") # ['1 + 6', '7']
|
|
18
|
+
steps("(1 + 2) x 3 + 4 x 5") # ['3 × 3 + 4 × 5', '9 + 20', '29']
|
|
19
|
+
steps("2 + 3 x (4 - 1)^2") # ['2 + 3 × 3²', '2 + 3 × 9', '2 + 27', '29']
|
|
20
|
+
steps("1/2 + 1/3") # ['1/2 + 1/3', '5/6']
|
|
21
|
+
|
|
22
|
+
# tìm x
|
|
23
|
+
steps("2x + 3 = 7") # ['2x = 7 - 3', '2x = 4', 'x = 4 ÷ 2', 'x = 2']
|
|
24
|
+
steps("2(x + 3) = 10") # ['2x + 6 = 10', '2x = 10 - 6', '2x = 4', 'x = 4 ÷ 2', 'x = 2']
|
|
25
|
+
steps("x^2 - 3x + 2 = 0") # a, b, c -> Δ -> x₁, x₂ (từng bước)
|
|
26
|
+
|
|
27
|
+
# kết quả có cấu trúc
|
|
28
|
+
sol = solve("x^2 - 3x + 2 = 0")
|
|
29
|
+
sol.kind, sol.values # ('roots', (2, 1))
|
|
30
|
+
|
|
31
|
+
evaluate("2 + 3 x 4") # 14 (nhanh, không tạo chuỗi bước)
|
|
32
|
+
|
|
33
|
+
# đổi chuỗi thành ký hiệu
|
|
34
|
+
prettify("6^6") # '6⁶'
|
|
35
|
+
prettify("x^2 + y^(n+1) <= sqrt(2)*pi") # 'x² + yⁿ⁺¹ ≤ √2×π'
|
|
36
|
+
prettify("a//b") # 'ᵃ√b' (căn bậc a của b)
|
|
37
|
+
prettify("(a/b)") # 'a⁄b' (a phần b); prettify("(3/7)") -> '³⁄₇'
|
|
38
|
+
prettify("a^b^c") # 'aᵇᶜ' (lũy thừa chồng lũy thừa)
|
|
39
|
+
prettify("a**") # 'a▫' (thiếu số mũ -> ô vuông rỗng)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Cú pháp nhập
|
|
43
|
+
|
|
44
|
+
| Viết | Ý nghĩa |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `+ - * x × · / ÷ :` | cộng, trừ, nhân, chia |
|
|
47
|
+
| `x` giữa hai toán hạng, vd `2 x 3` | **phép nhân**; còn `2x`, `x + 1`, `x²` thì `x` là **biến** |
|
|
48
|
+
| `^`, `**`, `²` `³` | lũy thừa (số mũ nguyên) |
|
|
49
|
+
| `sqrt(…)`, `√9`, `//9` | căn bậc hai |
|
|
50
|
+
| `a//b` | căn bậc **a** của **b** (`3//27` = 3) |
|
|
51
|
+
| `(a/b)` | phân số "a phần b" (hai vế là một số/biến/lũy thừa/nhóm trong ngoặc, vd `((1+2)/(3+4))`) |
|
|
52
|
+
| `pi`, `π` | số π = 3.14159265359 (`2pi`, `a*pi`, `api` = a × π) |
|
|
53
|
+
| `( )`, `[ ]`, `{ }` | ngoặc |
|
|
54
|
+
| `2x`, `3(x+1)`, `(a)(b)` | nhân ngầm |
|
|
55
|
+
| `1,5` hoặc `1.5` | số thập phân |
|
|
56
|
+
| `=` | phương trình (1 biến, bậc 1 hoặc 2) |
|
|
57
|
+
|
|
58
|
+
## Tùy chọn của `steps()` / `solve()`
|
|
59
|
+
|
|
60
|
+
| Tham số | Mặc định | Ý nghĩa |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `include_original` | `False` | thêm biểu thức gốc làm bước đầu |
|
|
63
|
+
| `detailed` | `False` | mỗi bước chỉ làm 1 phép (`1+2+3` → `3 + 3` → `6`) thay vì gộp |
|
|
64
|
+
| `pretty` | `True` | dùng `× ÷ ² √`; `False` ra ASCII (`* / ^ sqrt()`), có thể đưa lại vào thư viện |
|
|
65
|
+
| `style` | `"auto"` | `"fraction"`, `"decimal"` hoặc `"auto"` (thập phân nếu đầu vào có số thập phân) |
|
|
66
|
+
| `lang` | `"vi"` | ngôn ngữ thông báo: `"vi"` / `"en"` |
|
|
67
|
+
|
|
68
|
+
Cách chọn bước: ngoặc trước → lũy thừa/căn → nhân chia → cộng trừ; các phép độc lập được làm **cùng một bước**
|
|
69
|
+
và chuỗi cộng/nhân liên tiếp được gộp để lời giải ngắn nhất. Với phương trình: rút gọn → chuyển vế → chia hệ số;
|
|
70
|
+
bậc 2 dùng Δ.
|
|
71
|
+
|
|
72
|
+
## Ký hiệu
|
|
73
|
+
|
|
74
|
+
`prettify()` đổi `^`→mũ trên, `a//b`→`ᵃ√b`, `(a/b)`→`a⁄b`, thiếu phần nào thì hiện `▫`
|
|
75
|
+
(đổi được bằng `placeholder="□"`). Mũ chồng mũ `a^b^c` hiện `aᵇᶜ`; với số như `2^3^2`
|
|
76
|
+
sẽ hiện `2³²` (dễ đọc nhầm), nên khi cần rõ ràng hãy dùng `steps("2^3^2", include_original=True)`
|
|
77
|
+
→ `2^(3²)`.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from math_step_to_step import superscript, subscript, prettify
|
|
81
|
+
superscript("123") # '¹²³'
|
|
82
|
+
subscript("12") # '₁₂'
|
|
83
|
+
prettify("H_2O, 6^6, x^(n+1), 1/2, a<=b, a!=b, +-3, alpha")
|
|
84
|
+
# 'H₂O, 6⁶, xⁿ⁺¹, ½, a≤b, a≠b, ±3, α'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Hiển thị như trong sách giáo khoa (CTkLabel)
|
|
88
|
+
|
|
89
|
+
`prettify()` chỉ đổi sang ký tự Unicode nên mũ/phân số/căn bị giới hạn bởi font. Muốn đúng ký hiệu
|
|
90
|
+
lớp học (phân số có gạch ngang, mũ & chỉ số thật, căn có gạch trên đầu, ngoặc co giãn theo chiều cao,
|
|
91
|
+
biến in nghiêng) thì dùng `MathLabel` — bản thân nó là một `CTkLabel` (biểu thức được vẽ thành ảnh
|
|
92
|
+
rồi gán vào `image=`, tự đổi màu sáng/tối, nét sắc trên màn hình HiDPI, kèm sẵn font STIX nên máy nào cũng giống nhau):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install "math-step-to-step[gui]" # pillow + customtkinter
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import customtkinter as ctk
|
|
100
|
+
from math_step_to_step import steps
|
|
101
|
+
from math_step_to_step.mathlabel import MathLabel
|
|
102
|
+
|
|
103
|
+
root = ctk.CTk()
|
|
104
|
+
MathLabel(root, "x_1 = (-b +- sqrt(b^2 - 4ac)) / (2a)", font_size=26).pack(padx=20, pady=20)
|
|
105
|
+
for st in steps("2 + 3 x (4 - 1)^2", pretty=False): # pretty=False: ASCII, dễ vẽ nhất
|
|
106
|
+
MathLabel(root, st).pack(anchor="w", padx=20)
|
|
107
|
+
root.mainloop()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Cú pháp giống `prettify` (và nhận cả đầu ra Unicode của `steps()` như `x²`, `√3`, `³√8`):
|
|
111
|
+
|
|
112
|
+
| Viết | Hiện |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `(a/b)`, `a/b`, `(x+1)/(x-1)`, `(3+1) / 2` | phân số xếp chồng (không có chuỗi `a/b/c`) |
|
|
115
|
+
| `(1/2)^2`, `3(1/2)` | giữ ngoặc, ngoặc tự cao theo phân số |
|
|
116
|
+
| `x^2`, `x^(n+1)`, `a^b^c` | mũ thật; `a^b^c` = tháp mũ a^(b^c) |
|
|
117
|
+
| `x_1`, `x_(n+1)`, `x_1^2` | chỉ số dưới / cả hai |
|
|
118
|
+
| `sqrt(x)`, `//x`, `√x`, `a//b` | căn có gạch trên đầu; `a//b` có chỉ số căn |
|
|
119
|
+
| `*`, `-`, `<=`, `>=`, `!=`, `+-`, `=>`, `pi` | `×`, `−`, `≤`, `≥`, `≠`, `±`, `⇒`, `π` |
|
|
120
|
+
| `a^`, `a**`, `a//`, `(a/)` | thiếu phần nào hiện ô vuông rỗng □ |
|
|
121
|
+
|
|
122
|
+
Đổi nội dung: `label.set_math("...")` hoặc `label.configure(math=..., font_size=..., text_color=("#000", "#fff"))`.
|
|
123
|
+
Chỉ cần ảnh (PIL) thì dùng `from math_step_to_step.mathrender import render_math`.
|
|
124
|
+
Chạy thử: `python examples/gui_demo.py`.
|
|
125
|
+
|
|
126
|
+
## Giới hạn (cố ý, để ít lỗi)
|
|
127
|
+
Một biến; phương trình bậc ≤ 2; số mũ nguyên; không có số phức; không rút gọn căn phức tạp
|
|
128
|
+
(căn không chính phương được rút gọn dạng `2 × √3` và cho giá trị gần đúng). Đầu vào sai/không hỗ trợ
|
|
129
|
+
luôn ném `MathStepError` (kế thừa `ValueError`) với thông báo rõ ràng.
|
|
130
|
+
|
|
131
|
+
## Dòng lệnh
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
math-step-to-step "(1 + 2) x 3 + 4 x 5"
|
|
135
|
+
python -m math_step_to_step "2x + 3 = 7" --en
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
MIT License.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "math-step-to-step"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Step-by-step solutions for math expressions and equations (1 + 2 x 3 -> ['1 + 6', '7']), plus text-to-symbol helpers (6^6 -> 6⁶)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "VVT" }]
|
|
14
|
+
keywords = ["math", "steps", "solver", "equation", "calculator", "superscript", "education"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Education",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Education",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
dependencies = []
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
gui = ["pillow>=10.1", "customtkinter>=5.2"]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
math-step-to-step = "math_step_to_step.cli:main"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.package-data]
|
|
41
|
+
math_step_to_step = ["py.typed", "fonts/*"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""math-step-to-step: step-by-step solutions for math expressions and equations."""
|
|
2
|
+
from .errors import MathStepError
|
|
3
|
+
from .solver import Solution, evaluate, solve, steps
|
|
4
|
+
from .symbols import prettify, subscript, superscript
|
|
5
|
+
|
|
6
|
+
__version__ = "0.2.0"
|
|
7
|
+
__all__ = [
|
|
8
|
+
"steps", "solve", "evaluate", "Solution", "MathStepError",
|
|
9
|
+
"superscript", "subscript", "prettify", "__version__",
|
|
10
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Command line: math-step-to-step "1 + 2 x 3"."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from . import __version__
|
|
8
|
+
from .errors import MathStepError
|
|
9
|
+
from .solver import solve
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(argv=None):
|
|
13
|
+
ap = argparse.ArgumentParser(prog="math-step-to-step", description=__doc__)
|
|
14
|
+
ap.add_argument("expression", nargs="*", help="expression; omit to read lines from stdin")
|
|
15
|
+
ap.add_argument("-d", "--detailed", action="store_true", help="one operation per step")
|
|
16
|
+
ap.add_argument("--ascii", action="store_true", help="plain ASCII output")
|
|
17
|
+
ap.add_argument("--en", action="store_true", help="English messages")
|
|
18
|
+
ap.add_argument("--style", choices=["auto", "fraction", "decimal"], default="auto")
|
|
19
|
+
ap.add_argument("-V", "--version", action="version", version=__version__)
|
|
20
|
+
a = ap.parse_args(argv)
|
|
21
|
+
|
|
22
|
+
lines = [" ".join(a.expression)] if a.expression else (l.strip() for l in sys.stdin)
|
|
23
|
+
code = 0
|
|
24
|
+
for line in lines:
|
|
25
|
+
if not line:
|
|
26
|
+
continue
|
|
27
|
+
try:
|
|
28
|
+
sol = solve(line, include_original=True, detailed=a.detailed,
|
|
29
|
+
pretty=not a.ascii, style=a.style, lang="en" if a.en else "vi")
|
|
30
|
+
except MathStepError as e:
|
|
31
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
32
|
+
code = 1
|
|
33
|
+
continue
|
|
34
|
+
print(sol.steps[0])
|
|
35
|
+
for s in sol.steps[1:]:
|
|
36
|
+
print(" →", s)
|
|
37
|
+
return code
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Options shared by the parser, engine and formatter."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .errors import LANGS
|
|
5
|
+
|
|
6
|
+
MAX_LEN = 10_000 # max characters of input
|
|
7
|
+
MAX_BITS = 14_000 # max size of any intermediate number (~4200 digits)
|
|
8
|
+
MAX_DEG = 60 # max polynomial degree
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Ctx:
|
|
12
|
+
__slots__ = ("pretty", "detailed", "lang", "style", "decimal")
|
|
13
|
+
|
|
14
|
+
def __init__(self, pretty=True, detailed=False, lang="vi", style="auto"):
|
|
15
|
+
if lang not in LANGS:
|
|
16
|
+
raise ValueError(f"lang must be one of {LANGS}")
|
|
17
|
+
if style not in ("auto", "fraction", "decimal"):
|
|
18
|
+
raise ValueError("style must be 'auto', 'fraction' or 'decimal'")
|
|
19
|
+
self.pretty = bool(pretty)
|
|
20
|
+
self.detailed = bool(detailed)
|
|
21
|
+
self.lang = lang
|
|
22
|
+
self.style = style
|
|
23
|
+
self.decimal = style == "decimal"
|