use std::sync::Arc;
use crate::symbolic::calculus::differentiate;
use crate::symbolic::calculus::factorial;
use crate::symbolic::core::Expr;
use crate::symbolic::simplify::is_zero;
use crate::symbolic::simplify_dag::simplify;
#[must_use]
pub fn gamma(arg: &Expr) -> Expr {
let s_arg = simplify(arg);
if let Some(n) = s_arg.to_f64() {
if n > 0.0 && n.fract() == 0.0 {
return Expr::Constant(factorial(((n - 1.0) as i64).try_into().unwrap_or(0)));
}
if (n - 0.5).abs() < 1e-9 {
return Expr::new_sqrt(Expr::Pi);
}
}
if s_arg == Expr::Constant(1.0) {
return Expr::Constant(1.0);
}
if let Expr::Add(a, b) = &s_arg {
if **b == Expr::Constant(1.0) {
return simplify(&Expr::new_mul(a.clone(), gamma(&a.as_ref().clone())));
}
if **a == Expr::Constant(1.0) {
return simplify(&Expr::new_mul(b.clone(), gamma(&b.as_ref().clone())));
}
}
Expr::new_gamma(s_arg)
}
#[must_use]
pub fn ln_gamma(arg: &Expr) -> Expr {
let g = gamma(arg);
if let Expr::Constant(v) = &g {
if *v > 0.0 {
return Expr::Constant(v.ln());
}
}
Expr::new_log(g)
}
#[must_use]
pub fn beta(
a: Expr,
b: Expr,
) -> Expr {
let gamma_a = gamma(&a);
let gamma_b = gamma(&b);
let gamma_a_plus_b = gamma(&simplify(&Expr::new_add(a, b)));
simplify(&Expr::new_div(
Expr::new_mul(gamma_a, gamma_b),
gamma_a_plus_b,
))
}
#[must_use]
pub fn digamma(arg: &Expr) -> Expr {
let s_arg = simplify(arg);
if let Some(n) = s_arg.to_f64() {
if (n - 1.0).abs() < 1e-9 {
return Expr::Variable("-gamma".to_string());
}
}
if let Expr::Add(a, b) = &s_arg {
if **b == Expr::Constant(1.0) {
return simplify(&Expr::new_add(
digamma(&a.as_ref().clone()),
Expr::new_div(Expr::Constant(1.0), a.clone()),
));
}
if **a == Expr::Constant(1.0) {
return simplify(&Expr::new_add(
digamma(&b.as_ref().clone()),
Expr::new_div(Expr::Constant(1.0), b.clone()),
));
}
}
Expr::new_digamma(s_arg)
}
#[must_use]
pub fn polygamma(
n: &Expr,
z: &Expr,
) -> Expr {
let s_n = simplify(n);
let s_z = simplify(z);
if let Some(order) = s_n.to_f64() {
if order.abs() < 1e-9 {
return digamma(&s_z);
}
}
Expr::BinaryList("polygamma".to_string(), Arc::new(s_n), Arc::new(s_z))
}
#[must_use]
pub fn erf(arg: &Expr) -> Expr {
let s_arg = simplify(arg);
if is_zero(&s_arg) {
return Expr::Constant(0.0);
}
if let Expr::Neg(inner) = s_arg {
return Expr::new_neg(erf(&(*inner).clone()));
}
Expr::new_erf(s_arg)
}
#[must_use]
pub fn erfc(arg: &Expr) -> Expr {
simplify(&Expr::new_sub(Expr::Constant(1.0), erf(arg)))
}
#[must_use]
pub fn erfi(arg: Expr) -> Expr {
let i = Expr::new_complex(Expr::Constant(0.0), Expr::Constant(1.0));
simplify(&Expr::new_mul(
Expr::new_neg(i.clone()),
erf(&Expr::new_mul(i, arg)),
))
}
#[must_use]
pub fn zeta(arg: &Expr) -> Expr {
let s_arg = simplify(arg);
if let Some(n) = s_arg.to_f64() {
if n.fract() == 0.0 {
let n_int = n as i32;
if n_int == 0 {
return Expr::Constant(-0.5);
}
if n_int == 1 {
return Expr::Infinity;
}
if n_int == 2 {
return simplify(&Expr::new_div(
Expr::new_pow(Expr::Pi, Expr::Constant(2.0)),
Expr::Constant(6.0),
));
}
if n_int == 4 {
return simplify(&Expr::new_div(
Expr::new_pow(Expr::Pi, Expr::Constant(4.0)),
Expr::Constant(90.0),
));
}
if n_int < 0 && n_int % 2 == 0 {
return Expr::Constant(0.0);
}
}
}
Expr::new_zeta(s_arg)
}
#[must_use]
pub fn bessel_j(
order: &Expr,
arg: &Expr,
) -> Expr {
let s_order = simplify(order);
let s_arg = simplify(arg);
if is_zero(&s_arg) {
if let Some(n) = s_order.to_f64() {
if n == 0.0 {
return Expr::Constant(1.0);
}
if n > 0.0 {
return Expr::Constant(0.0);
}
}
}
if let Expr::Neg(inner_order) = &s_order {
if let Some(n) = inner_order.to_f64() {
if n.fract() == 0.0 {
let factor = Expr::new_pow(Expr::Constant(-1.0), Expr::Constant(n));
return simplify(&Expr::new_mul(
factor,
bessel_j(&inner_order.as_ref().clone(), &s_arg),
));
}
}
}
Expr::new_bessel_j(s_order, s_arg)
}
#[must_use]
pub fn bessel_y(
order: &Expr,
arg: &Expr,
) -> Expr {
let s_order = simplify(order);
let s_arg = simplify(arg);
if is_zero(&s_arg) {
return Expr::NegativeInfinity;
}
Expr::new_bessel_y(s_order, s_arg)
}
#[must_use]
pub fn bessel_i(
order: &Expr,
arg: &Expr,
) -> Expr {
let s_order = simplify(order);
let s_arg = simplify(arg);
if is_zero(&s_arg) {
if let Some(n) = s_order.to_f64() {
if n == 0.0 {
return Expr::Constant(1.0);
}
if n > 0.0 {
return Expr::Constant(0.0);
}
}
}
Expr::BinaryList("bessel_i".to_string(), Arc::new(s_order), Arc::new(s_arg))
}
#[must_use]
pub fn bessel_k(
order: &Expr,
arg: &Expr,
) -> Expr {
let s_order = simplify(order);
let s_arg = simplify(arg);
if is_zero(&s_arg) {
return Expr::Infinity;
}
Expr::BinaryList("bessel_k".to_string(), Arc::new(s_order), Arc::new(s_arg))
}
#[must_use]
pub fn legendre_p(
degree: &Expr,
arg: Expr,
) -> Expr {
let s_degree = simplify(degree);
if let Some(n) = s_degree.to_f64() {
let n_int = n as i32;
if n >= 0.0 && n.fract() == 0.0 {
if n_int == 0 {
return Expr::Constant(1.0);
}
if n_int == 1 {
return arg;
}
if n_int <= 10 {
let p_n = legendre_p(&Expr::Constant(n - 1.0), arg.clone());
let p_n_minus_1 = legendre_p(&Expr::Constant(n - 2.0), arg.clone());
let term1 = Expr::new_mul(
Expr::Constant(2.0f64.mul_add(n, -1.0)),
Expr::new_mul(arg, p_n),
);
let term2 = Expr::new_mul(Expr::Constant(n - 1.0), p_n_minus_1);
return simplify(&Expr::new_div(
Expr::new_sub(term1, term2),
Expr::Constant(n),
));
}
}
}
Expr::new_legendre_p(s_degree, arg)
}
#[must_use]
pub fn laguerre_l(
degree: &Expr,
arg: Expr,
) -> Expr {
let s_degree = simplify(degree);
if let Some(n) = s_degree.to_f64() {
let n_int = n as i32;
if n >= 0.0 && n.fract() == 0.0 {
if n_int == 0 {
return Expr::Constant(1.0);
}
if n_int == 1 {
return simplify(&Expr::new_sub(Expr::Constant(1.0), arg));
}
if n_int <= 10 {
let l_n = laguerre_l(&Expr::Constant(n - 1.0), arg.clone());
let l_n_minus_1 = laguerre_l(&Expr::Constant(n - 2.0), arg.clone());
let term1_factor =
simplify(&Expr::new_sub(Expr::Constant(2.0f64.mul_add(n, -1.0)), arg));
let term1 = Expr::new_mul(term1_factor, l_n);
let term2 = Expr::new_mul(Expr::Constant(n - 1.0), l_n_minus_1);
return simplify(&Expr::new_div(
Expr::new_sub(term1, term2),
Expr::Constant(n),
));
}
}
}
Expr::new_laguerre_l(s_degree, arg)
}
#[must_use]
pub fn generalized_laguerre(
n: &Expr,
alpha: &Expr,
x: &Expr,
) -> Expr {
let s_n = simplify(n);
let s_alpha = simplify(alpha);
let s_x = simplify(x);
if let Some(a) = s_alpha.to_f64() {
if a.abs() < 1e-9 {
return laguerre_l(&s_n, s_x);
}
}
if let Some(n_val) = s_n.to_f64() {
if n_val.abs() < 1e-9 {
return Expr::Constant(1.0);
}
}
Expr::NaryList("generalized_laguerre".to_string(), vec![s_n, s_alpha, s_x])
}
#[must_use]
pub fn hermite_h(
degree: &Expr,
arg: Expr,
) -> Expr {
let s_degree = simplify(degree);
if let Some(n) = s_degree.to_f64() {
let n_int = n as i32;
if n >= 0.0 && n.fract() == 0.0 {
if n_int == 0 {
return Expr::Constant(1.0);
}
if n_int == 1 {
return simplify(&Expr::new_mul(Expr::Constant(2.0), arg));
}
if n_int <= 10 {
let h_n = hermite_h(&Expr::Constant(n - 1.0), arg.clone());
let h_n_minus_1 = hermite_h(&Expr::Constant(n - 2.0), arg.clone());
let term1 = Expr::new_mul(Expr::Constant(2.0), Expr::new_mul(arg, h_n));
let term2 = Expr::new_mul(Expr::Constant(2.0 * (n - 1.0)), h_n_minus_1);
return simplify(&Expr::new_sub(term1, term2));
}
}
}
Expr::new_hermite_h(s_degree, arg)
}
#[must_use]
pub fn chebyshev_t(
n: &Expr,
x: &Expr,
) -> Expr {
let s_n = simplify(n);
let s_x = simplify(x);
if let Some(n_val) = s_n.to_f64() {
let n_int = n_val as i32;
if n_val >= 0.0 && n_val.fract() == 0.0 {
if n_int == 0 {
return Expr::Constant(1.0);
}
if n_int == 1 {
return s_x;
}
if n_int <= 10 {
let t_n1 = chebyshev_t(&Expr::Constant(n_val - 1.0), &s_x);
let t_n2 = chebyshev_t(&Expr::Constant(n_val - 2.0), &s_x);
return simplify(&Expr::new_sub(
Expr::new_mul(Expr::Constant(2.0), Expr::new_mul(s_x, t_n1)),
t_n2,
));
}
}
}
Expr::BinaryList("chebyshev_t".to_string(), Arc::new(s_n), Arc::new(s_x))
}
#[must_use]
pub fn chebyshev_u(
n: &Expr,
x: &Expr,
) -> Expr {
let s_n = simplify(n);
let s_x = simplify(x);
if let Some(n_val) = s_n.to_f64() {
let n_int = n_val as i32;
if n_val >= 0.0 && n_val.fract() == 0.0 {
if n_int == 0 {
return Expr::Constant(1.0);
}
if n_int == 1 {
return simplify(&Expr::new_mul(Expr::Constant(2.0), s_x));
}
if n_int <= 10 {
let u_n1 = chebyshev_u(&Expr::Constant(n_val - 1.0), &s_x);
let u_n2 = chebyshev_u(&Expr::Constant(n_val - 2.0), &s_x);
return simplify(&Expr::new_sub(
Expr::new_mul(Expr::Constant(2.0), Expr::new_mul(s_x, u_n1)),
u_n2,
));
}
}
}
Expr::BinaryList("chebyshev_u".to_string(), Arc::new(s_n), Arc::new(s_x))
}
#[must_use]
pub fn bessel_differential_equation(
y: &Expr,
x: &Expr,
n: &Expr,
) -> Expr {
let y_prime = differentiate(y, "x");
let y_double_prime = differentiate(&y_prime, "x");
let term1 = Expr::new_mul(
Expr::new_pow(x.clone(), Expr::Constant(2.0)),
y_double_prime,
);
let term2 = Expr::new_mul(x.clone(), y_prime);
let term3 = Expr::new_mul(
Expr::new_sub(
Expr::new_pow(x.clone(), Expr::Constant(2.0)),
Expr::new_pow(n.clone(), Expr::Constant(2.0)),
),
y.clone(),
);
Expr::Eq(
Arc::new(Expr::new_add(term1, Expr::new_add(term2, term3))),
Arc::new(Expr::Constant(0.0)),
)
}
#[must_use]
pub fn legendre_differential_equation(
y: &Expr,
x: &Expr,
n: &Expr,
) -> Expr {
let y_prime = differentiate(y, "x");
let y_double_prime = differentiate(&y_prime, "x");
let term1 = Expr::new_mul(
Expr::new_sub(
Expr::Constant(1.0),
Expr::new_pow(x.clone(), Expr::Constant(2.0)),
),
y_double_prime,
);
let term2 = Expr::new_mul(Expr::Constant(-2.0), Expr::new_mul(x.clone(), y_prime));
let term3 = Expr::new_mul(
Expr::new_mul(n.clone(), Expr::new_add(n.clone(), Expr::Constant(1.0))),
y.clone(),
);
Expr::Eq(
Arc::new(Expr::new_add(term1, Expr::new_add(term2, term3))),
Arc::new(Expr::Constant(0.0)),
)
}
#[must_use]
pub fn legendre_rodrigues_formula(
n: &Expr,
x: &Expr,
) -> Expr {
let n_f64 = if let Expr::Constant(val) = n {
*val
} else {
return Expr::new_legendre_p(n.clone(), x.clone());
};
let n_factorial = Expr::Constant(if n_f64 >= 0.0 {
factorial((n_f64 as i64).try_into().unwrap_or(0))
} else {
f64::NAN
});
Expr::Eq(
Arc::new(legendre_p(&n.clone(), x.clone())),
Arc::new(Expr::new_mul(
Expr::new_div(
Expr::Constant(1.0),
Expr::new_mul(Expr::new_pow(Expr::Constant(2.0), n.clone()), n_factorial),
),
Expr::DerivativeN(
Arc::new(Expr::new_pow(
Expr::new_sub(
Expr::new_pow(x.clone(), Expr::Constant(2.0)),
Expr::Constant(1.0),
),
n.clone(),
)),
"x".to_string(),
Arc::new(n.clone()),
),
)),
)
}
#[must_use]
pub fn laguerre_differential_equation(
y: &Expr,
x: &Expr,
n: &Expr,
) -> Expr {
let y_prime = differentiate(y, "x");
let y_double_prime = differentiate(&y_prime, "x");
let term1 = Expr::new_mul(x.clone(), y_double_prime);
let term2 = Expr::new_mul(Expr::new_sub(Expr::Constant(1.0), x.clone()), y_prime);
let term3 = Expr::new_mul(n.clone(), y.clone());
Expr::Eq(
Arc::new(Expr::new_add(term1, Expr::new_add(term2, term3))),
Arc::new(Expr::Constant(0.0)),
)
}
#[must_use]
pub fn hermite_differential_equation(
y: &Expr,
x: &Expr,
n: &Expr,
) -> Expr {
let y_prime = differentiate(y, "x");
let y_double_prime = differentiate(&y_prime, "x");
let term1 = y_double_prime;
let term2 = Expr::new_mul(Expr::Constant(-2.0), Expr::new_mul(x.clone(), y_prime));
let term3 = Expr::new_mul(Expr::new_mul(Expr::Constant(2.0), n.clone()), y.clone());
Expr::Eq(
Arc::new(Expr::new_add(term1, Expr::new_add(term2, term3))),
Arc::new(Expr::Constant(0.0)),
)
}
#[must_use]
pub fn hermite_rodrigues_formula(
n: &Expr,
x: &Expr,
) -> Expr {
Expr::Eq(
Arc::new(hermite_h(&n.clone(), x.clone())),
Arc::new(Expr::new_mul(
Expr::new_pow(Expr::Constant(-1.0), n.clone()),
Expr::new_mul(
Expr::new_exp(Expr::new_pow(x.clone(), Expr::Constant(2.0))),
Expr::DerivativeN(
Arc::new(Expr::new_exp(Expr::new_neg(Expr::new_pow(
x.clone(),
Expr::Constant(2.0),
)))),
"x".to_string(),
Arc::new(n.clone()),
),
),
)),
)
}
#[must_use]
pub fn chebyshev_differential_equation(
y: &Expr,
x: &Expr,
n: &Expr,
) -> Expr {
let y_prime = differentiate(y, "x");
let y_double_prime = differentiate(&y_prime, "x");
let term1 = Expr::new_mul(
Expr::new_sub(
Expr::Constant(1.0),
Expr::new_pow(x.clone(), Expr::Constant(2.0)),
),
y_double_prime,
);
let term2 = Expr::new_neg(Expr::new_mul(x.clone(), y_prime));
let term3 = Expr::new_mul(Expr::new_pow(n.clone(), Expr::Constant(2.0)), y.clone());
Expr::Eq(
Arc::new(Expr::new_add(term1, Expr::new_add(term2, term3))),
Arc::new(Expr::Constant(0.0)),
)
}