diff --git a/src/bin/07_weird_assembly_machine.rs b/src/bin/07_weird_assembly_machine.rs index ef074e2..7b0c3ea 100644 --- a/src/bin/07_weird_assembly_machine.rs +++ b/src/bin/07_weird_assembly_machine.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io::{BufRead, BufReader}; use std::str::FromStr; use std::sync::LazyLock; -use lib::compute_async; +use lib::submit; static DATA: LazyLock> = LazyLock::new(|| { let file = File::open("./res/07_weird_assembly_machine.data"); @@ -21,29 +21,32 @@ const N: u64 = 10000117800000000; const X: u64 = 5905739161907566595; fn main() { - compute_async(|tx| { - let mut n = N; - let mut x = X; - loop { - n += 1; - x = sub(f(x), n); - - for _ in 1..1_000_000 { - n += 1; - x = sub(fg(x), n); - } - - x = g(x); - - tx.send((n, x)).unwrap(); - } - }); + let (n, x) = h_n(N, X, 1_000_000_000_000_000_000u64 - N); + submit(n, x).unwrap(); } fn h(x: u64, n: u64) -> u64 { g(sub(f(x), n)) } +fn h_n(mut n: u64, mut x: u64, k: u64) -> (u64, u64) { + n += 1; + x = sub(f(x), n); + + let fast_k = (k - 1) / 192 * 192; + let sum = fast_fg_sum(n, fast_k); + x = sub(fg_n(x, fast_k), sum); + n += fast_k; + + let remaining = (k - 1) - fast_k; + let sum = fg_sum(n, remaining); + x = sub(fg_n(x, remaining), sum); + n += remaining; + + x = g(x); + (n, x) +} + fn f(x: u64) -> u64 { evaluate(x, &DATA[0..128]) } @@ -57,6 +60,31 @@ fn fg(x: u64) -> u64 { !x.rotate_left(17) } +/// Computes [`fg`](fg)(...[`fg`](fg)(x)...) where [`fg`] is applied `n` times. +fn fg_n(mut x: u64, n: u64) -> u64 { + for _ in 0..n%64 { + x = fg(x); + } + x +} + +/// Computes the sum from `i = 1` to `k` over `fg^(k-i)(n + i)`. +fn fg_sum(n: u64, k: u64) -> u64 { + let mut sum = 0u64; + for i in 1..=k { + sum = add(sum, fg_n(n + i, k - i)); + } + sum +} + +/// Quickly computes `fg_sum` when `k` is divisible by `192`. +fn fast_fg_sum(n: u64, k: u64) -> u64 { + debug_assert!(k % 192 == 0); + let mut result = fg_sum(n, 0); + result = add(result, ((13556435138434861179u128 * (k / 192) as u128) % (u64::MAX as u128)) as u64); + result +} + /// Evaluates a polynomial over `GF(2)[X] / (X^64 + 1)` fn evaluate(x: u64, data: &[u64]) -> u64 { let mut out = 0; @@ -66,6 +94,11 @@ fn evaluate(x: u64, data: &[u64]) -> u64 { out } +fn add(a: u64, b: u64) -> u64 { + let (result, overflow) = a.overflowing_add(b); + result + overflow as u64 +} + fn sub(a: u64, mut b: u64) -> u64 { if a <= b { b += 1;