1use std::array;
2use std::fmt::{Display, Formatter, Write};
3use std::ops::MulAssign;
4use utils::prelude::*;
5
6#[derive(Clone, Debug)]
12pub struct Day16 {
13 dance: Dance,
14}
15
16#[derive(Clone, Debug)]
17enum DanceMove {
18 Spin(u8),
19 Exchange(u8, u8),
20 Partner(u8, u8),
21}
22
23#[derive(Copy, Clone, Debug)]
24struct Dance {
25 index_perm: [usize; 16],
26 value_perm: [usize; 16],
27}
28
29impl Day16 {
30 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
31 let program = parser::byte_range(b'a'..=b'p').map(|b| b - b'a');
32 let position = parser::number_range(0..=15);
33
34 Ok(Self {
35 dance: parser::one_of((
36 position.with_prefix(b's').map(DanceMove::Spin),
37 position
38 .with_prefix(b'x')
39 .then(position.with_prefix(b'/'))
40 .map(|(a, b)| DanceMove::Exchange(a, b)),
41 program
42 .with_prefix(b'p')
43 .then(program.with_prefix(b'/'))
44 .map(|(a, b)| DanceMove::Partner(a, b)),
45 ))
46 .with_suffix(b','.or(parser::eof()))
47 .parse_iterator(input)
48 .collect::<Result<Dance, InputError>>()?,
49 })
50 }
51
52 #[must_use]
53 pub fn part1(&self) -> String {
54 self.dance.to_string()
55 }
56
57 #[must_use]
58 pub fn part2(&self) -> String {
59 let mut result = Dance::default();
61 let mut base = self.dance;
62 let mut exponent = 1_000_000_000;
63
64 while exponent > 0 {
65 if exponent % 2 == 1 {
66 result *= base;
67 }
68 exponent >>= 1;
69 base *= base;
70 }
71
72 result.to_string()
73 }
74}
75
76impl Default for Dance {
77 fn default() -> Self {
78 Dance {
79 index_perm: array::from_fn(|i| i),
80 value_perm: array::from_fn(|i| i),
81 }
82 }
83}
84
85impl FromIterator<DanceMove> for Dance {
86 #[inline]
87 fn from_iter<T: IntoIterator<Item = DanceMove>>(iter: T) -> Self {
88 let mut dance = Dance::default();
89 let mut value_positions: [usize; 16] = array::from_fn(|i| i);
90 let mut rotation = 0; for dance_move in iter {
93 match dance_move {
94 DanceMove::Spin(r) => rotation += 16 - r as usize,
95 DanceMove::Exchange(a, b) => {
96 let a_idx = (a as usize + rotation) % 16;
97 let b_idx = (b as usize + rotation) % 16;
98 dance.index_perm.swap(a_idx, b_idx);
99 }
100 DanceMove::Partner(a, b) => {
101 value_positions.swap(a as usize, b as usize);
102 let a_idx = value_positions[a as usize];
103 let b_idx = value_positions[b as usize];
104 dance.value_perm.swap(a_idx, b_idx);
105 }
106 }
107 }
108 dance.index_perm.rotate_left(rotation % 16);
109
110 dance
111 }
112}
113
114impl Display for Dance {
115 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
116 for &i in &self.index_perm {
117 f.write_char((self.value_perm[i] as u8 + b'a') as char)?;
118 }
119 Ok(())
120 }
121}
122
123impl MulAssign for Dance {
124 fn mul_assign(&mut self, rhs: Self) {
125 self.index_perm = self.index_perm.map(|i| rhs.index_perm[i]);
126 self.value_perm = self.value_perm.map(|i| rhs.value_perm[i]);
127 }
128}
129
130examples!(Day16 -> (&'static str, &'static str) []);