Skip to content

Commit bf81ce8

Browse files
author
ויויאן אומנסקי
committed
add test dijkstra
1 parent 4f27744 commit bf81ce8

File tree

4 files changed

+281
-0
lines changed

4 files changed

+281
-0
lines changed

TestDijkstra/ConsoleApp1.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

TestDijkstra/Dijkstra.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
/**
5+
* A generic implementation of the Dijkstra algorithm for weighted graphs.
6+
* @author Vivian Umansky
7+
* @since 2023-12
8+
*/
9+
public class Dijkstra
10+
{
11+
public static List<NodeType> GetPath<NodeType>(
12+
IGraph<NodeType> graph,
13+
NodeType startNode,
14+
NodeType endNode,
15+
System.Func<NodeType, NodeType, float> getEdgeWeight,
16+
int maxIterations = 1000
17+
) where NodeType : notnull // Ensure NodeType is non-nullable
18+
{
19+
var distances = new Dictionary<NodeType, float>();
20+
var previousNodes = new Dictionary<NodeType, NodeType>();
21+
var visited = new HashSet<NodeType>();
22+
var priorityQueue = new SortedSet<(float, NodeType)>(Comparer<(float, NodeType)>.Create((a, b) =>
23+
a.Item1 != b.Item1 ? a.Item1.CompareTo(b.Item1) : Comparer<NodeType>.Default.Compare(a.Item2, b.Item2)));
24+
25+
// Initialize distances and add the start node to the queue
26+
distances[startNode] = 0;
27+
priorityQueue.Add((0, startNode));
28+
29+
for (int iteration = 0; iteration < maxIterations; iteration++)
30+
{
31+
if (priorityQueue.Count == 0)
32+
break;
33+
34+
var (currentDistance, currentNode) = priorityQueue.First();
35+
priorityQueue.Remove(priorityQueue.First());
36+
37+
if (visited.Contains(currentNode))
38+
continue;
39+
40+
visited.Add(currentNode);
41+
42+
if (currentNode.Equals(endNode))
43+
break;
44+
45+
foreach (var neighbor in graph.Neighbors(currentNode))
46+
{
47+
if (visited.Contains(neighbor))
48+
continue;
49+
50+
float edgeWeight = getEdgeWeight(currentNode, neighbor);
51+
float newDistance = currentDistance + edgeWeight;
52+
53+
if (!distances.ContainsKey(neighbor) || newDistance < distances[neighbor])
54+
{
55+
if (distances.ContainsKey(neighbor))
56+
{
57+
priorityQueue.Remove((distances[neighbor], neighbor));
58+
}
59+
distances[neighbor] = newDistance;
60+
previousNodes[neighbor] = currentNode!;
61+
priorityQueue.Add((newDistance, neighbor));
62+
}
63+
}
64+
}
65+
66+
// Construct the path
67+
var path = new List<NodeType>();
68+
if (previousNodes.ContainsKey(endNode) || startNode.Equals(endNode))
69+
{
70+
for (var current = endNode; current != null; current = previousNodes.ContainsKey(current) ? previousNodes[current] : default(NodeType))
71+
{
72+
path.Add(current);
73+
if (current.Equals(startNode))
74+
break;
75+
}
76+
path.Reverse();
77+
}
78+
79+
return path;
80+
}
81+
}

TestDijkstra/IGraph.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Collections.Generic;
2+
3+
/**
4+
* Interface representing a graph with nodes of type NodeType.
5+
* Used for generic graph traversal algorithms like BFS and Dijkstra.
6+
*/
7+
public interface IGraph<NodeType>
8+
{
9+
// Returns the neighbors of a given node
10+
IEnumerable<NodeType> Neighbors(NodeType node);
11+
}

TestDijkstra/TestDijkstra.cs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace TestDijkstraNamespace
5+
{
6+
public class SimpleGraph : IGraph<int>
7+
{
8+
private Dictionary<int, List<(int, float)>> adjacencyList = new Dictionary<int, List<(int, float)>>();
9+
10+
// Add an edge to the graph
11+
public void AddEdge(int from, int to, float weight)
12+
{
13+
if (!adjacencyList.ContainsKey(from))
14+
{
15+
adjacencyList[from] = new List<(int, float)>();
16+
}
17+
adjacencyList[from].Add((to, weight));
18+
}
19+
20+
// Return neighbors of a node
21+
public IEnumerable<int> Neighbors(int node)
22+
{
23+
if (adjacencyList.ContainsKey(node))
24+
{
25+
foreach (var neighbor in adjacencyList[node])
26+
{
27+
yield return neighbor.Item1;
28+
}
29+
}
30+
}
31+
32+
// Get the weight of an edge
33+
public float GetEdgeWeight(int from, int to)
34+
{
35+
if (adjacencyList.ContainsKey(from))
36+
{
37+
foreach (var (neighbor, weight) in adjacencyList[from])
38+
{
39+
if (neighbor == to)
40+
{
41+
return weight;
42+
}
43+
}
44+
}
45+
return float.MaxValue; // Return "infinite" weight if no edge exists
46+
}
47+
}
48+
49+
public class TestDijkstra
50+
{
51+
public static void Main(string[] args)
52+
{
53+
Console.WriteLine("Start Dijkstra Tests\n");
54+
55+
// Create a simple graph
56+
var graph = new SimpleGraph();
57+
graph.AddEdge(1, 2, 1); // A -> B (weight 1)
58+
graph.AddEdge(1, 3, 4); // A -> C (weight 4)
59+
graph.AddEdge(2, 3, 2); // B -> C (weight 2)
60+
graph.AddEdge(2, 4, 6); // B -> D (weight 6)
61+
graph.AddEdge(3, 4, 3); // C -> D (weight 3)
62+
63+
// Run tests
64+
TestShortestPath(graph);
65+
TestDisconnectedGraph();
66+
TestNegativeWeightGraph();
67+
TestSingleNodeGraph();
68+
TestNoPath();
69+
TestShortestPathAlternativeRoute();
70+
TestShortestPathCycle();
71+
72+
Console.WriteLine("\nEnd Dijkstra Tests");
73+
}
74+
75+
private static void TestShortestPath(SimpleGraph graph)
76+
{
77+
Console.WriteLine("Test 1: Shortest Path");
78+
var path = Dijkstra.GetPath(graph, 1, 4, graph.GetEdgeWeight);
79+
string expected = "1 -> 2 -> 3 -> 4";
80+
PrintResults(path, expected);
81+
}
82+
83+
private static void TestShortestPathAlternativeRoute()
84+
{
85+
Console.WriteLine("Test 6: Shortest Path with Alternative Route");
86+
var graph = new SimpleGraph();
87+
graph.AddEdge(1, 2, 1);
88+
graph.AddEdge(1, 3, 5);
89+
graph.AddEdge(2, 3, 2);
90+
graph.AddEdge(3, 4, 1);
91+
graph.AddEdge(2, 4, 4);
92+
93+
var path = Dijkstra.GetPath(graph, 1, 4, graph.GetEdgeWeight);
94+
string expected = "1 -> 2 -> 3 -> 4"; // Corrected expected path
95+
PrintResults(path, expected);
96+
}
97+
98+
99+
private static void TestShortestPathCycle()
100+
{
101+
Console.WriteLine("Test 7: Shortest Path with Cycle");
102+
var graph = new SimpleGraph();
103+
graph.AddEdge(1, 2, 1);
104+
graph.AddEdge(2, 3, 1);
105+
graph.AddEdge(3, 1, 2); // Cycle
106+
graph.AddEdge(3, 4, 1);
107+
108+
var path = Dijkstra.GetPath(graph, 1, 4, graph.GetEdgeWeight);
109+
string expected = "1 -> 2 -> 3 -> 4"; // Shortest path avoids unnecessary cycles
110+
PrintResults(path, expected);
111+
}
112+
113+
private static void TestDisconnectedGraph()
114+
{
115+
Console.WriteLine("Test 2: Disconnected Graph");
116+
var graph = new SimpleGraph();
117+
graph.AddEdge(1, 2, 5);
118+
graph.AddEdge(3, 4, 2);
119+
120+
var path = Dijkstra.GetPath(graph, 1, 4, graph.GetEdgeWeight);
121+
string expected = ""; // No path exists
122+
PrintResults(path, expected);
123+
}
124+
125+
private static void TestNegativeWeightGraph()
126+
{
127+
Console.WriteLine("Test 3: Graph with Negative Weights");
128+
var graph = new SimpleGraph();
129+
graph.AddEdge(1, 2, -5); // Dijkstra cannot handle negative weights properly
130+
graph.AddEdge(2, 3, 2);
131+
graph.AddEdge(1, 3, 10);
132+
133+
var path = Dijkstra.GetPath(graph, 1, 3, graph.GetEdgeWeight);
134+
string expected = "1 -> 2 -> 3"; // In Dijkstra, behavior may be undefined with negative weights
135+
PrintResults(path, expected, warnNegativeWeights: true);
136+
}
137+
138+
private static void TestSingleNodeGraph()
139+
{
140+
Console.WriteLine("Test 4: Single Node Graph");
141+
var graph = new SimpleGraph();
142+
graph.AddEdge(1, 1, 0); // A single node pointing to itself
143+
144+
var path = Dijkstra.GetPath(graph, 1, 1, graph.GetEdgeWeight);
145+
string expected = "1";
146+
PrintResults(path, expected);
147+
}
148+
149+
private static void TestNoPath()
150+
{
151+
Console.WriteLine("Test 5: No Path Between Nodes");
152+
var graph = new SimpleGraph();
153+
graph.AddEdge(1, 2, 3);
154+
155+
var path = Dijkstra.GetPath(graph, 2, 1, graph.GetEdgeWeight); // No reverse edge
156+
string expected = ""; // No path exists
157+
PrintResults(path, expected);
158+
}
159+
160+
private static void PrintResults(List<int> path, string expected, bool warnNegativeWeights = false)
161+
{
162+
string actual = string.Join(" -> ", path);
163+
Console.WriteLine("Expected: " + (string.IsNullOrEmpty(expected) ? "No path" : expected));
164+
Console.WriteLine("Actual: " + (string.IsNullOrEmpty(actual) ? "No path" : actual));
165+
if (warnNegativeWeights)
166+
{
167+
Console.WriteLine("Warning: Dijkstra may behave unpredictably with negative weights.");
168+
}
169+
Console.WriteLine(path.SequenceEqual(ParsePath(expected)) ? "Test Passed" : "Test Failed");
170+
Console.WriteLine();
171+
}
172+
173+
private static List<int> ParsePath(string path)
174+
{
175+
if (string.IsNullOrEmpty(path)) return new List<int>();
176+
return new List<int>(Array.ConvertAll(path.Split(" -> "), int.Parse));
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)