Skip to content

Commit 5aee965

Browse files
authored
Add Bridges algorithm (#575)
1 parent 1baa726 commit 5aee965

File tree

2 files changed

+515
-0
lines changed

2 files changed

+515
-0
lines changed
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Algorithms.Graph;
5+
using FluentAssertions;
6+
using NUnit.Framework;
7+
8+
namespace Algorithms.Tests.Graph;
9+
10+
public class BridgesTests
11+
{
12+
[Test]
13+
public void Find_SimpleChain_ReturnsAllEdges()
14+
{
15+
// Arrange: A - B - C (both edges are bridges)
16+
var vertices = new[] { "A", "B", "C" };
17+
IEnumerable<string> GetNeighbors(string v) => v switch
18+
{
19+
"A" => new[] { "B" },
20+
"B" => new[] { "A", "C" },
21+
"C" => new[] { "B" },
22+
_ => Array.Empty<string>(),
23+
};
24+
25+
// Act
26+
var result = Bridges.Find(vertices, GetNeighbors);
27+
28+
// Assert
29+
result.Should().HaveCount(2);
30+
result.Should().Contain(new[] { ("A", "B"), ("B", "C") });
31+
}
32+
33+
[Test]
34+
public void Find_Triangle_ReturnsEmpty()
35+
{
36+
// Arrange: A - B - C - A (no bridges in cycle)
37+
var vertices = new[] { "A", "B", "C" };
38+
IEnumerable<string> GetNeighbors(string v) => v switch
39+
{
40+
"A" => new[] { "B", "C" },
41+
"B" => new[] { "A", "C" },
42+
"C" => new[] { "A", "B" },
43+
_ => Array.Empty<string>(),
44+
};
45+
46+
// Act
47+
var result = Bridges.Find(vertices, GetNeighbors);
48+
49+
// Assert
50+
result.Should().BeEmpty();
51+
}
52+
53+
[Test]
54+
public void Find_TwoComponentsConnectedByBridge_ReturnsBridge()
55+
{
56+
// Arrange: (A-B-C) - D - (E-F-G)
57+
var vertices = new[] { "A", "B", "C", "D", "E", "F", "G" };
58+
IEnumerable<string> GetNeighbors(string v) => v switch
59+
{
60+
"A" => new[] { "B", "C" },
61+
"B" => new[] { "A", "C", "D" },
62+
"C" => new[] { "A", "B" },
63+
"D" => new[] { "B", "E" },
64+
"E" => new[] { "D", "F", "G" },
65+
"F" => new[] { "E", "G" },
66+
"G" => new[] { "E", "F" },
67+
_ => Array.Empty<string>(),
68+
};
69+
70+
// Act
71+
var result = Bridges.Find(vertices, GetNeighbors);
72+
73+
// Assert
74+
result.Should().HaveCount(2);
75+
result.Should().Contain(new[] { ("B", "D"), ("D", "E") });
76+
}
77+
78+
[Test]
79+
public void Find_StarGraph_ReturnsAllEdges()
80+
{
81+
// Arrange: Star with center A
82+
var vertices = new[] { "A", "B", "C", "D" };
83+
IEnumerable<string> GetNeighbors(string v) => v switch
84+
{
85+
"A" => new[] { "B", "C", "D" },
86+
"B" => new[] { "A" },
87+
"C" => new[] { "A" },
88+
"D" => new[] { "A" },
89+
_ => Array.Empty<string>(),
90+
};
91+
92+
// Act
93+
var result = Bridges.Find(vertices, GetNeighbors);
94+
95+
// Assert
96+
result.Should().HaveCount(3);
97+
result.Should().Contain(new[] { ("A", "B"), ("A", "C"), ("A", "D") });
98+
}
99+
100+
[Test]
101+
public void Find_DisconnectedGraph_FindsBridgesInEachComponent()
102+
{
103+
// Arrange: (A-B-C) and (D-E-F)
104+
var vertices = new[] { "A", "B", "C", "D", "E", "F" };
105+
IEnumerable<string> GetNeighbors(string v) => v switch
106+
{
107+
"A" => new[] { "B" },
108+
"B" => new[] { "A", "C" },
109+
"C" => new[] { "B" },
110+
"D" => new[] { "E" },
111+
"E" => new[] { "D", "F" },
112+
"F" => new[] { "E" },
113+
_ => Array.Empty<string>(),
114+
};
115+
116+
// Act
117+
var result = Bridges.Find(vertices, GetNeighbors);
118+
119+
// Assert
120+
result.Should().HaveCount(4);
121+
result.Should().Contain(new[] { ("A", "B"), ("B", "C"), ("D", "E"), ("E", "F") });
122+
}
123+
124+
[Test]
125+
public void Find_SingleVertex_ReturnsEmpty()
126+
{
127+
// Arrange
128+
var vertices = new[] { "A" };
129+
IEnumerable<string> GetNeighbors(string v) => Array.Empty<string>();
130+
131+
// Act
132+
var result = Bridges.Find(vertices, GetNeighbors);
133+
134+
// Assert
135+
result.Should().BeEmpty();
136+
}
137+
138+
[Test]
139+
public void Find_TwoVertices_ReturnsBridge()
140+
{
141+
// Arrange: A - B
142+
var vertices = new[] { "A", "B" };
143+
IEnumerable<string> GetNeighbors(string v) => v switch
144+
{
145+
"A" => new[] { "B" },
146+
"B" => new[] { "A" },
147+
_ => Array.Empty<string>(),
148+
};
149+
150+
// Act
151+
var result = Bridges.Find(vertices, GetNeighbors);
152+
153+
// Assert
154+
result.Should().ContainSingle();
155+
result.Should().Contain(("A", "B"));
156+
}
157+
158+
[Test]
159+
public void Find_ComplexGraph_ReturnsCorrectBridges()
160+
{
161+
// Arrange: Complex graph with cycles and bridges
162+
var vertices = new[] { 1, 2, 3, 4, 5, 6, 7 };
163+
IEnumerable<int> GetNeighbors(int v) => v switch
164+
{
165+
1 => new[] { 2, 3 },
166+
2 => new[] { 1, 3 },
167+
3 => new[] { 1, 2, 4 },
168+
4 => new[] { 3, 5, 6 },
169+
5 => new[] { 4, 6 },
170+
6 => new[] { 4, 5, 7 },
171+
7 => new[] { 6 },
172+
_ => Array.Empty<int>(),
173+
};
174+
175+
// Act
176+
var result = Bridges.Find(vertices, GetNeighbors);
177+
178+
// Assert
179+
result.Should().Contain(new[] { (3, 4), (6, 7) });
180+
}
181+
182+
[Test]
183+
public void Find_EmptyGraph_ReturnsEmpty()
184+
{
185+
// Arrange
186+
var vertices = Array.Empty<string>();
187+
IEnumerable<string> GetNeighbors(string v) => Array.Empty<string>();
188+
189+
// Act
190+
var result = Bridges.Find(vertices, GetNeighbors);
191+
192+
// Assert
193+
result.Should().BeEmpty();
194+
}
195+
196+
[Test]
197+
public void Find_NullVertices_ThrowsArgumentNullException()
198+
{
199+
// Act
200+
Action act = () => Bridges.Find<string>(null!, v => Array.Empty<string>());
201+
202+
// Assert
203+
act.Should().Throw<ArgumentNullException>().WithParameterName("vertices");
204+
}
205+
206+
[Test]
207+
public void Find_NullGetNeighbors_ThrowsArgumentNullException()
208+
{
209+
// Arrange
210+
var vertices = new[] { "A" };
211+
212+
// Act
213+
Action act = () => Bridges.Find(vertices, null!);
214+
215+
// Assert
216+
act.Should().Throw<ArgumentNullException>().WithParameterName("getNeighbors");
217+
}
218+
219+
[Test]
220+
public void IsBridge_ValidBridge_ReturnsTrue()
221+
{
222+
// Arrange: A - B - C
223+
var vertices = new[] { "A", "B", "C" };
224+
IEnumerable<string> GetNeighbors(string v) => v switch
225+
{
226+
"A" => new[] { "B" },
227+
"B" => new[] { "A", "C" },
228+
"C" => new[] { "B" },
229+
_ => Array.Empty<string>(),
230+
};
231+
232+
// Act
233+
var result = Bridges.IsBridge("A", "B", vertices, GetNeighbors);
234+
235+
// Assert
236+
result.Should().BeTrue();
237+
}
238+
239+
[Test]
240+
public void IsBridge_ReverseEdge_ReturnsTrue()
241+
{
242+
// Arrange: A - B - C
243+
var vertices = new[] { "A", "B", "C" };
244+
IEnumerable<string> GetNeighbors(string v) => v switch
245+
{
246+
"A" => new[] { "B" },
247+
"B" => new[] { "A", "C" },
248+
"C" => new[] { "B" },
249+
_ => Array.Empty<string>(),
250+
};
251+
252+
// Act
253+
var result = Bridges.IsBridge("B", "A", vertices, GetNeighbors);
254+
255+
// Assert
256+
result.Should().BeTrue();
257+
}
258+
259+
[Test]
260+
public void IsBridge_NotBridge_ReturnsFalse()
261+
{
262+
// Arrange: A - B - C - A (triangle)
263+
var vertices = new[] { "A", "B", "C" };
264+
IEnumerable<string> GetNeighbors(string v) => v switch
265+
{
266+
"A" => new[] { "B", "C" },
267+
"B" => new[] { "A", "C" },
268+
"C" => new[] { "A", "B" },
269+
_ => Array.Empty<string>(),
270+
};
271+
272+
// Act
273+
var result = Bridges.IsBridge("A", "B", vertices, GetNeighbors);
274+
275+
// Assert
276+
result.Should().BeFalse();
277+
}
278+
279+
[Test]
280+
public void Count_SimpleChain_ReturnsTwo()
281+
{
282+
// Arrange: A - B - C
283+
var vertices = new[] { "A", "B", "C" };
284+
IEnumerable<string> GetNeighbors(string v) => v switch
285+
{
286+
"A" => new[] { "B" },
287+
"B" => new[] { "A", "C" },
288+
"C" => new[] { "B" },
289+
_ => Array.Empty<string>(),
290+
};
291+
292+
// Act
293+
var result = Bridges.Count(vertices, GetNeighbors);
294+
295+
// Assert
296+
result.Should().Be(2);
297+
}
298+
299+
[Test]
300+
public void Count_Triangle_ReturnsZero()
301+
{
302+
// Arrange: A - B - C - A
303+
var vertices = new[] { "A", "B", "C" };
304+
IEnumerable<string> GetNeighbors(string v) => v switch
305+
{
306+
"A" => new[] { "B", "C" },
307+
"B" => new[] { "A", "C" },
308+
"C" => new[] { "A", "B" },
309+
_ => Array.Empty<string>(),
310+
};
311+
312+
// Act
313+
var result = Bridges.Count(vertices, GetNeighbors);
314+
315+
// Assert
316+
result.Should().Be(0);
317+
}
318+
319+
[Test]
320+
public void Find_LargeChain_FindsAllBridges()
321+
{
322+
// Arrange: Large chain
323+
var vertices = Enumerable.Range(1, 10).ToArray();
324+
IEnumerable<int> GetNeighbors(int v)
325+
{
326+
var neighbors = new List<int>();
327+
if (v > 1)
328+
{
329+
neighbors.Add(v - 1);
330+
}
331+
332+
if (v < 10)
333+
{
334+
neighbors.Add(v + 1);
335+
}
336+
337+
return neighbors;
338+
}
339+
340+
// Act
341+
var result = Bridges.Find(vertices, GetNeighbors);
342+
343+
// Assert
344+
result.Should().HaveCount(9); // All edges in chain are bridges
345+
}
346+
}

0 commit comments

Comments
 (0)