I love large numbers v2
This commit is contained in:
parent
92046cd091
commit
e5a8846271
7 changed files with 182 additions and 65 deletions
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
109
src/day6/mod.rs
109
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::<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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
61
src/main.rs
61
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<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);
|
||||
|
||||
|
|
18
src/utils.rs
18
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 #"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue