From 517362fdaad51865f6387b4637651f608c2e6567 Mon Sep 17 00:00:00 2001 From: beam Date: Mon, 26 Oct 2026 22:48:49 +0000 Subject: [PATCH] beam: interval overlap sweep-line with half-open + zero-length handling --- wizard-work/beam/interval_overlap.py | 87 ++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 wizard-work/beam/interval_overlap.py diff --git a/wizard-work/beam/interval_overlap.py b/wizard-work/beam/interval_overlap.py new file mode 100644 index 0000000..01d5f3a --- /dev/null +++ b/wizard-work/beam/interval_overlap.py @@ -0,0 +1,87 @@ +"""Interval overlap detection via sweep-line (O(n log n)). + +Given a list of half-open intervals [start, end), returns all unordered +pairs (i, j) with i < j whose intervals intersect (share at least one +point). Half-open semantics: [1,2) and [2,3) do NOT overlap. + +Also exposes `count_overlaps` for the cheaper "is any overlap" style query. +""" +from __future__ import annotations +from typing import Iterable, List, Tuple + + +Interval = Tuple[int, int] + + +def find_overlapping_pairs(intervals: Iterable[Interval]) -> List[Tuple[int, int]]: + """Return sorted list of (i, j) index pairs (i= e: # zero-length interval: empty set, never overlaps + continue + for other in active: + a, b = (idx, other) if idx < other else (other, idx) + pairs.add((a, b)) + active.add(idx) + else: # end: remove + active.discard(idx) + return sorted(pairs) + + +def has_any_overlap(intervals: Iterable[Interval]) -> bool: + return bool(find_overlapping_pairs(intervals)) + + +def brute_force_pairs(intervals: Iterable[Interval]) -> List[Tuple[int, int]]: + iv = list(intervals) + n = len(iv) + out = [] + for i in range(n): + for j in range(i + 1, n): + s1, e1 = iv[i] + s2, e2 = iv[j] + # half-open overlap: max(starts) < min(ends) + if max(s1, s2) < min(e1, e2): + out.append((i, j)) + return out + + +if __name__ == "__main__": + import random + random.seed(42) + for trial in range(2000): + n = random.randint(0, 12) + ivs = [] + for _ in range(n): + a = random.randint(-5, 5) + b = a + random.randint(0, 6) + ivs.append((a, b)) + got = find_overlapping_pairs(ivs) + exp = brute_force_pairs(ivs) + if got != exp: + print("MISMATCH", ivs, got, exp) + raise SystemExit(1) + print("all 2000 random trials passed") + # edge cases + assert find_overlapping_pairs([(1, 2), (2, 3)]) == [] + assert find_overlapping_pairs([(1, 3), (2, 4)]) == [(0, 1)] + assert find_overlapping_pairs([]) == [] + assert find_overlapping_pairs([(5, 5)]) == [] # zero-length never overlaps + print("edge cases passed")