diff --git a/include/hgl/impl/incidence_list.hpp b/include/hgl/impl/incidence_list.hpp index f0b41cf..b9a5171 100644 --- a/include/hgl/impl/incidence_list.hpp +++ b/include/hgl/impl/incidence_list.hpp @@ -289,16 +289,16 @@ class incidence_list final { gl_attr_force_inline void bind_tail( const types::id_type vertex_id, const types::id_type hyperedge_id ) noexcept { - // TODO: validate if minor_id is not in head const auto [major_id, minor_id] = layout_tag::majmin(vertex_id, hyperedge_id); + this->_remove_no_align(this->_major_storage[major_id].head, minor_id); this->_unique_insert(this->_major_storage[major_id].tail, minor_id); } gl_attr_force_inline void bind_head( const types::id_type vertex_id, const types::id_type hyperedge_id ) noexcept { - // TODO: validate if minor_id is not in tail const auto [major_id, minor_id] = layout_tag::majmin(vertex_id, hyperedge_id); + this->_remove_no_align(this->_major_storage[major_id].tail, minor_id); this->_unique_insert(this->_major_storage[major_id].head, minor_id); } diff --git a/tests/source/hgl/test_incidence_list.cpp b/tests/source/hgl/test_incidence_list.cpp index fc10e9a..8598803 100644 --- a/tests/source/hgl/test_incidence_list.cpp +++ b/tests/source/hgl/test_incidence_list.cpp @@ -783,6 +783,36 @@ TEST_CASE_FIXTURE( CHECK(std::ranges::contains(vertices, constants::id1)); } +TEST_CASE_FIXTURE( + test_bf_directed_vertex_major_incidence_list, + "binding methods should rebind the elements if they are already bound" +) { + sut_type sut{constants::n_vertices, constants::n_hyperedges}; + constexpr auto vertex_id = constants::id1, hyperedge_id = constants::id2; + + REQUIRE_FALSE(sut.are_bound(vertex_id, hyperedge_id)); + REQUIRE_FALSE(sut.is_head(vertex_id, hyperedge_id)); + REQUIRE_FALSE(sut.is_head(vertex_id, hyperedge_id)); + + // initial bind + sut.bind_head(vertex_id, hyperedge_id); + CHECK(sut.are_bound(vertex_id, hyperedge_id)); + CHECK(sut.is_head(vertex_id, hyperedge_id)); + CHECK_FALSE(sut.is_tail(vertex_id, hyperedge_id)); + + // rebind tail + sut.bind_tail(vertex_id, hyperedge_id); + CHECK(sut.are_bound(vertex_id, hyperedge_id)); + CHECK(sut.is_tail(vertex_id, hyperedge_id)); + CHECK_FALSE(sut.is_head(vertex_id, hyperedge_id)); + + // rebind head + sut.bind_head(vertex_id, hyperedge_id); + CHECK(sut.are_bound(vertex_id, hyperedge_id)); + CHECK(sut.is_head(vertex_id, hyperedge_id)); + CHECK_FALSE(sut.is_tail(vertex_id, hyperedge_id)); +} + TEST_CASE_FIXTURE( test_bf_directed_vertex_major_incidence_list, "unbind should erase the hyperedge id from a proper vertex entry" @@ -1104,6 +1134,36 @@ TEST_CASE_FIXTURE( CHECK(std::ranges::contains(hyperedges, constants::id1)); } +TEST_CASE_FIXTURE( + test_bf_directed_vertex_major_incidence_list, + "binding methods should rebind the elements if they are already bound" +) { + sut_type sut{constants::n_vertices, constants::n_hyperedges}; + constexpr auto vertex_id = constants::id1, hyperedge_id = constants::id2; + + REQUIRE_FALSE(sut.are_bound(vertex_id, hyperedge_id)); + REQUIRE_FALSE(sut.is_head(vertex_id, hyperedge_id)); + REQUIRE_FALSE(sut.is_head(vertex_id, hyperedge_id)); + + // initial bind + sut.bind_head(vertex_id, hyperedge_id); + CHECK(sut.are_bound(vertex_id, hyperedge_id)); + CHECK(sut.is_head(vertex_id, hyperedge_id)); + CHECK_FALSE(sut.is_tail(vertex_id, hyperedge_id)); + + // rebind tail + sut.bind_tail(vertex_id, hyperedge_id); + CHECK(sut.are_bound(vertex_id, hyperedge_id)); + CHECK(sut.is_tail(vertex_id, hyperedge_id)); + CHECK_FALSE(sut.is_head(vertex_id, hyperedge_id)); + + // rebind head + sut.bind_head(vertex_id, hyperedge_id); + CHECK(sut.are_bound(vertex_id, hyperedge_id)); + CHECK(sut.is_head(vertex_id, hyperedge_id)); + CHECK_FALSE(sut.is_tail(vertex_id, hyperedge_id)); +} + TEST_CASE_FIXTURE( test_bf_directed_vertex_major_incidence_list, "unbind should clear the corresponding bit" ) {