use nalgebra::Matrix3;
use nalgebra::Vector3;
use num_complex::Complex64;
pub trait Matrix3Ext {
fn cofactor_matrix(&self) -> Matrix3<f64>;
fn adjugate(&self) -> Matrix3<f64>;
}
impl Matrix3Ext for Matrix3<f64> {
fn cofactor_matrix(&self) -> Matrix3<f64> {
let m = self;
Matrix3::new(
m[(1, 1)] * m[(2, 2)] - m[(1, 2)] * m[(2, 1)], -(m[(1, 0)] * m[(2, 2)] - m[(1, 2)] * m[(2, 0)]), m[(1, 0)] * m[(2, 1)] - m[(1, 1)] * m[(2, 0)], -(m[(0, 1)] * m[(2, 2)] - m[(0, 2)] * m[(2, 1)]), m[(0, 0)] * m[(2, 2)] - m[(0, 2)] * m[(2, 0)], -(m[(0, 0)] * m[(2, 1)] - m[(0, 1)] * m[(2, 0)]), m[(0, 1)] * m[(1, 2)] - m[(0, 2)] * m[(1, 1)], -(m[(0, 0)] * m[(1, 2)] - m[(0, 2)] * m[(1, 0)]), m[(0, 0)] * m[(1, 1)] - m[(0, 1)] * m[(1, 0)], )
}
fn adjugate(&self) -> Matrix3<f64> {
self.cofactor_matrix().transpose()
}
}
pub trait Vector3Ext {
type Output;
fn skew_symmetric_matrix(&self) -> Self::Output;
}
impl Vector3Ext for Vector3<f64> {
type Output = Matrix3<f64>;
fn skew_symmetric_matrix(&self) -> Matrix3<f64> {
Matrix3::new(
0.0, -self[2], self[1], self[2], 0.0, -self[0], -self[1], self[0], 0.0,
)
}
}
impl Vector3Ext for Vector3<Complex64> {
type Output = Matrix3<Complex64>;
fn skew_symmetric_matrix(&self) -> Matrix3<Complex64> {
Matrix3::new(
Complex64::from(0.0),
-self[2],
self[1],
self[2],
Complex64::from(0.0),
-self[0],
-self[1],
self[0],
Complex64::from(0.0),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < 1e-10
}
#[test]
fn test_cofactor_identity() {
let identity: Matrix3<f64> = Matrix3::identity();
let cof = identity.cofactor_matrix();
for i in 0..3 {
for j in 0..3 {
if i == j {
assert!(approx_eq(cof[(i, j)], 1.0));
} else {
assert!(approx_eq(cof[(i, j)], 0.0));
}
}
}
}
#[test]
fn test_adjugate_is_cofactor_transpose() {
let m: Matrix3<f64> = Matrix3::new(1.0, 2.0, 3.0, 0.0, 1.0, 4.0, 5.0, 6.0, 0.0);
let adj = m.adjugate();
let cof_transpose = m.cofactor_matrix().transpose();
for i in 0..3 {
for j in 0..3 {
assert!(
approx_eq(adj[(i, j)], cof_transpose[(i, j)]),
"At ({}, {}): adj={}, cof^T={}",
i,
j,
adj[(i, j)],
cof_transpose[(i, j)]
);
}
}
}
#[test]
fn test_adjugate_identity() {
let identity: Matrix3<f64> = Matrix3::identity();
let adj = identity.adjugate();
for i in 0..3 {
for j in 0..3 {
if i == j {
assert!(approx_eq(adj[(i, j)], 1.0));
} else {
assert!(approx_eq(adj[(i, j)], 0.0));
}
}
}
}
#[test]
fn test_adjugate_property() {
let m: Matrix3<f64> = Matrix3::new(1.0, 2.0, 3.0, 0.0, 1.0, 4.0, 5.0, 6.0, 0.0);
let adj = m.adjugate();
let product = m * adj;
let det = m.determinant();
for i in 0..3 {
for j in 0..3 {
let expected = if i == j { det } else { 0.0 };
assert!(
approx_eq(product[(i, j)], expected),
"At ({}, {}): expected {}, got {}",
i,
j,
expected,
product[(i, j)]
);
}
}
}
#[test]
fn test_adjugate_inverse_relationship() {
let m: Matrix3<f64> = Matrix3::new(2.0, 1.0, 3.0, 1.0, 0.0, 1.0, 1.0, 2.0, 1.0);
let adj = m.adjugate();
let det = m.determinant();
if det.abs() > 1e-10 {
let inv = m.try_inverse().unwrap();
let adj_over_det = adj / det;
for i in 0..3 {
for j in 0..3 {
assert!(
approx_eq(adj_over_det[(i, j)], inv[(i, j)]),
"At ({}, {}): adjugate/det={}, inverse={}",
i,
j,
adj_over_det[(i, j)],
inv[(i, j)]
);
}
}
}
}
#[test]
fn test_adjugate_known() {
let m: Matrix3<f64> = Matrix3::new(-3.0, 2.0, -5.0, -1.0, 0.0, -2.0, 3.0, -4.0, 1.0);
let adj = m.adjugate();
let expected: Matrix3<f64> =
Matrix3::new(-8.0, 18.0, -4.0, -5.0, 12.0, -1.0, 4.0, -6.0, 2.0);
assert_eq!(adj, expected);
}
#[test]
fn test_skew_symmetric() {
use nalgebra::Vector3;
let v = Vector3::new(1.0, 2.0, 3.0);
let skew = v.skew_symmetric_matrix();
let expected = Matrix3::new(0.0, -3.0, 2.0, 3.0, 0.0, -1.0, -2.0, 1.0, 0.0);
assert_eq!(skew, expected);
assert_eq!(skew.transpose(), -skew);
let w = Vector3::new(4.0, 5.0, 6.0);
let cross_via_matrix = skew * w;
let cross_direct = v.cross(&w);
for i in 0..3 {
assert!(
approx_eq(cross_via_matrix[i], cross_direct[i]),
"Cross product mismatch at index {}: matrix={}, direct={}",
i,
cross_via_matrix[i],
cross_direct[i]
);
}
}
}