mathhook_core/core/polynomial/
arithmetic.rs1use super::error::PolynomialError;
7use super::properties::PolynomialProperties;
8use crate::core::{Expression, Number, Symbol};
9use crate::simplify::Simplify;
10
11pub trait PolynomialArithmetic: PolynomialProperties {
40 fn poly_div(
78 &self,
79 divisor: &Expression,
80 var: &Symbol,
81 ) -> Result<(Expression, Expression), PolynomialError>;
82
83 fn poly_quo(&self, divisor: &Expression, var: &Symbol) -> Result<Expression, PolynomialError>;
94
95 fn poly_rem(&self, divisor: &Expression, var: &Symbol) -> Result<Expression, PolynomialError>;
106
107 fn is_divisible_by(&self, divisor: &Expression, var: &Symbol) -> bool;
118}
119
120impl PolynomialArithmetic for Expression {
121 fn poly_div(
122 &self,
123 divisor: &Expression,
124 var: &Symbol,
125 ) -> Result<(Expression, Expression), PolynomialError> {
126 polynomial_long_division(self, divisor, var)
127 }
128
129 fn poly_quo(&self, divisor: &Expression, var: &Symbol) -> Result<Expression, PolynomialError> {
130 let (quotient, _) = self.poly_div(divisor, var)?;
131 Ok(quotient)
132 }
133
134 fn poly_rem(&self, divisor: &Expression, var: &Symbol) -> Result<Expression, PolynomialError> {
135 let (_, remainder) = self.poly_div(divisor, var)?;
136 Ok(remainder)
137 }
138
139 fn is_divisible_by(&self, divisor: &Expression, var: &Symbol) -> bool {
140 match self.poly_rem(divisor, var) {
141 Ok(rem) => rem.is_zero(),
142 Err(_) => false,
143 }
144 }
145}
146
147fn polynomial_long_division(
151 dividend: &Expression,
152 divisor: &Expression,
153 var: &Symbol,
154) -> Result<(Expression, Expression), PolynomialError> {
155 use super::classification::PolynomialClassification;
156
157 if !dividend.is_polynomial_in(std::slice::from_ref(var)) {
159 return Err(PolynomialError::NotPolynomial {
160 expression: dividend.clone(),
161 reason: "dividend is not a polynomial in the given variable".to_owned(),
162 });
163 }
164 if !divisor.is_polynomial_in(std::slice::from_ref(var)) {
165 return Err(PolynomialError::NotPolynomial {
166 expression: divisor.clone(),
167 reason: "divisor is not a polynomial in the given variable".to_owned(),
168 });
169 }
170
171 let dividend_deg = dividend.degree(var).unwrap_or(0);
173 let divisor_deg = divisor.degree(var).unwrap_or(0);
174
175 if divisor.is_zero() {
177 return Err(PolynomialError::DivisionByZero);
178 }
179
180 if divisor_deg > dividend_deg {
182 return Ok((Expression::integer(0), dividend.clone()));
183 }
184
185 let divisor_lc = divisor.leading_coefficient(var);
187
188 let mut quotient_terms: Vec<Expression> = Vec::new();
190 let mut remainder = dividend.simplify();
191
192 let max_iterations = (dividend_deg + 2) as usize;
194 let mut iterations = 0;
195
196 while !remainder.is_zero() && iterations < max_iterations {
198 iterations += 1;
199
200 let rem_deg = remainder.degree(var).unwrap_or(-1);
201 if rem_deg < divisor_deg {
202 break;
203 }
204
205 let rem_lc = remainder.leading_coefficient(var);
207 let term_coef = divide_expressions(&rem_lc, &divisor_lc);
208 let term_deg = rem_deg - divisor_deg;
209
210 let term = if term_deg == 0 {
211 term_coef.clone()
212 } else {
213 Expression::mul(vec![
214 term_coef.clone(),
215 Expression::pow(
216 Expression::symbol(var.clone()),
217 Expression::integer(term_deg),
218 ),
219 ])
220 };
221
222 quotient_terms.push(term.clone());
223
224 let subtrahend = multiply_poly(&term, divisor);
226 remainder = subtract_poly(&remainder, &subtrahend);
227
228 remainder = remainder.simplify();
230 }
231
232 let quotient = if quotient_terms.is_empty() {
233 Expression::integer(0)
234 } else if quotient_terms.len() == 1 {
235 quotient_terms.into_iter().next().unwrap()
236 } else {
237 Expression::add(quotient_terms)
238 };
239
240 Ok((quotient.simplify(), remainder))
241}
242
243fn divide_expressions(num: &Expression, den: &Expression) -> Expression {
245 match (num, den) {
246 (Expression::Number(Number::Integer(a)), Expression::Number(Number::Integer(b)))
247 if *b != 0 =>
248 {
249 if a % b == 0 {
250 Expression::integer(a / b)
251 } else {
252 Expression::mul(vec![
253 num.clone(),
254 Expression::pow(den.clone(), Expression::integer(-1)),
255 ])
256 }
257 }
258 _ => Expression::mul(vec![
259 num.clone(),
260 Expression::pow(den.clone(), Expression::integer(-1)),
261 ]),
262 }
263}
264
265fn multiply_poly(a: &Expression, b: &Expression) -> Expression {
267 Expression::mul(vec![a.clone(), b.clone()])
268}
269
270fn subtract_poly(a: &Expression, b: &Expression) -> Expression {
272 Expression::add(vec![
273 a.clone(),
274 Expression::mul(vec![Expression::integer(-1), b.clone()]),
275 ])
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use crate::symbol;
282
283 #[test]
284 fn test_poly_div_simple() {
285 let x = symbol!(x);
286
287 let dividend = Expression::add(vec![
289 Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
290 Expression::integer(-1),
291 ]);
292 let divisor = Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(-1)]);
293
294 let result = dividend.poly_div(&divisor, &x);
295 assert!(result.is_ok());
296 }
297
298 #[test]
299 fn test_poly_div_with_remainder() {
300 let x = symbol!(x);
301
302 let dividend = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
304 let divisor = Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(-1)]);
305
306 let result = dividend.poly_div(&divisor, &x);
307 assert!(result.is_ok());
308 }
309
310 #[test]
311 fn test_poly_quo() {
312 let x = symbol!(x);
313
314 let dividend = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
315 let divisor = Expression::symbol(x.clone());
316
317 let result = dividend.poly_quo(&divisor, &x);
318 assert!(result.is_ok());
319 }
320
321 #[test]
322 fn test_poly_rem() {
323 let x = symbol!(x);
324
325 let dividend = Expression::symbol(x.clone());
326 let divisor = Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(1)]);
327
328 let result = dividend.poly_rem(&divisor, &x);
329 assert!(result.is_ok());
330 }
331
332 #[test]
333 fn test_division_by_zero() {
334 let x = symbol!(x);
335
336 let dividend = Expression::symbol(x.clone());
337 let divisor = Expression::integer(0);
338
339 let result = dividend.poly_div(&divisor, &x);
340 assert!(matches!(result, Err(PolynomialError::DivisionByZero)));
341 }
342
343 #[test]
344 fn test_is_divisible_by() {
345 let x = symbol!(x);
346
347 let dividend = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
349 let divisor = Expression::symbol(x.clone());
350
351 assert!(dividend.is_divisible_by(&divisor, &x));
352 }
353}