[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.
This commit is contained in:
2025-09-16 20:27:14 +02:00
parent 3dbf23547a
commit f8563d118c

View File

@@ -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<Vec<u64>> = 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;