use crate::math;
use super::node::DeviationNode;
use super::ring::NodeRing;
use super::smith::{self, SmithCoefficients};
use super::table::DeviationTable;
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeviationAnalysis {
pub coefficients: SmithCoefficients,
pub rms_residual: f64,
pub max_residual: f64,
pub max_abs_deviation: f64,
pub max_gap: f64,
pub max_slope: f64,
pub nodes: usize,
}
pub fn analyze(table: &DeviationTable) -> Result<DeviationAnalysis> {
let nodes = table.nodes();
let coefficients = smith::smith_coefficients(table)?;
let residuals = smith::residuals(nodes, &coefficients);
Ok(DeviationAnalysis {
coefficients,
rms_residual: residuals.rms,
max_residual: residuals.largest,
max_abs_deviation: max_abs_deviation(nodes),
max_gap: max_gap(nodes),
max_slope: max_slope(nodes),
nodes: nodes.len(),
})
}
#[must_use]
pub(crate) fn max_gap(nodes: &[DeviationNode]) -> f64 {
let ring = NodeRing::new(nodes);
(0..nodes.len()).fold(0.0_f64, |widest, index| widest.max(ring.span(index)))
}
#[must_use]
pub(crate) fn max_slope(nodes: &[DeviationNode]) -> f64 {
let ring = NodeRing::new(nodes);
(0..nodes.len()).fold(0.0_f64, |steepest, index| {
let span = ring.span(index);
if span > 0.0 {
let rise = ring.value(ring.after(index)) - ring.value(index);
steepest.max(math::abs(rise) / span)
} else {
steepest
}
})
}
#[must_use]
pub(crate) fn max_abs_deviation(nodes: &[DeviationNode]) -> f64 {
nodes.iter().fold(0.0_f64, |largest, node| {
largest.max(math::abs(node.deviation_degrees()))
})
}
#[must_use]
pub(crate) fn is_invertible(nodes: &[DeviationNode]) -> bool {
max_slope(nodes) < 1.0
}