use std::fs::read_to_string; use std::cmp; use std::time::Instant; fn get_numbers_in_line(str: &str) -> Vec { return str.split_whitespace().filter_map(|substr| substr.parse::().ok()).collect(); } fn process(seeds: Vec, mut maps: Vec>) -> (u32, u32) { for map in maps.iter_mut() { map.sort_unstable_by(|&(a, _, _), &(b, _, _)| a.partial_cmp(&b).unwrap()); } // Task 1 - mapping a single number through all mappings let min1 = seeds.iter().map( |seed| { let mut n = *seed; for map in &maps { for &(src, dst, len) in map { if src > n { break; } if n - src < len { n = n - src + dst; break; } } } n } ).min().unwrap(); // Task 2 - mapping whole ranges through all mappings // Array of ranges that will be mapped for task 2 initialized with pairs from seeds let mut processed_ranges: Vec<_> = (0..seeds.len()).step_by(2).map(|i| (seeds[i], seeds[i + 1])).collect(); let mut swap_ranges: Vec<(u32, u32)> = Vec::new(); // Map the processed_ranged through all mappings for map in &maps { swap_ranges.clear(); for &(working_start, working_len) in &processed_ranges { // Applying one or more mapping ranges to the processed range let (mut temp_start, mut temp_len) = (working_start, working_len); for &(src, dst, len) in map { // Working range starts before mapping range, means it's an unmapped segment if temp_start < src { let out_len = cmp::min(temp_len, src - temp_start); swap_ranges.push((temp_start, out_len)); if out_len == temp_len { break; } temp_start = src; } // Working range is sure to not begin before mapping range let start_diff = temp_start - src; // Mapping range is so short that it doesn't reach the working range, skip if len <= start_diff { continue; } let out_len = cmp::min(temp_len, len - start_diff); swap_ranges.push((dst + start_diff, out_len)); if out_len == temp_len { break; } // Consume the beginning of the working range temp_start += out_len; temp_len -= out_len; } } std::mem::swap(&mut processed_ranges, &mut swap_ranges); } // Find the range with lowest starting value, length is guaranteed to be nonzero let min2 = processed_ranges.iter().map(|&(start, _)| start).min().unwrap(); (min1, min2) } fn main() { let time_start = Instant::now(); // Set of inputs let mut seeds: Vec = Vec::new(); // Mappings of ranges, ordered in order to easily find the lowest relevant range // Order: (src, dst, len) let mut maps: Vec> = Vec::new(); maps.push(Vec::new()); let mut did_read_seeds: bool = false; for line in read_to_string("input.txt").unwrap().lines() { if !did_read_seeds { seeds = get_numbers_in_line(line); did_read_seeds = true; } else { let numbers = get_numbers_in_line(line); if numbers.len() == 3 { maps.last_mut().unwrap().push((numbers[1],numbers[0], numbers[2])); } else { if !maps.last().unwrap().is_empty() { maps.push(Vec::new()); } } } } if maps.last().unwrap().is_empty() { maps.pop(); } let time_start_no_io = Instant::now(); // for _ in 0..1000 { // let (_, _) = process(seeds.clone(), maps.clone()); // } let (min1, min2) = process(seeds, maps); // Measurements let elapsed = time_start.elapsed().as_nanos(); let elapsed_no_io = time_start_no_io.elapsed().as_nanos(); println!("Time: {}ns", elapsed); println!("Time without file i/o: {}ns", elapsed_no_io); println!("Min location1: {}", min1); println!("Min location2: {}", min2); }