use super::{contains, ddx, div, exp_of, integrate_or_decline, is_zero, normalized, simp, sub};
use crate::kernel::{ExprId, ExprPool};
use crate::ode::dsolve::DsolveError;
use std::collections::HashMap;
fn det(m: &[Vec<ExprId>], pool: &ExprPool) -> ExprId {
let n = m.len();
match n {
0 => pool.integer(1_i32),
1 => m[0][0],
2 => sub(
simp(pool.mul(vec![m[0][0], m[1][1]]), pool),
simp(pool.mul(vec![m[0][1], m[1][0]]), pool),
pool,
),
_ => {
let mut terms = Vec::with_capacity(n);
for j in 0..n {
if is_zero(m[0][j], pool) {
continue;
}
let minor: Vec<Vec<ExprId>> = m[1..]
.iter()
.map(|row| {
row.iter()
.enumerate()
.filter(|(c, _)| *c != j)
.map(|(_, &v)| v)
.collect()
})
.collect();
let sign = if j % 2 == 0 { 1_i32 } else { -1_i32 };
terms.push(pool.mul(vec![pool.integer(sign), m[0][j], det(&minor, pool)]));
}
simp(pool.add(terms), pool)
}
}
}
pub(crate) fn variation_of_parameters(
basis: &[ExprId],
g: ExprId,
x: ExprId,
pool: &ExprPool,
) -> Result<Option<ExprId>, DsolveError> {
let n = basis.len();
if n == 0 {
return Ok(None);
}
let mut d: Vec<Vec<ExprId>> = Vec::with_capacity(n);
d.push(basis.to_vec());
for i in 1..n {
let prev = d[i - 1].clone();
let mut row = Vec::with_capacity(n);
for b in prev {
row.push(ddx(b, x, pool)?);
}
d.push(row);
}
let w = det(&d, pool);
if is_zero(w, pool) {
return Ok(None);
}
let mut terms = Vec::with_capacity(n);
for k in 0..n {
let mut mk = d.clone();
for (i, row) in mk.iter_mut().enumerate() {
row[k] = pool.integer(i32::from(i + 1 == n));
}
let wk = det(&mk, pool);
if is_zero(wk, pool) {
continue;
}
let num = simp(pool.mul(vec![wk, g]), pool);
let raw = div(num, w, pool);
let norm = div(num, normalized(w, pool), pool);
let anti = super::integrate_first_of(&[raw, norm], x, pool)?;
terms.push(pool.mul(vec![basis[k], anti]));
}
Ok(Some(simp(pool.add(terms), pool)))
}
pub(crate) fn reduction_of_order(
y1: ExprId,
p: ExprId,
x: ExprId,
pool: &ExprPool,
) -> Result<Option<ExprId>, DsolveError> {
if is_zero(y1, pool) {
return Ok(None);
}
let int_p = integrate_or_decline(p, x, pool)?;
let neg = simp(pool.mul(vec![pool.integer(-1_i32), int_p]), pool);
let mu = exp_of(neg, pool);
let y1sq = simp(pool.pow(y1, pool.integer(2_i32)), pool);
let integrand = div(mu, y1sq, pool);
let v = integrate_or_decline(integrand, x, pool)?;
if is_zero(v, pool) {
return Ok(None);
}
let y2 = simp(pool.mul(vec![y1, v]), pool);
let ratio = simp(div(y2, y1, pool), pool);
if !contains(ratio, x, pool) {
return Ok(None);
}
Ok(Some(y2))
}
fn y1_candidates(x: ExprId, pool: &ExprPool) -> Vec<ExprId> {
let mut out = vec![pool.integer(1_i32), x];
for m in [2_i32, 3, -1, -2] {
out.push(simp(pool.pow(x, pool.integer(m)), pool));
}
out.push(simp(pool.pow(x, pool.rational(1_i32, 2_i32)), pool));
for r in [1_i32, -1, 2, -2] {
let rx = simp(pool.mul(vec![pool.integer(r), x]), pool);
out.push(simp(pool.func("exp", vec![rx]), pool));
}
out.push(pool.func("sin", vec![x]));
out.push(pool.func("cos", vec![x]));
out.push(pool.func("log", vec![x]));
out
}
pub(crate) fn find_first_solution(
p: ExprId,
q: ExprId,
x: ExprId,
pool: &ExprPool,
) -> Result<Option<ExprId>, DsolveError> {
for cand in y1_candidates(x, pool) {
if is_zero(cand, pool) {
continue;
}
let d1 = ddx(cand, x, pool)?;
let d2 = ddx(d1, x, pool)?;
let residual = normalized(
simp(
pool.add(vec![
d2,
simp(pool.mul(vec![p, d1]), pool),
simp(pool.mul(vec![q, cand]), pool),
]),
pool,
),
pool,
);
if is_zero(residual, pool) || numerically_zero(residual, x, pool) {
return Ok(Some(cand));
}
}
Ok(None)
}
fn numerically_zero(expr: ExprId, x: ExprId, pool: &ExprPool) -> bool {
let mut checked = 0usize;
for xv in [0.37_f64, 0.81, 1.29, 1.73, 2.11] {
let mut env: HashMap<ExprId, f64> = HashMap::new();
env.insert(x, xv);
match super::verify::eval(expr, &env, pool) {
Some(v) if v.is_finite() => {
if v.abs() > 1e-9 {
return false;
}
checked += 1;
}
Some(_) => {}
None => return false,
}
}
checked >= 3
}