#![forbid(unsafe_code)]
use super::conversions::{safe_coords_to_f64, safe_scalar_from_f64, safe_usize_to_scalar};
use super::norms::hypot;
use crate::core::facet::FacetView;
use crate::core::traits::data_type::DataType;
use crate::geometry::matrix::{Matrix, matrix_get, matrix_set};
use crate::geometry::point::Point;
use crate::geometry::traits::coordinate::{Coordinate, CoordinateScalar, ScalarAccumulative};
use la_stack::{DEFAULT_SINGULAR_TOL, LaError};
use num_traits::Float;
use std::ops::AddAssign;
pub use super::{CircumcenterError, SurfaceMeasureError, ValueConversionError};
pub fn simplex_volume<T, const D: usize>(points: &[Point<T, D>]) -> Result<T, CircumcenterError>
where
T: CoordinateScalar,
{
#[cfg(debug_assertions)]
if std::env::var_os("DELAUNAY_DEBUG_UNUSED_IMPORTS").is_some() {
eprintln!(
"measures::simplex_volume called (points_len={}, D={})",
points.len(),
D
);
}
if points.len() != D + 1 {
return Err(CircumcenterError::InvalidSimplex {
actual: points.len(),
expected: D + 1,
dimension: D,
});
}
match D {
1 => {
let p0 = points[0].coords();
let p1 = points[1].coords();
let diff = [p1[0] - p0[0]];
let length = Float::abs(diff[0]);
let epsilon = T::from(1e-12).ok_or_else(|| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: "1e-12".to_string(),
from_type: "f64",
to_type: std::any::type_name::<T>(),
details: "Failed to convert epsilon threshold".to_string(),
})
})?;
if length < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Degenerate simplex with zero volume (coincident points)".to_string(),
});
}
Ok(length)
}
2 => {
let p0 = points[0].coords();
let p1 = points[1].coords();
let p2 = points[2].coords();
let v1 = [p1[0] - p0[0], p1[1] - p0[1]];
let v2 = [p2[0] - p0[0], p2[1] - p0[1]];
let cross_z = v1[0] * v2[1] - v1[1] * v2[0];
let area = Float::abs(cross_z) / T::from(2).unwrap_or_else(|| T::one() + T::one());
let epsilon = T::from(1e-12).ok_or_else(|| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: "1e-12".to_string(),
from_type: "f64",
to_type: std::any::type_name::<T>(),
details: "Failed to convert epsilon threshold".to_string(),
})
})?;
if area < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Degenerate simplex with zero volume (collinear points)".to_string(),
});
}
Ok(area)
}
3 => {
let p0 = points[0].coords();
let p1 = points[1].coords();
let p2 = points[2].coords();
let p3 = points[3].coords();
let v1 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];
let v2 = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]];
let v3 = [p3[0] - p0[0], p3[1] - p0[1], p3[2] - p0[2]];
let cross_x = v2[1] * v3[2] - v2[2] * v3[1];
let cross_y = v2[2] * v3[0] - v2[0] * v3[2];
let cross_z = v2[0] * v3[1] - v2[1] * v3[0];
let triple_product = v1[0] * cross_x + v1[1] * cross_y + v1[2] * cross_z;
let six = T::from(6)
.unwrap_or_else(|| T::one() + T::one() + T::one() + T::one() + T::one() + T::one());
let volume = Float::abs(triple_product) / six;
let epsilon = T::from(1e-12).ok_or_else(|| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: "1e-12".to_string(),
from_type: "f64",
to_type: std::any::type_name::<T>(),
details: "Failed to convert epsilon threshold".to_string(),
})
})?;
if volume < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Degenerate simplex with zero volume (coplanar points)".to_string(),
});
}
Ok(volume)
}
_ => {
simplex_volume_gram_matrix::<T, D>(points)
}
}
}
fn clamp_gram_determinant(mut det: f64) -> Result<f64, CircumcenterError> {
if !det.is_finite() {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Gram determinant is non-finite".to_string(),
});
}
if det < 0.0 {
if det > -1e-12 {
det = 0.0;
} else {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Gram matrix has negative determinant (degenerate simplex)".to_string(),
});
}
}
if det == 0.0 {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Degenerate simplex with zero volume (collinear or coplanar points)"
.to_string(),
});
}
Ok(det)
}
#[inline]
fn gram_determinant_ldlt<const D: usize>(gram_matrix: Matrix<D>) -> f64 {
match gram_matrix.ldlt(DEFAULT_SINGULAR_TOL) {
Ok(ldlt) => ldlt.det(),
Err(LaError::Singular { .. }) => 0.0,
Err(_) => f64::NAN,
}
}
fn simplex_volume_gram_matrix<T, const D: usize>(
points: &[Point<T, D>],
) -> Result<T, CircumcenterError>
where
T: CoordinateScalar,
{
let p0_coords = points[0].coords();
let p0_f64 = safe_coords_to_f64(p0_coords)?;
let mut edge_matrix = crate::geometry::matrix::Matrix::<D>::zero();
for (row, point) in points.iter().skip(1).enumerate() {
let point_f64 = safe_coords_to_f64(point.coords())?;
for (j, (&p, &p0)) in point_f64.iter().zip(p0_f64.iter()).enumerate() {
matrix_set(&mut edge_matrix, row, j, p - p0);
}
}
let mut gram_matrix = crate::geometry::matrix::Matrix::<D>::zero();
for i in 0..D {
for j in 0..D {
let mut dot_product = 0.0;
for k in 0..D {
dot_product += matrix_get(&edge_matrix, i, k) * matrix_get(&edge_matrix, j, k);
}
matrix_set(&mut gram_matrix, i, j, dot_product);
}
}
let det = clamp_gram_determinant(gram_determinant_ldlt(gram_matrix))?;
let volume_f64 = {
let sqrt_det = det.sqrt();
let mut d_fact = 1.0f64;
for k in 2..=D {
let k_f64 = safe_usize_to_scalar::<f64>(k).map_err(|e| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: k.to_string(),
from_type: "usize",
to_type: "f64",
details: e.to_string(),
})
})?;
d_fact *= k_f64;
}
sqrt_det / d_fact
};
safe_scalar_from_f64(volume_f64).map_err(CircumcenterError::CoordinateConversion)
}
pub fn inradius<T, const D: usize>(points: &[Point<T, D>]) -> Result<T, CircumcenterError>
where
T: CoordinateScalar + AddAssign<T>,
{
if points.len() != D + 1 {
return Err(CircumcenterError::InvalidSimplex {
actual: points.len(),
expected: D + 1,
dimension: D,
});
}
if D == 1 {
let length = simplex_volume(points)?; return Ok(length / T::from(2).unwrap_or_else(|| T::one() + T::one()));
}
let volume = simplex_volume(points)?;
let epsilon = T::from(1e-12).ok_or_else(|| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: "1e-12".to_string(),
from_type: "f64",
to_type: std::any::type_name::<T>(),
details: "Failed to convert epsilon threshold".to_string(),
})
})?;
if volume < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: format!("Degenerate simplex with volume ≈ {volume:?}"),
});
}
let mut surface_area = T::zero();
for i in 0..=D {
let facet_points: Vec<Point<T, D>> = points
.iter()
.enumerate()
.filter(|(j, _)| *j != i)
.map(|(_, p)| *p)
.collect();
if facet_points.len() != D {
continue;
}
let facet_area = facet_measure(&facet_points)?;
surface_area += facet_area;
}
if surface_area < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: format!("Degenerate simplex with surface_area ≈ {surface_area:?}"),
});
}
let d_scalar = T::from(D).ok_or_else(|| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: D.to_string(),
from_type: "usize",
to_type: std::any::type_name::<T>(),
details: "Failed to convert dimension to coordinate type".to_string(),
})
})?;
let inradius = (d_scalar * volume) / surface_area;
Ok(inradius)
}
pub fn facet_measure<T, const D: usize>(points: &[Point<T, D>]) -> Result<T, CircumcenterError>
where
T: CoordinateScalar,
{
if points.len() != D {
return Err(CircumcenterError::InvalidSimplex {
actual: points.len(),
expected: D,
dimension: D,
});
}
match D {
1 => {
if points.len() != 1 {
return Err(CircumcenterError::InvalidSimplex {
actual: points.len(),
expected: 1,
dimension: 1,
});
}
Ok(T::zero())
}
2 => {
let p0 = points[0].coords();
let p1 = points[1].coords();
let diff = [p1[0] - p0[0], p1[1] - p0[1]];
let length = hypot(&diff);
let epsilon = T::from(1e-12).unwrap_or_else(T::zero);
if length < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Degenerate facet with zero length (coincident points)".to_string(),
});
}
Ok(length)
}
3 => {
let p0 = points[0].coords();
let p1 = points[1].coords();
let p2 = points[2].coords();
let v1 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];
let v2 = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]];
let cross = [
v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0],
];
let cross_magnitude = hypot(&cross);
let area = cross_magnitude / (T::one() + T::one());
let epsilon = T::from(1e-12).unwrap_or_else(T::zero);
if area < epsilon {
return Err(CircumcenterError::MatrixInversionFailed {
details: "Degenerate facet with zero area (collinear points)".to_string(),
});
}
Ok(area)
}
4 => {
facet_measure_gram_matrix::<T, D>(points)
}
_ => {
facet_measure_gram_matrix::<T, D>(points)
}
}
}
fn facet_measure_gram_matrix<T, const D: usize>(
points: &[Point<T, D>],
) -> Result<T, CircumcenterError>
where
T: CoordinateScalar,
{
let mut coords_f64 = [[0.0f64; D]; D];
for (dst, p) in coords_f64.iter_mut().zip(points.iter()) {
*dst = safe_coords_to_f64(p.coords())?;
}
let gram_dim = D - 1;
let det = try_with_la_stack_matrix!(gram_dim, |gram_matrix| {
for i in 0..gram_dim {
for j in 0..gram_dim {
let mut dot_product = 0.0;
for ((&ai, &aj), &a0) in coords_f64[i + 1]
.iter()
.zip(coords_f64[j + 1].iter())
.zip(coords_f64[0].iter())
{
let di = ai - a0;
let dj = aj - a0;
dot_product += di * dj;
}
matrix_set(&mut gram_matrix, i, j, dot_product);
}
}
clamp_gram_determinant(gram_determinant_ldlt(gram_matrix))
})?;
let volume_f64 = {
let sqrt_det = det.sqrt();
let mut d_fact = 1.0f64;
for k in 2..D {
let k_f64 = safe_usize_to_scalar::<f64>(k).map_err(|e| {
CircumcenterError::ValueConversion(ValueConversionError::ConversionFailed {
value: k.to_string(),
from_type: "usize",
to_type: "f64",
details: e.to_string(),
})
})?;
d_fact *= k_f64;
}
sqrt_det / d_fact
};
safe_scalar_from_f64(volume_f64).map_err(CircumcenterError::CoordinateConversion)
}
pub fn surface_measure<T, U, V, const D: usize>(
facets: &[FacetView<'_, T, U, V, D>],
) -> Result<T, SurfaceMeasureError>
where
T: ScalarAccumulative,
U: DataType,
V: DataType,
{
let mut total_measure = T::zero();
for facet in facets {
let facet_vertices = facet.vertices();
let points: Vec<Point<T, D>> = facet_vertices
.map_err(SurfaceMeasureError::FacetError)?
.map(|v| {
let coords = *v.point().coords();
Point::new(coords)
})
.collect();
let measure = facet_measure(&points).map_err(SurfaceMeasureError::GeometryError)?;
total_measure += measure;
}
Ok(total_measure)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::traits::boundary_analysis::BoundaryAnalysis;
use crate::core::vertex::Vertex;
use crate::geometry::point::Point;
use crate::vertex;
use approx::assert_relative_eq;
#[test]
fn test_simplex_volume_1d_line_segment() {
let line = vec![Point::new([0.0]), Point::new([5.0])];
let volume = simplex_volume(&line).unwrap();
assert_relative_eq!(volume, 5.0, epsilon = 1e-10);
let line_neg = vec![Point::new([5.0]), Point::new([0.0])];
let volume_neg = simplex_volume(&line_neg).unwrap();
assert_relative_eq!(volume_neg, 5.0, epsilon = 1e-10);
}
#[test]
fn test_simplex_volume_2d_triangle() {
let triangle = vec![
Point::new([0.0, 0.0]),
Point::new([3.0, 0.0]),
Point::new([0.0, 4.0]),
];
let area = simplex_volume(&triangle).unwrap();
assert_relative_eq!(area, 6.0, epsilon = 1e-10);
let equilateral = vec![
Point::new([0.0, 0.0]),
Point::new([1.0, 0.0]),
Point::new([0.5, 0.866_025]), ];
let area_eq = simplex_volume(&equilateral).unwrap();
assert_relative_eq!(area_eq, 0.433_013, epsilon = 1e-5);
}
#[test]
fn test_simplex_volume_3d_tetrahedron() {
let tetrahedron = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
Point::new([0.0, 0.0, 1.0]),
];
let volume = simplex_volume(&tetrahedron).unwrap();
assert_relative_eq!(volume, 1.0 / 6.0, epsilon = 1e-10); }
#[test]
fn test_simplex_volume_4d_simplex() {
let simplex_4d = vec![
Point::new([0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 1.0]),
];
let volume = simplex_volume(&simplex_4d).unwrap();
assert_relative_eq!(volume, 1.0 / 24.0, epsilon = 1e-10);
}
#[test]
fn test_simplex_volume_degenerate() {
let collinear = vec![
Point::new([0.0, 0.0]),
Point::new([1.0, 1.0]),
Point::new([2.0, 2.0]),
];
let result = simplex_volume(&collinear);
assert!(result.is_err(), "Degenerate simplex should return an error");
}
#[test]
fn test_simplex_volume_wrong_point_count() {
let points = vec![Point::new([0.0, 0.0]), Point::new([1.0, 0.0])];
let result = simplex_volume::<f64, 2>(&points);
assert!(result.is_err());
}
#[test]
fn test_inradius_2d_equilateral_triangle() {
let triangle = vec![
Point::new([0.0, 0.0]),
Point::new([1.0, 0.0]),
Point::new([0.5, 0.866_025]), ];
let r_in = inradius(&triangle).unwrap();
assert_relative_eq!(r_in, 0.288_675_13, epsilon = 1e-5);
}
#[test]
fn test_inradius_2d_right_triangle() {
let triangle = vec![
Point::new([0.0, 0.0]),
Point::new([3.0, 0.0]),
Point::new([0.0, 4.0]),
];
let r_in = inradius(&triangle).unwrap();
assert_relative_eq!(r_in, 1.0, epsilon = 1e-10);
}
#[test]
fn test_inradius_3d_regular_tetrahedron() {
let tetrahedron = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
Point::new([0.0, 0.0, 1.0]),
];
let r_in = inradius(&tetrahedron).unwrap();
assert_relative_eq!(r_in, 0.2113, epsilon = 1e-3);
}
#[test]
fn test_inradius_degenerate() {
let collinear = vec![
Point::new([0.0, 0.0]),
Point::new([1.0, 0.0]),
Point::new([2.0, 0.0]),
];
let result = inradius(&collinear);
assert!(result.is_err()); }
#[test]
fn test_facet_measure_1d_point() {
let points = vec![Point::new([5.0])];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 0.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_2d_line_segment() {
let points = vec![Point::new([0.0, 0.0]), Point::new([3.0, 4.0])];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 5.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_3d_triangle_right_angle() {
let points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 6.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_4d_tetrahedron() {
let points = vec![
Point::new([0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0]),
];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 1.0 / 6.0, epsilon = 1e-10);
}
fn gram_det_from_edges<const AMBIENT: usize>(
edges: &[[f64; AMBIENT]],
) -> Result<f64, CircumcenterError> {
let k = edges.len();
try_with_la_stack_matrix!(k, |gram_matrix| {
for i in 0..k {
for j in 0..k {
let mut dot_product = 0.0;
for (&a, &b) in edges[i].iter().zip(edges[j].iter()) {
dot_product += a * b;
}
matrix_set(&mut gram_matrix, i, j, dot_product);
}
}
clamp_gram_determinant(gram_determinant_ldlt(gram_matrix))
})
}
#[test]
fn test_gram_determinant_ldlt_known_spd() {
let gram = Matrix::<2>::from_rows([[4.0, 2.0], [2.0, 3.0]]);
let det = gram_determinant_ldlt(gram);
assert_relative_eq!(det, 8.0, epsilon = 1e-12);
}
#[test]
fn test_gram_determinant_parallel_edges_errors() {
let edges = [[1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
assert!(gram_det_from_edges(&edges).is_err());
}
#[test]
fn test_clamp_gram_determinant_tiny_negative_errors() {
assert!(clamp_gram_determinant(-1e-13).is_err());
}
macro_rules! test_gram_det_orthogonal {
($test_name:ident, $dim:literal) => {
#[test]
fn $test_name() {
let mut edges = [[0.0f64; $dim]; $dim];
for i in 0..$dim {
edges[i][i] = 1.0;
}
let det = gram_det_from_edges::<$dim>(&edges).unwrap();
assert_relative_eq!(det, 1.0, epsilon = 1e-10);
}
};
}
test_gram_det_orthogonal!(test_gram_determinant_orthogonal_2d, 2);
test_gram_det_orthogonal!(test_gram_determinant_orthogonal_3d, 3);
test_gram_det_orthogonal!(test_gram_determinant_orthogonal_4d, 4);
test_gram_det_orthogonal!(test_gram_determinant_orthogonal_5d, 5);
macro_rules! test_gram_det_scaled {
($test_name:ident, $dim:literal, $scale:expr, $expected_det:expr) => {
#[test]
fn $test_name() {
let mut edges = [[0.0f64; $dim]; $dim];
for i in 0..$dim {
edges[i][i] = $scale;
}
let det = gram_det_from_edges::<$dim>(&edges).unwrap();
assert_relative_eq!(det, $expected_det, epsilon = 1e-9);
}
};
}
test_gram_det_scaled!(test_gram_determinant_scaled_2d, 2, 2.0, 16.0); test_gram_det_scaled!(test_gram_determinant_scaled_3d, 3, 2.0, 64.0); test_gram_det_scaled!(test_gram_determinant_scaled_4d, 4, 2.0, 256.0); test_gram_det_scaled!(test_gram_determinant_scaled_5d, 5, 2.0, 1024.0);
#[test]
fn test_gram_matrix_debug() {
let triangle_3d = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
];
let area_3d = facet_measure(&triangle_3d).unwrap();
if std::env::var_os("TEST_DEBUG").is_some() {
println!("3D triangle area: {area_3d} (expected: 0.5)");
}
let eps = 1e-10;
let near_singular = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([1.0, eps, 0.0]),
];
let area_ns = facet_measure(&near_singular).unwrap();
assert!(area_ns >= 0.0);
let area_3d_gram = facet_measure_gram_matrix::<f64, 3>(&triangle_3d).unwrap();
if std::env::var_os("TEST_DEBUG").is_some() {
println!("3D triangle area (Gram): {area_3d_gram} (expected: 0.5)");
}
let tetrahedron_4d = vec![
Point::new([0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0]),
];
let volume_4d = facet_measure(&tetrahedron_4d).unwrap();
if std::env::var_os("TEST_DEBUG").is_some() {
println!(
"4D tetrahedron volume: {} (expected: {})",
volume_4d,
1.0 / 6.0
);
}
let volume_4d_gram = facet_measure_gram_matrix::<f64, 4>(&tetrahedron_4d).unwrap();
if std::env::var_os("TEST_DEBUG").is_some() {
println!(
"4D tetrahedron volume (Gram): {} (expected: {})",
volume_4d_gram,
1.0 / 6.0
);
}
}
#[test]
fn test_facet_measure_5d_simplex() {
let points = vec![
Point::new([0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 1.0, 0.0]),
];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 1.0 / 24.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_6d_simplex() {
let points = vec![
Point::new([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 0.0, 1.0, 0.0]),
];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 1.0 / 120.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_wrong_point_count() {
let points = vec![Point::new([0.0, 0.0, 0.0]), Point::new([1.0, 0.0, 0.0])];
let result = facet_measure::<f64, 3>(&points);
assert!(result.is_err());
match result.unwrap_err() {
CircumcenterError::InvalidSimplex {
actual,
expected,
dimension,
} => {
assert_eq!(actual, 2);
assert_eq!(expected, 3);
assert_eq!(dimension, 3);
}
other => panic!("Expected InvalidSimplex error, got: {other:?}"),
}
}
#[test]
fn test_facet_measure_zero_area_triangle() {
let points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([2.0, 0.0, 0.0]), ];
let result = facet_measure(&points);
assert!(result.is_err(), "Collinear points should return an error");
}
#[test]
fn test_facet_measure_nearly_collinear_points_2d() {
let eps = 1e-10;
let points = vec![
Point::new([0.0, 0.0]),
Point::new([1.0, eps]), ];
let measure = facet_measure(&points).unwrap();
let expected = eps.mul_add(eps, 1.0).sqrt(); assert_relative_eq!(measure, expected, epsilon = 1e-9);
}
#[test]
fn test_facet_measure_nearly_coplanar_points_3d() {
let eps = 1e-8;
let points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([1.0, eps, eps]), ];
let measure = facet_measure(&points).unwrap();
assert!(
measure > 0.0,
"Nearly coplanar triangle should have positive area"
);
assert!(
measure < 1e-6,
"Nearly coplanar triangle should have very small area, got: {measure}"
);
}
#[test]
fn test_facet_measure_degenerate_4d_tetrahedron() {
let points = vec![
Point::new([0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0]),
Point::new([0.5, 0.5, 0.0, 0.0]), ];
let result = facet_measure(&points);
assert!(
result.is_err(),
"Degenerate 4D tetrahedron should return an error"
);
}
#[test]
fn test_surface_measure_empty_facets() {
let facets: Vec<FacetView<'_, f64, (), (), 3>> = vec![];
let result = surface_measure(&facets).unwrap();
assert_relative_eq!(result, 0.0, epsilon = 1e-10);
}
#[test]
#[expect(
clippy::float_cmp,
reason = "Comparisons are against exact literals (constructed geometry), acceptable in this test"
)]
fn test_surface_measure_single_facet() {
let vertices: Vec<Vertex<f64, (), 3>> = vec![
vertex!([0.0, 0.0, 0.0]), vertex!([3.0, 0.0, 0.0]), vertex!([0.0, 4.0, 0.0]), vertex!([0.0, 0.0, 1.0]), ];
let dt: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 3> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices).unwrap();
let boundary_facets: Vec<_> = dt.tds().boundary_facets().unwrap().collect();
let target_facet = boundary_facets
.iter()
.find(|facet| {
let facet_vertices: Vec<_> = facet.vertices().unwrap().collect();
facet_vertices.len() == 3
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [0.0, 0.0, 0.0]
})
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [3.0, 0.0, 0.0]
})
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [0.0, 4.0, 0.0]
})
})
.expect("Should find the target facet");
let surface_area = surface_measure(&[*target_facet]).unwrap();
assert_relative_eq!(surface_area, 6.0, epsilon = 1e-10);
}
#[test]
fn test_surface_measure_consistency_with_facet_measure() {
let vertices: Vec<Vertex<f64, (), 3>> = vec![
vertex!([0.0, 0.0, 0.0]), vertex!([1.0, 0.0, 0.0]), vertex!([0.0, 1.0, 0.0]), vertex!([0.0, 0.0, 1.0]), vertex!([1.0, 1.0, 1.0]), ];
let dt: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 3> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices).unwrap();
let boundary_facets: Vec<_> = dt.tds().boundary_facets().unwrap().collect();
let facet1 = boundary_facets[0];
let facet2 = boundary_facets[1];
let total_surface = surface_measure(&[facet1, facet2]).unwrap();
let points1: Vec<Point<f64, 3>> = facet1
.vertices()
.unwrap()
.map(|v| {
let coords = *v.point().coords();
Point::new(coords)
})
.collect();
let points2: Vec<Point<f64, 3>> = facet2
.vertices()
.unwrap()
.map(|v| {
let coords = *v.point().coords();
Point::new(coords)
})
.collect();
let measure1 = facet_measure(&points1).unwrap();
let measure2 = facet_measure(&points2).unwrap();
let sum_individual = measure1 + measure2;
assert_relative_eq!(total_surface, sum_individual, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_scaled_simplex_2d() {
let scale = 3.0;
let original_points = vec![Point::new([0.0, 0.0]), Point::new([1.0, 0.0])];
let scaled_points = vec![
Point::new([0.0 * scale, 0.0 * scale]),
Point::new([1.0 * scale, 0.0 * scale]),
];
let original_measure = facet_measure(&original_points).unwrap();
let scaled_measure = facet_measure(&scaled_points).unwrap();
assert_relative_eq!(scaled_measure, original_measure * scale, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_scaled_simplex_3d() {
let scale = 2.5;
let original_points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([2.0, 0.0, 0.0]),
Point::new([0.0, 3.0, 0.0]),
];
let scaled_points = vec![
Point::new([0.0 * scale, 0.0 * scale, 0.0 * scale]),
Point::new([2.0 * scale, 0.0 * scale, 0.0 * scale]),
Point::new([0.0 * scale, 3.0 * scale, 0.0 * scale]),
];
let original_measure = facet_measure(&original_points).unwrap();
let scaled_measure = facet_measure(&scaled_points).unwrap();
assert_relative_eq!(
scaled_measure,
original_measure * scale * scale,
epsilon = 1e-10
);
}
#[test]
fn test_facet_measure_scaled_simplex_4d() {
let scale = 2.0;
let original_points = vec![
Point::new([0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0]),
];
let scaled_points = vec![
Point::new([0.0 * scale, 0.0 * scale, 0.0 * scale, 0.0 * scale]),
Point::new([1.0 * scale, 0.0 * scale, 0.0 * scale, 0.0 * scale]),
Point::new([0.0 * scale, 1.0 * scale, 0.0 * scale, 0.0 * scale]),
Point::new([0.0 * scale, 0.0 * scale, 1.0 * scale, 0.0 * scale]),
];
let original_measure = facet_measure(&original_points).unwrap();
let scaled_measure = facet_measure(&scaled_points).unwrap();
assert_relative_eq!(
scaled_measure,
original_measure * scale.powi(3),
epsilon = 1e-10
);
}
#[test]
fn test_facet_measure_very_large_coordinates() {
let large_val = 1e8;
let points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([large_val, 0.0, 0.0]),
Point::new([0.0, large_val, 0.0]),
];
let result = facet_measure(&points);
assert!(result.is_ok(), "Large coordinates should work");
let measure = result.unwrap();
assert!(measure.is_finite(), "Measure should be finite");
let expected = large_val * large_val / 2.0;
assert_relative_eq!(measure, expected, epsilon = 1e-3);
}
#[test]
fn test_facet_measure_very_small_coordinates() {
let small_val = 1e-5;
let points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([small_val, 0.0, 0.0]),
Point::new([0.0, small_val, 0.0]),
];
let result = facet_measure(&points);
assert!(result.is_ok(), "Small coordinates should work");
let measure = result.unwrap();
assert!(measure.is_finite(), "Measure should be finite");
let expected = small_val * small_val / 2.0;
assert_relative_eq!(measure, expected, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_mixed_positive_negative_coordinates() {
let points = vec![
Point::new([-1.0, -1.0, 0.0]),
Point::new([2.0, -1.0, 0.0]),
Point::new([-1.0, 3.0, 0.0]),
];
let measure = facet_measure(&points).unwrap();
assert_relative_eq!(measure, 6.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_f32_vs_f64_consistency() {
let points_f64 = vec![
Point::new([0.0_f64, 0.0_f64, 0.0_f64]),
Point::new([3.0_f64, 0.0_f64, 0.0_f64]),
Point::new([0.0_f64, 4.0_f64, 0.0_f64]),
];
let points_f32 = vec![
Point::new([0.0_f32, 0.0_f32, 0.0_f32]),
Point::new([3.0_f32, 0.0_f32, 0.0_f32]),
Point::new([0.0_f32, 4.0_f32, 0.0_f32]),
];
let measure_f64 = facet_measure(&points_f64).unwrap();
let measure_f32 = facet_measure(&points_f32).unwrap();
let measure_f32_as_f64 = f64::from(measure_f32);
assert_relative_eq!(measure_f64, measure_f32_as_f64, epsilon = 1e-6);
assert_relative_eq!(measure_f64, 6.0, epsilon = 1e-10);
assert_relative_eq!(measure_f32_as_f64, 6.0, epsilon = 1e-6);
}
#[test]
fn test_facet_measure_translation_invariance() {
let translation = [10.0, 20.0, 30.0];
let original_points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
];
let translated_points = vec![
Point::new([
0.0 + translation[0],
0.0 + translation[1],
0.0 + translation[2],
]),
Point::new([
3.0 + translation[0],
0.0 + translation[1],
0.0 + translation[2],
]),
Point::new([
0.0 + translation[0],
4.0 + translation[1],
0.0 + translation[2],
]),
];
let original_measure = facet_measure(&original_points).unwrap();
let translated_measure = facet_measure(&translated_points).unwrap();
assert_relative_eq!(original_measure, translated_measure, epsilon = 1e-10);
assert_relative_eq!(original_measure, 6.0, epsilon = 1e-10); }
#[test]
fn test_facet_measure_vertex_permutation_invariance() {
let points_order1 = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
];
let points_order2 = vec![
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
Point::new([0.0, 0.0, 0.0]),
];
let points_order3 = vec![
Point::new([0.0, 4.0, 0.0]),
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
];
let measure1 = facet_measure(&points_order1).unwrap();
let measure2 = facet_measure(&points_order2).unwrap();
let measure3 = facet_measure(&points_order3).unwrap();
assert_relative_eq!(measure1, measure2, epsilon = 1e-10);
assert_relative_eq!(measure1, measure3, epsilon = 1e-10);
assert_relative_eq!(measure1, 6.0, epsilon = 1e-10);
}
#[test]
fn test_facet_measure_various_triangle_orientations() {
let triangles = [
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
],
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0]),
],
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0]),
Point::new([0.0, 0.0, 1.0]),
],
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 1.0, 0.0]),
Point::new([0.0, 1.0, 1.0]),
],
];
let expected_areas = [0.5, 0.5, 0.5];
for (i, triangle) in triangles.iter().take(3).enumerate() {
let measure = facet_measure(triangle).unwrap();
assert_relative_eq!(measure, expected_areas[i], epsilon = 1e-10);
}
let measure4 = facet_measure(&triangles[3]).unwrap();
assert!(
measure4 > 0.0,
"Diagonal triangle should have positive area"
);
assert!(
measure4.is_finite(),
"Diagonal triangle area should be finite"
);
}
#[test]
#[expect(
clippy::float_cmp,
reason = "Comparisons are against exact literals (constructed geometry), acceptable in this test"
)]
fn test_surface_measure_multiple_facets_different_sizes() {
let vertices1: Vec<Vertex<f64, (), 3>> = vec![
vertex!([0.0, 0.0, 0.0]), vertex!([1.0, 0.0, 0.0]), vertex!([0.0, 1.0, 0.0]), vertex!([0.0, 0.0, 1.0]), ];
let dt1: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 3> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices1).unwrap();
let boundary_facets1: Vec<_> = dt1.tds().boundary_facets().unwrap().collect();
let small_facet = boundary_facets1
.iter()
.find(|facet| {
let facet_vertices: Vec<_> = facet.vertices().unwrap().collect();
facet_vertices.len() == 3
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [0.0, 0.0, 0.0]
})
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [1.0, 0.0, 0.0]
})
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [0.0, 1.0, 0.0]
})
})
.expect("Should find small triangle facet");
let vertices2: Vec<Vertex<f64, (), 3>> = vec![
vertex!([0.0, 0.0, 0.0]), vertex!([6.0, 0.0, 0.0]), vertex!([0.0, 8.0, 0.0]), vertex!([0.0, 0.0, 1.0]), ];
let dt2: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 3> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices2).unwrap();
let boundary_facets2: Vec<_> = dt2.tds().boundary_facets().unwrap().collect();
let large_facet = boundary_facets2
.iter()
.find(|facet| {
let facet_vertices: Vec<_> = facet.vertices().unwrap().collect();
facet_vertices.len() == 3
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [0.0, 0.0, 0.0]
})
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [6.0, 0.0, 0.0]
})
&& facet_vertices.iter().any(|v| {
let coords = *v.point().coords();
coords == [0.0, 8.0, 0.0]
})
})
.expect("Should find large triangle facet");
let total_surface = surface_measure(&[*small_facet, *large_facet]).unwrap();
let expected_total = 0.5 + 24.0;
assert_relative_eq!(total_surface, expected_total, epsilon = 1e-10);
}
#[test]
fn test_surface_measure_2d_perimeter() {
let vertices: Vec<Vertex<f64, (), 2>> = vec![
vertex!([0.0, 0.0]), vertex!([3.0, 0.0]), vertex!([0.0, 4.0]), ];
let dt: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 2> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices).unwrap();
let boundary_facets: Vec<_> = dt.tds().boundary_facets().unwrap().collect();
let total_perimeter = surface_measure(&boundary_facets).unwrap();
assert_relative_eq!(total_perimeter, 12.0, epsilon = 1e-10);
}
#[test]
fn test_surface_measure_4d_boundary() {
let vertices: Vec<Vertex<f64, (), 4>> = vec![
vertex!([0.0, 0.0, 0.0, 0.0]), vertex!([1.0, 0.0, 0.0, 0.0]), vertex!([0.0, 1.0, 0.0, 0.0]), vertex!([0.0, 0.0, 1.0, 0.0]), vertex!([0.0, 0.0, 0.0, 1.0]), ];
let dt: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 4> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices).unwrap();
let boundary_facets: Vec<_> = dt.tds().boundary_facets().unwrap().collect();
let total_surface = surface_measure(&boundary_facets).unwrap();
let expected_total = 1.0;
assert_relative_eq!(total_surface, expected_total, epsilon = 1e-10);
}
#[test]
fn test_surface_measure_with_invalid_facet() {
let vertices: Vec<Vertex<f64, (), 3>> = vec![
vertex!([0.0, 0.0, 0.0]), vertex!([1.0, 0.0, 0.0]), vertex!([0.0, 1.0, 0.0]), vertex!([0.0, 0.0, 1.0]), ];
let dt: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 3> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices).unwrap();
let boundary_facets: Vec<_> = dt.tds().boundary_facets().unwrap().collect();
let result = surface_measure(&boundary_facets[0..1]);
assert!(result.is_ok(), "Valid facets should work");
let area = result.unwrap();
assert!(area > 0.0, "Area should be positive");
assert!(area.is_finite(), "Area should be finite");
}
#[test]
fn test_facet_measure_performance_many_dimensions() {
let points_7d = vec![
Point::new([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]),
];
let measure_7d = facet_measure(&points_7d).unwrap();
assert_relative_eq!(measure_7d, 1.0 / 720.0, epsilon = 1e-10);
let points_8d = vec![
Point::new([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]),
Point::new([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]),
];
let measure_8d = facet_measure(&points_8d).unwrap();
assert_relative_eq!(measure_8d, 1.0 / 5040.0, epsilon = 1e-10);
}
#[test]
fn test_surface_measure_many_facets() {
let vertices: Vec<Vertex<f64, (), 3>> = vec![
vertex!([0.0, 0.0, 0.0]),
vertex!([2.0, 0.0, 0.0]),
vertex!([0.0, 2.0, 0.0]),
vertex!([0.0, 0.0, 2.0]),
];
let dt: crate::core::delaunay_triangulation::DelaunayTriangulation<_, (), (), 3> =
crate::core::delaunay_triangulation::DelaunayTriangulation::new(&vertices).unwrap();
let boundary_facets: Vec<_> = dt.tds().boundary_facets().unwrap().collect();
assert_eq!(
boundary_facets.len(),
4,
"Tetrahedron should have 4 boundary facets, got {}",
boundary_facets.len()
);
let total_surface = surface_measure(&boundary_facets).unwrap();
assert!(total_surface.is_finite(), "Total surface should be finite");
assert!(total_surface > 0.0, "Total surface should be positive");
}
#[test]
fn test_facet_measure_equilateral_triangles() {
let side_lengths = [1.0, 2.0, 5.0, 10.0];
for &side in &side_lengths {
let height = side * 3.0_f64.sqrt() / 2.0;
let points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([side, 0.0, 0.0]),
Point::new([side / 2.0, height, 0.0]),
];
let measure = facet_measure(&points).unwrap();
let expected_area = side * side * 3.0_f64.sqrt() / 4.0;
assert_relative_eq!(measure, expected_area, epsilon = 1e-10);
}
}
#[test]
fn test_facet_measure_regular_tetrahedron_faces() {
let side = 2.0;
let height = side * (2.0_f64 / 3.0).sqrt();
let center_offset = side / (2.0 * 3.0_f64.sqrt());
let v1 = Point::new([0.0, 0.0, 0.0]);
let v2 = Point::new([side, 0.0, 0.0]);
let v3 = Point::new([side / 2.0, side * 3.0_f64.sqrt() / 2.0, 0.0]);
let v4 = Point::new([side / 2.0, center_offset, height]);
let faces = [
vec![v1, v2, v3], vec![v1, v2, v4], vec![v2, v3, v4], vec![v3, v1, v4], ];
let expected_face_area = side * side * 3.0_f64.sqrt() / 4.0;
for face in &faces {
let measure = facet_measure(face).unwrap();
assert_relative_eq!(measure, expected_face_area, epsilon = 1e-9);
}
}
#[test]
fn test_facet_measure_reflection_invariance() {
let original_points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
];
let reflections = [
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([-3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
],
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, -4.0, 0.0]),
],
vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([3.0, 0.0, 0.0]),
Point::new([0.0, 4.0, 0.0]),
],
];
let original_measure = facet_measure(&original_points).unwrap();
for reflected_points in &reflections {
let reflected_measure = facet_measure(reflected_points).unwrap();
assert_relative_eq!(original_measure, reflected_measure, epsilon = 1e-10);
}
}
#[test]
fn test_facet_measure_rotation_invariance_2d() {
let original_points = vec![Point::new([0.0, 0.0]), Point::new([3.0, 4.0])];
let rotated_points = vec![
Point::new([0.0, 0.0]),
Point::new([-4.0, 3.0]), ];
let original_measure = facet_measure(&original_points).unwrap();
let rotated_measure = facet_measure(&rotated_points).unwrap();
assert_relative_eq!(original_measure, rotated_measure, epsilon = 1e-10);
assert_relative_eq!(original_measure, 5.0, epsilon = 1e-10); }
#[test]
fn test_facet_measure_gram_matrix_degenerate() {
let degenerate_points = vec![
Point::new([0.0, 0.0, 0.0]),
Point::new([1.0, 0.0, 0.0]),
Point::new([2.0, 0.0, 0.0]), ];
let result = facet_measure(°enerate_points);
if let Ok(measure) = result {
assert_relative_eq!(measure, 0.0, epsilon = 1e-10);
}
}
}