First of all, to say that your sample numbers are wrong, as the second has the most significant bit at one, it should be larger than 2147483643, but it is only 4194303, and the third one should be 7, so I guess you have inverted the bit positions when converting them to decimal. See my last complete code for a comment at the beginning of main(), on how the numbers have been determined (to look like in your example) The numbers corresponding to your bit pattern are (hex/dec):
[0xffffffff/4294967295][0xfffffc00/4294966272][0x00000007/7]
(if we put the more weight digits at the left, why don't we do also in binary?)
To solve your problem, you can consider that when you have n consecutive ones in the LSB part of a number, and you increment that value by one, then you have all those consecutive ones switched to zeros (by means of carry propagation) until the next o the last one you have, and if you have n consecutive zeros and decrement the value, then you have all these zeros convert into ones... well, with one more bit, as carry cascades again one bit further. The idea is to check what bit do we have in the LSB, and depending on this, increment or decrement the value and XOR it with the original value.... the result you will get is a number that has as many ones in the LSBs as equal bits to the LSB, plus one, e.g:
1100100011111111
as the LSB is 1, we increment it:
1100100100000000 ^^^^^^^^^ changed bits.
if we now xor this value with the previous:
0000000111111111 => 9 "1" bits, that indicate that 8 "1" consecutive bits were present
if we prepare a switch statement with all the possible values we can get from this function, you can get a very effective way to the following result:
int get_consecutive_bits(unsigned value) { unsigned next = value; switch (value) { case 0: case ~0: return 32; /* these are special cases, see below */ } switch (value & 1) { /* get the lower bit */ case 0: next--; break; /* decrement */ case 1: next++; break; /* increment */ } switch (value ^ next) { /* make the xor */ case 0x00000003: return 1; case 0x00000007: return 2; case 0x0000000f: return 3; case 0x0000001f: return 4; case 0x0000003f: return 5; case 0x0000007f: return 6; /* ... */ case 0xffffffff: return 31; } /* switch */ }
Now you have to accumulate that value in case the next array cell begins with the same bit value as you finished the previous. The reason we never have a case statement of 0x00000001 is that we are forcing a carry in the second bit, so we always have a value of 1 or more, with two bits changed (...0000001 => ...0000010 => ...0000011 and ...11111110 => ...11111101 => ...00000011) and this also means that for values 0000...0000 and 1111...1111 we should get one bit more than the word length, making these values special (as they make the carry go to the next bit to the msb, the 33rd) so we check for those values first.
This is a very efficient way to do the task in chunks of one array cell. You have to accumulate, when the value you get includes the MSB, as the next word can start with that same bit you ended before.
The next code should illustrate the algorithm:
pru_49297910.c
/* pru_49297910.c -- answer to https://stackoverflow.com/questions/49297910/ * Author: Luis Colorado <[email protected]> * Date: Wed Apr 24 11:12:21 EEST 2019 * Copyright: (C) Luis Colorado. All rights reserved. * License: BSD. Open source. */ #include <cassert> #include <iostream> #define BITS_PER_ELEMENT 32 int get_consecutive_bits(unsigned value) { switch (value) { case 0: case ~0: /* these are special cases, see below */ return BITS_PER_ELEMENT; } unsigned next = value; switch (value & 1) { /* get the lower bit */ case 0: next--; break; /* decrement */ case 1: next++; break; /* increment */ } switch (value ^ next) { /* make the xor */ case 0x00000003: return 1; case 0x00000007: return 2; case 0x0000000f: return 3; case 0x0000001f: return 4; case 0x0000003f: return 5; case 0x0000007f: return 6; case 0x000000ff: return 7; case 0x000001ff: return 8; case 0x000003ff: return 9; case 0x000007ff: return 10; case 0x00000fff: return 11; case 0x00001fff: return 12; case 0x00003fff: return 13; case 0x00007fff: return 14; case 0x0000ffff: return 15; case 0x0001ffff: return 16; case 0x0003ffff: return 17; case 0x0007ffff: return 18; case 0x000fffff: return 19; case 0x001fffff: return 20; case 0x003fffff: return 21; case 0x007fffff: return 22; case 0x00ffffff: return 23; case 0x01ffffff: return 24; case 0x03ffffff: return 25; case 0x07ffffff: return 26; case 0x0fffffff: return 27; case 0x1fffffff: return 28; case 0x3fffffff: return 29; case 0x7fffffff: return 30; case 0xffffffff: return 31; } /* switch */ assert(!"Impossible"); return 0; } #define FLUSH() do{ \ runlen(accum, state); \ state ^= 1; \ accum = 0; \ } while (0) void run_runlen_encoding(unsigned array[], int n, void (*runlen)(int, unsigned)) { int state = 0; /* always begin in 0 */ int accum = 0; /* accumulated bits */ while (n--) { /* see if we have to change */ if (state ^ (array[n] & 1)) /* we changed state */ FLUSH(); int nb = BITS_PER_ELEMENT; /* number of bits to check */ int w = array[n]; while (nb > 0) { int b = get_consecutive_bits(w); if (b < nb) { accum += b; FLUSH(); w >>= b; nb -= b; } else { /* b >= nb, we only accumulate nb */ accum += nb; nb = 0; } } } if (accum) FLUSH(); } /* run_runlen_encoding */ void output_runlen(int n, unsigned kind) { if (n) { /* don't print for n == 0 */ static int i = 0; std::cout << "[" << n << "/" << kind << "]"; if (!(++i % 10)) std::cout << std::endl; } } /* output_runlen */ int main() { /* 0b1111_1111_1111_1111_1111_1111_1111_1111, 0b1111_1111_1111_1111_1111_1100_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0111 */ /* 0xf____f____f____f____f____f____f____f, 0xf____f____f____f____f____c____0____0, 0x0____0____0____0____0____0____0____7 */ /* 0xffffffff, 0xfffffc00, 0x00000007 */ unsigned int array[] = #if 1 { 0xffffffff, 0xfffffc00, 0x00000007 }; /* correct values for your example */ #else { 4294967295, 4194303, 3758096384 }; /* original values, only first matches. */ #endif size_t array_n = sizeof array / sizeof array[0]; run_runlen_encoding(array, array_n, output_runlen); std::cout << std::endl; } /* main */
Note:
As we needed to compute how far the carry bit jumps in one increment, we have to go from the less significant bit to the most, making the output just the reverse order than you tried, but I'm sure you will be able to change the order to make it appear as you stated in the question.
The program output shows:
$ pru_49297910 [3/1][39/0][54/1]
N*32with N the number of integers. And it is in the example:54+39+3=96The values are the length of the sequences of ones and zeros.