1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//! # Camp Cleanup
//!
//! This puzzle asks to compute range intersections. To simplify the 2nd part, we can use a trick.
//! Rather than consider each possible case of intersection (overlapping start, overlapping end,
//! completely enclosed or completely enclosing) it's simpler to check if two ranges *don't* overlap
//! then invert.
//!
//! If `a` and `b` are the ordered start and end of the first range and `c` and `d` the ordered
//! start and end of the second range, then if:
//!
//! `a > d || c > b`
//!
//! the 2 ranges can't overlap. Using [DeMorgan's laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws)
//! this can be inverted to:
//!
//! `a <= d && c <= b`
//!
//! to check when two ranges do overlap.
use crate::util::iter::*;
use crate::util::parse::*;

type Pairs = [u32; 4];

/// Parse each line into 4 integers.
///
/// Notes:
/// * Extracting integers from redundant text is a very common theme in Advent of Code that
///   the [`iter_unsigned`] method handles.
///
/// [`iter_unsigned`]: ParseOps::iter_unsigned
pub fn parse(input: &str) -> Vec<Pairs> {
    input.iter_unsigned().chunk::<4>().collect()
}

/// Count ranges completely enclosed by each other.
pub fn part1(input: &[Pairs]) -> usize {
    input.iter().filter(|[a, b, c, d]| (a >= c && b <= d) || (c >= a && d <= b)).count()
}

/// Count ranges with any intersection.
pub fn part2(input: &[Pairs]) -> usize {
    input.iter().filter(|[a, b, c, d]| a <= d && c <= b).count()
}