From b71d904770d15bb13764c362c2dbdd47adf150f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Tue, 17 Jun 2025 20:50:11 +0200 Subject: [PATCH] Refactor Graph class into a template to remove LabelGraph and WeightGraph --- include/aoc/LanParty.hpp | 7 +- include/aoc/RamRun.hpp | 13 +- include/aoc/ReindeerMaze.hpp | 8 +- .../common/{WeightGraph.hpp => Dijkstra.hpp} | 33 +++-- include/aoc/common/Graph.hpp | 123 +++++++----------- include/aoc/common/GraphBase.hpp | 111 ++++++++++++++++ include/aoc/common/GraphPathsResult.hpp | 33 ----- include/aoc/common/LabelGraph.hpp | 38 ------ src/LanParty.cpp | 18 +-- src/RamRun.cpp | 23 ++-- src/ReindeerMaze.cpp | 22 ++-- src/common/{WeightGraph.cpp => Dijkstra.cpp} | 45 +------ src/common/{Graph.cpp => GraphBase.cpp} | 50 +++---- src/common/LabelGraph.cpp | 53 -------- 14 files changed, 256 insertions(+), 321 deletions(-) rename include/aoc/common/{WeightGraph.hpp => Dijkstra.hpp} (58%) create mode 100644 include/aoc/common/GraphBase.hpp delete mode 100644 include/aoc/common/GraphPathsResult.hpp delete mode 100644 include/aoc/common/LabelGraph.hpp rename src/common/{WeightGraph.cpp => Dijkstra.cpp} (58%) rename src/common/{Graph.cpp => GraphBase.cpp} (50%) delete mode 100644 src/common/LabelGraph.cpp diff --git a/include/aoc/LanParty.hpp b/include/aoc/LanParty.hpp index 94ac1ab..a3abd01 100644 --- a/include/aoc/LanParty.hpp +++ b/include/aoc/LanParty.hpp @@ -15,7 +15,9 @@ #pragma once -#include +#include + +#include #include class LanParty @@ -29,7 +31,8 @@ class LanParty private: static constexpr std::string getFirstTxComputerName(); static constexpr std::string getLastTxComputerName(); - LabelGraph lan_; + Graph lan_{}; + std::map labelMap_{}; int findOrAddVertex(const std::string& vertexId); void computeInterconnectedThreeSetCount(const int vertexTx); bool canProcessVertex(const int vertexToCheck, const int vertexTx) const; diff --git a/include/aoc/RamRun.hpp b/include/aoc/RamRun.hpp index 2817109..7ee0a8f 100644 --- a/include/aoc/RamRun.hpp +++ b/include/aoc/RamRun.hpp @@ -15,9 +15,11 @@ #pragma once -#include +#include + +#include +#include #include -#include #include class RamRun @@ -34,11 +36,12 @@ class RamRun int maxBytes_; int nBytes_; Grid vertexReferences_; - GraphPathsResult dijkstraResult_; + std::function edgeWeightFunctor_; + Dijkstra::PathsResult dijkstraResult_; static constexpr char getUnknownVertexReference(); static constexpr char getNoVertexReference(); bool tryMarkCorrupted(const std::string& line, int& corruptedVertex); - WeightGraph buildGraph(); - int getVertex(WeightGraph& graph, const size_t x, const size_t y); + Graph buildGraph(); + int getVertex(Graph& graph, const size_t x, const size_t y); void resetVertexReferences(); }; diff --git a/include/aoc/ReindeerMaze.hpp b/include/aoc/ReindeerMaze.hpp index 69d0e60..18ee713 100644 --- a/include/aoc/ReindeerMaze.hpp +++ b/include/aoc/ReindeerMaze.hpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include @@ -39,15 +39,15 @@ class ReindeerMaze static constexpr char getEndChar(); static constexpr char getWallChar(); static constexpr int getTurnCost(); - void buildPathSegmentGraph(WeightGraph& graph, VertexAttachedPositions& vertexAttachedPositions, + void buildPathSegmentGraph(Graph& graph, VertexAttachedPositions& vertexAttachedPositions, const int entry, const int exit); void initializeWorkList(std::list& crossings, const int entryVertex); void addCheckedIncidence(std::vector& incidences, const Point2 start, const Point2 direction); - void addPathSegmentEdges(WeightGraph& graph, const ReindeerMazePathIncidence& pathIncidence, + void addPathSegmentEdges(Graph& graph, const ReindeerMazePathIncidence& pathIncidence, const std::vector& otherPathIncidences); std::pair makePositionsIdPair(const Point2& position1, const Point2& position2); - int calcShortestPaths(const WeightGraph& graph, const VertexAttachedPositions& vertexAttachedPositions, + int calcShortestPaths(const Graph& graph, const VertexAttachedPositions& vertexAttachedPositions, const int entry, const int exit, const std::vector& shortestDistances); int getNUniqueAttachedPositions(const VertexAttachedPositions& vertexAttachedPositions, const std::set& vertices); diff --git a/include/aoc/common/WeightGraph.hpp b/include/aoc/common/Dijkstra.hpp similarity index 58% rename from include/aoc/common/WeightGraph.hpp rename to include/aoc/common/Dijkstra.hpp index e0acd0d..4a92779 100644 --- a/include/aoc/common/WeightGraph.hpp +++ b/include/aoc/common/Dijkstra.hpp @@ -15,24 +15,29 @@ #pragma once +#include #include -#include -#include +#include -class WeightGraph +class Dijkstra { public: static constexpr int getInfiniteDistance(); - int addVertex(const int weight); - void addEdge(const int vertex1, const int vertex2, const int weight); - int getVertexWeight(const int vertex) const; - int getEdgeWeight(const int edge) const; - GraphPathsResult dijkstra(const int source) const; - Graph::NeighborIterator begin(const int vertex) const; - Graph::NeighborIterator end() const; -private: - Graph graph_{}; - std::vector edgeWeights_{}; - std::vector vertexWeights_{}; + + class PathsResult + { + public: + PathsResult() : distances{}, predecessors{} + { + } + PathsResult(const size_t size, const int initialDistance, const int initialPredecessor) + : distances(size, initialDistance), predecessors(size, initialPredecessor) + { + } + std::vector distances; + std::vector predecessors; + }; + + static PathsResult run(const GraphBase& graph, const std::function& edgeWeightFunctor, const int source); }; diff --git a/include/aoc/common/Graph.hpp b/include/aoc/common/Graph.hpp index 96df9a0..050aad5 100644 --- a/include/aoc/common/Graph.hpp +++ b/include/aoc/common/Graph.hpp @@ -17,93 +17,58 @@ #include #include +#include +#include + +template class Graph + : public GraphBase { public: - int addVertex(); - void addEdge(const int vertex1, const int vertex2); - size_t getNVertices() const; - size_t getNEdges() const; - bool areAdjacent(const int vertex1, const int vertex2) const; - - struct Incidence + int addVertex() + requires(std::is_void_v) { - public: - Incidence(const int vertex, const int edge) - : vertex{ vertex }, edge{ edge } - { - } - int vertex; - int edge; - }; + return addVertexInternal(); + } - struct NeighborIterator + template + requires(!std::is_void_v) + int addVertex(const T& data) { - public: - // Iterator traits. - using difference_type = std::ptrdiff_t; - using value_type = Incidence; - using pointer = const value_type*; - using reference = const value_type&; - using iterator_category = std::forward_iterator_tag; + vertexData_.push_back(data); + return addVertexInternal(); + } - NeighborIterator(const Graph& graph, const int vertex) - : graph_{ graph }, incidence_{ -1 }, value_{ -1, -1 } - { - if (vertex >= 0 && vertex < graph_.firstVertexIncidences_.size()) - { - setIncidence(graph_.firstVertexIncidences_[vertex]); - } - }; - NeighborIterator& operator++() - { - if (incidence_ >= 0) - { - setIncidence(graph_.nextIncidences_[incidence_]); - } - return *this; - } - NeighborIterator operator++(int) - { - NeighborIterator it = *this; - ++(*this); - return it; - } - bool operator==(NeighborIterator other) const - { - return incidence_ == other.incidence_; - } - bool operator!=(NeighborIterator other) const - { - return !(*this == other); - } - const reference operator*() const - { - return value_; - } - const pointer operator->() const - { - return &value_; - } - private: - const Graph& graph_; - int incidence_; - value_type value_; - void setIncidence(const int incidence) - { - incidence_ = incidence; - if (incidence_ >= 0) - { - value_ = { graph_.incidenceVertices_[incidence_], incidence_ >> 1 }; - } - } - }; + void addEdge(const int vertex1, const int vertex2) + requires(std::is_void_v) + { + addEdgeInternal(vertex1, vertex2); + } + + template + requires(!std::is_void_v) + void addEdge(const int vertex1, const int vertex2, const T& data) + { + edgeData_.push_back(data); + addEdgeInternal(vertex1, vertex2); + } + + template + requires(!std::is_void_v) + const T& getVertexData(const int vertex) const + { + return vertexData_[vertex]; + } + + template + requires(!std::is_void_v) + const T& getEdgeData(int edge) const + { + return edgeData_[edge]; + } - NeighborIterator begin(const int vertex) const; - NeighborIterator end() const; private: - std::vector firstVertexIncidences_{}; - std::vector nextIncidences_{}; - std::vector incidenceVertices_{}; + std::conditional_t, char, std::vector> vertexData_{}; + std::conditional_t, char, std::vector> edgeData_{}; }; diff --git a/include/aoc/common/GraphBase.hpp b/include/aoc/common/GraphBase.hpp new file mode 100644 index 0000000..3686bd5 --- /dev/null +++ b/include/aoc/common/GraphBase.hpp @@ -0,0 +1,111 @@ +// Solutions to the Advent Of Code 2024. +// Copyright (C) 2025 Stefan Müller +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . + +#pragma once + +#include +#include + +class GraphBase +{ +public: + size_t getNVertices() const; + size_t getNEdges() const; + bool areAdjacent(const int vertex1, const int vertex2) const; + + struct Incidence + { + public: + Incidence(const int vertex, const int edge) + : vertex{ vertex }, edge{ edge } + { + } + int vertex; + int edge; + }; + + struct NeighborIterator + { + public: + // Iterator traits. + using difference_type = std::ptrdiff_t; + using value_type = Incidence; + using pointer = const value_type*; + using reference = const value_type&; + using iterator_category = std::forward_iterator_tag; + + NeighborIterator(const GraphBase& graph, const int vertex) + : graph_{ graph }, incidence_{ -1 }, value_{ -1, -1 } + { + if (vertex >= 0 && vertex < graph_.firstVertexIncidences.size()) + { + setIncidence(graph_.firstVertexIncidences[vertex]); + } + }; + NeighborIterator& operator++() + { + if (incidence_ >= 0) + { + setIncidence(graph_.nextIncidences[incidence_]); + } + return *this; + } + NeighborIterator operator++(int) + { + NeighborIterator it = *this; + ++(*this); + return it; + } + bool operator==(NeighborIterator other) const + { + return incidence_ == other.incidence_; + } + bool operator!=(NeighborIterator other) const + { + return !(*this == other); + } + const reference operator*() const + { + return value_; + } + const pointer operator->() const + { + return &value_; + } + + private: + const GraphBase& graph_; + int incidence_; + value_type value_; + void setIncidence(const int incidence) + { + incidence_ = incidence; + if (incidence_ >= 0) + { + value_ = { graph_.incidenceVertices[incidence_], incidence_ >> 1 }; + } + } + }; + + NeighborIterator begin(const int vertex) const; + NeighborIterator end() const; + +protected: + std::vector firstVertexIncidences{}; + std::vector nextIncidences{}; + std::vector incidenceVertices{}; + int addVertexInternal(); + void addEdgeInternal(const int vertex1, const int vertex2); +}; diff --git a/include/aoc/common/GraphPathsResult.hpp b/include/aoc/common/GraphPathsResult.hpp deleted file mode 100644 index 75af80d..0000000 --- a/include/aoc/common/GraphPathsResult.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Solutions to the Advent Of Code 2024. -// Copyright (C) 2025 Stefan Müller -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation, either version 3 of the License, or (at your option) any later -// version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see . - -#pragma once - -#include - -class GraphPathsResult -{ -public: - GraphPathsResult() - : distances{}, predecessors{} - { - } - GraphPathsResult(const size_t size, const int initialDistance, const int initialPredecessor) - : distances(size, initialDistance), predecessors(size, initialPredecessor) - { - } - std::vector distances; - std::vector predecessors; -}; diff --git a/include/aoc/common/LabelGraph.hpp b/include/aoc/common/LabelGraph.hpp deleted file mode 100644 index 493b106..0000000 --- a/include/aoc/common/LabelGraph.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Solutions to the Advent Of Code 2024. -// Copyright (C) 2025 Stefan Müller -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation, either version 3 of the License, or (at your option) any later -// version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see . - -#pragma once - -#include -#include -#include - -#include - -class LabelGraph -{ -public: - int addVertex(const std::string& label); - void addEdge(const int vertex1, const int vertex2); - bool areAdjacent(const int vertex1, const int vertex2) const; - const std::string& getVertexLabel(const int vertex) const; - const std::map& getLabelVertices() const; - Graph::NeighborIterator begin(const int vertex) const; - Graph::NeighborIterator end() const; -private: - Graph graph_{}; - std::vector vertexLabels_{}; - std::map labelVertices_{}; -}; diff --git a/src/LanParty.cpp b/src/LanParty.cpp index fcb1034..7eb013e 100644 --- a/src/LanParty.cpp +++ b/src/LanParty.cpp @@ -32,9 +32,8 @@ void LanParty::processDataLine(const std::string& line) void LanParty::finish() { - const auto& vertices = lan_.getLabelVertices(); - auto itTx = vertices.lower_bound(getFirstTxComputerName()); - const auto itTxEnd = vertices.upper_bound(getLastTxComputerName()); + auto itTx = labelMap_.lower_bound(getFirstTxComputerName()); + const auto itTxEnd = labelMap_.upper_bound(getLastTxComputerName()); while (itTx != itTxEnd) { computeInterconnectedThreeSetCount(itTx->second); @@ -54,15 +53,16 @@ constexpr std::string LanParty::getLastTxComputerName() int LanParty::findOrAddVertex(const std::string& vertexId) { - const auto& vertices = lan_.getLabelVertices(); - const auto found = vertices.find(vertexId); - if (found != vertices.end()) + const auto found = labelMap_.find(vertexId); + if (found != labelMap_.end()) { return found->second; } else { - return lan_.addVertex(vertexId); + int vertex = lan_.addVertex(vertexId); + labelMap_.insert({ vertexId, vertex }); + return vertex; } } @@ -93,6 +93,6 @@ void LanParty::computeInterconnectedThreeSetCount(const int vertexTx) bool LanParty::canProcessVertex(const int vertexToCheck, const int vertexTx) const { - const std::string& label{ lan_.getVertexLabel(vertexToCheck) }; - return (label[0] != 't' || label > lan_.getVertexLabel(vertexTx)); + const std::string& label{ lan_.getVertexData(vertexToCheck) }; + return (label[0] != 't' || label > lan_.getVertexData(vertexTx)); } diff --git a/src/RamRun.cpp b/src/RamRun.cpp index 87aa50a..4949dc5 100644 --- a/src/RamRun.cpp +++ b/src/RamRun.cpp @@ -18,7 +18,8 @@ #include RamRun::RamRun(const size_t memorySize, const int maxBytes) - : memorySize_{ memorySize }, maxBytes_{ maxBytes }, nBytes_{ 0 }, vertexReferences_{ memorySize, memorySize } + : memorySize_{ memorySize }, maxBytes_{ maxBytes }, nBytes_{ 0 }, vertexReferences_{ memorySize, memorySize }, + edgeWeightFunctor_{ [](int x) { return 1; } }, dijkstraResult_{} { vertexReferences_.fill(getUnknownVertexReference()); } @@ -47,7 +48,7 @@ void RamRun::processDataLine(const std::string& line) { // Calculates initial distances. auto graph = buildGraph(); - dijkstraResult_ = graph.dijkstra(vertexReferences_[0][0]); + dijkstraResult_ = Dijkstra::run(graph, edgeWeightFunctor_, vertexReferences_[0][0]); part1 = dijkstraResult_.distances[vertexReferences_[memorySize_ - 1][memorySize_ - 1]]; } else if (maxBytes_ < nBytes_) @@ -63,9 +64,9 @@ void RamRun::processDataLine(const std::string& line) // is now blocked. resetVertexReferences(); auto graph = buildGraph(); - dijkstraResult_ = graph.dijkstra(vertexReferences_[0][0]); - if (dijkstraResult_.distances[vertexReferences_[memorySize_ - 1][memorySize_ - 1]] - == WeightGraph::getInfiniteDistance()) + dijkstraResult_ = Dijkstra::run(graph, edgeWeightFunctor_, vertexReferences_[0][0]); + if (dijkstraResult_.distances[static_cast(vertexReferences_[memorySize_ - 1][memorySize_ - 1])] + == Dijkstra::getInfiniteDistance()) { // Path is blocked. part2 = line; @@ -105,9 +106,9 @@ bool RamRun::tryMarkCorrupted(const std::string& line, int& corruptedVertex) return false; } -WeightGraph RamRun::buildGraph() +Graph RamRun::buildGraph() { - WeightGraph graph; + Graph graph; for (size_t j = 0; j < vertexReferences_.getNRows(); j++) { for (size_t i = 0; i < vertexReferences_.getNColumns(); i++) @@ -119,14 +120,14 @@ WeightGraph RamRun::buildGraph() { if (vertexReferences_[j][i - 1] != getNoVertexReference()) { - graph.addEdge(getVertex(graph, i - 1, j), v, 1); + graph.addEdge(getVertex(graph, i - 1, j), v); } } if (j != 0) { if (vertexReferences_[j - 1][i] != getNoVertexReference()) { - graph.addEdge(getVertex(graph, i, j - 1), v, 1); + graph.addEdge(getVertex(graph, i, j - 1), v); } } } @@ -135,11 +136,11 @@ WeightGraph RamRun::buildGraph() return graph; } -int RamRun::getVertex(WeightGraph& graph, const size_t x, const size_t y) +int RamRun::getVertex(Graph& graph, const size_t x, const size_t y) { if (vertexReferences_[y][x] == getUnknownVertexReference()) { - vertexReferences_[y][x] = graph.addVertex(0); + vertexReferences_[y][x] = graph.addVertex(); } return vertexReferences_[y][x]; } diff --git a/src/ReindeerMaze.cpp b/src/ReindeerMaze.cpp index 9b5c6fe..42001d6 100644 --- a/src/ReindeerMaze.cpp +++ b/src/ReindeerMaze.cpp @@ -13,10 +13,12 @@ // You should have received a copy of the GNU General Public License along with // this program. If not, see . +#include + #include #include -#include +#include ReindeerMaze::ReindeerMaze(const int inputFileNameSuffix) : LinesSolver{ inputFileNameSuffix } @@ -40,12 +42,12 @@ void ReindeerMaze::finish() // do not want to hardcode this. VertexAttachedPositions vertexAttachedPositions; - WeightGraph graph{}; + Graph graph{}; auto entry = graph.addVertex(0); auto exit = graph.addVertex(0); buildPathSegmentGraph(graph, vertexAttachedPositions, entry, exit); - auto result = graph.dijkstra(exit); + auto result = Dijkstra::run(graph, [&graph](int edge) { return graph.getEdgeData(edge); }, exit); part1 = result.distances[entry] / 2; part2 = calcShortestPaths(graph, vertexAttachedPositions, entry, exit, result.distances); } @@ -72,7 +74,7 @@ constexpr int ReindeerMaze::getTurnCost() // Constructs the graph of path segment incidences, starting with a graph that already contains the entry and the exit // vertices. -void ReindeerMaze::buildPathSegmentGraph(WeightGraph& graph, VertexAttachedPositions& vertexAttachedPositions, +void ReindeerMaze::buildPathSegmentGraph(Graph& graph, VertexAttachedPositions& vertexAttachedPositions, const int entry, const int exit) { // Uses list for work items to prevent invalidation of iterators on add. @@ -243,7 +245,7 @@ void ReindeerMaze::addCheckedIncidence(std::vector& i } } -void ReindeerMaze::addPathSegmentEdges(WeightGraph& graph, const ReindeerMazePathIncidence& pathIncidence, +void ReindeerMaze::addPathSegmentEdges(Graph& graph, const ReindeerMazePathIncidence& pathIncidence, const std::vector& otherPathIncidences) { for (auto& otherIncidence : otherPathIncidences) @@ -267,13 +269,13 @@ std::pair ReindeerMaze::makePositionsIdPair(const Point2& position1, c return std::make_pair(position1.x * offset + position1.y, position2.x * offset + position2.y); } -int ReindeerMaze::calcShortestPaths(const WeightGraph& graph, +int ReindeerMaze::calcShortestPaths(const Graph& graph, const VertexAttachedPositions& vertexAttachedPositions, const int entry, const int exit, const std::vector& shortestDistances) { std::set shortestPathsVertices{}; - std::stack stack{}; + std::stack::NeighborIterator> stack{}; auto v = graph.begin(entry); stack.emplace(v); int cost{ 0 }; @@ -286,7 +288,7 @@ int ReindeerMaze::calcShortestPaths(const WeightGraph& graph, auto& current = stack.top(); int newCost{ 0 }; while (current != graph.end() && - ((newCost = graph.getEdgeWeight(current->edge) + cost) + shortestDistances[current->vertex] > + ((newCost = graph.getEdgeData(current->edge) + cost) + shortestDistances[current->vertex] > shortestDistances[entry] || stackedVertices.contains(current->vertex))) { @@ -320,7 +322,7 @@ int ReindeerMaze::calcShortestPaths(const WeightGraph& graph, if (!stack.empty()) { stackedVertices.erase(stack.top()->vertex); - cost -= graph.getEdgeWeight(stack.top()->edge); + cost -= graph.getEdgeData(stack.top()->edge); stack.top()++; } } @@ -330,7 +332,7 @@ int ReindeerMaze::calcShortestPaths(const WeightGraph& graph, shortestPathsVertices.erase(entry); return std::accumulate(shortestPathsVertices.begin(), shortestPathsVertices.end(), getNUniqueAttachedPositions(vertexAttachedPositions, shortestPathsVertices), - [&graph](int total, int x) { return total + graph.getVertexWeight(x); }); + [&graph](int total, int x) { return total + graph.getVertexData(x); }); } int ReindeerMaze::getNUniqueAttachedPositions(const VertexAttachedPositions& vertexAttachedPositions, diff --git a/src/common/WeightGraph.cpp b/src/common/Dijkstra.cpp similarity index 58% rename from src/common/WeightGraph.cpp rename to src/common/Dijkstra.cpp index 5316204..11b9133 100644 --- a/src/common/WeightGraph.cpp +++ b/src/common/Dijkstra.cpp @@ -13,41 +13,20 @@ // You should have received a copy of the GNU General Public License along with // this program. If not, see . -#include +#include #include #include -constexpr int WeightGraph::getInfiniteDistance() +constexpr int Dijkstra::getInfiniteDistance() { return std::numeric_limits::max(); } -int WeightGraph::addVertex(const int weight) +Dijkstra::PathsResult Dijkstra::run(const GraphBase& graph, const std::function& edgeWeightFunctor, + const int source) { - vertexWeights_.push_back(weight); - return graph_.addVertex(); -} - -void WeightGraph::addEdge(const int vertex1, const int vertex2, const int weight) -{ - edgeWeights_.push_back(weight); - graph_.addEdge(vertex1, vertex2); -} - -int WeightGraph::getVertexWeight(const int vertex) const -{ - return vertexWeights_[vertex]; -} - -int WeightGraph::getEdgeWeight(const int edge) const -{ - return edgeWeights_[edge]; -} - -GraphPathsResult WeightGraph::dijkstra(const int source) const -{ - GraphPathsResult result(graph_.getNVertices(), getInfiniteDistance(), -1); + PathsResult result{ graph.getNVertices(), getInfiniteDistance(), -1 }; auto compare = [&result](int left, int right) { return result.distances[left] > result.distances[right]; }; std::priority_queue, decltype(compare)> queue{ compare }; @@ -59,9 +38,9 @@ GraphPathsResult WeightGraph::dijkstra(const int source) const int v{ queue.top() }; queue.pop(); - for (auto neighbor = begin(v); neighbor != end(); ++neighbor) + for (auto neighbor = graph.begin(v); neighbor != graph.end(); ++neighbor) { - int newDistance{ result.distances[v] + edgeWeights_[neighbor->edge] }; + int newDistance{ result.distances[v] + edgeWeightFunctor(neighbor->edge) }; if (result.distances[neighbor->vertex] > newDistance) { result.distances[neighbor->vertex] = newDistance; @@ -73,13 +52,3 @@ GraphPathsResult WeightGraph::dijkstra(const int source) const return result; } - -Graph::NeighborIterator WeightGraph::begin(const int vertex) const -{ - return graph_.begin(vertex); -} - -Graph::NeighborIterator WeightGraph::end() const -{ - return graph_.end(); -} diff --git a/src/common/Graph.cpp b/src/common/GraphBase.cpp similarity index 50% rename from src/common/Graph.cpp rename to src/common/GraphBase.cpp index bb380eb..702a9a0 100644 --- a/src/common/Graph.cpp +++ b/src/common/GraphBase.cpp @@ -13,36 +13,19 @@ // You should have received a copy of the GNU General Public License along with // this program. If not, see . -#include +#include -int Graph::addVertex() +size_t GraphBase::getNVertices() const { - firstVertexIncidences_.push_back(-1); - return static_cast(firstVertexIncidences_.size()) - 1; + return firstVertexIncidences.size(); } -void Graph::addEdge(const int vertex1, const int vertex2) +size_t GraphBase::getNEdges() const { - incidenceVertices_.push_back(vertex2); - nextIncidences_.push_back(firstVertexIncidences_[vertex1]); - firstVertexIncidences_[vertex1] = static_cast(incidenceVertices_.size()) - 1; - - incidenceVertices_.push_back(vertex1); - nextIncidences_.push_back(firstVertexIncidences_[vertex2]); - firstVertexIncidences_[vertex2] = static_cast(incidenceVertices_.size()) - 1; + return nextIncidences.size() >> 1; } -size_t Graph::getNVertices() const -{ - return firstVertexIncidences_.size(); -} - -size_t Graph::getNEdges() const -{ - return nextIncidences_.size() >> 1; -} - -bool Graph::areAdjacent(const int vertex1, const int vertex2) const +bool GraphBase::areAdjacent(const int vertex1, const int vertex2) const { auto it = begin(vertex1); while (it != end()) @@ -56,12 +39,29 @@ bool Graph::areAdjacent(const int vertex1, const int vertex2) const return false; } -Graph::NeighborIterator Graph::begin(const int vertex) const +GraphBase::NeighborIterator GraphBase::begin(const int vertex) const { return { *this, vertex }; } -Graph::NeighborIterator Graph::end() const +GraphBase::NeighborIterator GraphBase::end() const { return { *this, -1 }; } + +int GraphBase::addVertexInternal() +{ + firstVertexIncidences.push_back(-1); + return static_cast(firstVertexIncidences.size()) - 1; +} + +void GraphBase::addEdgeInternal(const int vertex1, const int vertex2) +{ + incidenceVertices.push_back(vertex2); + nextIncidences.push_back(firstVertexIncidences[vertex1]); + firstVertexIncidences[vertex1] = static_cast(incidenceVertices.size()) - 1; + + incidenceVertices.push_back(vertex1); + nextIncidences.push_back(firstVertexIncidences[vertex2]); + firstVertexIncidences[vertex2] = static_cast(incidenceVertices.size()) - 1; +} diff --git a/src/common/LabelGraph.cpp b/src/common/LabelGraph.cpp deleted file mode 100644 index 4bbc742..0000000 --- a/src/common/LabelGraph.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Solutions to the Advent Of Code 2024. -// Copyright (C) 2025 Stefan Müller -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation, either version 3 of the License, or (at your option) any later -// version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see . - -#include - -int LabelGraph::addVertex(const std::string& label) -{ - vertexLabels_.push_back(label); - labelVertices_.insert({ label, static_cast(vertexLabels_.size()) - 1 }); - return graph_.addVertex(); -} - -void LabelGraph::addEdge(const int vertex1, const int vertex2) -{ - graph_.addEdge(vertex1, vertex2); -} - -bool LabelGraph::areAdjacent(const int vertex1, const int vertex2) const -{ - return graph_.areAdjacent(vertex1, vertex2); -} - -const std::string& LabelGraph::getVertexLabel(const int vertex) const -{ - return vertexLabels_[vertex]; -} - -const std::map& LabelGraph::getLabelVertices() const -{ - return labelVertices_; -} - -Graph::NeighborIterator LabelGraph::begin(const int vertex) const -{ - return graph_.begin(vertex); -} - -Graph::NeighborIterator LabelGraph::end() const -{ - return graph_.end(); -}