I love large numbers v2

This commit is contained in:
Tyler Beckman 2023-12-06 21:55:04 -07:00
parent 92046cd091
commit e5a8846271
Signed by: Ty
GPG key ID: 2813440C772555A4
7 changed files with 182 additions and 65 deletions

View file

@ -131,7 +131,7 @@ mod tests {
.664.598.."# .664.598.."#
.to_string(), .to_string(),
} }
.solve(1)); .solve(1, false));
} }
#[test] #[test]
@ -150,6 +150,6 @@ mod tests {
"# "#
.to_string(), .to_string(),
} }
.solve(2)); .solve(2, false));
} }
} }

View file

@ -95,7 +95,7 @@ Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"#
acc: 0usize, acc: 0usize,
cards: HashMap::new() cards: HashMap::new()
} }
.solve(1)); .solve(1, false));
} }
#[test] #[test]
@ -111,6 +111,6 @@ Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"#
acc: 0usize, acc: 0usize,
cards: HashMap::new() cards: HashMap::new()
} }
.solve(2)); .solve(2, false));
} }
} }

View file

@ -189,7 +189,7 @@ humidity-to-location map:
56 93 4"# 56 93 4"#
.to_string(), .to_string(),
} }
.solve(1)); .solve(1, false));
} }
#[test] #[test]
@ -230,6 +230,6 @@ humidity-to-location map:
56 93 4"# 56 93 4"#
.to_string(), .to_string(),
} }
.solve(2)); .solve(2, false));
} }
} }

View file

@ -1,6 +1,6 @@
use std::str::FromStr; use std::str::FromStr;
use num_bigint::BigInt; use num_bigint::{BigInt, BigUint};
use num_irrational::QuadraticSurd; use num_irrational::QuadraticSurd;
use crate::utils::Day; use crate::utils::Day;
@ -12,13 +12,21 @@ pub struct Day6 {
impl Day for Day6 { impl Day for Day6 {
fn part1(&mut self) -> String { fn part1(&mut self) -> String {
let lines = self.input.lines().map(|l| l.split_ascii_whitespace().skip(1).collect::<Vec<_>>()).collect::<Vec<_>>(); let mut lines = self
let races: [(f64, f64); 4] = [ .input
(lines[0][0].parse().unwrap(), lines[1][0].parse().unwrap()), .lines()
(lines[0][1].parse().unwrap(), lines[1][1].parse().unwrap()), .map(|l| l.split_ascii_whitespace().skip(1));
(lines[0][2].parse().unwrap(), lines[1][2].parse().unwrap()), let times = lines.next().unwrap();
(lines[0][3].parse().unwrap(), lines[1][3].parse().unwrap()), let mut distances = lines.next().unwrap();
]; let races = times.map(|t| {
(
t.parse::<f64>().unwrap(),
distances
.next()
.and_then(|d| d.parse::<f64>().ok())
.unwrap(),
)
});
let mut acc = 1usize; let mut acc = 1usize;
@ -29,39 +37,84 @@ impl Day for Day6 {
// if distance > record_distance { wins += 1; } // if distance > record_distance { wins += 1; }
// } // }
// Mathy solution: // Mathy solution:
let b = ((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).sqrt())) / 2.0).ceil(); 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::<f64>().unwrap(),
distances
.next()
.and_then(|d| d.parse::<f64>().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() acc.to_string()
} }
fn part2(&mut self) -> String { fn part2(&mut self) -> String {
let lines = self.input.lines().map(|l| l[10..].split_ascii_whitespace().collect::<String>()).collect::<Vec<_>>(); let lines = self
.input
.lines()
.map(|l| l[10..].split_ascii_whitespace().collect::<String>())
.collect::<Vec<_>>();
// Boring solution: let time = lines[0].parse::<f64>().unwrap();
// let mut wins = 0u128; let record_distance = lines[1].parse::<f64>().unwrap();
// for time_holding_button in 0..time {
// let distance = (time - time_holding_button) * time_holding_button /* velocity */;
// if distance > record_distance { wins += 1; }
// }
// wins.to_string() let b = ((time + ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).floor();
// Mathy solution: let a = ((time - ((time * time - 4.0 * (record_distance + 1.0)).sqrt())) / 2.0).ceil();
// 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(); (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::<String>())
.collect::<Vec<_>>();
// (b - a).to_string()
// Arbitrary precision solution:
let lower = QuadraticSurd::from_equation( let lower = QuadraticSurd::from_equation(
Into::<BigInt>::into(-1).into(), BigInt::from(-1).into(),
BigInt::from_str(&lines[0]).unwrap().into(), BigInt::from_str(&lines[0]).unwrap().into(),
BigInt::from_str(&("-".to_string() + &lines[1])).unwrap().into() (BigInt::from_str(&("-".to_string() + &lines[1])).unwrap() - BigInt::from(1)).into(),
).unwrap(); )
.unwrap();
let higher = lower.conj_ref(); 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()
} }
} }

View file

@ -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<String>) {
(
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) { pub async fn fetch_example(auth: String, day: u8, part: u8) -> (String, String) {
let html = reqwest::Client::new() let html = reqwest::Client::new()
.get(format!("https://adventofcode.com/2023/day/{day}")) .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) (input, solution)
} }
#[cfg(test)]
mod tests {
use crate::fetcher;
#[tokio::test]
async fn fetches_large_data() {
fetcher::fetch_large(6, 1).await;
}
}

View file

@ -37,11 +37,11 @@ enum Subcommands {
/// The part to solve for /// The part to solve for
part: u8, part: u8,
/// If used, then the example input will be solved and checked rather than the real one /// 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, example: bool,
/// If used, then the custom input will be solved and checked rather than the real one /// If used, then the large input will be solved and checked rather than the real one
#[arg(short, long)] #[arg(short, long, group = "inputs")]
custom: bool, large: bool,
}, },
/// Runs solution N amount of times, averaging speed /// Runs solution N amount of times, averaging speed
Benchmark { Benchmark {
@ -53,6 +53,9 @@ enum Subcommands {
warmup: u32, warmup: u32,
/// The amount of times to run /// The amount of times to run
n: u32, 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 /// Fetches and prints the given input for a specified day
Input { Input {
@ -74,11 +77,16 @@ async fn main() {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
match cli.subcommand { match cli.subcommand {
Subcommands::Solve { day, part, example, custom } => { Subcommands::Solve {
day,
part,
example,
large,
} => {
let (input, solution) = match example { let (input, solution) = match example {
false => match custom { false => match large {
false => (fetcher::fetch_input(cli.auth, day).await, None), 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 => { true => {
let example = fetcher::fetch_example(cli.auth, day, part).await; let example = fetcher::fetch_example(cli.auth, day, part).await;
@ -103,12 +111,15 @@ async fn main() {
_ => panic!("Invalid day #"), _ => panic!("Invalid day #"),
}; };
let start = std::time::Instant::now(); let start = std::time::Instant::now();
let result = day.solve(part); let result = day.solve(part, large);
let end = std::time::Instant::now(); let end = std::time::Instant::now();
println!("Solution: {result}"); println!("Solution: {result}");
if let Some(solution) = solution { 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(); let timing = (end - start).as_nanos();
@ -128,43 +139,37 @@ async fn main() {
part, part,
warmup, warmup,
n, n,
large,
} => { } => {
let response = client let input = match large {
.get(format!("https://adventofcode.com/2023/day/{day}/input")) false => fetcher::fetch_input(cli.auth, day).await,
.header("Cookie", format!("session={auth}", auth = cli.auth)) true => fetcher::fetch_large(day, part).await.0,
.send() };
.await
.unwrap()
.text()
.await
.unwrap()
.trim_end()
.to_owned();
let mut day: Box<dyn Day> = match day { let mut day: Box<dyn Day> = match day {
1 => Box::new(Day1 { input: response }), 1 => Box::new(Day1 { input }),
2 => Box::new(Day2 { 2 => Box::new(Day2 {
input: response, input,
games: vec![], games: vec![],
}), }),
3 => Box::new(Day3 { input: response }), 3 => Box::new(Day3 { input }),
4 => Box::new(Day4 { 4 => Box::new(Day4 {
input: response, input,
acc: 0usize, acc: 0usize,
cards: HashMap::new(), cards: HashMap::new(),
}), }),
5 => Box::new(Day5 { input: response }), 5 => Box::new(Day5 { input }),
6 => Box::new(Day6 { input: response }), 6 => Box::new(Day6 { input }),
_ => panic!("Invalid day #"), _ => panic!("Invalid day #"),
}; };
let mut timings = Vec::<Duration>::new(); let mut timings = Vec::<Duration>::new();
for _ in 1..warmup { for _ in 1..warmup {
day.solve(part); day.solve(part, large);
} }
for i in 1..=n { for i in 1..=n {
let start = Instant::now(); let start = Instant::now();
day.solve(part); day.solve(part, large);
let end = Instant::now(); let end = Instant::now();
timings.push(end - start); timings.push(end - start);

View file

@ -1,12 +1,24 @@
pub trait Day: std::fmt::Debug { pub trait Day: std::fmt::Debug {
fn part1(&mut self) -> String; fn part1(&mut self) -> String;
fn part2(&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 parse(&mut self) {}
fn solve(&mut self, part: u8) -> String { fn solve(&mut self, part: u8, large: bool) -> String {
self.parse(); self.parse();
match part { match part {
1 => self.part1(), 1 => match large {
2 => self.part2(), false => self.part1(),
true => self.part1_large(),
},
2 => match large {
false => self.part2(),
true => self.part2_large(),
},
_ => panic!("Invalid part #"), _ => panic!("Invalid part #"),
} }
} }