6 #include <gtest/gtest.h>
30 template<
typename UnificationRootFunctor,
typename SuccessorFunctor>
34 UnificationRootFunctor & unificationRoot,
35 SuccessorFunctor & successors,
37 const std::vector<size_t> & sccIndex,
38 const std::vector<size_t> & reverseTopologicalOrder)
40 EXPECT_EQ(numNodes, sccIndex.size());
43 std::vector<size_t> numNodesInScc(numSccs, 0);
44 for (
size_t i = 0; i < numNodes; i++)
46 auto node = unificationRoot(i);
47 EXPECT_LT(sccIndex[node], numSccs);
48 numNodesInScc[sccIndex[node]]++;
50 for (
size_t i = 0; i < numSccs; i++)
51 EXPECT_GT(numNodesInScc[i], 0u);
54 for (
size_t i = 0; i < numNodes; i++)
57 if (unificationRoot(i) != i)
60 for (
auto next : successors(i))
62 next = unificationRoot(next);
65 EXPECT_LE(sccIndex[next], sccIndex[i]);
71 reverseTopologicalOrder.begin(),
72 reverseTopologicalOrder.end());
73 EXPECT_EQ(nodeInTopologicalOrder.
Size(), reverseTopologicalOrder.size());
75 for (
size_t i = 0; i < numNodes; i++)
77 if (unificationRoot(i) == i)
78 EXPECT_TRUE(nodeInTopologicalOrder.
Contains(i));
80 EXPECT_TRUE(!nodeInTopologicalOrder.
Contains(i));
84 for (
size_t i = 1; i < reverseTopologicalOrder.size(); i++)
86 EXPECT_LE(sccIndex[reverseTopologicalOrder[i - 1]], sccIndex[reverseTopologicalOrder[i]]);
90 TEST(TarjanSccTests, TestDag)
93 const size_t numNodes = 8;
94 std::vector<std::vector<size_t>> successors{
105 auto GetSuccessors = [&](
size_t node)
107 return successors[node];
110 std::vector<size_t> sccIndex;
111 std::vector<size_t> reverseTopologicalOrder;
116 reverseTopologicalOrder);
123 reverseTopologicalOrder);
125 EXPECT_EQ(numSccs, numNodes);
129 TEST(TarjanSccTests, TestCycles)
131 const size_t numNodes = 7;
132 std::vector<std::vector<size_t>> successors{
142 auto GetSuccessors = [&](
size_t node)
144 return successors[node];
147 std::vector<size_t> sccIndex;
148 std::vector<size_t> reverseTopologicalOrder;
153 reverseTopologicalOrder);
160 reverseTopologicalOrder);
162 EXPECT_EQ(numSccs, 3u);
164 EXPECT_EQ(sccIndex[5], 0u);
165 EXPECT_EQ(reverseTopologicalOrder[0], 5u);
167 EXPECT_EQ(sccIndex[6], 2u);
168 EXPECT_EQ(reverseTopologicalOrder[numNodes - 1], 6u);
170 for (
size_t i = 0; i < 5; i++)
171 EXPECT_EQ(sccIndex[i], 1u);
194 static std::tuple<size_t, size_t, std::vector<size_t>>
197 EXPECT_GE(knots, 2u);
198 const size_t numNodes = 3 * knots - 2;
199 std::vector<std::vector<size_t>> successors(numNodes);
202 std::vector<size_t> perm(numNodes);
203 for (
size_t i = 0; i < numNodes; i++)
206 perm[i] = (i * 3) % numNodes;
209 for (
size_t knot = 0; knot + 3 < numNodes; knot += 3)
211 successors[perm[knot]].push_back(perm[knot + 1]);
212 successors[perm[knot]].push_back(perm[knot + 2]);
213 successors[perm[knot + 1]].push_back(perm[knot + 3]);
214 successors[perm[knot + 2]].push_back(perm[knot + 3]);
219 EXPECT_LT(extraEdge->first, numNodes);
220 EXPECT_LT(extraEdge->second, numNodes);
222 successors[perm[extraEdge->first]].push_back(perm[extraEdge->second]);
225 auto GetSuccessors = [&](
size_t node)
227 return successors[node];
230 std::vector<size_t> sccIndex;
231 std::vector<size_t> reverseTopologicalOrder;
236 reverseTopologicalOrder);
243 reverseTopologicalOrder);
245 std::vector<size_t> unshuffledNodeIndex(numNodes);
246 for (
size_t i = 0; i < numNodes; i++)
248 unshuffledNodeIndex[i] = sccIndex[perm[i]];
251 return { numNodes, numSccs, std::move(unshuffledNodeIndex) };
254 TEST(TarjanSccTests, TestSimpleDiamondChain)
257 EXPECT_EQ(numNodes, numSccs);
260 TEST(TarjanSccTests, TestDiamondChainWithForwardEdge)
263 std::pair<size_t, size_t> forwardEdge{ 5, 200 };
265 EXPECT_EQ(numNodes, numSccs);
268 TEST(TarjanSccTests, TestDiamondChainWithBackEdge)
271 std::pair<size_t, size_t> backEdge{ 3 * 50, 3 * 3 };
274 std::cerr <<
"numSccs: " << numSccs << std::endl;
275 std::cerr <<
"numNodes: " << numNodes << std::endl;
276 EXPECT_EQ(numSccs, backEdge.second + numNodes - backEdge.first);
278 auto largeScc = sccIndex[backEdge.second];
281 for (
size_t i = 0; i < backEdge.second; i++)
282 EXPECT_GT(sccIndex[i], largeScc);
285 for (
size_t i = backEdge.first + 1; i < numNodes; i++)
286 EXPECT_LT(sccIndex[i], largeScc);
289 for (
size_t i = backEdge.second; i <= backEdge.first; i++)
290 EXPECT_EQ(sccIndex[i], largeScc);
294 TEST(TarjanSccTests, TestVisitEachNodeTwice)
296 const size_t numNodes = 5;
297 std::vector<std::vector<size_t>> successors{
306 std::vector<size_t> successorsQueried(numNodes, 0);
307 auto GetSuccessors = [&](
size_t node)
309 EXPECT_LT(node, numNodes);
310 successorsQueried[node]++;
311 return successors[node];
314 std::vector<size_t> sccIndex;
315 std::vector<size_t> reverseTopologicalOrder;
320 reverseTopologicalOrder);
322 EXPECT_EQ(numSccs, 4u);
323 for (
size_t timesQueried : successorsQueried)
324 EXPECT_LE(timesQueried, 2u);
333 reverseTopologicalOrder);
336 TEST(TarjanSccTests, TestUnifiedNodes)
339 const size_t numNodes = 10;
340 std::vector<std::vector<size_t>> successors{
349 auto GetUnificationRoot = [&](
size_t node)
356 auto GetSuccessors = [&](
size_t node)
359 return successors[node];
362 std::vector<size_t> sccIndex;
363 std::vector<size_t> reverseTopologicalOrder;
369 reverseTopologicalOrder);
371 EXPECT_EQ(numSccs, 4u);
380 reverseTopologicalOrder);
static std::tuple< size_t, size_t, std::vector< size_t > > CreateDiamondChain(size_t knots, std::optional< std::pair< size_t, size_t >> extraEdge)
static void ValidateTopologicalOrderAndSccIndices(size_t numNodes, UnificationRootFunctor &unificationRoot, SuccessorFunctor &successors, size_t numSccs, const std::vector< size_t > &sccIndex, const std::vector< size_t > &reverseTopologicalOrder)
TEST(TarjanSccTests, TestDag)
static size_t Identity(size_t i)
std::size_t Size() const noexcept
bool Contains(const ItemType &item) const noexcept
NodeType FindStronglyConnectedComponents(NodeType numNodes, UnificationRootFunctor unificationRoot, SuccessorFunctor &successors, std::vector< NodeType > &sccIndex, std::vector< NodeType > &reverseTopologicalOrder)