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.."#
|
.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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
109
src/day6/mod.rs
109
src/day6/mod.rs
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
61
src/main.rs
61
src/main.rs
|
@ -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);
|
||||||
|
|
||||||
|
|
18
src/utils.rs
18
src/utils.rs
|
@ -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 #"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue