home *** CD-ROM | disk | FTP | other *** search
/ H4CK3R 5 / hacker05 / 05_HACK_05.ISO / programacao / freewrap / TCLLIBsampleApp.exe / sample / tcllib / tcllib1.0 / struct / graph.tcl next >
Encoding:
Text File  |  2001-08-17  |  35.7 KB  |  1,541 lines

  1. # graph.tcl --
  2. #
  3. #    Implementation of a graph data structure for Tcl.
  4. #
  5. # Copyright (c) 2000 by Andreas Kupries
  6. #
  7. # See the file "license.terms" for information on usage and redistribution
  8. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  9. # RCS: @(#) $Id: graph.tcl,v 1.3 2001/06/22 15:29:18 andreas_kupries Exp $
  10.  
  11. namespace eval ::struct {}
  12.  
  13. namespace eval ::struct::graph {
  14.     # Data storage in the graph module
  15.     # -------------------------------
  16.     #
  17.     # There's a lot of bits to keep track of for each graph:
  18.     #    nodes
  19.     #    node values
  20.     #    node relationships (arcs)
  21.     #   arc values
  22.     #
  23.     # It would quickly become unwieldy to try to keep these in arrays or lists
  24.     # within the graph namespace itself.  Instead, each graph structure will
  25.     # get its own namespace.  Each namespace contains:
  26.     #    node:$node    array mapping keys to values for the node $node
  27.     #    arc:$arc    array mapping keys to values for the arc $arc
  28.     #    inArcs        array mapping nodes to the list of incoming arcs
  29.     #    outArcs        array mapping nodes to the list of outgoing arcs
  30.     #    arcNodes    array mapping arcs to the two nodes (start & end)
  31.     
  32.     # counter is used to give a unique name for unnamed graph
  33.     variable counter 0
  34.  
  35.     # commands is the list of subcommands recognized by the graph
  36.     variable commands [list    \
  37.         "arc"        \
  38.         "arcs"        \
  39.         "destroy"        \
  40.         "node"        \
  41.         "nodes"        \
  42.         "swap"        \
  43.         "walk"        \
  44.         ]
  45.  
  46.     variable arcCommands [list    \
  47.         "delete"    \
  48.         "exists"    \
  49.         "get"    \
  50.         "insert"    \
  51.         "set"    \
  52.         "source"    \
  53.         "target"    \
  54.         "unset"    \
  55.         ]
  56.  
  57.     variable nodeCommands [list    \
  58.         "degree"    \
  59.         "delete"    \
  60.         "exists"    \
  61.         "get"    \
  62.         "insert"    \
  63.         "opposite"    \
  64.         "set"    \
  65.         "unset"    \
  66.         ]
  67.  
  68.     # Only export one command, the one used to instantiate a new graph
  69.     namespace export graph
  70. }
  71.  
  72. # ::struct::graph::graph --
  73. #
  74. #    Create a new graph with a given name; if no name is given, use
  75. #    graphX, where X is a number.
  76. #
  77. # Arguments:
  78. #    name    name of the graph; if null, generate one.
  79. #
  80. # Results:
  81. #    name    name of the graph created
  82.  
  83. proc ::struct::graph::graph {{name ""}} {
  84.     variable counter
  85.     
  86.     if { [llength [info level 0]] == 1 } {
  87.     incr counter
  88.     set name "graph${counter}"
  89.     }
  90.  
  91.     if { ![string equal [info commands ::$name] ""] } {
  92.     error "command \"$name\" already exists, unable to create graph"
  93.     }
  94.  
  95.     # Set up the namespace
  96.     namespace eval ::struct::graph::graph$name {
  97.  
  98.     # Set up the map from nodes to the arcs coming to them
  99.     variable  inArcs
  100.     array set inArcs {}
  101.  
  102.     # Set up the map from nodes to the arcs going out from them
  103.     variable  outArcs
  104.     array set outArcs {}
  105.  
  106.     # Set up the map from arcs to the nodes they touch.
  107.     variable  arcNodes
  108.     array set arcNodes {}
  109.  
  110.     # Set up a value for use in creating unique node names
  111.     variable nextUnusedNode
  112.     set nextUnusedNode 1
  113.  
  114.     # Set up a value for use in creating unique arc names
  115.     variable nextUnusedArc
  116.     set nextUnusedArc 1
  117.     }
  118.  
  119.     # Create the command to manipulate the graph
  120.     interp alias {} ::$name {} ::struct::graph::GraphProc $name
  121.  
  122.     return $name
  123. }
  124.  
  125. ##########################
  126. # Private functions follow
  127.  
  128. # ::struct::graph::GraphProc --
  129. #
  130. #    Command that processes all graph object commands.
  131. #
  132. # Arguments:
  133. #    name    name of the graph object to manipulate.
  134. #    args    command name and args for the command
  135. #
  136. # Results:
  137. #    Varies based on command to perform
  138.  
  139. proc ::struct::graph::GraphProc {name {cmd ""} args} {
  140.     # Do minimal args checks here
  141.     if { [llength [info level 0]] == 2 } {
  142.     error "wrong # args: should be \"$name option ?arg arg ...?\""
  143.     }
  144.     
  145.     # Split the args into command and args components
  146.     if { [llength [info commands ::struct::graph::_$cmd]] == 0 } {
  147.     variable commands
  148.     set optlist [join $commands ", "]
  149.     set optlist [linsert $optlist "end-1" "or"]
  150.     error "bad option \"$cmd\": must be $optlist"
  151.     }
  152.     eval [list ::struct::graph::_$cmd $name] $args
  153. }
  154.  
  155. # ::struct::graph::_arc --
  156. #
  157. #    Dispatches the invocation of arc methods to the proper handler
  158. #    procedure.
  159. #
  160. # Arguments:
  161. #    name    name of the graph.
  162. #    cmd    arc command to invoke
  163. #    args    arguments to propagate to the handler for the arc command
  164. #
  165. # Results:
  166. #    As of the invoked handler.
  167.  
  168. proc ::struct::graph::_arc {name cmd args} {
  169.  
  170.     # Split the args into command and args components
  171.     if { [llength [info commands ::struct::graph::__arc_$cmd]] == 0 } {
  172.     variable arcCommands
  173.     set optlist [join $arcCommands ", "]
  174.     set optlist [linsert $optlist "end-1" "or"]
  175.     error "bad option \"$cmd\": must be $optlist"
  176.     }
  177.  
  178.     eval [list ::struct::graph::__arc_$cmd $name] $args
  179. }
  180.  
  181. # ::struct::graph::__arc_delete --
  182. #
  183. #    Remove an arc from a graph, including all of its values.
  184. #
  185. # Arguments:
  186. #    name    name of the graph.
  187. #    args    list of arcs to delete.
  188. #
  189. # Results:
  190. #    None.
  191.  
  192. proc ::struct::graph::__arc_delete {name args} {
  193.  
  194.     foreach arc $args {
  195.     if { ![__arc_exists $name $arc] } {
  196.         error "arc \"$arc\" does not exist in graph \"$name\""
  197.     }
  198.     }
  199.  
  200.     upvar ::struct::graph::graph${name}::inArcs   inArcs
  201.     upvar ::struct::graph::graph${name}::outArcs  outArcs
  202.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  203.  
  204.     foreach arc $args {
  205.     foreach {source target} $arcNodes($arc) break ; # lassign
  206.  
  207.     unset arcNodes($arc)
  208.     # FRINK: nocheck
  209.     unset ::struct::graph::graph${name}::arc$arc
  210.  
  211.     # Remove arc from the arc lists of source and target nodes.
  212.  
  213.     set index            [lsearch -exact $outArcs($source) $arc]
  214.     set outArcs($source) [lreplace       $outArcs($source) $index $index]
  215.  
  216.     set index            [lsearch -exact $inArcs($target)  $arc]
  217.     set inArcs($target)  [lreplace       $inArcs($target)  $index $index]
  218.     }
  219.  
  220.     return
  221. }
  222.  
  223. # ::struct::graph::__arc_exists --
  224. #
  225. #    Test for existance of a given arc in a graph.
  226. #
  227. # Arguments:
  228. #    name    name of the graph.
  229. #    arc    arc to look for.
  230. #
  231. # Results:
  232. #    1 if the arc exists, 0 else.
  233.  
  234. proc ::struct::graph::__arc_exists {name arc} {
  235.     return [info exists ::struct::graph::graph${name}::arcNodes($arc)]
  236. }
  237.  
  238. # ::struct::graph::__arc_get --
  239. #
  240. #    Get a keyed value from an arc in a graph.
  241. #
  242. # Arguments:
  243. #    name    name of the graph.
  244. #    arc    arc to query.
  245. #    flag    -key; anything else is an error
  246. #    key    key to lookup; defaults to data
  247. #
  248. # Results:
  249. #    value    value associated with the key given.
  250.  
  251. proc ::struct::graph::__arc_get {name arc {flag -key} {key data}} {
  252.     if { ![__arc_exists $name $arc] } {
  253.     error "arc \"$arc\" does not exist in graph \"$name\""
  254.     }
  255.     
  256.     upvar ::struct::graph::graph${name}::arc${arc} data
  257.  
  258.     if { ![info exists data($key)] } {
  259.     error "invalid key \"$key\" for arc \"$arc\""
  260.     }
  261.  
  262.     return $data($key)
  263. }
  264.  
  265. # ::struct::graph::__arc_insert --
  266. #
  267. #    Add an arc to a graph.
  268. #
  269. # Arguments:
  270. #    name        name of the graph.
  271. #    source        source node of the new arc
  272. #    target        target node of the new arc
  273. #    args        arc to insert; must be unique.  If none is given,
  274. #            the routine will generate a unique node name.
  275. #
  276. # Results:
  277. #    arc        The name of the new arc.
  278.  
  279. proc ::struct::graph::__arc_insert {name source target args} {
  280.  
  281.     if { [llength $args] == 0 } {
  282.     # No arc name was given; generate a unique one
  283.     set arc [__generateUniqueArcName $name]
  284.     } else {
  285.     set arc [lindex $args 0]
  286.     }
  287.  
  288.     if { [__arc_exists $name $arc] } {
  289.     error "arc \"$arc\" already exists in graph \"$name\""
  290.     }
  291.     
  292.     if { ![__node_exists $name $source] } {
  293.     error "source node \"$source\" does not exist in graph \"$name\""
  294.     }
  295.     
  296.     if { ![__node_exists $name $target] } {
  297.     error "target node \"$target\" does not exist in graph \"$name\""
  298.     }
  299.     
  300.     upvar ::struct::graph::graph${name}::inArcs    inArcs
  301.     upvar ::struct::graph::graph${name}::outArcs   outArcs
  302.     upvar ::struct::graph::graph${name}::arcNodes  arcNodes
  303.     upvar ::struct::graph::graph${name}::arc${arc} data
  304.  
  305.     # Set up the new arc
  306.     set data(data)       ""
  307.     set arcNodes($arc) [list $source $target]
  308.  
  309.     # Add this arc to the arc lists of its source resp. target nodes.
  310.     lappend outArcs($source) $arc
  311.     lappend inArcs($target)  $arc
  312.  
  313.     return $arc
  314. }
  315.  
  316. # ::struct::graph::__arc_set --
  317. #
  318. #    Set or get a value for an arc in a graph.
  319. #
  320. # Arguments:
  321. #    name    name of the graph.
  322. #    arc    arc to modify or query.
  323. #    args    ?-key key? ?value?
  324. #
  325. # Results:
  326. #    val    value associated with the given key of the given arc
  327.  
  328. proc ::struct::graph::__arc_set {name arc args} {
  329.     if { ![__arc_exists $name $arc] } {
  330.     error "arc \"$arc\" does not exist in graph \"$name\""
  331.     }
  332.  
  333.     upvar ::struct::graph::graph${name}::arc$arc data
  334.  
  335.     if { [llength $args] > 3 } {
  336.     error "wrong # args: should be \"$name arc set $arc ?-key key?\
  337.         ?value?\""
  338.     }
  339.     
  340.     set key "data"
  341.     set haveValue 0
  342.     if { [llength $args] > 1 } {
  343.     foreach {flag key} $args break
  344.     if { ![string match "${flag}*" "-key"] } {
  345.         error "invalid option \"$flag\": should be key"
  346.     }
  347.     if { [llength $args] == 3 } {
  348.         set haveValue 1
  349.         set value [lindex $args end]
  350.     }
  351.     } elseif { [llength $args] == 1 } {
  352.     set haveValue 1
  353.     set value [lindex $args end]
  354.     }
  355.  
  356.     if { $haveValue } {
  357.     # Setting a value
  358.     return [set data($key) $value]
  359.     } else {
  360.     # Getting a value
  361.     if { ![info exists data($key)] } {
  362.         error "invalid key \"$key\" for arc \"$arc\""
  363.     }
  364.     return $data($key)
  365.     }
  366. }
  367.  
  368. # ::struct::graph::__arc_source --
  369. #
  370. #    Return the node at the beginning of the specified arc.
  371. #
  372. # Arguments:
  373. #    name    name of the graph object.
  374. #    arc    arc to look up.
  375. #
  376. # Results:
  377. #    node    name of the node.
  378.  
  379. proc ::struct::graph::__arc_source {name arc} {
  380.     if { ![__arc_exists $name $arc] } {
  381.     error "arc \"$arc\" does not exist in graph \"$name\""
  382.     }
  383.  
  384.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  385.     return [lindex $arcNodes($arc) 0]
  386. }
  387.  
  388. # ::struct::graph::__arc_target --
  389. #
  390. #    Return the node at the end of the specified arc.
  391. #
  392. # Arguments:
  393. #    name    name of the graph object.
  394. #    arc    arc to look up.
  395. #
  396. # Results:
  397. #    node    name of the node.
  398.  
  399. proc ::struct::graph::__arc_target {name arc} {
  400.     if { ![__arc_exists $name $arc] } {
  401.     error "arc \"$arc\" does not exist in graph \"$name\""
  402.     }
  403.  
  404.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  405.     return [lindex $arcNodes($arc) 1]
  406. }
  407.  
  408. # ::struct::graph::__arc_unset --
  409. #
  410. #    Remove a keyed value from a arc.
  411. #
  412. # Arguments:
  413. #    name    name of the graph.
  414. #    arc    arc to modify.
  415. #    args    additional args: ?-key key?
  416. #
  417. # Results:
  418. #    None.
  419.  
  420. proc ::struct::graph::__arc_unset {name arc {flag -key} {key data}} {
  421.     if { ![__arc_exists $name $arc] } {
  422.     error "arc \"$arc\" does not exist in graph \"$name\""
  423.     }
  424.     
  425.     if { ![string match "${flag}*" "-key"] } {
  426.     error "invalid option \"$flag\": should be \"$name unset\
  427.         $arc ?-key key?\""
  428.     }
  429.  
  430.     upvar ::struct::graph::graph${name}::arc${arc} data
  431.     if { [info exists data($key)] } {
  432.     unset data($key)
  433.     }
  434.     return
  435. }
  436.  
  437. # ::struct::graph::_arcs --
  438. #
  439. #    Return a list of all arcs in a graph satisfying some
  440. #    node based restriction.
  441. #
  442. # Arguments:
  443. #    name    name of the graph.
  444. #
  445. # Results:
  446. #    arcs    list of arcs
  447.  
  448. proc ::struct::graph::_arcs {name args} {
  449.  
  450.     if {[llength $args] == 0} {
  451.     # No restriction, deliver all.
  452.  
  453.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  454.     return [array names arcNodes]
  455.     }
  456.  
  457.     # Get mode and node list
  458.  
  459.     set cond [lindex $args 0]
  460.     set args [lrange $args 1 end]
  461.  
  462.     # Validate that the cond is good.
  463.     switch -glob -- $cond {
  464.     "-in" {
  465.         set cond "in"
  466.     }
  467.     "-out" {
  468.         set cond "out"
  469.     }
  470.     "-adj" {
  471.         set cond "adj"
  472.     }
  473.     "-inner" {
  474.         set cond "inner"
  475.     }
  476.     "-embedding" {
  477.         set cond "embedding"
  478.     }
  479.     default {
  480.         error "invalid restriction \"$cond\": should be -in, -out,\
  481.             -adj, -inner or -embedding"
  482.     }
  483.     }
  484.  
  485.     # Validate that there are nodes to use in the restriction.
  486.     # otherwise what's the point?
  487.     if {[llength $args] == 0} {
  488.     set usage "$name arcs ?-in|-out|-adj|-inner|-embedding node node...?"
  489.     error "no nodes specified: should be \"$usage\""
  490.     }
  491.  
  492.     # Make sure that the specified nodes exist!
  493.     foreach node $args {
  494.     if { ![__node_exists $name $node] } {
  495.         error "node \"$node\" does not exist in graph \"$name\""
  496.     }
  497.     }
  498.  
  499.     # Now we are able to go to work
  500.     upvar ::struct::graph::graph${name}::inArcs   inArcs
  501.     upvar ::struct::graph::graph${name}::outArcs  outArcs
  502.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  503.  
  504.     set       arcs [list]
  505.     array set coll  {}
  506.  
  507.     switch -exact -- $cond {
  508.     in {
  509.         # Result is all arcs going to at least one node
  510.         # in the list of arguments.
  511.  
  512.         foreach node $args {
  513.         foreach e $inArcs($node) {
  514.             if {[info exists coll($e)]} {continue}
  515.             lappend arcs    $e
  516.             set     coll($e) .
  517.         }
  518.         }
  519.     }
  520.     out {
  521.         # Result is all arcs coming from at least one node
  522.         # in the list of arguments.
  523.  
  524.         foreach node $args {
  525.         foreach e $outArcs($node) {
  526.             if {[info exists coll($e)]} {continue}
  527.             lappend arcs    $e
  528.             set     coll($e) .
  529.         }
  530.         }
  531.     }
  532.     adj {
  533.         # Result is all arcs coming from or going to at
  534.         # least one node in the list of arguments.
  535.  
  536.         foreach node $args {
  537.         foreach e $inArcs($node) {
  538.             if {[info exists coll($e)]} {continue}
  539.             lappend arcs    $e
  540.             set     coll($e) .
  541.         }
  542.         foreach e $outArcs($node) {
  543.             if {[info exists coll($e)]} {continue}
  544.             lappend arcs    $e
  545.             set     coll($e) .
  546.         }
  547.         }
  548.     }
  549.     inner {
  550.         # Result is all arcs running between nodes in the list.
  551.  
  552.         array set group {}
  553.         foreach node $args {
  554.         set group($node) .
  555.         }
  556.  
  557.         foreach node $args {
  558.         foreach e $inArcs($node) {
  559.             set n [lindex $arcNodes($e) 0]
  560.             if {![info exists group($n)]} {continue}
  561.             if { [info exists coll($e)]}  {continue}
  562.             lappend arcs    $e
  563.             set     coll($e) .
  564.         }
  565.         foreach e $outArcs($node) {
  566.             set n [lindex $arcNodes($e) 1]
  567.             if {![info exists group($n)]} {continue}
  568.             if { [info exists coll($e)]}  {continue}
  569.             lappend arcs    $e
  570.             set     coll($e) .
  571.         }
  572.         }
  573.     }
  574.     embedding {
  575.         # Result is all arcs from -adj minus the arcs from -inner.
  576.         # IOW all arcs goint from a node in the list to a node
  577.         # which is *not* in the list
  578.  
  579.         array set group {}
  580.         foreach node $args {
  581.         set group($node) .
  582.         }
  583.  
  584.         foreach node $args {
  585.         foreach e $inArcs($node) {
  586.             set n [lindex $arcNodes($e) 0]
  587.             if {[info exists group($n)]} {continue}
  588.             if {[info exists coll($e)]}  {continue}
  589.             lappend arcs    $e
  590.             set     coll($e) .
  591.         }
  592.         foreach e $outArcs($node) {
  593.             set n [lindex $arcNodes($e) 1]
  594.             if {[info exists group($n)]} {continue}
  595.             if {[info exists coll($e)]}  {continue}
  596.             lappend arcs    $e
  597.             set     coll($e) .
  598.         }
  599.         }
  600.     }
  601.     default {error "Can't happen, panic"}
  602.     }
  603.  
  604.     return $arcs
  605. }
  606.  
  607. # ::struct::graph::_destroy --
  608. #
  609. #    Destroy a graph, including its associated command and data storage.
  610. #
  611. # Arguments:
  612. #    name    name of the graph.
  613. #
  614. # Results:
  615. #    None.
  616.  
  617. proc ::struct::graph::_destroy {name} {
  618.     namespace delete ::struct::graph::graph$name
  619.     interp alias {} ::$name {}
  620. }
  621.  
  622. # ::struct::graph::__generateUniqueArcName --
  623. #
  624. #    Generate a unique arc name for the given graph.
  625. #
  626. # Arguments:
  627. #    name    name of the graph.
  628. #
  629. # Results:
  630. #    arc    name of a arc guaranteed to not exist in the graph.
  631.  
  632. proc ::struct::graph::__generateUniqueArcName {name} {
  633.     upvar ::struct::graph::graph${name}::nextUnusedArc nextUnusedArc
  634.     while {[__arc_exists $name "arc${nextUnusedArc}"]} {
  635.     incr nextUnusedArc
  636.     }
  637.     return "arc${nextUnusedArc}"
  638. }
  639.  
  640. # ::struct::graph::__generateUniqueNodeName --
  641. #
  642. #    Generate a unique node name for the given graph.
  643. #
  644. # Arguments:
  645. #    name    name of the graph.
  646. #
  647. # Results:
  648. #    node    name of a node guaranteed to not exist in the graph.
  649.  
  650. proc ::struct::graph::__generateUniqueNodeName {name} {
  651.     upvar ::struct::graph::graph${name}::nextUnusedNode nextUnusedNode
  652.     while {[__node_exists $name "node${nextUnusedNode}"]} {
  653.     incr nextUnusedNode
  654.     }
  655.     return "node${nextUnusedNode}"
  656. }
  657.  
  658. # ::struct::graph::_node --
  659. #
  660. #    Dispatches the invocation of node methods to the proper handler
  661. #    procedure.
  662. #
  663. # Arguments:
  664. #    name    name of the graph.
  665. #    cmd    node command to invoke
  666. #    args    arguments to propagate to the handler for the node command
  667. #
  668. # Results:
  669. #    As of the the invoked handler.
  670.  
  671. proc ::struct::graph::_node {name cmd args} {
  672.  
  673.     # Split the args into command and args components
  674.     if { [llength [info commands ::struct::graph::__node_$cmd]] == 0 } {
  675.     variable nodeCommands
  676.     set optlist [join $nodeCommands ", "]
  677.     set optlist [linsert $optlist "end-1" "or"]
  678.     error "bad option \"$cmd\": must be $optlist"
  679.     }
  680.  
  681.     eval [list ::struct::graph::__node_$cmd $name] $args
  682. }
  683.  
  684. # ::struct::graph::__node_degree --
  685. #
  686. #    Return the number of arcs adjacent to the specified node.
  687. #    If one of the restrictions -in or -out is given only
  688. #    incoming resp. outgoing arcs are counted.
  689. #
  690. # Arguments:
  691. #    name    name of the graph.
  692. #    args    option, followed by the node.
  693. #
  694. # Results:
  695. #    None.
  696.  
  697. proc ::struct::graph::__node_degree {name args} {
  698.  
  699.     if {([llength $args] < 1) || ([llength $args] > 2)} {
  700.     error "wrong # args: should be \"$name node degree ?-in|-out| node\""
  701.     }
  702.  
  703.     switch -exact -- [llength $args] {
  704.     1 {
  705.         set opt {}
  706.         set node [lindex $args 0]
  707.     }
  708.     2 {
  709.         set opt  [lindex $args 0]
  710.         set node [lindex $args 1]
  711.     }
  712.     default {
  713.         error "Wrong # arguments given to 'degree'"
  714.     }
  715.     }
  716.  
  717.     # Validate the option.
  718.  
  719.     switch -exact -- $opt {
  720.     {}   -
  721.     -in  -
  722.     -out {}
  723.     default {
  724.         error "invalid option \"$opt\": should be -in or -out"
  725.     }
  726.     }
  727.  
  728.     # Validate the node
  729.  
  730.     if { ![__node_exists $name $node] } {
  731.     error "node \"$node\" does not exist in graph \"$name\""
  732.     }
  733.  
  734.     upvar ::struct::graph::graph${name}::inArcs   inArcs
  735.     upvar ::struct::graph::graph${name}::outArcs  outArcs
  736.  
  737.     switch -exact -- $opt {
  738.     -in  {
  739.         set result [llength $inArcs($node)]
  740.     }
  741.     -out {
  742.         set result [llength $outArcs($node)]
  743.     }
  744.     {} {
  745.         set result [expr {[llength $inArcs($node)] \
  746.             + [llength $outArcs($node)]}]
  747.  
  748.         # loops count twice, don't do <set> arithmetics, i.e. no union!
  749.         if {0} {
  750.         array set coll  {}
  751.         set result [llength $inArcs($node)]
  752.  
  753.         foreach e $inArcs($node) {
  754.             set coll($e) .
  755.         }
  756.         foreach e $outArcs($node) {
  757.             if {[info exists coll($e)]} {continue}
  758.             incr result
  759.             set     coll($e) .
  760.         }
  761.         }
  762.     }
  763.     default {error "Can't happen, panic"}
  764.     }
  765.  
  766.     return $result
  767. }
  768.  
  769. # ::struct::graph::__node_delete --
  770. #
  771. #    Remove a node from a graph, including all of its values.
  772. #    Additionally removes the arcs connected to this node.
  773. #
  774. # Arguments:
  775. #    name    name of the graph.
  776. #    args    list of the nodes to delete.
  777. #
  778. # Results:
  779. #    None.
  780.  
  781. proc ::struct::graph::__node_delete {name args} {
  782.  
  783.     foreach node $args {
  784.     if { ![__node_exists $name $node] } {
  785.         error "node \"$node\" does not exist in graph \"$name\""
  786.     }
  787.     }
  788.  
  789.     upvar ::struct::graph::graph${name}::inArcs  inArcs
  790.     upvar ::struct::graph::graph${name}::outArcs outArcs
  791.  
  792.     foreach node $args {
  793.     # Remove all the arcs connected to this node
  794.     foreach e $inArcs($node) {
  795.         __arc_delete $name $e
  796.     }
  797.     foreach e $outArcs($node) {
  798.         # Check existence to avoid problems with
  799.         # loops (they are in and out arcs! at
  800.         # the same time and thus already deleted)
  801.         if { [__arc_exists $name $e] } {
  802.         __arc_delete $name $e
  803.         }
  804.     }
  805.  
  806.     unset inArcs($node)
  807.     unset outArcs($node)
  808.     # FRINK: nocheck
  809.     unset ::struct::graph::graph${name}::node$node
  810.     }
  811.  
  812.     return
  813. }
  814.  
  815. # ::struct::graph::__node_exists --
  816. #
  817. #    Test for existance of a given node in a graph.
  818. #
  819. # Arguments:
  820. #    name    name of the graph.
  821. #    node    node to look for.
  822. #
  823. # Results:
  824. #    1 if the node exists, 0 else.
  825.  
  826. proc ::struct::graph::__node_exists {name node} {
  827.     return [info exists ::struct::graph::graph${name}::inArcs($node)]
  828. }
  829.  
  830. # ::struct::graph::__node_get --
  831. #
  832. #    Get a keyed value from a node in a graph.
  833. #
  834. # Arguments:
  835. #    name    name of the graph.
  836. #    node    node to query.
  837. #    flag    -key; anything else is an error
  838. #    key    key to lookup; defaults to data
  839. #
  840. # Results:
  841. #    value    value associated with the key given.
  842.  
  843. proc ::struct::graph::__node_get {name node {flag -key} {key data}} {
  844.     if { ![__node_exists $name $node] } {
  845.     error "node \"$node\" does not exist in graph \"$name\""
  846.     }
  847.     
  848.     upvar ::struct::graph::graph${name}::node${node} data
  849.  
  850.     if { ![info exists data($key)] } {
  851.     error "invalid key \"$key\" for node \"$node\""
  852.     }
  853.  
  854.     return $data($key)
  855. }
  856.  
  857. # ::struct::graph::__node_insert --
  858. #
  859. #    Add a node to a graph.
  860. #
  861. # Arguments:
  862. #    name        name of the graph.
  863. #    args        node to insert; must be unique.  If none is given,
  864. #            the routine will generate a unique node name.
  865. #
  866. # Results:
  867. #    node        The namee of the new node.
  868.  
  869. proc ::struct::graph::__node_insert {name args} {
  870.  
  871.     if { [llength $args] == 0 } {
  872.     # No node name was given; generate a unique one
  873.     set node [__generateUniqueNodeName $name]
  874.     } else {
  875.     set node [lindex $args 0]
  876.     }
  877.  
  878.     if { [__node_exists $name $node] } {
  879.     error "node \"$node\" already exists in graph \"$name\""
  880.     }
  881.     
  882.     upvar ::struct::graph::graph${name}::inArcs      inArcs
  883.     upvar ::struct::graph::graph${name}::outArcs     outArcs
  884.     upvar ::struct::graph::graph${name}::node${node} data
  885.  
  886.     # Set up the new node
  887.     set inArcs($node)  [list]
  888.     set outArcs($node) [list]
  889.     set data(data) ""
  890.  
  891.     return $node
  892. }
  893.  
  894. # ::struct::graph::__node_opposite --
  895. #
  896. #    Retrieve node opposite to the specified one, along the arc.
  897. #
  898. # Arguments:
  899. #    name        name of the graph.
  900. #    node        node to look up.
  901. #    arc        arc to look up.
  902. #
  903. # Results:
  904. #    nodex    Node opposite to <node,arc>
  905.  
  906. proc ::struct::graph::__node_opposite {name node arc} {
  907.     if {![__node_exists $name $node] } {
  908.     error "node \"$node\" does not exist in graph \"$name\""
  909.     }
  910.     
  911.     if {![__arc_exists $name $arc] } {
  912.     error "arc \"$arc\" does not exist in graph \"$name\""
  913.     }
  914.  
  915.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  916.  
  917.     # Node must be connected to at least one end of the arc.
  918.  
  919.     if {[string equal $node [lindex $arcNodes($arc) 0]]} {
  920.     set result [lindex $arcNodes($arc) 1]
  921.     } elseif {[string equal $node [lindex $arcNodes($arc) 1]]} {
  922.     set result [lindex $arcNodes($arc) 0]
  923.     } else {
  924.     error "node \"$node\" and arc \"$arc\" are not connected\
  925.         in graph \"$name\""
  926.     }
  927.  
  928.     return $result
  929. }
  930.  
  931. # ::struct::graph::__node_set --
  932. #
  933. #    Set or get a value for a node in a graph.
  934. #
  935. # Arguments:
  936. #    name    name of the graph.
  937. #    node    node to modify or query.
  938. #    args    ?-key key? ?value?
  939. #
  940. # Results:
  941. #    val    value associated with the given key of the given node
  942.  
  943. proc ::struct::graph::__node_set {name node args} {
  944.     if { ![__node_exists $name $node] } {
  945.     error "node \"$node\" does not exist in graph \"$name\""
  946.     }
  947.     upvar ::struct::graph::graph${name}::node$node data
  948.  
  949.     if { [llength $args] > 3 } {
  950.     error "wrong # args: should be \"$name node set $node ?-key key?\
  951.         ?value?\""
  952.     }
  953.     
  954.     set key "data"
  955.     set haveValue 0
  956.     if { [llength $args] > 1 } {
  957.     foreach {flag key} $args break
  958.     if { ![string match "${flag}*" "-key"] } {
  959.         error "invalid option \"$flag\": should be key"
  960.     }
  961.     if { [llength $args] == 3 } {
  962.         set haveValue 1
  963.         set value [lindex $args end]
  964.     }
  965.     } elseif { [llength $args] == 1 } {
  966.     set haveValue 1
  967.     set value [lindex $args end]
  968.     }
  969.  
  970.     if { $haveValue } {
  971.     # Setting a value
  972.     return [set data($key) $value]
  973.     } else {
  974.     # Getting a value
  975.     if { ![info exists data($key)] } {
  976.         error "invalid key \"$key\" for node \"$node\""
  977.     }
  978.     return $data($key)
  979.     }
  980. }
  981.  
  982. # ::struct::graph::__node_unset --
  983. #
  984. #    Remove a keyed value from a node.
  985. #
  986. # Arguments:
  987. #    name    name of the graph.
  988. #    node    node to modify.
  989. #    args    additional args: ?-key key?
  990. #
  991. # Results:
  992. #    None.
  993.  
  994. proc ::struct::graph::__node_unset {name node {flag -key} {key data}} {
  995.     if { ![__node_exists $name $node] } {
  996.     error "node \"$node\" does not exist in graph \"$name\""
  997.     }
  998.     
  999.     if { ![string match "${flag}*" "-key"] } {
  1000.     error "invalid option \"$flag\": should be \"$name unset\
  1001.         $node ?-key key?\""
  1002.     }
  1003.  
  1004.     upvar ::struct::graph::graph${name}::node${node} data
  1005.     if { [info exists data($key)] } {
  1006.     unset data($key)
  1007.     }
  1008.     return
  1009. }
  1010.  
  1011. # ::struct::graph::_nodes --
  1012. #
  1013. #    Return a list of all nodes in a graph satisfying some restriction.
  1014. #
  1015. # Arguments:
  1016. #    name    name of the graph.
  1017. #    args    list of options and nodes specifying the restriction.
  1018. #
  1019. # Results:
  1020. #    nodes    list of nodes
  1021.  
  1022. proc ::struct::graph::_nodes {name args} {
  1023.  
  1024.     if {[llength $args] == 0} {
  1025.     # No restriction, deliver all.
  1026.  
  1027.     upvar ::struct::graph::graph${name}::inArcs inArcs
  1028.     return [array names inArcs]
  1029.     }
  1030.  
  1031.     # Get mode and node list
  1032.  
  1033.     set cond [lindex $args 0]
  1034.     set args [lrange $args 1 end]
  1035.  
  1036.     # Validate that the cond is good.
  1037.     switch -glob -- $cond {
  1038.     "-in" {
  1039.         set cond "in"
  1040.     }
  1041.     "-out" {
  1042.         set cond "out"
  1043.     }
  1044.     "-adj" {
  1045.         set cond "adj"
  1046.     }
  1047.     "-inner" {
  1048.         set cond "inner"
  1049.     }
  1050.     "-embedding" {
  1051.         set cond "embedding"
  1052.     }
  1053.     default {
  1054.         error "invalid restriction \"$cond\": should be -in, -out,\
  1055.             -adj, -inner or -embedding"
  1056.     }
  1057.     }
  1058.  
  1059.     # Validate that there are nodes to use in the restriction.
  1060.     # otherwise what's the point?
  1061.     if {[llength $args] == 0} {
  1062.     set usage "$name nodes ?-in|-out|-adj|-inner|-embedding node node...?"
  1063.     error "no nodes specified: should be \"$usage\""
  1064.     }
  1065.  
  1066.     # Make sure that the specified nodes exist!
  1067.     foreach node $args {
  1068.     if { ![__node_exists $name $node] } {
  1069.         error "node \"$node\" does not exist in graph \"$name\""
  1070.     }
  1071.     }
  1072.  
  1073.     # Now we are able to go to work
  1074.     upvar ::struct::graph::graph${name}::inArcs   inArcs
  1075.     upvar ::struct::graph::graph${name}::outArcs  outArcs
  1076.     upvar ::struct::graph::graph${name}::arcNodes arcNodes
  1077.  
  1078.     set       nodes [list]
  1079.     array set coll  {}
  1080.  
  1081.     switch -exact -- $cond {
  1082.     in {
  1083.         # Result is all nodes with at least one arc going to
  1084.         # at least one node in the list of arguments.
  1085.  
  1086.         foreach node $args {
  1087.         foreach e $inArcs($node) {
  1088.             set n [lindex $arcNodes($e) 0]
  1089.             if {[info exists coll($n)]} {continue}
  1090.             lappend nodes    $n
  1091.             set     coll($n) .
  1092.         }
  1093.         }
  1094.     }
  1095.     out {
  1096.         # Result is all nodes with at least one arc coming from
  1097.         # at least one node in the list of arguments.
  1098.  
  1099.         foreach node $args {
  1100.         foreach e $outArcs($node) {
  1101.             set n [lindex $arcNodes($e) 1]
  1102.             if {[info exists coll($n)]} {continue}
  1103.             lappend nodes    $n
  1104.             set     coll($n) .
  1105.         }
  1106.         }
  1107.     }
  1108.     adj {
  1109.         # Result is all nodes with at least one arc coming from
  1110.         # or going to at least one node in the list of arguments.
  1111.  
  1112.         foreach node $args {
  1113.         foreach e $inArcs($node) {
  1114.             set n [lindex $arcNodes($e) 0]
  1115.             if {[info exists coll($n)]} {continue}
  1116.             lappend nodes    $n
  1117.             set     coll($n) .
  1118.         }
  1119.         foreach e $outArcs($node) {
  1120.             set n [lindex $arcNodes($e) 1]
  1121.             if {[info exists coll($n)]} {continue}
  1122.             lappend nodes    $n
  1123.             set     coll($n) .
  1124.         }
  1125.         }
  1126.     }
  1127.     inner {
  1128.         # Result is all nodes from the list! with at least one arc
  1129.         # coming from or going to at least one node in the list of
  1130.         # arguments.
  1131.  
  1132.         array set group {}
  1133.         foreach node $args {
  1134.         set group($node) .
  1135.         }
  1136.  
  1137.         foreach node $args {
  1138.         foreach e $inArcs($node) {
  1139.             set n [lindex $arcNodes($e) 0]
  1140.             if {![info exists group($n)]} {continue}
  1141.             if { [info exists coll($n)]}  {continue}
  1142.             lappend nodes    $n
  1143.             set     coll($n) .
  1144.         }
  1145.         foreach e $outArcs($node) {
  1146.             set n [lindex $arcNodes($e) 1]
  1147.             if {![info exists group($n)]} {continue}
  1148.             if { [info exists coll($n)]}  {continue}
  1149.             lappend nodes    $n
  1150.             set     coll($n) .
  1151.         }
  1152.         }
  1153.     }
  1154.     embedding {
  1155.         # Result is all nodes with at least one arc coming from
  1156.         # or going to at least one node in the list of arguments,
  1157.         # but not in the list itself!
  1158.  
  1159.         array set group {}
  1160.         foreach node $args {
  1161.         set group($node) .
  1162.         }
  1163.  
  1164.         foreach node $args {
  1165.         foreach e $inArcs($node) {
  1166.             set n [lindex $arcNodes($e) 0]
  1167.             if {[info exists group($n)]} {continue}
  1168.             if {[info exists coll($n)]}  {continue}
  1169.             lappend nodes    $n
  1170.             set     coll($n) .
  1171.         }
  1172.         foreach e $outArcs($node) {
  1173.             set n [lindex $arcNodes($e) 1]
  1174.             if {[info exists group($n)]} {continue}
  1175.             if {[info exists coll($n)]}  {continue}
  1176.             lappend nodes    $n
  1177.             set     coll($n) .
  1178.         }
  1179.         }
  1180.     }
  1181.     default {error "Can't happen, panic"}
  1182.     }
  1183.  
  1184.     return $nodes
  1185. }
  1186.  
  1187. # ::struct::graph::_swap --
  1188. #
  1189. #    Swap two nodes in a graph.
  1190. #
  1191. # Arguments:
  1192. #    name    name of the graph.
  1193. #    node1    first node to swap.
  1194. #    node2    second node to swap.
  1195. #
  1196. # Results:
  1197. #    None.
  1198.  
  1199. proc ::struct::graph::_swap {name node1 node2} {
  1200.     # Can only swap two real nodes
  1201.     if { ![__node_exists $name $node1] } {
  1202.     error "node \"$node1\" does not exist in graph \"$name\""
  1203.     }
  1204.     if { ![__node_exists $name $node2] } {
  1205.     error "node \"$node2\" does not exist in graph \"$name\""
  1206.     }
  1207.  
  1208.     # Can't swap a node with itself
  1209.     if { [string equal $node1 $node2] } {
  1210.     error "cannot swap node \"$node1\" with itself"
  1211.     }
  1212.  
  1213.     # Swapping nodes means swapping their labels, values and arcs
  1214.     upvar ::struct::graph::graph${name}::outArcs      outArcs
  1215.     upvar ::struct::graph::graph${name}::inArcs       inArcs
  1216.     upvar ::struct::graph::graph${name}::arcNodes     arcNodes
  1217.     upvar ::struct::graph::graph${name}::node${node1} node1Vals
  1218.     upvar ::struct::graph::graph${name}::node${node2} node2Vals
  1219.  
  1220.     # Redirect arcs to the new nodes.
  1221.  
  1222.     foreach e $inArcs($node1) {
  1223.     set arcNodes($e) [lreplace $arcNodes($e) end end $node2]
  1224.     }
  1225.     foreach e $inArcs($node2) {
  1226.     set arcNodes($e) [lreplace $arcNodes($e) end end $node1]
  1227.     }
  1228.     foreach e $outArcs($node1) {
  1229.     set arcNodes($e) [lreplace $arcNodes($e) 0 0 $node2]
  1230.     }
  1231.     foreach e $outArcs($node2) {
  1232.     set arcNodes($e) [lreplace $arcNodes($e) 0 0 $node1]
  1233.     }
  1234.  
  1235.     # Swap arc lists
  1236.  
  1237.     set tmp            $inArcs($node1)
  1238.     set inArcs($node1) $inArcs($node2)
  1239.     set inArcs($node2) $tmp
  1240.  
  1241.     set tmp             $outArcs($node1)
  1242.     set outArcs($node1) $outArcs($node2)
  1243.     set outArcs($node2) $tmp
  1244.  
  1245.     # Swap the values
  1246.     set   value1        [array get node1Vals]
  1247.     unset node1Vals
  1248.     array set node1Vals [array get node2Vals]
  1249.     unset node2Vals
  1250.     array set node2Vals $value1
  1251.  
  1252.     return
  1253. }
  1254.  
  1255. # ::struct::graph::_walk --
  1256. #
  1257. #    Walk a graph using a pre-order depth or breadth first
  1258. #    search. Pre-order DFS is the default.  At each node that is visited,
  1259. #    a command will be called with the name of the graph and the node.
  1260. #
  1261. # Arguments:
  1262. #    name    name of the graph.
  1263. #    node    node at which to start.
  1264. #    args    additional args: ?-order pre|post|both? ?-type {bfs|dfs}?
  1265. #        -command cmd
  1266. #
  1267. # Results:
  1268. #    None.
  1269.  
  1270. proc ::struct::graph::_walk {name node args} {
  1271.     set usage "$name walk $node ?-dir forward|backward?\
  1272.         ?-order pre|post|both? ?-type {bfs|dfs}? -command cmd"
  1273.  
  1274.     if {[llength $args] > 8 || [llength $args] < 2} {
  1275.     error "wrong # args: should be \"$usage\""
  1276.     }
  1277.  
  1278.     if { ![__node_exists $name $node] } {
  1279.     error "node \"$node\" does not exist in graph \"$name\""
  1280.     }
  1281.  
  1282.     # Set defaults
  1283.     set type  dfs
  1284.     set order pre
  1285.     set cmd   ""
  1286.     set dir   forward
  1287.  
  1288.     # Process specified options
  1289.     for {set i 0} {$i < [llength $args]} {incr i} {
  1290.     set flag [lindex $args $i]
  1291.     incr i
  1292.     if { $i >= [llength $args] } {
  1293.         error "value for \"$flag\" missing: should be \"$usage\""
  1294.     }
  1295.     switch -glob -- $flag {
  1296.         "-type" {
  1297.         set type [string tolower [lindex $args $i]]
  1298.         }
  1299.         "-order" {
  1300.         set order [string tolower [lindex $args $i]]
  1301.         }
  1302.         "-command" {
  1303.         set cmd [lindex $args $i]
  1304.         }
  1305.         "-dir" {
  1306.         set dir [string tolower [lindex $args $i]]
  1307.         }
  1308.         default {
  1309.         error "unknown option \"$flag\": should be \"$usage\""
  1310.         }
  1311.     }
  1312.     }
  1313.     
  1314.     # Make sure we have a command to run, otherwise what's the point?
  1315.     if { [string equal $cmd ""] } {
  1316.     error "no command specified: should be \"$usage\""
  1317.     }
  1318.  
  1319.     # Validate that the given type is good
  1320.     switch -glob -- $type {
  1321.     "dfs" {
  1322.         set type "dfs"
  1323.     }
  1324.     "bfs" {
  1325.         set type "bfs"
  1326.     }
  1327.     default {
  1328.         error "invalid search type \"$type\": should be dfs, or bfs"
  1329.     }
  1330.     }
  1331.     
  1332.     # Validate that the given order is good
  1333.     switch -glob -- $order {
  1334.     "both" {
  1335.         set order both
  1336.     }
  1337.     "pre" {
  1338.         set order pre
  1339.     }
  1340.     "post" {
  1341.         set order post
  1342.     }
  1343.     default {
  1344.         error "invalid search order \"$order\": should be both,\
  1345.             pre or post"
  1346.     }
  1347.     }
  1348.  
  1349.     # Validate that the given direction is good
  1350.     switch -glob -- $dir {
  1351.     "forward" {
  1352.         set dir -out
  1353.     }
  1354.     "backward" {
  1355.         set dir -in
  1356.     }
  1357.     default {
  1358.         error "invalid search direction \"$dir\": should be\
  1359.             forward or backward"
  1360.     }
  1361.     }
  1362.  
  1363.     # Do the walk
  1364.  
  1365.     set st [list ]
  1366.     lappend st $node
  1367.     array set visited {}
  1368.  
  1369.     if { [string equal $type "dfs"] } {
  1370.     if { [string equal $order "pre"] } {
  1371.         # Pre-order Depth-first search
  1372.  
  1373.         while { [llength $st] > 0 } {
  1374.         set node [lindex   $st end]
  1375.         set st   [lreplace $st end end]
  1376.  
  1377.         # Evaluate the command at this node
  1378.         set cmdcpy $cmd
  1379.         lappend cmdcpy enter $name $node
  1380.         uplevel 2 $cmdcpy
  1381.  
  1382.         set visited($node) .
  1383.  
  1384.         # Add this node's neighbours (according to direction)
  1385.         #  Have to add them in reverse order
  1386.         #  so that they will be popped left-to-right
  1387.  
  1388.         set next [_nodes $name $dir $node]
  1389.         set len  [llength $next]
  1390.  
  1391.         for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
  1392.             set nextnode [lindex $next $i]
  1393.             if {[info exists visited($nextnode)]} {
  1394.             # Skip nodes already visited
  1395.             continue
  1396.             }
  1397.             lappend st $nextnode
  1398.         }
  1399.         }
  1400.     } elseif { [string equal $order "post"] } {
  1401.         # Post-order Depth-first search
  1402.  
  1403.         while { [llength $st] > 0 } {
  1404.         set node [lindex $st end]
  1405.  
  1406.         if {[info exists visited($node)]} {
  1407.             # Second time we are here, pop it,
  1408.             # then evaluate the command.
  1409.  
  1410.             set st [lreplace $st end end]
  1411.  
  1412.             # Evaluate the command at this node
  1413.             set cmdcpy $cmd
  1414.             lappend cmdcpy leave $name $node
  1415.             uplevel 2 $cmdcpy
  1416.         } else {
  1417.             # First visit. Remember it.
  1418.             set visited($node) .
  1419.         
  1420.             # Add this node's neighbours.
  1421.             set next [_nodes $name $dir $node]
  1422.             set len  [llength $next]
  1423.  
  1424.             for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
  1425.             set nextnode [lindex $next $i]
  1426.             if {[info exists visited($nextnode)]} {
  1427.                 # Skip nodes already visited
  1428.                 continue
  1429.             }
  1430.             lappend st $nextnode
  1431.             }
  1432.         }
  1433.         }
  1434.     } else {
  1435.         # Both-order Depth-first search
  1436.  
  1437.         while { [llength $st] > 0 } {
  1438.         set node [lindex $st end]
  1439.  
  1440.         if {[info exists visited($node)]} {
  1441.             # Second time we are here, pop it,
  1442.             # then evaluate the command.
  1443.  
  1444.             set st [lreplace $st end end]
  1445.  
  1446.             # Evaluate the command at this node
  1447.             set cmdcpy $cmd
  1448.             lappend cmdcpy leave $name $node
  1449.             uplevel 2 $cmdcpy
  1450.         } else {
  1451.             # First visit. Remember it.
  1452.             set visited($node) .
  1453.  
  1454.             # Evaluate the command at this node
  1455.             set cmdcpy $cmd
  1456.             lappend cmdcpy enter $name $node
  1457.             uplevel 2 $cmdcpy
  1458.         
  1459.             # Add this node's neighbours.
  1460.             set next [_nodes $name $dir $node]
  1461.             set len  [llength $next]
  1462.  
  1463.             for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
  1464.             set nextnode [lindex $next $i]
  1465.             if {[info exists visited($nextnode)]} {
  1466.                 # Skip nodes already visited
  1467.                 continue
  1468.             }
  1469.             lappend st $nextnode
  1470.             }
  1471.         }
  1472.         }
  1473.     }
  1474.  
  1475.     } else {
  1476.     if { [string equal $order "pre"] } {
  1477.         # Pre-order Breadth first search
  1478.         while { [llength $st] > 0 } {
  1479.         set node [lindex $st 0]
  1480.         set st   [lreplace $st 0 0]
  1481.         # Evaluate the command at this node
  1482.         set cmdcpy $cmd
  1483.         lappend cmdcpy enter $name $node
  1484.         uplevel 2 $cmdcpy
  1485.         
  1486.         set visited($node) .
  1487.  
  1488.         # Add this node's neighbours.
  1489.         foreach child [_nodes $name $dir $node] {
  1490.             if {[info exists visited($child)]} {
  1491.             # Skip nodes already visited
  1492.             continue
  1493.             }
  1494.             lappend st $child
  1495.         }
  1496.         }
  1497.     } else {
  1498.         # Post-order Breadth first search
  1499.         # Both-order Breadth first search
  1500.         # Haven't found anything in Knuth
  1501.         # and unable to define something
  1502.         # consistent for myself. Leave it
  1503.         # out.
  1504.  
  1505.         error "unable to do a ${order}-order breadth first walk"
  1506.     }
  1507.     }
  1508.     return
  1509. }
  1510.  
  1511. # ::struct::graph::Union --
  1512. #
  1513. #    Return a list which is the union of the elements
  1514. #    in the specified lists.
  1515. #
  1516. # Arguments:
  1517. #    args    list of lists representing sets.
  1518. #
  1519. # Results:
  1520. #    set    list representing the union of the argument lists.
  1521.  
  1522. proc ::struct::graph::Union {args} {
  1523.     switch -- [llength $args] {
  1524.     0 {
  1525.         return {}
  1526.     }
  1527.     1 {
  1528.         return [lindex $args 0]
  1529.     }
  1530.     default {
  1531.         foreach set $args {
  1532.         foreach e $set {
  1533.             set tmp($e) .
  1534.         }
  1535.         }
  1536.         return [array names tmp]
  1537.     }
  1538.     }
  1539. }
  1540.