1. Formal Invariant — Exact Orientation & Segment Intersection Predicates
Invariant: The CCW Orientation Determinant
Given three points $A(x_A, y_A)$, $B(x_B, y_B)$, and $C(x_C, y_C)$ in $\mathbb{Z}^2$, define the displacement vectors $\vec{AB} = B - A$ and $\vec{AC} = C - A$. The 2D cross product (twice the signed area of $\triangle ABC$) is:
$$\text{cross}(A, B, C) = \det \begin{pmatrix} x_B - x_A & y_B - y_A \\ x_C - x_A & y_C - y_A \end{pmatrix} = (x_B - x_A)(y_C - y_A) - (y_B - y_A)(x_C - x_A)$$
- $\text{cross}(A, B, C) > 0 \iff A \to B \to C$ makes a strict Counter-Clockwise (Left) turn.
- $\text{cross}(A, B, C) < 0 \iff A \to B \to C$ makes a strict Clockwise (Right) turn.
- $\text{cross}(A, B, C) = 0 \iff A, B, C$ are collinear. Furthermore, $C$ lies on the closed segment $\overline{AB}$ iff $\text{cross}(A, B, C) = 0$ and $(A - C) \cdot (B - C) \le 0$.
By the Shoelace Theorem (Discrete Green's Theorem), twice the signed area of any simple polygon $P_0, P_1, \dots, P_{N-1}$ is the exact integer sum $\sum_{i=0}^{N-1} (P_i \times P_{(i+1) \bmod N})$.
2. Exact Segment-Segment Intersection Decision Tree
s3 = sgn(cross(C, D, A)), s4 = sgn(cross(C, D, B))"] EvalCCW --> Proper{"s1 * s2 < 0 AND
s3 * s4 < 0 ?"} Proper -->|"Yes"| HitProper["Proper Interior Intersection (True)"] Proper -->|"No"| Collinear{"Does any endpoint lie
on the other segment?
(e.g. s1 == 0 && on_seg(A, B, C))"} Collinear -->|"Yes"| HitDegenerate["Degenerate / Collinear Intersection (True)"] Collinear -->|"No"| Miss["Disjoint Segments (False)"]
3. Why sgn() Normalization Prevents Overflow
If coordinates satisfy $|x|, |y| \le 10^9$, then $\text{cross}(A, B, C)$ is bounded by $4 \times 10^{18}$, which fits inside a signed 64-bit integer (int64_t max $\approx 9.22 \times 10^{18}$). However, if you test opposite orientations by multiplying $\text{cross}(A, B, C) \times \text{cross}(A, B, D) < 0$ directly in int64_t, the product reaches $1.6 \times 10^{37}$ and overflows! Mapping each cross product through $\text{sgn}(v) \in \{-1, 0, +1\}$ first reduces the product to $\{-1, 0, +1\}$ with zero overflow risk.
4. Hardware & Floating-Point Realities
IEEE-754 Catastrophic Cancellation vs Exact Integer Predicates
In IEEE-754 double (53-bit significand), subtracting two nearly equal products $u_x v_y - u_y v_x$ loses all leading matching bits (catastrophic cancellation), which can flip the sign of cross(A, B, C) from positive to negative and cause Convex Hull or Delaunay Triangulation algorithms to enter infinite loops! Production CAD/GIS kernels (CGAL, Jonathan Shewchuk's Adaptive Precision Floating-Point Predicates) use a fast hardware floating-point filter first and fall back to exact multi-word expansion arithmetic only when $|u_x v_y - u_y v_x|$ is smaller than the rounding error bound.
5. Complexity Matrix
Trace 2D cross-product orientation tests cross(O, A, B) = (Ax-Ox)(By-Oy) - (Ay-Oy)(Bx-Ox) to prune clockwise interior vertices and detect sweep-line segment intersections.
Invariant & State
| Geometric Predicate / Algorithm | Time Complexity | Space Complexity | Arithmetic Requirement |
|---|---|---|---|
| 2D CCW / Point-on-Segment / Segment Intersection | O(1) | O(1) | int64_t (for coords $\le 10^9$) |
| Shoelace Polygon Area ($N$ vertices) | O(N) | O(1) | int64_t ($2 \times \text{Area}$ is exact integer) |
| Point-in-Polygon (Winding Number / Ray Casting) | O(N) | O(1) | int64_t cross products |
| Point-in-Convex-Polygon (Binary Search Wedge) | O(log N) | O(1) | int64_t cross products |
6. Production-Grade C++20 Implementation
#include <cstdint>
#include <vector>
#include <cmath>
struct Point {
int64_t x = 0, y = 0;
Point operator-(const Point& o) const { return {x - o.x, y - o.y}; }
Point operator+(const Point& o) const { return {x + o.x, y + o.y}; }
};
inline int64_t dot(const Point& u, const Point& v) {
return u.x * v.x + u.y * v.y;
}
inline int64_t cross(const Point& u, const Point& v) {
return u.x * v.y - u.y * v.x;
}
// Twice the signed area of triangle ABC (>0: CCW, <0: CW, 0: collinear)
inline int64_t cross(const Point& a, const Point& b, const Point& c) {
return cross(b - a, c - a);
}
inline int sgn(int64_t val) {
return (val > 0) - (val < 0);
}
// Returns true iff point p lies on closed segment AB
inline bool on_segment(const Point& a, const Point& b, const Point& p) {
return cross(a, b, p) == 0 && dot(a - p, b - p) <= 0;
}
// Exact closed segment intersection between AB and CD
bool segments_intersect(const Point& a, const Point& b,
const Point& c, const Point& d) {
int s1 = sgn(cross(a, b, c));
int s2 = sgn(cross(a, b, d));
int s3 = sgn(cross(c, d, a));
int s4 = sgn(cross(c, d, b));
// Proper intersection: C,D strictly on opposite sides of AB and vice versa
if (s1 * s2 < 0 && s3 * s4 < 0) return true;
// Collinear / endpoint touching cases
if (s1 == 0 && on_segment(a, b, c)) return true;
if (s2 == 0 && on_segment(a, b, d)) return true;
if (s3 == 0 && on_segment(c, d, a)) return true;
if (s4 == 0 && on_segment(c, d, b)) return true;
return false;
}
// Returns 2 * Area of simple polygon P (always an exact integer!)
int64_t polygon_twice_area(const std::vector<Point>& poly) {
int64_t sum = 0;
const std::size_t n = poly.size();
for (std::size_t i = 0; i < n; ++i) {
sum += cross(poly[i], poly[(i + 1) % n]);
}
return sum < 0 ? -sum : sum;
}
Production Pitfall — Using atan2 for Polar Angle Sorting
Sorting points or vectors around an origin by calling std::atan2(y, x) is both slow ($sim 40$ CPU cycles per transcendental evaluation) and inaccurate for integer coordinates near $10^9$ where distinct rays have identical double angles. Instead, partition vectors by half-plane (half(p) = (p.y > 0 || (p.y == 0 && p.x > 0)) ? 0 : 1) and sort within each half-plane using exact cross products cross(u, v) > 0!