1/* struct::graph - critcl - layer 1 definitions
2 * (c) Graph functions
3 */
4
5#include <arc.h>
6#include <attr.h>
7#include <graph.h>
8#include <node.h>
9#include <objcmd.h>
10#include <util.h>
11
12/* .................................................. */
13
14static void swap (G* dst, G* src);
15static G*   dup  (G* src);
16
17/* .................................................. */
18
19G*
20g_new (void)
21{
22    G* g = ALLOC (G);
23
24    g->nodes.map = ALLOC (Tcl_HashTable);
25    g->arcs.map  = ALLOC (Tcl_HashTable);
26
27    Tcl_InitHashTable (g->nodes.map, TCL_STRING_KEYS);
28    Tcl_InitHashTable (g->arcs.map,  TCL_STRING_KEYS);
29
30    g->nodes.first = NULL;
31    g->nodes.n	   = 0;
32    g->arcs.first  = NULL;
33    g->arcs.n	   = 0;
34
35    g->attr        = NULL;
36
37    g->cmd	   = NULL;
38    g->ncounter	   = 0;
39    g->acounter	   = 0;
40
41    return g;
42}
43
44/* .................................................. */
45
46void
47g_delete (G* g)
48{
49    /* Delete a graph in toto. Deletes all arcs first, then all nodes. This
50     * also handles the nodes/arcs lists. Then the name -> node/arc mapping,
51     * and the object name.
52     */
53
54    while (g->arcs.first)  { ga_delete ((GA*) g->arcs.first);  }
55    while (g->nodes.first) { gn_delete ((GN*) g->nodes.first); }
56
57    Tcl_DeleteHashTable (g->arcs.map);
58    Tcl_DeleteHashTable (g->nodes.map);
59
60    ckfree ((char*) g->arcs.map);
61    ckfree ((char*) g->nodes.map);
62
63    g->arcs.map  = NULL;
64    g->nodes.map = NULL;
65
66    g->cmd = NULL;
67
68    g_attr_delete (&g->attr);
69    ckfree ((char*) g);
70}
71
72/* .................................................. */
73
74const char*
75g_newnodename (G* g)
76{
77    int ok;
78    Tcl_HashEntry* he;
79
80    do {
81	g->ncounter ++;
82	sprintf (g->handle, "node%d", g->ncounter);
83
84	/* Check that there is no node using that name already */
85	he = Tcl_FindHashEntry (g->nodes.map, g->handle);
86	ok = (he == NULL);
87    } while (!ok);
88
89    return g->handle;
90}
91
92/* .................................................. */
93
94const char*
95g_newarcname (G* g)
96{
97    int ok;
98    Tcl_HashEntry* he;
99
100    do {
101	g->acounter ++;
102	sprintf (g->handle, "arc%d", g->acounter);
103
104	/* Check that there is no node using that name already */
105	he = Tcl_FindHashEntry (g->arcs.map, g->handle);
106	ok = (he == NULL);
107    } while (!ok);
108
109    return g->handle;
110}
111
112/* .................................................. */
113
114/*
115 *---------------------------------------------------------------------------
116 *
117 * g_ms_serialize --
118 *
119 *	Generates Tcl value from graph, serialized graph data.
120 *
121 * Results:
122 *	A standard Tcl result code.
123 *
124 * Side effects:
125 *	Only internal, memory allocation changes ...
126 *
127 *---------------------------------------------------------------------------
128 */
129
130Tcl_Obj*
131g_ms_serialize (Tcl_Interp* interp, Tcl_Obj* go, G* g, int oc, Tcl_Obj* const* ov)
132{
133    Tcl_Obj*  ser;
134    Tcl_Obj*  empty;
135
136    int       lc = 1 + 3 * (oc ? oc : g->nodes.n);
137    Tcl_Obj** lv = NALLOC (lc, Tcl_Obj*);
138
139    Tcl_HashTable cn;
140    int k, new;
141    GN* n;
142
143    /* Enumerate the nodes for the references used in arcs. FUTURE, TODO: Skip
144     * this step if there are no arcs! We cannot skip testing the validity of
145     * the nodes however, if the set is explicit. In that case we also check
146     * and remove duplicates.  */
147
148    Tcl_InitHashTable (&cn, TCL_ONE_WORD_KEYS);
149
150    if (oc) {
151	/* Enumerate the specified nodes, remove duplicates along the way */
152	Tcl_HashEntry* he;
153	int i, j, new;
154
155	j = 0;
156	for (i=0; i < oc; i++) {
157	    ASSERT_BOUNDS(i, oc);
158	    n = gn_get_node (g, ov[i], interp, go);
159	    if (!n) {
160		goto abort;
161	    }
162	    if (Tcl_FindHashEntry (&cn, (char*) n)) continue;
163	    ASSERT_BOUNDS(j, lc-1);
164	    he = Tcl_CreateHashEntry (&cn, (char*) n, &new);
165	    lv [j] = n->base.name;
166	    Tcl_SetHashValue (he, (ClientData) j);
167	    j += 3;
168	}
169	lc = j + 1;
170    } else {
171	/* Enumerate all nodes */
172	Tcl_HashEntry* he;
173	int j, new;
174
175	j = 0;
176	for (n = (GN*) g->nodes.first;
177	     n != NULL;
178	     n = (GN*) n->base.next) {
179
180	    ASSERT_BOUNDS(j, lc-1);
181	    he = Tcl_CreateHashEntry (&cn, (char*) n, &new);
182	    lv [j] = n->base.name;
183	    Tcl_SetHashValue (he, (ClientData) j);
184	    j += 3;
185	}
186	lc = j + 1;
187    }
188
189    empty = Tcl_NewObj ();
190    Tcl_IncrRefCount (empty);
191
192    /* Fill in the arcs, attributes per node, and graph attributes */
193
194    for (k=0; k < lc-1; k++) {
195	ASSERT_BOUNDS(k, lc-1);
196	n = gn_get_node (g, lv[k], NULL, NULL);
197	k ++;
198
199	ASSERT_BOUNDS(k, lc-1);
200	lv [k] = g_attr_serial (n->base.attr, empty);
201	k ++;
202
203	ASSERT_BOUNDS(k, lc-1);
204	lv [k] = gn_serial_arcs (n, empty, &cn);
205    }
206
207    ASSERT_BOUNDS(k, lc);
208    lv [k] = g_attr_serial (g->attr, empty);
209
210    /* Put everything together, release scratch space */
211
212    ser = Tcl_NewListObj (lc, lv);
213
214    Tcl_DecrRefCount (empty);
215    Tcl_DeleteHashTable(&cn);
216    ckfree ((char*) lv);
217
218    return ser;
219
220 abort:
221    Tcl_DeleteHashTable(&cn);
222    ckfree ((char*) lv);
223    return NULL;
224}
225
226
227/* .................................................. */
228
229int
230g_deserialize (G* dst, Tcl_Interp* interp, Tcl_Obj* src)
231{
232    /*
233     * SV   = { NODE ATTR/node ARCS ... ATTR/graph }
234     *
235     * using:
236     *		ATTR/x = { key value ... }
237     *		ARCS   = { { NAME targetNODEref ATTR/arc } ... }
238     *
239     * Basic checks:
240     * - Is the input a list ?
241     * - Is its length a multiple of three modulo 1 ?
242     */
243
244    int	      lc, i, j, k;
245    Tcl_Obj** lv;
246    int	      ac;
247    Tcl_Obj** av;
248    int	      axc, nref;
249    Tcl_Obj** axv;
250    int	      nodes;
251    G*        new;
252    GN*       n;
253    GN*       ndst;
254    GA*       a;
255    int       code = TCL_ERROR;
256
257    if (Tcl_ListObjGetElements (interp, src, &lc, &lv) != TCL_OK) {
258	return TCL_ERROR;
259    }
260    if ((lc % 3) != 1) {
261	Tcl_AppendResult (interp,
262			  "error in serialization: list length not 1 mod 3.",
263			  NULL);
264	return TCL_ERROR;
265    }
266
267    nodes = (lc-1)/3;
268
269    /* Iteration 1. Check the overall structure of the incoming value (node
270     * attributes, arcs, arc attributes, graph attributes).
271     */
272
273    if (!g_attr_serok (interp, lv[lc-1], "graph")) {
274	return TCL_ERROR;
275    }
276
277    for (i=0; i < (lc-1); ) {
278	/* Skip node name */
279	ASSERT_BOUNDS (i, lc-1);
280	i ++ ;
281	/* Check node attributes */
282	if (!g_attr_serok (interp, lv[i], "node")) {
283	    return TCL_ERROR;
284	}
285	/* Go to the arc information block for the node */
286	ASSERT_BOUNDS (i, lc-1);
287	i ++;
288	/* Check arc information */
289	if (Tcl_ListObjGetElements (interp, lv[i], &ac, &av) != TCL_OK) {
290	    return TCL_ERROR;
291	}
292	for (k=0; k < ac; k++) {
293	    ASSERT_BOUNDS (k, ac);
294	    /* Check each arc */
295	    if (Tcl_ListObjGetElements (interp, av[k], &axc, &axv) != TCL_OK) {
296		return TCL_ERROR;
297	    }
298	    if ((axc != 3) && (axc != 4)) {
299		Tcl_AppendResult (interp,
300				  "error in serialization: arc information length not 3 or 4.",
301				  NULL);
302		return TCL_ERROR;
303	    }
304	    /* Check arc attributes */
305	    if (!g_attr_serok (interp, axv[2], "arc")) {
306		return TCL_ERROR;
307	    }
308	    /* Check node reference for arc destination */
309	    if ((Tcl_GetIntFromObj (interp, axv[1], &nref) != TCL_OK) ||
310		(nref % 3) || (nref < 0) || (nref >= lc)) {
311		Tcl_ResetResult (interp);
312		Tcl_AppendResult (interp,
313				  "error in serialization: bad arc destination reference \"",
314				  Tcl_GetString (axv[1]),
315				  "\".", NULL);
316		return TCL_ERROR;
317	    }
318	}
319	/* Go to the next node */
320	ASSERT_BOUNDS (i, lc-1);
321	i ++;
322    }
323
324    /* We now know that the value is structurally sound, i.e. lists, of the
325     * specified lengths, fixed, and proper multiples, and that references are
326     * kept inside to the proper locations. We can now go over the information
327     * again and use it to build up a graph. At that time we can also do the
328     * more complex semantic checks (dup nodes, dup arcs).
329     *
330     * The information is collected directly into a graph structure. We have
331     * no better place where to put it. In case of problems we can tear it
332     * down again easily, and otherwise we can swap with the actual graph and
333     * then tear that one down, effectively replacing it with the new graph.
334     */
335
336    new = g_new ();
337
338    /* I. Import the nodes */
339
340    for (i=0; i < (lc-1); i += 3) {
341	ASSERT_BOUNDS (i, lc-1);
342	n = gn_get_node (new, lv[i], NULL, NULL);
343	if (n) {
344	    Tcl_AppendResult (interp,
345			      "error in serialization: duplicate node names.",
346			      NULL);
347	    goto done;
348	}
349	gn_new (new, Tcl_GetString (lv [i]));
350    }
351
352    /* II. Import the arcs */
353
354    for (i=2; i < (lc-1); i += 3) {
355	ASSERT_BOUNDS (i, lc-1);
356	n = gn_get_node (new, lv[i-2], NULL, NULL);
357	Tcl_ListObjGetElements (interp, lv[i], &ac, &av);
358
359	for (k=0; k < ac; k++) {
360	    ASSERT_BOUNDS (k, ac);
361	    Tcl_ListObjGetElements (interp, av[k], &axc, &axv);
362	    a = ga_get_arc (new, axv[0], NULL, NULL);
363	    if (a) {
364		Tcl_AppendResult (interp,
365				  "error in serialization: duplicate definition of arc \"",
366				  Tcl_GetString (axv[0]),"\".", NULL);
367		goto done;
368	    }
369	    Tcl_GetIntFromObj (interp, axv[1], &nref);
370	    ndst = gn_get_node (new, lv[nref], NULL, NULL);
371	    a = ga_new (new, Tcl_GetString (axv[0]), n, ndst);
372
373	    if (axc == 4) {
374		a->weight = axv[3];
375		Tcl_IncrRefCount (a->weight);
376	    }
377	}
378    }
379
380    /* III. Import the various attributes */
381
382    for (i=0; i < (lc-1); ) {
383	ASSERT_BOUNDS (i, lc-1);
384	n = gn_get_node (new, lv[i], NULL, NULL);
385	/* Goto node attributes */
386	i ++ ;
387	/* Import node attributes */
388	ASSERT_BOUNDS (i, lc-1);
389	g_attr_deserial (&n->base.attr, lv[i]);
390	/* Go to the arc information block for the node */
391	ASSERT_BOUNDS (i, lc-1);
392	i ++;
393	/* Check arc information */
394	Tcl_ListObjGetElements (interp, lv[i], &ac, &av);
395	for (k=0; k < ac; k++) {
396	    ASSERT_BOUNDS (k, ac);
397	    Tcl_ListObjGetElements (interp, av[k], &axc, &axv);
398	    a = ga_get_arc (new, axv[0], NULL, NULL);
399	    g_attr_deserial (&a->base.attr, axv[2]);
400	}
401	/* Go to the next node */
402	ASSERT_BOUNDS (i, lc-1);
403	i ++;
404    }
405
406    g_attr_deserial (&new->attr, lv[lc-1]);
407
408    /* swap dst <-> new. This puts the collected information into the graph
409     * associated with the command, and the old information is put into the
410     * scratch structure scheduled for destruction, making cleanup automatic.
411     */
412
413    swap (dst, new);
414    code = TCL_OK;
415
416 done:
417    g_delete (new);
418    return code;
419}
420
421/* .................................................. */
422
423int
424g_assign (G* dst, G* src)
425{
426    G* new = dup (src);
427    swap (dst, new);
428    g_delete (new);
429    return TCL_OK;
430}
431
432/*
433 *---------------------------------------------------------------------------
434 *
435 * g_ms_assign --
436 *
437 *	Copies the argument graph over into this one. Uses direct
438 *	access to internal data structures for matching graph objects, and
439 *	goes through a serialize/deserialize combination otherwise.
440 *
441 * Results:
442 *	A standard Tcl result code.
443 *
444 * Side effects:
445 *	Only internal, memory allocation changes ...
446 *
447 *---------------------------------------------------------------------------
448 */
449
450int
451g_ms_assign (Tcl_Interp* interp, G* g, Tcl_Obj* src)
452{
453    Tcl_CmdInfo srcInfo;
454
455    if (!Tcl_GetCommandInfo(interp, Tcl_GetString (src), &srcInfo)) {
456	Tcl_AppendResult (interp, "invalid command name \"",
457			  Tcl_GetString (src), "\"", NULL);
458	return TCL_ERROR;
459    }
460
461    if (srcInfo.objProc == g_objcmd) {
462	/* The source graph object is managed by this code also. We can
463	 * retrieve and copy the data directly.
464	 */
465
466	G* gsrc = (G*) srcInfo.objClientData;
467
468	return g_assign (g, gsrc);
469
470    } else {
471	/* The source graph is not managed by this package. Use
472	 * (de)serialization to transfer the information We do not invoke the
473	 * command proc directly
474	 */
475
476	int	 res;
477	Tcl_Obj* ser;
478	Tcl_Obj* cmd [2];
479
480	/* Phase 1: Obtain a serialization by invoking the relevant object
481	 * method
482	 */
483
484	cmd [0] = src;
485	cmd [1] = Tcl_NewStringObj ("serialize", -1);
486
487	Tcl_IncrRefCount (cmd [0]);
488	Tcl_IncrRefCount (cmd [1]);
489
490	res = Tcl_EvalObjv (interp, 2, cmd, 0);
491
492	Tcl_DecrRefCount (cmd [0]);
493	Tcl_DecrRefCount (cmd [1]);
494
495	if (res != TCL_OK) {
496	    return TCL_ERROR;
497	}
498
499	ser = Tcl_GetObjResult (interp);
500	Tcl_IncrRefCount (ser);
501	Tcl_ResetResult (interp);
502
503	/* Phase 2: Copy the serializtion into ourselves using the regular
504	 * deserialization functionality
505	 */
506
507	res = g_deserialize (g, interp, ser);
508	Tcl_DecrRefCount (ser);
509	return res;
510    }
511}
512
513/*
514 *---------------------------------------------------------------------------
515 *
516 * g_ms_set --
517 *
518 *	Copies this graph over into the argument graph. Uses direct access to
519 *	internal data structures for matching graph objects, and goes through a
520 *	serialize/deserialize combination otherwise.
521 *
522 * Results:
523 *	A standard Tcl result code.
524 *
525 * Side effects:
526 *	Only internal, memory allocation changes ...
527 *
528 *---------------------------------------------------------------------------
529 */
530
531int
532g_ms_set (Tcl_Interp* interp, Tcl_Obj* go, G* g, Tcl_Obj* dst)
533{
534    Tcl_CmdInfo dstInfo;
535
536    if (!Tcl_GetCommandInfo(interp, Tcl_GetString (dst), &dstInfo)) {
537	Tcl_AppendResult (interp, "invalid command name \"",
538			  Tcl_GetString (dst), "\"", NULL);
539	return TCL_ERROR;
540    }
541
542    if (dstInfo.objProc == g_objcmd) {
543	/* The destination graph object is managed by this code also We can
544	 * retrieve and copy the data directly.
545	 */
546
547	G* gdest = (G*) dstInfo.objClientData;
548
549	return g_assign (gdest, g);
550
551    } else {
552	/* The destination graph is not managed by this package. Use
553	 * (de)serialization to transfer the information We do not invoke the
554	 * command proc directly.
555	 */
556
557	int	 res;
558	Tcl_Obj* ser;
559	Tcl_Obj* cmd [3];
560
561	/* Phase 1: Obtain our serialization */
562
563	ser = g_ms_serialize (interp, go, g, 0, NULL);
564
565	/* Phase 2: Copy into destination by invoking the regular
566	 * deserialization method
567	 */
568
569	cmd [0] = dst;
570	cmd [1] = Tcl_NewStringObj ("deserialize", -1);
571	cmd [2] = ser;
572
573	Tcl_IncrRefCount (cmd [0]);
574	Tcl_IncrRefCount (cmd [1]);
575	Tcl_IncrRefCount (cmd [2]);
576
577	res = Tcl_EvalObjv (interp, 3, cmd, 0);
578
579	Tcl_DecrRefCount (cmd [0]);
580	Tcl_DecrRefCount (cmd [1]);
581	Tcl_DecrRefCount (cmd [2]); /* == ser, is gone now */
582
583	if (res != TCL_OK) {
584	    return TCL_ERROR;
585	}
586
587	Tcl_ResetResult (interp);
588	return TCL_OK;
589    }
590    return TCL_ERROR;
591}
592
593
594/* .................................................. */
595
596static void
597swap (G* dst, G* src)
598{
599    GC* c;
600    G tmp;
601
602    /* Swap the main information */
603
604    tmp  = *dst;
605    *dst = *src;
606    *src = tmp;
607
608    /* Swap the cmd right back, because this part of the dst structure has to
609     * be kept.
610     */
611
612    tmp.cmd  = dst->cmd;
613    dst->cmd = src->cmd;
614    src->cmd = tmp.cmd;
615
616    /* At last fix the node/arc ownership in both structures, or else g_delete
617     * will access and destroy the newly created information, and a future
618     * delete of the graph accesses long gone memory.
619     */
620
621    for (c = src->nodes.first; c != NULL; c = c->next) {
622	c->graph = src;
623    }
624    for (c = src->arcs.first; c != NULL; c = c->next) {
625	c->graph = src;
626    }
627
628    for (c = dst->nodes.first; c != NULL; c = c->next) {
629	c->graph = dst;
630    }
631    for (c = dst->arcs.first; c != NULL; c = c->next) {
632	c->graph = dst;
633    }
634}
635
636/* .................................................. */
637
638static G*
639dup (G* src)
640{
641    G* new = g_new ();
642    GN* no; GN* n;
643    GA* ao; GA* a;
644    GC* c;
645
646    /* I. Duplicate nodes. NOTE. In the list of nodes in src we break the chain
647     * of prev references and use that to point from each src node to its
648     * duplicate. This is then used during the duplication of arcs (-> II.) to
649     * quickly locate the nodes to connect. After that is done the chain can
650     * and is restored.
651     */
652#define ORIG base.prev
653
654    for (no = (GN*) src->nodes.first;
655	 no != NULL;
656	 no = (GN*) no->base.next) {
657
658	n = gn_new (new, Tcl_GetString(no->base.name));
659	no->ORIG = (GC*) n;
660	g_attr_dup (&n->base.attr, no->base.attr);
661    }
662
663    /* II. Duplicate the arcs */
664
665    for (ao = (GA*) src->arcs.first;
666	 ao != NULL;
667	 ao = (GA*) ao->base.next) {
668	a = ga_new (new, Tcl_GetString(ao->base.name),
669		    (GN*) ao->start->n->ORIG,
670		    (GN*) ao->end->n->ORIG);
671	g_attr_dup (&a->base.attr, ao->base.attr);
672
673	if (ao->weight) {
674	    a->weight = ao->weight;
675	    Tcl_IncrRefCount (a->weight);
676	}
677    }
678
679#undef ORIG
680
681    /* III. Re-chain the nodes in the original */
682
683    c = src->nodes.first;
684    if (c) {
685	c->prev = NULL;
686	c = c->next;
687
688	for (; c != NULL; c = c->next) {
689	    if (!c->next) break;
690	    c->next->prev = c;
691	}
692    }
693
694    g_attr_dup (&new->attr, src->attr);
695    return new;
696}
697
698/* .................................................. */
699
700/*
701 * Local Variables:
702 * mode: c
703 * c-basic-offset: 4
704 * fill-column: 78
705 * End:
706 */
707