Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/dsm/dsm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

static constexpr uint8_t DSM_VERSION_MAJOR = 2;
static constexpr uint8_t DSM_VERSION_MINOR = 3;
static constexpr uint8_t DSM_VERSION_PATCH = 20;
static constexpr uint8_t DSM_VERSION_PATCH = 21;

static auto const DSM_VERSION =
std::format("{}.{}.{}", DSM_VERSION_MAJOR, DSM_VERSION_MINOR, DSM_VERSION_PATCH);
Expand Down
95 changes: 95 additions & 0 deletions src/dsm/headers/AdjacencyMatrix.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

#pragma once

#include <algorithm>
#include <numeric>
#include <unordered_map>
#include <vector>

#include "../headers/Street.hpp"
#include "../utility/Typedef.hpp"

namespace dsm {

class AdjacencyMatrix;

namespace test {
std::vector<Id> offsets(const AdjacencyMatrix& adj);
std::vector<Id> indices(const AdjacencyMatrix& adj);
} // namespace test

class AdjacencyMatrix {
private:
std::vector<Id> m_rowOffsets;
std::vector<Id> m_columnIndices;
size_t m_nRows;
size_t m_nCols;

friend std::vector<Id> test::offsets(const AdjacencyMatrix& adj);
friend std::vector<Id> test::indices(const AdjacencyMatrix& adj);

public:
using value_type = Id;
using difference_type = std::ptrdiff_t;
using reference = Id&;
using const_reference = const Id&;
using iterator = std::vector<Id>::iterator;
using const_iterator = std::vector<Id>::const_iterator;
using reverse_iterator = std::vector<Id>::reverse_iterator;
using const_reverse_iterator = std::vector<Id>::const_reverse_iterator;

AdjacencyMatrix()
: m_rowOffsets{std::vector<Id>(2, 0)},
m_columnIndices{},
m_nRows{1},
m_nCols{0} {}
AdjacencyMatrix(const std::unordered_map<Id, std::unique_ptr<Street>>& streets)

Check warning

Code scanning / Cppcheck (reported by Codacy)

Class 'AdjacencyMatrix' has a constructor with 1 argument that is not explicit. Warning

Class 'AdjacencyMatrix' has a constructor with 1 argument that is not explicit.
: m_rowOffsets(streets.size()), m_nRows{0}, m_nCols{0} {
auto edgeFirst = streets.begin();
auto edgeLast = streets.end();
const auto size = streets.size();
std::vector<Id> rowSizes(size, 0);
std::vector<Id> colIndexes(size);
std::for_each(edgeFirst,
edgeLast,
[this, &rowSizes, &colIndexes, i = 0](const auto& pair) -> void {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule
auto row = pair.second->source();
rowSizes[row + 1]++;
if (row >= m_nRows)

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.6 rule Note

MISRA 15.6 rule
m_nRows = row + 1;
});
std::vector<Id> tempOffsets(m_nRows + 1, 0);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
tempOffsets[0] = 0;
std::inclusive_scan(
rowSizes.begin(), rowSizes.begin() + m_nRows + 1, tempOffsets.begin());

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
m_rowOffsets = tempOffsets;

std::for_each(edgeFirst,
edgeLast,
[this, &tempOffsets, &colIndexes, i = 0](const auto& pair) -> void {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule
auto row = pair.second->source();
auto col = pair.second->target();
if (col >= m_nCols)

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.6 rule Note

MISRA 15.6 rule
m_nCols = col + 1;
colIndexes[tempOffsets[row]] = col;
++tempOffsets[row];
});
/* m_columnIndices = std::move(colIndexes); */
m_columnIndices = std::move(colIndexes);
}

auto nRows() const { return m_nRows; }
auto nCols() const { return m_nCols; }

void insert(Id row, Id col);

bool contains(Id row, Id col);
bool operator()(Id row, Id col);

std::vector<Id> getRow(Id row);
std::vector<Id> getCol(Id col);

std::vector<int> getInDegreeVector();
std::vector<int> getOutDegreeVector();
};
} // namespace dsm
80 changes: 80 additions & 0 deletions src/dsm/sources/AdjacencyMatrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

#include "../headers/AdjacencyMatrix.hpp"
#include <iostream>

namespace dsm {

namespace test {
std::vector<Id> offsets(const AdjacencyMatrix& adj) { return adj.m_rowOffsets; }

std::vector<Id> indices(const AdjacencyMatrix& adj) { return adj.m_columnIndices; }
} // namespace test

void AdjacencyMatrix::insert(Id row, Id col) {
if (row > m_rowOffsets.size() - 2) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
for (auto i = 0ul; i < row - m_rowOffsets.size() + 2; ++i) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 7.3 rule Note

MISRA 7.3 rule
m_rowOffsets.push_back(m_rowOffsets.back() + 1);
}
m_nRows = row + 1;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
} else {
std::for_each(
m_rowOffsets.begin() + row + 1, m_rowOffsets.end(), [](Id& x) { x++; });

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.8 rule Note

MISRA 17.8 rule

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 13.1 rule Note

MISRA 13.1 rule
}

if (col >= m_nCols)

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.6 rule Note

MISRA 15.6 rule
m_nCols = col + 1;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
const auto offset = m_rowOffsets[row + 1] - 1;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
m_columnIndices.insert(m_columnIndices.begin() + offset, col);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
}

bool AdjacencyMatrix::contains(Id row, Id col) {
if (row >= m_nRows or col >= m_nCols) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
throw std::out_of_range("Row or column index out of range.");
}

iterator itFirst = m_columnIndices.begin() + m_rowOffsets[row];
iterator itLast = m_columnIndices.begin() + m_rowOffsets[row + 1];

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
return std::find(itFirst, itLast, col) != itLast;
}
bool AdjacencyMatrix::operator()(Id row, Id col) { return contains(row, col); }

std::vector<Id> AdjacencyMatrix::getRow(Id row) {
const auto lowerOffset = m_rowOffsets[row];
const auto upperOffset = m_rowOffsets[row + 1];

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
std::vector<Id> rowVector(upperOffset - lowerOffset);

std::copy(m_columnIndices.begin() + m_rowOffsets[row],
m_columnIndices.begin() + m_rowOffsets[row + 1],

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
rowVector.begin());
return rowVector;
}

Check warning on line 50 in src/dsm/sources/AdjacencyMatrix.cpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/sources/AdjacencyMatrix.cpp#L50

Added line #L50 was not covered by tests
std::vector<Id> AdjacencyMatrix::getCol(Id col) {
std::vector<Id> colVector{};
for (auto row = 0u; row < m_nRows; ++row) {
const auto lowerOffset = m_rowOffsets[row];
const auto upperOffset = m_rowOffsets[row + 1];

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
if (std::find(m_columnIndices.begin() + lowerOffset,
m_columnIndices.begin() + upperOffset,
col) != m_columnIndices.begin() + upperOffset) {
colVector.push_back(row);
}
}
return colVector;
}

Check warning on line 63 in src/dsm/sources/AdjacencyMatrix.cpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/sources/AdjacencyMatrix.cpp#L63

Added line #L63 was not covered by tests

std::vector<int> AdjacencyMatrix::getOutDegreeVector() {
auto degVector = std::vector<int>(m_nRows);
std::adjacent_difference(
m_rowOffsets.begin() + 1, m_rowOffsets.end(), degVector.begin());
return degVector;
}

std::vector<int> AdjacencyMatrix::getInDegreeVector() {
auto degVector = std::vector<int>(m_nCols, 0);
std::for_each(m_columnIndices.begin(),
m_columnIndices.end(),
[&degVector](const auto& x) { degVector[x]++; });
return degVector;
}

} // namespace dsm
212 changes: 212 additions & 0 deletions test/Test_AdjacencyMatrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@

#include "AdjacencyMatrix.hpp"
#include "Graph.hpp"

#include "doctest.h"

using namespace dsm;

TEST_CASE("Test default construction and insertion") {
AdjacencyMatrix adj;
adj.insert(0, 1);
auto offsets = test::offsets(adj);
auto indices = test::indices(adj);
CHECK(offsets.size() == 2);
CHECK(offsets[0] == 0);
CHECK(offsets[1] == 1);
CHECK(indices.size() == 1);
CHECK(indices[0] == 1);
CHECK(adj.nRows() == 1);
CHECK(adj.nCols() == 2);

adj.insert(1, 2);
adj.insert(1, 3);
adj.insert(2, 3);
adj.insert(3, 4);
offsets = test::offsets(adj);
indices = test::indices(adj);
CHECK(offsets.size() == 5);
CHECK(offsets[0] == 0);
CHECK(offsets[1] == 1);
CHECK(offsets[2] == 3);
CHECK(offsets[3] == 4);
CHECK(offsets[4] == 5);
CHECK(indices.size() == 5);
CHECK(indices[0] == 1);
CHECK(indices[1] == 2);
CHECK(indices[2] == 3);
CHECK(indices[3] == 3);
CHECK(indices[4] == 4);
CHECK(adj.nCols() == 5);
CHECK(adj.nRows() == 4);

adj.insert(0, 0);
offsets = test::offsets(adj);
indices = test::indices(adj);
CHECK(offsets.size() == 5);
CHECK(offsets[0] == 0);
CHECK(offsets[1] == 2);
CHECK(offsets[2] == 4);
CHECK(offsets[3] == 5);
CHECK(offsets[4] == 6);
CHECK(indices.size() == 6);
CHECK(indices[0] == 1);
CHECK(indices[1] == 0);
CHECK(indices[2] == 2);
CHECK(indices[3] == 3);
CHECK(indices[4] == 3);
CHECK(indices[5] == 4);
CHECK(adj.nCols() == 5);
CHECK(adj.nRows() == 4);

SUBCASE("Test contains") {
CHECK(adj(0, 1));
CHECK(adj(1, 2));
CHECK(adj(1, 3));
CHECK(adj(2, 3));
CHECK(adj(3, 4));
CHECK_FALSE(adj(0, 2));
CHECK_FALSE(adj(2, 0));
CHECK_FALSE(adj(3, 3));
CHECK_THROWS(adj(5, 0));
CHECK_THROWS(adj(10, 0));
CHECK_THROWS(adj(0, 5));
CHECK_THROWS(adj(0, 10));
}
SUBCASE("Test getCol") {
auto col0 = adj.getCol(0);
CHECK(col0.size() == 1);
CHECK(col0[0] == 0);
auto col1 = adj.getCol(1);
CHECK(col1.size() == 1);
CHECK(col1[0] == 0);
auto col2 = adj.getCol(2);
CHECK(col2.size() == 1);
CHECK(col2[0] == 1);
auto col3 = adj.getCol(3);
CHECK(col3.size() == 2);
CHECK(col3[0] == 1);
CHECK(col3[1] == 2);
}
SUBCASE("Test getRow") {
auto row0 = adj.getRow(0);
CHECK(row0.size() == 2);
CHECK(row0[0] == 1);
CHECK(row0[1] == 0);
auto row1 = adj.getRow(1);
CHECK(row1.size() == 2);
CHECK(row1[0] == 2);
CHECK(row1[1] == 3);
auto row2 = adj.getRow(2);
CHECK(row2.size() == 1);
CHECK(row2[0] == 3);
auto row3 = adj.getRow(3);
CHECK(row3.size() == 1);
CHECK(row3[0] == 4);
}
SUBCASE("Test getInDegreeVector") {
auto inDegreeVector = adj.getInDegreeVector();
CHECK(inDegreeVector.size() == 5);
CHECK(inDegreeVector[0] == 1);
CHECK(inDegreeVector[1] == 1);
CHECK(inDegreeVector[2] == 1);
CHECK(inDegreeVector[3] == 2);
CHECK(inDegreeVector[4] == 1);
}
SUBCASE("Test getOutDegreeVector") {
auto outDegreeVector = adj.getOutDegreeVector();
CHECK(outDegreeVector.size() == 4);
CHECK(outDegreeVector[0] == 2);
CHECK(outDegreeVector[1] == 2);
CHECK(outDegreeVector[2] == 1);
CHECK(outDegreeVector[3] == 1);
}
}

TEST_CASE("Test construction from edge map") {
Graph g;
g.addEdge<Street>(0, std::make_pair<Id, Id>(0, 1));

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
g.addEdge<Street>(1, std::make_pair<Id, Id>(1, 2));

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
g.addEdge<Street>(2, std::make_pair<Id, Id>(1, 3));

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
g.addEdge<Street>(3, std::make_pair<Id, Id>(2, 3));

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
g.addEdge<Street>(4, std::make_pair<Id, Id>(3, 4));

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
AdjacencyMatrix adj(g.streetSet());

auto offsets = test::offsets(adj);
auto indices = test::indices(adj);
CHECK(offsets.size() == 5);
CHECK(offsets[0] == 0);
CHECK(offsets[1] == 1);
CHECK(offsets[2] == 3);
CHECK(offsets[3] == 4);
CHECK(offsets[4] == 5);
CHECK(indices.size() == 5);
CHECK(indices[0] == 1);
CHECK(indices[1] == 3);
CHECK(indices[2] == 2);
CHECK(indices[3] == 3);
CHECK(indices[4] == 4);
CHECK(adj.nCols() == 5);
CHECK(adj.nRows() == 4);

SUBCASE("Test contains") {
CHECK(adj(0, 1));
CHECK(adj(1, 2));
CHECK(adj(1, 3));
CHECK(adj(2, 3));
CHECK(adj(3, 4));
CHECK_FALSE(adj(0, 2));
CHECK_FALSE(adj(2, 0));
CHECK_FALSE(adj(3, 3));
CHECK_THROWS(adj(5, 0));
CHECK_THROWS(adj(10, 0));
CHECK_THROWS(adj(0, 5));
CHECK_THROWS(adj(0, 10));
}
SUBCASE("Test getCol") {
auto col0 = adj.getCol(0);
CHECK(col0.size() == 0);
auto col1 = adj.getCol(1);
CHECK(col1.size() == 1);
CHECK(col1[0] == 0);
auto col2 = adj.getCol(2);
CHECK(col2.size() == 1);
CHECK(col2[0] == 1);
auto col3 = adj.getCol(3);
CHECK(col3.size() == 2);
CHECK(col3[0] == 1);
CHECK(col3[1] == 2);
}
SUBCASE("Test getRow") {
auto row0 = adj.getRow(0);
CHECK(row0.size() == 1);
CHECK(row0[0] == 1);
auto row1 = adj.getRow(1);
CHECK(row1.size() == 2);
CHECK(row1[0] == 3);
CHECK(row1[1] == 2);
auto row2 = adj.getRow(2);
CHECK(row2.size() == 1);
CHECK(row2[0] == 3);
auto row3 = adj.getRow(3);
CHECK(row3.size() == 1);
CHECK(row3[0] == 4);
}
SUBCASE("Test getInDegreeVector") {
auto inDegreeVector = adj.getInDegreeVector();
CHECK(inDegreeVector.size() == 5);
CHECK(inDegreeVector[0] == 0);
CHECK(inDegreeVector[1] == 1);
CHECK(inDegreeVector[2] == 1);
CHECK(inDegreeVector[3] == 2);
CHECK(inDegreeVector[4] == 1);
}
SUBCASE("Test getOutDegreeVector") {
auto outDegreeVector = adj.getOutDegreeVector();
CHECK(outDegreeVector.size() == 4);
CHECK(outDegreeVector[0] == 1);
CHECK(outDegreeVector[1] == 2);
CHECK(outDegreeVector[2] == 1);
CHECK(outDegreeVector[3] == 1);
}
}
Loading