Skip to content

Commit

Permalink
Year 2015: Day 25
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 2, 2024
1 parent 1f190c1 commit ef204db
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2015.24.1"
version = "2015.25.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down
39 changes: 39 additions & 0 deletions NOTES_2015.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,3 +688,42 @@ year_2015::day_24/year_2015::day_24_v2
</details>
Would you believe it? The `combinations(n)` method also exists in Rust!

## Day 25: Let It Snow

<details>
<summary>📊Tests and benchmarks</summary>

```
test year_2015::day_25::tests::works_with_samples ... ok
test year_2015::tests::day_25 ... ok
year_2015::day_25/year_2015::day_25
time: [148.18 ns 148.33 ns 148.49 ns]
```
</details>

<details>
<summary>Ruby version comments</summary>

> You could solve this one by calculating each number one-by-one, cell-by-cell,... Or you could use [modular exponentiation](https://en.wikipedia.org/wiki/Modular_exponentiation).
>
> If you're careful, you will quickly understand that your operation will at some point look like `STARTER * MULT % MODULO * MULT % MODULO etc...`, and that once you know how many cells you need to iterate through, you can just repeat the `* MULT % MODULO` part.
>
> Not being a math nerd at all (I've learned to talk to machines, not to numbers), the subject was completely lost on me, but here is some help: the modulo operation itself is very funny: once you apply it once, you don't need to repeat it, since it would just end up on the same number. So you can just repeat the multiply operation many times (isn't that a "power" operation?) and apply the modulo only once.
>
> How about this:
> ```ruby
> > 9999 * 18 % 7 * 18 % 7 * 18 % 7 * 18 % 7 * 18 % 7 * 18 % 7 * 18 % 7 * 18 % 7
> => 6
> > 9999 * 18 * 18 * 18 * 18 * 18 * 18 * 18 * 18 % 7 % 7 % 7 % 7 % 7 % 7 % 7 % 7
> => 6
> > 9999 * 18 * 18 * 18 * 18 * 18 * 18 * 18 * 18 % 7
> => 6
> > 9999 * 18**8 % 7
> => 6
> ```
> Now, I got it, and so do you. Merry Xmas!
</details>
That one was very easy in Rust too, I just had to [pick up an algorithm online](https://rob.co.bb/posts/2019-02-10-modular-exponentiation-in-rust/) to properly perform modular exponentiation since it doesn't seem available in Rust.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I haven't taken the time to pick up a new language in quite a while.

I'm also adding notes that may be useful if you're (like me) discovering Rust:
- [2015, up to day 24](NOTES_2015.md)
- [2015, complete!](NOTES_2015.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
8 changes: 8 additions & 0 deletions benches/advent-bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use advent_rs::year_2015::day_21;
use advent_rs::year_2015::day_22;
use advent_rs::year_2015::day_23;
use advent_rs::year_2015::day_24;
use advent_rs::year_2015::day_25;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn year_2015_benchmark(c: &mut Criterion) {
Expand Down Expand Up @@ -266,6 +267,13 @@ pub fn year_2015_benchmark(c: &mut Criterion) {
b.iter(|| day_24::day_24_v2(black_box(input_year_2015_day_24)))
});
g2015_day_24.finish();

let mut g2015_day_25 = c.benchmark_group("year_2015::day_25");
let input_year_2015_day_25 = include_str!("../inputs/year_2015_day_25_input");
g2015_day_25.bench_function("year_2015::day_25", |b| {
b.iter(|| day_25::day_25(black_box(input_year_2015_day_25)))
});
g2015_day_25.finish();
}

criterion_group!(benches, year_2015_benchmark);
Expand Down
1 change: 1 addition & 0 deletions inputs/year_2015_day_25_input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3029,2947
8 changes: 8 additions & 0 deletions src/year_2015.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub mod day_21;
pub mod day_22;
pub mod day_23;
pub mod day_24;
pub mod day_25;

/// Returns the solution for a specified exercise and input.
///
Expand Down Expand Up @@ -110,6 +111,7 @@ pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
22 => Some(format!("{}", day_22::day_22(part, input))),
23 => Some(format!("{}", day_23::day_23(part, input))),
24 => Some(format!("{}", day_24::day_24(part, input))),
25 => Some(format!("{}", day_25::day_25(input))),
_ => None,
}
}
Expand Down Expand Up @@ -284,4 +286,10 @@ mod tests {
assert_eq!(day_24::day_24_v1(input), 10_439_961_859);
assert_eq!(day_24::day_24_v2(input), 72_050_269);
}

#[test]
fn day_25() {
let input = include_str!("../inputs/year_2015_day_25_input");
assert_eq!(day_25::day_25(input), 19_980_801);
}
}
110 changes: 110 additions & 0 deletions src/year_2015/day_25.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! Advent of Code 2015: Day 25: Let It Snow
use itertools::Itertools;

const STARTER: u64 = 20_151_125;
const MULT: u64 = 252_533;
const MODULO: u64 = 33_554_393;

fn mod_exp(mut base: u64, mut exp: u64, modulus: u64) -> u64 {
if modulus == 1 {
return 0;
}
let mut result: u64 = 1;
base = base % modulus;
while exp > 0 {
if exp % 2 == 1 {
result = result * base % modulus;
}
exp = exp >> 1;
base = base * base % modulus
}
result
}

fn find_iterations_count(col: u64, row: u64) -> u64 {
((((col + row - 1) * (col + row)) / 2) - row) + 1
}

fn parse_input(input: &str) -> (u64, u64) {
input
.lines()
.next()
.unwrap()
.split(",")
.map(|p| p.parse::<u64>().unwrap())
.collect_tuple()
.expect("Wrong input")
}

pub fn day_25(input: impl Into<String>) -> u64 {
let (column, row) = parse_input(&input.into());
let itr = find_iterations_count(column, row) - 1;
let exp = mod_exp(MULT, itr, MODULO);

(STARTER * exp) % MODULO
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn works_with_samples() {
let sample: [[u64; 6]; 6] = [
[
20_151_125,
18_749_137,
17_289_845,
30_943_339,
10_071_777,
33_511_524,
],
[
31_916_031,
21_629_792,
16_929_656,
7_726_640,
15_514_188,
4_041_754,
],
[
16_080_970,
8_057_251,
1_601_130,
7_981_243,
11_661_866,
16_474_243,
],
[
24_592_653,
32_451_966,
21_345_942,
9_380_097,
10_600_672,
31_527_494,
],
[
77_061,
17_552_253,
28_094_349,
6_899_651,
9_250_759,
31_663_883,
],
[
33_071_741,
6_796_745,
25_397_450,
24_659_492,
1_534_922,
27_995_004,
],
];
for (i, numbers) in sample.iter().enumerate() {
for (j, number) in numbers.iter().enumerate() {
assert_eq!(day_25(format!("{},{}", j + 1, i + 1)), *number);
}
}
}
}

0 comments on commit ef204db

Please sign in to comment.