#![allow(clippy::similar_names)]
#![forbid(unsafe_code)]
use crate::geometry::traits::coordinate::{
Coordinate, CoordinateConversionError, CoordinateScalar, CoordinateValidationError,
};
use num_traits::cast;
use serde::de::{Error, SeqAccess, Visitor};
use serde::{Deserialize, Serialize};
use std::any;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
#[derive(Clone, Copy, Debug)]
pub struct Point<T, const D: usize> {
coords: [T; D],
}
impl<T, const D: usize> Point<T, D>
where
T: CoordinateScalar,
{
#[inline]
#[must_use]
pub const fn coords(&self) -> &[T; D] {
&self.coords
}
}
impl<T, const D: usize> Coordinate<T, D> for Point<T, D>
where
T: CoordinateScalar,
{
#[inline]
fn new(coords: [T; D]) -> Self {
Self { coords }
}
#[inline]
fn to_array(&self) -> [T; D] {
self.coords
}
#[inline]
fn get(&self, index: usize) -> Option<T> {
self.coords.get(index).copied()
}
fn validate(&self) -> Result<(), CoordinateValidationError> {
for (index, &coord) in self.coords.iter().enumerate() {
if !coord.is_finite_generic() {
return Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index: index,
coordinate_value: format!("{coord:?}"),
dimension: D,
});
}
}
Ok(())
}
fn hash_coordinate<H: Hasher>(&self, state: &mut H) {
for &coord in &self.coords {
coord.hash_scalar(state);
}
}
fn ordered_equals(&self, other: &Self) -> bool {
self.coords
.iter()
.zip(other.coords.iter())
.all(|(a, b)| a.ordered_eq(b))
}
}
impl<T, const D: usize> Hash for Point<T, D>
where
T: CoordinateScalar,
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash_coordinate(state);
}
}
impl<T, const D: usize> PartialEq for Point<T, D>
where
T: CoordinateScalar,
{
fn eq(&self, other: &Self) -> bool {
self.ordered_equals(other)
}
}
impl<T, const D: usize> Eq for Point<T, D> where T: CoordinateScalar {}
impl<T, const D: usize> PartialOrd for Point<T, D>
where
T: CoordinateScalar,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
for (a, b) in self.coords.iter().zip(other.coords.iter()) {
match a.ordered_partial_cmp(b) {
Some(Ordering::Equal) => {}
other_ordering => return other_ordering,
}
}
Some(Ordering::Equal)
}
}
impl<T, const D: usize> Default for Point<T, D>
where
T: CoordinateScalar,
{
fn default() -> Self {
Self {
coords: [T::default(); D],
}
}
}
impl<T, const D: usize> Serialize for Point<T, D>
where
T: CoordinateScalar,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeTuple;
let mut tuple = serializer.serialize_tuple(D)?;
for coord in &self.coords {
if coord.is_finite_generic() {
tuple.serialize_element(coord)?;
} else if coord.is_nan() {
tuple.serialize_element(&Option::<T>::None)?;
} else if coord.is_infinite() {
if coord.is_sign_positive() {
tuple.serialize_element("Infinity")?;
} else {
tuple.serialize_element("-Infinity")?;
}
} else {
tuple.serialize_element(&Option::<T>::None)?;
}
}
tuple.end()
}
}
#[derive(Deserialize)]
#[serde(untagged)]
enum CoordRepr<T> {
Num(T),
Str(String),
Null,
}
impl<'de, T, const D: usize> Deserialize<'de> for Point<T, D>
where
T: CoordinateScalar,
{
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
where
DE: serde::Deserializer<'de>,
{
struct ArrayVisitor<T, const D: usize>(PhantomData<T>);
impl<'de, T, const D: usize> Visitor<'de> for ArrayVisitor<T, D>
where
T: CoordinateScalar,
{
type Value = Point<T, D>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_fmt(format_args!(
"an array of {D} coordinates (numbers, null, \"Infinity\", \"-Infinity\", \"NaN\", or their case-insensitive variants)"
))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut coords = Vec::with_capacity(D);
for i in 0..D {
let element: CoordRepr<T> = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(i, &self))?;
let coord = match element {
CoordRepr::Num(value) => {
value
}
CoordRepr::Str(s) => {
let sl = s.trim().to_ascii_lowercase();
match sl.as_str() {
"infinity" | "inf" => T::infinity(),
"-infinity" | "-inf" => T::neg_infinity(),
"nan" => T::nan(),
_ => {
return Err(Error::custom(format!(
"Unknown special value: {s}"
)));
}
}
}
CoordRepr::Null => {
T::nan()
}
};
coords.push(coord);
}
let coords_len = coords.len();
let coords_array: [T; D] = coords
.try_into()
.map_err(|_| Error::invalid_length(coords_len, &self))?;
Ok(Point::new(coords_array))
}
}
deserializer.deserialize_tuple(D, ArrayVisitor(PhantomData))
}
}
impl<T, U, const D: usize> TryFrom<[T; D]> for Point<U, D>
where
T: cast::NumCast + fmt::Debug,
U: CoordinateScalar + cast::NumCast,
{
type Error = CoordinateConversionError;
#[inline]
fn try_from(coords: [T; D]) -> Result<Self, Self::Error> {
let mut out: [U; D] = [U::zero(); D];
for (i, c) in coords.into_iter().enumerate() {
let c_debug = format!("{c:?}");
let v: U =
cast::cast(c).ok_or_else(|| CoordinateConversionError::ConversionFailed {
coordinate_index: i,
coordinate_value: c_debug,
from_type: any::type_name::<T>(),
to_type: any::type_name::<U>(),
})?;
if !v.is_finite_generic() {
return Err(CoordinateConversionError::NonFiniteValue {
coordinate_index: i,
coordinate_value: format!("{v:?}"),
});
}
out[i] = v;
}
Ok(Self::new(out))
}
}
impl<T, const D: usize> From<Point<T, D>> for [T; D]
where
T: CoordinateScalar,
{
#[inline]
fn from(point: Point<T, D>) -> [T; D] {
point.to_array()
}
}
impl<T, const D: usize> From<&Point<T, D>> for [T; D]
where
T: CoordinateScalar,
{
#[inline]
fn from(point: &Point<T, D>) -> [T; D] {
point.to_array()
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::mem;
fn get_hash<T: Hash>(value: &T) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
fn test_point_equality_and_hash<T, const D: usize>(
point1: Point<T, D>,
point2: Point<T, D>,
should_be_equal: bool,
) where
T: CoordinateScalar,
Point<T, D>: Hash,
{
if should_be_equal {
assert_eq!(point1, point2);
assert_eq!(get_hash(&point1), get_hash(&point2));
} else {
assert_ne!(point1, point2);
}
}
macro_rules! test_point_across_dimensions {
(creation: $test_name:ident) => {
#[test]
fn $test_name() {
let point_2d = Point::new([1.0, 2.0]);
assert_relative_eq!(point_2d.to_array().as_slice(), [1.0, 2.0].as_slice());
assert_eq!(point_2d.dim(), 2);
let point_3d = Point::new([1.0, 2.0, 3.0]);
assert_relative_eq!(point_3d.to_array().as_slice(), [1.0, 2.0, 3.0].as_slice());
assert_eq!(point_3d.dim(), 3);
let point_4d = Point::new([1.0, 2.0, 3.0, 4.0]);
assert_relative_eq!(
point_4d.to_array().as_slice(),
[1.0, 2.0, 3.0, 4.0].as_slice()
);
assert_eq!(point_4d.dim(), 4);
let point_5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert_relative_eq!(
point_5d.to_array().as_slice(),
[1.0, 2.0, 3.0, 4.0, 5.0].as_slice()
);
assert_eq!(point_5d.dim(), 5);
}
};
(equality: $test_name:ident) => {
#[test]
fn $test_name() {
let p2d_a = Point::new([1.0, 2.0]);
let p2d_b = Point::new([1.0, 2.0]);
let p2d_c = Point::new([1.0, 3.0]);
assert_eq!(p2d_a, p2d_b);
assert_ne!(p2d_a, p2d_c);
let p3d_a = Point::new([1.0, 2.0, 3.0]);
let p3d_b = Point::new([1.0, 2.0, 3.0]);
let p3d_c = Point::new([1.0, 2.0, 4.0]);
assert_eq!(p3d_a, p3d_b);
assert_ne!(p3d_a, p3d_c);
let p4d_a = Point::new([1.0, 2.0, 3.0, 4.0]);
let p4d_b = Point::new([1.0, 2.0, 3.0, 4.0]);
let p4d_c = Point::new([1.0, 2.0, 3.0, 5.0]);
assert_eq!(p4d_a, p4d_b);
assert_ne!(p4d_a, p4d_c);
let p5d_a = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
let p5d_b = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
let p5d_c = Point::new([1.0, 2.0, 3.0, 4.0, 6.0]);
assert_eq!(p5d_a, p5d_b);
assert_ne!(p5d_a, p5d_c);
}
};
(hashing: $test_name:ident) => {
#[test]
fn $test_name() {
let p2d_a = Point::new([1.0, 2.0]);
let p2d_b = Point::new([1.0, 2.0]);
assert_eq!(get_hash(&p2d_a), get_hash(&p2d_b));
let p3d_a = Point::new([1.0, 2.0, 3.0]);
let p3d_b = Point::new([1.0, 2.0, 3.0]);
assert_eq!(get_hash(&p3d_a), get_hash(&p3d_b));
let p4d_a = Point::new([1.0, 2.0, 3.0, 4.0]);
let p4d_b = Point::new([1.0, 2.0, 3.0, 4.0]);
assert_eq!(get_hash(&p4d_a), get_hash(&p4d_b));
let p5d_a = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
let p5d_b = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert_eq!(get_hash(&p5d_a), get_hash(&p5d_b));
}
};
(ordering: $test_name:ident) => {
#[test]
fn $test_name() {
let p2d_a = Point::new([1.0, 2.0]);
let p2d_b = Point::new([1.0, 3.0]);
assert!(p2d_a < p2d_b);
assert!(p2d_b > p2d_a);
let p3d_a = Point::new([1.0, 2.0, 3.0]);
let p3d_b = Point::new([1.0, 2.0, 4.0]);
assert!(p3d_a < p3d_b);
assert!(p3d_b > p3d_a);
let p4d_a = Point::new([1.0, 2.0, 3.0, 4.0]);
let p4d_b = Point::new([1.0, 2.0, 3.0, 5.0]);
assert!(p4d_a < p4d_b);
assert!(p4d_b > p4d_a);
let p5d_a = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
let p5d_b = Point::new([1.0, 2.0, 3.0, 4.0, 6.0]);
assert!(p5d_a < p5d_b);
assert!(p5d_b > p5d_a);
}
};
(validation: $test_name:ident) => {
#[test]
fn $test_name() {
let valid_2d = Point::new([1.0, 2.0]);
assert!(valid_2d.validate().is_ok());
let invalid_2d = Point::new([f64::NAN, 2.0]);
assert!(invalid_2d.validate().is_err());
let valid_3d = Point::new([1.0, 2.0, 3.0]);
assert!(valid_3d.validate().is_ok());
let invalid_3d = Point::new([1.0, f64::INFINITY, 3.0]);
assert!(invalid_3d.validate().is_err());
let valid_4d = Point::new([1.0, 2.0, 3.0, 4.0]);
assert!(valid_4d.validate().is_ok());
let invalid_4d = Point::new([1.0, 2.0, f64::NEG_INFINITY, 4.0]);
assert!(invalid_4d.validate().is_err());
let valid_5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert!(valid_5d.validate().is_ok());
let invalid_5d = Point::new([1.0, 2.0, 3.0, f64::NAN, 5.0]);
assert!(invalid_5d.validate().is_err());
}
};
(serialization: $test_name:ident) => {
#[test]
fn $test_name() {
let p2d = Point::new([1.0, 2.0]);
let json2d = serde_json::to_string(&p2d).unwrap();
let de2d: Point<f64, 2> = serde_json::from_str(&json2d).unwrap();
assert_eq!(p2d, de2d);
let p3d = Point::new([1.0, 2.0, 3.0]);
let json3d = serde_json::to_string(&p3d).unwrap();
let de3d: Point<f64, 3> = serde_json::from_str(&json3d).unwrap();
assert_eq!(p3d, de3d);
let p4d = Point::new([1.0, 2.0, 3.0, 4.0]);
let json4d = serde_json::to_string(&p4d).unwrap();
let de4d: Point<f64, 4> = serde_json::from_str(&json4d).unwrap();
assert_eq!(p4d, de4d);
let p5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
let json5d = serde_json::to_string(&p5d).unwrap();
let de5d: Point<f64, 5> = serde_json::from_str(&json5d).unwrap();
assert_eq!(p5d, de5d);
}
};
(origin: $test_name:ident) => {
#[test]
fn $test_name() {
let origin_2d: Point<f64, 2> = Point::origin();
assert_relative_eq!(origin_2d.to_array().as_slice(), [0.0, 0.0].as_slice());
let origin_3d: Point<f64, 3> = Point::origin();
assert_relative_eq!(origin_3d.to_array().as_slice(), [0.0, 0.0, 0.0].as_slice());
let origin_4d: Point<f64, 4> = Point::origin();
assert_relative_eq!(
origin_4d.to_array().as_slice(),
[0.0, 0.0, 0.0, 0.0].as_slice()
);
let origin_5d: Point<f64, 5> = Point::origin();
assert_relative_eq!(
origin_5d.to_array().as_slice(),
[0.0, 0.0, 0.0, 0.0, 0.0].as_slice()
);
}
};
(hashmap: $test_name:ident) => {
#[test]
fn $test_name() {
let mut map2d: HashMap<Point<f64, 2>, i32> = HashMap::new();
let p2d = Point::new([1.0, 2.0]);
map2d.insert(p2d, 42);
assert_eq!(map2d.get(&Point::new([1.0, 2.0])), Some(&42));
let mut map3d: HashMap<Point<f64, 3>, i32> = HashMap::new();
let p3d = Point::new([1.0, 2.0, 3.0]);
map3d.insert(p3d, 42);
assert_eq!(map3d.get(&Point::new([1.0, 2.0, 3.0])), Some(&42));
let mut map4d: HashMap<Point<f64, 4>, i32> = HashMap::new();
let p4d = Point::new([1.0, 2.0, 3.0, 4.0]);
map4d.insert(p4d, 42);
assert_eq!(map4d.get(&Point::new([1.0, 2.0, 3.0, 4.0])), Some(&42));
let mut map5d: HashMap<Point<f64, 5>, i32> = HashMap::new();
let p5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
map5d.insert(p5d, 42);
assert_eq!(map5d.get(&Point::new([1.0, 2.0, 3.0, 4.0, 5.0])), Some(&42));
}
};
(copy: $test_name:ident) => {
#[test]
fn $test_name() {
let p2d_original = Point::new([1.0, 2.0]);
let p2d_copy = p2d_original;
assert_eq!(p2d_original, p2d_copy);
assert_relative_eq!(
p2d_original.to_array().as_slice(),
p2d_copy.to_array().as_slice()
);
let p3d_original = Point::new([1.0, 2.0, 3.0]);
let p3d_copy = p3d_original;
assert_eq!(p3d_original, p3d_copy);
let p4d_original = Point::new([1.0, 2.0, 3.0, 4.0]);
let p4d_copy = p4d_original;
assert_eq!(p4d_original, p4d_copy);
let p5d_original = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
let p5d_copy = p5d_original;
assert_eq!(p5d_original, p5d_copy);
}
};
}
test_point_across_dimensions!(creation: point_creation_dimensional);
test_point_across_dimensions!(equality: point_equality_dimensional);
test_point_across_dimensions!(hashing: point_hashing_dimensional);
test_point_across_dimensions!(ordering: point_ordering_dimensional);
test_point_across_dimensions!(validation: point_validation_dimensional);
test_point_across_dimensions!(serialization: point_serialization_dimensional);
test_point_across_dimensions!(origin: point_origin_dimensional);
test_point_across_dimensions!(hashmap: point_hashmap_dimensional);
test_point_across_dimensions!(copy: point_copy_dimensional);
#[test]
fn point_default() {
let point: Point<f64, 4> = Point::default();
let coords = point.to_array();
assert_relative_eq!(
coords.as_slice(),
[0.0, 0.0, 0.0, 0.0].as_slice(),
epsilon = 1e-9
);
println!("Default: {point:?}");
}
#[test]
fn point_coords() {
let point = Point::new([1.0, 2.0, 3.0]);
let coords_ref = point.coords();
assert_relative_eq!(
coords_ref.as_slice(),
[1.0, 2.0, 3.0].as_slice(),
epsilon = 1e-9
);
assert_eq!(coords_ref.len(), 3);
assert_relative_eq!(coords_ref[0], 1.0, epsilon = 1e-9);
assert_relative_eq!(coords_ref[1], 2.0, epsilon = 1e-9);
assert_relative_eq!(coords_ref[2], 3.0, epsilon = 1e-9);
let point_2d = Point::new([5.5, -2.5]);
assert_relative_eq!(
point_2d.coords().as_slice(),
[5.5, -2.5].as_slice(),
epsilon = 1e-9
);
let point_4d = Point::new([1.0, 2.0, 3.0, 4.0]);
assert_relative_eq!(
point_4d.coords().as_slice(),
[1.0, 2.0, 3.0, 4.0].as_slice(),
epsilon = 1e-9
);
let point_f32 = Point::new([1.0f32, 2.0f32, 3.0f32]);
assert_relative_eq!(
point_f32.coords().as_slice(),
[1.0f32, 2.0f32, 3.0f32].as_slice(),
epsilon = 1e-6
);
let point_5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert_relative_eq!(
point_5d.coords().as_slice(),
[1.0, 2.0, 3.0, 4.0, 5.0].as_slice(),
epsilon = 1e-9
);
assert_eq!(point_5d.coords().len(), 5);
println!("coords() provides efficient read-only access to coordinates");
}
#[test]
fn point_from_array_f32_to_f64() {
let coords = [1.5f32, 2.5f32, 3.5f32, 4.5f32];
let point: Point<f64, 4> = Point::new(coords.map(Into::into));
let result_coords = point.to_array();
assert_relative_eq!(
result_coords.as_slice(),
[1.5, 2.5, 3.5, 4.5].as_slice(),
epsilon = 1e-9
);
assert_eq!(point.dim(), 4);
}
#[test]
fn point_type_conversions() {
let coords_f32 = [1.5f32, 2.5f32, 3.5f32];
let point_f32: Point<f32, 3> = Point::new(coords_f32);
let result_f32 = point_f32.to_array();
assert_relative_eq!(
result_f32.as_slice(),
[1.5f32, 2.5f32, 3.5f32].as_slice(),
epsilon = 1e-9
);
let coords_f32_upcast = [1.5f32, 2.5f32];
let point_f64: Point<f64, 2> = Point::new(coords_f32_upcast.map(Into::into));
let result_f64 = point_f64.to_array();
assert_relative_eq!(
result_f64.as_slice(),
[1.5f64, 2.5f64].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_with_f32() {
let point: Point<f32, 2> = Point::new([1.5, 2.5]);
let coords = point.to_array();
assert_relative_eq!(coords.as_slice(), [1.5, 2.5].as_slice(), epsilon = 1e-9);
assert_eq!(point.dim(), 2);
let origin: Point<f32, 2> = Point::origin();
let origin_coords = origin.to_array();
assert_relative_eq!(
origin_coords.as_slice(),
[0.0, 0.0].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_debug_format() {
let point = Point::new([1.0, 2.0, 3.0]);
let debug_str = format!("{point:?}");
assert!(debug_str.contains("Point"));
assert!(debug_str.contains("coords"));
assert!(debug_str.contains("1.0"));
assert!(debug_str.contains("2.0"));
assert!(debug_str.contains("3.0"));
}
#[test]
fn point_eq_trait() {
let point1 = Point::new([1.0, 2.0, 3.0]);
let point2 = Point::new([1.0, 2.0, 3.0]);
let point3 = Point::new([1.0, 2.0, 4.0]);
assert_eq!(point1, point1); assert_eq!(point1, point2); assert_eq!(point2, point1); assert_ne!(point1, point3);
assert_ne!(point3, point1);
}
#[test]
fn point_comprehensive_serialization() {
let point_3d = Point::new([1.0, 2.0, 3.0]);
let serialized_3d = serde_json::to_string(&point_3d).unwrap();
let deserialized_3d: Point<f64, 3> = serde_json::from_str(&serialized_3d).unwrap();
assert_eq!(point_3d, deserialized_3d);
let point_2d = Point::new([10.5, -5.3]);
let serialized_2d = serde_json::to_string(&point_2d).unwrap();
let deserialized_2d: Point<f64, 2> = serde_json::from_str(&serialized_2d).unwrap();
assert_eq!(point_2d, deserialized_2d);
let point_1d = Point::new([42.0]);
let serialized_1d = serde_json::to_string(&point_1d).unwrap();
let deserialized_1d: Point<f64, 1> = serde_json::from_str(&serialized_1d).unwrap();
assert_eq!(point_1d, deserialized_1d);
let point_large = Point::new([1e100, -1e100, 0.0]);
let serialized_large = serde_json::to_string(&point_large).unwrap();
let deserialized_large: Point<f64, 3> = serde_json::from_str(&serialized_large).unwrap();
assert_eq!(point_large, deserialized_large);
let point_small = Point::new([1e-100, -1e-100, 0.0]);
let serialized_small = serde_json::to_string(&point_small).unwrap();
let deserialized_small: Point<f64, 3> = serde_json::from_str(&serialized_small).unwrap();
assert_eq!(point_small, deserialized_small);
}
#[test]
fn point_negative_coordinates() {
let point = Point::new([-1.0, -2.0, -3.0]);
assert_relative_eq!(
point.to_array().as_slice(),
[-1.0, -2.0, -3.0].as_slice(),
epsilon = 1e-9
);
assert_eq!(point.dim(), 3);
let mixed_point = Point::new([1.0, -2.0, 3.0, -4.0]);
assert_relative_eq!(
mixed_point.to_array().as_slice(),
[1.0, -2.0, 3.0, -4.0].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_zero_coordinates() {
let zero_point = Point::new([0.0, 0.0, 0.0]);
let origin: Point<f64, 3> = Point::origin();
assert_eq!(zero_point, origin);
assert_relative_eq!(
zero_point.to_array().as_slice(),
[0.0, 0.0, 0.0].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_large_coordinates() {
let large_point = Point::new([1e6, 2e6, 3e6]);
let coords = large_point.to_array();
assert_relative_eq!(
coords.as_slice(),
[1_000_000.0, 2_000_000.0, 3_000_000.0].as_slice(),
epsilon = 1e-9
);
assert_eq!(large_point.dim(), 3);
}
#[test]
fn point_small_coordinates() {
let small_point = Point::new([1e-6, 2e-6, 3e-6]);
let coords = small_point.to_array();
assert_relative_eq!(
coords.as_slice(),
[0.000_001, 0.000_002, 0.000_003].as_slice(),
epsilon = 1e-9
);
assert_eq!(small_point.dim(), 3);
}
#[test]
fn point_ordering_edge_cases() {
let point1 = Point::new([1.0, 2.0]);
let point2 = Point::new([1.0, 2.0]);
assert_ne!(point1.partial_cmp(&point2), Some(Ordering::Less));
assert_ne!(point2.partial_cmp(&point1), Some(Ordering::Less));
assert!(point1 <= point2);
assert!(point2 <= point1);
assert!(point1 >= point2);
assert!(point2 >= point1);
}
#[test]
fn point_eq_different_types() {
let point_f64_1 = Point::new([1.0, 2.0]);
let point_f64_2 = Point::new([1.0, 2.0]);
let point_f64_3 = Point::new([1.0, 2.1]);
assert_eq!(point_f64_1, point_f64_2);
assert_ne!(point_f64_1, point_f64_3);
let point_f32_1 = Point::new([1.5f32, 2.5f32]);
let point_f32_2 = Point::new([1.5f32, 2.5f32]);
let point_f32_3 = Point::new([1.5f32, 2.6f32]);
assert_eq!(point_f32_1, point_f32_2);
assert_ne!(point_f32_1, point_f32_3);
}
#[test]
fn point_hash_consistency_floating_point() {
let point1 = Point::new([1.0, 2.0, 3.5]);
let point2 = Point::new([1.0, 2.0, 3.5]);
test_point_equality_and_hash(point1, point2, true);
let point_f32_1 = Point::new([1.5f32, 2.5f32]);
let point_f32_2 = Point::new([1.5f32, 2.5f32]);
test_point_equality_and_hash(point_f32_1, point_f32_2, true);
}
#[test]
fn point_implicit_conversion_to_coordinates() {
let point: Point<f64, 3> = Point::new([1.0, 2.0, 3.0]);
let coords_owned: [f64; 3] = point.into();
assert_relative_eq!(coords_owned.as_slice(), [1.0, 2.0, 3.0].as_slice());
let point_ref: Point<f64, 3> = Point::new([4.0, 5.0, 6.0]);
let coords_ref: [f64; 3] = (&point_ref).into();
assert_relative_eq!(coords_ref.as_slice(), [4.0, 5.0, 6.0].as_slice());
assert_relative_eq!(point_ref.to_array().as_slice(), [4.0, 5.0, 6.0].as_slice());
}
#[test]
fn point_is_valid_f64() {
let valid_point = Point::new([1.0, 2.0, 3.0]);
assert!(valid_point.validate().is_ok());
let valid_negative = Point::new([-1.0, -2.0, -3.0]);
assert!(valid_negative.validate().is_ok());
let valid_zero = Point::new([0.0, 0.0, 0.0]);
assert!(valid_zero.validate().is_ok());
let valid_mixed = Point::new([1.0, -2.5, 0.0, 42.7]);
assert!(valid_mixed.validate().is_ok());
let invalid_nan_single = Point::new([1.0, f64::NAN, 3.0]);
assert!(invalid_nan_single.validate().is_err());
let invalid_nan_all = Point::new([f64::NAN, f64::NAN, f64::NAN]);
assert!(invalid_nan_all.validate().is_err());
let invalid_nan_first = Point::new([f64::NAN, 2.0, 3.0]);
assert!(invalid_nan_first.validate().is_err());
let invalid_nan_last = Point::new([1.0, 2.0, f64::NAN]);
assert!(invalid_nan_last.validate().is_err());
let invalid_pos_inf = Point::new([1.0, f64::INFINITY, 3.0]);
assert!(invalid_pos_inf.validate().is_err());
let invalid_neg_inf = Point::new([1.0, f64::NEG_INFINITY, 3.0]);
assert!(invalid_neg_inf.validate().is_err());
let invalid_both_inf = Point::new([f64::INFINITY, f64::NEG_INFINITY]);
assert!(invalid_both_inf.validate().is_err());
let invalid_nan_and_inf = Point::new([f64::NAN, f64::INFINITY, 1.0]);
assert!(invalid_nan_and_inf.validate().is_err());
}
#[test]
fn point_is_valid_f32() {
let valid_point = Point::new([1.0f32, 2.0f32, 3.0f32]);
assert!(valid_point.validate().is_ok());
let valid_negative = Point::new([-1.5f32, -2.5f32]);
assert!(valid_negative.validate().is_ok());
let valid_zero = Point::new([0.0f32]);
assert!(valid_zero.validate().is_ok());
let invalid_nan = Point::new([1.0f32, f32::NAN]);
assert!(invalid_nan.validate().is_err());
let invalid_all_nan = Point::new([f32::NAN, f32::NAN, f32::NAN, f32::NAN]);
assert!(invalid_all_nan.validate().is_err());
let invalid_pos_inf = Point::new([f32::INFINITY, 2.0f32]);
assert!(invalid_pos_inf.validate().is_err());
let invalid_neg_inf = Point::new([1.0f32, f32::NEG_INFINITY]);
assert!(invalid_neg_inf.validate().is_err());
let valid_small = Point::new([f32::MIN_POSITIVE, -f32::MIN_POSITIVE]);
assert!(valid_small.validate().is_ok());
let valid_large = Point::new([f32::MAX, -f32::MAX]);
assert!(valid_large.validate().is_ok());
}
#[test]
fn point_is_valid_different_dimensions() {
let valid_1d_f64 = Point::new([42.0]);
assert!(valid_1d_f64.validate().is_ok());
let invalid_1d_nan = Point::new([f64::NAN]);
assert!(invalid_1d_nan.validate().is_err());
let valid_2d = Point::new([1.0, 2.0]);
assert!(valid_2d.validate().is_ok());
let invalid_2d = Point::new([1.0, f64::INFINITY]);
assert!(invalid_2d.validate().is_err());
let valid_5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert!(valid_5d.validate().is_ok());
let invalid_5d = Point::new([1.0, 2.0, f64::NAN, 4.0, 5.0]);
assert!(invalid_5d.validate().is_err());
let valid_10d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]);
assert!(valid_10d.validate().is_ok());
let invalid_10d = Point::new([
1.0,
2.0,
3.0,
4.0,
5.0,
f64::NEG_INFINITY,
7.0,
8.0,
9.0,
10.0,
]);
assert!(invalid_10d.validate().is_err());
}
#[test]
fn point_is_valid_edge_cases() {
let tiny_valid = Point::new([f64::MIN_POSITIVE, -f64::MIN_POSITIVE, 0.0]);
assert!(tiny_valid.validate().is_ok());
let large_valid = Point::new([f64::MAX, -f64::MAX]);
assert!(large_valid.validate().is_ok());
let subnormal = f64::MIN_POSITIVE / 2.0;
let subnormal_point = Point::new([subnormal, -subnormal]);
assert!(subnormal_point.validate().is_ok());
let zero_point = Point::new([0.0, -0.0]);
assert!(zero_point.validate().is_ok());
let mixed_invalid = Point::new([1.0, 2.0, 3.0, f64::NAN, 5.0]);
assert!(mixed_invalid.validate().is_err());
let one_invalid = Point::new([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, f64::INFINITY]);
assert!(one_invalid.validate().is_err());
}
#[test]
fn point_special_values_hash_consistency() {
let point_nan1 = Point::new([f64::NAN, 2.0]);
let point_nan2 = Point::new([f64::NAN, 2.0]);
assert_eq!(get_hash(&point_nan1), get_hash(&point_nan2));
let point_pos_inf1 = Point::new([f64::INFINITY, 2.0]);
let point_pos_inf2 = Point::new([f64::INFINITY, 2.0]);
assert_eq!(get_hash(&point_pos_inf1), get_hash(&point_pos_inf2));
let point_neg_inf1 = Point::new([f64::NEG_INFINITY, 2.0]);
let point_neg_inf2 = Point::new([f64::NEG_INFINITY, 2.0]);
assert_eq!(get_hash(&point_neg_inf1), get_hash(&point_neg_inf2));
assert_ne!(get_hash(&point_pos_inf1), get_hash(&point_neg_inf1));
let mut map: HashMap<Point<f64, 2>, i32> = HashMap::new();
let point_nan_lookup = Point::new([f64::NAN, 2.0]);
let point_inf_lookup = Point::new([f64::INFINITY, 2.0]);
map.insert(point_nan1, 100);
map.insert(point_pos_inf1, 200);
assert_eq!(map.get(&point_nan_lookup), Some(&100));
assert_eq!(map.get(&point_inf_lookup), Some(&200));
assert_eq!(map.len(), 2);
let point_f32_nan1 = Point::new([f32::NAN, 1.0f32]);
let point_f32_nan2 = Point::new([f32::NAN, 1.0f32]);
assert_eq!(get_hash(&point_f32_nan1), get_hash(&point_f32_nan2));
}
#[test]
fn point_nan_equality_comparison() {
let point_nan1 = Point::new([f64::NAN, 2.0, 3.0]);
let point_nan2 = Point::new([f64::NAN, 2.0, 3.0]);
let point_nan3 = Point::new([f64::NAN, f64::NAN, f64::NAN]);
let point_nan4 = Point::new([f64::NAN, f64::NAN, f64::NAN]);
assert_eq!(point_nan1, point_nan2);
assert_eq!(point_nan3, point_nan4);
let point_nan_diff1 = Point::new([f64::NAN, 2.0, 3.0]);
let point_nan_diff2 = Point::new([1.0, f64::NAN, 3.0]);
assert_ne!(point_nan_diff1, point_nan_diff2);
let point_f32_nan1 = Point::new([f32::NAN, 1.5f32]);
let point_f32_nan2 = Point::new([f32::NAN, 1.5f32]);
assert_eq!(point_f32_nan1, point_f32_nan2);
let point_mixed1 = Point::new([1.0, f64::NAN, 3.0, 4.0]);
let point_mixed2 = Point::new([1.0, f64::NAN, 3.0, 4.0]);
let point_mixed3 = Point::new([1.0, f64::NAN, 3.0, 5.0]);
assert_eq!(point_mixed1, point_mixed2);
assert_ne!(point_mixed1, point_mixed3);
}
#[test]
fn point_nan_vs_normal_comparison() {
let point_normal = Point::new([1.0, 2.0, 3.0]);
let point_nan = Point::new([f64::NAN, 2.0, 3.0]);
let point_nan_all = Point::new([f64::NAN, f64::NAN, f64::NAN]);
assert_ne!(point_normal, point_nan);
assert_ne!(point_normal, point_nan_all);
assert_ne!(point_nan, point_normal);
assert_ne!(point_nan_all, point_normal);
let point_f32_normal = Point::new([1.0f32, 2.0f32]);
let point_f32_nan = Point::new([f32::NAN, 2.0f32]);
assert_ne!(point_f32_normal, point_f32_nan);
assert_ne!(point_f32_nan, point_f32_normal);
}
#[test]
fn point_infinity_comparison() {
let point_pos_inf1 = Point::new([f64::INFINITY, 2.0]);
let point_pos_inf2 = Point::new([f64::INFINITY, 2.0]);
assert_eq!(point_pos_inf1, point_pos_inf2);
let point_neg_inf1 = Point::new([f64::NEG_INFINITY, 2.0]);
let point_neg_inf2 = Point::new([f64::NEG_INFINITY, 2.0]);
assert_eq!(point_neg_inf1, point_neg_inf2);
assert_ne!(point_pos_inf1, point_neg_inf1);
let point_normal = Point::new([1.0, 2.0]);
assert_ne!(point_pos_inf1, point_normal);
assert_ne!(point_neg_inf1, point_normal);
let point_f32_pos_inf1 = Point::new([f32::INFINITY]);
let point_f32_pos_inf2 = Point::new([f32::INFINITY]);
let point_f32_neg_inf = Point::new([f32::NEG_INFINITY]);
assert_eq!(point_f32_pos_inf1, point_f32_pos_inf2);
assert_ne!(point_f32_pos_inf1, point_f32_neg_inf);
}
#[test]
fn point_nan_infinity_mixed_comparison() {
let point_nan_inf1 = Point::new([f64::NAN, f64::INFINITY, 1.0]);
let point_nan_inf2 = Point::new([f64::NAN, f64::INFINITY, 1.0]);
let point_nan_inf3 = Point::new([f64::NAN, f64::NEG_INFINITY, 1.0]);
assert_eq!(point_nan_inf1, point_nan_inf2);
assert_ne!(point_nan_inf1, point_nan_inf3);
let point_all_special = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, f64::NAN]);
let point_all_special_copy =
Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, f64::NAN]);
let point_all_special_diff =
Point::new([f64::NAN, f64::NEG_INFINITY, f64::INFINITY, f64::NAN]);
assert_eq!(point_all_special, point_all_special_copy);
assert_ne!(point_all_special, point_all_special_diff);
}
#[test]
fn point_nan_equality_properties() {
let point_nan = Point::new([f64::NAN, f64::NAN, f64::NAN]);
assert_eq!(point_nan, point_nan);
let point_mixed = Point::new([1.0, f64::NAN, 3.0, f64::INFINITY]);
assert_eq!(point_mixed, point_mixed);
let point_a = Point::new([f64::NAN, 2.0, f64::INFINITY]);
let point_b = Point::new([f64::NAN, 2.0, f64::INFINITY]);
assert_eq!(point_a, point_b);
assert_eq!(point_b, point_a);
let point_c = Point::new([f64::NAN, 2.0, f64::INFINITY]);
assert_eq!(point_a, point_b);
assert_eq!(point_b, point_c);
assert_eq!(point_a, point_c);
let point_f32_a = Point::new([f32::NAN, 1.0f32, f32::NEG_INFINITY]);
let point_f32_b = Point::new([f32::NAN, 1.0f32, f32::NEG_INFINITY]);
assert_eq!(point_f32_a, point_f32_b);
assert_eq!(point_f32_b, point_f32_a);
}
#[test]
fn point_nan_different_bit_patterns() {
let nan1 = f64::NAN;
#[expect(clippy::zero_divided_by_zero)]
let nan2 = 0.0f64 / 0.0f64; let nan3 = f64::INFINITY - f64::INFINITY;
assert!(nan1.is_nan());
assert!(nan2.is_nan());
assert!(nan3.is_nan());
let point1 = Point::new([nan1, 1.0]);
let point2 = Point::new([nan2, 1.0]);
let point3 = Point::new([nan3, 1.0]);
assert_eq!(point1, point2);
assert_eq!(point2, point3);
assert_eq!(point1, point3);
let f32_nan1 = f32::NAN;
#[expect(clippy::zero_divided_by_zero)]
let f32_nan2 = 0.0f32 / 0.0f32;
let point_f32_1 = Point::new([f32_nan1]);
let point_f32_2 = Point::new([f32_nan2]);
assert_eq!(point_f32_1, point_f32_2);
}
#[test]
fn point_nan_in_different_dimensions() {
let point_1d_a = Point::new([f64::NAN]);
let point_1d_b = Point::new([f64::NAN]);
assert_eq!(point_1d_a, point_1d_b);
let point_2d_a = Point::new([f64::NAN, f64::NAN]);
let point_2d_b = Point::new([f64::NAN, f64::NAN]);
assert_eq!(point_2d_a, point_2d_b);
let point_3d_a = Point::new([f64::NAN, 1.0, f64::NAN]);
let point_3d_b = Point::new([f64::NAN, 1.0, f64::NAN]);
assert_eq!(point_3d_a, point_3d_b);
let point_5d_a = Point::new([f64::NAN, 1.0, f64::NAN, f64::INFINITY, f64::NAN]);
let point_5d_b = Point::new([f64::NAN, 1.0, f64::NAN, f64::INFINITY, f64::NAN]);
assert_eq!(point_5d_a, point_5d_b);
let point_10d_a = Point::new([
f64::NAN,
1.0,
f64::NAN,
f64::INFINITY,
f64::NEG_INFINITY,
0.0,
-0.0,
f64::NAN,
42.0,
f64::NAN,
]);
let point_10d_b = Point::new([
f64::NAN,
1.0,
f64::NAN,
f64::INFINITY,
f64::NEG_INFINITY,
0.0,
-0.0,
f64::NAN,
42.0,
f64::NAN,
]);
assert_eq!(point_10d_a, point_10d_b);
}
#[test]
fn point_nan_zero_comparison() {
let point_nan = Point::new([f64::NAN, f64::NAN]);
let point_pos_zero = Point::new([0.0, 0.0]);
let point_neg_zero = Point::new([-0.0, -0.0]);
let point_mixed_zero = Point::new([0.0, -0.0]);
assert_ne!(point_nan, point_pos_zero);
assert_ne!(point_nan, point_neg_zero);
assert_ne!(point_nan, point_mixed_zero);
assert_eq!(point_pos_zero, point_neg_zero);
assert_eq!(point_pos_zero, point_mixed_zero);
assert_eq!(point_neg_zero, point_mixed_zero);
let point_f32_nan = Point::new([f32::NAN]);
let point_f32_zero = Point::new([0.0f32]);
let point_f32_neg_zero = Point::new([-0.0f32]);
assert_ne!(point_f32_nan, point_f32_zero);
assert_ne!(point_f32_nan, point_f32_neg_zero);
assert_eq!(point_f32_zero, point_f32_neg_zero);
}
#[test]
#[expect(clippy::cast_precision_loss)]
fn point_extreme_dimensions() {
let coords_20d = [1.0; 20];
let point_20d = Point::new(coords_20d);
assert_eq!(point_20d.dim(), 20);
assert_relative_eq!(point_20d.to_array().as_slice(), coords_20d.as_slice());
assert!(point_20d.validate().is_ok());
let coords_25d = [2.5; 25];
let point_25d = Point::new(coords_25d);
assert_eq!(point_25d.dim(), 25);
assert_relative_eq!(point_25d.to_array().as_slice(), coords_25d.as_slice());
assert!(point_25d.validate().is_ok());
let mut coords_32d = [0.0; 32];
for (i, coord) in coords_32d.iter_mut().enumerate() {
*coord = i as f64;
}
let point_32d = Point::new(coords_32d);
assert_eq!(point_32d.dim(), 32);
assert_relative_eq!(point_32d.to_array().as_slice(), coords_32d.as_slice());
assert!(point_32d.validate().is_ok());
let mut coords_with_nan = [1.0; 25];
coords_with_nan[12] = f64::NAN;
let point_with_nan = Point::new(coords_with_nan);
assert!(point_with_nan.validate().is_err());
let point_20d_copy = Point::new([1.0; 20]);
assert_eq!(point_20d, point_20d_copy);
let coords_30d_a = [std::f64::consts::PI; 30];
let coords_30d_b = [std::f64::consts::PI; 30];
let point_30d_a = Point::new(coords_30d_a);
let point_30d_b = Point::new(coords_30d_b);
assert_eq!(point_30d_a, point_30d_b);
assert!(point_30d_a.validate().is_ok());
}
#[test]
fn point_boundary_numeric_values() {
let large_point = Point::new([f64::MAX, f64::MAX / 2.0, 1e308]);
assert!(large_point.validate().is_ok());
assert_relative_eq!(large_point.to_array()[0], f64::MAX);
let small_point = Point::new([f64::MIN, f64::MIN_POSITIVE, 1e-308]);
assert!(small_point.validate().is_ok());
let subnormal = f64::MIN_POSITIVE / 2.0;
let subnormal_point = Point::new([subnormal, -subnormal, 0.0]);
assert!(subnormal_point.validate().is_ok());
let extreme_f32_point = Point::new([f32::MAX, f32::MIN, f32::MIN_POSITIVE]);
assert!(extreme_f32_point.validate().is_ok());
}
#[test]
fn point_clone_and_copy_semantics() {
let original = Point::new([1.0, 2.0, 3.0]);
#[expect(clippy::clone_on_copy)]
let cloned = original.clone();
assert_relative_eq!(original.to_array().as_slice(), cloned.to_array().as_slice());
let copied = original; assert_eq!(original, copied);
assert_eq!(original.dim(), 3);
assert_eq!(copied.dim(), 3);
let f32_point = Point::new([1.5f32, 2.5f32, 3.5f32, 4.5f32]);
let f32_copied = f32_point;
assert_eq!(f32_point, f32_copied);
}
#[test]
fn point_partial_ord_comprehensive() {
let point_a = Point::new([1.0, 2.0, 3.0]);
let point_b = Point::new([1.0, 2.0, 4.0]); let point_c = Point::new([1.0, 3.0, 0.0]); let point_d = Point::new([2.0, 0.0, 0.0]);
assert!(point_a < point_b);
assert!(point_b > point_a);
assert!(point_a <= point_b);
assert!(point_b >= point_a);
assert!(point_a < point_c);
assert!(point_a < point_d);
assert!(point_c < point_d);
assert_eq!(point_a.partial_cmp(&point_b), Some(Ordering::Less));
assert_eq!(point_b.partial_cmp(&point_a), Some(Ordering::Greater));
assert_eq!(point_a.partial_cmp(&point_a), Some(Ordering::Equal));
let neg_point_a = Point::new([-1.0, -2.0]);
let neg_point_b = Point::new([-1.0, -1.0]);
assert!(neg_point_a < neg_point_b);
let mixed_a = Point::new([-1.0, 2.0]);
let mixed_b = Point::new([1.0, -2.0]);
assert!(mixed_a < mixed_b);
let zero_a = Point::new([0.0, 0.0]);
let zero_b = Point::new([0.0, 0.0]);
assert_eq!(zero_a.partial_cmp(&zero_b), Some(Ordering::Equal));
let inf_point = Point::new([f64::INFINITY]);
let normal_point = Point::new([1.0]);
assert!(normal_point < inf_point);
}
#[test]
fn point_partial_ord_special_values() {
let point_nan1 = Point::new([f64::NAN, 1.0]);
let point_nan2 = Point::new([f64::NAN, 1.0]);
let point_normal = Point::new([1.0, 1.0]);
assert_eq!(point_nan1.partial_cmp(&point_nan2), Some(Ordering::Equal));
assert_eq!(
point_nan1.partial_cmp(&point_normal),
Some(Ordering::Greater)
);
assert_eq!(point_normal.partial_cmp(&point_nan1), Some(Ordering::Less));
let point_inf = Point::new([f64::INFINITY, 1.0]);
let point_neg_inf = Point::new([f64::NEG_INFINITY, 1.0]);
let point_normal2 = Point::new([2.0, 1.0]);
assert_eq!(
point_inf.partial_cmp(&point_normal2),
Some(Ordering::Greater)
);
assert_eq!(point_normal2.partial_cmp(&point_inf), Some(Ordering::Less));
assert_eq!(
point_neg_inf.partial_cmp(&point_normal2),
Some(Ordering::Less)
);
assert_eq!(
point_normal2.partial_cmp(&point_neg_inf),
Some(Ordering::Greater)
);
assert_eq!(
point_inf.partial_cmp(&point_neg_inf),
Some(Ordering::Greater)
);
assert_eq!(point_neg_inf.partial_cmp(&point_inf), Some(Ordering::Less));
assert_eq!(point_nan1.partial_cmp(&point_inf), Some(Ordering::Greater));
assert_eq!(point_inf.partial_cmp(&point_nan1), Some(Ordering::Less));
assert!(point_normal < point_inf); assert!(point_normal2 < point_inf); assert!(point_neg_inf < point_normal); assert!(point_inf > point_normal); assert!(point_nan1 > point_normal); assert!(point_nan1 > point_inf);
let point_mixed1 = Point::new([1.0, f64::NAN]);
let point_mixed2 = Point::new([1.0, 2.0]);
assert_eq!(
point_mixed1.partial_cmp(&point_mixed2),
Some(Ordering::Greater)
);
let point_mixed3 = Point::new([f64::NEG_INFINITY, 2.0]);
let point_mixed4 = Point::new([1.0, 2.0]);
assert_eq!(
point_mixed3.partial_cmp(&point_mixed4),
Some(Ordering::Less)
);
let point_f32_nan = Point::new([f32::NAN, 1.0f32]);
let point_f32_normal = Point::new([1.0f32, 1.0f32]);
assert_eq!(
point_f32_nan.partial_cmp(&point_f32_normal),
Some(Ordering::Greater)
);
assert!(point_f32_nan > point_f32_normal);
}
#[test]
fn point_memory_layout_and_size() {
assert_eq!(mem::size_of::<Point<f64, 3>>(), mem::size_of::<[f64; 3]>());
assert_eq!(mem::size_of::<Point<f32, 4>>(), mem::size_of::<[f32; 4]>());
assert_eq!(
mem::align_of::<Point<f64, 3>>(),
mem::align_of::<[f64; 3]>()
);
assert_eq!(mem::size_of::<Point<f64, 1>>(), 8); assert_eq!(mem::size_of::<Point<f64, 2>>(), 16); assert_eq!(mem::size_of::<Point<f64, 10>>(), 80);
assert_eq!(mem::size_of::<Point<f32, 1>>(), 4); assert_eq!(mem::size_of::<Point<f32, 2>>(), 8); }
#[test]
fn point_zero_dimensional() {
let point_0d: Point<f64, 0> = Point::new([]);
assert_eq!(point_0d.dim(), 0);
assert_relative_eq!(point_0d.to_array().as_slice(), ([] as [f64; 0]).as_slice());
assert!(point_0d.validate().is_ok());
let point_0d_2: Point<f64, 0> = Point::new([]);
assert_eq!(point_0d, point_0d_2);
let hash_0d = get_hash(&point_0d);
let hash_0d_2 = get_hash(&point_0d_2);
assert_eq!(hash_0d, hash_0d_2);
let origin_0d: Point<f64, 0> = Point::origin();
assert_eq!(origin_0d, point_0d);
}
#[test]
fn point_serialize_nan_infinity_comprehensive() {
let point_nan_single = Point::new([f64::NAN, 1.0, 2.0]);
let json_nan_single = serde_json::to_string(&point_nan_single).unwrap();
assert_eq!(json_nan_single, "[null,1.0,2.0]");
let point_nan_multiple = Point::new([f64::NAN, f64::NAN, 1.0]);
let json_nan_multiple = serde_json::to_string(&point_nan_multiple).unwrap();
assert_eq!(json_nan_multiple, "[null,null,1.0]");
let point_all_nan = Point::new([f64::NAN, f64::NAN]);
let json_all_nan = serde_json::to_string(&point_all_nan).unwrap();
assert_eq!(json_all_nan, "[null,null]");
let point_pos_inf = Point::new([f64::INFINITY, 1.0]);
let json_pos_inf = serde_json::to_string(&point_pos_inf).unwrap();
assert_eq!(json_pos_inf, "[\"Infinity\",1.0]");
let point_neg_inf = Point::new([1.0, f64::NEG_INFINITY]);
let json_neg_inf = serde_json::to_string(&point_neg_inf).unwrap();
assert_eq!(json_neg_inf, "[1.0,\"-Infinity\"]");
let point_mixed = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 1.0]);
let json_mixed = serde_json::to_string(&point_mixed).unwrap();
assert_eq!(json_mixed, "[null,\"Infinity\",\"-Infinity\",1.0]");
let point_all_special = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY]);
let json_all_special = serde_json::to_string(&point_all_special).unwrap();
assert_eq!(json_all_special, "[null,\"Infinity\",\"-Infinity\"]");
}
#[test]
fn point_serialize_f32_nan_infinity() {
let point_f32_nan = Point::new([f32::NAN, 1.0f32]);
let json_f32_nan = serde_json::to_string(&point_f32_nan).unwrap();
assert_eq!(json_f32_nan, "[null,1.0]");
let point_f32_inf = Point::new([f32::INFINITY, f32::NEG_INFINITY]);
let json_f32_inf = serde_json::to_string(&point_f32_inf).unwrap();
assert_eq!(json_f32_inf, "[\"Infinity\",\"-Infinity\"]");
}
#[test]
fn point_deserialize_null_maps_to_nan() {
let json = "[null,1.0,2.0]";
let p: Point<f64, 3> = serde_json::from_str(json).unwrap();
let coords = p.to_array();
assert!(coords[0].is_nan());
assert_relative_eq!(coords[1], 1.0);
assert_relative_eq!(coords[2], 2.0);
}
#[test]
fn point_deserialize_format_agnostic_comprehensive() {
let json_regular = "[1.0, 2.5, 4.25]";
let point_regular: Point<f64, 3> = serde_json::from_str(json_regular).unwrap();
assert_relative_eq!(
point_regular.to_array().as_slice(),
[1.0, 2.5, 4.25].as_slice()
);
let json_special = "[1.0, null, \"Infinity\", \"-Infinity\"]";
let point_special: Point<f64, 4> = serde_json::from_str(json_special).unwrap();
let coords = point_special.to_array();
assert_relative_eq!(coords[0], 1.0);
assert!(coords[1].is_nan());
assert!(coords[2].is_infinite() && coords[2].is_sign_positive());
assert!(coords[3].is_infinite() && coords[3].is_sign_negative());
let json_all_null = "[null, null, null]";
let point_all_null: Point<f64, 3> = serde_json::from_str(json_all_null).unwrap();
let all_null_coords = point_all_null.to_array();
assert!(all_null_coords.iter().all(|&x| x.is_nan()));
let json_all_special = "[\"Infinity\", \"-Infinity\", \"Infinity\"]";
let point_all_special: Point<f64, 3> = serde_json::from_str(json_all_special).unwrap();
let special_coords = point_all_special.to_array();
assert!(special_coords[0].is_infinite() && special_coords[0].is_sign_positive());
assert!(special_coords[1].is_infinite() && special_coords[1].is_sign_negative());
assert!(special_coords[2].is_infinite() && special_coords[2].is_sign_positive());
let original = Point::new([1.5, f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0]);
let serialized = serde_json::to_string(&original).unwrap();
let deserialized: Point<f64, 5> = serde_json::from_str(&serialized).unwrap();
let orig_coords = original.to_array();
let deser_coords = deserialized.to_array();
assert_relative_eq!(orig_coords[0], deser_coords[0]);
assert!(orig_coords[1].is_nan() && deser_coords[1].is_nan());
assert!(
orig_coords[2].is_infinite()
&& orig_coords[2].is_sign_positive()
&& deser_coords[2].is_infinite()
&& deser_coords[2].is_sign_positive()
);
assert!(
orig_coords[3].is_infinite()
&& orig_coords[3].is_sign_negative()
&& deser_coords[3].is_infinite()
&& deser_coords[3].is_sign_negative()
);
assert_relative_eq!(orig_coords[4], deser_coords[4]);
let json_f32 = "[1.5, 2.5]";
let point_f32: Point<f32, 2> = serde_json::from_str(json_f32).unwrap();
assert_relative_eq!(point_f32.to_array().as_slice(), [1.5f32, 2.5f32].as_slice());
let json_invalid = "[1.0, \"NotASpecialValue\", 2.0]";
let result: Result<Point<f64, 3>, _> = serde_json::from_str(json_invalid);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Unknown special value"));
}
#[test]
fn point_deserialize_case_insensitive_special_values() {
let test_cases = vec![
(r#"["infinity", 1.0]"#, "lowercase infinity"),
(r#"["INFINITY", 1.0]"#, "uppercase infinity"),
(r#"["Infinity", 1.0]"#, "mixed case infinity"),
(r#"["inf", 1.0]"#, "lowercase inf"),
(r#"["INF", 1.0]"#, "uppercase inf"),
(r#"["Inf", 1.0]"#, "mixed case inf"),
];
for (json_str, description) in test_cases {
let point: Point<f64, 2> = serde_json::from_str(json_str).unwrap_or_else(|e| {
panic!("Failed to deserialize {description} ({json_str}): {e}")
});
assert!(
point.coords[0].is_infinite() && point.coords[0].is_sign_positive(),
"First coordinate should be positive infinity for {description}"
);
assert_relative_eq!(point.coords[1], 1.0, epsilon = 1e-10);
}
let neg_inf_cases = vec![
(r#"["-infinity", 2.0]"#, "lowercase -infinity"),
(r#"["-INFINITY", 2.0]"#, "uppercase -infinity"),
(r#"["-Infinity", 2.0]"#, "mixed case -infinity"),
(r#"["-inf", 2.0]"#, "lowercase -inf"),
(r#"["-INF", 2.0]"#, "uppercase -inf"),
(r#"["-Inf", 2.0]"#, "mixed case -inf"),
];
for (json_str, description) in neg_inf_cases {
let point: Point<f64, 2> = serde_json::from_str(json_str).unwrap_or_else(|e| {
panic!("Failed to deserialize {description} ({json_str}): {e}")
});
assert!(
point.coords[0].is_infinite() && point.coords[0].is_sign_negative(),
"First coordinate should be negative infinity for {description}"
);
assert_relative_eq!(point.coords[1], 2.0, epsilon = 1e-10);
}
let nan_cases = vec![
(r#"["nan", 3.0]"#, "lowercase nan"),
(r#"["NaN", 3.0]"#, "mixed case NaN"),
(r#"["NAN", 3.0]"#, "uppercase NAN"),
(r#"["Nan", 3.0]"#, "title case Nan"),
];
for (json_str, description) in nan_cases {
let point: Point<f64, 2> = serde_json::from_str(json_str).unwrap_or_else(|e| {
panic!("Failed to deserialize {description} ({json_str}): {e}")
});
assert!(
point.coords[0].is_nan(),
"First coordinate should be NaN for {description}"
);
assert_relative_eq!(point.coords[1], 3.0, epsilon = 1e-10);
}
let whitespace_cases = vec![
(r#"[" infinity ", 1.0]"#, "spaces around infinity"),
(r#"["\tinf\n", 2.0]"#, "tabs and newlines around inf"),
(r#"[" NaN ", 3.0]"#, "spaces around NaN"),
];
for (json_str, description) in whitespace_cases {
let point: Point<f64, 2> = serde_json::from_str(json_str).unwrap_or_else(|e| {
panic!("Failed to deserialize {description} ({json_str}): {e}")
});
if description.contains("infinity") || description.contains("inf") {
assert!(
point.coords[0].is_infinite() && point.coords[0].is_sign_positive(),
"First coordinate should be positive infinity for {description}"
);
} else {
assert!(
point.coords[0].is_nan(),
"First coordinate should be NaN for {description}"
);
}
}
let combined = r#"["INFINITY", "-inf", "Nan", 42.0]"#;
let point: Point<f64, 4> = serde_json::from_str(combined).unwrap();
assert!(point.coords[0].is_infinite() && point.coords[0].is_sign_positive());
assert!(point.coords[1].is_infinite() && point.coords[1].is_sign_negative());
assert!(point.coords[2].is_nan());
assert_relative_eq!(point.coords[3], 42.0, epsilon = 1e-10);
let invalid = r#"["unknown_special", 1.0]"#;
let result: Result<Point<f64, 2>, _> = serde_json::from_str(invalid);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Unknown special value")
);
}
#[test]
fn point_serialize_edge_values() {
let point_max = Point::new([f64::MAX, f64::MIN]);
let json_max = serde_json::to_string(&point_max).unwrap();
assert!(!json_max.contains("null"));
let point_min = Point::new([f64::MIN_POSITIVE, -f64::MIN_POSITIVE]);
let json_min = serde_json::to_string(&point_min).unwrap();
assert!(!json_min.contains("null"));
let point_zero = Point::new([0.0, -0.0]);
let json_zero = serde_json::to_string(&point_zero).unwrap();
assert!(!json_zero.contains("null")); assert_eq!(json_zero, "[0.0,-0.0]");
}
#[test]
fn point_conversion_edge_cases() {
let precise_coords = [1.000_000_000_000_001_f64, 2.000_000_000_000_002_f64];
let point_precise: Point<f64, 2> = Point::new(precise_coords);
assert_relative_eq!(
point_precise.to_array().as_slice(),
precise_coords.as_slice()
);
let coords_ref = &[1.0f32, 2.0f32, 3.0f32];
let point_from_ref: Point<f64, 3> = Point::new(coords_ref.map(Into::into));
assert_relative_eq!(
point_from_ref.to_array().as_slice(),
[1.0f64, 2.0f64, 3.0f64].as_slice()
);
let point = Point::new([1.0, 2.0, 3.0]);
let coords_into: [f64; 3] = point.into();
assert_relative_eq!(coords_into.as_slice(), [1.0, 2.0, 3.0].as_slice());
let point_ref = Point::new([4.0, 5.0]);
let coords_from_ref: [f64; 2] = (&point_ref).into();
assert_relative_eq!(coords_from_ref.as_slice(), [4.0, 5.0].as_slice());
assert_relative_eq!(point_ref.to_array().as_slice(), [4.0, 5.0].as_slice());
}
#[test]
fn point_cast_conversions() {
let coords_f32: [f32; 3] = [1.5, 2.5, 3.5];
let point_f64: Point<f64, 3> = Point::try_from(coords_f32).unwrap();
assert_relative_eq!(
point_f64.to_array().as_slice(),
[1.5f64, 2.5f64, 3.5f64].as_slice(),
epsilon = 1e-9
);
let coords_f64: [f64; 2] = [10.0, 20.0];
let point_f64_same: Point<f64, 2> = Point::try_from(coords_f64).unwrap();
assert_relative_eq!(
point_f64_same.to_array().as_slice(),
[10.0, 20.0].as_slice()
);
let coords_i32: [i32; 4] = [1, 2, 3, 4];
let point_f64_from_int: Point<f64, 4> = Point::try_from(coords_i32).unwrap();
assert_relative_eq!(
point_f64_from_int.to_array().as_slice(),
[1.0, 2.0, 3.0, 4.0].as_slice(),
epsilon = 1e-9
);
let coords_large_i32: [i32; 2] = [i32::MAX, i32::MIN];
let point_f64_from_large: Point<f64, 2> = Point::try_from(coords_large_i32).unwrap();
assert_relative_eq!(
point_f64_from_large.to_array().as_slice(),
[f64::from(i32::MAX), f64::from(i32::MIN)].as_slice(),
epsilon = 1e-9
);
let coords_mixed: [f32; 3] = [0.0, 1.5, -3.5];
let point_mixed: Point<f64, 3> = Point::try_from(coords_mixed).unwrap();
assert_relative_eq!(
point_mixed.to_array().as_slice(),
[0.0, 1.5, -3.5].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_hash_special_values() {
let point_nan1 = Point::new([f64::NAN, 2.0]);
let point_nan2 = Point::new([f64::NAN, 2.0]);
let mut hasher_nan1 = DefaultHasher::new();
let mut hasher_nan2 = DefaultHasher::new();
point_nan1.hash(&mut hasher_nan1);
point_nan2.hash(&mut hasher_nan2);
assert_eq!(hasher_nan1.finish(), hasher_nan2.finish());
let point_inf1 = Point::new([f64::INFINITY, 2.0]);
let point_inf2 = Point::new([f64::INFINITY, 2.0]);
let mut hasher_inf1 = DefaultHasher::new();
let mut hasher_inf2 = DefaultHasher::new();
point_inf1.hash(&mut hasher_inf1);
point_inf2.hash(&mut hasher_inf2);
assert_eq!(hasher_inf1.finish(), hasher_inf2.finish());
let point_neg_inf1 = Point::new([f64::NEG_INFINITY, 2.0]);
let point_neg_inf2 = Point::new([f64::NEG_INFINITY, 2.0]);
let mut hasher_neg_inf1 = DefaultHasher::new();
let mut hasher_neg_inf2 = DefaultHasher::new();
point_neg_inf1.hash(&mut hasher_neg_inf1);
point_neg_inf2.hash(&mut hasher_neg_inf2);
assert_eq!(hasher_neg_inf1.finish(), hasher_neg_inf2.finish());
let point_pos_zero = Point::new([0.0, 2.0]);
let point_neg_zero = Point::new([-0.0, 2.0]);
let mut hasher_pos_zero = DefaultHasher::new();
let mut hasher_neg_zero = DefaultHasher::new();
point_pos_zero.hash(&mut hasher_pos_zero);
point_neg_zero.hash(&mut hasher_neg_zero);
assert_eq!(hasher_pos_zero.finish(), hasher_neg_zero.finish());
}
#[test]
fn point_hashmap_special_values() {
let mut map: HashMap<Point<f64, 2>, &str> = HashMap::new();
let point_nan = Point::new([f64::NAN, 2.0]);
let point_inf = Point::new([f64::INFINITY, 2.0]);
let point_neg_inf = Point::new([f64::NEG_INFINITY, 2.0]);
let point_zero = Point::new([0.0, 2.0]);
map.insert(point_nan, "NaN Point");
map.insert(point_inf, "Infinity Point");
map.insert(point_neg_inf, "Negative Infinity Point");
map.insert(point_zero, "Zero Point");
assert_eq!(map[&Point::new([f64::NAN, 2.0])], "NaN Point");
assert_eq!(map[&Point::new([f64::INFINITY, 2.0])], "Infinity Point");
assert_eq!(
map[&Point::new([f64::NEG_INFINITY, 2.0])],
"Negative Infinity Point"
);
assert_eq!(map[&Point::new([-0.0, 2.0])], "Zero Point");
}
#[test]
fn point_hashset_special_values() {
let mut set: HashSet<Point<f64, 2>> = HashSet::new();
set.insert(Point::new([f64::NAN, 2.0]));
set.insert(Point::new([f64::INFINITY, 2.0]));
set.insert(Point::new([f64::NEG_INFINITY, 2.0]));
set.insert(Point::new([0.0, 2.0]));
set.insert(Point::new([-0.0, 2.0]));
assert_eq!(set.len(), 4);
assert!(set.contains(&Point::new([f64::NAN, 2.0])));
assert!(set.contains(&Point::new([f64::INFINITY, 2.0])));
assert!(set.contains(&Point::new([f64::NEG_INFINITY, 2.0])));
assert!(set.contains(&Point::new([-0.0, 2.0])));
}
#[test]
fn point_hash_distribution_basic() {
let mut hashes = HashSet::new();
for i in 0..100 {
let point = Point::new([f64::from(i), f64::from(i * 2)]);
let hash = get_hash(&point);
hashes.insert(hash);
}
assert!(
hashes.len() > 90,
"Hash distribution seems poor: {} unique hashes out of 100",
hashes.len()
);
for i in -50..50 {
let point = Point::new([f64::from(i), f64::from(i * 3), f64::from(i * 5)]);
let hash = get_hash(&point);
hashes.insert(hash);
}
assert!(
hashes.len() > 140,
"Hash distribution with negatives: {} unique hashes",
hashes.len()
);
}
#[test]
fn point_validation_error_details() {
let invalid_point = Point::new([1.0, f64::NAN, 3.0]);
let result = invalid_point.validate();
assert!(result.is_err());
if let Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index,
coordinate_value,
dimension,
}) = result
{
assert_eq!(coordinate_index, 1);
assert_eq!(dimension, 3);
assert!(coordinate_value.contains("NaN"));
} else {
panic!("Expected InvalidCoordinate error");
}
let inf_point = Point::new([f64::INFINITY, 2.0, 3.0, 4.0]);
let result = inf_point.validate();
if let Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index,
coordinate_value,
dimension,
}) = result
{
assert_eq!(coordinate_index, 0);
assert_eq!(dimension, 4);
assert!(coordinate_value.contains("inf"));
} else {
panic!("Expected InvalidCoordinate error");
}
let neg_inf_point = Point::new([1.0, 2.0, f64::NEG_INFINITY]);
let result = neg_inf_point.validate();
if let Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index,
coordinate_value,
dimension,
}) = result
{
assert_eq!(coordinate_index, 2);
assert_eq!(dimension, 3);
assert!(coordinate_value.contains("inf"));
}
let invalid_f32_point = Point::new([1.0f32, f32::NAN, 3.0f32]);
let result = invalid_f32_point.validate();
if let Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index,
coordinate_value,
dimension,
}) = result
{
assert_eq!(coordinate_index, 1);
assert_eq!(dimension, 3);
assert!(coordinate_value.contains("NaN"));
}
}
#[test]
fn point_validation_error_display() {
let invalid_point = Point::new([1.0, f64::NAN, 3.0]);
let result = invalid_point.validate();
if let Err(error) = result {
let error_msg = format!("{error}");
assert!(error_msg.contains("Invalid coordinate at index 1"));
assert!(error_msg.contains("in dimension 3"));
assert!(error_msg.contains("NaN"));
} else {
panic!("Expected validation error");
}
let inf_point = Point::new([f64::INFINITY]);
let result = inf_point.validate();
if let Err(error) = result {
let error_msg = format!("{error}");
assert!(error_msg.contains("Invalid coordinate at index 0"));
assert!(error_msg.contains("in dimension 1"));
assert!(error_msg.contains("inf"));
}
}
#[test]
fn point_validation_error_clone_and_eq() {
let invalid_point = Point::new([f64::NAN, 2.0]);
let result1 = invalid_point.validate();
let result2 = invalid_point.validate();
assert!(result1.is_err());
assert!(result2.is_err());
let error1 = result1.unwrap_err();
let error2 = result2.unwrap_err();
let error1_clone = error1.clone();
assert_eq!(error1, error1_clone);
assert_eq!(error1, error2);
let debug_output = format!("{error1:?}");
assert!(debug_output.contains("InvalidCoordinate"));
assert!(debug_output.contains("coordinate_index"));
assert!(debug_output.contains("dimension"));
}
#[test]
fn point_validation_all_coordinate_types() {
assert!(Point::new([1.0f32, 2.0f32]).validate().is_ok());
assert!(Point::new([1.0f64, 2.0f64]).validate().is_ok());
assert!(Point::new([f32::NAN, 2.0f32]).validate().is_err());
assert!(Point::new([f64::NAN, 2.0f64]).validate().is_err());
}
#[test]
fn point_validation_first_invalid_coordinate() {
let multi_invalid = Point::new([1.0, f64::NAN, f64::INFINITY, f64::NAN]);
let result = multi_invalid.validate();
if let Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index, ..
}) = result
{
assert_eq!(coordinate_index, 1);
} else {
panic!("Expected InvalidCoordinate error");
}
let first_invalid = Point::new([f64::INFINITY, f64::NAN, 3.0]);
let result = first_invalid.validate();
if let Err(CoordinateValidationError::InvalidCoordinate {
coordinate_index, ..
}) = result
{
assert_eq!(coordinate_index, 0);
}
}
#[test]
fn point_hashmap_with_special_values() {
let mut point_map: HashMap<Point<f64, 3>, &str> = HashMap::new();
let point_normal = Point::new([1.0, 2.0, 3.0]);
let point_nan = Point::new([f64::NAN, 2.0, 3.0]);
let point_inf = Point::new([f64::INFINITY, 2.0, 3.0]);
let point_neg_inf = Point::new([f64::NEG_INFINITY, 2.0, 3.0]);
point_map.insert(point_normal, "normal point");
point_map.insert(point_nan, "point with NaN");
point_map.insert(point_inf, "point with +∞");
point_map.insert(point_neg_inf, "point with -∞");
assert_eq!(point_map.len(), 4);
let point_normal_copy = Point::new([1.0, 2.0, 3.0]);
let point_nan_copy = Point::new([f64::NAN, 2.0, 3.0]);
let point_inf_copy = Point::new([f64::INFINITY, 2.0, 3.0]);
let point_neg_inf_copy = Point::new([f64::NEG_INFINITY, 2.0, 3.0]);
assert!(point_map.contains_key(&point_normal_copy));
assert!(point_map.contains_key(&point_nan_copy));
assert!(point_map.contains_key(&point_inf_copy));
assert!(point_map.contains_key(&point_neg_inf_copy));
assert_eq!(point_map.get(&point_normal_copy), Some(&"normal point"));
assert_eq!(point_map.get(&point_nan_copy), Some(&"point with NaN"));
assert_eq!(point_map.get(&point_inf_copy), Some(&"point with +∞"));
assert_eq!(point_map.get(&point_neg_inf_copy), Some(&"point with -∞"));
let mut nan_counter = HashMap::new();
for _ in 0..5 {
let nan_point = Point::new([f64::NAN, 1.0]);
*nan_counter.entry(nan_point).or_insert(0) += 1;
}
assert_eq!(*nan_counter.values().next().unwrap(), 5);
}
#[test]
fn point_hashset_with_special_values() {
let mut point_set: HashSet<Point<f64, 2>> = HashSet::new();
let points = vec![
Point::new([1.0, 2.0]),
Point::new([1.0, 2.0]), Point::new([f64::NAN, 2.0]),
Point::new([f64::NAN, 2.0]), Point::new([f64::INFINITY, 2.0]),
Point::new([f64::INFINITY, 2.0]), Point::new([0.0, -0.0]), Point::new([-0.0, 0.0]), ];
for point in points {
point_set.insert(point);
}
assert_eq!(point_set.len(), 4);
let test_nan = Point::new([f64::NAN, 2.0]);
let test_inf = Point::new([f64::INFINITY, 2.0]);
let test_normal = Point::new([1.0, 2.0]);
assert!(point_set.contains(&test_nan));
assert!(point_set.contains(&test_inf));
assert!(point_set.contains(&test_normal));
}
#[test]
fn point_try_from_overflow_f64_to_f32() {
let large_coords = [f64::MAX, 1.0];
let result: Result<Point<f32, 2>, _> = Point::try_from(large_coords);
assert!(result.is_err(), "f64::MAX should overflow when cast to f32");
if let Err(CoordinateConversionError::NonFiniteValue {
coordinate_index,
coordinate_value,
}) = result
{
assert_eq!(coordinate_index, 0);
assert!(coordinate_value.contains("inf") || coordinate_value.contains("Inf"));
} else {
panic!("Expected NonFiniteValue error, got: {result:?}");
}
}
#[test]
fn point_try_from_negative_overflow_f64_to_f32() {
let large_negative_coords = [f64::MIN, 1.0];
let result: Result<Point<f32, 2>, _> = Point::try_from(large_negative_coords);
assert!(result.is_err(), "f64::MIN should overflow when cast to f32");
if let Err(CoordinateConversionError::NonFiniteValue {
coordinate_index,
coordinate_value,
}) = result
{
assert_eq!(coordinate_index, 0);
assert!(coordinate_value.contains("inf") || coordinate_value.contains("Inf"));
} else {
panic!("Expected NonFiniteValue error");
}
}
#[test]
fn point_try_from_multiple_overflow_coordinates() {
let coords = [1.0, f64::MAX, f64::MIN, f64::MAX];
let result: Result<Point<f32, 4>, _> = Point::try_from(coords);
assert!(result.is_err());
if let Err(CoordinateConversionError::NonFiniteValue {
coordinate_index, ..
}) = result
{
assert_eq!(coordinate_index, 1);
} else {
panic!("Expected NonFiniteValue error");
}
}
#[test]
fn point_try_from_successful_conversions() {
let coords_f32: [f32; 3] = [1.5, -2.5, 3.5];
let point_f64: Point<f64, 3> = Point::try_from(coords_f32).unwrap();
assert_relative_eq!(
point_f64.to_array().as_slice(),
[1.5f64, -2.5f64, 3.5f64].as_slice(),
epsilon = 1e-9
);
let coords_i32: [i32; 4] = [1, -2, 3, -4];
let point_from_int: Point<f64, 4> = Point::try_from(coords_i32).unwrap();
assert_relative_eq!(
point_from_int.to_array().as_slice(),
[1.0, -2.0, 3.0, -4.0].as_slice(),
epsilon = 1e-9
);
let coords_same: [f64; 2] = [10.0, 20.0];
let point_same: Point<f64, 2> = Point::try_from(coords_same).unwrap();
assert_relative_eq!(point_same.to_array().as_slice(), [10.0, 20.0].as_slice());
}
#[test]
fn point_try_from_edge_case_values() {
let coords_near_f32_max: [f64; 2] = [f64::from(f32::MAX), f64::from(f32::MIN)];
let result: Result<Point<f32, 2>, _> = Point::try_from(coords_near_f32_max);
assert!(result.is_ok(), "Values within f32 range should convert");
let coords_zero: [f64; 2] = [0.0, -0.0];
let point_zero: Point<f32, 2> = Point::try_from(coords_zero).unwrap();
assert_relative_eq!(point_zero.to_array()[0], 0.0f32);
assert_relative_eq!(point_zero.to_array()[1], -0.0f32);
let coords_small: [f64; 2] = [1e-10, -1e-10];
let point_small: Point<f32, 2> = Point::try_from(coords_small).unwrap();
assert!(point_small.to_array()[0].is_finite());
assert!(point_small.to_array()[1].is_finite());
}
#[test]
fn point_try_from_integer_to_float_conversions() {
let coords_u32: [u32; 3] = [100, 200, 300];
let point_u32: Point<f64, 3> = Point::try_from(coords_u32).unwrap();
assert_relative_eq!(
point_u32.to_array().as_slice(),
[100.0, 200.0, 300.0].as_slice(),
epsilon = 1e-9
);
let coords_i16: [i16; 2] = [-100, 200];
let point_i16: Point<f32, 2> = Point::try_from(coords_i16).unwrap();
assert_relative_eq!(
point_i16.to_array().as_slice(),
[-100.0f32, 200.0f32].as_slice(),
epsilon = 1e-6
);
let coords_large_i32: [i32; 2] = [1_000_000, -1_000_000];
let point_large: Point<f64, 2> = Point::try_from(coords_large_i32).unwrap();
assert_relative_eq!(
point_large.to_array().as_slice(),
[1_000_000.0, -1_000_000.0].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_try_from_all_coordinates_must_be_finite() {
let valid_coords: [f32; 3] = [1.0, 2.0, 3.0];
let result: Result<Point<f64, 3>, _> = Point::try_from(valid_coords);
assert!(result.is_ok());
let invalid_coords = [1.0, f64::MAX, 3.0];
let result: Result<Point<f32, 3>, _> = Point::try_from(invalid_coords);
assert!(
result.is_err(),
"Should fail if any coordinate becomes non-finite"
);
}
#[test]
fn point_dim_method_explicit() {
let point_1d: Point<f64, 1> = Point::new([1.0]);
assert_eq!(point_1d.dim(), 1);
let point_2d: Point<f64, 2> = Point::new([1.0, 2.0]);
assert_eq!(point_2d.dim(), 2);
let point_3d: Point<f64, 3> = Point::new([1.0, 2.0, 3.0]);
assert_eq!(point_3d.dim(), 3);
let point_5d: Point<f64, 5> = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert_eq!(point_5d.dim(), 5);
let point_10d: Point<f64, 10> = Point::new([0.0; 10]);
assert_eq!(point_10d.dim(), 10);
let point_32d: Point<f64, 32> = Point::new([0.0; 32]);
assert_eq!(point_32d.dim(), 32);
}
#[test]
fn point_dim_with_different_types() {
let point_f32: Point<f32, 3> = Point::new([1.0, 2.0, 3.0]);
assert_eq!(point_f32.dim(), 3);
let point_f64: Point<f64, 4> = Point::new([1.0, 2.0, 3.0, 4.0]);
assert_eq!(point_f64.dim(), 4);
}
#[test]
fn point_to_array_explicit() {
let point = Point::new([1.0, 2.0, 3.0]);
let arr = point.to_array();
assert_relative_eq!(arr.as_slice(), [1.0, 2.0, 3.0].as_slice());
let point2 = Point::new([4.0, 5.0]);
let arr2 = point2.to_array();
assert_relative_eq!(arr2.as_slice(), [4.0, 5.0].as_slice());
let point_1d = Point::new([42.0]);
assert_relative_eq!(point_1d.to_array().as_slice(), [42.0].as_slice());
let point_5d = Point::new([1.0, 2.0, 3.0, 4.0, 5.0]);
assert_relative_eq!(
point_5d.to_array().as_slice(),
[1.0, 2.0, 3.0, 4.0, 5.0].as_slice()
);
}
#[test]
fn point_to_array_with_special_values() {
let point_nan = Point::new([f64::NAN, 1.0, 2.0]);
let arr = point_nan.to_array();
assert!(arr[0].is_nan());
assert_relative_eq!(arr[1], 1.0);
assert_relative_eq!(arr[2], 2.0);
let point_inf = Point::new([f64::INFINITY, f64::NEG_INFINITY]);
let arr_inf = point_inf.to_array();
assert!(arr_inf[0].is_infinite() && arr_inf[0].is_sign_positive());
assert!(arr_inf[1].is_infinite() && arr_inf[1].is_sign_negative());
}
#[test]
fn point_ordered_equals_direct() {
let point1 = Point::new([1.0, 2.0, 3.0]);
let point2 = Point::new([1.0, 2.0, 3.0]);
let point3 = Point::new([1.0, 2.0, 4.0]);
assert!(point1.ordered_equals(&point2));
assert!(!point1.ordered_equals(&point3));
let point_nan1 = Point::new([f64::NAN, 2.0]);
let point_nan2 = Point::new([f64::NAN, 2.0]);
assert!(point_nan1.ordered_equals(&point_nan2));
let point_inf1 = Point::new([f64::INFINITY, 1.0]);
let point_inf2 = Point::new([f64::INFINITY, 1.0]);
assert!(point_inf1.ordered_equals(&point_inf2));
}
#[test]
fn point_hash_coordinate_direct() {
let point1 = Point::new([1.0, 2.0, 3.0]);
let point2 = Point::new([1.0, 2.0, 3.0]);
let mut hasher1 = DefaultHasher::new();
let mut hasher2 = DefaultHasher::new();
point1.hash_coordinate(&mut hasher1);
point2.hash_coordinate(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish());
let point3 = Point::new([1.0, 2.0, 4.0]);
let mut hasher3 = DefaultHasher::new();
point3.hash_coordinate(&mut hasher3);
assert_ne!(hasher1.finish(), hasher3.finish());
}
#[test]
fn point_hash_coordinate_special_values() {
let point_nan1 = Point::new([f64::NAN, 2.0]);
let point_nan2 = Point::new([f64::NAN, 2.0]);
let mut hasher1 = DefaultHasher::new();
let mut hasher2 = DefaultHasher::new();
point_nan1.hash_coordinate(&mut hasher1);
point_nan2.hash_coordinate(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish());
}
#[test]
fn point_1d_comprehensive() {
let point = Point::new([42.0]);
assert_eq!(point.dim(), 1);
assert_relative_eq!(point.to_array().as_slice(), [42.0].as_slice());
let point2 = Point::new([42.0]);
assert_eq!(point, point2);
let point3 = Point::new([43.0]);
assert_ne!(point, point3);
assert_eq!(get_hash(&point), get_hash(&point2));
assert_ne!(get_hash(&point), get_hash(&point3));
assert!(point < point3);
assert!(point3 > point);
assert!(point.validate().is_ok());
let invalid_1d = Point::new([f64::NAN]);
assert!(invalid_1d.validate().is_err());
let origin: Point<f64, 1> = Point::origin();
assert_relative_eq!(origin.to_array().as_slice(), [0.0].as_slice());
let json = serde_json::to_string(&point).unwrap();
assert_eq!(json, "[42.0]");
let deserialized: Point<f64, 1> = serde_json::from_str(&json).unwrap();
assert_eq!(point, deserialized);
}
#[test]
fn point_1d_special_values() {
let point_nan = Point::new([f64::NAN]);
let point_nan2 = Point::new([f64::NAN]);
assert_eq!(point_nan, point_nan2);
let point_inf = Point::new([f64::INFINITY]);
let point_neg_inf = Point::new([f64::NEG_INFINITY]);
assert_ne!(point_inf, point_neg_inf);
assert!(point_neg_inf < point_inf);
assert!(point_nan > point_inf);
}
#[test]
fn point_mathematical_properties_comprehensive() {
let point_a = Point::new([f64::NAN, 2.0, f64::INFINITY]);
let point_b = Point::new([f64::NAN, 2.0, f64::INFINITY]);
let point_c = Point::new([f64::NAN, 2.0, f64::INFINITY]);
assert_eq!(point_a, point_a);
let symmetry_ab = point_a == point_b;
let symmetry_ba = point_b == point_a;
assert_eq!(symmetry_ab, symmetry_ba);
assert!(symmetry_ab && symmetry_ba);
let trans_ab = point_a == point_b;
let trans_bc = point_b == point_c;
let trans_ac = point_a == point_c;
assert!(trans_ab && trans_bc && trans_ac);
let point_mixed1 = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0]);
let point_mixed2 = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0]);
let point_mixed3 = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0]);
assert_eq!(point_mixed1, point_mixed2);
assert_eq!(point_mixed2, point_mixed3);
assert_eq!(point_mixed1, point_mixed3);
assert_eq!(point_mixed1, point_mixed1);
}
#[test]
fn point_numeric_types_f32() {
let point_f32_1 = Point::new([1.5f32, 2.5f32]);
let point_f32_2 = Point::new([1.5f32, 2.5f32]);
let point_f32_nan = Point::new([f32::NAN, 2.5f32]);
let point_f32_nan2 = Point::new([f32::NAN, 2.5f32]);
assert_eq!(point_f32_1, point_f32_2);
assert_eq!(point_f32_nan, point_f32_nan2);
let point_f32_inf1 = Point::new([f32::INFINITY, 1.0f32]);
let point_f32_inf2 = Point::new([f32::INFINITY, 1.0f32]);
let point_f32_neg_inf = Point::new([f32::NEG_INFINITY, 1.0f32]);
assert_eq!(point_f32_inf1, point_f32_inf2);
assert_ne!(point_f32_inf1, point_f32_neg_inf);
let mut f32_map: HashMap<Point<f32, 2>, &str> = HashMap::new();
f32_map.insert(point_f32_1, "f32 point");
f32_map.insert(point_f32_nan, "f32 NaN point");
let lookup_f32 = Point::new([1.5f32, 2.5f32]);
let lookup_f32_nan = Point::new([f32::NAN, 2.5f32]);
assert!(f32_map.contains_key(&lookup_f32));
assert!(f32_map.contains_key(&lookup_f32_nan));
assert_eq!(f32_map.get(&lookup_f32), Some(&"f32 point"));
assert_eq!(f32_map.get(&lookup_f32_nan), Some(&"f32 NaN point"));
}
#[test]
fn point_integer_like_values() {
let point_int_1 = Point::new([10.0, 20.0, 30.0]);
let point_int_2 = Point::new([10.0, 20.0, 30.0]);
let point_int_3 = Point::new([10.0, 20.0, 31.0]);
assert_eq!(point_int_1, point_int_2);
assert_ne!(point_int_1, point_int_3);
let mut int_map: HashMap<Point<f64, 2>, String> = HashMap::new();
int_map.insert(Point::new([1.0, 2.0]), "integer-like point".to_string());
let lookup_key = Point::new([1.0, 2.0]);
assert!(int_map.contains_key(&lookup_key));
assert_eq!(int_map.get(&lookup_key).unwrap(), "integer-like point");
}
#[test]
fn point_floating_point_precision() {
let point_epsilon1 = Point::new([1.0 + f64::EPSILON, 2.0]);
let point_epsilon2 = Point::new([1.0, 2.0]);
assert_ne!(point_epsilon1, point_epsilon2);
let point_exact1 = Point::new([0.1 + 0.2, 1.0]);
let point_exact2 = Point::new([0.3, 1.0]);
assert_ne!(point_exact1, point_exact2);
let point_a = Point::new([1.0, 2.0]);
let point_b = Point::new([1.0 + f64::EPSILON, 2.0]);
assert_ne!(point_a, point_b);
let point_same1 = Point::new([1.0, 2.0]);
let point_same2 = Point::new([1.0, 2.0]);
assert_eq!(point_same1, point_same2);
}
#[test]
fn point_zero_and_negative_zero() {
let point_pos_zero = Point::new([0.0, 0.0]);
let point_neg_zero = Point::new([-0.0, -0.0]);
let point_mixed_zero = Point::new([0.0, -0.0]);
let point_mixed_zero2 = Point::new([-0.0, 0.0]);
assert_eq!(point_pos_zero, point_neg_zero);
assert_eq!(point_pos_zero, point_mixed_zero);
assert_eq!(point_pos_zero, point_mixed_zero2);
assert_eq!(point_neg_zero, point_mixed_zero);
assert_eq!(point_neg_zero, point_mixed_zero2);
assert_eq!(point_mixed_zero, point_mixed_zero2);
let hash_pos = get_hash(&point_pos_zero);
let hash_neg = get_hash(&point_neg_zero);
let hash_mixed1 = get_hash(&point_mixed_zero);
let hash_mixed2 = get_hash(&point_mixed_zero2);
assert_eq!(hash_pos, hash_neg);
assert_eq!(hash_pos, hash_mixed1);
assert_eq!(hash_pos, hash_mixed2);
}
#[test]
fn point_nan_different_creation_methods() {
let nan1 = f64::NAN;
let nan2 = f64::NAN;
let nan3 = f64::NAN;
let point_nan_variant1 = Point::new([nan1, 1.0]);
let point_nan_variant2 = Point::new([nan2, 1.0]);
let point_nan_variant3 = Point::new([nan3, 1.0]);
assert_eq!(point_nan_variant1, point_nan_variant2);
assert_eq!(point_nan_variant2, point_nan_variant3);
assert_eq!(point_nan_variant1, point_nan_variant3);
let hash1 = get_hash(&point_nan_variant1);
let hash2 = get_hash(&point_nan_variant2);
let hash3 = get_hash(&point_nan_variant3);
assert_eq!(hash1, hash2);
assert_eq!(hash2, hash3);
}
#[test]
fn point_mixed_special_values_comprehensive() {
let point_all_special = Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0, -0.0]);
let point_all_special_copy =
Point::new([f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 0.0, -0.0]);
assert_eq!(point_all_special, point_all_special_copy);
let point_combo1 = Point::new([f64::NAN, 1.0, f64::INFINITY]);
let point_combo2 = Point::new([f64::NAN, 1.0, f64::INFINITY]);
let point_combo3 = Point::new([f64::NAN, 1.0, f64::NEG_INFINITY]);
assert_eq!(point_combo1, point_combo2);
assert_ne!(point_combo1, point_combo3);
let mut special_set: HashSet<Point<f64, 3>> = HashSet::new();
special_set.insert(point_combo1);
special_set.insert(point_combo2); special_set.insert(point_combo3);
assert_eq!(special_set.len(), 2);
}
#[test]
fn point_try_from_conversion_errors() {
let coords_with_nan = [f64::NAN, 1.0, 2.0];
let result: Result<Point<f32, 3>, _> = Point::try_from(coords_with_nan);
assert!(result.is_err());
match result.unwrap_err() {
CoordinateConversionError::NonFiniteValue {
coordinate_index, ..
} => {
assert_eq!(coordinate_index, 0);
}
CoordinateConversionError::ConversionFailed { .. }
| CoordinateConversionError::InsphereInconsistency { .. } => {
panic!("Expected NonFiniteValue error")
}
}
let coords_with_inf = [1.0, f64::INFINITY, 2.0];
let result: Result<Point<f32, 3>, _> = Point::try_from(coords_with_inf);
assert!(result.is_err());
match result.unwrap_err() {
CoordinateConversionError::NonFiniteValue {
coordinate_index, ..
} => {
assert_eq!(coordinate_index, 1);
}
CoordinateConversionError::ConversionFailed { .. }
| CoordinateConversionError::InsphereInconsistency { .. } => {
panic!("Expected NonFiniteValue error")
}
}
}
#[test]
fn point_try_from_success_cases() {
let coords_f32 = [1.5f32, 2.5f32, 3.5f32];
let result: Result<Point<f64, 3>, _> = Point::try_from(coords_f32);
assert!(result.is_ok());
let point = result.unwrap();
assert_relative_eq!(
point.to_array().as_slice(),
[1.5f64, 2.5f64, 3.5f64].as_slice(),
epsilon = 1e-9
);
let coords_i32 = [1i32, -2i32, 3i32];
let result: Result<Point<f64, 3>, _> = Point::try_from(coords_i32);
assert!(result.is_ok());
let point = result.unwrap();
assert_relative_eq!(
point.to_array().as_slice(),
[1.0f64, -2.0f64, 3.0f64].as_slice(),
epsilon = 1e-9
);
let coords_f64 = [1.0f64, 2.0f64];
let result: Result<Point<f64, 2>, _> = Point::try_from(coords_f64);
assert!(result.is_ok());
let point = result.unwrap();
assert_relative_eq!(
point.to_array().as_slice(),
[1.0f64, 2.0f64].as_slice(),
epsilon = 1e-9
);
}
#[test]
fn point_try_from_error_details() {
let coords_with_nan = [f64::NAN, 1.0];
let result: Result<Point<f32, 2>, _> = Point::try_from(coords_with_nan);
assert!(result.is_err());
let error = result.unwrap_err();
let error_msg = format!("{error}");
assert!(error_msg.contains("Non-finite value"));
assert!(error_msg.contains("coordinate index 0"));
assert!(error_msg.contains("NaN"));
let coords_with_inf = [f64::INFINITY, 2.0];
let result2: Result<Point<f32, 2>, _> = Point::try_from(coords_with_inf);
let error2 = result2.unwrap_err();
let error2_clone = error2.clone();
assert_eq!(error2, error2_clone);
let coords_overflow = [f64::MAX, 1.0];
let result3: Result<Point<f32, 2>, _> = Point::try_from(coords_overflow);
match result3 {
Err(CoordinateConversionError::NonFiniteValue {
coordinate_index,
coordinate_value,
}) => {
assert_eq!(coordinate_index, 0);
assert!(!coordinate_value.is_empty());
assert!(coordinate_value.contains("inf") || coordinate_value.contains("Inf"));
}
_ => panic!("Expected NonFiniteValue error for overflow"),
}
}
#[test]
fn point_try_from_different_error_positions() {
let test_cases = [
([f64::NAN, 1.0, 2.0, 3.0], 0), ([1.0, f64::NAN, 2.0, 3.0], 1), ([1.0, 2.0, f64::INFINITY, 3.0], 2), ([1.0, 2.0, 3.0, f64::NEG_INFINITY], 3), ];
for &(coords, expected_index) in &test_cases {
let result: Result<Point<f32, 4>, _> = Point::try_from(coords);
assert!(result.is_err());
match result.unwrap_err() {
CoordinateConversionError::NonFiniteValue {
coordinate_index, ..
} => {
assert_eq!(coordinate_index, expected_index);
}
CoordinateConversionError::ConversionFailed { .. }
| CoordinateConversionError::InsphereInconsistency { .. } => {
panic!("Expected NonFiniteValue error at position {expected_index}")
}
}
}
}
#[test]
fn point_try_from_first_error_reported() {
let coords_multi_error = [f64::NAN, f64::INFINITY, f64::NEG_INFINITY];
let result: Result<Point<f32, 3>, _> = Point::try_from(coords_multi_error);
assert!(result.is_err());
match result.unwrap_err() {
CoordinateConversionError::NonFiniteValue {
coordinate_index, ..
} => {
assert_eq!(coordinate_index, 0);
}
CoordinateConversionError::ConversionFailed { .. }
| CoordinateConversionError::InsphereInconsistency { .. } => {
panic!("Expected NonFiniteValue error")
}
}
}
#[test]
fn point_deserialize_nan_handling() {
let json_with_null = "[null,1.0,2.0]";
let result: Result<Point<f64, 3>, _> = serde_json::from_str(json_with_null);
assert!(result.is_ok());
let point = result.unwrap();
let coords = point.to_array();
assert!(coords[0].is_nan());
assert_relative_eq!(coords[1], 1.0);
assert_relative_eq!(coords[2], 2.0);
let json_multiple_nulls = "[null,null,3.0]";
let result_multi: Result<Point<f64, 3>, _> = serde_json::from_str(json_multiple_nulls);
assert!(result_multi.is_ok());
let point_multi = result_multi.unwrap();
let coords_multi = point_multi.to_array();
assert!(coords_multi[0].is_nan());
assert!(coords_multi[1].is_nan());
assert_relative_eq!(coords_multi[2], 3.0);
let json_f32_null = "[null,1.5]";
let result_f32: Result<Point<f32, 2>, _> = serde_json::from_str(json_f32_null);
assert!(result_f32.is_ok());
let point_f32 = result_f32.unwrap();
let coords_f32 = point_f32.to_array();
assert!(coords_f32[0].is_nan());
assert_relative_eq!(coords_f32[1], 1.5);
}
#[test]
fn point_trait_completeness() {
fn assert_send<T: Send>(_: T) {}
fn assert_sync<T: Sync>(_: T) {}
let point = Point::new([1.0, 2.0, 3.0]);
let debug_output = format!("{point:?}");
assert!(!debug_output.is_empty());
assert!(debug_output.contains("Point"));
let default_point: Point<f64, 3> = Point::default();
assert_relative_eq!(
default_point.to_array().as_slice(),
[0.0, 0.0, 0.0].as_slice()
);
let point_smaller = Point::new([1.0, 2.0, 2.9]);
assert!(point_smaller < point);
assert_send(point);
assert_sync(point);
#[expect(clippy::clone_on_copy)]
let cloned = point.clone();
let copied = point;
assert_eq!(copied.dim(), cloned.dim());
let mut set = HashSet::new();
set.insert(point);
assert!(set.contains(&point));
}
}