--- /dev/null
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
--- /dev/null
+[package]
+name = "advent_of_code_2022"
+version = "0.1.0"
+authors = ["Greg Burri <greg.burri@gmail.com>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+itertools = "0.10"
+regex = "1"
+clap = { version = "4", features = ["derive"] }
+rayon = "1.10"
+
+[profile.release]
+opt-level = 3
+lto = true
+codegen-units = 1
--- /dev/null
+# AdventOfCode2024
+
+https://adventofcode.com/2024
+
+
+# Running tests
+
+Example for day 1 tests:
+
+~~~
+cargo test day01 -- --nocapture
+~~~
+
+All tests:
+
+~~~
+cargo test -- --nocapture
+~~~
+
+
+# Running a day code
+
+~~~
+cargo run -- n
+~~~
+
+Where 'n' is a number from 1 to 25
--- /dev/null
+use std::{cmp::Ordering, io::BufRead, iter::Iterator};
+
+pub fn read_location_ids_as_sorted<R>(reader: R) -> (Vec<i32>, Vec<i32>)
+where
+ R: BufRead,
+{
+ let mut l1 = Vec::<i32>::new();
+ let mut l2 = Vec::<i32>::new();
+
+ for l in reader.lines() {
+ let ids: Vec<i32> = l
+ .unwrap()
+ .split_whitespace()
+ .into_iter()
+ .map(|n| n.parse::<i32>().unwrap())
+ .collect();
+
+ l1.push(ids[0]);
+ l2.push(ids[1]);
+ }
+
+ l1.sort();
+ l2.sort();
+
+ (l1, l2)
+}
+
+pub fn sum_distances(sorted_ids1: &[i32], sorted_ids2: &[i32]) -> i32 {
+ sorted_ids1
+ .iter()
+ .zip(sorted_ids2)
+ .fold(0, |sum, (id1, id2)| sum + (id1 - id2).abs())
+}
+
+pub fn similarity_score(sorted_ids1: &[i32], sorted_ids2: &[i32]) -> i32 {
+ let mut score: i32 = 0;
+ let (mut i, mut j) = (0, 0);
+ while i < sorted_ids1.len() {
+ match sorted_ids1[i].cmp(&sorted_ids2[j]) {
+ Ordering::Greater => {
+ j += 1;
+ }
+ Ordering::Less => {
+ i += 1;
+ }
+ Ordering::Equal => {
+ score += sorted_ids2[j..]
+ .iter()
+ .take_while(|v| **v == sorted_ids1[i])
+ .sum::<i32>();
+ i += 1;
+ }
+ }
+ }
+ score
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ static IDS: &str = "3 4
+4 3
+2 5
+1 3
+3 9
+3 3";
+
+ #[test]
+ fn part1() {
+ let (ids1, ids2) = read_location_ids_as_sorted(IDS.as_bytes());
+ assert_eq!(sum_distances(&ids1, &ids2), 11);
+ }
+
+ #[test]
+ fn part2() {
+ let (ids1, ids2) = read_location_ids_as_sorted(IDS.as_bytes());
+ assert_eq!(similarity_score(&ids1, &ids2), 31);
+ }
+}
--- /dev/null
+use std::{fs, io::BufReader};
+
+use crate::*;
+
+pub fn day01() -> String {
+ let f = fs::File::open("data/day01.input").unwrap();
+ let (ids1, ids2) = day01::read_location_ids_as_sorted(BufReader::new(f));
+ format!(
+ "part1: {}, part2: {}",
+ day01::sum_distances(&ids1, &ids2),
+ day01::similarity_score(&ids1, &ids2)
+ )
+}
--- /dev/null
+use std::time::Instant;
+
+use clap::Parser;
+use rayon::prelude::*;
+
+mod day01;
+// mod day02;
+// mod day03;
+// mod day04;
+// mod day05;
+// mod day06;
+// mod day07;
+// mod day08;
+// mod day09;
+// mod day10;
+// mod day11;
+// mod day12;
+// mod day13;
+// mod day14;
+// mod day15;
+// mod day16;
+// mod day17;
+// mod day18;
+// mod day19;
+// mod day20;
+// mod day21;
+// mod day22;
+// mod day23;
+// mod day24;
+mod days;
+
+#[derive(Parser, Debug)]
+#[command(author = "Greg Burri", version = "1.0", about = "Advent of Code 2024")]
+struct Args {
+ #[arg(index(1), exclusive(true))]
+ day: Option<usize>,
+
+ #[arg(short, long)]
+ parallel: bool,
+}
+
+fn main() {
+ println!("https://adventofcode.com/2024");
+
+ let days: Vec<fn() -> String> = vec![
+ days::day01,
+ // days::day02,
+ // days::day03,
+ // days::day04,
+ // days::day05,
+ // days::day06,
+ // days::day07,
+ // days::day08,
+ // days::day09,
+ // days::day10,
+ // days::day11,
+ // days::day12,
+ // days::day13,
+ // days::day14,
+ // days::day15,
+ // days::day16,
+ // days::day17,
+ // days::day18,
+ // days::day19,
+ // days::day20,
+ // days::day21,
+ // days::day22,
+ // days::day23,
+ // days::day24,
+ ];
+
+ let args = Args::parse();
+
+ match args.day {
+ Some(day) => {
+ if day >= 1 && day <= days.len() {
+ do_day(&days, day)
+ } else {
+ println!("Unknown day: {}", day)
+ }
+ }
+ // No argument -> execute all day problems.
+ None => {
+ let now = Instant::now();
+
+ if args.parallel {
+ (1..=days.len())
+ .into_par_iter()
+ .for_each(|d| do_day(&days, d));
+ } else {
+ (1..=days.len()).for_each(|d| do_day(&days, d));
+ }
+
+ println!(
+ "Time to execute all days: {}",
+ format_micros(now.elapsed().as_micros())
+ );
+ }
+ }
+}
+
+fn do_day(days: &[fn() -> String], day: usize) {
+ let now = Instant::now();
+ println!(
+ "Result of day {:02}: {} (time: {})",
+ day,
+ days[day - 1](),
+ format_micros(now.elapsed().as_micros())
+ );
+}
+
+fn format_micros(t: u128) -> String {
+ if t < 10_000 {
+ format!("{} μs", t)
+ } else if t < 10_000_000u128 {
+ format!("{:.2} ms", t as f64 / 1e3f64)
+ } else {
+ format!("{:.2} s", t as f64 / 1e6f64)
+ }
+}