#include <string>
#include <vector>

#include <sdsl/bits.hpp>
#include <sdsl/int_vector.hpp>
#include <sdsl/util.hpp>

#include <gtest/gtest.h>

namespace
{

SDSL_UNUSED uint32_t cnt_naive(uint64_t x)
{
    uint32_t res = 0;
    for (int i = 0; i < 64; ++i)
    {
        res += x & 1;
        x >>= 1;
    }
    return res;
}

SDSL_UNUSED inline uint32_t cnt10_naive(uint64_t x, uint64_t & c)
{
    uint32_t sum = 0, lastbit = c;
    for (uint32_t i = 0; i < 64; ++i)
    {
        if ((x & 1) == 0 and lastbit == 1) { ++sum; }
        lastbit = (x & 1);
        x >>= 1;
    }
    c = lastbit;
    return sum;
}

SDSL_UNUSED uint32_t cnt11_naive(uint64_t x)
{
    uint32_t res = 0;
    for (int i = 0; i < 64; ++i)
    {
        if ((x & 3) == 3)
        {
            ++res;
            ++i;
            x >>= 1;
        }
        x >>= 1;
    }
    return res;
}

SDSL_UNUSED inline uint32_t hi_naive(uint64_t x)
{
    uint32_t res = 63;
    while (x)
    {
        if (x & 0x8000000000000000ULL) { return res; }
        --res;
        x <<= 1;
    }
    return 0;
}

inline uint32_t lo_naive(uint64_t x)
{
    if (x & 1) return 0;
    x >>= 1;
    for (int i = 1; i < 64; ++i)
        if (x & 1)
            return i;
        else
            x >>= 1;
    return 63;
}

SDSL_UNUSED inline uint32_t sel_naive(uint64_t x, uint32_t i)
{
    uint32_t pos = 0;
    while (x)
    {
        i -= x & 1;
        x >>= 1;
        if (!i) break;
        ++pos;
    }
    return pos;
}

SDSL_UNUSED uint32_t sel11_naive(uint64_t x, uint32_t i)
{
    for (uint32_t j = 0; j < 63; ++j)
    {
        if ((x & 3) == 3)
        {
            i--;
            if (!i) return j + 1;
            x >>= 1;
            ++j;
        }
        x >>= 1;
    }
    return 63;
}

SDSL_UNUSED inline uint64_t rev_naive(uint64_t x)
{
    uint64_t y = 0;
    for (size_t i = 0; i < 64; i++)
    {
        if (x & (1ULL << i)) { y |= (1ULL << (63 - i)); }
    }
    return y;
}

// The fixture for testing class int_vector.
class bits_test : public ::testing::Test
{
  protected:
    bits_test()
    {
        m_data = sdsl::int_vector<64>(1000000);
        sdsl::util::set_random_bits(m_data);
    }

    virtual ~bits_test() {}

    virtual void SetUp() {}

    virtual void TearDown() {}
    sdsl::int_vector<64> m_data;
};

//! Test the default constructor
TEST_F(bits_test, cnt)
{
    for (uint64_t i = 0; i < 64; ++i) { ASSERT_EQ((uint64_t)1, sdsl::bits::cnt(1ULL << i)); }
}

//! Test the parametrized constructor
TEST_F(bits_test, sel)
{
    for (uint64_t i = 0; i < 64; ++i) { ASSERT_EQ(i, sdsl::bits::sel(1ULL << i, 1)); }
    for (uint64_t i = 0; i < this->m_data.size(); ++i)
    {
        uint64_t x = this->m_data[i];
        for (uint64_t j = 0, ones = 0; j < 64; ++j)
        {
            if ((x >> j) & 1)
            {
                ++ones;
                ASSERT_EQ(j, sdsl::bits::sel(x, ones));
            }
        }
    }
}

TEST_F(bits_test, hi)
{
    for (uint64_t i = 0; i < 64; ++i)
    {
        uint64_t x = 1ULL << i;
        ASSERT_EQ(hi_naive(x), sdsl::bits::hi(x));
    }
}

TEST_F(bits_test, lo)
{
    for (uint64_t i = 0; i < 64; ++i)
    {
        uint64_t x = 1ULL << i;
        ASSERT_EQ(lo_naive(x), sdsl::bits::lo(x));
    }
}

TEST_F(bits_test, rev)
{
    for (uint64_t i = 0; i < this->m_data.size(); ++i)
    {
        uint64_t x = this->m_data[i];
        uint64_t rx = sdsl::bits::rev(x);
        ASSERT_EQ(rev_naive(x), rx);
        ASSERT_EQ(x, sdsl::bits::rev(rx));
    }
}

} // namespace

int main(int argc, char ** argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
