mathhook_core/algebra/collect/
terms.rs1use crate::core::commutativity::Commutativity;
4use crate::core::{Expression, Number, Symbol};
5use crate::expr;
6use num_bigint::BigInt;
7use num_traits::{One, Zero};
8
9impl Expression {
10 pub(super) fn collect_addition_terms(&self, terms: &[Expression], var: &Symbol) -> Expression {
12 let mut term_coefficients: Vec<(Expression, BigInt)> = Vec::new();
13 let mut constant_term = BigInt::zero();
14
15 for term in terms {
16 let (coeff, power_expr) = self.extract_coefficient_and_power(term, var);
17
18 if power_expr == expr!(0) {
19 constant_term += coeff;
20 } else {
21 let mut found = false;
22 for (existing_expr, existing_coeff) in term_coefficients.iter_mut() {
23 if *existing_expr == power_expr {
24 *existing_coeff += &coeff;
25 found = true;
26 break;
27 }
28 }
29 if !found {
30 term_coefficients.push((power_expr, coeff));
31 }
32 }
33 }
34
35 let mut result_terms = Vec::new();
36
37 if !constant_term.is_zero() {
38 result_terms.push(Expression::big_integer(constant_term));
39 }
40
41 for (power_expr, coeff) in term_coefficients {
42 if !coeff.is_zero() {
43 let term = if coeff.is_one() {
44 if power_expr == expr!(1) {
45 Expression::symbol(var.clone())
46 } else {
47 Expression::pow(Expression::symbol(var.clone()), power_expr)
48 }
49 } else {
50 let var_part = if power_expr == expr!(1) {
51 Expression::symbol(var.clone())
52 } else {
53 Expression::pow(Expression::symbol(var.clone()), power_expr)
54 };
55 Expression::mul(vec![Expression::big_integer(coeff), var_part])
56 };
57 result_terms.push(term);
58 }
59 }
60
61 if result_terms.is_empty() {
62 expr!(0)
63 } else if result_terms.len() == 1 {
64 result_terms[0].clone()
65 } else {
66 Expression::add(result_terms)
67 }
68 }
69
70 pub(super) fn collect_all_like_terms(&self, terms: &[Expression]) -> Expression {
75 let mut term_coefficients: Vec<(Expression, BigInt)> = Vec::new();
76
77 for term in terms {
78 let (coeff, base_term) = self.extract_coefficient_and_base(term);
79
80 let mut found = false;
81 for (existing_expr, existing_coeff) in term_coefficients.iter_mut() {
82 if *existing_expr == base_term {
83 let commutativity = Commutativity::combine(vec![
84 existing_expr.commutativity(),
85 base_term.commutativity(),
86 ]);
87
88 if commutativity.can_sort() || self.same_factor_order(existing_expr, &base_term)
89 {
90 *existing_coeff += &coeff;
91 found = true;
92 break;
93 }
94 }
95 }
96 if !found {
97 term_coefficients.push((base_term, coeff));
98 }
99 }
100
101 let mut result_terms = Vec::new();
102
103 for (base_term, total_coeff) in term_coefficients {
104 if !total_coeff.is_zero() {
105 let final_term = if total_coeff.is_one() {
106 base_term
107 } else if base_term == expr!(1) {
108 Expression::big_integer(total_coeff)
109 } else {
110 Expression::mul(vec![Expression::big_integer(total_coeff), base_term])
111 };
112 result_terms.push(final_term);
113 }
114 }
115
116 if result_terms.is_empty() {
117 expr!(0)
118 } else if result_terms.len() == 1 {
119 result_terms[0].clone()
120 } else {
121 Expression::add(result_terms)
122 }
123 }
124
125 pub(super) fn collect_multiplication_terms(&self, factors: &[Expression]) -> Expression {
127 let mut base_powers: Vec<(Expression, Vec<Expression>)> = Vec::new();
128 let mut numeric_factor = BigInt::one();
129 let mut other_factors = Vec::new();
130
131 for factor in factors {
132 match factor {
133 Expression::Number(Number::Integer(n)) => {
134 numeric_factor *= BigInt::from(*n);
135 }
136 Expression::Pow(base, exp) => {
137 let base_expr = (**base).clone();
138 let exp_expr = (**exp).clone();
139 let mut found = false;
140 for (existing_base, powers) in base_powers.iter_mut() {
141 if *existing_base == base_expr {
142 powers.push(exp_expr.clone());
143 found = true;
144 break;
145 }
146 }
147 if !found {
148 base_powers.push((base_expr, vec![exp_expr]));
149 }
150 }
151 Expression::Symbol(_) => {
152 let mut found = false;
153 for (existing_base, powers) in base_powers.iter_mut() {
154 if *existing_base == *factor {
155 powers.push(expr!(1));
156 found = true;
157 break;
158 }
159 }
160 if !found {
161 base_powers.push((factor.clone(), vec![expr!(1)]));
162 }
163 }
164 _ => {
165 other_factors.push(factor.clone());
166 }
167 }
168 }
169
170 let mut result_factors = Vec::new();
171
172 if !numeric_factor.is_one() {
173 result_factors.push(Expression::big_integer(numeric_factor));
174 }
175
176 for (base, exponents) in base_powers {
177 if exponents.len() == 1 {
178 if exponents[0] == expr!(1) {
179 result_factors.push(base);
180 } else {
181 result_factors.push(Expression::pow(base, exponents[0].clone()));
182 }
183 } else {
184 let total_exp = Expression::add(exponents);
185 result_factors.push(Expression::pow(base, total_exp));
186 }
187 }
188
189 result_factors.extend(other_factors);
190
191 if result_factors.is_empty() {
192 expr!(1)
193 } else if result_factors.len() == 1 {
194 result_factors[0].clone()
195 } else {
196 Expression::mul(result_factors)
197 }
198 }
199
200 pub fn separate_constants(&self) -> (Expression, Expression) {
202 match self {
203 Expression::Add(terms) => {
204 let mut constants = Vec::new();
205 let mut variables = Vec::new();
206
207 for term in terms.iter() {
208 if term.is_constant() {
209 constants.push(term.clone());
210 } else {
211 variables.push(term.clone());
212 }
213 }
214
215 let const_part = if constants.is_empty() {
216 expr!(0)
217 } else {
218 Expression::add(constants)
219 };
220
221 let var_part = if variables.is_empty() {
222 expr!(0)
223 } else {
224 Expression::add(variables)
225 };
226
227 (const_part, var_part)
228 }
229 _ => {
230 if self.is_constant() {
231 (self.clone(), expr!(0))
232 } else {
233 (expr!(0), self.clone())
234 }
235 }
236 }
237 }
238}