Introduction
Super Mario 64 has a heavily overcomplicated RNG, ably explained here by Pannenkoek.
I thought it might be interesting to implement it.
Challenge
Implement the RNG function, except for the two special cases. (Not part of the below description; everything below is what you should implement.)
Input and output are both 16-bit integers.
The standard C implementation is as follows. &
is bitwise-AND, ^
is bitwise-XOR. Shifts have higher precedence than bitwise operations, so (s0 & 0xFF)<<1 ^ input
does the left-shift before XORing with input
.
rng(input) {
s0 = (input & 0xFF) << 8; // shift low byte to high
s0 = s0 ^ input;
input = (s0 & 0xFF) << 8 | (s0 & 0xFF00) >> 8; // swap 8-bit halves
s0 = (s0 & 0xFF) << 1 ^ input;
s1 = s0 >> 1 ^ 0xFF80;
if(s0 & 1) input = s1 ^ 0x8180; // XOR with one of 2 constants
else input = s1 ^ 0x1FF4; // depending on s0 odd or even
return input;
}
In math notation, where \$\oplus\$ is bitwise XOR and \$x_L\$ is \$x\$ mod 256:
\begin{equation} \text{rng}(a):= \text{let } \begin{array}{l} b = 256a_L \oplus a \\ c = 256b_L \oplus \lfloor b/256 \rfloor \\ d = 32256 \text{ if $c$ odd, } 57460 \text{ otherwise}\end{array} \text{ in } b_L \oplus \lfloor c/2 \rfloor \oplus d. \end{equation}
Example input and output
The number just before an arrow should be taken to the number just after it, so \$f(1789)\$ should be \$0\$, and \$f(0)\$ should be \$57460\$. \$22026\$ and \$58704\$ map to each other.
12567 -> 60400 -> 1789 -> 0 -> 57460 -> 55882 -> 50550
64917 -> 43605 -> 21674 -> 46497 -> 45151
22026 <-> 58704
Rules
Shortest code wins.
Standard loopholes apply.