mathai 0.6.0__py3-none-any.whl
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.
- mathai/__init__.py +53 -0
- mathai/apart.py +142 -0
- mathai/base.py +419 -0
- mathai/bivariate_inequality.py +317 -0
- mathai/console.py +84 -0
- mathai/diff.py +68 -0
- mathai/expand.py +124 -0
- mathai/factor.py +304 -0
- mathai/fraction.py +103 -0
- mathai/integrate.py +459 -0
- mathai/inverse.py +65 -0
- mathai/limit.py +156 -0
- mathai/linear.py +165 -0
- mathai/logic.py +230 -0
- mathai/matrix.py +22 -0
- mathai/ode.py +124 -0
- mathai/parser.py +158 -0
- mathai/printeq.py +34 -0
- mathai/simplify.py +521 -0
- mathai/structure.py +103 -0
- mathai/tool.py +163 -0
- mathai/trig.py +276 -0
- mathai/univariate_inequality.py +458 -0
- mathai-0.6.0.dist-info/METADATA +234 -0
- mathai-0.6.0.dist-info/RECORD +27 -0
- mathai-0.6.0.dist-info/WHEEL +5 -0
- mathai-0.6.0.dist-info/top_level.txt +1 -0
mathai/tool.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from .diff import diff
|
|
2
|
+
from .expand import expand
|
|
3
|
+
from .simplify import simplify
|
|
4
|
+
from .base import *
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
def poly_div(dividend_coeffs, divisor_coeffs):
|
|
8
|
+
"""
|
|
9
|
+
Perform polynomial division using coefficients with symbolic simplification.
|
|
10
|
+
"""
|
|
11
|
+
# Deep copy inputs using copy_tree()
|
|
12
|
+
dividend = [item.copy_tree() for item in dividend_coeffs]
|
|
13
|
+
divisor = [item.copy_tree() for item in divisor_coeffs]
|
|
14
|
+
|
|
15
|
+
# Remove leading zeros
|
|
16
|
+
while len(dividend) > 1 and simplify(dividend[0]) == 0:
|
|
17
|
+
dividend.pop(0)
|
|
18
|
+
while len(divisor) > 1 and simplify(divisor[0]) == 0:
|
|
19
|
+
divisor.pop(0)
|
|
20
|
+
|
|
21
|
+
# Validate divisor
|
|
22
|
+
if len(divisor) == 0 or simplify(divisor[0]) == 0:
|
|
23
|
+
raise ValueError("Invalid divisor")
|
|
24
|
+
|
|
25
|
+
if len(dividend) < len(divisor):
|
|
26
|
+
return [tree_form("d_0")], [item.copy_tree() for item in dividend]
|
|
27
|
+
|
|
28
|
+
# Calculate degrees
|
|
29
|
+
deg_p = len(dividend) - 1
|
|
30
|
+
deg_q = len(divisor) - 1
|
|
31
|
+
deg_quot = deg_p - deg_q
|
|
32
|
+
|
|
33
|
+
# Initialize quotient (highest degree first)
|
|
34
|
+
quotient = [tree_form("d_0")] * (deg_quot + 1)
|
|
35
|
+
|
|
36
|
+
# Working dividend - keep original structure
|
|
37
|
+
working_dividend = [item.copy_tree() for item in dividend]
|
|
38
|
+
|
|
39
|
+
# Long division - align by current leading terms
|
|
40
|
+
for k in range(deg_quot, -1, -1):
|
|
41
|
+
# Remove leading zeros from working dividend
|
|
42
|
+
while len(working_dividend) > 1 and simplify(working_dividend[0]) == 0:
|
|
43
|
+
working_dividend.pop(0)
|
|
44
|
+
|
|
45
|
+
if len(working_dividend) == 0 or simplify(working_dividend[0]) == 0:
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Calculate quotient term for degree k
|
|
49
|
+
leading_ratio = simplify(working_dividend[0] / divisor[0])
|
|
50
|
+
quotient[k] = leading_ratio
|
|
51
|
+
|
|
52
|
+
# Subtract leading_ratio * divisor (aligned at leading terms)
|
|
53
|
+
new_dividend = []
|
|
54
|
+
for i in range(max(len(working_dividend), len(divisor))):
|
|
55
|
+
dividend_term = working_dividend[i] if i < len(working_dividend) else tree_form("d_0")
|
|
56
|
+
divisor_term = simplify(leading_ratio * divisor[i]) if i < len(divisor) else tree_form("d_0")
|
|
57
|
+
result = simplify(dividend_term - divisor_term)
|
|
58
|
+
new_dividend.append(result)
|
|
59
|
+
|
|
60
|
+
working_dividend = new_dividend
|
|
61
|
+
|
|
62
|
+
# Remainder is terms with degree < deg_q (last deg_q terms of final dividend)
|
|
63
|
+
remainder = working_dividend[-(deg_q):] if len(working_dividend) > deg_q else working_dividend
|
|
64
|
+
while len(remainder) > 1 and simplify(remainder[0]) == 0:
|
|
65
|
+
remainder.pop(0)
|
|
66
|
+
if not remainder:
|
|
67
|
+
remainder = [tree_form("d_0")]
|
|
68
|
+
|
|
69
|
+
# Clean quotient trailing zeros
|
|
70
|
+
while len(quotient) > 1 and simplify(quotient[-1]) == 0:
|
|
71
|
+
quotient.pop()
|
|
72
|
+
|
|
73
|
+
return quotient, remainder
|
|
74
|
+
|
|
75
|
+
def unpoly(eq, var):
|
|
76
|
+
eq = eq[::-1]
|
|
77
|
+
eq = [simplify(item) for item in eq]
|
|
78
|
+
eq2 = copy.deepcopy([eq[i]*tree_form(var)**tree_form("d_"+str(i)) if i != 0 else eq[i] for i in range(len(eq))])
|
|
79
|
+
return summation(eq2)
|
|
80
|
+
|
|
81
|
+
def longdiv(p, q, p_min=0, q_min=0):
|
|
82
|
+
p, q = simplify(p), simplify(q)
|
|
83
|
+
|
|
84
|
+
var = set(vlist(p)) & set(vlist(q))
|
|
85
|
+
if len(var) > 0:
|
|
86
|
+
var = list(var)[0]
|
|
87
|
+
p = poly(p, var)
|
|
88
|
+
q = poly(q, var)
|
|
89
|
+
if p is not None and q is not None and len(p)-1>=p_min and len(q)-1>=q_min and len(p)<=len(q):
|
|
90
|
+
a, b = poly_div(p, q)
|
|
91
|
+
return unpoly(a, var), unpoly(b, var)
|
|
92
|
+
return None
|
|
93
|
+
def poly_simplify(eq):
|
|
94
|
+
a, b = num_dem(eq)
|
|
95
|
+
b = simplify(b)
|
|
96
|
+
if b != 1:
|
|
97
|
+
return simplify(poly_simplify(a)/poly_simplify(b))
|
|
98
|
+
for var in vlist(eq):
|
|
99
|
+
n = poly(eq, var, 20)
|
|
100
|
+
if n is not None:
|
|
101
|
+
return simplify(unpoly(n, var))
|
|
102
|
+
return TreeNode(eq.name, [poly_simplify(child) for child in eq.children])
|
|
103
|
+
def enclose_const(eq):
|
|
104
|
+
def req(eq, dic):
|
|
105
|
+
for key in dic.keys():
|
|
106
|
+
eq = replace(eq, dic[key], key)
|
|
107
|
+
return eq
|
|
108
|
+
alloclst = []
|
|
109
|
+
for i in range(0,26):
|
|
110
|
+
if "v_"+str(i) not in vlist(eq):
|
|
111
|
+
alloclst.append(tree_form("v_"+str(i)))
|
|
112
|
+
dic = {}
|
|
113
|
+
def helper(eq):
|
|
114
|
+
nonlocal alloclst, dic
|
|
115
|
+
if frac(eq) is not None:
|
|
116
|
+
return eq
|
|
117
|
+
|
|
118
|
+
if "v_" not in str_form(eq):
|
|
119
|
+
if eq not in dic.keys():
|
|
120
|
+
n = alloclst.pop(0)
|
|
121
|
+
dic[eq] = n
|
|
122
|
+
return dic[eq]
|
|
123
|
+
else:
|
|
124
|
+
if eq.name == "f_pow":
|
|
125
|
+
return TreeNode(eq.name, [helper(eq.children[0]), eq.children[1]])
|
|
126
|
+
return TreeNode(eq.name, [helper(child) for child in eq.children])
|
|
127
|
+
eq= helper(eq)
|
|
128
|
+
return eq, lambda x: req(x, dic)
|
|
129
|
+
|
|
130
|
+
def poly(eq, to_compute, m=10):
|
|
131
|
+
def substitute_val(eq, val, var="v_0"):
|
|
132
|
+
eq = replace(eq, tree_form(var), tree_form("d_"+str(val)))
|
|
133
|
+
return eq
|
|
134
|
+
def inv(eq):
|
|
135
|
+
if eq.name[:2] == "f_" and eq.name[2:] in "ref try integrate subs".split(" "):
|
|
136
|
+
return False
|
|
137
|
+
if eq.name[2:] in ["sin", "cos", "log"] and contain(eq.children[0], tree_form(to_compute)):
|
|
138
|
+
return False
|
|
139
|
+
if eq.name == "f_pow" and contain(eq.children[0], tree_form(to_compute)) and\
|
|
140
|
+
(frac(eq.children[1]) is None or frac(eq.children[1]) < 0 or frac(eq.children[1]).denominator != 1):
|
|
141
|
+
return False
|
|
142
|
+
if eq.name == "f_abs":
|
|
143
|
+
return False
|
|
144
|
+
if any(not inv(child) for child in eq.children):
|
|
145
|
+
return False
|
|
146
|
+
return True
|
|
147
|
+
if not inv(eq):
|
|
148
|
+
return None
|
|
149
|
+
out = []
|
|
150
|
+
eq2 = eq
|
|
151
|
+
for i in range(m):
|
|
152
|
+
out.append(expand(simplify(eq2)))
|
|
153
|
+
eq2 = diff(eq2, to_compute)
|
|
154
|
+
for i in range(len(out)-1,-1,-1):
|
|
155
|
+
if out[i] == tree_form("d_0"):
|
|
156
|
+
out.pop(i)
|
|
157
|
+
else:
|
|
158
|
+
break
|
|
159
|
+
final = []
|
|
160
|
+
for index, item in enumerate(out):
|
|
161
|
+
final.append(substitute_val(item, 0, to_compute)/tree_form("d_"+str(math.factorial(index))))
|
|
162
|
+
|
|
163
|
+
return [expand(simplify(item)) for item in final][::-1]
|
mathai/trig.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
from .simplify import simplify
|
|
3
|
+
from .base import *
|
|
4
|
+
from .expand import expand
|
|
5
|
+
from .structure import transform_formula
|
|
6
|
+
from .parser import parse
|
|
7
|
+
trig_sin_table = {
|
|
8
|
+
(0,1): parse("0"),
|
|
9
|
+
(1,6): parse("1/2"),
|
|
10
|
+
(1,4): parse("2^(1/2)/2"), # π/4
|
|
11
|
+
(1,3): parse("3^(1/2)/2"), # π/3
|
|
12
|
+
(1,2): parse("1"), # π/2
|
|
13
|
+
(2,3): parse("3^(1/2)/2"), # 2π/3
|
|
14
|
+
(3,4): parse("2^(1/2)/2"), # 3π/4
|
|
15
|
+
(5,6): parse("1/2"), # 5π/6
|
|
16
|
+
(1,1): parse("0") # π
|
|
17
|
+
}
|
|
18
|
+
trig_cos_table = {
|
|
19
|
+
(0,1): parse("1"), # 0
|
|
20
|
+
(1,6): parse("3^(1/2)/2"), # π/6
|
|
21
|
+
(1,4): parse("2^(1/2)/2"), # π/4
|
|
22
|
+
(1,3): parse("1/2"), # π/3
|
|
23
|
+
(1,2): parse("0"), # π/2
|
|
24
|
+
(2,3): parse("-1/2"), # 2π/3
|
|
25
|
+
(3,4): parse("-2^(1/2)/2"), # 3π/4
|
|
26
|
+
(5,6): parse("-1/2"), # 5π/6
|
|
27
|
+
(1,1): parse("-1") # π
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for key in trig_cos_table.keys():
|
|
31
|
+
trig_cos_table[key] = simplify(trig_cos_table[key])
|
|
32
|
+
for key in trig_sin_table.keys():
|
|
33
|
+
trig_sin_table[key] = simplify(trig_sin_table[key])
|
|
34
|
+
def trig0(eq):
|
|
35
|
+
if eq is None:
|
|
36
|
+
return None
|
|
37
|
+
def isneg(eq):
|
|
38
|
+
if eq.name[:2] != "d_":
|
|
39
|
+
return False
|
|
40
|
+
if int(eq.name[2:]) >= 0:
|
|
41
|
+
return False
|
|
42
|
+
return True
|
|
43
|
+
def single_pi(lst):
|
|
44
|
+
if tree_form("d_0") in lst:
|
|
45
|
+
return 0, 1
|
|
46
|
+
count = 0
|
|
47
|
+
for item in lst:
|
|
48
|
+
if item == tree_form("s_pi"):
|
|
49
|
+
count += 1
|
|
50
|
+
if count != 1:
|
|
51
|
+
return None
|
|
52
|
+
eq = simplify(product(lst)/tree_form("s_pi"))
|
|
53
|
+
out = frac(eq)
|
|
54
|
+
if out is None or out < 0:
|
|
55
|
+
return None
|
|
56
|
+
a,b= out.numerator, out.denominator
|
|
57
|
+
a %= 2*b
|
|
58
|
+
if a > b:
|
|
59
|
+
a = 2*b - a
|
|
60
|
+
return a, b
|
|
61
|
+
if eq.name == "f_arccosec":
|
|
62
|
+
return (1/eq.children[0]).fx("arcsin")
|
|
63
|
+
|
|
64
|
+
if eq.name == "f_arctan":
|
|
65
|
+
if eq.children[0].name == "d_0":
|
|
66
|
+
return tree_form("d_0")
|
|
67
|
+
if eq.name == "f_log":
|
|
68
|
+
if eq.children[0].name == "d_1":
|
|
69
|
+
return tree_form("d_0")
|
|
70
|
+
if eq.name=="f_tan":
|
|
71
|
+
if eq.children[0].name == "f_arctan":
|
|
72
|
+
return eq.children[0].children[0]
|
|
73
|
+
return eq.children[0].fx("sin")/eq.children[0].fx("cos")
|
|
74
|
+
if eq.name == "f_sec":
|
|
75
|
+
return eq.children[0].fx("cos")**-1
|
|
76
|
+
if eq.name == "f_cosec":
|
|
77
|
+
return eq.children[0].fx("sin")**-1
|
|
78
|
+
if eq.name == "f_cot":
|
|
79
|
+
return eq.children[0].fx("cos")/eq.children[0].fx("sin")
|
|
80
|
+
|
|
81
|
+
if eq.name == "f_sin":
|
|
82
|
+
if eq.children[0].name == "f_arcsin":
|
|
83
|
+
return eq.children[0].children[0]
|
|
84
|
+
lst = factor_generation(eq.children[0])
|
|
85
|
+
if any(isneg(item) for item in lst):
|
|
86
|
+
return -(eq.children[0]*-1).fx("sin")
|
|
87
|
+
out=single_pi(lst)
|
|
88
|
+
if out is not None and tuple(out) in trig_sin_table.keys():
|
|
89
|
+
return trig_sin_table[tuple(out)]
|
|
90
|
+
|
|
91
|
+
if eq.name == "f_cos":
|
|
92
|
+
if eq.children[0].name == "f_arccos":
|
|
93
|
+
return eq.children[0].children[0]
|
|
94
|
+
lst = factor_generation(eq.children[0])
|
|
95
|
+
if any(isneg(item) for item in lst):
|
|
96
|
+
return (eq.children[0]*-1).fx("cos")
|
|
97
|
+
out=single_pi(lst)
|
|
98
|
+
if out is not None:
|
|
99
|
+
if tuple(out) in trig_cos_table.keys():
|
|
100
|
+
return trig_cos_table[tuple(out)]
|
|
101
|
+
return TreeNode(eq.name, [trig0(child) for child in eq.children])
|
|
102
|
+
def cog(expr):
|
|
103
|
+
expr = TreeNode(expr.name, [product_to_sum(child) for child in expr.children])
|
|
104
|
+
expr = trig0(simplify(expr))
|
|
105
|
+
expr = expand(simplify(expr))
|
|
106
|
+
return expr
|
|
107
|
+
def product_to_sum(expr):
|
|
108
|
+
factors = factor_generation(expr)
|
|
109
|
+
other = []
|
|
110
|
+
lst = []
|
|
111
|
+
for item in factors:
|
|
112
|
+
if item.name in ["f_cos", "f_sin"]:
|
|
113
|
+
lst.append(item)
|
|
114
|
+
else:
|
|
115
|
+
other.append(item)
|
|
116
|
+
if len(lst) <= 1:
|
|
117
|
+
|
|
118
|
+
return dowhile(expr, cog)
|
|
119
|
+
if len(lst) == 2:
|
|
120
|
+
a, b = lst
|
|
121
|
+
out = None
|
|
122
|
+
if a.name < b.name:
|
|
123
|
+
a, b = b, a
|
|
124
|
+
A, B = a.children[0], b.children[0]
|
|
125
|
+
if a.name == "f_sin" and b.name == "f_sin":
|
|
126
|
+
out =((A - B).fx("cos") - (A + B).fx("cos")) / tree_form("d_2")
|
|
127
|
+
elif a.name == "f_cos" and b.name == "f_cos":
|
|
128
|
+
out =((A - B).fx("cos") + (A + B).fx("cos")) / tree_form("d_2")
|
|
129
|
+
elif a.name == "f_sin" and b.name == "f_cos":
|
|
130
|
+
out =((A + B).fx("sin") + (A - B).fx("sin")) / tree_form("d_2")
|
|
131
|
+
|
|
132
|
+
return out * product(other)
|
|
133
|
+
|
|
134
|
+
rest = tree_form("d_1")
|
|
135
|
+
if len(lst) % 2 == 1:
|
|
136
|
+
rest = lst.pop(0)
|
|
137
|
+
out = []
|
|
138
|
+
for i in range(0, len(lst), 2):
|
|
139
|
+
out.append(product_to_sum(product(lst[i:i+2])))
|
|
140
|
+
expr = product(out)*rest*product(other)
|
|
141
|
+
|
|
142
|
+
return dowhile(expr, cog)
|
|
143
|
+
|
|
144
|
+
def trig_formula_init():
|
|
145
|
+
var = ""
|
|
146
|
+
formula_list = [(f"A*sin(B)+C*sin(B)", f"(A^2+C^2)^(1/2)*sin(B+arctan(C/A))"),\
|
|
147
|
+
(f"sin(B+D)", f"sin(B)*cos(D)+cos(B)*sin(D)"),\
|
|
148
|
+
(f"cos(B+D)", f"cos(B)*cos(D)-sin(B)*sin(D)"),\
|
|
149
|
+
(f"cos(B)^2", f"1-sin(B)^2"),\
|
|
150
|
+
(f"1/cos(B)^2", f"1/(1-sin(B)^2)"),\
|
|
151
|
+
(f"cos(arcsin(B))", f"sqrt(1-B^2)"),\
|
|
152
|
+
(f"sin(arccos(B))", f"sqrt(1-B^2)"),\
|
|
153
|
+
(f"arccos(B)", f"pi/2-arcsin(B)"),\
|
|
154
|
+
(f"sin(arctan(B))", f"x/sqrt(1+x^2)"),\
|
|
155
|
+
(f"cos(arctan(B))", f"1/sqrt(1+x^2)")]
|
|
156
|
+
formula_list = [[simplify(parse(y)) for y in x] for x in formula_list]
|
|
157
|
+
expr = [[parse("A"), parse("1")], [parse("B")], [parse("C"), parse("1")], [parse("D")]]
|
|
158
|
+
return [formula_list, var, expr]
|
|
159
|
+
#formula_gen4 = trig_formula_init()
|
|
160
|
+
def trig3(eq):
|
|
161
|
+
def iseven(eq):
|
|
162
|
+
if eq.name[:2] != "d_":
|
|
163
|
+
return False
|
|
164
|
+
if int(eq.name[2:]) < 2 or int(eq.name[2:]) % 2 != 0:
|
|
165
|
+
return False
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
if eq.name == "f_sin":
|
|
169
|
+
lst = factor_generation(eq.children[0])
|
|
170
|
+
if any(iseven(item) for item in lst):
|
|
171
|
+
eq= 2*(eq.children[0]/2).fx("sin")*(eq.children[0]/2).fx("cos")
|
|
172
|
+
if eq.name == "f_cos":
|
|
173
|
+
lst = factor_generation(eq.children[0])
|
|
174
|
+
if any(iseven(item) for item in lst):
|
|
175
|
+
eq = (eq.children[0]/2).fx("cos")**2-(eq.children[0]/2).fx("sin")**2
|
|
176
|
+
eq = expand(simplify(eq))
|
|
177
|
+
return TreeNode(eq.name, [trig3(child) for child in eq.children])
|
|
178
|
+
def noneg_pow(eq):
|
|
179
|
+
if eq.name == "f_pow" and frac(eq.children[1]) is not None and frac(eq.children[1])<0:
|
|
180
|
+
return (eq.children[0]**(simplify(-eq.children[1])))**-1
|
|
181
|
+
return TreeNode(eq.name, [noneg_pow(child) for child in eq.children])
|
|
182
|
+
|
|
183
|
+
def trig1(eq):
|
|
184
|
+
eq = noneg_pow(eq)
|
|
185
|
+
return product_to_sum(eq)
|
|
186
|
+
|
|
187
|
+
def trig4(eq):
|
|
188
|
+
done = False
|
|
189
|
+
def _trig4(eq, numer=True, chance="sin"):
|
|
190
|
+
nonlocal done
|
|
191
|
+
if eq.name == "f_sin":
|
|
192
|
+
if eq.children[0].name == "f_add" and len(eq.children[0].children)>=2:
|
|
193
|
+
r = len(eq.children[0].children)%2
|
|
194
|
+
a, b = TreeNode("f_add", eq.children[0].children[:round((len(eq.children[0].children)-r)/2)]),\
|
|
195
|
+
TreeNode("f_add", eq.children[0].children[round((len(eq.children[0].children)-r)/2):])
|
|
196
|
+
if len(a.children)==1:
|
|
197
|
+
a=a.children[0]
|
|
198
|
+
if len(b.children)==1:
|
|
199
|
+
b=b.children[0]
|
|
200
|
+
return a.fx("sin")*b.fx("cos") + a.fx("cos")*b.fx("sin")
|
|
201
|
+
if eq.children[0].name == "f_arccos":
|
|
202
|
+
a = eq.children[0].children[0]
|
|
203
|
+
return (1-a**2)**(tree_form("d_2")**-1)
|
|
204
|
+
if eq.children[0].name == "f_arctan":
|
|
205
|
+
a = eq.children[0].children[0]
|
|
206
|
+
return a/(1+a**2)**(tree_form("d_2")**-1)
|
|
207
|
+
if eq.name == "f_pow" and numer:
|
|
208
|
+
if eq.children[0].name == "f_cos" and chance == "cos":
|
|
209
|
+
a = eq.children[0].children[0]
|
|
210
|
+
if frac(eq.children[1]) == 2:
|
|
211
|
+
done = True
|
|
212
|
+
return 1 - a.fx("sin")**2
|
|
213
|
+
if eq.children[0].name == "f_sin" and chance == "cos":
|
|
214
|
+
a = eq.children[0].children[0]
|
|
215
|
+
if frac(eq.children[1]) == 2:
|
|
216
|
+
done = True
|
|
217
|
+
return 1 - a.fx("cos")**2
|
|
218
|
+
if eq.name == "f_cos":
|
|
219
|
+
if eq.children[0].name == "f_add" and len(eq.children[0].children)>=2:
|
|
220
|
+
r = len(eq.children[0].children)%2
|
|
221
|
+
a, b = TreeNode("f_add", eq.children[0].children[:round((len(eq.children[0].children)-r)/2)]),\
|
|
222
|
+
TreeNode("f_add", eq.children[0].children[round((len(eq.children[0].children)-r)/2):])
|
|
223
|
+
if len(a.children)==1:
|
|
224
|
+
a=a.children[0]
|
|
225
|
+
if len(b.children)==1:
|
|
226
|
+
b=b.children[0]
|
|
227
|
+
return a.fx("cos")*b.fx("cos") - a.fx("sin")*b.fx("sin")
|
|
228
|
+
if eq.children[0].name == "f_arcsin":
|
|
229
|
+
a = eq.children[0].children[0]
|
|
230
|
+
return (1-a**2)**(tree_form("d_2")**-1)
|
|
231
|
+
if eq.children[0].name == "f_arctan":
|
|
232
|
+
a = eq.children[0].children[0]
|
|
233
|
+
return tree_form("d_1")/(1+a**2)**(tree_form("d_2")**-1)
|
|
234
|
+
|
|
235
|
+
return TreeNode(eq.name, [_trig4(child, False, chance) if eq.name != "f_add" and\
|
|
236
|
+
(not numer or (eq.name == "f_pow" and frac(eq.children[1]) is not None and frac(eq.children[1]) < 0))\
|
|
237
|
+
else _trig4(child, True, chance) for child in eq.children])
|
|
238
|
+
eq= _trig4(eq)
|
|
239
|
+
if not done:
|
|
240
|
+
eq = _trig4(eq,"cos")
|
|
241
|
+
return eq
|
|
242
|
+
def trig2(eq):
|
|
243
|
+
# Base case: if not an addition, recurse into children
|
|
244
|
+
if eq.name != "f_add":
|
|
245
|
+
return TreeNode(eq.name, [trig2(child) for child in eq.children])
|
|
246
|
+
|
|
247
|
+
# Try all pairs in the addition
|
|
248
|
+
for i, j in itertools.combinations(range(len(eq.children)), 2):
|
|
249
|
+
c1, c2 = eq.children[i], eq.children[j]
|
|
250
|
+
|
|
251
|
+
# Combine only sin/sin or cos/cos
|
|
252
|
+
if c1.name in ["f_sin", "f_cos"] and c2.name in ["f_sin", "f_cos"]:
|
|
253
|
+
A, B = c1.children[0], c2.children[0]
|
|
254
|
+
rest = [eq.children[k] for k in range(len(eq.children)) if k not in (i, j)]
|
|
255
|
+
rest_tree = summation(rest) if rest else tree_form("d_0")
|
|
256
|
+
|
|
257
|
+
two = tree_form("d_2")
|
|
258
|
+
|
|
259
|
+
# sinA + sinB
|
|
260
|
+
if c1.name == "f_sin" and c2.name == "f_sin":
|
|
261
|
+
combined = two * ((A + B) / two).fx("sin") * ((A - B) / two).fx("cos")
|
|
262
|
+
|
|
263
|
+
# cosA + cosB
|
|
264
|
+
elif c1.name == "f_cos" and c2.name == "f_cos":
|
|
265
|
+
combined = two * ((A + B) / two).fx("cos") * ((A - B) / two).fx("cos")
|
|
266
|
+
|
|
267
|
+
# sinA + cosB (leave unchanged)
|
|
268
|
+
else:
|
|
269
|
+
continue
|
|
270
|
+
|
|
271
|
+
new_expr = rest_tree + combined
|
|
272
|
+
# Re-run trig2 in case there are more sin/cos sums to simplify
|
|
273
|
+
return trig2(new_expr)
|
|
274
|
+
|
|
275
|
+
# If no sin/cos pairs found, just recurse on children
|
|
276
|
+
return TreeNode(eq.name, [trig2(child) for child in eq.children])
|