--- /dev/null
+extern crate clap;
+extern crate rayon;
+
+use std::error::Error;
+use std::fs::File;
+use std::io::{prelude::*, BufReader};
+
+use rayon::prelude::*;
+use clap::{Parser, ArgAction};
+
+/// Advent of Code 2023 day 6
+#[derive(Parser, Default, Debug)]
+struct Arguments {
+ /// specify the input file name
+ filename: String,
+ #[arg(short, long, action=ArgAction::SetTrue)]
+ /// enable debug output
+ debug: Option<bool>,
+}
+
+type Parsed = (Vec<i64>, Vec<i64>);
+
+fn parse_input(filename: &str) -> Parsed {
+ let input = File::open(filename).expect("file not found");
+ let buffered = BufReader::new(input);
+ let data: Vec<String> = buffered.lines().collect::<Result<_,_>>().unwrap();
+ (
+ data[0].strip_prefix("Time: ")
+ .unwrap()
+ .split_whitespace()
+ .map(|x| x.parse::<i64>().unwrap())
+ .collect::<Vec<i64>>(),
+ data[1].strip_prefix("Distance: ")
+ .unwrap()
+ .split_whitespace()
+ .map(|x| x.parse::<i64>().unwrap())
+ .collect::<Vec<i64>>(),
+ )
+}
+
+fn part1(input: &Parsed) -> i64 {
+ input.0.iter().zip(input.1.iter())
+ .map(|(&t, &d)| (1..t)
+ .into_par_iter()
+ .filter(|&x| x * (t - x) > d)
+ .count() as i64)
+ .product()
+}
+
+fn part2(filename: &str) -> i64 {
+ let handle = File::open(filename).expect("file not found");
+ let buffered = BufReader::new(handle);
+ let data: Vec<String> = buffered.lines().collect::<Result<_,_>>().unwrap();
+ let input = (
+ data[0].strip_prefix("Time: ")
+ .unwrap()
+ .split_whitespace()
+ .collect::<String>()
+ .parse()
+ .unwrap(),
+ data[1].strip_prefix("Distance: ")
+ .unwrap()
+ .split_whitespace()
+ .collect::<String>()
+ .parse()
+ .unwrap()
+ );
+ part1(&(vec![input.0], vec![input.1]))
+}
+
+fn main() -> Result<(), Box<dyn Error>> {
+ let args = Arguments::parse();
+ let input = parse_input(&args.filename);
+ if args.debug.is_some() {
+ println!("Time: {:?}, Distance: {:?}", input.0, input.1);
+ }
+ println!("part 1: {}", part1(&input));
+ println!("part 2: {}", part2(&args.filename));
+
+ Ok(())
+}