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.."#
.to_string(),
}
.solve(1));
.solve(1, false));
}
#[test]
@ -150,6 +150,6 @@ mod tests {
"#
.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,
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));
}
}

View file

@ -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));
}
}

View file

@ -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::<Vec<_>>()).collect::<Vec<_>>();
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::<f64>().unwrap(),
distances
.next()
.and_then(|d| d.parse::<f64>().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::<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()
}
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 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::<f64>().unwrap();
let record_distance = lines[1].parse::<f64>().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::<String>())
.collect::<Vec<_>>();
// (b - a).to_string()
// Arbitrary precision solution:
let lower = QuadraticSurd::from_equation(
Into::<BigInt>::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()
}
}

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) {
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;
}
}

View file

@ -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<dyn Day> = 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::<Duration>::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);

View file

@ -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 #"),
}
}