Skip to content

Commit

Permalink
Factor out bit_array
Browse files Browse the repository at this point in the history
  • Loading branch information
adamant-pwn committed Nov 12, 2024
1 parent 5382014 commit 355435a
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 58 deletions.
44 changes: 44 additions & 0 deletions cp-algo/bit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef CP_ALGO_BIT_HPP
#define CP_ALGO_BIT_HPP
#include <immintrin.h>
#include <cstdint>
#include <array>
#include <bit>
namespace cp_algo {
template<typename Uint>
constexpr size_t bit_width = sizeof(Uint) * 8;
template<size_t maxc, typename Uint = uint64_t>
using popcount_array = std::array<int, maxc / bit_width<Uint> + 1>;

size_t order_of_bit(auto x, size_t k) {
return k ? std::popcount(x << (bit_width<decltype(x)> - k)) : 0;
}
// Requires GCC target("popcnt,bmi2")
size_t kth_set_bit(uint64_t x, size_t k) {
return std::countr_zero(_pdep_u64(1ULL << k, x));
}

template<size_t N, typename Uint = uint64_t>
struct bit_array {
static constexpr size_t width = bit_width<Uint>;
static constexpr size_t blocks = N / width + 1;
std::array<Uint, blocks> data = {};

uint64_t word(size_t x) const {
return data[x];
}
void set(size_t x) {
data[x / width] |= 1ULL << (x % width);
}
void flip(size_t x) {
data[x / width] ^= 1ULL << (x % width);
}
bool test(size_t x) const {
return (data[x / width] >> (x % width)) & 1;
}
bool operator[](size_t x) const {
return test(x);
}
};
}
#endif // CP_ALGO_BIT_HPP
39 changes: 15 additions & 24 deletions cp-algo/structures/bitpack.hpp
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@

#ifndef CP_ALGO_STRUCTURES_BITPACK_HPP
#define CP_ALGO_STRUCTURES_BITPACK_HPP
#include "cp-algo/bit.hpp"
#include <cstdint>
#include <cstddef>
#include <string>
#include <array>
#include <bit>
namespace cp_algo::structures {
template<size_t n, typename Int = uint64_t>
struct bitpack {
static constexpr uint8_t bits_per_block = 8 * sizeof(Int);
static constexpr uint32_t blocks = (n + bits_per_block - 1) / bits_per_block;
std::array<Int, blocks> data;

struct bitpack: bit_array<n, Int> {
using Base = bit_array<n, Int>;
using Base::width, Base::blocks, Base::data;
auto operator <=> (bitpack const& t) const = default;

bitpack(): data{} {}
bitpack(std::string bits): data{} {
size_t rem = size(bits) % bits_per_block;
bitpack() {}
bitpack(std::string bits) {
size_t rem = size(bits) % width;
if(rem) {
bits += std::string(bits_per_block - rem, '0');
bits += std::string(width - rem, '0');
}
for(size_t i = 0, pos = 0; pos < size(bits); i++, pos += bits_per_block) {
for(size_t j = bits_per_block; j; j--) {
for(size_t i = 0, pos = 0; pos < size(bits); i++, pos += width) {
for(size_t j = width; j; j--) {
data[i] *= 2;
data[i] ^= bits[pos + j - 1] == '1';
}
}
}

bitpack& xor_hint(bitpack const& t, size_t hint) {
for(size_t i = hint / bits_per_block; i < blocks; i++) {
for(size_t i = hint / width; i < blocks; i++) {
data[i] ^= t.data[i];
}
return *this;
Expand All @@ -42,18 +40,11 @@ namespace cp_algo::structures {
return bitpack(*this) ^= t;
}

int operator[](size_t i) const {
return (data[i / bits_per_block] >> (i % bits_per_block)) & 1;
}
void set(size_t i) {
data[i / bits_per_block] |= 1ULL << (i % bits_per_block);
}

std::string to_string() const {
std::string res(blocks * bits_per_block, '0');
for(size_t i = 0, pos = 0; i < blocks; i++, pos += bits_per_block) {
std::string res(blocks * width, '0');
for(size_t i = 0, pos = 0; i < blocks; i++, pos += width) {
Int block = data[i];
for(size_t j = 0; j < bits_per_block; j++) {
for(size_t j = 0; j < width; j++) {
res[pos + j] = '0' + block % 2;
block /= 2;
}
Expand All @@ -66,7 +57,7 @@ namespace cp_algo::structures {
size_t res = 0;
size_t i = 0;
while(i < blocks && data[i] == 0) {
res += bits_per_block;
res += width;
i++;
}
if(i < blocks) {
Expand Down
1 change: 0 additions & 1 deletion cp-algo/structures/fenwick.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CP_ALGO_STRUCTURES_FENWICK_HPP
#include <cassert>
#include <vector>
#include <bit>
namespace cp_algo::structures {
template<typename T, typename Container = std::vector<T>>
struct fenwick {
Expand Down
46 changes: 13 additions & 33 deletions cp-algo/structures/fenwick_set.hpp
Original file line number Diff line number Diff line change
@@ -1,76 +1,56 @@
#ifndef CP_ALGO_STRUCTURES_FENWICK_SET_HPP
#define CP_ALGO_STRUCTURES_FENWICK_SET_HPP
#include "fenwick.hpp"
#include <immintrin.h>
#include <cstdint>
#include <bitset>
#include "cp-algo/bit.hpp"
namespace cp_algo::structures {
// fenwick-based set for [0, maxc)
// Requires GCC target("popcnt,bmi2")
template<typename Uint>
constexpr size_t width = sizeof(Uint) * 8;
template<size_t maxc, typename Uint>
using popcount_array = std::array<int, maxc / width<Uint> + 1>;
template<size_t maxc, typename Uint = uint64_t>
struct fenwick_set: fenwick<int, popcount_array<maxc, Uint>> {
using Base = fenwick<int, popcount_array<maxc, Uint>>;
static constexpr size_t word = width<Uint>;
static constexpr size_t word = bit_width<Uint>;
size_t sz = 0;
std::array<Uint, maxc / word + 1> bits;

void flip_bit(size_t x) {
bits[x / word] ^= 1ULL << (x % word);
}
bool present(size_t x) const {
return (bits[x / word] >> (x % word)) & 1;
}
bit_array<maxc, Uint> bits;

fenwick_set(): Base(popcount_array<maxc, Uint>{}) {}
fenwick_set(auto &&range): fenwick_set() {
for(auto x: range) {
Base::data[x / word + 1] += 1;
if(!present(x)) {
if(!bits.test(x)) {
sz++;
flip_bit(x);
bits.flip(x);
}
}
Base::to_prefix_sums();
}
void insert(size_t x) {
if(present(x)) return;
flip_bit(x);
if(bits.test(x)) return;
bits.flip(x);
sz++;
Base::add(x / word, 1);
}
void erase(size_t x) {
if(!present(x)) return;
flip_bit(x);
if(!bits.test(x)) return;
bits.flip(x);
sz--;
Base::add(x / word, -1);
}
static size_t order_of_bit(Uint x, size_t k) {
return k ? std::popcount(x << (word - k)) : 0;
}
size_t order_of_key(size_t x) const {
return Base::prefix_sum(x / word) + order_of_bit(bits[x / word], x % word);
}
static size_t kth_set_bit(Uint x, size_t k) {
return std::countr_zero(_pdep_u64(1ULL << k, x));
return Base::prefix_sum(x / word) + order_of_bit(bits.word(x / word), x % word);
}
size_t find_by_order(size_t order) const {
if(order >= sz) {
return -1;
}
auto [x, remainder] = Base::prefix_lower_bound(order + 1);
return x * word + kth_set_bit(bits[x], remainder - 1);
return x * word + kth_set_bit(bits.word(x), remainder - 1);
}
size_t lower_bound(size_t x) const {
if(present(x)) {return x;}
if(bits.test(x)) {return x;}
auto order = order_of_key(x);
return order < sz ? find_by_order(order) : -1;
}
size_t pre_upper_bound(size_t x) const {
if(present(x)) {return x;}
if(bits.test(x)) {return x;}
auto order = order_of_key(x);
return order ? find_by_order(order - 1) : -1;
}
Expand Down

0 comments on commit 355435a

Please sign in to comment.