00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 package require Tcl 8.2
00016
00017 namespace ::struct {}
00018
00019 namespace ::struct::matrix {
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 variable counter 0
00041
00042
00043 namespace export matrix
00044 }
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 ret ::struct::matrix::matrix (optional name ="") {
00058 variable counter
00059
00060 if { [llength [info level 0]] == 1 } {
00061 incr counter
00062 set name "matrix${counter}"
00063 }
00064
00065 # FIRST, qualify the name.
00066 if {![string match "::*" $name]} {
00067 # Get caller's namespace; append :: if not global namespace.
00068 set ns [uplevel 1 namespace current]
00069 if {"::" != $ns} {
00070 append ns "::"
00071 }
00072 set name "$ns$name"
00073 }
00074
00075 if { [llength [info commands $name]] } {
00076 return -code error "command \"$name\" already exists, unable to create matrix"
00077 }
00078
00079 # Set up the namespace
00080 namespace eval $name {
00081 variable columns 0
00082 variable rows 0
00083
00084 variable data
00085 variable colw
00086 variable rowh
00087 variable link
00088 variable lock
00089 variable unset
00090
00091 array set data {}
00092 array set colw {}
00093 array set rowh {}
00094 array set link {}
00095 set lock 0
00096 set unset {}
00097 }
00098
00099 # Create the command to manipulate the matrix
00100 interp alias {} $name {} ::struct::matrix::MatrixProc $name
00101
00102 return $name
00103 }
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120 ret ::struct::matrix::MatrixProc (type name , optional cmd ="" , type args) {
00121 # Do minimal args checks here
00122 if { [llength [info level 0]] == 2 } {
00123 return -code error "wrong # args: should be \"$name option ?arg arg ...?\""
00124 }
00125
00126 # Split the args into command and args components
00127 set sub _$cmd
00128 if {[llength [info commands ::struct::matrix::$sub]] == 0} {
00129 set optlist [lsort [info commands ::struct::matrix::_*]]
00130 set xlist {}
00131 foreach p $optlist {
00132 set p [namespace tail $p]
00133 if {[string match __* $p]} {continue}
00134 lappend xlist [string range $p 1 end]
00135 }
00136 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00137 return -code error \
00138 "bad option \"$cmd\": must be $optlist"
00139 }
00140 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00141 }
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155 ret ::struct::matrix::_add (type name , optional cmd ="" , type args) {
00156 # Do minimal args checks here
00157 if { [llength [info level 0]] == 2 } {
00158 return -code error "wrong # args: should be \"$name add option ?arg arg ...?\""
00159 }
00160
00161 # Split the args into command and args components
00162 set sub __add_$cmd
00163 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00164 set optlist [lsort [info commands ::struct::matrix::__add_*]]
00165 set xlist {}
00166 foreach p $optlist {
00167 set p [namespace tail $p]
00168 lappend xlist [string range $p 6 end]
00169 }
00170 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00171 return -code error \
00172 "bad option \"$cmd\": must be $optlist"
00173 }
00174 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00175 }
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189 ret ::struct::matrix::_delete (type name , optional cmd ="" , type args) {
00190 # Do minimal args checks here
00191 if { [llength [info level 0]] == 2 } {
00192 return -code error "wrong # args: should be \"$name delete option ?arg arg ...?\""
00193 }
00194
00195 # Split the args into command and args components
00196 set sub __delete_$cmd
00197 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00198 set optlist [lsort [info commands ::struct::matrix::__delete_*]]
00199 set xlist {}
00200 foreach p $optlist {
00201 set p [namespace tail $p]
00202 lappend xlist [string range $p 9 end]
00203 }
00204 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00205 return -code error \
00206 "bad option \"$cmd\": must be $optlist"
00207 }
00208 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00209 }
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 ret ::struct::matrix::_format (type name , optional cmd ="" , type args) {
00224 # Do minimal args checks here
00225 if { [llength [info level 0]] == 2 } {
00226 return -code error "wrong # args: should be \"$name format option ?arg arg ...?\""
00227 }
00228
00229 # Split the args into command and args components
00230 set sub __format_$cmd
00231 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00232 set optlist [lsort [info commands ::struct::matrix::__format_*]]
00233 set xlist {}
00234 foreach p $optlist {
00235 set p [namespace tail $p]
00236 lappend xlist [string range $p 9 end]
00237 }
00238 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00239 return -code error \
00240 "bad option \"$cmd\": must be $optlist"
00241 }
00242 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257 ret ::struct::matrix::_get (type name , optional cmd ="" , type args) {
00258 # Do minimal args checks here
00259 if { [llength [info level 0]] == 2 } {
00260 return -code error "wrong # args: should be \"$name get option ?arg arg ...?\""
00261 }
00262
00263 # Split the args into command and args components
00264 set sub __get_$cmd
00265 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00266 set optlist [lsort [info commands ::struct::matrix::__get_*]]
00267 set xlist {}
00268 foreach p $optlist {
00269 set p [namespace tail $p]
00270 lappend xlist [string range $p 6 end]
00271 }
00272 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00273 return -code error \
00274 "bad option \"$cmd\": must be $optlist"
00275 }
00276 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 ret ::struct::matrix::_insert (type name , optional cmd ="" , type args) {
00292 # Do minimal args checks here
00293 if { [llength [info level 0]] == 2 } {
00294 return -code error "wrong # args: should be \"$name insert option ?arg arg ...?\""
00295 }
00296
00297 # Split the args into command and args components
00298 set sub __insert_$cmd
00299 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00300 set optlist [lsort [info commands ::struct::matrix::__insert_*]]
00301 set xlist {}
00302 foreach p $optlist {
00303 set p [namespace tail $p]
00304 lappend xlist [string range $p 9 end]
00305 }
00306 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00307 return -code error \
00308 "bad option \"$cmd\": must be $optlist"
00309 }
00310 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324 ret ::struct::matrix::_search (type name , type args) {
00325 set mode exact
00326 set nocase 0
00327
00328 while {1} {
00329 switch -glob -- [lindex $args 0] {
00330 -exact - -glob - -regexp {
00331 set mode [string range [lindex $args 0] 1 end]
00332 set args [lrange $args 1 end]
00333 }
00334 -nocase {
00335 set nocase 1
00336 }
00337 -* {
00338 return -code error \
00339 "invalid option \"[lindex $args 0]\":\
00340 should be -nocase, -exact, -glob, or -regexp"
00341 }
00342 default {
00343 break
00344 }
00345 }
00346 }
00347
00348 # Possible argument signatures after option processing
00349 #
00350 # \ | args
00351 # --+--------------------------------------------------------
00352 # 2 | all pattern
00353 # 3 | row row pattern, column col pattern
00354 # 6 | rect ctl rtl cbr rbr pattern
00355 #
00356 # All range specifications are internally converted into a
00357 # rectangle.
00358
00359 switch -exact -- [llength $args] {
00360 2 - 3 - 6 {}
00361 default {
00362 return -code error \
00363 "wrong # args: should be\
00364 \"$name search ?option...? (all|row row|column col|rect c r c r) pattern\""
00365 }
00366 }
00367
00368 set range [lindex $args 0]
00369 set pattern [lindex $args end]
00370 set args [lrange $args 1 end-1]
00371
00372 variable ${name}::data
00373 variable ${name}::columns
00374 variable ${name}::rows
00375
00376 switch -exact -- $range {
00377 all {
00378 set ctl 0 ; set cbr $columns ; incr cbr -1
00379 set rtl 0 ; set rbr $rows ; incr rbr -1
00380 }
00381 column {
00382 set ctl [ChkColumnIndex $name [lindex $args 0]]
00383 set cbr $ctl
00384 set rtl 0 ; set rbr $rows ; incr rbr -1
00385 }
00386 row {
00387 set rtl [ChkRowIndex $name [lindex $args 0]]
00388 set ctl 0 ; set cbr $columns ; incr cbr -1
00389 set rbr $rtl
00390 }
00391 rect {
00392 foreach {ctl rtl cbr rbr} $args break
00393 set ctl [ChkColumnIndex $name $ctl]
00394 set rtl [ChkRowIndex $name $rtl]
00395 set cbr [ChkColumnIndex $name $cbr]
00396 set rbr [ChkRowIndex $name $rbr]
00397 if {($ctl > $cbr) || ($rtl > $rbr)} {
00398 return -code error "Invalid cell indices, wrong ordering"
00399 }
00400 }
00401 default {
00402 return -code error "invalid range spec \"$range\": should be all, column, row, or rect"
00403 }
00404 }
00405
00406 if {$nocase} {
00407 set pattern [string tolower $pattern]
00408 }
00409
00410 set matches [list]
00411 for {set r $rtl} {$r <= $rbr} {incr r} {
00412 for {set c $ctl} {$c <= $cbr} {incr c} {
00413 set v $data($c,$r)
00414 if {$nocase} {
00415 set v [string tolower $v]
00416 }
00417 switch -exact -- $mode {
00418 exact {set matched [string equal $pattern $v]}
00419 glob {set matched [string match $pattern $v]}
00420 regexp {set matched [regexp -- $pattern $v]}
00421 }
00422 if {$matched} {
00423 lappend matches [list $c $r]
00424 }
00425 }
00426 }
00427 return $matches
00428 }
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442 ret ::struct::matrix::_set (type name , optional cmd ="" , type args) {
00443 # Do minimal args checks here
00444 if { [llength [info level 0]] == 2 } {
00445 return -code error "wrong # args: should be \"$name set option ?arg arg ...?\""
00446 }
00447
00448 # Split the args into command and args components
00449 set sub __set_$cmd
00450 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00451 set optlist [lsort [info commands ::struct::matrix::__set_*]]
00452 set xlist {}
00453 foreach p $optlist {
00454 set p [namespace tail $p]
00455 lappend xlist [string range $p 6 end]
00456 }
00457 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00458 return -code error \
00459 "bad option \"$cmd\": must be $optlist"
00460 }
00461 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00462 }
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 ret ::struct::matrix::_sort (type name , type cmd , type args) {
00477 # Do minimal args checks here
00478 if { [llength [info level 0]] == 2 } {
00479 return -code error "wrong # args: should be \"$name sort option ?arg arg ...?\""
00480 }
00481 if {[string equal $cmd "rows"]} {
00482 set code r
00483 set byrows 1
00484 } elseif {[string equal $cmd "columns"]} {
00485 set code c
00486 set byrows 0
00487 } else {
00488 return -code error \
00489 "bad option \"$cmd\": must be columns, or rows"
00490 }
00491
00492 set revers 0 ;# Default: -increasing
00493 while {1} {
00494 switch -glob -- [lindex $args 0] {
00495 -increasing {set revers 0}
00496 -decreasing {set revers 1}
00497 default {
00498 if {[llength $args] > 1} {
00499 return -code error \
00500 "invalid option \"[lindex $args 0]\":\
00501 should be -increasing, or -decreasing"
00502 }
00503 break
00504 }
00505 }
00506 set args [lrange $args 1 end]
00507 }
00508 # ASSERT: [llength $args] == 1
00509
00510 if {[llength $args] != 1} {
00511 return -code error "wrong # args: should be \"$name sort option ?arg arg ...?\""
00512 }
00513
00514 set key [lindex $args 0]
00515
00516 if {$byrows} {
00517 set key [ChkColumnIndex $name $key]
00518 variable ${name}::rows
00519
00520 # Adapted by EAS from BUILD-MAX-HEAP(A) of CRLS 6.3
00521 set heapSize $rows
00522 } else {
00523 set key [ChkRowIndex $name $key]
00524 variable ${name}::columns
00525
00526 # Adapted by EAS from BUILD-MAX-HEAP(A) of CRLS 6.3
00527 set heapSize $columns
00528 }
00529
00530 for {set i [expr {int($heapSize/2)-1}]} {$i>=0} {incr i -1} {
00531 SortMaxHeapify $name $i $key $code $heapSize $revers
00532 }
00533
00534 # Adapted by EAS from remainder of HEAPSORT(A) of CRLS 6.4
00535 for {set i [expr {$heapSize-1}]} {$i>=1} {incr i -1} {
00536 if {$byrows} {
00537 SwapRows $name 0 $i
00538 } else {
00539 SwapColumns $name 0 $i
00540 }
00541 incr heapSize -1
00542 SortMaxHeapify $name 0 $key $code $heapSize $revers
00543 }
00544 return
00545 }
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559 ret ::struct::matrix::_swap (type name , optional cmd ="" , type args) {
00560 # Do minimal args checks here
00561 if { [llength [info level 0]] == 2 } {
00562 return -code error "wrong # args: should be \"$name swap option ?arg arg ...?\""
00563 }
00564
00565 # Split the args into command and args components
00566 set sub __swap_$cmd
00567 if { [llength [info commands ::struct::matrix::$sub]] == 0 } {
00568 set optlist [lsort [info commands ::struct::matrix::__swap_*]]
00569 set xlist {}
00570 foreach p $optlist {
00571 set p [namespace tail $p]
00572 lappend xlist [string range $p 7 end]
00573 }
00574 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00575 return -code error \
00576 "bad option \"$cmd\": must be $optlist"
00577 }
00578 uplevel 1 [linsert $args 0 ::struct::matrix::$sub $name]
00579 }
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596 ret ::struct::matrix::__add_column (type name , optional values ={)} {
00597 variable ${name}::data
00598 variable ${name}::columns
00599 variable ${name}::rows
00600 variable ${name}::rowh
00601
00602 if {[ l = [llength $values]] < $rows} {
00603
00604
00605 for {} {$l < $rows} {incr l} {
00606 lappend values {}
00607 }
00608 } elseif {[llength $values] > $rows} {
00609
00610 values = [lrange $values 0 [expr {$rows - 1}]]
00611 }
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621 r = 0
00622 foreach v $values {
00623 if {$v != {}} {
00624
00625 catch {un rowh = ($r)}
00626 } ;
00627 data = ($columns,$r) $v
00628 incr r
00629 }
00630 incr columns
00631 return
00632 }
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649 ret ::struct::matrix::__add_row (type name , optional values ={)} {
00650 variable ${name}::data
00651 variable ${name}::columns
00652 variable ${name}::rows
00653 variable ${name}::colw
00654
00655 if {[ l = [llength $values]] < $columns} {
00656
00657
00658 for {} {$l < $columns} {incr l} {
00659 lappend values {}
00660 }
00661 } elseif {[llength $values] > $columns} {
00662
00663 values = [lrange $values 0 [expr {$columns - 1}]]
00664 }
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674 c = 0
00675 foreach v $values {
00676 if {$v != {}} {
00677
00678 catch {un colw = ($c)}
00679 } ;
00680 data = ($c,$rows) $v
00681 incr c
00682 }
00683 incr rows
00684 return
00685 }
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701 ret ::struct::matrix::__add_columns (type name , type n) {
00702 if {$n <= 0} {
00703 return -code error "A value of n <= 0 is not allowed"
00704 }
00705
00706 variable ${name}::data
00707 variable ${name}::columns
00708 variable ${name}::rows
00709
00710 # The new values set into the cell is always the empty
00711 # string. These have a length and height of 0, i.e. the don't
00712 # influence cached widths and heights as they are at least that
00713 # big. IOW there is no need to touch and change the width and
00714 # height caches.
00715
00716 while {$n > 0} {
00717 for {set r 0} {$r < $rows} {incr r} {
00718 set data($columns,$r) ""
00719 }
00720 incr columns
00721 incr n -1
00722 }
00723
00724 return
00725 }
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741 ret ::struct::matrix::__add_rows (type name , type n) {
00742 if {$n <= 0} {
00743 return -code error "A value of n <= 0 is not allowed"
00744 }
00745
00746 variable ${name}::data
00747 variable ${name}::columns
00748 variable ${name}::rows
00749
00750 # The new values set into the cell is always the empty
00751 # string. These have a length and height of 0, i.e. the don't
00752 # influence cached widths and heights as they are at least that
00753 # big. IOW there is no need to touch and change the width and
00754 # height caches.
00755
00756 while {$n > 0} {
00757 for {set c 0} {$c < $columns} {incr c} {
00758 set data($c,$rows) ""
00759 }
00760 incr rows
00761 incr n -1
00762 }
00763 return
00764 }
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777 ret ::struct::matrix::_cells (type name) {
00778 variable ${name}::rows
00779 variable ${name}::columns
00780 return [expr {$rows * $columns}]
00781 }
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796 ret ::struct::matrix::_cellsize (type name , type column , type row) {
00797 set column [ChkColumnIndex $name $column]
00798 set row [ChkRowIndex $name $row]
00799
00800 variable ${name}::data
00801 return [string length $data($column,$row)]
00802 }
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815 ret ::struct::matrix::_columns (type name) {
00816 variable ${name}::columns
00817 return $columns
00818 }
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835 ret ::struct::matrix::_columnwidth (type name , type column) {
00836 set column [ChkColumnIndex $name $column]
00837
00838 variable ${name}::colw
00839
00840 if {![info exists colw($column)]} {
00841 variable ${name}::rows
00842 variable ${name}::data
00843
00844 set width 0
00845 for {set r 0} {$r < $rows} {incr r} {
00846 foreach line [split $data($column,$r) \n] {
00847 set len [string length $line]
00848 if {$len > $width} {
00849 set width $len
00850 }
00851 }
00852 }
00853
00854 set colw($column) $width
00855 }
00856
00857 return $colw($column)
00858 }
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872 ret ::struct::matrix::__delete_column (type name , type column) {
00873 set column [ChkColumnIndex $name $column]
00874
00875 variable ${name}::data
00876 variable ${name}::rows
00877 variable ${name}::columns
00878 variable ${name}::colw
00879 variable ${name}::rowh
00880
00881 # Move all data from the higher columns down and then delete the
00882 # superfluous data in the old last column. Move the data in the
00883 # width cache too, take partial fill into account there too.
00884 # Invalidate the height cache for all rows.
00885
00886 for {set r 0} {$r < $rows} {incr r} {
00887 for {set c $column; set cn [expr {$c + 1}]} {$cn < $columns} {incr c ; incr cn} {
00888 set data($c,$r) $data($cn,$r)
00889 if {[info exists colw($cn)]} {
00890 set colw($c) $colw($cn)
00891 unset colw($cn)
00892 }
00893 }
00894 unset data($c,$r)
00895 catch {unset rowh($r)}
00896 }
00897 incr columns -1
00898 return
00899 }
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913 ret ::struct::matrix::__delete_row (type name , type row) {
00914 set row [ChkRowIndex $name $row]
00915
00916 variable ${name}::data
00917 variable ${name}::rows
00918 variable ${name}::columns
00919 variable ${name}::colw
00920 variable ${name}::rowh
00921
00922 # Move all data from the higher rows down and then delete the
00923 # superfluous data in the old last row. Move the data in the
00924 # height cache too, take partial fill into account there too.
00925 # Invalidate the width cache for all columns.
00926
00927 for {set c 0} {$c < $columns} {incr c} {
00928 for {set r $row; set rn [expr {$r + 1}]} {$rn < $rows} {incr r ; incr rn} {
00929 set data($c,$r) $data($c,$rn)
00930 if {[info exists rowh($rn)]} {
00931 set rowh($r) $rowh($rn)
00932 unset rowh($rn)
00933 }
00934 }
00935 unset data($c,$r)
00936 catch {unset colw($c)}
00937 }
00938 incr rows -1
00939 return
00940 }
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952 ret ::struct::matrix::_destroy (type name) {
00953 variable ${name}::link
00954
00955 # Unlink all existing arrays before destroying the object so that
00956 # we don't leave dangling references / traces.
00957
00958 foreach avar [array names link] {
00959 _unlink $name $avar
00960 }
00961
00962 namespace delete $name
00963 interp alias {} $name {}
00964 }
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979 ret ::struct::matrix::__format_2string (type name , optional report ={)} {
00980 if {$report == {}} {
00981
00982
00983
00984
00985
00986 array cw = {}
00987 cols = [_columns $name]
00988 for { c = 0} {$c < $cols} {incr c} {
00989 cw = ($c) [_columnwidth $name $c]
00990 }
00991
00992 result = [list]
00993 n = [_rows $name]
00994 for { r = 0} {$r < $n} {incr r} {
00995 rh = [_rowheight $name $r]
00996 if {$rh < 2} {
00997
00998 line = [list]
00999 for { c = 0} {$c < $cols} {incr c} {
01000 val = [__get_cell $name $c $r]
01001 lappend line "$val[string repeat " " [expr {$cw($c)-[string length $val]}]]"
01002 }
01003 lappend result [join $line " "]
01004 } else {
01005
01006 for { h = 0} {$h < $rh} {incr h} {
01007 line = [list]
01008 for { c = 0} {$c < $cols} {incr c} {
01009 val = [lindex [split [__get_cell $name $c $r] \n] $h]
01010 lappend line "$val[string repeat " " [expr {$cw($c)-[string length $val]}]]"
01011 }
01012 lappend result [join $line " "]
01013 }
01014 }
01015 }
01016 return [join $result \n]
01017 } else {
01018 return [$report printmatrix $name]
01019 }
01020 }
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037 ret ::struct::matrix::__format_2chan (type name , optional report ={) {chan stdout}} {
01038 if {$report == {}} {
01039
01040
01041 puts -nonewline [__format_2string $name]
01042 } else {
01043 $report printmatrix2channel $name $chan
01044 }
01045 return
01046 }
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061 ret ::struct::matrix::__get_cell (type name , type column , type row) {
01062 set column [ChkColumnIndex $name $column]
01063 set row [ChkRowIndex $name $row]
01064
01065 variable ${name}::data
01066 return $data($column,$row)
01067 }
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082 ret ::struct::matrix::__get_column (type name , type column) {
01083 set column [ChkColumnIndex $name $column]
01084 return [GetColumn $name $column]
01085 }
01086
01087 ret ::struct::matrix::GetColumn (type name , type column) {
01088 variable ${name}::data
01089 variable ${name}::rows
01090
01091 set result [list]
01092 for {set r 0} {$r < $rows} {incr r} {
01093 lappend result $data($column,$r)
01094 }
01095 return $result
01096 }
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122 ret ::struct::matrix::__get_rect (type name , type column_, type tl , type row_, type tl , type column_, type br , type row_, type br) {
01123 set column_tl [ChkColumnIndex $name $column_tl]
01124 set row_tl [ChkRowIndex $name $row_tl]
01125 set column_br [ChkColumnIndex $name $column_br]
01126 set row_br [ChkRowIndex $name $row_br]
01127
01128 if {
01129 ($column_tl > $column_br) ||
01130 ($row_tl > $row_br)
01131 } {
01132 return -code error "Invalid cell indices, wrong ordering"
01133 }
01134
01135 variable ${name}::data
01136 set result [list]
01137
01138 for {set r $row_tl} {$r <= $row_br} {incr r} {
01139 set row [list]
01140 for {set c $column_tl} {$c <= $column_br} {incr c} {
01141 lappend row $data($c,$r)
01142 }
01143 lappend result $row
01144 }
01145
01146 return $result
01147 }
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162 ret ::struct::matrix::__get_row (type name , type row) {
01163 set row [ChkRowIndex $name $row]
01164 return [GetRow $name $row]
01165 }
01166
01167 ret ::struct::matrix::GetRow (type name , type row) {
01168 variable ${name}::data
01169 variable ${name}::columns
01170
01171 set result [list]
01172 for {set c 0} {$c < $columns} {incr c} {
01173 lappend result $data($c,$row)
01174 }
01175 return $result
01176 }
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201 ret ::struct::matrix::__insert_column (type name , type column , optional values ={)} {
01202 # Allow both negative and too big indices.
01203 set column [ChkColumnIndexAll $name $column]
01204
01205 variable ${name}::columns
01206
01207 if {$column > $columns} {
01208
01209 __add_column $name $values
01210 return
01211 }
01212
01213 variable ${name}::data
01214 variable ${name}::rows
01215 variable ${name}::rowh
01216 variable ${name}::colw
01217
01218 firstcol = $column
01219 if {$firstcol < 0} {
01220 firstcol = 0
01221 }
01222
01223 if {[ l = [llength $values]] < $rows} {
01224
01225
01226 for {} {$l < $rows} {incr l} {
01227 lappend values {}
01228 }
01229 } elseif {[llength $values] > $rows} {
01230
01231 values = [lrange $values 0 [expr {$rows - 1}]]
01232 }
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243 for { r = 0} {$r < $rows} {incr r} {
01244 for { cn = $columns ; c = [expr {$cn - 1}]} {$c >= $firstcol} {incr c -1 ; incr cn -1} {
01245 data = ($cn,$r) $data($c,$r)
01246 if {[info exists colw($c)]} {
01247 colw = ($cn) $colw($c)
01248 un colw = ($c)
01249 }
01250 }
01251 data = ($firstcol,$r) [lindex $values $r]
01252 catch {un rowh = ($r)}
01253 }
01254 incr columns
01255 return
01256 }
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280 ret ::struct::matrix::__insert_row (type name , type row , optional values ={)} {
01281 # Allow both negative and too big indices.
01282 set row [ChkRowIndexAll $name $row]
01283
01284 variable ${name}::rows
01285
01286 if {$row > $rows} {
01287
01288 __add_row $name $values
01289 return
01290 }
01291
01292 variable ${name}::data
01293 variable ${name}::columns
01294 variable ${name}::rowh
01295 variable ${name}::colw
01296
01297 firstrow = $row
01298 if {$firstrow < 0} {
01299 firstrow = 0
01300 }
01301
01302 if {[ l = [llength $values]] < $columns} {
01303
01304
01305 for {} {$l < $columns} {incr l} {
01306 lappend values {}
01307 }
01308 } elseif {[llength $values] > $columns} {
01309
01310 values = [lrange $values 0 [expr {$columns - 1}]]
01311 }
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322 for { c = 0} {$c < $columns} {incr c} {
01323 for { rn = $rows ; r = [expr {$rn - 1}]} {$r >= $firstrow} {incr r -1 ; incr rn -1} {
01324 data = ($c,$rn) $data($c,$r)
01325 if {[info exists rowh($r)]} {
01326 rowh = ($rn) $rowh($r)
01327 un rowh = ($r)
01328 }
01329 }
01330 data = ($c,$firstrow) [lindex $values $c]
01331 catch {un colw = ($c)}
01332 }
01333 incr rows
01334 return
01335 }
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357 ret ::struct::matrix::_link (type name , type args) {
01358 switch -exact -- [llength $args] {
01359 0 {
01360 return -code error "$name: wrong # args: link ?-transpose? arrayvariable"
01361 }
01362 1 {
01363 set transpose 0
01364 set variable [lindex $args 0]
01365 }
01366 2 {
01367 foreach {t variable} $args break
01368 if {[string compare $t -transpose]} {
01369 return -code error "$name: illegal syntax: link ?-transpose? arrayvariable"
01370 }
01371 set transpose 1
01372 }
01373 default {
01374 return -code error "$name: wrong # args: link ?-transpose? arrayvariable"
01375 }
01376 }
01377
01378 variable ${name}::link
01379
01380 if {[info exists link($variable)]} {
01381 return -code error "$name link: Variable \"$variable\" already linked to matrix"
01382 }
01383
01384 # Ok, a new variable we are linked to. Record this information,
01385 # dump our current contents into the array, at last generate the
01386 # traces actually performing the link.
01387
01388 set link($variable) $transpose
01389
01390 upvar #0 $variable array
01391 variable ${name}::data
01392
01393 foreach key [array names data] {
01394 foreach {c r} [split $key ,] break
01395 if {$transpose} {
01396 set array($r,$c) $data($key)
01397 } else {
01398 set array($c,$r) $data($key)
01399 }
01400 }
01401
01402 trace variable array wu [list ::struct::matrix::MatTraceIn $variable $name]
01403 trace variable data w [list ::struct::matrix::MatTraceOut $variable $name]
01404 return
01405 }
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418 ret ::struct::matrix::_links (type name) {
01419 variable ${name}::link
01420 return [array names link]
01421 }
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436 ret ::struct::matrix::_rowheight (type name , type row) {
01437 set row [ChkRowIndex $name $row]
01438
01439 variable ${name}::rowh
01440
01441 if {![info exists rowh($row)]} {
01442 variable ${name}::columns
01443 variable ${name}::data
01444
01445 set height 1
01446 for {set c 0} {$c < $columns} {incr c} {
01447 set cheight [llength [split $data($c,$row) \n]]
01448 if {$cheight > $height} {
01449 set height $cheight
01450 }
01451 }
01452
01453 set rowh($row) $height
01454 }
01455 return $rowh($row)
01456 }
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468 ret ::struct::matrix::_rows (type name) {
01469 variable ${name}::rows
01470 return $rows
01471 }
01472
01473
01474
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485
01486
01487 ret ::struct::matrix::__set_cell (type name , type column , type row , type value) {
01488 set column [ChkColumnIndex $name $column]
01489 set row [ChkRowIndex $name $row]
01490
01491 variable ${name}::data
01492
01493 if {![string compare $value $data($column,$row)]} {
01494 # No change, ignore call!
01495 return
01496 }
01497
01498 set data($column,$row) $value
01499
01500 if {$value != {}} {
01501 variable ${name}::colw
01502 variable ${name}::rowh
01503 catch {unset colw($column)}
01504 catch {unset rowh($row)}
01505 }
01506 return
01507 }
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525
01526
01527
01528 ret ::struct::matrix::__set_column (type name , type column , type values) {
01529 set column [ChkColumnIndex $name $column]
01530
01531 variable ${name}::data
01532 variable ${name}::columns
01533 variable ${name}::rows
01534 variable ${name}::rowh
01535 variable ${name}::colw
01536
01537 if {[set l [llength $values]] < $rows} {
01538 # Missing values. Fill up with empty strings
01539
01540 for {} {$l < $rows} {incr l} {
01541 lappend values {}
01542 }
01543 } elseif {[llength $values] > $rows} {
01544 # To many values. Remove the superfluous items
01545 set values [lrange $values 0 [expr {$rows - 1}]]
01546 }
01547
01548 # "values" now contains the information to set into the array.
01549 # Regarding the width and height caches:
01550
01551 # - Invalidate the column in the width cache.
01552 # - The rows are either removed from the height cache or left
01553 # unchanged, depending on the contents set into the cell.
01554
01555 set r 0
01556 foreach v $values {
01557 if {$v != {}} {
01558 # Data changed unpredictably, invalidate cache
01559 catch {unset rowh($r)}
01560 } ; # {else leave the row unchanged}
01561 set data($column,$r) $v
01562 incr r
01563 }
01564 catch {unset colw($column)}
01565 return
01566 }
01567
01568
01569
01570
01571
01572
01573
01574
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590 ret ::struct::matrix::__set_rect (type name , type column , type row , type values) {
01591 # Allow negative indices!
01592 set column [ChkColumnIndexNeg $name $column]
01593 set row [ChkRowIndexNeg $name $row]
01594
01595 variable ${name}::data
01596 variable ${name}::columns
01597 variable ${name}::rows
01598 variable ${name}::colw
01599 variable ${name}::rowh
01600
01601 if {$row < 0} {
01602 # Remove rows from the head of values to restrict it to the
01603 # overlapping area.
01604
01605 set values [lrange $values [expr {0 - $row}] end]
01606 set row 0
01607 }
01608
01609 # Restrict it at the end too.
01610 if {($row + [llength $values]) > $rows} {
01611 set values [lrange $values 0 [expr {$rows - $row - 1}]]
01612 }
01613
01614 # Same for columns, but store it in some vars as this is required
01615 # in a loop.
01616 set firstcol 0
01617 if {$column < 0} {
01618 set firstcol [expr {0 - $column}]
01619 set column 0
01620 }
01621
01622 # Now pan through values and area and copy the external data into
01623 # the matrix.
01624
01625 set r $row
01626 foreach line $values {
01627 set line [lrange $line $firstcol end]
01628
01629 set l [expr {$column + [llength $line]}]
01630 if {$l > $columns} {
01631 set line [lrange $line 0 [expr {$columns - $column - 1}]]
01632 } elseif {$l < [expr {$columns - $firstcol}]} {
01633 # We have to take the offset into the line into account
01634 # or we add fillers we don't need, overwriting part of the
01635 # data array we shouldn't.
01636
01637 for {} {$l < [expr {$columns - $firstcol}]} {incr l} {
01638 lappend line {}
01639 }
01640 }
01641
01642 set c $column
01643 foreach cell $line {
01644 if {$cell != {}} {
01645 catch {unset rowh($r)}
01646 catch {unset colw($c)}
01647 }
01648 set data($c,$r) $cell
01649 incr c
01650 }
01651 incr r
01652 }
01653 return
01654 }
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676 ret ::struct::matrix::__set_row (type name , type row , type values) {
01677 set row [ChkRowIndex $name $row]
01678
01679 variable ${name}::data
01680 variable ${name}::columns
01681 variable ${name}::rows
01682 variable ${name}::colw
01683 variable ${name}::rowh
01684
01685 if {[set l [llength $values]] < $columns} {
01686 # Missing values. Fill up with empty strings
01687
01688 for {} {$l < $columns} {incr l} {
01689 lappend values {}
01690 }
01691 } elseif {[llength $values] > $columns} {
01692 # To many values. Remove the superfluous items
01693 set values [lrange $values 0 [expr {$columns - 1}]]
01694 }
01695
01696 # "values" now contains the information to set into the array.
01697 # Regarding the width and height caches:
01698
01699 # - Invalidate the row in the height cache.
01700 # - The columns are either removed from the width cache or left
01701 # unchanged, depending on the contents set into the cell.
01702
01703 set c 0
01704 foreach v $values {
01705 if {$v != {}} {
01706 # Data changed unpredictably, invalidate cache
01707 catch {unset colw($c)}
01708 } ; # {else leave the row unchanged}
01709 set data($c,$row) $v
01710 incr c
01711 }
01712 catch {unset rowh($row)}
01713 return
01714 }
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728 ret ::struct::matrix::__swap_columns (type name , type column_, type a , type column_, type b) {
01729 set column_a [ChkColumnIndex $name $column_a]
01730 set column_b [ChkColumnIndex $name $column_b]
01731 return [SwapColumns $name $column_a $column_b]
01732 }
01733
01734 ret ::struct::matrix::SwapColumns (type name , type column_, type a , type column_, type b) {
01735 variable ${name}::data
01736 variable ${name}::rows
01737 variable ${name}::colw
01738
01739 # Note: This operation does not influence the height cache for all
01740 # rows and the width cache only insofar as its contents has to be
01741 # swapped too for the two columns we are touching. Note that the
01742 # cache might be partially filled or not at all, so we don't have
01743 # to "swap" in some situations.
01744
01745 for {set r 0} {$r < $rows} {incr r} {
01746 set tmp $data($column_a,$r)
01747 set data($column_a,$r) $data($column_b,$r)
01748 set data($column_b,$r) $tmp
01749 }
01750
01751 set cwa [info exists colw($column_a)]
01752 set cwb [info exists colw($column_b)]
01753
01754 if {$cwa && $cwb} {
01755 set tmp $colw($column_a)
01756 set colw($column_a) $colw($column_b)
01757 set colw($column_b) $tmp
01758 } elseif {$cwa} {
01759 # Move contents, don't swap.
01760 set colw($column_b) $colw($column_a)
01761 unset colw($column_a)
01762 } elseif {$cwb} {
01763 # Move contents, don't swap.
01764 set colw($column_a) $colw($column_b)
01765 unset colw($column_b)
01766 } ; # else {nothing to do at all}
01767 return
01768 }
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782 ret ::struct::matrix::__swap_rows (type name , type row_, type a , type row_, type b) {
01783 set row_a [ChkRowIndex $name $row_a]
01784 set row_b [ChkRowIndex $name $row_b]
01785 return [SwapRows $name $row_a $row_b]
01786 }
01787
01788 ret ::struct::matrix::SwapRows (type name , type row_, type a , type row_, type b) {
01789 variable ${name}::data
01790 variable ${name}::columns
01791 variable ${name}::rowh
01792
01793 # Note: This operation does not influence the width cache for all
01794 # columns and the height cache only insofar as its contents has to be
01795 # swapped too for the two rows we are touching. Note that the
01796 # cache might be partially filled or not at all, so we don't have
01797 # to "swap" in some situations.
01798
01799 for {set c 0} {$c < $columns} {incr c} {
01800 set tmp $data($c,$row_a)
01801 set data($c,$row_a) $data($c,$row_b)
01802 set data($c,$row_b) $tmp
01803 }
01804
01805 set rha [info exists rowh($row_a)]
01806 set rhb [info exists rowh($row_b)]
01807
01808 if {$rha && $rhb} {
01809 set tmp $rowh($row_a)
01810 set rowh($row_a) $rowh($row_b)
01811 set rowh($row_b) $tmp
01812 } elseif {$rha} {
01813 # Move contents, don't swap.
01814 set rowh($row_b) $rowh($row_a)
01815 unset rowh($row_a)
01816 } elseif {$rhb} {
01817 # Move contents, don't swap.
01818 set rowh($row_a) $rowh($row_b)
01819 unset rowh($row_b)
01820 } ; # else {nothing to do at all}
01821 return
01822 }
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836 ret ::struct::matrix::_unlink (type name , type avar) {
01837
01838 variable ${name}::link
01839
01840 if {![info exists link($avar)]} {
01841 # Ignore unlinking of unkown variables.
01842 return
01843 }
01844
01845 # Delete the traces first, then remove the link management
01846 # information from the object.
01847
01848 upvar #0 $avar array
01849 variable ${name}::data
01850
01851 trace vdelete array wu [list ::struct::matrix::MatTraceIn $avar $name]
01852 trace vdelete date w [list ::struct::matrix::MatTraceOut $avar $name]
01853
01854 unset link($avar)
01855 return
01856 }
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871 ret ::struct::matrix::ChkColumnIndex (type name , type column) {
01872 variable ${name}::columns
01873
01874 switch -regex -- $column {
01875 {end-[0-9]+} {
01876 set column [string map {end- ""} $column]
01877 set cc [expr {$columns - 1 - $column}]
01878 if {($cc < 0) || ($cc >= $columns)} {
01879 return -code error "bad column index end-$column, column does not exist"
01880 }
01881 return $cc
01882 }
01883 end {
01884 if {$columns <= 0} {
01885 return -code error "bad column index $column, column does not exist"
01886 }
01887 return [expr {$columns - 1}]
01888 }
01889 {[0-9]+} {
01890 if {($column < 0) || ($column >= $columns)} {
01891 return -code error "bad column index $column, column does not exist"
01892 }
01893 return $column
01894 }
01895 default {
01896 return -code error "bad column index \"$column\", syntax error"
01897 }
01898 }
01899 # Will not come to this place
01900 }
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915 ret ::struct::matrix::ChkRowIndex (type name , type row) {
01916 variable ${name}::rows
01917
01918 switch -regex -- $row {
01919 {end-[0-9]+} {
01920 set row [string map {end- ""} $row]
01921 set rr [expr {$rows - 1 - $row}]
01922 if {($rr < 0) || ($rr >= $rows)} {
01923 return -code error "bad row index end-$row, row does not exist"
01924 }
01925 return $rr
01926 }
01927 end {
01928 if {$rows <= 0} {
01929 return -code error "bad row index $row, row does not exist"
01930 }
01931 return [expr {$rows - 1}]
01932 }
01933 {[0-9]+} {
01934 if {($row < 0) || ($row >= $rows)} {
01935 return -code error "bad row index $row, row does not exist"
01936 }
01937 return $row
01938 }
01939 default {
01940 return -code error "bad row index \"$row\", syntax error"
01941 }
01942 }
01943 # Will not come to this place
01944 }
01945
01946
01947
01948
01949
01950
01951
01952
01953
01954
01955
01956
01957
01958
01959
01960 ret ::struct::matrix::ChkColumnIndexNeg (type name , type column) {
01961 variable ${name}::columns
01962
01963 switch -regex -- $column {
01964 {end-[0-9]+} {
01965 set column [string map {end- ""} $column]
01966 set cc [expr {$columns - 1 - $column}]
01967 if {$cc >= $columns} {
01968 return -code error "bad column index end-$column, column does not exist"
01969 }
01970 return $cc
01971 }
01972 end {
01973 return [expr {$columns - 1}]
01974 }
01975 {[0-9]+} {
01976 if {$column >= $columns} {
01977 return -code error "bad column index $column, column does not exist"
01978 }
01979 return $column
01980 }
01981 default {
01982 return -code error "bad column index \"$column\", syntax error"
01983 }
01984 }
01985 # Will not come to this place
01986 }
01987
01988
01989
01990
01991
01992
01993
01994
01995
01996
01997
01998
01999
02000
02001
02002 ret ::struct::matrix::ChkRowIndexNeg (type name , type row) {
02003 variable ${name}::rows
02004
02005 switch -regex -- $row {
02006 {end-[0-9]+} {
02007 set row [string map {end- ""} $row]
02008 set rr [expr {$rows - 1 - $row}]
02009 if {$rr >= $rows} {
02010 return -code error "bad row index end-$row, row does not exist"
02011 }
02012 return $rr
02013 }
02014 end {
02015 return [expr {$rows - 1}]
02016 }
02017 {[0-9]+} {
02018 if {$row >= $rows} {
02019 return -code error "bad row index $row, row does not exist"
02020 }
02021 return $row
02022 }
02023 default {
02024 return -code error "bad row index \"$row\", syntax error"
02025 }
02026 }
02027 # Will not come to this place
02028 }
02029
02030
02031
02032
02033
02034
02035
02036
02037
02038
02039
02040
02041
02042
02043 ret ::struct::matrix::ChkColumnIndexAll (type name , type column) {
02044 variable ${name}::columns
02045
02046 switch -regex -- $column {
02047 {end-[0-9]+} {
02048 set column [string map {end- ""} $column]
02049 set cc [expr {$columns - 1 - $column}]
02050 return $cc
02051 }
02052 end {
02053 return $columns
02054 }
02055 {[0-9]+} {
02056 return $column
02057 }
02058 default {
02059 return -code error "bad column index \"$column\", syntax error"
02060 }
02061 }
02062 # Will not come to this place
02063 }
02064
02065
02066
02067
02068
02069
02070
02071
02072
02073
02074
02075
02076
02077
02078 ret ::struct::matrix::ChkRowIndexAll (type name , type row) {
02079 variable ${name}::rows
02080
02081 switch -regex -- $row {
02082 {end-[0-9]+} {
02083 set row [string map {end- ""} $row]
02084 set rr [expr {$rows - 1 - $row}]
02085 return $rr
02086 }
02087 end {
02088 return $rows
02089 }
02090 {[0-9]+} {
02091 return $row
02092 }
02093 default {
02094 return -code error "bad row index \"$row\", syntax error"
02095 }
02096 }
02097 # Will not come to this place
02098 }
02099
02100
02101
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113 ret ::struct::matrix::MatTraceIn (type avar , type name , type var , type idx , type op) {
02114 # Propagate changes in the linked array back into the matrix.
02115
02116 variable ${name}::lock
02117 if {$lock} {return}
02118
02119 # We have to cover two possibilities when encountering an "unset" operation ...
02120 # 1. The external array was destroyed: perform automatic unlink.
02121 # 2. An individual element was unset: Set the corresponding cell to the empty string.
02122 # See SF Tcllib Bug #532791.
02123
02124 if {(![string compare $op u]) && ($idx == {})} {
02125 # Possibility 1: Array was destroyed
02126 $name unlink $avar
02127 return
02128 }
02129
02130 upvar #0 $avar array
02131 variable ${name}::data
02132 variable ${name}::link
02133
02134 set transpose $link($avar)
02135 if {$transpose} {
02136 foreach {r c} [split $idx ,] break
02137 } else {
02138 foreach {c r} [split $idx ,] break
02139 }
02140
02141 # Use standard method to propagate the change.
02142 # => Get automatically index checks, cache updates, ...
02143
02144 if {![string compare $op u]} {
02145 # Unset possibility 2: Element was unset.
02146 # Note: Setting the cell to the empty string will
02147 # invoke MatTraceOut for this array and thus try
02148 # to recreate the destroyed element of the array.
02149 # We don't want this. But we do want to propagate
02150 # the change to other arrays, as "unset". To do
02151 # all of this we use another state variable to
02152 # signal this situation.
02153
02154 variable ${name}::unset
02155 set unset $avar
02156
02157 $name set cell $c $r ""
02158
02159 set unset {}
02160 return
02161 }
02162
02163 $name set cell $c $r $array($idx)
02164 return
02165 }
02166
02167
02168
02169
02170
02171
02172
02173
02174
02175
02176
02177
02178
02179 ret ::struct::matrix::MatTraceOut (type avar , type name , type var , type idx , type op) {
02180 # Propagate changes in the matrix data array into the linked array.
02181
02182 variable ${name}::unset
02183
02184 if {![string compare $avar $unset]} {
02185 # Do not change the variable currently unsetting
02186 # one of its elements.
02187 return
02188 }
02189
02190 variable ${name}::lock
02191 set lock 1 ; # Disable MatTraceIn [#532783]
02192
02193 upvar #0 $avar array
02194 variable ${name}::data
02195 variable ${name}::link
02196
02197 set transpose $link($avar)
02198
02199 if {$transpose} {
02200 foreach {r c} [split $idx ,] break
02201 } else {
02202 foreach {c r} [split $idx ,] break
02203 }
02204
02205 if {$unset != {}} {
02206 # We are currently propagating the unset of an
02207 # element in a different linked array to this
02208 # array. We make sure that this is an unset too.
02209
02210 unset array($c,$r)
02211 } else {
02212 set array($c,$r) $data($idx)
02213 }
02214 set lock 0
02215 return
02216 }
02217
02218
02219
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236
02237
02238 ret ::struct::matrix::SortMaxHeapify (type name , type i , type key , type rowCol , type heapSize , optional rev =0) {
02239 # MAX-HEAPIFY, adapted by EAS from CLRS 6.2
02240 switch $rowCol {
02241 r { set A [GetColumn $name $key] }
02242 c { set A [GetRow $name $key] }
02243 }
02244 # Weird expressions below for clarity, as CLRS uses A[1...n]
02245 # format and TCL uses A[0...n-1]
02246 set left [expr {int(2*($i+1) -1)}]
02247 set right [expr {int(2*($i+1)+1 -1)}]
02248
02249 # left, right are tested as < rather than <= because they are
02250 # in A[0...n-1]
02251 if {
02252 $left < $heapSize &&
02253 ( !$rev && [lindex $A $left] > [lindex $A $i] ||
02254 $rev && [lindex $A $left] < [lindex $A $i] )
02255 } {
02256 set largest $left
02257 } else {
02258 set largest $i
02259 }
02260
02261 if {
02262 $right < $heapSize &&
02263 ( !$rev && [lindex $A $right] > [lindex $A $largest] ||
02264 $rev && [lindex $A $right] < [lindex $A $largest] )
02265 } {
02266 set largest $right
02267 }
02268
02269 if { $largest != $i } {
02270 switch $rowCol {
02271 r { SwapRows $name $i $largest }
02272 c { SwapColumns $name $i $largest }
02273 }
02274 SortMaxHeapify $name $largest $key $rowCol $heapSize $rev
02275 }
02276 return
02277 }
02278
02279
02280
02281
02282 namespace ::struct {
02283
02284 namespace import -force matrix::matrix
02285 namespace export matrix
02286 }
02287 package provide struct::matrix 1.2.1
02288