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