use super::alg_field::{RatFn, RationalFunctionField};
use super::number_field::CoeffField;
use super::rational_rde::solve_rational_rde_generalized;
use super::tower_field::{
solve_tower_rde, solve_tower_rde_generic, 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 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))
}
}
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(self, f, g)
}
}
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(self, f, g)
}
}
#[cfg(test)]
mod tests {
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());
}
}