Here's some highly illegal code, which is BLAZING FAST - this is the kind of stuff I want the C++ standard std::bitset to do for me... (because it's complex and should be kept firmly out of sight).
It even works perfectly on linux using gcc.
It is highly illegal because it uses this:
bitset<33> y(7);
cout << *reinterpret_cast<const ulong *>(&y) << '\n';
(So the libstdc++ implementation of bitset has the the internal bit representation at the right spot! Nice.
https://github.com/gcc-mirror/gcc/blob/d353bf189d2bbaf4059f402ee4d2a5ea074c349f/libstdc%2B%2B-v3/include/std/bitset#L76 )
////////////////////////////////////////////////////////
#include <bitset>
#include <stdexcept>
#include <limits>
#include <climits>
#include <iostream>
#include <cassert>
#include <climits>
using namespace std;
namespace Bitset {
size_t npos = numeric_limits<size_t>::max();
};
/// some lookup tables
#if (ULONG_MAX == 4294967295UL)
// unsigned long has 32 bits
static_assert(sizeof(unsigned long) == 4, "unsigned long must have 4 bytes");
static const int MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
#else
#if (ULONG_MAX == 18446744073709551615UL)
// unsigned long has 64 bits
static_assert(sizeof(unsigned long) == 8, "unsigned long must have 8 bytes");
static const int MultiplyDeBruijnBitPosition[64] =
{
0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61,
54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62,
46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63
};
#else
static_assert(0, "strange machine architecture");
#endif
#endif
int find_lowest_idx_set_in_ulong(unsigned long val)
// only call this function if val does definately have a bit that is set
{
#if (ULONG_MAX == 4294967295UL)
// unsigned long has 32 bits
// Reference:
http://stackoverflow.com/a/757266 (
http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup )
return MultiplyDeBruijnBitPosition[(((val & -val) * 0x077CB531UL)) >> 27];
#else
#if (ULONG_MAX == 18446744073709551615UL)
// unsigned long has 64 bits
// Reference:
http://stackoverflow.com/a/24146182 (
http://chessprogramming.wikispaces.com/BitScan#KimWalisch )
return MultiplyDeBruijnBitPosition[(((val ^ (val-1)) * 0x03F79D71B4CB0A89UL)) >> 58];
#endif
#endif
}
template<size_t N>
size_t find(const bitset<N>& bs, size_t idx)
{
if (idx >= N)
return Bitset::npos; // not found
using ulong = unsigned long;
constexpr int num_bits_ulong = CHAR_BIT * sizeof(ulong);
size_t idx_quot = idx / num_bits_ulong; // index into ulong array
const size_t idx_rem = idx - idx_quot * num_bits_ulong;
constexpr size_t N_1 = N-1;
const size_t lim_quot = (N_1) / num_bits_ulong;
const size_t lim_rem = (N_1) - lim_quot * num_bits_ulong;
assert((reinterpret_cast<ulong>(reinterpret_cast<const ulong *>(&bs)) % sizeof(ulong)) == 0); /* check alignment */
const ulong *mem = reinterpret_cast<const ulong *>(&bs) + idx_quot;
ulong val = *mem & (-1UL << idx_rem);
if (idx_quot < lim_quot) {
if (val) {
return idx_quot * num_bits_ulong + find_lowest_idx_set_in_ulong(val);
}
while (++idx_quot < lim_quot) {
if (mem[idx_quot])
return idx_quot * num_bits_ulong + find_lowest_idx_set_in_ulong(mem[idx_quot]);
}
val = mem[idx_quot];
}
val &= (-1UL >> (num_bits_ulong - lim_rem - 1));
if (val) {
return idx_quot * num_bits_ulong + find_lowest_idx_set_in_ulong(val);
}
return Bitset::npos; // not found
}
int main()
{
bitset<33> y(7);
cout << *reinterpret_cast<const ulong *>(&y) << '\n';
bitset<1000> x;
x.set(0); /* this is the low bit we are looking for -- it gets shifted along in the for loop below */
x.set(7); /* (we're not looking for this bit) */
for (; x.any(); x <<= 1)
cout << find(x, 0) << ' ';
cout << '\n';
return 0;
}
////////////////////////////////////////////////////////