#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
use std::str::FromStr;
fn main() -> anyhow::Result<()> {
let input = std::io::stdin()
.lines()
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|v| u32::from_str(&v))
.collect::<Result<Vec<_>, _>>()?;
println!("{}", part1(&input));
println!("{}", part2(&input));
Ok(())
}
fn part1(input: &[u32]) -> u64 {
let mut acc = 0;
for &i in input {
let mut c = i;
for _ in 0..2000 {
c = cipher(c);
}
acc += u64::from(c);
}
acc
}
fn part2(input: &[u32]) -> u32 {
const RANGE: usize = 19;
const OFFSET: usize = 9;
let mut tally =
vec![vec![[[0u32; RANGE]; RANGE]; RANGE].into_boxed_slice(); RANGE].into_boxed_slice();
let mut seen =
vec![vec![[[0; RANGE]; RANGE]; RANGE].into_boxed_slice(); RANGE].into_boxed_slice();
let mut generation = 1;
for &i in input {
let mut prev = cipher(i);
let mut a = prev;
let mut b = cipher(a);
let mut c = cipher(b);
let mut d = cipher(c);
for _ in 0..1998_u32 {
let dp = (prev % 10) as i8;
let da = (a % 10) as i8;
let db = (b % 10) as i8;
let dc = (c % 10) as i8;
let dd = (d % 10) as i8;
let delta1 = (da - dp + OFFSET as i8) as usize;
let delta2 = (db - da + OFFSET as i8) as usize;
let delta3 = (dc - db + OFFSET as i8) as usize;
let delta4 = (dd - dc + OFFSET as i8) as usize;
if seen[delta1][delta2][delta3][delta4] != generation {
seen[delta1][delta2][delta3][delta4] = generation;
tally[delta1][delta2][delta3][delta4] += d % 10;
}
prev = a;
a = b;
b = c;
c = d;
d = cipher(d);
}
generation += 1;
}
tally
.iter()
.flatten()
.flatten()
.flatten()
.max()
.copied()
.unwrap_or(u32::MAX)
}
fn cipher(n: u32) -> u32 {
let n = ((n << 6) ^ n) & 0x00FF_FFFF;
let n = ((n >> 5) ^ n) & 0x00FF_FFFF;
((n << 11) ^ n) & 0x00FF_FFFF
}