Forráskód Böngészése

Solved day 4 and 5

Alexey Dorokhov 2 éve
szülő
commit
f599ecd1ec

+ 1 - 0
day_4a/.gitignore

@@ -0,0 +1 @@
+target

+ 32 - 0
day_4a/Cargo.lock

@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day_4a"
+version = "0.1.0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]

+ 9 - 0
day_4a/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "day_4a"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+nom = "7"

+ 95 - 0
day_4a/src/main.rs

@@ -0,0 +1,95 @@
+use std::fs;
+use std::env;
+use std::collections::HashSet;
+
+use nom::IResult;
+use nom::multi::separated_list0;
+use nom::bytes::complete::tag;
+use nom::character::complete::i32;
+use nom::character::complete::space1;
+use nom::character::complete::space0;
+use nom::sequence::tuple;
+use nom::sequence::preceded;
+use nom::sequence::separated_pair;
+use nom::sequence::terminated;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Card {
+    id: i32,
+    winning: HashSet<i32>,
+    unscratched: HashSet<i32>,
+}
+
+
+fn parse_card_id(input: &str) -> IResult<&str, i32> {
+    terminated(
+        preceded(
+            tuple((space0, tag("Card"),space1)),
+            i32,
+        ),
+        space0,
+    )(input)
+}
+
+fn parse_numbers(input: &str) -> IResult<&str, Vec<i32>> {
+    separated_list0(
+        space1,
+        i32
+    )(input)
+}
+
+fn parse_all_numbers(input: &str) -> IResult<&str, (Vec<i32>, Vec<i32>)> {
+    separated_pair(
+        terminated(parse_numbers, space0),
+        tag("|"),
+        preceded(space0, parse_numbers),
+    )(input)
+}
+
+fn parse_card(input: &str) -> IResult<&str, (i32, (Vec<i32>, Vec<i32>))> {
+    separated_pair(
+        parse_card_id,
+        tag(":"),
+        preceded(space0, parse_all_numbers),
+    )(input)
+}
+
+fn create_card_from_data(data: (i32, (Vec<i32>, Vec<i32>))) -> Card {
+    let (id, (winning_vec, unscratched_vec)) = data;
+    Card {
+        id,
+        winning: HashSet::from_iter(winning_vec.into_iter()),
+        unscratched: HashSet::from_iter(unscratched_vec.into_iter())
+    }
+}
+
+fn build_card(input: &str) -> Result<Card, String> {
+    match parse_card(input) {
+        Ok((rest, data)) => if rest == "" {
+            Ok(create_card_from_data(data))
+        } else {
+            Err(format!("Incomplete parse, remaining: {}", rest))
+        },
+        Err(error) => Err(error.to_string()),
+    }
+}
+
+fn main() {
+    let mut result = 0;
+
+    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)});
+    for line in input_data.lines() {
+        let card = build_card(line).unwrap_or_else(|e|{panic!("Can't build card: {}", e)});
+        let match_count = card.unscratched.intersection(&card.winning).count();
+        if match_count >= 2 {
+            result += 2 << match_count - 2;
+        } else if match_count == 1 {
+            result += 1;
+        }
+    }
+
+    println!("{}", result);
+}

+ 1 - 0
day_4b/.gitignore

@@ -0,0 +1 @@
+target

+ 32 - 0
day_4b/Cargo.lock

@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day_4b"
+version = "0.1.0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]

+ 9 - 0
day_4b/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "day_4b"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+nom = "7"

+ 109 - 0
day_4b/src/main.rs

@@ -0,0 +1,109 @@
+use std::fs;
+use std::env;
+use std::collections::HashSet;
+
+use nom::IResult;
+use nom::multi::separated_list0;
+use nom::bytes::complete::tag;
+use nom::character::complete::i32;
+use nom::character::complete::space1;
+use nom::character::complete::space0;
+use nom::sequence::tuple;
+use nom::sequence::preceded;
+use nom::sequence::separated_pair;
+use nom::sequence::terminated;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Card {
+    id: i32,
+    winning: HashSet<i32>,
+    unscratched: HashSet<i32>,
+    count: i32,
+}
+
+
+fn parse_card_id(input: &str) -> IResult<&str, i32> {
+    terminated(
+        preceded(
+            tuple((space0, tag("Card"),space1)),
+            i32,
+        ),
+        space0,
+    )(input)
+}
+
+fn parse_numbers(input: &str) -> IResult<&str, Vec<i32>> {
+    separated_list0(
+        space1,
+        i32
+    )(input)
+}
+
+fn parse_all_numbers(input: &str) -> IResult<&str, (Vec<i32>, Vec<i32>)> {
+    separated_pair(
+        terminated(parse_numbers, space0),
+        tag("|"),
+        preceded(space0, parse_numbers),
+    )(input)
+}
+
+fn parse_card(input: &str) -> IResult<&str, (i32, (Vec<i32>, Vec<i32>))> {
+    separated_pair(
+        parse_card_id,
+        tag(":"),
+        preceded(space0, parse_all_numbers),
+    )(input)
+}
+
+fn create_card_from_data(data: (i32, (Vec<i32>, Vec<i32>))) -> Card {
+    let (id, (winning_vec, unscratched_vec)) = data;
+    Card {
+        id,
+        winning: HashSet::from_iter(winning_vec.into_iter()),
+        unscratched: HashSet::from_iter(unscratched_vec.into_iter()),
+        count: 1,
+    }
+}
+
+fn build_card(input: &str) -> Result<Card, String> {
+    match parse_card(input) {
+        Ok((rest, data)) => if rest == "" {
+            Ok(create_card_from_data(data))
+        } else {
+            Err(format!("Incomplete parse, remaining: {}", rest))
+        },
+        Err(error) => Err(error.to_string()),
+    }
+}
+
+fn build_cards(input_data: String) -> Vec<Card> {
+    let mut cards: Vec<Card> = Vec::new();
+    for line in input_data.lines() {
+        let card = build_card(line).unwrap_or_else(|e|{panic!("Can't build card: {}", e)});
+        cards.push(card)
+    }
+    cards
+}
+
+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 mut cards = build_cards(input_data);
+
+    for offset in 0..cards.len() {
+        let match_count = cards[offset].unscratched.intersection(&cards[offset].winning).count();
+        let increase = cards[offset].count;
+        for offset2 in offset+1..offset+1+match_count {
+            cards[offset2].count += increase;
+        }
+    }
+
+    let mut result = 0;
+    for offset in 0..cards.len() {
+        result += cards[offset].count;
+    }
+
+    println!("{}", result);
+}

+ 1 - 0
day_5a/.gitignore

@@ -0,0 +1 @@
+target

+ 32 - 0
day_5a/Cargo.lock

@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day_5a"
+version = "0.1.0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]

+ 9 - 0
day_5a/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "day_5a"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+nom = "7"

+ 140 - 0
day_5a/src/main.rs

@@ -0,0 +1,140 @@
+use std::fs;
+use std::env;
+use std::cmp::min;
+
+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, Debug, PartialEq, Eq)]
+struct Range {
+    dst_start: i64,
+    src_start: i64,
+    len: i64,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Almanac {
+    seeds: Vec<i64>,
+    seed_to_soil_map: Vec<Range>,
+    soil_to_fertilizer_map: Vec<Range>,
+    fertilizer_to_water_map: Vec<Range>,
+    water_to_light_map: Vec<Range>,
+    light_to_temperature_map: Vec<Range>,
+    temperature_to_humidity_map: Vec<Range>,
+    humidity_to_location_map: Vec<Range>,
+}
+
+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<Range> {
+    let mut ranges = Vec::new();
+    for range_item in data {
+        ranges.push(Range{
+            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 fallthrough_lookup(ranges: &Vec<Range>, key: i64) -> i64 {
+    for range in ranges {
+        if key >= range.src_start && key < range.src_start + range.len {
+            return range.dst_start + key - range.src_start;
+        }
+    }
+    key
+}
+
+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 min_location:Option<i64> = None;
+    for seed in almanac.seeds {
+        let soil = fallthrough_lookup(&almanac.seed_to_soil_map, seed);
+        let fertilizer = fallthrough_lookup(&almanac.soil_to_fertilizer_map, soil);
+        let water = fallthrough_lookup(&almanac.fertilizer_to_water_map, fertilizer);
+        let light = fallthrough_lookup(&almanac.water_to_light_map, water);
+        let temperature = fallthrough_lookup(&almanac.light_to_temperature_map, light);
+        let humidity = fallthrough_lookup(&almanac.temperature_to_humidity_map, temperature);
+        let location = fallthrough_lookup(&almanac.humidity_to_location_map, humidity);
+
+        match min_location {
+            Some(current) => min_location = Some(min(location, current)),
+            None => min_location = Some(location),
+        }
+    }
+
+
+    println!("{}", min_location.unwrap());
+}

+ 1 - 0
day_5b/.gitignore

@@ -0,0 +1 @@
+target

+ 32 - 0
day_5b/Cargo.lock

@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day_5b"
+version = "0.1.0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]

+ 9 - 0
day_5b/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "day_5b"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+nom = "7"

+ 33 - 0
day_5b/example2.txt

@@ -0,0 +1,33 @@
+seeds: 79 14
+
+seed-to-soil map:
+50 98 2
+52 50 48
+
+soil-to-fertilizer map:
+0 15 37
+37 52 2
+39 0 15
+
+fertilizer-to-water map:
+49 53 8
+0 11 42
+42 0 7
+57 7 4
+
+water-to-light map:
+88 18 7
+18 25 70
+
+light-to-temperature map:
+45 77 23
+81 45 19
+68 64 13
+
+temperature-to-humidity map:
+0 69 1
+1 0 69
+
+humidity-to-location map:
+60 56 37
+56 93 4

+ 307 - 0
day_5b/src/main.rs

@@ -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());
+}