Compare commits
2 commits
188d490e8e
...
ef970ed47a
| Author | SHA1 | Date | |
|---|---|---|---|
| ef970ed47a | |||
| f0dc93b0bb |
6 changed files with 193 additions and 1 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
use aoc2025::day01;
|
use aoc2025::day01;
|
||||||
|
use aoc2025::day02;
|
||||||
|
|
||||||
fn aoc2025_all(c: &mut Criterion) {
|
fn aoc2025_all(c: &mut Criterion) {
|
||||||
c.bench_function("day01", |b| b.iter(|| day01::run().unwrap()));
|
c.bench_function("day01", |b| b.iter(|| day01::run().unwrap()));
|
||||||
|
c.bench_function("day02", |b| b.iter(|| day02::run().unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group! {
|
criterion_group! {
|
||||||
|
|
|
||||||
1
aoc2025/input/day02.txt
Normal file
1
aoc2025/input/day02.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
52-75,71615244-71792700,89451761-89562523,594077-672686,31503-39016,733-976,1-20,400309-479672,458-635,836793365-836858811,3395595155-3395672258,290-391,5168-7482,4545413413-4545538932,65590172-65702074,25-42,221412-256187,873499-1078482,118-154,68597355-68768392,102907-146478,4251706-4487069,64895-87330,8664371543-8664413195,4091-5065,537300-565631,77-115,83892238-83982935,6631446-6694349,1112-1649,7725-9776,1453397-1493799,10240-12328,15873-20410,1925-2744,4362535948-4362554186,3078725-3256936,710512-853550,279817-346202,45515-60928,3240-3952
|
||||||
1
aoc2025/input/day02_provided.txt
Normal file
1
aoc2025/input/day02_provided.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124
|
||||||
186
aoc2025/src/day02.rs
Normal file
186
aoc2025/src/day02.rs
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use std::{fmt::Write, ops::RangeInclusive, str::FromStr};
|
||||||
|
|
||||||
|
const INPUT: &str = include_str!("../input/day02.txt");
|
||||||
|
|
||||||
|
pub fn run() -> Result<String> {
|
||||||
|
let mut res = String::with_capacity(128);
|
||||||
|
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||||
|
writeln!(res, "part 2: {}", part2(INPUT)?)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IdRange(RangeInclusive<u64>);
|
||||||
|
|
||||||
|
impl FromStr for IdRange {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
let (left, right) = s
|
||||||
|
.split_once('-')
|
||||||
|
.ok_or_else(|| anyhow!("couldn't find dash in range: `{}'", s))?;
|
||||||
|
let (left, right) = (
|
||||||
|
left.parse()
|
||||||
|
.with_context(|| format!("couldn't parse left member of range: `{}'", left))?,
|
||||||
|
right
|
||||||
|
.trim()
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("couldn't parse right member of range: `{}'", right))?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(IdRange(left..=right))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for IdRange {
|
||||||
|
type Item = <RangeInclusive<u64> as Iterator>::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_num_digits(num: &u64) -> usize {
|
||||||
|
let mut digits = 0;
|
||||||
|
let mut num = *num;
|
||||||
|
while num != 0 {
|
||||||
|
num /= 10;
|
||||||
|
digits += 1;
|
||||||
|
}
|
||||||
|
digits
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_repeated_twice(num: &u64) -> bool {
|
||||||
|
let num_digits = get_num_digits(num);
|
||||||
|
if !num_digits.is_multiple_of(2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let half = num_digits / 2;
|
||||||
|
let mask = 10_u64.pow(half as u32);
|
||||||
|
|
||||||
|
*num / mask == *num % mask
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> Result<u64> {
|
||||||
|
let ranges = input
|
||||||
|
.split(',')
|
||||||
|
.map(IdRange::from_str)
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(ranges
|
||||||
|
.into_iter()
|
||||||
|
.map(|range| range.filter(is_repeated_twice).sum::<u64>())
|
||||||
|
.sum())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_identical_chunks(num: &u64, chunk_size: usize) -> bool {
|
||||||
|
// build a "mask" for our chunk size
|
||||||
|
let mask = 10_u64.pow(chunk_size as u32);
|
||||||
|
|
||||||
|
// this is what all chunks should look like to satisfy our condition
|
||||||
|
let target = *num % mask;
|
||||||
|
|
||||||
|
let mut num = *num;
|
||||||
|
while num > target {
|
||||||
|
// shift num right by 10^(sub_digit_num)
|
||||||
|
num /= mask;
|
||||||
|
if num % mask != target {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_repeated_at_least_twice(num: &u64) -> bool {
|
||||||
|
let num_digits = get_num_digits(num);
|
||||||
|
if num_digits < 2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
has_identical_chunks(num, 1)
|
||||||
|
|| (2..=(num_digits / 2)).any(|n| {
|
||||||
|
// walk all possible divisors of our number of digits
|
||||||
|
if !num_digits.is_multiple_of(n) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let chunk_size = num_digits / n;
|
||||||
|
has_identical_chunks(num, chunk_size)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> Result<u64> {
|
||||||
|
let ranges = input
|
||||||
|
.split(',')
|
||||||
|
.map(IdRange::from_str)
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(ranges
|
||||||
|
.into_iter()
|
||||||
|
.map(|range| range.filter(is_repeated_at_least_twice).sum::<u64>())
|
||||||
|
.sum())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const PROVIDED: &str = include_str!("../input/day02_provided.txt");
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn repeated_twice() {
|
||||||
|
assert!(is_repeated_twice(&11));
|
||||||
|
assert!(is_repeated_twice(&22));
|
||||||
|
assert!(is_repeated_twice(&99));
|
||||||
|
assert!(is_repeated_twice(&1010));
|
||||||
|
assert!(is_repeated_twice(&1188511885));
|
||||||
|
assert!(is_repeated_twice(&222222));
|
||||||
|
assert!(is_repeated_twice(&446446));
|
||||||
|
assert!(is_repeated_twice(&38593859));
|
||||||
|
|
||||||
|
assert!(!is_repeated_twice(&111));
|
||||||
|
assert!(!is_repeated_twice(&121));
|
||||||
|
assert!(!is_repeated_twice(&1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_provided() {
|
||||||
|
assert_eq!(part1(PROVIDED).unwrap(), 1227775554);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_real() {
|
||||||
|
assert_eq!(part1(INPUT).unwrap(), 26255179562);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn repeated_at_least_twice() {
|
||||||
|
assert!(is_repeated_at_least_twice(&11));
|
||||||
|
assert!(is_repeated_at_least_twice(&22));
|
||||||
|
assert!(is_repeated_at_least_twice(&99));
|
||||||
|
assert!(is_repeated_at_least_twice(&111));
|
||||||
|
assert!(is_repeated_at_least_twice(&999));
|
||||||
|
assert!(is_repeated_at_least_twice(&1010));
|
||||||
|
assert!(is_repeated_at_least_twice(&1188511885));
|
||||||
|
assert!(is_repeated_at_least_twice(&222222));
|
||||||
|
assert!(is_repeated_at_least_twice(&446446));
|
||||||
|
assert!(is_repeated_at_least_twice(&38593859));
|
||||||
|
assert!(is_repeated_at_least_twice(&565656));
|
||||||
|
assert!(is_repeated_at_least_twice(&824824824));
|
||||||
|
assert!(is_repeated_at_least_twice(&2121212121));
|
||||||
|
|
||||||
|
assert!(!is_repeated_at_least_twice(&1121));
|
||||||
|
assert!(!is_repeated_at_least_twice(&121));
|
||||||
|
assert!(!is_repeated_at_least_twice(&1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_provided() {
|
||||||
|
assert_eq!(part2(PROVIDED).unwrap(), 4174379265);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_real() {
|
||||||
|
assert_eq!(part2(INPUT).unwrap(), 31680313976);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod day01;
|
pub mod day01;
|
||||||
|
pub mod day02;
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ use anyhow::Result;
|
||||||
use aoc::DayFunc;
|
use aoc::DayFunc;
|
||||||
|
|
||||||
use aoc2025::day01;
|
use aoc2025::day01;
|
||||||
|
use aoc2025::day02;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let days: &[DayFunc] = &[day01::run];
|
let days: &[DayFunc] = &[day01::run, day02::run];
|
||||||
|
|
||||||
aoc::run(days)
|
aoc::run(days)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue