|
1 /* glpapi15.c (basic graph and network routines) */ |
|
2 |
|
3 /*********************************************************************** |
|
4 * This code is part of GLPK (GNU Linear Programming Kit). |
|
5 * |
|
6 * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, |
|
7 * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, |
|
8 * Moscow Aviation Institute, Moscow, Russia. All rights reserved. |
|
9 * E-mail: <mao@gnu.org>. |
|
10 * |
|
11 * GLPK is free software: you can redistribute it and/or modify it |
|
12 * under the terms of the GNU General Public License as published by |
|
13 * the Free Software Foundation, either version 3 of the License, or |
|
14 * (at your option) any later version. |
|
15 * |
|
16 * GLPK is distributed in the hope that it will be useful, but WITHOUT |
|
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
|
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
|
19 * License for more details. |
|
20 * |
|
21 * You should have received a copy of the GNU General Public License |
|
22 * along with GLPK. If not, see <http://www.gnu.org/licenses/>. |
|
23 ***********************************************************************/ |
|
24 |
|
25 #include "glpapi.h" |
|
26 |
|
27 /* CAUTION: DO NOT CHANGE THE LIMITS BELOW */ |
|
28 |
|
29 #define NV_MAX 100000000 /* = 100*10^6 */ |
|
30 /* maximal number of vertices in the graph */ |
|
31 |
|
32 #define NA_MAX 500000000 /* = 500*10^6 */ |
|
33 /* maximal number of arcs in the graph */ |
|
34 |
|
35 /*********************************************************************** |
|
36 * NAME |
|
37 * |
|
38 * glp_create_graph - create graph |
|
39 * |
|
40 * SYNOPSIS |
|
41 * |
|
42 * glp_graph *glp_create_graph(int v_size, int a_size); |
|
43 * |
|
44 * DESCRIPTION |
|
45 * |
|
46 * The routine creates a new graph, which initially is empty, i.e. has |
|
47 * no vertices and arcs. |
|
48 * |
|
49 * The parameter v_size specifies the size of data associated with each |
|
50 * vertex of the graph (0 to 256 bytes). |
|
51 * |
|
52 * The parameter a_size specifies the size of data associated with each |
|
53 * arc of the graph (0 to 256 bytes). |
|
54 * |
|
55 * RETURNS |
|
56 * |
|
57 * The routine returns a pointer to the graph created. */ |
|
58 |
|
59 static void create_graph(glp_graph *G, int v_size, int a_size) |
|
60 { G->pool = dmp_create_pool(); |
|
61 G->name = NULL; |
|
62 G->nv_max = 50; |
|
63 G->nv = G->na = 0; |
|
64 G->v = xcalloc(1+G->nv_max, sizeof(glp_vertex *)); |
|
65 G->index = NULL; |
|
66 G->v_size = v_size; |
|
67 G->a_size = a_size; |
|
68 return; |
|
69 } |
|
70 |
|
71 glp_graph *glp_create_graph(int v_size, int a_size) |
|
72 { glp_graph *G; |
|
73 if (!(0 <= v_size && v_size <= 256)) |
|
74 xerror("glp_create_graph: v_size = %d; invalid size of vertex " |
|
75 "data\n", v_size); |
|
76 if (!(0 <= a_size && a_size <= 256)) |
|
77 xerror("glp_create_graph: a_size = %d; invalid size of arc dat" |
|
78 "a\n", a_size); |
|
79 G = xmalloc(sizeof(glp_graph)); |
|
80 create_graph(G, v_size, a_size); |
|
81 return G; |
|
82 } |
|
83 |
|
84 /*********************************************************************** |
|
85 * NAME |
|
86 * |
|
87 * glp_set_graph_name - assign (change) graph name |
|
88 * |
|
89 * SYNOPSIS |
|
90 * |
|
91 * void glp_set_graph_name(glp_graph *G, const char *name); |
|
92 * |
|
93 * DESCRIPTION |
|
94 * |
|
95 * The routine glp_set_graph_name assigns a symbolic name specified by |
|
96 * the character string name (1 to 255 chars) to the graph. |
|
97 * |
|
98 * If the parameter name is NULL or an empty string, the routine erases |
|
99 * the existing symbolic name of the graph. */ |
|
100 |
|
101 void glp_set_graph_name(glp_graph *G, const char *name) |
|
102 { if (G->name != NULL) |
|
103 { dmp_free_atom(G->pool, G->name, strlen(G->name)+1); |
|
104 G->name = NULL; |
|
105 } |
|
106 if (!(name == NULL || name[0] == '\0')) |
|
107 { int j; |
|
108 for (j = 0; name[j] != '\0'; j++) |
|
109 { if (j == 256) |
|
110 xerror("glp_set_graph_name: graph name too long\n"); |
|
111 if (iscntrl((unsigned char)name[j])) |
|
112 xerror("glp_set_graph_name: graph name contains invalid " |
|
113 "character(s)\n"); |
|
114 } |
|
115 G->name = dmp_get_atom(G->pool, strlen(name)+1); |
|
116 strcpy(G->name, name); |
|
117 } |
|
118 return; |
|
119 } |
|
120 |
|
121 /*********************************************************************** |
|
122 * NAME |
|
123 * |
|
124 * glp_add_vertices - add new vertices to graph |
|
125 * |
|
126 * SYNOPSIS |
|
127 * |
|
128 * int glp_add_vertices(glp_graph *G, int nadd); |
|
129 * |
|
130 * DESCRIPTION |
|
131 * |
|
132 * The routine glp_add_vertices adds nadd vertices to the specified |
|
133 * graph. New vertices are always added to the end of the vertex list, |
|
134 * so ordinal numbers of existing vertices remain unchanged. |
|
135 * |
|
136 * Being added each new vertex is isolated (has no incident arcs). |
|
137 * |
|
138 * RETURNS |
|
139 * |
|
140 * The routine glp_add_vertices returns an ordinal number of the first |
|
141 * new vertex added to the graph. */ |
|
142 |
|
143 int glp_add_vertices(glp_graph *G, int nadd) |
|
144 { int i, nv_new; |
|
145 if (nadd < 1) |
|
146 xerror("glp_add_vertices: nadd = %d; invalid number of vertice" |
|
147 "s\n", nadd); |
|
148 if (nadd > NV_MAX - G->nv) |
|
149 xerror("glp_add_vertices: nadd = %d; too many vertices\n", |
|
150 nadd); |
|
151 /* determine new number of vertices */ |
|
152 nv_new = G->nv + nadd; |
|
153 /* increase the room, if necessary */ |
|
154 if (G->nv_max < nv_new) |
|
155 { glp_vertex **save = G->v; |
|
156 while (G->nv_max < nv_new) |
|
157 { G->nv_max += G->nv_max; |
|
158 xassert(G->nv_max > 0); |
|
159 } |
|
160 G->v = xcalloc(1+G->nv_max, sizeof(glp_vertex *)); |
|
161 memcpy(&G->v[1], &save[1], G->nv * sizeof(glp_vertex *)); |
|
162 xfree(save); |
|
163 } |
|
164 /* add new vertices to the end of the vertex list */ |
|
165 for (i = G->nv+1; i <= nv_new; i++) |
|
166 { glp_vertex *v; |
|
167 G->v[i] = v = dmp_get_atom(G->pool, sizeof(glp_vertex)); |
|
168 v->i = i; |
|
169 v->name = NULL; |
|
170 v->entry = NULL; |
|
171 if (G->v_size == 0) |
|
172 v->data = NULL; |
|
173 else |
|
174 { v->data = dmp_get_atom(G->pool, G->v_size); |
|
175 memset(v->data, 0, G->v_size); |
|
176 } |
|
177 v->temp = NULL; |
|
178 v->in = v->out = NULL; |
|
179 } |
|
180 /* set new number of vertices */ |
|
181 G->nv = nv_new; |
|
182 /* return the ordinal number of the first vertex added */ |
|
183 return nv_new - nadd + 1; |
|
184 } |
|
185 |
|
186 /**********************************************************************/ |
|
187 |
|
188 void glp_set_vertex_name(glp_graph *G, int i, const char *name) |
|
189 { /* assign (change) vertex name */ |
|
190 glp_vertex *v; |
|
191 if (!(1 <= i && i <= G->nv)) |
|
192 xerror("glp_set_vertex_name: i = %d; vertex number out of rang" |
|
193 "e\n", i); |
|
194 v = G->v[i]; |
|
195 if (v->name != NULL) |
|
196 { if (v->entry != NULL) |
|
197 { xassert(G->index != NULL); |
|
198 avl_delete_node(G->index, v->entry); |
|
199 v->entry = NULL; |
|
200 } |
|
201 dmp_free_atom(G->pool, v->name, strlen(v->name)+1); |
|
202 v->name = NULL; |
|
203 } |
|
204 if (!(name == NULL || name[0] == '\0')) |
|
205 { int k; |
|
206 for (k = 0; name[k] != '\0'; k++) |
|
207 { if (k == 256) |
|
208 xerror("glp_set_vertex_name: i = %d; vertex name too lon" |
|
209 "g\n", i); |
|
210 if (iscntrl((unsigned char)name[k])) |
|
211 xerror("glp_set_vertex_name: i = %d; vertex name contain" |
|
212 "s invalid character(s)\n", i); |
|
213 } |
|
214 v->name = dmp_get_atom(G->pool, strlen(name)+1); |
|
215 strcpy(v->name, name); |
|
216 if (G->index != NULL) |
|
217 { xassert(v->entry == NULL); |
|
218 v->entry = avl_insert_node(G->index, v->name); |
|
219 avl_set_node_link(v->entry, v); |
|
220 } |
|
221 } |
|
222 return; |
|
223 } |
|
224 |
|
225 /*********************************************************************** |
|
226 * NAME |
|
227 * |
|
228 * glp_add_arc - add new arc to graph |
|
229 * |
|
230 * SYNOPSIS |
|
231 * |
|
232 * glp_arc *glp_add_arc(glp_graph *G, int i, int j); |
|
233 * |
|
234 * DESCRIPTION |
|
235 * |
|
236 * The routine glp_add_arc adds a new arc to the specified graph. |
|
237 * |
|
238 * The parameters i and j specify the ordinal numbers of, resp., tail |
|
239 * and head vertices of the arc. Note that self-loops and multiple arcs |
|
240 * are allowed. |
|
241 * |
|
242 * RETURNS |
|
243 * |
|
244 * The routine glp_add_arc returns a pointer to the arc added. */ |
|
245 |
|
246 glp_arc *glp_add_arc(glp_graph *G, int i, int j) |
|
247 { glp_arc *a; |
|
248 if (!(1 <= i && i <= G->nv)) |
|
249 xerror("glp_add_arc: i = %d; tail vertex number out of range\n" |
|
250 , i); |
|
251 if (!(1 <= j && j <= G->nv)) |
|
252 xerror("glp_add_arc: j = %d; head vertex number out of range\n" |
|
253 , j); |
|
254 if (G->na == NA_MAX) |
|
255 xerror("glp_add_arc: too many arcs\n"); |
|
256 a = dmp_get_atom(G->pool, sizeof(glp_arc)); |
|
257 a->tail = G->v[i]; |
|
258 a->head = G->v[j]; |
|
259 if (G->a_size == 0) |
|
260 a->data = NULL; |
|
261 else |
|
262 { a->data = dmp_get_atom(G->pool, G->a_size); |
|
263 memset(a->data, 0, G->a_size); |
|
264 } |
|
265 a->temp = NULL; |
|
266 a->t_prev = NULL; |
|
267 a->t_next = G->v[i]->out; |
|
268 if (a->t_next != NULL) a->t_next->t_prev = a; |
|
269 a->h_prev = NULL; |
|
270 a->h_next = G->v[j]->in; |
|
271 if (a->h_next != NULL) a->h_next->h_prev = a; |
|
272 G->v[i]->out = G->v[j]->in = a; |
|
273 G->na++; |
|
274 return a; |
|
275 } |
|
276 |
|
277 /*********************************************************************** |
|
278 * NAME |
|
279 * |
|
280 * glp_del_vertices - delete vertices from graph |
|
281 * |
|
282 * SYNOPSIS |
|
283 * |
|
284 * void glp_del_vertices(glp_graph *G, int ndel, const int num[]); |
|
285 * |
|
286 * DESCRIPTION |
|
287 * |
|
288 * The routine glp_del_vertices deletes vertices along with all |
|
289 * incident arcs from the specified graph. Ordinal numbers of vertices |
|
290 * to be deleted should be placed in locations num[1], ..., num[ndel], |
|
291 * ndel > 0. |
|
292 * |
|
293 * Note that deleting vertices involves changing ordinal numbers of |
|
294 * other vertices remaining in the graph. New ordinal numbers of the |
|
295 * remaining vertices are assigned under the assumption that the |
|
296 * original order of vertices is not changed. */ |
|
297 |
|
298 void glp_del_vertices(glp_graph *G, int ndel, const int num[]) |
|
299 { glp_vertex *v; |
|
300 int i, k, nv_new; |
|
301 /* scan the list of vertices to be deleted */ |
|
302 if (!(1 <= ndel && ndel <= G->nv)) |
|
303 xerror("glp_del_vertices: ndel = %d; invalid number of vertice" |
|
304 "s\n", ndel); |
|
305 for (k = 1; k <= ndel; k++) |
|
306 { /* take the number of vertex to be deleted */ |
|
307 i = num[k]; |
|
308 /* obtain pointer to i-th vertex */ |
|
309 if (!(1 <= i && i <= G->nv)) |
|
310 xerror("glp_del_vertices: num[%d] = %d; vertex number out o" |
|
311 "f range\n", k, i); |
|
312 v = G->v[i]; |
|
313 /* check that the vertex is not marked yet */ |
|
314 if (v->i == 0) |
|
315 xerror("glp_del_vertices: num[%d] = %d; duplicate vertex nu" |
|
316 "mbers not allowed\n", k, i); |
|
317 /* erase symbolic name assigned to the vertex */ |
|
318 glp_set_vertex_name(G, i, NULL); |
|
319 xassert(v->name == NULL); |
|
320 xassert(v->entry == NULL); |
|
321 /* free vertex data, if allocated */ |
|
322 if (v->data != NULL) |
|
323 dmp_free_atom(G->pool, v->data, G->v_size); |
|
324 /* delete all incoming arcs */ |
|
325 while (v->in != NULL) |
|
326 glp_del_arc(G, v->in); |
|
327 /* delete all outgoing arcs */ |
|
328 while (v->out != NULL) |
|
329 glp_del_arc(G, v->out); |
|
330 /* mark the vertex to be deleted */ |
|
331 v->i = 0; |
|
332 } |
|
333 /* delete all marked vertices from the vertex list */ |
|
334 nv_new = 0; |
|
335 for (i = 1; i <= G->nv; i++) |
|
336 { /* obtain pointer to i-th vertex */ |
|
337 v = G->v[i]; |
|
338 /* check if the vertex is marked */ |
|
339 if (v->i == 0) |
|
340 { /* it is marked, delete it */ |
|
341 dmp_free_atom(G->pool, v, sizeof(glp_vertex)); |
|
342 } |
|
343 else |
|
344 { /* it is not marked, keep it */ |
|
345 v->i = ++nv_new; |
|
346 G->v[v->i] = v; |
|
347 } |
|
348 } |
|
349 /* set new number of vertices in the graph */ |
|
350 G->nv = nv_new; |
|
351 return; |
|
352 } |
|
353 |
|
354 /*********************************************************************** |
|
355 * NAME |
|
356 * |
|
357 * glp_del_arc - delete arc from graph |
|
358 * |
|
359 * SYNOPSIS |
|
360 * |
|
361 * void glp_del_arc(glp_graph *G, glp_arc *a); |
|
362 * |
|
363 * DESCRIPTION |
|
364 * |
|
365 * The routine glp_del_arc deletes an arc from the specified graph. |
|
366 * The arc to be deleted must exist. */ |
|
367 |
|
368 void glp_del_arc(glp_graph *G, glp_arc *a) |
|
369 { /* some sanity checks */ |
|
370 xassert(G->na > 0); |
|
371 xassert(1 <= a->tail->i && a->tail->i <= G->nv); |
|
372 xassert(a->tail == G->v[a->tail->i]); |
|
373 xassert(1 <= a->head->i && a->head->i <= G->nv); |
|
374 xassert(a->head == G->v[a->head->i]); |
|
375 /* remove the arc from the list of incoming arcs */ |
|
376 if (a->h_prev == NULL) |
|
377 a->head->in = a->h_next; |
|
378 else |
|
379 a->h_prev->h_next = a->h_next; |
|
380 if (a->h_next == NULL) |
|
381 ; |
|
382 else |
|
383 a->h_next->h_prev = a->h_prev; |
|
384 /* remove the arc from the list of outgoing arcs */ |
|
385 if (a->t_prev == NULL) |
|
386 a->tail->out = a->t_next; |
|
387 else |
|
388 a->t_prev->t_next = a->t_next; |
|
389 if (a->t_next == NULL) |
|
390 ; |
|
391 else |
|
392 a->t_next->t_prev = a->t_prev; |
|
393 /* free arc data, if allocated */ |
|
394 if (a->data != NULL) |
|
395 dmp_free_atom(G->pool, a->data, G->a_size); |
|
396 /* delete the arc from the graph */ |
|
397 dmp_free_atom(G->pool, a, sizeof(glp_arc)); |
|
398 G->na--; |
|
399 return; |
|
400 } |
|
401 |
|
402 /*********************************************************************** |
|
403 * NAME |
|
404 * |
|
405 * glp_erase_graph - erase graph content |
|
406 * |
|
407 * SYNOPSIS |
|
408 * |
|
409 * void glp_erase_graph(glp_graph *G, int v_size, int a_size); |
|
410 * |
|
411 * DESCRIPTION |
|
412 * |
|
413 * The routine glp_erase_graph erases the content of the specified |
|
414 * graph. The effect of this operation is the same as if the graph |
|
415 * would be deleted with the routine glp_delete_graph and then created |
|
416 * anew with the routine glp_create_graph, with exception that the |
|
417 * handle (pointer) to the graph remains valid. */ |
|
418 |
|
419 static void delete_graph(glp_graph *G) |
|
420 { dmp_delete_pool(G->pool); |
|
421 xfree(G->v); |
|
422 if (G->index != NULL) avl_delete_tree(G->index); |
|
423 return; |
|
424 } |
|
425 |
|
426 void glp_erase_graph(glp_graph *G, int v_size, int a_size) |
|
427 { if (!(0 <= v_size && v_size <= 256)) |
|
428 xerror("glp_erase_graph: v_size = %d; invalid size of vertex d" |
|
429 "ata\n", v_size); |
|
430 if (!(0 <= a_size && a_size <= 256)) |
|
431 xerror("glp_erase_graph: a_size = %d; invalid size of arc data" |
|
432 "\n", a_size); |
|
433 delete_graph(G); |
|
434 create_graph(G, v_size, a_size); |
|
435 return; |
|
436 } |
|
437 |
|
438 /*********************************************************************** |
|
439 * NAME |
|
440 * |
|
441 * glp_delete_graph - delete graph |
|
442 * |
|
443 * SYNOPSIS |
|
444 * |
|
445 * void glp_delete_graph(glp_graph *G); |
|
446 * |
|
447 * DESCRIPTION |
|
448 * |
|
449 * The routine glp_delete_graph deletes the specified graph and frees |
|
450 * all the memory allocated to this program object. */ |
|
451 |
|
452 void glp_delete_graph(glp_graph *G) |
|
453 { delete_graph(G); |
|
454 xfree(G); |
|
455 return; |
|
456 } |
|
457 |
|
458 /**********************************************************************/ |
|
459 |
|
460 void glp_create_v_index(glp_graph *G) |
|
461 { /* create vertex name index */ |
|
462 glp_vertex *v; |
|
463 int i; |
|
464 if (G->index == NULL) |
|
465 { G->index = avl_create_tree(avl_strcmp, NULL); |
|
466 for (i = 1; i <= G->nv; i++) |
|
467 { v = G->v[i]; |
|
468 xassert(v->entry == NULL); |
|
469 if (v->name != NULL) |
|
470 { v->entry = avl_insert_node(G->index, v->name); |
|
471 avl_set_node_link(v->entry, v); |
|
472 } |
|
473 } |
|
474 } |
|
475 return; |
|
476 } |
|
477 |
|
478 int glp_find_vertex(glp_graph *G, const char *name) |
|
479 { /* find vertex by its name */ |
|
480 AVLNODE *node; |
|
481 int i = 0; |
|
482 if (G->index == NULL) |
|
483 xerror("glp_find_vertex: vertex name index does not exist\n"); |
|
484 if (!(name == NULL || name[0] == '\0' || strlen(name) > 255)) |
|
485 { node = avl_find_node(G->index, name); |
|
486 if (node != NULL) |
|
487 i = ((glp_vertex *)avl_get_node_link(node))->i; |
|
488 } |
|
489 return i; |
|
490 } |
|
491 |
|
492 void glp_delete_v_index(glp_graph *G) |
|
493 { /* delete vertex name index */ |
|
494 int i; |
|
495 if (G->index != NULL) |
|
496 { avl_delete_tree(G->index), G->index = NULL; |
|
497 for (i = 1; i <= G->nv; i++) G->v[i]->entry = NULL; |
|
498 } |
|
499 return; |
|
500 } |
|
501 |
|
502 /*********************************************************************** |
|
503 * NAME |
|
504 * |
|
505 * glp_read_graph - read graph from plain text file |
|
506 * |
|
507 * SYNOPSIS |
|
508 * |
|
509 * int glp_read_graph(glp_graph *G, const char *fname); |
|
510 * |
|
511 * DESCRIPTION |
|
512 * |
|
513 * The routine glp_read_graph reads a graph from a plain text file. |
|
514 * |
|
515 * RETURNS |
|
516 * |
|
517 * If the operation was successful, the routine returns zero. Otherwise |
|
518 * it prints an error message and returns non-zero. */ |
|
519 |
|
520 int glp_read_graph(glp_graph *G, const char *fname) |
|
521 { glp_data *data; |
|
522 jmp_buf jump; |
|
523 int nv, na, i, j, k, ret; |
|
524 glp_erase_graph(G, G->v_size, G->a_size); |
|
525 xprintf("Reading graph from `%s'...\n", fname); |
|
526 data = glp_sdf_open_file(fname); |
|
527 if (data == NULL) |
|
528 { ret = 1; |
|
529 goto done; |
|
530 } |
|
531 if (setjmp(jump)) |
|
532 { ret = 1; |
|
533 goto done; |
|
534 } |
|
535 glp_sdf_set_jump(data, jump); |
|
536 nv = glp_sdf_read_int(data); |
|
537 if (nv < 0) |
|
538 glp_sdf_error(data, "invalid number of vertices\n"); |
|
539 na = glp_sdf_read_int(data); |
|
540 if (na < 0) |
|
541 glp_sdf_error(data, "invalid number of arcs\n"); |
|
542 xprintf("Graph has %d vert%s and %d arc%s\n", |
|
543 nv, nv == 1 ? "ex" : "ices", na, na == 1 ? "" : "s"); |
|
544 if (nv > 0) glp_add_vertices(G, nv); |
|
545 for (k = 1; k <= na; k++) |
|
546 { i = glp_sdf_read_int(data); |
|
547 if (!(1 <= i && i <= nv)) |
|
548 glp_sdf_error(data, "tail vertex number out of range\n"); |
|
549 j = glp_sdf_read_int(data); |
|
550 if (!(1 <= j && j <= nv)) |
|
551 glp_sdf_error(data, "head vertex number out of range\n"); |
|
552 glp_add_arc(G, i, j); |
|
553 } |
|
554 xprintf("%d lines were read\n", glp_sdf_line(data)); |
|
555 ret = 0; |
|
556 done: if (data != NULL) glp_sdf_close_file(data); |
|
557 return ret; |
|
558 } |
|
559 |
|
560 /*********************************************************************** |
|
561 * NAME |
|
562 * |
|
563 * glp_write_graph - write graph to plain text file |
|
564 * |
|
565 * SYNOPSIS |
|
566 * |
|
567 * int glp_write_graph(glp_graph *G, const char *fname). |
|
568 * |
|
569 * DESCRIPTION |
|
570 * |
|
571 * The routine glp_write_graph writes the specified graph to a plain |
|
572 * text file. |
|
573 * |
|
574 * RETURNS |
|
575 * |
|
576 * If the operation was successful, the routine returns zero. Otherwise |
|
577 * it prints an error message and returns non-zero. */ |
|
578 |
|
579 int glp_write_graph(glp_graph *G, const char *fname) |
|
580 { XFILE *fp; |
|
581 glp_vertex *v; |
|
582 glp_arc *a; |
|
583 int i, count, ret; |
|
584 xprintf("Writing graph to `%s'...\n", fname); |
|
585 fp = xfopen(fname, "w"), count = 0; |
|
586 if (fp == NULL) |
|
587 { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); |
|
588 ret = 1; |
|
589 goto done; |
|
590 } |
|
591 xfprintf(fp, "%d %d\n", G->nv, G->na), count++; |
|
592 for (i = 1; i <= G->nv; i++) |
|
593 { v = G->v[i]; |
|
594 for (a = v->out; a != NULL; a = a->t_next) |
|
595 xfprintf(fp, "%d %d\n", a->tail->i, a->head->i), count++; |
|
596 } |
|
597 xfflush(fp); |
|
598 if (xferror(fp)) |
|
599 { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); |
|
600 ret = 1; |
|
601 goto done; |
|
602 } |
|
603 xprintf("%d lines were written\n", count); |
|
604 ret = 0; |
|
605 done: if (fp != NULL) xfclose(fp); |
|
606 return ret; |
|
607 } |
|
608 |
|
609 /* eof */ |