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