1use crate::util::grid::*;
13use crate::util::hash::*;
14use crate::util::iter::*;
15use crate::util::parse::*;
16use crate::util::point::*;
17
18#[derive(Clone, Copy)]
19pub enum Fold {
20 Horizontal(i32),
21 Vertical(i32),
22}
23
24pub struct Input {
25 points: Vec<Point>,
26 folds: Vec<Fold>,
27}
28
29pub fn parse(input: &str) -> Input {
31 let (prefix, suffix) = input.split_once("\n\n").unwrap();
32
33 let points: Vec<_> = prefix.iter_signed().chunk::<2>().map(|[x, y]| Point::new(x, y)).collect();
34
35 let folds: Vec<_> = suffix
36 .lines()
37 .map(|line| match line.split_once('=').unwrap() {
38 ("fold along x", x) => Fold::Horizontal(x.signed()),
39 ("fold along y", y) => Fold::Vertical(y.signed()),
40 _ => unreachable!(),
41 })
42 .collect();
43
44 Input { points, folds }
45}
46
47pub fn part1(input: &Input) -> usize {
50 match input.folds[0] {
51 Fold::Horizontal(x) => {
52 input.points.iter().map(|&p| fold_horizontal(x, p)).collect::<FastSet<_>>().len()
53 }
54 Fold::Vertical(y) => {
55 input.points.iter().map(|&p| fold_vertical(y, p)).collect::<FastSet<_>>().len()
56 }
57 }
58}
59
60pub fn part2(input: &Input) -> String {
65 let (width, height) = input.folds.iter().fold((0, 0), |(width, height), &fold| match fold {
66 Fold::Horizontal(x) => (x, height),
67 Fold::Vertical(y) => (width, y),
68 });
69
70 let mut grid = Grid::new(width + 1, height, '.');
71
72 for &start in &input.points {
73 let end = input.folds.iter().fold(start, |point, &fold| match fold {
74 Fold::Horizontal(x) => fold_horizontal(x, point),
75 Fold::Vertical(y) => fold_vertical(y, point),
76 });
77 grid[end + RIGHT] = '#';
78 }
79
80 (0..height).for_each(|y| grid[Point::new(0, y)] = '\n');
81 grid.bytes.iter().collect()
82}
83
84#[inline]
86fn fold_horizontal(x: i32, p: Point) -> Point {
87 if p.x < x { p } else { Point::new(2 * x - p.x, p.y) }
88}
89
90#[inline]
92fn fold_vertical(y: i32, p: Point) -> Point {
93 if p.y < y { p } else { Point::new(p.x, 2 * y - p.y) }
94}