| author | wenzelm | 
| Wed, 15 Feb 2006 21:35:11 +0100 | |
| changeset 19060 | c814a7856121 | 
| parent 19029 | 8635700e2c9c | 
| child 19290 | 033c3ade1e84 | 
| permissions | -rw-r--r-- | 
| 6134 | 1  | 
(* Title: Pure/General/graph.ML  | 
2  | 
ID: $Id$  | 
|
| 15759 | 3  | 
Author: Markus Wenzel and Stefan Berghofer, TU Muenchen  | 
| 6134 | 4  | 
|
5  | 
Directed graphs.  | 
|
6  | 
*)  | 
|
7  | 
||
8  | 
signature GRAPH =  | 
|
9  | 
sig  | 
|
10  | 
type key  | 
|
11  | 
type 'a T  | 
|
| 9321 | 12  | 
exception DUP of key  | 
13  | 
exception DUPS of key list  | 
|
| 19029 | 14  | 
exception SAME  | 
15  | 
exception UNDEF of key  | 
|
| 6134 | 16  | 
val empty: 'a T  | 
| 6659 | 17  | 
val keys: 'a T -> key list  | 
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
18  | 
val dest: 'a T -> (key * key list) list  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
19  | 
val minimals: 'a T -> key list  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
20  | 
val maximals: 'a T -> key list  | 
| 6142 | 21  | 
  val map_nodes: ('a -> 'b) -> 'a T -> 'b T
 | 
| 17767 | 22  | 
val fold_nodes: (key * 'b -> 'a -> 'a) -> 'b T -> 'a -> 'a  | 
| 17580 | 23  | 
val fold_map_nodes: (key * 'b -> 'a -> 'c * 'a) -> 'b T -> 'a -> 'c T * 'a  | 
| 15759 | 24  | 
val get_node: 'a T -> key -> 'a (*exception UNDEF*)  | 
| 6142 | 25  | 
  val map_node: key -> ('a -> 'a) -> 'a T -> 'a T
 | 
| 17767 | 26  | 
  val map_node_yield: key -> ('a -> 'b * 'a) -> 'a T -> 'b * 'a T
 | 
| 6142 | 27  | 
val imm_preds: 'a T -> key -> key list  | 
28  | 
val imm_succs: 'a T -> key -> key list  | 
|
| 6134 | 29  | 
val all_preds: 'a T -> key list -> key list  | 
30  | 
val all_succs: 'a T -> key list -> key list  | 
|
| 
14161
 
73ad4884441f
Added function strong_conn for computing the strongly connected components
 
berghofe 
parents: 
12451 
diff
changeset
 | 
31  | 
val strong_conn: 'a T -> key list list  | 
| 17912 | 32  | 
val subgraph: key list -> 'a T -> 'a T  | 
| 6134 | 33  | 
val find_paths: 'a T -> key * key -> key list list  | 
| 15759 | 34  | 
val new_node: key * 'a -> 'a T -> 'a T (*exception DUP*)  | 
| 17179 | 35  | 
val default_node: key * 'a -> 'a T -> 'a T  | 
| 15759 | 36  | 
val del_nodes: key list -> 'a T -> 'a T (*exception UNDEF*)  | 
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
37  | 
val is_edge: 'a T -> key * key -> bool  | 
| 6134 | 38  | 
val add_edge: key * key -> 'a T -> 'a T  | 
| 6152 | 39  | 
val del_edge: key * key -> 'a T -> 'a T  | 
| 15759 | 40  | 
  val merge: ('a * 'a -> bool) -> 'a T * 'a T -> 'a T                 (*exception DUPS*)
 | 
| 19029 | 41  | 
val join: (key -> 'a * 'a -> 'a) (*exception DUP/SAME*) ->  | 
42  | 
'a T * 'a T -> 'a T (*exception DUPS*)  | 
|
| 6142 | 43  | 
exception CYCLES of key list list  | 
| 15759 | 44  | 
val add_edge_acyclic: key * key -> 'a T -> 'a T (*exception CYCLES*)  | 
45  | 
val add_deps_acyclic: key * key list -> 'a T -> 'a T (*exception CYCLES*)  | 
|
46  | 
  val merge_acyclic: ('a * 'a -> bool) -> 'a T * 'a T -> 'a T         (*exception CYCLES*)
 | 
|
47  | 
val add_edge_trans_acyclic: key * key -> 'a T -> 'a T (*exception CYCLES*)  | 
|
48  | 
  val merge_trans_acyclic: ('a * 'a -> bool) -> 'a T * 'a T -> 'a T   (*exception CYCLES*)
 | 
|
| 6134 | 49  | 
end;  | 
50  | 
||
51  | 
functor GraphFun(Key: KEY): GRAPH =  | 
|
52  | 
struct  | 
|
53  | 
||
54  | 
(* keys *)  | 
|
55  | 
||
56  | 
type key = Key.key;  | 
|
57  | 
||
| 18970 | 58  | 
val eq_key = is_equal o Key.ord;  | 
| 6134 | 59  | 
|
| 18921 | 60  | 
val member_key = member eq_key;  | 
| 15759 | 61  | 
val remove_key = remove eq_key;  | 
| 6152 | 62  | 
|
| 6134 | 63  | 
|
64  | 
(* tables and sets of keys *)  | 
|
65  | 
||
66  | 
structure Table = TableFun(Key);  | 
|
67  | 
type keys = unit Table.table;  | 
|
68  | 
||
| 6142 | 69  | 
val empty_keys = Table.empty: keys;  | 
70  | 
||
| 18921 | 71  | 
fun member_keys tab = Table.defined (tab: keys);  | 
72  | 
fun insert_keys x tab = Table.insert (K true) (x, ()) (tab: keys);  | 
|
| 6134 | 73  | 
|
74  | 
||
| 6142 | 75  | 
(* graphs *)  | 
| 6134 | 76  | 
|
77  | 
datatype 'a T = Graph of ('a * (key list * key list)) Table.table;
 | 
|
78  | 
||
| 9321 | 79  | 
exception DUP = Table.DUP;  | 
80  | 
exception DUPS = Table.DUPS;  | 
|
| 19029 | 81  | 
exception UNDEF = Table.UNDEF;  | 
82  | 
exception SAME = Table.SAME;  | 
|
| 6134 | 83  | 
|
84  | 
val empty = Graph Table.empty;  | 
|
| 6659 | 85  | 
fun keys (Graph tab) = Table.keys tab;  | 
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
86  | 
fun dest (Graph tab) = map (fn (x, (_, (_, succs))) => (x, succs)) (Table.dest tab);  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
87  | 
|
| 16445 | 88  | 
fun minimals (Graph tab) = Table.fold (fn (m, (_, ([], _))) => cons m | _ => I) tab [];  | 
89  | 
fun maximals (Graph tab) = Table.fold (fn (m, (_, (_, []))) => cons m | _ => I) tab [];  | 
|
| 6134 | 90  | 
|
| 6142 | 91  | 
fun get_entry (Graph tab) x =  | 
| 17412 | 92  | 
(case Table.lookup tab x of  | 
| 15531 | 93  | 
SOME entry => entry  | 
94  | 
| NONE => raise UNDEF x);  | 
|
| 6134 | 95  | 
|
| 17412 | 96  | 
fun map_entry x f (G as Graph tab) = Graph (Table.update (x, f (get_entry G x)) tab);  | 
| 17767 | 97  | 
fun map_entry_yield x f (G as Graph tab) =  | 
98  | 
let val (a, node') = f (get_entry G x)  | 
|
99  | 
in (a, Graph (Table.update (x, node') tab)) end;  | 
|
| 6134 | 100  | 
|
101  | 
||
| 6142 | 102  | 
(* nodes *)  | 
103  | 
||
104  | 
fun map_nodes f (Graph tab) = Graph (Table.map (fn (i, ps) => (f i, ps)) tab);  | 
|
| 6134 | 105  | 
|
| 17767 | 106  | 
fun fold_nodes f (Graph tab) s =  | 
107  | 
Table.fold (fn (k, (i, ps)) => f (k, i)) tab s  | 
|
108  | 
||
| 17580 | 109  | 
fun fold_map_nodes f (Graph tab) s =  | 
110  | 
s  | 
|
111  | 
|> Table.fold_map (fn (k, (i, ps)) => f (k, i) #> apfst (rpair ps)) tab  | 
|
112  | 
|> apfst Graph;  | 
|
113  | 
||
| 6142 | 114  | 
fun get_node G = #1 o get_entry G;  | 
| 18133 | 115  | 
|
| 6142 | 116  | 
fun map_node x f = map_entry x (fn (i, ps) => (f i, ps));  | 
| 17767 | 117  | 
fun map_node_yield x f = map_entry_yield x (fn (i, ps) =>  | 
118  | 
let val (a, i') = f i in (a, (i', ps)) end);  | 
|
| 6142 | 119  | 
|
| 18133 | 120  | 
|
| 6142 | 121  | 
(* reachability *)  | 
122  | 
||
| 6659 | 123  | 
(*nodes reachable from xs -- topologically sorted for acyclic graphs*)  | 
| 6142 | 124  | 
fun reachable next xs =  | 
| 6134 | 125  | 
let  | 
| 
18006
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
126  | 
fun reach x (rs, R) =  | 
| 18921 | 127  | 
if member_keys R x then (rs, R)  | 
128  | 
else apfst (cons x) (fold reach (next x) (rs, insert_keys x R))  | 
|
| 
18006
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
129  | 
in fold_map (fn x => reach x o pair []) xs empty_keys end;  | 
| 6134 | 130  | 
|
| 6142 | 131  | 
(*immediate*)  | 
132  | 
fun imm_preds G = #1 o #2 o get_entry G;  | 
|
133  | 
fun imm_succs G = #2 o #2 o get_entry G;  | 
|
| 6134 | 134  | 
|
| 6142 | 135  | 
(*transitive*)  | 
| 
18006
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
136  | 
fun all_preds G = List.concat o fst o reachable (imm_preds G);  | 
| 
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
137  | 
fun all_succs G = List.concat o fst o reachable (imm_succs G);  | 
| 
14161
 
73ad4884441f
Added function strong_conn for computing the strongly connected components
 
berghofe 
parents: 
12451 
diff
changeset
 | 
138  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
139  | 
(*strongly connected components; see: David King and John Launchbury,  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
140  | 
"Structuring Depth First Search Algorithms in Haskell"*)  | 
| 
18006
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
141  | 
fun strong_conn G = filter_out null (fst (reachable (imm_preds G)  | 
| 
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
142  | 
(List.concat (rev (fst (reachable (imm_succs G) (keys G)))))));  | 
| 6134 | 143  | 
|
| 17912 | 144  | 
(*subgraph induced by node subset*)  | 
145  | 
fun subgraph keys (Graph tab) =  | 
|
146  | 
let  | 
|
147  | 
val select = member eq_key keys;  | 
|
148  | 
fun subg (k, (i, (preds, succs))) tab' =  | 
|
149  | 
if select k  | 
|
150  | 
then tab' |> Table.update (k, (i, (filter select preds, filter select succs)))  | 
|
151  | 
else tab'  | 
|
152  | 
in Table.empty |> Table.fold subg tab |> Graph end;  | 
|
| 6134 | 153  | 
|
| 18133 | 154  | 
|
| 6142 | 155  | 
(* paths *)  | 
| 6134 | 156  | 
|
157  | 
fun find_paths G (x, y) =  | 
|
158  | 
let  | 
|
| 
18006
 
535de280c812
reachable - abandoned foldl_map in favor of fold_map
 
haftmann 
parents: 
17912 
diff
changeset
 | 
159  | 
val (_, X) = reachable (imm_succs G) [x];  | 
| 6134 | 160  | 
fun paths ps p =  | 
| 12451 | 161  | 
if not (null ps) andalso eq_key (p, x) then [p :: ps]  | 
| 18921 | 162  | 
else if member_keys X p andalso not (member_key ps p)  | 
| 15570 | 163  | 
then List.concat (map (paths (p :: ps)) (imm_preds G p))  | 
| 12451 | 164  | 
else [];  | 
165  | 
in paths [] y end;  | 
|
| 6134 | 166  | 
|
167  | 
||
| 9321 | 168  | 
(* nodes *)  | 
| 6134 | 169  | 
|
| 6152 | 170  | 
fun new_node (x, info) (Graph tab) =  | 
| 17412 | 171  | 
Graph (Table.update_new (x, (info, ([], []))) tab);  | 
| 6134 | 172  | 
|
| 17179 | 173  | 
fun default_node (x, info) (Graph tab) =  | 
174  | 
Graph (Table.default (x, (info, ([], []))) tab);  | 
|
| 17140 | 175  | 
|
| 6659 | 176  | 
fun del_nodes xs (Graph tab) =  | 
| 15759 | 177  | 
Graph (tab  | 
178  | 
|> fold Table.delete xs  | 
|
179  | 
|> Table.map (fn (i, (preds, succs)) =>  | 
|
180  | 
(i, (fold remove_key xs preds, fold remove_key xs succs))));  | 
|
| 6659 | 181  | 
|
| 6152 | 182  | 
|
| 9321 | 183  | 
(* edges *)  | 
184  | 
||
| 18921 | 185  | 
fun is_edge G (x, y) = member_key (imm_succs G x) y handle UNDEF _ => false;  | 
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
186  | 
|
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
187  | 
fun add_edge (x, y) G =  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
188  | 
if is_edge G (x, y) then G  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
189  | 
else  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
190  | 
G |> map_entry y (fn (i, (preds, succs)) => (i, (x :: preds, succs)))  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
191  | 
|> map_entry x (fn (i, (preds, succs)) => (i, (preds, y :: succs)));  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
192  | 
|
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
193  | 
fun del_edge (x, y) G =  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
194  | 
if is_edge G (x, y) then  | 
| 15759 | 195  | 
G |> map_entry y (fn (i, (preds, succs)) => (i, (remove_key x preds, succs)))  | 
196  | 
|> map_entry x (fn (i, (preds, succs)) => (i, (preds, remove_key y succs)))  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
197  | 
else G;  | 
| 9321 | 198  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
199  | 
fun diff_edges G1 G2 =  | 
| 15570 | 200  | 
List.concat (dest G1 |> map (fn (x, ys) => ys |> List.mapPartial (fn y =>  | 
| 15531 | 201  | 
if is_edge G2 (x, y) then NONE else SOME (x, y))));  | 
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
202  | 
|
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
203  | 
fun edges G = diff_edges G empty;  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
204  | 
|
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
205  | 
|
| 18126 | 206  | 
(* join and merge *)  | 
207  | 
||
| 18133 | 208  | 
fun no_edges (i, _) = (i, ([], []));  | 
209  | 
||
210  | 
fun join f (Graph tab1, G2 as Graph tab2) =  | 
|
| 19029 | 211  | 
let fun join_node key ((i1, edges1), (i2, _)) = (f key (i1, i2), edges1)  | 
| 18133 | 212  | 
in fold add_edge (edges G2) (Graph (Table.join join_node (tab1, Table.map no_edges tab2))) end;  | 
| 6152 | 213  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
214  | 
fun gen_merge add eq (Graph tab1, G2 as Graph tab2) =  | 
| 18133 | 215  | 
let fun eq_node ((i1, _), (i2, _)) = eq (i1, i2)  | 
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
216  | 
in fold add (edges G2) (Graph (Table.merge eq_node (tab1, Table.map no_edges tab2))) end;  | 
| 6152 | 217  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
218  | 
fun merge eq GG = gen_merge add_edge eq GG;  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
219  | 
|
| 18133 | 220  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
221  | 
(* maintain acyclic graphs *)  | 
| 6142 | 222  | 
|
223  | 
exception CYCLES of key list list;  | 
|
| 6134 | 224  | 
|
225  | 
fun add_edge_acyclic (x, y) G =  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
226  | 
if is_edge G (x, y) then G  | 
| 9347 | 227  | 
else  | 
228  | 
(case find_paths G (y, x) of  | 
|
229  | 
[] => add_edge (x, y) G  | 
|
230  | 
| cycles => raise CYCLES (map (cons x) cycles));  | 
|
| 6134 | 231  | 
|
| 15759 | 232  | 
fun add_deps_acyclic (y, xs) = fold (fn x => add_edge_acyclic (x, y)) xs;  | 
| 9321 | 233  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
234  | 
fun merge_acyclic eq GG = gen_merge add_edge_acyclic eq GG;  | 
| 9321 | 235  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
236  | 
|
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
237  | 
(* maintain transitive acyclic graphs *)  | 
| 9321 | 238  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
239  | 
fun add_edge_trans_acyclic (x, y) G =  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
240  | 
add_edge_acyclic (x, y) G |>  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
241  | 
fold add_edge (Library.product (all_preds G [x]) (all_succs G [y]));  | 
| 9321 | 242  | 
|
| 
14793
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
243  | 
fun merge_trans_acyclic eq (G1, G2) =  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
244  | 
merge_acyclic eq (G1, G2) |>  | 
| 
 
32d94d1e4842
added dest, minimals, maximals, is_edge, add_edge/merge_trans_acyclic;
 
wenzelm 
parents: 
14161 
diff
changeset
 | 
245  | 
fold add_edge_trans_acyclic (diff_edges G1 G2 @ diff_edges G2 G1);  | 
| 6134 | 246  | 
|
247  | 
end;  | 
|
248  | 
||
249  | 
||
250  | 
(*graphs indexed by strings*)  | 
|
| 16810 | 251  | 
structure Graph = GraphFun(type key = string val ord = fast_string_ord);  |