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