use id_arena::{Arena, Id}; use itertools::Itertools; type NodeId = Id; #[derive(Debug, Eq, PartialEq)] struct Node { value: i32, prev: Option, next: Option, } fn main() { // Use command line arguments to specify the input filename. let args: Vec = std::env::args().collect(); if args.len() < 2 { panic!("Usage: ./main \nNo input file provided. Exiting."); } // Next, read the contents of the input file into a string for easier processing. let input = std::fs::read_to_string(&args[1]).expect("Error opening file"); // The actual set of initial numbers, completely immutable. let numbers = input .lines() .map(|e| e.parse::().unwrap()) .collect_vec(); // Translate this list into a self-rolled linked-list with the help of the `id-arena` crate. let mut nodes = Arena::::with_capacity(numbers.len()); // Keep the original order of references for the mixing operation. let mut og_order: Vec = Vec::with_capacity(numbers.len()); // Fill the arena with the Nodes. let mut prev_node = None; for num in numbers { // Create the current Node. let current_node = nodes.alloc(Node { value: num, prev: prev_node, next: None, }); og_order.push(current_node); // Fix the next-pointer of the previous node, if it exists. if let Some(pn) = prev_node { nodes[pn].next = Some(current_node); } // And make the current element the previous one. prev_node = Some(current_node); } // Make the linked list cyclic, as that matches the mixing behvaior of the task. let first = og_order.first().copied().unwrap(); let last = og_order.last().copied().unwrap(); nodes[first].prev = Some(last); nodes[last].next = Some(first); // We'll need it later: Find the element with value 0, it has special significance. let zero_node = nodes .iter() .find_map(|(i, e)| if e.value == 0 { Some(i) } else { None }) .unwrap(); let print_list = |a: &Arena, s: NodeId| { print!("["); let mut c = s; loop { print!("{}", a[c].value); c = a[c].next.unwrap(); if c == s { break; } print!(", "); } println!("]"); }; // Now iterate through all nodes in their original order. for id in og_order { // print_list(&nodes, zero_node); // Save the hassle if the value is 0. if nodes[id].value == 0 { continue; } // Keep track of the element after which to insert the current one. let mut target = nodes[id].prev.unwrap(); // Move as many steps as the value requires. for _ in 0..nodes[id].value.abs() { // Move left or right, depending on sign of value. if nodes[id].value.is_negative() { // println!("Moving {} to the left.", nodes[id].value); target = nodes[target].prev.unwrap(); // Move again if the target is ourselves. if target == id { // println!("Skipping self, moving to the left."); target = nodes[target].prev.unwrap(); } } else { // println!("Moving {} to the right.", nodes[id].value); target = nodes[target].next.unwrap(); // Move again if the target is ourselves. if target == id { // println!("Skipping self, moving to the right."); target = nodes[target].next.unwrap(); } } } // Actually perform the swap. let new_prev = target; let new_next = nodes[target].next.unwrap(); let old_prev = nodes[id].prev.unwrap(); let old_next = nodes[id].next.unwrap(); // Rebind the old neighbors to each other. nodes[old_prev].next = Some(old_next); nodes[old_next].prev = Some(old_prev); // Bind the new neighbors to the node. nodes[new_prev].next = Some(id); nodes[new_next].prev = Some(id); // Bind the node itself to the new neighbors. nodes[id].prev = Some(new_prev); nodes[id].next = Some(new_next); } // print_list(&nodes, zero_node); // Find the 1000th, 2000th and 3000th element, starting form the zero-element, // and add them together for the final result. let mut result = 0i32; let mut current_node = zero_node; for i in 1..=3000 { current_node = nodes[current_node].next.unwrap(); if i % 1000 == 0 { // println!("Adding {}.", nodes[current_node].value); result += nodes[current_node].value; } } println!("Result: {}", result); }