use crate::algebra::gcd::PolynomialGcd;
use crate::core::constants::EPSILON;
use crate::core::{Expression, Number, Symbol};
use crate::simplify::Simplify;
pub mod helpers;
pub mod linear;
pub mod quadratic;
use helpers::{is_polynomial, polynomial_degree};
use linear::integrate_linear_factor;
use quadratic::{integrate_repeated_quadratic, integrate_simple_quadratic};
#[derive(Debug, Clone)]
pub struct LinearTerm {
pub coefficient: Expression,
pub root: Expression,
pub power: i64,
}
#[derive(Debug, Clone)]
pub struct QuadraticTerm {
pub numerator_linear_coeff: Expression,
pub numerator_constant: Expression,
pub p_coeff: Expression,
pub q_coeff: Expression,
pub power: i64,
}
#[derive(Debug, Clone)]
pub struct PartialFractionDecomposition {
pub polynomial_part: Expression,
pub linear_terms: Vec<LinearTerm>,
pub quadratic_terms: Vec<QuadraticTerm>,
}
#[derive(Debug, Clone)]
enum Factor {
Linear {
root: Expression,
power: i64,
},
Quadratic {
p: Expression,
q: Expression,
power: i64,
},
}
pub fn is_rational_function(expr: &Expression, var: &Symbol) -> bool {
if is_polynomial(expr, var) {
return true;
}
match expr {
Expression::Add(terms) => terms.iter().all(|term| is_rational_function(term, var)),
Expression::Mul(factors) => {
factors.iter().all(|factor| {
match factor {
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(_e)) = exp.as_ref() {
is_polynomial(base, var) } else {
false
}
}
_ => is_polynomial(factor, var),
}
})
}
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(_e)) = exp.as_ref() {
is_polynomial(base, var) } else {
false
}
}
_ => false,
}
}
pub fn extract_numerator_denominator(expr: &Expression) -> (Expression, Expression) {
match expr {
Expression::Mul(factors) => {
let mut numerator_factors = Vec::new();
let mut denominator_factors = Vec::new();
for factor in factors.iter() {
if let Expression::Pow(base, exp) = factor {
if let Expression::Number(Number::Integer(e)) = exp.as_ref() {
if *e < 0 {
let positive_exp = Expression::integer(-e);
denominator_factors
.push(Expression::pow((**base).clone(), positive_exp));
} else {
numerator_factors.push(factor.clone());
}
} else {
numerator_factors.push(factor.clone());
}
} else {
numerator_factors.push(factor.clone());
}
}
let numerator = if numerator_factors.is_empty() {
Expression::integer(1)
} else {
Expression::mul(numerator_factors)
};
let denominator = if denominator_factors.is_empty() {
Expression::integer(1)
} else {
Expression::mul(denominator_factors)
};
(numerator, denominator)
}
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(e)) = exp.as_ref() {
if *e < 0 {
(
Expression::integer(1),
Expression::pow((**base).clone(), Expression::integer(-e)),
)
} else {
(expr.clone(), Expression::integer(1))
}
} else {
(expr.clone(), Expression::integer(1))
}
}
_ => (expr.clone(), Expression::integer(1)),
}
}
pub fn integrate_rational(expr: &Expression, var: &Symbol) -> Option<Expression> {
if !is_rational_function(expr, var) {
return None;
}
let (numerator, denominator) = extract_numerator_denominator(expr);
let num_degree = polynomial_degree(&numerator, var);
let den_degree = polynomial_degree(&denominator, var);
let is_simple_monomial = match &denominator {
Expression::Symbol(_) => true,
Expression::Pow(base, _) => matches!(base.as_ref(), Expression::Symbol(_)),
_ => false,
};
if num_degree == 0 && den_degree >= 1 && is_simple_monomial {
return None;
}
if den_degree == 0 {
return None;
}
let (quotient, remainder) = if num_degree >= den_degree {
numerator.div_polynomial(&denominator, var)
} else {
(Expression::integer(0), numerator)
};
let polynomial_integral = if !quotient.is_zero() {
integrate_polynomial("ient, var)
} else {
Expression::integer(0)
};
if remainder.is_zero() {
return Some(polynomial_integral);
}
let factors = factor_simple_denominator(&denominator, var)?;
let mut result = polynomial_integral;
for factor in factors.iter() {
match factor {
Factor::Linear { root, power } => {
let factor_result =
integrate_linear_factor(&remainder, &denominator, root, *power, var)?;
result = Expression::add(vec![result, factor_result]).simplify();
}
Factor::Quadratic { p, q, power } => {
if *power == 1 {
let factor_result =
integrate_simple_quadratic(&remainder, &denominator, p, q, var)?;
result = Expression::add(vec![result, factor_result]).simplify();
} else {
let factor_result =
integrate_repeated_quadratic(&remainder, &denominator, p, q, *power, var)?;
result = Expression::add(vec![result, factor_result]).simplify();
}
}
}
}
Some(result.simplify())
}
fn integrate_polynomial(poly: &Expression, var: &Symbol) -> Expression {
match poly {
Expression::Number(_) => {
Expression::mul(vec![poly.clone(), Expression::symbol(var.clone())])
}
Expression::Symbol(s) if s == var => Expression::mul(vec![
Expression::rational(1, 2),
Expression::pow(Expression::symbol(var.clone()), Expression::integer(2)),
]),
Expression::Pow(base, exp) => {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(n))) =
(base.as_ref(), exp.as_ref())
{
if s == var {
let new_exp = n + 1;
return Expression::mul(vec![
Expression::rational(1, new_exp),
Expression::pow(
Expression::symbol(var.clone()),
Expression::integer(new_exp),
),
]);
}
}
Expression::mul(vec![poly.clone(), Expression::symbol(var.clone())])
}
Expression::Mul(factors) => {
let mut coeff = Expression::integer(1);
let mut var_power = 0i64;
for factor in factors.iter() {
if let Expression::Symbol(s) = factor {
if s == var {
var_power += 1;
continue;
}
}
if let Expression::Pow(base, exp) = factor {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(e))) =
(base.as_ref(), exp.as_ref())
{
if s == var {
var_power += e;
continue;
}
}
}
coeff = Expression::mul(vec![coeff, factor.clone()]);
}
let new_power = var_power + 1;
Expression::mul(vec![
Expression::mul(vec![coeff, Expression::rational(1, new_power)]),
Expression::pow(
Expression::symbol(var.clone()),
Expression::integer(new_power),
),
])
}
Expression::Add(terms) => {
Expression::add(terms.iter().map(|t| integrate_polynomial(t, var)).collect())
}
_ => Expression::mul(vec![poly.clone(), Expression::symbol(var.clone())]),
}
}
fn factor_simple_denominator(denom: &Expression, var: &Symbol) -> Option<Vec<Factor>> {
let mut factors = Vec::new();
match denom {
Expression::Add(terms) => {
if terms.len() == 2 {
if let Expression::Symbol(s) = &terms[0] {
if s == var {
if let Expression::Number(_) = &terms[1] {
let root =
Expression::mul(vec![Expression::integer(-1), terms[1].clone()]);
factors.push(Factor::Linear { root, power: 1 });
return Some(factors);
}
}
}
}
if let Some((p, q)) = helpers::try_extract_quadratic(denom, var) {
if let (
Expression::Number(Number::Integer(p_val)),
Expression::Number(Number::Integer(q_val)),
) = (&p, &q)
{
let discriminant = p_val * p_val - 4 * q_val;
if discriminant == 0 {
let root = Expression::rational(-p_val, 2);
factors.push(Factor::Linear { root, power: 2 });
return Some(factors);
} else if discriminant > 0 {
let sqrt_disc = (discriminant as f64).sqrt();
if sqrt_disc.fract().abs() < EPSILON {
let sqrt_disc_int = sqrt_disc as i64;
let root1 = Expression::rational(-p_val + sqrt_disc_int, 2);
let root2 = Expression::rational(-p_val - sqrt_disc_int, 2);
factors.push(Factor::Linear {
root: root1,
power: 1,
});
factors.push(Factor::Linear {
root: root2,
power: 1,
});
return Some(factors);
}
}
}
factors.push(Factor::Quadratic { p, q, power: 1 });
return Some(factors);
}
factors.push(Factor::Linear {
root: Expression::integer(0),
power: 1,
});
}
Expression::Mul(terms) => {
for term in terms.iter() {
if let Some(mut term_factors) = factor_simple_denominator(term, var) {
factors.append(&mut term_factors);
}
}
}
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(e)) = exp.as_ref() {
if let Some(base_factors) = factor_simple_denominator(base, var) {
for factor in base_factors {
match factor {
Factor::Linear { root, power } => {
factors.push(Factor::Linear {
root,
power: power * e,
});
}
Factor::Quadratic { p, q, power } => {
factors.push(Factor::Quadratic {
p,
q,
power: power * e,
});
}
}
}
return Some(factors);
}
}
}
Expression::Symbol(s) if s == var => {
factors.push(Factor::Linear {
root: Expression::integer(0),
power: 1,
});
}
_ => {
return None;
}
}
Some(factors)
}