use nom::{self, bytes::complete::tag, character::complete, sequence::separated_pair, IResult}; use std::collections::HashSet; const INPUT: &str = include_str!("../data/day4.txt"); pub fn part1() -> String { format!("{}", count_full_containments(input(INPUT))) } pub fn part2() -> String { format!("{}", count_overlaps(input(INPUT))) } fn count_full_containments(pairings: Vec) -> u32 { pairings.into_iter().fold(0, |cur, pairing| { if pairing.has_full_containment() { cur + 1 } else { cur } }) } fn count_overlaps(pairings: Vec) -> u32 { pairings.into_iter().fold( 0, |cur, pairing| { if pairing.has_overlap() { cur + 1 } else { cur } }, ) } struct ElfPairing { elf1: Vec, elf2: Vec, } impl ElfPairing { fn has_full_containment(&self) -> bool { let elf1 = self.elf1.iter().cloned().collect::>(); let elf2 = self.elf2.iter().cloned().collect::>(); elf1.is_subset(&elf2) || elf2.is_subset(&elf1) } fn has_overlap(&self) -> bool { let element_count = self.elf1.len() + self.elf2.len(); let combined_set = self .elf1 .iter() .chain(self.elf2.iter()) .cloned() .collect::>(); combined_set.len() < element_count } } fn input(data: &str) -> Vec { data.lines() .map(|line| { let (_, pairing) = elf_pair(line).unwrap(); pairing }) .collect::>() } fn elf_pair(input: &str) -> IResult<&str, ElfPairing> { let (input, elf1) = elf_range(input)?; let (input, _) = tag(",")(input)?; let (input, elf2) = elf_range(input)?; Ok((input, ElfPairing { elf1, elf2 })) } fn elf_range(input: &str) -> IResult<&str, Vec> { let (input, start) = complete::u32(input)?; let (input, _) = tag("-")(input)?; let (input, end) = complete::u32(input)?; Ok((input, (start..end + 1).collect::>())) } #[cfg(test)] mod test { use super::*; const TEST_DATA: &str = "2-4,6-8 2-3,4-5 5-7,7-9 2-8,3-7 6-6,4-6 2-6,4-8"; #[test] fn it_gets_a_hashset_from_a_range() { assert_eq!(vec![2, 3, 4], (2..5).collect::>()); } fn with_input(test: F) where F: Fn(Vec) -> () + std::panic::UnwindSafe, { test(input(TEST_DATA)); } #[test] fn it_parses_input_correctly() { with_input(|pairings| { assert_eq!(vec![2, 3, 4], pairings[0].elf1); assert_eq!(vec![6, 7, 8], pairings[0].elf2); assert_eq!(vec![6], pairings[4].elf1); assert_eq!(vec![4, 5, 6], pairings[4].elf2); }); } #[test] fn it_checks_full_containment() { with_input(|pairings| { assert!(!pairings[0].has_full_containment()); assert!(!pairings[1].has_full_containment()); assert!(!pairings[2].has_full_containment()); assert!(pairings[3].has_full_containment()); assert!(pairings[4].has_full_containment()); assert!(!pairings[5].has_full_containment()); }); } #[test] fn it_counts_full_containments() { with_input(|pairings| { assert_eq!(2, count_full_containments(pairings)); }); } #[test] fn it_checks_overlap() { with_input(|pairings| { assert!(!pairings[0].has_overlap()); assert!(!pairings[1].has_overlap()); assert!(pairings[2].has_overlap()); assert!(pairings[3].has_overlap()); assert!(pairings[4].has_overlap()); assert!(pairings[5].has_overlap()); }); } }