Skip to content

Commit a4bc7bf

Browse files
committed
Add tests to Boruvkas algo
1 parent bcf1bac commit a4bc7bf

File tree

3 files changed

+270
-48
lines changed

3 files changed

+270
-48
lines changed

src/main/java/com/williamfiset/algorithms/graphtheory/Boruvkas.java

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -112,54 +112,6 @@ private void solve() {
112112
solved = true;
113113
}
114114

115-
private boolean check() {
116-
117-
if (!mstExists) return true;
118-
119-
// check that it is acyclic
120-
UnionFind uf = new UnionFind(n);
121-
for (Edge e : mst) {
122-
int u = e.u, v = e.v;
123-
if (uf.connected(u, v)) {
124-
System.err.println("Not a forest");
125-
return false;
126-
}
127-
uf.union(u, v);
128-
}
129-
130-
// check that it is a spanning forest
131-
for (Edge e : mst) {
132-
int u = e.u, v = e.v;
133-
if (!uf.connected(u, v)) {
134-
System.err.println("Not a spanning forest");
135-
return false;
136-
}
137-
}
138-
139-
// check that it is a minimal spanning forest (cut optimality conditions)
140-
for (Edge e : mst) {
141-
142-
// all edges in MST except e
143-
uf = new UnionFind(n);
144-
for (Edge f : mst) {
145-
int x = f.u, y = f.v;
146-
if (f != e) uf.union(x, y);
147-
}
148-
149-
// check that e is min weight edge in crossing cut
150-
for (Edge f : graph) {
151-
int x = f.u, y = f.v;
152-
if (!uf.connected(x, y)) {
153-
if (f.cost < e.cost) {
154-
System.err.println("Edge " + f + " violates cut optimality conditions");
155-
return false;
156-
}
157-
}
158-
}
159-
}
160-
return true;
161-
}
162-
163115
public static void main(String[] args) {
164116

165117
int n = 10, m = 18, i = 0;

src/test/java/com/williamfiset/algorithms/graphtheory/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ TEST_DEPS = [
2424

2525
# Core graphtheory tests
2626

27+
# bazel test //src/test/java/com/williamfiset/algorithms/graphtheory:BoruvkasTest
28+
java_test(
29+
name = "BoruvkasTest",
30+
srcs = ["BoruvkasTest.java"],
31+
main_class = "org.junit.platform.console.ConsoleLauncher",
32+
use_testrunner = False,
33+
args = ["--select-class=com.williamfiset.algorithms.graphtheory.BoruvkasTest"],
34+
runtime_deps = JUNIT5_RUNTIME_DEPS,
35+
deps = TEST_DEPS,
36+
)
37+
2738
# bazel test //src/test/java/com/williamfiset/algorithms/graphtheory:ArticulationPointsAdjacencyListTest
2839
java_test(
2940
name = "ArticulationPointsAdjacencyListTest",
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
package com.williamfiset.algorithms.graphtheory;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import com.williamfiset.algorithms.graphtheory.Boruvkas.Edge;
7+
import java.util.*;
8+
import org.junit.jupiter.api.*;
9+
10+
public class BoruvkasTest {
11+
12+
@Test
13+
public void testNullGraphThrowsException() {
14+
assertThrows(IllegalArgumentException.class, () -> new Boruvkas(5, null));
15+
}
16+
17+
@Test
18+
public void testSingleNode() {
19+
Edge[] graph = new Edge[0];
20+
Boruvkas solver = new Boruvkas(1, graph);
21+
assertThat(solver.getMstCost()).isEqualTo(0L);
22+
assertThat(solver.getMst()).isEmpty();
23+
}
24+
25+
@Test
26+
public void testTwoNodesConnected() {
27+
Edge[] graph = new Edge[] {new Edge(0, 1, 5)};
28+
Boruvkas solver = new Boruvkas(2, graph);
29+
assertThat(solver.getMstCost()).isEqualTo(5L);
30+
assertThat(solver.getMst()).hasSize(1);
31+
}
32+
33+
@Test
34+
public void testTwoNodesDisconnected() {
35+
Edge[] graph = new Edge[0];
36+
Boruvkas solver = new Boruvkas(2, graph);
37+
assertThat(solver.getMstCost()).isNull();
38+
assertThat(solver.getMst()).isNull();
39+
}
40+
41+
@Test
42+
public void testSimpleTriangle() {
43+
Edge[] graph =
44+
new Edge[] {new Edge(0, 1, 1), new Edge(1, 2, 2), new Edge(0, 2, 3)};
45+
Boruvkas solver = new Boruvkas(3, graph);
46+
assertThat(solver.getMstCost()).isEqualTo(3L);
47+
assertThat(solver.getMst()).hasSize(2);
48+
}
49+
50+
@Test
51+
public void testDisconnectedGraph() {
52+
// Two separate components: {0,1} and {2,3}
53+
Edge[] graph = new Edge[] {new Edge(0, 1, 1), new Edge(2, 3, 2)};
54+
Boruvkas solver = new Boruvkas(4, graph);
55+
assertThat(solver.getMstCost()).isNull();
56+
assertThat(solver.getMst()).isNull();
57+
}
58+
59+
@Test
60+
public void testExampleFromMainMethod() {
61+
int n = 10, m = 18, i = 0;
62+
Edge[] g = new Edge[m];
63+
64+
g[i++] = new Edge(0, 1, 5);
65+
g[i++] = new Edge(0, 3, 4);
66+
g[i++] = new Edge(0, 4, 1);
67+
g[i++] = new Edge(1, 2, 4);
68+
g[i++] = new Edge(1, 3, 2);
69+
g[i++] = new Edge(2, 7, 4);
70+
g[i++] = new Edge(2, 8, 1);
71+
g[i++] = new Edge(2, 9, 2);
72+
g[i++] = new Edge(3, 6, 11);
73+
g[i++] = new Edge(3, 7, 2);
74+
g[i++] = new Edge(4, 3, 2);
75+
g[i++] = new Edge(4, 5, 1);
76+
g[i++] = new Edge(5, 3, 5);
77+
g[i++] = new Edge(5, 6, 7);
78+
g[i++] = new Edge(6, 7, 1);
79+
g[i++] = new Edge(6, 8, 4);
80+
g[i++] = new Edge(7, 8, 6);
81+
g[i++] = new Edge(9, 8, 0);
82+
83+
Boruvkas solver = new Boruvkas(n, g);
84+
85+
assertThat(solver.getMstCost()).isEqualTo(14L);
86+
assertThat(solver.getMst()).hasSize(n - 1);
87+
}
88+
89+
@Test
90+
public void testLinearGraph() {
91+
// 0 -- 1 -- 2 -- 3 -- 4
92+
Edge[] graph =
93+
new Edge[] {
94+
new Edge(0, 1, 1), new Edge(1, 2, 2), new Edge(2, 3, 3), new Edge(3, 4, 4)
95+
};
96+
Boruvkas solver = new Boruvkas(5, graph);
97+
assertThat(solver.getMstCost()).isEqualTo(10L);
98+
assertThat(solver.getMst()).hasSize(4);
99+
}
100+
101+
@Test
102+
public void testCompleteGraphK4() {
103+
// Complete graph with 4 nodes
104+
Edge[] graph =
105+
new Edge[] {
106+
new Edge(0, 1, 1),
107+
new Edge(0, 2, 4),
108+
new Edge(0, 3, 3),
109+
new Edge(1, 2, 2),
110+
new Edge(1, 3, 5),
111+
new Edge(2, 3, 6)
112+
};
113+
Boruvkas solver = new Boruvkas(4, graph);
114+
// MST should be: 0-1 (1), 1-2 (2), 0-3 (3) = 6
115+
assertThat(solver.getMstCost()).isEqualTo(6L);
116+
assertThat(solver.getMst()).hasSize(3);
117+
}
118+
119+
@Test
120+
public void testGraphWithZeroWeightEdges() {
121+
Edge[] graph =
122+
new Edge[] {new Edge(0, 1, 0), new Edge(1, 2, 0), new Edge(2, 3, 0)};
123+
Boruvkas solver = new Boruvkas(4, graph);
124+
assertThat(solver.getMstCost()).isEqualTo(0L);
125+
assertThat(solver.getMst()).hasSize(3);
126+
}
127+
128+
@Test
129+
public void testGraphWithNegativeWeightEdges() {
130+
Edge[] graph =
131+
new Edge[] {new Edge(0, 1, -5), new Edge(1, 2, -3), new Edge(0, 2, 10)};
132+
Boruvkas solver = new Boruvkas(3, graph);
133+
assertThat(solver.getMstCost()).isEqualTo(-8L);
134+
assertThat(solver.getMst()).hasSize(2);
135+
}
136+
137+
@Test
138+
public void testGraphWithEqualWeightEdges() {
139+
// All edges have the same weight
140+
Edge[] graph =
141+
new Edge[] {
142+
new Edge(0, 1, 5),
143+
new Edge(1, 2, 5),
144+
new Edge(2, 3, 5),
145+
new Edge(3, 0, 5),
146+
new Edge(0, 2, 5)
147+
};
148+
Boruvkas solver = new Boruvkas(4, graph);
149+
assertThat(solver.getMstCost()).isEqualTo(15L);
150+
assertThat(solver.getMst()).hasSize(3);
151+
}
152+
153+
@Test
154+
public void testStarGraph() {
155+
// Node 0 is connected to all other nodes
156+
Edge[] graph =
157+
new Edge[] {
158+
new Edge(0, 1, 1), new Edge(0, 2, 2), new Edge(0, 3, 3), new Edge(0, 4, 4)
159+
};
160+
Boruvkas solver = new Boruvkas(5, graph);
161+
assertThat(solver.getMstCost()).isEqualTo(10L);
162+
assertThat(solver.getMst()).hasSize(4);
163+
}
164+
165+
@Test
166+
public void testMstIsIdempotent() {
167+
Edge[] graph =
168+
new Edge[] {new Edge(0, 1, 1), new Edge(1, 2, 2), new Edge(0, 2, 3)};
169+
Boruvkas solver = new Boruvkas(3, graph);
170+
171+
// Call multiple times to verify idempotency
172+
Long cost1 = solver.getMstCost();
173+
Long cost2 = solver.getMstCost();
174+
List<Edge> mst1 = solver.getMst();
175+
List<Edge> mst2 = solver.getMst();
176+
177+
assertThat(cost1).isEqualTo(cost2);
178+
assertThat(mst1).isEqualTo(mst2);
179+
}
180+
181+
@Test
182+
public void testLargerGraph() {
183+
// A more complex graph to ensure algorithm works on larger inputs
184+
Edge[] graph =
185+
new Edge[] {
186+
new Edge(0, 1, 4),
187+
new Edge(0, 7, 8),
188+
new Edge(1, 2, 8),
189+
new Edge(1, 7, 11),
190+
new Edge(2, 3, 7),
191+
new Edge(2, 5, 4),
192+
new Edge(2, 8, 2),
193+
new Edge(3, 4, 9),
194+
new Edge(3, 5, 14),
195+
new Edge(4, 5, 10),
196+
new Edge(5, 6, 2),
197+
new Edge(6, 7, 1),
198+
new Edge(6, 8, 6),
199+
new Edge(7, 8, 7)
200+
};
201+
Boruvkas solver = new Boruvkas(9, graph);
202+
// Known MST cost for this classic graph
203+
assertThat(solver.getMstCost()).isEqualTo(37L);
204+
assertThat(solver.getMst()).hasSize(8);
205+
}
206+
207+
@Test
208+
public void testMstEdgesFormSpanningTree() {
209+
Edge[] graph =
210+
new Edge[] {
211+
new Edge(0, 1, 1),
212+
new Edge(1, 2, 2),
213+
new Edge(2, 3, 3),
214+
new Edge(3, 0, 4),
215+
new Edge(0, 2, 5)
216+
};
217+
Boruvkas solver = new Boruvkas(4, graph);
218+
List<Edge> mst = solver.getMst();
219+
220+
assertThat(mst).isNotNull();
221+
assertThat(mst).hasSize(3);
222+
223+
// Verify MST connects all nodes (using simple connectivity check)
224+
Set<Integer> connected = new HashSet<>();
225+
connected.add(mst.get(0).u);
226+
connected.add(mst.get(0).v);
227+
228+
boolean changed = true;
229+
while (changed) {
230+
changed = false;
231+
for (Edge e : mst) {
232+
if (connected.contains(e.u) && !connected.contains(e.v)) {
233+
connected.add(e.v);
234+
changed = true;
235+
} else if (connected.contains(e.v) && !connected.contains(e.u)) {
236+
connected.add(e.u);
237+
changed = true;
238+
}
239+
}
240+
}
241+
assertThat(connected).hasSize(4);
242+
}
243+
244+
@Test
245+
public void testParallelEdges() {
246+
// Multiple edges between same nodes
247+
Edge[] graph =
248+
new Edge[] {
249+
new Edge(0, 1, 5),
250+
new Edge(0, 1, 3),
251+
new Edge(0, 1, 7),
252+
new Edge(1, 2, 2)
253+
};
254+
Boruvkas solver = new Boruvkas(3, graph);
255+
// Should pick the minimum weight edge between 0 and 1
256+
assertThat(solver.getMstCost()).isEqualTo(5L);
257+
assertThat(solver.getMst()).hasSize(2);
258+
}
259+
}

0 commit comments

Comments
 (0)