Skip to content

Commit 355435a

Browse files
committed
Factor out bit_array
1 parent 5382014 commit 355435a

File tree

4 files changed

+72
-58
lines changed

4 files changed

+72
-58
lines changed

cp-algo/bit.hpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#ifndef CP_ALGO_BIT_HPP
2+
#define CP_ALGO_BIT_HPP
3+
#include <immintrin.h>
4+
#include <cstdint>
5+
#include <array>
6+
#include <bit>
7+
namespace cp_algo {
8+
template<typename Uint>
9+
constexpr size_t bit_width = sizeof(Uint) * 8;
10+
template<size_t maxc, typename Uint = uint64_t>
11+
using popcount_array = std::array<int, maxc / bit_width<Uint> + 1>;
12+
13+
size_t order_of_bit(auto x, size_t k) {
14+
return k ? std::popcount(x << (bit_width<decltype(x)> - k)) : 0;
15+
}
16+
// Requires GCC target("popcnt,bmi2")
17+
size_t kth_set_bit(uint64_t x, size_t k) {
18+
return std::countr_zero(_pdep_u64(1ULL << k, x));
19+
}
20+
21+
template<size_t N, typename Uint = uint64_t>
22+
struct bit_array {
23+
static constexpr size_t width = bit_width<Uint>;
24+
static constexpr size_t blocks = N / width + 1;
25+
std::array<Uint, blocks> data = {};
26+
27+
uint64_t word(size_t x) const {
28+
return data[x];
29+
}
30+
void set(size_t x) {
31+
data[x / width] |= 1ULL << (x % width);
32+
}
33+
void flip(size_t x) {
34+
data[x / width] ^= 1ULL << (x % width);
35+
}
36+
bool test(size_t x) const {
37+
return (data[x / width] >> (x % width)) & 1;
38+
}
39+
bool operator[](size_t x) const {
40+
return test(x);
41+
}
42+
};
43+
}
44+
#endif // CP_ALGO_BIT_HPP

cp-algo/structures/bitpack.hpp

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
11

22
#ifndef CP_ALGO_STRUCTURES_BITPACK_HPP
33
#define CP_ALGO_STRUCTURES_BITPACK_HPP
4+
#include "cp-algo/bit.hpp"
45
#include <cstdint>
56
#include <cstddef>
67
#include <string>
78
#include <array>
8-
#include <bit>
99
namespace cp_algo::structures {
1010
template<size_t n, typename Int = uint64_t>
11-
struct bitpack {
12-
static constexpr uint8_t bits_per_block = 8 * sizeof(Int);
13-
static constexpr uint32_t blocks = (n + bits_per_block - 1) / bits_per_block;
14-
std::array<Int, blocks> data;
15-
11+
struct bitpack: bit_array<n, Int> {
12+
using Base = bit_array<n, Int>;
13+
using Base::width, Base::blocks, Base::data;
1614
auto operator <=> (bitpack const& t) const = default;
1715

18-
bitpack(): data{} {}
19-
bitpack(std::string bits): data{} {
20-
size_t rem = size(bits) % bits_per_block;
16+
bitpack() {}
17+
bitpack(std::string bits) {
18+
size_t rem = size(bits) % width;
2119
if(rem) {
22-
bits += std::string(bits_per_block - rem, '0');
20+
bits += std::string(width - rem, '0');
2321
}
24-
for(size_t i = 0, pos = 0; pos < size(bits); i++, pos += bits_per_block) {
25-
for(size_t j = bits_per_block; j; j--) {
22+
for(size_t i = 0, pos = 0; pos < size(bits); i++, pos += width) {
23+
for(size_t j = width; j; j--) {
2624
data[i] *= 2;
2725
data[i] ^= bits[pos + j - 1] == '1';
2826
}
2927
}
3028
}
3129

3230
bitpack& xor_hint(bitpack const& t, size_t hint) {
33-
for(size_t i = hint / bits_per_block; i < blocks; i++) {
31+
for(size_t i = hint / width; i < blocks; i++) {
3432
data[i] ^= t.data[i];
3533
}
3634
return *this;
@@ -42,18 +40,11 @@ namespace cp_algo::structures {
4240
return bitpack(*this) ^= t;
4341
}
4442

45-
int operator[](size_t i) const {
46-
return (data[i / bits_per_block] >> (i % bits_per_block)) & 1;
47-
}
48-
void set(size_t i) {
49-
data[i / bits_per_block] |= 1ULL << (i % bits_per_block);
50-
}
51-
5243
std::string to_string() const {
53-
std::string res(blocks * bits_per_block, '0');
54-
for(size_t i = 0, pos = 0; i < blocks; i++, pos += bits_per_block) {
44+
std::string res(blocks * width, '0');
45+
for(size_t i = 0, pos = 0; i < blocks; i++, pos += width) {
5546
Int block = data[i];
56-
for(size_t j = 0; j < bits_per_block; j++) {
47+
for(size_t j = 0; j < width; j++) {
5748
res[pos + j] = '0' + block % 2;
5849
block /= 2;
5950
}
@@ -66,7 +57,7 @@ namespace cp_algo::structures {
6657
size_t res = 0;
6758
size_t i = 0;
6859
while(i < blocks && data[i] == 0) {
69-
res += bits_per_block;
60+
res += width;
7061
i++;
7162
}
7263
if(i < blocks) {

cp-algo/structures/fenwick.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#define CP_ALGO_STRUCTURES_FENWICK_HPP
33
#include <cassert>
44
#include <vector>
5-
#include <bit>
65
namespace cp_algo::structures {
76
template<typename T, typename Container = std::vector<T>>
87
struct fenwick {

cp-algo/structures/fenwick_set.hpp

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,56 @@
11
#ifndef CP_ALGO_STRUCTURES_FENWICK_SET_HPP
22
#define CP_ALGO_STRUCTURES_FENWICK_SET_HPP
33
#include "fenwick.hpp"
4-
#include <immintrin.h>
5-
#include <cstdint>
6-
#include <bitset>
4+
#include "cp-algo/bit.hpp"
75
namespace cp_algo::structures {
86
// fenwick-based set for [0, maxc)
9-
// Requires GCC target("popcnt,bmi2")
10-
template<typename Uint>
11-
constexpr size_t width = sizeof(Uint) * 8;
12-
template<size_t maxc, typename Uint>
13-
using popcount_array = std::array<int, maxc / width<Uint> + 1>;
147
template<size_t maxc, typename Uint = uint64_t>
158
struct fenwick_set: fenwick<int, popcount_array<maxc, Uint>> {
169
using Base = fenwick<int, popcount_array<maxc, Uint>>;
17-
static constexpr size_t word = width<Uint>;
10+
static constexpr size_t word = bit_width<Uint>;
1811
size_t sz = 0;
19-
std::array<Uint, maxc / word + 1> bits;
20-
21-
void flip_bit(size_t x) {
22-
bits[x / word] ^= 1ULL << (x % word);
23-
}
24-
bool present(size_t x) const {
25-
return (bits[x / word] >> (x % word)) & 1;
26-
}
12+
bit_array<maxc, Uint> bits;
2713

2814
fenwick_set(): Base(popcount_array<maxc, Uint>{}) {}
2915
fenwick_set(auto &&range): fenwick_set() {
3016
for(auto x: range) {
3117
Base::data[x / word + 1] += 1;
32-
if(!present(x)) {
18+
if(!bits.test(x)) {
3319
sz++;
34-
flip_bit(x);
20+
bits.flip(x);
3521
}
3622
}
3723
Base::to_prefix_sums();
3824
}
3925
void insert(size_t x) {
40-
if(present(x)) return;
41-
flip_bit(x);
26+
if(bits.test(x)) return;
27+
bits.flip(x);
4228
sz++;
4329
Base::add(x / word, 1);
4430
}
4531
void erase(size_t x) {
46-
if(!present(x)) return;
47-
flip_bit(x);
32+
if(!bits.test(x)) return;
33+
bits.flip(x);
4834
sz--;
4935
Base::add(x / word, -1);
5036
}
51-
static size_t order_of_bit(Uint x, size_t k) {
52-
return k ? std::popcount(x << (word - k)) : 0;
53-
}
5437
size_t order_of_key(size_t x) const {
55-
return Base::prefix_sum(x / word) + order_of_bit(bits[x / word], x % word);
56-
}
57-
static size_t kth_set_bit(Uint x, size_t k) {
58-
return std::countr_zero(_pdep_u64(1ULL << k, x));
38+
return Base::prefix_sum(x / word) + order_of_bit(bits.word(x / word), x % word);
5939
}
6040
size_t find_by_order(size_t order) const {
6141
if(order >= sz) {
6242
return -1;
6343
}
6444
auto [x, remainder] = Base::prefix_lower_bound(order + 1);
65-
return x * word + kth_set_bit(bits[x], remainder - 1);
45+
return x * word + kth_set_bit(bits.word(x), remainder - 1);
6646
}
6747
size_t lower_bound(size_t x) const {
68-
if(present(x)) {return x;}
48+
if(bits.test(x)) {return x;}
6949
auto order = order_of_key(x);
7050
return order < sz ? find_by_order(order) : -1;
7151
}
7252
size_t pre_upper_bound(size_t x) const {
73-
if(present(x)) {return x;}
53+
if(bits.test(x)) {return x;}
7454
auto order = order_of_key(x);
7555
return order ? find_by_order(order - 1) : -1;
7656
}

0 commit comments

Comments
 (0)