use nalgebra::{Point, Scalar};
use num_traits::Zero;
use crate::{types::IsNan, Ordering, Vec};
fn validate_input<T: Scalar + PartialOrd + IsNan, const N: usize>(input: &[Point<T, N>]) -> bool {
!(N.is_zero() || input.iter().any(|a| a.coords.iter().any(|b| b.is_nan())))
}
fn lex_sort_func<T: Scalar + PartialOrd + IsNan, const N: usize>(
a: &Point<T, N>,
b: &Point<T, N>,
) -> Ordering {
(0..N).fold(Ordering::Equal, |ord, i| {
ord.then_with(|| a.coords[i].partial_cmp(&b.coords[i]).unwrap())
})
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("Lexicographical Sort In Place", skip_all)
)]
pub fn lex_sort_in_place<T: Scalar + PartialOrd + IsNan, const N: usize>(
input: &mut [Point<T, N>],
) -> bool {
if !validate_input(input) {
return false;
}
input.sort_by(lex_sort_func);
true
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("Lexicographical Sort With Copy", skip_all)
)]
pub fn lex_sort<T: Scalar + PartialOrd + IsNan, const N: usize>(
input: &[Point<T, N>],
) -> Option<Vec<Point<T, N>>> {
if !validate_input(input) {
return None;
}
let mut input = input.to_vec();
input.sort_by(lex_sort_func);
Some(input)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("Lexicographical Sort With References", skip_all)
)]
pub fn lex_sort_ref<T: Scalar + PartialOrd + IsNan, const N: usize>(
input: &[Point<T, N>],
) -> Option<Vec<&Point<T, N>>> {
if !validate_input(input) {
return None;
}
let mut refs = input.iter().collect::<Vec<_>>();
refs.sort_by(|&a, &b| lex_sort_func(a, b));
Some(refs)
}
#[cfg(test)]
mod tests {
use nalgebra::Point3;
use crate::point_clouds::generate_point_cloud;
use super::*;
#[test]
fn test_lex_sort_in_place() {
let mut input = Vec::from([
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 4.0),
Point3::new(1.0, 2.0, 2.0),
]);
assert!(lex_sort_in_place(&mut input));
assert_eq!(
input,
Vec::from([
Point3::new(1.0, 2.0, 2.0),
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 4.0)
])
);
}
#[test]
fn test_lex_sort_in_place_nan() {
let mut input = Vec::from([
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 4.0),
Point3::new(1.0, 2.0, f32::NAN),
]);
assert!(!lex_sort_in_place(&mut input));
}
#[test]
fn test_lex_sort() {
let input = Vec::from([
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 4.0),
Point3::new(1.0, 2.0, 2.0),
]);
assert_eq!(
lex_sort(&input),
Some(Vec::from([
Point3::new(1.0, 2.0, 2.0),
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 4.0)
]))
);
}
#[test]
fn ensure_starting_point() {
let input = generate_point_cloud(100, [-10.0..=10.0, -10.0..=10.0]);
let sorted = lex_sort(&input).unwrap();
assert_eq!(
sorted
.iter()
.fold(f64::INFINITY, |acc, it| { acc.min(it.x) }),
sorted[0].x
);
}
#[test]
fn test_lex_sort_nan() {
let input = Vec::from([
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 4.0),
Point3::new(1.0, 2.0, f32::NAN),
]);
assert_eq!(lex_sort(&input), None);
}
#[test]
fn test_lex_sort_coplanar() {
let input = Vec::from([
Point3::new(1.0, 4.0, 3.0),
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 2.0, 1.0),
]);
assert_eq!(
lex_sort(&input),
Some(Vec::from([
Point3::new(1.0, 2.0, 1.0),
Point3::new(1.0, 2.0, 3.0),
Point3::new(1.0, 4.0, 3.0)
]))
);
}
}