use crate::calculus::limits::Limits;
use crate::core::{Expression, Symbol};
use super::helpers::{is_expression_zero, is_finite};
#[derive(Debug, Clone, PartialEq)]
pub enum SingularityType {
Removable,
Pole(u32),
Essential,
Unknown,
}
pub trait ComplexAnalysis {
fn is_analytic_at(&self, variable: &Symbol, point: &Expression) -> bool;
fn pole_order(&self, variable: &Symbol, pole: &Expression) -> u32;
fn is_removable_singularity(&self, variable: &Symbol, point: &Expression) -> bool;
fn is_essential_singularity(&self, variable: &Symbol, point: &Expression) -> bool;
}
impl ComplexAnalysis for Expression {
fn is_analytic_at(&self, variable: &Symbol, point: &Expression) -> bool {
use crate::calculus::ResidueCalculus;
let poles = self.find_poles(variable);
!poles.contains(point)
}
fn pole_order(&self, variable: &Symbol, pole: &Expression) -> u32 {
use crate::calculus::derivatives::Derivative;
use crate::calculus::integrals::rational::helpers::substitute_variable;
use crate::simplify::Simplify;
use super::helpers::extract_denominator;
let denominator = match extract_denominator(self) {
Some(denom) => denom,
None => return 0, };
let denom_at_pole = substitute_variable(&denominator, variable, pole).simplify();
if !is_expression_zero(&denom_at_pole) {
return 0; }
if let Expression::Pow(base, exp) = &denominator {
let base_at_pole = substitute_variable(base, variable, pole).simplify();
if is_expression_zero(&base_at_pole) {
if let Expression::Number(crate::core::Number::Integer(n)) = exp.as_ref() {
if *n > 0 {
return *n as u32;
}
}
}
}
const MAX_ORDER: u32 = 100;
let mut current_deriv = denominator;
for order in 1..=MAX_ORDER {
current_deriv = current_deriv.derivative(variable.clone());
let deriv_at_pole = substitute_variable(¤t_deriv, variable, pole).simplify();
if !is_expression_zero(&deriv_at_pole) {
return order;
}
}
0
}
fn is_removable_singularity(&self, variable: &Symbol, point: &Expression) -> bool {
let limit_result = self.limit(variable, point);
is_finite(&limit_result)
}
fn is_essential_singularity(&self, variable: &Symbol, point: &Expression) -> bool {
match self {
Expression::Function { name, args } if name.as_ref() == "exp" && args.len() == 1 => {
has_reciprocal_of_shifted_variable(&args[0], variable, point)
}
Expression::Function { name, args } if name.as_ref() == "sin" && args.len() == 1 => {
has_reciprocal_of_shifted_variable(&args[0], variable, point)
}
Expression::Function { name, args } if name.as_ref() == "cos" && args.len() == 1 => {
has_reciprocal_of_shifted_variable(&args[0], variable, point)
}
Expression::Function { name, args } if name.as_ref() == "log" && args.len() == 1 => {
is_shifted_variable(&args[0], variable, point)
}
_ => false,
}
}
}
fn has_reciprocal_of_shifted_variable(
expr: &Expression,
variable: &Symbol,
point: &Expression,
) -> bool {
use crate::calculus::integrals::rational::helpers::substitute_variable;
use crate::simplify::Simplify;
use super::helpers::{is_infinity, is_undefined};
match expr {
Expression::Pow(base, exp) => {
if let Expression::Number(crate::core::Number::Integer(n)) = exp.as_ref() {
if *n == -1 && is_shifted_variable(base, variable, point) {
return true;
}
}
false
}
Expression::Mul(factors) => factors
.iter()
.any(|f| has_reciprocal_of_shifted_variable(f, variable, point)),
_ => {
let test_expr = substitute_variable(expr, variable, point);
is_infinity(&test_expr.simplify()) || is_undefined(&test_expr.simplify())
}
}
}
fn is_shifted_variable(expr: &Expression, variable: &Symbol, point: &Expression) -> bool {
match expr {
Expression::Symbol(s) if s == variable && is_expression_zero(point) => true,
Expression::Add(terms) if terms.len() == 2 => {
let has_variable = terms
.iter()
.any(|t| matches!(t, Expression::Symbol(s) if s == variable));
let has_negative_point = terms.iter().any(|t| {
matches!(t, Expression::Mul(factors) if factors.len() == 2
&& matches!(&factors[0], Expression::Number(n) if *n == crate::core::Number::Integer(-1))
&& &factors[1] == point)
});
has_variable && has_negative_point
}
_ => false,
}
}
pub fn classify_singularity(
expr: &Expression,
variable: &Symbol,
point: &Expression,
) -> SingularityType {
let order = expr.pole_order(variable, point);
if order > 0 {
return SingularityType::Pole(order);
}
if expr.is_removable_singularity(variable, point) {
return SingularityType::Removable;
}
if expr.is_essential_singularity(variable, point) {
return SingularityType::Essential;
}
SingularityType::Unknown
}