#![warn(clippy::pedantic)] use std::{ collections::HashMap, convert::Infallible, io::{stdin, BufRead}, str::FromStr, }; use vector2d::Vector2D; #[derive(Copy, Clone, Debug)] struct Line { start: Vector2D, end: Vector2D, } impl Line { pub fn is_straight(&self) -> bool { self.start.x == self.end.x || self.start.y == self.end.y } pub fn expand(&self) -> Vec> { let delta = self.end.as_isizes() - self.start.as_isizes(); #[allow(clippy::if_not_else)] let len = if delta.x != 0 { delta.x.abs() } else if delta.y != 0 { delta.y.abs() } else { unreachable!() }; let step = delta / len; (0..=len) .scan(self.start, |acc, _| { let prev = *acc; *acc = (acc.as_isizes() + step).as_usizes(); Some(prev) }) .collect() } } impl FromStr for Line { type Err = Infallible; fn from_str(s: &str) -> Result { let parts: Vec<_> = s.split(' ').collect(); let start: Vec<_> = parts[0].split(',').map(|n| n.parse().unwrap()).collect(); let end: Vec<_> = parts[2].split(',').map(|n| n.parse().unwrap()).collect(); Ok(Line { start: Vector2D::new(start[0], start[1]), end: Vector2D::new(end[0], end[1]), }) } } fn count_overlaps<'a>(it: impl IntoIterator) -> usize { let counts: HashMap<(usize, usize), usize> = it.into_iter() .flat_map(Line::expand) .fold(HashMap::new(), |mut map, point| { *map.entry(point.into()).or_default() += 1; map }); counts.into_iter().filter(|&(_k, v)| v >= 2).count() } fn main() { let lines: Vec = stdin() .lock() .lines() .map(|s| s.unwrap().parse().unwrap()) .collect(); println!( "{}", count_overlaps(lines.iter().filter(|line| line.is_straight())) ); println!("{}", count_overlaps(&lines)); }