From e5a88462714da9c6be348772ec1b046b218cc21e Mon Sep 17 00:00:00 2001 From: Ty Date: Wed, 6 Dec 2023 21:55:04 -0700 Subject: [PATCH] I love large numbers v2 --- src/day3/mod.rs | 4 +- src/day4/mod.rs | 4 +- src/day5/mod.rs | 4 +- src/day6/mod.rs | 109 +++++++++++++++++++++++++++++++++++------------- src/fetcher.rs | 47 +++++++++++++++++++++ src/main.rs | 61 ++++++++++++++------------- src/utils.rs | 18 ++++++-- 7 files changed, 182 insertions(+), 65 deletions(-) diff --git a/src/day3/mod.rs b/src/day3/mod.rs index 1647f42..2a4269a 100644 --- a/src/day3/mod.rs +++ b/src/day3/mod.rs @@ -131,7 +131,7 @@ mod tests { .664.598.."# .to_string(), } - .solve(1)); + .solve(1, false)); } #[test] @@ -150,6 +150,6 @@ mod tests { "# .to_string(), } - .solve(2)); + .solve(2, false)); } } diff --git a/src/day4/mod.rs b/src/day4/mod.rs index 2157f68..37de3ef 100644 --- a/src/day4/mod.rs +++ b/src/day4/mod.rs @@ -95,7 +95,7 @@ Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"# acc: 0usize, cards: HashMap::new() } - .solve(1)); + .solve(1, false)); } #[test] @@ -111,6 +111,6 @@ Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"# acc: 0usize, cards: HashMap::new() } - .solve(2)); + .solve(2, false)); } } diff --git a/src/day5/mod.rs b/src/day5/mod.rs index 27d1f13..a6a945a 100644 --- a/src/day5/mod.rs +++ b/src/day5/mod.rs @@ -189,7 +189,7 @@ humidity-to-location map: 56 93 4"# .to_string(), } - .solve(1)); + .solve(1, false)); } #[test] @@ -230,6 +230,6 @@ humidity-to-location map: 56 93 4"# .to_string(), } - .solve(2)); + .solve(2, false)); } } diff --git a/src/day6/mod.rs b/src/day6/mod.rs index ad6d7cc..2fb334f 100644 --- a/src/day6/mod.rs +++ b/src/day6/mod.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use num_bigint::BigInt; +use num_bigint::{BigInt, BigUint}; use num_irrational::QuadraticSurd; use crate::utils::Day; @@ -12,13 +12,21 @@ pub struct Day6 { impl Day for Day6 { fn part1(&mut self) -> String { - let lines = self.input.lines().map(|l| l.split_ascii_whitespace().skip(1).collect::>()).collect::>(); - let races: [(f64, f64); 4] = [ - (lines[0][0].parse().unwrap(), lines[1][0].parse().unwrap()), - (lines[0][1].parse().unwrap(), lines[1][1].parse().unwrap()), - (lines[0][2].parse().unwrap(), lines[1][2].parse().unwrap()), - (lines[0][3].parse().unwrap(), lines[1][3].parse().unwrap()), - ]; + let mut lines = self + .input + .lines() + .map(|l| l.split_ascii_whitespace().skip(1)); + let times = lines.next().unwrap(); + let mut distances = lines.next().unwrap(); + let races = times.map(|t| { + ( + t.parse::().unwrap(), + distances + .next() + .and_then(|d| d.parse::().ok()) + .unwrap(), + ) + }); let mut acc = 1usize; @@ -29,39 +37,84 @@ impl Day for Day6 { // if distance > record_distance { wins += 1; } // } // Mathy solution: - let b = ((time+((time*time-4.0*record_distance).sqrt())) / 2.0).ceil(); - let a = ((time-((time*time-4.0*record_distance).sqrt())) / 2.0).ceil(); + let b = ((time + ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).floor(); + let a = ((time - ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).ceil(); - acc *= (b - a) as usize; + acc *= (b - a + 1.0) as usize; + } + + acc.to_string() + } + + fn part1_large(&mut self) -> String { + let mut lines = self + .input + .lines() + .map(|l| l.split_ascii_whitespace().skip(1)); + let times = lines.next().unwrap(); + let mut distances = lines.next().unwrap(); + let races = times.map(|t| { + ( + t.parse::().unwrap(), + distances + .next() + .and_then(|d| d.parse::().ok()) + .unwrap(), + ) + }); + + let mut acc = BigUint::from(1u8); + + for (time, record_distance) in races { + // let mut wins = 0usize; + // for time_holding_button in 0..time { + // let distance = (time - time_holding_button) * time_holding_button /* velocity */; + // if distance > record_distance { wins += 1; } + // } + // Mathy solution: + let b = ((time + ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).floor(); + let a = ((time - ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).ceil(); + + acc *= (b - a + 1.0) as usize; } acc.to_string() } fn part2(&mut self) -> String { - let lines = self.input.lines().map(|l| l[10..].split_ascii_whitespace().collect::()).collect::>(); + let lines = self + .input + .lines() + .map(|l| l[10..].split_ascii_whitespace().collect::()) + .collect::>(); - // Boring solution: - // let mut wins = 0u128; - // for time_holding_button in 0..time { - // let distance = (time - time_holding_button) * time_holding_button /* velocity */; - // if distance > record_distance { wins += 1; } - // } + let time = lines[0].parse::().unwrap(); + let record_distance = lines[1].parse::().unwrap(); - // wins.to_string() - // Mathy solution: - // let b = ((time+((time*time-4.0*record_distance).sqrt())) / 2.0).ceil(); - // let a = ((time-((time*time-4.0*record_distance).sqrt())) / 2.0).ceil(); + let b = ((time + ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).floor(); + let a = ((time - ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).ceil(); + + (b - a + 1.0).to_string() + } + + fn part2_large(&mut self) -> String { + let lines = self + .input + .lines() + .map(|l| l[10..].split_ascii_whitespace().collect::()) + .collect::>(); - // (b - a).to_string() - // Arbitrary precision solution: let lower = QuadraticSurd::from_equation( - Into::::into(-1).into(), + BigInt::from(-1).into(), BigInt::from_str(&lines[0]).unwrap().into(), - BigInt::from_str(&("-".to_string() + &lines[1])).unwrap().into() - ).unwrap(); + (BigInt::from_str(&("-".to_string() + &lines[1])).unwrap() - BigInt::from(1)).into(), + ) + .unwrap(); let higher = lower.conj_ref(); - ((higher - lower).floor().to_integer().value() + 1u8).to_string() + ((higher.floor() - (lower.floor() + BigInt::from(1)) + BigInt::from(1)) + .to_integer() + .value()) + .to_string() } } diff --git a/src/fetcher.rs b/src/fetcher.rs index 329ac48..db3ae2e 100644 --- a/src/fetcher.rs +++ b/src/fetcher.rs @@ -19,6 +19,43 @@ pub async fn fetch_input(auth: String, day: u8) -> String { } } +pub async fn fetch_large(day: u8, part: u8) -> (String, Option) { + ( + reqwest::Client::new() + .get(format!("https://github.com/Vap0r1ze/aoc-large-inputs/raw/main/dist/{day:02}/input_large.txt")) + .send() + .await + .expect("Unable to make input request") + .error_for_status() + .expect("Unable to fetch large response for day, does it exist?") + .text() + .await + .expect("Unable to parse string from input response") + .trim_end() + .to_owned(), + match reqwest::Client::new() + .get(format!("https://github.com/Vap0r1ze/aoc-large-inputs/raw/main/dist/{day:02}/output_large.txt")) + .send() + .await + .expect("Unable to make input request") + .error_for_status() + .ok() { + Some(r) => Some( + r.text() + .await + .expect("Unable to parse string from input response") + .trim_end() + .to_owned() + .lines() + .nth((part - 1) as usize) + .expect("Can't get part-th line of large output") + .to_string() + ), + _ => None + } + ) +} + pub async fn fetch_example(auth: String, day: u8, part: u8) -> (String, String) { let html = reqwest::Client::new() .get(format!("https://adventofcode.com/2023/day/{day}")) @@ -56,3 +93,13 @@ pub async fn fetch_example(auth: String, day: u8, part: u8) -> (String, String) (input, solution) } + +#[cfg(test)] +mod tests { + use crate::fetcher; + + #[tokio::test] + async fn fetches_large_data() { + fetcher::fetch_large(6, 1).await; + } +} diff --git a/src/main.rs b/src/main.rs index d1094ee..bcebf96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,11 +37,11 @@ enum Subcommands { /// The part to solve for part: u8, /// If used, then the example input will be solved and checked rather than the real one - #[arg(short, long)] + #[arg(short, long, group = "inputs")] example: bool, - /// If used, then the custom input will be solved and checked rather than the real one - #[arg(short, long)] - custom: bool, + /// If used, then the large input will be solved and checked rather than the real one + #[arg(short, long, group = "inputs")] + large: bool, }, /// Runs solution N amount of times, averaging speed Benchmark { @@ -53,6 +53,9 @@ enum Subcommands { warmup: u32, /// The amount of times to run n: u32, + /// If used, then the large input will be solved and checked rather than the real one + #[arg(short, long, group = "inputs")] + large: bool, }, /// Fetches and prints the given input for a specified day Input { @@ -74,11 +77,16 @@ async fn main() { let client = reqwest::Client::new(); match cli.subcommand { - Subcommands::Solve { day, part, example, custom } => { + Subcommands::Solve { + day, + part, + example, + large, + } => { let (input, solution) = match example { - false => match custom { + false => match large { false => (fetcher::fetch_input(cli.auth, day).await, None), - true => (include_str!("input.txt").to_string(), None) + true => fetcher::fetch_large(day, part).await, }, true => { let example = fetcher::fetch_example(cli.auth, day, part).await; @@ -103,12 +111,15 @@ async fn main() { _ => panic!("Invalid day #"), }; let start = std::time::Instant::now(); - let result = day.solve(part); + let result = day.solve(part, large); let end = std::time::Instant::now(); println!("Solution: {result}"); if let Some(solution) = solution { - println!("Correct solution: {solution}") + match result == solution { + true => println!("Solution was correct!"), + false => println!("Correct solution: {solution}"), + } } let timing = (end - start).as_nanos(); @@ -128,43 +139,37 @@ async fn main() { part, warmup, n, + large, } => { - let response = client - .get(format!("https://adventofcode.com/2023/day/{day}/input")) - .header("Cookie", format!("session={auth}", auth = cli.auth)) - .send() - .await - .unwrap() - .text() - .await - .unwrap() - .trim_end() - .to_owned(); + let input = match large { + false => fetcher::fetch_input(cli.auth, day).await, + true => fetcher::fetch_large(day, part).await.0, + }; let mut day: Box = match day { - 1 => Box::new(Day1 { input: response }), + 1 => Box::new(Day1 { input }), 2 => Box::new(Day2 { - input: response, + input, games: vec![], }), - 3 => Box::new(Day3 { input: response }), + 3 => Box::new(Day3 { input }), 4 => Box::new(Day4 { - input: response, + input, acc: 0usize, cards: HashMap::new(), }), - 5 => Box::new(Day5 { input: response }), - 6 => Box::new(Day6 { input: response }), + 5 => Box::new(Day5 { input }), + 6 => Box::new(Day6 { input }), _ => panic!("Invalid day #"), }; let mut timings = Vec::::new(); for _ in 1..warmup { - day.solve(part); + day.solve(part, large); } for i in 1..=n { let start = Instant::now(); - day.solve(part); + day.solve(part, large); let end = Instant::now(); timings.push(end - start); diff --git a/src/utils.rs b/src/utils.rs index 57a7f97..5115fda 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,24 @@ pub trait Day: std::fmt::Debug { fn part1(&mut self) -> String; fn part2(&mut self) -> String; + fn part1_large(&mut self) -> String { + self.part1() + } + fn part2_large(&mut self) -> String { + self.part2() + } fn parse(&mut self) {} - fn solve(&mut self, part: u8) -> String { + fn solve(&mut self, part: u8, large: bool) -> String { self.parse(); match part { - 1 => self.part1(), - 2 => self.part2(), + 1 => match large { + false => self.part1(), + true => self.part1_large(), + }, + 2 => match large { + false => self.part2(), + true => self.part2_large(), + }, _ => panic!("Invalid part #"), } }