mathhook_core/calculus/integrals/rational/
linear.rs

1//! Integration of linear factors in partial fraction decomposition
2//!
3//! Implements Heaviside's method for computing coefficients of repeated linear factors.
4
5use crate::calculus::derivatives::Derivative;
6use crate::core::{Expression, Symbol};
7use crate::simplify::Simplify;
8
9use super::helpers::{factorial, substitute_variable};
10
11/// Integrate linear partial fraction using Heaviside's method
12///
13/// For simple pole (power=1): Uses cover-up method
14/// For repeated pole (power>1): Uses Heaviside's method with derivatives
15///
16/// # Mathematical Basis
17///
18/// For `P(x)/Q(x)` with `Q(x) = (x-r)^n · R(x)`, we want:
19/// ```text
20/// P(x)/Q(x) = A₁/(x-r) + A₂/(x-r)² + ... + Aₙ/(x-r)ⁿ + (other terms)
21/// ```
22///
23/// Heaviside's method: Define `g(x) = (x-r)ⁿ · P(x)/Q(x) = P(x)/R(x)`
24/// Then: `Aₖ = (1/(n-k)!) · [d^(n-k)/dx^(n-k) g(x)]|ₓ₌ᵣ`
25///
26/// # Integration Formulas
27///
28/// - `∫A/(x-r) dx = A·ln|x-r| + C`
29/// - `∫A/(x-r)ⁿ dx = -A/((n-1)(x-r)^(n-1)) + C` for `n > 1`
30///
31/// # Arguments
32///
33/// * `numerator` - Numerator polynomial `P(x)`
34/// * `denominator` - Full denominator `Q(x)`
35/// * `root` - The root `r` of the linear factor
36/// * `power` - Multiplicity `n` of the root
37/// * `var` - Integration variable
38///
39/// # Returns
40///
41/// Integrated expression or `None` if integration fails
42///
43/// # Examples
44///
45/// ```
46/// use mathhook_core::{Expression, symbol};
47/// use mathhook_core::calculus::integrals::rational::linear::integrate_linear_factor;
48/// use mathhook_core::simplify::Simplify;
49///
50/// let x = symbol!(x);
51///
52/// let numerator = Expression::integer(1);
53/// let denominator = Expression::pow(
54///     Expression::add(vec![
55///         Expression::symbol(x.clone()),
56///         Expression::integer(-2),
57///     ]),
58///     Expression::integer(2),
59/// );
60/// let root = Expression::integer(2);
61///
62/// let result = integrate_linear_factor(&numerator, &denominator, &root, 2, &x);
63/// assert!(result.is_some());
64/// ```
65pub fn integrate_linear_factor(
66    numerator: &Expression,
67    denominator: &Expression,
68    root: &Expression,
69    power: i64,
70    var: &Symbol,
71) -> Option<Expression> {
72    let mut result = Expression::integer(0);
73
74    for k in 1..=power {
75        let coeff = if power == 1 {
76            substitute_variable(numerator, var, root).simplify()
77        } else {
78            compute_heaviside_coefficient(numerator, denominator, root, power, k, var)?
79        };
80
81        let x_minus_r = Expression::add(vec![
82            Expression::symbol(var.clone()),
83            Expression::mul(vec![Expression::integer(-1), root.clone()]),
84        ]);
85
86        let term = if k == 1 {
87            Expression::mul(vec![
88                coeff,
89                Expression::function("ln", vec![Expression::function("abs", vec![x_minus_r])]),
90            ])
91        } else {
92            Expression::mul(vec![
93                Expression::integer(-1),
94                coeff,
95                Expression::rational(1, k - 1),
96                Expression::pow(x_minus_r, Expression::integer(-(k - 1))),
97            ])
98        };
99
100        result = Expression::add(vec![result, term]);
101    }
102
103    Some(result)
104}
105
106/// Compute Heaviside coefficient for repeated linear factors
107///
108/// For `(x-r)^n` in denominator, coefficient k is:
109/// ```text
110/// Aₖ = (1/(n-k)!) · [d^(n-k)/dx^(n-k) of g(x)]|ₓ₌ᵣ
111/// ```
112/// where `g(x) = (x-r)^n · P(x)/Q(x)`
113///
114/// # Arguments
115///
116/// * `numerator` - Numerator polynomial `P(x)`
117/// * `denominator` - Full denominator `Q(x)`
118/// * `root` - The root `r`
119/// * `total_power` - Multiplicity `n`
120/// * `k` - Coefficient index (1 to n)
121/// * `var` - Integration variable
122///
123/// # Returns
124///
125/// The coefficient `Aₖ` or `None` if computation fails
126///
127/// # Examples
128///
129/// ```
130/// use mathhook_core::{Expression, symbol};
131/// use mathhook_core::calculus::integrals::rational::linear::compute_heaviside_coefficient;
132///
133/// let x = symbol!(x);
134/// let numerator = Expression::integer(1);
135/// let denominator = Expression::pow(
136///     Expression::add(vec![
137///         Expression::symbol(x.clone()),
138///         Expression::integer(-1),
139///     ]),
140///     Expression::integer(3),
141/// );
142/// let root = Expression::integer(1);
143///
144/// let coeff = compute_heaviside_coefficient(&numerator, &denominator, &root, 3, 1, &x);
145/// assert!(coeff.is_some());
146/// ```
147pub fn compute_heaviside_coefficient(
148    numerator: &Expression,
149    denominator: &Expression,
150    root: &Expression,
151    total_power: i64,
152    k: i64,
153    var: &Symbol,
154) -> Option<Expression> {
155    let x_minus_r = Expression::add(vec![
156        Expression::symbol(var.clone()),
157        Expression::mul(vec![Expression::integer(-1), root.clone()]),
158    ]);
159    let x_minus_r_pow_n = Expression::pow(x_minus_r, Expression::integer(total_power));
160
161    let g = Expression::mul(vec![
162        x_minus_r_pow_n,
163        Expression::mul(vec![
164            numerator.clone(),
165            Expression::pow(denominator.clone(), Expression::integer(-1)),
166        ]),
167    ])
168    .simplify();
169
170    let derivative_order = total_power - k;
171
172    let derivative_result = if derivative_order == 0 {
173        g
174    } else {
175        g.nth_derivative(var.clone(), derivative_order as u32)
176    };
177
178    let evaluated = substitute_variable(&derivative_result, var, root).simplify();
179
180    let fact = factorial(derivative_order);
181    Some(Expression::mul(vec![
182        Expression::rational(1, fact),
183        evaluated,
184    ]))
185}