1use crate::util::iter::*;
6use crate::util::parse::*;
7use crate::util::thread::*;
8use std::sync::atomic::{AtomicU32, Ordering};
9
10#[derive(Clone, Copy)]
11enum Command {
12 On,
13 Off,
14 Toggle,
15}
16
17impl Command {
18 fn from(bytes: &[u8]) -> Command {
19 match bytes[6] {
20 b'n' => Command::On,
21 b'f' => Command::Off,
22 b' ' => Command::Toggle,
23 _ => unreachable!(),
24 }
25 }
26}
27
28#[derive(Clone, Copy)]
29struct Rectangle {
30 x1: usize,
31 x2: usize,
32 y1: usize,
33 y2: usize,
34}
35
36impl Rectangle {
37 fn from([x1, y1, x2, y2]: [usize; 4]) -> Rectangle {
39 Rectangle { x1, y1, x2: x2 + 1, y2: y2 + 1 }
40 }
41}
42
43#[derive(Clone, Copy)]
44pub struct Instruction {
45 command: Command,
46 rectangle: Rectangle,
47}
48
49impl Instruction {
50 fn from((bytes, points): (&[u8], [usize; 4])) -> Instruction {
51 let command = Command::from(bytes);
52 let rectangle = Rectangle::from(points);
53 Instruction { command, rectangle }
54 }
55}
56
57pub fn parse(input: &str) -> Vec<Instruction> {
58 let first = input.lines().map(str::as_bytes);
59 let second = input.iter_unsigned().chunk::<4>();
60 first.zip(second).map(Instruction::from).collect()
61}
62
63pub fn part1(input: &[Instruction]) -> u32 {
64 let items: Vec<_> = (0..1000).collect();
65 let atomic = AtomicU32::new(0);
66
67 spawn_parallel_iterator(&items, |iter| worker_one(input, &atomic, iter));
68 atomic.into_inner()
69}
70
71pub fn part2(input: &[Instruction]) -> u32 {
72 let items: Vec<_> = (0..1000).collect();
73 let atomic = AtomicU32::new(0);
74
75 spawn_parallel_iterator(&items, |iter| worker_two(input, &atomic, iter));
76 atomic.into_inner()
77}
78
79fn worker_one(input: &[Instruction], atomic: &AtomicU32, iter: ParIter<'_, usize>) {
80 let mut result = 0;
81
82 for row in iter {
83 let mut grid = [0_u8; 1_024];
84
85 for &Instruction { command, rectangle: Rectangle { x1, y1, x2, y2 } } in input {
86 if (y1..y2).contains(row) {
87 let iter = grid[x1..x2].iter_mut();
88 match command {
89 Command::On => iter.for_each(|b| *b = 1),
90 Command::Off => iter.for_each(|b| *b = 0),
91 Command::Toggle => iter.for_each(|b| *b ^= 1),
92 }
93 }
94 }
95
96 result += grid.into_iter().map(|b| b as u32).sum::<u32>();
97 }
98
99 atomic.fetch_add(result, Ordering::Relaxed);
100}
101
102fn worker_two(input: &[Instruction], atomic: &AtomicU32, iter: ParIter<'_, usize>) {
103 let mut result = 0;
104
105 for row in iter {
106 let mut grid = [0_u8; 1_024];
107
108 for &Instruction { command, rectangle: Rectangle { x1, y1, x2, y2 } } in input {
109 if (y1..y2).contains(row) {
110 let iter = grid[x1..x2].iter_mut();
111 match command {
112 Command::On => iter.for_each(|b| *b += 1),
113 Command::Off => iter.for_each(|b| *b = b.saturating_sub(1)),
114 Command::Toggle => iter.for_each(|b| *b += 2),
115 }
116 }
117 }
118
119 result += grid.into_iter().map(|b| b as u32).sum::<u32>();
120 }
121
122 atomic.fetch_add(result, Ordering::Relaxed);
123}