generate-shapes/src/util/graph/radix_heap.h
2022-03-28 14:59:55 +02:00

226 lines
6.3 KiB
C++

// based on https://github.com/iwiwi/radix-heap
#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
namespace radix_heap {
namespace internal {
template <bool Is64bit>
class find_bucket_impl;
template <>
class find_bucket_impl<false> {
public:
static inline constexpr size_t find_bucket(uint32_t x, uint32_t last) {
return x == last ? 0 : 32 - __builtin_clz(x ^ last);
}
};
template <>
class find_bucket_impl<true> {
public:
static inline constexpr size_t find_bucket(uint64_t x, uint64_t last) {
return x == last ? 0 : 64 - __builtin_clzll(x ^ last);
}
};
template <typename T>
inline constexpr size_t find_bucket(T x, T last) {
return find_bucket_impl<sizeof(T) == 8>::find_bucket(x, last);
}
template <typename KeyType, bool IsSigned>
class encoder_impl_integer;
template <typename KeyType>
class encoder_impl_integer<KeyType, false> {
public:
typedef KeyType key_type;
typedef KeyType unsigned_key_type;
inline static constexpr unsigned_key_type encode(key_type x) { return x; }
inline static constexpr key_type decode(unsigned_key_type x) { return x; }
};
template <typename KeyType>
class encoder_impl_integer<KeyType, true> {
public:
typedef KeyType key_type;
typedef typename std::make_unsigned<KeyType>::type unsigned_key_type;
inline static constexpr unsigned_key_type encode(key_type x) {
return static_cast<unsigned_key_type>(x) ^
(unsigned_key_type(1) << unsigned_key_type(
std::numeric_limits<unsigned_key_type>::digits - 1));
}
inline static constexpr key_type decode(unsigned_key_type x) {
return static_cast<key_type>(
x ^ (unsigned_key_type(1)
<< (std::numeric_limits<unsigned_key_type>::digits - 1)));
}
};
template <typename KeyType, typename UnsignedKeyType>
class encoder_impl_decimal {
public:
typedef KeyType key_type;
typedef UnsignedKeyType unsigned_key_type;
inline static constexpr unsigned_key_type encode(key_type x) {
return raw_cast<key_type, unsigned_key_type>(x) ^
((-(raw_cast<key_type, unsigned_key_type>(x) >>
(std::numeric_limits<unsigned_key_type>::digits - 1))) |
(unsigned_key_type(1)
<< (std::numeric_limits<unsigned_key_type>::digits - 1)));
}
inline static constexpr key_type decode(unsigned_key_type x) {
return raw_cast<unsigned_key_type, key_type>(
x ^ (((x >> (std::numeric_limits<unsigned_key_type>::digits - 1)) - 1) |
(unsigned_key_type(1)
<< (std::numeric_limits<unsigned_key_type>::digits - 1))));
}
private:
template <typename T, typename U>
union raw_cast {
public:
constexpr raw_cast(T t) : t_(t) {}
operator U() const { return u_; }
private:
T t_;
U u_;
};
};
template <typename KeyType>
class encoder
: public encoder_impl_integer<KeyType, std::is_signed<KeyType>::value> {};
template <>
class encoder<float> : public encoder_impl_decimal<float, uint32_t> {};
template <>
class encoder<double> : public encoder_impl_decimal<double, uint64_t> {};
} // namespace internal
template <typename KeyType, typename ValueType,
typename EncoderType = internal::encoder<KeyType>>
class pair_radix_heap {
public:
typedef KeyType key_type;
typedef ValueType value_type;
typedef EncoderType encoder_type;
typedef typename encoder_type::unsigned_key_type unsigned_key_type;
pair_radix_heap() : size_(0), last_(), buckets_() {
buckets_min_.fill(std::numeric_limits<unsigned_key_type>::max());
}
void push(key_type key, const value_type &value) {
unsigned_key_type x = encoder_type::encode(key);
if (last_ > x) {
std::cerr << "PQ: not monotone: " << last_ << " vs " << x << std::endl;
x = last_;
}
++size_;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(x, value);
buckets_min_[k] = std::min(buckets_min_[k], x);
}
void push(key_type key, value_type &&value) {
unsigned_key_type x = encoder_type::encode(key);
if (last_ > x) {
std::cerr << "PQ: not monotone: " << last_ << " vs " << x << std::endl;
x = last_;
}
++size_;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(x, std::move(value));
buckets_min_[k] = std::min(buckets_min_[k], x);
}
template <class... Args>
void emplace(key_type key, Args &&... args) {
unsigned_key_type x = encoder_type::encode(key);
if (last_ > x) x = last_;
++size_;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(std::piecewise_construct, std::forward_as_tuple(x),
std::forward_as_tuple(args...));
buckets_min_[k] = std::min(buckets_min_[k], x);
}
key_type topKey() {
pull();
return encoder_type::decode(last_);
}
value_type &topVal() {
pull();
return buckets_[0].back().second;
}
void pop() {
pull();
buckets_[0].pop_back();
--size_;
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
void clear() {
size_ = 0;
last_ = key_type();
for (auto &b : buckets_) b.clear();
buckets_min_.fill(std::numeric_limits<unsigned_key_type>::max());
}
void swap(pair_radix_heap<KeyType, ValueType, EncoderType> &a) {
std::swap(size_, a.size_);
std::swap(last_, a.last_);
buckets_.swap(a.buckets_);
buckets_min_.swap(a.buckets_min_);
}
private:
size_t size_;
unsigned_key_type last_;
std::array<std::vector<std::pair<unsigned_key_type, value_type>>,
std::numeric_limits<unsigned_key_type>::digits + 1>
buckets_;
std::array<unsigned_key_type,
std::numeric_limits<unsigned_key_type>::digits + 1>
buckets_min_;
void pull() {
assert(size_ > 0);
if (!buckets_[0].empty()) return;
size_t i;
for (i = 1; buckets_[i].empty(); ++i)
;
last_ = buckets_min_[i];
for (size_t j = 0; j < buckets_[i].size(); ++j) {
const unsigned_key_type x = buckets_[i][j].first;
const size_t k = internal::find_bucket(x, last_);
buckets_[k].emplace_back(std::move(buckets_[i][j]));
buckets_min_[k] = std::min(buckets_min_[k], x);
}
buckets_[i].clear();
buckets_min_[i] = std::numeric_limits<unsigned_key_type>::max();
}
};
} // namespace radix_heap