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