use crate::input::Input;
use crate::mod_exp::mod_exp;
pub fn solve(input: &Input) -> Result<i128, String> {
if input.is_part_one() {
let mut deck = Vec::new();
for i in 0..10_007 {
deck.push(i);
}
for (line_index, line) in input.text.lines().enumerate() {
let error_message = || format!("Invalid line: {}", line_index + 1);
let error_message_arg = |_| error_message();
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(error_message)?;
let how_many = how_many.parse::<i32>().map_err(error_message_arg)?;
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)
.ok_or_else(error_message)
.and_then(|value_str| value_str.parse::<usize>().map_err(error_message_arg))?;
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_index + 1));
}
}
let desired_card = 2019;
deck.iter()
.position(|&card| card == 2019)
.map(|p| p as i128)
.ok_or_else(|| format!("No card {desired_card} found"))
} else {
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_index, line) in input.text.lines().enumerate() {
let error_message = || format!("Invalid line: {}", line_index + 1);
let error_message_arg = |_| error_message();
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)
.ok_or_else(error_message)
.and_then(|value_str| value_str.parse::<i128>().map_err(error_message_arg))?;
offset_diff += increment_mul * n;
} else if line.starts_with("deal with") {
let n = line
.split_whitespace()
.nth(3)
.ok_or_else(error_message)
.and_then(|value_str| value_str.parse::<i128>().map_err(error_message_arg))?;
increment_mul *= inv(n);
} else {
return Err(format!("Invalid line: {}", line_index + 1));
}
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() {
let input = include_str!("day22_input.txt");
test_part_one!(input => 6526);
test_part_two!(input => 79_855_812_422_607);
}