diff --git a/2021/src/common.rs b/2021/src/common.rs index 600cd04..751c8e4 100644 --- a/2021/src/common.rs +++ b/2021/src/common.rs @@ -101,3 +101,38 @@ where } } } + +pub struct BitSet { + buffer: Vec, +} + +impl BitSet { + pub fn new() -> Self { + Self::with_capacity(0) + } + + pub fn with_capacity(capacity: usize) -> Self { + let buffer = Vec::with_capacity(capacity); + + Self { buffer } + } + + pub fn insert(&mut self, value: usize) -> bool { + let chunk = value / 32; + let bit = 1 << (31 - (value % 32)); + + if self.buffer.len() <= chunk + 1 { + self.buffer.resize(chunk + 1, 0); + } + + let not_present = self.buffer[chunk] & bit; + + self.buffer[chunk] |= bit; + + not_present == 0 + } + + pub fn len(&self) -> usize { + self.buffer.iter().map(|c| c.count_ones() as usize).sum() + } +} diff --git a/2021/src/day05.rs b/2021/src/day05.rs index 445c5b1..73286a6 100644 --- a/2021/src/day05.rs +++ b/2021/src/day05.rs @@ -1,69 +1,91 @@ -use std::collections::HashMap; use std::io::Read; use std::iter::repeat; use nom::bytes::complete::tag; -use nom::sequence::tuple; -use nom::Finish; +use nom::character::complete::newline; +use nom::combinator::map; +use nom::multi::separated_list1; +use nom::sequence::separated_pair; use nom::IResult; use crate::common::ordered; -use crate::common::LineIter; +use crate::common::read_input; +use crate::common::BitSet; type Coord = (u16, u16); -fn coordinates(input: &str) -> IResult<&str, Coord> { - use nom::character::complete; +fn coordinates(input: &[u8]) -> IResult<&[u8], Coord> { + use nom::character::complete::char; + use nom::character::complete::u16; - let (input, (x, _, y)) = tuple((complete::u16, complete::char(','), complete::u16))(input)?; - - Ok((input, (x, y))) + separated_pair(u16, char(','), u16)(input) } -fn line_definition(input: &str) -> IResult<&str, (Coord, Coord)> { - let (input, (begin, _, end)) = tuple((coordinates, tag(" -> "), coordinates))(input)?; +fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<(Coord, Coord)>> { + let read_line = map( + separated_pair(coordinates, tag(" -> "), coordinates), + |(begin, end)| ordered(begin, end), + ); - // Sorting the coordinates saves trouble later - Ok((input, ordered(begin, end))) + separated_list1(newline, read_line)(input) } fn stripe( - map: &mut HashMap, + once: &mut BitSet, + twice: &mut BitSet, + width: usize, xs: impl Iterator, ys: impl Iterator, ) { for (x, y) in xs.zip(ys) { - *map.entry((x, y)).or_default() += 1; + let index = x as usize + y as usize * width; + if !once.insert(index) { + twice.insert(index); + } } } fn part_common(input: &mut dyn Read, diagonals: bool) -> String { - let mut reader = LineIter::new(input); - let mut map = HashMap::new(); + let lines = read_input(input, parse_input); - while let Some(line) = reader.next() { - let (begin, end) = line_definition(line).finish().unwrap().1; + let width = lines.iter().map(|&((_, x_max), _)| x_max).max().unwrap() as usize + 1; + let mut once_map = BitSet::new(); + let mut twice_map = BitSet::new(); + + for (begin, end) in lines { if begin.0 == end.0 { let y_range = begin.1..=end.1; - stripe(&mut map, repeat(begin.0), y_range); + stripe( + &mut once_map, + &mut twice_map, + width, + repeat(begin.0), + y_range, + ); } else if begin.1 == end.1 { let x_range = begin.0..=end.0; - stripe(&mut map, x_range, repeat(begin.1)); + stripe( + &mut once_map, + &mut twice_map, + width, + x_range, + repeat(begin.1), + ); } else if diagonals { let x_range = begin.0..=end.0; let y_range = (begin.1.min(end.1))..=(begin.1.max(end.1)); if begin.1 > end.1 { // For a downward slope we need to reverse Y - stripe(&mut map, x_range, y_range.rev()); + stripe(&mut once_map, &mut twice_map, width, x_range, y_range.rev()); } else { - stripe(&mut map, x_range, y_range); + stripe(&mut once_map, &mut twice_map, width, x_range, y_range); } } } - map.values().filter(|&&v| v > 1).count().to_string() + twice_map.len().to_string() } pub fn part1(input: &mut dyn Read) -> String { @@ -82,11 +104,6 @@ mod tests { const SAMPLE: &[u8] = include_bytes!("samples/05.txt"); - #[test] - fn test_parser() { - assert_eq!(line_definition("6,4 -> 2,0"), Ok(("", ((2, 0), (6, 4))))); - } - #[test] fn sample_part1() { test_implementation(part1, SAMPLE, 5) diff --git a/2021/src/day16.rs b/2021/src/day16.rs index 6aadc8a..2ebdef1 100644 --- a/2021/src/day16.rs +++ b/2021/src/day16.rs @@ -182,7 +182,7 @@ mod tests { fn sample_part1() { let answers = [16, 12, 23, 31]; - for (&sample, answer) in SAMPLE.into_iter().zip(answers) { + for (&sample, answer) in SAMPLE.iter().zip(answers) { test_implementation(part1, sample, answer); } } diff --git a/2021/src/lib.rs b/2021/src/lib.rs index a1a530a..5b712aa 100644 --- a/2021/src/lib.rs +++ b/2021/src/lib.rs @@ -92,7 +92,7 @@ pub fn get_implementation(day: usize, part2: bool) -> Solution { #[cfg(test)] fn test_implementation(solution: Solution, data: &[u8], answer: impl ToString) { - let result = solution(&mut &data[..]); + let result = solution(&mut &*data); assert_eq!(answer.to_string(), result); }