TAOCP 7.1.3 Exercise 21
The earlier solution fails because it tries to reconstruct hidden structure using $y \mathbin{\&} (-y)$, which only isolates the least significant 1-bit and does not encode any run length information.
Section 7.1.3: Bitwise Tricks and Techniques
Exercise 21. [22] [22] Construct the reverse of Gosper's hack: Show how to compute $x$ from $y$.
Verified: no
Solve time: 4m
The earlier solution fails because it tries to reconstruct hidden structure using $y \mathbin{&} (-y)$, which only isolates the least significant 1-bit and does not encode any run length information. The correct approach is to reverse the actual structural transformation of Gosper’s hack on the suffix pattern.
1. Correct structural model of Gosper’s hack
Gosper’s hack maps the suffix of $x$ of the form
$$ \cdots 0,1^m 0^k $$
into
$$ \cdots 1,0^{k+1}1^{m-1}, $$
while leaving all more significant bits unchanged.
Thus the inverse problem is purely local: recover $m$ and $k$, then reconstruct the original suffix.
2. First correction: how to recover $m$
In $y$, the trailing block consists of exactly $m-1$ ones. This is the only place where run length information appears directly.
Let
$$ t = \operatorname{ctz}(\neg y), $$
which counts trailing 1-bits of $y$. Then
$$ m = t + 1. $$
This is correct because trailing ones of $y$ are precisely $m-1$.
3. Second correction: isolating the pivot structure
Remove the trailing ones:
$$ y_1 = \left\lfloor \frac{y}{2^t} \right\rfloor. $$
Then $y_1$ has the form
$$ \cdots 1,0^{k+1}. $$
Now the least significant 1-bit of $y_1$ is exactly the pivot that ended the zero-block.
Define
$$ p = y_1 \mathbin{&} (-y_1). $$
This step is now valid, but with a different meaning than in the rejected solution: it isolates the pivot bit $2^{k+1}$.
Hence
$$ k + 1 = \operatorname{ctz}(y_1), \quad k = \operatorname{ctz}(y_1) - 1. $$
4. Third correction: reconstructing the original suffix
The original suffix in $x$ has length $m + k + 1$ and equals
$$ 0,1^m 0^k. $$
We construct it directly.
Let
$$ L = m + k + 1. $$
The low $L$ bits of $x$ are:
$$ x_{\text{low}} = (2^m - 1) \ll k. $$
Indeed, this places $m$ consecutive 1s starting at position $k$, followed by $k$ zeros, preceded by a zero at the pivot position.
5. Final assembly
All higher bits are unchanged from $y$ except the transformed suffix.
Thus
$$ x = \left(y ;\mathbin{&}; \neg(2^L - 1)\right); \mathbin{|}; \left((2^m - 1)\ll k\right), $$
where
$$ t = \operatorname{ctz}(\neg y), \quad m = t+1, $$
$$ y_1 = y \gg t, \quad k = \operatorname{ctz}(y_1) - 1, $$
$$ L = m + k + 1. $$
6. Why this works
The correction avoids all incorrect global inferences from $y \mathbin{&} (-y)$. Instead:
- $t = \operatorname{ctz}(\neg y)$ correctly captures trailing ones,
- shifting exposes the pivot cleanly,
- $y_1 \mathbin{&} (-y_1)$ is used only to locate a true isolated 1-bit,
- reconstruction is done by explicit bit placement, not algebraic identities.
This yields a correct and complete inverse of Gosper’s hack.