Skip to content

Commit

Permalink
Year 2016: Day 09
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 16, 2024
1 parent aff738f commit b2403eb
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Of note:
- The changelog 2015.5.2 has been rewritten from each commit content.
- This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs)

## [2016.9.1]
### Added
- Solved [exercice for 2016, day 09](src/year_2016/day_09.rs).

## [2016.8.1]
### Added
- Solved [exercice for 2016, day 07](src/year_2016/day_07.rs).
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2016.8.1"
version = "2016.9.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down Expand Up @@ -57,3 +57,6 @@ harness = false
[[bench]]
name = "year_2016_day_04"
harness = false
# [[bench]]
# name = "year_2016_day_09"
# harness = false
8 changes: 8 additions & 0 deletions NOTES_2016.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ Some iterators are annoying, but sometimes, like when calculating whethere there
I wouldn't call it an "object model", but I like the way Rust works with `impl X for Y`.

Also, remember the [`splice()` method](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.splice), it's very useful to replace large parts of a vector at once.

## Day 09: Explosives in Cyberspace

I knew the Rust version would be faster than my [Ruby implementation](https://github.com/joshleaves/advent-rb/blob/master/year_2016/day_09.rb), but I still felt I wasn't fast enough. I compared myself to [galenelias's implementation](https://github.com/galenelias/AdventOfCode_2016/blob/master/src/Day9/mod.rs) and indeed I wasn't fast enough.

I went back to the workbench to try another implementation, and to my delight, I was the faster. But still not faster than [fornwall's implementation](https://github.com/fornwall/advent-of-code/blob/main/crates/core/src/year2016/day09.rs).

Looking at it carefully proved very smart: both galenelias and me lost too much time **creating a string, then getting its length**, when the exercise only asked to **get the string's length**.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I

I'm also adding notes that may be useful if you're (like me) discovering Rust:
- [2015, complete!](NOTES_2015.md)
- [2016, up to day 08](NOTES_2016.md)
- [2016, up to day 09](NOTES_2016.md)

# Regarding style rules
I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me.
Expand Down
16 changes: 15 additions & 1 deletion benches/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use advent_rs::year_2016::day_05;
use advent_rs::year_2016::day_06;
use advent_rs::year_2016::day_07;
use advent_rs::year_2016::day_08;
use advent_rs::year_2016::day_09;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn year_2016_day_01(c: &mut Criterion) {
Expand Down Expand Up @@ -104,6 +105,18 @@ pub fn year_2016_day_08(c: &mut Criterion) {
g2016_day_08.finish();
}

pub fn year_2016_day_09(c: &mut Criterion) {
let mut g2016_day_09 = c.benchmark_group("year_2016::day_09");
let input_year_2016_day_09 = include_str!("../inputs/year_2016/day_09_input");
g2016_day_09.bench_function("year_2016::day_09_v1", |b| {
b.iter(|| day_09::day_09_v1(black_box(input_year_2016_day_09)))
});
g2016_day_09.bench_function("year_2016::day_09_v2", |b| {
b.iter(|| day_09::day_09_v2(black_box(input_year_2016_day_09)))
});
g2016_day_09.finish();
}

criterion_group!(
benches,
year_2016_day_01,
Expand All @@ -113,6 +126,7 @@ criterion_group!(
year_2016_day_05,
year_2016_day_06,
year_2016_day_07,
year_2016_day_08
year_2016_day_08,
year_2016_day_09
);
criterion_main!(benches);
136 changes: 136 additions & 0 deletions benches/year_2016_day_09.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use advent_rs::year_2016::day_09;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use regex::Regex;
use std::time::Duration;

// This snippet was taken from fornwall
// SRC: https://github.com/fornwall/advent-of-code/blob/main/crates/core/src/year2016/day09.rs
fn fornwall_uncompressed_size(text: &[u8], recursive: bool) -> Result<u64, String> {
let error_mapper_uf8 = |_| "Invalid input";
let error_mapper_parse = |_| "Invalid input";
let mut start_parenthesis_idx = None;
let mut uncompressed_len = 0_u64;

let mut i = 0;
while i < text.len() {
let c = text[i];
if c == b'(' {
start_parenthesis_idx = Some(i);
} else if c == b')' {
if let Some(from) = start_parenthesis_idx {
let inside_parenthesis = &text[from + 1..i];
let parts = inside_parenthesis
.split(|&c| c == b'x')
.collect::<Vec<&[u8]>>();
if parts.len() != 2 {
return Err("Invalid input".into());
}
let chars_to_take = std::str::from_utf8(parts[0])
.map_err(error_mapper_uf8)?
.parse::<u64>()
.map_err(error_mapper_parse)?;
let repetitions = std::str::from_utf8(parts[1])
.map_err(error_mapper_uf8)?
.parse::<u64>()
.map_err(error_mapper_parse)?;
uncompressed_len += repetitions
* if recursive {
fornwall_uncompressed_size(&text[i + 1..i + 1 + chars_to_take as usize], true)?
} else {
chars_to_take
};
i += chars_to_take as usize;
start_parenthesis_idx = None;
}
} else if start_parenthesis_idx.is_none() {
uncompressed_len += 1;
}
i += 1;
}

Ok(uncompressed_len)
}

// This snippet was taken from galenelias:
// SRC: https://github.com/galenelias/AdventOfCode_2016/blob/master/src/Day9/mod.rs
fn galenelias_decompress(mut input: &str, recursive: bool) -> String {
let mut result = String::new();
let repeat_regex: Regex = Regex::new(r"([^(]*)\((\d+)x(\d+)\)").unwrap();

while !input.is_empty() {
if let Some(m) = repeat_regex.captures(input) {
let rep_len = m.get(2).unwrap().as_str().parse::<usize>().unwrap();
let rep_count = m.get(3).unwrap().as_str().parse::<usize>().unwrap();
let match_end = m.get(0).unwrap().end();

// Push the prefix into the result
result.push_str(m.get(1).unwrap().as_str());
if !recursive {
result.push_str(&input[match_end..match_end + rep_len].repeat(rep_count));
} else {
result.push_str(
&galenelias_decompress(&input[match_end..match_end + rep_len], true).repeat(rep_count),
);
}
input = &input[match_end + rep_len..];
} else {
result.push_str(input);
return result;
}
}

return result;
}

pub fn bench_year_2016_day_09_v1(c: &mut Criterion) {
let mut group = c.benchmark_group("year_2016::day_09_v1");
group.warm_up_time(Duration::from_millis(100));
let input = include_str!("../inputs/year_2016/day_09_input");

group.bench_with_input(
BenchmarkId::new("fornwall", input.len()),
input,
|b, input| b.iter(|| fornwall_uncompressed_size(input.as_bytes(), false)),
);
group.bench_with_input(
BenchmarkId::new("galenelias", input.len()),
input,
|b, input| b.iter(|| galenelias_decompress(&input, false)),
);
group.bench_with_input(
BenchmarkId::new("joshleaves", input.len()),
input,
|b, input| b.iter(|| day_09::day_09_v1(input)),
);
group.finish();
}

pub fn bench_year_2016_day_09_v2(c: &mut Criterion) {
let mut group = c.benchmark_group("year_2016::day_09_v2");
group.warm_up_time(Duration::from_millis(100));
let input = include_str!("../inputs/year_2016/day_09_input");

group.bench_with_input(
BenchmarkId::new("fornwall", input.len()),
input,
|b, input| b.iter(|| fornwall_uncompressed_size(input.as_bytes(), true)),
);
group.bench_with_input(
BenchmarkId::new("galenelias", input.len()),
input,
|b, input| b.iter(|| galenelias_decompress(&input, true)),
);
group.bench_with_input(
BenchmarkId::new("joshleaves", input.len()),
input,
|b, input| b.iter(|| day_09::day_09_v2(input)),
);
group.finish();
}

criterion_group!(
bench_year_2016_day_09,
bench_year_2016_day_09_v1,
bench_year_2016_day_09_v2
);
criterion_main!(bench_year_2016_day_09);
Loading

0 comments on commit b2403eb

Please sign in to comment.