use crate::expr::{div, mul, pow, Expr, ExprKind};
use crate::rational::Rational;
use crate::symbol::Symbol;
use super::util::{as_const, chain_rule_scale, linear_factor};
pub fn try_u_substitution(expr: &Expr, var: Symbol) -> Option<Expr> {
match expr.kind() {
ExprKind::Mul(f, g) => try_substitution_product(f, g, var)
.or_else(|| try_substitution_product(g, f, var)),
_ => None,
}
}
fn try_substitution_product(f: &Expr, gp: &Expr, var: Symbol) -> Option<Expr> {
let gp = gp.clone().simplify();
if let ExprKind::Pow(base, exp) = f.kind() {
if let Some(n) = as_const(exp).and_then(|r| r.as_integer()) {
if n != -1 {
let u = base.clone();
let du = u.clone().diff(var).simplify();
if let Some(scale) = linear_factor(&gp, &du) {
let new_n = n + 1;
return Some(div(
pow(u, crate::expr::const_(Rational::from(new_n))),
crate::expr::const_(Rational::from(new_n) * scale),
));
}
}
}
if let Some(n) = as_const(exp).and_then(|r| r.as_integer()) {
if n == -2 {
if let ExprKind::Cos(inner) = base.kind() {
if let Some(scale) = chain_rule_scale(&gp, inner, var) {
return Some(mul(
crate::expr::const_(scale),
crate::expr::tan(inner.clone()),
));
}
}
}
}
}
match f.kind() {
ExprKind::Sin(inner) => chain_elementary(
inner,
&gp,
var,
|u| -crate::expr::cos(u.clone()),
),
ExprKind::Cos(inner) => chain_elementary(inner, &gp, var, |u| crate::expr::sin(u.clone())),
ExprKind::Tan(inner) => chain_elementary(
inner,
&gp,
var,
|u| -crate::expr::ln(crate::expr::cos(u.clone())),
),
ExprKind::Exp(inner) => chain_elementary(inner, &gp, var, |u| crate::expr::exp(u.clone())),
ExprKind::Ln(inner) => chain_elementary(
inner,
&gp,
var,
|u| mul(u.clone(), crate::expr::ln(u.clone())) - u.clone(),
),
ExprKind::Atan(inner) => chain_elementary(
inner,
&gp,
var,
|u| {
mul(u.clone(), crate::expr::atan(u.clone()))
- div(
crate::expr::ln(
crate::expr::const_(Rational::one())
+ pow(u.clone(), crate::expr::const_(Rational::from(2))),
),
crate::expr::const_(Rational::from(2)),
)
},
),
ExprKind::Cot(inner) => {
chain_elementary(inner, &gp, var, |u| crate::expr::ln(crate::expr::sin(u.clone())))
}
ExprKind::Sec(inner) => chain_elementary(inner, &gp, var, |u| {
crate::expr::ln(crate::expr::add(
crate::expr::sec(u.clone()),
crate::expr::tan(u.clone()),
))
}),
ExprKind::Csc(inner) => chain_elementary(inner, &gp, var, |u| {
-crate::expr::ln(crate::expr::add(
crate::expr::csc(u.clone()),
crate::expr::cot(u.clone()),
))
}),
ExprKind::Sinh(inner) => {
chain_elementary(inner, &gp, var, |u| crate::expr::cosh(u.clone()))
}
ExprKind::Cosh(inner) => {
chain_elementary(inner, &gp, var, |u| crate::expr::sinh(u.clone()))
}
ExprKind::Tanh(inner) => chain_elementary(inner, &gp, var, |u| {
crate::expr::ln(crate::expr::cosh(u.clone()))
}),
ExprKind::Coth(inner) => {
chain_elementary(inner, &gp, var, |u| crate::expr::ln(crate::expr::sinh(u.clone())))
}
ExprKind::Sech(inner) => chain_elementary(inner, &gp, var, |u| {
crate::expr::atan(crate::expr::sinh(u.clone()))
}),
_ => None,
}
}
fn chain_elementary(
inner: &Expr,
gp: &Expr,
var: Symbol,
antideriv_in_u: fn(&Expr) -> Expr,
) -> Option<Expr> {
let scale = chain_rule_scale(gp, inner, var)?;
Some(mul(crate::expr::const_(scale), antideriv_in_u(inner)))
}