aoc/year2022/
day04.rs

1//! # Camp Cleanup
2//!
3//! This puzzle asks to compute range intersections. To simplify the 2nd part, we can use a trick.
4//! Rather than consider each possible case of intersection (overlapping start, overlapping end,
5//! completely enclosed or completely enclosing) it's simpler to check if two ranges *don't* overlap
6//! then invert.
7//!
8//! If `a` and `b` are the ordered start and end of the first range and `c` and `d` the ordered
9//! start and end of the second range, then if:
10//!
11//! `a > d || c > b`
12//!
13//! the 2 ranges can't overlap. Using [DeMorgan's laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws)
14//! this can be inverted to:
15//!
16//! `a <= d && c <= b`
17//!
18//! to check when two ranges do overlap.
19use crate::util::iter::*;
20use crate::util::parse::*;
21
22type Pairs = [u32; 4];
23
24/// Parse each line into 4 integers.
25///
26/// Notes:
27/// * Extracting integers from redundant text is a very common theme in Advent of Code that
28///   the [`iter_unsigned`] method handles.
29///
30/// [`iter_unsigned`]: ParseOps::iter_unsigned
31pub fn parse(input: &str) -> Vec<Pairs> {
32    input.iter_unsigned().chunk::<4>().collect()
33}
34
35/// Count ranges completely enclosed by each other.
36pub fn part1(input: &[Pairs]) -> usize {
37    input.iter().filter(|[a, b, c, d]| (a >= c && b <= d) || (c >= a && d <= b)).count()
38}
39
40/// Count ranges with any intersection.
41pub fn part2(input: &[Pairs]) -> usize {
42    input.iter().filter(|[a, b, c, d]| a <= d && c <= b).count()
43}