00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 namespace ::struct {}
00017 namespace ::struct::graph {}
00018
00019
00020
00021
00022 if {![catch {package require cgraph 0.6}]} {
00023
00024 return
00025 }
00026
00027 namespace ::struct {}
00028 namespace ::struct::graph {
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 variable counter 0
00049
00050
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
00100 namespace export graph
00101 }
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
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
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
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
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
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
00217
00218
00219
00220
00221
00222
00223
00224
00225
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
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 ret ::struct::graph::__arc_exists (type name , type arc) {
00270 return [info exists ::struct::graph::graph${name}::arcNodes($arc)]
00271 }
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
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
00301
00302
00303
00304
00305
00306
00307
00308
00309
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
00326
00327
00328
00329
00330
00331
00332
00333
00334
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
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
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
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
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
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
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
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
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
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
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
00557
00558
00559
00560
00561
00562
00563
00564
00565
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
00577
00578
00579
00580
00581
00582
00583
00584
00585
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
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
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
00626
00627
00628
00629
00630
00631
00632
00633
00634
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
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871 ret ::struct::graph::_destroy (type name) {
00872 namespace delete ::struct::graph::graph$name
00873 interp alias {} ::$name {}
00874 }
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
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
00895
00896
00897
00898
00899
00900
00901
00902
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
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
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
00935
00936
00937
00938
00939
00940
00941
00942
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
00954
00955
00956
00957
00958
00959
00960
00961
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
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
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
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
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
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
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
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
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
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159 ret ::struct::graph::__node_exists (type name , type node) {
01160 return [info exists ::struct::graph::graph${name}::inArcs($node)]
01161 }
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
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
01191
01192
01193
01194
01195
01196
01197
01198
01199
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
01216
01217
01218
01219
01220
01221
01222
01223
01224
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
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
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
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
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
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314
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
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
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
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
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
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
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
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
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
01496
01497
01498
01499
01500
01501
01502
01503
01504
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
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
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
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
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
01835
01836
01837
01838
01839
01840
01841
01842
01843
01844
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
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
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
02118
02119
02120
02121
02122
02123
02124
02125
02126
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
02149
02150 namespace ::struct {
02151
02152 namespace import -force graph::graph
02153 namespace export graph
02154 }
02155 package provide struct::graph 1.2.1
02156