#[derive(Clone, Debug, PartialEq)]
pub(crate) struct Lin {
pub(crate) v: f64,
pub(crate) e: f64,
pub(crate) d: Vec<f64>,
pub(crate) de: Vec<f64>,
}
impl Lin {
pub(crate) fn constant(v: f64, n: usize) -> Self {
Self {
v,
e: 0.0,
d: vec![0.0; n],
de: vec![0.0; n],
}
}
pub(crate) fn div(&self, b: &Self) -> Self {
let v = self.v / b.v;
let e = (self.e + v.abs() * b.e) / b.v.abs();
let d: Vec<f64> = self
.d
.iter()
.zip(&b.d)
.map(|(da, db)| (da - v * db) / b.v)
.collect();
let de = (0..d.len())
.map(|k| {
(self.de[k] + v.abs() * b.de[k] + d[k].abs() * b.e + e * b.d[k].abs()) / b.v.abs()
})
.collect();
Self { v, e, d, de }
}
pub(crate) fn scale(&self, c: f64) -> Self {
Self {
v: c * self.v,
e: c.abs() * self.e,
d: self.d.iter().map(|x| c * x).collect(),
de: self.de.iter().map(|x| c.abs() * x).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_quotient_rule_matches_finite_differences() {
let a = Lin {
v: 3.0,
e: 1e-3,
d: vec![2.0],
de: vec![1e-4],
};
let b = Lin {
v: 5.0,
e: 2e-3,
d: vec![-1.0],
de: vec![0.0],
};
let q = a.div(&b);
let h = 1e-6;
let fd = ((3.0 + 2.0 * h) / (5.0 - h) - (3.0 - 2.0 * h) / (5.0 + h)) / (2.0 * h);
assert!((q.d[0] - fd).abs() < 1e-9);
assert!((q.v - 0.6).abs() < 1e-15);
let worst = ((3.0f64 + 1e-3) / (5.0 - 2e-3) - 0.6).abs();
assert!(worst <= q.e * (1.0 + 1e-3) && worst >= q.e * (1.0 - 1e-3));
assert_eq!(a.scale(2.0).d, vec![4.0]);
}
}