1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! # Point of Incidence
//!
//! We store each row of a grid as a binary number. For example `#.##..##.` becomes `101100110`.
//! Then to count smudges we bitwise XOR the respective rows together and count one bits
//! using the [`count_ones`] function.
//!
//! For example:
//! ```none
//!     ..##..###     001100111 ^ 000100111 = 00100000 => 1
//!    v#####.##.v => 111110110 ^ 111110110 = 00000000 => 0
//!    ^#####.##.^
//!     ...#..###
//! ````
//!
//! To handle columns we transpose the grid then convert into integers the same way. For part one
//! we look for a reflection axis with 0 smudges and for part two 1 smudge, allowing the same
//! code to be reused.
//!
//! [`count_ones`]: u32::count_ones
use crate::util::grid::*;
use crate::util::point::*;

type Input = Vec<(Vec<u32>, Vec<u32>)>;

pub fn parse(input: &str) -> Input {
    input
        .split("\n\n")
        .map(|block| {
            let grid: Grid<_> = Grid::parse(block);
            let mut rows = Vec::with_capacity(grid.height as usize);
            let mut columns = Vec::with_capacity(grid.width as usize);

            for y in 0..grid.height {
                let mut n = 0;

                for x in 0..grid.width {
                    n = (n << 1) | (grid[Point::new(x, y)] == b'#') as u32;
                }

                rows.push(n);
            }

            for x in 0..grid.width {
                let mut n = 0;

                for y in 0..grid.height {
                    n = (n << 1) | (grid[Point::new(x, y)] == b'#') as u32;
                }

                columns.push(n);
            }

            (rows, columns)
        })
        .collect()
}

pub fn part1(input: &Input) -> usize {
    reflect(input, 0)
}

pub fn part2(input: &Input) -> usize {
    reflect(input, 1)
}

fn reflect(input: &Input, target: u32) -> usize {
    input
        .iter()
        .map(|(rows, columns)| {
            if let Some(x) = reflect_axis(columns, target) {
                x
            } else if let Some(y) = reflect_axis(rows, target) {
                100 * y
            } else {
                unreachable!()
            }
        })
        .sum()
}

fn reflect_axis(axis: &[u32], target: u32) -> Option<usize> {
    let size = axis.len();

    for i in 1..size {
        let mut smudges = 0;

        // Only consider rows/columns within the boundary of the grid.
        for j in 0..i.min(size - i) {
            smudges += (axis[i - j - 1] ^ axis[i + j]).count_ones();
        }

        if smudges == target {
            return Some(i);
        }
    }

    None
}