use super::alg_field::{AlgElem, AlgExtension, RatFn, RationalFunctionField};
use super::alg_rde::solve_alg_rde_general;
use super::number_field::CoeffField;
use super::poly_rde::{degree, poly_deriv, trim};
use super::rational_rde::{
numerator_degree_bound, poly_div_exact, poly_gcd, solve_rational_rde_generalized,
};
use super::tower_field::{
solve_tower_coupled_radical_rde_bounded, solve_tower_rde_generic_bounded, tower_x_degree_bound,
ExpTowerField, LogTowerField, TExpr,
};
pub trait DifferentialField {
type Elem: Clone + std::fmt::Debug;
fn zero(&self) -> Self::Elem;
fn one(&self) -> Self::Elem;
fn add(&self, a: &Self::Elem, b: &Self::Elem) -> Self::Elem;
fn sub(&self, a: &Self::Elem, b: &Self::Elem) -> Self::Elem;
fn mul(&self, a: &Self::Elem, b: &Self::Elem) -> Self::Elem;
fn neg(&self, a: &Self::Elem) -> Self::Elem;
fn inv(&self, a: &Self::Elem) -> Option<Self::Elem>;
fn is_zero(&self, a: &Self::Elem) -> bool;
fn eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool;
fn derivation(&self, a: &Self::Elem) -> Self::Elem;
fn is_constant(&self, a: &Self::Elem) -> bool {
self.is_zero(&self.derivation(a))
}
fn rational_rde(&self, f: &Self::Elem, g: &Self::Elem) -> Option<Self::Elem>;
fn rde_degree_bound(&self, f: &Self::Elem, g: &Self::Elem) -> Option<usize> {
let _ = (f, g);
None
}
fn coupled_radical_rde(
&self,
n: usize,
a: &Self::Elem,
f: &[Self::Elem],
g: &[Self::Elem],
) -> Option<Vec<Self::Elem>> {
let _ = (n, a, f, g);
None
}
fn limited_integrate(
&self,
g: &Self::Elem,
ws: &[Self::Elem],
) -> Option<(Self::Elem, Vec<Self::Elem>)> {
let _ = (g, ws);
None
}
fn param_log_deriv(&self, f: &Self::Elem, w: &Self::Elem) -> Option<(i64, i64, Self::Elem)> {
let _ = (f, w);
None
}
}
#[derive(Clone, Debug, Default)]
pub struct RationalDiffField {
inner: RationalFunctionField,
}
impl RationalDiffField {
pub fn new() -> Self {
Self::default()
}
}
impl DifferentialField for RationalDiffField {
type Elem = RatFn;
fn zero(&self) -> RatFn {
self.inner.zero()
}
fn one(&self) -> RatFn {
self.inner.one()
}
fn add(&self, a: &RatFn, b: &RatFn) -> RatFn {
self.inner.add(a, b)
}
fn sub(&self, a: &RatFn, b: &RatFn) -> RatFn {
self.inner.sub(a, b)
}
fn mul(&self, a: &RatFn, b: &RatFn) -> RatFn {
self.inner.mul(a, b)
}
fn neg(&self, a: &RatFn) -> RatFn {
self.inner.neg(a)
}
fn inv(&self, a: &RatFn) -> Option<RatFn> {
self.inner.inv(a)
}
fn is_zero(&self, a: &RatFn) -> bool {
self.inner.is_zero(a)
}
fn eq(&self, a: &RatFn, b: &RatFn) -> bool {
self.inner.eq(a, b)
}
fn derivation(&self, a: &RatFn) -> RatFn {
self.inner.derivation(a)
}
fn rational_rde(&self, f: &RatFn, g: &RatFn) -> Option<RatFn> {
let (num, den) =
solve_rational_rde_generalized(f.numer(), f.denom(), g.numer(), g.denom())?;
Some(RatFn::new(num, den))
}
fn rde_degree_bound(&self, f: &RatFn, g: &RatFn) -> Option<usize> {
let c_num = trim(g.numer().clone());
let c_den = trim(g.denom().clone());
if c_num.is_empty() {
return Some(0);
}
if c_den.is_empty() {
return None;
}
let gc = poly_gcd(&c_num, &c_den);
let big_c = poly_div_exact(&c_num, &gc);
let big_b = poly_div_exact(&c_den, &gc);
let bprime = poly_deriv(&big_b);
let e_poly = poly_gcd(&big_b, &bprime);
let deg_b = degree(&big_b);
let deg_c = degree(&big_c);
let deg_e = degree(&e_poly);
let f_den = trim(f.denom().clone());
let deg_f = degree(f.numer());
let deg_bf = if degree(&f_den) > 0 {
degree(&f_den).max(0)
} else {
0
};
Some(numerator_degree_bound(deg_b, deg_c, deg_e, deg_f) + deg_bf as usize)
}
fn coupled_radical_rde(
&self,
n: usize,
a: &RatFn,
f: &[RatFn],
g: &[RatFn],
) -> Option<Vec<RatFn>> {
if n < 2 {
return None;
}
if degree(a.denom()) != 0 {
return None;
}
let ext = AlgExtension::radical(n, a.numer());
let to_alg = |v: &[RatFn]| -> AlgElem {
let mut comps = vec![RatFn::int(0); n];
for (i, c) in v.iter().take(n).enumerate() {
comps[i] = c.clone();
}
ext.reduce(&comps)
};
let f_alg = to_alg(f);
let g_alg = to_alg(g);
let u_alg = solve_alg_rde_general(&ext, &f_alg, &g_alg)?;
let mut u = vec![RatFn::int(0); n];
for (i, c) in u_alg.into_iter().take(n).enumerate() {
u[i] = c;
}
Some(u)
}
}
impl DifferentialField for ExpTowerField {
type Elem = TExpr;
fn zero(&self) -> TExpr {
<Self as CoeffField>::zero(self)
}
fn one(&self) -> TExpr {
<Self as CoeffField>::one(self)
}
fn add(&self, a: &TExpr, b: &TExpr) -> TExpr {
<Self as CoeffField>::add(self, a, b)
}
fn sub(&self, a: &TExpr, b: &TExpr) -> TExpr {
<Self as CoeffField>::sub(self, a, b)
}
fn mul(&self, a: &TExpr, b: &TExpr) -> TExpr {
<Self as CoeffField>::mul(self, a, b)
}
fn neg(&self, a: &TExpr) -> TExpr {
<Self as CoeffField>::neg(self, a)
}
fn inv(&self, a: &TExpr) -> Option<TExpr> {
<Self as CoeffField>::inv(self, a)
}
fn is_zero(&self, a: &TExpr) -> bool {
<Self as CoeffField>::is_zero(self, a)
}
fn eq(&self, a: &TExpr, b: &TExpr) -> bool {
<Self as CoeffField>::eq(self, a, b)
}
fn derivation(&self, a: &TExpr) -> TExpr {
<Self as CoeffField>::derivation(self, a)
}
fn rational_rde(&self, f: &TExpr, g: &TExpr) -> Option<TExpr> {
solve_tower_rde_generic_bounded(self, f, g, self.rde_degree_bound(f, g))
}
fn rde_degree_bound(&self, f: &TExpr, g: &TExpr) -> Option<usize> {
let drift = degree(self.deta.numer()).max(degree(self.deta.denom()));
Some(tower_x_degree_bound(f, g, drift))
}
fn coupled_radical_rde(
&self,
n: usize,
a: &TExpr,
f: &[TExpr],
g: &[TExpr],
) -> Option<Vec<TExpr>> {
let drift = degree(self.deta.numer()).max(degree(self.deta.denom()));
let x_bound = tower_coupled_x_bound(a, f, g, drift);
solve_tower_coupled_radical_rde_bounded(self, n, a, f, g, Some(x_bound))
}
}
impl DifferentialField for LogTowerField {
type Elem = TExpr;
fn zero(&self) -> TExpr {
<Self as CoeffField>::zero(self)
}
fn one(&self) -> TExpr {
<Self as CoeffField>::one(self)
}
fn add(&self, a: &TExpr, b: &TExpr) -> TExpr {
<Self as CoeffField>::add(self, a, b)
}
fn sub(&self, a: &TExpr, b: &TExpr) -> TExpr {
<Self as CoeffField>::sub(self, a, b)
}
fn mul(&self, a: &TExpr, b: &TExpr) -> TExpr {
<Self as CoeffField>::mul(self, a, b)
}
fn neg(&self, a: &TExpr) -> TExpr {
<Self as CoeffField>::neg(self, a)
}
fn inv(&self, a: &TExpr) -> Option<TExpr> {
<Self as CoeffField>::inv(self, a)
}
fn is_zero(&self, a: &TExpr) -> bool {
<Self as CoeffField>::is_zero(self, a)
}
fn eq(&self, a: &TExpr, b: &TExpr) -> bool {
<Self as CoeffField>::eq(self, a, b)
}
fn derivation(&self, a: &TExpr) -> TExpr {
<Self as CoeffField>::derivation(self, a)
}
fn rational_rde(&self, f: &TExpr, g: &TExpr) -> Option<TExpr> {
solve_tower_rde_generic_bounded(self, f, g, self.rde_degree_bound(f, g))
}
fn rde_degree_bound(&self, f: &TExpr, g: &TExpr) -> Option<usize> {
let drift = degree(self.dh_over_h.numer()).max(degree(self.dh_over_h.denom()));
Some(tower_x_degree_bound(f, g, drift))
}
fn coupled_radical_rde(
&self,
n: usize,
a: &TExpr,
f: &[TExpr],
g: &[TExpr],
) -> Option<Vec<TExpr>> {
let drift = degree(self.dh_over_h.numer()).max(degree(self.dh_over_h.denom()));
let x_bound = tower_coupled_x_bound(a, f, g, drift);
solve_tower_coupled_radical_rde_bounded(self, n, a, f, g, Some(x_bound))
}
}
fn tower_coupled_x_bound(a: &TExpr, f: &[TExpr], g: &[TExpr], drift: i64) -> usize {
let mut bound = tower_x_degree_bound(a, a, drift);
for c in f.iter().chain(g.iter()) {
bound = bound.max(tower_x_degree_bound(c, c, drift));
}
bound
}
#[cfg(test)]
mod tests {
use super::super::tower_field::{solve_tower_rde, solve_tower_rde_generic};
use super::*;
use crate::integrate::risch::poly_rde::QPoly;
use rug::Rational;
fn rat(n: i64) -> Rational {
Rational::from(n)
}
fn rf_poly(c: &[i64]) -> RatFn {
let p: QPoly = c.iter().map(|&n| rat(n)).collect();
RatFn::from_poly(&p)
}
fn check_qx(f: &RatFn, g: &RatFn) {
let field = RationalDiffField::new();
let trait_res = field.rational_rde(f, g);
let direct = solve_rational_rde_generalized(f.numer(), f.denom(), g.numer(), g.denom())
.map(|(n, d)| RatFn::new(n, d));
assert_eq!(
trait_res, direct,
"trait rational_rde must match the wrapped solver exactly"
);
if let Some(y) = trait_res {
let lhs = field.add(&field.derivation(&y), &field.mul(f, &y));
assert!(
field.eq(&lhs, g),
"ℚ(x) RDE solution must satisfy D(y)+f·y=g; got y={y:?}"
);
}
}
#[test]
fn qx_f0_g_2x_gives_x_squared() {
let field = RationalDiffField::new();
let f = field.zero();
let g = rf_poly(&[0, 2]); check_qx(&f, &g);
let y = field.rational_rde(&f, &g).expect("solvable");
assert!(
field.eq(&y, &rf_poly(&[0, 0, 1])),
"y should be x²; got {y:?}"
);
}
#[test]
fn qx_f1_known_solution() {
let field = RationalDiffField::new();
let f = field.one();
let g = rf_poly(&[1, 1]); check_qx(&f, &g);
let y = field.rational_rde(&f, &g).expect("solvable");
assert!(field.eq(&y, &rf_poly(&[0, 1])), "y should be x; got {y:?}");
}
#[test]
fn qx_f_const_g_const() {
let field = RationalDiffField::new();
let f = rf_poly(&[2]);
let g = rf_poly(&[4]);
check_qx(&f, &g);
let y = field.rational_rde(&f, &g).expect("solvable");
assert!(field.eq(&y, &rf_poly(&[2])), "y should be 2; got {y:?}");
}
#[test]
fn qx_rational_f_solvable() {
let field = RationalDiffField::new();
let f = RatFn::new(vec![rat(1)], vec![rat(0), rat(1)]); let y_expected = rf_poly(&[0, 1]); let g = field.add(&field.derivation(&y_expected), &field.mul(&f, &y_expected));
check_qx(&f, &g);
let y = field.rational_rde(&f, &g).expect("solvable");
assert!(field.eq(&y, &y_expected), "y should be x; got {y:?}");
}
#[test]
fn qx_ei_type_is_none() {
let field = RationalDiffField::new();
let f = field.zero();
let g = RatFn::new(vec![rat(1)], vec![rat(0), rat(1)]); check_qx(&f, &g); assert!(field.rational_rde(&f, &g).is_none(), "1/x ⇒ None (Ei/Li)");
}
#[test]
fn qx_derivation_and_is_constant() {
let field = RationalDiffField::new();
let x = rf_poly(&[0, 1]);
assert!(field.eq(&field.derivation(&x), &field.one()));
assert!(!field.is_constant(&x));
let c = rf_poly(&[7]);
assert!(field.is_zero(&field.derivation(&c)));
assert!(field.is_constant(&c));
let x2 = rf_poly(&[0, 0, 1]);
assert!(field.eq(&field.derivation(&x2), &rf_poly(&[0, 2])));
}
fn x_elem() -> TExpr {
TExpr::from_ratfn(rf_poly(&[0, 1]))
}
#[test]
fn exp_tower_derivation_basics() {
let field = ExpTowerField::new(RatFn::int(1));
let t = TExpr::t();
assert!(<ExpTowerField as DifferentialField>::eq(
&field,
&DifferentialField::derivation(&field, &t),
&t
));
assert!(!field.is_constant(&t));
let x = x_elem();
assert!(<ExpTowerField as DifferentialField>::eq(
&field,
&DifferentialField::derivation(&field, &x),
&DifferentialField::one(&field)
));
let c = TExpr::int(5);
assert!(field.is_constant(&c));
}
#[test]
fn exp_tower_v_equals_t() {
let field = ExpTowerField::new(RatFn::int(1));
let f = DifferentialField::zero(&field);
let g = TExpr::t();
let trait_res = DifferentialField::rational_rde(&field, &f, &g);
let direct = solve_tower_rde(&field, &f, &g);
assert_eq!(trait_res, direct, "trait must match solve_tower_rde");
let v = trait_res.expect("v = t");
assert_eq!(v, TExpr::t());
}
#[test]
fn exp_tower_example15_component() {
let field = ExpTowerField::new(RatFn::int(1));
let a = DifferentialField::add(&field, &x_elem(), &TExpr::t()); let a_prime = DifferentialField::derivation(&field, &a); let two_thirds = TExpr::from_ratfn(RatFn::new(vec![rat(2)], vec![rat(3)]));
let inv_a = DifferentialField::inv(&field, &a).unwrap();
let omega2 = DifferentialField::mul(
&field,
&two_thirds,
&DifferentialField::mul(&field, &a_prime, &inv_a),
);
let num = vec![
RatFn::from_poly(&vec![rat(0), rat(5)]), RatFn::from_poly(&vec![rat(3), rat(2)]), ];
let den = vec![
RatFn::from_poly(&vec![rat(0), rat(1)]), RatFn::int(1), ];
let c2 = TExpr::new(num, den);
let trait_res = DifferentialField::rational_rde(&field, &omega2, &c2);
let direct = solve_tower_rde(&field, &omega2, &c2);
assert_eq!(trait_res, direct, "trait must match solve_tower_rde");
let v = trait_res.expect("Example-15 component is solvable");
let expected = TExpr::from_ratfn(RatFn::from_poly(&vec![rat(0), rat(3)])); assert!(
<ExpTowerField as DifferentialField>::eq(&field, &v, &expected),
"v₂ should be 3x; got {v:?}"
);
let lhs = DifferentialField::add(
&field,
&DifferentialField::derivation(&field, &v),
&DifferentialField::mul(&field, &omega2, &v),
);
assert!(
<ExpTowerField as DifferentialField>::eq(&field, &lhs, &c2),
"D(v)+ω·v=c must hold in-field"
);
}
#[test]
fn exp_tower_nonelementary_is_none() {
let field = ExpTowerField::new(RatFn::int(1));
let f = DifferentialField::zero(&field);
let inv_x = DifferentialField::inv(&field, &x_elem()).unwrap();
let g = DifferentialField::mul(&field, &TExpr::t(), &inv_x); let trait_res = DifferentialField::rational_rde(&field, &f, &g);
let direct = solve_tower_rde(&field, &f, &g);
assert_eq!(trait_res, direct);
assert!(trait_res.is_none(), "Ei-type ⇒ None");
}
#[test]
fn log_tower_derivation_basics() {
let dh_over_h = RatFn::new(vec![rat(1)], vec![rat(0), rat(1)]); let field = LogTowerField::new(dh_over_h.clone());
let t = TExpr::t();
let dt = DifferentialField::derivation(&field, &t);
let expected = TExpr::from_ratfn(dh_over_h);
assert!(
<LogTowerField as DifferentialField>::eq(&field, &dt, &expected),
"D(log x) = 1/x; got {dt:?}"
);
assert!(!field.is_constant(&t));
let x = x_elem();
assert!(<LogTowerField as DifferentialField>::eq(
&field,
&DifferentialField::derivation(&field, &x),
&DifferentialField::one(&field)
));
}
#[test]
fn log_tower_v_equals_t() {
let dh_over_h = RatFn::new(vec![rat(1)], vec![rat(0), rat(1)]); let field = LogTowerField::new(dh_over_h.clone());
let f = DifferentialField::zero(&field);
let g = TExpr::from_ratfn(dh_over_h); let trait_res = DifferentialField::rational_rde(&field, &f, &g);
let direct = solve_tower_rde_generic(&field, &f, &g);
assert_eq!(
trait_res, direct,
"trait must match solve_tower_rde_generic"
);
let v = trait_res.expect("v = log x");
assert_eq!(v, TExpr::t());
let lhs = DifferentialField::derivation(&field, &v);
assert!(<LogTowerField as DifferentialField>::eq(&field, &lhs, &g));
}
#[test]
fn log_tower_polynomial_in_t() {
let dh_over_h = RatFn::new(vec![rat(1)], vec![rat(0), rat(1)]); let field = LogTowerField::new(dh_over_h);
let t = TExpr::t();
let t2 = DifferentialField::mul(&field, &t, &t);
let g = DifferentialField::derivation(&field, &t2); let f = DifferentialField::zero(&field);
let trait_res = DifferentialField::rational_rde(&field, &f, &g);
let direct = solve_tower_rde_generic(&field, &f, &g);
assert_eq!(trait_res, direct);
let v = trait_res.expect("v = (log x)²");
assert!(
<LogTowerField as DifferentialField>::eq(&field, &v, &t2),
"v should be t²; got {v:?}"
);
}
#[test]
fn stubs_decline() {
let qx = RationalDiffField::new();
assert!(qx.limited_integrate(&qx.one(), &[qx.one()]).is_none());
assert!(qx.param_log_deriv(&qx.one(), &qx.one()).is_none());
let exp = ExpTowerField::new(RatFn::int(1));
let one_e = DifferentialField::one(&exp);
assert!(exp
.limited_integrate(&one_e, std::slice::from_ref(&one_e))
.is_none());
assert!(exp.param_log_deriv(&one_e, &one_e).is_none());
}
}