#![forbid(unsafe_code)]
use crate::core::facet::FacetError;
use crate::core::traits::data_type::DataType;
use crate::core::triangulation_data_structure::{CellKey, Tds, VertexKey};
use crate::core::util::hashing::stable_hash_u64_slice;
use crate::geometry::traits::coordinate::CoordinateScalar;
use slotmap::Key;
use thiserror::Error;
pub fn checked_facet_key_from_vertex_keys<const D: usize>(
facet_vertex_keys: &[VertexKey],
) -> Result<u64, FacetError> {
use crate::core::facet::facet_key_from_vertices;
if facet_vertex_keys.len() != D {
return Err(FacetError::InsufficientVertices {
expected: D,
actual: facet_vertex_keys.len(),
dimension: D,
});
}
Ok(facet_key_from_vertices(facet_vertex_keys))
}
#[derive(Clone, Debug, Error, PartialEq, Eq)]
pub(crate) enum PeriodicFacetKeyDerivationError {
#[error("Invalid lifted cell arity: expected {expected} vertices, got {actual}")]
InvalidLiftedCellArity {
expected: usize,
actual: usize,
},
#[error("Facet index {facet_index} out of bounds for lifted vertex count {vertex_count}")]
FacetIndexOutOfBounds {
facet_index: usize,
vertex_count: usize,
},
#[error(
"Periodic offset component {component} (axis {axis}) is out of encodable range 0..=255"
)]
RelativeOffsetOutOfRange {
axis: usize,
component: i16,
},
}
pub(crate) fn periodic_facet_key_from_lifted_vertices<const D: usize>(
lifted_vertices: &[(VertexKey, [i8; D])],
facet_index: usize,
) -> Result<u64, PeriodicFacetKeyDerivationError> {
let expected_arity = D + 1;
if lifted_vertices.len() != expected_arity {
return Err(PeriodicFacetKeyDerivationError::InvalidLiftedCellArity {
expected: expected_arity,
actual: lifted_vertices.len(),
});
}
if facet_index >= lifted_vertices.len() {
return Err(PeriodicFacetKeyDerivationError::FacetIndexOutOfBounds {
facet_index,
vertex_count: lifted_vertices.len(),
});
}
let mut lifted_facet: Vec<(u64, [i8; D])> =
Vec::with_capacity(lifted_vertices.len().saturating_sub(1));
for (idx, (vertex_key, offset)) in lifted_vertices.iter().enumerate() {
if idx != facet_index {
lifted_facet.push((vertex_key.data().as_ffi(), *offset));
}
}
lifted_facet.sort_unstable_by(|(key_a, offset_a), (key_b, offset_b)| {
key_a.cmp(key_b).then_with(|| offset_a.cmp(offset_b))
});
let facet_anchor_offset = lifted_facet
.first()
.map_or([0_i8; D], |(_, offset)| *offset);
let mut packed_signature: Vec<u64> = Vec::with_capacity(lifted_facet.len() * (D + 1));
for (vertex_key_value, offset) in lifted_facet {
packed_signature.push(vertex_key_value);
for axis in 0..D {
let shifted = i16::from(offset[axis]) - i16::from(facet_anchor_offset[axis]) + 128;
if !(0..=255).contains(&shifted) {
return Err(PeriodicFacetKeyDerivationError::RelativeOffsetOutOfRange {
axis,
component: shifted,
});
}
packed_signature
.push(u64::try_from(shifted).expect("validated shifted offset is in 0..=255"));
}
}
Ok(stable_hash_u64_slice(&packed_signature))
}
pub fn verify_facet_index_consistency<const D: usize>(
tds: &Tds<impl CoordinateScalar, impl DataType, impl DataType, D>,
cell1_key: CellKey,
cell2_key: CellKey,
facet_idx: usize,
) -> Result<bool, FacetError> {
let cell1_facets = crate::core::cell::Cell::facet_views_from_tds(tds, cell1_key)?;
let cell2_facets = crate::core::cell::Cell::facet_views_from_tds(tds, cell2_key)?;
if facet_idx >= cell1_facets.len() {
let idx_u8 = u8::try_from(facet_idx).unwrap_or(u8::MAX);
return Err(FacetError::InvalidFacetIndex {
index: idx_u8,
facet_count: cell1_facets.len(),
});
}
let cell1_facet = &cell1_facets[facet_idx];
let cell1_key_value = cell1_facet.key()?;
for cell2_facet in &cell2_facets {
if cell1_key_value == cell2_facet.key()? {
return Ok(true);
}
}
Ok(false) }
pub fn usize_to_u8(idx: usize, facet_count: usize) -> Result<u8, FacetError> {
u8::try_from(idx).map_err(|_| FacetError::InvalidFacetIndexOverflow {
original_index: idx,
facet_count,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::util::measure_with_result;
use crate::vertex;
use std::thread;
use std::time::Instant;
#[test]
fn periodic_facet_key_rejects_invalid_lifted_cell_arity() {
let lifted_vertices = vec![
(VertexKey::null(), [0_i8; 2]),
(VertexKey::null(), [0_i8; 2]),
];
let err = periodic_facet_key_from_lifted_vertices::<2>(&lifted_vertices, 0).unwrap_err();
assert!(matches!(
err,
PeriodicFacetKeyDerivationError::InvalidLiftedCellArity {
expected: 3,
actual: 2
}
));
}
#[test]
fn periodic_facet_key_rejects_relative_offset_out_of_range() {
let lifted_vertices = vec![
(VertexKey::null(), [-128_i8, 0_i8]),
(VertexKey::null(), [0_i8, 0_i8]),
(VertexKey::null(), [127_i8, 0_i8]),
];
let err = periodic_facet_key_from_lifted_vertices::<2>(&lifted_vertices, 1).unwrap_err();
assert!(matches!(
err,
PeriodicFacetKeyDerivationError::RelativeOffsetOutOfRange { axis: 0, .. }
));
}
#[test]
fn periodic_facet_key_happy_path_translation_invariance_and_distinctness() {
let lifted_base = vec![
(VertexKey::null(), [0_i8, 0_i8]),
(VertexKey::null(), [1_i8, 0_i8]),
(VertexKey::null(), [0_i8, 1_i8]),
];
let lifted_translated = vec![
(VertexKey::null(), [7_i8, -3_i8]),
(VertexKey::null(), [8_i8, -3_i8]),
(VertexKey::null(), [7_i8, -2_i8]),
];
let lifted_distinct = vec![
(VertexKey::null(), [0_i8, 0_i8]),
(VertexKey::null(), [2_i8, 0_i8]),
(VertexKey::null(), [0_i8, 1_i8]),
];
let base_key: Result<u64, PeriodicFacetKeyDerivationError> =
periodic_facet_key_from_lifted_vertices::<2>(&lifted_base, 0);
let translated_key: Result<u64, PeriodicFacetKeyDerivationError> =
periodic_facet_key_from_lifted_vertices::<2>(&lifted_translated, 0);
let distinct_key: Result<u64, PeriodicFacetKeyDerivationError> =
periodic_facet_key_from_lifted_vertices::<2>(&lifted_distinct, 0);
assert!(base_key.is_ok());
assert!(translated_key.is_ok());
assert!(distinct_key.is_ok());
let base_key = base_key.unwrap();
let translated_key = translated_key.unwrap();
let distinct_key = distinct_key.unwrap();
assert_eq!(
base_key, translated_key,
"uniform global translation should preserve periodic facet key",
);
assert_ne!(
base_key, distinct_key,
"different relative offsets should produce a distinct periodic facet key",
);
}
#[test]
fn periodic_facet_key_rejects_out_of_bounds_facet_index() {
let lifted_vertices = vec![
(VertexKey::null(), [0_i8; 2]),
(VertexKey::null(), [0_i8; 2]),
(VertexKey::null(), [0_i8; 2]),
];
let err = periodic_facet_key_from_lifted_vertices::<2>(&lifted_vertices, 3).unwrap_err();
assert!(matches!(
err,
PeriodicFacetKeyDerivationError::FacetIndexOutOfBounds {
facet_index: 3,
vertex_count: 3
}
));
}
#[test]
#[expect(clippy::too_many_lines)]
fn test_checked_facet_key_from_vertex_keys_comprehensive() {
println!("Testing checked_facet_key_from_vertex_keys comprehensively");
let vertices = 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::new(&vertices).unwrap();
let tds = &dt.as_triangulation().tds;
println!(" Testing basic functionality...");
let cell = tds.cells().map(|(_, cell)| cell).next().unwrap();
let facet_vertex_keys: Vec<_> = cell.vertices().iter().skip(1).copied().collect();
let result = checked_facet_key_from_vertex_keys::<3>(&facet_vertex_keys);
assert!(
result.is_ok(),
"Facet key derivation should succeed for valid vertex keys"
);
let facet_key = result.unwrap();
println!(" Derived facet key: {facet_key}");
let result2 = checked_facet_key_from_vertex_keys::<3>(&facet_vertex_keys);
assert!(result2.is_ok(), "Second derivation should also succeed");
assert_eq!(
facet_key,
result2.unwrap(),
"Same vertex keys should produce same facet key"
);
let all_vertex_keys = cell.vertices();
let different_facet_vertex_keys: Vec<_> = all_vertex_keys.iter().take(3).copied().collect();
if different_facet_vertex_keys.len() == 3
&& different_facet_vertex_keys != facet_vertex_keys
{
let result3 = checked_facet_key_from_vertex_keys::<3>(&different_facet_vertex_keys);
assert!(
result3.is_ok(),
"Different facet key derivation should succeed"
);
let different_facet_key = result3.unwrap();
assert_ne!(
facet_key, different_facet_key,
"Different vertex keys should produce different facet keys"
);
println!(" Different facet key: {different_facet_key}");
}
println!(" Testing error handling...");
let single_key: Vec<VertexKey> = vec![facet_vertex_keys[0]];
let result_count = checked_facet_key_from_vertex_keys::<3>(&single_key);
assert!(
result_count.is_err(),
"Should return error for wrong vertex key count"
);
if let Err(error) = result_count {
match error {
FacetError::InsufficientVertices {
expected,
actual,
dimension,
} => {
assert_eq!(expected, 3, "Expected 3 vertex keys for 3D");
assert_eq!(actual, 1, "Got 1 vertex key");
assert_eq!(dimension, 3, "Dimension should be 3");
}
_ => panic!("Expected InsufficientVertices error, got: {error:?}"),
}
}
let empty_keys: Vec<VertexKey> = vec![];
let result_empty = checked_facet_key_from_vertex_keys::<3>(&empty_keys);
assert!(
result_empty.is_err(),
"Empty vertex keys should fail validation"
);
if let Err(error) = result_empty {
match error {
FacetError::InsufficientVertices {
expected,
actual,
dimension,
} => {
assert_eq!(expected, 3, "Expected 3 vertex keys for 3D");
assert_eq!(actual, 0, "Got 0 vertex keys");
assert_eq!(dimension, 3, "Dimension should be 3");
}
_ => {
panic!(
"Expected InsufficientVertices error for empty vertex keys, got: {error:?}"
)
}
}
}
println!(" Testing consistency with TDS...");
let cache = tds
.build_facet_to_cells_map()
.expect("Should build facet map in test");
let mut keys_found = 0;
let mut keys_tested = 0;
for cell in tds.cells().map(|(_, cell)| cell) {
let cell_vertex_keys = cell.vertices();
for skip_vertex_idx in 0..cell_vertex_keys.len() {
let facet_vertex_keys: Vec<_> = cell_vertex_keys
.iter()
.enumerate()
.filter(|(i, _)| *i != skip_vertex_idx)
.map(|(_, &vk)| vk)
.collect();
if !facet_vertex_keys.is_empty() {
let key_result = checked_facet_key_from_vertex_keys::<3>(&facet_vertex_keys);
if let Ok(derived_key) = key_result {
keys_tested += 1;
if cache.contains_key(&derived_key) {
keys_found += 1;
}
}
}
}
}
println!(" Found {keys_found}/{keys_tested} derived keys in TDS cache");
assert!(keys_tested > 0, "Should have tested some keys");
println!(" ✓ All facet key derivation tests passed");
}
#[test]
fn test_verify_facet_index_consistency_true_false_and_error_cases() {
let vertices = 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::new(&vertices).unwrap();
let tds = &dt.as_triangulation().tds;
let cell_key = tds.cell_keys().next().unwrap();
assert!(verify_facet_index_consistency(tds, cell_key, cell_key, 0).unwrap());
let err = verify_facet_index_consistency(tds, cell_key, cell_key, 99).unwrap_err();
assert!(matches!(err, FacetError::InvalidFacetIndex { .. }));
let err_large = verify_facet_index_consistency(tds, cell_key, cell_key, 300).unwrap_err();
println!(" Large facet_idx=300 error: {err_large:?}");
assert!(matches!(err_large, FacetError::InvalidFacetIndex { .. }));
let mut tds2: Tds<f64, (), (), 2> = Tds::empty();
let v_a = tds2
.insert_vertex_with_mapping(vertex!([0.0, 0.0]))
.unwrap();
let v_b = tds2
.insert_vertex_with_mapping(vertex!([1.0, 0.0]))
.unwrap();
let v_c = tds2
.insert_vertex_with_mapping(vertex!([0.0, 1.0]))
.unwrap();
let v_d = tds2
.insert_vertex_with_mapping(vertex!([10.0, 10.0]))
.unwrap();
let v_e = tds2
.insert_vertex_with_mapping(vertex!([11.0, 10.0]))
.unwrap();
let v_f = tds2
.insert_vertex_with_mapping(vertex!([10.0, 11.0]))
.unwrap();
let c1 = tds2
.insert_cell_with_mapping(
crate::core::cell::Cell::new(vec![v_a, v_b, v_c], None).unwrap(),
)
.unwrap();
let c2 = tds2
.insert_cell_with_mapping(
crate::core::cell::Cell::new(vec![v_d, v_e, v_f], None).unwrap(),
)
.unwrap();
assert!(!verify_facet_index_consistency(&tds2, c1, c2, 0).unwrap());
}
#[test]
fn test_usize_to_u8_conversion_comprehensive() {
assert_eq!(usize_to_u8(0, 4), Ok(0));
assert_eq!(usize_to_u8(1, 4), Ok(1));
assert_eq!(usize_to_u8(255, 256), Ok(255));
assert_eq!(usize_to_u8(u8::MAX as usize, 256), Ok(u8::MAX));
for i in 0u8..=255 {
let result = usize_to_u8(i as usize, 256);
assert_eq!(result, Ok(i), "Failed to convert {i}");
}
assert_eq!(usize_to_u8(254, 300), Ok(254));
assert_eq!(usize_to_u8(255, 300), Ok(255));
assert!(usize_to_u8(256, 300).is_err());
assert!(usize_to_u8(257, 300).is_err());
let facet_counts = [1, 10, 100, 255, 256, 1000, usize::MAX];
for &count in &facet_counts {
assert_eq!(
usize_to_u8(0, count),
Ok(0),
"Valid conversion should succeed"
);
let result_invalid = usize_to_u8(256, count);
assert!(result_invalid.is_err(), "Invalid conversion should fail");
if let Err(FacetError::InvalidFacetIndexOverflow { facet_count, .. }) = result_invalid {
assert_eq!(facet_count, count);
}
}
for i in 0..10 {
assert_eq!(
usize_to_u8(i, 20),
usize_to_u8(i, 20),
"Should be deterministic"
);
}
for i in [256, 1000, usize::MAX] {
assert_eq!(
usize_to_u8(i, 100),
usize_to_u8(i, 100),
"Error cases should be deterministic"
);
}
}
#[test]
fn test_usize_to_u8_error_handling() {
let result = usize_to_u8(256, 10);
assert!(result.is_err());
if let Err(FacetError::InvalidFacetIndexOverflow {
original_index,
facet_count,
}) = result
{
assert_eq!(original_index, 256);
assert_eq!(facet_count, 10);
} else {
panic!("Expected InvalidFacetIndexOverflow error");
}
let result = usize_to_u8(usize::MAX, 5);
assert!(result.is_err());
if let Err(FacetError::InvalidFacetIndexOverflow {
original_index,
facet_count,
}) = result
{
assert_eq!(original_index, usize::MAX);
assert_eq!(facet_count, 5);
} else {
panic!("Expected InvalidFacetIndexOverflow error");
}
let test_cases = [
(256, 100),
(300, 500),
(1000, 1500),
(usize::MAX, 42),
(65536, 10),
];
for &(idx, count) in &test_cases {
let result = usize_to_u8(idx, count);
assert!(result.is_err(), "Should fail for index {idx}");
match result {
Err(FacetError::InvalidFacetIndexOverflow {
original_index,
facet_count,
}) => {
assert_eq!(original_index, idx, "Should preserve original index");
assert_eq!(facet_count, count, "Should preserve facet_count");
}
_ => panic!("Expected InvalidFacetIndex error for index {idx}"),
}
}
let large_values = [257, 1000, 10000, 65536, usize::MAX];
for &val in &large_values {
let result = usize_to_u8(val, val);
assert!(result.is_err(), "Should fail for value {val}");
if let Err(FacetError::InvalidFacetIndexOverflow {
original_index,
facet_count,
}) = result
{
assert_eq!(original_index, val);
assert_eq!(facet_count, val);
}
}
let result = usize_to_u8(300, 42);
assert!(result.is_err());
if let Err(error) = result {
let error_string = format!("{error}");
assert!(
error_string.contains("InvalidFacetIndex") || error_string.contains("index"),
"Error message should indicate invalid index: {error_string}"
);
}
let result = usize_to_u8(usize::MAX, 7);
if let Err(FacetError::InvalidFacetIndexOverflow {
original_index,
facet_count,
}) = result
{
assert_eq!(original_index, usize::MAX);
assert_eq!(facet_count, 7);
} else {
panic!("Expected InvalidFacetIndexOverflow error");
}
}
#[test]
fn test_usize_to_u8_performance_and_threading() {
let start = Instant::now();
for i in 0..1000 {
let _ = usize_to_u8(i % 256, 300);
}
let duration = start.elapsed();
eprintln!("usize_to_u8 valid conversions: 1000 iters in {duration:?}");
let start = Instant::now();
for i in 256..1256 {
let _ = usize_to_u8(i, 100);
}
let duration = start.elapsed();
eprintln!("usize_to_u8 error conversions: 1000 iters in {duration:?}");
let (result, _alloc_info) = measure_with_result(|| {
let mut results = Vec::new();
for i in 0..100 {
results.push(usize_to_u8(i, 200));
}
results
});
for (i, result) in result.iter().enumerate() {
assert_eq!(
*result,
Ok(u8::try_from(i).unwrap()),
"Result should be correct for {i}"
);
}
let handles: Vec<_> = (0..4)
.map(|thread_id| {
thread::spawn(move || {
let mut results = Vec::new();
for i in 0..100 {
let val = (thread_id * 50 + i) % 256;
results.push(usize_to_u8(val, 300));
}
results
})
})
.collect();
for handle in handles {
let results = handle.join().expect("Thread should complete successfully");
for result in results {
assert!(result.is_ok(), "All results should be successful");
}
}
}
}