Skip to content

Commit

Permalink
Allow handles to be disengaged.
Browse files Browse the repository at this point in the history
  • Loading branch information
asoffer committed Jan 6, 2024
1 parent 841c50e commit 3f0e8a6
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 10 deletions.
45 changes: 35 additions & 10 deletions nth/container/disjoint_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <utility>

#include "nth/container/flyweight_map.h"
#include "nth/debug/debug.h"
#include "nth/utility/buffer.h"

namespace nth {
Expand Down Expand Up @@ -49,22 +50,32 @@ struct disjoint_set {
}

struct handle {
// Returns an empty handle.
explicit constexpr handle() : ptr_(nullptr) {}

explicit handle(
flyweight_map<value_type, internal_disjoint_set::Entry>::iterator iter,
internal_disjoint_set::passkey = {})
: iter_(iter) {}
: ptr_(&*iter) {}

[[nodiscard]] bool empty() const { return ptr_ == nullptr; }

value_type const &operator*() const { return iter_->first; }
// Returns a reference to the underlying element referred to by this handle.
// Behavior is undefined if the handle is unpopulated.
value_type const &operator*() const {
NTH_REQUIRE((v.debug), ptr_ != nullptr);
return ptr_->first;
}

private:
friend disjoint_set;

friend bool operator==(handle, handle) = default;

handle &parent() { return iter_->second.buffer.template as<handle>(); }
size_t &size() { return iter_->second.size; }
handle &parent() { return ptr_->second.buffer.template as<handle>(); }
size_t &size() { return ptr_->second.size; }

flyweight_map<value_type, internal_disjoint_set::Entry>::iterator iter_;
std::pair<value_type const, internal_disjoint_set::Entry> *ptr_;
};
static_assert(std::is_trivially_destructible_v<handle>);

Expand All @@ -75,10 +86,12 @@ struct disjoint_set {
std::pair<handle, bool> insert(value_type const &v);
std::pair<handle, bool> insert(value_type &&v);

// Returns a handle to an element equivalent to `v`.
// Returns a handle to an element equivalent to `v` if an element equivalent
// to `v` is present in the container. An empty handle is returned otherwise.
handle find(value_type const &v);

// Returns a handle to an element in the same subset as `v`.
// Returns a handle to an element in the same subset as `v`. Behavior is
// undefined if `v` is not present in the container.
handle find_representative(value_type const &v);

// Returns a representative handle referencing an element in the same subset
Expand All @@ -96,20 +109,26 @@ struct disjoint_set {
size_t size() const { return entries_.size(); }

private:
handle representative_impl(handle h);

flyweight_map<value_type, internal_disjoint_set::Entry> entries_;
};

// Implementation

template <typename T>
disjoint_set<T>::handle disjoint_set<T>::find(value_type const &v) {
return handle(entries_.find(v));
auto iter = entries_.find(v);
if (iter == entries_.end()) { return handle(); }
return handle(iter);
}

template <typename T>
disjoint_set<T>::handle disjoint_set<T>::find_representative(
value_type const &v) {
return representative(find(v));
auto iter = entries_.find(v);
if (iter == entries_.end()) { return handle(); }
return representative(handle(iter));
}

template <typename T>
Expand Down Expand Up @@ -145,9 +164,15 @@ std::pair<typename disjoint_set<T>::handle, bool> disjoint_set<T>::insert(

template <typename T>
typename disjoint_set<T>::handle disjoint_set<T>::representative(handle h) {
if (h.empty()) { return h; }
return representative_impl(h);
}

template <typename T>
typename disjoint_set<T>::handle disjoint_set<T>::representative_impl(handle h) {
handle &parent = h.parent();
if (parent == h) { return h; }
return parent = representative(parent);
return parent = representative_impl(parent);
}

} // namespace nth
Expand Down
21 changes: 21 additions & 0 deletions nth/container/disjoint_set_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
namespace nth {
namespace {

NTH_TEST("disjoint_set/handle") {
disjoint_set<int>::handle h;
NTH_EXPECT(h.empty());
}

NTH_TEST("disjoint_set/empty") {
disjoint_set<int> set;
NTH_EXPECT(set.empty());
Expand Down Expand Up @@ -35,15 +40,31 @@ NTH_TEST("disjoint_set/representative") {
disjoint_set<int> set{1, 2, 3};
auto h = set.find(1);
NTH_EXPECT(h == set.representative(h));
NTH_ASSERT(h != disjoint_set<int>::handle());
NTH_ASSERT(not h.empty());
NTH_EXPECT(*h == 1);

h = set.find(2);
NTH_EXPECT(h == set.representative(h));
NTH_ASSERT(h != disjoint_set<int>::handle());
NTH_ASSERT(not h.empty());
NTH_EXPECT(*h == 2);

h = set.find(3);
NTH_EXPECT(h == set.representative(h));
NTH_ASSERT(h != disjoint_set<int>::handle());
NTH_ASSERT(not h.empty());
NTH_EXPECT(*h == 3);

h = set.find(4);
NTH_EXPECT(h == set.representative(h));
NTH_ASSERT(h == disjoint_set<int>::handle());
NTH_ASSERT(h.empty());

h = set.find_representative(5);
NTH_EXPECT(h == set.representative(h));
NTH_ASSERT(h == disjoint_set<int>::handle());
NTH_ASSERT(h.empty());
}

NTH_TEST("disjoint_set/join") {
Expand Down

0 comments on commit 3f0e8a6

Please sign in to comment.