Question

I've been working on this puzzle for awhile. I'm trying to figure out how to rotate 4 bits in a number (x) around to the left (with wrapping) by n where 0 <= n <= 31.. The code will look like:

moveNib(int x, int n){
//... some code here
}

The trick is that I can only use these operators:

~ & ^ | + << >>

and of them only a combination of 25. I also can not use If statements, loops, function calls. And I may only use type int.

An example would be moveNib(0x87654321,1) = 0x76543218.

My attempt: I have figured out how to use a mask to store the the bits and all but I can't figure out how to move by an arbitrary number. Any help would be appreciated thank you!

Was it helpful?

Solution

How about:

uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((8-n)<<2); }

It uses <<2 to convert from nibbles to bits, and then shifts the bits by that much. To handle wraparound, we OR by a copy of the number which has been shifted by the opposite amount in the opposite direciton. For example, with x=0x87654321 and n=1, the left part is shifted 4 bits to the left and becomes 0x76543210, and the right part is shifted 28 bits to the right and becomes 0x00000008, and when ORed together, the result is 0x76543218, as requested.

Edit: If - really isn't allowed, then this will get the same result (assuming an architecture with two's complement integers) without using it:

uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((9+~n)<<2); } 

Edit2: OK. Since you aren't allowed to use anything but int, how about this, then?

int moveNib(int x, int n) { return (x&0xffffffff)<<(n<<2) | (x&0xffffffff)>>((9+~n)<<2); }

The logic is the same as before, but we force the calculation to use unsigned integers by ANDing with 0xffffffff. All this assumes 32 bit integers, though. Is there anything else I have missed now?

Edit3: Here's one more version, which should be a bit more portable:

int moveNib(int x, int n) { return ((x|0u)<<((n&7)<<2) | (x|0u)>>((9+~(n&7))<<2))&0xffffffff; }

It caps n as suggested by chux, and uses |0u to convert to unsigned in order to avoid the sign bit duplication you get with signed integers. This works because (from the standard):

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

Since int and 0u have the same rank, but 0u is unsigned, then the result is unsigned, even though ORing with 0 otherwise would be a null operation.

It then truncates the result to the range of a 32-bit int so that the function will still work if ints have more bits than this (though the rotation will still be performed on the lowest 32 bits in that case. A 64-bit version would replace 7 by 15, 9 by 17 and truncate using 0xffffffffffffffff).

This solution uses 12 operators (11 if you skip the truncation, 10 if you store n&7 in a variable).

To see what happens in detail here, let's go through it for the example you gave: x=0x87654321, n=1. x|0u results in a the unsigned number 0x87654321u. (n&7)<<2=4, so we will shift 4 bits to the left, while ((9+~(n&7))<<2=28, so we will shift 28 bits to the right. So putting this together, we will compute 0x87654321u<<4 | 0x87654321u >> 28. For 32-bit integers, this is 0x76543210|0x8=0x76543218. But for 64-bit integers it is 0x876543210|0x8=0x876543218, so in that case we need to truncate to 32 bits, which is what the final &0xffffffff does. If the integers are shorter than 32 bits, then this won't work, but your example in the question had 32 bits, so I assume the integer types are at least that long.

As a small side-note: If you allow one operator which is not on the list, the sizeof operator, then we can make a version that works with all the bits of a longer int automatically. Inspired by Aki, we get (using 16 operators (remember, sizeof is an operator in C)):

int moveNib(int x, int n) {
    int nbit = (n&((sizeof(int)<<1)+~0u))<<2;
    return (x|0u)<<nbit | (x|0u)>>((sizeof(int)<<3)+1u+~nbit);
}

OTHER TIPS

Without the additional restrictions, the typical rotate_left operation (by 0 < n < 32) is trivial.

uint32_t X = (x << 4*n) | (x >> 4*(8-n));

Since we are talking about rotations, n < 0 is not a problem. Rotation right by 1 is the same as rotation left by 7 units. Ie. nn=n & 7; and we are through.

int nn = (n & 7) << 2;                         // Remove the multiplication
uint32_t X = (x << nn) | (x >> (32-nn));

When nn == 0, x would be shifted by 32, which is undefined. This can be replaced simply with x >> 0, i.e. no rotation at all. (x << 0) | (x >> 0) == x.

Replacing the subtraction with addition: a - b = a + (~b+1) and simplifying:

int nn = (n & 7) << 2;
int mm = (33 + ~nn) & 31;
uint32_t X = (x << nn) | (x >> mm);    // when nn=0, also mm=0

Now the only problem is in shifting a signed int x right, which would duplicate the sign bit. That should be cured by a mask: (x << nn) - 1

int nn = (n & 7) << 2;
int mm = (33 + ~nn) & 31;
int result = (x << nn) | ((x >> mm) & ((1 << nn) + ~0));

At this point we have used just 12 of the allowed operations -- next we can start to dig into the problem of sizeof(int)...

int nn = (n & (sizeof(int)-1)) << 2; // etc.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top