graph_tcl.tcl

Go to the documentation of this file.
00001 /*  graph_tcl.tcl --*/
00002 /* */
00003 /*  Implementation of a graph data structure for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 2000-2006 by Andreas Kupries <andreas_kupries@users.sourceforge.net>*/
00006 /* */
00007 /*  See the file "license.terms" for information on usage and redistribution*/
00008 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00009 /*  */
00010 /*  RCS: @(#) $Id: graph_tcl.tcl,v 1.1 2006/11/16 06:33:12 andreas_kupries Exp $*/
00011 
00012 package require Tcl 8.2
00013 package require struct::list
00014 package require struct::
00015 
00016 namespace =  eval ::struct::graph {
00017     /*  Data storage in the graph module*/
00018     /*  -------------------------------*/
00019     /* */
00020     /*  There's a lot of bits to keep track of for each graph:*/
00021     /*  nodes*/
00022     /*  node values*/
00023     /*  node relationships (arcs)*/
00024     /*    arc values*/
00025     /* */
00026     /*  It would quickly become unwieldy to try to keep these in arrays or lists*/
00027     /*  within the graph namespace itself.  Instead, each graph structure will*/
00028     /*  get its own namespace.  Each namespace contains:*/
00029     /*  node:$node  array mapping keys to values for the node $node*/
00030     /*  arc:$arc    array mapping keys to values for the arc $arc*/
00031     /*  inArcs      array mapping nodes to the list of incoming arcs*/
00032     /*  outArcs     array mapping nodes to the list of outgoing arcs*/
00033     /*  arcNodes    array mapping arcs to the two nodes (start & end)*/
00034     
00035     /*  counter is used to give a unique name for unnamed graph*/
00036     variable counter 0
00037 
00038     /*  Only export one command, the one used to instantiate a new graph*/
00039     namespace export graph_tcl
00040 }
00041 
00042 /*  ::struct::graph::graph_tcl --*/
00043 /* */
00044 /*  Create a new graph with a given name; if no name is given, use*/
00045 /*  graphX, where X is a number.*/
00046 /* */
00047 /*  Arguments:*/
00048 /*  name    name of the graph; if null, generate one.*/
00049 /* */
00050 /*  Results:*/
00051 /*  name    name of the graph created*/
00052 
00053 ret  ::struct::graph::graph_tcl (type args) {
00054     variable counter
00055     
00056     set src     {}
00057     set srctype {}
00058 
00059     switch -exact -- [llength [info level 0]] {
00060     1 {
00061         # Missing name, generate one.
00062         incr counter
00063         set name "graph${counter}"
00064     }
00065     2 {
00066         # Standard call. New empty graph.
00067         set name [lindex $args 0]
00068     }
00069     4 {
00070         # Copy construction.
00071         foreach {name as src} $args break
00072         switch -exact -- $as {
00073         = - := - as {
00074             set srctype graph
00075         }
00076         deserialize {
00077             set srctype serial
00078         }
00079         default {
00080             return -code error \
00081                 "wrong # args: should be \"graph ?name ?=|:=|as|deserialize source??\""
00082         }
00083         }
00084     }
00085     default {
00086         # Error.
00087         return -code error \
00088             "wrong # args: should be \"graph ?name ?=|:=|as|deserialize source??\""
00089     }
00090     }
00091 
00092     # FIRST, qualify the name.
00093     if {![string match "::*" $name]} {
00094         # Get caller's namespace; append :: if not global namespace.
00095         set ns [uplevel 1 [list namespace current]]
00096         if {"::" != $ns} {
00097             append ns "::"
00098         }
00099 
00100         set name "$ns$name"
00101     }
00102     if {[llength [info commands $name]]} {
00103     return -code error "command \"$name\" already exists, unable to create graph"
00104     }
00105 
00106     # Set up the namespace
00107     namespace eval $name {
00108 
00109     # Set up the map for values associated with the graph itself
00110     variable  graphAttr
00111     array set graphAttr {}
00112 
00113     # Set up the node attribute mapping
00114     variable  nodeAttr
00115     array set nodeAttr {}
00116 
00117     # Set up the arc attribute mapping
00118     variable  arcAttr
00119     array set arcAttr {}
00120 
00121     # Set up the map from nodes to the arcs coming to them
00122     variable  inArcs
00123     array set inArcs {}
00124 
00125     # Set up the map from nodes to the arcs going out from them
00126     variable  outArcs
00127     array set outArcs {}
00128 
00129     # Set up the map from arcs to the nodes they touch.
00130     variable  arcNodes
00131     array set arcNodes {}
00132 
00133     # Set up a value for use in creating unique node names
00134     variable nextUnusedNode
00135     set      nextUnusedNode 1
00136 
00137     # Set up a value for use in creating unique arc names
00138     variable nextUnusedArc
00139     set      nextUnusedArc 1
00140 
00141     # Set up a counter for use in creating attribute arrays.
00142     variable nextAttr
00143     set      nextAttr 0
00144     }
00145 
00146     # Create the command to manipulate the graph
00147     interp alias {} $name {} ::struct::graph::GraphProc $name
00148 
00149     # Automatic execution of assignment if a source
00150     # is present.
00151     if {$src != {}} {
00152     switch -exact -- $srctype {
00153         graph  {_= $name $src}
00154         serial {_deserialize $name $src}
00155         default {
00156         return -code error \
00157             "Internal error, illegal srctype \"$srctype\""
00158         }
00159     }
00160     }
00161 
00162     return $name
00163 }
00164 
00165 /* */
00166 /*  Private functions follow*/
00167 
00168 /*  ::struct::graph::GraphProc --*/
00169 /* */
00170 /*  Command that processes all graph object commands.*/
00171 /* */
00172 /*  Arguments:*/
00173 /*  name    name of the graph object to manipulate.*/
00174 /*  args    command name and args for the command*/
00175 /* */
00176 /*  Results:*/
00177 /*  Varies based on command to perform*/
00178 
00179 ret  ::struct::graph::GraphProc (type name , optional cmd ="" , type args) {
00180     # Do minimal args checks here
00181     if { [llength [info level 0]] == 2 } {
00182     return -code error "wrong # args: should be \"$name option ?arg arg ...?\""
00183     }
00184     
00185     # Split the args into command and args components
00186     set sub _$cmd
00187     if { [llength [info commands ::struct::graph::$sub]] == 0 } {
00188     set optlist [lsort [info commands ::struct::graph::_*]]
00189     set xlist {}
00190     foreach p $optlist {
00191         set p [namespace tail $p]
00192         if {[string match __* $p]} {continue}
00193         lappend xlist [string range $p 1 end]
00194     }
00195     set optlist [linsert [join $xlist ", "] "end-1" "or"]
00196     return -code error \
00197         "bad option \"$cmd\": must be $optlist"
00198     }
00199     uplevel 1 [linsert $args 0 ::struct::graph::$sub $name]
00200 }
00201 
00202 /*  ::struct::graph::_= --*/
00203 /* */
00204 /*  Assignment operator. Copies the source graph into the*/
00205 /*        destination, destroying the original information.*/
00206 /* */
00207 /*  Arguments:*/
00208 /*  name    Name of the graph object we are copying into.*/
00209 /*  source  Name of the graph object providing us with the*/
00210 /*      data to copy.*/
00211 /* */
00212 /*  Results:*/
00213 /*  Nothing.*/
00214 
00215 ret  ::struct::graph::_= (type name , type source) {
00216     _deserialize $name [$source serialize]
00217     return
00218 }
00219 
00220 /*  ::struct::graph::_--> --*/
00221 /* */
00222 /*  Reverse assignment operator. Copies this graph into the*/
00223 /*        destination, destroying the original information.*/
00224 /* */
00225 /*  Arguments:*/
00226 /*  name    Name of the graph object to copy*/
00227 /*  dest    Name of the graph object we are copying to.*/
00228 /* */
00229 /*  Results:*/
00230 /*  Nothing.*/
00231 
00232 ret  ::struct::graph::_--> (type name , type dest) {
00233     $dest deserialize [_serialize $name]
00234     return
00235 }
00236 
00237 /*  ::struct::graph::_append --*/
00238 /* */
00239 /*  Append a value for an attribute in a graph.*/
00240 /* */
00241 /*  Arguments:*/
00242 /*  name    name of the graph.*/
00243 /*  args    key value*/
00244 /* */
00245 /*  Results:*/
00246 /*  val value associated with the given key of the given arc*/
00247 
00248 ret  ::struct::graph::_append (type name , type key , type value) {
00249     variable ${name}::graphAttr
00250     return [append    graphAttr($key) $value]
00251 }
00252 
00253 /*  ::struct::graph::_lappend --*/
00254 /* */
00255 /*  lappend a value for an attribute in a graph.*/
00256 /* */
00257 /*  Arguments:*/
00258 /*  name    name of the graph.*/
00259 /*  args    key value*/
00260 /* */
00261 /*  Results:*/
00262 /*  val value associated with the given key of the given arc*/
00263 
00264 ret  ::struct::graph::_lappend (type name , type key , type value) {
00265     variable ${name}::graphAttr
00266     return [lappend   graphAttr($key) $value]
00267 }
00268 
00269 /*  ::struct::graph::_arc --*/
00270 /* */
00271 /*  Dispatches the invocation of arc methods to the proper handler*/
00272 /*  procedure.*/
00273 /* */
00274 /*  Arguments:*/
00275 /*  name    name of the graph.*/
00276 /*  cmd arc command to invoke*/
00277 /*  args    arguments to propagate to the handler for the arc command*/
00278 /* */
00279 /*  Results:*/
00280 /*  As of the invoked handler.*/
00281 
00282 ret  ::struct::graph::_arc (type name , type cmd , type args) {
00283     # Split the args into command and args components
00284 
00285     set sub __arc_$cmd
00286     if { [llength [info commands ::struct::graph::$sub]] == 0 } {
00287     set optlist [lsort [info commands ::struct::graph::__arc_*]]
00288     set xlist {}
00289     foreach p $optlist {
00290         set p [namespace tail $p]
00291         lappend xlist [string range $p 6 end]
00292     }
00293     set optlist [linsert [join $xlist ", "] "end-1" "or"]
00294     return -code error \
00295         "bad option \"$cmd\": must be $optlist"
00296     }
00297     uplevel 1 [linsert $args 0 ::struct::graph::$sub $name]
00298 }
00299 
00300 /*  ::struct::graph::__arc_delete --*/
00301 /* */
00302 /*  Remove an arc from a graph, including all of its values.*/
00303 /* */
00304 /*  Arguments:*/
00305 /*  name    name of the graph.*/
00306 /*  args    list of arcs to delete.*/
00307 /* */
00308 /*  Results:*/
00309 /*  None.*/
00310 
00311 ret  ::struct::graph::__arc_delete (type name , type args) {
00312     if {![llength $args]} {
00313     return {wrong # args: should be "::struct::graph::__arc_delete name arc arc..."}
00314     }
00315 
00316     foreach arc $args {CheckMissingArc $name $arc}
00317 
00318     variable ${name}::inArcs
00319     variable ${name}::outArcs
00320     variable ${name}::arcNodes
00321     variable ${name}::arcAttr
00322 
00323     foreach arc $args {
00324     foreach {source target} $arcNodes($arc) break ; # lassign
00325 
00326     unset arcNodes($arc)
00327 
00328     if {[info exists arcAttr($arc)]} {
00329         unset ${name}::$arcAttr($arc)
00330         unset arcAttr($arc)
00331     }
00332 
00333     # Remove arc from the arc lists of source and target nodes.
00334 
00335     set index [lsearch -exact $outArcs($source) $arc]
00336     ldelete outArcs($source) $index
00337 
00338     set index [lsearch -exact $inArcs($target)  $arc]
00339     ldelete inArcs($target) $index
00340     }
00341 
00342     return
00343 }
00344 
00345 /*  ::struct::graph::__arc_exists --*/
00346 /* */
00347 /*  Test for existence of a given arc in a graph.*/
00348 /* */
00349 /*  Arguments:*/
00350 /*  name    name of the graph.*/
00351 /*  arc arc to look for.*/
00352 /* */
00353 /*  Results:*/
00354 /*  1 if the arc exists, 0 else.*/
00355 
00356 ret  ::struct::graph::__arc_exists (type name , type arc) {
00357     return [info exists ${name}::arcNodes($arc)]
00358 }
00359 
00360 /*  ::struct::graph::__arc_flip --*/
00361 /* */
00362 /*  Exchanges origin and destination node of the specified arc.*/
00363 /* */
00364 /*  Arguments:*/
00365 /*  name        name of the graph object.*/
00366 /*  arc     arc to change.*/
00367 /* */
00368 /*  Results:*/
00369 /*  None*/
00370 
00371 ret  ::struct::graph::__arc_flip (type name , type arc) {
00372     CheckMissingArc  $name $arc
00373 
00374     variable ${name}::arcNodes
00375     variable ${name}::outArcs
00376     variable ${name}::inArcs
00377 
00378     set oldsource [lindex $arcNodes($arc) 0]
00379     set oldtarget [lindex $arcNodes($arc) 1]
00380 
00381     if {[string equal $oldsource $oldtarget]} return
00382 
00383     set newtarget $oldsource
00384     set newsource $oldtarget
00385 
00386     set arcNodes($arc) [lreplace $arcNodes($arc) 0 0 $newsource]
00387     lappend outArcs($newsource) $arc
00388     ldelete outArcs($oldsource) [lsearch -exact $outArcs($oldsource) $arc]
00389 
00390     set arcNodes($arc) [lreplace $arcNodes($arc) 1 1 $newtarget]
00391     lappend inArcs($newtarget) $arc
00392     ldelete inArcs($oldtarget) [lsearch -exact $inArcs($oldtarget) $arc]
00393     return
00394 }
00395 
00396 /*  ::struct::graph::__arc_get --*/
00397 /* */
00398 /*  Get a keyed value from an arc in a graph.*/
00399 /* */
00400 /*  Arguments:*/
00401 /*  name    name of the graph.*/
00402 /*  arc arc to query.*/
00403 /*  key key to lookup*/
00404 /* */
00405 /*  Results:*/
00406 /*  value   value associated with the key given.*/
00407 
00408 ret  ::struct::graph::__arc_get (type name , type arc , type key) {
00409     CheckMissingArc $name $arc
00410 
00411     variable ${name}::arcAttr
00412     if {![info exists arcAttr($arc)]} {
00413     # No attribute data for this arc, key has to be invalid.
00414     return -code error "invalid key \"$key\" for arc \"$arc\""
00415     }
00416 
00417     upvar ${name}::$arcAttr($arc) data
00418     if { ![info exists data($key)] } {
00419     return -code error "invalid key \"$key\" for arc \"$arc\""
00420     }
00421     return $data($key)
00422 }
00423 
00424 /*  ::struct::graph::__arc_getall --*/
00425 /* */
00426 /*  Get a serialized array of key/value pairs from an arc in a graph.*/
00427 /* */
00428 /*  Arguments:*/
00429 /*  name    name of the graph.*/
00430 /*  arc arc to query.*/
00431 /*  pattern optional glob pattern to restrict retrieval*/
00432 /* */
00433 /*  Results:*/
00434 /*  value   serialized array of key/value pairs.*/
00435 
00436 ret  ::struct::graph::__arc_getall (type name , type arc , optional pattern =*) {
00437     CheckMissingArc $name $arc
00438 
00439     variable ${name}::arcAttr
00440     if {![info exists arcAttr($arc)]} {
00441     # No attributes ...
00442     return {}
00443     }
00444 
00445     upvar ${name}::$arcAttr($arc) data
00446     return [array get data $pattern]
00447 }
00448 
00449 /*  ::struct::graph::__arc_keys --*/
00450 /* */
00451 /*  Get a list of keys for an arc in a graph.*/
00452 /* */
00453 /*  Arguments:*/
00454 /*  name    name of the graph.*/
00455 /*  arc arc to query.*/
00456 /*  pattern optional glob pattern to restrict retrieval*/
00457 /* */
00458 /*  Results:*/
00459 /*  value   value associated with the key given.*/
00460 
00461 ret  ::struct::graph::__arc_keys (type name , type arc , optional pattern =*) {
00462     CheckMissingArc $name $arc
00463 
00464     variable ${name}::arcAttr
00465     if {![info exists arcAttr($arc)]} {
00466     # No attributes ...
00467     return {}
00468     }
00469 
00470     upvar ${name}::$arcAttr($arc) data
00471     return [array names data $pattern]
00472 }
00473 
00474 /*  ::struct::graph::__arc_keyexists --*/
00475 /* */
00476 /*  Test for existence of a given key for a given arc in a graph.*/
00477 /* */
00478 /*  Arguments:*/
00479 /*  name    name of the graph.*/
00480 /*  arc arc to query.*/
00481 /*  key key to lookup*/
00482 /* */
00483 /*  Results:*/
00484 /*  1 if the key exists, 0 else.*/
00485 
00486 ret  ::struct::graph::__arc_keyexists (type name , type arc , type key) {
00487     CheckMissingArc $name $arc
00488 
00489     variable ${name}::arcAttr
00490     if {![info exists arcAttr($arc)]} {
00491     # No attribute data for this arc, key cannot exist.
00492     return 0
00493     }
00494 
00495     upvar ${name}::$arcAttr($arc) data
00496     return [info exists data($key)]
00497 }
00498 
00499 /*  ::struct::graph::__arc_insert --*/
00500 /* */
00501 /*  Add an arc to a graph.*/
00502 /* */
00503 /*  Arguments:*/
00504 /*  name        name of the graph.*/
00505 /*  source      source node of the new arc*/
00506 /*  target      target node of the new arc*/
00507 /*  args        arc to insert; must be unique.  If none is given,*/
00508 /*          the routine will generate a unique node name.*/
00509 /* */
00510 /*  Results:*/
00511 /*  arc     The name of the new arc.*/
00512 
00513 ret  ::struct::graph::__arc_insert (type name , type source , type target , type args) {
00514 
00515     if { [llength $args] == 0 } {
00516     # No arc name was given; generate a unique one
00517     set arc [__generateUniqueArcName $name]
00518     } elseif { [llength $args] > 1 } {
00519     return {wrong # args: should be "::struct::graph::__arc_insert name source target ?arc?"}
00520     } else {
00521     set arc [lindex $args 0]
00522     }
00523 
00524     CheckDuplicateArc $name $arc    
00525     CheckMissingNode  $name $source {source }
00526     CheckMissingNode  $name $target {target }
00527     
00528     variable ${name}::inArcs
00529     variable ${name}::outArcs
00530     variable ${name}::arcNodes
00531 
00532     # Set up the new arc
00533     set arcNodes($arc) [list $source $target]
00534 
00535     # Add this arc to the arc lists of its source resp. target nodes.
00536     lappend outArcs($source) $arc
00537     lappend inArcs($target)  $arc
00538 
00539     return $arc
00540 }
00541 
00542 /*  ::struct::graph::__arc_rename --*/
00543 /* */
00544 /*  Rename a arc in place.*/
00545 /* */
00546 /*  Arguments:*/
00547 /*  name    name of the graph.*/
00548 /*  arc Name of the arc to rename*/
00549 /*  newname The new name of the arc.*/
00550 /* */
00551 /*  Results:*/
00552 /*  The new name of the arc.*/
00553 
00554 ret  ::struct::graph::__arc_rename (type name , type arc , type newname) {
00555     CheckMissingArc   $name $arc
00556     CheckDuplicateArc $name $newname
00557 
00558     set oldname  $arc
00559 
00560     # Perform the rename in the internal
00561     # data structures.
00562 
00563     # - graphAttr - not required, arc independent.
00564     # - nodeAttr  - not required, arc independent.
00565     # - counters  - not required
00566 
00567     variable ${name}::arcAttr
00568     variable ${name}::inArcs
00569     variable ${name}::outArcs
00570     variable ${name}::arcNodes
00571 
00572     # Arc relocation
00573 
00574     set arcNodes($newname) [set nodes $arcNodes($oldname)]
00575     unset                              arcNodes($oldname)
00576 
00577     # Update the two nodes ...
00578     foreach {start end} $nodes break
00579 
00580     set pos [lsearch -exact $inArcs($end) $oldname]
00581     lset inArcs($end) $pos $newname
00582 
00583     set pos [lsearch -exact $outArcs($start) $oldname]
00584     lset outArcs($start) $pos $newname
00585 
00586     if {[info exists arcAttr($oldname)]} {
00587     set arcAttr($newname) $arcAttr($oldname)
00588     unset                  arcAttr($oldname)
00589     }
00590 
00591     return $newname
00592 }
00593 
00594 /*  ::struct::graph::__arc_set --*/
00595 /* */
00596 /*  Set or get a value for an arc in a graph.*/
00597 /* */
00598 /*  Arguments:*/
00599 /*  name    name of the graph.*/
00600 /*  arc arc to modify or query.*/
00601 /*  key attribute to modify or query*/
00602 /*  args    ?value?*/
00603 /* */
00604 /*  Results:*/
00605 /*  val value associated with the given key of the given arc*/
00606 
00607 ret  ::struct::graph::__arc_set (type name , type arc , type key , type args) {
00608     if { [llength $args] > 1 } {
00609     return -code error "wrong # args: should be \"$name arc set arc key ?value?\""
00610     }
00611     CheckMissingArc $name $arc
00612 
00613     if { [llength $args] > 0 } {
00614     # Setting the value. This may have to create
00615     # the attribute array for this particular
00616     # node
00617 
00618     variable ${name}::arcAttr
00619     if {![info exists arcAttr($arc)]} {
00620         # No attribute data for this node,
00621         # so create it as we need it now.
00622         GenAttributeStorage $name arc $arc
00623     }
00624 
00625     upvar ${name}::$arcAttr($arc) data
00626     return [set data($key) [lindex $args end]]
00627     } else {
00628     # Getting a value
00629     return [__arc_get $name $arc $key]
00630     }
00631 }
00632 
00633 /*  ::struct::graph::__arc_append --*/
00634 /* */
00635 /*  Append a value for an arc in a graph.*/
00636 /* */
00637 /*  Arguments:*/
00638 /*  name    name of the graph.*/
00639 /*  arc arc to modify or query.*/
00640 /*  args    key value*/
00641 /* */
00642 /*  Results:*/
00643 /*  val value associated with the given key of the given arc*/
00644 
00645 ret  ::struct::graph::__arc_append (type name , type arc , type key , type value) {
00646     CheckMissingArc $name $arc
00647 
00648     variable ${name}::arcAttr
00649     if {![info exists arcAttr($arc)]} {
00650     # No attribute data for this arc,
00651     # so create it as we need it.
00652     GenAttributeStorage $name arc $arc
00653     }
00654 
00655     upvar ${name}::$arcAttr($arc) data
00656     return [append data($key) $value]
00657 }
00658 
00659 /*  ::struct::graph::__arc_attr --*/
00660 /* */
00661 /*  Return attribute data for one key and multiple arcs, possibly all.*/
00662 /* */
00663 /*  Arguments:*/
00664 /*  name    Name of the graph object.*/
00665 /*  key Name of the attribute to retrieve.*/
00666 /* */
00667 /*  Results:*/
00668 /*  children    Dictionary mapping arcs to attribute data.*/
00669 
00670 ret  ::struct::graph::__arc_attr (type name , type key , type args) {
00671     # Syntax:
00672     #
00673     # t attr key
00674     # t attr key -arcs {arclist}
00675     # t attr key -glob arcpattern
00676     # t attr key -regexp arcpattern
00677 
00678     variable ${name}::arcAttr
00679 
00680     set usage "wrong # args: should be \"[list $name] arc attr key ?-arcs list|-glob pattern|-regexp pattern?\""
00681     if {([llength $args] != 0) && ([llength $args] != 2)} {
00682     return -code error $usage
00683     } elseif {[llength $args] == 0} {
00684     # This automatically restricts the list
00685     # to arcs which can have the attribute
00686     # in question.
00687 
00688     set arcs [array names arcAttr]
00689     } else {
00690     # Determine a list of arcs to look at
00691     # based on the chosen restriction.
00692 
00693     foreach {mode value} $args break
00694     switch -exact -- $mode {
00695         -arcs {
00696         # This is the only branch where we have to
00697         # perform an explicit restriction to the
00698         # arcs which have attributes.
00699         set arcs {}
00700         foreach n $value {
00701             if {![info exists arcAttr($n)]} continue
00702             lappend arcs $n
00703         }
00704         }
00705         -glob {
00706         set arcs [array names arcAttr $value]
00707         }
00708         -regexp {
00709         set arcs {}
00710         foreach n [array names arcAttr] {
00711             if {![regexp -- $value $n]} continue
00712             lappend arcs $n
00713         }
00714         }
00715         default {
00716         return -code error "bad type \"$mode\": must be -arcs, -glob, or -regexp"
00717         }
00718     }
00719     }
00720 
00721     # Without possibly matching arcs
00722     # the result has to be empty.
00723 
00724     if {![llength $arcs]} {
00725     return {}
00726     }
00727 
00728     # Now locate matching keys and their values.
00729 
00730     set result {}
00731     foreach n $arcs {
00732     upvar ${name}::$arcAttr($n) data
00733     if {[info exists data($key)]} {
00734         lappend result $n $data($key)
00735     }
00736     }
00737 
00738     return $result
00739 }
00740 
00741 /*  ::struct::graph::__arc_lappend --*/
00742 /* */
00743 /*  lappend a value for an arc in a graph.*/
00744 /* */
00745 /*  Arguments:*/
00746 /*  name    name of the graph.*/
00747 /*  arc arc to modify or query.*/
00748 /*  args    key value*/
00749 /* */
00750 /*  Results:*/
00751 /*  val value associated with the given key of the given arc*/
00752 
00753 ret  ::struct::graph::__arc_lappend (type name , type arc , type key , type value) {
00754     CheckMissingArc $name $arc
00755 
00756     variable ${name}::arcAttr
00757     if {![info exists arcAttr($arc)]} {
00758     # No attribute data for this arc,
00759     # so create it as we need it.
00760     GenAttributeStorage $name arc $arc
00761     }
00762 
00763     upvar ${name}::$arcAttr($arc) data
00764     return [lappend data($key) $value]
00765 }
00766 
00767 /*  ::struct::graph::__arc_source --*/
00768 /* */
00769 /*  Return the node at the beginning of the specified arc.*/
00770 /* */
00771 /*  Arguments:*/
00772 /*  name    name of the graph object.*/
00773 /*  arc arc to look up.*/
00774 /* */
00775 /*  Results:*/
00776 /*  node    name of the node.*/
00777 
00778 ret  ::struct::graph::__arc_source (type name , type arc) {
00779     CheckMissingArc $name $arc
00780 
00781     variable ${name}::arcNodes
00782     return [lindex $arcNodes($arc) 0]
00783 }
00784 
00785 /*  ::struct::graph::__arc_target --*/
00786 /* */
00787 /*  Return the node at the end of the specified arc.*/
00788 /* */
00789 /*  Arguments:*/
00790 /*  name    name of the graph object.*/
00791 /*  arc arc to look up.*/
00792 /* */
00793 /*  Results:*/
00794 /*  node    name of the node.*/
00795 
00796 ret  ::struct::graph::__arc_target (type name , type arc) {
00797     CheckMissingArc $name $arc
00798 
00799     variable ${name}::arcNodes
00800     return [lindex $arcNodes($arc) 1]
00801 }
00802 
00803 /*  ::struct::graph::__arc_move-target --*/
00804 /* */
00805 /*  Change the destination node of the specified arc.*/
00806 /*  The arc is rotated around its origin to a different*/
00807 /*  node.*/
00808 /* */
00809 /*  Arguments:*/
00810 /*  name        name of the graph object.*/
00811 /*  arc     arc to change.*/
00812 /*  newtarget   new destination/target of the arc.*/
00813 /* */
00814 /*  Results:*/
00815 /*  None*/
00816 
00817 ret  ::struct::graph::__arc_move-target (type name , type arc , type newtarget) {
00818     CheckMissingArc  $name $arc
00819     CheckMissingNode $name $newtarget
00820 
00821     variable ${name}::arcNodes
00822     variable ${name}::inArcs
00823 
00824     set oldtarget [lindex $arcNodes($arc) 1]
00825     if {[string equal $oldtarget $newtarget]} return
00826 
00827     set arcNodes($arc) [lreplace $arcNodes($arc) 1 1 $newtarget]
00828 
00829     lappend inArcs($newtarget) $arc
00830     ldelete inArcs($oldtarget) [lsearch -exact $inArcs($oldtarget) $arc]
00831     return
00832 }
00833 
00834 /*  ::struct::graph::__arc_move-source --*/
00835 /* */
00836 /*  Change the origin node of the specified arc.*/
00837 /*  The arc is rotated around its destination to a different*/
00838 /*  node.*/
00839 /* */
00840 /*  Arguments:*/
00841 /*  name        name of the graph object.*/
00842 /*  arc     arc to change.*/
00843 /*  newsource   new origin/source of the arc.*/
00844 /* */
00845 /*  Results:*/
00846 /*  None*/
00847 
00848 ret  ::struct::graph::__arc_move-source (type name , type arc , type newsource) {
00849     CheckMissingArc  $name $arc
00850     CheckMissingNode $name $newsource
00851 
00852     variable ${name}::arcNodes
00853     variable ${name}::outArcs
00854 
00855     set oldsource [lindex $arcNodes($arc) 0]
00856     if {[string equal $oldsource $newsource]} return
00857 
00858     set arcNodes($arc) [lreplace $arcNodes($arc) 0 0 $newsource]
00859 
00860     lappend outArcs($newsource) $arc
00861     ldelete outArcs($oldsource) [lsearch -exact $outArcs($oldsource) $arc]
00862     return
00863 }
00864 
00865 /*  ::struct::graph::__arc_move --*/
00866 /* */
00867 /*  Changes both origin and destination node of the specified arc.*/
00868 /* */
00869 /*  Arguments:*/
00870 /*  name        name of the graph object.*/
00871 /*  arc     arc to change.*/
00872 /*  newsource   new origin/source of the arc.*/
00873 /*  newtarget   new destination/target of the arc.*/
00874 /* */
00875 /*  Results:*/
00876 /*  None*/
00877 
00878 ret  ::struct::graph::__arc_move (type name , type arc , type newsource , type newtarget) {
00879     CheckMissingArc  $name $arc
00880     CheckMissingNode $name $newsource
00881     CheckMissingNode $name $newtarget
00882 
00883     variable ${name}::arcNodes
00884     variable ${name}::outArcs
00885     variable ${name}::inArcs
00886 
00887     set oldsource [lindex $arcNodes($arc) 0]
00888     if {![string equal $oldsource $newsource]} {
00889     set arcNodes($arc) [lreplace $arcNodes($arc) 0 0 $newsource]
00890     lappend outArcs($newsource) $arc
00891     ldelete outArcs($oldsource) [lsearch -exact $outArcs($oldsource) $arc]
00892     }
00893 
00894     set oldtarget [lindex $arcNodes($arc) 1]
00895     if {![string equal $oldtarget $newtarget]} {
00896     set arcNodes($arc) [lreplace $arcNodes($arc) 1 1 $newtarget]
00897     lappend inArcs($newtarget) $arc
00898     ldelete inArcs($oldtarget) [lsearch -exact $inArcs($oldtarget) $arc]
00899     }
00900     return
00901 }
00902 
00903 /*  ::struct::graph::__arc_unset --*/
00904 /* */
00905 /*  Remove a keyed value from a arc.*/
00906 /* */
00907 /*  Arguments:*/
00908 /*  name    name of the graph.*/
00909 /*  arc arc to modify.*/
00910 /*  key attribute to remove*/
00911 /* */
00912 /*  Results:*/
00913 /*  None.*/
00914 
00915 ret  ::struct::graph::__arc_unset (type name , type arc , type key) {
00916     CheckMissingArc $name $arc
00917 
00918     variable ${name}::arcAttr
00919     if {![info exists arcAttr($arc)]} {
00920     # No attribute data for this arc,
00921     # nothing to do.
00922     return
00923     }
00924 
00925     upvar ${name}::$arcAttr($arc) data
00926     catch {unset data($key)}
00927 
00928     if {[array size data] == 0} {
00929     # No attributes stored for this arc, squash the whole array.
00930     unset arcAttr($arc)
00931     unset data
00932     }
00933     return
00934 }
00935 
00936 /*  ::struct::graph::_arcs --*/
00937 /* */
00938 /*  Return a list of all arcs in a graph satisfying some*/
00939 /*  node based restriction.*/
00940 /* */
00941 /*  Arguments:*/
00942 /*  name    name of the graph.*/
00943 /* */
00944 /*  Results:*/
00945 /*  arcs    list of arcs*/
00946 
00947 ret  ::struct::graph::_arcs (type name , type args) {
00948 
00949     CheckE $name arcs $args
00950 
00951     switch -exact -- $cond {
00952     none      {set arcs [ArcsNONE $name]}
00953     in        {set arcs [ArcsIN   $name $condNodes]}
00954     out       {set arcs [ArcsOUT  $name $condNodes]}
00955     adj       {set arcs [ArcsADJ  $name $condNodes]}
00956     inner     {set arcs [ArcsINN  $name $condNodes]}
00957     embedding {set arcs [ArcsEMB  $name $condNodes]}
00958     default   {return -code error "Can't happen, panic"}
00959     }
00960 
00961     #
00962     # We have a list of arcs that match the relation to the nodes.
00963     # Now filter according to -key and -value.
00964     #
00965 
00966     if {$haveKey && $haveValue} {
00967     set arcs [ArcsKV $name $key $value $arcs]
00968     } elseif {$haveKey} {
00969     set arcs [ArcsK $name $key $arcs]
00970     }
00971 
00972     #
00973     # Apply the general filter command, if specified.
00974     #
00975 
00976     if {$haveFilter} {
00977     lappend fcmd $name
00978     set arcs [uplevel 1 [list ::struct::list filter $arcs $fcmd]]
00979     }
00980 
00981     return $arcs
00982 }
00983 
00984 ret  ::struct::graph::ArcsIN (type name , type cn) {
00985     # arcs -in. "Arcs going into the node set"
00986     #
00987     # ARC/in (NS) := { a | target(a) in NS }
00988 
00989     # The result is all arcs going to at least one node in the set
00990     # 'cn' of nodes.
00991 
00992     # As an arc has only one destination, i.e. is the
00993     # in-arc of exactly one node it is impossible to
00994     # count an arc twice. Therefore there is no need
00995     # to keep track of arcs to avoid duplicates.
00996 
00997     variable ${name}::inArcs
00998 
00999     set arcs {}
01000     foreach node $cn {
01001     foreach e $inArcs($node) {
01002         lappend arcs $e
01003     }
01004     }
01005 
01006     return $arcs
01007 }
01008 
01009 ret  ::struct::graph::ArcsOUT (type name , type cn) {
01010     # arcs -out. "Arcs coming from the node set"
01011     #
01012     # ARC/out (NS) := { a | source(a) in NS }
01013 
01014     # The result is all arcs coming from at least one node in the list
01015     # of arguments.
01016 
01017     variable ${name}::outArcs
01018 
01019     set arcs {}
01020     foreach node $cn {
01021     foreach e $outArcs($node) {
01022         lappend arcs $e
01023     }
01024     }
01025 
01026     return $arcs
01027 }
01028 
01029 ret  ::struct::graph::ArcsADJ (type name , type cn) {
01030     # arcs -adj. "Arcs adjacent to the node set"
01031     #
01032     # ARC/adj (NS) := ARC/in (NS) + ARC/out (NS)
01033 
01034     # Result is all arcs coming from or going to at
01035     # least one node in the list of arguments.
01036 
01037     return [struct::set union \
01038         [ArcsIN  $name $cn] \
01039         [ArcsOUT $name $cn]]
01040     if 0 {
01041     # Alternate implementation using arrays,
01042     # implementing the set union directly,
01043     # intertwined with the data retrieval.
01044 
01045     array set coll  {}
01046     foreach node $condNodes {
01047         foreach e $inArcs($node) {
01048         if {[info exists coll($e)]} {continue}
01049         lappend arcs     $e
01050         set     coll($e) .
01051         }
01052         foreach e $outArcs($node) {
01053         if {[info exists coll($e)]} {continue}
01054         lappend arcs     $e
01055         set     coll($e) .
01056         }
01057     }
01058     }
01059 }
01060 
01061 ret  ::struct::graph::ArcsINN (type name , type cn) {
01062     # arcs -adj. "Arcs inside the node set"
01063     #
01064     # ARC/inner (NS) := ARC/in (NS) * ARC/out (NS)
01065 
01066     # Result is all arcs running between nodes
01067     # in the list.
01068 
01069     return [struct::set intersect \
01070         [ArcsIN  $name $cn] \
01071         [ArcsOUT $name $cn]]
01072     if 0 {
01073     # Alternate implementation using arrays,
01074     # implementing the set intersection
01075     # directly, intertwined with the data
01076     # retrieval.
01077 
01078     array set coll  {}
01079     # Here we do need 'coll' as each might be an in- and
01080     # out-arc for one or two nodes in the list of arguments.
01081 
01082     array set group {}
01083     foreach node $condNodes {
01084         set group($node) .
01085     }
01086 
01087     foreach node $condNodes {
01088         foreach e $inArcs($node) {
01089         set n [lindex $arcNodes($e) 0]
01090         if {![info exists group($n)]} {continue}
01091         if { [info exists coll($e)]}  {continue}
01092         lappend arcs    $e
01093         set     coll($e) .
01094         }
01095         # Second iteration over outgoing arcs not
01096         # required. Any arc found above would be found here as
01097         # well, and arcs not recognized above can't be
01098         # recognized by the out loop either.
01099     }
01100     }
01101 }
01102 
01103 ret  ::struct::graph::ArcsEMB (type name , type cn) {
01104     # arcs -adj. "Arcs bordering the node set"
01105     #
01106     # ARC/emb (NS) := ARC/inner (NS) - ARC/adj (NS)
01107     # <=> (ARC/in + ARC/out) - (ARC/in * ARC/out)
01108     # <=> (ARC/in - ARC/out) + (ARC/out - ARC/in)
01109     # <=> symmetric difference (ARC/in, ARC/out)
01110 
01111     # Result is all arcs from -adj minus the arcs from -inner.
01112     # IOW all arcs going from a node in the list to a node
01113     # which is *not* in the list
01114 
01115     return [struct::set symdiff \
01116         [ArcsIN  $name $cn] \
01117         [ArcsOUT $name $cn]]
01118     if 0 {
01119     # Alternate implementation using arrays,
01120     # implementing the set intersection
01121     # directly, intertwined with the data
01122     # retrieval.
01123 
01124     # This also means that no arc can be counted twice as it
01125     # is either going to a node, or coming from a node in the
01126     # list, but it can't do both, because then it is part of
01127     # -inner, which was excluded!
01128 
01129     array set group {}
01130     foreach node $condNodes {
01131         set group($node) .
01132     }
01133 
01134     foreach node $condNodes {
01135         foreach e $inArcs($node) {
01136         set n [lindex $arcNodes($e) 0]
01137         if {[info exists group($n)]} {continue}
01138         # if {[info exists coll($e)]}  {continue}
01139         lappend arcs    $e
01140         # set     coll($e) .
01141         }
01142         foreach e $outArcs($node) {
01143         set n [lindex $arcNodes($e) 1]
01144         if {[info exists group($n)]} {continue}
01145         # if {[info exists coll($e)]}  {continue}
01146         lappend arcs    $e
01147         # set     coll($e) .
01148         }
01149     }
01150     }
01151 }
01152 
01153 ret  ::struct::graph::ArcsNONE (type name) {
01154     variable ${name}::arcNodes
01155     return [array names arcNodes]
01156 }
01157 
01158 ret  ::struct::graph::ArcsKV (type name , type key , type value , type arcs) {
01159     set filteredArcs {}
01160     foreach arc $arcs {
01161     catch {
01162         set aval [__arc_get $name $arc $key]
01163         if {$aval == $value} {
01164         lappend filteredArcs $arc
01165         }
01166     }
01167     }
01168     return $filteredArcs
01169 }
01170 
01171 ret  ::struct::graph::ArcsK (type name , type key , type arcs) {
01172     set filteredArcs {}
01173     foreach arc $arcs {
01174     catch {
01175         __arc_get $name $arc $key
01176         lappend filteredArcs $arc
01177     }
01178     }
01179     return $filteredArcs
01180 }
01181 
01182 /*  ::struct::graph::_deserialize --*/
01183 /* */
01184 /*  Assignment operator. Copies a serialization into the*/
01185 /*        destination, destroying the original information.*/
01186 /* */
01187 /*  Arguments:*/
01188 /*  name    Name of the graph object we are copying into.*/
01189 /*  serial  Serialized graph to copy from.*/
01190 /* */
01191 /*  Results:*/
01192 /*  Nothing.*/
01193 
01194 ret  ::struct::graph::_deserialize (type name , type serial) {
01195     # As we destroy the original graph as part of
01196     # the copying process we don't have to deal
01197     # with issues like node names from the new graph
01198     # interfering with the old ...
01199 
01200     # I. Get the serialization of the source graph
01201     #    and check it for validity.
01202 
01203     CheckSerialization $serial \
01204         gattr nattr aattr ina outa arcn
01205 
01206     # Get all the relevant data into the scope
01207 
01208     variable ${name}::graphAttr
01209     variable ${name}::nodeAttr
01210     variable ${name}::arcAttr
01211     variable ${name}::inArcs
01212     variable ${name}::outArcs
01213     variable ${name}::arcNodes
01214     variable ${name}::nextAttr
01215 
01216     # Kill the existing information and insert the new
01217     # data in their place.
01218 
01219     foreach n [array names inArcs] {
01220     unset inArcs($n) outArcs($n)
01221     }
01222     array set inArcs   [array get ina]
01223     array set outArcs  [array get outa]
01224     unset ina outa
01225 
01226     foreach a [array names arcNodes] {
01227     unset arcNodes($a)
01228     }
01229     array set arcNodes [array get arcn]
01230     unset arcn
01231 
01232     set nextAttr 0
01233     foreach a [array names nodeAttr] {
01234     unset ${name}::$nodeAttr($a)
01235     }
01236     foreach a [array names arcAttr] {
01237     unset ${name}::$arcAttr($a)
01238     }
01239     foreach n [array names nattr] {
01240     GenAttributeStorage $name node $n
01241     array set ${name}::$nodeAttr($n) $nattr($n)
01242     }
01243     foreach a [array names aattr] {
01244     GenAttributeStorage $name arc $a
01245     array set ${name}::$arcAttr($a) $aattr($a)
01246     }
01247     foreach k [array names graphAttr] {
01248     unset graphAttr($k)
01249     }
01250     array set graphAttr $gattr
01251 
01252     ## Debug ## Dump internals ...
01253     if {0} {
01254     puts "___________________________________ $name"
01255     parray inArcs
01256     parray outArcs
01257     parray arcNodes
01258     parray nodeAttr
01259     parray arcAttr
01260     parray graphAttr
01261     puts ___________________________________
01262     }
01263     return
01264 }
01265 
01266 /*  ::struct::graph::_destroy --*/
01267 /* */
01268 /*  Destroy a graph, including its associated command and data storage.*/
01269 /* */
01270 /*  Arguments:*/
01271 /*  name    name of the graph.*/
01272 /* */
01273 /*  Results:*/
01274 /*  None.*/
01275 
01276 ret  ::struct::graph::_destroy (type name) {
01277     namespace delete $name
01278     interp alias {} $name {}
01279 }
01280 
01281 /*  ::struct::graph::__generateUniqueArcName --*/
01282 /* */
01283 /*  Generate a unique arc name for the given graph.*/
01284 /* */
01285 /*  Arguments:*/
01286 /*  name    name of the graph.*/
01287 /* */
01288 /*  Results:*/
01289 /*  arc name of a arc guaranteed to not exist in the graph.*/
01290 
01291 ret  ::struct::graph::__generateUniqueArcName (type name) {
01292     variable ${name}::nextUnusedArc
01293     while {[__arc_exists $name "arc${nextUnusedArc}"]} {
01294     incr nextUnusedArc
01295     }
01296     return "arc${nextUnusedArc}"
01297 }
01298 
01299 /*  ::struct::graph::__generateUniqueNodeName --*/
01300 /* */
01301 /*  Generate a unique node name for the given graph.*/
01302 /* */
01303 /*  Arguments:*/
01304 /*  name    name of the graph.*/
01305 /* */
01306 /*  Results:*/
01307 /*  node    name of a node guaranteed to not exist in the graph.*/
01308 
01309 ret  ::struct::graph::__generateUniqueNodeName (type name) {
01310     variable ${name}::nextUnusedNode
01311     while {[__node_exists $name "node${nextUnusedNode}"]} {
01312     incr nextUnusedNode
01313     }
01314     return "node${nextUnusedNode}"
01315 }
01316 
01317 /*  ::struct::graph::_get --*/
01318 /* */
01319 /*  Get a keyed value from the graph itself*/
01320 /* */
01321 /*  Arguments:*/
01322 /*  name    name of the graph.*/
01323 /*  key key to lookup*/
01324 /* */
01325 /*  Results:*/
01326 /*  value   value associated with the key given.*/
01327 
01328 ret  ::struct::graph::_get (type name , type key) {
01329     variable  ${name}::graphAttr
01330     if { ![info exists graphAttr($key)] } {
01331     return -code error "invalid key \"$key\" for graph \"$name\""
01332     }
01333     return $graphAttr($key)
01334 }
01335 
01336 /*  ::struct::graph::_getall --*/
01337 /* */
01338 /*  Get an attribute dictionary from a graph.*/
01339 /* */
01340 /*  Arguments:*/
01341 /*  name    name of the graph.*/
01342 /*  pattern optional, glob pattern*/
01343 /* */
01344 /*  Results:*/
01345 /*  value   value associated with the key given.*/
01346 
01347 ret  ::struct::graph::_getall (type name , optional pattern =*) { 
01348     variable ${name}::graphAttr
01349     return [array get graphAttr $pattern]
01350 }
01351 
01352 /*  ::struct::graph::_keys --*/
01353 /* */
01354 /*  Get a list of keys from a graph.*/
01355 /* */
01356 /*  Arguments:*/
01357 /*  name    name of the graph.*/
01358 /*  pattern optional, glob pattern*/
01359 /* */
01360 /*  Results:*/
01361 /*  value   list of known keys*/
01362 
01363 ret  ::struct::graph::_keys (type name , optional pattern =*) { 
01364     variable   ${name}::graphAttr
01365     return [array names graphAttr $pattern]
01366 }
01367 
01368 /*  ::struct::graph::_keyexists --*/
01369 /* */
01370 /*  Test for existence of a given key in a graph.*/
01371 /* */
01372 /*  Arguments:*/
01373 /*  name    name of the graph.*/
01374 /*  key key to lookup*/
01375 /* */
01376 /*  Results:*/
01377 /*  1 if the key exists, 0 else.*/
01378 
01379 ret  ::struct::graph::_keyexists (type name , type key) {
01380     variable   ${name}::graphAttr
01381     return [info exists graphAttr($key)]
01382 }
01383 
01384 /*  ::struct::graph::_node --*/
01385 /* */
01386 /*  Dispatches the invocation of node methods to the proper handler*/
01387 /*  procedure.*/
01388 /* */
01389 /*  Arguments:*/
01390 /*  name    name of the graph.*/
01391 /*  cmd node command to invoke*/
01392 /*  args    arguments to propagate to the handler for the node command*/
01393 /* */
01394 /*  Results:*/
01395 /*  As of the the invoked handler.*/
01396 
01397 ret  ::struct::graph::_node (type name , type cmd , type args) {
01398     # Split the args into command and args components
01399     set sub __node_$cmd
01400     if { [llength [info commands ::struct::graph::$sub]] == 0 } {
01401     set optlist [lsort [info commands ::struct::graph::__node_*]]
01402     set xlist {}
01403     foreach p $optlist {
01404         set p [namespace tail $p]
01405         lappend xlist [string range $p 7 end]
01406     }
01407     set optlist [linsert [join $xlist ", "] "end-1" "or"]
01408     return -code error \
01409         "bad option \"$cmd\": must be $optlist"
01410     }
01411     uplevel 1 [linsert $args 0 ::struct::graph::$sub $name]
01412 }
01413 
01414 /*  ::struct::graph::__node_degree --*/
01415 /* */
01416 /*  Return the number of arcs adjacent to the specified node.*/
01417 /*  If one of the restrictions -in or -out is given only*/
01418 /*  incoming resp. outgoing arcs are counted.*/
01419 /* */
01420 /*  Arguments:*/
01421 /*  name    name of the graph.*/
01422 /*  args    option, followed by the node.*/
01423 /* */
01424 /*  Results:*/
01425 /*  None.*/
01426 
01427 ret  ::struct::graph::__node_degree (type name , type args) {
01428 
01429     if {([llength $args] < 1) || ([llength $args] > 2)} {
01430     return -code error "wrong # args: should be \"$name node degree ?-in|-out? node\""
01431     }
01432 
01433     switch -exact -- [llength $args] {
01434     1 {
01435         set opt {}
01436         set node [lindex $args 0]
01437     }
01438     2 {
01439         set opt  [lindex $args 0]
01440         set node [lindex $args 1]
01441     }
01442     default {return -code error "Can't happen, panic"}
01443     }
01444 
01445     # Validate the option.
01446 
01447     switch -exact -- $opt {
01448     {}   -
01449     -in  -
01450     -out {}
01451     default {
01452         return -code error "bad option \"$opt\": must be -in or -out"
01453     }
01454     }
01455 
01456     # Validate the node
01457 
01458     CheckMissingNode $name $node
01459 
01460     variable ${name}::inArcs
01461     variable ${name}::outArcs
01462 
01463     switch -exact -- $opt {
01464     -in  {
01465         set result [llength $inArcs($node)]
01466     }
01467     -out {
01468         set result [llength $outArcs($node)]
01469     }
01470     {} {
01471         set result [expr {[llength $inArcs($node)] \
01472             + [llength $outArcs($node)]}]
01473 
01474         # loops count twice, don't do <set> arithmetics, i.e. no union!
01475         if {0} {
01476         array set coll  {}
01477         set result [llength $inArcs($node)]
01478 
01479         foreach e $inArcs($node) {
01480             set coll($e) .
01481         }
01482         foreach e $outArcs($node) {
01483             if {[info exists coll($e)]} {continue}
01484             incr result
01485             set     coll($e) .
01486         }
01487         }
01488     }
01489     default {return -code error "Can't happen, panic"}
01490     }
01491 
01492     return $result
01493 }
01494 
01495 /*  ::struct::graph::__node_delete --*/
01496 /* */
01497 /*  Remove a node from a graph, including all of its values.*/
01498 /*  Additionally removes the arcs connected to this node.*/
01499 /* */
01500 /*  Arguments:*/
01501 /*  name    name of the graph.*/
01502 /*  args    list of the nodes to delete.*/
01503 /* */
01504 /*  Results:*/
01505 /*  None.*/
01506 
01507 ret  ::struct::graph::__node_delete (type name , type args) {
01508     if {![llength $args]} {
01509     return {wrong # args: should be "::struct::graph::__node_delete name node node..."}
01510     }
01511     foreach node $args {CheckMissingNode $name $node}
01512 
01513     variable ${name}::inArcs
01514     variable ${name}::outArcs
01515     variable ${name}::nodeAttr
01516 
01517     foreach node $args {
01518     # Remove all the arcs connected to this node
01519     foreach e $inArcs($node) {
01520         __arc_delete $name $e
01521     }
01522     foreach e $outArcs($node) {
01523         # Check existence to avoid problems with
01524         # loops (they are in and out arcs! at
01525         # the same time and thus already deleted)
01526         if { [__arc_exists $name $e] } {
01527         __arc_delete $name $e
01528         }
01529     }
01530 
01531     unset inArcs($node)
01532     unset outArcs($node)
01533 
01534     if {[info exists nodeAttr($node)]} {
01535         unset ${name}::$nodeAttr($node)
01536         unset nodeAttr($node)
01537     }
01538     }
01539 
01540     return
01541 }
01542 
01543 /*  ::struct::graph::__node_exists --*/
01544 /* */
01545 /*  Test for existence of a given node in a graph.*/
01546 /* */
01547 /*  Arguments:*/
01548 /*  name    name of the graph.*/
01549 /*  node    node to look for.*/
01550 /* */
01551 /*  Results:*/
01552 /*  1 if the node exists, 0 else.*/
01553 
01554 ret  ::struct::graph::__node_exists (type name , type node) {
01555     return [info exists ${name}::inArcs($node)]
01556 }
01557 
01558 /*  ::struct::graph::__node_get --*/
01559 /* */
01560 /*  Get a keyed value from a node in a graph.*/
01561 /* */
01562 /*  Arguments:*/
01563 /*  name    name of the graph.*/
01564 /*  node    node to query.*/
01565 /*  key key to lookup*/
01566 /* */
01567 /*  Results:*/
01568 /*  value   value associated with the key given.*/
01569 
01570 ret  ::struct::graph::__node_get (type name , type node , type key) {
01571     CheckMissingNode $name $node
01572  
01573     variable ${name}::nodeAttr
01574     if {![info exists nodeAttr($node)]} {
01575     # No attribute data for this node, key has to be invalid.
01576     return -code error "invalid key \"$key\" for node \"$node\""
01577     }
01578 
01579     upvar ${name}::$nodeAttr($node) data
01580     if { ![info exists data($key)] } {
01581     return -code error "invalid key \"$key\" for node \"$node\""
01582     }
01583     return $data($key)
01584 }
01585 
01586 /*  ::struct::graph::__node_getall --*/
01587 /* */
01588 /*  Get a serialized list of key/value pairs from a node in a graph.*/
01589 /* */
01590 /*  Arguments:*/
01591 /*  name    name of the graph.*/
01592 /*  node    node to query.*/
01593 /*  pattern optional glob pattern to restrict retrieval*/
01594 /* */
01595 /*  Results:*/
01596 /*  value   value associated with the key given.*/
01597 
01598 ret  ::struct::graph::__node_getall (type name , type node , optional pattern =*) { 
01599     CheckMissingNode $name $node
01600 
01601     variable ${name}::nodeAttr
01602     if {![info exists nodeAttr($node)]} {
01603     # No attributes ...
01604     return {}
01605     }
01606 
01607     upvar ${name}::$nodeAttr($node) data
01608     return [array get data $pattern]
01609 }
01610 
01611 /*  ::struct::graph::__node_keys --*/
01612 /* */
01613 /*  Get a list of keys from a node in a graph.*/
01614 /* */
01615 /*  Arguments:*/
01616 /*  name    name of the graph.*/
01617 /*  node    node to query.*/
01618 /*  pattern optional glob pattern to restrict retrieval*/
01619 /* */
01620 /*  Results:*/
01621 /*  value   value associated with the key given.*/
01622 
01623 ret  ::struct::graph::__node_keys (type name , type node , optional pattern =*) { 
01624     CheckMissingNode $name $node
01625 
01626     variable ${name}::nodeAttr
01627     if {![info exists nodeAttr($node)]} {
01628     # No attributes ...
01629     return {}
01630     }
01631 
01632     upvar ${name}::$nodeAttr($node) data
01633     return [array names data $pattern]
01634 }
01635 
01636 /*  ::struct::graph::__node_keyexists --*/
01637 /* */
01638 /*  Test for existence of a given key for a node in a graph.*/
01639 /* */
01640 /*  Arguments:*/
01641 /*  name    name of the graph.*/
01642 /*  node    node to query.*/
01643 /*  key key to lookup*/
01644 /* */
01645 /*  Results:*/
01646 /*  1 if the key exists, 0 else.*/
01647 
01648 ret  ::struct::graph::__node_keyexists (type name , type node , type key) {
01649     CheckMissingNode $name $node
01650     
01651     variable ${name}::nodeAttr
01652     if {![info exists nodeAttr($node)]} {
01653     # No attribute data for this node, key cannot exist.
01654     return 0
01655     }
01656 
01657     upvar ${name}::$nodeAttr($node) data
01658     return [info exists data($key)]
01659 }
01660 
01661 /*  ::struct::graph::__node_insert --*/
01662 /* */
01663 /*  Add a node to a graph.*/
01664 /* */
01665 /*  Arguments:*/
01666 /*  name        name of the graph.*/
01667 /*  args        node to insert; must be unique.  If none is given,*/
01668 /*          the routine will generate a unique node name.*/
01669 /* */
01670 /*  Results:*/
01671 /*  node        The name of the new node.*/
01672 
01673 ret  ::struct::graph::__node_insert (type name , type args) {
01674     if {[llength $args] == 0} {
01675     # No node name was given; generate a unique one
01676     set args [list [__generateUniqueNodeName $name]]
01677     } else {
01678     foreach node $args {CheckDuplicateNode $name $node}
01679     }
01680     
01681     variable ${name}::inArcs
01682     variable ${name}::outArcs
01683 
01684     foreach node $args {
01685     # Set up the new node
01686     set inArcs($node)  {}
01687     set outArcs($node) {}
01688     }
01689 
01690     return $args
01691 }
01692 
01693 /*  ::struct::graph::__node_opposite --*/
01694 /* */
01695 /*  Retrieve node opposite to the specified one, along the arc.*/
01696 /* */
01697 /*  Arguments:*/
01698 /*  name        name of the graph.*/
01699 /*  node        node to look up.*/
01700 /*  arc     arc to look up.*/
01701 /* */
01702 /*  Results:*/
01703 /*  nodex   Node opposite to <node,arc>*/
01704 
01705 ret  ::struct::graph::__node_opposite (type name , type node , type arc) {
01706     CheckMissingNode $name $node    
01707     CheckMissingArc  $name $arc
01708 
01709     variable ${name}::arcNodes
01710 
01711     # Node must be connected to at least one end of the arc.
01712 
01713     if {[string equal $node [lindex $arcNodes($arc) 0]]} {
01714     set result [lindex $arcNodes($arc) 1]
01715     } elseif {[string equal $node [lindex $arcNodes($arc) 1]]} {
01716     set result [lindex $arcNodes($arc) 0]
01717     } else {
01718     return -code error "node \"$node\" and arc \"$arc\" are not connected\
01719         in graph \"$name\""
01720     }
01721 
01722     return $result
01723 }
01724 
01725 /*  ::struct::graph::__node_set --*/
01726 /* */
01727 /*  Set or get a value for a node in a graph.*/
01728 /* */
01729 /*  Arguments:*/
01730 /*  name    name of the graph.*/
01731 /*  node    node to modify or query.*/
01732 /*  key attribute to modify or query*/
01733 /*  args    ?value?*/
01734 /* */
01735 /*  Results:*/
01736 /*  val value associated with the given key of the given node*/
01737 
01738 ret  ::struct::graph::__node_set (type name , type node , type key , type args) {
01739     if { [llength $args] > 1 } {
01740     return -code error "wrong # args: should be \"$name node set node key ?value?\""
01741     }
01742     CheckMissingNode $name $node
01743     
01744     if { [llength $args] > 0 } {
01745     # Setting the value. This may have to create
01746     # the attribute array for this particular
01747     # node
01748 
01749     variable ${name}::nodeAttr
01750     if {![info exists nodeAttr($node)]} {
01751         # No attribute data for this node,
01752         # so create it as we need it now.
01753         GenAttributeStorage $name node $node
01754     }
01755     upvar ${name}::$nodeAttr($node) data
01756 
01757     return [set data($key) [lindex $args end]]
01758     } else {
01759     # Getting a value
01760     return [__node_get $name $node $key]
01761     }
01762 }
01763 
01764 /*  ::struct::graph::__node_append --*/
01765 /* */
01766 /*  Append a value for a node in a graph.*/
01767 /* */
01768 /*  Arguments:*/
01769 /*  name    name of the graph.*/
01770 /*  node    node to modify or query.*/
01771 /*  args    key value*/
01772 /* */
01773 /*  Results:*/
01774 /*  val value associated with the given key of the given node*/
01775 
01776 ret  ::struct::graph::__node_append (type name , type node , type key , type value) {
01777     CheckMissingNode $name $node
01778 
01779     variable ${name}::nodeAttr
01780     if {![info exists nodeAttr($node)]} {
01781     # No attribute data for this node,
01782     # so create it as we need it.
01783     GenAttributeStorage $name node $node
01784     }
01785 
01786     upvar ${name}::$nodeAttr($node) data
01787     return [append data($key) $value]
01788 }
01789 
01790 /*  ::struct::graph::__node_attr --*/
01791 /* */
01792 /*  Return attribute data for one key and multiple nodes, possibly all.*/
01793 /* */
01794 /*  Arguments:*/
01795 /*  name    Name of the graph object.*/
01796 /*  key Name of the attribute to retrieve.*/
01797 /* */
01798 /*  Results:*/
01799 /*  children    Dictionary mapping nodes to attribute data.*/
01800 
01801 ret  ::struct::graph::__node_attr (type name , type key , type args) {
01802     # Syntax:
01803     #
01804     # t attr key
01805     # t attr key -nodes {nodelist}
01806     # t attr key -glob nodepattern
01807     # t attr key -regexp nodepattern
01808 
01809     variable ${name}::nodeAttr
01810 
01811     set usage "wrong # args: should be \"[list $name] node attr key ?-nodes list|-glob pattern|-regexp pattern?\""
01812     if {([llength $args] != 0) && ([llength $args] != 2)} {
01813     return -code error $usage
01814     } elseif {[llength $args] == 0} {
01815     # This automatically restricts the list
01816     # to nodes which can have the attribute
01817     # in question.
01818 
01819     set nodes [array names nodeAttr]
01820     } else {
01821     # Determine a list of nodes to look at
01822     # based on the chosen restriction.
01823 
01824     foreach {mode value} $args break
01825     switch -exact -- $mode {
01826         -nodes {
01827         # This is the only branch where we have to
01828         # perform an explicit restriction to the
01829         # nodes which have attributes.
01830         set nodes {}
01831         foreach n $value {
01832             if {![info exists nodeAttr($n)]} continue
01833             lappend nodes $n
01834         }
01835         }
01836         -glob {
01837         set nodes [array names nodeAttr $value]
01838         }
01839         -regexp {
01840         set nodes {}
01841         foreach n [array names nodeAttr] {
01842             if {![regexp -- $value $n]} continue
01843             lappend nodes $n
01844         }
01845         }
01846         default {
01847         return -code error "bad type \"$mode\": must be -glob, -nodes, or -regexp"
01848         }
01849     }
01850     }
01851 
01852     # Without possibly matching nodes
01853     # the result has to be empty.
01854 
01855     if {![llength $nodes]} {
01856     return {}
01857     }
01858 
01859     # Now locate matching keys and their values.
01860 
01861     set result {}
01862     foreach n $nodes {
01863     upvar ${name}::$nodeAttr($n) data
01864     if {[info exists data($key)]} {
01865         lappend result $n $data($key)
01866     }
01867     }
01868 
01869     return $result
01870 }
01871 
01872 /*  ::struct::graph::__node_lappend --*/
01873 /* */
01874 /*  lappend a value for a node in a graph.*/
01875 /* */
01876 /*  Arguments:*/
01877 /*  name    name of the graph.*/
01878 /*  node    node to modify or query.*/
01879 /*  args    key value*/
01880 /* */
01881 /*  Results:*/
01882 /*  val value associated with the given key of the given node*/
01883 
01884 ret  ::struct::graph::__node_lappend (type name , type node , type key , type value) {
01885     CheckMissingNode $name $node
01886 
01887     variable ${name}::nodeAttr
01888     if {![info exists nodeAttr($node)]} {
01889     # No attribute data for this node,
01890     # so create it as we need it.
01891     GenAttributeStorage $name node $node
01892     }
01893 
01894     upvar ${name}::$nodeAttr($node) data
01895     return [lappend data($key) $value]
01896 }
01897 
01898 /*  ::struct::graph::__node_unset --*/
01899 /* */
01900 /*  Remove a keyed value from a node.*/
01901 /* */
01902 /*  Arguments:*/
01903 /*  name    name of the graph.*/
01904 /*  node    node to modify.*/
01905 /*  key attribute to remove*/
01906 /* */
01907 /*  Results:*/
01908 /*  None.*/
01909 
01910 ret  ::struct::graph::__node_unset (type name , type node , type key) {
01911     CheckMissingNode $name $node
01912 
01913     variable ${name}::nodeAttr
01914     if {![info exists nodeAttr($node)]} {
01915     # No attribute data for this node,
01916     # nothing to do.
01917     return
01918     }
01919 
01920     upvar ${name}::$nodeAttr($node) data
01921     catch {unset data($key)}
01922 
01923     if {[array size data] == 0} {
01924     # No attributes stored for this node, squash the whole array.
01925     unset nodeAttr($node)
01926     unset data
01927     }
01928     return
01929 }
01930 
01931 /*  ::struct::graph::_nodes --*/
01932 /* */
01933 /*  Return a list of all nodes in a graph satisfying some restriction.*/
01934 /* */
01935 /*  Arguments:*/
01936 /*  name    name of the graph.*/
01937 /*  args    list of options and nodes specifying the restriction.*/
01938 /* */
01939 /*  Results:*/
01940 /*  nodes   list of nodes*/
01941 
01942 ret  ::struct::graph::_nodes (type name , type args) {
01943 
01944     CheckE $name nodes $args
01945 
01946     switch -exact -- $cond {
01947     none      {set nodes [NodesNONE $name]}
01948     in        {set nodes [NodesIN   $name $condNodes]}
01949     out       {set nodes [NodesOUT  $name $condNodes]}
01950     adj       {set nodes [NodesADJ  $name $condNodes]}
01951     inner     {set nodes [NodesINN  $name $condNodes]}
01952     embedding {set nodes [NodesEMB  $name $condNodes]}
01953     default   {return -code error "Can't happen, panic"}
01954     }
01955 
01956     #
01957     # We have a list of nodes that match the relation to the nodes.
01958     # Now filter according to -key and -value.
01959     #
01960 
01961     if {$haveKey && $haveValue} {
01962     set nodes [NodesKV $name $key $value $nodes]
01963     } elseif {$haveKey} {
01964     set nodes [NodesK $name $key $nodes]
01965     }
01966 
01967     #
01968     # Apply the general filter command, if specified.
01969     #
01970 
01971     if {$haveFilter} {
01972     lappend fcmd $name
01973     set nodes [uplevel 1 [list ::struct::list filter $nodes $fcmd]]
01974     }
01975 
01976     return $nodes
01977 }
01978 
01979 ret  ::struct::graph::NodesIN (type name , type cn) {
01980     # nodes -in.
01981     # "Neighbours with arcs going into the node set"
01982     #
01983     # NODES/in (NS) := { source(a) | a in ARC/in (NS) }
01984 
01985     # Result is all nodes with at least one arc going to
01986     # at least one node in the list of arguments.
01987 
01988     variable ${name}::inArcs
01989     variable ${name}::arcNodes
01990 
01991     set nodes {}
01992     array set coll {}
01993 
01994     foreach node $cn {
01995     foreach e $inArcs($node) {
01996         set n [lindex $arcNodes($e) 0]
01997         if {[info exists coll($n)]} {continue}
01998         lappend nodes    $n
01999         set     coll($n) .
02000     }
02001     }
02002     return $nodes
02003 }
02004 
02005 ret  ::struct::graph::NodesOUT (type name , type cn) {
02006     # nodes -out.
02007     # "Neighbours with arcs coming from the node set"
02008     #
02009     # NODES/out (NS) := { target(a) | a in ARC/out (NS) }
02010 
02011     # Result is all nodes with at least one arc coming from
02012     # at least one node in the list of arguments.
02013 
02014     variable ${name}::outArcs
02015     variable ${name}::arcNodes
02016 
02017     set nodes {}
02018     array set coll {}
02019 
02020     foreach node $cn {
02021     foreach e $outArcs($node) {
02022         set n [lindex $arcNodes($e) 1]
02023         if {[info exists coll($n)]} {continue}
02024         lappend nodes    $n
02025         set     coll($n) .
02026     }
02027     }
02028     return $nodes
02029 }
02030 
02031 ret  ::struct::graph::NodesADJ (type name , type cn) {
02032     # nodes -adj.
02033     # "Neighbours of the node set"
02034     #
02035     # NODES/adj (NS) := NODES/in (NS) + NODES/out (NS)
02036 
02037     # Result is all nodes with at least one arc coming from
02038     # or going to at least one node in the list of arguments.
02039 
02040     return [struct::set union \
02041         [NodesIN  $name $cn] \
02042         [NodesOUT $name $cn]]
02043     if 0 {
02044     # Alternate implementation using arrays,
02045     # implementing the set union directly,
02046     # intertwined with the data retrieval.
02047 
02048     foreach node $cn {
02049         foreach e $inArcs($node) {
02050         set n [lindex $arcNodes($e) 0]
02051         if {[info exists coll($n)]} {continue}
02052         lappend nodes    $n
02053         set     coll($n) .
02054         }
02055         foreach e $outArcs($node) {
02056         set n [lindex $arcNodes($e) 1]
02057         if {[info exists coll($n)]} {continue}
02058         lappend nodes    $n
02059         set     coll($n) .
02060         }
02061     }
02062     }
02063 }
02064 
02065 ret  ::struct::graph::NodesINN (type name , type cn) {
02066     # nodes -adj.
02067     # "Inner node of the node set"
02068     #
02069     # NODES/inner (NS) := NODES/adj (NS) * NS
02070 
02071     # Result is all nodes from the set with at least one arc coming
02072     # from or going to at least one node in the set.
02073     #
02074     # I.e the adjacent nodes also in the set.
02075 
02076     return [struct::set intersect \
02077         [NodesADJ $name $cn] $cn]
02078 
02079     if 0 {
02080     # Alternate implementation using arrays,
02081     # implementing the set intersect/union
02082     # directly, intertwined with the data retrieval.
02083 
02084     array set group {}
02085     foreach node $cn {
02086         set group($node) .
02087     }
02088 
02089     foreach node $cn {
02090         foreach e $inArcs($node) {
02091         set n [lindex $arcNodes($e) 0]
02092         if {![info exists group($n)]} {continue}
02093         if { [info exists coll($n)]}  {continue}
02094         lappend nodes    $n
02095         set     coll($n) .
02096         }
02097         foreach e $outArcs($node) {
02098         set n [lindex $arcNodes($e) 1]
02099         if {![info exists group($n)]} {continue}
02100         if { [info exists coll($n)]}  {continue}
02101         lappend nodes    $n
02102         set     coll($n) .
02103         }
02104     }
02105     }
02106 }
02107 
02108 ret  ::struct::graph::NodesEMB (type name , type cn) {
02109     # nodes -embedding.
02110     # "Embedding nodes for the node set"
02111     #
02112     # NODES/emb (NS) := NODES/adj (NS) - NS
02113 
02114     # Result is all nodes with at least one arc coming from or going
02115     # to at least one node in the set, but not in the set itself
02116     #
02117     # I.e the adjacent nodes not in the set.
02118 
02119     # Result is all nodes from the set with at least one arc coming
02120     # from or going to at least one node in the set.
02121     # I.e the adjacent nodes still in the set.
02122 
02123     return [struct::set difference \
02124         [NodesADJ $name $cn] $cn]
02125 
02126     if 0 {
02127     # Alternate implementation using arrays,
02128     # implementing the set diff/union directly,
02129     # intertwined with the data retrieval.
02130 
02131     array set group {}
02132     foreach node $cn {
02133         set group($node) .
02134     }
02135 
02136     foreach node $cn {
02137         foreach e $inArcs($node) {
02138         set n [lindex $arcNodes($e) 0]
02139         if {[info exists group($n)]} {continue}
02140         if {[info exists coll($n)]}  {continue}
02141         lappend nodes    $n
02142         set     coll($n) .
02143         }
02144         foreach e $outArcs($node) {
02145         set n [lindex $arcNodes($e) 1]
02146         if {[info exists group($n)]} {continue}
02147         if {[info exists coll($n)]}  {continue}
02148         lappend nodes    $n
02149         set     coll($n) .
02150         }
02151     }
02152     }
02153 }
02154 
02155 ret  ::struct::graph::NodesNONE (type name) {
02156     variable ${name}::inArcs
02157     return [array names inArcs]
02158 }
02159 
02160 ret  ::struct::graph::NodesKV (type name , type key , type value , type nodes) {
02161     set filteredNodes {}
02162     foreach node $nodes {
02163     catch {
02164         set nval [__node_get $name $node $key]
02165         if {$nval == $value} {
02166         lappend filteredNodes $node
02167         }
02168     }
02169     }
02170     return $filteredNodes
02171 }
02172 
02173 ret  ::struct::graph::NodesK (type name , type key , type nodes) {
02174     set filteredNodes {}
02175     foreach node $nodes {
02176     catch {
02177         __node_get $name $node $key
02178         lappend filteredNodes $node
02179     }
02180     }
02181     return $filteredNodes
02182 }
02183 
02184 /*  ::struct::graph::__node_rename --*/
02185 /* */
02186 /*  Rename a node in place.*/
02187 /* */
02188 /*  Arguments:*/
02189 /*  name    name of the graph.*/
02190 /*  node    Name of the node to rename*/
02191 /*  newname The new name of the node.*/
02192 /* */
02193 /*  Results:*/
02194 /*  The new name of the node.*/
02195 
02196 ret  ::struct::graph::__node_rename (type name , type node , type newname) {
02197     CheckMissingNode   $name $node
02198     CheckDuplicateNode $name $newname
02199 
02200     set oldname  $node
02201 
02202     # Perform the rename in the internal
02203     # data structures.
02204 
02205     # - graphAttr - not required, node independent.
02206     # - arcAttr   - not required, node independent.
02207     # - counters  - not required
02208 
02209     variable ${name}::nodeAttr
02210     variable ${name}::inArcs
02211     variable ${name}::outArcs
02212     variable ${name}::arcNodes
02213 
02214     # Node relocation
02215 
02216     set inArcs($newname)    [set in $inArcs($oldname)]
02217     unset                            inArcs($oldname)
02218     set outArcs($newname) [set out $outArcs($oldname)]
02219     unset                           outArcs($oldname)
02220 
02221     if {[info exists nodeAttr($oldname)]} {
02222     set nodeAttr($newname) $nodeAttr($oldname)
02223     unset                   nodeAttr($oldname)
02224     }
02225 
02226     # Update all relevant arcs.
02227     # 8.4: lset ...
02228 
02229     foreach a $in {
02230     set arcNodes($a) [list [lindex $arcNodes($a) 0] $newname]
02231     }
02232     foreach a $out {
02233     set arcNodes($a) [list $newname [lindex $arcNodes($a) 1]]
02234     }
02235 
02236     return $newname
02237 }
02238 
02239 /*  ::struct::graph::_serialize --*/
02240 /* */
02241 /*  Serialize a graph object (partially) into a transportable value.*/
02242 /*  If only a subset of nodes is serialized the result will be a sub-*/
02243 /*  graph in the mathematical sense of the word: These nodes and all*/
02244 /*  arcs which are only between these nodes. No arcs to modes outside*/
02245 /*  of the listed set.*/
02246 /* */
02247 /*  Arguments:*/
02248 /*  name    Name of the graph.*/
02249 /*  args    list of nodes to place into the serialized graph*/
02250 /* */
02251 /*  Results:*/
02252 /*  A list structure describing the part of the graph which was serialized.*/
02253 
02254 ret  ::struct::graph::_serialize (type name , type args) {
02255 
02256     # all - boolean flag - set if and only if the all nodes of the
02257     # graph are chosen for serialization. Because if that is true we
02258     # can skip the step finding the relevant arcs and simply take all
02259     # arcs.
02260 
02261     variable ${name}::arcNodes
02262     variable ${name}::inArcs
02263 
02264     set all 0
02265     if {[llength $args] > 0} {
02266     set nodes [luniq $args]
02267     foreach n $nodes {CheckMissingNode $name $n}
02268     if {[llength $nodes] == [array size inArcs]} {
02269         set all 1
02270     }
02271     } else {
02272     set nodes [array names inArcs]
02273     set all 1
02274     }
02275 
02276     if {$all} {
02277     set arcs [array names arcNodes]
02278     } else {
02279     set arcs [eval [linsert $nodes 0 _arcs $name -inner]]
02280     }
02281 
02282     variable ${name}::nodeAttr
02283     variable ${name}::arcAttr
02284     variable ${name}::graphAttr
02285 
02286     set na {}
02287     set aa {}
02288     array set np {}
02289 
02290     # node indices, attribute data ...
02291     set i 0
02292     foreach n $nodes {
02293     set np($n) [list $i]
02294     incr i 3
02295 
02296     if {[info exists nodeAttr($n)]} {
02297         upvar ${name}::$nodeAttr($n) data
02298         lappend np($n) [array get data]
02299     } else {
02300         lappend np($n) {}
02301     }
02302     }
02303 
02304     # arc dictionary
02305     set arcdata  {}
02306     foreach a $arcs {
02307     foreach {src dst} $arcNodes($a) break
02308     # Arc information
02309 
02310     set     arc [list $a]
02311     lappend arc [lindex $np($dst) 0]
02312     if {[info exists arcAttr($a)]} {
02313         upvar ${name}::$arcAttr($a) data
02314         lappend arc [array get data]
02315     } else {
02316         lappend arc {}
02317     }
02318 
02319     # Add the information to the node
02320     # indices ...
02321 
02322     lappend np($src) $arc
02323     }
02324 
02325     # Combine the transient data into one result.
02326 
02327     set result [list]
02328     foreach n $nodes {
02329     lappend result $n
02330     lappend result [lindex $np($n) 1]
02331     lappend result [lrange $np($n) 2 end]
02332     }
02333     lappend result [array get graphAttr]
02334 
02335     return $result
02336 }
02337 
02338 /*  ::struct::graph::_set --*/
02339 /* */
02340 /*  Set or get a keyed value from the graph itself*/
02341 /* */
02342 /*  Arguments:*/
02343 /*  name    name of the graph.*/
02344 /*  key attribute to modify or query*/
02345 /*  args    ?value?*/
02346 /* */
02347 /*  Results:*/
02348 /*  value   value associated with the key given.*/
02349 
02350 ret  ::struct::graph::_set (type name , type key , type args) {
02351     if { [llength $args] > 1 } {
02352     return -code error "wrong # args: should be \"$name set key ?value?\""
02353     }
02354     if { [llength $args] > 0 } {
02355     variable ${name}::graphAttr
02356     return [set graphAttr($key) [lindex $args end]]
02357     } else {
02358     # Getting a value
02359     return [_get $name $key]
02360     }
02361 }
02362 
02363 /*  ::struct::graph::_swap --*/
02364 /* */
02365 /*  Swap two nodes in a graph.*/
02366 /* */
02367 /*  Arguments:*/
02368 /*  name    name of the graph.*/
02369 /*  node1   first node to swap.*/
02370 /*  node2   second node to swap.*/
02371 /* */
02372 /*  Results:*/
02373 /*  None.*/
02374 
02375 ret  ::struct::graph::_swap (type name , type node1 , type node2) {
02376     # Can only swap two real nodes
02377     CheckMissingNode $name $node1
02378     CheckMissingNode $name $node2
02379 
02380     # Can't swap a node with itself
02381     if { [string equal $node1 $node2] } {
02382     return -code error "cannot swap node \"$node1\" with itself"
02383     }
02384 
02385     # Swapping nodes means swapping their labels, values and arcs
02386     variable ${name}::outArcs
02387     variable ${name}::inArcs
02388     variable ${name}::arcNodes
02389     variable ${name}::nodeAttr
02390 
02391     # Redirect arcs to the new nodes.
02392 
02393     foreach e $inArcs($node1)  {lset arcNodes($e) end $node2}
02394     foreach e $inArcs($node2)  {lset arcNodes($e) end $node1}
02395     foreach e $outArcs($node1) {lset arcNodes($e) 0 $node2}
02396     foreach e $outArcs($node2) {lset arcNodes($e) 0 $node1}
02397 
02398     # Swap arc lists
02399 
02400     set tmp            $inArcs($node1)
02401     set inArcs($node1) $inArcs($node2)
02402     set inArcs($node2) $tmp
02403 
02404     set tmp             $outArcs($node1)
02405     set outArcs($node1) $outArcs($node2)
02406     set outArcs($node2) $tmp
02407 
02408     # Swap the values
02409     # More complicated now with the possibility that nodes do not have
02410     # attribute storage associated with them. But also
02411     # simpler as we just have to swap/move the array
02412     # reference
02413 
02414     if {
02415     [set ia [info exists nodeAttr($node1)]] ||
02416     [set ib [info exists nodeAttr($node2)]]
02417     } {
02418     # At least one of the nodes has attribute data. We simply swap
02419     # the references to the arrays containing them. No need to
02420     # copy the actual data around.
02421 
02422     if {$ia && $ib} {
02423         set tmp               $nodeAttr($node1)
02424         set nodeAttr($node1) $nodeAttr($node2)
02425         set nodeAttr($node2) $tmp
02426     } elseif {$ia} {
02427         set   nodeAttr($node2) $nodeAttr($node1)
02428         unset nodeAttr($node1)
02429     } elseif {$ib} {
02430         set   nodeAttr($node1) $nodeAttr($node2)
02431         unset nodeAttr($node2)
02432     } else {
02433         return -code error "Impossible condition."
02434     }
02435     } ; # else: No attribute storage => Nothing to do {}
02436 
02437     return
02438 }
02439 
02440 /*  ::struct::graph::_unset --*/
02441 /* */
02442 /*  Remove a keyed value from the graph itself*/
02443 /* */
02444 /*  Arguments:*/
02445 /*  name    name of the graph.*/
02446 /*  key attribute to remove*/
02447 /* */
02448 /*  Results:*/
02449 /*  None.*/
02450 
02451 ret  ::struct::graph::_unset (type name , type key) {
02452     variable ${name}::graphAttr
02453     if {[info exists  graphAttr($key)]} {
02454     unset graphAttr($key)
02455     }
02456     return
02457 }
02458 
02459 /*  ::struct::graph::_walk --*/
02460 /* */
02461 /*  Walk a graph using a pre-order depth or breadth first*/
02462 /*  search. Pre-order DFS is the default.  At each node that is visited,*/
02463 /*  a command will be called with the name of the graph and the node.*/
02464 /* */
02465 /*  Arguments:*/
02466 /*  name    name of the graph.*/
02467 /*  node    node at which to start.*/
02468 /*  args    additional args: ?-order pre|post|both? ?-type {bfs|dfs}?*/
02469 /*      -command cmd*/
02470 /* */
02471 /*  Results:*/
02472 /*  None.*/
02473 
02474 ret  ::struct::graph::_walk (type name , type node , type args) {
02475     set usage "$name walk node ?-dir forward|backward?\
02476         ?-order pre|post|both? ?-type bfs|dfs? -command cmd"
02477 
02478     if {[llength $args] < 2} {
02479     return -code error "wrong # args: should be \"$usage\""
02480     }
02481 
02482     CheckMissingNode $name $node
02483 
02484     # Set defaults
02485     set type  dfs
02486     set order pre
02487     set cmd   ""
02488     set dir   forward
02489 
02490     # Process specified options
02491     for {set i 0} {$i < [llength $args]} {incr i} {
02492     set flag [lindex $args $i]
02493     switch -glob -- $flag {
02494         "-type" {
02495         incr i
02496         if { $i >= [llength $args] } {
02497             return -code error "value for \"$flag\" missing: should be \"$usage\""
02498         }
02499         set type [string tolower [lindex $args $i]]
02500         }
02501         "-order" {
02502         incr i
02503         if { $i >= [llength $args] } {
02504             return -code error "value for \"$flag\" missing: should be \"$usage\""
02505         }
02506         set order [string tolower [lindex $args $i]]
02507         }
02508         "-command" {
02509         incr i
02510         if { $i >= [llength $args] } {
02511             return -code error "value for \"$flag\" missing: should be \"$usage\""
02512         }
02513         set cmd [lindex $args $i]
02514         }
02515         "-dir" {
02516         incr i
02517         if { $i >= [llength $args] } {
02518             return -code error "value for \"$flag\" missing: should be \"$usage\""
02519         }
02520         set dir [string tolower [lindex $args $i]]
02521         }
02522         default {
02523         return -code error "unknown option \"$flag\": should be \"$usage\""
02524         }
02525     }
02526     }
02527     
02528     # Make sure we have a command to run, otherwise what's the point?
02529     if { [string equal $cmd ""] } {
02530     return -code error "no command specified: should be \"$usage\""
02531     }
02532 
02533     # Validate that the given type is good
02534     switch -glob -- $type {
02535     "dfs" {
02536         set type "dfs"
02537     }
02538     "bfs" {
02539         set type "bfs"
02540     }
02541     default {
02542         return -code error "bad search type \"$type\": must be bfs or dfs"
02543     }
02544     }
02545     
02546     # Validate that the given order is good
02547     switch -glob -- $order {
02548     "both" {
02549         set order both
02550     }
02551     "pre" {
02552         set order pre
02553     }
02554     "post" {
02555         set order post
02556     }
02557     default {
02558         return -code error "bad search order \"$order\": must be both,\
02559             pre, or post"
02560     }
02561     }
02562 
02563     # Validate that the given direction is good
02564     switch -glob -- $dir {
02565     "forward" {
02566         set dir -out
02567     }
02568     "backward" {
02569         set dir -in
02570     }
02571     default {
02572         return -code error "bad search direction \"$dir\": must be\
02573             backward or forward"
02574     }
02575     }
02576 
02577     # Do the walk
02578 
02579     set st [list ]
02580     lappend st $node
02581     array set visited {}
02582 
02583     if { [string equal $type "dfs"] } {
02584     if { [string equal $order "pre"] } {
02585         # Pre-order Depth-first search
02586 
02587         while { [llength $st] > 0 } {
02588         set node [lindex   $st end]
02589         ldelete st end
02590 
02591         # Evaluate the command at this node
02592         set cmdcpy $cmd
02593         lappend cmdcpy enter $name $node
02594         uplevel 1 $cmdcpy
02595 
02596         set visited($node) .
02597 
02598         # Add this node's neighbours (according to direction)
02599         #  Have to add them in reverse order
02600         #  so that they will be popped left-to-right
02601 
02602         set next [_nodes $name $dir $node]
02603         set len  [llength $next]
02604 
02605         for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
02606             set nextnode [lindex $next $i]
02607             if {[info exists visited($nextnode)]} {
02608             # Skip nodes already visited
02609             continue
02610             }
02611             lappend st $nextnode
02612         }
02613         }
02614     } elseif { [string equal $order "post"] } {
02615         # Post-order Depth-first search
02616 
02617         while { [llength $st] > 0 } {
02618         set node [lindex $st end]
02619 
02620         if {[info exists visited($node)]} {
02621             # Second time we are here, pop it,
02622             # then evaluate the command.
02623 
02624             ldelete st end
02625 
02626             # Evaluate the command at this node
02627             set cmdcpy $cmd
02628             lappend cmdcpy leave $name $node
02629             uplevel 1 $cmdcpy
02630         } else {
02631             # First visit. Remember it.
02632             set visited($node) .
02633         
02634             # Add this node's neighbours.
02635             set next [_nodes $name $dir $node]
02636             set len  [llength $next]
02637 
02638             for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
02639             set nextnode [lindex $next $i]
02640             if {[info exists visited($nextnode)]} {
02641                 # Skip nodes already visited
02642                 continue
02643             }
02644             lappend st $nextnode
02645             }
02646         }
02647         }
02648     } else {
02649         # Both-order Depth-first search
02650 
02651         while { [llength $st] > 0 } {
02652         set node [lindex $st end]
02653 
02654         if {[info exists visited($node)]} {
02655             # Second time we are here, pop it,
02656             # then evaluate the command.
02657 
02658             ldelete st end
02659 
02660             # Evaluate the command at this node
02661             set cmdcpy $cmd
02662             lappend cmdcpy leave $name $node
02663             uplevel 1 $cmdcpy
02664         } else {
02665             # First visit. Remember it.
02666             set visited($node) .
02667 
02668             # Evaluate the command at this node
02669             set cmdcpy $cmd
02670             lappend cmdcpy enter $name $node
02671             uplevel 1 $cmdcpy
02672         
02673             # Add this node's neighbours.
02674             set next [_nodes $name $dir $node]
02675             set len  [llength $next]
02676 
02677             for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
02678             set nextnode [lindex $next $i]
02679             if {[info exists visited($nextnode)]} {
02680                 # Skip nodes already visited
02681                 continue
02682             }
02683             lappend st $nextnode
02684             }
02685         }
02686         }
02687     }
02688 
02689     } else {
02690     if { [string equal $order "pre"] } {
02691         # Pre-order Breadth first search
02692         while { [llength $st] > 0 } {
02693         set node [lindex $st 0]
02694         ldelete st 0
02695         # Evaluate the command at this node
02696         set cmdcpy $cmd
02697         lappend cmdcpy enter $name $node
02698         uplevel 1 $cmdcpy
02699         
02700         set visited($node) .
02701 
02702         # Add this node's neighbours.
02703         foreach child [_nodes $name $dir $node] {
02704             if {[info exists visited($child)]} {
02705             # Skip nodes already visited
02706             continue
02707             }
02708             lappend st $child
02709         }
02710         }
02711     } else {
02712         # Post-order Breadth first search
02713         # Both-order Breadth first search
02714         # Haven't found anything in Knuth
02715         # and unable to define something
02716         # consistent for myself. Leave it
02717         # out.
02718 
02719         return -code error "unable to do a ${order}-order breadth first walk"
02720     }
02721     }
02722     return
02723 }
02724 
02725 /*  ::struct::graph::Union --*/
02726 /* */
02727 /*  Return a list which is the union of the elements*/
02728 /*  in the specified lists.*/
02729 /* */
02730 /*  Arguments:*/
02731 /*  args    list of lists representing sets.*/
02732 /* */
02733 /*  Results:*/
02734 /*  set list representing the union of the argument lists.*/
02735 
02736 ret  ::struct::graph::Union (type args) {
02737     switch -- [llength $args] {
02738     0 {
02739         return {}
02740     }
02741     1 {
02742         return [lindex $args 0]
02743     }
02744     default {
02745         foreach set $args {
02746         foreach e $set {
02747             set tmp($e) .
02748         }
02749         }
02750         return [array names tmp]
02751     }
02752     }
02753 }
02754 
02755 /*  ::struct::graph::GenAttributeStorage --*/
02756 /* */
02757 /*  Create an array to store the attributes of a node in.*/
02758 /* */
02759 /*  Arguments:*/
02760 /*  name    Name of the graph containing the node*/
02761 /*  type    Type of object for the attribute*/
02762 /*  obj Name of the node or arc which got attributes.*/
02763 /* */
02764 /*  Results:*/
02765 /*  none*/
02766 
02767 ret  ::struct::graph::GenAttributeStorage (type name , type type , type obj) {
02768     variable ${name}::nextAttr
02769     upvar    ${name}::${type}Attr attribute
02770 
02771     set   attr "a[incr nextAttr]"
02772     set   attribute($obj) $attr
02773     return
02774 }
02775 
02776 ret  ::struct::graph::CheckMissingArc (type name , type arc) {
02777     if {![__arc_exists $name $arc]} {
02778     return -code error "arc \"$arc\" does not exist in graph \"$name\""
02779     }
02780 }
02781 
02782 ret  ::struct::graph::CheckMissingNode (type name , type node , optional prefix ={)} {
02783     if {![__node_exists $name $node]} {
02784     return -code error "${prefix}node \"$node\" does not exist in graph \"$name\""
02785     }
02786 }
02787 
02788 ret  ::struct::graph::CheckDuplicateArc (type name , type arc) {
02789     if {[__arc_exists $name $arc]} {
02790     return -code error "arc \"$arc\" already exists in graph \"$name\""
02791     }
02792 }
02793 
02794 ret  ::struct::graph::CheckDuplicateNode (type name , type node) {
02795     if {[__node_exists $name $node]} {
02796     return -code error "node \"$node\" already exists in graph \"$name\""
02797     }
02798 }
02799 
02800 ret  ::struct::graph::CheckE (type name , type what , type arguments) {
02801 
02802     # Discriminate between conditions and nodes
02803 
02804     upvar 1 haveCond   haveCond   ; set haveCond   0
02805     upvar 1 haveKey    haveKey    ; set haveKey    0
02806     upvar 1 key        key        ; set key        {}
02807     upvar 1 haveValue  haveValue  ; set haveValue  0
02808     upvar 1 value      value      ; set value      {}
02809     upvar 1 haveFilter haveFilter ; set haveFilter 0
02810     upvar 1 fcmd       fcmd       ; set fcmd       {}
02811     upvar 1 cond       cond       ; set cond       "none"
02812     upvar 1 condNodes  condNodes  ; set condNodes  {}
02813 
02814     set wa_usage "wrong # args: should be \"$name $what ?-key key? ?-value value? ?-filter cmd? ?-in|-out|-adj|-inner|-embedding node node...?\""
02815 
02816     for {set i 0} {$i < [llength $arguments]} {incr i} {
02817     set arg [lindex $arguments $i]
02818     switch -glob -- $arg {
02819         -in -
02820         -out -
02821         -adj -
02822         -inner -
02823         -embedding {
02824         if {$haveCond} {
02825             return -code error "invalid restriction:\
02826                 illegal multiple use of\
02827                 \"-in\"|\"-out\"|\"-adj\"|\"-inner\"|\"-embedding\""
02828         }
02829 
02830         set haveCond 1
02831         set cond [string range $arg 1 end]
02832         }
02833         -key {
02834         if {($i + 1) == [llength $arguments]} {
02835             return -code error $wa_usage
02836         }
02837         if {$haveKey} {
02838             return -code error {invalid restriction: illegal multiple use of "-key"}
02839         }
02840 
02841         incr i
02842         set key [lindex $arguments $i]
02843         set haveKey 1
02844         }
02845         -value {
02846         if {($i + 1) == [llength $arguments]} {
02847             return -code error $wa_usage
02848         }
02849         if {$haveValue} {
02850             return -code error {invalid restriction: illegal multiple use of "-value"}
02851         }
02852 
02853         incr i
02854         set value [lindex $arguments $i]
02855         set haveValue 1
02856         }
02857         -filter {
02858         if {($i + 1) == [llength $arguments]} {
02859             return -code error $wa_usage
02860         }
02861         if {$haveFilter} {
02862             return -code error {invalid restriction: illegal multiple use of "-filter"}
02863         }
02864 
02865         incr i
02866         set fcmd [lindex $arguments $i]
02867         set haveFilter 1
02868         }
02869         -* {
02870         return -code error "bad restriction \"$arg\": must be -adj, -embedding,\
02871             -filter, -in, -inner, -key, -out, or -value"
02872         }
02873         default {
02874         lappend condNodes $arg
02875         }
02876     }
02877     }
02878 
02879     # Validate that there are nodes to use in the restriction.
02880     # otherwise what's the point?
02881     if {$haveCond} {
02882     if {[llength $condNodes] == 0} {
02883         return -code error $wa_usage
02884     }
02885 
02886     # Remove duplicates. Note: lsort -unique is not present in Tcl
02887     # 8.2, thus not usable here.
02888 
02889     array set nx {}
02890     foreach c $condNodes {set nx($c) .}
02891     set condNodes [array names nx]
02892     unset nx
02893 
02894     # Make sure that the specified nodes exist!
02895     foreach node $condNodes {CheckMissingNode $name $node}
02896     }
02897 
02898     if {$haveValue && !$haveKey} {
02899     return -code error {invalid restriction: use of "-value" without "-key"}
02900     }
02901 
02902     return
02903 }
02904 
02905 ret  ::struct::graph::CheckSerialization (type ser , type gavar , type navar , type aavar , type inavar , type outavar , type arcnvar) {
02906     upvar 1 \
02907         $gavar   graphAttr \
02908         $navar   nodeAttr  \
02909         $aavar   arcAttr   \
02910         $inavar  inArcs    \
02911         $outavar outArcs   \
02912         $arcnvar arcNodes
02913 
02914     array set nodeAttr  {}
02915     array set arcAttr   {}
02916     array set inArcs    {}
02917     array set outArcs   {}
02918     array set arcNodes  {}
02919 
02920     # Overall length ok ?
02921     if {[llength $ser] % 3 != 1} {
02922     return -code error \
02923         "error in serialization: list length not 1 mod 3."
02924     }
02925 
02926     # Attribute length ok ? Dictionary!
02927     set graphAttr [lindex $ser end]
02928     if {[llength $graphAttr] % 2} {
02929     return -code error \
02930         "error in serialization: malformed graph attribute dictionary."
02931     }
02932 
02933     # Basic decoder pass
02934 
02935     foreach {node attr narcs} [lrange $ser 0 end-1] {
02936     if {![info exists inArcs($node)]} {
02937         set inArcs($node)  [list]
02938     }
02939     set outArcs($node) [list]
02940 
02941     # Attribute length ok ? Dictionary!
02942     if {[llength $attr] % 2} {
02943         return -code error \
02944             "error in serialization: malformed node attribute dictionary."
02945     }
02946     # Remember attribute data only for non-empty nodes
02947     if {[llength $attr]} {
02948         set nodeAttr($node) $attr
02949     }
02950 
02951     foreach arcd $narcs {
02952         if {[llength $arcd] != 3} {
02953         return -code error \
02954             "error in serialization: arc information length not 3."
02955         }
02956 
02957         foreach {arc dst aattr} $arcd break
02958 
02959         if {[info exists arcNodes($arc)]} {
02960         return -code error \
02961             "error in serialization: duplicate definition of arc \"$arc\"."
02962         }
02963 
02964         # Attribute length ok ? Dictionary!
02965         if {[llength $aattr] % 2} {
02966         return -code error \
02967             "error in serialization: malformed arc attribute dictionary."
02968         }
02969         # Remember attribute data only for non-empty nodes
02970         if {[llength $aattr]} {
02971         set arcAttr($arc) $aattr
02972         }
02973 
02974         # Destination reference ok ?
02975         if {
02976         ![string is integer -strict $dst] ||
02977         ($dst % 3) ||
02978         ($dst < 0) ||
02979         ($dst >= [llength $ser])
02980         } {
02981         return -code error \
02982             "error in serialization: bad arc destination reference \"$dst\"."
02983         }
02984 
02985         # Get destination and reconstruct the
02986         # various relationships.
02987 
02988         set dstnode [lindex $ser $dst]
02989 
02990         set arcNodes($arc) [list $node $dstnode]
02991         lappend inArcs($dstnode) $arc
02992         lappend outArcs($node)   $arc
02993     }
02994     }
02995 
02996     # Duplicate node names ?
02997 
02998     if {[array size outArcs] < ([llength $ser] / 3)} {
02999     return -code error \
03000         "error in serialization: duplicate node names."
03001     }
03002 
03003     # Ok. The data is now ready for the caller.
03004     return
03005 }
03006 
03007 /* */
03008 /*  Private functions follow*/
03009 /* */
03010 /*  Do a compatibility version of [lset] for pre-8.4 versions of Tcl.*/
03011 /*  This version does not do multi-arg [lset]!*/
03012 
03013 ret  ::struct::graph::K ( type x , type y ) { set x }
03014 
03015 if { [package vcompare [package provide Tcl] 8.4] < 0 } {
03016     ret  ::struct::graph::lset ( type var , type index , type arg ) {
03017     upvar 1 $var list
03018     set list [::lreplace [K $list [set list {}]] $index $index $arg]
03019     }
03020 }
03021 
03022 ret  ::struct::graph::ldelete (type var , type index , optional end ={)} {
03023     upvar 1 $var list
03024     if {$end == {}} { end =  $index}
03025      list =  [lreplace [K $list [ list =  {}]] $index $end]
03026     return
03027 }
03028 
03029 ret  ::struct::graph::luniq (type list) {
03030     array set _ {}
03031     set result [list]
03032     foreach e $list {
03033     if {[info exists _($e)]} {continue}
03034     lappend result $e
03035     set _($e) .
03036     }
03037     return $result
03038 }
03039 
03040 /*  ### ### ### ######### ######### #########*/
03041 /*  Ready*/
03042 
03043 namespace ::struct {
03044     /*  Put 'graph::graph' into the general structure namespace*/
03045     /*  for pickup by the main management.*/
03046 
03047     namespace import -force graph::graph_tcl
03048 }
03049 
03050 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1