use crate::core::polynomial::IntPoly;
use crate::core::{Expression, Number, Symbol};
use crate::error::MathError;
use crate::simplify::Simplify;
pub fn polynomial_div(
dividend: &Expression,
divisor: &Expression,
var: &Symbol,
) -> Result<(Expression, Expression), MathError> {
if divisor.is_zero() {
return Err(MathError::DivisionByZero);
}
if dividend.is_zero() {
return Ok((Expression::integer(0), Expression::integer(0)));
}
if dividend == divisor {
return Ok((Expression::integer(1), Expression::integer(0)));
}
if let Some(divisor_const) = extract_constant(divisor) {
if divisor_const.is_zero() {
return Err(MathError::DivisionByZero);
}
let quotient = Expression::mul(vec![
dividend.clone(),
Expression::pow(divisor.clone(), Expression::integer(-1)),
])
.simplify();
return Ok((quotient, Expression::integer(0)));
}
let vars = dividend.find_variables();
if vars.len() == 1 {
let dividend_var = &vars[0];
if dividend_var == var {
let divisor_vars = divisor.find_variables();
if divisor_vars.len() == 1
&& &divisor_vars[0] == var
&& IntPoly::can_convert(dividend, var)
&& IntPoly::can_convert(divisor, var)
{
if let (Some(p1), Some(p2)) = (
IntPoly::try_from_expression(dividend, var),
IntPoly::try_from_expression(divisor, var),
) {
if let Ok((q, r)) = p1.div_rem(&p2) {
return Ok((q.to_expression(var), r.to_expression(var)));
}
}
}
}
}
symbolic_polynomial_div(dividend, divisor, var)
}
fn extract_constant(expr: &Expression) -> Option<Expression> {
match expr {
Expression::Number(_) => Some(expr.clone()),
_ => None,
}
}
fn symbolic_polynomial_div(
dividend: &Expression,
divisor: &Expression,
var: &Symbol,
) -> Result<(Expression, Expression), MathError> {
let dividend_degree = polynomial_degree_in_var(dividend, var);
let divisor_degree = polynomial_degree_in_var(divisor, var);
if dividend_degree < divisor_degree {
return Ok((Expression::integer(0), dividend.clone()));
}
if dividend_degree == divisor_degree {
let dividend_lc = polynomial_leading_coefficient(dividend, var);
let divisor_lc = polynomial_leading_coefficient(divisor, var);
let quotient_term = Expression::mul(vec![
dividend_lc,
Expression::pow(divisor_lc, Expression::integer(-1)),
])
.simplify();
let product = Expression::mul(vec![quotient_term.clone(), divisor.clone()]).simplify();
let remainder = Expression::add(vec![
dividend.clone(),
Expression::mul(vec![Expression::integer(-1), product]),
])
.simplify();
return Ok((quotient_term, remainder));
}
Err(MathError::NotImplemented {
feature: format!(
"complex symbolic polynomial division (degree {} ÷ degree {})",
dividend_degree, divisor_degree
),
})
}
fn polynomial_degree_in_var(expr: &Expression, var: &Symbol) -> i64 {
match expr {
Expression::Symbol(s) if s == var => 1,
Expression::Number(_) => 0,
Expression::Pow(base, exp) => {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(e))) =
(base.as_ref(), exp.as_ref())
{
if s == var {
return *e;
}
}
0
}
Expression::Add(terms) => {
let mut max_degree = 0i64;
for term in terms.iter() {
let deg = polynomial_degree_in_var(term, var);
max_degree = max_degree.max(deg);
}
max_degree
}
Expression::Mul(factors) => {
let mut total_degree = 0i64;
for factor in factors.iter() {
total_degree += polynomial_degree_in_var(factor, var);
}
total_degree
}
_ => 0,
}
}
fn polynomial_leading_coefficient(expr: &Expression, var: &Symbol) -> Expression {
let degree = polynomial_degree_in_var(expr, var);
match expr {
Expression::Number(_n) => expr.clone(),
Expression::Symbol(s) if s == var => Expression::integer(1),
Expression::Pow(base, exp) => {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(_))) =
(base.as_ref(), exp.as_ref())
{
if s == var {
return Expression::integer(1);
}
}
expr.clone()
}
Expression::Mul(factors) => {
let mut coeff = Expression::integer(1);
for factor in factors.iter() {
if polynomial_degree_in_var(factor, var) == 0 {
coeff = Expression::mul(vec![coeff, factor.clone()]);
}
}
coeff
}
Expression::Add(terms) => {
for term in terms.iter() {
if polynomial_degree_in_var(term, var) == degree {
return polynomial_leading_coefficient(term, var);
}
}
Expression::integer(0)
}
_ => Expression::integer(1),
}
}
pub fn polynomial_quo(
dividend: &Expression,
divisor: &Expression,
var: &Symbol,
) -> Result<Expression, MathError> {
polynomial_div(dividend, divisor, var).map(|(q, _)| q)
}
pub fn polynomial_rem(
dividend: &Expression,
divisor: &Expression,
var: &Symbol,
) -> Result<Expression, MathError> {
let vars = dividend.find_variables();
if vars.len() == 1 {
let dividend_var = &vars[0];
if dividend_var == var {
let divisor_vars = divisor.find_variables();
if divisor_vars.len() == 1
&& &divisor_vars[0] == var
&& IntPoly::can_convert(dividend, var)
&& IntPoly::can_convert(divisor, var)
{
if let (Some(p1), Some(p2)) = (
IntPoly::try_from_expression(dividend, var),
IntPoly::try_from_expression(divisor, var),
) {
if let Ok((_, r)) = p1.div_rem(&p2) {
return Ok(r.to_expression(var));
}
}
}
}
}
polynomial_div(dividend, divisor, var).map(|(_, r)| r)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{expr, symbol};
#[test]
fn test_polynomial_div_exact() {
let x = symbol!(x);
let dividend = expr!((x ^ 2) - 1);
let divisor = expr!(x - 1);
let (_quot, rem) = polynomial_div(÷nd, &divisor, &x).unwrap();
assert!(rem.is_zero(), "Expected zero remainder");
}
#[test]
fn test_polynomial_div_with_remainder() {
let x = symbol!(x);
let dividend = expr!((x ^ 2) + 1);
let divisor = expr!(x - 1);
let (_quot, rem) = polynomial_div(÷nd, &divisor, &x).unwrap();
assert!(!rem.is_zero(), "Expected non-zero remainder");
}
#[test]
fn test_polynomial_div_by_constant() {
let x = symbol!(x);
let dividend = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
Expression::integer(1),
]);
let divisor = Expression::integer(2);
let (_quot, rem) = polynomial_div(÷nd, &divisor, &x).unwrap();
assert!(rem.is_zero(), "Expected zero remainder");
}
#[test]
fn test_polynomial_div_identical() {
let x = symbol!(x);
let dividend = expr!(x + 1);
let divisor = expr!(x + 1);
let (quot, rem) = polynomial_div(÷nd, &divisor, &x).unwrap();
assert_eq!(quot, Expression::integer(1));
assert!(rem.is_zero());
}
#[test]
fn test_polynomial_quo() {
let x = symbol!(x);
let dividend = expr!((x ^ 2) - 1);
let divisor = expr!(x - 1);
let quot = polynomial_quo(÷nd, &divisor, &x).unwrap();
assert!(!quot.is_zero());
}
#[test]
fn test_polynomial_rem() {
let x = symbol!(x);
let dividend = expr!((x ^ 2) + 1);
let divisor = expr!(x - 1);
let rem = polynomial_rem(÷nd, &divisor, &x).unwrap();
assert!(!rem.is_zero());
}
#[test]
fn test_intpoly_fastpath() {
let x = symbol!(x);
let dividend = expr!((x ^ 3) + (2 * (x ^ 2)) + (3 * x) + 4);
let divisor = expr!((x ^ 2) + 1);
let (quot, rem) = polynomial_div(÷nd, &divisor, &x).unwrap();
println!("Quotient: {}, Remainder: {}", quot, rem);
assert_ne!(quot, Expression::undefined());
}
#[test]
fn test_polynomial_div_by_zero() {
let x = symbol!(x);
let dividend = expr!(x ^ 2);
let divisor = Expression::integer(0);
let result = polynomial_div(÷nd, &divisor, &x);
assert!(matches!(result, Err(MathError::DivisionByZero)));
}
}