z = interleave(x, y) = expand(x) | (expand(y) << 1)
For the inverse you define the map compact(x_0, x_1, ..., x_2n) = (x_0, x_2, ..., x_2n) and then uninterleave(z) = (compact(z), compact(x >> 1)) = (x, y)
The expand map can be implemented efficiently as a logarithmic number of shift-and-merge stages, and compact can be constructed by inverting each stage separately and composing them in reverse order. A stage has this form: x' = x | (x << n)
You sometimes also see it written with xor. That's not necessary. What you care about is that the operation is bitwise (e.g. no carries from addition) and it must have 0 as an identity.If x has 2n bits then this is reversible: the lower n bits in x and x' are the same, and the upper n bits in x and (x' >> n) are the same, so you can recover all 2n bits of x from x' with low(x') | high(x' >> n). You can cascade stages like this if you alternate them with appropriate masking to allow data-parallel operation. The net effect of the first stage, after masking, is to shift the upper n bits of x up by n bits while leaving the lower n bits in place. The next stage then does the same thing on the two halves in parallel with a shift of n/2, and so on until you're done. The masking prevents cross-talk between subvectors so you can operate on a single vector as if it were a set of independent subvectors. Or put another way, it enforces the precondition for the reversibility of each stage.
Anyway, just some random connections to low-level hacking.
1. Moser–de Bruijn sequence https://en.wikipedia.org/wiki/Moser%E2%80%93de_Bruijn_sequen...
2. A 'Binary' System for Complex Numbers, by Walter Pennyhttps://www.nsa.gov/news-features/declassified-documents/tec...
Previous: https://news.ycombinator.com/item?id=10648847