973 lines
19 KiB
ReStructuredText
973 lines
19 KiB
ReStructuredText
Advent of Code 2017
|
||
===================
|
||
|
||
December 3rd
|
||
------------
|
||
|
||
You come across an experimental new kind of memory stored on an infinite
|
||
two-dimensional grid.
|
||
|
||
Each square on the grid is allocated in a spiral pattern starting at a
|
||
location marked 1 and then counting up while spiraling outward. For
|
||
example, the first few squares are allocated like this:
|
||
|
||
::
|
||
|
||
17 16 15 14 13
|
||
18 5 4 3 12
|
||
19 6 1 2 11
|
||
20 7 8 9 10
|
||
21 22 23---> ...
|
||
|
||
While this is very space-efficient (no squares are skipped), requested
|
||
data must be carried back to square 1 (the location of the only access
|
||
port for this memory system) by programs that can only move up, down,
|
||
left, or right. They always take the shortest path: the Manhattan
|
||
Distance between the location of the data and square 1.
|
||
|
||
For example:
|
||
|
||
- Data from square 1 is carried 0 steps, since it’s at the access port.
|
||
- Data from square 12 is carried 3 steps, such as: down, left, left.
|
||
- Data from square 23 is carried only 2 steps: up twice.
|
||
- Data from square 1024 must be carried 31 steps.
|
||
|
||
How many steps are required to carry the data from the square identified
|
||
in your puzzle input all the way to the access port?
|
||
|
||
Analysis
|
||
~~~~~~~~
|
||
|
||
I freely admit that I worked out the program I wanted to write using
|
||
graph paper and some Python doodles. There’s no point in trying to write
|
||
a Joy program until I’m sure I understand the problem well enough.
|
||
|
||
The first thing I did was to write a column of numbers from 1 to n (32
|
||
as it happens) and next to them the desired output number, to look for
|
||
patterns directly:
|
||
|
||
::
|
||
|
||
1 0
|
||
2 1
|
||
3 2
|
||
4 1
|
||
5 2
|
||
6 1
|
||
7 2
|
||
8 1
|
||
9 2
|
||
10 3
|
||
11 2
|
||
12 3
|
||
13 4
|
||
14 3
|
||
15 2
|
||
16 3
|
||
17 4
|
||
18 3
|
||
19 2
|
||
20 3
|
||
21 4
|
||
22 3
|
||
23 2
|
||
24 3
|
||
25 4
|
||
26 5
|
||
27 4
|
||
28 3
|
||
29 4
|
||
30 5
|
||
31 6
|
||
32 5
|
||
|
||
There are four groups repeating for a given “rank”, then the pattern
|
||
enlarges and four groups repeat again, etc.
|
||
|
||
::
|
||
|
||
1 2
|
||
3 2 3 4
|
||
5 4 3 4 5 6
|
||
7 6 5 4 5 6 7 8
|
||
9 8 7 6 5 6 7 8 9 10
|
||
|
||
Four of this pyramid interlock to tile the plane extending from the
|
||
initial “1” square.
|
||
|
||
::
|
||
|
||
2 3 | 4 5 | 6 7 | 8 9
|
||
10 11 12 13|14 15 16 17|18 19 20 21|22 23 24 25
|
||
|
||
And so on.
|
||
|
||
We can figure out the pattern for a row of the pyramid at a given “rank”
|
||
:math:`k`:
|
||
|
||
:math:`2k - 1, 2k - 2, ..., k, k + 1, k + 2, ..., 2k`
|
||
|
||
or
|
||
|
||
:math:`k + (k - 1), k + (k - 2), ..., k, k + 1, k + 2, ..., k + k`
|
||
|
||
This shows that the series consists at each place of :math:`k` plus some
|
||
number that begins at :math:`k - 1`, decreases to zero, then increases
|
||
to :math:`k`. Each row has :math:`2k` members.
|
||
|
||
Let’s figure out how, given an index into a row, we can calculate the
|
||
value there. The index will be from 0 to :math:`k - 1`.
|
||
|
||
Let’s look at an example, with :math:`k = 4`:
|
||
|
||
::
|
||
|
||
0 1 2 3 4 5 6 7
|
||
7 6 5 4 5 6 7 8
|
||
|
||
.. code:: ipython2
|
||
|
||
k = 4
|
||
|
||
Subtract :math:`k` from the index and take the absolute value:
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in range(2 * k):
|
||
print abs(n - k),
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
4 3 2 1 0 1 2 3
|
||
|
||
|
||
Not quite. Subtract :math:`k - 1` from the index and take the absolute
|
||
value:
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in range(2 * k):
|
||
print abs(n - (k - 1)),
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
3 2 1 0 1 2 3 4
|
||
|
||
|
||
Great, now add :math:`k`\ …
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in range(2 * k):
|
||
print abs(n - (k - 1)) + k,
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
7 6 5 4 5 6 7 8
|
||
|
||
|
||
So to write a function that can give us the value of a row at a given
|
||
index:
|
||
|
||
.. code:: ipython2
|
||
|
||
def row_value(k, i):
|
||
i %= (2 * k) # wrap the index at the row boundary.
|
||
return abs(i - (k - 1)) + k
|
||
|
||
.. code:: ipython2
|
||
|
||
k = 5
|
||
for i in range(2 * k):
|
||
print row_value(k, i),
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
9 8 7 6 5 6 7 8 9 10
|
||
|
||
|
||
(I’m leaving out details of how I figured this all out and just giving
|
||
the relevent bits. It took a little while to zero in of the aspects of
|
||
the pattern that were important for the task.)
|
||
|
||
Finding the rank and offset of a number.
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Now that we can compute the desired output value for a given rank and
|
||
the offset (index) into that rank, we need to determine how to find the
|
||
rank and offset of a number.
|
||
|
||
The rank is easy to find by iteratively stripping off the amount already
|
||
covered by previous ranks until you find the one that brackets the
|
||
target number. Because each row is :math:`2k` places and there are
|
||
:math:`4` per rank each rank contains :math:`8k` places. Counting the
|
||
initial square we have:
|
||
|
||
:math:`corner_k = 1 + \sum_{n=1}^k 8n`
|
||
|
||
I’m not mathematically sophisticated enough to turn this directly into a
|
||
formula (but Sympy is, see below.) I’m going to write a simple Python
|
||
function to iterate and search:
|
||
|
||
.. code:: ipython2
|
||
|
||
def rank_and_offset(n):
|
||
assert n >= 2 # Guard the domain.
|
||
n -= 2 # Subtract two,
|
||
# one for the initial square,
|
||
# and one because we are counting from 1 instead of 0.
|
||
k = 1
|
||
while True:
|
||
m = 8 * k # The number of places total in this rank, 4(2k).
|
||
if n < m:
|
||
return k, n % (2 * k)
|
||
n -= m # Remove this rank's worth.
|
||
k += 1
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in range(2, 51):
|
||
print n, rank_and_offset(n)
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
2 (1, 0)
|
||
3 (1, 1)
|
||
4 (1, 0)
|
||
5 (1, 1)
|
||
6 (1, 0)
|
||
7 (1, 1)
|
||
8 (1, 0)
|
||
9 (1, 1)
|
||
10 (2, 0)
|
||
11 (2, 1)
|
||
12 (2, 2)
|
||
13 (2, 3)
|
||
14 (2, 0)
|
||
15 (2, 1)
|
||
16 (2, 2)
|
||
17 (2, 3)
|
||
18 (2, 0)
|
||
19 (2, 1)
|
||
20 (2, 2)
|
||
21 (2, 3)
|
||
22 (2, 0)
|
||
23 (2, 1)
|
||
24 (2, 2)
|
||
25 (2, 3)
|
||
26 (3, 0)
|
||
27 (3, 1)
|
||
28 (3, 2)
|
||
29 (3, 3)
|
||
30 (3, 4)
|
||
31 (3, 5)
|
||
32 (3, 0)
|
||
33 (3, 1)
|
||
34 (3, 2)
|
||
35 (3, 3)
|
||
36 (3, 4)
|
||
37 (3, 5)
|
||
38 (3, 0)
|
||
39 (3, 1)
|
||
40 (3, 2)
|
||
41 (3, 3)
|
||
42 (3, 4)
|
||
43 (3, 5)
|
||
44 (3, 0)
|
||
45 (3, 1)
|
||
46 (3, 2)
|
||
47 (3, 3)
|
||
48 (3, 4)
|
||
49 (3, 5)
|
||
50 (4, 0)
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in range(2, 51):
|
||
k, i = rank_and_offset(n)
|
||
print n, row_value(k, i)
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
2 1
|
||
3 2
|
||
4 1
|
||
5 2
|
||
6 1
|
||
7 2
|
||
8 1
|
||
9 2
|
||
10 3
|
||
11 2
|
||
12 3
|
||
13 4
|
||
14 3
|
||
15 2
|
||
16 3
|
||
17 4
|
||
18 3
|
||
19 2
|
||
20 3
|
||
21 4
|
||
22 3
|
||
23 2
|
||
24 3
|
||
25 4
|
||
26 5
|
||
27 4
|
||
28 3
|
||
29 4
|
||
30 5
|
||
31 6
|
||
32 5
|
||
33 4
|
||
34 3
|
||
35 4
|
||
36 5
|
||
37 6
|
||
38 5
|
||
39 4
|
||
40 3
|
||
41 4
|
||
42 5
|
||
43 6
|
||
44 5
|
||
45 4
|
||
46 3
|
||
47 4
|
||
48 5
|
||
49 6
|
||
50 7
|
||
|
||
|
||
Putting it all together
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code:: ipython2
|
||
|
||
def row_value(k, i):
|
||
return abs(i - (k - 1)) + k
|
||
|
||
|
||
def rank_and_offset(n):
|
||
n -= 2 # Subtract two,
|
||
# one for the initial square,
|
||
# and one because we are counting from 1 instead of 0.
|
||
k = 1
|
||
while True:
|
||
m = 8 * k # The number of places total in this rank, 4(2k).
|
||
if n < m:
|
||
return k, n % (2 * k)
|
||
n -= m # Remove this rank's worth.
|
||
k += 1
|
||
|
||
|
||
def aoc20173(n):
|
||
if n <= 1:
|
||
return 0
|
||
k, i = rank_and_offset(n)
|
||
return row_value(k, i)
|
||
|
||
.. code:: ipython2
|
||
|
||
aoc20173(23)
|
||
|
||
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
2
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
aoc20173(23000)
|
||
|
||
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
105
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
aoc20173(23000000000000)
|
||
|
||
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
4572225
|
||
|
||
|
||
|
||
Sympy to the Rescue
|
||
===================
|
||
|
||
Find the rank for large numbers
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Using e.g. Sympy we can find the rank directly by solving for the roots
|
||
of an equation. For large numbers this will (eventually) be faster than
|
||
iterating as ``rank_and_offset()`` does.
|
||
|
||
.. code:: ipython2
|
||
|
||
from sympy import floor, lambdify, solve, symbols
|
||
from sympy import init_printing
|
||
init_printing()
|
||
|
||
.. code:: ipython2
|
||
|
||
k = symbols('k')
|
||
|
||
Since
|
||
|
||
:math:`1 + 2 + 3 + ... + N = \frac{N(N + 1)}{2}`
|
||
|
||
and
|
||
|
||
:math:`\sum_{n=1}^k 8n = 8(\sum_{n=1}^k n) = 8\frac{k(k + 1)}{2}`
|
||
|
||
We want:
|
||
|
||
.. code:: ipython2
|
||
|
||
E = 2 + 8 * k * (k + 1) / 2 # For the reason for adding 2 see above.
|
||
|
||
E
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
4 k \left(k + 1\right) + 2
|
||
|
||
|
||
|
||
We can write a function to solve for :math:`k` given some :math:`n`\ …
|
||
|
||
.. code:: ipython2
|
||
|
||
def rank_of(n):
|
||
return floor(max(solve(E - n, k))) + 1
|
||
|
||
First ``solve()`` for :math:`E - n = 0` which has two solutions (because
|
||
the equation is quadratic so it has two roots) and since we only care
|
||
about the larger one we use ``max()`` to select it. It will generally
|
||
not be a nice integer (unless :math:`n` is the number of an end-corner
|
||
of a rank) so we take the ``floor()`` and add 1 to get the integer rank
|
||
of :math:`n`. (Taking the ``ceiling()`` gives off-by-one errors on the
|
||
rank boundaries. I don’t know why. I’m basically like a monkey doing
|
||
math here.) =-D
|
||
|
||
It gives correct answers:
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in (9, 10, 25, 26, 49, 50):
|
||
print n, rank_of(n)
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
9 1
|
||
10 2
|
||
25 2
|
||
26 3
|
||
49 3
|
||
50 4
|
||
|
||
|
||
And it runs much faster (at least for large numbers):
|
||
|
||
.. code:: ipython2
|
||
|
||
%time rank_of(23000000000000) # Compare runtime with rank_and_offset()!
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
CPU times: user 68 ms, sys: 8 ms, total: 76 ms
|
||
Wall time: 73.8 ms
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
2397916
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
%time rank_and_offset(23000000000000)
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
CPU times: user 308 ms, sys: 0 ns, total: 308 ms
|
||
Wall time: 306 ms
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
\left ( 2397916, \quad 223606\right )
|
||
|
||
|
||
|
||
After finding the rank you would still have to find the actual value of
|
||
the rank’s first corner and subtract it (plus 2) from the number and
|
||
compute the offset as above and then the final output, but this overhead
|
||
is partially shared by the other method, and overshadowed by the time it
|
||
(the other iterative method) would take for really big inputs.
|
||
|
||
The fun thing to do here would be to graph the actual runtime of both
|
||
methods against each other to find the trade-off point.
|
||
|
||
It took me a second to realize I could do this…
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Sympy is a *symbolic* math library, and it supports symbolic
|
||
manipulation of equations. I can put in :math:`y` (instead of a value)
|
||
and ask it to solve for :math:`k`.
|
||
|
||
.. code:: ipython2
|
||
|
||
y = symbols('y')
|
||
|
||
.. code:: ipython2
|
||
|
||
g, f = solve(E - y, k)
|
||
|
||
The equation is quadratic so there are two roots, we are interested in
|
||
the greater one…
|
||
|
||
.. code:: ipython2
|
||
|
||
g
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
- \frac{1}{2} \sqrt{y - 1} - \frac{1}{2}
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
f
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
\frac{1}{2} \sqrt{y - 1} - \frac{1}{2}
|
||
|
||
|
||
|
||
Now we can take the ``floor()``, add 1, and ``lambdify()`` the equation
|
||
to get a Python function that calculates the rank directly.
|
||
|
||
.. code:: ipython2
|
||
|
||
floor(f) + 1
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
\lfloor{\frac{1}{2} \sqrt{y - 1} - \frac{1}{2}}\rfloor + 1
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
F = lambdify(y, floor(f) + 1)
|
||
|
||
.. code:: ipython2
|
||
|
||
for n in (9, 10, 25, 26, 49, 50):
|
||
print n, int(F(n))
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
9 1
|
||
10 2
|
||
25 2
|
||
26 3
|
||
49 3
|
||
50 4
|
||
|
||
|
||
It’s pretty fast.
|
||
|
||
.. code:: ipython2
|
||
|
||
%time int(F(23000000000000)) # The clear winner.
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
|
||
Wall time: 11.9 µs
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
2397916
|
||
|
||
|
||
|
||
Knowing the equation we could write our own function manually, but the
|
||
speed is no better.
|
||
|
||
.. code:: ipython2
|
||
|
||
from math import floor as mfloor, sqrt
|
||
|
||
def mrank_of(n):
|
||
return int(mfloor(sqrt(23000000000000 - 1) / 2 - 0.5) + 1)
|
||
|
||
.. code:: ipython2
|
||
|
||
%time mrank_of(23000000000000)
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
|
||
Wall time: 12.9 µs
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
2397916
|
||
|
||
|
||
|
||
Given :math:`n` and a rank, compute the offset.
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Now that we have a fast way to get the rank, we still need to use it to
|
||
compute the offset into a pyramid row.
|
||
|
||
.. code:: ipython2
|
||
|
||
def offset_of(n, k):
|
||
return (n - 2 + 4 * k * (k - 1)) % (2 * k)
|
||
|
||
(Note the sneaky way the sign changes from :math:`k(k + 1)` to
|
||
:math:`k(k - 1)`. This is because we want to subract the
|
||
:math:`(k - 1)`\ th rank’s total places (its own and those of lesser
|
||
rank) from our :math:`n` of rank :math:`k`. Substituting :math:`k - 1`
|
||
for :math:`k` in :math:`k(k + 1)` gives :math:`(k - 1)(k - 1 + 1)`,
|
||
which of course simplifies to :math:`k(k - 1)`.)
|
||
|
||
.. code:: ipython2
|
||
|
||
offset_of(23000000000000, 2397916)
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
223606
|
||
|
||
|
||
|
||
So, we can compute the rank, then the offset, then the row value.
|
||
|
||
.. code:: ipython2
|
||
|
||
def rank_of(n):
|
||
return int(mfloor(sqrt(n - 1) / 2 - 0.5) + 1)
|
||
|
||
|
||
def offset_of(n, k):
|
||
return (n - 2 + 4 * k * (k - 1)) % (2 * k)
|
||
|
||
|
||
def row_value(k, i):
|
||
return abs(i - (k - 1)) + k
|
||
|
||
|
||
def aoc20173(n):
|
||
k = rank_of(n)
|
||
i = offset_of(n, k)
|
||
return row_value(k, i)
|
||
|
||
.. code:: ipython2
|
||
|
||
aoc20173(23)
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
2
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
aoc20173(23000)
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
105
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
aoc20173(23000000000000)
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
4572225
|
||
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
%time aoc20173(23000000000000000000000000) # Fast for large values.
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
|
||
Wall time: 20 µs
|
||
|
||
|
||
|
||
|
||
.. math::
|
||
|
||
2690062495969
|
||
|
||
|
||
|
||
A Joy Version
|
||
=============
|
||
|
||
At this point I feel confident that I can implement a concise version of
|
||
this code in Joy. ;-)
|
||
|
||
.. code:: ipython2
|
||
|
||
from notebook_preamble import J, V, define
|
||
|
||
``rank_of``
|
||
~~~~~~~~~~~
|
||
|
||
::
|
||
|
||
n rank_of
|
||
---------------
|
||
k
|
||
|
||
The translation is straightforward.
|
||
|
||
::
|
||
|
||
int(floor(sqrt(n - 1) / 2 - 0.5) + 1)
|
||
|
||
rank_of == -- sqrt 2 / 0.5 - floor ++
|
||
|
||
.. code:: ipython2
|
||
|
||
define('rank_of == -- sqrt 2 / 0.5 - floor ++')
|
||
|
||
``offset_of``
|
||
~~~~~~~~~~~~~
|
||
|
||
::
|
||
|
||
n k offset_of
|
||
-------------------
|
||
i
|
||
|
||
(n - 2 + 4 * k * (k - 1)) % (2 * k)
|
||
|
||
A little tricky…
|
||
|
||
::
|
||
|
||
n k dup 2 *
|
||
n k k 2 *
|
||
n k k*2 [Q] dip %
|
||
n k Q k*2 %
|
||
|
||
n k dup --
|
||
n k k --
|
||
n k k-1 4 * * 2 + -
|
||
n k*k-1*4 2 + -
|
||
n k*k-1*4+2 -
|
||
n-k*k-1*4+2
|
||
|
||
n-k*k-1*4+2 k*2 %
|
||
n-k*k-1*4+2%k*2
|
||
|
||
Ergo:
|
||
|
||
::
|
||
|
||
offset_of == dup 2 * [dup -- 4 * * 2 + -] dip %
|
||
|
||
.. code:: ipython2
|
||
|
||
define('offset_of == dup 2 * [dup -- 4 * * 2 + -] dip %')
|
||
|
||
``row_value``
|
||
~~~~~~~~~~~~~
|
||
|
||
::
|
||
|
||
k i row_value
|
||
-------------------
|
||
n
|
||
|
||
abs(i - (k - 1)) + k
|
||
|
||
k i over -- - abs +
|
||
k i k -- - abs +
|
||
k i k-1 - abs +
|
||
k i-k-1 abs +
|
||
k |i-k-1| +
|
||
k+|i-k-1|
|
||
|
||
.. code:: ipython2
|
||
|
||
define('row_value == over -- - abs +')
|
||
|
||
``aoc2017.3``
|
||
~~~~~~~~~~~~~
|
||
|
||
::
|
||
|
||
n aoc2017.3
|
||
-----------------
|
||
m
|
||
|
||
n dup rank_of
|
||
n k [offset_of] dupdip
|
||
n k offset_of k
|
||
i k swap row_value
|
||
k i row_value
|
||
m
|
||
|
||
.. code:: ipython2
|
||
|
||
define('aoc2017.3 == dup rank_of [offset_of] dupdip swap row_value')
|
||
|
||
.. code:: ipython2
|
||
|
||
J('23 aoc2017.3')
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
2
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
J('23000 aoc2017.3')
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
105
|
||
|
||
|
||
.. code:: ipython2
|
||
|
||
V('23000000000000 aoc2017.3')
|
||
|
||
|
||
.. parsed-literal::
|
||
|
||
. 23000000000000 aoc2017.3
|
||
23000000000000 . aoc2017.3
|
||
23000000000000 . dup rank_of [offset_of] dupdip swap row_value
|
||
23000000000000 23000000000000 . rank_of [offset_of] dupdip swap row_value
|
||
23000000000000 23000000000000 . -- sqrt 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 22999999999999 . sqrt 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 4795831.523312615 . 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 4795831.523312615 2 . / 0.5 - floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 2397915.7616563076 . 0.5 - floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 2397915.7616563076 0.5 . - floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 2397915.2616563076 . floor ++ [offset_of] dupdip swap row_value
|
||
23000000000000 2397915 . ++ [offset_of] dupdip swap row_value
|
||
23000000000000 2397916 . [offset_of] dupdip swap row_value
|
||
23000000000000 2397916 [offset_of] . dupdip swap row_value
|
||
23000000000000 2397916 . offset_of 2397916 swap row_value
|
||
23000000000000 2397916 . dup 2 * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
|
||
23000000000000 2397916 2397916 . 2 * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
|
||
23000000000000 2397916 2397916 2 . * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
|
||
23000000000000 2397916 4795832 . [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
|
||
23000000000000 2397916 4795832 [dup -- 4 * * 2 + -] . dip % 2397916 swap row_value
|
||
23000000000000 2397916 . dup -- 4 * * 2 + - 4795832 % 2397916 swap row_value
|
||
23000000000000 2397916 2397916 . -- 4 * * 2 + - 4795832 % 2397916 swap row_value
|
||
23000000000000 2397916 2397915 . 4 * * 2 + - 4795832 % 2397916 swap row_value
|
||
23000000000000 2397916 2397915 4 . * * 2 + - 4795832 % 2397916 swap row_value
|
||
23000000000000 2397916 9591660 . * 2 + - 4795832 % 2397916 swap row_value
|
||
23000000000000 22999994980560 . 2 + - 4795832 % 2397916 swap row_value
|
||
23000000000000 22999994980560 2 . + - 4795832 % 2397916 swap row_value
|
||
23000000000000 22999994980562 . - 4795832 % 2397916 swap row_value
|
||
5019438 . 4795832 % 2397916 swap row_value
|
||
5019438 4795832 . % 2397916 swap row_value
|
||
223606 . 2397916 swap row_value
|
||
223606 2397916 . swap row_value
|
||
2397916 223606 . row_value
|
||
2397916 223606 . over -- - abs +
|
||
2397916 223606 2397916 . -- - abs +
|
||
2397916 223606 2397915 . - abs +
|
||
2397916 -2174309 . abs +
|
||
2397916 2174309 . +
|
||
4572225 .
|
||
|
||
|
||
::
|
||
|
||
rank_of == -- sqrt 2 / 0.5 - floor ++
|
||
offset_of == dup 2 * [dup -- 4 * * 2 + -] dip %
|
||
row_value == over -- - abs +
|
||
|
||
aoc2017.3 == dup rank_of [offset_of] dupdip swap row_value
|