From 282306e66f02f434e71600e9482b3a1620bdfae1 Mon Sep 17 00:00:00 2001 From: Toby Davis Date: Thu, 18 Dec 2025 01:08:17 -0800 Subject: [PATCH 1/3] transpose and remove unnecessary sort calls --- src/year2025/day09.rs | 103 +++++++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/src/year2025/day09.rs b/src/year2025/day09.rs index 0a8ff3e..6d3ace4 100644 --- a/src/year2025/day09.rs +++ b/src/year2025/day09.rs @@ -40,18 +40,18 @@ impl Interval { } pub fn parse(input: &str) -> Vec { - input.iter_unsigned::().chunk::<2>().collect() + let mut tiles: Vec<_> = input.iter_unsigned::().chunk::<2>().collect(); + tiles.sort_unstable_by_key(|&[x, y]| (y, x)); + tiles } pub fn part1(tiles: &[Tile]) -> u64 { - let mut tiles = tiles.to_vec(); - - tiles.sort_by_key(|&[x, y]| (x, y)); + // let mut tiles = tiles.to_vec(); - let (top_left_tiles, bottom_left_tiles) = - get_potential_left_corner_tiles(tiles.iter().copied()); + // tiles.sort_by_key(|&[x, y]| (x, y)); - let (top_right_tiles, bottom_right_tiles) = + let (top_left_tiles, top_right_tiles) = get_potential_left_corner_tiles(tiles.iter().copied()); + let (bottom_left_tiles, bottom_right_tiles) = get_potential_left_corner_tiles(tiles.iter().copied().rev()); find_largest_from_all_corners(&top_left_tiles, &bottom_right_tiles) @@ -81,42 +81,86 @@ pub fn part1(tiles: &[Tile]) -> u64 { /// /// The `top_tiles` and `bottom_tiles` are the corner points of this region `R`, built up by scanning through tiles /// in either left to right or right to left order. +// fn get_potential_left_corner_tiles( +// sorted_tiles: impl Iterator, +// ) -> (Vec<[u64; 2]>, Vec<[u64; 2]>) { +// let mut top_tiles = Vec::new(); +// let mut top_tiles_last_y = u64::MAX; + +// let mut bottom_tiles = Vec::new(); +// let mut bottom_tiles_last_y = u64::MIN; + +// let mut it = sorted_tiles.peekable(); + +// while let Some(first_in_column) = it.next() { +// let mut last_in_column = first_in_column; + +// while let Some(p) = it.next_if(|p| p[0] == first_in_column[0]) { +// last_in_column = p; +// } + +// let (x, top_y, bottom_y) = ( +// first_in_column[0], +// first_in_column[1].min(last_in_column[1]), +// first_in_column[1].max(last_in_column[1]), +// ); + +// if top_y < top_tiles_last_y { +// top_tiles.push([x, top_y]); +// top_tiles_last_y = top_y; +// } + +// if bottom_y > bottom_tiles_last_y { +// bottom_tiles.push([x, bottom_y]); +// bottom_tiles_last_y = bottom_y; +// } +// } + +// (top_tiles, bottom_tiles) +// } + fn get_potential_left_corner_tiles( sorted_tiles: impl Iterator, ) -> (Vec<[u64; 2]>, Vec<[u64; 2]>) { - let mut top_tiles = Vec::new(); - let mut top_tiles_last_y = u64::MAX; + let mut left_tiles = Vec::new(); + let mut left_tiles_last_x = u64::MAX; - let mut bottom_tiles = Vec::new(); - let mut bottom_tiles_last_y = u64::MIN; + let mut right_tiles = Vec::new(); + let mut right_tiles_last_x = u64::MIN; let mut it = sorted_tiles.peekable(); - while let Some(first_in_column) = it.next() { - let mut last_in_column = first_in_column; + while let Some(first_in_row) = it.next() { + let mut last_in_row = first_in_row; - while let Some(p) = it.next_if(|p| p[0] == first_in_column[0]) { - last_in_column = p; + while let Some(p) = it.next_if(|p| p[1] == first_in_row[1]) { + last_in_row = p; } - let (x, top_y, bottom_y) = ( - first_in_column[0], - first_in_column[1].min(last_in_column[1]), - first_in_column[1].max(last_in_column[1]), + // let (x, top_y, bottom_y) = ( + // first_in_column[0], + // first_in_column[1].min(last_in_column[1]), + // first_in_column[1].max(last_in_column[1]), + // ); + + let (y, left_x, right_x) = ( + first_in_row[1], + first_in_row[0].min(last_in_row[0]), + first_in_row[0].max(last_in_row[0]), ); - if top_y < top_tiles_last_y { - top_tiles.push([x, top_y]); - top_tiles_last_y = top_y; + if left_x < left_tiles_last_x { + left_tiles.push([left_x, y]); + left_tiles_last_x = left_x; } - if bottom_y > bottom_tiles_last_y { - bottom_tiles.push([x, bottom_y]); - bottom_tiles_last_y = bottom_y; + if right_x > right_tiles_last_x { + right_tiles.push([right_x, y]); + right_tiles_last_x = right_x; } } - (top_tiles, bottom_tiles) + (left_tiles, right_tiles) } #[inline] @@ -133,9 +177,6 @@ fn find_largest_from_all_corners(corner: &[[u64; 2]], opposite_corner: &[[u64; 2 } pub fn part2(tiles: &[Tile]) -> u64 { - let mut tiles = tiles.to_vec(); - tiles.sort_unstable_by_key(|&[x, y]| (y, x)); - // Track the largest area so far during scanning: let mut largest_area: u64 = 0; @@ -148,9 +189,9 @@ pub fn part2(tiles: &[Tile]) -> u64 { let mut intervals_from_descending_edges = vec![]; // Invariants on the input data (defined by the puzzle) result in points arriving in pairs on the same y line: - let mut it = tiles.into_iter(); + let mut it = tiles.iter(); - while let (Some([x0, y]), Some([x1, y1])) = (it.next(), it.next()) { + while let (Some(&[x0, y]), Some(&[x1, y1])) = (it.next(), it.next()) { debug_assert_eq!(y, y1); // Update the descending edges; since we are scanning from top to bottom, and within each line left to right, From f47fadf96896c01fdf48a515e061bfeb08999659 Mon Sep 17 00:00:00 2001 From: Toby Davis Date: Thu, 18 Dec 2025 01:09:09 -0800 Subject: [PATCH 2/3] use u32 where possible --- src/year2025/day09.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/year2025/day09.rs b/src/year2025/day09.rs index 6d3ace4..9e4cc1d 100644 --- a/src/year2025/day09.rs +++ b/src/year2025/day09.rs @@ -60,10 +60,10 @@ pub fn part1(tiles: &[Tile]) -> u64 { /// This function filters `sorted_tiles` into two lists, one containing all tiles that could be the top left /// corner of the largest rectangle (assuming the largest rectangle has a top left corner), and the second -/// containing all tiles that could be the bottom left corner. +/// containing all tiles that could be the top right corner. /// -/// It assumes `sorted_tiles` is sorted in ascending "x" values, or, to get the top right and bottom right corners, -/// that `sorted_tiles` is sorted in descending "x" order. +/// It assumes `sorted_tiles` is sorted in ascending "y" values, or, to get the top right and bottom right corners, +/// that `sorted_tiles` is sorted in descending "y" order. /// /// It works (for the top left corners, for illustration) by only returning tiles (from the set of all tiles, "T") within /// the region: From 334d71b4ded730606735357f766dd7f4cd5b3cb8 Mon Sep 17 00:00:00 2001 From: Toby Davis Date: Thu, 18 Dec 2025 01:09:09 -0800 Subject: [PATCH 3/3] update description for part 1 --- src/year2025/day09.rs | 90 +++++++++++-------------------------------- 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/src/year2025/day09.rs b/src/year2025/day09.rs index 9e4cc1d..930e7e1 100644 --- a/src/year2025/day09.rs +++ b/src/year2025/day09.rs @@ -2,23 +2,23 @@ use crate::util::iter::*; use crate::util::parse::*; -type Tile = [u64; 2]; +type Tile = [u32; 2]; struct Candidate { - x: u64, - y: u64, + x: u32, + y: u32, interval: Interval, } -/// The set { x in u64 | l <= x <= r }. +/// The set { x in u32 | l <= x <= r }. #[derive(Clone, Copy)] struct Interval { - l: u64, - r: u64, + l: u32, + r: u32, } impl Interval { - fn new(l: u64, r: u64) -> Self { + fn new(l: u32, r: u32) -> Self { debug_assert!(l <= r); Interval { l, r } @@ -34,22 +34,18 @@ impl Interval { Interval::new(self.l.max(other.l), self.r.min(other.r)) } - fn contains(self, x: u64) -> bool { + fn contains(self, x: u32) -> bool { self.l <= x && x <= self.r } } pub fn parse(input: &str) -> Vec { - let mut tiles: Vec<_> = input.iter_unsigned::().chunk::<2>().collect(); + let mut tiles: Vec<_> = input.iter_unsigned::().chunk::<2>().collect(); tiles.sort_unstable_by_key(|&[x, y]| (y, x)); tiles } pub fn part1(tiles: &[Tile]) -> u64 { - // let mut tiles = tiles.to_vec(); - - // tiles.sort_by_key(|&[x, y]| (x, y)); - let (top_left_tiles, top_right_tiles) = get_potential_left_corner_tiles(tiles.iter().copied()); let (bottom_left_tiles, bottom_right_tiles) = get_potential_left_corner_tiles(tiles.iter().copied().rev()); @@ -81,52 +77,14 @@ pub fn part1(tiles: &[Tile]) -> u64 { /// /// The `top_tiles` and `bottom_tiles` are the corner points of this region `R`, built up by scanning through tiles /// in either left to right or right to left order. -// fn get_potential_left_corner_tiles( -// sorted_tiles: impl Iterator, -// ) -> (Vec<[u64; 2]>, Vec<[u64; 2]>) { -// let mut top_tiles = Vec::new(); -// let mut top_tiles_last_y = u64::MAX; - -// let mut bottom_tiles = Vec::new(); -// let mut bottom_tiles_last_y = u64::MIN; - -// let mut it = sorted_tiles.peekable(); - -// while let Some(first_in_column) = it.next() { -// let mut last_in_column = first_in_column; - -// while let Some(p) = it.next_if(|p| p[0] == first_in_column[0]) { -// last_in_column = p; -// } - -// let (x, top_y, bottom_y) = ( -// first_in_column[0], -// first_in_column[1].min(last_in_column[1]), -// first_in_column[1].max(last_in_column[1]), -// ); - -// if top_y < top_tiles_last_y { -// top_tiles.push([x, top_y]); -// top_tiles_last_y = top_y; -// } - -// if bottom_y > bottom_tiles_last_y { -// bottom_tiles.push([x, bottom_y]); -// bottom_tiles_last_y = bottom_y; -// } -// } - -// (top_tiles, bottom_tiles) -// } - fn get_potential_left_corner_tiles( - sorted_tiles: impl Iterator, -) -> (Vec<[u64; 2]>, Vec<[u64; 2]>) { + sorted_tiles: impl Iterator, +) -> (Vec<[u32; 2]>, Vec<[u32; 2]>) { let mut left_tiles = Vec::new(); - let mut left_tiles_last_x = u64::MAX; + let mut left_tiles_last_x = u32::MAX; let mut right_tiles = Vec::new(); - let mut right_tiles_last_x = u64::MIN; + let mut right_tiles_last_x = u32::MIN; let mut it = sorted_tiles.peekable(); @@ -137,12 +95,6 @@ fn get_potential_left_corner_tiles( last_in_row = p; } - // let (x, top_y, bottom_y) = ( - // first_in_column[0], - // first_in_column[1].min(last_in_column[1]), - // first_in_column[1].max(last_in_column[1]), - // ); - let (y, left_x, right_x) = ( first_in_row[1], first_in_row[0].min(last_in_row[0]), @@ -164,12 +116,13 @@ fn get_potential_left_corner_tiles( } #[inline] -fn find_largest_from_all_corners(corner: &[[u64; 2]], opposite_corner: &[[u64; 2]]) -> u64 { +fn find_largest_from_all_corners(corner: &[[u32; 2]], opposite_corner: &[[u32; 2]]) -> u64 { let mut largest = 0_u64; for &p in corner { for &q in opposite_corner { - largest = largest.max((p[0].abs_diff(q[0]) + 1) * (p[1].abs_diff(q[1]) + 1)); + largest = + largest.max((p[0].abs_diff(q[0]) + 1) as u64 * (p[1].abs_diff(q[1]) + 1) as u64); } } @@ -185,7 +138,7 @@ pub fn part2(tiles: &[Tile]) -> u64 { let mut candidates: Vec = Vec::with_capacity(512); // Maintain an ordered list of descending edges, i.e. [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...]: - let mut descending_edges: Vec = vec![]; + let mut descending_edges: Vec = vec![]; let mut intervals_from_descending_edges = vec![]; // Invariants on the input data (defined by the puzzle) result in points arriving in pairs on the same y line: @@ -220,8 +173,9 @@ pub fn part2(tiles: &[Tile]) -> u64 { for candidate in &candidates { for x in [x0, x1] { if candidate.interval.contains(x) { - largest_area = largest_area - .max((candidate.x.abs_diff(x) + 1) * (candidate.y.abs_diff(y) + 1)); + largest_area = largest_area.max( + (candidate.x.abs_diff(x) + 1) as u64 * (candidate.y.abs_diff(y) + 1) as u64, + ); } } } @@ -253,7 +207,7 @@ pub fn part2(tiles: &[Tile]) -> u64 { } // Adds `value` if it isn't in `ordered_list`, removes it if it is, maintaining the order. -fn toggle_value_membership_in_ordered_list(ordered_list: &mut Vec, value: u64) { +fn toggle_value_membership_in_ordered_list(ordered_list: &mut Vec, value: u32) { match ordered_list.binary_search(&value) { Ok(i) => { ordered_list.remove(i); @@ -267,7 +221,7 @@ fn toggle_value_membership_in_ordered_list(ordered_list: &mut Vec, value: u // Changes the list of descending edges, [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...], // into a vector containing the intervals. #[inline] -fn update_intervals_from_descending_edges(descending_edges: &[u64], to_update: &mut Vec) { +fn update_intervals_from_descending_edges(descending_edges: &[u32], to_update: &mut Vec) { debug_assert!(descending_edges.len().is_multiple_of(2)); to_update.clear();