From f8563d118ca9c738acaedeacd6e4c020828c800d Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Tue, 16 Sep 2025 20:27:14 +0200 Subject: [PATCH] [07_weird_assembly_machine] add solution This solution takes advantage of the fact that fg is linear in terms of add and sub. The main loop consists of repeated application of fg and subtraction of n. Reordering this expression results in a subtraction between a power of fg applied to x and the sum of powers of fg applied to k successive values. Powers of fg can be computed quickly as fg^64 = id and the sum of powers of fg applied to successive values happens to show a pattern every 192 terms. --- src/bin/07_weird_assembly_machine.rs | 69 ++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 18 deletions(-) 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;