#include "bit_utils.h"

#if defined(__GNUC__) && defined(__BMI2__)
    #include <x86intrin.h>
    #include <bmi2intrin.h>     // _pdep_u32
#endif


int bitParity(uint32_t x) {
#ifdef __GNUC__
    return __builtin_parity(x);
#else
    bool p = static_cast<bool>(x);
    while (x &= x - 1) {
        p = !p;
    }
    return static_cast<int>(p);
#endif
}

int bitCount(uint32_t x) {
#ifdef __GNUC__
    return __builtin_popcount(x); // `POPulation COUNT`
#else
    int count = 0; // counts set bits

    // increment count until there are no bits set in x
    for (; x; count++) {
        x &= x - 1; // unset least significant bit
    }

    return count;
#endif
}

int bitScanLS(uint32_t x) {
#ifdef __GNUC__
    return __builtin_ctz(x);    // Count Trailing Zeros
#else
    // result storing the Count of Trailing Zeros
    int ctz = 0;

    // boolean variable storing if a bit has not found (search area is empty)
    bool empty;

    // logarithmic search for LSB bit index (-1)
    ctz += (empty = !(x & static_cast<uint32_t>(65535))) << 4;
    x >>= 16 * empty;
    ctz += (empty = !(x & static_cast<uint32_t>(  255))) << 3;
    x >>=  8 * empty;
    ctz += (empty = !(x & static_cast<uint32_t>(   15))) << 2;
    x >>=  4 * empty;
    ctz += (empty = !(x & static_cast<uint32_t>(    3))) << 1;
    x >>=  2 * empty;
    ctz += (empty = !(x & static_cast<uint32_t>(    1)));

    return ctz;
#endif
}

uint32_t bitDeposit(uint32_t val, uint32_t mask) {
#if (defined(__GNUC__) && defined(__BMI2__))
    return _pdep_u32(val, mask);
#else
    uint32_t res = 0;
    for (uint32_t pos = 1; mask; pos <<= 1) {
        if (val & pos) {
            res |= mask & -mask;
        }
        mask &= mask - 1;
    }
    return res;
#endif
}

uint32_t bitNextPerm(uint32_t val) {
    // Sets all least significant 0-bits of val to 1
    uint32_t t = val | (val - 1);
    // Next set to 1 the most significant bit to change,
    // set to 0 the least significant ones, and add the necessary 1 bits.
    return (t + 1) | (((~t & -~t) - 1) >> (bitScanLS(val) + 1));
}
