advent_of_code/year2021/
day03.rs1use crate::input::Input;
2
3fn set_lowest_bits(n: u8) -> u16 {
7 u16::MAX >> (u16::BITS as u16 - u16::from(n))
8}
9
10fn is_bit_mostly_set(numbers: &[u16], bit_idx: usize) -> bool {
11 numbers.iter().fold(0, |acc, x| {
12 acc + if x & (1 << bit_idx) == 0 { -1 } else { 1 }
13 }) >= 0
14}
15
16fn keep_one_according_to_bitset_criteria(
17 candidates: &mut [u16],
18 num_bits: usize,
19 criteria_wants_most: bool,
20) -> Result<u16, String> {
21 let mut num_candidates = candidates.len();
22 for current_bit_idx in (0..num_bits).rev() {
23 let mostly_set = is_bit_mostly_set(&candidates[0..num_candidates], current_bit_idx);
24 let mut current_candidate_idx = 0;
25 while current_candidate_idx < num_candidates {
26 let is_bit_set = (candidates[current_candidate_idx] & (1 << current_bit_idx)) != 0;
27 if (is_bit_set == mostly_set) == criteria_wants_most {
28 current_candidate_idx += 1;
29 } else {
30 num_candidates -= 1;
31 candidates.swap(current_candidate_idx, num_candidates);
32 }
33 }
34 if num_candidates == 1 {
35 return Ok(candidates[0]);
36 }
37 }
38
39 Err("Bit criteria did not result in single number".to_string())
40}
41
42pub fn solve(input: &Input) -> Result<u32, String> {
43 const MAX_BITS: usize = u16::BITS as usize;
44
45 let num_bits = input.text.lines().next().map(str::len).unwrap_or_default();
46 if num_bits == 0 || num_bits > MAX_BITS {
47 return Err(format!(
48 "Invalid number of bits - must be between 1 and {MAX_BITS}"
49 ));
50 }
51
52 if input.is_part_one() {
53 let set_bits_at_position = &mut [0; 16][0..num_bits];
54 for line in input.text.lines() {
55 if line.len() != num_bits {
56 return Err("All lines does not have equal length".to_string());
57 }
58 for (idx, byte) in line.bytes().rev().enumerate() {
59 set_bits_at_position[idx] += if byte == b'1' { 1 } else { -1 };
60 }
61 }
62
63 let gamma: u16 = set_bits_at_position
64 .iter()
65 .enumerate()
66 .filter_map(|(bit_idx, &count)| (count >= 0).then_some(1 << bit_idx))
67 .sum();
68 let epsilon = !gamma & set_lowest_bits(num_bits as u8);
69 Ok(u32::from(gamma) * u32::from(epsilon))
70 } else {
71 let mut numbers = input
72 .text
73 .lines()
74 .enumerate()
75 .map(|(line_idx, line)| {
76 u16::from_str_radix(line, 2)
77 .map_err(|_| format!("Line {}: Not a binary integer", line_idx + 1))
78 })
79 .collect::<Result<Vec<u16>, _>>()?;
80
81 let oxygen_generator_rating =
82 keep_one_according_to_bitset_criteria(&mut numbers, num_bits, true)?;
83 let co2_scrubber_rating =
84 keep_one_according_to_bitset_criteria(&mut numbers, num_bits, false)?;
85 Ok(u32::from(oxygen_generator_rating) * u32::from(co2_scrubber_rating))
86 }
87}
88
89#[test]
90fn test_set_bits() {
91 assert_eq!(set_lowest_bits(1), 0b0001);
92 assert_eq!(set_lowest_bits(2), 0b0011);
93 assert_eq!(set_lowest_bits(3), 0b0111);
94 assert_eq!(set_lowest_bits(16), u16::MAX);
95}
96
97#[test]
98pub fn tests() {
99 let example = "00100
10011110
10110110
10210111
10310101
10401111
10500111
10611100
10710000
10811001
10900010
11001010";
111 test_part_one!(example => 198);
112 test_part_two!(example => 230);
113
114 let real_input = include_str!("day03_input.txt");
115 test_part_one!(real_input => 3_985_686);
116 test_part_two!(real_input => 2_555_739);
117}