#![cfg(test)]
use crate::prelude::*;
mod test_line_line_parallel_detection {
use super::*;
#[test]
fn test_nearly_parallel_lines_detected() {
let line0 = Line::new(point(0.0, 0.0), point(1.0, 0.0));
let line1 = Line::new(point(0.0, 100.0), point(1.0, 1e-10));
match int_line_line(&line0, &line1) {
LineLineConfig::ParallelDistinct() => {
}
LineLineConfig::OnePoint(p, s0, s1) => {
panic!(
"Lines are nearly parallel but detected as intersecting! \
Got intersection at {:?} with params s0={}, s1={} \
(intersection is {} units away)",
p, s0, s1, s0.abs()
);
}
_ => panic!("Unexpected result"),
}
}
#[test]
fn test_very_nearly_parallel_lines() {
let line0 = Line::new(point(0.0, 0.0), point(1.0, 0.0));
let line1 = Line::new(point(0.0, 1.0), point(1.0, 1e-12));
match int_line_line(&line0, &line1) {
LineLineConfig::ParallelDistinct() => {
}
LineLineConfig::OnePoint(p, s0, s1) => {
panic!(
"Lines are nearly parallel but detected as intersecting! \
Intersection at {:?}, params s0={}, s1={}",
p, s0, s1
);
}
_ => panic!("Unexpected result"),
}
}
#[test]
fn test_exactly_parallel_lines_detected() {
let line0 = Line::new(point(0.0, 0.0), point(1.0, 0.0));
let line1 = Line::new(point(0.0, 1.0), point(1.0, 0.0));
match int_line_line(&line0, &line1) {
LineLineConfig::ParallelDistinct() => {
}
_ => panic!("Exactly parallel lines should be detected as parallel"),
}
}
#[test]
fn test_same_line_detected() {
let line0 = Line::new(point(0.0, 0.0), point(1.0, 0.0));
let line1 = Line::new(point(5.0, 0.0), point(1.0, 0.0));
match int_line_line(&line0, &line1) {
LineLineConfig::ParallelTheSame() => {
}
_ => panic!("Same line should be detected"),
}
}
#[test]
fn test_intersection_parameters_reasonable() {
let line0 = Line::new(point(0.0, 0.0), point(1.0, 0.0));
let line1 = Line::new(point(0.5, -1.0), point(0.0, 1.0));
match int_line_line(&line0, &line1) {
LineLineConfig::OnePoint(p, s0, s1) => {
assert!(
s0.abs() < 1e6,
"Parameter s0 is unreasonably large: {}",
s0
);
assert!(
s1.abs() < 1e6,
"Parameter s1 is unreasonably large: {}",
s1
);
assert!(
p.x.is_finite() && p.y.is_finite(),
"Intersection point should be finite"
);
}
_ => panic!("Lines should intersect"),
}
}
}
mod test_arc_bulge_division {
use super::*;
#[test]
fn test_tiny_bulge_produces_finite_arc() {
let arc = arc_from_bulge(
point(0.0, 0.0),
point(1.0, 0.0),
1e-15, );
assert_eq!(arc.a, point(0.0, 0.0), "Arc start point should be correct");
assert_eq!(arc.b, point(1.0, 0.0), "Arc end point should be correct");
assert!(!arc.c.x.is_nan() && !arc.c.y.is_nan(), "Arc center should not be NaN");
assert!(!arc.r.is_nan(), "Arc radius should not be NaN");
}
#[test]
fn test_zero_bulge_produces_line_segment() {
let arc = arc_from_bulge(
point(0.0, 0.0),
point(2.0, 0.0),
0.0, );
assert!(arc.r.is_infinite(), "Zero bulge should produce line segment with infinite radius");
assert!(
arc.c.x.is_infinite() && arc.c.y.is_infinite(),
"Line segment should have infinite center point"
);
}
#[test]
fn test_epsilon_bulge_produces_finite_arc() {
let arc = arc_from_bulge(
point(0.0, 0.0),
point(1.0, 0.0),
f64::EPSILON, );
assert!(!arc.c.x.is_nan() && !arc.c.y.is_nan(), "Arc center should not be NaN");
assert!(!arc.r.is_nan(), "Arc radius should not be NaN");
assert_eq!(arc.a, point(0.0, 0.0), "Arc start point should be correct");
assert_eq!(arc.b, point(1.0, 0.0), "Arc end point should be correct");
}
#[test]
fn test_negative_tiny_bulge() {
let arc = arc_from_bulge(
point(0.0, 0.0),
point(1.0, 0.0),
-1e-15, );
assert!(!arc.c.x.is_nan() && !arc.c.y.is_nan(), "Arc center should not be NaN");
assert!(!arc.r.is_nan(), "Arc radius should not be NaN");
assert_eq!(arc.a, point(1.0, 0.0), "Arc start point should be swapped for negative bulge");
assert_eq!(arc.b, point(0.0, 0.0), "Arc end point should be swapped for negative bulge");
}
}
mod test_sqrt_guards {
use super::*;
#[test]
fn test_arc_with_numerical_error_in_bulge() {
let bulges = [1e-10, 1e-8, 1e-6, 0.1, 0.5, 0.9];
for &bulge in &bulges {
let arc = arc_from_bulge(
point(0.0, 0.0),
point(1.0, 0.0),
bulge,
);
assert!(
!arc.c.x.is_nan(),
"Arc center x is NaN for bulge {}",
bulge
);
assert!(
!arc.c.y.is_nan(),
"Arc center y is NaN for bulge {}",
bulge
);
assert!(
!arc.r.is_nan(),
"Arc radius is NaN for bulge {}",
bulge
);
}
}
#[test]
fn test_circle_intersection_discriminant() {
let c0 = circle(point(0.0, 0.0), 1.0);
let c1 = circle(point(2.0 + 1e-10, 0.0), 1.0);
let result = int_circle_circle(c0, c1);
match result {
CircleCircleConfig::NoncocircularOnePoint(p) => {
assert!(p.x.is_finite() && p.y.is_finite());
}
CircleCircleConfig::NoIntersection() => {
}
_ => panic!("Unexpected result for tangent circles"),
}
}
}
mod test_convex_hull_nan {
use super::*;
#[test]
fn test_convex_hull_filters_nan_points() {
let points = vec![
point(0.0, 0.0),
point(1.0, 0.0),
point(1.0, 1.0),
point(0.0, 1.0),
point(f64::NAN, 0.5), ];
let hull = points_convex_hull(&points);
for p in &hull {
assert!(
p.x.is_finite() && p.y.is_finite(),
"Hull contains non-finite point: {:?}",
p
);
}
assert!(
hull.len() >= 3,
"Hull should contain valid points after filtering NaN"
);
}
#[test]
fn test_convex_hull_filters_infinity() {
let points = vec![
point(0.0, 0.0),
point(1.0, 0.0),
point(1.0, 1.0),
point(f64::INFINITY, 0.5), ];
let hull = points_convex_hull(&points);
for p in &hull {
assert!(
p.x.is_finite() && p.y.is_finite(),
"Hull contains non-finite point: {:?}",
p
);
}
}
#[test]
fn test_convex_hull_all_nan_points() {
let points = vec![
point(f64::NAN, f64::NAN),
point(f64::NAN, 1.0),
point(1.0, f64::NAN),
];
let hull = points_convex_hull(&points);
assert!(
hull.is_empty() || hull.iter().all(|p| p.x.is_finite() && p.y.is_finite()),
"Should handle all-NaN input gracefully"
);
}
}
mod test_exact_zero_comparisons {
use super::*;
#[test]
fn test_collinear_points_with_numerical_error() {
let p1 = point(0.0, 0.0);
let p2 = point(1.0, 1.0);
let p3 = point(2.0, 2.0 + 1e-14);
let cross = (p2 - p1).perp(p3 - p2);
const COLLINEAR_TOLERANCE: f64 = 1e-10;
assert!(
cross.abs() < COLLINEAR_TOLERANCE,
"Points should be treated as collinear. Cross product: {}",
cross
);
}
#[test]
fn test_convex_hull_handles_numerical_collinearity() {
let points = vec![
point(0.0, 0.0),
point(1.0, 1.0),
point(2.0, 2.0 + 1e-14),
point(3.0, 3.0 - 1e-14),
point(4.0, 4.0),
];
let hull = points_convex_hull(&points);
assert!(
hull.len() <= 3,
"Collinear points should produce minimal hull. Got {} points",
hull.len()
);
}
#[test]
fn test_circle_center_comparison_with_tolerance() {
let c0 = circle(point(0.0, 0.0), 1.0);
let c1 = circle(point(1e-15, 1e-15), 1.0);
let result = int_circle_circle(c0, c1);
match result {
CircleCircleConfig::SameCircles() => {
}
CircleCircleConfig::NoncocircularOnePoint(_) => {
}
_ => panic!("Nearly identical circles should be handled properly"),
}
}
}
mod test_division_guards {
use super::*;
#[test]
fn test_distance_to_degenerate_line() {
let line = Line::new(
point(0.0, 0.0),
point(1e-100, 1e-100), );
let circle = circle(point(1.0, 1.0), 1.0);
let result = dist_line_circle(&line, &circle);
match result {
DistLineCircleConfig::OnePair(dist, _, _, _) |
DistLineCircleConfig::TwoPairs(dist, _, _, _, _, _, _) => {
assert!(
dist.is_finite(),
"Distance should be finite for degenerate line"
);
}
}
}
#[test]
fn test_point_segment_distance_zero_length() {
let seg = segment(point(1.0, 2.0), point(1.0, 2.0));
let p = point(3.0, 4.0);
let (dist, closest) = dist_point_segment(&p, &seg);
assert!(dist.is_finite(), "Distance should be finite");
assert!(
closest.x.is_finite() && closest.y.is_finite(),
"Closest point should be finite"
);
let expected = ((3.0_f64 - 1.0).powi(2) + (4.0_f64 - 2.0).powi(2)).sqrt();
assert!(
(dist - expected).abs() < 1e-10,
"Distance should be from point to segment endpoint"
);
}
}
mod test_large_coordinates {
use super::*;
#[test]
fn test_distance_with_large_coordinates() {
let scales = [1.0, 1e3, 1e6, 1e9];
for &scale in &scales {
let p1 = point(scale, scale);
let p2 = point(scale + 1.0, scale);
let dist = (p2 - p1).norm();
if scale < 1e8 {
assert!(
(dist - 1.0).abs() < 1e-6,
"Distance should be ~1.0 at scale {}. Got: {}",
scale,
dist
);
} else {
assert!(
dist.is_finite() && dist >= 0.0,
"Distance should be finite and non-negative at scale {}",
scale
);
}
}
}
#[test]
fn test_line_intersection_large_coordinates() {
let scale = 1e8;
let line0 = Line::new(
point(scale, scale),
point(1.0, 0.0),
);
let line1 = Line::new(
point(scale, scale + 1.0),
point(0.0, -1.0),
);
match int_line_line(&line0, &line1) {
LineLineConfig::OnePoint(p, s0, s1) => {
assert!(p.x.is_finite() && p.y.is_finite());
assert!(s0.is_finite() && s1.is_finite());
assert!(
(p.x - scale).abs() < 10.0,
"Intersection x should be near {}",
scale
);
}
_ => panic!("Lines should intersect"),
}
}
}
mod test_parameter_overflow {
use super::*;
#[test]
fn test_nearly_parallel_lines_reject_far_intersection() {
let line0 = Line::new(point(0.0, 0.0), point(1.0, 0.0));
let line1 = Line::new(point(0.0, 1e6), point(1.0, 1e-10));
match int_line_line(&line0, &line1) {
LineLineConfig::ParallelDistinct() => {
}
LineLineConfig::OnePoint(p, s0, s1) => {
assert!(
s0.abs() < 1e8 && s1.abs() < 1e8,
"Intersection parameters are too far: s0={}, s1={} \
(intersection at {:?} is {} units away)",
s0, s1, p, s0.abs()
);
}
_ => {}
}
}
}
mod test_area_edge_cases {
use super::*;
#[test]
fn test_degenerate_arc_area() {
let arc = arc(
point(1.0, 0.0),
point(0.0, 1.0),
point(1.0, 0.0), 1.0,
);
let arcline = vec![arc];
let area = arcline_area(&arcline);
assert!(
area.is_finite(),
"Area should be finite for degenerate arc"
);
}
#[test]
fn test_nearly_full_circle_arc() {
let c = point(0.0, 0.0);
let r = 1.0;
let start = point(r, 0.0); let epsilon: f64 = 1e-11; let end = point(r * epsilon.cos(), r * epsilon.sin());
let arc = arc(start, end, c, r);
let arcline = vec![arc];
let area = arcline_area(&arcline);
assert!(area.is_finite(), "Area should be finite");
let full_circle_area = std::f64::consts::PI * r * r;
assert!(
area.abs() > full_circle_area * 0.9,
"Nearly-full-circle arc should have area close to full circle. Got {}",
area
);
}
}
mod test_tolerance_consistency {
use super::*;
#[test]
fn test_close_enough_with_nan() {
assert!(
!close_enough(f64::NAN, 1.0, 0.1),
"close_enough should return false for NaN"
);
assert!(
!close_enough(1.0, f64::NAN, 0.1),
"close_enough should return false for NaN"
);
}
#[test]
fn test_close_enough_with_infinity() {
assert!(
!close_enough(f64::INFINITY, 1.0, 0.1),
"close_enough should return false for infinity"
);
assert!(
!close_enough(1.0, f64::INFINITY, 0.1),
"close_enough should return false for infinity"
);
}
#[test]
fn test_close_enough_with_negative_epsilon() {
let result = close_enough(1.0, 1.1, -0.1);
assert!(
!result,
"close_enough should handle negative epsilon"
);
}
}
mod test_integration {
use super::*;
#[test]
fn test_area_calculation_robustness() {
let scales = [1.0, 1e3, 1e6];
for &scale in &scales {
let square = vec![
point(0.0 * scale, 0.0 * scale),
point(1.0 * scale, 0.0 * scale),
point(1.0 * scale, 1.0 * scale),
point(0.0 * scale, 1.0 * scale),
];
let area = pointline_area(&square);
assert!(area.is_finite(), "Area should be finite at scale {}", scale);
if scale < 1e6 {
let expected = scale * scale;
assert!(
(area - expected).abs() / expected < 0.01,
"Area should be accurate at scale {}. Expected {}, got {}",
scale,
expected,
area
);
}
}
}
}