File 0552-stdlib-Fix-preorder-and-postorder-traversals-in-digr.patch of Package erlang

From f1f456cc93279aa31d7eefc73e579cc522e159f8 Mon Sep 17 00:00:00 2001
From: lucioleKi <isabell@erlang.org>
Date: Thu, 14 Nov 2024 14:58:01 +0100
Subject: [PATCH] stdlib: Fix preorder and postorder traversals in
 `digraph_utils`

The previous `digraph_utils:preorder/1` and `digraph_utils:postorder/1`
did not start the traversal from root nodes. This fix makes both
traversals only start or restart from a root node in one of the
components, or an arbitrary node if no root node can be visited.
Since in the previous version, the search can start and restart from
arbitrary nodes, this fix should not break backwards compatibility.

It is worth noting that the digraph does not have a notion of a left or
right subtree. The result of both traversals may not be the same after
relabelling vertices.
---
 lib/stdlib/src/digraph_utils.erl        | 72 +++++++++++++------------
 lib/stdlib/test/digraph_utils_SUITE.erl | 20 ++++++-
 2 files changed, 56 insertions(+), 36 deletions(-)

diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl
index c324f8ab69..b2f8160204 100644
--- a/lib/stdlib/src/digraph_utils.erl
+++ b/lib/stdlib/src/digraph_utils.erl
@@ -390,7 +390,9 @@ collecting visited vertices in preorder.
       Vertices :: [digraph:vertex()].
 
 preorder(G) ->
-    lists:reverse(revpreorder(G)).
+    T = sets:new(),
+    {_, Acc} = ptraverse(roots(G), fun out/3, G, T, [], []),
+    lists:reverse(lists:append(Acc)).
 
 -spec postorder(Digraph) -> Vertices when
       Digraph :: digraph:graph(),
@@ -405,12 +407,19 @@ vertices.
       Vertices :: [digraph:vertex()].
 
 postorder(G) ->
-    lists:reverse(revpostorder(G)).
+    T = sets:new(),
+    {Acc, _} = posttraverse(roots(G), G, T, []),
+    lists:reverse(Acc).
 
 %%
 %%  Local functions
 %%
 
+roots(G) ->
+    R1 = [V || V <- digraph:vertices(G), digraph:in_degree(G, V) =:= 0],
+    R2 = [X || [X|_] <- components(G)],
+    R1 ++ R2.
+
 forest(G, SF) ->
     forest(G, SF, digraph:vertices(G)).
 
@@ -418,53 +427,48 @@ forest(G, SF, Vs) ->
     forest(G, SF, Vs, first).
 
 forest(G, SF, Vs, HandleFirst) ->
-    T = ets:new(forest, [set]),
-    F = fun(V, LL) -> pretraverse(HandleFirst, V, SF, G, T, LL) end,
-    LL = lists:foldl(F, [], Vs),
-    ets:delete(T),
+    T = sets:new(),
+    F = fun(V, {T0, LL}) -> pretraverse(HandleFirst, V, SF, G, T0, LL) end,
+    {_, LL} = lists:foldl(F, {T, []}, Vs),
     LL.
 
 pretraverse(first, V, SF, G, T, LL) ->
     ptraverse([V], SF, G, T, [], LL);
 pretraverse(not_first, V, SF, G, T, LL) ->
-    case ets:member(T, V) of
+    case sets:is_element(V, T) of
 	false -> ptraverse(SF(G, V, []), SF, G, T, [], LL);
-	true  -> LL
+	true  -> {T, LL}
     end.
 
-ptraverse([V | Vs], SF, G, T, Rs, LL) ->
-    case ets:member(T, V) of
+ptraverse([V | Vs], SF, G, T0, Rs, LL) ->
+    case sets:is_element(V, T0) of
 	false ->
-	    ets:insert(T, {V}),
-	    ptraverse(SF(G, V, Vs), SF, G, T, [V | Rs], LL);
+	    T1 = sets:add_element(V, T0),
+	    ptraverse(SF(G, V, Vs), SF, G, T1, [V | Rs], LL);
 	true ->
-	    ptraverse(Vs, SF, G, T, Rs, LL)
+	    ptraverse(Vs, SF, G, T0, Rs, LL)
     end;
-ptraverse([], _SF, _G, _T, [], LL) ->
-    LL;
-ptraverse([], _SF, _G, _T, Rs, LL) ->
-    [Rs | LL].
-
-revpreorder(G) ->
-    lists:append(forest(G, fun out/3)).
+ptraverse([], _SF, _G, T, [], LL) ->
+    {T, LL};
+ptraverse([], _SF, _G, T, Rs, LL) ->
+    {T, [Rs | LL]}.
 
 revpostorder(G) ->
-    T = ets:new(forest, [set]),
-    L = posttraverse(digraph:vertices(G), G, T, []),
-    ets:delete(T),
+    T = sets:new(),
+    {L, _} = posttraverse(digraph:vertices(G), G, T, []),
     L.
 
-posttraverse([V | Vs], G, T, L) ->
-    L1 = case ets:member(T, V) of
-	     false ->
-		 ets:insert(T, {V}),
-		 [V | posttraverse(out(G, V, []), G, T, L)];
-	     true ->
-		 L
-	 end,
-    posttraverse(Vs, G, T, L1);
-posttraverse([], _G, _T, L) ->
-    L.
+posttraverse([V | Vs], G, T0, Acc0) ->
+    case sets:is_element(V, T0) of
+        false ->
+            T1 = sets:add_element(V, T0),
+            {Acc1, T2} = posttraverse(out(G, V, []), G, T1, Acc0),
+            posttraverse(Vs, G, T2, [V|Acc1]);
+        true ->
+            posttraverse(Vs, G, T0, Acc0)
+    end;
+posttraverse([], _G, T, Acc) ->
+    {Acc, T}.
 
 in(G, V, Vs) ->
     digraph:in_neighbours(G, V) ++ Vs.
diff --git a/lib/stdlib/test/digraph_utils_SUITE.erl b/lib/stdlib/test/digraph_utils_SUITE.erl
index 1dda4d6624..ca2c5f117a 100644
--- a/lib/stdlib/test/digraph_utils_SUITE.erl
+++ b/lib/stdlib/test/digraph_utils_SUITE.erl
@@ -30,7 +30,7 @@
 	 init_per_group/2,end_per_group/2]).
 
 -export([simple/1, loop/1, isolated/1, topsort/1, subgraph/1, 
-         condensation/1, tree/1]).
+         condensation/1, tree/1, traversals/1]).
 
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -39,7 +39,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() -> 
     [simple, loop, isolated, topsort, subgraph,
-     condensation, tree].
+     condensation, tree, traversals].
 
 groups() -> 
     [].
@@ -248,6 +248,22 @@ tree(Config) when is_list(Config) ->
 
     ok.
 
+%% OTP-9040
+traversals(Config) when is_list(Config) ->
+    G = digraph:new([]),
+    [] = digraph_utils:preorder(G),
+    [] = digraph_utils:postorder(G),
+    add_edges(G, [{a,b},{b,c},{c,d},{d,e}]),
+    [a,b,c,d,e] = digraph_utils:preorder(G),
+    [e,d,c,b,a] = digraph_utils:postorder(G),
+    add_edges(G, [{0,1},{1,2},{2,0}]),
+    [a,b,c,d,e,0,1,2] = digraph_utils:preorder(G),
+    [e,d,c,b,a,2,1,0] = digraph_utils:postorder(G),
+    add_edges(G, [{x,0},{y,1},{z,2}]),
+    [z,2,0,1,y,x,a,b,c,d,e] = digraph_utils:preorder(G),
+    [1,0,2,z,y,x,e,d,c,b,a] = digraph_utils:postorder(G),
+    ok.
+
 is_tree(Es) ->
     is_tree([], Es).
 
-- 
2.43.0

openSUSE Build Service is sponsored by