diff options
Diffstat (limited to 'tests/unit/test-1-among.c')
-rw-r--r-- | tests/unit/test-1-among.c | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/tests/unit/test-1-among.c b/tests/unit/test-1-among.c new file mode 100644 index 0000000..bd19192 --- /dev/null +++ b/tests/unit/test-1-among.c @@ -0,0 +1,105 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +static inline unsigned long statistical_prng() +{ + static unsigned long statistical_prng_state = 2463534242U; + unsigned long x = statistical_prng_state; + + if (sizeof(long) <= 4) { + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + } else { + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + } + return statistical_prng_state = x; +} + +/* returns the position of one bit set in <v>, starting at position <bit>, and + * searching in other halves if not found. This is intended to be used to + * report the position of one bit set among several based on a counter or a + * random generator while preserving a relatively good distribution so that + * values made of holes in the middle do not see one of the bits around the + * hole being returned much more often than the other one. It can be seen as a + * disturbed ffsl() where the initial search starts at bit <bit>. The look up + * is performed in O(logN) time for N bit words, yielding a bit among 64 in + * about 16 cycles. Passing value 0 for <v> makes no sense and -1 is returned + * in this case. + */ +int one_among(unsigned long v, int bit) +{ + /* note, these masks may be produced by ~0UL/((1UL<<scale)+1) but + * that's more expensive. + */ + static const unsigned long halves[] = { + (unsigned long)0x5555555555555555ULL, + (unsigned long)0x3333333333333333ULL, + (unsigned long)0x0F0F0F0F0F0F0F0FULL, + (unsigned long)0x00FF00FF00FF00FFULL, + (unsigned long)0x0000FFFF0000FFFFULL, + (unsigned long)0x00000000FFFFFFFFULL + }; + unsigned long halfword = ~0UL; + int scope = 0; + int mirror; + int scale; + + if (!v) + return -1; + + /* we check if the exact bit is set or if it's present in a mirror + * position based on the current scale we're checking, in which case + * it's returned with its current (or mirrored) value. Otherwise we'll + * make sure there's at least one bit in the half we're in, and will + * scale down to a smaller scope and try again, until we find the + * closest bit. + */ + for (scale = (sizeof(long) > 4) ? 5 : 4; scale >= 0; scale--) { + halfword >>= (1UL << scale); + scope |= (1UL << scale); + mirror = bit ^ (1UL << scale); + if (v & ((1UL << bit) | (1UL << mirror))) + return (v & (1UL << bit)) ? bit : mirror; + + if (!((v >> (bit & scope)) & halves[scale] & halfword)) + bit = mirror; + } + return bit; +} + +int main(int argc, char **argv) +{ + unsigned long mask; + int bit; + + if (argc < 2) { + unsigned long long tests = 0; + int ret; + + while (1) { + mask = statistical_prng(); // note: cannot be zero + bit = statistical_prng() % (sizeof(long) * 8); + ret = one_among(mask, bit); + if (ret < 0 || !((mask >> ret) & 1)) + printf("###ERR### mask=%#lx bit=%d ret=%d\n", mask, bit, ret); + if (!(tests & 0xffffff)) + printf("count=%Ld mask=%lx bit=%d ret=%d\n", tests, mask, bit, ret); + tests++; + } + } + + mask = atol(argv[1]); + + if (argc < 3) { + for (bit = 0; bit < 8*sizeof(long); bit++) + printf("v %#x bit %d best %d\n", mask, bit, one_among(mask, bit)); + } else { + bit = atoi(argv[2]); + printf("v %#x bit %d best %d\n", mask, bit, one_among(mask, bit)); + } + return 0; +} |