Skip to content

Commit 1f713c7

Browse files
Improve performance on year2025::day09 (#19)
* transpose and remove unnecessary sort calls * use u32 where possible * update description for part 1
1 parent 6755119 commit 1f713c7

File tree

1 file changed

+48
-53
lines changed

1 file changed

+48
-53
lines changed

src/year2025/day09.rs

Lines changed: 48 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22
use crate::util::iter::*;
33
use crate::util::parse::*;
44

5-
type Tile = [u64; 2];
5+
type Tile = [u32; 2];
66

77
struct Candidate {
8-
x: u64,
9-
y: u64,
8+
x: u32,
9+
y: u32,
1010
interval: Interval,
1111
}
1212

13-
/// The set { x in u64 | l <= x <= r }.
13+
/// The set { x in u32 | l <= x <= r }.
1414
#[derive(Clone, Copy)]
1515
struct Interval {
16-
l: u64,
17-
r: u64,
16+
l: u32,
17+
r: u32,
1818
}
1919

2020
impl Interval {
21-
fn new(l: u64, r: u64) -> Self {
21+
fn new(l: u32, r: u32) -> Self {
2222
debug_assert!(l <= r);
2323

2424
Interval { l, r }
@@ -34,24 +34,20 @@ impl Interval {
3434
Interval::new(self.l.max(other.l), self.r.min(other.r))
3535
}
3636

37-
fn contains(self, x: u64) -> bool {
37+
fn contains(self, x: u32) -> bool {
3838
self.l <= x && x <= self.r
3939
}
4040
}
4141

4242
pub fn parse(input: &str) -> Vec<Tile> {
43-
input.iter_unsigned::<u64>().chunk::<2>().collect()
43+
let mut tiles: Vec<_> = input.iter_unsigned::<u32>().chunk::<2>().collect();
44+
tiles.sort_unstable_by_key(|&[x, y]| (y, x));
45+
tiles
4446
}
4547

4648
pub fn part1(tiles: &[Tile]) -> u64 {
47-
let mut tiles = tiles.to_vec();
48-
49-
tiles.sort_by_key(|&[x, y]| (x, y));
50-
51-
let (top_left_tiles, bottom_left_tiles) =
52-
get_potential_left_corner_tiles(tiles.iter().copied());
53-
54-
let (top_right_tiles, bottom_right_tiles) =
49+
let (top_left_tiles, top_right_tiles) = get_potential_left_corner_tiles(tiles.iter().copied());
50+
let (bottom_left_tiles, bottom_right_tiles) =
5551
get_potential_left_corner_tiles(tiles.iter().copied().rev());
5652

5753
find_largest_from_all_corners(&top_left_tiles, &bottom_right_tiles)
@@ -60,10 +56,10 @@ pub fn part1(tiles: &[Tile]) -> u64 {
6056

6157
/// This function filters `sorted_tiles` into two lists, one containing all tiles that could be the top left
6258
/// corner of the largest rectangle (assuming the largest rectangle has a top left corner), and the second
63-
/// containing all tiles that could be the bottom left corner.
59+
/// containing all tiles that could be the top right corner.
6460
///
65-
/// It assumes `sorted_tiles` is sorted in ascending "x" values, or, to get the top right and bottom right corners,
66-
/// that `sorted_tiles` is sorted in descending "x" order.
61+
/// It assumes `sorted_tiles` is sorted in ascending "y" values, or, to get the top right and bottom right corners,
62+
/// that `sorted_tiles` is sorted in descending "y" order.
6763
///
6864
/// It works (for the top left corners, for illustration) by only returning tiles (from the set of all tiles, "T") within
6965
/// the region:
@@ -82,60 +78,58 @@ pub fn part1(tiles: &[Tile]) -> u64 {
8278
/// The `top_tiles` and `bottom_tiles` are the corner points of this region `R`, built up by scanning through tiles
8379
/// in either left to right or right to left order.
8480
fn get_potential_left_corner_tiles(
85-
sorted_tiles: impl Iterator<Item = [u64; 2]>,
86-
) -> (Vec<[u64; 2]>, Vec<[u64; 2]>) {
87-
let mut top_tiles = Vec::new();
88-
let mut top_tiles_last_y = u64::MAX;
81+
sorted_tiles: impl Iterator<Item = [u32; 2]>,
82+
) -> (Vec<[u32; 2]>, Vec<[u32; 2]>) {
83+
let mut left_tiles = Vec::new();
84+
let mut left_tiles_last_x = u32::MAX;
8985

90-
let mut bottom_tiles = Vec::new();
91-
let mut bottom_tiles_last_y = u64::MIN;
86+
let mut right_tiles = Vec::new();
87+
let mut right_tiles_last_x = u32::MIN;
9288

9389
let mut it = sorted_tiles.peekable();
9490

95-
while let Some(first_in_column) = it.next() {
96-
let mut last_in_column = first_in_column;
91+
while let Some(first_in_row) = it.next() {
92+
let mut last_in_row = first_in_row;
9793

98-
while let Some(p) = it.next_if(|p| p[0] == first_in_column[0]) {
99-
last_in_column = p;
94+
while let Some(p) = it.next_if(|p| p[1] == first_in_row[1]) {
95+
last_in_row = p;
10096
}
10197

102-
let (x, top_y, bottom_y) = (
103-
first_in_column[0],
104-
first_in_column[1].min(last_in_column[1]),
105-
first_in_column[1].max(last_in_column[1]),
98+
let (y, left_x, right_x) = (
99+
first_in_row[1],
100+
first_in_row[0].min(last_in_row[0]),
101+
first_in_row[0].max(last_in_row[0]),
106102
);
107103

108-
if top_y < top_tiles_last_y {
109-
top_tiles.push([x, top_y]);
110-
top_tiles_last_y = top_y;
104+
if left_x < left_tiles_last_x {
105+
left_tiles.push([left_x, y]);
106+
left_tiles_last_x = left_x;
111107
}
112108

113-
if bottom_y > bottom_tiles_last_y {
114-
bottom_tiles.push([x, bottom_y]);
115-
bottom_tiles_last_y = bottom_y;
109+
if right_x > right_tiles_last_x {
110+
right_tiles.push([right_x, y]);
111+
right_tiles_last_x = right_x;
116112
}
117113
}
118114

119-
(top_tiles, bottom_tiles)
115+
(left_tiles, right_tiles)
120116
}
121117

122118
#[inline]
123-
fn find_largest_from_all_corners(corner: &[[u64; 2]], opposite_corner: &[[u64; 2]]) -> u64 {
119+
fn find_largest_from_all_corners(corner: &[[u32; 2]], opposite_corner: &[[u32; 2]]) -> u64 {
124120
let mut largest = 0_u64;
125121

126122
for &p in corner {
127123
for &q in opposite_corner {
128-
largest = largest.max((p[0].abs_diff(q[0]) + 1) * (p[1].abs_diff(q[1]) + 1));
124+
largest =
125+
largest.max((p[0].abs_diff(q[0]) + 1) as u64 * (p[1].abs_diff(q[1]) + 1) as u64);
129126
}
130127
}
131128

132129
largest
133130
}
134131

135132
pub fn part2(tiles: &[Tile]) -> u64 {
136-
let mut tiles = tiles.to_vec();
137-
tiles.sort_unstable_by_key(|&[x, y]| (y, x));
138-
139133
// Track the largest area so far during scanning:
140134
let mut largest_area: u64 = 0;
141135

@@ -144,13 +138,13 @@ pub fn part2(tiles: &[Tile]) -> u64 {
144138
let mut candidates: Vec<Candidate> = Vec::with_capacity(512);
145139

146140
// Maintain an ordered list of descending edges, i.e. [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...]:
147-
let mut descending_edges: Vec<u64> = vec![];
141+
let mut descending_edges: Vec<u32> = vec![];
148142
let mut intervals_from_descending_edges = vec![];
149143

150144
// Invariants on the input data (defined by the puzzle) result in points arriving in pairs on the same y line:
151-
let mut it = tiles.into_iter();
145+
let mut it = tiles.iter();
152146

153-
while let (Some([x0, y]), Some([x1, y1])) = (it.next(), it.next()) {
147+
while let (Some(&[x0, y]), Some(&[x1, y1])) = (it.next(), it.next()) {
154148
debug_assert_eq!(y, y1);
155149

156150
// Update the descending edges; since we are scanning from top to bottom, and within each line left to right,
@@ -179,8 +173,9 @@ pub fn part2(tiles: &[Tile]) -> u64 {
179173
for candidate in &candidates {
180174
for x in [x0, x1] {
181175
if candidate.interval.contains(x) {
182-
largest_area = largest_area
183-
.max((candidate.x.abs_diff(x) + 1) * (candidate.y.abs_diff(y) + 1));
176+
largest_area = largest_area.max(
177+
(candidate.x.abs_diff(x) + 1) as u64 * (candidate.y.abs_diff(y) + 1) as u64,
178+
);
184179
}
185180
}
186181
}
@@ -212,7 +207,7 @@ pub fn part2(tiles: &[Tile]) -> u64 {
212207
}
213208

214209
// Adds `value` if it isn't in `ordered_list`, removes it if it is, maintaining the order.
215-
fn toggle_value_membership_in_ordered_list(ordered_list: &mut Vec<u64>, value: u64) {
210+
fn toggle_value_membership_in_ordered_list(ordered_list: &mut Vec<u32>, value: u32) {
216211
match ordered_list.binary_search(&value) {
217212
Ok(i) => {
218213
ordered_list.remove(i);
@@ -226,7 +221,7 @@ fn toggle_value_membership_in_ordered_list(ordered_list: &mut Vec<u64>, value: u
226221
// Changes the list of descending edges, [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...],
227222
// into a vector containing the intervals.
228223
#[inline]
229-
fn update_intervals_from_descending_edges(descending_edges: &[u64], to_update: &mut Vec<Interval>) {
224+
fn update_intervals_from_descending_edges(descending_edges: &[u32], to_update: &mut Vec<Interval>) {
230225
debug_assert!(descending_edges.len().is_multiple_of(2));
231226

232227
to_update.clear();

0 commit comments

Comments
 (0)