use crate::input::Input;
const fn set_lowest_bits(n: u8) -> u8 {
u8::MAX >> (u8::BITS as u8 - n)
}
fn bit_value(character: u8) -> u8 {
if character.is_ascii_digit() {
character - b'0'
} else {
if !(b'A'..=b'F').contains(&character) {
return 0;
}
0b1010 + (character - b'A')
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
struct Transmission<'a> {
bit_offset: usize,
hex_bytes: &'a [u8],
}
impl<'a> Transmission<'a> {
const fn new(hex_bytes: &'a [u8]) -> Self {
Self {
bit_offset: 0,
hex_bytes,
}
}
fn next_bits(&mut self, num_bits: usize) -> Option<u16> {
let mut remaining_bits = num_bits;
let mut result = 0_u16;
while remaining_bits > 0 {
let hex_byte_offset = self.bit_offset / 4;
let bits_left_in_byte = 4 - (self.bit_offset % 4);
let bits_to_read = std::cmp::min(remaining_bits, bits_left_in_byte);
let hex_byte_value = bit_value(*self.hex_bytes.get(hex_byte_offset)?);
remaining_bits -= bits_to_read;
result |=
u16::from((hex_byte_value >> (bits_left_in_byte - bits_to_read))
& set_lowest_bits(bits_to_read as u8))
<< (remaining_bits as u16);
self.bit_offset += bits_to_read;
}
Some(result)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum LengthOrValue {
Value(u64),
TotalBitLength(u16),
NumPackets(u16),
}
impl LengthOrValue {
const fn expects_more(self) -> bool {
match self {
Self::TotalBitLength(v) | Self::NumPackets(v) => v > 0,
_ => false,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct Packet {
version: u8,
type_id: u8,
contents: LengthOrValue,
length: u8,
}
impl Packet {
fn parse(transmission: &mut Transmission) -> Option<Self> {
let initial_bit_offset = transmission.bit_offset;
let version = transmission.next_bits(3)?;
let type_id = transmission.next_bits(3)?;
let length_or_value = if type_id == 4 {
let mut value = 0_u64;
let mut bit_offset = 0_usize;
loop {
let next_five_bits = transmission.next_bits(5)?;
value |= u64::from(next_five_bits & 0b1111) << (bit_offset as u64);
bit_offset += 4;
if bit_offset > 60 {
return None;
}
if next_five_bits & 0b10000 == 0 {
let mut new_value = 0;
while bit_offset != 0 {
new_value |= (value & 0b1111) << (bit_offset - 4);
value >>= 4;
bit_offset -= 4;
}
break LengthOrValue::Value(new_value);
}
}
} else {
let next_bit = transmission.next_bits(1)?;
if next_bit == 0 {
LengthOrValue::TotalBitLength(transmission.next_bits(15)?)
} else {
LengthOrValue::NumPackets(transmission.next_bits(11)?)
}
};
Some(Self {
version: version as u8,
type_id: type_id as u8,
contents: length_or_value,
length: (transmission.bit_offset - initial_bit_offset) as u8,
})
}
fn parse_and_eval(transmission: &mut Transmission) -> Option<u64> {
let current_package = Self::parse(transmission)?;
let mut remaining_length = match current_package.contents {
LengthOrValue::Value(value) => {
return Some(value);
}
_ => current_package.contents,
};
let mut current_value = if current_package.type_id == 1 {
1
} else if current_package.type_id == 2 {
u64::MAX
} else {
0
};
while remaining_length.expects_more() {
let bit_offset_at_start_of_sub_packets = transmission.bit_offset;
let next_package_value = Self::parse_and_eval(transmission)?;
match current_package.type_id {
0 => {
current_value = current_value.checked_add(next_package_value)?;
}
1 => {
current_value = current_value.checked_mul(next_package_value)?;
}
2 => {
current_value = std::cmp::min(next_package_value, current_value);
}
3 => {
current_value = std::cmp::max(next_package_value, current_value);
}
5 => {
let second_package_value = Self::parse_and_eval(transmission)?;
return Some(u64::from(next_package_value > second_package_value));
}
6 => {
let second_package_value = Self::parse_and_eval(transmission)?;
return Some(u64::from(next_package_value < second_package_value));
}
7 => {
let second_package_value = Self::parse_and_eval(transmission)?;
return Some(u64::from(next_package_value == second_package_value));
}
_ => {
return None;
}
}
remaining_length = match remaining_length {
LengthOrValue::NumPackets(value) => LengthOrValue::NumPackets(value - 1),
LengthOrValue::TotalBitLength(value) => {
let consumed_bit_length =
transmission.bit_offset - bit_offset_at_start_of_sub_packets;
if consumed_bit_length > usize::from(u16::MAX)
|| consumed_bit_length as u16 > value
{
return None;
}
LengthOrValue::TotalBitLength(value - consumed_bit_length as u16)
}
_ => {
return None;
}
};
}
Some(current_value)
}
}
pub fn solve(input: &Input) -> Result<u64, String> {
let hex_bytes = input.text.as_bytes();
let mut transmission = Transmission::new(hex_bytes);
if input.is_part_one() {
let mut version_sum = 0_u64;
while let Some(packet) = Packet::parse(&mut transmission) {
version_sum += u64::from(packet.version);
}
Ok(version_sum)
} else {
Packet::parse_and_eval(&mut transmission)
.ok_or_else(|| "Unable to parse outermost package - check transmission".to_string())
}
}
#[test]
pub fn test_packet_parsing() {
let hex_bytes = b"D2FE28";
let mut transmission = Transmission::new(hex_bytes);
assert_eq!(
Some(Packet {
version: 6,
type_id: 4,
contents: LengthOrValue::Value(2021),
length: 21
}),
Packet::parse(&mut transmission)
);
assert!(Packet::parse(&mut transmission).is_none());
let hex_bytes = b"38006F45291200";
let mut transmission = Transmission::new(hex_bytes);
assert_eq!(
Some(Packet {
version: 1,
type_id: 6,
contents: LengthOrValue::TotalBitLength(27),
length: 22
}),
Packet::parse(&mut transmission)
);
assert_eq!(
Some(Packet {
version: 0b110,
type_id: 0b100,
contents: LengthOrValue::Value(10),
length: 11,
}),
Packet::parse(&mut transmission)
);
assert_eq!(
Some(Packet {
version: 0b010,
type_id: 0b100,
contents: LengthOrValue::Value(20),
length: 16,
}),
Packet::parse(&mut transmission)
);
assert!(Packet::parse(&mut transmission).is_none());
let hex_bytes = b"EE00D40C823060";
let mut transmission = Transmission::new(hex_bytes);
assert_eq!(
Some(Packet {
version: 7,
type_id: 3,
contents: LengthOrValue::NumPackets(3),
length: 18,
}),
Packet::parse(&mut transmission)
);
assert_eq!(
Some(Packet {
version: 0b010,
type_id: 0b100,
contents: LengthOrValue::Value(1),
length: 11,
}),
Packet::parse(&mut transmission)
);
assert_eq!(
Some(Packet {
version: 0b100,
type_id: 0b100,
contents: LengthOrValue::Value(2),
length: 11,
}),
Packet::parse(&mut transmission)
);
assert_eq!(
Some(Packet {
version: 0b001,
type_id: 0b100,
contents: LengthOrValue::Value(3),
length: 11,
}),
Packet::parse(&mut transmission)
);
assert!(Packet::parse(&mut transmission).is_none());
}
#[test]
pub fn tests() {
let real_input = include_str!("day16_input.txt");
test_part_one!("8A004A801A8002F478" => 16);
test_part_one!("620080001611562C8802118E34" => 12);
test_part_one!("C0015000016115A2E0802F182340" => 23);
test_part_one!("A0016C880162017C3686B18A3D4780" => 31);
test_part_one!(real_input => 960);
test_part_two!("C200B40A82" => 3);
test_part_two!("04005AC33890" => 54);
test_part_two!("880086C3E88112" => 7);
test_part_two!("CE00C43D881120" => 9);
test_part_two!("D8005AC2A8F0" => 1);
test_part_two!("F600BC2D8F" => 0);
test_part_two!("9C005AC2F8F0" => 0);
test_part_two!("9C0141080250320F1802104A08" => 1);
test_part_two!(real_input => 12_301_926_782_560);
#[cfg(feature = "count-allocations")]
{
let allocations = allocation_counter::measure(|| {
test_part_one!(real_input => 960);
test_part_two!(real_input => 12_301_926_782_560);
});
assert_eq!(allocations.count_total, 0);
}
test_part_two_error!("b1/ 5105 |" => "Unable to parse outermost package - check transmission");
}