Reading the book “C – A reference manual (Fifth Edition)”, I stumbled upon this piece of code (each integer in the SET is represented by a bit position):
typedef unsigned int SET;
#define emptyset ((SET) 0)
#define first_set_of_n_elements(n) (SET)((1<<(n))-1)
/* next_set_of_n_elements(s): Given a set of n elements,
produce a new set of n elements. If you start with the
result of first_set_of_n_elements(k)/ and then at each
step apply next_set_of_n_elements to the previous result,
and keep going until a set is obtained containing m as a
member, you will have obtained a set representing all
possible ways of choosing k things from m things. */
SET next_set_of_n_elements(SET x) {
/* This code exploits many unusual properties of unsigned arithmetic. As an illustration:
if x == 001011001111000, then
smallest == 000000000001000
ripple == 001011010000000
new_smallest == 000000010000000
ones == 000000000000111
returned value == 001011010000111
The overall idea is that you find the rightmost
contiguous group of 1-bits. Of that group, you slide the
leftmost 1-bit to the left one place, and slide all the
others back to the extreme right.
(This code was adapted from HAKMEM.) */
SET smallest, ripple, new_smallest, ones;
if (x == emptyset) return x;
smallest = (x & -x);
ripple = x + smallest;
new_smallest = (ripple & -ripple);
ones = ((new_smallest / smallest) >> 1) -1;
return (ripple | ones);
}
I’m lost at the calculation of ‘ones’, and it’s significance in the calculation. Although I can understand the calculation mathematically, I cannot understand why this works, or how.
On a related note, the authors of the book claim that the calculation for first_set_of_n_elements “exploits the properties of unsigned subtractions”. How is (2^n)-1 an “exploit”?
The smallest computation gets the first non-0 bit of your int. How does it works ?
Let n be the bit length of your int. The opposite of a number x (bits bn-1…b0) is computed in a way that when you sum x to -x, you will get 2n. Since your integer is only n-bit long, the resulting bit is discarded and you obtain 0.
Now, let b’n-1…b’0 be the binary representation of -x.
Since x+(-x) must be equal to 2n, when you meet the first bit 1 of x (say at position i), the related -x bit will also be set to 1 and when adding the numbers, you’ll get a carry.
To obtain the 2n, this carry must propagate through all the bits until the end of the bit sequence of your int. Thus, the bit of -x at each position j with i < j < n follows the properties below :
On the other hand, the bits b’j of -x located at a position j such that 0 <= j < i are ruled by what follows :
Thus, the only bit that is 1 in both x and -x is the one at position i
In your example :
Thus,
The ripple then turns every contiguous “1” after position i (bit i included) to 0, and the first following 0 bit to 1 (due to the carry propagation). That’s why you ripple is :
Ones is computed as the division of smallest(r(x)) over smallest(x). Since smallest(x) is an integer with only a single bit set to 1 at position i, you have :
The resulting integer has also only one bit set to 1, at say index p, thus, substract -1 to this value will get you an integer ones such that :
Finally, the return value is the integer such that :
Then, I can’t explain the remaining part since I did not understand the sentence :