diff --git a/Exam/IKT203Exam/Portfolio/Assignment-04/CMakeLists.txt b/Exam/IKT203Exam/Portfolio/Assignment-04/CMakeLists.txt index 578df4e..87888c4 100644 --- a/Exam/IKT203Exam/Portfolio/Assignment-04/CMakeLists.txt +++ b/Exam/IKT203Exam/Portfolio/Assignment-04/CMakeLists.txt @@ -3,10 +3,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # "ON" = build Option 1, "OFF" = build Option 2. -option(BUILD_ASSIGNMENT_04_OPTION_1 "Build Assignment Option 1 (Standard)" OFF) +option(BUILD_ASSIGNMENT_04_OPTION_1 "Build Assignment Option 1 (Standard)" ON) add_executable(Assignment-04 main.cpp + option1.cpp ) if(BUILD_ASSIGNMENT_04_OPTION_1) diff --git a/Exam/IKT203Exam/Portfolio/Assignment-04/main.cpp b/Exam/IKT203Exam/Portfolio/Assignment-04/main.cpp index ae431f3..898323f 100644 --- a/Exam/IKT203Exam/Portfolio/Assignment-04/main.cpp +++ b/Exam/IKT203Exam/Portfolio/Assignment-04/main.cpp @@ -28,16 +28,18 @@ To force an update (e.g., in Visual Studio): #include #include +#include "option1.h" static constexpr std::string_view AssignmentName = "Category 4: Graphs & Dijkstra's Algorithm"; - -#if ASSIGNMENT_04_OPTION == 1 static constexpr std::string_view AssignmentOption = "Option 1 (Standard): Data Center Network Monitor."; + +/* +#if ASSIGNMENT_04_OPTION == 1 #include "option1.h" #elif ASSIGNMENT_04_OPTION == 2 static constexpr std::string_view AssignmentOption = "Option 2 (Advanced): Inter-city Logistics Router."; #include "option2.h" #endif - +*/ int main(int argc, char* argv[]) { diff --git a/Exam/IKT203Exam/Portfolio/Assignment-04/option1.cpp b/Exam/IKT203Exam/Portfolio/Assignment-04/option1.cpp index 37f5e0d..4b261cf 100644 --- a/Exam/IKT203Exam/Portfolio/Assignment-04/option1.cpp +++ b/Exam/IKT203Exam/Portfolio/Assignment-04/option1.cpp @@ -1,6 +1,261 @@ #include "option1.h" #include +#include "SharedLib.h" + +// Necessary declarations +constexpr float INF = 1e9f; +Graph g; +std::unordered_map nameToIndex; +std::string filename = R"(C:\Users\csand\IKT203\Exam\IKT203Exam\DATA\network_graph.txt)"; // Local path to MY graph file + +//////////////////////////////// Callbacks //////////////////////////////// +bool onNodeRead(const int aIndex, const int aTotalCount, const std::string& aNode) +{ + const int idx = g.AddVertex(aNode); + nameToIndex[aNode] = idx; + return true; +} + +bool onEdgeRead(const int aIndex, const int aTotalCount, const std::string& aFromNode, const std::string& aToNode, const float aWeight) +{ + const int fromIdx = nameToIndex[aFromNode]; + const int toIdx = nameToIndex[aToNode]; + + g.AddUndirectedEdge(fromIdx, toIdx, aWeight); + return true; +} + +//////////////////////////////// Dijkstra algorithm //////////////////////////////// +float Dijkstra(const Graph& graph, int src, int dst, std::vector& outPath) +{ + const int n = graph.GetVertexCount(); + + std::vector dist(n, INF); + std::vector prev(n, -1); + std::vector visited(n, false); + + dist[src] = 0.0f; + + MinHeap heap; + heap.Push(src, 0.0f); + + while (!heap.isEmpty()) { + HeapNode* node = heap.Pop(); + const int u = node->vertex; + float d = node->distance; + delete node; + + if (visited[u]) + continue; + visited[u] = true; + if (u == dst) + break; + + for (const TEdge* e : graph.GetEdges(u)) { + int v = e->toIndex; + float w = e->weight; + + if (visited[v]) + continue; + if (dist[u] + w < dist[v]) { + dist[v] = dist[u] + w; + prev[v] = u; + heap.Push(v, dist[v]); + } + } + } + + outPath.clear(); + if (dist[dst] == INF) + return INF; + for (int v = dst; v != -1; v = prev[v]) + outPath.push_back(v); + std::reverse(outPath.begin(), outPath.end()); + return dist[dst]; +} + +//////////////////////////////// Class logic //////////////////////////////// +// Graph +Graph::~Graph() +{ + for (TVertex* v : vertices) { + for (TEdge* e : v->edges) { + delete e; + } + delete v; + } +} + +int Graph::AddVertex(const std::string& name) +{ + auto* v = new TVertex; + v->name = name; + vertices.push_back(v); + return static_cast(vertices.size()) - 1; +} + +void Graph::AddUndirectedEdge(int fromIndex, int toIndex, float weight) +{ + TEdge* e1 = new TEdge; + e1->toIndex = toIndex; + e1->weight = weight; + + TEdge* e2 = new TEdge; + e2->toIndex = fromIndex; + e2->weight = weight; + + vertices[fromIndex]->edges.push_back(e1); + vertices[toIndex]->edges.push_back(e2); +} + +int Graph::GetVertexCount() const +{ + return static_cast(vertices.size()); +} + +const TVertex *Graph::GetVertex(int index) const +{ + return vertices[index]; +} + +const std::vector& Graph::GetEdges(const int index) const +{ + return vertices[index]->edges; +} + +// Heap +MinHeap::~MinHeap() +{ + for (HeapNode* n : data) + delete n; +} + +bool MinHeap::isEmpty() const +{ + return data.empty(); +} + +void MinHeap::Push(int vertex, float dist) +{ + auto* n = new HeapNode{vertex, dist}; + data.push_back(n); + HeapUp(static_cast(data.size()) - 1); +} + +HeapNode* MinHeap::Pop() +{ + if (data.empty()) + return nullptr; + + HeapNode* root = data[0]; + data[0] = data.back(); + data.pop_back(); + if (!data.empty()) + HeapDown(0); + return root; +} + +void MinHeap::HeapUp(int idx) +{ + while (idx > 0) { + int parent = (idx - 1) / 2; + if (data[parent]->distance <= data[idx]->distance) + break; + std::swap(data[idx], data[parent]); + idx = parent; + } +} + +void MinHeap::HeapDown(int idx) +{ + int n = static_cast(data.size()); + + while (true) { + int left = 2 * idx + 1; + int right = 2 * idx + 2; + int smallest = idx; + + if (left < n && data[left]->distance < data[smallest]->distance) + smallest = left; + if (right < n && data[right]->distance < data[smallest]->distance) + smallest = right; + + if (smallest == idx) + break; + std::swap(data[idx], data[smallest]); + idx = smallest; + } +} + + + + + + + + + int RunApp() { + + readGraphFromFile(filename, onNodeRead, onEdgeRead); + + // Debug: Print all nodes and vertices + /* + pack("Graph"); + for (int i = 0; i < g.GetVertexCount(); i++) { + const TVertex* v = g.GetVertex(i); + std::cout << i << ": " << v->name << std::endl; + for (const TEdge* e : g.GetEdges(i)) { + std::cout << " -> " << e->toIndex << " (weight = " << e->weight << ")" << std::endl; + } + } + */ + + /* Debug heap test + pack("Heap"); + MinHeap test; + test.Push(1, 5.0f); + test.Push(2, 3.0f); + test.Push(3, 10.0f); + while (!test.isEmpty()) + { + const HeapNode* n = test.Pop(); + std::cout << "(" << n->vertex << ", " << n->distance << ")" << "\n"; + delete n; + } + printline(); + */ + + std::cout << "\nGraph:" << std::endl; + for (int i = 0; i < g.GetVertexCount(); ++i) + std::cout << i << ": " << g.GetVertex(i)->name << std::endl; + + int src, dst; + std::cout << "\nEnter source index: "; + std::cin >> src; + std::cout << "\nEnter destination index: "; + std::cin >> dst; + if (src < 0 || src >= g.GetVertexCount() || + dst < 0 || dst >= g.GetVertexCount()) { + std::cout << "Invalid indices.\n"; + return 0; + } + + std::vector path; + float total = Dijkstra(g, src, dst, path); + + if (total >= INF) { + std::cout << "\nNo path between those nodes.\n"; + } else { + std::cout << "\nLowest latency path: "; + for (size_t i = 0; i < path.size(); ++i) { + std::cout << g.GetVertex(path[i])->name; + if (i + 1 < path.size()) std::cout << " -> "; + } + std::cout << " \n(Total: " << total << " ms)\n"; + } + printline(); + return 0; -} \ No newline at end of file +} diff --git a/Exam/IKT203Exam/Portfolio/Assignment-04/option1.h b/Exam/IKT203Exam/Portfolio/Assignment-04/option1.h index 658aeb1..909da09 100644 --- a/Exam/IKT203Exam/Portfolio/Assignment-04/option1.h +++ b/Exam/IKT203Exam/Portfolio/Assignment-04/option1.h @@ -2,6 +2,81 @@ #ifndef OPTION1_H #define OPTION1_H +#include +#include +#include +#include + +// Structs and classes +struct TEdge { + int toIndex; + float weight; +}; + +struct TVertex { + std::string name; + std::vector edges; +}; + +struct HeapNode { + int vertex; + float distance; +}; + +class MinHeap { +private: + std::vector data; + + void HeapUp(int idx); + void HeapDown(int idx); + +public: + ~MinHeap(); + + [[nodiscard]] bool isEmpty() const; + void Push(int vertex, float dist); + HeapNode* Pop(); + +}; + +class Graph { +private: + std::vector vertices; // Owns all TVertex* + +public: + ~Graph(); + + int AddVertex(const std::string& name); + void AddUndirectedEdge(int fromIndex, int toIndex, float weight); + + [[nodiscard]] int GetVertexCount() const; + [[nodiscard]] const TVertex* GetVertex(int index) const; + [[nodiscard]] const std::vector& GetEdges(int index) const; +}; + +// Callbacks & funcs +bool onNodeRead(int aIndex, int aTotalCount, const std::string& aNode); +bool onEdgeRead(int aIndex, int aTotalCount, std::string& aFromNode, std::string& aToNode, float aWeight); + +float Dijkstra(const Graph& graph, int src, int dst, std::vector& outPath); + +// Terminal tidying +inline void printline() +{ + std::cout << "----------------------------------------" << std::endl; +} + +inline void pack(const std::string& line) +{ + std::cout << "\n\n\n" << std::endl; + printline(); + std::cout << line << std::endl; + printline(); +} + + + + int RunApp(); diff --git a/Exam/IKT203Exam/Portfolio/Assignment-04/option2.cpp b/Exam/IKT203Exam/Portfolio/Assignment-04/option2.cpp index 48aeef6..6084da3 100644 --- a/Exam/IKT203Exam/Portfolio/Assignment-04/option2.cpp +++ b/Exam/IKT203Exam/Portfolio/Assignment-04/option2.cpp @@ -27,7 +27,7 @@ static bool EdgeReadCallback(const int aIndex, const int aTotalCount, const std: return true; } - +/* int RunApp() { @@ -44,4 +44,4 @@ int RunApp() std::cout << "\nFinished reading graph." << std::endl; return 0; -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/Exam/IKT203Exam/Portfolio/Assignment-04/option2.h b/Exam/IKT203Exam/Portfolio/Assignment-04/option2.h index 7813abf..8c71889 100644 --- a/Exam/IKT203Exam/Portfolio/Assignment-04/option2.h +++ b/Exam/IKT203Exam/Portfolio/Assignment-04/option2.h @@ -2,8 +2,8 @@ #ifndef OPTION2_H #define OPTION2_H - +/* int RunApp(); - +*/ #endif // OPTION2_H