Compare commits
2 Commits
e12c0134b0
...
498c96eaeb
Author | SHA1 | Date | |
---|---|---|---|
498c96eaeb | |||
84bcbd550b |
177
5/Cargo.lock
generated
177
5/Cargo.lock
generated
@ -14,45 +14,13 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day5"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"rayon",
|
||||
"num",
|
||||
"test-case",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -71,36 +39,145 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
name = "num"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
name = "syn"
|
||||
version = "2.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-case"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
|
||||
dependencies = [
|
||||
"test-case-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-case-core"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-case-macros"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
@ -7,4 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.12.0"
|
||||
rayon = "1.8.0"
|
||||
num = "0.4.1"
|
||||
test-case = "3.3.1"
|
||||
|
155
5/src/main.rs
155
5/src/main.rs
@ -1,9 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Lines};
|
||||
use std::ops::Range;
|
||||
use std::time::Instant;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
// --- Day 5: If You Give A Seed A Fertilizer ---
|
||||
|
||||
@ -138,24 +138,31 @@ fn get_input() -> InputIter {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Problem 1 solution: {}", problem1(get_input()));
|
||||
println!("Problem 2 solution: {}", problem2(get_input()));
|
||||
let start = Instant::now();
|
||||
let ans1 = problem1(get_input());
|
||||
let duration = start.elapsed();
|
||||
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
|
||||
|
||||
let start = Instant::now();
|
||||
let ans2 = problem2(get_input());
|
||||
let duration = start.elapsed();
|
||||
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
|
||||
}
|
||||
|
||||
// PARSING
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AlmanacMapping {
|
||||
source_range: Range<u64>,
|
||||
dest_range: Range<u64>,
|
||||
source_range: Range<i64>,
|
||||
dest_range: Range<i64>,
|
||||
}
|
||||
|
||||
impl From<&str> for AlmanacMapping {
|
||||
fn from(s: &str) -> Self {
|
||||
let s_nums: Vec<_> = s.split_whitespace().take(3).collect();
|
||||
let length: u64 = s_nums[2].parse().unwrap();
|
||||
let source_start: u64 = s_nums[1].parse().unwrap();
|
||||
let dest_start: u64 = s_nums[0].parse().unwrap();
|
||||
let length: i64 = s_nums[2].parse().unwrap();
|
||||
let source_start: i64 = s_nums[1].parse().unwrap();
|
||||
let dest_start: i64 = s_nums[0].parse().unwrap();
|
||||
AlmanacMapping {
|
||||
source_range: source_start..source_start + length,
|
||||
dest_range: dest_start..dest_start + length,
|
||||
@ -164,7 +171,7 @@ impl From<&str> for AlmanacMapping {
|
||||
}
|
||||
|
||||
impl AlmanacMapping {
|
||||
fn lookup(&self, key: u64) -> Option<u64> {
|
||||
fn lookup(&self, key: i64) -> Option<i64> {
|
||||
if self.source_range.contains(&key) {
|
||||
Some(self.dest_range.start + (key - self.source_range.start))
|
||||
} else {
|
||||
@ -179,13 +186,74 @@ struct AlmanacMappingTable {
|
||||
mappings: Vec<AlmanacMapping>,
|
||||
}
|
||||
|
||||
fn range_overlap<T: num::Num + Ord + Copy>(r1: &Range<T>, r2: &Range<T>) -> Option<Range<T>> {
|
||||
let new_start = std::cmp::max(r1.start, r2.start);
|
||||
let new_end = std::cmp::min(r1.end - T::one(), r2.end - T::one());
|
||||
|
||||
if new_start <= std::cmp::min(r1.end - T::one(), r2.end - T::one())
|
||||
&& new_end >= std::cmp::max(r1.start, r2.start)
|
||||
{
|
||||
Some(new_start..new_end + T::one())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn range_exclude<T: num::Num + Ord + Copy>(keep: &Range<T>, exclude: &Range<T>) -> Vec<Range<T>> {
|
||||
let mut residual = Vec::new();
|
||||
if let Some(overlap) = range_overlap(keep, exclude) {
|
||||
if keep.start < overlap.start {
|
||||
residual.push(keep.start..overlap.start);
|
||||
}
|
||||
if keep.end > overlap.end {
|
||||
residual.push(overlap.end..keep.end);
|
||||
}
|
||||
} else {
|
||||
residual.push(keep.clone());
|
||||
}
|
||||
residual
|
||||
}
|
||||
|
||||
impl AlmanacMappingTable {
|
||||
fn lookup(&self, key: u64) -> u64 {
|
||||
fn lookup(&self, key: i64) -> i64 {
|
||||
self.mappings
|
||||
.iter()
|
||||
.find_map(|map| map.lookup(key))
|
||||
.unwrap_or(key)
|
||||
}
|
||||
fn lookup_ranges(&self, ranges: Vec<Range<i64>>) -> Vec<Range<i64>> {
|
||||
ranges
|
||||
.iter()
|
||||
.flat_map(|range| self.lookup_range(range))
|
||||
.collect()
|
||||
}
|
||||
fn lookup_range(&self, range: &Range<i64>) -> Vec<Range<i64>> {
|
||||
let mut residual = vec![range.clone()];
|
||||
let mut resolved_mappings = Vec::new();
|
||||
|
||||
for map in &self.mappings {
|
||||
let mut local_residual = Vec::new();
|
||||
for range in residual.into_iter() {
|
||||
if let Some(overlap) = range_overlap(&range, &map.source_range) {
|
||||
let dest_start_idx = overlap.start - map.source_range.start;
|
||||
let length = overlap.end.saturating_sub(overlap.start);
|
||||
resolved_mappings.push(
|
||||
(map.dest_range.start + dest_start_idx)
|
||||
..(map.dest_range.start + dest_start_idx + length),
|
||||
);
|
||||
local_residual.extend_from_slice(&range_exclude(&range, &overlap));
|
||||
} else {
|
||||
local_residual.push(range);
|
||||
}
|
||||
}
|
||||
residual = local_residual;
|
||||
}
|
||||
|
||||
// unresolved maps map 1:1
|
||||
resolved_mappings.extend_from_slice(&residual);
|
||||
|
||||
resolved_mappings
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BufRead> From<&mut Lines<T>> for AlmanacMappingTable {
|
||||
@ -211,7 +279,7 @@ impl<T: BufRead> From<&mut Lines<T>> for AlmanacMappingTable {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Almanac {
|
||||
seeds: Vec<u64>,
|
||||
seeds: Vec<i64>,
|
||||
seed_to_soil: AlmanacMappingTable,
|
||||
soil_to_fertilizer: AlmanacMappingTable,
|
||||
fertilizer_to_water: AlmanacMappingTable,
|
||||
@ -224,7 +292,7 @@ struct Almanac {
|
||||
impl<T: BufRead> From<Lines<T>> for Almanac {
|
||||
fn from(mut lines: Lines<T>) -> Self {
|
||||
let seeds_s = lines.next().unwrap().unwrap();
|
||||
let seeds: Vec<u64> = seeds_s
|
||||
let seeds: Vec<i64> = seeds_s
|
||||
.split_once(": ")
|
||||
.unwrap()
|
||||
.1
|
||||
@ -247,7 +315,7 @@ impl<T: BufRead> From<Lines<T>> for Almanac {
|
||||
}
|
||||
|
||||
impl Almanac {
|
||||
fn lookup(&self, seed_id: u64) -> u64 {
|
||||
fn lookup(&self, seed_id: i64) -> i64 {
|
||||
self.humidity_to_location.lookup(
|
||||
self.temperature_to_humidity.lookup(
|
||||
self.light_to_temperature.lookup(
|
||||
@ -261,6 +329,20 @@ impl Almanac {
|
||||
),
|
||||
)
|
||||
}
|
||||
fn lookup_range(&self, range: Range<i64>) -> Vec<Range<i64>> {
|
||||
self.humidity_to_location.lookup_ranges(
|
||||
self.temperature_to_humidity.lookup_ranges(
|
||||
self.light_to_temperature.lookup_ranges(
|
||||
self.water_to_light.lookup_ranges(
|
||||
self.fertilizer_to_water.lookup_ranges(
|
||||
self.soil_to_fertilizer
|
||||
.lookup_ranges(self.seed_to_soil.lookup_ranges(vec![range])),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PROBLEM 1 solution
|
||||
@ -280,7 +362,7 @@ impl Almanac {
|
||||
|
||||
// What is the lowest location number that corresponds to any of the initial seed numbers?
|
||||
|
||||
fn problem1_lowest_location(almanac: &Almanac) -> u64 {
|
||||
fn problem1_lowest_location(almanac: &Almanac) -> i64 {
|
||||
almanac
|
||||
.seeds
|
||||
.iter()
|
||||
@ -289,7 +371,7 @@ fn problem1_lowest_location(almanac: &Almanac) -> u64 {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
fn problem1<T: BufRead>(input: Lines<T>) -> i64 {
|
||||
let almanac = Almanac::from(input);
|
||||
problem1_lowest_location(&almanac)
|
||||
}
|
||||
@ -320,33 +402,40 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
// the almanac. What is the lowest location number that corresponds to any of the
|
||||
// initial seed numbers?
|
||||
|
||||
fn problem2_lowest_location(almanac: &Almanac) -> u64 {
|
||||
fn problem2_lowest_location(almanac: &Almanac) -> i64 {
|
||||
let seed_ranges = almanac
|
||||
.seeds
|
||||
.iter()
|
||||
.tuples()
|
||||
.map(|(range_start, length)| *range_start..*range_start + *length);
|
||||
|
||||
seed_ranges
|
||||
.map(|range| {
|
||||
range
|
||||
.into_par_iter()
|
||||
.map(|x| almanac.lookup(x))
|
||||
.min()
|
||||
.unwrap()
|
||||
})
|
||||
.min()
|
||||
.unwrap()
|
||||
// seed_ranges
|
||||
// .map(|range| {
|
||||
// range
|
||||
// .into_par_iter()
|
||||
// .map(|x| almanac.lookup(x))
|
||||
// .min()
|
||||
// .unwrap()
|
||||
// })
|
||||
// .min()
|
||||
// .unwrap()
|
||||
let ranges = seed_ranges
|
||||
.flat_map(|range| almanac.lookup_range(range))
|
||||
.collect_vec();
|
||||
|
||||
ranges.iter().map(|range| range.start).min().unwrap()
|
||||
}
|
||||
|
||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
fn problem2<T: BufRead>(input: Lines<T>) -> i64 {
|
||||
let almanac = Almanac::from(input);
|
||||
problem2_lowest_location(&almanac)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num::Num;
|
||||
use std::io::{BufRead, Cursor};
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::*;
|
||||
|
||||
@ -406,6 +495,18 @@ humidity-to-location map:
|
||||
assert_eq!(a.lookup(79), 82);
|
||||
}
|
||||
|
||||
#[test_case(0..5, 2..4, Some(2..4) ; "one range overlaps completely")]
|
||||
#[test_case(0..5, 2..10, Some(2..5) ; "one range overlaps from below")]
|
||||
#[test_case(0..5, 5..10, None ; "no overlap, touching")]
|
||||
#[test_case(0..5, 6..10, None ; "no overlap, disjoint")]
|
||||
fn test_range_overlap<T: Num + Copy + Ord + std::fmt::Debug>(
|
||||
r1: Range<T>,
|
||||
r2: Range<T>,
|
||||
expected: Option<Range<T>>,
|
||||
) {
|
||||
assert_eq!(range_overlap(&r1, &r2), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_problem1_example() {
|
||||
let c = Cursor::new(EXAMPLE_ALMANAC);
|
||||
|
Loading…
Reference in New Issue
Block a user