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
120
use mod_exp::mod_exp;
pub fn part1(input_string: &str) -> Result<usize, String> {
let mut deck = Vec::new();
for i in 0..10_007 {
deck.push(i);
}
for (index, line) in input_string.lines().enumerate() {
let line_number = index + 1;
if line.starts_with("deal into") {
deck = deck.iter().rev().copied().collect();
} else if line.starts_with("cut") {
let how_many = line.split_whitespace().nth(1).ok_or_else(|| {
format!(
"Incorrect input at line {}: No argument to cut",
line_number
)
})?;
let how_many = how_many.parse::<i32>().map_err(|parse_error| {
format!(
"Incorrect input at line {}: Cannot parse number of cuts - {}",
line_number, parse_error
)
})?;
let how_many = if how_many > 0 {
how_many
} else {
10_007 - how_many.abs()
} as usize;
deck = [&deck[how_many..], &deck[..how_many]].concat();
} else if line.starts_with("deal with") {
let increment = line
.split_whitespace()
.nth(3)
.unwrap()
.parse::<usize>()
.unwrap();
let old_deck = deck.clone();
let mut current_index = 0;
for &card_at_front in old_deck.iter() {
deck[current_index] = card_at_front;
current_index = (current_index + increment) % deck.len();
}
} else {
return Err(format!("Invalid line: {}", line));
}
}
Ok(deck.iter().position(|&card| card == 2019).unwrap())
}
pub fn part2(input_string: &str) -> Result<i128, String> {
fn inv(n: i128) -> i128 {
mod_exp(n, MOD - 2, MOD)
}
const SHUFFLES: i128 = 101_741_582_076_661;
const NUM_CARDS: i128 = 119_315_717_514_047;
const MOD: i128 = NUM_CARDS;
let mut offset_diff: i128 = 0;
let mut increment_mul: i128 = 1;
for line in input_string.lines() {
if line.starts_with("deal into") {
increment_mul *= -1;
offset_diff += increment_mul;
} else if line.starts_with("cut") {
let n = line
.split_whitespace()
.nth(1)
.unwrap()
.parse::<i128>()
.unwrap();
offset_diff += increment_mul * n;
} else if line.starts_with("deal with") {
let n = line
.split_whitespace()
.nth(3)
.unwrap()
.parse::<i128>()
.unwrap();
increment_mul *= inv(n);
} else {
return Err(format!("Invalid line: {}", line));
}
increment_mul = increment_mul.rem_euclid(MOD);
offset_diff = offset_diff.rem_euclid(MOD);
}
let increment = mod_exp(increment_mul, SHUFFLES, MOD);
let offset = (offset_diff * (1_i128 - increment)).rem_euclid(MOD) * inv(1 - increment_mul);
let result = (offset + increment * 2020).rem_euclid(MOD);
Ok(result)
}
#[test]
pub fn tests_part1() {
assert_eq!(part1(include_str!("day22_input.txt")), Ok(6526));
}
#[test]
fn tests_part2() {
assert_eq!(
part2(include_str!("day22_input.txt")),
Ok(79_855_812_422_607)
);
}