use nalgebra::{DMatrix, Matrix3, SMatrix, Vector3};
pub fn sinc(x: f64) -> f64 {
let x2 = x * x;
if x2 < 1e-10 {
1.0 - x2 / 6.0 + x2 * x2 / 120.0
} else {
x.sin() / x
}
}
pub fn skew(v: &Vector3<f64>) -> Matrix3<f64> {
Matrix3::new(
0.0, -v.z, v.y, v.z, 0.0, -v.x, -v.y, v.x, 0.0,
)
}
pub fn symm_sqrt_inverse<const D: usize>(m: &SMatrix<f64, D, D>) -> SMatrix<f64, D, D> {
let eigen = nalgebra::SymmetricEigen::new(DMatrix::from_iterator(D, D, m.iter().copied()));
let epsilon = 1e-12;
let mut s_inv_sqrt = SMatrix::<f64, D, D>::zeros();
for i in 0..D {
let ev = eigen.eigenvalues[i];
if ev > epsilon {
s_inv_sqrt[(i, i)] = 1.0 / ev.sqrt();
}
}
let eigenvectors = SMatrix::<f64, D, D>::from_iterator(eigen.eigenvectors.iter().copied());
s_inv_sqrt * eigenvectors.transpose()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sinc_at_zero() {
assert!((sinc(0.0) - 1.0).abs() < 1e-15);
}
#[test]
fn sinc_small_angle_matches_exact() {
let x = 1e-8_f64;
assert!((sinc(x) - x.sin() / x).abs() < 1e-14);
}
#[test]
fn sinc_large_angle() {
let x = 1.0_f64;
assert!((sinc(x) - x.sin() / x).abs() < 1e-15);
}
#[test]
fn skew_matches_cross_product() {
let v = Vector3::new(1.0, 2.0, 3.0);
let w = Vector3::new(4.0, 5.0, 6.0);
assert!((skew(&v) * w - v.cross(&w)).norm() < 1e-14);
}
#[test]
fn symm_sqrt_inverse_of_identity() {
let id = SMatrix::<f64, 15, 15>::identity();
assert!((symm_sqrt_inverse(&id) - id).norm() < 1e-10);
}
#[test]
fn symm_sqrt_inverse_roundtrip() {
let mut m = SMatrix::<f64, 15, 15>::identity() * 2.0;
m[(0, 1)] = 0.5;
m[(1, 0)] = 0.5;
let u = symm_sqrt_inverse(&m);
let product = u.transpose() * u * m;
let id = SMatrix::<f64, 15, 15>::identity();
assert!((product - id).norm() < 1e-8);
}
#[test]
fn symm_sqrt_inverse_generic_over_dimension() {
let mut m = SMatrix::<f64, 9, 9>::identity() * 4.0;
m[(2, 5)] = 0.25;
m[(5, 2)] = 0.25;
let u = symm_sqrt_inverse(&m);
let product = u.transpose() * u * m;
assert!((product - SMatrix::<f64, 9, 9>::identity()).norm() < 1e-8);
}
#[test]
fn symm_sqrt_inverse_clamps_rank_deficient_directions() {
let mut m = SMatrix::<f64, 3, 3>::identity();
m[(2, 2)] = 0.0;
let u = symm_sqrt_inverse(&m);
assert!(u.iter().all(|v| v.is_finite()));
}
}