1mod helpers;
13mod pole_finding;
14mod singularities;
15
16use crate::calculus::derivatives::Derivative;
17use crate::core::{Expression, Symbol};
18use crate::simplify::Simplify;
19
20use pole_finding::{find_rational_poles, find_transcendental_poles};
21
22pub use singularities::{classify_singularity, ComplexAnalysis, SingularityType};
24
25pub trait ResidueCalculus {
27 fn residue(&self, variable: &Symbol, pole: &Expression) -> Expression;
47
48 fn find_poles(&self, variable: &Symbol) -> Vec<Expression>;
67
68 fn contour_integral(&self, variable: &Symbol) -> Expression;
84}
85
86pub struct ResidueMethods;
88
89impl ResidueMethods {
90 pub fn simple_pole_residue(
103 numerator: &Expression,
104 denominator: &Expression,
105 variable: &Symbol,
106 pole: &Expression,
107 ) -> Expression {
108 let z_minus_a = Expression::add(vec![
110 Expression::symbol(variable.clone()),
111 Expression::mul(vec![Expression::integer(-1), pole.clone()]),
112 ]);
113
114 let limit_expr = Expression::mul(vec![
115 z_minus_a,
116 Expression::mul(vec![
117 numerator.clone(),
118 Expression::pow(denominator.clone(), Expression::integer(-1)),
119 ]),
120 ]);
121
122 Expression::function(
123 "limit",
124 vec![
125 limit_expr,
126 Expression::symbol(variable.clone()),
127 pole.clone(),
128 ],
129 )
130 }
131
132 pub fn higher_order_pole_residue(
146 numerator: &Expression,
147 denominator: &Expression,
148 variable: &Symbol,
149 pole: &Expression,
150 order: u32,
151 ) -> Expression {
152 if order == 1 {
153 return Self::simple_pole_residue(numerator, denominator, variable, pole);
154 }
155
156 let z_minus_a = Expression::add(vec![
158 Expression::symbol(variable.clone()),
159 Expression::mul(vec![Expression::integer(-1), pole.clone()]),
160 ]);
161
162 let multiplied_expr = Expression::mul(vec![
163 Expression::pow(z_minus_a, Expression::integer(order as i64)),
164 Expression::mul(vec![
165 numerator.clone(),
166 Expression::pow(denominator.clone(), Expression::integer(-1)),
167 ]),
168 ]);
169
170 let derivative_expr = multiplied_expr.nth_derivative(variable.clone(), order - 1);
171
172 let factorial = Self::factorial(order - 1);
173 let limit_result = Expression::function(
174 "limit",
175 vec![
176 derivative_expr,
177 Expression::symbol(variable.clone()),
178 pole.clone(),
179 ],
180 );
181
182 Expression::mul(vec![
183 Expression::pow(factorial, Expression::integer(-1)),
184 limit_result,
185 ])
186 .simplify()
187 }
188
189 pub fn factorial(n: u32) -> Expression {
199 let result = match n {
200 0 | 1 => 1,
201 _ => (2..=n as i64).product(),
202 };
203 Expression::integer(result)
204 }
205}
206
207impl ResidueCalculus for Expression {
208 fn residue(&self, variable: &Symbol, pole: &Expression) -> Expression {
209 if let Expression::Mul(factors) = self {
211 if factors.len() == 2 {
212 if let Expression::Pow(denom, exp) = &factors[1] {
213 if **exp == Expression::integer(-1) {
214 let numerator = &factors[0];
216 let denominator = denom;
217
218 let order = self.pole_order(variable, pole);
220 return ResidueMethods::higher_order_pole_residue(
221 numerator,
222 denominator,
223 variable,
224 pole,
225 order,
226 );
227 }
228 }
229 }
230 }
231
232 Expression::function(
234 "residue",
235 vec![
236 self.clone(),
237 Expression::symbol(variable.clone()),
238 pole.clone(),
239 ],
240 )
241 }
242
243 fn find_poles(&self, variable: &Symbol) -> Vec<Expression> {
244 match self {
246 Expression::Mul(factors) if factors.len() == 2 => {
248 if let Expression::Pow(denom, exp) = &factors[1] {
249 if **exp == Expression::integer(-1) {
250 let numerator = &factors[0];
251 return find_rational_poles(numerator, denom, variable);
252 }
253 }
254 vec![]
255 }
256 Expression::Pow(base, exp) => {
258 if let Expression::Number(crate::core::Number::Integer(n)) = exp.as_ref() {
259 if *n == -1 {
260 return find_rational_poles(&Expression::integer(1), base, variable);
261 }
262 }
263 vec![]
264 }
265 Expression::Function { name, args } => find_transcendental_poles(name, args, variable),
267 _ => vec![],
268 }
269 }
270
271 fn contour_integral(&self, variable: &Symbol) -> Expression {
272 let poles = self.find_poles(variable);
273 if poles.is_empty() {
274 return Expression::integer(0);
275 }
276
277 let residue_sum = if poles.len() == 1 {
279 self.residue(variable, &poles[0])
280 } else {
281 let residues: Vec<Expression> = poles
282 .iter()
283 .map(|pole| self.residue(variable, pole))
284 .collect();
285 Expression::add(residues)
286 };
287
288 Expression::mul(vec![
289 Expression::integer(2),
290 Expression::pi(),
291 Expression::i(),
292 residue_sum,
293 ])
294 .simplify()
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use crate::expr;
302 use crate::symbol;
303
304 #[test]
305 fn test_simple_pole_residue() {
306 let z = symbol!(z);
307 let numerator = Expression::integer(1);
308 let denominator =
309 Expression::add(vec![Expression::symbol(z.clone()), Expression::integer(-1)]);
310 let pole = Expression::integer(1);
311
312 let result = ResidueMethods::simple_pole_residue(&numerator, &denominator, &z, &pole);
313
314 assert!(matches!(result, Expression::Function { name, .. } if name.as_ref() == "limit"));
316 }
317
318 #[test]
319 fn test_factorial() {
320 assert_eq!(ResidueMethods::factorial(0), Expression::integer(1));
321 assert_eq!(ResidueMethods::factorial(1), Expression::integer(1));
322 assert_eq!(ResidueMethods::factorial(4), Expression::integer(24));
323 assert_eq!(ResidueMethods::factorial(5), Expression::integer(120));
324 }
325
326 #[test]
327 fn test_pole_order() {
328 let z = symbol!(z);
329 let expr = Expression::mul(vec![
330 Expression::integer(1),
331 Expression::pow(
332 Expression::pow(
333 Expression::add(vec![Expression::symbol(z.clone()), Expression::integer(-1)]),
334 Expression::integer(2),
335 ),
336 Expression::integer(-1),
337 ),
338 ]);
339 let pole = Expression::integer(1);
340
341 let order = expr.pole_order(&z, &pole);
342 assert!(order >= 1, "Pole order should be at least 1, got {}", order);
343 }
344
345 #[test]
346 fn test_is_analytic() {
347 let z = symbol!(z);
348 let polynomial = Expression::pow(Expression::symbol(z.clone()), Expression::integer(2));
349 let point = Expression::integer(1);
350
351 assert!(polynomial.is_analytic_at(&z, &point));
352 }
353
354 #[test]
355 fn test_pole_order_simple_pole() {
356 let z = symbol!(z);
357 let expr = Expression::mul(vec![
359 Expression::integer(1),
360 Expression::pow(
361 Expression::add(vec![Expression::symbol(z.clone()), Expression::integer(-2)]),
362 Expression::integer(-1),
363 ),
364 ]);
365 let pole = Expression::integer(2);
366
367 let order = expr.pole_order(&z, &pole);
368 assert_eq!(order, 1, "1/(z-2) should have pole of order 1 at z=2");
369 }
370
371 #[test]
372 fn test_pole_order_double_pole() {
373 let z = symbol!(z);
374 let expr = Expression::pow(
376 Expression::add(vec![Expression::symbol(z.clone()), Expression::integer(-3)]),
377 Expression::integer(-2),
378 );
379 let pole = Expression::integer(3);
380
381 let order = expr.pole_order(&z, &pole);
382 assert_eq!(order, 2, "1/(z-3)^2 should have pole of order 2 at z=3");
383 }
384
385 #[test]
386 fn test_pole_order_triple_pole() {
387 let z = symbol!(z);
388 let expr = Expression::pow(
390 Expression::add(vec![Expression::symbol(z.clone()), Expression::integer(-1)]),
391 Expression::integer(-3),
392 );
393 let pole = Expression::integer(1);
394
395 let order = expr.pole_order(&z, &pole);
396 assert_eq!(order, 3, "1/(z-1)^3 should have pole of order 3 at z=1");
397 }
398
399 #[test]
400 fn test_pole_order_no_pole() {
401 let z = symbol!(z);
402 let expr = Expression::mul(vec![
404 Expression::integer(1),
405 Expression::pow(
406 Expression::add(vec![Expression::symbol(z.clone()), Expression::integer(-2)]),
407 Expression::integer(-1),
408 ),
409 ]);
410 let point = Expression::integer(5);
411
412 let order = expr.pole_order(&z, &point);
413 assert_eq!(
414 order, 0,
415 "1/(z-2) should have no pole at z=5 (denominator is non-zero)"
416 );
417 }
418
419 #[test]
420 fn test_pole_order_polynomial_no_pole() {
421 let z = symbol!(z);
422 let expr = Expression::pow(Expression::symbol(z.clone()), Expression::integer(2));
424 let point = Expression::integer(0);
425
426 let order = expr.pole_order(&z, &point);
427 assert_eq!(order, 0, "Polynomial z^2 should have no poles");
428 }
429
430 #[test]
431 fn test_pole_order_at_origin() {
432 let z = symbol!(z);
433 let expr = Expression::pow(Expression::symbol(z.clone()), Expression::integer(-1));
435 let pole = Expression::integer(0);
436
437 let order = expr.pole_order(&z, &pole);
438 assert_eq!(order, 1, "1/z should have pole of order 1 at z=0");
439 }
440
441 #[test]
442 fn test_pole_order_at_origin_higher() {
443 let z = symbol!(z);
444 let expr = Expression::pow(Expression::symbol(z.clone()), Expression::integer(-4));
446 let pole = Expression::integer(0);
447
448 let order = expr.pole_order(&z, &pole);
449 assert_eq!(order, 4, "1/z^4 should have pole of order 4 at z=0");
450 }
451
452 #[test]
453 fn test_classify_singularity_pole() {
454 let z = symbol!(z);
455 let expr = expr!((z - 1) ^ (-2));
457 let point = expr!(1);
458
459 let classification = classify_singularity(&expr, &z, &point);
460 assert_eq!(classification, SingularityType::Pole(2));
461 }
462
463 #[test]
464 fn test_find_poles_transcendental_tan() {
465 let x = symbol!(x);
466 let expr = Expression::function("tan", vec![Expression::symbol(x.clone())]);
467
468 let poles = expr.find_poles(&x);
469 assert!(!poles.is_empty(), "tan(x) should have poles");
470 }
471
472 #[test]
473 fn test_find_poles_transcendental_cot() {
474 let x = symbol!(x);
475 let expr = Expression::function("cot", vec![Expression::symbol(x.clone())]);
476
477 let poles = expr.find_poles(&x);
478 assert!(!poles.is_empty(), "cot(x) should have poles");
479 assert_eq!(poles[0], expr!(0));
480 }
481}