mirror of
https://github.com/NixOS/nix.git
synced 2025-11-12 21:46:01 +01:00
fix(libstore/find-cycles): add second pass to merge multiedges
The single-pass greedy algorithm could fail to connect all edges if they arrived in certain orders. For example, edges A→B, C→D, D→A, B→C would result in two paths [D→A→B→C] and [C→D] instead of one complete cycle. Added a second pass that repeatedly tries to merge existing multiedges with each other until no more merges are possible. This ensures we find complete cycle paths regardless of edge discovery order.
This commit is contained in:
parent
8f19a6cb83
commit
7eaa4991a4
1 changed files with 30 additions and 9 deletions
|
|
@ -155,20 +155,16 @@ void transformEdgesToMultiedges(StoreCycleEdgeVec & edges, StoreCycleEdgeVec & m
|
||||||
{
|
{
|
||||||
debug("transformEdgesToMultiedges: processing %lu edges", edges.size());
|
debug("transformEdgesToMultiedges: processing %lu edges", edges.size());
|
||||||
|
|
||||||
|
// First pass: join edges to multiedges
|
||||||
for (auto & edge2 : edges) {
|
for (auto & edge2 : edges) {
|
||||||
bool edge2Joined = false;
|
bool edge2Joined = false;
|
||||||
|
|
||||||
for (auto & edge1 : multiedges) {
|
for (auto & edge1 : multiedges) {
|
||||||
debug("comparing edge1 (size=%lu) with edge2 (size=%lu)", edge1.size(), edge2.size());
|
|
||||||
|
|
||||||
// Check if edge1.back() == edge2.front()
|
// Check if edge1.back() == edge2.front()
|
||||||
// This means we can extend edge1 by appending edge2
|
// This means we can extend edge1 by appending edge2
|
||||||
if (edge1.back() == edge2.front()) {
|
if (edge1.back() == edge2.front()) {
|
||||||
debug("appending: edge1.back()='%s' == edge2.front()='%s'", edge1.back(), edge2.front());
|
|
||||||
|
|
||||||
// Append all but the first element of edge2 (to avoid duplication)
|
// Append all but the first element of edge2 (to avoid duplication)
|
||||||
for (size_t i = 1; i < edge2.size(); i++) {
|
for (size_t i = 1; i < edge2.size(); i++) {
|
||||||
debug(" appending edge2[%lu] = %s", i, edge2[i]);
|
|
||||||
edge1.push_back(edge2[i]);
|
edge1.push_back(edge2[i]);
|
||||||
}
|
}
|
||||||
edge2Joined = true;
|
edge2Joined = true;
|
||||||
|
|
@ -178,11 +174,8 @@ void transformEdgesToMultiedges(StoreCycleEdgeVec & edges, StoreCycleEdgeVec & m
|
||||||
// Check if edge2.back() == edge1.front()
|
// Check if edge2.back() == edge1.front()
|
||||||
// This means we can extend edge1 by prepending edge2
|
// This means we can extend edge1 by prepending edge2
|
||||||
if (edge2.back() == edge1.front()) {
|
if (edge2.back() == edge1.front()) {
|
||||||
debug("prepending: edge2.back()='%s' == edge1.front()='%s'", edge2.back(), edge1.front());
|
|
||||||
|
|
||||||
// Prepend all but the last element of edge2 (to avoid duplication)
|
// Prepend all but the last element of edge2 (to avoid duplication)
|
||||||
for (int i = edge2.size() - 2; i >= 0; i--) {
|
for (int i = edge2.size() - 2; i >= 0; i--) {
|
||||||
debug(" prepending edge2[%d] = %s", i, edge2[i]);
|
|
||||||
edge1.push_front(edge2[i]);
|
edge1.push_front(edge2[i]);
|
||||||
}
|
}
|
||||||
edge2Joined = true;
|
edge2Joined = true;
|
||||||
|
|
@ -191,11 +184,39 @@ void transformEdgesToMultiedges(StoreCycleEdgeVec & edges, StoreCycleEdgeVec & m
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!edge2Joined) {
|
if (!edge2Joined) {
|
||||||
debug("edge2 is new, adding as separate multiedge");
|
|
||||||
multiedges.push_back(edge2);
|
multiedges.push_back(edge2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Second pass: merge multiedges that can now be connected
|
||||||
|
// After joining edges, some multiedges might now be connectable
|
||||||
|
bool merged = true;
|
||||||
|
while (merged) {
|
||||||
|
merged = false;
|
||||||
|
for (size_t i = 0; i < multiedges.size() && !merged; i++) {
|
||||||
|
for (size_t j = i + 1; j < multiedges.size() && !merged; j++) {
|
||||||
|
auto & path1 = multiedges[i];
|
||||||
|
auto & path2 = multiedges[j];
|
||||||
|
|
||||||
|
if (path1.back() == path2.front()) {
|
||||||
|
// Append path2 to path1
|
||||||
|
for (size_t k = 1; k < path2.size(); k++) {
|
||||||
|
path1.push_back(path2[k]);
|
||||||
|
}
|
||||||
|
multiedges.erase(multiedges.begin() + j);
|
||||||
|
merged = true;
|
||||||
|
} else if (path2.back() == path1.front()) {
|
||||||
|
// Prepend path2 to path1
|
||||||
|
for (int k = path2.size() - 2; k >= 0; k--) {
|
||||||
|
path1.push_front(path2[k]);
|
||||||
|
}
|
||||||
|
multiedges.erase(multiedges.begin() + j);
|
||||||
|
merged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug("transformEdgesToMultiedges: result has %lu multiedges", multiedges.size());
|
debug("transformEdgesToMultiedges: result has %lu multiedges", multiedges.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue