From 87235313552602e8a4ee3c4f0a3c28205292d490 Mon Sep 17 00:00:00 2001 From: Matthias Schimek Date: Wed, 5 Nov 2025 22:35:58 +0100 Subject: [PATCH 1/5] edge range unifying access to a graph's edges --- kagen/edge_range.cpp | 201 +++++++++++++++++++++++++++++++++++++++++++ kagen/edge_range.h | 81 +++++++++++++++++ tests/CMakeLists.txt | 3 + tests/edge_range.cpp | 108 +++++++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 kagen/edge_range.cpp create mode 100644 kagen/edge_range.h create mode 100644 tests/edge_range.cpp diff --git a/kagen/edge_range.cpp b/kagen/edge_range.cpp new file mode 100644 index 0000000..66a4f42 --- /dev/null +++ b/kagen/edge_range.cpp @@ -0,0 +1,201 @@ +#include "kagen/edge_range.h" + +#include +#include + +namespace kagen { + +EdgeRange::EdgeRange(const Edgelist& edgelist) noexcept + : representation_(GraphRepresentation::EDGE_LIST), + edgelist_ptr_(std::addressof(edgelist)), + xadj_ptr_(nullptr), + adjncy_ptr_(nullptr), + vertex_base_(Vertex(0)) {} + +EdgeRange::EdgeRange(const XadjArray& xadj, const AdjncyArray& adjncy, VertexRange vertex_range) noexcept + : representation_(GraphRepresentation::CSR), + edgelist_ptr_(nullptr), + xadj_ptr_(std::addressof(xadj)), + adjncy_ptr_(std::addressof(adjncy)), + vertex_base_(vertex_range.first) { + assert(xadj_ptr_->size() >= 1); +} + +EdgeRange::EdgeRange(const Graph& graph) noexcept : EdgeRange(FromGraph(graph)) {} + +EdgeRange EdgeRange::FromGraph(const Graph& g) noexcept { + if (g.representation == GraphRepresentation::EDGE_LIST) { + return EdgeRange(g.edges); + } else { + return EdgeRange(g.xadj, g.adjncy, g.vertex_range); + } +} + +EdgeRange::iterator EdgeRange::iterator::edgelist_begin(const EdgeRange* parent) noexcept { + iterator it; + it.parent_ = parent; + it.idx_ = 0; + it.load_current(); + return it; +} + +EdgeRange::iterator EdgeRange::iterator::edgelist_end(const EdgeRange* parent) noexcept { + iterator it; + it.parent_ = parent; + it.idx_ = parent->edgelist_ptr_->size(); + return it; +} + +EdgeRange::iterator EdgeRange::iterator::csr_begin(const EdgeRange* parent) noexcept { + iterator it; + it.parent_ = parent; + it.u_ = 0; + it.off_ = 0; + it.init_csr_begin(); // finds first valid (u,off) or sets to end + it.load_current(); + return it; +} + +EdgeRange::iterator EdgeRange::iterator::csr_end(const EdgeRange* parent) noexcept { + iterator it; + it.parent_ = parent; + it.u_ = parent->xadj_ptr_->size() == 0 ? 0u : parent->xadj_ptr_->size() - 1; + it.off_ = parent->adjncy_ptr_->size(); + return it; +} + +std::size_t EdgeRange::iterator::edge_index() const noexcept { + if (parent_->representation_ == GraphRepresentation::EDGE_LIST) { + return idx_; + } else { + return off_; + } +} + +EdgeRange::iterator::value_type EdgeRange::iterator::operator*() const noexcept { + return cur_; +} + +EdgeRange::iterator& EdgeRange::iterator::operator++() { + assert(parent_ != nullptr); + + if (parent_->representation_ == GraphRepresentation::EDGE_LIST) { + ++idx_; + load_current(); + return *this; + } + + advance_to_next_valid_csr(); + load_current(); + return *this; +} + +EdgeRange::iterator EdgeRange::iterator::operator++(int) { + iterator tmp = *this; + ++(*this); + return tmp; +} + +bool EdgeRange::iterator::operator==(const iterator& other) const noexcept { + // must belong to same parent to compare reliably + if (parent_ != other.parent_) + return false; + if (parent_ == nullptr) + return true; // both default constructed? + if (parent_->representation_ != other.parent_->representation_) + return false; + if (parent_->representation_ == GraphRepresentation::EDGE_LIST) { + return idx_ == other.idx_; + } else { + return (u_ == other.u_) && (off_ == other.off_); + } +} + +bool EdgeRange::iterator::operator!=(const iterator& other) const noexcept { + return !(*this == other); +} + +void EdgeRange::iterator::load_current() noexcept { + if (parent_->representation_ == GraphRepresentation::EDGE_LIST) { + const auto& elist = *parent_->edgelist_ptr_; + if (idx_ < elist.size()) { + cur_ = elist[idx_]; + } + // else leave cur_ unspecified for end() + } else { + const auto& xadj = *parent_->xadj_ptr_; + const auto& adjncy = *parent_->adjncy_ptr_; + const std::size_t n_local = xadj.empty() ? 0 : (xadj.size() - 1); + if (u_ >= n_local || off_ >= adjncy.size()) { + // end iterator; cur_ remains unspecified + return; + } + Vertex u_global = static_cast(u_) + parent_->vertex_base_; + Vertex v_global = adjncy[off_]; + cur_.first = u_global; + cur_.second = v_global; + } +} + +void EdgeRange::iterator::init_csr_begin() noexcept { + const auto& xadj = *parent_->xadj_ptr_; + const auto& adjncy = *parent_->adjncy_ptr_; + const std::size_t n_local = xadj.empty() ? 0 : (xadj.size() - 1); + if (n_local == 0) { + // empty CSR: set end + u_ = 0; + off_ = 0; + return; + } + off_ = xadj[0]; + u_ = 0; + // skip vertices with empty adjacency ranges + while (u_ < n_local && off_ >= xadj[u_ + 1]) { + ++u_; + if (u_ < n_local) + off_ = xadj[u_]; + } + if (u_ >= n_local) { + // set to end state + u_ = n_local; + off_ = adjncy.size(); + } +} + +void EdgeRange::iterator::advance_to_next_valid_csr() noexcept { + // CSR: advance off_, move to next vertex if necessary + ++off_; + const auto& xadj = *parent_->xadj_ptr_; + const std::size_t n_local = xadj.empty() ? 0 : (xadj.size() - 1); + while (u_ < n_local && off_ >= xadj[u_ + 1]) { + ++u_; + if (u_ < n_local) + off_ = xadj[u_]; + } + if (u_ >= n_local) { + // set canonical end state + off_ = parent_->adjncy_ptr_->size(); + } +} + +EdgeRange::iterator EdgeRange::begin() const noexcept { + if (representation_ == GraphRepresentation::EDGE_LIST) + return iterator::edgelist_begin(this); + return iterator::csr_begin(this); +} + +EdgeRange::iterator EdgeRange::end() const noexcept { + if (representation_ == GraphRepresentation::EDGE_LIST) + return iterator::edgelist_end(this); + return iterator::csr_end(this); +} + +std::size_t EdgeRange::size() const noexcept { + if (representation_ == GraphRepresentation::EDGE_LIST) { + return edgelist_ptr_->size(); + } else { + return adjncy_ptr_->size(); + } +} + +} // namespace kagen diff --git a/kagen/edge_range.h b/kagen/edge_range.h new file mode 100644 index 0000000..1b92507 --- /dev/null +++ b/kagen/edge_range.h @@ -0,0 +1,81 @@ +#pragma once + +#include "kagen/kagen.h" + +#include +#include +#include + +namespace kagen { + +class EdgeRange { +public: + using Vertex = SInt; + using Edge = std::pair; + + explicit EdgeRange(const Edgelist& edgelist) noexcept; + EdgeRange(const XadjArray& xadj, const AdjncyArray& adjncy, VertexRange vertex_range) noexcept; + EdgeRange(const Graph& graph)noexcept; + + static EdgeRange FromGraph(const Graph& graph) noexcept; + + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = Edge; + using difference_type = std::ptrdiff_t; + + iterator() noexcept = default; + + static iterator edgelist_begin(const EdgeRange* parent) noexcept; + static iterator edgelist_end(const EdgeRange* parent) noexcept; + + // begin / end for CSR + static iterator csr_begin(const EdgeRange* parent) noexcept; + static iterator csr_end(const EdgeRange* parent) noexcept; + + std::size_t edge_index() const noexcept; + + value_type operator*() const noexcept; + + iterator& operator++(); + + iterator operator++(int); + + bool operator==(const iterator& other) const noexcept; + bool operator!=(const iterator& other) const noexcept; + + private: + const EdgeRange* parent_{nullptr}; + + // EDGELIST state + std::size_t idx_{0}; + + // CSR state + std::size_t u_{0}; // current vertex index (0..n_local-1) + std::size_t off_{0}; // current offset into adjncy + + Edge cur_{}; + + void load_current() noexcept; + void init_csr_begin() noexcept; + void advance_to_next_valid_csr() noexcept; + }; + + iterator begin() const noexcept; + iterator end() const noexcept; + + std::size_t size() const noexcept; + +private: + GraphRepresentation representation_; + + const Edgelist* edgelist_ptr_; + const XadjArray* xadj_ptr_; + const AdjncyArray* adjncy_ptr_; + Vertex vertex_base_; + + friend class iterator; +}; + +} // namespace kagen diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4dca900..3131939 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -74,3 +74,6 @@ kagen_add_test(test_permutation FILES permutation/permutation_test.cpp CORES 1 2 4) +kagen_add_test(test_edge_range + FILES edge_range.cpp + CORES 1) diff --git a/tests/edge_range.cpp b/tests/edge_range.cpp new file mode 100644 index 0000000..836c97e --- /dev/null +++ b/tests/edge_range.cpp @@ -0,0 +1,108 @@ +#include "kagen/edge_range.h" + +#include "kagen/kagen.h" + +#include + +#include "tests/gather.h" +#include "tests/utils.h" +#include "tools/converter.h" +#include "tools/geometry.h" + +using namespace kagen; + +void check_edge_range(const Graph& graph) { + Edgelist edgelist = graph.edges; + if (graph.representation == GraphRepresentation::CSR) { + edgelist = BuildEdgeListFromCSR(graph.vertex_range, graph.xadj, graph.adjncy); + } + EdgeRange edge_range(graph); + + { + std::size_t expected_index = 0; + for (auto it = edge_range.begin(); it != edge_range.end(); ++it) { + auto edge = *it; + EXPECT_EQ(it.edge_index(), expected_index); + EXPECT_EQ(*it, edge); + ++expected_index; + } + } + + { + EXPECT_EQ(edge_range.size(), edgelist.size()); + for (std::size_t i = 0; auto elem: edge_range) { + EXPECT_EQ(elem, edgelist[i]); + ++i; + } + } +} + +void check_edge_range(KaGen& generator, SInt n, SInt m) { + // GNM + { + kagen::Graph graph = generator.GenerateUndirectedGNM(n, m); + check_edge_range(graph); + } + // RMAT + { + kagen::Graph graph = generator.GenerateRMAT(n, m, 0.56, 0.19, 0.19); + check_edge_range(graph); + } + // RGG2D + { + kagen::Graph graph = generator.GenerateRGG2D_NM(n, m); + check_edge_range(graph); + } + // RGG3D + { + kagen::Graph graph = generator.GenerateRGG3D_NM(n, m); + check_edge_range(graph); + } + // RHG + { + kagen::Graph graph = generator.GenerateRHG_NM(2.6, n, m); + check_edge_range(graph); + } + // GRID2D + { + kagen::Graph graph = generator.GenerateGrid2D_NM(n, m); + check_edge_range(graph); + } + // GRID2D + { + kagen::Graph graph = generator.GenerateGrid3D_NM(n, m); + check_edge_range(graph); + } +} + +TEST(EdgeRangeTest, iterate_edgelist_representation) { + kagen::KaGen generator(MPI_COMM_WORLD); + generator.UseEdgeListRepresentation(); + const SInt n = 1000; + const SInt m = 16 * n; + check_edge_range(generator, n, m); +} + +TEST(EdgeRangeTest, iterate_sparse_edgelist_representation) { + kagen::KaGen generator(MPI_COMM_WORLD); + generator.UseEdgeListRepresentation(); + const SInt n = 1000; + const SInt m = 2 * n; + check_edge_range(generator, n, m); +} + +TEST(EdgeRangeTest, iterate_csr_representation) { + kagen::KaGen generator(MPI_COMM_WORLD); + generator.UseCSRRepresentation(); + const SInt n = 1000; + const SInt m = 16 * n; + check_edge_range(generator, n, m); +} + +TEST(EdgeRangeTest, iterate_sparse_csr_representation) { + kagen::KaGen generator(MPI_COMM_WORLD); + generator.UseCSRRepresentation(); + const SInt n = 1000; + const SInt m = 2 * n; + check_edge_range(generator, n, m); +} From 2e74eb19605628374a69a23523c2bdb978f8ed91 Mon Sep 17 00:00:00 2001 From: Matthias Schimek Date: Thu, 6 Nov 2025 08:53:29 +0100 Subject: [PATCH 2/5] Update tests/edge_range.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/edge_range.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/edge_range.cpp b/tests/edge_range.cpp index 836c97e..edc3f3e 100644 --- a/tests/edge_range.cpp +++ b/tests/edge_range.cpp @@ -7,7 +7,6 @@ #include "tests/gather.h" #include "tests/utils.h" #include "tools/converter.h" -#include "tools/geometry.h" using namespace kagen; From d913bbf69b18a75bd7ebcc9d51490599c55aa34c Mon Sep 17 00:00:00 2001 From: Matthias Schimek Date: Thu, 6 Nov 2025 08:54:00 +0100 Subject: [PATCH 3/5] Update tests/edge_range.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/edge_range.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/edge_range.cpp b/tests/edge_range.cpp index edc3f3e..c93b4b0 100644 --- a/tests/edge_range.cpp +++ b/tests/edge_range.cpp @@ -67,7 +67,7 @@ void check_edge_range(KaGen& generator, SInt n, SInt m) { kagen::Graph graph = generator.GenerateGrid2D_NM(n, m); check_edge_range(graph); } - // GRID2D + // GRID3D { kagen::Graph graph = generator.GenerateGrid3D_NM(n, m); check_edge_range(graph); From cb642e9bd03ac859374d256ae6d1c9e53acd6c9a Mon Sep 17 00:00:00 2001 From: Matthias Schimek Date: Thu, 6 Nov 2025 08:54:35 +0100 Subject: [PATCH 4/5] Update tests/CMakeLists.txt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3131939..baa40c4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -75,5 +75,5 @@ kagen_add_test(test_permutation CORES 1 2 4) kagen_add_test(test_edge_range - FILES edge_range.cpp + FILES edge_range.cpp CORES 1) From 9e7ae63a43f0b61dc306ee69c45899d2ca19ec3d Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:37:00 +0100 Subject: [PATCH 5/5] Refactor edge_range tests to use GoogleTest matchers and parameterized tests (#76) * Initial plan * Refactor edge_range test to use GoogleTest matchers Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Use matchers for edge index verification as well Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Remove check_edge_range helper and inline logic into tests Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Remove unnecessary CSR check from edgelist tests Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Convert tests to parameterized tests by generator type and remove always-true conditionals Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Use custom matcher for edge index check without allocating vector Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Revert custom matcher, use explicit loop for edge index check Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> * Use Pointwise matcher with EdgeIndexMatches for edge index verification Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: niklas-uhl <23433189+niklas-uhl@users.noreply.github.com> Co-authored-by: Tim Niklas Uhl --- tests/edge_range.cpp | 189 ++++++++++++++++++++++++++----------------- 1 file changed, 115 insertions(+), 74 deletions(-) diff --git a/tests/edge_range.cpp b/tests/edge_range.cpp index c93b4b0..4256f8e 100644 --- a/tests/edge_range.cpp +++ b/tests/edge_range.cpp @@ -2,106 +2,147 @@ #include "kagen/kagen.h" +#include #include +#include +#include +#include + #include "tests/gather.h" #include "tests/utils.h" #include "tools/converter.h" using namespace kagen; -void check_edge_range(const Graph& graph) { - Edgelist edgelist = graph.edges; - if (graph.representation == GraphRepresentation::CSR) { - edgelist = BuildEdgeListFromCSR(graph.vertex_range, graph.xadj, graph.adjncy); - } - EdgeRange edge_range(graph); +using GeneratorFunc = std::function; - { - std::size_t expected_index = 0; - for (auto it = edge_range.begin(); it != edge_range.end(); ++it) { - auto edge = *it; - EXPECT_EQ(it.edge_index(), expected_index); - EXPECT_EQ(*it, edge); - ++expected_index; - } - } - - { - EXPECT_EQ(edge_range.size(), edgelist.size()); - for (std::size_t i = 0; auto elem: edge_range) { - EXPECT_EQ(elem, edgelist[i]); - ++i; - } - } +MATCHER(EdgeIndexMatches, "") { + auto [iter, expected_idx] = arg; + return iter.edge_index() == expected_idx; } -void check_edge_range(KaGen& generator, SInt n, SInt m) { - // GNM - { - kagen::Graph graph = generator.GenerateUndirectedGNM(n, m); - check_edge_range(graph); - } - // RMAT - { - kagen::Graph graph = generator.GenerateRMAT(n, m, 0.56, 0.19, 0.19); - check_edge_range(graph); - } - // RGG2D - { - kagen::Graph graph = generator.GenerateRGG2D_NM(n, m); - check_edge_range(graph); - } - // RGG3D - { - kagen::Graph graph = generator.GenerateRGG3D_NM(n, m); - check_edge_range(graph); - } - // RHG - { - kagen::Graph graph = generator.GenerateRHG_NM(2.6, n, m); - check_edge_range(graph); - } - // GRID2D - { - kagen::Graph graph = generator.GenerateGrid2D_NM(n, m); - check_edge_range(graph); - } - // GRID3D - { - kagen::Graph graph = generator.GenerateGrid3D_NM(n, m); - check_edge_range(graph); - } -} +struct EdgeRangeTestFixture : public ::testing::TestWithParam> {}; -TEST(EdgeRangeTest, iterate_edgelist_representation) { - kagen::KaGen generator(MPI_COMM_WORLD); - generator.UseEdgeListRepresentation(); + +INSTANTIATE_TEST_SUITE_P( + EdgeRangeTests, EdgeRangeTestFixture, + ::testing::Values( + std::make_tuple("GNM", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateUndirectedGNM(n, m); })), + std::make_tuple("RMAT", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateRMAT(n, m, 0.56, 0.19, 0.19); })), + std::make_tuple("RGG2D", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateRGG2D_NM(n, m); })), + std::make_tuple("RGG3D", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateRGG3D_NM(n, m); })), + std::make_tuple("RHG", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateRHG_NM(2.6, n, m); })), + std::make_tuple("Grid2D", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateGrid2D_NM(n, m); })), + std::make_tuple("Grid3D", GeneratorFunc([](KaGen& gen, SInt n, SInt m) { return gen.GenerateGrid3D_NM(n, m); }))), + [](const ::testing::TestParamInfo& info) { + return std::get<0>(info.param); + }); + +TEST_P(EdgeRangeTestFixture, iterate_edgelist_representation) { + using ::testing::ElementsAreArray; + using ::testing::Pointwise; + + auto [name, generate] = GetParam(); const SInt n = 1000; const SInt m = 16 * n; - check_edge_range(generator, n, m); -} -TEST(EdgeRangeTest, iterate_sparse_edgelist_representation) { kagen::KaGen generator(MPI_COMM_WORLD); generator.UseEdgeListRepresentation(); + Graph graph = generate(generator, n, m); + + Edgelist expected = graph.edges; + EdgeRange edge_range(graph); + + // Check edges match and indices are consecutive + EXPECT_THAT(std::vector(edge_range.begin(), edge_range.end()), ElementsAreArray(expected)); + + std::vector iterators; + for (auto it = edge_range.begin(); it != edge_range.end(); ++it) { + iterators.push_back(it); + } + std::vector expected_indices(edge_range.size()); + std::iota(expected_indices.begin(), expected_indices.end(), 0); + EXPECT_THAT(iterators, Pointwise(EdgeIndexMatches(), expected_indices)); +} + +TEST_P(EdgeRangeTestFixture, iterate_sparse_edgelist_representation) { + using ::testing::ElementsAreArray; + using ::testing::Pointwise; + + auto [name, generate] = GetParam(); const SInt n = 1000; const SInt m = 2 * n; - check_edge_range(generator, n, m); -} -TEST(EdgeRangeTest, iterate_csr_representation) { kagen::KaGen generator(MPI_COMM_WORLD); - generator.UseCSRRepresentation(); + generator.UseEdgeListRepresentation(); + Graph graph = generate(generator, n, m); + + Edgelist expected = graph.edges; + EdgeRange edge_range(graph); + + // Check edges match and indices are consecutive + EXPECT_THAT(std::vector(edge_range.begin(), edge_range.end()), ElementsAreArray(expected)); + + std::vector iterators; + for (auto it = edge_range.begin(); it != edge_range.end(); ++it) { + iterators.push_back(it); + } + std::vector expected_indices(edge_range.size()); + std::iota(expected_indices.begin(), expected_indices.end(), 0); + EXPECT_THAT(iterators, Pointwise(EdgeIndexMatches(), expected_indices)); +} + +TEST_P(EdgeRangeTestFixture, iterate_csr_representation) { + using ::testing::ElementsAreArray; + using ::testing::Pointwise; + + auto [name, generate] = GetParam(); const SInt n = 1000; const SInt m = 16 * n; - check_edge_range(generator, n, m); -} -TEST(EdgeRangeTest, iterate_sparse_csr_representation) { kagen::KaGen generator(MPI_COMM_WORLD); generator.UseCSRRepresentation(); + Graph graph = generate(generator, n, m); + + Edgelist expected = BuildEdgeListFromCSR(graph.vertex_range, graph.xadj, graph.adjncy); + EdgeRange edge_range(graph); + + // Check edges match and indices are consecutive + EXPECT_THAT(std::vector(edge_range.begin(), edge_range.end()), ElementsAreArray(expected)); + + std::vector iterators; + for (auto it = edge_range.begin(); it != edge_range.end(); ++it) { + iterators.push_back(it); + } + std::vector expected_indices(edge_range.size()); + std::iota(expected_indices.begin(), expected_indices.end(), 0); + EXPECT_THAT(iterators, Pointwise(EdgeIndexMatches(), expected_indices)); +} + +TEST_P(EdgeRangeTestFixture, iterate_sparse_csr_representation) { + using ::testing::ElementsAreArray; + using ::testing::Pointwise; + + auto [name, generate] = GetParam(); const SInt n = 1000; const SInt m = 2 * n; - check_edge_range(generator, n, m); + + kagen::KaGen generator(MPI_COMM_WORLD); + generator.UseCSRRepresentation(); + Graph graph = generate(generator, n, m); + + Edgelist expected = BuildEdgeListFromCSR(graph.vertex_range, graph.xadj, graph.adjncy); + EdgeRange edge_range(graph); + + // Check edges match and indices are consecutive + EXPECT_THAT(std::vector(edge_range.begin(), edge_range.end()), ElementsAreArray(expected)); + + std::vector iterators; + for (auto it = edge_range.begin(); it != edge_range.end(); ++it) { + iterators.push_back(it); + } + std::vector expected_indices(edge_range.size()); + std::iota(expected_indices.begin(), expected_indices.end(), 0); + EXPECT_THAT(iterators, Pointwise(EdgeIndexMatches(), expected_indices)); }