#![forbid(unsafe_code)]
use crate::geometry::matrix::{Matrix, matrix_set};
use crate::geometry::point::Point;
use crate::geometry::traits::coordinate::CoordinateConversionError;
pub fn sos_orientation_sign<const D: usize>(
points: &[Point<f64, D>],
) -> Result<i32, CoordinateConversionError> {
if points.len() != D + 1 {
return Err(CoordinateConversionError::ConversionFailed {
coordinate_index: 0,
coordinate_value: format!(
"SoS orientation requires exactly D+1 = {} points, got {}",
D + 1,
points.len()
),
from_type: "point count",
to_type: "valid simplex",
});
}
for (point_idx, point) in points.iter().enumerate() {
for (coord_idx, &val) in point.coords().iter().enumerate() {
if !val.is_finite() {
return Err(CoordinateConversionError::NonFiniteValue {
coordinate_index: point_idx * D + coord_idx,
coordinate_value: val.to_string(),
});
}
}
}
let n = D + 1;
let coords: Vec<[f64; D]> = points.iter().map(|p| *p.coords()).collect();
for remove_row in (0..n).rev() {
for remove_col in (0..D).rev() {
let minor_sign = orientation_cofactor_det::<D>(&coords, remove_row, remove_col);
if minor_sign != 0 {
let cofactor_sign = if (remove_row + remove_col).is_multiple_of(2) {
1
} else {
-1
};
return Ok(cofactor_sign * minor_sign);
}
}
}
Err(CoordinateConversionError::ConversionFailed {
coordinate_index: 0,
coordinate_value: "all SoS orientation cofactors vanished (points may be identical)"
.to_string(),
from_type: "SoS orientation",
to_type: "non-zero sign",
})
}
pub fn sos_insphere_sign<const D: usize>(
simplex: &[Point<f64, D>],
test: &Point<f64, D>,
) -> Result<i32, CoordinateConversionError> {
if simplex.len() != D + 1 {
return Err(CoordinateConversionError::ConversionFailed {
coordinate_index: 0,
coordinate_value: format!(
"SoS insphere requires exactly D+1 = {} simplex points, got {}",
D + 1,
simplex.len()
),
from_type: "point count",
to_type: "valid simplex",
});
}
for (point_idx, point) in simplex.iter().enumerate() {
for (coord_idx, &val) in point.coords().iter().enumerate() {
if !val.is_finite() {
return Err(CoordinateConversionError::NonFiniteValue {
coordinate_index: point_idx * D + coord_idx,
coordinate_value: val.to_string(),
});
}
}
}
for (coord_idx, &val) in test.coords().iter().enumerate() {
if !val.is_finite() {
return Err(CoordinateConversionError::NonFiniteValue {
coordinate_index: (D + 1) * D + coord_idx,
coordinate_value: val.to_string(),
});
}
}
let n = D + 1;
let base_coords = simplex[0].coords();
let mut rel_coords: Vec<[f64; D]> = Vec::with_capacity(n);
let mut lifted_col: Vec<f64> = Vec::with_capacity(n);
for point in simplex.iter().skip(1) {
let mut rel = [0.0f64; D];
for j in 0..D {
rel[j] = point.coords()[j] - base_coords[j];
}
let sq_norm: f64 = rel.iter().fold(0.0f64, |acc, &x| x.mul_add(x, acc));
rel_coords.push(rel);
lifted_col.push(sq_norm);
}
let mut test_rel = [0.0f64; D];
for j in 0..D {
test_rel[j] = test.coords()[j] - base_coords[j];
}
let test_sq_norm: f64 = test_rel.iter().fold(0.0f64, |acc, &x| x.mul_add(x, acc));
rel_coords.push(test_rel);
lifted_col.push(test_sq_norm);
for remove_row in (0..n).rev() {
for remove_col in (0..n).rev() {
let minor_sign =
insphere_cofactor_det::<D>(&rel_coords, &lifted_col, remove_row, remove_col);
if minor_sign != 0 {
let cofactor_sign = if (remove_row + remove_col).is_multiple_of(2) {
1
} else {
-1
};
return Ok(cofactor_sign * minor_sign);
}
}
}
Err(CoordinateConversionError::ConversionFailed {
coordinate_index: 0,
coordinate_value: "all SoS insphere cofactors vanished (points may be identical)"
.to_string(),
from_type: "SoS insphere",
to_type: "non-zero sign",
})
}
fn orientation_cofactor_det<const D: usize>(
coords: &[[f64; D]],
remove_row: usize,
remove_col: usize,
) -> i32 {
if D == 0 {
return 1; }
let mut matrix = Matrix::<D>::zero();
let mut r = 0;
for (i, coord_row) in coords.iter().enumerate() {
if i == remove_row {
continue;
}
let mut c = 0;
for (j, &val) in coord_row.iter().enumerate() {
if j == remove_col {
continue;
}
matrix_set(&mut matrix, r, c, val);
c += 1;
}
matrix_set(&mut matrix, r, c, 1.0);
r += 1;
}
exact_det_sign(&matrix)
}
fn insphere_cofactor_det<const D: usize>(
rel_coords: &[[f64; D]],
lifted_col: &[f64],
remove_row: usize,
remove_col: usize,
) -> i32 {
if D == 0 {
return 1;
}
let num_rows = D + 1;
let num_cols = D + 1;
let mut matrix = Matrix::<D>::zero();
let mut r = 0;
for i in 0..num_rows {
if i == remove_row {
continue;
}
let mut c = 0;
#[expect(clippy::needless_range_loop, reason = "mixed data sources")]
for j in 0..num_cols {
if j == remove_col {
continue;
}
let val = if j < D {
rel_coords[i][j]
} else {
lifted_col[i]
};
matrix_set(&mut matrix, r, c, val);
c += 1;
}
r += 1;
}
exact_det_sign(&matrix)
}
pub(crate) fn exact_det_sign<const N: usize>(matrix: &Matrix<N>) -> i32 {
if let (Some(det), Some(bound)) = (matrix.det_direct(), matrix.det_errbound())
&& det.is_finite()
&& bound.is_finite()
{
if det > bound {
return 1;
}
if det < -bound {
return -1;
}
}
matrix.det_sign_exact().map_or(0, i32::from)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::point::Point;
use crate::geometry::traits::coordinate::Coordinate;
fn degenerate_orient_points<const D: usize>() -> Vec<Point<f64, D>> {
let mut points = Vec::with_capacity(D + 1);
points.push(Point::new([0.0; D]));
for i in 0..D.saturating_sub(1) {
let mut coords = [0.0; D];
coords[i] = 1.0;
points.push(Point::new(coords));
}
let mut bary = [0.0; D];
for c in bary.iter_mut().take(D.saturating_sub(1)) {
*c = 0.5;
}
points.push(Point::new(bary));
points
}
fn cospherical_points<const D: usize>() -> (Vec<Point<f64, D>>, Point<f64, D>) {
let mut simplex = Vec::with_capacity(D + 1);
simplex.push(Point::new([0.0; D]));
for i in 0..D {
let mut coords = [0.0; D];
coords[i] = 1.0;
simplex.push(Point::new(coords));
}
(simplex, Point::new([1.0; D]))
}
fn translate_point<const D: usize>(p: &Point<f64, D>) -> Point<f64, D> {
const OFFSETS: [f64; 5] = [1e6, -5e5, 7.77, -3.33e4, 42.0];
let mut coords = [0.0; D];
for (i, c) in coords.iter_mut().enumerate() {
*c = p.coords()[i] + OFFSETS[i % OFFSETS.len()];
}
Point::new(coords)
}
macro_rules! gen_sos_dim_tests {
($dim:literal) => {
pastey::paste! {
#[test]
fn [<test_sos_orientation_ $dim d_degenerate_nonzero>]() {
let points = degenerate_orient_points::<$dim>();
let sign = sos_orientation_sign(&points).unwrap();
assert!(sign == 1 || sign == -1, "SoS must return ±1, got {sign}");
}
#[test]
fn [<test_sos_orientation_ $dim d_degenerate_deterministic>]() {
let points = degenerate_orient_points::<$dim>();
let s1 = sos_orientation_sign(&points).unwrap();
let s2 = sos_orientation_sign(&points).unwrap();
assert_eq!(s1, s2, "SoS must be deterministic");
}
#[test]
fn [<test_sos_orientation_ $dim d_translation_invariant>]() {
let points = degenerate_orient_points::<$dim>();
let s1 = sos_orientation_sign(&points).unwrap();
let translated: Vec<_> = points.iter().map(translate_point).collect();
let s2 = sos_orientation_sign(&translated).unwrap();
assert_eq!(s1, s2, "SoS orientation must be translation-invariant");
}
#[test]
fn [<test_sos_insphere_ $dim d_cospherical_nonzero>]() {
let (simplex, test) = cospherical_points::<$dim>();
let sign = sos_insphere_sign(&simplex, &test).unwrap();
assert!(
sign == 1 || sign == -1,
"SoS insphere must return ±1, got {sign}"
);
}
#[test]
fn [<test_sos_insphere_ $dim d_cospherical_deterministic>]() {
let (simplex, test) = cospherical_points::<$dim>();
let results: Vec<i32> = (0..10)
.map(|_| sos_insphere_sign(&simplex, &test).unwrap())
.collect();
assert!(
results.iter().all(|&r| r == results[0]),
"SoS insphere must be deterministic across calls"
);
}
#[test]
fn [<test_sos_insphere_ $dim d_translation_invariant>]() {
let (simplex, test) = cospherical_points::<$dim>();
let s1 = sos_insphere_sign(&simplex, &test).unwrap();
let translated_simplex: Vec<_> =
simplex.iter().map(translate_point).collect();
let translated_test = translate_point(&test);
let s2 =
sos_insphere_sign(&translated_simplex, &translated_test).unwrap();
assert_eq!(
s1, s2,
"SoS insphere must be translation-invariant"
);
}
#[test]
fn [<test_sos_orientation_ $dim d_all_identical_returns_err>]() {
let points = vec![Point::new([0.0; $dim]); $dim + 1];
assert!(
sos_orientation_sign(&points).is_err(),
"All-identical points must return Err"
);
}
#[test]
fn [<test_sos_insphere_ $dim d_all_identical_returns_err>]() {
let simplex = vec![Point::new([1.0; $dim]); $dim + 1];
let test_pt = Point::new([1.0; $dim]);
assert!(
sos_insphere_sign(&simplex, &test_pt).is_err(),
"All-identical insphere must return Err"
);
}
}
};
}
gen_sos_dim_tests!(2);
gen_sos_dim_tests!(3);
gen_sos_dim_tests!(4);
gen_sos_dim_tests!(5);
#[test]
fn test_sos_orientation_nondegenerate_returns_correct_sign() {
let positive = vec![
Point::new([0.0, 0.0]),
Point::new([1.0, 0.0]),
Point::new([0.0, 1.0]),
];
let sign = sos_orientation_sign(&positive).unwrap();
assert_eq!(sign, 1, "Non-degenerate positive triangle should return +1");
}
#[test]
fn test_sos_orientation_wrong_point_count_returns_error() {
let points = vec![Point::new([0.0, 0.0]), Point::new([1.0, 0.0])];
let result = sos_orientation_sign(&points);
assert!(result.is_err(), "Should return Err for wrong point count");
}
#[test]
fn test_sos_insphere_wrong_simplex_count_returns_error() {
let simplex = vec![Point::new([0.0, 0.0]), Point::new([1.0, 0.0])];
let test = Point::new([0.5, 0.5]);
let result = sos_insphere_sign(&simplex, &test);
assert!(result.is_err(), "Should return Err for wrong simplex count");
}
#[test]
fn test_exact_det_sign_identity_2x2() {
let m = Matrix::<2>::from_rows([[1.0, 0.0], [0.0, 1.0]]);
assert_eq!(exact_det_sign(&m), 1);
}
#[test]
fn test_exact_det_sign_singular_2x2() {
let m = Matrix::<2>::from_rows([[1.0, 2.0], [2.0, 4.0]]);
assert_eq!(exact_det_sign(&m), 0);
}
#[test]
fn test_exact_det_sign_negative_2x2() {
let m = Matrix::<2>::from_rows([[0.0, 1.0], [1.0, 0.0]]);
assert_eq!(exact_det_sign(&m), -1);
}
#[test]
fn test_exact_det_sign_identity_3x3() {
let m = Matrix::<3>::from_rows([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
assert_eq!(exact_det_sign(&m), 1);
}
#[test]
fn test_exact_det_sign_negative_3x3() {
let m = Matrix::<3>::from_rows([[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]);
assert_eq!(exact_det_sign(&m), -1);
}
#[test]
fn test_exact_det_sign_identity_4x4() {
let m = Matrix::<4>::from_rows([
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]);
assert_eq!(exact_det_sign(&m), 1);
}
#[test]
fn test_exact_det_sign_identity_5x5_bareiss_only() {
let m = Matrix::<5>::from_rows([
[1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 1.0],
]);
assert_eq!(exact_det_sign(&m), 1);
}
#[test]
fn test_exact_det_sign_near_singular_uses_bareiss() {
let perturbation = f64::from_bits(0x3CD0_0000_0000_0000); let m = Matrix::<3>::from_rows([
[1.0 + perturbation, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
]);
assert_eq!(exact_det_sign(&m), -1);
}
#[test]
fn test_exact_det_sign_overflow_det_recovered_by_bareiss() {
let m = Matrix::<2>::from_rows([[1e200, 0.0], [0.0, 1e200]]);
assert_eq!(exact_det_sign(&m), 1);
}
#[test]
fn test_exact_det_sign_nan_entry_returns_zero() {
let m = Matrix::<2>::from_rows([[f64::NAN, 0.0], [0.0, 1.0]]);
assert_eq!(exact_det_sign(&m), 0);
}
#[test]
fn test_sos_orientation_1d_identical_points() {
let points = vec![Point::new([5.0]), Point::new([5.0])];
let sign = sos_orientation_sign(&points).unwrap();
assert!(
sign == 1 || sign == -1,
"SoS must return ±1 for 1D, got {sign}"
);
}
#[test]
fn test_sos_orientation_1d_distinct_degenerate() {
let points = vec![Point::new([0.0]), Point::new([1.0])];
let sign = sos_orientation_sign(&points).unwrap();
assert!(
sign == 1 || sign == -1,
"SoS must return ±1 for 1D, got {sign}"
);
}
}