#include <algorithm>
#include <functional>
#include <iostream>
#include <limits>
#include <queue>
#include <random>
#include <tuple>
#include <vector>
#include <tlx/container/radix_heap.hpp>
#include <tlx/die.hpp>
#include <tlx/logger.hpp>
class naive_bitarray
{
public:
naive_bitarray(size_t n) : bits_(n, false) { }
void set_bit(size_t i) {
bits_.at(i) = true;
}
void clear_bit(size_t i) {
bits_.at(i) = false;
}
bool is_set(size_t i) const {
return bits_.at(i);
}
void clear_all() {
for (size_t i = 0; i < bits_.size(); i++)
bits_[i] = false;
}
bool empty() {
for (size_t i = 0; i < bits_.size(); i++)
if (bits_[i])
return false;
return true;
}
size_t find_lsb() {
for (size_t i = 0; i < bits_.size(); i++)
if (bits_[i])
return i;
die("find_lsb on empty field");
}
void swap(naive_bitarray& o) {
std::swap(bits_, o.bits_);
}
protected:
std::vector<bool> bits_;
};
template <typename Field, size_t Size>
void bitarray_test(std::mt19937& rng) {
Field f;
{
std::uniform_int_distribution<int> skip_dist(0, Size / 4096);
for (size_t i = 0; i < Size; i++) {
die_unless(f.empty());
f.set_bit(i);
die_if(f.empty());
die_unequal(i, f.find_lsb());
f.clear_bit(i);
die_unless(f.empty());
if (Size > 64 * 64 + 1) {
i += skip_dist(rng);
}
}
}
Field f0;
std::swap(f0, f);
std::uniform_int_distribution<unsigned> distr(0, Size - 1);
std::uniform_real_distribution<float> rdist;
naive_bitarray nf(Size);
for (size_t i = 0; i < std::min<size_t>(100, Size * 10); i++) {
const auto idx = distr(rng);
die_unequal(nf.is_set(idx), f.is_set(idx));
if (nf.is_set(idx)) {
nf.clear_bit(idx);
f.clear_bit(idx);
}
else {
nf.set_bit(idx);
f.set_bit(idx);
}
die_unequal(nf.empty(), f.empty());
if (f.empty()) continue;
die_unequal(nf.find_lsb(), f.find_lsb());
if (rdist(rng) > 0.3) {
const auto first = f.find_lsb();
nf.clear_bit(first);
f.clear_bit(first);
}
}
f.set_bit(0);
die_unless(f0.empty());
Field f1;
die_unless(f1.empty());
std::swap(f1, f);
die_unless(f.empty());
die_if(f1.empty());
f1.clear_all();
die_unless(f1.empty());
}
void test_main_bitarray(std::mt19937& rng) {
bitarray_test<tlx::radix_heap_detail::BitArrayRecursive<32, true>, 32>(rng);
bitarray_test<tlx::radix_heap_detail::BitArrayRecursive<64, true>, 64>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<32>, 32>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<33>, 33>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<63>, 63>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<64>, 64>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<65>, 65>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<500>, 500>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<1001>, 1001>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<4004>, 4004>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<4096>, 4096>(rng);
bitarray_test<tlx::radix_heap_detail::BitArray<64 * 64 + 1>, 64 * 64 + 1>(
rng);
bitarray_test<tlx::radix_heap_detail::BitArray<1 << 18>, 1 << 18>(rng);
}
template <typename T>
void int_rank_test(std::mt19937& prng) {
using IR = tlx::radix_heap_detail::IntegerRank<T>;
constexpr T min = std::numeric_limits<T>::min();
constexpr T max = std::numeric_limits<T>::max();
constexpr T zero = 0;
constexpr T one = 1;
die_unequal(min, IR::int_at_rank(IR::rank_of_int(min)));
die_unequal(min + one, IR::int_at_rank(IR::rank_of_int(min + one)));
die_unequal(zero, IR::int_at_rank(IR::rank_of_int(zero)));
die_unequal(one, IR::int_at_rank(IR::rank_of_int(one)));
die_unequal(max - one, IR::int_at_rank(IR::rank_of_int(max - one)));
die_unequal(max, IR::int_at_rank(IR::rank_of_int(max)));
std::uniform_int_distribution<T> dist;
for (size_t i = 0; i < 1000; i++) {
T a = dist(prng);
T b;
do {
b = dist(prng);
} while (b == a);
if (a > b) std::swap(a, b);
die_unless(IR::rank_of_int(a) < IR::rank_of_int(b));
die_unequal(a, IR::int_at_rank(IR::rank_of_int(a)));
die_unequal(b, IR::int_at_rank(IR::rank_of_int(b)));
}
}
void test_main_int_rank(std::mt19937& prng) {
int_rank_test<uint16_t>(prng);
int_rank_test<uint32_t>(prng);
int_rank_test<uint64_t>(prng);
int_rank_test<int16_t>(prng);
int_rank_test<int32_t>(prng);
int_rank_test<int64_t>(prng);
}
template <unsigned Radix, typename T>
void test_bucket_bounds() {
constexpr bool debug = false;
using Comp = tlx::radix_heap_detail::BucketComputation<Radix, T>;
Comp comp;
LOG << "test_bucket_bounds<"
"Radix=" << Radix << ", sizeof(T)=" << sizeof(T) << ">: "
"num_buckets=" << size_t(comp.num_buckets) << "\n";
die_unequal(comp.lower_bound(0u), std::numeric_limits<T>::min());
die_unequal(comp.upper_bound(comp.num_buckets - 1u),
std::numeric_limits<T>::max());
for (size_t i = 1; i < comp.num_buckets; i++) {
die_unless(comp.lower_bound(i - 1) < comp.lower_bound(i));
}
for (size_t i = 0; i < comp.num_buckets; i++) {
const auto lb = comp.lower_bound(i);
const auto ub = comp.upper_bound(i);
const auto mid = (ub - lb) / 2 + lb;
die_unless(lb >= ub - lb);
die_unless(lb <= mid);
die_unless(mid <= ub);
die_unequal(comp(lb, 0u), i);
die_unequal(comp(mid, 0u), i);
die_unequal(comp(ub, 0u), i);
}
}
void test_main_bucket(std::mt19937&) {
test_bucket_bounds<2, uint32_t>();
test_bucket_bounds<8, uint32_t>();
test_bucket_bounds<64, uint32_t>();
test_bucket_bounds<2, uint64_t>();
test_bucket_bounds<8, uint64_t>();
test_bucket_bounds<64, uint64_t>();
}
namespace tlx {
using payload_type = uint64_t;
using value_type = std::tuple<int64_t, payload_type>;
class KeyExtractor
{
public:
int64_t operator () (const value_type& p) const { return std::get<0>(p); }
};
template class RadixHeap<value_type, KeyExtractor, int64_t, 64>;
}
template <typename KeyType, unsigned Radix>
void allin_allout(std::mt19937& prng, KeyType min, KeyType max, size_t n) {
constexpr bool debug = false;
LOG << "allin_allout<"
"sizeof(T)=" << sizeof(KeyType) << ", Radix=" << Radix << ">("
"min=" << min << ", "
"max=" << max << ", "
"n=" << n << ")\n";
using payload_type = uint64_t;
using value_type = std::tuple<KeyType, payload_type>;
auto heap = tlx::make_radix_heap<value_type, Radix>(
[](const value_type& p) { return std::get<0>(p); });
std::vector<std::pair<KeyType, payload_type> > values;
values.reserve(n);
std::uniform_int_distribution<KeyType> distr(min, max);
die_unless(heap.empty());
KeyType running_min = std::numeric_limits<KeyType>::max();
for (size_t i = 0; i < n; i++) {
die_unequal(heap.size(), i);
const auto key = distr(prng);
heap.push(value_type{ key, i });
values.emplace_back(key, i);
running_min = std::min(running_min, key);
die_unequal(running_min, heap.peak_top_key());
die_if(heap.empty());
}
std::sort(values.begin(), values.end());
for (size_t i = 0; i < n; i++) {
die_unequal(heap.size(), n - i);
const auto exp_key = values[i].first;
die_unequal(heap.peak_top_key(), exp_key);
die_unequal(std::get<0>(heap.top()), exp_key);
heap.pop();
}
die_unless(heap.empty());
}
template <typename KeyType, unsigned Radix>
void allin_allout_pair(std::mt19937& prng, KeyType min, KeyType max, size_t n) {
constexpr bool debug = false;
LOG << "allin_allout_pair<"
"sizeof(T)=" << sizeof(KeyType) << ", Radix=" << Radix << ">("
"min=" << min << ", "
"max=" << max << ", "
"n=" << n << ")\n";
using payload_type = uint64_t;
tlx::RadixHeapPair<KeyType, payload_type, Radix> heap;
std::vector<std::pair<KeyType, payload_type> > values;
values.reserve(n);
std::uniform_int_distribution<KeyType> distr(min, max);
die_unless(heap.empty());
KeyType running_min = std::numeric_limits<KeyType>::max();
for (size_t i = 0; i < n; i++) {
die_unequal(heap.size(), i);
const auto key = distr(prng);
heap.push({ key, i });
values.emplace_back(key, i);
running_min = std::min(running_min, key);
die_unequal(running_min, heap.peak_top_key());
die_if(heap.empty());
}
std::sort(values.begin(), values.end());
for (size_t i = 0; i < n; i++) {
die_unequal(heap.size(), n - i);
const auto exp_key = values[i].first;
die_unequal(heap.peak_top_key(), exp_key);
die_unequal(heap.top().first, exp_key);
heap.pop();
}
die_unless(heap.empty());
}
enum class PullMode {
Single, AllOfKey, ExtractBucket
};
template <typename KeyType, unsigned Radix>
void random_inout(std::mt19937& prng, const KeyType min, const KeyType max,
const KeyType iters, const double insert_prob,
const size_t prefill_n, const PullMode pm) {
constexpr bool debug = false;
LOG << "random_inout<"
"sizeof(T)=" << sizeof(KeyType) << ", Radix=" << Radix << ">("
"min=" << min << ", "
"max=" << max << ", "
"iters=" << iters << ", "
"insert_prob=" << insert_prob << ", "
"prefill_n=" << prefill_n << ", "
"pm=" << static_cast<int>(pm) << ")\n";
using payload_type = uint32_t;
using value_type = std::tuple<KeyType, payload_type>;
auto heap = tlx::make_radix_heap<value_type, Radix>(
[](const value_type& p) { return std::get<0>(p); });
using bucket_type = typename decltype(heap)::bucket_data_type;
using pq_type = std::pair<KeyType, payload_type>;
std::priority_queue<pq_type,
std::vector<pq_type>,
std::greater<pq_type> > pq;
auto insert = [&](KeyType minv, KeyType maxv) {
std::uniform_int_distribution<KeyType> kdistr(minv, maxv);
std::uniform_int_distribution<payload_type> pdistr;
auto key = kdistr(prng);
auto pay = pdistr(prng);
pq.emplace(key, pay);
heap.emplace(key, key, pay);
};
for (size_t i = 0; i < prefill_n; i++) {
insert(min, max - iters);
}
std::uniform_real_distribution<double> rdist;
KeyType running_min = min;
std::vector<payload_type> ref_data, heap_data;
for (int64_t i = 0; i < static_cast<int64_t>(iters) || !pq.empty(); i++) {
while (i < static_cast<int64_t>(iters) && rdist(prng) < insert_prob)
insert(running_min, max - std::max<int64_t>(0, iters - i));
die_unequal(pq.empty(), heap.empty());
if (pq.empty()) continue;
if (pm == PullMode::Single) {
die_unequal(pq.top().first, std::get<0>(heap.top()));
running_min = std::get<0>(heap.top());
pq.pop();
heap.pop();
}
else {
const KeyType key = pq.top().first;
for (ref_data.clear();
!pq.empty() && pq.top().first == key;
pq.pop())
{
ref_data.push_back(pq.top().second);
}
running_min = key;
die_unequal(std::get<0>(heap.top()), key);
if (pm == PullMode::AllOfKey) {
for (heap_data.clear();
!heap.empty() && heap.peak_top_key() == key;
heap.pop())
{
heap_data.push_back(std::get<1>(heap.top()));
}
}
else {
bucket_type bucket;
heap.swap_top_bucket(bucket);
heap_data.clear();
heap_data.reserve(bucket.size());
for (const auto& x : bucket) {
heap_data.push_back(std::get<1>(x));
}
}
std::sort(ref_data.begin(), ref_data.end());
std::sort(heap_data.begin(), heap_data.end());
die_unequal(ref_data.size(), heap_data.size());
for (size_t j = 0; j < heap_data.size(); j++)
die_unequal(heap_data[j], ref_data[j]);
}
}
}
template <typename KeyType, unsigned Radix>
void random_inout_pair(std::mt19937& prng,
const KeyType min, const KeyType max,
const KeyType iters, const double insert_prob,
const size_t prefill_n, const PullMode pm) {
constexpr bool debug = false;
LOG << "random_inout_pair<"
"sizeof(T)=" << sizeof(KeyType) << ", Radix=" << Radix << ">("
"min=" << min << ", "
"max=" << max << ", "
"iters=" << iters << ", "
"insert_prob=" << insert_prob << ", "
"prefill_n=" << prefill_n << ", "
"pm=" << static_cast<int>(pm) << ")\n";
using payload_type = uint32_t;
using pq_type = std::pair<KeyType, payload_type>;
std::priority_queue<pq_type, std::vector<pq_type>, std::greater<pq_type> > pq;
using heap_type = tlx::RadixHeapPair<KeyType, payload_type, Radix>;
using bucket_type = typename
tlx::RadixHeapPair<KeyType, payload_type, Radix>::bucket_data_type;
heap_type heap;
auto insert = [&](KeyType minv, KeyType maxv) {
std::uniform_int_distribution<KeyType> kdistr(minv, maxv);
std::uniform_int_distribution<payload_type> pdistr;
auto key = kdistr(prng);
auto pay = pdistr(prng);
pq.emplace(key, pay);
heap.emplace_keyfirst(key, pay);
};
for (size_t i = 0; i < prefill_n; i++) {
insert(min, max - iters);
}
std::uniform_real_distribution<double> rdist;
KeyType running_min = min;
std::vector<payload_type> ref_data, heap_data;
for (int64_t i = 0; i < static_cast<int64_t>(iters) || !pq.empty(); i++) {
while (i < static_cast<int64_t>(iters) && rdist(prng) < insert_prob) {
insert(running_min,
static_cast<KeyType>(max - std::max<int64_t>(0, iters - i)));
}
die_unequal(pq.empty(), heap.empty());
if (pq.empty()) continue;
if (pm == PullMode::Single) {
die_unequal(pq.top().first, heap.top().first);
running_min = heap.top().first;
pq.pop();
heap.pop();
}
else {
const KeyType key = pq.top().first;
for (ref_data.clear();
!pq.empty() && pq.top().first == key;
pq.pop())
{
ref_data.push_back(pq.top().second);
}
running_min = key;
die_unequal(heap.top().first, key);
if (pm == PullMode::AllOfKey) {
for (heap_data.clear();
!heap.empty() && heap.peak_top_key() == key;
heap.pop())
{
heap_data.push_back(heap.top().second);
}
}
else {
bucket_type bucket;
heap.swap_top_bucket(bucket);
heap_data.clear();
heap_data.reserve(bucket.size());
for (const auto& x : bucket) {
heap_data.push_back(x.second);
}
}
std::sort(ref_data.begin(), ref_data.end());
std::sort(heap_data.begin(), heap_data.end());
die_unequal(ref_data.size(), heap_data.size());
for (size_t j = 0; j < heap_data.size(); j++)
die_unequal(heap_data[j], ref_data[j]);
}
}
}
void random_inout_all(std::mt19937& prng,
const int64_t min, const int64_t max,
const size_t iters, const double insert_prob,
const size_t prefill_n, const PullMode pm) {
#define RADIXHEAP_TESTSET(T) \
random_inout<T, 2>(prng, static_cast<T>(min), static_cast<T>(max), \
iters, insert_prob, prefill_n, pm); \
random_inout<T, 64>(prng, static_cast<T>(min), static_cast<T>(max), \
iters, insert_prob, prefill_n, pm); \
random_inout_pair<T, 2>(prng, static_cast<T>(min), static_cast<T>(max), \
iters, insert_prob, prefill_n, pm); \
random_inout_pair<T, 64>(prng, static_cast<T>(min), static_cast<T>(max), \
iters, insert_prob, prefill_n, pm);
die_unless(min < max);
if (min >= 0 && max >= 0) {
RADIXHEAP_TESTSET(uint32_t)
RADIXHEAP_TESTSET(uint64_t)
}
RADIXHEAP_TESTSET(int32_t)
RADIXHEAP_TESTSET(int64_t)
#undef RADIXHEAP_TESTSET
}
void allin_allout_all(std::mt19937& prng, int64_t min, int64_t max, size_t n) {
#define RADIXHEAP_TESTSET(T) \
allin_allout<T, 2>(prng, static_cast<T>(min), static_cast<T>(max), n); \
allin_allout<T, 64>(prng, static_cast<T>(min), static_cast<T>(max), n); \
allin_allout_pair<T, 2>(prng, static_cast<T>(min), static_cast<T>(max), n); \
allin_allout_pair<T, 64>(prng, static_cast<T>(min), static_cast<T>(max), n);
if (min >= 0) {
RADIXHEAP_TESTSET(uint32_t);
RADIXHEAP_TESTSET(uint64_t);
}
RADIXHEAP_TESTSET(int32_t);
RADIXHEAP_TESTSET(int64_t);
#undef RADIXHEAP_TESTSET
}
void test_main_radix_heap_pair(std::mt19937& prng) {
for (int64_t min : { 0, 1000000, -1000000 }) {
for (size_t x : { 1000, 1000000 }) {
allin_allout_all(prng, min, min + x, 500);
}
}
for (PullMode pm : { PullMode::Single, PullMode::AllOfKey,
PullMode::ExtractBucket }) {
for (int64_t prefill : { 0, 100 }) {
for (int64_t min : { 0, 10000000, -10000000, -250 }) {
for (size_t length : { 500 }) {
random_inout_all(prng, min, min + length, 200, 0.9, prefill, pm);
}
}
}
}
}
int main() {
std::mt19937 prng(1);
test_main_bitarray(prng);
test_main_int_rank(prng);
test_main_bucket(prng);
test_main_radix_heap_pair(prng);
return 0;
}