Skip to main content

oxilean_std/computational_geometry/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::types::{
8    AlphaShapeBuilder, BentleyOttmannEvents, ConvexHull2D, ConvexLayerPeeler,
9    DelaunayTriangulation, FrechetDistanceApprox, KdNode, MinkowskiSum, Orientation,
10    PlaneSubdivision, Point2D, RangeTree1D, RangeTree2D, SpatialHash, Triangle, VoronoiDiagram,
11};
12
13pub fn app(f: Expr, a: Expr) -> Expr {
14    Expr::App(Box::new(f), Box::new(a))
15}
16pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
17    app(app(f, a), b)
18}
19pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
20    app(app2(f, a, b), c)
21}
22pub fn cst(s: &str) -> Expr {
23    Expr::Const(Name::str(s), vec![])
24}
25pub fn prop() -> Expr {
26    Expr::Sort(Level::zero())
27}
28pub fn type0() -> Expr {
29    Expr::Sort(Level::succ(Level::zero()))
30}
31pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
32    Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
33}
34pub fn arrow(a: Expr, b: Expr) -> Expr {
35    pi(BinderInfo::Default, "_", a, b)
36}
37pub fn nat_ty() -> Expr {
38    cst("Nat")
39}
40pub fn real_ty() -> Expr {
41    cst("Real")
42}
43pub fn bool_ty() -> Expr {
44    cst("Bool")
45}
46pub fn list_ty(elem: Expr) -> Expr {
47    app(cst("List"), elem)
48}
49pub fn pair_ty(a: Expr, b: Expr) -> Expr {
50    app2(cst("Prod"), a, b)
51}
52pub fn point2d_ty() -> Expr {
53    pair_ty(real_ty(), real_ty())
54}
55pub fn list_point2d_ty() -> Expr {
56    list_ty(point2d_ty())
57}
58pub fn list_nat_ty() -> Expr {
59    list_ty(nat_ty())
60}
61/// `Point2D : Type` — a 2D point (x, y) ∈ ℝ²
62pub fn point2d_kernel_ty() -> Expr {
63    pair_ty(real_ty(), real_ty())
64}
65/// `Point3D : Type` — a 3D point (x, y, z) ∈ ℝ³
66pub fn point3d_kernel_ty() -> Expr {
67    pair_ty(real_ty(), pair_ty(real_ty(), real_ty()))
68}
69/// `Segment2D : Type` — a line segment in ℝ² defined by two endpoints
70pub fn segment2d_ty() -> Expr {
71    pair_ty(point2d_ty(), point2d_ty())
72}
73/// `Triangle2D : Type` — a triangle in ℝ² defined by three vertices
74pub fn triangle2d_ty() -> Expr {
75    pair_ty(point2d_ty(), pair_ty(point2d_ty(), point2d_ty()))
76}
77/// `Polygon2D : List Point2D -> Type` — polygon given as an ordered vertex list
78pub fn polygon2d_ty() -> Expr {
79    arrow(list_point2d_ty(), type0())
80}
81/// `ConvexHull : List Point2D -> List Point2D`
82/// The convex hull function mapping a point set to its extremal vertices
83pub fn convex_hull_fn_ty() -> Expr {
84    arrow(list_point2d_ty(), list_point2d_ty())
85}
86/// `IsConvex : List Point2D -> Prop` — polygon is convex
87pub fn is_convex_ty() -> Expr {
88    arrow(list_point2d_ty(), prop())
89}
90/// `IsOnConvexHull : Point2D -> List Point2D -> Prop`
91/// A point is on the convex hull of the set
92pub fn is_on_convex_hull_ty() -> Expr {
93    arrow(point2d_ty(), arrow(list_point2d_ty(), prop()))
94}
95/// `Triangulation : List Point2D -> List (Nat × Nat × Nat)`
96/// A triangulation maps a point set to a list of triangles (vertex index triples)
97pub fn triangulation_ty() -> Expr {
98    let triple = pair_ty(nat_ty(), pair_ty(nat_ty(), nat_ty()));
99    arrow(list_point2d_ty(), list_ty(triple))
100}
101/// `DelaunayTriangulation : List Point2D -> List (Nat × Nat × Nat)`
102/// Delaunay triangulation: maximises minimum angle, empty circumcircle property
103pub fn delaunay_triangulation_ty() -> Expr {
104    triangulation_ty()
105}
106/// `IsDelaunay : List Point2D -> List (Nat × Nat × Nat) -> Prop`
107/// Predicate: the triangulation satisfies the Delaunay condition
108pub fn is_delaunay_ty() -> Expr {
109    let triple = pair_ty(nat_ty(), pair_ty(nat_ty(), nat_ty()));
110    arrow(list_point2d_ty(), arrow(list_ty(triple), prop()))
111}
112/// `VoronoiCell : List Point2D -> Nat -> (Point2D -> Prop)`
113/// The i-th Voronoi cell: set of points closest to site i
114pub fn voronoi_cell_ty() -> Expr {
115    arrow(
116        list_point2d_ty(),
117        arrow(nat_ty(), arrow(point2d_ty(), prop())),
118    )
119}
120/// `VoronoiDiagram : List Point2D -> List (List Point2D)`
121/// Maps sites to their Voronoi cell boundary polygon vertices
122pub fn voronoi_diagram_ty() -> Expr {
123    arrow(list_point2d_ty(), list_ty(list_point2d_ty()))
124}
125/// `CircumcircleCenter : Point2D -> Point2D -> Point2D -> Point2D`
126/// The circumcenter of three points
127pub fn circumcircle_center_ty() -> Expr {
128    arrow(
129        point2d_ty(),
130        arrow(point2d_ty(), arrow(point2d_ty(), point2d_ty())),
131    )
132}
133/// `PointLocation : (List Point2D) -> Point2D -> Nat`
134/// Returns the index of the region containing the query point
135pub fn point_location_ty() -> Expr {
136    arrow(list_point2d_ty(), arrow(point2d_ty(), nat_ty()))
137}
138/// `KdTree : Nat -> Type` — a k-d tree for points in ℝ^k
139pub fn kd_tree_ty() -> Expr {
140    arrow(nat_ty(), type0())
141}
142/// `KdTreeQuery : KdTree -> Point2D -> Real -> List Point2D`
143/// Range query: return all points within distance r of the query point
144pub fn kd_tree_query_ty() -> Expr {
145    arrow(
146        type0(),
147        arrow(point2d_ty(), arrow(real_ty(), list_point2d_ty())),
148    )
149}
150/// `NearestNeighbour : (List Point2D) -> Point2D -> Point2D`
151/// Returns the point in the set closest to the query
152pub fn nearest_neighbour_ty() -> Expr {
153    arrow(list_point2d_ty(), arrow(point2d_ty(), point2d_ty()))
154}
155/// `ClosestPair : (List Point2D) -> (Point2D × Point2D)`
156/// Returns the closest pair of distinct points
157pub fn closest_pair_fn_ty() -> Expr {
158    arrow(list_point2d_ty(), pair_ty(point2d_ty(), point2d_ty()))
159}
160/// `SegmentsIntersect : Segment2D -> Segment2D -> Prop`
161/// Predicate: two line segments share a common point
162pub fn segments_intersect_ty() -> Expr {
163    arrow(segment2d_ty(), arrow(segment2d_ty(), prop()))
164}
165/// `SegmentIntersectionPoint : Segment2D -> Segment2D -> Point2D`
166/// The intersection point of two segments (if it exists)
167pub fn segment_intersection_point_ty() -> Expr {
168    arrow(segment2d_ty(), arrow(segment2d_ty(), point2d_ty()))
169}
170/// `AnySegmentsIntersect : (List Segment2D) -> Prop`
171/// Shamos-Hoey: at least one pair of segments intersects
172pub fn any_segments_intersect_ty() -> Expr {
173    arrow(list_ty(segment2d_ty()), prop())
174}
175/// `PolygonArea : List Point2D -> Real`
176/// Signed area of a simple polygon (shoelace formula)
177pub fn polygon_area_ty() -> Expr {
178    arrow(list_point2d_ty(), real_ty())
179}
180/// `PolygonCentroid : List Point2D -> Point2D`
181/// Centroid (centre of mass) of a simple polygon
182pub fn polygon_centroid_ty() -> Expr {
183    arrow(list_point2d_ty(), point2d_ty())
184}
185/// `PointInPolygon : Point2D -> List Point2D -> Prop`
186/// Ray casting: point is strictly inside the polygon
187pub fn point_in_polygon_ty() -> Expr {
188    arrow(point2d_ty(), arrow(list_point2d_ty(), prop()))
189}
190/// `PolygonPerimeter : List Point2D -> Real`
191pub fn polygon_perimeter_ty() -> Expr {
192    arrow(list_point2d_ty(), real_ty())
193}
194/// `SpatialHash : Nat -> Type` — spatial hash map with grid resolution n
195pub fn spatial_hash_ty() -> Expr {
196    arrow(nat_ty(), type0())
197}
198/// `SpatialHashInsert : SpatialHash -> Point2D -> SpatialHash`
199pub fn spatial_hash_insert_ty() -> Expr {
200    arrow(type0(), arrow(point2d_ty(), type0()))
201}
202/// `Hyperplane : Nat -> Type` — a hyperplane in ℝ^d defined by a normal vector and offset
203pub fn hyperplane_ty() -> Expr {
204    arrow(nat_ty(), type0())
205}
206/// `HyperplaneArrangement : List (Hyperplane d) -> Type`
207/// A finite collection of hyperplanes inducing a cell decomposition
208pub fn hyperplane_arrangement_ty() -> Expr {
209    arrow(list_ty(type0()), type0())
210}
211/// `ZoneTheoremBound : Nat -> Nat`
212/// Zone theorem: for a hyperplane h in an arrangement of n hyperplanes in ℝ^d,
213/// the complexity of the zone of h is O(n^{d-1})
214pub fn zone_theorem_bound_ty() -> Expr {
215    arrow(nat_ty(), nat_ty())
216}
217/// `ArrangementFaceCount : Nat -> Nat -> Nat`
218/// f(n, d) = number of faces (cells, facets, ...) in a simple arrangement
219/// of n hyperplanes in ℝ^d
220pub fn arrangement_face_count_ty() -> Expr {
221    arrow(nat_ty(), arrow(nat_ty(), nat_ty()))
222}
223/// `CellOfArrangement : HyperplaneArrangement -> Point -> Nat`
224/// Returns the index of the cell containing a given point
225pub fn cell_of_arrangement_ty() -> Expr {
226    arrow(type0(), arrow(point2d_ty(), nat_ty()))
227}
228/// `AlphaComplex : List Point2D -> Real -> Type`
229/// The alpha complex at parameter alpha: a subcomplex of the Delaunay triangulation
230pub fn alpha_complex_ty() -> Expr {
231    arrow(list_point2d_ty(), arrow(real_ty(), type0()))
232}
233/// `CechComplex : List Point2D -> Real -> Type`
234/// The Čech complex at radius r: nerve of the union of balls of radius r
235pub fn cech_complex_ty() -> Expr {
236    arrow(list_point2d_ty(), arrow(real_ty(), type0()))
237}
238/// `PersistenceDiagram : Type`
239/// A persistence diagram: multiset of (birth, death) pairs encoding homology lifetimes
240pub fn persistence_diagram_ty() -> Expr {
241    list_ty(pair_ty(real_ty(), real_ty()))
242}
243/// `ComputePersistence : (Real -> Type) -> PersistenceDiagram`
244/// Compute persistent homology from a filtration
245pub fn compute_persistence_ty() -> Expr {
246    arrow(arrow(real_ty(), type0()), persistence_diagram_ty())
247}
248/// `AlphaShapeFiltration : List Point2D -> List (Real × AlphaComplex)`
249/// The full filtration from alpha = 0 to alpha = infinity
250pub fn alpha_shape_filtration_ty() -> Expr {
251    arrow(list_point2d_ty(), list_ty(pair_ty(real_ty(), type0())))
252}
253/// `KineticCertificate : Type`
254/// A combinatorial predicate on moving objects that holds over a time interval
255pub fn kinetic_certificate_ty() -> Expr {
256    type0()
257}
258/// `KineticTournament : Nat -> Type`
259/// A kinetic tournament on n moving points maintains the maximum
260pub fn kinetic_tournament_ty() -> Expr {
261    arrow(nat_ty(), type0())
262}
263/// `KineticHeap : Nat -> Type`
264/// A kinetic heap maintains the max of n objects with trajectories
265pub fn kinetic_heap_ty() -> Expr {
266    arrow(nat_ty(), type0())
267}
268/// `EventQueue : Type -> Type`
269/// A priority queue of future events ordered by event time
270pub fn event_queue_ty() -> Expr {
271    arrow(type0(), type0())
272}
273/// `KineticConvexHull : Nat -> Type`
274/// Maintains the convex hull of n moving points in the plane
275pub fn kinetic_convex_hull_ty() -> Expr {
276    arrow(nat_ty(), type0())
277}
278/// `ParametricSearchProblem : Type`
279/// A decision problem parameterised by a real value lambda,
280/// monotone in lambda (used by Cole's technique)
281pub fn parametric_search_problem_ty() -> Expr {
282    arrow(real_ty(), prop())
283}
284/// `ColeParallelBinarySearch : (Real -> Prop) -> Real`
285/// Cole's parametric search: find the critical lambda in O(T_seq * log n) time
286pub fn cole_parallel_binary_search_ty() -> Expr {
287    arrow(arrow(real_ty(), prop()), real_ty())
288}
289/// `ParallelBinarySearch : List (Real × Real) -> List Real`
290/// Simultaneous binary search over k sorted arrays
291pub fn parallel_binary_search_ty() -> Expr {
292    arrow(list_ty(pair_ty(real_ty(), real_ty())), list_ty(real_ty()))
293}
294/// `PointHyperplaneDual : Point2D -> (Real -> Prop)`
295/// Point-hyperplane duality: maps point (a,b) to the hyperplane y = ax - b
296pub fn point_hyperplane_dual_ty() -> Expr {
297    arrow(point2d_ty(), arrow(real_ty(), prop()))
298}
299/// `GaleTransform : List Point2D -> List Point2D`
300/// The Gale transform of a point configuration in general position
301pub fn gale_transform_ty() -> Expr {
302    arrow(list_point2d_ty(), list_point2d_ty())
303}
304/// `DualArrangement : HyperplaneArrangement -> List Point2D`
305/// Convert a hyperplane arrangement to its dual point set
306pub fn dual_arrangement_ty() -> Expr {
307    arrow(type0(), list_point2d_ty())
308}
309/// `SweepLineState : Type`
310/// The ordered sequence of segments active at the current sweep-line position
311pub fn sweep_line_state_ty() -> Expr {
312    list_ty(pair_ty(point2d_ty(), point2d_ty()))
313}
314/// `ShamosHoeyAlgorithm : List Segment2D -> Bool`
315/// Returns true iff any two segments intersect — O(n log n)
316pub fn shamos_hoey_algorithm_ty() -> Expr {
317    arrow(list_ty(segment2d_ty()), bool_ty())
318}
319/// `BentleyOttmannOutput : List Segment2D -> List Point2D`
320/// Reports all k intersection points of n segments in O((n+k) log n) time
321pub fn bentley_ottmann_output_ty() -> Expr {
322    arrow(list_ty(segment2d_ty()), list_point2d_ty())
323}
324/// `BentleyOttmannCorrectness : BentleyOttmannOutput s = AllIntersections s`
325/// Correctness theorem for Bentley-Ottmann
326pub fn bentley_ottmann_correctness_ty() -> Expr {
327    arrow(list_ty(segment2d_ty()), prop())
328}
329/// `SteinerPoint : List Point2D -> Point2D`
330/// A Steiner point inserted to improve mesh quality
331pub fn steiner_point_ty() -> Expr {
332    arrow(list_point2d_ty(), point2d_ty())
333}
334/// `DelaunayRefinement : List Point2D -> Real -> List Point2D`
335/// Ruppert/Chew Delaunay refinement: insert Steiner points until
336/// all angles >= min_angle (typically 20.7 degrees)
337pub fn delaunay_refinement_ty() -> Expr {
338    arrow(list_point2d_ty(), arrow(real_ty(), list_point2d_ty()))
339}
340/// `MinAngleGuarantee : List Point2D -> Real -> Prop`
341/// All triangles in the refined mesh have minimum angle >= threshold
342pub fn min_angle_guarantee_ty() -> Expr {
343    arrow(list_point2d_ty(), arrow(real_ty(), prop()))
344}
345/// `TriangulationQuality : List Point2D -> Real`
346/// The minimum angle over all triangles in a triangulation
347pub fn triangulation_quality_ty() -> Expr {
348    arrow(list_point2d_ty(), real_ty())
349}
350/// `VCDimension : (Point2D -> Prop) -> Nat`
351/// VC dimension of a set system (concept class) on ℝ²
352pub fn vc_dimension_ty() -> Expr {
353    arrow(arrow(point2d_ty(), prop()), nat_ty())
354}
355/// `EpsilonNet : List Point2D -> Real -> List Point2D`
356/// An epsilon-net: a subset R such that every heavy range contains a point of R
357pub fn epsilon_net_ty() -> Expr {
358    arrow(list_point2d_ty(), arrow(real_ty(), list_point2d_ty()))
359}
360/// `HausslerPackingLemma : Nat -> Nat -> Nat`
361/// Haussler packing lemma: upper bound on number of distinct projections
362pub fn haussler_packing_lemma_ty() -> Expr {
363    arrow(nat_ty(), arrow(nat_ty(), nat_ty()))
364}
365/// `EpsilonNetBound : Nat -> Real -> Nat`
366/// Upper bound on epsilon-net size: O((d/eps) log (d/eps)) for VC-dim d
367pub fn epsilon_net_bound_ty() -> Expr {
368    arrow(nat_ty(), arrow(real_ty(), nat_ty()))
369}
370/// `RangeTree : Nat -> Type`
371/// A range tree for d-dimensional orthogonal range searching
372pub fn range_tree_ty() -> Expr {
373    arrow(nat_ty(), type0())
374}
375/// `RangeTreeQuery : RangeTree -> (Real × Real) -> (Real × Real) -> List Point2D`
376/// Orthogonal range query: report all points in axis-aligned box [x1,x2] × [y1,y2]
377pub fn range_tree_query_ty() -> Expr {
378    arrow(
379        type0(),
380        arrow(
381            pair_ty(real_ty(), real_ty()),
382            arrow(pair_ty(real_ty(), real_ty()), list_point2d_ty()),
383        ),
384    )
385}
386/// `FractionalCascading : List (List Real) -> Real -> List Nat`
387/// Fractional cascading: simultaneously search k sorted arrays in O(k + log n) time
388pub fn fractional_cascading_ty() -> Expr {
389    arrow(list_ty(list_ty(real_ty())), arrow(real_ty(), list_nat_ty()))
390}
391/// `OrthogonalRangeSearching : List Point2D -> (Real × Real) -> (Real × Real) -> List Point2D`
392/// 2D orthogonal range searching using a range tree
393pub fn orthogonal_range_searching_ty() -> Expr {
394    arrow(
395        list_point2d_ty(),
396        arrow(
397            pair_ty(real_ty(), real_ty()),
398            arrow(pair_ty(real_ty(), real_ty()), list_point2d_ty()),
399        ),
400    )
401}
402/// `SegmentTree : Type`
403/// A segment tree storing intervals for stabbing/window queries
404pub fn segment_tree_ty() -> Expr {
405    type0()
406}
407/// `StabbingQuery : SegmentTree -> Real -> List (Real × Real)`
408/// Report all intervals containing the query point
409pub fn stabbing_query_ty() -> Expr {
410    arrow(
411        type0(),
412        arrow(real_ty(), list_ty(pair_ty(real_ty(), real_ty()))),
413    )
414}
415/// `IntervalIntersectionQuery : SegmentTree -> (Real × Real) -> List (Real × Real)`
416/// Report all intervals overlapping a query interval
417pub fn interval_intersection_query_ty() -> Expr {
418    arrow(
419        type0(),
420        arrow(
421            pair_ty(real_ty(), real_ty()),
422            list_ty(pair_ty(real_ty(), real_ty())),
423        ),
424    )
425}
426/// `ConvexLayers : List Point2D -> List (List Point2D)`
427/// Onion peeling: peel successive convex hulls until the set is exhausted
428pub fn convex_layers_ty() -> Expr {
429    arrow(list_point2d_ty(), list_ty(list_point2d_ty()))
430}
431/// `ConvexLayerDepth : List Point2D -> Point2D -> Nat`
432/// The convex layer (depth) of a point: 0 = outermost hull
433pub fn convex_layer_depth_ty() -> Expr {
434    arrow(list_point2d_ty(), arrow(point2d_ty(), nat_ty()))
435}
436/// `TukeyDepth : List Point2D -> Point2D -> Nat`
437/// Tukey (halfspace) depth of a point with respect to a point set
438pub fn tukey_depth_ty() -> Expr {
439    arrow(list_point2d_ty(), arrow(point2d_ty(), nat_ty()))
440}
441/// `KCenterClustering : List Point2D -> Nat -> List Point2D`
442/// k-center clustering: find k centers minimising the maximum cluster radius
443pub fn k_center_clustering_ty() -> Expr {
444    arrow(list_point2d_ty(), arrow(nat_ty(), list_point2d_ty()))
445}
446/// `KMedianClustering : List Point2D -> Nat -> List Point2D`
447/// k-median clustering: find k centers minimising sum of distances
448pub fn k_median_clustering_ty() -> Expr {
449    arrow(list_point2d_ty(), arrow(nat_ty(), list_point2d_ty()))
450}
451/// `FacilityLocation : List Point2D -> Real -> List Point2D`
452/// Metric facility location: open facilities to minimise opening cost + assignment cost
453pub fn facility_location_ty() -> Expr {
454    arrow(list_point2d_ty(), arrow(real_ty(), list_point2d_ty()))
455}
456/// `KCenterApproxRatio : Nat`
457/// Best known approximation ratio for k-center: 2 (Gonzalez 1985)
458pub fn k_center_approx_ratio_ty() -> Expr {
459    nat_ty()
460}
461/// `HausdorffDistance : List Point2D -> List Point2D -> Real`
462/// Hausdorff distance: max of directed Hausdorff distances
463pub fn hausdorff_distance_ty() -> Expr {
464    arrow(list_point2d_ty(), arrow(list_point2d_ty(), real_ty()))
465}
466/// `DirectedHausdorff : List Point2D -> List Point2D -> Real`
467/// Directed Hausdorff distance from A to B: max_{a in A} min_{b in B} d(a,b)
468pub fn directed_hausdorff_ty() -> Expr {
469    arrow(list_point2d_ty(), arrow(list_point2d_ty(), real_ty()))
470}
471/// `FrechetDistance : List Point2D -> List Point2D -> Real`
472/// Fréchet distance between two polygonal curves
473pub fn frechet_distance_ty() -> Expr {
474    arrow(list_point2d_ty(), arrow(list_point2d_ty(), real_ty()))
475}
476/// `DiscreteFrechetDistance : List Point2D -> List Point2D -> Real`
477/// Discrete Fréchet distance (dog-leash metric on vertices)
478pub fn discrete_frechet_distance_ty() -> Expr {
479    arrow(list_point2d_ty(), arrow(list_point2d_ty(), real_ty()))
480}
481/// `ShapeMatchingOptimal : List Point2D -> List Point2D -> Real`
482/// Minimum-cost shape matching under rigid motions
483pub fn shape_matching_optimal_ty() -> Expr {
484    arrow(list_point2d_ty(), arrow(list_point2d_ty(), real_ty()))
485}
486/// `ConfigurationSpace : Type -> Type`
487/// The configuration space C-space of a robot: maps robot description to its C-space
488pub fn configuration_space_ty() -> Expr {
489    arrow(type0(), type0())
490}
491/// `FreeSpace : Type -> (Type -> Prop)`
492/// The free configuration space: obstacle-free subset of C-space
493pub fn free_space_ty() -> Expr {
494    arrow(type0(), arrow(type0(), prop()))
495}
496/// `ProbabilisticRoadmap : Type -> Nat -> Type`
497/// A probabilistic roadmap: random samples in free C-space connected by a graph
498pub fn probabilistic_roadmap_ty() -> Expr {
499    arrow(type0(), arrow(nat_ty(), type0()))
500}
501/// `RRTPath : Type -> Point2D -> Point2D -> List Point2D`
502/// Rapidly-exploring random tree path from start to goal in free space
503pub fn rrt_path_ty() -> Expr {
504    arrow(
505        type0(),
506        arrow(point2d_ty(), arrow(point2d_ty(), list_point2d_ty())),
507    )
508}
509/// `MotionPlanningCompleteness : Type -> Prop`
510/// Completeness of a motion planner: finds a path iff one exists
511pub fn motion_planning_completeness_ty() -> Expr {
512    arrow(type0(), prop())
513}
514/// Build the computational geometry kernel environment.
515pub fn build_computational_geometry_env(env: &mut Environment) {
516    let axioms: &[(&str, Expr)] = &[
517        ("Point2D", point2d_kernel_ty()),
518        ("Point3D", point3d_kernel_ty()),
519        ("Segment2D", segment2d_ty()),
520        ("Triangle2D", triangle2d_ty()),
521        ("Polygon2D", polygon2d_ty()),
522        ("ConvexHullFn", convex_hull_fn_ty()),
523        ("IsConvex", is_convex_ty()),
524        ("IsOnConvexHull", is_on_convex_hull_ty()),
525        ("TriangulationFn", triangulation_ty()),
526        ("DelaunayTriangulationFn", delaunay_triangulation_ty()),
527        ("IsDelaunay", is_delaunay_ty()),
528        ("VoronoiCell", voronoi_cell_ty()),
529        ("VoronoiDiagramFn", voronoi_diagram_ty()),
530        ("CircumcircleCenter", circumcircle_center_ty()),
531        ("PointLocationFn", point_location_ty()),
532        ("KdTree", kd_tree_ty()),
533        ("KdTreeQuery", kd_tree_query_ty()),
534        ("NearestNeighbourFn", nearest_neighbour_ty()),
535        ("ClosestPairFn", closest_pair_fn_ty()),
536        ("SegmentsIntersect", segments_intersect_ty()),
537        ("SegmentIntersectionPoint", segment_intersection_point_ty()),
538        ("AnySegmentsIntersect", any_segments_intersect_ty()),
539        ("PolygonArea", polygon_area_ty()),
540        ("PolygonCentroid", polygon_centroid_ty()),
541        ("PointInPolygon", point_in_polygon_ty()),
542        ("PolygonPerimeter", polygon_perimeter_ty()),
543        ("SpatialHash", spatial_hash_ty()),
544        ("SpatialHashInsert", spatial_hash_insert_ty()),
545        ("ConvexHullCorrectness", arrow(list_point2d_ty(), prop())),
546        ("DelaunayMaxMinAngle", arrow(list_point2d_ty(), prop())),
547        ("DelaunayUniqueness", arrow(list_point2d_ty(), prop())),
548        ("VoronoiDelaunayDuality", arrow(list_point2d_ty(), prop())),
549        (
550            "ShamosHoeyCorrectness",
551            arrow(list_ty(segment2d_ty()), prop()),
552        ),
553        (
554            "ClosestPairOptimality",
555            arrow(list_point2d_ty(), arrow(real_ty(), prop())),
556        ),
557        ("GrahamScanComplexity", nat_ty()),
558        ("DelaunayComplexity", nat_ty()),
559        ("FortuneAlgorithmComplexity", nat_ty()),
560        ("HyperplaneTy", hyperplane_ty()),
561        ("HyperplaneArrangementTy", hyperplane_arrangement_ty()),
562        ("ZoneTheoremBound", zone_theorem_bound_ty()),
563        ("ArrangementFaceCount", arrangement_face_count_ty()),
564        ("CellOfArrangement", cell_of_arrangement_ty()),
565        ("AlphaComplex", alpha_complex_ty()),
566        ("CechComplex", cech_complex_ty()),
567        ("PersistenceDiagramTy", persistence_diagram_ty()),
568        ("ComputePersistence", compute_persistence_ty()),
569        ("AlphaShapeFiltration", alpha_shape_filtration_ty()),
570        ("KineticCertificate", kinetic_certificate_ty()),
571        ("KineticTournament", kinetic_tournament_ty()),
572        ("KineticHeap", kinetic_heap_ty()),
573        ("EventQueue", event_queue_ty()),
574        ("KineticConvexHull", kinetic_convex_hull_ty()),
575        ("ParametricSearchProblem", parametric_search_problem_ty()),
576        ("ColeParallelBinarySearch", cole_parallel_binary_search_ty()),
577        ("ParallelBinarySearch", parallel_binary_search_ty()),
578        ("PointHyperplaneDual", point_hyperplane_dual_ty()),
579        ("GaleTransform", gale_transform_ty()),
580        ("DualArrangement", dual_arrangement_ty()),
581        ("SweepLineState", sweep_line_state_ty()),
582        ("ShamosHoeyAlgorithm", shamos_hoey_algorithm_ty()),
583        ("BentleyOttmannOutput", bentley_ottmann_output_ty()),
584        (
585            "BentleyOttmannCorrectness",
586            bentley_ottmann_correctness_ty(),
587        ),
588        ("SteinerPoint", steiner_point_ty()),
589        ("DelaunayRefinement", delaunay_refinement_ty()),
590        ("MinAngleGuarantee", min_angle_guarantee_ty()),
591        ("TriangulationQuality", triangulation_quality_ty()),
592        ("VCDimension", vc_dimension_ty()),
593        ("EpsilonNet", epsilon_net_ty()),
594        ("HausslerPackingLemma", haussler_packing_lemma_ty()),
595        ("EpsilonNetBound", epsilon_net_bound_ty()),
596        ("RangeTree", range_tree_ty()),
597        ("RangeTreeQuery", range_tree_query_ty()),
598        ("FractionalCascading", fractional_cascading_ty()),
599        ("OrthogonalRangeSearching", orthogonal_range_searching_ty()),
600        ("SegmentTree", segment_tree_ty()),
601        ("StabbingQuery", stabbing_query_ty()),
602        (
603            "IntervalIntersectionQuery",
604            interval_intersection_query_ty(),
605        ),
606        ("ConvexLayers", convex_layers_ty()),
607        ("ConvexLayerDepth", convex_layer_depth_ty()),
608        ("TukeyDepth", tukey_depth_ty()),
609        ("KCenterClustering", k_center_clustering_ty()),
610        ("KMedianClustering", k_median_clustering_ty()),
611        ("FacilityLocation", facility_location_ty()),
612        ("KCenterApproxRatio", k_center_approx_ratio_ty()),
613        ("HausdorffDistance", hausdorff_distance_ty()),
614        ("DirectedHausdorff", directed_hausdorff_ty()),
615        ("FrechetDistance", frechet_distance_ty()),
616        ("DiscreteFrechetDistance", discrete_frechet_distance_ty()),
617        ("ShapeMatchingOptimal", shape_matching_optimal_ty()),
618        ("ConfigurationSpace", configuration_space_ty()),
619        ("FreeSpace", free_space_ty()),
620        ("ProbabilisticRoadmap", probabilistic_roadmap_ty()),
621        ("RRTPath", rrt_path_ty()),
622        (
623            "MotionPlanningCompleteness",
624            motion_planning_completeness_ty(),
625        ),
626    ];
627    for (name, ty) in axioms {
628        env.add(Declaration::Axiom {
629            name: Name::str(*name),
630            univ_params: vec![],
631            ty: ty.clone(),
632        })
633        .ok();
634    }
635}
636/// 2D cross product of vectors (p1-p0) × (p2-p0)
637/// Positive: p2 is to the left of p0→p1 (counter-clockwise)
638/// Negative: p2 is to the right (clockwise)
639/// Zero: collinear
640pub fn cross(p0: &Point2D, p1: &Point2D, p2: &Point2D) -> f64 {
641    (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x)
642}
643/// Compute the orientation of three points
644pub fn orientation(p: &Point2D, q: &Point2D, r: &Point2D) -> Orientation {
645    let val = cross(p, q, r);
646    if val > 1e-12 {
647        Orientation::CounterClockwise
648    } else if val < -1e-12 {
649        Orientation::Clockwise
650    } else {
651        Orientation::Collinear
652    }
653}
654/// Compute the convex hull of a set of points using Graham scan.
655/// Returns vertices in counter-clockwise order.
656pub fn graham_scan(points: &[Point2D]) -> Vec<Point2D> {
657    let n = points.len();
658    if n < 3 {
659        return points.to_vec();
660    }
661    let pivot_idx = points
662        .iter()
663        .enumerate()
664        .min_by(|(_, a), (_, b)| {
665            a.y.partial_cmp(&b.y)
666                .unwrap_or(std::cmp::Ordering::Equal)
667                .then(a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal))
668        })
669        .map(|(i, _)| i)
670        .expect("points is non-empty: checked by caller");
671    let pivot = points[pivot_idx];
672    let mut sorted: Vec<Point2D> = points.to_vec();
673    sorted.swap(0, pivot_idx);
674    let p0 = sorted[0];
675    sorted[1..].sort_by(|a, b| {
676        let ca = cross(&p0, a, b);
677        if ca.abs() < 1e-12 {
678            p0.dist_sq(a)
679                .partial_cmp(&p0.dist_sq(b))
680                .unwrap_or(std::cmp::Ordering::Equal)
681        } else if ca > 0.0 {
682            std::cmp::Ordering::Less
683        } else {
684            std::cmp::Ordering::Greater
685        }
686    });
687    let mut hull: Vec<Point2D> = Vec::with_capacity(n);
688    hull.push(sorted[0]);
689    hull.push(sorted[1]);
690    for i in 2..n {
691        while hull.len() >= 2 {
692            let m = hull.len();
693            let cr = cross(&hull[m - 2], &hull[m - 1], &sorted[i]);
694            if cr <= 0.0 {
695                hull.pop();
696            } else {
697                break;
698            }
699        }
700        hull.push(sorted[i]);
701    }
702    let _ = pivot;
703    hull
704}
705/// Compute the convex hull using the Jarvis march (gift wrapping) algorithm.
706/// O(nh) where h is the hull size.
707pub fn jarvis_march(points: &[Point2D]) -> Vec<Point2D> {
708    let n = points.len();
709    if n < 3 {
710        return points.to_vec();
711    }
712    let start = points
713        .iter()
714        .enumerate()
715        .min_by(|(_, a), (_, b)| {
716            a.x.partial_cmp(&b.x)
717                .unwrap_or(std::cmp::Ordering::Equal)
718                .then(a.y.partial_cmp(&b.y).unwrap_or(std::cmp::Ordering::Equal))
719        })
720        .map(|(i, _)| i)
721        .expect("points is non-empty: checked by n < 3 guard");
722    let mut hull = Vec::new();
723    let mut current = start;
724    loop {
725        hull.push(points[current]);
726        let mut next = 0usize;
727        for i in 1..n {
728            if next == current {
729                next = i;
730                continue;
731            }
732            let c = cross(&points[current], &points[next], &points[i]);
733            if c < -1e-12 {
734                next = i;
735            } else if c.abs() <= 1e-12 {
736                if points[current].dist_sq(&points[i]) > points[current].dist_sq(&points[next]) {
737                    next = i;
738                }
739            }
740        }
741        current = next;
742        if current == start {
743            break;
744        }
745        if hull.len() > n {
746            break;
747        }
748    }
749    hull
750}
751/// Find the closest pair of points in O(n log n) time.
752/// Returns (distance, point_a, point_b).
753pub fn closest_pair(points: &[Point2D]) -> Option<(f64, Point2D, Point2D)> {
754    if points.len() < 2 {
755        return None;
756    }
757    let mut sorted_x: Vec<Point2D> = points.to_vec();
758    sorted_x.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
759    let result = closest_pair_rec(&sorted_x);
760    Some(result)
761}
762pub fn closest_pair_rec(pts: &[Point2D]) -> (f64, Point2D, Point2D) {
763    let n = pts.len();
764    if n <= 3 {
765        return brute_force_closest(pts);
766    }
767    let mid = n / 2;
768    let mid_x = pts[mid].x;
769    let (d_left, pa_l, pb_l) = closest_pair_rec(&pts[..mid]);
770    let (d_right, pa_r, pb_r) = closest_pair_rec(&pts[mid..]);
771    let (mut best_d, mut best_a, mut best_b) = if d_left <= d_right {
772        (d_left, pa_l, pb_l)
773    } else {
774        (d_right, pa_r, pb_r)
775    };
776    let strip: Vec<Point2D> = pts
777        .iter()
778        .filter(|p| (p.x - mid_x).abs() < best_d)
779        .cloned()
780        .collect();
781    let mut strip_y = strip.clone();
782    strip_y.sort_by(|a, b| a.y.partial_cmp(&b.y).unwrap_or(std::cmp::Ordering::Equal));
783    for i in 0..strip_y.len() {
784        for j in (i + 1)..strip_y.len() {
785            if strip_y[j].y - strip_y[i].y >= best_d {
786                break;
787            }
788            let d = strip_y[i].dist(&strip_y[j]);
789            if d < best_d {
790                best_d = d;
791                best_a = strip_y[i];
792                best_b = strip_y[j];
793            }
794        }
795    }
796    (best_d, best_a, best_b)
797}
798pub fn brute_force_closest(pts: &[Point2D]) -> (f64, Point2D, Point2D) {
799    let mut best_d = f64::INFINITY;
800    let mut best_a = pts[0];
801    let mut best_b = pts[1.min(pts.len() - 1)];
802    for i in 0..pts.len() {
803        for j in (i + 1)..pts.len() {
804            let d = pts[i].dist(&pts[j]);
805            if d < best_d {
806                best_d = d;
807                best_a = pts[i];
808                best_b = pts[j];
809            }
810        }
811    }
812    (best_d, best_a, best_b)
813}
814/// Compute the signed area of a simple polygon using the shoelace formula.
815/// Positive for CCW, negative for CW.
816pub fn polygon_signed_area(vertices: &[Point2D]) -> f64 {
817    let n = vertices.len();
818    if n < 3 {
819        return 0.0;
820    }
821    let mut area = 0.0f64;
822    for i in 0..n {
823        let j = (i + 1) % n;
824        area += vertices[i].x * vertices[j].y;
825        area -= vertices[j].x * vertices[i].y;
826    }
827    area / 2.0
828}
829/// Compute the (positive) area of a simple polygon.
830pub fn polygon_area(vertices: &[Point2D]) -> f64 {
831    polygon_signed_area(vertices).abs()
832}
833/// Compute the centroid of a simple polygon.
834pub fn polygon_centroid(vertices: &[Point2D]) -> Option<Point2D> {
835    let n = vertices.len();
836    if n < 3 {
837        return None;
838    }
839    let area = polygon_signed_area(vertices);
840    if area.abs() < 1e-14 {
841        return None;
842    }
843    let mut cx = 0.0f64;
844    let mut cy = 0.0f64;
845    for i in 0..n {
846        let j = (i + 1) % n;
847        let cross = vertices[i].x * vertices[j].y - vertices[j].x * vertices[i].y;
848        cx += (vertices[i].x + vertices[j].x) * cross;
849        cy += (vertices[i].y + vertices[j].y) * cross;
850    }
851    let inv = 1.0 / (6.0 * area);
852    Some(Point2D::new(cx * inv, cy * inv))
853}
854/// Compute the perimeter of a polygon.
855pub fn polygon_perimeter(vertices: &[Point2D]) -> f64 {
856    let n = vertices.len();
857    if n < 2 {
858        return 0.0;
859    }
860    (0..n)
861        .map(|i| vertices[i].dist(&vertices[(i + 1) % n]))
862        .sum()
863}
864/// Test if a point is strictly inside a simple polygon using ray casting.
865pub fn point_in_polygon(p: &Point2D, polygon: &[Point2D]) -> bool {
866    let n = polygon.len();
867    if n < 3 {
868        return false;
869    }
870    let mut inside = false;
871    let mut j = n - 1;
872    for i in 0..n {
873        let vi = &polygon[i];
874        let vj = &polygon[j];
875        let crosses_y = (vi.y > p.y) != (vj.y > p.y);
876        if crosses_y {
877            let x_intersect = (vj.x - vi.x) * (p.y - vi.y) / (vj.y - vi.y) + vi.x;
878            if p.x < x_intersect {
879                inside = !inside;
880            }
881        }
882        j = i;
883    }
884    inside
885}
886/// Test if a polygon (given as CCW ordered vertices) is convex.
887pub fn is_convex_polygon(vertices: &[Point2D]) -> bool {
888    let n = vertices.len();
889    if n < 3 {
890        return true;
891    }
892    let mut sign = 0i32;
893    for i in 0..n {
894        let j = (i + 1) % n;
895        let k = (i + 2) % n;
896        let c = cross(&vertices[i], &vertices[j], &vertices[k]);
897        if c > 1e-12 {
898            if sign == -1 {
899                return false;
900            }
901            sign = 1;
902        } else if c < -1e-12 {
903            if sign == 1 {
904                return false;
905            }
906            sign = -1;
907        }
908    }
909    true
910}
911/// Test if two line segments (p1,p2) and (p3,p4) intersect.
912/// Uses orientation-based predicates.
913pub fn segments_intersect(p1: &Point2D, p2: &Point2D, p3: &Point2D, p4: &Point2D) -> bool {
914    let d1 = cross(p3, p4, p1);
915    let d2 = cross(p3, p4, p2);
916    let d3 = cross(p1, p2, p3);
917    let d4 = cross(p1, p2, p4);
918    if ((d1 > 0.0 && d2 < 0.0) || (d1 < 0.0 && d2 > 0.0))
919        && ((d3 > 0.0 && d4 < 0.0) || (d3 < 0.0 && d4 > 0.0))
920    {
921        return true;
922    }
923    if d1.abs() < 1e-12 && on_segment(p3, p4, p1) {
924        return true;
925    }
926    if d2.abs() < 1e-12 && on_segment(p3, p4, p2) {
927        return true;
928    }
929    if d3.abs() < 1e-12 && on_segment(p1, p2, p3) {
930        return true;
931    }
932    if d4.abs() < 1e-12 && on_segment(p1, p2, p4) {
933        return true;
934    }
935    false
936}
937/// Check if point `r` lies on segment (p, q) given collinearity.
938pub fn on_segment(p: &Point2D, q: &Point2D, r: &Point2D) -> bool {
939    r.x >= p.x.min(q.x) - 1e-12
940        && r.x <= p.x.max(q.x) + 1e-12
941        && r.y >= p.y.min(q.y) - 1e-12
942        && r.y <= p.y.max(q.y) + 1e-12
943}
944/// Compute the intersection point of two segments (if they intersect).
945/// Returns None if they are parallel or do not intersect.
946pub fn segment_intersection(
947    p1: &Point2D,
948    p2: &Point2D,
949    p3: &Point2D,
950    p4: &Point2D,
951) -> Option<Point2D> {
952    let denom = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
953    if denom.abs() < 1e-12 {
954        return None;
955    }
956    let t_num = (p1.x - p3.x) * (p3.y - p4.y) - (p1.y - p3.y) * (p3.x - p4.x);
957    let t = t_num / denom;
958    Some(Point2D::new(
959        p1.x + t * (p2.x - p1.x),
960        p1.y + t * (p2.y - p1.y),
961    ))
962}
963/// Test if point `p` is inside or on the circumcircle of triangle (pa, pb, pc).
964/// Assumes the triangle is in CCW order.
965pub fn in_circumcircle(pa: &Point2D, pb: &Point2D, pc: &Point2D, p: &Point2D) -> bool {
966    let ax = pa.x - p.x;
967    let ay = pa.y - p.y;
968    let bx = pb.x - p.x;
969    let by = pb.y - p.y;
970    let cx = pc.x - p.x;
971    let cy = pc.y - p.y;
972    let det = ax * (by * (cx * cx + cy * cy) - cy * (bx * bx + by * by))
973        - ay * (bx * (cx * cx + cy * cy) - cx * (bx * bx + by * by))
974        + (ax * ax + ay * ay) * (bx * cy - by * cx);
975    det > 1e-12
976}
977/// Compute the circumcenter of three points.
978pub fn circumcenter(a: &Point2D, b: &Point2D, c: &Point2D) -> Option<Point2D> {
979    let ax = b.x - a.x;
980    let ay = b.y - a.y;
981    let bx = c.x - a.x;
982    let by = c.y - a.y;
983    let d = 2.0 * (ax * by - ay * bx);
984    if d.abs() < 1e-14 {
985        return None;
986    }
987    let ux = (by * (ax * ax + ay * ay) - ay * (bx * bx + by * by)) / d;
988    let uy = (ax * (bx * bx + by * by) - bx * (ax * ax + ay * ay)) / d;
989    Some(Point2D::new(a.x + ux, a.y + uy))
990}
991/// Bowyer-Watson incremental Delaunay triangulation.
992/// Returns a list of triangles (index triples) for the input point set.
993pub fn delaunay_triangulation(points: &[Point2D]) -> Vec<Triangle> {
994    let n = points.len();
995    if n < 3 {
996        return vec![];
997    }
998    let min_x = points.iter().map(|p| p.x).fold(f64::INFINITY, f64::min);
999    let max_x = points.iter().map(|p| p.x).fold(f64::NEG_INFINITY, f64::max);
1000    let min_y = points.iter().map(|p| p.y).fold(f64::INFINITY, f64::min);
1001    let max_y = points.iter().map(|p| p.y).fold(f64::NEG_INFINITY, f64::max);
1002    let dx = max_x - min_x;
1003    let dy = max_y - min_y;
1004    let delta_max = dx.max(dy);
1005    let mid_x = (min_x + max_x) / 2.0;
1006    let mid_y = (min_y + max_y) / 2.0;
1007    let mut pts: Vec<Point2D> = points.to_vec();
1008    let st0 = Point2D::new(mid_x - 20.0 * delta_max, mid_y - delta_max);
1009    let st1 = Point2D::new(mid_x, mid_y + 20.0 * delta_max);
1010    let st2 = Point2D::new(mid_x + 20.0 * delta_max, mid_y - delta_max);
1011    pts.push(st0);
1012    pts.push(st1);
1013    pts.push(st2);
1014    let si0 = n;
1015    let si1 = n + 1;
1016    let si2 = n + 2;
1017    let mut triangulation: Vec<Triangle> = vec![Triangle::new(si0, si1, si2)];
1018    for i in 0..n {
1019        let p = pts[i];
1020        let mut bad: Vec<Triangle> = Vec::new();
1021        let mut good: Vec<Triangle> = Vec::new();
1022        for &tri in &triangulation {
1023            let ta = pts[tri.a];
1024            let tb = pts[tri.b];
1025            let tc = pts[tri.c];
1026            if in_circumcircle(&ta, &tb, &tc, &p) {
1027                bad.push(tri);
1028            } else {
1029                good.push(tri);
1030            }
1031        }
1032        let mut edges: Vec<(usize, usize)> = Vec::new();
1033        for tri in &bad {
1034            let tri_edges = [(tri.a, tri.b), (tri.b, tri.c), (tri.c, tri.a)];
1035            for &(u, v) in &tri_edges {
1036                let already = edges
1037                    .iter()
1038                    .any(|&(eu, ev)| (eu == u && ev == v) || (eu == v && ev == u));
1039                if already {
1040                    edges.retain(|&(eu, ev)| !((eu == u && ev == v) || (eu == v && ev == u)));
1041                } else {
1042                    edges.push((u, v));
1043                }
1044            }
1045        }
1046        triangulation = good;
1047        for (u, v) in edges {
1048            let pa = pts[u];
1049            let pb = pts[v];
1050            if cross(&pa, &pb, &p) > 0.0 {
1051                triangulation.push(Triangle::new(u, v, i));
1052            } else {
1053                triangulation.push(Triangle::new(v, u, i));
1054            }
1055        }
1056    }
1057    triangulation
1058        .retain(|t| !t.contains_vertex(si0) && !t.contains_vertex(si1) && !t.contains_vertex(si2));
1059    triangulation
1060}
1061/// Build a k-d tree from a slice of points.
1062pub fn build_kd_tree(points: &[Point2D]) -> KdNode {
1063    build_kd_tree_rec(&mut points.to_vec(), 0)
1064}
1065pub fn build_kd_tree_rec(points: &mut Vec<Point2D>, depth: usize) -> KdNode {
1066    if points.is_empty() {
1067        return KdNode::Leaf;
1068    }
1069    let axis = (depth % 2) as u8;
1070    if axis == 0 {
1071        points.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
1072    } else {
1073        points.sort_by(|a, b| a.y.partial_cmp(&b.y).unwrap_or(std::cmp::Ordering::Equal));
1074    }
1075    let median = points.len() / 2;
1076    let point = points[median];
1077    let mut left_pts: Vec<Point2D> = points[..median].to_vec();
1078    let mut right_pts: Vec<Point2D> = points[(median + 1)..].to_vec();
1079    KdNode::Node {
1080        point,
1081        left: Box::new(build_kd_tree_rec(&mut left_pts, depth + 1)),
1082        right: Box::new(build_kd_tree_rec(&mut right_pts, depth + 1)),
1083        axis,
1084    }
1085}
1086/// Find the nearest neighbour in a k-d tree to a query point.
1087pub fn kd_nearest(tree: &KdNode, query: &Point2D) -> Option<Point2D> {
1088    let mut best: Option<(f64, Point2D)> = None;
1089    kd_nearest_rec(tree, query, &mut best);
1090    best.map(|(_, p)| p)
1091}
1092pub fn kd_nearest_rec(node: &KdNode, query: &Point2D, best: &mut Option<(f64, Point2D)>) {
1093    match node {
1094        KdNode::Leaf => {}
1095        KdNode::Node {
1096            point,
1097            left,
1098            right,
1099            axis,
1100        } => {
1101            let d = query.dist(point);
1102            if best.map_or(true, |(bd, _)| d < bd) {
1103                *best = Some((d, *point));
1104            }
1105            let (near, far) =
1106                if (*axis == 0 && query.x < point.x) || (*axis == 1 && query.y < point.y) {
1107                    (left.as_ref(), right.as_ref())
1108                } else {
1109                    (right.as_ref(), left.as_ref())
1110                };
1111            kd_nearest_rec(near, query, best);
1112            let split_diff = if *axis == 0 {
1113                (query.x - point.x).abs()
1114            } else {
1115                (query.y - point.y).abs()
1116            };
1117            if best.map_or(true, |(bd, _)| split_diff < bd) {
1118                kd_nearest_rec(far, query, best);
1119            }
1120        }
1121    }
1122}
1123/// Range query: find all points within distance `r` of the query in the k-d tree.
1124pub fn kd_range_query(tree: &KdNode, query: &Point2D, r: f64) -> Vec<Point2D> {
1125    let mut results = Vec::new();
1126    kd_range_rec(tree, query, r, &mut results);
1127    results
1128}
1129pub fn kd_range_rec(node: &KdNode, query: &Point2D, r: f64, results: &mut Vec<Point2D>) {
1130    match node {
1131        KdNode::Leaf => {}
1132        KdNode::Node {
1133            point,
1134            left,
1135            right,
1136            axis,
1137        } => {
1138            if query.dist(point) <= r {
1139                results.push(*point);
1140            }
1141            let split_diff = if *axis == 0 {
1142                (query.x - point.x).abs()
1143            } else {
1144                (query.y - point.y).abs()
1145            };
1146            kd_range_rec(left, query, r, results);
1147            if split_diff <= r {
1148                kd_range_rec(right, query, r, results);
1149            }
1150        }
1151    }
1152}
1153/// Build a 1D range tree (BST by `key`) from a sorted slice.
1154pub fn build_range_tree_1d(pts: &[(f64, Point2D)]) -> RangeTree1D {
1155    if pts.is_empty() {
1156        return RangeTree1D::Empty;
1157    }
1158    let mid = pts.len() / 2;
1159    let (key, point) = pts[mid];
1160    RangeTree1D::Node {
1161        key,
1162        point,
1163        left: Box::new(build_range_tree_1d(&pts[..mid])),
1164        right: Box::new(build_range_tree_1d(&pts[(mid + 1)..])),
1165    }
1166}
1167/// Query a 1D range tree for all points with key in [lo, hi].
1168pub fn query_range_tree_1d(node: &RangeTree1D, lo: f64, hi: f64, result: &mut Vec<Point2D>) {
1169    match node {
1170        RangeTree1D::Empty => {}
1171        RangeTree1D::Node {
1172            key,
1173            point,
1174            left,
1175            right,
1176        } => {
1177            if *key >= lo && *key <= hi {
1178                result.push(*point);
1179            }
1180            if lo < *key {
1181                query_range_tree_1d(left, lo, hi, result);
1182            }
1183            if hi > *key {
1184                query_range_tree_1d(right, lo, hi, result);
1185            }
1186        }
1187    }
1188}
1189#[cfg(test)]
1190mod tests {
1191    use super::*;
1192    fn pts(coords: &[(f64, f64)]) -> Vec<Point2D> {
1193        coords.iter().map(|&(x, y)| Point2D::new(x, y)).collect()
1194    }
1195    #[test]
1196    fn test_convex_hull_square() {
1197        let points = pts(&[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.5, 0.5)]);
1198        let hull = graham_scan(&points);
1199        assert_eq!(
1200            hull.len(),
1201            4,
1202            "square hull should have 4 points, got {}",
1203            hull.len()
1204        );
1205    }
1206    #[test]
1207    fn test_convex_hull_collinear() {
1208        let points = pts(&[(0.0, 0.0), (1.0, 0.0), (2.0, 0.0), (1.0, 1.0)]);
1209        let hull = graham_scan(&points);
1210        let has_middle = hull
1211            .iter()
1212            .any(|p| (p.x - 1.0).abs() < 1e-9 && p.y.abs() < 1e-9);
1213        assert!(!has_middle, "collinear middle point should not be on hull");
1214    }
1215    #[test]
1216    fn test_polygon_area_unit_square() {
1217        let square = pts(&[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]);
1218        let area = polygon_area(&square);
1219        assert!(
1220            (area - 1.0).abs() < 1e-10,
1221            "unit square area = 1, got {area}"
1222        );
1223    }
1224    #[test]
1225    fn test_polygon_centroid_square() {
1226        let square = pts(&[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]);
1227        let c = polygon_centroid(&square).expect("operation should succeed");
1228        assert!(
1229            (c.x - 0.5).abs() < 1e-10 && (c.y - 0.5).abs() < 1e-10,
1230            "centroid of unit square should be (0.5, 0.5), got ({}, {})",
1231            c.x,
1232            c.y
1233        );
1234    }
1235    #[test]
1236    fn test_point_in_polygon() {
1237        let square = pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]);
1238        assert!(
1239            point_in_polygon(&Point2D::new(2.0, 2.0), &square),
1240            "center should be inside"
1241        );
1242        assert!(
1243            !point_in_polygon(&Point2D::new(5.0, 2.0), &square),
1244            "outside point should not be inside"
1245        );
1246    }
1247    #[test]
1248    fn test_segments_intersect_crossing() {
1249        let p1 = Point2D::new(0.0, 0.0);
1250        let p2 = Point2D::new(2.0, 2.0);
1251        let p3 = Point2D::new(0.0, 2.0);
1252        let p4 = Point2D::new(2.0, 0.0);
1253        assert!(
1254            segments_intersect(&p1, &p2, &p3, &p4),
1255            "crossing segments should intersect"
1256        );
1257    }
1258    #[test]
1259    fn test_segments_no_intersect_parallel() {
1260        let p1 = Point2D::new(0.0, 0.0);
1261        let p2 = Point2D::new(1.0, 0.0);
1262        let p3 = Point2D::new(0.0, 1.0);
1263        let p4 = Point2D::new(1.0, 1.0);
1264        assert!(
1265            !segments_intersect(&p1, &p2, &p3, &p4),
1266            "parallel segments should not intersect"
1267        );
1268    }
1269    #[test]
1270    fn test_closest_pair() {
1271        let points = pts(&[(0.0, 0.0), (5.0, 5.0), (1.0, 0.1), (10.0, 10.0)]);
1272        let (d, _a, _b) = closest_pair(&points).expect("operation should succeed");
1273        let expected = ((0.0 - 1.0f64).powi(2) + (0.0 - 0.1f64).powi(2)).sqrt();
1274        assert!(
1275            (d - expected).abs() < 1e-9,
1276            "closest pair distance: expected {expected}, got {d}"
1277        );
1278    }
1279    #[test]
1280    fn test_kd_tree_nearest() {
1281        let points = pts(&[(0.0, 0.0), (3.0, 4.0), (1.0, 1.0), (7.0, 2.0)]);
1282        let tree = build_kd_tree(&points);
1283        let query = Point2D::new(1.1, 1.1);
1284        let nearest = kd_nearest(&tree, &query).expect("Point2D::new should succeed");
1285        assert!(
1286            (nearest.x - 1.0).abs() < 1e-9 && (nearest.y - 1.0).abs() < 1e-9,
1287            "nearest to (1.1,1.1) should be (1.0,1.0), got ({},{})",
1288            nearest.x,
1289            nearest.y
1290        );
1291    }
1292    #[test]
1293    fn test_bentley_ottmann_events_no_intersection() {
1294        let segs = vec![
1295            (Point2D::new(0.0, 0.0), Point2D::new(2.0, 0.0)),
1296            (Point2D::new(0.0, 1.0), Point2D::new(2.0, 1.0)),
1297        ];
1298        let eq = BentleyOttmannEvents::new(&segs);
1299        let intersections = eq.all_intersections();
1300        assert!(
1301            intersections.is_empty(),
1302            "parallel segments should not intersect"
1303        );
1304    }
1305    #[test]
1306    fn test_bentley_ottmann_events_crossing() {
1307        let segs = vec![
1308            (Point2D::new(0.0, 0.0), Point2D::new(2.0, 2.0)),
1309            (Point2D::new(0.0, 2.0), Point2D::new(2.0, 0.0)),
1310        ];
1311        let eq = BentleyOttmannEvents::new(&segs);
1312        let intersections = eq.all_intersections();
1313        assert_eq!(intersections.len(), 1, "exactly one intersection expected");
1314        let p = &intersections[0];
1315        assert!(
1316            (p.x - 1.0).abs() < 1e-9 && (p.y - 1.0).abs() < 1e-9,
1317            "intersection should be (1,1), got ({},{})",
1318            p.x,
1319            p.y
1320        );
1321    }
1322    #[test]
1323    fn test_bentley_ottmann_event_queue_ordering() {
1324        let segs = vec![
1325            (Point2D::new(3.0, 0.0), Point2D::new(5.0, 2.0)),
1326            (Point2D::new(0.0, 1.0), Point2D::new(2.0, 1.0)),
1327        ];
1328        let eq = BentleyOttmannEvents::new(&segs);
1329        assert_eq!(eq.len(), 4, "4 endpoint events expected");
1330    }
1331    #[test]
1332    fn test_alpha_shape_builder_trivial() {
1333        let pts_data = pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0), (2.0, 2.0)]);
1334        let builder = AlphaShapeBuilder::new(&pts_data);
1335        let alphas = builder.critical_alphas();
1336        let max_finite = alphas
1337            .iter()
1338            .filter(|a| a.is_finite())
1339            .copied()
1340            .fold(f64::NEG_INFINITY, f64::max);
1341        if max_finite > 0.0 {
1342            let step = builder.at_alpha(max_finite);
1343            assert!(
1344                !step.triangles.is_empty(),
1345                "should have at least one triangle at max finite alpha={max_finite}"
1346            );
1347        }
1348        let step_inf = builder.at_alpha(f64::INFINITY);
1349        let _ = step_inf;
1350    }
1351    #[test]
1352    fn test_alpha_shape_builder_filtration_monotone() {
1353        let pts_data = pts(&[(0.0, 0.0), (1.0, 0.0), (0.5, 1.0), (0.5, 0.3)]);
1354        let builder = AlphaShapeBuilder::new(&pts_data);
1355        let filt = builder.filtration();
1356        let mut prev_count = 0usize;
1357        for step in &filt {
1358            assert!(
1359                step.triangles.len() >= prev_count,
1360                "filtration must be monotone: at alpha={} have {} triangles, prev {}",
1361                step.alpha,
1362                step.triangles.len(),
1363                prev_count
1364            );
1365            prev_count = step.triangles.len();
1366        }
1367    }
1368    #[test]
1369    fn test_range_tree_2d_basic() {
1370        let points = pts(&[(1.0, 1.0), (2.0, 3.0), (3.0, 2.0), (5.0, 5.0), (4.0, 1.0)]);
1371        let tree = RangeTree2D::new(&points);
1372        let mut result = tree.query(1.5, 3.5, 1.5, 3.5);
1373        result.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
1374        assert_eq!(
1375            result.len(),
1376            2,
1377            "should find 2 points in [1.5,3.5]x[1.5,3.5], got {:?}",
1378            result
1379        );
1380    }
1381    #[test]
1382    fn test_range_tree_2d_via_tree() {
1383        let points = pts(&[(1.0, 2.0), (3.0, 4.0), (5.0, 6.0), (2.0, 1.0)]);
1384        let tree = RangeTree2D::new(&points);
1385        let result = tree.query_via_tree(1.0, 3.0, 1.0, 4.0);
1386        assert!(!result.is_empty(), "should find points in range");
1387    }
1388    #[test]
1389    fn test_range_tree_2d_empty_result() {
1390        let points = pts(&[(10.0, 10.0), (20.0, 20.0)]);
1391        let tree = RangeTree2D::new(&points);
1392        let result = tree.query(0.0, 5.0, 0.0, 5.0);
1393        assert!(result.is_empty(), "no points in small range");
1394    }
1395    #[test]
1396    fn test_frechet_distance_identical_curves() {
1397        let curve = pts(&[(0.0, 0.0), (1.0, 0.0), (2.0, 0.0)]);
1398        let d = FrechetDistanceApprox::compute(&curve, &curve);
1399        assert!(
1400            d < 1e-9,
1401            "Fréchet distance between identical curves should be 0, got {d}"
1402        );
1403    }
1404    #[test]
1405    fn test_frechet_distance_parallel_curves() {
1406        let p = pts(&[(0.0, 0.0), (1.0, 0.0), (2.0, 0.0)]);
1407        let q = pts(&[(0.0, 1.0), (1.0, 1.0), (2.0, 1.0)]);
1408        let d = FrechetDistanceApprox::compute(&p, &q);
1409        assert!(
1410            (d - 1.0).abs() < 1e-9,
1411            "parallel curves at distance 1.0 should have Fréchet distance 1.0, got {d}"
1412        );
1413    }
1414    #[test]
1415    fn test_frechet_decision_true() {
1416        let p = pts(&[(0.0, 0.0), (1.0, 0.0)]);
1417        let q = pts(&[(0.0, 0.5), (1.0, 0.5)]);
1418        assert!(
1419            FrechetDistanceApprox::decide(&p, &q, 1.0),
1420            "Fréchet distance should be <= 1.0"
1421        );
1422    }
1423    #[test]
1424    fn test_frechet_decision_false() {
1425        let p = pts(&[(0.0, 0.0), (1.0, 0.0)]);
1426        let q = pts(&[(0.0, 2.0), (1.0, 2.0)]);
1427        assert!(
1428            !FrechetDistanceApprox::decide(&p, &q, 1.5),
1429            "Fréchet distance 2.0 should not be <= 1.5"
1430        );
1431    }
1432    #[test]
1433    fn test_convex_layer_peeler_basic() {
1434        let points = pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0), (2.0, 2.0)]);
1435        let mut peeler = ConvexLayerPeeler::new(&points);
1436        peeler.peel_all();
1437        assert!(
1438            peeler.num_layers() >= 2,
1439            "should have at least 2 layers, got {}",
1440            peeler.num_layers()
1441        );
1442    }
1443    #[test]
1444    fn test_convex_layer_peeler_all_hull() {
1445        let points = pts(&[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]);
1446        let mut peeler = ConvexLayerPeeler::new(&points);
1447        peeler.peel_all();
1448        assert_eq!(peeler.num_layers(), 1, "all hull points = 1 layer");
1449    }
1450    #[test]
1451    fn test_convex_layer_peeler_depth() {
1452        let points = pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0), (2.0, 2.0)]);
1453        let mut peeler = ConvexLayerPeeler::new(&points);
1454        peeler.peel_all();
1455        let inner = Point2D::new(2.0, 2.0);
1456        let d = peeler.depth_of(&inner);
1457        assert!(
1458            d.map_or(false, |depth| depth >= 1),
1459            "interior point should have depth >= 1"
1460        );
1461    }
1462    #[test]
1463    fn test_build_computational_geometry_env_extended() {
1464        use oxilean_kernel::Environment;
1465        let mut env = Environment::new();
1466        build_computational_geometry_env(&mut env);
1467        for name in [
1468            "AlphaComplex",
1469            "CechComplex",
1470            "KineticTournament",
1471            "BentleyOttmannOutput",
1472            "VCDimension",
1473            "EpsilonNet",
1474            "RangeTree",
1475            "FractionalCascading",
1476            "HausdorffDistance",
1477            "FrechetDistance",
1478            "ConfigurationSpace",
1479            "ConvexLayers",
1480            "KCenterClustering",
1481            "DelaunayRefinement",
1482        ] {
1483            assert!(
1484                env.get(&oxilean_kernel::Name::str(name)).is_some(),
1485                "axiom '{}' should be registered",
1486                name
1487            );
1488        }
1489    }
1490}
1491#[allow(dead_code)]
1492pub fn cross_2d(o: (f64, f64), a: (f64, f64), b: (f64, f64)) -> f64 {
1493    (a.0 - o.0) * (b.1 - o.1) - (a.1 - o.1) * (b.0 - o.0)
1494}
1495#[cfg(test)]
1496mod tests_cg_extra {
1497    use super::*;
1498    #[test]
1499    fn test_voronoi_nearest_site() {
1500        let v = VoronoiDiagram::new(vec![(0.0, 0.0), (5.0, 0.0), (2.5, 5.0)]);
1501        let nearest = v
1502            .nearest_site((1.0, 0.0))
1503            .expect("VoronoiDiagram::new should succeed");
1504        assert_eq!(nearest, 0, "Nearest to (1,0) should be site 0 at origin");
1505    }
1506    #[test]
1507    fn test_convex_hull() {
1508        let points = vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.5, 0.5)];
1509        let hull = ConvexHull2D::compute(points);
1510        assert_eq!(hull.n_hull_points(), 4, "Square should have 4 hull points");
1511        let area = hull.area();
1512        assert!((area - 1.0).abs() < 1e-9, "Area should be 1.0, got {area}");
1513    }
1514    #[test]
1515    fn test_minkowski_sum() {
1516        let square = vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)];
1517        let ms = MinkowskiSum::new(square.clone(), square.clone());
1518        assert_eq!(ms.result_size_upper_bound(), 8);
1519        assert!(ms.is_convex_if_inputs_convex());
1520    }
1521    #[test]
1522    fn test_plane_subdivision_euler() {
1523        let mut sub = PlaneSubdivision::new(vec![(0.0, 0.0), (1.0, 0.0), (0.5, 1.0)]);
1524        sub.add_edge(0, 1);
1525        sub.add_edge(1, 2);
1526        sub.add_edge(2, 0);
1527        assert_eq!(sub.n_faces(), 2);
1528    }
1529}