diff --git a/Cargo.lock b/Cargo.lock index d525de8..d2835d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,51 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "cfg-if" +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 = "day-01" version = "0.1.0" @@ -23,12 +68,45 @@ dependencies = [ "nom", ] +[[package]] +name = "day-05" +version = "0.1.0" +dependencies = [ + "itertools", + "ranges", + "rayon", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -44,3 +122,35 @@ dependencies = [ "memchr", "minimal-lexical", ] + +[[package]] +name = "ranges" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f781d391cd4838df77e09fd26e33a87e0ac9bf2edf6ff770cfc65f83d50e3948" + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" diff --git a/Cargo.toml b/Cargo.toml index 29da117..2ee7536 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,11 @@ resolver = "2" members = [ "day-01", "day-02", - "day-03" + "day-03", + "day-05" ] [workspace.dependencies] +itertools = "0.12.0" nom = "7.1.3" +rayon = "1.8.0" diff --git a/day-05/Cargo.toml b/day-05/Cargo.toml new file mode 100644 index 0000000..1cc2024 --- /dev/null +++ b/day-05/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "day-05" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools.workspace = true +ranges = "0.3.3" +rayon.workspace = true diff --git a/day-05/src/bin/input.txt b/day-05/src/bin/input.txt new file mode 100644 index 0000000..845b4df --- /dev/null +++ b/day-05/src/bin/input.txt @@ -0,0 +1,245 @@ +seeds: 3037945983 743948277 2623786093 391282324 195281306 62641412 769611781 377903357 2392990228 144218002 1179463071 45174621 2129467491 226193957 1994898626 92402726 1555863421 340215202 426882817 207194644 + +seed-to-soil map: +3078006360 2182201339 30483272 +803630304 624445326 165226844 +2393736333 2745251526 281120946 +717936870 789672170 85693434 +598717319 410599330 27984688 +3999095007 2024628810 157572529 +3605588191 3026372472 22322803 +3555659576 2678166775 3396919 +968857148 438584018 1780307 +3216227818 2212684611 87459567 +2302084376 4122083708 91651957 +970637455 0 188112122 +507182228 299146916 40412346 +1372302034 1689624457 202945009 +1370123632 191483770 2178402 +324787204 193662172 105484744 +3116425470 2671328191 6838584 +626702007 875365604 82756204 +1575247043 978774853 317322423 +3134996187 4213735665 81231631 +2024628810 2681563694 63687832 +714565222 188112122 3371648 +547594574 1620884480 51122745 +3529388087 3374604163 26271489 +709458211 973428243 5107011 +2713008276 3985570976 98361735 +2088316642 3048695275 213767734 +3627910994 2300144178 371184013 +2674857279 4083932711 38150997 +1229789645 958121808 15306435 +4156667536 3328662676 45941487 +0 1296097276 324787204 +3108489632 3320726838 7935838 +4202609023 3667512001 92358273 +1352266801 978535254 239599 +1352506400 1672007225 17617232 +1245096080 440364325 107170721 +2811370011 3400875652 266636349 +430271948 547535046 76910280 +1158749577 339559262 71040068 +3559056495 3262463009 46531696 +3123264054 3308994705 11732133 +3303687385 3759870274 225700702 + +soil-to-fertilizer map: +2937874770 2957653952 339980892 +1886469734 2145122669 192293654 +3277855662 822424488 19779182 +2622882196 2393077006 314992574 +3449876679 3769116301 525850995 +583550735 842203670 1302918999 +2145755543 345297835 477126653 +2078763388 2890661797 66992155 +2650514 2708069580 182592217 +0 2337416323 2650514 +530540566 2340066837 53010169 +185242731 0 345297835 +3975727674 3449876679 319239622 + +fertilizer-to-water map: +861477134 5168332 68211907 +136969509 2229711837 29094441 +2823248929 1150509810 118368045 +3678888284 3073610919 53498438 +3948051821 3682691325 96234592 +1302827191 2387840795 504257794 +1198743248 1926818347 104083943 +1807084985 1104177008 46332802 +2143096098 619653304 259805223 +2063436946 2385211148 2629647 +2066066593 445026117 35759449 +358008423 537865723 81787581 +621204445 0 5168332 +2724438904 1861632296 65186051 +1853417787 2258806278 126404870 +3933311080 4141091197 14740741 +851739278 2892098589 9737856 +4044286413 3029323079 44287840 +1979822657 1778018007 83614289 +2101826042 2084781230 3070511 +4088574253 4268409625 26557671 +929689041 111346117 211974050 +3566310597 4155831938 112577687 +439796004 2030902290 53878940 +166063950 1490707297 191944473 +8760514 888219041 128208995 +3794695843 3778925917 57203243 +3029323079 3127109357 409045756 +2792635116 77722143 30613813 +3438368835 4013149435 127941762 +3732386722 3620382204 62309121 +2402901321 1682651770 95366237 +0 879458527 8760514 +493674944 2901836445 39780529 +3851899086 3536155113 81411994 +2498267558 1268877855 221829442 +4117947021 3836129160 177020275 +2789624955 108335956 3010161 +1141663091 480785566 57080157 +2104896553 406826572 38199545 +533455473 1016428036 87748972 +626372777 2087851741 141860096 +2720097000 73380239 4341904 +4115131924 3617567107 2815097 +768232873 323320167 83506405 + +water-to-light map: +3846882465 367033980 98093832 +1878565977 3292746518 62917983 +4255729420 661438934 39237876 +469590509 2191298319 301681796 +381948234 1999013894 87642275 +3688496086 199351627 156562666 +1300818753 2086656169 104642150 +806539912 2798447654 224466318 +1265336919 355914293 11119687 +1405460903 1914042148 28882526 +2577391070 1942924674 56089220 +3680239306 4136990116 8256780 +1941483960 700676810 607954854 +3845058752 3022913972 1823713 +4239658038 1308631664 16071382 +2566162195 4254580741 11228875 +1671792383 3831845903 10462472 +3944976297 3842308375 294681741 +3290662499 3160062910 132683608 +2549438814 1324703046 16723381 +3423346107 1341426427 27108304 +1031006230 3355664501 234330689 +1276456606 4145246896 24362147 +3450454411 54538430 144813197 +1682254855 465127812 196311122 +54538430 1403802338 272790856 +2633480290 2492980115 305467539 +3595267608 4169609043 84971698 +3242064105 3644614138 48598394 +3077581200 4265809616 29157680 +771272305 1368534731 35267607 +1434343429 1676593194 237448954 +327329286 3589995190 54618948 +3106738880 3024737685 135325225 +2938947829 3693212532 138633371 + +light-to-temperature map: +2777813298 2971073270 586210802 +1687968665 0 334152507 +4159107034 3882460035 135860262 +0 2095520416 192800212 +3640671099 3557284072 3145370 +2455782705 3560429442 322030593 +2022121172 1272848785 266199456 +773517036 914869331 357979454 +1131496490 1539048241 556472175 +3364024100 4018320297 60669366 +3643816469 2455782705 515290565 +192800212 334152507 580716824 +3424693466 4078989663 215977633 + +temperature-to-humidity map: +4072523312 605654847 17750681 +1174610018 540191835 65463012 +2038455907 3792024734 100202248 +2539396783 866566556 128459181 +96342672 2296045868 14715058 +3827330744 1522255720 106701221 +3816190028 4081148893 11140716 +1706101724 3892226982 188921911 +3780839952 623405528 35350076 +765616949 1813669629 408993069 +4225769488 3778728770 13295964 +2752105545 1645897858 167771771 +2138658155 1121517092 400738628 +4239065452 4155853973 55901844 +3934031965 96342672 35726394 +3005272654 658755604 22724553 +3989311833 4211755817 83211479 +280430452 3186777866 320785315 +111057730 2310760926 65268270 +176326000 3659551759 104104452 +1895023635 1628956941 16940917 +4093334384 3507563181 132435104 +3027997207 132069066 179421352 +1477400307 311490418 228701417 +2934949875 2225723089 70322779 +601215767 681480157 100836818 +2919877316 3763656211 15072559 +3969758359 3639998285 19553474 +3207418559 2613356473 573421393 +4090273993 2222662698 3060391 +1911964552 995025737 126491355 +2667855964 782316975 84249581 +1240073030 2376029196 237327277 +702052585 4092289609 63564364 + +humidity-to-location map: +2848734682 2982177676 22285660 +3380476660 3717224958 24199873 +3201930685 734568132 100088122 +764851360 4087339561 71173655 +188169313 2953711255 28466421 +3189375901 2832231336 12554784 +3369909102 47909639 10567558 +47909639 3741424831 99762378 +2871020342 58477197 7400020 +3042878026 3409715295 146497875 +1196348942 2734551883 97679453 +3418711171 3387790447 21924848 +1587973141 573552831 65833150 +1121006696 889063447 75342246 +1294028395 567796360 5756471 +3302018807 499906065 67890295 +2915035031 2921050411 32660844 +1982422286 3064299481 301982155 +704786709 4084864539 2475022 +299076626 834656254 54407193 +3623423724 2182055199 207702290 +388851709 2881400789 39649622 +147672017 3592293988 40497296 +2947695875 639385981 95182151 +707261731 3556213170 36080818 +2284404441 2389757489 138657919 +353483819 2146687309 35367890 +2519626540 441189704 58319568 +743342549 3366281636 21508811 +2878420362 2844786120 36614669 +216635734 4212526404 82440892 +3440636019 1235194267 182787705 +2577946108 964405693 270788574 +1453221956 3877915244 70435137 +836025015 1861705628 284981681 +1299784866 499509272 396793 +1859942766 3948350381 122479520 +2423062360 3004463336 59836145 +3404676533 4070829901 14034638 +1399208768 4158513216 54013188 +1300181659 342162595 99027109 +1523657093 3652908910 64316048 +3851243640 1417981972 443723656 +3831126014 3632791284 20117626 +1653806291 2528415408 206136475 +428501331 65877217 276285378 +2482898505 3841187209 36728035 \ No newline at end of file diff --git a/day-05/src/bin/part1.rs b/day-05/src/bin/part1.rs new file mode 100644 index 0000000..ab291e4 --- /dev/null +++ b/day-05/src/bin/part1.rs @@ -0,0 +1,115 @@ +use std::str::Lines; + +fn main() { + println!("{}", part1(include_str!("./input.txt"))); +} + +#[derive(Debug)] +struct MapEntry { + destination: u64, + source: u64, + range_length: u64, +} +impl MapEntry { + fn new(destination: u64, source: u64, range_length: u64) -> Self { + Self { + destination, + source, + range_length, + } + } + fn get(&self, value: u64) -> Option { + if value >= self.source && (self.source..self.source + self.range_length).contains(&value) { + Some(value - self.source + self.destination) + } else { + None + } + } +} + +fn part1(input: &str) -> u64 { + let mut seeds = Vec::new(); + let mut seed_to_soil = Vec::new(); + let mut soil_to_fertilizer = Vec::new(); + let mut fertilizer_to_water = Vec::new(); + let mut water_to_light = Vec::new(); + let mut light_to_temperature = Vec::new(); + let mut temperature_to_humidity = Vec::new(); + let mut humidity_to_location = Vec::new(); + + input.split("\n\n").for_each(|part| { + let mut lines = part.lines(); + let mut split = lines.next().unwrap().split(':'); + let key = split.next().unwrap(); + match key { + "seeds" => split + .next() + .unwrap() + .split_ascii_whitespace() + .for_each(|s| { + seeds.push(s.parse::().unwrap()); + }), + "seed-to-soil map" => process_map(lines, &mut seed_to_soil), + "soil-to-fertilizer map" => process_map(lines, &mut soil_to_fertilizer), + "fertilizer-to-water map" => process_map(lines, &mut fertilizer_to_water), + "water-to-light map" => process_map(lines, &mut water_to_light), + "light-to-temperature map" => process_map(lines, &mut light_to_temperature), + "temperature-to-humidity map" => process_map(lines, &mut temperature_to_humidity), + "humidity-to-location map" => process_map(lines, &mut humidity_to_location), + key => unreachable!("Invalid key: {}", key), + } + }); + + let maps = [ + seed_to_soil, + soil_to_fertilizer, + fertilizer_to_water, + water_to_light, + light_to_temperature, + temperature_to_humidity, + humidity_to_location, + ]; + + seeds + .into_iter() + .map(|s| find_location(s, &maps)) + .min() + .unwrap() +} + +fn process_map(lines: Lines, map: &mut Vec) { + lines + .map(|l| { + let parts = l.split_ascii_whitespace().collect::>(); + MapEntry::new( + parts[0].parse::().unwrap(), + parts[1].parse::().unwrap(), + parts[2].parse::().unwrap(), + ) + }) + .for_each(|entry| { + map.push(entry); + }) +} + +fn find_location(seed: u64, maps: &[Vec; 7]) -> u64 { + let mut current = seed; + for map in maps.iter() { + current = map + .iter() + .find_map(|entry| entry.get(current)) + .unwrap_or(current); + } + current +} + +#[cfg(test)] +mod tests { + use super::*; + const INPUT: &str = "seeds: 79 14 55 13\n\nseed-to-soil map:\n50 98 2\n52 50 48\n\nsoil-to-fertilizer map:\n0 15 37\n37 52 2\n39 0 15\n\nfertilizer-to-water map:\n49 53 8\n0 11 42\n42 0 7\n57 7 4\n\nwater-to-light map:\n88 18 7\n18 25 70\n\nlight-to-temperature map:\n45 77 23\n81 45 19\n68 64 13\n\ntemperature-to-humidity map:\n0 69 1\n1 0 69\n\nhumidity-to-location map:\n60 56 37\n56 93 4"; + + #[test] + fn test_part1() { + assert_eq!(part1(INPUT), 35); + } +} diff --git a/day-05/src/bin/part2.rs b/day-05/src/bin/part2.rs new file mode 100644 index 0000000..cbb543e --- /dev/null +++ b/day-05/src/bin/part2.rs @@ -0,0 +1,160 @@ +use itertools::Itertools; +use ranges::{GenericRange, Ranges}; +use rayon::prelude::*; +use std::{ + ops::{Bound, Range, RangeBounds}, + str::Lines, +}; + +fn main() { + println!("{}", part2(include_str!("./input.txt"))); +} + +#[derive(Debug)] +struct MapEntry { + destination: u64, + source: u64, + range_length: u64, +} +impl MapEntry { + fn new(destination: u64, source: u64, range_length: u64) -> Self { + Self { + destination, + source, + range_length, + } + } + fn source_range(&self) -> Range { + self.source..(self.source + self.range_length) + } + fn get_offset(&self) -> i64 { + self.destination as i64 - self.source as i64 + } +} + +fn part2(input: &str) -> u64 { + let mut seeds = Vec::new(); + let mut seed_to_soil = Vec::new(); + let mut soil_to_fertilizer = Vec::new(); + let mut fertilizer_to_water = Vec::new(); + let mut water_to_light = Vec::new(); + let mut light_to_temperature = Vec::new(); + let mut temperature_to_humidity = Vec::new(); + let mut humidity_to_location = Vec::new(); + + input.split("\n\n").for_each(|part| { + let mut lines = part.lines(); + let mut split = lines.next().unwrap().split(':'); + let key = split.next().unwrap(); + match key { + "seeds" => split + .next() + .unwrap() + .split_ascii_whitespace() + .tuples() + .for_each(|(s, l)| { + let start = s.parse::().unwrap(); + let length = l.parse::().unwrap(); + seeds.push(start..(start + length)); + }), + "seed-to-soil map" => process_map(lines, &mut seed_to_soil), + "soil-to-fertilizer map" => process_map(lines, &mut soil_to_fertilizer), + "fertilizer-to-water map" => process_map(lines, &mut fertilizer_to_water), + "water-to-light map" => process_map(lines, &mut water_to_light), + "light-to-temperature map" => process_map(lines, &mut light_to_temperature), + "temperature-to-humidity map" => process_map(lines, &mut temperature_to_humidity), + "humidity-to-location map" => process_map(lines, &mut humidity_to_location), + key => unreachable!("Invalid key: {}", key), + } + }); + + let maps = [ + seed_to_soil, + soil_to_fertilizer, + fertilizer_to_water, + water_to_light, + light_to_temperature, + temperature_to_humidity, + humidity_to_location, + ]; + let seeds = seeds.into_iter().fold(Ranges::new(), |acc, r| acc.union(r)); + + find_min_location(seeds, &maps) +} + +fn process_map(lines: Lines, map: &mut Vec) { + lines + .map(|l| { + let parts = l.split_ascii_whitespace().collect::>(); + MapEntry::new( + parts[0].parse::().unwrap(), + parts[1].parse::().unwrap(), + parts[2].parse::().unwrap(), + ) + }) + .for_each(|entry| { + map.push(entry); + }) +} + +fn find_min_location(seeds: Ranges, maps: &[Vec; 7]) -> u64 { + let mut new_ranges = seeds; + for map in maps { + new_ranges = apply_map(new_ranges, map); + } + + new_ranges + .as_slice() + .par_iter() + .map(|r| r.into_iter().min().unwrap()) + .min() + .unwrap() +} + +fn apply_map(mut seeds: Ranges, map: &[MapEntry]) -> Ranges { + let mut new_ranges = Ranges::new(); + for entry in map { + let matching_ranges_for_entry = seeds.clone().intersect(Ranges::from(entry.source_range())); + seeds = seeds.difference(matching_ranges_for_entry.clone()); + + let offset = entry.get_offset(); + let offset_ranges = offset_ranges(matching_ranges_for_entry, offset); + + new_ranges = new_ranges.union(offset_ranges); + } + new_ranges.union(seeds) +} + +fn offset_ranges(ranges: Ranges, offset: i64) -> Ranges { + ranges + .as_slice() + .iter() + .map(|r| offset_range(*r, offset)) + .collect::>() +} + +fn offset_range(range: GenericRange, offset: i64) -> GenericRange { + let range_start = if let Bound::Included(start) = range.start_bound() { + *start as i128 + offset as i128 + } else { + panic!("should only be called with included start bound") + } as u64; + let range_end = if let Bound::Excluded(end) = range.end_bound() { + *end as i128 + offset as i128 + } else { + panic!("should only be called with included start bound") + } as u64; + + (range_start..range_end).into() +} + +#[cfg(test)] +mod tests { + use super::*; + const INPUT: &str = "seeds: 79 14 55 13\n\nseed-to-soil map:\n50 98 2\n52 50 48\n\nsoil-to-fertilizer map:\n0 15 37\n37 52 2\n39 0 15\n\nfertilizer-to-water map:\n49 53 8\n0 11 42\n42 0 7\n57 7 4\n\nwater-to-light map:\n88 18 7\n18 25 70\n\nlight-to-temperature map:\n45 77 23\n81 45 19\n68 64 13\n\ntemperature-to-humidity map:\n0 69 1\n1 0 69\n\nhumidity-to-location map:\n60 56 37\n56 93 4"; + + #[test] + fn test_part2() { + assert_eq!(part2(INPUT), 46); + } +}