graph1.tcl

Go to the documentation of this file.
00001 /*  graph.tcl --*/
00002 /* */
00003 /*  Implementation of a graph data structure for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 2000 by Andreas Kupries*/
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: graph1.tcl,v 1.4 2005/09/28 04:51:24 andreas_kupries Exp $*/
00011 
00012 /*  Create the namespace before determining cgraph vs. tcl*/
00013 /*  Otherwise the loading 'struct.tcl' may get into trouble*/
00014 /*  when trying to import commands from them*/
00015 
00016 namespace ::struct {}
00017 namespace ::struct::graph {}
00018 
00019 /*  Try to load the cgraph package*/
00020 /*  Get it at http://physnet.uni-oldenburg.de/~schlenk/tcl/graph/ */
00021 
00022 if {![catch {package require cgraph 0.6}]} {
00023     /*  the cgraph package takes over, so we can return*/
00024     return
00025 }
00026 
00027 namespace ::struct {}
00028 namespace ::struct::graph {
00029     /*  Data storage in the graph module*/
00030     /*  -------------------------------*/
00031     /* */
00032     /*  There's a lot of bits to keep track of for each graph:*/
00033     /*  nodes*/
00034     /*  node values*/
00035     /*  node relationships (arcs)*/
00036     /*    arc values*/
00037     /* */
00038     /*  It would quickly become unwieldy to try to keep these in arrays or lists*/
00039     /*  within the graph namespace itself.  Instead, each graph structure will*/
00040     /*  get its own namespace.  Each namespace contains:*/
00041     /*  node:$node  array mapping keys to values for the node $node*/
00042     /*  arc:$arc    array mapping keys to values for the arc $arc*/
00043     /*  inArcs      array mapping nodes to the list of incoming arcs*/
00044     /*  outArcs     array mapping nodes to the list of outgoing arcs*/
00045     /*  arcNodes    array mapping arcs to the two nodes (start & end)*/
00046     
00047     /*  counter is used to give a unique name for unnamed graph*/
00048     variable counter 0
00049 
00050     /*  commands is the list of subcommands recognized by the graph*/
00051     variable commands [list \
00052         "arc"       \
00053         "arcs"      \
00054         "destroy"       \
00055         "get"       \
00056         "getall"        \
00057         "keys"      \
00058         "keyexists"     \
00059         "node"      \
00060         "nodes"     \
00061         ""      \
00062         "swap = "       \
00063         "un"             \
00064         "walk = "       \
00065         ]
00066 
00067     variable arcCommands [list  \
00068         "append"    \
00069         "delete"    \
00070         "exists"    \
00071         "get"   \
00072         "getall"    \
00073         "insert"    \
00074         "keys"  \
00075         "keyexists" \
00076         "lappend"   \
00077         ""  \
00078         "source = " \
00079         "target"    \
00080         "un"    \
00081         ]
00082 
00083     variable =  nodeCommands [list  \
00084         "append"    \
00085         "degree"    \
00086         "delete"    \
00087         "exists"    \
00088         "get"   \
00089         "getall"    \
00090         "insert"    \
00091         "keys"  \
00092         "keyexists" \
00093         "lappend"   \
00094         "opposite"  \
00095         ""  \
00096         "unset = "  \
00097         ]
00098 
00099     /*  Only export one command, the one used to instantiate a new graph*/
00100     namespace export graph
00101 }
00102 
00103 /*  ::struct::graph::graph --*/
00104 /* */
00105 /*  Create a new graph with a given name; if no name is given, use*/
00106 /*  graphX, where X is a number.*/
00107 /* */
00108 /*  Arguments:*/
00109 /*  name    name of the graph; if null, generate one.*/
00110 /* */
00111 /*  Results:*/
00112 /*  name    name of the graph created*/
00113 
00114 ret  ::struct::graph::graph (optional name ="") {
00115     variable counter
00116     
00117     if { [llength [info level 0]] == 1 } {
00118     incr counter
00119     set name "graph${counter}"
00120     }
00121 
00122     if { ![string equal [info commands ::$name] ""] } {
00123     error "command \"$name\" already exists, unable to create graph"
00124     }
00125 
00126     # Set up the namespace
00127     namespace eval ::struct::graph::graph$name {
00128 
00129     # Set up the map for values associated with the graph itself
00130     variable graphData
00131     array set graphData {data ""}
00132 
00133     # Set up the map from nodes to the arcs coming to them
00134     variable  inArcs
00135     array set inArcs {}
00136 
00137     # Set up the map from nodes to the arcs going out from them
00138     variable  outArcs
00139     array set outArcs {}
00140 
00141     # Set up the map from arcs to the nodes they touch.
00142     variable  arcNodes
00143     array set arcNodes {}
00144 
00145     # Set up a value for use in creating unique node names
00146     variable nextUnusedNode
00147     set nextUnusedNode 1
00148 
00149     # Set up a value for use in creating unique arc names
00150     variable nextUnusedArc
00151     set nextUnusedArc 1
00152     }
00153 
00154     # Create the command to manipulate the graph
00155     interp alias {} ::$name {} ::struct::graph::GraphProc $name
00156 
00157     return $name
00158 }
00159 
00160 /* */
00161 /*  Private functions follow*/
00162 
00163 /*  ::struct::graph::GraphProc --*/
00164 /* */
00165 /*  Command that processes all graph object commands.*/
00166 /* */
00167 /*  Arguments:*/
00168 /*  name    name of the graph object to manipulate.*/
00169 /*  args    command name and args for the command*/
00170 /* */
00171 /*  Results:*/
00172 /*  Varies based on command to perform*/
00173 
00174 ret  ::struct::graph::GraphProc (type name , optional cmd ="" , type args) {
00175     # Do minimal args checks here
00176     if { [llength [info level 0]] == 2 } {
00177     error "wrong # args: should be \"$name option ?arg arg ...?\""
00178     }
00179     
00180     # Split the args into command and args components
00181     if { [llength [info commands ::struct::graph::_$cmd]] == 0 } {
00182     variable commands
00183     set optlist [join $commands ", "]
00184     set optlist [linsert $optlist "end-1" "or"]
00185     error "bad option \"$cmd\": must be $optlist"
00186     }
00187     eval [list ::struct::graph::_$cmd $name] $args
00188 }
00189 
00190 /*  ::struct::graph::_arc --*/
00191 /* */
00192 /*  Dispatches the invocation of arc methods to the proper handler*/
00193 /*  procedure.*/
00194 /* */
00195 /*  Arguments:*/
00196 /*  name    name of the graph.*/
00197 /*  cmd arc command to invoke*/
00198 /*  args    arguments to propagate to the handler for the arc command*/
00199 /* */
00200 /*  Results:*/
00201 /*  As of the invoked handler.*/
00202 
00203 ret  ::struct::graph::_arc (type name , type cmd , type args) {
00204 
00205     # Split the args into command and args components
00206     if { [llength [info commands ::struct::graph::__arc_$cmd]] == 0 } {
00207     variable arcCommands
00208     set optlist [join $arcCommands ", "]
00209     set optlist [linsert $optlist "end-1" "or"]
00210     error "bad option \"$cmd\": must be $optlist"
00211     }
00212 
00213     eval [list ::struct::graph::__arc_$cmd $name] $args
00214 }
00215 
00216 /*  ::struct::graph::__arc_delete --*/
00217 /* */
00218 /*  Remove an arc from a graph, including all of its values.*/
00219 /* */
00220 /*  Arguments:*/
00221 /*  name    name of the graph.*/
00222 /*  args    list of arcs to delete.*/
00223 /* */
00224 /*  Results:*/
00225 /*  None.*/
00226 
00227 ret  ::struct::graph::__arc_delete (type name , type args) {
00228 
00229     foreach arc $args {
00230     if { ![__arc_exists $name $arc] } {
00231         error "arc \"$arc\" does not exist in graph \"$name\""
00232     }
00233     }
00234 
00235     upvar ::struct::graph::graph${name}::inArcs   inArcs
00236     upvar ::struct::graph::graph${name}::outArcs  outArcs
00237     upvar ::struct::graph::graph${name}::arcNodes arcNodes
00238 
00239     foreach arc $args {
00240     foreach {source target} $arcNodes($arc) break ; # lassign
00241 
00242     unset arcNodes($arc)
00243     # FRINK: nocheck
00244     unset ::struct::graph::graph${name}::arc$arc
00245 
00246     # Remove arc from the arc lists of source and target nodes.
00247 
00248     set index            [lsearch -exact $outArcs($source) $arc]
00249     set outArcs($source) [lreplace       $outArcs($source) $index $index]
00250 
00251     set index            [lsearch -exact $inArcs($target)  $arc]
00252     set inArcs($target)  [lreplace       $inArcs($target)  $index $index]
00253     }
00254 
00255     return
00256 }
00257 
00258 /*  ::struct::graph::__arc_exists --*/
00259 /* */
00260 /*  Test for existance of a given arc in a graph.*/
00261 /* */
00262 /*  Arguments:*/
00263 /*  name    name of the graph.*/
00264 /*  arc arc to look for.*/
00265 /* */
00266 /*  Results:*/
00267 /*  1 if the arc exists, 0 else.*/
00268 
00269 ret  ::struct::graph::__arc_exists (type name , type arc) {
00270     return [info exists ::struct::graph::graph${name}::arcNodes($arc)]
00271 }
00272 
00273 /*  ::struct::graph::__arc_get --*/
00274 /* */
00275 /*  Get a keyed value from an arc in a graph.*/
00276 /* */
00277 /*  Arguments:*/
00278 /*  name    name of the graph.*/
00279 /*  arc arc to query.*/
00280 /*  flag    -key; anything else is an error*/
00281 /*  key key to lookup; defaults to data*/
00282 /* */
00283 /*  Results:*/
00284 /*  value   value associated with the key given.*/
00285 
00286 ret  ::struct::graph::__arc_get (type name , type arc , optional flag =-key , optional key =data) {
00287     if { ![__arc_exists $name $arc] } {
00288     error "arc \"$arc\" does not exist in graph \"$name\""
00289     }
00290     
00291     upvar ::struct::graph::graph${name}::arc${arc} data
00292 
00293     if { ![info exists data($key)] } {
00294     error "invalid key \"$key\" for arc \"$arc\""
00295     }
00296 
00297     return $data($key)
00298 }
00299 
00300 /*  ::struct::graph::__arc_getall --*/
00301 /* */
00302 /*  Get a serialized array of key/value pairs from an arc in a graph.*/
00303 /* */
00304 /*  Arguments:*/
00305 /*  name    name of the graph.*/
00306 /*  arc arc to query.*/
00307 /* */
00308 /*  Results:*/
00309 /*  value   serialized array of key/value pairs.*/
00310 
00311 ret  ::struct::graph::__arc_getall (type name , type arc , type args) {
00312     if { ![__arc_exists $name $arc] } {
00313     error "arc \"$arc\" does not exist in graph \"$name\""
00314     }
00315 
00316     if { [llength $args] } {
00317     error "wrong # args: should be none"
00318     }
00319     
00320     upvar ::struct::graph::graph${name}::arc${arc} data
00321 
00322     return [array get data]
00323 }
00324 
00325 /*  ::struct::graph::__arc_keys --*/
00326 /* */
00327 /*  Get a list of keys for an arc in a graph.*/
00328 /* */
00329 /*  Arguments:*/
00330 /*  name    name of the graph.*/
00331 /*  arc arc to query.*/
00332 /* */
00333 /*  Results:*/
00334 /*  value   value associated with the key given.*/
00335 
00336 ret  ::struct::graph::__arc_keys (type name , type arc , type args) {
00337     if { ![__arc_exists $name $arc] } {
00338     error "arc \"$arc\" does not exist in graph \"$name\""
00339     }
00340 
00341     if { [llength $args] } {
00342     error "wrong # args: should be none"
00343     }    
00344 
00345     upvar ::struct::graph::graph${name}::arc${arc} data
00346 
00347     return [array names data]
00348 }
00349 
00350 /*  ::struct::graph::__arc_keyexists --*/
00351 /* */
00352 /*  Test for existance of a given key for a given arc in a graph.*/
00353 /* */
00354 /*  Arguments:*/
00355 /*  name    name of the graph.*/
00356 /*  arc arc to query.*/
00357 /*  flag    -key; anything else is an error*/
00358 /*  key key to lookup; defaults to data*/
00359 /* */
00360 /*  Results:*/
00361 /*  1 if the key exists, 0 else.*/
00362 
00363 ret  ::struct::graph::__arc_keyexists (type name , type arc , optional flag =-key , optional key =data) {
00364     if { ![__arc_exists $name $arc] } {
00365     error "arc \"$arc\" does not exist in graph \"$name\""
00366     }
00367 
00368     if { ![string equal $flag "-key"] } {
00369     error "invalid option \"$flag\": should be -key"
00370     }
00371     
00372     upvar ::struct::graph::graph${name}::arc${arc} data
00373 
00374     return [info exists data($key)]
00375 }
00376 
00377 /*  ::struct::graph::__arc_insert --*/
00378 /* */
00379 /*  Add an arc to a graph.*/
00380 /* */
00381 /*  Arguments:*/
00382 /*  name        name of the graph.*/
00383 /*  source      source node of the new arc*/
00384 /*  target      target node of the new arc*/
00385 /*  args        arc to insert; must be unique.  If none is given,*/
00386 /*          the routine will generate a unique node name.*/
00387 /* */
00388 /*  Results:*/
00389 /*  arc     The name of the new arc.*/
00390 
00391 ret  ::struct::graph::__arc_insert (type name , type source , type target , type args) {
00392 
00393     if { [llength $args] == 0 } {
00394     # No arc name was given; generate a unique one
00395     set arc [__generateUniqueArcName $name]
00396     } else {
00397     set arc [lindex $args 0]
00398     }
00399 
00400     if { [__arc_exists $name $arc] } {
00401     error "arc \"$arc\" already exists in graph \"$name\""
00402     }
00403     
00404     if { ![__node_exists $name $source] } {
00405     error "source node \"$source\" does not exist in graph \"$name\""
00406     }
00407     
00408     if { ![__node_exists $name $target] } {
00409     error "target node \"$target\" does not exist in graph \"$name\""
00410     }
00411     
00412     upvar ::struct::graph::graph${name}::inArcs    inArcs
00413     upvar ::struct::graph::graph${name}::outArcs   outArcs
00414     upvar ::struct::graph::graph${name}::arcNodes  arcNodes
00415     upvar ::struct::graph::graph${name}::arc${arc} data
00416 
00417     # Set up the new arc
00418     set data(data)       ""
00419     set arcNodes($arc) [list $source $target]
00420 
00421     # Add this arc to the arc lists of its source resp. target nodes.
00422     lappend outArcs($source) $arc
00423     lappend inArcs($target)  $arc
00424 
00425     return $arc
00426 }
00427 
00428 /*  ::struct::graph::__arc_set --*/
00429 /* */
00430 /*  Set or get a value for an arc in a graph.*/
00431 /* */
00432 /*  Arguments:*/
00433 /*  name    name of the graph.*/
00434 /*  arc arc to modify or query.*/
00435 /*  args    ?-key key? ?value?*/
00436 /* */
00437 /*  Results:*/
00438 /*  val value associated with the given key of the given arc*/
00439 
00440 ret  ::struct::graph::__arc_set (type name , type arc , type args) {
00441     if { ![__arc_exists $name $arc] } {
00442     error "arc \"$arc\" does not exist in graph \"$name\""
00443     }
00444 
00445     upvar ::struct::graph::graph${name}::arc$arc data
00446 
00447     if { [llength $args] > 3 } {
00448     error "wrong # args: should be \"$name arc set $arc ?-key key?\
00449         ?value?\""
00450     }
00451     
00452     set key "data"
00453     set haveValue 0
00454     if { [llength $args] > 1 } {
00455     foreach {flag key} $args break
00456     if { ![string match "${flag}*" "-key"] } {
00457         error "invalid option \"$flag\": should be key"
00458     }
00459     if { [llength $args] == 3 } {
00460         set haveValue 1
00461         set value [lindex $args end]
00462     }
00463     } elseif { [llength $args] == 1 } {
00464     set haveValue 1
00465     set value [lindex $args end]
00466     }
00467 
00468     if { $haveValue } {
00469     # Setting a value
00470     return [set data($key) $value]
00471     } else {
00472     # Getting a value
00473     if { ![info exists data($key)] } {
00474         error "invalid key \"$key\" for arc \"$arc\""
00475     }
00476     return $data($key)
00477     }
00478 }
00479 
00480 /*  ::struct::graph::__arc_append --*/
00481 /* */
00482 /*  Append a value for an arc in a graph.*/
00483 /* */
00484 /*  Arguments:*/
00485 /*  name    name of the graph.*/
00486 /*  arc arc to modify or query.*/
00487 /*  args    ?-key key? value*/
00488 /* */
00489 /*  Results:*/
00490 /*  val value associated with the given key of the given arc*/
00491 
00492 ret  ::struct::graph::__arc_append (type name , type arc , type args) {
00493     if { ![__arc_exists $name $arc] } {
00494     error "arc \"$arc\" does not exist in graph \"$name\""
00495     }
00496 
00497     upvar ::struct::graph::graph${name}::arc$arc data
00498 
00499     if { [llength $args] != 1 && [llength $args] != 3 } {
00500     error "wrong # args: should be \"$name arc append $arc ?-key key?\
00501         value\""
00502     }
00503     
00504     if { [llength $args] == 3 } {
00505     foreach {flag key} $args break
00506     if { ![string equal $flag "-key"] } {
00507         error "invalid option \"$flag\": should be -key"
00508     }
00509     } else {
00510     set key "data"
00511     }
00512 
00513     set value [lindex $args end]
00514 
00515     return [append data($key) $value]
00516 }
00517 
00518 /*  ::struct::graph::__arc_lappend --*/
00519 /* */
00520 /*  lappend a value for an arc in a graph.*/
00521 /* */
00522 /*  Arguments:*/
00523 /*  name    name of the graph.*/
00524 /*  arc arc to modify or query.*/
00525 /*  args    ?-key key? value*/
00526 /* */
00527 /*  Results:*/
00528 /*  val value associated with the given key of the given arc*/
00529 
00530 ret  ::struct::graph::__arc_lappend (type name , type arc , type args) {
00531     if { ![__arc_exists $name $arc] } {
00532     error "arc \"$arc\" does not exist in graph \"$name\""
00533     }
00534 
00535     upvar ::struct::graph::graph${name}::arc$arc data
00536 
00537     if { [llength $args] != 1 && [llength $args] != 3 } {
00538     error "wrong # args: should be \"$name arc lappend $arc ?-key key?\
00539         value\""
00540     }
00541     
00542     if { [llength $args] == 3 } {
00543     foreach {flag key} $args break
00544     if { ![string equal $flag "-key"] } {
00545         error "invalid option \"$flag\": should be -key"
00546     }
00547     } else {
00548     set key "data"
00549     }
00550 
00551     set value [lindex $args end]
00552 
00553     return [lappend data($key) $value]
00554 }
00555 
00556 /*  ::struct::graph::__arc_source --*/
00557 /* */
00558 /*  Return the node at the beginning of the specified arc.*/
00559 /* */
00560 /*  Arguments:*/
00561 /*  name    name of the graph object.*/
00562 /*  arc arc to look up.*/
00563 /* */
00564 /*  Results:*/
00565 /*  node    name of the node.*/
00566 
00567 ret  ::struct::graph::__arc_source (type name , type arc) {
00568     if { ![__arc_exists $name $arc] } {
00569     error "arc \"$arc\" does not exist in graph \"$name\""
00570     }
00571 
00572     upvar ::struct::graph::graph${name}::arcNodes arcNodes
00573     return [lindex $arcNodes($arc) 0]
00574 }
00575 
00576 /*  ::struct::graph::__arc_target --*/
00577 /* */
00578 /*  Return the node at the end of the specified arc.*/
00579 /* */
00580 /*  Arguments:*/
00581 /*  name    name of the graph object.*/
00582 /*  arc arc to look up.*/
00583 /* */
00584 /*  Results:*/
00585 /*  node    name of the node.*/
00586 
00587 ret  ::struct::graph::__arc_target (type name , type arc) {
00588     if { ![__arc_exists $name $arc] } {
00589     error "arc \"$arc\" does not exist in graph \"$name\""
00590     }
00591 
00592     upvar ::struct::graph::graph${name}::arcNodes arcNodes
00593     return [lindex $arcNodes($arc) 1]
00594 }
00595 
00596 /*  ::struct::graph::__arc_unset --*/
00597 /* */
00598 /*  Remove a keyed value from a arc.*/
00599 /* */
00600 /*  Arguments:*/
00601 /*  name    name of the graph.*/
00602 /*  arc arc to modify.*/
00603 /*  args    additional args: ?-key key?*/
00604 /* */
00605 /*  Results:*/
00606 /*  None.*/
00607 
00608 ret  ::struct::graph::__arc_unset (type name , type arc , optional flag =-key , optional key =data) {
00609     if { ![__arc_exists $name $arc] } {
00610     error "arc \"$arc\" does not exist in graph \"$name\""
00611     }
00612     
00613     if { ![string match "${flag}*" "-key"] } {
00614     error "invalid option \"$flag\": should be \"$name arc unset\
00615         $arc ?-key key?\""
00616     }
00617 
00618     upvar ::struct::graph::graph${name}::arc${arc} data
00619     if { [info exists data($key)] } {
00620     unset data($key)
00621     }
00622     return
00623 }
00624 
00625 /*  ::struct::graph::_arcs --*/
00626 /* */
00627 /*  Return a list of all arcs in a graph satisfying some*/
00628 /*  node based restriction.*/
00629 /* */
00630 /*  Arguments:*/
00631 /*  name    name of the graph.*/
00632 /* */
00633 /*  Results:*/
00634 /*  arcs    list of arcs*/
00635 
00636 ret  ::struct::graph::_arcs (type name , type args) {
00637 
00638     # Discriminate between conditions and nodes
00639 
00640     set haveCond 0
00641     set haveKey 0
00642     set haveValue 0
00643     set cond "none"
00644     set condNodes [list]
00645 
00646     for {set i 0} {$i < [llength $args]} {incr i} {
00647     set arg [lindex $args $i]
00648     switch -glob -- $arg {
00649         -in -
00650         -out -
00651         -adj -
00652         -inner -
00653         -embedding {
00654         if {$haveCond} {
00655             return -code error "invalid restriction:\
00656                 illegal multiple use of\
00657                 \"-in\"|\"-out\"|\"-adj\"|\"-inner\"|\"-embedding\""
00658         }
00659 
00660         set haveCond 1
00661         set cond [string range $arg 1 end]
00662         }
00663         -key {
00664         if {$haveKey} {
00665             return -code error {invalid restriction: illegal multiple use of "-key"}
00666         }
00667 
00668         incr i
00669         set key [lindex $args $i]
00670         set haveKey 1
00671         }
00672         -value {
00673         if {$haveValue} {
00674             return -code error {invalid restriction: illegal multiple use of "-value"}
00675         }
00676 
00677         incr i
00678         set value [lindex $args $i]
00679         set haveValue 1
00680         }
00681         -* {
00682         error "invalid restriction \"$arg\": should be -in, -out,\
00683             -adj, -inner, -embedding, -key or -value"
00684         }
00685         default {
00686         lappend condNodes $arg
00687         }
00688     }
00689     }
00690 
00691     # Validate that there are nodes to use in the restriction.
00692     # otherwise what's the point?
00693     if {$haveCond} {
00694     if {[llength $condNodes] == 0} {
00695         set usage "$name arcs ?-key key? ?-value value? ?-in|-out|-adj|-inner|-embedding node node...?"
00696         error "no nodes specified: should be \"$usage\""
00697     }
00698 
00699     # Make sure that the specified nodes exist!
00700     foreach node $condNodes {
00701         if { ![__node_exists $name $node] } {
00702         error "node \"$node\" does not exist in graph \"$name\""
00703         }
00704     }
00705     }
00706 
00707     # Now we are able to go to work
00708     upvar ::struct::graph::graph${name}::inArcs   inArcs
00709     upvar ::struct::graph::graph${name}::outArcs  outArcs
00710     upvar ::struct::graph::graph${name}::arcNodes arcNodes
00711 
00712     set       arcs [list]
00713 
00714     switch -exact -- $cond {
00715     in {
00716         # Result is all arcs going to at least one node
00717         # in the list of arguments.
00718 
00719         foreach node $condNodes {
00720         foreach e $inArcs($node) {
00721             # As an arc has only one destination, i.e. is the
00722             # in-arc of exactly one node it is impossible to
00723             # count an arc twice. IOW the [info exists] below
00724             # is never true. Found through coverage analysis
00725             # and then trying to think up a testcase invoking
00726             # the continue.
00727             # if {[info exists coll($e)]} {continue}
00728             lappend arcs    $e
00729             #set     coll($e) .
00730         }
00731         }
00732     }
00733     out {
00734         # Result is all arcs coming from at least one node
00735         # in the list of arguments.
00736 
00737         foreach node $condNodes {
00738         foreach e $outArcs($node) {
00739             # See above 'in', same reasoning, one source per arc.
00740             # if {[info exists coll($e)]} {continue}
00741             lappend arcs    $e
00742             #set     coll($e) .
00743         }
00744         }
00745     }
00746     adj {
00747         # Result is all arcs coming from or going to at
00748         # least one node in the list of arguments.
00749 
00750         array set coll  {}
00751         # Here we do need 'coll' as each might be an in- and
00752         # out-arc for one or two nodes in the list of arguments.
00753 
00754         foreach node $condNodes {
00755         foreach e $inArcs($node) {
00756             if {[info exists coll($e)]} {continue}
00757             lappend arcs    $e
00758             set     coll($e) .
00759         }
00760         foreach e $outArcs($node) {
00761             if {[info exists coll($e)]} {continue}
00762             lappend arcs    $e
00763             set     coll($e) .
00764         }
00765         }
00766     }
00767     inner {
00768         # Result is all arcs running between nodes in the list.
00769 
00770         array set coll  {}
00771         # Here we do need 'coll' as each might be an in- and
00772         # out-arc for one or two nodes in the list of arguments.
00773 
00774         array set group {}
00775         foreach node $condNodes {
00776         set group($node) .
00777         }
00778 
00779         foreach node $condNodes {
00780         foreach e $inArcs($node) {
00781             set n [lindex $arcNodes($e) 0]
00782             if {![info exists group($n)]} {continue}
00783             if { [info exists coll($e)]}  {continue}
00784             lappend arcs    $e
00785             set     coll($e) .
00786         }
00787         foreach e $outArcs($node) {
00788             set n [lindex $arcNodes($e) 1]
00789             if {![info exists group($n)]} {continue}
00790             if { [info exists coll($e)]}  {continue}
00791             lappend arcs    $e
00792             set     coll($e) .
00793         }
00794         }
00795     }
00796     embedding {
00797         # Result is all arcs from -adj minus the arcs from -inner.
00798         # IOW all arcs going from a node in the list to a node
00799         # which is *not* in the list
00800 
00801         # This also means that no arc can be counted twice as it
00802         # is either going to a node, or coming from a node in the
00803         # list, but it can't do both, because then it is part of
00804         # -inner, which was excluded!
00805 
00806         array set group {}
00807         foreach node $condNodes {
00808         set group($node) .
00809         }
00810 
00811         foreach node $condNodes {
00812         foreach e $inArcs($node) {
00813             set n [lindex $arcNodes($e) 0]
00814             if {[info exists group($n)]} {continue}
00815             # if {[info exists coll($e)]}  {continue}
00816             lappend arcs    $e
00817             # set     coll($e) .
00818         }
00819         foreach e $outArcs($node) {
00820             set n [lindex $arcNodes($e) 1]
00821             if {[info exists group($n)]} {continue}
00822             # if {[info exists coll($e)]}  {continue}
00823             lappend arcs    $e
00824             # set     coll($e) .
00825         }
00826         }
00827     }
00828     none {
00829         set arcs [array names arcNodes]
00830     }
00831     default {error "Can't happen, panic"}
00832     }
00833 
00834     #
00835     # We have a list of arcs that match the relation to the nodes.
00836     # Now filter according to -key and -value.
00837     #
00838 
00839     set filteredArcs [list]
00840 
00841     if {$haveKey} {
00842     foreach arc $arcs {
00843         catch {
00844         set aval [__arc_get $name $arc -key $key]
00845         if {$haveValue} {
00846             if {$aval == $value} {
00847             lappend filteredArcs $arc
00848             }
00849         } else {
00850             lappend filteredArcs $arc
00851         }
00852         }
00853     }
00854     } else {
00855     set filteredArcs $arcs
00856     }
00857 
00858     return $filteredArcs
00859 }
00860 
00861 /*  ::struct::graph::_destroy --*/
00862 /* */
00863 /*  Destroy a graph, including its associated command and data storage.*/
00864 /* */
00865 /*  Arguments:*/
00866 /*  name    name of the graph.*/
00867 /* */
00868 /*  Results:*/
00869 /*  None.*/
00870 
00871 ret  ::struct::graph::_destroy (type name) {
00872     namespace delete ::struct::graph::graph$name
00873     interp alias {} ::$name {}
00874 }
00875 
00876 /*  ::struct::graph::__generateUniqueArcName --*/
00877 /* */
00878 /*  Generate a unique arc name for the given graph.*/
00879 /* */
00880 /*  Arguments:*/
00881 /*  name    name of the graph.*/
00882 /* */
00883 /*  Results:*/
00884 /*  arc name of a arc guaranteed to not exist in the graph.*/
00885 
00886 ret  ::struct::graph::__generateUniqueArcName (type name) {
00887     upvar ::struct::graph::graph${name}::nextUnusedArc nextUnusedArc
00888     while {[__arc_exists $name "arc${nextUnusedArc}"]} {
00889     incr nextUnusedArc
00890     }
00891     return "arc${nextUnusedArc}"
00892 }
00893 
00894 /*  ::struct::graph::__generateUniqueNodeName --*/
00895 /* */
00896 /*  Generate a unique node name for the given graph.*/
00897 /* */
00898 /*  Arguments:*/
00899 /*  name    name of the graph.*/
00900 /* */
00901 /*  Results:*/
00902 /*  node    name of a node guaranteed to not exist in the graph.*/
00903 
00904 ret  ::struct::graph::__generateUniqueNodeName (type name) {
00905     upvar ::struct::graph::graph${name}::nextUnusedNode nextUnusedNode
00906     while {[__node_exists $name "node${nextUnusedNode}"]} {
00907     incr nextUnusedNode
00908     }
00909     return "node${nextUnusedNode}"
00910 }
00911 
00912 /*  ::struct::graph::_get --*/
00913 /* */
00914 /*  Get a keyed value from the graph itself*/
00915 /* */
00916 /*  Arguments:*/
00917 /*  name    name of the graph.*/
00918 /*  flag    -key; anything else is an error*/
00919 /*  key key to lookup; defaults to data*/
00920 /* */
00921 /*  Results:*/
00922 /*  value   value associated with the key given.*/
00923 
00924 ret  ::struct::graph::_get (type name , optional flag =-key , optional key =data) {
00925     upvar ::struct::graph::graph${name}::graphData data
00926 
00927     if { ![info exists data($key)] } {
00928     error "invalid key \"$key\" for graph \"$name\""
00929     }
00930 
00931     return $data($key)
00932 }
00933 
00934 /*  ::struct::graph::_getall --*/
00935 /* */
00936 /*  Get a serialized list of key/value pairs from a graph.*/
00937 /* */
00938 /*  Arguments:*/
00939 /*  name    name of the graph.*/
00940 /* */
00941 /*  Results:*/
00942 /*  value   value associated with the key given.*/
00943 
00944 ret  ::struct::graph::_getall (type name , type args) { 
00945     if { [llength $args] } {
00946     error "wrong # args: should be none"
00947     }
00948     
00949     upvar ::struct::graph::graph${name}::graphData data
00950     return [array get data]
00951 }
00952 
00953 /*  ::struct::graph::_keys --*/
00954 /* */
00955 /*  Get a list of keys from a graph.*/
00956 /* */
00957 /*  Arguments:*/
00958 /*  name    name of the graph.*/
00959 /* */
00960 /*  Results:*/
00961 /*  value   list of known keys*/
00962 
00963 ret  ::struct::graph::_keys (type name , type args) { 
00964     if { [llength $args] } {
00965     error "wrong # args: should be none"
00966     }
00967 
00968     upvar ::struct::graph::graph${name}::graphData data
00969     return [array names data]
00970 }
00971 
00972 /*  ::struct::graph::_keyexists --*/
00973 /* */
00974 /*  Test for existance of a given key in a graph.*/
00975 /* */
00976 /*  Arguments:*/
00977 /*  name    name of the graph.*/
00978 /*  flag    -key; anything else is an error*/
00979 /*  key key to lookup; defaults to data*/
00980 /* */
00981 /*  Results:*/
00982 /*  1 if the key exists, 0 else.*/
00983 
00984 ret  ::struct::graph::_keyexists (type name , optional flag =-key , optional key =data) {
00985     if { ![string equal $flag "-key"] } {
00986     error "invalid option \"$flag\": should be -key"
00987     }
00988     
00989     upvar ::struct::graph::graph${name}::graphData data
00990     return [info exists data($key)]
00991 }
00992 
00993 /*  ::struct::graph::_node --*/
00994 /* */
00995 /*  Dispatches the invocation of node methods to the proper handler*/
00996 /*  procedure.*/
00997 /* */
00998 /*  Arguments:*/
00999 /*  name    name of the graph.*/
01000 /*  cmd node command to invoke*/
01001 /*  args    arguments to propagate to the handler for the node command*/
01002 /* */
01003 /*  Results:*/
01004 /*  As of the the invoked handler.*/
01005 
01006 ret  ::struct::graph::_node (type name , type cmd , type args) {
01007 
01008     # Split the args into command and args components
01009     if { [llength [info commands ::struct::graph::__node_$cmd]] == 0 } {
01010     variable nodeCommands
01011     set optlist [join $nodeCommands ", "]
01012     set optlist [linsert $optlist "end-1" "or"]
01013     error "bad option \"$cmd\": must be $optlist"
01014     }
01015 
01016     eval [list ::struct::graph::__node_$cmd $name] $args
01017 }
01018 
01019 /*  ::struct::graph::__node_degree --*/
01020 /* */
01021 /*  Return the number of arcs adjacent to the specified node.*/
01022 /*  If one of the restrictions -in or -out is given only*/
01023 /*  incoming resp. outgoing arcs are counted.*/
01024 /* */
01025 /*  Arguments:*/
01026 /*  name    name of the graph.*/
01027 /*  args    option, followed by the node.*/
01028 /* */
01029 /*  Results:*/
01030 /*  None.*/
01031 
01032 ret  ::struct::graph::__node_degree (type name , type args) {
01033 
01034     if {([llength $args] < 1) || ([llength $args] > 2)} {
01035     error "wrong # args: should be \"$name node degree ?-in|-out? node\""
01036     }
01037 
01038     switch -exact -- [llength $args] {
01039     1 {
01040         set opt {}
01041         set node [lindex $args 0]
01042     }
01043     2 {
01044         set opt  [lindex $args 0]
01045         set node [lindex $args 1]
01046     }
01047     default {error "Can't happen, panic"}
01048     }
01049 
01050     # Validate the option.
01051 
01052     switch -exact -- $opt {
01053     {}   -
01054     -in  -
01055     -out {}
01056     default {
01057         error "invalid option \"$opt\": should be -in or -out"
01058     }
01059     }
01060 
01061     # Validate the node
01062 
01063     if { ![__node_exists $name $node] } {
01064     error "node \"$node\" does not exist in graph \"$name\""
01065     }
01066 
01067     upvar ::struct::graph::graph${name}::inArcs   inArcs
01068     upvar ::struct::graph::graph${name}::outArcs  outArcs
01069 
01070     switch -exact -- $opt {
01071     -in  {
01072         set result [llength $inArcs($node)]
01073     }
01074     -out {
01075         set result [llength $outArcs($node)]
01076     }
01077     {} {
01078         set result [expr {[llength $inArcs($node)] \
01079             + [llength $outArcs($node)]}]
01080 
01081         # loops count twice, don't do <set> arithmetics, i.e. no union!
01082         if {0} {
01083         array set coll  {}
01084         set result [llength $inArcs($node)]
01085 
01086         foreach e $inArcs($node) {
01087             set coll($e) .
01088         }
01089         foreach e $outArcs($node) {
01090             if {[info exists coll($e)]} {continue}
01091             incr result
01092             set     coll($e) .
01093         }
01094         }
01095     }
01096     default {error "Can't happen, panic"}
01097     }
01098 
01099     return $result
01100 }
01101 
01102 /*  ::struct::graph::__node_delete --*/
01103 /* */
01104 /*  Remove a node from a graph, including all of its values.*/
01105 /*  Additionally removes the arcs connected to this node.*/
01106 /* */
01107 /*  Arguments:*/
01108 /*  name    name of the graph.*/
01109 /*  args    list of the nodes to delete.*/
01110 /* */
01111 /*  Results:*/
01112 /*  None.*/
01113 
01114 ret  ::struct::graph::__node_delete (type name , type args) {
01115 
01116     foreach node $args {
01117     if { ![__node_exists $name $node] } {
01118         error "node \"$node\" does not exist in graph \"$name\""
01119     }
01120     }
01121 
01122     upvar ::struct::graph::graph${name}::inArcs  inArcs
01123     upvar ::struct::graph::graph${name}::outArcs outArcs
01124 
01125     foreach node $args {
01126     # Remove all the arcs connected to this node
01127     foreach e $inArcs($node) {
01128         __arc_delete $name $e
01129     }
01130     foreach e $outArcs($node) {
01131         # Check existence to avoid problems with
01132         # loops (they are in and out arcs! at
01133         # the same time and thus already deleted)
01134         if { [__arc_exists $name $e] } {
01135         __arc_delete $name $e
01136         }
01137     }
01138 
01139     unset inArcs($node)
01140     unset outArcs($node)
01141     # FRINK: nocheck
01142     unset ::struct::graph::graph${name}::node$node
01143     }
01144 
01145     return
01146 }
01147 
01148 /*  ::struct::graph::__node_exists --*/
01149 /* */
01150 /*  Test for existance of a given node in a graph.*/
01151 /* */
01152 /*  Arguments:*/
01153 /*  name    name of the graph.*/
01154 /*  node    node to look for.*/
01155 /* */
01156 /*  Results:*/
01157 /*  1 if the node exists, 0 else.*/
01158 
01159 ret  ::struct::graph::__node_exists (type name , type node) {
01160     return [info exists ::struct::graph::graph${name}::inArcs($node)]
01161 }
01162 
01163 /*  ::struct::graph::__node_get --*/
01164 /* */
01165 /*  Get a keyed value from a node in a graph.*/
01166 /* */
01167 /*  Arguments:*/
01168 /*  name    name of the graph.*/
01169 /*  node    node to query.*/
01170 /*  flag    -key; anything else is an error*/
01171 /*  key key to lookup; defaults to data*/
01172 /* */
01173 /*  Results:*/
01174 /*  value   value associated with the key given.*/
01175 
01176 ret  ::struct::graph::__node_get (type name , type node , optional flag =-key , optional key =data) {
01177     if { ![__node_exists $name $node] } {
01178     error "node \"$node\" does not exist in graph \"$name\""
01179     }
01180     
01181     upvar ::struct::graph::graph${name}::node${node} data
01182 
01183     if { ![info exists data($key)] } {
01184     error "invalid key \"$key\" for node \"$node\""
01185     }
01186 
01187     return $data($key)
01188 }
01189 
01190 /*  ::struct::graph::__node_getall --*/
01191 /* */
01192 /*  Get a serialized list of key/value pairs from a node in a graph.*/
01193 /* */
01194 /*  Arguments:*/
01195 /*  name    name of the graph.*/
01196 /*  node    node to query.*/
01197 /* */
01198 /*  Results:*/
01199 /*  value   value associated with the key given.*/
01200 
01201 ret  ::struct::graph::__node_getall (type name , type node , type args) { 
01202     if { ![__node_exists $name $node] } {
01203     error "node \"$node\" does not exist in graph \"$name\""
01204     }
01205 
01206     if { [llength $args] } {
01207     error "wrong # args: should be none"
01208     }
01209     
01210     upvar ::struct::graph::graph${name}::node${node} data
01211 
01212     return [array get data]
01213 }
01214 
01215 /*  ::struct::graph::__node_keys --*/
01216 /* */
01217 /*  Get a list of keys from a node in a graph.*/
01218 /* */
01219 /*  Arguments:*/
01220 /*  name    name of the graph.*/
01221 /*  node    node to query.*/
01222 /* */
01223 /*  Results:*/
01224 /*  value   value associated with the key given.*/
01225 
01226 ret  ::struct::graph::__node_keys (type name , type node , type args) { 
01227     if { ![__node_exists $name $node] } {
01228     error "node \"$node\" does not exist in graph \"$name\""
01229     }
01230     
01231     if { [llength $args] } {
01232     error "wrong # args: should be none"
01233     }
01234 
01235     upvar ::struct::graph::graph${name}::node${node} data
01236 
01237     return [array names data]
01238 }
01239 
01240 /*  ::struct::graph::__node_keyexists --*/
01241 /* */
01242 /*  Test for existance of a given key for a node in a graph.*/
01243 /* */
01244 /*  Arguments:*/
01245 /*  name    name of the graph.*/
01246 /*  node    node to query.*/
01247 /*  flag    -key; anything else is an error*/
01248 /*  key key to lookup; defaults to data*/
01249 /* */
01250 /*  Results:*/
01251 /*  1 if the key exists, 0 else.*/
01252 
01253 ret  ::struct::graph::__node_keyexists (type name , type node , optional flag =-key , optional key =data) {
01254     if { ![__node_exists $name $node] } {
01255     error "node \"$node\" does not exist in graph \"$name\""
01256     }
01257     
01258     if { ![string equal $flag "-key"] } {
01259     error "invalid option \"$flag\": should be -key"
01260     }
01261     
01262     upvar ::struct::graph::graph${name}::node${node} data
01263 
01264     return [info exists data($key)]
01265 }
01266 
01267 /*  ::struct::graph::__node_insert --*/
01268 /* */
01269 /*  Add a node to a graph.*/
01270 /* */
01271 /*  Arguments:*/
01272 /*  name        name of the graph.*/
01273 /*  args        node to insert; must be unique.  If none is given,*/
01274 /*          the routine will generate a unique node name.*/
01275 /* */
01276 /*  Results:*/
01277 /*  node        The namee of the new node.*/
01278 
01279 ret  ::struct::graph::__node_insert (type name , type args) {
01280 
01281     if { [llength $args] == 0 } {
01282     # No node name was given; generate a unique one
01283     set node [__generateUniqueNodeName $name]
01284     } else {
01285     set node [lindex $args 0]
01286     }
01287 
01288     if { [__node_exists $name $node] } {
01289     error "node \"$node\" already exists in graph \"$name\""
01290     }
01291     
01292     upvar ::struct::graph::graph${name}::inArcs      inArcs
01293     upvar ::struct::graph::graph${name}::outArcs     outArcs
01294     upvar ::struct::graph::graph${name}::node${node} data
01295 
01296     # Set up the new node
01297     set inArcs($node)  [list]
01298     set outArcs($node) [list]
01299     set data(data) ""
01300 
01301     return $node
01302 }
01303 
01304 /*  ::struct::graph::__node_opposite --*/
01305 /* */
01306 /*  Retrieve node opposite to the specified one, along the arc.*/
01307 /* */
01308 /*  Arguments:*/
01309 /*  name        name of the graph.*/
01310 /*  node        node to look up.*/
01311 /*  arc     arc to look up.*/
01312 /* */
01313 /*  Results:*/
01314 /*  nodex   Node opposite to <node,arc>*/
01315 
01316 ret  ::struct::graph::__node_opposite (type name , type node , type arc) {
01317     if {![__node_exists $name $node] } {
01318     error "node \"$node\" does not exist in graph \"$name\""
01319     }
01320     
01321     if {![__arc_exists $name $arc] } {
01322     error "arc \"$arc\" does not exist in graph \"$name\""
01323     }
01324 
01325     upvar ::struct::graph::graph${name}::arcNodes arcNodes
01326 
01327     # Node must be connected to at least one end of the arc.
01328 
01329     if {[string equal $node [lindex $arcNodes($arc) 0]]} {
01330     set result [lindex $arcNodes($arc) 1]
01331     } elseif {[string equal $node [lindex $arcNodes($arc) 1]]} {
01332     set result [lindex $arcNodes($arc) 0]
01333     } else {
01334     error "node \"$node\" and arc \"$arc\" are not connected\
01335         in graph \"$name\""
01336     }
01337 
01338     return $result
01339 }
01340 
01341 /*  ::struct::graph::__node_set --*/
01342 /* */
01343 /*  Set or get a value for a node in a graph.*/
01344 /* */
01345 /*  Arguments:*/
01346 /*  name    name of the graph.*/
01347 /*  node    node to modify or query.*/
01348 /*  args    ?-key key? ?value?*/
01349 /* */
01350 /*  Results:*/
01351 /*  val value associated with the given key of the given node*/
01352 
01353 ret  ::struct::graph::__node_set (type name , type node , type args) {
01354     if { ![__node_exists $name $node] } {
01355     error "node \"$node\" does not exist in graph \"$name\""
01356     }
01357     upvar ::struct::graph::graph${name}::node$node data
01358 
01359     if { [llength $args] > 3 } {
01360     error "wrong # args: should be \"$name node set $node ?-key key?\
01361         ?value?\""
01362     }
01363     
01364     set key "data"
01365     set haveValue 0
01366     if { [llength $args] > 1 } {
01367     foreach {flag key} $args break
01368     if { ![string match "${flag}*" "-key"] } {
01369         error "invalid option \"$flag\": should be key"
01370     }
01371     if { [llength $args] == 3 } {
01372         set haveValue 1
01373         set value [lindex $args end]
01374     }
01375     } elseif { [llength $args] == 1 } {
01376     set haveValue 1
01377     set value [lindex $args end]
01378     }
01379 
01380     if { $haveValue } {
01381     # Setting a value
01382     return [set data($key) $value]
01383     } else {
01384     # Getting a value
01385     if { ![info exists data($key)] } {
01386         error "invalid key \"$key\" for node \"$node\""
01387     }
01388     return $data($key)
01389     }
01390 }
01391 
01392 /*  ::struct::graph::__node_append --*/
01393 /* */
01394 /*  Append a value for a node in a graph.*/
01395 /* */
01396 /*  Arguments:*/
01397 /*  name    name of the graph.*/
01398 /*  node    node to modify or query.*/
01399 /*  args    ?-key key? value*/
01400 /* */
01401 /*  Results:*/
01402 /*  val value associated with the given key of the given node*/
01403 
01404 ret  ::struct::graph::__node_append (type name , type node , type args) {
01405     if { ![__node_exists $name $node] } {
01406     error "node \"$node\" does not exist in graph \"$name\""
01407     }
01408     upvar ::struct::graph::graph${name}::node$node data
01409 
01410     if { [llength $args] != 1 && [llength $args] != 3 } {
01411     error "wrong # args: should be \"$name node append $node ?-key key?\
01412         value\""
01413     }
01414     
01415     if { [llength $args] == 3 } {
01416     foreach {flag key} $args break
01417     if { ![string equal $flag "-key"] } {
01418         error "invalid option \"$flag\": should be -key"
01419     }
01420     } else {
01421     set key "data"
01422     }
01423 
01424     set value [lindex $args end]
01425 
01426     return [append data($key) $value]
01427 }
01428 
01429 /*  ::struct::graph::__node_lappend --*/
01430 /* */
01431 /*  lappend a value for a node in a graph.*/
01432 /* */
01433 /*  Arguments:*/
01434 /*  name    name of the graph.*/
01435 /*  node    node to modify or query.*/
01436 /*  args    ?-key key? value*/
01437 /* */
01438 /*  Results:*/
01439 /*  val value associated with the given key of the given node*/
01440 
01441 ret  ::struct::graph::__node_lappend (type name , type node , type args) {
01442     if { ![__node_exists $name $node] } {
01443     error "node \"$node\" does not exist in graph \"$name\""
01444     }
01445     upvar ::struct::graph::graph${name}::node$node data
01446 
01447     if { [llength $args] != 1 && [llength $args] != 3 } {
01448     error "wrong # args: should be \"$name node lappend $node ?-key key?\
01449         value\""
01450     }
01451     
01452     if { [llength $args] == 3 } {
01453     foreach {flag key} $args break
01454     if { ![string equal $flag "-key"] } {
01455         error "invalid option \"$flag\": should be -key"
01456     }
01457     } else {
01458     set key "data"
01459     }
01460 
01461     set value [lindex $args end]
01462 
01463     return [lappend data($key) $value]
01464 }
01465 
01466 /*  ::struct::graph::__node_unset --*/
01467 /* */
01468 /*  Remove a keyed value from a node.*/
01469 /* */
01470 /*  Arguments:*/
01471 /*  name    name of the graph.*/
01472 /*  node    node to modify.*/
01473 /*  args    additional args: ?-key key?*/
01474 /* */
01475 /*  Results:*/
01476 /*  None.*/
01477 
01478 ret  ::struct::graph::__node_unset (type name , type node , optional flag =-key , optional key =data) {
01479     if { ![__node_exists $name $node] } {
01480     error "node \"$node\" does not exist in graph \"$name\""
01481     }
01482     
01483     if { ![string match "${flag}*" "-key"] } {
01484     error "invalid option \"$flag\": should be \"$name node unset\
01485         $node ?-key key?\""
01486     }
01487 
01488     upvar ::struct::graph::graph${name}::node${node} data
01489     if { [info exists data($key)] } {
01490     unset data($key)
01491     }
01492     return
01493 }
01494 
01495 /*  ::struct::graph::_nodes --*/
01496 /* */
01497 /*  Return a list of all nodes in a graph satisfying some restriction.*/
01498 /* */
01499 /*  Arguments:*/
01500 /*  name    name of the graph.*/
01501 /*  args    list of options and nodes specifying the restriction.*/
01502 /* */
01503 /*  Results:*/
01504 /*  nodes   list of nodes*/
01505 
01506 ret  ::struct::graph::_nodes (type name , type args) {
01507 
01508     # Discriminate between conditions and nodes
01509 
01510     set haveCond 0
01511     set haveKey 0
01512     set haveValue 0
01513     set cond "none"
01514     set condNodes [list]
01515 
01516     for {set i 0} {$i < [llength $args]} {incr i} {
01517     set arg [lindex $args $i]
01518     switch -glob -- $arg {
01519         -in -
01520         -out -
01521         -adj -
01522         -inner -
01523         -embedding {
01524         if {$haveCond} {
01525             return -code error "invalid restriction:\
01526                 illegal multiple use of\
01527                 \"-in\"|\"-out\"|\"-adj\"|\"-inner\"|\"-embedding\""
01528         }
01529 
01530         set haveCond 1
01531         set cond [string range $arg 1 end]
01532         }
01533         -key {
01534         if {$haveKey} {
01535             return -code error {invalid restriction: illegal multiple use of "-key"}
01536         }
01537 
01538         incr i
01539         set key [lindex $args $i]
01540         set haveKey 1
01541         }
01542         -value {
01543         if {$haveValue} {
01544             return -code error {invalid restriction: illegal multiple use of "-value"}
01545         }
01546 
01547         incr i
01548         set value [lindex $args $i]
01549         set haveValue 1
01550         }
01551         -* {
01552         error "invalid restriction \"$arg\": should be -in, -out,\
01553             -adj, -inner, -embedding, -key or -value"
01554         }
01555         default {
01556         lappend condNodes $arg
01557         }
01558     }
01559     }
01560 
01561     # Validate that there are nodes to use in the restriction.
01562     # otherwise what's the point?
01563     if {$haveCond} {
01564     if {[llength $condNodes] == 0} {
01565         set usage "$name nodes ?-key key? ?-value value? ?-in|-out|-adj|-inner|-embedding node node...?"
01566         error "no nodes specified: should be \"$usage\""
01567     }
01568 
01569     # Make sure that the specified nodes exist!
01570     foreach node $condNodes {
01571         if { ![__node_exists $name $node] } {
01572         error "node \"$node\" does not exist in graph \"$name\""
01573         }
01574     }
01575     }
01576 
01577     # Now we are able to go to work
01578     upvar ::struct::graph::graph${name}::inArcs   inArcs
01579     upvar ::struct::graph::graph${name}::outArcs  outArcs
01580     upvar ::struct::graph::graph${name}::arcNodes arcNodes
01581 
01582     set       nodes [list]
01583     array set coll  {}
01584 
01585     switch -exact -- $cond {
01586     in {
01587         # Result is all nodes with at least one arc going to
01588         # at least one node in the list of arguments.
01589 
01590         foreach node $condNodes {
01591         foreach e $inArcs($node) {
01592             set n [lindex $arcNodes($e) 0]
01593             if {[info exists coll($n)]} {continue}
01594             lappend nodes    $n
01595             set     coll($n) .
01596         }
01597         }
01598     }
01599     out {
01600         # Result is all nodes with at least one arc coming from
01601         # at least one node in the list of arguments.
01602 
01603         foreach node $condNodes {
01604         foreach e $outArcs($node) {
01605             set n [lindex $arcNodes($e) 1]
01606             if {[info exists coll($n)]} {continue}
01607             lappend nodes    $n
01608             set     coll($n) .
01609         }
01610         }
01611     }
01612     adj {
01613         # Result is all nodes with at least one arc coming from
01614         # or going to at least one node in the list of arguments.
01615 
01616         foreach node $condNodes {
01617         foreach e $inArcs($node) {
01618             set n [lindex $arcNodes($e) 0]
01619             if {[info exists coll($n)]} {continue}
01620             lappend nodes    $n
01621             set     coll($n) .
01622         }
01623         foreach e $outArcs($node) {
01624             set n [lindex $arcNodes($e) 1]
01625             if {[info exists coll($n)]} {continue}
01626             lappend nodes    $n
01627             set     coll($n) .
01628         }
01629         }
01630     }
01631     inner {
01632         # Result is all nodes from the list! with at least one arc
01633         # coming from or going to at least one node in the list of
01634         # arguments.
01635 
01636         array set group {}
01637         foreach node $condNodes {
01638         set group($node) .
01639         }
01640 
01641         foreach node $condNodes {
01642         foreach e $inArcs($node) {
01643             set n [lindex $arcNodes($e) 0]
01644             if {![info exists group($n)]} {continue}
01645             if { [info exists coll($n)]}  {continue}
01646             lappend nodes    $n
01647             set     coll($n) .
01648         }
01649         foreach e $outArcs($node) {
01650             set n [lindex $arcNodes($e) 1]
01651             if {![info exists group($n)]} {continue}
01652             if { [info exists coll($n)]}  {continue}
01653             lappend nodes    $n
01654             set     coll($n) .
01655         }
01656         }
01657     }
01658     embedding {
01659         # Result is all nodes with at least one arc coming from
01660         # or going to at least one node in the list of arguments,
01661         # but not in the list itself!
01662 
01663         array set group {}
01664         foreach node $condNodes {
01665         set group($node) .
01666         }
01667 
01668         foreach node $condNodes {
01669         foreach e $inArcs($node) {
01670             set n [lindex $arcNodes($e) 0]
01671             if {[info exists group($n)]} {continue}
01672             if {[info exists coll($n)]}  {continue}
01673             lappend nodes    $n
01674             set     coll($n) .
01675         }
01676         foreach e $outArcs($node) {
01677             set n [lindex $arcNodes($e) 1]
01678             if {[info exists group($n)]} {continue}
01679             if {[info exists coll($n)]}  {continue}
01680             lappend nodes    $n
01681             set     coll($n) .
01682         }
01683         }
01684     }
01685     none {
01686         set nodes [array names inArcs]
01687     }
01688     default {error "Can't happen, panic"}
01689     }
01690 
01691     #
01692     # We have a list of nodes that match the relation to the nodes.
01693     # Now filter according to -key and -value.
01694     #
01695 
01696     set filteredNodes [list]
01697 
01698     if {$haveKey} {
01699     foreach node $nodes {
01700         catch {
01701         set nval [__node_get $name $node -key $key]
01702         if {$haveValue} {
01703             if {$nval == $value} {
01704             lappend filteredNodes $node
01705             }
01706         } else {
01707             lappend filteredNodes $node
01708         }
01709         }
01710     }
01711     } else {
01712     set filteredNodes $nodes
01713     }
01714 
01715     return $filteredNodes
01716 }
01717 
01718 /*  ::struct::graph::_set --*/
01719 /* */
01720 /*  Set or get a keyed value from the graph itself*/
01721 /* */
01722 /*  Arguments:*/
01723 /*  name    name of the graph.*/
01724 /*  flag    -key; anything else is an error*/
01725 /*  args    ?-key key? ?value?*/
01726 /* */
01727 /*  Results:*/
01728 /*  value   value associated with the key given.*/
01729 
01730 ret  ::struct::graph::_set (type name , type args) {
01731     upvar ::struct::graph::graph${name}::graphData data
01732 
01733     if { [llength $args] > 3 } {
01734     error "wrong # args: should be \"$name set ?-key key?\
01735         ?value?\""
01736     }
01737 
01738     set key "data"
01739     set haveValue 0
01740     if { [llength $args] > 1 } {
01741     foreach {flag key} $args break
01742     if { ![string match "${flag}*" "-key"] } {
01743         error "invalid option \"$flag\": should be key"
01744     }
01745     if { [llength $args] == 3 } {
01746         set haveValue 1
01747         set value [lindex $args end]
01748     }
01749     } elseif { [llength $args] == 1 } {
01750     set haveValue 1
01751     set value [lindex $args end]
01752     }
01753 
01754     if { $haveValue } {
01755     # Setting a value
01756     return [set data($key) $value]
01757     } else {
01758     # Getting a value
01759     if { ![info exists data($key)] } {
01760         error "invalid key \"$key\" for graph \"$name\""
01761     }
01762     return $data($key)
01763     }
01764 }
01765 
01766 /*  ::struct::graph::_swap --*/
01767 /* */
01768 /*  Swap two nodes in a graph.*/
01769 /* */
01770 /*  Arguments:*/
01771 /*  name    name of the graph.*/
01772 /*  node1   first node to swap.*/
01773 /*  node2   second node to swap.*/
01774 /* */
01775 /*  Results:*/
01776 /*  None.*/
01777 
01778 ret  ::struct::graph::_swap (type name , type node1 , type node2) {
01779     # Can only swap two real nodes
01780     if { ![__node_exists $name $node1] } {
01781     error "node \"$node1\" does not exist in graph \"$name\""
01782     }
01783     if { ![__node_exists $name $node2] } {
01784     error "node \"$node2\" does not exist in graph \"$name\""
01785     }
01786 
01787     # Can't swap a node with itself
01788     if { [string equal $node1 $node2] } {
01789     error "cannot swap node \"$node1\" with itself"
01790     }
01791 
01792     # Swapping nodes means swapping their labels, values and arcs
01793     upvar ::struct::graph::graph${name}::outArcs      outArcs
01794     upvar ::struct::graph::graph${name}::inArcs       inArcs
01795     upvar ::struct::graph::graph${name}::arcNodes     arcNodes
01796     upvar ::struct::graph::graph${name}::node${node1} node1Vals
01797     upvar ::struct::graph::graph${name}::node${node2} node2Vals
01798 
01799     # Redirect arcs to the new nodes.
01800 
01801     foreach e $inArcs($node1) {
01802     set arcNodes($e) [lreplace $arcNodes($e) end end $node2]
01803     }
01804     foreach e $inArcs($node2) {
01805     set arcNodes($e) [lreplace $arcNodes($e) end end $node1]
01806     }
01807     foreach e $outArcs($node1) {
01808     set arcNodes($e) [lreplace $arcNodes($e) 0 0 $node2]
01809     }
01810     foreach e $outArcs($node2) {
01811     set arcNodes($e) [lreplace $arcNodes($e) 0 0 $node1]
01812     }
01813 
01814     # Swap arc lists
01815 
01816     set tmp            $inArcs($node1)
01817     set inArcs($node1) $inArcs($node2)
01818     set inArcs($node2) $tmp
01819 
01820     set tmp             $outArcs($node1)
01821     set outArcs($node1) $outArcs($node2)
01822     set outArcs($node2) $tmp
01823 
01824     # Swap the values
01825     set   value1        [array get node1Vals]
01826     unset node1Vals
01827     array set node1Vals [array get node2Vals]
01828     unset node2Vals
01829     array set node2Vals $value1
01830 
01831     return
01832 }
01833 
01834 /*  ::struct::graph::_unset --*/
01835 /* */
01836 /*  Remove a keyed value from the graph itself*/
01837 /* */
01838 /*  Arguments:*/
01839 /*  name    name of the graph.*/
01840 /*  flag    -key; anything else is an error*/
01841 /*  args    additional args: ?-key key?*/
01842 /* */
01843 /*  Results:*/
01844 /*  None.*/
01845 
01846 ret  ::struct::graph::_unset (type name , optional flag =-key , optional key =data) {
01847     upvar ::struct::graph::graph${name}::graphData data
01848     
01849     if { ![string match "${flag}*" "-key"] } {
01850     error "invalid option \"$flag\": should be \"$name unset\
01851         ?-key key?\""
01852     }
01853 
01854     if { [info exists data($key)] } {
01855     unset data($key)
01856     }
01857 
01858     return
01859 }
01860 
01861 /*  ::struct::graph::_walk --*/
01862 /* */
01863 /*  Walk a graph using a pre-order depth or breadth first*/
01864 /*  search. Pre-order DFS is the default.  At each node that is visited,*/
01865 /*  a command will be called with the name of the graph and the node.*/
01866 /* */
01867 /*  Arguments:*/
01868 /*  name    name of the graph.*/
01869 /*  node    node at which to start.*/
01870 /*  args    additional args: ?-order pre|post|both? ?-type {bfs|dfs}?*/
01871 /*      -command cmd*/
01872 /* */
01873 /*  Results:*/
01874 /*  None.*/
01875 
01876 ret  ::struct::graph::_walk (type name , type node , type args) {
01877     set usage "$name walk $node ?-dir forward|backward?\
01878         ?-order pre|post|both? ?-type {bfs|dfs}? -command cmd"
01879 
01880     if {[llength $args] > 8 || [llength $args] < 2} {
01881     error "wrong # args: should be \"$usage\""
01882     }
01883 
01884     if { ![__node_exists $name $node] } {
01885     error "node \"$node\" does not exist in graph \"$name\""
01886     }
01887 
01888     # Set defaults
01889     set type  dfs
01890     set order pre
01891     set cmd   ""
01892     set dir   forward
01893 
01894     # Process specified options
01895     for {set i 0} {$i < [llength $args]} {incr i} {
01896     set flag [lindex $args $i]
01897     incr i
01898     if { $i >= [llength $args] } {
01899         error "value for \"$flag\" missing: should be \"$usage\""
01900     }
01901     switch -glob -- $flag {
01902         "-type" {
01903         set type [string tolower [lindex $args $i]]
01904         }
01905         "-order" {
01906         set order [string tolower [lindex $args $i]]
01907         }
01908         "-command" {
01909         set cmd [lindex $args $i]
01910         }
01911         "-dir" {
01912         set dir [string tolower [lindex $args $i]]
01913         }
01914         default {
01915         error "unknown option \"$flag\": should be \"$usage\""
01916         }
01917     }
01918     }
01919     
01920     # Make sure we have a command to run, otherwise what's the point?
01921     if { [string equal $cmd ""] } {
01922     error "no command specified: should be \"$usage\""
01923     }
01924 
01925     # Validate that the given type is good
01926     switch -glob -- $type {
01927     "dfs" {
01928         set type "dfs"
01929     }
01930     "bfs" {
01931         set type "bfs"
01932     }
01933     default {
01934         error "invalid search type \"$type\": should be dfs, or bfs"
01935     }
01936     }
01937     
01938     # Validate that the given order is good
01939     switch -glob -- $order {
01940     "both" {
01941         set order both
01942     }
01943     "pre" {
01944         set order pre
01945     }
01946     "post" {
01947         set order post
01948     }
01949     default {
01950         error "invalid search order \"$order\": should be both,\
01951             pre or post"
01952     }
01953     }
01954 
01955     # Validate that the given direction is good
01956     switch -glob -- $dir {
01957     "forward" {
01958         set dir -out
01959     }
01960     "backward" {
01961         set dir -in
01962     }
01963     default {
01964         error "invalid search direction \"$dir\": should be\
01965             forward or backward"
01966     }
01967     }
01968 
01969     # Do the walk
01970 
01971     set st [list ]
01972     lappend st $node
01973     array set visited {}
01974 
01975     if { [string equal $type "dfs"] } {
01976     if { [string equal $order "pre"] } {
01977         # Pre-order Depth-first search
01978 
01979         while { [llength $st] > 0 } {
01980         set node [lindex   $st end]
01981         set st   [lreplace $st end end]
01982 
01983         # Evaluate the command at this node
01984         set cmdcpy $cmd
01985         lappend cmdcpy enter $name $node
01986         uplevel 2 $cmdcpy
01987 
01988         set visited($node) .
01989 
01990         # Add this node's neighbours (according to direction)
01991         #  Have to add them in reverse order
01992         #  so that they will be popped left-to-right
01993 
01994         set next [_nodes $name $dir $node]
01995         set len  [llength $next]
01996 
01997         for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
01998             set nextnode [lindex $next $i]
01999             if {[info exists visited($nextnode)]} {
02000             # Skip nodes already visited
02001             continue
02002             }
02003             lappend st $nextnode
02004         }
02005         }
02006     } elseif { [string equal $order "post"] } {
02007         # Post-order Depth-first search
02008 
02009         while { [llength $st] > 0 } {
02010         set node [lindex $st end]
02011 
02012         if {[info exists visited($node)]} {
02013             # Second time we are here, pop it,
02014             # then evaluate the command.
02015 
02016             set st [lreplace $st end end]
02017 
02018             # Evaluate the command at this node
02019             set cmdcpy $cmd
02020             lappend cmdcpy leave $name $node
02021             uplevel 2 $cmdcpy
02022         } else {
02023             # First visit. Remember it.
02024             set visited($node) .
02025         
02026             # Add this node's neighbours.
02027             set next [_nodes $name $dir $node]
02028             set len  [llength $next]
02029 
02030             for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
02031             set nextnode [lindex $next $i]
02032             if {[info exists visited($nextnode)]} {
02033                 # Skip nodes already visited
02034                 continue
02035             }
02036             lappend st $nextnode
02037             }
02038         }
02039         }
02040     } else {
02041         # Both-order Depth-first search
02042 
02043         while { [llength $st] > 0 } {
02044         set node [lindex $st end]
02045 
02046         if {[info exists visited($node)]} {
02047             # Second time we are here, pop it,
02048             # then evaluate the command.
02049 
02050             set st [lreplace $st end end]
02051 
02052             # Evaluate the command at this node
02053             set cmdcpy $cmd
02054             lappend cmdcpy leave $name $node
02055             uplevel 2 $cmdcpy
02056         } else {
02057             # First visit. Remember it.
02058             set visited($node) .
02059 
02060             # Evaluate the command at this node
02061             set cmdcpy $cmd
02062             lappend cmdcpy enter $name $node
02063             uplevel 2 $cmdcpy
02064         
02065             # Add this node's neighbours.
02066             set next [_nodes $name $dir $node]
02067             set len  [llength $next]
02068 
02069             for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
02070             set nextnode [lindex $next $i]
02071             if {[info exists visited($nextnode)]} {
02072                 # Skip nodes already visited
02073                 continue
02074             }
02075             lappend st $nextnode
02076             }
02077         }
02078         }
02079     }
02080 
02081     } else {
02082     if { [string equal $order "pre"] } {
02083         # Pre-order Breadth first search
02084         while { [llength $st] > 0 } {
02085         set node [lindex $st 0]
02086         set st   [lreplace $st 0 0]
02087         # Evaluate the command at this node
02088         set cmdcpy $cmd
02089         lappend cmdcpy enter $name $node
02090         uplevel 2 $cmdcpy
02091         
02092         set visited($node) .
02093 
02094         # Add this node's neighbours.
02095         foreach child [_nodes $name $dir $node] {
02096             if {[info exists visited($child)]} {
02097             # Skip nodes already visited
02098             continue
02099             }
02100             lappend st $child
02101         }
02102         }
02103     } else {
02104         # Post-order Breadth first search
02105         # Both-order Breadth first search
02106         # Haven't found anything in Knuth
02107         # and unable to define something
02108         # consistent for myself. Leave it
02109         # out.
02110 
02111         error "unable to do a ${order}-order breadth first walk"
02112     }
02113     }
02114     return
02115 }
02116 
02117 /*  ::struct::graph::Union --*/
02118 /* */
02119 /*  Return a list which is the union of the elements*/
02120 /*  in the specified lists.*/
02121 /* */
02122 /*  Arguments:*/
02123 /*  args    list of lists representing sets.*/
02124 /* */
02125 /*  Results:*/
02126 /*  set list representing the union of the argument lists.*/
02127 
02128 ret  ::struct::graph::Union (type args) {
02129     switch -- [llength $args] {
02130     0 {
02131         return {}
02132     }
02133     1 {
02134         return [lindex $args 0]
02135     }
02136     default {
02137         foreach set $args {
02138         foreach e $set {
02139             set tmp($e) .
02140         }
02141         }
02142         return [array names tmp]
02143     }
02144     }
02145 }
02146 
02147 /*  ### ### ### ######### ######### #########*/
02148 /*  Ready*/
02149 
02150 namespace ::struct {
02151     /*  Get 'graph::graph' into the general structure namespace.*/
02152     namespace import -force graph::graph
02153     namespace export graph
02154 }
02155 package provide struct::graph 1.2.1
02156 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1