mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Replace hashset with bitset
This commit is contained in:
@@ -101,3 +101,38 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BitSet {
|
||||||
|
buffer: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,69 +1,91 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
|
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::sequence::tuple;
|
use nom::character::complete::newline;
|
||||||
use nom::Finish;
|
use nom::combinator::map;
|
||||||
|
use nom::multi::separated_list1;
|
||||||
|
use nom::sequence::separated_pair;
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
|
|
||||||
use crate::common::ordered;
|
use crate::common::ordered;
|
||||||
use crate::common::LineIter;
|
use crate::common::read_input;
|
||||||
|
use crate::common::BitSet;
|
||||||
|
|
||||||
type Coord = (u16, u16);
|
type Coord = (u16, u16);
|
||||||
|
|
||||||
fn coordinates(input: &str) -> IResult<&str, Coord> {
|
fn coordinates(input: &[u8]) -> IResult<&[u8], Coord> {
|
||||||
use nom::character::complete;
|
use nom::character::complete::char;
|
||||||
|
use nom::character::complete::u16;
|
||||||
|
|
||||||
let (input, (x, _, y)) = tuple((complete::u16, complete::char(','), complete::u16))(input)?;
|
separated_pair(u16, char(','), u16)(input)
|
||||||
|
|
||||||
Ok((input, (x, y)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_definition(input: &str) -> IResult<&str, (Coord, Coord)> {
|
fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<(Coord, Coord)>> {
|
||||||
let (input, (begin, _, end)) = tuple((coordinates, tag(" -> "), coordinates))(input)?;
|
let read_line = map(
|
||||||
|
separated_pair(coordinates, tag(" -> "), coordinates),
|
||||||
|
|(begin, end)| ordered(begin, end),
|
||||||
|
);
|
||||||
|
|
||||||
// Sorting the coordinates saves trouble later
|
separated_list1(newline, read_line)(input)
|
||||||
Ok((input, ordered(begin, end)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stripe(
|
fn stripe(
|
||||||
map: &mut HashMap<Coord, u16>,
|
once: &mut BitSet,
|
||||||
|
twice: &mut BitSet,
|
||||||
|
width: usize,
|
||||||
xs: impl Iterator<Item = u16>,
|
xs: impl Iterator<Item = u16>,
|
||||||
ys: impl Iterator<Item = u16>,
|
ys: impl Iterator<Item = u16>,
|
||||||
) {
|
) {
|
||||||
for (x, y) in xs.zip(ys) {
|
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 {
|
fn part_common(input: &mut dyn Read, diagonals: bool) -> String {
|
||||||
let mut reader = LineIter::new(input);
|
let lines = read_input(input, parse_input);
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
while let Some(line) = reader.next() {
|
let width = lines.iter().map(|&((_, x_max), _)| x_max).max().unwrap() as usize + 1;
|
||||||
let (begin, end) = line_definition(line).finish().unwrap().1;
|
|
||||||
|
|
||||||
|
let mut once_map = BitSet::new();
|
||||||
|
let mut twice_map = BitSet::new();
|
||||||
|
|
||||||
|
for (begin, end) in lines {
|
||||||
if begin.0 == end.0 {
|
if begin.0 == end.0 {
|
||||||
let y_range = begin.1..=end.1;
|
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 {
|
} else if begin.1 == end.1 {
|
||||||
let x_range = begin.0..=end.0;
|
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 {
|
} else if diagonals {
|
||||||
let x_range = begin.0..=end.0;
|
let x_range = begin.0..=end.0;
|
||||||
let y_range = (begin.1.min(end.1))..=(begin.1.max(end.1));
|
let y_range = (begin.1.min(end.1))..=(begin.1.max(end.1));
|
||||||
|
|
||||||
if begin.1 > end.1 {
|
if begin.1 > end.1 {
|
||||||
// For a downward slope we need to reverse Y
|
// 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 {
|
} 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 {
|
pub fn part1(input: &mut dyn Read) -> String {
|
||||||
@@ -82,11 +104,6 @@ mod tests {
|
|||||||
|
|
||||||
const SAMPLE: &[u8] = include_bytes!("samples/05.txt");
|
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]
|
#[test]
|
||||||
fn sample_part1() {
|
fn sample_part1() {
|
||||||
test_implementation(part1, SAMPLE, 5)
|
test_implementation(part1, SAMPLE, 5)
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ mod tests {
|
|||||||
fn sample_part1() {
|
fn sample_part1() {
|
||||||
let answers = [16, 12, 23, 31];
|
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);
|
test_implementation(part1, sample, answer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ pub fn get_implementation(day: usize, part2: bool) -> Solution {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn test_implementation(solution: Solution, data: &[u8], answer: impl ToString) {
|
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);
|
assert_eq!(answer.to_string(), result);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user