grambulate 0.1.0

An implementation of grambulation for positive integers in Rust.
Documentation

grambulate-rust

An implementation of grambulation in Rust, as initially explained on Reddit and more precisely defined for Code Golf.

Implementation

To grambulate two numbers $a$ and $b$ to a solution $c$, we must find the coordinates $c'$ of the solution, for which we must first determine the coordinates of $a$ and $b$ ($a'$ and $b'$) on the spiral.

To do so, we can calculate the 'rings' $r_a$ and $r_b$ the numbers are in with the formula $$r(x)=\left\lfloor{\frac{\left\lceil{\sqrt{x}}\right\rceil}{2}}\right\rfloor$$ Then, the formula $$v_d(r)=4r^2-(5-d)\cdot{}r+1$$ can be used to to calculate the four values on the four main diagonals for ring $x$, where $d$ specifies which diagonal to use. To do this, we specify the number on a given diagonal in ring 1 as $d$:

Diagonal $d$
top-right 3
top-left 5
bottom-left 7
bottom-right 9

This gives us 4 values near our desired value (either $a$ or $b$). If we find the value closest to but larger than or equal to our target value, we can calculate the difference and apply it as either an x or y offset, giving us the coordinates of our desired value.

Now that we have both pairs of coordinates, we can calculate the connecting vector $\vec{v}$ and apply to it to $b'$ to find $c'$.

Once we know $c'$, we know that the ring is the larger of the absolute x and y values.

We now determine which diagonal we need to calculate, retrieve the value using the above formula and subtract the difference of the relevant coordinate.

Example

Let $a=5$ and $b=11$.

We can use the ring formula to determine $r_a=1$ and $r_b=2$.

Starting with $a$, we can calculate the 4 diagonal values on ring $r_a$:
$v_3=3$
$v_5=5$
$v_7=7$
$v_9=9$

The closest of these values that is $\le{}a$ is $v_5=5$. The coordinates of that value are $v_5'=(-1\cdot{}r_a~|+1\cdot{}r_a) = (-1~|~1)$. As $v_5=a$, we don't need any further offsets.

Moving on to $b$:
$v_3=13$
$v_5=17$
$v_7=21$
$v_9=25$

The closest of these values that is $\le{}b$ is $v_3=13$. The coordinates of that value are $v_3'=(+1\cdot{}r_b~|+1\cdot{}r_b)=(2~|~2)$. As $v_3\neq{}b$, we still need to do more.

As we determined the closest value ahead of $b$ to be the top-right diagonal, we need to decrease the y coordinate of $v_3'$ by $v_3-b=13-11=2$. That leaves us with:

\vec{c'}=\vec{v_3'}-\begin{pmatrix}0\\2\end{pmatrix}=\begin{pmatrix}2-0\\2-2\end{pmatrix}=\begin{pmatrix}2\\0\end{pmatrix}

Now we know:

a'=(-1~|~1)\hspace{2cm}b'=(2~|~0)

The connecting vector is

\vec{v}=\vec{b'}-\vec{a'}=\begin{pmatrix}2-(-1)\\0-1\end{pmatrix}=\begin{pmatrix}3\\-1\end{pmatrix}

Applying this to $b'$ gives us the position vector of $c'$.

$$\vec{c'}=\vec{b'}+\vec{v}=\begin{pmatrix}2+3\\0+(-1)\end{pmatrix}=\begin{pmatrix}5\\-1\end{pmatrix}$$

As the value is not directly on a diagonal ($|x|\neq{}|y|$), we can use the following table to determine the diagonal ahead of our target value:

condition diagonal
$|x|< y$ top-left
$x>|y|$ top-right
$x<-|y|$ bottom-left
$-|x|>y$ bottom-right

We need the top-left diagonal. We also know that our value is on ring $r_c=\max(|5|, |-1|)=5$. The value of the top-left diagonal at ring 5 is, using the formula above, $v_3(5)=91$. By definition, $c$ is smaller than that value. As the straight in front of the top-left diagonal is vertical and upwards, we need to subtract the difference between the y-value of 91 and the y-value of $c'$ from 91 and we should find $c$. $$c=91-((y_{91})-(y_{c'}))=91-((+1\cdot{}5)-(-1))=91-6=85$$

That's it! $5~\lozenge{}~11=85$.

Speed

This approach may seem confusing, but it avoids any loops that change with the inputs; the only loops in my code iterate over the 4 types of Diagonal, no more.

This allows very low execution time regardless of input; results from a rough test on my computer:

value_a value_b time per iteration, average of 100'000'000
1 2 529ns
10 25 395ns
100000 2500000 601ns
100000000000000 2500000000000000 364ns

These were just the first numbers I chose and this is in no way a proper test, but it'll do for an impression.