|
@@ -0,0 +1,307 @@
|
|
|
|
|
+use std::fs;
|
|
|
|
|
+use std::env;
|
|
|
|
|
+use std::cmp::min;
|
|
|
|
|
+use std::cmp::max;
|
|
|
|
|
+use std::collections::HashSet;
|
|
|
|
|
+
|
|
|
|
|
+use nom::IResult;
|
|
|
|
|
+use nom::multi::separated_list1;
|
|
|
|
|
+use nom::bytes::complete::tag;
|
|
|
|
|
+use nom::character::complete::i64;
|
|
|
|
|
+use nom::character::complete::space1;
|
|
|
|
|
+use nom::character::complete::line_ending;
|
|
|
|
|
+use nom::sequence::tuple;
|
|
|
|
|
+use nom::sequence::preceded;
|
|
|
|
|
+use nom::sequence::terminated;
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
|
+struct RangeMap {
|
|
|
|
|
+ dst_start: i64,
|
|
|
|
|
+ src_start: i64,
|
|
|
|
|
+ len: i64,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
|
|
|
+struct RangeValue {
|
|
|
|
|
+ start: i64,
|
|
|
|
|
+ len: i64,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
+struct RangeLookupResult {
|
|
|
|
|
+ dest_range: RangeValue,
|
|
|
|
|
+ src_remainder: Vec<RangeValue>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
+struct Almanac {
|
|
|
|
|
+ seeds: Vec<i64>,
|
|
|
|
|
+ seed_to_soil_map: Vec<RangeMap>,
|
|
|
|
|
+ soil_to_fertilizer_map: Vec<RangeMap>,
|
|
|
|
|
+ fertilizer_to_water_map: Vec<RangeMap>,
|
|
|
|
|
+ water_to_light_map: Vec<RangeMap>,
|
|
|
|
|
+ light_to_temperature_map: Vec<RangeMap>,
|
|
|
|
|
+ temperature_to_humidity_map: Vec<RangeMap>,
|
|
|
|
|
+ humidity_to_location_map: Vec<RangeMap>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn parse_input_data(input: &str) ->
|
|
|
|
|
+ IResult<&str, (Vec<i64>, Vec<Vec<i64>>, Vec<Vec<i64>>, Vec<Vec<i64>>,
|
|
|
|
|
+ Vec<Vec<i64>>, Vec<Vec<i64>>, Vec<Vec<i64>>, Vec<Vec<i64>>)> {
|
|
|
|
|
+ tuple((terminated(preceded(tuple((tag("seeds:"), space1)),
|
|
|
|
|
+ separated_list1(space1,i64)),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("seed-to-soil map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("soil-to-fertilizer map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("fertilizer-to-water map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("water-to-light map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("light-to-temperature map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("temperature-to-humidity map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ tuple((line_ending,line_ending))),
|
|
|
|
|
+ terminated(preceded(tuple((tag("humidity-to-location map:"), line_ending)),
|
|
|
|
|
+ separated_list1(line_ending, separated_list1(space1, i64))),
|
|
|
|
|
+ line_ending),
|
|
|
|
|
+ ))(input)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn ranges_from_vec(data: Vec<Vec<i64>>) -> Vec<RangeMap> {
|
|
|
|
|
+ let mut ranges = Vec::new();
|
|
|
|
|
+ for range_item in data {
|
|
|
|
|
+ ranges.push(RangeMap{
|
|
|
|
|
+ dst_start: range_item[0],
|
|
|
|
|
+ src_start: range_item[1],
|
|
|
|
|
+ len: range_item[2],
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ ranges
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn create_almanac_from_data(data: (Vec<i64>, Vec<Vec<i64>>, Vec<Vec<i64>>, Vec<Vec<i64>>,
|
|
|
|
|
+ Vec<Vec<i64>>, Vec<Vec<i64>>, Vec<Vec<i64>>, Vec<Vec<i64>>)) -> Almanac {
|
|
|
|
|
+ let (seeds, seed_to_soil_data, soil_to_fertilizer_data,
|
|
|
|
|
+ fertilizer_to_water_data, water_to_light_data,
|
|
|
|
|
+ light_to_temperature_data, temperature_to_humidity_data, humidity_to_location_data) = data;
|
|
|
|
|
+
|
|
|
|
|
+ Almanac {
|
|
|
|
|
+ seeds,
|
|
|
|
|
+ seed_to_soil_map: ranges_from_vec(seed_to_soil_data),
|
|
|
|
|
+ soil_to_fertilizer_map: ranges_from_vec(soil_to_fertilizer_data),
|
|
|
|
|
+ fertilizer_to_water_map: ranges_from_vec(fertilizer_to_water_data),
|
|
|
|
|
+ water_to_light_map: ranges_from_vec(water_to_light_data),
|
|
|
|
|
+ light_to_temperature_map: ranges_from_vec(light_to_temperature_data),
|
|
|
|
|
+ temperature_to_humidity_map: ranges_from_vec(temperature_to_humidity_data),
|
|
|
|
|
+ humidity_to_location_map: ranges_from_vec(humidity_to_location_data),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn parse_almanac(input_data: &str) -> Result<Almanac, String> {
|
|
|
|
|
+ match parse_input_data(input_data) {
|
|
|
|
|
+ Ok((rest, data)) => if rest == "" {
|
|
|
|
|
+ Ok(create_almanac_from_data(data))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(format!("Incomplete parse, remaining: {}", rest))
|
|
|
|
|
+ },
|
|
|
|
|
+ Err(error) => Err(error.to_string()),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn range_intersection(range_a: RangeValue, range_b: RangeValue) -> RangeValue {
|
|
|
|
|
+ if range_a.start + range_a.len <= range_b.start || range_b.start + range_b.len <= range_a.start {
|
|
|
|
|
+ return RangeValue {
|
|
|
|
|
+ start: 0,
|
|
|
|
|
+ len: 0,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let intersection_start = max(range_a.start, range_b.start);
|
|
|
|
|
+ let intersection_end = min(range_a.start + range_a.len - 1, range_b.start + range_b.len - 1);
|
|
|
|
|
+ RangeValue {
|
|
|
|
|
+ start: intersection_start,
|
|
|
|
|
+ len: intersection_end - intersection_start + 1,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn range_diff(range_a: RangeValue, range_b: RangeValue) -> Vec<RangeValue> {
|
|
|
|
|
+ let corrected_range_b = range_intersection(range_a, range_b);
|
|
|
|
|
+
|
|
|
|
|
+ if range_a == corrected_range_b {
|
|
|
|
|
+ return Vec::new();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if corrected_range_b.len == 0 {
|
|
|
|
|
+ return vec![range_a];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let res_part_1 = RangeValue {
|
|
|
|
|
+ start: range_a.start,
|
|
|
|
|
+ len: corrected_range_b.start - range_a.start,
|
|
|
|
|
+ };
|
|
|
|
|
+ let res_part_2 = RangeValue {
|
|
|
|
|
+ start: range_b.start + range_b.len,
|
|
|
|
|
+ len: range_a.start + range_a.len - range_b.start - range_b.len,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let mut res = Vec::new();
|
|
|
|
|
+ if res_part_1.len != 0 {
|
|
|
|
|
+ res.push(res_part_1);
|
|
|
|
|
+ }
|
|
|
|
|
+ if res_part_2.len != 0 {
|
|
|
|
|
+ res.push(res_part_2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ res
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
+struct LookupResult {
|
|
|
|
|
+ dst_range: RangeValue,
|
|
|
|
|
+ src_remainders: Vec<RangeValue>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn lookup_range_map(range_map: RangeMap, key: RangeValue) -> LookupResult {
|
|
|
|
|
+ let map_src_range = RangeValue {
|
|
|
|
|
+ start: range_map.src_start,
|
|
|
|
|
+ len: range_map.len,
|
|
|
|
|
+ };
|
|
|
|
|
+ let intersection = range_intersection(map_src_range, key);
|
|
|
|
|
+ if intersection.len == 0 {
|
|
|
|
|
+ return LookupResult {
|
|
|
|
|
+ dst_range: RangeValue {
|
|
|
|
|
+ start: 0,
|
|
|
|
|
+ len: 0,
|
|
|
|
|
+ },
|
|
|
|
|
+ src_remainders: vec![key],
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ LookupResult {
|
|
|
|
|
+ dst_range: RangeValue {
|
|
|
|
|
+ start: range_map.dst_start + intersection.start - range_map.src_start,
|
|
|
|
|
+ len: intersection.len,
|
|
|
|
|
+ },
|
|
|
|
|
+ src_remainders: range_diff(key, intersection),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
+struct MultiLookupResult {
|
|
|
|
|
+ dst_ranges: Vec<RangeValue>,
|
|
|
|
|
+ src_remainders: Vec<RangeValue>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn lookup_range_maps(range_maps: &Vec<RangeMap>, key: RangeValue) -> MultiLookupResult {
|
|
|
|
|
+ let mut src_remainders = vec![key];
|
|
|
|
|
+ let mut dst_ranges = Vec::new();
|
|
|
|
|
+
|
|
|
|
|
+ loop {
|
|
|
|
|
+ let mut new_remainders = Vec::new();
|
|
|
|
|
+
|
|
|
|
|
+ for key in &src_remainders {
|
|
|
|
|
+ let mut is_found = false;
|
|
|
|
|
+ for range_map in range_maps {
|
|
|
|
|
+ let lookup_res = lookup_range_map(*range_map, *key);
|
|
|
|
|
+ if lookup_res.dst_range.len != 0 {
|
|
|
|
|
+ dst_ranges.push(lookup_res.dst_range);
|
|
|
|
|
+ for src_remainder in lookup_res.src_remainders {
|
|
|
|
|
+ new_remainders.push(src_remainder);
|
|
|
|
|
+ }
|
|
|
|
|
+ is_found = true;
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if !is_found {
|
|
|
|
|
+ new_remainders.push(*key);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let next_rem_set:HashSet<RangeValue> = new_remainders.into_iter().collect();
|
|
|
|
|
+ let src_rem_set:HashSet<RangeValue> = src_remainders.clone().into_iter().collect();
|
|
|
|
|
+
|
|
|
|
|
+ if src_rem_set == next_rem_set {
|
|
|
|
|
+ src_remainders = src_rem_set.into_iter().collect();
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ src_remainders = next_rem_set.into_iter().collect();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ MultiLookupResult {
|
|
|
|
|
+ dst_ranges,
|
|
|
|
|
+ src_remainders,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+fn lookup_range_maps_fallthrough(range_maps: &Vec<RangeMap>, key: RangeValue) -> Vec<RangeValue> {
|
|
|
|
|
+ let multi_lookup_result = lookup_range_maps(range_maps, key);
|
|
|
|
|
+ let mut lookup_res = multi_lookup_result.dst_ranges;
|
|
|
|
|
+ for remainder in multi_lookup_result.src_remainders {
|
|
|
|
|
+ lookup_res.push(remainder);
|
|
|
|
|
+ }
|
|
|
|
|
+ lookup_res
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn lookup_range_maps_all_keys(range_maps: &Vec<RangeMap>, key: &Vec<RangeValue>) -> Vec<RangeValue> {
|
|
|
|
|
+ let mut res = Vec::new();
|
|
|
|
|
+ for k in key {
|
|
|
|
|
+ for rv in lookup_range_maps_fallthrough(range_maps, *k) {
|
|
|
|
|
+ res.push(rv);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ res
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+fn main() {
|
|
|
|
|
+ let args: Vec<String> = env::args().collect();
|
|
|
|
|
+ let file_path = &args[1];
|
|
|
|
|
+ let input_data = fs::read_to_string(file_path).unwrap_or_else(
|
|
|
|
|
+ |_|{panic!("Can't read file {}", file_path)});
|
|
|
|
|
+ let almanac = parse_almanac(&input_data).unwrap_or_else(
|
|
|
|
|
+ |e|{panic!("Failed to parse almanac: {}", e)});
|
|
|
|
|
+
|
|
|
|
|
+ let mut i:usize = 0;
|
|
|
|
|
+ let mut seed = Vec::new();
|
|
|
|
|
+ loop {
|
|
|
|
|
+ seed.push(RangeValue{
|
|
|
|
|
+ start: almanac.seeds[i],
|
|
|
|
|
+ len: almanac.seeds[i+1],
|
|
|
|
|
+ });
|
|
|
|
|
+ i += 2;
|
|
|
|
|
+ if i >= almanac.seeds.len() {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let soil = lookup_range_maps_all_keys(&almanac.seed_to_soil_map, &seed);
|
|
|
|
|
+ let fertilizer = lookup_range_maps_all_keys(&almanac.soil_to_fertilizer_map, &soil);
|
|
|
|
|
+ let water = lookup_range_maps_all_keys(&almanac.fertilizer_to_water_map, &fertilizer);
|
|
|
|
|
+ let light = lookup_range_maps_all_keys(&almanac.water_to_light_map, &water);
|
|
|
|
|
+ let temperature = lookup_range_maps_all_keys(&almanac.light_to_temperature_map, &light);
|
|
|
|
|
+ let humidity = lookup_range_maps_all_keys(&almanac.temperature_to_humidity_map, &temperature);
|
|
|
|
|
+ let location = lookup_range_maps_all_keys(&almanac.humidity_to_location_map, &humidity);
|
|
|
|
|
+
|
|
|
|
|
+ let mut min_location:Option<i64> = None;
|
|
|
|
|
+ for l in location {
|
|
|
|
|
+ if l.len == 0 {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ match min_location {
|
|
|
|
|
+ Some(current) => min_location = Some(min(l.start, current)),
|
|
|
|
|
+ None => min_location = Some(l.start),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ println!("{}", min_location.unwrap());
|
|
|
|
|
+}
|