admin commited on
Commit
cc646d3
·
1 Parent(s): 5e217aa
Files changed (10) hide show
  1. .gitignore +10 -0
  2. app.py +164 -0
  3. output_util.py +14 -0
  4. requirements.txt +4 -0
  5. sgntools.py +35 -0
  6. solution.py +148 -0
  7. solutions/e.py +70 -0
  8. solutions/eq.py +182 -0
  9. solutions/pi.py +140 -0
  10. solutions/pin.py +191 -0
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ .idea/
2
+ .vscode/
3
+ workdir/mathjax-full/
4
+ workdir/res.html
5
+ workdir/pre/mma2sympy_input.txt
6
+ workdir/pre/mma2sympy_output.py
7
+ pre.nb
8
+ *__pycache__*
9
+ test.*
10
+ rename.sh
app.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from sympy.core.numbers import Rational
4
+ from solution import BadInput, solutions
5
+
6
+
7
+ def generate_md(args_dict, name):
8
+ try:
9
+ ans_latex = solutions[name](**args_dict).get_latex_ans()
10
+
11
+ except BadInput as e:
12
+ return f"输入错误, 请输入有效的数字: {e}"
13
+
14
+ return f"注意到 $${ans_latex}$$ 证毕!"
15
+
16
+
17
+ def float_to_fraction(x):
18
+ x_str = "{0:.10f}".format(x).rstrip("0").rstrip(".") # 移除小数点后的无效零
19
+
20
+ # 检查是否为整数
21
+ if "." not in x_str:
22
+ return Rational(x_str), Rational(1)
23
+
24
+ # 分割整数部分和小数部分
25
+ integer_part, decimal_part = x_str.split(".")
26
+ decimal_digits = len(decimal_part)
27
+
28
+ # 构造分子和分母
29
+ numerator = int(integer_part + decimal_part)
30
+ denominator = 10**decimal_digits
31
+
32
+ # 简化分数
33
+ gcd_value = 1
34
+ a = numerator
35
+ b = denominator
36
+ while b != 0:
37
+ a, b = b, a % b
38
+ gcd_value = a
39
+
40
+ p = Rational(numerator // gcd_value)
41
+ q = Rational(denominator // gcd_value)
42
+ return p, q
43
+
44
+
45
+ def infer_pi(p, q):
46
+ if q == 0:
47
+ return "分母不能为 0 !"
48
+
49
+ p, q = float_to_fraction(p / q)
50
+ args_dict = {"p": p, "q": q}
51
+ return generate_md(args_dict, "π")
52
+
53
+
54
+ def infer_e(p, q):
55
+ if q == 0:
56
+ return "分母不能为 0 !"
57
+
58
+ p, q = float_to_fraction(p / q)
59
+ args_dict = {"p": p, "q": q}
60
+ return generate_md(args_dict, "e")
61
+
62
+
63
+ def infer_eq(q1, q2, u, v):
64
+ if q2 == 0 or v == 0:
65
+ return "分母不能为 0 !"
66
+
67
+ q1, q2 = float_to_fraction(q1 / q2)
68
+ u, v = float_to_fraction(u / v)
69
+ args_dict = {"q1": q1, "q2": q2, "u": u, "v": v}
70
+ return generate_md(args_dict, "e^q")
71
+
72
+
73
+ def infer_pin(n: int, p, q):
74
+ if q == 0:
75
+ return "分母不能为 0 !"
76
+
77
+ p, q = float_to_fraction(p / q)
78
+ args_dict = {"n": n, "p": p, "q": q}
79
+ return generate_md(args_dict, "π^n")
80
+
81
+
82
+ if __name__ == "__main__":
83
+ os.chdir(os.path.dirname(__file__))
84
+ for file_name in os.listdir("solutions"):
85
+ if not file_name.endswith(".py"):
86
+ continue
87
+
88
+ __import__(f"solutions.{file_name[:-3]}")
89
+
90
+ with gr.Blocks() as demo:
91
+ gr.Markdown("# “注意到”证明法比较大小")
92
+ with gr.Tabs():
93
+ with gr.TabItem("π"):
94
+ gr.Interface(
95
+ fn=infer_pi,
96
+ inputs=[
97
+ gr.Number(label="p", value=314),
98
+ gr.Number(label="q", value=100),
99
+ ],
100
+ outputs=gr.Markdown(
101
+ value="#### 证明结果",
102
+ show_copy_button=True,
103
+ container=True,
104
+ min_height=122,
105
+ ),
106
+ title="比较 π 与 p/q 大小",
107
+ allow_flagging="never",
108
+ )
109
+
110
+ with gr.TabItem("e"):
111
+ gr.Interface(
112
+ fn=infer_e,
113
+ inputs=[
114
+ gr.Number(label="p", value=2718),
115
+ gr.Number(label="q", value=1000),
116
+ ],
117
+ outputs=gr.Markdown(
118
+ value="#### 证明结果",
119
+ show_copy_button=True,
120
+ container=True,
121
+ min_height=122,
122
+ ),
123
+ title="比较 e 与 p/q 大小",
124
+ allow_flagging="never",
125
+ )
126
+
127
+ with gr.TabItem("e^q"):
128
+ gr.Interface(
129
+ fn=infer_eq,
130
+ inputs=[
131
+ gr.Number(label="p", value=3),
132
+ gr.Number(label="q", value=4),
133
+ gr.Number(label="u", value=2117),
134
+ gr.Number(label="v", value=1000),
135
+ ],
136
+ outputs=gr.Markdown(
137
+ value="#### 证明结果",
138
+ show_copy_button=True,
139
+ container=True,
140
+ min_height=122,
141
+ ),
142
+ title="比较 e^(p/q) 与 u/v 大小",
143
+ allow_flagging="never",
144
+ )
145
+
146
+ with gr.TabItem("π^n"):
147
+ gr.Interface(
148
+ fn=infer_pin,
149
+ inputs=[
150
+ gr.Number(label="n", value=3, step=1),
151
+ gr.Number(label="p", value=31),
152
+ gr.Number(label="q", value=1),
153
+ ],
154
+ outputs=gr.Markdown(
155
+ value="#### 证明结果",
156
+ show_copy_button=True,
157
+ container=True,
158
+ min_height=122,
159
+ ),
160
+ title="比较 π^n 与 p/q 大小",
161
+ allow_flagging="never",
162
+ )
163
+
164
+ demo.launch()
output_util.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy import latex
2
+
3
+
4
+ def to_latex(expr, is_coeff=False):
5
+ return "" if is_coeff and expr == 1 else f"{{{latex(expr)}}}"
6
+
7
+
8
+ def to_latexes(*args, is_coeff=False):
9
+ return [to_latex(i, is_coeff=is_coeff) for i in args]
10
+
11
+
12
+ sign2cmp = {1: ">", -1: "<"}
13
+
14
+ sign2cmp_inv = {1: "<", -1: ">"}
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ sympy~=1.12
2
+ pywebview~=5.3.2
3
+ ttkbootstrap~=1.10.1
4
+ gradio
sgntools.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def sgns(*args):
2
+ for i in args:
3
+ if i < 0:
4
+ break
5
+ else:
6
+ return 1
7
+
8
+ for i in args:
9
+ if i > 0:
10
+ break
11
+ else:
12
+ return -1
13
+
14
+ return 0
15
+
16
+
17
+ def sq_func_sgn(a, b, c):
18
+ r"""
19
+ let f(x) = a+bx+cx^2, x \in [0, 1]
20
+ :return: f(x)>=0? 1; f(x)<=0? -1; 0
21
+ """
22
+ f0 = a
23
+ f1 = a + b + c
24
+ fm = 0
25
+
26
+ if c != 0:
27
+ x_m = -b / (2 * c)
28
+ if 0 <= x_m <= 1:
29
+ fm = a + b * x_m + c * x_m**2
30
+
31
+ return sgns(f0, f1, fm)
32
+
33
+
34
+ def lin_func_sgn(a, b):
35
+ return sgns(a, a + b)
solution.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Iterable, Callable
2
+ from sympy import Eq, linsolve, simplify, latex, Expr, Symbol
3
+
4
+
5
+ class CannotCalculate(Exception):
6
+ pass
7
+
8
+
9
+ class BadInput(Exception):
10
+ pass
11
+
12
+
13
+ CONSTANT_TERM_KEY = "constant_term"
14
+
15
+
16
+ class GetIntegrate:
17
+ integrate_result_classes: dict[str, Expr | None]
18
+
19
+ def integrate_and_separate(
20
+ self, integrate_formula: Expr, integrate_args: tuple
21
+ ) -> dict[str, Expr]:
22
+ """
23
+ 解积分,分离各项
24
+ """
25
+ raise NotImplementedError(
26
+ "该函数已弃用,请使用 GetIntegrateFromData 类。如果想要使用它预处理,见 pre/old_solution.py"
27
+ )
28
+ # # 解积分
29
+ # res = integrate(integrate_formula, integrate_args)
30
+ # res = expand(expand(res))
31
+ # print(f'integrate[0,1] {integrate_formula}={res}')
32
+ #
33
+ # # 分离各项
34
+ # di = {}
35
+ # exprs = []
36
+ # for key, expr in self.integrate_result_classes.items():
37
+ # if key == CONSTANT_TERM_KEY:
38
+ # continue
39
+ # exprs.append(expr)
40
+ # di[key] = res.coeff(expr)
41
+ #
42
+ # constant_term, _ = res.as_independent(*exprs)
43
+ # return {**di, CONSTANT_TERM_KEY: constant_term}
44
+
45
+ def get_integrate_args(self, try_arg):
46
+ """
47
+ :return: integrand_function, (independent_variable, lower_limit, upper_limit)
48
+ 被积函数, (自变量, 下限, 上限)
49
+ """
50
+ raise NotImplementedError()
51
+
52
+ def tries(self, try_arg):
53
+ """
54
+ :return: {term_name: coefficient_of_the_term}
55
+ {该项的名称: 该项系数}
56
+ """
57
+ return self.integrate_and_separate(*self.get_integrate_args(try_arg))
58
+
59
+ def get_latex(self, try_arg, subs):
60
+ expr, args = self.get_integrate_args(try_arg)
61
+ expr = simplify(expr).subs(subs)
62
+ sym, low, high = args
63
+ return r"\int_{%s}^{%s} {%s} \mathrm{d} {%s}" % (
64
+ low,
65
+ high,
66
+ latex(simplify(expr)),
67
+ sym,
68
+ )
69
+
70
+
71
+ class GetIntegrateFromData(GetIntegrate):
72
+ data: dict[object, dict[str, Expr]]
73
+
74
+ def tries(self, try_arg):
75
+ return self.data[try_arg]
76
+
77
+
78
+ class Solution:
79
+ get_integrate: GetIntegrate
80
+ # gui: Pattern
81
+
82
+ get_tries_args: Callable[[], Iterable[Expr]]
83
+ """
84
+ Generate try_arg for each trial
85
+ 生成每一次尝试的 try_arg
86
+ """
87
+
88
+ symbols: tuple[Symbol, ...]
89
+ """
90
+ The undetermined variable to solve.
91
+ 要求的待定系数
92
+ """
93
+
94
+ integrate_result_classes_eq: dict[str, Expr]
95
+ """
96
+ {term_name: coefficient}
97
+ The value to which each term's coefficient is equal.
98
+ 每个项系数分别要等于的值
99
+ """
100
+
101
+ check_sgn: Callable
102
+ """
103
+ Input: unpacked solution of undetermined variables;
104
+ Output: 0 (cannot determine), 1 or -1 (can determine; 1 and -1 are interchangeable for greater or less than).
105
+ Module sgntools provides lin_func_sgn and sq_func_sgn. Call help() for further details.
106
+
107
+ 输入:解包的待定系数的值列表;
108
+ 输出: 0(无法确定), 1或-1(可以确定, 1和-1哪个代表大于、哪个代表小于, 是可以互换的)
109
+ sgntools 模块提供了 lin_func_sgn 和 sq_func_sgn 函数,详见 help()
110
+ """
111
+ # check_sgn: Callable[?, 1 | 0 | -1]
112
+
113
+ def get_symbols(self, separate_result):
114
+ """凑积分结果系数"""
115
+ system = [
116
+ Eq(int_term, self.integrate_result_classes_eq[key])
117
+ for key, int_term in separate_result.items()
118
+ ]
119
+ print(f"{system=}")
120
+ return linsolve(system, *self.symbols)
121
+
122
+ def try_times(self) -> tuple[Expr, dict, int] | tuple[None, None, None]:
123
+ for try_arg in self.get_tries_args():
124
+ separate = self.get_integrate.tries(try_arg)
125
+ for symbol_solution in self.get_symbols(separate):
126
+ if sgn := self.check_sgn(*symbol_solution):
127
+ return try_arg, dict(zip(self.symbols, symbol_solution)), sgn
128
+
129
+ return None, None, None
130
+
131
+ def get_latex_ans(self):
132
+ """
133
+ :return: None if it can't be solved; Otherwise, LaTeX (without "$$")
134
+ """
135
+ raise NotImplementedError()
136
+
137
+
138
+ # 插件注册(历史遗留问题)
139
+ solutions = {}
140
+ solution_sort = []
141
+
142
+
143
+ def register(name, solution, top=False):
144
+ solutions[name] = solution
145
+ if top:
146
+ solution_sort.insert(0, name)
147
+ else:
148
+ solution_sort.append(name)
solutions/e.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy import *
2
+ from sympy.abc import a, b, x
3
+ from output_util import to_latex, sign2cmp
4
+ from sgntools import lin_func_sgn
5
+ from solution import *
6
+
7
+ _data = {
8
+ 1: {"constant_term": 3 * a - 8 * b, "e_term": -a + 3 * b},
9
+ 2: {"constant_term": -38 * a + 174 * b, "e_term": 14 * a - 64 * b},
10
+ 3: {"constant_term": 1158 * a - 7584 * b, "e_term": -426 * a + 2790 * b},
11
+ 4: {"constant_term": -65304 * a + 557400 * b, "e_term": 24024 * a - 205056 * b},
12
+ 5: {
13
+ "constant_term": 5900520 * a - 62118720 * b,
14
+ "e_term": -2170680 * a + 22852200 * b,
15
+ },
16
+ 6: {
17
+ "constant_term": -780827760 * a + 9778048560 * b,
18
+ "e_term": 287250480 * a - 3597143040 * b,
19
+ },
20
+ }
21
+
22
+
23
+ class EIntegrate(GetIntegrateFromData):
24
+ # sympy 算力不够,以下由 MMA 算出
25
+ data = _data
26
+
27
+ def get_integrate_args(self, n):
28
+ return x**n * (1 - x) ** n * (a + b * x) * exp(x), (x, 0, 1)
29
+
30
+
31
+ class ESolution(Solution):
32
+ def __init__(self, p, q):
33
+ self.p = p
34
+ self.q = q
35
+
36
+ if self.q == 0:
37
+ raise BadInput()
38
+
39
+ self.get_integrate = EIntegrate()
40
+ self.symbols = (a, b)
41
+
42
+ self.integrate_result_classes_eq = {
43
+ "e_term": q,
44
+ CONSTANT_TERM_KEY: -p,
45
+ }
46
+ # check
47
+ self.check_sgn = lin_func_sgn
48
+
49
+ @staticmethod
50
+ def get_tries_args():
51
+ return EIntegrate.data.keys()
52
+
53
+ def get_latex_ans(self):
54
+ try_arg, symbol_val, sgn = self.try_times()
55
+ print(f"{(try_arg, symbol_val, sgn)=}")
56
+
57
+ if try_arg is None:
58
+ return None
59
+
60
+ p = to_latex(self.p)
61
+ q = to_latex(self.q, is_coeff=True)
62
+ I = self.get_integrate.get_latex(try_arg, symbol_val)
63
+ return rf"{q}e-{p} = {I} {sign2cmp[sgn]} 0"
64
+
65
+
66
+ register("e", ESolution)
67
+
68
+
69
+ if __name__ == "__main__":
70
+ print(ESolution(25, 9))
solutions/eq.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy import *
2
+ from sympy.abc import *
3
+ from output_util import to_latex, sign2cmp, to_latexes
4
+ from sgntools import lin_func_sgn
5
+ from solution import *
6
+
7
+ _data = {
8
+ (1,): {
9
+ "constant_term": a / q**2 + 2 * a / q**3 - 2 * b / q**3 - 6 * b / q**4,
10
+ "eq_term": a / q**2 - 2 * a / q**3 + b / q**2 - 4 * b / q**3 + 6 * b / q**4,
11
+ },
12
+ (2,): {
13
+ "constant_term": -2 * a / q**3
14
+ - 12 * a / q**4
15
+ - 24 * a / q**5
16
+ + 6 * b / q**4
17
+ + 48 * b / q**5
18
+ + 120 * b / q**6,
19
+ "eq_term": 2 * a / q**3
20
+ - 12 * a / q**4
21
+ + 24 * a / q**5
22
+ + 2 * b / q**3
23
+ - 18 * b / q**4
24
+ + 72 * b / q**5
25
+ - 120 * b / q**6,
26
+ },
27
+ (3,): {
28
+ "constant_term": 6 * a / q**4
29
+ + 72 * a / q**5
30
+ + 360 * a / q**6
31
+ + 720 * a / q**7
32
+ - 24 * b / q**5
33
+ - 360 * b / q**6
34
+ - 2160 * b / q**7
35
+ - 5040 * b / q**8,
36
+ "eq_term": 6 * a / q**4
37
+ - 72 * a / q**5
38
+ + 360 * a / q**6
39
+ - 720 * a / q**7
40
+ + 6 * b / q**4
41
+ - 96 * b / q**5
42
+ + 720 * b / q**6
43
+ - 2880 * b / q**7
44
+ + 5040 * b / q**8,
45
+ },
46
+ (4,): {
47
+ "constant_term": -24 * a / q**5
48
+ - 480 * a / q**6
49
+ - 4320 * a / q**7
50
+ - 20160 * a / q**8
51
+ - 40320 * a / q**9
52
+ + 120 * b / q**6
53
+ + 2880 * b / q**7
54
+ + 30240 * b / q**8
55
+ + 161280 * b / q**9
56
+ + 362880 * b / q**10,
57
+ "eq_term": 24 * a / q**5
58
+ - 480 * a / q**6
59
+ + 4320 * a / q**7
60
+ - 20160 * a / q**8
61
+ + 40320 * a / q**9
62
+ + 24 * b / q**5
63
+ - 600 * b / q**6
64
+ + 7200 * b / q**7
65
+ - 50400 * b / q**8
66
+ + 201600 * b / q**9
67
+ - 362880 * b / q**10,
68
+ },
69
+ (5,): {
70
+ "constant_term": 120 * a / q**6
71
+ + 3600 * a / q**7
72
+ + 50400 * a / q**8
73
+ + 403200 * a / q**9
74
+ + 1814400 * a / q**10
75
+ + 3628800 * a / q**11
76
+ - 720 * b / q**7
77
+ - 25200 * b / q**8
78
+ - 403200 * b / q**9
79
+ - 3628800 * b / q**10
80
+ - 18144000 * b / q**11
81
+ - 39916800 * b / q**12,
82
+ "eq_term": 120 * a / q**6
83
+ - 3600 * a / q**7
84
+ + 50400 * a / q**8
85
+ - 403200 * a / q**9
86
+ + 1814400 * a / q**10
87
+ - 3628800 * a / q**11
88
+ + 120 * b / q**6
89
+ - 4320 * b / q**7
90
+ + 75600 * b / q**8
91
+ - 806400 * b / q**9
92
+ + 5443200 * b / q**10
93
+ - 21772800 * b / q**11
94
+ + 39916800 * b / q**12,
95
+ },
96
+ (6,): {
97
+ "constant_term": -720 * a / q**7
98
+ - 30240 * a / q**8
99
+ - 604800 * a / q**9
100
+ - 7257600 * a / q**10
101
+ - 54432000 * a / q**11
102
+ - 239500800 * a / q**12
103
+ - 479001600 * a / q**13
104
+ + 5040 * b / q**8
105
+ + 241920 * b / q**9
106
+ + 5443200 * b / q**10
107
+ + 72576000 * b / q**11
108
+ + 598752000 * b / q**12
109
+ + 2874009600 * b / q**13
110
+ + 6227020800 * b / q**14,
111
+ "eq_term": 720 * a / q**7
112
+ - 30240 * a / q**8
113
+ + 604800 * a / q**9
114
+ - 7257600 * a / q**10
115
+ + 54432000 * a / q**11
116
+ - 239500800 * a / q**12
117
+ + 479001600 * a / q**13
118
+ + 720 * b / q**7
119
+ - 35280 * b / q**8
120
+ + 846720 * b / q**9
121
+ - 12700800 * b / q**10
122
+ + 127008000 * b / q**11
123
+ - 838252800 * b / q**12
124
+ + 3353011200 * b / q**13
125
+ - 6227020800 * b / q**14,
126
+ },
127
+ }
128
+
129
+
130
+ class EQIntegrate(GetIntegrateFromData):
131
+ data = _data
132
+
133
+ def get_integrate_args(self, try_arg):
134
+ n, q = try_arg
135
+ return x**n * (1 - x) ** n * (a + b * x) * e ** (q * x), (x, 0, 1)
136
+
137
+ def tries(self, try_arg):
138
+ n, q_value = try_arg
139
+ return {key: expr.subs(q, q_value) for key, expr in self.data[(n,)].items()}
140
+
141
+
142
+ class EQSolution(Solution):
143
+ def __init__(self, q1, q2, u, v):
144
+ if q2 == 0 or v == 0:
145
+ raise BadInput()
146
+
147
+ self.q = q1 / q2 # 输入都是 Rational 类型的,可以不损失精度相除
148
+ self.u = u
149
+ self.v = v
150
+
151
+ self.get_integrate = EQIntegrate()
152
+ self.symbols = (a, b)
153
+
154
+ self.integrate_result_classes_eq = {
155
+ "eq_term": v,
156
+ CONSTANT_TERM_KEY: -u,
157
+ }
158
+ # check
159
+ self.check_sgn = lin_func_sgn
160
+
161
+ def get_tries_args(self):
162
+ for (n,) in EQIntegrate.data.keys():
163
+ yield n, self.q
164
+
165
+ def get_latex_ans(self):
166
+ try_arg, symbol_val, sgn = self.try_times()
167
+ print(f"{(try_arg, symbol_val, sgn)=}")
168
+
169
+ if try_arg is None:
170
+ return None
171
+
172
+ u, q = to_latexes(self.u, self.q)
173
+ v = to_latex(self.v, is_coeff=True)
174
+ I = self.get_integrate.get_latex(try_arg, symbol_val)
175
+ return rf"{v}e^{q}-{u} = {I} {sign2cmp[sgn]} 0"
176
+
177
+
178
+ register("e^q", EQSolution)
179
+
180
+
181
+ if __name__ == "__main__":
182
+ print(EQSolution(Rational(2), 1, 7, 1).get_latex_ans())
solutions/pi.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.abc import a, b, c, x
2
+ from output_util import to_latex, sign2cmp
3
+ from sgntools import sq_func_sgn
4
+ from solution import *
5
+
6
+
7
+ class PiIntegrate(GetIntegrateFromData):
8
+ # sympy 算力不够,以下由 MMA 算出
9
+ # PiIF[n_] :=
10
+ # Collect[ Collect[
11
+ # PowerExpand[ Expand[
12
+ # Integrate[x^n*(1 - x)^n*(a + b x + c x^2)/(x^2 + 1),
13
+ # {x, 0, 1}]
14
+ # ]],
15
+ # Log[2]], Pi]
16
+
17
+ data = {
18
+ 1: {
19
+ "pi_term": (a - b - c) / 4,
20
+ "log2_term": (a + b - c) / 2,
21
+ CONSTANT_TERM_KEY: -a + b / 2 + 7 * c / 6,
22
+ },
23
+ 2: {
24
+ "pi_term": -b / 2,
25
+ "log2_term": a - c,
26
+ CONSTANT_TERM_KEY: (-2 * a / 3 + 19 * b / 12 + 7 * c / 10),
27
+ },
28
+ 3: {
29
+ "pi_term": (-a - b + c) / 2,
30
+ "log2_term": a - b - c,
31
+ CONSTANT_TERM_KEY: 53 * a / 60 + 34 * b / 15 - 92 * c / 105,
32
+ },
33
+ 4: {
34
+ "pi_term": -a + c,
35
+ "log2_term": 2 * b,
36
+ CONSTANT_TERM_KEY: 22 * a / 7 + 233 * b / 168 - 1979 * c / 630,
37
+ },
38
+ 5: {
39
+ "pi_term": -a + b + c,
40
+ "log2_term": 2 * (-a - b + c),
41
+ CONSTANT_TERM_KEY: 11411 * a / 2520 - 4423 * b / 2520 - 41837 * c / 9240,
42
+ },
43
+ 6: {
44
+ "pi_term": 2 * b,
45
+ "log2_term": -4 * a + 4 * c,
46
+ CONSTANT_TERM_KEY: (38429 * a) / 13860
47
+ - (174169 * b) / 27720
48
+ - (35683 * c) / 12870,
49
+ },
50
+ 7: {
51
+ "pi_term": 2 * (a + b - c),
52
+ "log2_term": 4 * (-a + b + c),
53
+ CONSTANT_TERM_KEY: -((421691 * a) / 120120)
54
+ - (407917 * b) / 45045
55
+ + (31627 * c) / 9009,
56
+ },
57
+ 8: {
58
+ "pi_term": 4 * (a - c),
59
+ "log2_term": 8 * b,
60
+ CONSTANT_TERM_KEY: -(188684 * a) / 15015
61
+ - (1332173 * b) / 240240
62
+ + (3849155 * c) / 306306,
63
+ },
64
+ 9: {
65
+ "pi_term": 4 * (a - b - c),
66
+ "log2_term": 8 * (-c + a + b),
67
+ CONSTANT_TERM_KEY: -((17069771 * a) / 942480)
68
+ + (86025349 * b) / 12252240
69
+ + (4216233689 * c) / 232792560,
70
+ },
71
+ 10: {
72
+ "pi_term": -8 * b,
73
+ "log2_term": 16 * (a - c),
74
+ CONSTANT_TERM_KEY: -((1290876029 * a) / 116396280)
75
+ + (325039733 * b) / 12932920
76
+ + (117352369 * c) / 10581480,
77
+ },
78
+ 11: {
79
+ "pi_term": 8 * (-a - b + c),
80
+ "log2_term": 16 * (a - b - c),
81
+ CONSTANT_TERM_KEY: (817240769 * a) / 58198140
82
+ + (4216233641 * b) / 116396280
83
+ - (37593075209 * c) / 2677114440,
84
+ },
85
+ 12: {
86
+ "pi_term": -16 * a + 16 * c,
87
+ "log2_term": -32 * b,
88
+ CONSTANT_TERM_KEY: (431302721 * a) / 8580495
89
+ + (9135430531 * b) / 411863760
90
+ - (5902037233 * c) / 117417300,
91
+ },
92
+ }
93
+
94
+ def get_integrate_args(self, n):
95
+ return x**n * (1 - x) ** n * (a + b * x + c * x**2) / (1 + x**2), (x, 0, 1)
96
+
97
+
98
+ class PiSolution(Solution):
99
+ def __init__(self, p, q):
100
+ self.p = p
101
+ self.q = q
102
+
103
+ if self.q == 0:
104
+ raise BadInput()
105
+
106
+ self.get_integrate = PiIntegrate()
107
+ self.symbols = (a, b, c)
108
+
109
+ self.integrate_result_classes_eq = {
110
+ "pi_term": q,
111
+ "log2_term": 0,
112
+ CONSTANT_TERM_KEY: -p,
113
+ }
114
+ # check
115
+ self.check_sgn = sq_func_sgn
116
+
117
+ @staticmethod
118
+ def get_tries_args():
119
+ return PiIntegrate.data.keys()
120
+
121
+ def get_latex_ans(self):
122
+ try_arg, symbol_val, sgn = self.try_times()
123
+ print(f"{(try_arg, symbol_val, sgn)=}")
124
+
125
+ if try_arg is None:
126
+ return None
127
+
128
+ p = to_latex(self.p)
129
+ q = to_latex(self.q, is_coeff=True)
130
+ I = self.get_integrate.get_latex(try_arg, symbol_val)
131
+ return rf"{q}\pi-{p} = {I} {sign2cmp[sgn]} 0"
132
+
133
+
134
+ register("π", PiSolution, top=True)
135
+
136
+ if __name__ == "__main__":
137
+ while True:
138
+ p = int(input(">>> "))
139
+ q = int(input(" / "))
140
+ print(PiSolution(p, q).get_latex_ans())
solutions/pin.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy import *
2
+ from sympy.abc import a, b, x
3
+ from output_util import to_latex, sign2cmp, to_latexes
4
+ from sgntools import lin_func_sgn
5
+ from solution import *
6
+
7
+ _data = {
8
+ (1, 2): {"constant_term": a - 2 * b / 3, "pin_term": -a / 4 + b / 4},
9
+ (1, 4): {"constant_term": -2 * a / 3 + 13 * b / 15, "pin_term": a / 4 - b / 4},
10
+ (1, 6): {"constant_term": 13 * a / 15 - 76 * b / 105, "pin_term": -a / 4 + b / 4},
11
+ (1, 8): {"constant_term": -76 * a / 105 + 263 * b / 315, "pin_term": a / 4 - b / 4},
12
+ (1, 10): {
13
+ "constant_term": 263 * a / 315 - 2578 * b / 3465,
14
+ "pin_term": -a / 4 + b / 4,
15
+ },
16
+ (1, 12): {
17
+ "constant_term": -2578 * a / 3465 + 36979 * b / 45045,
18
+ "pin_term": a / 4 - b / 4,
19
+ },
20
+ (2, 3): {"constant_term": a / 4 - 3 * b / 16, "pin_term": -a / 48 + b / 48},
21
+ (2, 5): {"constant_term": -3 * a / 16 + 31 * b / 144, "pin_term": a / 48 - b / 48},
22
+ (2, 7): {
23
+ "constant_term": 31 * a / 144 - 115 * b / 576,
24
+ "pin_term": -a / 48 + b / 48,
25
+ },
26
+ (2, 9): {
27
+ "constant_term": -115 * a / 576 + 3019 * b / 14400,
28
+ "pin_term": a / 48 - b / 48,
29
+ },
30
+ (2, 11): {
31
+ "constant_term": 3019 * a / 14400 - 973 * b / 4800,
32
+ "pin_term": -a / 48 + b / 48,
33
+ },
34
+ (2, 13): {
35
+ "constant_term": -973 * a / 4800 + 48877 * b / 235200,
36
+ "pin_term": a / 48 - b / 48,
37
+ },
38
+ (3, 2): {"constant_term": 2 * a - 52 * b / 27, "pin_term": -a / 16 + b / 16},
39
+ (3, 4): {
40
+ "constant_term": -52 * a / 27 + 6554 * b / 3375,
41
+ "pin_term": a / 16 - b / 16,
42
+ },
43
+ (3, 6): {
44
+ "constant_term": 6554 * a / 3375 - 2241272 * b / 1157625,
45
+ "pin_term": -a / 16 + b / 16,
46
+ },
47
+ (3, 8): {
48
+ "constant_term": -2241272 * a / 1157625 + 60600094 * b / 31255875,
49
+ "pin_term": a / 16 - b / 16,
50
+ },
51
+ (3, 10): {
52
+ "constant_term": 60600094 * a / 31255875 - 80596213364 * b / 41601569625,
53
+ "pin_term": -a / 16 + b / 16,
54
+ },
55
+ (3, 12): {
56
+ "constant_term": -80596213364 * a / 41601569625
57
+ + 177153083899958 * b / 91398648466125,
58
+ "pin_term": a / 16 - b / 16,
59
+ },
60
+ (4, 3): {
61
+ "constant_term": 3 * a / 8 - 45 * b / 128,
62
+ "pin_term": -7 * a / 1920 + 7 * b / 1920,
63
+ },
64
+ (4, 5): {
65
+ "constant_term": -45 * a / 128 + 1231 * b / 3456,
66
+ "pin_term": 7 * a / 1920 - 7 * b / 1920,
67
+ },
68
+ (4, 7): {
69
+ "constant_term": 1231 * a / 3456 - 19615 * b / 55296,
70
+ "pin_term": -7 * a / 1920 + 7 * b / 1920,
71
+ },
72
+ (4, 9): {
73
+ "constant_term": -19615 * a / 55296 + 12280111 * b / 34560000,
74
+ "pin_term": 7 * a / 1920 - 7 * b / 1920,
75
+ },
76
+ (4, 11): {
77
+ "constant_term": 12280111 * a / 34560000 - 4090037 * b / 11520000,
78
+ "pin_term": -7 * a / 1920 + 7 * b / 1920,
79
+ },
80
+ (4, 13): {
81
+ "constant_term": -4090037 * a / 11520000 + 9824498837 * b / 27659520000,
82
+ "pin_term": 7 * a / 1920 - 7 * b / 1920,
83
+ },
84
+ (5, 2): {
85
+ "constant_term": 24 * a - 1936 * b / 81,
86
+ "pin_term": -5 * a / 64 + 5 * b / 64,
87
+ },
88
+ (5, 4): {
89
+ "constant_term": -1936 * a / 81 + 6051944 * b / 253125,
90
+ "pin_term": 5 * a / 64 - 5 * b / 64,
91
+ },
92
+ (5, 6): {
93
+ "constant_term": 6051944 * a / 253125 - 101708947808 * b / 4254271875,
94
+ "pin_term": -5 * a / 64 + 5 * b / 64,
95
+ },
96
+ (5, 8): {
97
+ "constant_term": -101708947808 * a / 4254271875
98
+ + 24715694492344 * b / 1033788065625,
99
+ "pin_term": 5 * a / 64 - 5 * b / 64,
100
+ },
101
+ (5, 10): {
102
+ "constant_term": 24715694492344 * a / 1033788065625
103
+ - 3980462502772918544 * b / 166492601756971875,
104
+ "pin_term": -5 * a / 64 + 5 * b / 64,
105
+ },
106
+ (5, 12): {
107
+ "constant_term": -3980462502772918544 * a / 166492601756971875
108
+ + 1477921859864507412282392 * b / 61817537584151358384375,
109
+ "pin_term": 5 * a / 64 - 5 * b / 64,
110
+ },
111
+ (6, 3): {
112
+ "constant_term": 15 * a / 8 - 945 * b / 512,
113
+ "pin_term": -31 * a / 16128 + 31 * b / 16128,
114
+ },
115
+ (6, 5): {
116
+ "constant_term": -945 * a / 512 + 229955 * b / 124416,
117
+ "pin_term": 31 * a / 16128 - 31 * b / 16128,
118
+ },
119
+ (6, 7): {
120
+ "constant_term": 229955 * a / 124416 - 14713475 * b / 7962624,
121
+ "pin_term": -31 * a / 16128 + 31 * b / 16128,
122
+ },
123
+ (6, 9): {
124
+ "constant_term": -14713475 * a / 7962624 + 45982595359 * b / 24883200000,
125
+ "pin_term": 31 * a / 16128 - 31 * b / 16128,
126
+ },
127
+ (6, 11): {
128
+ "constant_term": 45982595359 * a / 24883200000 - 5109066151 * b / 2764800000,
129
+ "pin_term": -31 * a / 16128 + 31 * b / 16128,
130
+ },
131
+ (6, 13): {
132
+ "constant_term": -5109066151 * a / 2764800000
133
+ + 601081707598999 * b / 325275955200000,
134
+ "pin_term": 31 * a / 16128 - 31 * b / 16128,
135
+ },
136
+ }
137
+
138
+
139
+ class PiNIntegrate(GetIntegrateFromData):
140
+ data = _data
141
+
142
+ def get_integrate_args(self, args):
143
+ n, m = args
144
+ return x**m * (a + b * x**2) * (log(1 / x)) ** (n - 1) / (1 + x**2), (x, 0, 1)
145
+
146
+
147
+ class PiNSolution(Solution):
148
+ def __init__(self, n, p, q):
149
+ if n < 0:
150
+ n = -n
151
+ p, q = q, p
152
+ self.n = n
153
+ self.p = p
154
+ self.q = q
155
+
156
+ if self.q == 0:
157
+ raise BadInput()
158
+
159
+ self.get_integrate = PiNIntegrate()
160
+ self.symbols = (a, b)
161
+
162
+ self.integrate_result_classes_eq = {
163
+ "pin_term": q,
164
+ CONSTANT_TERM_KEY: -p,
165
+ }
166
+ # check
167
+ self.check_sgn = lin_func_sgn
168
+
169
+ def get_tries_args(self):
170
+ for n, m in PiNIntegrate.data.keys():
171
+ if n == self.n:
172
+ yield n, m
173
+
174
+ def get_latex_ans(self):
175
+ try_arg, symbol_val, sgn = self.try_times()
176
+ print(f"{(try_arg, symbol_val, sgn)=}")
177
+
178
+ if try_arg is None:
179
+ return None
180
+
181
+ p, n = to_latexes(self.p, self.n)
182
+ q = to_latex(self.q, is_coeff=True)
183
+ I = self.get_integrate.get_latex(try_arg, symbol_val)
184
+ return rf"{q}\pi^{n}-{p} = {I} {sign2cmp[sgn]} 0"
185
+
186
+
187
+ register("π^n", PiNSolution)
188
+
189
+
190
+ if __name__ == "__main__":
191
+ print(PiNSolution(3, 31, 1).get_latex_ans())