1
0
Fork 0

blazingly fast day5::second

This commit is contained in:
mat 2023-12-06 01:51:41 -06:00
commit 19e4f1932a
5 changed files with 159 additions and 33 deletions

5
.gitignore vendored
View file

@ -1 +1,6 @@
/target /target
flamegraph.svg
perf.data
perf.data.old

70
Cargo.lock generated
View file

@ -5,3 +5,73 @@ version = 3
[[package]] [[package]]
name = "advent_of_code" name = "advent_of_code"
version = "0.1.0" version = "0.1.0"
dependencies = [
"heapless",
"lexical-core",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "lexical-core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46"
dependencies = [
"lexical-parse-integer",
"lexical-util",
]
[[package]]
name = "lexical-parse-integer"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9"
dependencies = [
"lexical-util",
"static_assertions",
]
[[package]]
name = "lexical-util"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc"
dependencies = [
"static_assertions",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

View file

@ -6,3 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
heapless = "0.8.0"
lexical-core = { version = "0.8.5", default-features = false, features = [
"parse-integers",
] }
[profile.release]
debug = true

View file

@ -1,6 +1,6 @@
use std::{ use std::{
collections::HashSet,
io::{BufRead, Cursor}, io::{BufRead, Cursor},
mem,
ops::Range, ops::Range,
}; };
@ -12,11 +12,14 @@ pub struct Mapping {
impl Mapping { impl Mapping {
fn parse(input: &str) -> Self { fn parse(input: &str) -> Self {
let mut parts = input.split(' ').map(|s| s.parse::<usize>().unwrap()); let input_bytes = input.as_bytes();
let (dst_start, mut offset): (usize, usize) =
let dst_start = parts.next().unwrap(); lexical_core::parse_partial(input_bytes).unwrap();
let src_start = parts.next().unwrap(); offset += 1;
let length = parts.next().unwrap(); let (src_start, src_count): (usize, usize) =
lexical_core::parse_partial(&input_bytes[offset..]).unwrap();
offset += src_count + 1;
let length: usize = lexical_core::parse(&input_bytes[offset..]).unwrap();
Mapping { Mapping {
source: src_start..(src_start + length), source: src_start..(src_start + length),
@ -33,51 +36,53 @@ pub struct Mappings {
impl Mappings { impl Mappings {
pub fn new(mut mappings: Vec<Mapping>) -> Self { pub fn new(mut mappings: Vec<Mapping>) -> Self {
// order mappings by src_start // order mappings by src_start
mappings.sort_by_key(|m| m.source.start); mappings.sort_unstable_by_key(|m| m.source.start);
Mappings { mappings } Mappings { mappings }
} }
pub fn find(&self, value: usize) -> Option<usize> { pub fn find(&self, value: usize) -> Option<usize> {
for mapping in &self.mappings { for mapping in &self.mappings {
if mapping.source.contains(&value) { if mapping.source.contains(&value) {
return Some(value.checked_add_signed(mapping.offset).unwrap()); return Some(value.wrapping_add_signed(mapping.offset));
} }
} }
None None
} }
pub fn map_range(&self, range: Range<usize>) -> Vec<Range<usize>> { pub fn map_range(&self, range: Range<usize>) -> heapless::Vec<Range<usize>, 10> {
let mut ranges = Vec::new(); let mut ranges = heapless::Vec::new();
let mut next_start = range.start; let mut next_start = range.start;
for mapping in &self.mappings { for mapping in &self.mappings {
if mapping.source.end <= range.start || range.end <= mapping.source.start { if mapping.source.end <= range.start {
// mapping out of range, skip it // mapping out of range, skip it
continue; continue;
} }
if range.end <= mapping.source.start {
break;
}
// add the unmapped range // add the unmapped range
let unmapped_range = next_start..usize::min(range.end, mapping.source.start); let unmapped_range = next_start..mapping.source.start;
if !unmapped_range.is_empty() { if !unmapped_range.is_empty() {
ranges.push(unmapped_range); ranges.push(unmapped_range).unwrap();
}
if mapping.source.end > next_start {
next_start = mapping.source.end;
} }
next_start = mapping.source.end;
// add the mapped range // add the mapped range
let overlap = usize::max(range.start, mapping.source.start) let overlap = usize::max(range.start, mapping.source.start)
..usize::min(range.end, mapping.source.end); ..usize::min(range.end, mapping.source.end);
if !overlap.is_empty() { if !overlap.is_empty() {
let dest_start = overlap.start.checked_add_signed(mapping.offset).unwrap(); let dest_start = overlap.start.wrapping_add_signed(mapping.offset);
let dest_end = dest_start + overlap.len(); let dest_end = dest_start + overlap.len();
ranges.push(dest_start..dest_end); ranges.push(dest_start..dest_end).unwrap();
} }
} }
// add the final unmapped range, if any // add the final unmapped range, if any
if next_start < range.end { if next_start < range.end {
ranges.push(next_start..range.end); ranges.push(next_start..range.end).unwrap();
} }
ranges ranges
@ -146,15 +151,50 @@ pub struct Part2Input {
impl Part2Input { impl Part2Input {
fn parse(input: &str) -> Self { fn parse(input: &str) -> Self {
let part1_input = Part1Input::parse(input); // let input = Cursor::new(input);
let mut seed_pairs = Vec::new(); let mut lines = input.lines();
for &[start, length] in part1_input.seeds.array_chunks::<2>() {
seed_pairs.push(start..(start + length)); // first line describes seeds
let seeds = lines
.next()
.unwrap()
.strip_prefix("seeds: ")
.unwrap()
.split(' ')
.map(|s| s.parse::<usize>().unwrap())
.array_chunks::<2>()
.map(|[start, length]| start..(start + length))
.collect::<Vec<_>>();
// skip empty line
lines.next();
let mut all_mappings = Vec::new();
loop {
// skip header
let header_line = lines.next();
if header_line.is_none() {
break;
}
let mut mappings = Vec::new();
// read lines until we get to an empty line
loop {
let Some(line) = lines.next() else { break };
if line.is_empty() {
break;
}
let mapping = Mapping::parse(&line);
mappings.push(mapping);
}
all_mappings.push(Mappings::new(mappings));
} }
Part2Input { Self {
seeds: seed_pairs, seeds,
all_mappings: part1_input.all_mappings, all_mappings,
} }
} }
} }
@ -183,16 +223,19 @@ pub fn first(input: &str) -> usize {
pub fn second(input: &str) -> usize { pub fn second(input: &str) -> usize {
let input = Part2Input::parse(input); let input = Part2Input::parse(input);
let mut current = input.seeds; let mut current = heapless::Vec::new();
current.extend(input.seeds.into_iter());
let mut new = heapless::Vec::<_, 100>::new();
for mappings in &input.all_mappings { for mappings in &input.all_mappings {
let mut new = HashSet::new(); for range in &current {
for range in current { new.extend(mappings.map_range(range.clone()));
new.extend(mappings.map_range(range));
} }
current = Vec::from_iter(new); current.clear();
mem::swap(&mut current, &mut new);
} }
let lowest = current.iter().map(|r| r.start).min().unwrap(); let lowest = current.into_iter().map(|r| r.start).min().unwrap();
return lowest; return lowest;
} }

View file

@ -1,4 +1,4 @@
#![feature(array_chunks)] #![feature(iter_array_chunks)]
mod day5; mod day5;