1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1996-2012. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: -module(digraph_SUITE).
   20: 
   21: %-define(STANDALONE,1).
   22: 
   23: -ifdef(STANDALONE).
   24: -define(line, put(line, ?LINE), ).
   25: -else.
   26: -include_lib("test_server/include/test_server.hrl").
   27: -endif.
   28: 
   29: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   30: 	 init_per_group/2,end_per_group/2]).
   31: 
   32: -export([opts/1, degree/1, path/1, cycle/1, vertices/1,
   33: 	 edges/1, data/1, otp_3522/1, otp_3630/1, otp_8066/1]).
   34: 
   35: -export([spawn_graph/2]).
   36: 
   37: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   38: 
   39: suite() -> [{ct_hooks,[ts_install_cth]}].
   40: 
   41: all() -> 
   42:     [opts, degree, path, cycle, {group, misc},
   43:      {group, tickets}].
   44: 
   45: groups() -> 
   46:     [{misc, [], [vertices, edges, data]},
   47:      {tickets, [], [otp_3522, otp_3630, otp_8066]}].
   48: 
   49: init_per_suite(Config) ->
   50:     Config.
   51: 
   52: end_per_suite(_Config) ->
   53:     ok.
   54: 
   55: init_per_group(_GroupName, Config) ->
   56:     Config.
   57: 
   58: end_per_group(_GroupName, Config) ->
   59:     Config.
   60: 
   61: 
   62: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   63: 
   64: opts(doc) -> [];
   65: opts(suite) -> [];
   66: opts(Config) when is_list(Config) ->
   67:     %% OTP-5985: the 'public' option has been removed
   68:     ?line {'EXIT',{badarg,_}} = (catch digraph:new([public])),
   69:     ?line {P2,G2} = spawn_graph([private]),
   70:     ?line {'EXIT',{badarg,_}} = (catch digraph:add_vertex(G2, x)),
   71:     ?line kill_graph(P2),
   72:     ?line {P3,G3} = spawn_graph([protected]),
   73:     ?line {'EXIT',{badarg,_}} = (catch digraph:add_vertex(G3, x)),
   74:     ?line kill_graph(P3),
   75:     ?line Template = [{v1,[v2]}, {v2,[v3]}, {v3,[v4]}, {v4,[]}],
   76:     ?line G4 = build_graph([], Template),
   77:     ?line e = digraph:add_edge(G4, e, v4, v1, []),
   78:     ?line digraph:delete(G4),
   79:     ?line G5 = build_graph([cyclic], Template),
   80:     ?line e = digraph:add_edge(G5, e, v4, v1, []),
   81:     ?line digraph:delete(G5),
   82:     ?line G6 = build_graph([acyclic], Template),
   83:     ?line acyclic = info(G6, cyclicity),
   84:     ?line {error, {bad_edge,_}} = digraph:add_edge(G6, v4, v1),
   85:     ?line digraph:delete(G6),
   86:     ok.
   87:     
   88: 
   89: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   90: 
   91: degree(doc) -> [];
   92: degree(suite) -> [];
   93: degree(Config) when is_list(Config) ->
   94:     ?line G = build_graph([], [{x1,[]}, {x2,[x1]}, {x3,[x1,x2]},
   95: 			       {x4,[x1,x2,x3]}, {x5,[x1,x2,x3,x4]}]),
   96:     %% out degree
   97:     ?line 0 = digraph:out_degree(G, x1),
   98:     ?line 1 = digraph:out_degree(G, x2),
   99:     ?line 2 = digraph:out_degree(G, x3),
  100:     ?line 3 = digraph:out_degree(G, x4),
  101:     ?line 4 = digraph:out_degree(G, x5),
  102:     %% out neighbours
  103:     ?line [] = check(digraph:out_neighbours(G, x1), []),
  104:     ?line [] = check(digraph:out_neighbours(G, x2), [x1]),
  105:     ?line [] = check(digraph:out_neighbours(G, x3), [x1,x2]),
  106:     ?line [] = check(digraph:out_neighbours(G, x4), [x1,x2,x3]),
  107:     ?line [] = check(digraph:out_neighbours(G, x5), [x1,x2,x3,x4]),
  108: 
  109:     %% in degree
  110:     ?line 4 = digraph:in_degree(G, x1),
  111:     ?line 3 = digraph:in_degree(G, x2),
  112:     ?line 2 = digraph:in_degree(G, x3),
  113:     ?line 1 = digraph:in_degree(G, x4),
  114:     ?line 0 = digraph:in_degree(G, x5),
  115:     %% in neighbours
  116:     ?line [] = check(digraph:in_neighbours(G, x1), [x2,x3,x4,x5]),
  117:     ?line [] = check(digraph:in_neighbours(G, x2), [x3,x4,x5]),
  118:     ?line [] = check(digraph:in_neighbours(G, x3), [x4,x5]),
  119:     ?line [] = check(digraph:in_neighbours(G, x4), [x5]),
  120:     ?line [] = check(digraph:in_neighbours(G, x5), []),
  121:     digraph:delete(G),
  122:     ok.
  123: 
  124: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  125: 
  126: path(doc) -> [];
  127: path(suite) -> [];
  128: path(Config) when is_list(Config) ->
  129:     ?line G = build_graph([], [{x1,[x2,x3]}, {x2,[x4]}, {x3,[x4]},
  130: 			       {x4,[x5,x6]}, {x5,[x7]}, {x6,[x7]}]),
  131:     ?line Vi = case digraph:get_path(G, x1, x7) of
  132: 		   [x1,x2,x4,x5,x7] -> digraph:del_vertex(G, x5), x6;
  133: 		   [x1,x2,x4,x6,x7] -> digraph:del_vertex(G, x6), x5;
  134: 		   [x1,x3,x4,x5,x7] -> digraph:del_vertex(G, x5), x6;
  135: 		   [x1,x3,x4,x6,x7] -> digraph:del_vertex(G, x6), x5
  136: 	       end,
  137:     ?line Vj = case digraph:get_path(G, x1, x7) of
  138: 		   [x1,x2,x4,Vi,x7] -> digraph:del_vertex(G,x2), x3;
  139: 		   [x1,x3,x4,Vi,x7] -> digraph:del_vertex(G,x3), x2
  140: 	       end,
  141:     ?line [x1,Vj,x4,Vi,x7] = digraph:get_path(G, x1, x7),
  142:     ?line digraph:del_vertex(G, Vj),
  143:     ?line false = digraph:get_path(G, x1, x7),
  144:     ?line [] = check(digraph:vertices(G), [x1,x4,Vi,x7]),
  145:     digraph:delete(G),
  146:     ok.
  147: 
  148: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  149: 
  150: cycle(doc) -> [];
  151: cycle(suite) -> [];
  152: cycle(Config) when is_list(Config) ->
  153:     ?line G = build_graph([], [{x1,[x2,x3]}, {x2,[x4]}, {x3,[x4]},
  154: 			       {x4,[x5,x6]}, {x5,[x7]}, {x6,[x7,x8]},
  155: 			       {x8,[x3,x8]}]), 
  156:     ?line false = digraph:get_cycle(G, x1),
  157:     ?line false = digraph:get_cycle(G, x2),
  158:     ?line false = digraph:get_cycle(G, x5),
  159:     ?line false = digraph:get_cycle(G, x7),
  160:     ?line [x3,x4,x6,x8,x3] = digraph:get_cycle(G, x3),
  161:     ?line [x4,x6,x8,x3,x4] = digraph:get_cycle(G, x4),
  162:     ?line [x6,x8,x3,x4,x6] = digraph:get_cycle(G, x6),
  163:     ?line [x8,x3,x4,x6,x8] = digraph:get_cycle(G, x8),
  164:     ?line digraph:del_vertex(G, x4),
  165:     ?line [x8] = digraph:get_cycle(G, x8),
  166:     digraph:delete(G),
  167:     ok.
  168: 
  169: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  170: 
  171: 
  172: 
  173: vertices(doc) -> [];
  174: vertices(suite) -> [];
  175: vertices(Config) when is_list(Config) ->
  176:     ?line G = build_graph([], [{x,[]}, {y,[]}]),
  177:     ?line [] = check(digraph:vertices(G), [x,y]),
  178:     ?line digraph:del_vertices(G, [x,y]),
  179:     ?line [] = digraph:vertices(G),
  180:     ?line digraph:delete(G),
  181:     ok.
  182: 
  183: edges(doc) -> [];
  184: edges(suite) -> [];
  185: edges(Config) when is_list(Config) ->
  186:     ?line G = build_graph([], [{x, [{exy,y},{exx,x}]},
  187: 			       {y, [{eyx,x}]}
  188: 			      ]),
  189:     ?line [] = check(digraph:edges(G), [exy, eyx, exx]),
  190:     ?line [] = check(digraph:out_edges(G, x), [exy,exx]),
  191:     ?line [] = check(digraph:in_edges(G, x), [eyx,exx]),
  192:     ?line [] = check(digraph:out_edges(G, y), [eyx]),
  193:     ?line [] = check(digraph:in_edges(G, y), [exy]),
  194:     ?line true = digraph:del_edges(G, [exy, eyx, does_not_exist]),
  195:     ?line [exx] = digraph:edges(G),
  196:     ?line [] = check(digraph:out_edges(G, x), [exx]),
  197:     ?line [] = check(digraph:in_edges(G, x), [exx]),
  198:     ?line [] = check(digraph:out_edges(G, y), []),
  199:     ?line [] = check(digraph:in_edges(G, y), []),
  200:     ?line digraph:del_vertices(G, [x,y]),
  201:     ?line [] = digraph:edges(G),
  202:     ?line [] = digraph:vertices(G),
  203:     ?line digraph:delete(G),
  204:     ok.
  205: 
  206: data(doc) -> [];
  207: data(suite) -> [];
  208: data(Config) when is_list(Config) ->
  209:     ?line G = build_graph([], [{x, [{exy, y}]}, {y, []}]),
  210:     
  211:     ?line {x,[]} = digraph:vertex(G, x),
  212:     ?line {y,[]} = digraph:vertex(G, y),
  213:     ?line {exy,x,y,[]} = digraph:edge(G, exy),
  214: 
  215:     ?line digraph:add_edge(G, exy, x, y, {data,x,y}),
  216:     ?line E = digraph:add_edge(G, x, y, {data,y,x}),
  217:     ?line digraph:add_vertex(G, x, {any}),
  218:     ?line digraph:add_vertex(G, y, '_'),
  219: 
  220:     ?line {x,{any}} = digraph:vertex(G, x),
  221:     ?line {y,'_'} = digraph:vertex(G, y),
  222:     ?line {exy,x,y,{data,x,y}} = digraph:edge(G, exy),
  223:     ?line {E,x,y,{data,y,x}} = digraph:edge(G, E),
  224:     ?line true = digraph:del_edge(G, E),
  225:     ?line false = digraph:edge(G, E),
  226:     ?line true = sane(G),
  227:     ?line digraph:delete(G),
  228:     ok.
  229: 
  230: 
  231: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  232: 
  233: 
  234: 
  235: otp_3522(doc) -> [];
  236: otp_3522(suite) -> [];
  237: otp_3522(Config) when is_list(Config) ->
  238:     ?line G1 = build_graph([acyclic], [{x, []}]),
  239:     ?line {error, {bad_edge,_}} = digraph:add_edge(G1, x, x),
  240:     ?line true = digraph:delete(G1),
  241: 
  242:     ?line G = digraph:new(),
  243:     ?line 0 = digraph:no_vertices(G),
  244:     ?line 0 = digraph:no_edges(G),
  245:     ?line V1 = digraph:add_vertex(G),
  246:     ?line '$vid' = digraph:add_vertex(G, '$vid'),
  247:     ?line V2 = digraph:add_vertex(G),
  248:     ?line '$eid' = digraph:add_edge(G, '$eid', V1, V2, []),
  249:     ?line E = digraph:add_edge(G, V1, V2),
  250:     ?line 3 = digraph:no_vertices(G),
  251:     ?line 2 = digraph:no_edges(G),
  252:     ?line cyclic = info(G, cyclicity),
  253:     ?line protected = info(G, protection),
  254: 
  255:     ?line [] = check(digraph:in_edges(G, V2), ['$eid', E]),
  256:     ?line [] = check(digraph:out_edges(G, V1), ['$eid', E]),
  257:     ?line [] = check(digraph:vertices(G), [V1,V2,'$vid']),
  258:     ?line [] = check(digraph:edges(G), [E, '$eid']),
  259:     ?line true = sane(G),
  260:     ?line true = digraph:delete(G),
  261:     ok.
  262: 
  263: otp_3630(doc) -> [];
  264: otp_3630(suite) -> [];
  265: otp_3630(Config) when is_list(Config) ->
  266:     ?line G = build_graph([], [{x, [{exy,y},{exx,x}]},
  267: 			       {y, [{eyy,y},{eyx,x}]}
  268: 			      ]),
  269:     ?line [x,y] = digraph:get_path(G, x, y),
  270:     ?line [y,x] = digraph:get_path(G, y, x),
  271: 
  272:     ?line [x,x] = digraph:get_short_path(G, x, x),
  273:     ?line [y,y] = digraph:get_short_path(G, y, y),
  274:     ?line true = digraph:delete(G),
  275: 
  276:     ?line G1 = build_graph([], [{1, [{12,2},{13,3},{11,1}]},
  277: 				{2, [{23,3}]},
  278: 				{3, [{34,4},{35,5}]},
  279: 				{4, [{45,5}]},
  280: 				{5, [{56,6},{57,7}]},
  281: 				{6, [{67,7}]},
  282: 				{7, [{71,1}]}
  283: 			       ]),
  284:     
  285:     ?line [1,3,5,7] = digraph:get_short_path(G1, 1, 7),
  286:     ?line [3,5,7,1,3] = digraph:get_short_cycle(G1, 3),
  287:     ?line [1,1] = digraph:get_short_cycle(G1, 1),
  288:     ?line true = digraph:delete(G1),
  289: 
  290:     F = 0.0, I = round(F),
  291:     ?line G2 = digraph:new([acyclic]),
  292:     ?line digraph:add_vertex(G2, F),
  293:     ?line digraph:add_vertex(G2, I),
  294:     ?line E = digraph:add_edge(G2, F, I),
  295:     ?line true = not is_tuple(E),
  296:     ?line true = sane(G2),
  297:     ?line true = digraph:delete(G2),
  298: 
  299:     ok.
  300: 
  301: otp_8066(doc) -> [];
  302: otp_8066(suite) -> [];
  303: otp_8066(Config) when is_list(Config) ->
  304:     fun() ->
  305:             D = digraph:new(),
  306:             V1 = digraph:add_vertex(D),
  307:             V2 = digraph:add_vertex(D),
  308:             _ = digraph:add_edge(D, V1, V2),
  309:             ?line [V1, V2] = digraph:get_path(D, V1, V2),
  310:             ?line true = sane(D),
  311:             ?line true = digraph:del_path(D, V1, V2),
  312:             ?line true = sane(D),
  313:             ?line false = digraph:get_path(D, V1, V2),
  314:             ?line true = digraph:del_path(D, V1, V2),
  315:             ?line true = digraph:delete(D)
  316:     end(),
  317: 
  318:     fun() ->
  319:             D = digraph:new(),
  320:             V1 = digraph:add_vertex(D),
  321:             V2 = digraph:add_vertex(D),
  322:             _ = digraph:add_edge(D, V1, V2),
  323:             _ = digraph:add_edge(D, V1, V2),
  324:             _ = digraph:add_edge(D, V1, V1),
  325:             _ = digraph:add_edge(D, V2, V2),
  326:             ?line [V1, V2] = digraph:get_path(D, V1, V2),
  327:             ?line true = sane(D),
  328:             ?line true = digraph:del_path(D, V1, V2),
  329:             ?line false = digraph:get_short_path(D, V2, V1),
  330: 
  331:             ?line true = sane(D),
  332:             ?line false = digraph:get_path(D, V1, V2),
  333:             ?line true = digraph:del_path(D, V1, V2),
  334:             ?line true = digraph:delete(D)
  335:     end(),
  336: 
  337:     fun() ->
  338:             G = digraph:new(),
  339:             W1 = digraph:add_vertex(G),
  340:             W2 = digraph:add_vertex(G),
  341:             W3 = digraph:add_vertex(G),
  342:             W4 = digraph:add_vertex(G),
  343:             _ = digraph:add_edge(G,['$e'|0], W1, W2, {}),
  344:             ?line {error,{bad_vertex, bv}} =
  345:                 digraph:add_edge(G, edge, bv, W1, {}),
  346:             ?line {error,{bad_vertex, bv}} =
  347:                 digraph:add_edge(G, edge, W1, bv, {}),
  348:             ?line false = digraph:get_short_cycle(G, W1),
  349:             ?line {error, {bad_edge,_}} =
  350:                 digraph:add_edge(G,['$e'|0], W3, W4, {}),
  351:             ?line true = sane(G),
  352:             ?line true = digraph:delete(G)
  353:     end(),
  354:     ok.
  355:     
  356: 
  357: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  358: 
  359: sane(G) ->
  360:     sane1(G),
  361:     erase(sane) =:= undefined.
  362: 
  363: sane1(G) ->
  364:     %% etab: {E, V1, V2, Label}
  365:     %% ntab: {{out,V},E} eller {{in,V},E}
  366:     %% vtab: {V,Label}
  367: 
  368:     Es = digraph:edges(G),
  369:     Vs = digraph:vertices(G),
  370:     VEs = lists:flatmap(fun(V) -> digraph:edges(G, V) end, Vs),
  371:     case lists:sort(Es++Es) =:= lists:sort(VEs) of
  372:         true -> ok;
  373:         false ->
  374:             io:format("Bad edges~n", []), put(sane, no)
  375:     end,
  376: 
  377:     lists:foreach(
  378:       fun(E) ->
  379:               Edge = {E, V1, V2, _L} = digraph:edge(G, E),
  380:               case {digraph:vertex(G, V1), digraph:vertex(G, V2)} of
  381:                   {{V1, _}, {V2, _}} -> ok;
  382:                   _ -> io:format("Missing vertex ~p~n", [Edge]), put(sane, no)
  383:               end,
  384:               In = digraph:in_edges(G, V2),
  385:               case lists:member(E, In) of 
  386:                   true -> ok;
  387:                   false ->
  388:                       io:format("Missing in-neighbour ~p~n", [Edge]),
  389:                       put(sane, no)
  390:               end,
  391:               Out = digraph:out_edges(G, V1),
  392:               case lists:member(E, Out) of 
  393:                   true -> ok;
  394:                   false ->
  395:                       io:format("Missing out-neighbour ~p~n", [Edge]),
  396:                       put(sane, no)
  397:               end
  398:       end, Es),
  399: 
  400:     lists:foreach(
  401:       fun(V) ->
  402:               InEs = digraph:in_edges(G, V),
  403:               %% *All* in-edoges of V
  404:               lists:foreach(
  405:                 fun(E) ->
  406:                         case digraph:edge(G, E) of
  407:                             {E, _, V, _} -> ok;
  408:                             _ -> 
  409:                                 io:format("Bad in-edge ~p: ~p~n", [V, E]),
  410:                                 put(sane, no)
  411:                         end
  412:                 end, InEs),
  413:               OutEs = digraph:out_edges(G, V),
  414:               lists:foreach(
  415:                 fun(E) ->
  416:                         case digraph:edge(G, E) of
  417:                             {E, V, _, _} -> ok;
  418:                             _ -> 
  419:                                 io:format("Bad out-edge ~p: ~p~n", [V, E]),
  420:                                 put(sane, no)
  421:                         end
  422:                 end, OutEs)
  423:       end, Vs),
  424:     
  425:     InEs = lists:flatmap(fun(V) -> digraph:in_edges(G, V) end, Vs),
  426:     OutEs = lists:flatmap(fun(V) -> digraph:out_edges(G, V) end, Vs),
  427:     lists:foreach(
  428:       fun(E) ->
  429:               case digraph:edge(G, E) of
  430:                   {E, _, _, _} -> ok;
  431:                   _ -> 
  432:                       io:format("Unknown edge (neighbour) ~p~n", [E]),
  433:                       put(sane, no)
  434:               end
  435:       end, InEs++OutEs),
  436: 
  437:     N_in = length(InEs),
  438:     N_out = length(OutEs),
  439:     N_edges = digraph:no_edges(G),
  440:     if 
  441:         N_in =/= N_out ->
  442:             io:format("Number of in- and out-edges differs~n", []),
  443:             put(sane, no);
  444:         N_in+N_out =/= N_edges+N_edges  ->
  445:             io:format("Invalid number of edges (~p+~p =/= 2*~p)~n",
  446:                       [N_in, N_out, N_edges]),
  447:             put(sane, no);
  448:         true -> ok
  449:     end,
  450:     Edges = [digraph:edge(G, E) || E <- Es],
  451:     EVs = lists:usort([V || {_, V, _, _} <- Edges] ++
  452:                       [V || {_, _, V, _} <- Edges]),
  453:     lists:foreach(
  454:       fun(V) ->
  455:               case digraph:vertex(G, V) of
  456:                   {_, _} -> ok;
  457:                   false ->
  458:                       io:format("Unknown vertex in edge: ~p~n", [V]),
  459:                       put(sane, no)
  460:               end
  461:       end, EVs),
  462: 
  463:     %% sink_vertices and source_vertices were introduced in 2001. They
  464:     %% are not documented.
  465: 
  466:     %% sink: a vertex with no outgoing edges
  467:     SinkVs = [V || V <- Vs, digraph:out_edges(G, V) =:= [] ],
  468:     case lists:sort(SinkVs) =:=  lists:sort(digraph:sink_vertices(G)) of
  469:         true -> ok;
  470:         false -> 
  471:             io:format("Bad sinks~n"), put(sane, no)
  472:     end,
  473:     %% sink: a vertex with no incoming edges
  474:     SourceVs = [V || V <- Vs, digraph:in_edges(G, V) =:= [] ],
  475:     case lists:sort(SourceVs) =:=  lists:sort(digraph:source_vertices(G)) of
  476:         true -> ok;
  477:         false -> 
  478:             io:format("Bad sources~n"), put(sane, no)
  479:     end,
  480: 
  481:     true.
  482: 
  483: build_graph(Opts, Gs) ->
  484:     G = digraph:new(Opts),
  485:     build_g(G, Gs).
  486: 
  487: build_g(G, [{V,Ns} | Gs]) ->
  488:     digraph:add_vertex(G, V),
  489:     build_ns(G, V, Ns),
  490:     build_g(G, Gs);
  491: build_g(G, []) -> 
  492:     true = sane(G),
  493:     G.
  494: 
  495: build_ns(G, V, [{E,W} | Ns]) ->
  496:     digraph:add_vertex(G, W),
  497:     digraph:add_edge(G, E, V, W, []),
  498:     build_ns(G, V, Ns);
  499: build_ns(G, V, [W | Ns]) ->
  500:     digraph:add_vertex(G, W),
  501:     digraph:add_edge(G, V, W),
  502:     build_ns(G, V, Ns);
  503: build_ns(_G, _V, []) ->
  504:     true.
  505: 
  506: %% Spawn a process that create a graph return {Pid, Graph}
  507: 
  508: spawn_graph(Opts) ->
  509:     Pid = spawn(?MODULE, spawn_graph, [self(),Opts]),
  510:     receive
  511: 	{Pid, G} -> {Pid,G}
  512:     end.
  513: 
  514: %% Create a graph and wait for die message
  515: spawn_graph(Starter, Opts) ->
  516:     G = digraph:new(Opts),
  517:     Starter ! {self(), G},
  518:     receive
  519: 	die -> true
  520:     end.
  521: 
  522: info(G, What) ->
  523:     case lists:keysearch(What, 1, digraph:info(G)) of
  524: 	{value, {What, Value}} -> Value;
  525: 	false -> []
  526:     end.
  527: 
  528: %% Kill process created by spawn_graph
  529: kill_graph(Pid) ->
  530:     Pid ! die.
  531: 
  532: check(R0, E0) ->
  533:     R = lists:sort(R0),
  534:     E = lists:sort(E0),
  535:     case R of
  536: 	E ->
  537: 	    [];
  538: 	_ ->
  539: 	    (R -- E) ++ (E -- R)
  540:     end.