Compare commits
2 commits
ef970ed47a
...
188d490e8e
| Author | SHA1 | Date | |
|---|---|---|---|
| 188d490e8e | |||
| a9a931cd31 |
6 changed files with 209 additions and 1 deletions
|
|
@ -1,9 +1,11 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use aoc2025::day01;
|
||||
use aoc2025::day02;
|
||||
|
||||
fn aoc2025_all(c: &mut Criterion) {
|
||||
c.bench_function("day01", |b| b.iter(|| day01::run().unwrap()));
|
||||
c.bench_function("day02", |b| b.iter(|| day02::run().unwrap()));
|
||||
}
|
||||
|
||||
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
|
||||
202
aoc2025/src/day02.rs
Normal file
202
aoc2025/src/day02.rs
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
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 get_nth_digit(num: &u64, n: usize) -> u64 {
|
||||
let mut num = *num;
|
||||
for _ in 0..n {
|
||||
num /= 10;
|
||||
}
|
||||
num % 10
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (right, left) in (0..half).zip(half..num_digits) {
|
||||
let ln = get_nth_digit(num, left);
|
||||
let rn = get_nth_digit(num, right);
|
||||
|
||||
if ln != rn {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
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 day02;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ use anyhow::Result;
|
|||
use aoc::DayFunc;
|
||||
|
||||
use aoc2025::day01;
|
||||
use aoc2025::day02;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let days: &[DayFunc] = &[day01::run];
|
||||
let days: &[DayFunc] = &[day01::run, day02::run];
|
||||
|
||||
aoc::run(days)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue