2020: day09: part 1 and 2
This commit is contained in:
parent
09a9654a1b
commit
a0611f1fb3
|
@ -8,6 +8,7 @@ use aoc2020::day05;
|
|||
use aoc2020::day06;
|
||||
use aoc2020::day07;
|
||||
use aoc2020::day08;
|
||||
use aoc2020::day09;
|
||||
|
||||
fn aoc2020_all(c: &mut Criterion) {
|
||||
c.bench_function("day01", |b| b.iter(|| day01::run().unwrap()));
|
||||
|
@ -18,6 +19,7 @@ fn aoc2020_all(c: &mut Criterion) {
|
|||
c.bench_function("day06", |b| b.iter(|| day06::run().unwrap()));
|
||||
c.bench_function("day07", |b| b.iter(|| day07::run().unwrap()));
|
||||
c.bench_function("day08", |b| b.iter(|| day08::run().unwrap()));
|
||||
c.bench_function("day09", |b| b.iter(|| day09::run().unwrap()));
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
|
|
1000
aoc2020/input/day09.txt
Normal file
1000
aoc2020/input/day09.txt
Normal file
File diff suppressed because it is too large
Load diff
20
aoc2020/input/day09_provided.txt
Normal file
20
aoc2020/input/day09_provided.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
35
|
||||
20
|
||||
15
|
||||
25
|
||||
47
|
||||
40
|
||||
62
|
||||
55
|
||||
65
|
||||
95
|
||||
102
|
||||
117
|
||||
150
|
||||
182
|
||||
127
|
||||
219
|
||||
299
|
||||
277
|
||||
309
|
||||
576
|
130
aoc2020/src/day09.rs
Normal file
130
aoc2020/src/day09.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use aoc::err;
|
||||
|
||||
const INPUT: &str = include_str!("../input/day09.txt");
|
||||
|
||||
pub fn run() -> aoc::Result<String> {
|
||||
let mut res = String::with_capacity(128);
|
||||
|
||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||
writeln!(res, "part 2: {}", part2(INPUT)?)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn find_pair_sum(data: &[u64], total: u64) -> Option<(u64, u64)> {
|
||||
// on huge entries using a set like in day 01 would be faster, but here it's actually slower,
|
||||
// probably due to relatively small input size
|
||||
for i in 0..data.len() {
|
||||
for j in (i + 1)..data.len() {
|
||||
if data[i] + data[j] == total {
|
||||
return Some((data[i], data[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn find_outlier(numbers: &[u64], preamble_size: usize) -> aoc::Result<(u64, usize)> {
|
||||
// start checking numbers after the preamble only
|
||||
for i in preamble_size..numbers.len() {
|
||||
let preamble = &numbers[(i - preamble_size)..i];
|
||||
let curr = numbers[i];
|
||||
|
||||
match find_pair_sum(preamble, curr) {
|
||||
Some(_) => continue,
|
||||
None => return Ok((curr, i)),
|
||||
}
|
||||
}
|
||||
|
||||
Err(err!("couldn't find number with that property"))
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> aoc::Result<u64> {
|
||||
let numbers = input
|
||||
.lines()
|
||||
.map(|line| line.parse::<u64>())
|
||||
.collect::<Result<Vec<u64>, _>>()
|
||||
.map_err(|e| err!("couldn't parse number: {}", e))?;
|
||||
|
||||
let (solution, _) = find_outlier(&numbers, 25)?;
|
||||
|
||||
Ok(solution)
|
||||
}
|
||||
|
||||
fn find_contiguous_range(numbers: &[u64], total: u64, total_idx: usize) -> aoc::Result<(u64, u64)> {
|
||||
for i in 0..total_idx {
|
||||
for j in (i + 1)..total_idx {
|
||||
let range = &numbers[i..=j];
|
||||
|
||||
if range.iter().sum::<u64>() == total {
|
||||
// it's safe to unwrap here because j > i so the range always has one number
|
||||
let min = range.iter().min().unwrap();
|
||||
let max = range.iter().max().unwrap();
|
||||
|
||||
return Ok((*min, *max));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err!("couldn't find number with that property"))
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> aoc::Result<u64> {
|
||||
let numbers = input
|
||||
.lines()
|
||||
.map(|line| line.parse::<u64>())
|
||||
.collect::<Result<Vec<u64>, _>>()
|
||||
.map_err(|e| err!("couldn't parse number: {}", e))?;
|
||||
|
||||
let (outlier, idx) = find_outlier(&numbers, 25)?;
|
||||
|
||||
let (min, max) = find_contiguous_range(&numbers, outlier, idx)?;
|
||||
|
||||
Ok(min + max)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const PROVIDED: &str = include_str!("../input/day09_provided.txt");
|
||||
|
||||
#[test]
|
||||
fn part1_provided() {
|
||||
let numbers = PROVIDED
|
||||
.lines()
|
||||
.map(|line| line.parse::<u64>().unwrap())
|
||||
.collect::<Vec<u64>>();
|
||||
|
||||
assert_eq!(find_outlier(&numbers, 5).unwrap(), (127, 14));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_real() {
|
||||
assert_eq!(part1(INPUT).unwrap(), 248131121);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_provided() {
|
||||
let numbers = PROVIDED
|
||||
.lines()
|
||||
.map(|line| line.parse::<u64>().unwrap())
|
||||
.collect::<Vec<u64>>();
|
||||
|
||||
let (outlier, idx) = find_outlier(&numbers, 5).unwrap();
|
||||
|
||||
let (min, max) = find_contiguous_range(&numbers, outlier, idx).unwrap();
|
||||
|
||||
assert_eq!(min, 15);
|
||||
assert_eq!(max, 47);
|
||||
assert_eq!(min + max, 62);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_real() {
|
||||
assert_eq!(part2(INPUT).unwrap(), 31580383);
|
||||
}
|
||||
}
|
|
@ -6,3 +6,4 @@ pub mod day05;
|
|||
pub mod day06;
|
||||
pub mod day07;
|
||||
pub mod day08;
|
||||
pub mod day09;
|
||||
|
|
|
@ -9,6 +9,7 @@ use aoc2020::day05;
|
|||
use aoc2020::day06;
|
||||
use aoc2020::day07;
|
||||
use aoc2020::day08;
|
||||
use aoc2020::day09;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let days: &[DayFunc] = &[
|
||||
|
@ -20,6 +21,7 @@ fn main() -> Result<()> {
|
|||
day06::run,
|
||||
day07::run,
|
||||
day08::run,
|
||||
day09::run,
|
||||
];
|
||||
|
||||
aoc::run(days)
|
||||
|
|
Loading…
Reference in a new issue