|
| 1 | +use std::str::FromStr; |
| 2 | + |
1 | 3 | use eyre::{eyre, OptionExt, Report, Result};
|
| 4 | +use nohash_hasher::IntMap; |
2 | 5 | use rayon::prelude::*;
|
3 | 6 |
|
4 | 7 | use crate::meta::Problem;
|
5 | 8 |
|
6 |
| -pub const HISTORIAN_HYSTERIA: Problem = Problem::partially_solved(&|input| -> Result<usize> { |
7 |
| - let (lhs, rhs) = input |
8 |
| - .lines() |
9 |
| - .map(|line| { |
10 |
| - let mut iter = line.split_ascii_whitespace(); |
11 |
| - let a_str = iter.next().ok_or_eyre("empty line")?; |
12 |
| - let b_str = iter |
13 |
| - .next() |
14 |
| - .ok_or_else(|| eyre!("No whitespace on line: \"{line}\""))?; |
15 |
| - |
16 |
| - debug_assert!(iter.next().is_none()); |
17 |
| - |
18 |
| - let a = a_str.parse()?; |
19 |
| - let b = b_str.parse()?; |
20 |
| - |
21 |
| - Ok::<(usize, usize), Report>((a, b)) |
22 |
| - }) |
23 |
| - .try_fold((Vec::new(), Vec::new()), |(mut lhs, mut rhs), res| { |
24 |
| - let (a, b) = res?; |
25 |
| - |
26 |
| - lhs.push(a); |
27 |
| - rhs.push(b); |
28 |
| - |
29 |
| - Ok::<_, Report>((lhs, rhs)) |
30 |
| - }) |
31 |
| - .map(|(mut lhs, mut rhs)| { |
32 |
| - lhs.sort_unstable(); |
33 |
| - rhs.sort_unstable(); |
34 |
| - |
35 |
| - (lhs, rhs) |
36 |
| - })?; |
37 |
| - |
38 |
| - Ok(lhs |
39 |
| - .into_par_iter() |
40 |
| - .zip(rhs) |
41 |
| - .map(|(a, b)| a.abs_diff(b)) |
42 |
| - .sum()) |
43 |
| -}); |
| 9 | +pub const HISTORIAN_HYSTERIA: Problem = Problem::solved( |
| 10 | + &|input| input.parse().map(LocationIds::total_distance), |
| 11 | + &|input| input.parse().map(LocationIds::total_similarity), |
| 12 | +); |
| 13 | + |
| 14 | +#[derive(Debug, Default)] |
| 15 | +struct LocationIds { |
| 16 | + lhs: Vec<usize>, |
| 17 | + rhs: Vec<usize>, |
| 18 | +} |
| 19 | + |
| 20 | +impl LocationIds { |
| 21 | + fn total_distance(self) -> usize { |
| 22 | + let LocationIds { mut lhs, mut rhs } = self; |
| 23 | + |
| 24 | + lhs.sort_unstable(); |
| 25 | + rhs.sort_unstable(); |
| 26 | + |
| 27 | + lhs.into_par_iter() |
| 28 | + .zip(rhs) |
| 29 | + .map(|(a, b)| a.abs_diff(b)) |
| 30 | + .sum() |
| 31 | + } |
| 32 | + |
| 33 | + fn total_similarity(self) -> usize { |
| 34 | + let LocationIds { lhs, rhs } = self; |
| 35 | + let mut cache = IntMap::default(); |
| 36 | + |
| 37 | + lhs.into_iter() |
| 38 | + .map(|id| { |
| 39 | + *cache |
| 40 | + .entry(id) |
| 41 | + .or_insert_with(|| rhs.par_iter().filter(|rhs_id| **rhs_id == id).count() * id) |
| 42 | + }) |
| 43 | + .sum() |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +impl FromStr for LocationIds { |
| 48 | + type Err = Report; |
| 49 | + |
| 50 | + fn from_str(s: &str) -> Result<Self> { |
| 51 | + s.lines() |
| 52 | + .map(|line| { |
| 53 | + let mut iter = line.split_ascii_whitespace(); |
| 54 | + let a_str = iter.next().ok_or_eyre("empty line")?; |
| 55 | + let b_str = iter |
| 56 | + .next() |
| 57 | + .ok_or_else(|| eyre!("No whitespace on line: \"{line}\""))?; |
| 58 | + |
| 59 | + debug_assert!(iter.next().is_none()); |
| 60 | + |
| 61 | + let a = a_str.parse()?; |
| 62 | + let b = b_str.parse()?; |
| 63 | + |
| 64 | + Ok::<(usize, usize), Report>((a, b)) |
| 65 | + }) |
| 66 | + .try_fold(LocationIds::default(), |mut ids, res| { |
| 67 | + let (a, b) = res?; |
| 68 | + |
| 69 | + ids.lhs.push(a); |
| 70 | + ids.rhs.push(b); |
| 71 | + |
| 72 | + Ok::<_, Report>(ids) |
| 73 | + }) |
| 74 | + } |
| 75 | +} |
0 commit comments