diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl index c324f8ab69f2..b2f81602046b 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)). -doc """ Returns all vertices of digraph `Digraph`. The order is given by a @@ -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 1dda4d662424..ca2c5f117af6 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).