Expand description
§Go With The Flow
There are two parts to this problem:
- Reverse engineering the assembly in order to figure out what the program is doing.
- Implementing the program more efficiently in Rust.
§Reverse Engineering
Raw | Pseudo-Assembly | Pseudo-Rust
-----------------+----------------------------------+-----------------------------------
#ip 1 | # a = 0 b = 2 c = 3 d = 4 e = 5 |
addi 1 16 1 | goto hotel |
seti 1 8 2 | alfa: b = 1 | for b in 1..=e {
seti 1 5 4 | bravo: d = 1 | for d in 1..=e {
mulr 2 4 3 | charlie: c = b * d |
eqrr 3 5 3 | c = (c == e) ? 1: 0 |
addr 3 1 1 | if c == 1 goto delta | if b * d == e {
addi 1 1 1 | goto echo |
addr 2 0 0 | delta: a += b | a += b
addi 4 1 4 | echo: d += 1 |
gtrr 4 5 3 | c = (d > e) ? 1: 0 | }
addr 1 3 1 | if c == 1 goto foxtrot |
seti 2 8 1 | goto charlie | }
addi 2 1 2 | foxtrot: b += 1 |
gtrr 2 5 3 | c = (b > e) ? 1: 0 |
addr 3 1 1 | if c == 1 goto golf |
seti 1 8 1 | goto bravo | }
mulr 1 1 1 | golf: goto end |
addi 5 2 5 | hotel: e = 2 |
mulr 5 5 5 | e = e * e |
mulr 1 5 5 | e *= 19 |
muli 5 11 5 | e *= 11 |
addi 3 $FIRST 3 | c = $FIRST |
mulr 3 1 3 | c *= 22 |
addi 3 $SECOND 3 | c += $SECOND |
addr 5 3 5 | e += c | e = (22 * $FIRST + $SECOND) + 836
addr 1 0 1 | if a == 1 goto india |
seti 0 7 1 | goto alfa |
setr 1 1 3 | india: c = 27 |
mulr 3 1 3 | c *= 28 |
addr 1 3 3 | c += 29 |
mulr 1 3 3 | c *= 30 |
muli 3 14 3 | c *= 14 |
mulr 3 1 3 | c *= 32 |
addr 5 3 5 | e += c | if a == 1 { e += 10550400 }
seti 0 9 0 | a = 0 |
seti 0 0 1 | goto alfa |
| end: |
The decoded assembly shows that the program is computing the
sum of the divisors of a number n
,
using two nested loops for a total complexity in part two of O(n²) = O(10¹⁴)
.
Clearly there is some room for performance improvements. The interesting part is that we only
need the two numbers $FIRST
and $SECOND
and can discard the rest of the input.
§Rust Implementation
We compute the divisor sum using trial division.
As we want the prime factors (instead of checking that n
is prime) the asymptotic complexity
is slightly lower in practice, being the square root of the largest prime factor of n
instead of the square root of n
itself.
As n
is on the order of 10,000,000 this gives a worst case upper bound of √10000000 = 3162
when n
is prime. However for most composite numbers the largest prime factor will be much
smaller, on the order of 100,000 for an approximate complexity of √100000 = 316
.
Functions§
- Returns the sum of the divisors of an integer
n
, including 1 andn
itself. For example20 => 1 + 2 + 4 + 5 + 10 + 20 = 42
. - Extracts the two unique numbers from the input then calculates the composite numbers needed for both parts.
Type Aliases§
- Input 🔒