Skip to content

Commit

Permalink
Introduced BinaryNumberLines struct
Browse files Browse the repository at this point in the history
To encapsulate the implementation with a single BitVec, and provide the needed functions
  • Loading branch information
shrugalic committed Jan 1, 2023
1 parent e1544d0 commit 319f89f
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 60 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
# IntelliJ folder
**/.idea
**/.DS_Store
**/dhat-heap.json
138 changes: 78 additions & 60 deletions src/day03.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,80 +21,98 @@ fn main() {
}

pub fn day03_part1() -> u32 {
let (width, bits) = parse(INPUT);
gamma_times_epsilon(width, bits)
let numbers = BinaryNumberLines::from(INPUT);
numbers.gamma_times_epsilon()
}

pub fn day03_part2() -> u32 {
let (width, bits) = parse(INPUT);
reduce_bits(width, bits)
let mut numbers = BinaryNumberLines::from(INPUT);
numbers.calculate_life_support_rating()
}

fn gamma_times_epsilon(width: usize, bits: BitVec) -> u32 {
let mut gamma = BitVec::with_capacity(width as usize);
for i in 0..width {
let (ones, zeroes) = count_ones_and_zeroes_at_index(width, &bits, i);
gamma.push(ones >= zeroes);
impl BinaryNumberLines {
fn gamma_times_epsilon(&self) -> u32 {
let gamma = self.decimal_value_of_most_frequent_bit_at_each_position();
let epsilon = (1 << self.line_width) - 1 - gamma; // complement of gamma
gamma * epsilon
}
fn decimal_value_of_most_frequent_bit_at_each_position(&self) -> u32 {
let mut gamma = BitVec::with_capacity(self.line_width as usize);
for i in 0..self.line_width {
let (ones, zeroes) = self.count_ones_and_zeroes_at_index(i, &self.bits);
gamma.push(ones >= zeroes);
}
to_decimal(&gamma)
}
fn count_ones_and_zeroes_at_index(&self, i: usize, bits: &BitSlice) -> (usize, usize) {
let ones = bits.chunks(self.line_width).filter(|&bits| bits[i]).count();
let zeroes = self.line_count() - ones;
(ones, zeroes)
}
let gamma = to_decimal(&gamma);
let epsilon = (1 << width) - 1 - gamma; // complement of gamma
gamma * epsilon
}

fn count_ones_and_zeroes_at_index(width: usize, bits: &BitSlice, i: usize) -> (usize, usize) {
let ones = bits.chunks(width).filter(|&bits| bits[i]).count();
let zeroes = bits.len() / width - ones;
(ones, zeroes)
}

fn reduce_bits(width: usize, bits: BitVec) -> u32 {
let og_rating = reduce(width, bits.clone(), |ones, zeroes| ones >= zeroes);
let cs_rating = reduce(width, bits, |ones, zeroes| ones < zeroes);
og_rating * cs_rating
}

type Filter = fn(usize, usize) -> bool;
fn reduce(width: usize, mut bits: BitVec, wanted: Filter) -> u32 {
let mut i = 0;
let mut start;
let mut end = bits.len();
while end > width {
let (ones, zeroes) = count_ones_and_zeroes_at_index(width, &bits[0..end], i);
start = 0;
while start < end {
if bits[start + i] == wanted(ones, zeroes) {
// good -> keep this chunk, and check the next
start += width;
} else {
// bad -> move the end one chunk to the left, and make sure this chunk is after it
// (if it's not already the last one, swap it with the one after the new end)
end -= width;
if start < end {
for k in 0..width {
bits.swap(start + k, end + k);
}
}
fn calculate_life_support_rating(&mut self) -> u32 {
let oxygen_rating = self.reduce_to_single_line_value(|ones, zeroes| ones >= zeroes);
let co2_scrubber_rating = self.reduce_to_single_line_value(|ones, zeroes| ones < zeroes);
oxygen_rating * co2_scrubber_rating
}
fn reduce_to_single_line_value(&mut self, wanted: Filter) -> u32 {
let mut line_indices: Vec<_> = (0..self.line_count()).into_iter().collect();
for bit_idx in 0..self.line_width {
let (ones, zeroes) = self.count_ones_and_zeroes_of_lines(&line_indices, &bit_idx);
line_indices.retain(|l| wanted(ones, zeroes) == self.bit_at(l, &bit_idx));
if line_indices.len() == 1 {
return to_decimal(self.line_at(&line_indices[0]));
}
}
i += 1;
unreachable!()
}
fn count_ones_and_zeroes_of_lines(
&self,
line_idx: &[usize],
bit_idx: &usize,
) -> (usize, usize) {
line_idx.iter().fold((0, 0), |(ones, zeroes), line_idx| {
if self.bit_at(line_idx, bit_idx) {
(ones + 1, zeroes)
} else {
(ones, zeroes + 1)
}
})
}
fn line_at(&self, line_idx: &usize) -> &BitSlice {
&self.bits[line_idx * self.line_width..line_idx * self.line_width + self.line_width]
}
fn bit_at(&self, line_idx: &usize, bit_idx: &usize) -> bool {
self.line_at(line_idx)[*bit_idx]
}
fn line_count(&self) -> usize {
self.bits.len() / self.line_width
}
to_decimal(&bits[0..end])
}

type Filter = fn(usize, usize) -> bool;

fn to_decimal(bits: &BitSlice) -> u32 {
bits.iter()
.map(|bit| bit.as_u32())
.fold(0, |a, i| (a << 1) + i)
}

fn parse(input: &str) -> (usize, BitVec) {
let mut lines_iter = input.trim().lines().peekable();
let width = lines_iter.peek().unwrap().chars().count();
let bits = lines_iter
.flat_map(|line| line.chars())
.map(|c| c == '1')
.collect::<BitVec>();
(width, bits)
struct BinaryNumberLines {
bits: BitVec,
line_width: usize,
}

impl From<&str> for BinaryNumberLines {
fn from(input: &str) -> Self {
let mut lines_iter = input.trim().lines().peekable();
let line_width = lines_iter.peek().unwrap().chars().count();
let bits = lines_iter
.flat_map(|line| line.chars())
.map(|c| c == '1')
.collect::<BitVec>();
BinaryNumberLines { bits, line_width }
}
}

#[cfg(test)]
Expand All @@ -117,14 +135,14 @@ mod tests {

#[test]
fn example1() {
let (width, bits) = parse(EXAMPLE);
assert_eq!(22 * 9, gamma_times_epsilon(width, bits));
let numbers = BinaryNumberLines::from(EXAMPLE);
assert_eq!(22 * 9, numbers.gamma_times_epsilon());
}

#[test]
fn example2() {
let (width, bits) = parse(EXAMPLE);
assert_eq!(23 * 10, reduce_bits(width, bits));
let mut numbers = BinaryNumberLines::from(EXAMPLE);
assert_eq!(23 * 10, numbers.calculate_life_support_rating());
}

#[test]
Expand Down

0 comments on commit 319f89f

Please sign in to comment.