1use crate::core::polynomial::IntPoly;
22use crate::core::{Expression, Number, Symbol};
23use crate::simplify::Simplify;
24
25pub fn polynomial_div(
56 dividend: &Expression,
57 divisor: &Expression,
58 var: &Symbol,
59) -> (Expression, Expression) {
60 if divisor.is_zero() {
61 return (Expression::undefined(), Expression::undefined());
62 }
63
64 if dividend.is_zero() {
65 return (Expression::integer(0), Expression::integer(0));
66 }
67
68 if dividend == divisor {
69 return (Expression::integer(1), Expression::integer(0));
70 }
71
72 if let Some(divisor_const) = extract_constant(divisor) {
74 if divisor_const.is_zero() {
75 return (Expression::undefined(), Expression::undefined());
76 }
77 let quotient = Expression::mul(vec![
78 dividend.clone(),
79 Expression::pow(divisor.clone(), Expression::integer(-1)),
80 ])
81 .simplify();
82 return (quotient, Expression::integer(0));
83 }
84
85 let vars = dividend.find_variables();
87 if vars.len() == 1 {
88 let dividend_var = &vars[0];
89 if dividend_var == var {
90 let divisor_vars = divisor.find_variables();
91 if divisor_vars.len() == 1
92 && &divisor_vars[0] == var
93 && IntPoly::can_convert(dividend, var)
94 && IntPoly::can_convert(divisor, var)
95 {
96 if let (Some(p1), Some(p2)) = (
97 IntPoly::try_from_expression(dividend, var),
98 IntPoly::try_from_expression(divisor, var),
99 ) {
100 if let Ok((q, r)) = p1.div_rem(&p2) {
102 return (q.to_expression(var), r.to_expression(var));
103 }
104 }
105 }
106 }
107 }
108
109 symbolic_polynomial_div(dividend, divisor, var)
111}
112
113fn extract_constant(expr: &Expression) -> Option<Expression> {
115 match expr {
116 Expression::Number(_) => Some(expr.clone()),
117 _ => None,
118 }
119}
120
121fn symbolic_polynomial_div(
126 dividend: &Expression,
127 divisor: &Expression,
128 var: &Symbol,
129) -> (Expression, Expression) {
130 let dividend_degree = polynomial_degree_in_var(dividend, var);
131 let divisor_degree = polynomial_degree_in_var(divisor, var);
132
133 if dividend_degree < divisor_degree {
134 return (Expression::integer(0), dividend.clone());
135 }
136
137 if dividend_degree == divisor_degree {
139 let dividend_lc = polynomial_leading_coefficient(dividend, var);
140 let divisor_lc = polynomial_leading_coefficient(divisor, var);
141
142 let quotient_term = Expression::mul(vec![
143 dividend_lc,
144 Expression::pow(divisor_lc, Expression::integer(-1)),
145 ])
146 .simplify();
147
148 let product = Expression::mul(vec![quotient_term.clone(), divisor.clone()]).simplify();
149 let remainder = Expression::add(vec![
150 dividend.clone(),
151 Expression::mul(vec![Expression::integer(-1), product]),
152 ])
153 .simplify();
154
155 return (quotient_term, remainder);
156 }
157
158 (Expression::integer(0), dividend.clone())
160}
161
162fn polynomial_degree_in_var(expr: &Expression, var: &Symbol) -> i64 {
164 match expr {
165 Expression::Symbol(s) if s == var => 1,
166 Expression::Number(_) => 0,
167 Expression::Pow(base, exp) => {
168 if let (Expression::Symbol(s), Expression::Number(Number::Integer(e))) =
169 (base.as_ref(), exp.as_ref())
170 {
171 if s == var {
172 return *e;
173 }
174 }
175 0
176 }
177 Expression::Add(terms) => {
178 let mut max_degree = 0i64;
179 for term in terms.iter() {
180 let deg = polynomial_degree_in_var(term, var);
181 max_degree = max_degree.max(deg);
182 }
183 max_degree
184 }
185 Expression::Mul(factors) => {
186 let mut total_degree = 0i64;
187 for factor in factors.iter() {
188 total_degree += polynomial_degree_in_var(factor, var);
189 }
190 total_degree
191 }
192 _ => 0,
193 }
194}
195
196fn polynomial_leading_coefficient(expr: &Expression, var: &Symbol) -> Expression {
198 let degree = polynomial_degree_in_var(expr, var);
199
200 match expr {
201 Expression::Number(_n) => expr.clone(),
202 Expression::Symbol(s) if s == var => Expression::integer(1),
203 Expression::Pow(base, exp) => {
204 if let (Expression::Symbol(s), Expression::Number(Number::Integer(_))) =
205 (base.as_ref(), exp.as_ref())
206 {
207 if s == var {
208 return Expression::integer(1);
209 }
210 }
211 expr.clone()
212 }
213 Expression::Mul(factors) => {
214 let mut coeff = Expression::integer(1);
215 for factor in factors.iter() {
216 if polynomial_degree_in_var(factor, var) == 0 {
217 coeff = Expression::mul(vec![coeff, factor.clone()]);
218 }
219 }
220 coeff
221 }
222 Expression::Add(terms) => {
223 for term in terms.iter() {
224 if polynomial_degree_in_var(term, var) == degree {
225 return polynomial_leading_coefficient(term, var);
226 }
227 }
228 Expression::integer(0)
229 }
230 _ => Expression::integer(1),
231 }
232}
233
234pub fn polynomial_quo(dividend: &Expression, divisor: &Expression, var: &Symbol) -> Expression {
256 polynomial_div(dividend, divisor, var).0
257}
258
259pub fn polynomial_rem(dividend: &Expression, divisor: &Expression, var: &Symbol) -> Expression {
281 let vars = dividend.find_variables();
283 if vars.len() == 1 {
284 let dividend_var = &vars[0];
285 if dividend_var == var {
286 let divisor_vars = divisor.find_variables();
287 if divisor_vars.len() == 1
288 && &divisor_vars[0] == var
289 && IntPoly::can_convert(dividend, var)
290 && IntPoly::can_convert(divisor, var)
291 {
292 if let (Some(p1), Some(p2)) = (
293 IntPoly::try_from_expression(dividend, var),
294 IntPoly::try_from_expression(divisor, var),
295 ) {
296 if let Ok((_, r)) = p1.div_rem(&p2) {
297 return r.to_expression(var);
298 }
299 }
300 }
301 }
302 }
303
304 polynomial_div(dividend, divisor, var).1
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use crate::{expr, symbol};
311
312 #[test]
313 fn test_polynomial_div_exact() {
314 let x = symbol!(x);
315
316 let dividend = expr!((x ^ 2) - 1);
317 let divisor = expr!(x - 1);
318 let (_quot, rem) = polynomial_div(÷nd, &divisor, &x);
319
320 assert!(rem.is_zero(), "Expected zero remainder");
321 }
322
323 #[test]
324 fn test_polynomial_div_with_remainder() {
325 let x = symbol!(x);
326
327 let dividend = expr!((x ^ 2) + 1);
328 let divisor = expr!(x - 1);
329 let (_quot, rem) = polynomial_div(÷nd, &divisor, &x);
330
331 assert!(!rem.is_zero(), "Expected non-zero remainder");
332 }
333
334 #[test]
335 fn test_polynomial_div_by_constant() {
336 let x = symbol!(x);
337
338 let dividend = Expression::add(vec![
339 Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
340 Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
341 Expression::integer(1),
342 ]);
343 let divisor = Expression::integer(2);
344 let (_quot, rem) = polynomial_div(÷nd, &divisor, &x);
345
346 assert!(rem.is_zero(), "Expected zero remainder");
347 }
348
349 #[test]
350 fn test_polynomial_div_identical() {
351 let x = symbol!(x);
352
353 let dividend = expr!(x + 1);
354 let divisor = expr!(x + 1);
355 let (quot, rem) = polynomial_div(÷nd, &divisor, &x);
356
357 assert_eq!(quot, Expression::integer(1));
358 assert!(rem.is_zero());
359 }
360
361 #[test]
362 fn test_polynomial_quo() {
363 let x = symbol!(x);
364
365 let dividend = expr!((x ^ 2) - 1);
366 let divisor = expr!(x - 1);
367 let quot = polynomial_quo(÷nd, &divisor, &x);
368
369 assert!(!quot.is_zero());
370 }
371
372 #[test]
373 fn test_polynomial_rem() {
374 let x = symbol!(x);
375
376 let dividend = expr!((x ^ 2) + 1);
377 let divisor = expr!(x - 1);
378 let rem = polynomial_rem(÷nd, &divisor, &x);
379
380 assert!(!rem.is_zero());
381 }
382
383 #[test]
384 fn test_intpoly_fastpath() {
385 let x = symbol!(x);
386
387 let dividend = expr!((x ^ 3) + (2 * (x ^ 2)) + (3 * x) + 4);
388 let divisor = expr!((x ^ 2) + 1);
389 let (quot, rem) = polynomial_div(÷nd, &divisor, &x);
390
391 println!("Quotient: {}, Remainder: {}", quot, rem);
392 assert_ne!(quot, Expression::undefined());
393 }
394}