Add the common code to run code for each day master
authorGreg Burri <greg.burri@gmail.com>
Tue, 10 Dec 2024 21:01:30 +0000 (22:01 +0100)
committerGreg Burri <greg.burri@gmail.com>
Tue, 10 Dec 2024 21:01:30 +0000 (22:01 +0100)
src/lib.rs

index e12f22f..164b6f3 100644 (file)
@@ -1,3 +1,92 @@
-pub fn a_function() {
-    println!("Hello there!");
+use std::{
+    fs,
+    io::{BufRead, Read},
+    time::Instant,
+};
+
+use clap::Parser;
+use rayon::prelude::*;
+
+#[derive(Parser, Debug)]
+#[command(author = "Greg Burri", version = "1.0", about = "Advent of Code 2024")]
+struct Args {
+    #[arg(index(1), conflicts_with_all(["parallel"]))]
+    day: Option<usize>,
+
+    /// Run all days in parallel.
+    #[arg(short, long)]
+    parallel: bool,
+
+    /// Number of time each day is executed, the average time is displayed.
+    #[arg(short, long, default_value_t = 1)]
+    repeat: u32,
+}
+
+pub fn run(days: Vec<fn(&mut dyn BufRead) -> String>) {
+    let args = Args::parse();
+
+    match args.day {
+        Some(day) => {
+            if day >= 1 && day <= days.len() {
+                do_day(&days, day, args.repeat)
+            } 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, args.repeat));
+            } else {
+                (1..=days.len()).for_each(|d| do_day(&days, d, args.repeat));
+            }
+
+            println!(
+                "Time to execute all days: {}",
+                format_micros(now.elapsed().as_micros())
+            );
+        }
+    }
+}
+
+fn do_day(days: &[fn(&mut dyn BufRead) -> String], day: usize, repeat: u32) {
+    let filepath = format!("data/day{:02}.input", day);
+    let mut f = fs::File::open(&filepath).unwrap_or_else(|error| {
+        println!(
+            "Cannot find file {}. Did you place the input files in the 'data' directory?\nError:{}",
+            filepath, error
+        );
+        panic!();
+    });
+
+    // We read the whole file to avoid measuring I/O time.
+    let mut buffer = Vec::new();
+    f.read_to_end(&mut buffer).unwrap();
+
+    let now = Instant::now();
+    for i in 0..repeat {
+        let result = days[day - 1](&mut buffer.as_slice());
+        if i == repeat - 1 {
+            println!(
+                "Result of day {:02}: {} (time: {})",
+                day,
+                result,
+                format_micros(now.elapsed().as_micros() / repeat as u128)
+            );
+        }
+    }
+}
+
+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)
+    }
 }