matrix.tcl

Go to the documentation of this file.
00001 /*  matrix.tcl --*/
00002 /* */
00003 /*  Implementation of a matrix data structure for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 2001-2004 by Andreas Kupries <andreas_kupries@users.sourceforge.net>*/
00006 /* */
00007 /*  Heapsort code Copyright (c) 2003 by Edwin A. Suominen <ed@eepatents.com>,*/
00008 /*  based on concepts in "Introduction to Algorithms" by Thomas H. Cormen et al.*/
00009 /* */
00010 /*  See the file "license.terms" for information on usage and redistribution*/
00011 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00012 /*  */
00013 /*  RCS: @(#) $Id: matrix.tcl,v 1.22 2005/09/30 05:36:39 andreas_kupries Exp $*/
00014 
00015 package require Tcl 8.2
00016 
00017 namespace ::struct {}
00018 
00019 namespace ::struct::matrix {
00020     /*  Data storage in the matrix module*/
00021     /*  -------------------------------*/
00022     /* */
00023     /*  One namespace per object, containing*/
00024     /* */
00025     /*  - Two scalar variables containing the current number of rows and columns.*/
00026     /*  - Four array variables containing the array data, the caches for*/
00027     /*    row heights and column widths and the information about linked arrays.*/
00028     /* */
00029     /*  The variables are*/
00030     /*  - columns #columns in data*/
00031     /*  - rows    #rows in data*/
00032     /*  - data    cell contents*/
00033     /*  - colw    cache of column widths*/
00034     /*  - rowh    cache of row heights*/
00035     /*  - link    information about linked arrays*/
00036     /*  - lock    boolean flag to disable MatTraceIn while in MatTraceOut [#532783]*/
00037     /*  - unset   string used to convey information about 'unset' traces from MatTraceIn to MatTraceOut.*/
00038 
00039     /*  counter is used to give a unique name for unnamed matrices*/
00040     variable counter 0
00041 
00042     /*  Only export one command, the one used to instantiate a new matrix*/
00043     namespace export matrix
00044 }
00045 
00046 /*  ::struct::matrix::matrix --*/
00047 /* */
00048 /*  Create a new matrix with a given name; if no name is given, use*/
00049 /*  matrixX, where X is a number.*/
00050 /* */
00051 /*  Arguments:*/
00052 /*  name    Optional name of the matrix; if null or not given, generate one.*/
00053 /* */
00054 /*  Results:*/
00055 /*  name    Name of the matrix created*/
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 /*  Private functions follow*/
00150 
00151 /*  ::struct::matrix::MatrixProc --*/
00152 /* */
00153 /*  Command that processes all matrix object commands.*/
00154 /* */
00155 /*  Arguments:*/
00156 /*  name    Name of the matrix object to manipulate.*/
00157 /*  cmd Subcommand to invoke.*/
00158 /*  args    Arguments for subcommand.*/
00159 /* */
00160 /*  Results:*/
00161 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_= --*/
00187 /* */
00188 /*  Assignment operator. Copies the source matrix into the*/
00189 /*        destination, destroying the original information.*/
00190 /* */
00191 /*  Arguments:*/
00192 /*  name    Name of the matrix object we are copying into.*/
00193 /*  source  Name of the matrix object providing us with the*/
00194 /*      data to copy.*/
00195 /* */
00196 /*  Results:*/
00197 /*  Nothing.*/
00198 
00199 ret  ::struct::matrix::_= (type name , type source) {
00200     _deserialize $name [$source serialize]
00201     return
00202 }
00203 
00204 /*  ::struct::matrix::_--> --*/
00205 /* */
00206 /*  Reverse assignment operator. Copies this matrix into the*/
00207 /*        destination, destroying the original information.*/
00208 /* */
00209 /*  Arguments:*/
00210 /*  name    Name of the matrix object to copy*/
00211 /*  dest    Name of the matrix object we are copying to.*/
00212 /* */
00213 /*  Results:*/
00214 /*  Nothing.*/
00215 
00216 ret  ::struct::matrix::_--> (type name , type dest) {
00217     $dest deserialize [_serialize $name]
00218     return
00219 }
00220 
00221 /*  ::struct::matrix::_add --*/
00222 /* */
00223 /*  Command that processes all 'add' subcommands.*/
00224 /* */
00225 /*  Arguments:*/
00226 /*  name    Name of the matrix object to manipulate.*/
00227 /*  cmd Subcommand of 'add' to invoke.*/
00228 /*  args    Arguments for subcommand of 'add'.*/
00229 /* */
00230 /*  Results:*/
00231 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_delete --*/
00256 /* */
00257 /*  Command that processes all 'delete' subcommands.*/
00258 /* */
00259 /*  Arguments:*/
00260 /*  name    Name of the matrix object to manipulate.*/
00261 /*  cmd Subcommand of 'delete' to invoke.*/
00262 /*  args    Arguments for subcommand of 'delete'.*/
00263 /* */
00264 /*  Results:*/
00265 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_format --*/
00290 /* */
00291 /*  Command that processes all 'format' subcommands.*/
00292 /* */
00293 /*  Arguments:*/
00294 /*  name    Name of the matrix object to manipulate.*/
00295 /*  cmd Subcommand of 'format' to invoke.*/
00296 /*  args    Arguments for subcommand of 'format'.*/
00297 /* */
00298 /*  Results:*/
00299 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_get --*/
00324 /* */
00325 /*  Command that processes all 'get' subcommands.*/
00326 /* */
00327 /*  Arguments:*/
00328 /*  name    Name of the matrix object to manipulate.*/
00329 /*  cmd Subcommand of 'get' to invoke.*/
00330 /*  args    Arguments for subcommand of 'get'.*/
00331 /* */
00332 /*  Results:*/
00333 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_insert --*/
00358 /* */
00359 /*  Command that processes all 'insert' subcommands.*/
00360 /* */
00361 /*  Arguments:*/
00362 /*  name    Name of the matrix object to manipulate.*/
00363 /*  cmd Subcommand of 'insert' to invoke.*/
00364 /*  args    Arguments for subcommand of 'insert'.*/
00365 /* */
00366 /*  Results:*/
00367 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_search --*/
00392 /* */
00393 /*  Command that processes all 'search' subcommands.*/
00394 /* */
00395 /*  Arguments:*/
00396 /*  name    Name of the matrix object to manipulate.*/
00397 /*  args    Arguments for search.*/
00398 /* */
00399 /*  Results:*/
00400 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_set --*/
00510 /* */
00511 /*  Command that processes all 'set' subcommands.*/
00512 /* */
00513 /*  Arguments:*/
00514 /*  name    Name of the matrix object to manipulate.*/
00515 /*  cmd Subcommand of 'set' to invoke.*/
00516 /*  args    Arguments for subcommand of 'set'.*/
00517 /* */
00518 /*  Results:*/
00519 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_sort --*/
00544 /* */
00545 /*  Command that processes all 'sort' subcommands.*/
00546 /* */
00547 /*  Arguments:*/
00548 /*  name    Name of the matrix object to manipulate.*/
00549 /*  cmd Subcommand of 'sort' to invoke.*/
00550 /*  args    Arguments for subcommand of 'sort'.*/
00551 /* */
00552 /*  Results:*/
00553 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::_swap --*/
00627 /* */
00628 /*  Command that processes all 'swap' subcommands.*/
00629 /* */
00630 /*  Arguments:*/
00631 /*  name    Name of the matrix object to manipulate.*/
00632 /*  cmd Subcommand of 'swap' to invoke.*/
00633 /*  args    Arguments for subcommand of 'swap'.*/
00634 /* */
00635 /*  Results:*/
00636 /*  Varies based on command to perform*/
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 /*  ::struct::matrix::__add_column --*/
00661 /* */
00662 /*  Extends the matrix by one column and then acts like*/
00663 /*  "setcolumn" (see below) on this new column if there were*/
00664 /*  "values" supplied. Without "values" the new cells will be set*/
00665 /*  to the empty string. The new column is appended immediately*/
00666 /*  behind the last existing column.*/
00667 /* */
00668 /*  Arguments:*/
00669 /*  name    Name of the matrix object.*/
00670 /*  values  Optional values to set into the new row.*/
00671 /* */
00672 /*  Results:*/
00673 /*  None.*/
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     /*  Missing values. Fill up with empty strings*/
00683 
00684     for {} {$l < $rows} {incr l} {
00685         lappend values {}
00686     }
00687     } elseif {[llength $values] > $rows} {
00688     /*  To many values. Remove the superfluous items*/
00689      values =  [lrange $values 0 [expr {$rows - 1}]]
00690     }
00691 
00692     /*  "values" now contains the information to set into the array.*/
00693     /*  Regarding the width and height caches:*/
00694 
00695     /*  - The new column is not added to the width cache, the other*/
00696     /*    columns are not touched, the cache therefore unchanged.*/
00697     /*  - The rows are either removed from the height cache or left*/
00698     /*    unchanged, depending on the contents set into the cell.*/
00699 
00700      r =  0
00701     foreach v $values {
00702     if {$v != {}} {
00703         /*  Data changed unpredictably, invalidate cache*/
00704         catch {un rowh = ($r)}
00705     } ; /*  {else leave the row unchanged}*/
00706      data = ($columns,$r) $v
00707     incr r
00708     }
00709     incr columns
00710     return
00711 }
00712 
00713 /*  ::struct::matrix::__add_row --*/
00714 /* */
00715 /*  Extends the matrix by one row and then acts like "setrow" (see*/
00716 /*  below) on this new row if there were "values"*/
00717 /*  supplied. Without "values" the new cells will be set to the*/
00718 /*  empty string. The new row is appended immediately behind the*/
00719 /*  last existing row.*/
00720 /* */
00721 /*  Arguments:*/
00722 /*  name    Name of the matrix object.*/
00723 /*  values  Optional values to set into the new row.*/
00724 /* */
00725 /*  Results:*/
00726 /*  None.*/
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     /*  Missing values. Fill up with empty strings*/
00736 
00737     for {} {$l < $columns} {incr l} {
00738         lappend values {}
00739     }
00740     } elseif {[llength $values] > $columns} {
00741     /*  To many values. Remove the superfluous items*/
00742      values =  [lrange $values 0 [expr {$columns - 1}]]
00743     }
00744 
00745     /*  "values" now contains the information to set into the array.*/
00746     /*  Regarding the width and height caches:*/
00747 
00748     /*  - The new row is not added to the height cache, the other*/
00749     /*    rows are not touched, the cache therefore unchanged.*/
00750     /*  - The columns are either removed from the width cache or left*/
00751     /*    unchanged, depending on the contents set into the cell.*/
00752 
00753      c =  0
00754     foreach v $values {
00755     if {$v != {}} {
00756         /*  Data changed unpredictably, invalidate cache*/
00757         catch {un colw = ($c)}
00758     } ; /*  {else leave the row unchanged}*/
00759      data = ($c,$rows) $v
00760     incr c
00761     }
00762     incr rows
00763     return
00764 }
00765 
00766 /*  ::struct::matrix::__add_columns --*/
00767 /* */
00768 /*  Extends the matrix by "n" columns. The new cells will be set*/
00769 /*  to the empty string. The new columns are appended immediately*/
00770 /*  behind the last existing column. A value of "n" equal to or*/
00771 /*  smaller than 0 is not allowed.*/
00772 /* */
00773 /*  Arguments:*/
00774 /*  name    Name of the matrix object.*/
00775 /*  n   The number of new columns to create.*/
00776 /* */
00777 /*  Results:*/
00778 /*  None.*/
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 /*  ::struct::matrix::__add_rows --*/
00811 /* */
00812 /*  Extends the matrix by "n" rows. The new cells will be set to*/
00813 /*  the empty string. The new rows are appended immediately behind*/
00814 /*  the last existing row. A value of "n" equal to or smaller than*/
00815 /*  0 is not allowed.*/
00816 /* */
00817 /*  Arguments:*/
00818 /*  name    Name of the matrix object.*/
00819 /*  n   The number of new rows to create.*/
00820 /* */
00821 /*  Results:*/
00822 /*  None.*/
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 /*  ::struct::matrix::_cells --*/
00854 /* */
00855 /*  Returns the number of cells currently managed by the*/
00856 /*  matrix. This is the product of "rows" and "columns".*/
00857 /* */
00858 /*  Arguments:*/
00859 /*  name    Name of the matrix object.*/
00860 /* */
00861 /*  Results:*/
00862 /*  The number of cells in the matrix.*/
00863 
00864 ret  ::struct::matrix::_cells (type name) {
00865     variable ${name}::rows
00866     variable ${name}::columns
00867     return [expr {$rows * $columns}]
00868 }
00869 
00870 /*  ::struct::matrix::_cellsize --*/
00871 /* */
00872 /*  Returns the length of the string representation of the value*/
00873 /*  currently contained in the addressed cell.*/
00874 /* */
00875 /*  Arguments:*/
00876 /*  name    Name of the matrix object.*/
00877 /*  column  Column index of the cell to query*/
00878 /*  row Row index of the cell to query*/
00879 /* */
00880 /*  Results:*/
00881 /*  The number of cells in the matrix.*/
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 /*  ::struct::matrix::_columns --*/
00892 /* */
00893 /*  Returns the number of columns currently managed by the*/
00894 /*  matrix.*/
00895 /* */
00896 /*  Arguments:*/
00897 /*  name    Name of the matrix object.*/
00898 /* */
00899 /*  Results:*/
00900 /*  The number of columns in the matrix.*/
00901 
00902 ret  ::struct::matrix::_columns (type name) {
00903     variable ${name}::columns
00904     return $columns
00905 }
00906 
00907 /*  ::struct::matrix::_columnwidth --*/
00908 /* */
00909 /*  Returns the length of the longest string representation of all*/
00910 /*  the values currently contained in the cells of the addressed*/
00911 /*  column if these are all spanning only one line. For cell*/
00912 /*  values spanning multiple lines the length of their longest*/
00913 /*  line goes into the computation.*/
00914 /* */
00915 /*  Arguments:*/
00916 /*  name    Name of the matrix object.*/
00917 /*  column  The index of the column whose width is asked for.*/
00918 /* */
00919 /*  Results:*/
00920 /*  See description.*/
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 /*  ::struct::matrix::__delete_column --*/
00948 /* */
00949 /*  Deletes the specified column from the matrix and shifts all*/
00950 /*  columns with higher indices one index down.*/
00951 /* */
00952 /*  Arguments:*/
00953 /*  name    Name of the matrix.*/
00954 /*  column  The index of the column to delete.*/
00955 /* */
00956 /*  Results:*/
00957 /*  None.*/
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 /*  ::struct::matrix::__delete_columns --*/
00989 /* */
00990 /*  Shrink the matrix by "n" columns (from the right).*/
00991 /*  A value of "n" equal to or smaller than 0 is not*/
00992 /*  allowed, nor is "n" allowed to be greater than the*/
00993 /*  number of columns in the matrix.*/
00994 /* */
00995 /*  Arguments:*/
00996 /*  name    Name of the matrix object.*/
00997 /*  n   The number of columns to remove.*/
00998 /* */
00999 /*  Results:*/
01000 /*  None.*/
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 /*  ::struct::matrix::__delete_row --*/
01018 /* */
01019 /*  Deletes the specified row from the matrix and shifts all*/
01020 /*  row with higher indices one index down.*/
01021 /* */
01022 /*  Arguments:*/
01023 /*  name    Name of the matrix.*/
01024 /*  row The index of the row to delete.*/
01025 /* */
01026 /*  Results:*/
01027 /*  None.*/
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 /*  ::struct::matrix::__delete_rows --*/
01059 /* */
01060 /*  Shrink the matrix by "n" rows (from the bottom).*/
01061 /*  A value of "n" equal to or smaller than 0 is not*/
01062 /*  allowed, nor is "n" allowed to be greater than the*/
01063 /*  number of rows in the matrix.*/
01064 /* */
01065 /*  Arguments:*/
01066 /*  name    Name of the matrix object.*/
01067 /*  n   The number of rows to remove.*/
01068 /* */
01069 /*  Results:*/
01070 /*  None.*/
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 /*  ::struct::matrix::_deserialize --*/
01088 /* */
01089 /*  Assignment operator. Copies a serialization into the*/
01090 /*        destination, destroying the original information.*/
01091 /* */
01092 /*  Arguments:*/
01093 /*  name    Name of the matrix object we are copying into.*/
01094 /*  serial  Serialized matrix to copy from.*/
01095 /* */
01096 /*  Results:*/
01097 /*  Nothing.*/
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 /*  ::struct::matrix::_destroy --*/
01147 /* */
01148 /*  Destroy a matrix, including its associated command and data storage.*/
01149 /* */
01150 /*  Arguments:*/
01151 /*  name    Name of the matrix to destroy.*/
01152 /* */
01153 /*  Results:*/
01154 /*  None.*/
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 /*  ::struct::matrix::__format_2string --*/
01171 /* */
01172 /*  Formats the matrix using the specified report object and*/
01173 /*  returns the string containing the result of this*/
01174 /*  operation. The report has to support the "printmatrix" method.*/
01175 /* */
01176 /*  Arguments:*/
01177 /*  name    Name of the matrix.*/
01178 /*  report  Name of the report object specifying the formatting.*/
01179 /* */
01180 /*  Results:*/
01181 /*  A string containing the formatting result.*/
01182 
01183 ret  ::struct::matrix::__format_2string (type name , optional report ={)} {
01184     if {$report == {}} {
01185     /*  Use an internal hardwired simple report to format the matrix.*/
01186     /*  1. Go through all columns and compute the column widths.*/
01187     /*  2. Then iterate through all rows and dump then into a*/
01188     /*     string, formatted to the number of characters per columns*/
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         /*  Simple row.*/
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         /*  Complex row, multiple passes*/
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 /*  ::struct::matrix::__format_2chan --*/
01227 /* */
01228 /*  Formats the matrix using the specified report object and*/
01229 /*  writes the string containing the result of this operation into*/
01230 /*  the channel. The report has to support the*/
01231 /*  "printmatrix2channel" method.*/
01232 /* */
01233 /*  Arguments:*/
01234 /*  name    Name of the matrix.*/
01235 /*  report  Name of the report object specifying the formatting.*/
01236 /*  chan    Handle of the channel to write to.*/
01237 /* */
01238 /*  Results:*/
01239 /*  None.*/
01240 
01241 ret  ::struct::matrix::__format_2chan (type name , optional report ={) {chan stdout}} {
01242     if {$report == {}} {
01243     /*  Use an internal hardwired simple report to format the matrix.*/
01244     /*  We delegate this to the string formatter and print its result.*/
01245     puts -nonewline [__format_2string $name]
01246     } else {
01247     $report printmatrix2channel $name $chan
01248     }
01249     return
01250 }
01251 
01252 /*  ::struct::matrix::__get_cell --*/
01253 /* */
01254 /*  Returns the value currently contained in the cell identified*/
01255 /*  by row and column index.*/
01256 /* */
01257 /*  Arguments:*/
01258 /*  name    Name of the matrix.*/
01259 /*  column  Column index of the addressed cell.*/
01260 /*  row Row index of the addressed cell.*/
01261 /* */
01262 /*  Results:*/
01263 /*  value   Value currently stored in the addressed cell.*/
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 /*  ::struct::matrix::__get_column --*/
01274 /* */
01275 /*  Returns a list containing the values from all cells in the*/
01276 /*  column identified by the index. The contents of the cell in*/
01277 /*  row 0 are stored as the first element of this list.*/
01278 /* */
01279 /*  Arguments:*/
01280 /*  name    Name of the matrix.*/
01281 /*  column  Column index of the addressed cell.*/
01282 /* */
01283 /*  Results:*/
01284 /*  List of values stored in the addressed row.*/
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 /*  ::struct::matrix::__get_rect --*/
01303 /* */
01304 /*  Returns a list of lists of cell values. The values stored in*/
01305 /*  the result come from the submatrix whose top-left and*/
01306 /*  bottom-right cells are specified by "column_tl", "row_tl" and*/
01307 /*  "column_br", "row_br" resp. Note that the following equations*/
01308 /*  have to be true: column_tl <= column_br and row_tl <= row_br.*/
01309 /*  The result is organized as follows: The outer list is the list*/
01310 /*  of rows, its elements are lists representing a single row. The*/
01311 /*  row with the smallest index is the first element of the outer*/
01312 /*  list. The elements of the row lists represent the selected*/
01313 /*  cell values. The cell with the smallest index is the first*/
01314 /*  element in each row list.*/
01315 /* */
01316 /*  Arguments:*/
01317 /*  name        Name of the matrix.*/
01318 /*  column_tl   Column index of the top-left cell of the area.*/
01319 /*  row_tl      Row index of the top-left cell of the the area*/
01320 /*  column_br   Column index of the bottom-right cell of the area.*/
01321 /*  row_br      Row index of the bottom-right cell of the the area*/
01322 /* */
01323 /*  Results:*/
01324 /*  List of a list of values stored in the addressed area.*/
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 /*  ::struct::matrix::__get_row --*/
01357 /* */
01358 /*  Returns a list containing the values from all cells in the*/
01359 /*  row identified by the index. The contents of the cell in*/
01360 /*  column 0 are stored as the first element of this list.*/
01361 /* */
01362 /*  Arguments:*/
01363 /*  name    Name of the matrix.*/
01364 /*  row Row index of the addressed cell.*/
01365 /* */
01366 /*  Results:*/
01367 /*  List of values stored in the addressed row.*/
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 /*  ::struct::matrix::__insert_column --*/
01386 /* */
01387 /*  Extends the matrix by one column and then acts like*/
01388 /*  "setcolumn" (see below) on this new column if there were*/
01389 /*  "values" supplied. Without "values" the new cells will be set*/
01390 /*  to the empty string. The new column is inserted just before*/
01391 /*  the column specified by the given index. This means, if*/
01392 /*  "column" is less than or equal to zero, then the new column is*/
01393 /*  inserted at the beginning of the matrix, before the first*/
01394 /*  column. If "column" has the value "Bend", or if it is greater*/
01395 /*  than or equal to the number of columns in the matrix, then the*/
01396 /*  new column is appended to the matrix, behind the last*/
01397 /*  column. The old column at the chosen index and all columns*/
01398 /*  with higher indices are shifted one index upward.*/
01399 /* */
01400 /*  Arguments:*/
01401 /*  name    Name of the matrix.*/
01402 /*  column  Index of the column where to insert.*/
01403 /*  values  Optional values to set the cells to.*/
01404 /* */
01405 /*  Results:*/
01406 /*  None.*/
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     /*  Same as 'addcolumn'*/
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     /*  Missing values. Fill up with empty strings*/
01432 
01433     for {} {$l < $rows} {incr l} {
01434         lappend values {}
01435     }
01436     } elseif {[llength $values] > $rows} {
01437     /*  To many values. Remove the superfluous items*/
01438      values =  [lrange $values 0 [expr {$rows - 1}]]
01439     }
01440 
01441     /*  "values" now contains the information to set into the array.*/
01442     /*  Regarding the width and height caches:*/
01443     /*  Invalidate all rows, move all columns*/
01444 
01445     /*  Move all data from the higher columns one up and then insert the*/
01446     /*  new data into the freed space. Move the data in the*/
01447     /*  width cache too, take partial fill into account there too.*/
01448     /*  Invalidate the height cache for all rows.*/
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 /*  ::struct::matrix::__insert_row --*/
01466 /* */
01467 /*  Extends the matrix by one row and then acts like "setrow" (see*/
01468 /*  below) on this new row if there were "values"*/
01469 /*  supplied. Without "values" the new cells will be set to the*/
01470 /*  empty string. The new row is inserted just before the row*/
01471 /*  specified by the given index. This means, if "row" is less*/
01472 /*  than or equal to zero, then the new row is inserted at the*/
01473 /*  beginning of the matrix, before the first row. If "row" has*/
01474 /*  the value "end", or if it is greater than or equal to the*/
01475 /*  number of rows in the matrix, then the new row is appended to*/
01476 /*  the matrix, behind the last row. The old row at that index and*/
01477 /*  all rows with higher indices are shifted one index upward.*/
01478 /* */
01479 /*  Arguments:*/
01480 /*  name    Name of the matrix.*/
01481 /*  row Index of the row where to insert.*/
01482 /*  values  Optional values to set the cells to.*/
01483 /* */
01484 /*  Results:*/
01485 /*  None.*/
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     /*  Same as 'addrow'*/
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     /*  Missing values. Fill up with empty strings*/
01511 
01512     for {} {$l < $columns} {incr l} {
01513         lappend values {}
01514     }
01515     } elseif {[llength $values] > $columns} {
01516     /*  To many values. Remove the superfluous items*/
01517      values =  [lrange $values 0 [expr {$columns - 1}]]
01518     }
01519 
01520     /*  "values" now contains the information to set into the array.*/
01521     /*  Regarding the width and height caches:*/
01522     /*  Invalidate all columns, move all rows*/
01523 
01524     /*  Move all data from the higher rows one up and then insert the*/
01525     /*  new data into the freed space. Move the data in the*/
01526     /*  height cache too, take partial fill into account there too.*/
01527     /*  Invalidate the width cache for all columns.*/
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 /*  ::struct::matrix::_link --*/
01545 /* */
01546 /*  Links the matrix to the specified array variable. This means*/
01547 /*  that the contents of all cells in the matrix is stored in the*/
01548 /*  array too, with all changes to the matrix propagated there*/
01549 /*  too. The contents of the cell "(column,row)" is stored in the*/
01550 /*  array using the key "column,row". If the option "-transpose"*/
01551 /*  is specified the key "row,column" will be used instead. It is*/
01552 /*  possible to link the matrix to more than one array. Note that*/
01553 /*  the link is bidirectional, i.e. changes to the array are*/
01554 /*  mirrored in the matrix too.*/
01555 /* */
01556 /*  Arguments:*/
01557 /*  name    Name of the matrix object.*/
01558 /*  option  Either empty of '-transpose'.*/
01559 /*  avar    Name of the variable to link to*/
01560 /* */
01561 /*  Results:*/
01562 /*  None*/
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 /*  ::struct::matrix::_links --*/
01615 /* */
01616 /*  Retrieves the names of all array variable the matrix is*/
01617 /*  officially linked to.*/
01618 /* */
01619 /*  Arguments:*/
01620 /*  name    Name of the matrix object.*/
01621 /* */
01622 /*  Results:*/
01623 /*  List of variables the matrix is linked to.*/
01624 
01625 ret  ::struct::matrix::_links (type name) {
01626     variable ${name}::link
01627     return [array names link]
01628 }
01629 
01630 /*  ::struct::matrix::_rowheight --*/
01631 /* */
01632 /*  Returns the height of the specified row in lines. This is the*/
01633 /*  highest number of lines spanned by a cell over all cells in*/
01634 /*  the row.*/
01635 /* */
01636 /*  Arguments:*/
01637 /*  name    Name of the matrix*/
01638 /*  row Index of the row queried for its height*/
01639 /* */
01640 /*  Results:*/
01641 /*  The height of the specified row in lines.*/
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 /*  ::struct::matrix::_rows --*/
01666 /* */
01667 /*  Returns the number of rows currently managed by the matrix.*/
01668 /* */
01669 /*  Arguments:*/
01670 /*  name    Name of the matrix object.*/
01671 /* */
01672 /*  Results:*/
01673 /*  The number of rows in the matrix.*/
01674 
01675 ret  ::struct::matrix::_rows (type name) {
01676     variable ${name}::rows
01677     return $rows
01678 }
01679 
01680 /*  ::struct::matrix::_serialize --*/
01681 /* */
01682 /*  Serialize a matrix object (partially) into a transportable value.*/
01683 /*  If only a rectangle is serialized the result will be a sub-*/
01684 /*  matrix in the mathematical sense of the word.*/
01685 /* */
01686 /*  Arguments:*/
01687 /*  name    Name of the matrix.*/
01688 /*  args    rectangle to place into the serialized matrix*/
01689 /* */
01690 /*  Results:*/
01691 /*  A list structure describing the part of the matrix which was serialized.*/
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 /*  ::struct::matrix::__set_cell --*/
01742 /* */
01743 /*  Sets the value in the cell identified by row and column index*/
01744 /*  to the data in the third argument.*/
01745 /* */
01746 /*  Arguments:*/
01747 /*  name    Name of the matrix object.*/
01748 /*  column  Column index of the cell to set.*/
01749 /*  row Row index of the cell to set.*/
01750 /*  value   The new value of the cell.*/
01751 /* */
01752 /*  Results:*/
01753 /*  None.*/
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 /*  ::struct::matrix::__set_column --*/
01778 /* */
01779 /*  Sets the values in the cells identified by the column index to*/
01780 /*  the elements of the list provided as the third argument. Each*/
01781 /*  element of the list is assigned to one cell, with the first*/
01782 /*  element going into the cell in row 0 and then upward. If there*/
01783 /*  are less values in the list than there are rows the remaining*/
01784 /*  rows are set to the empty string. If there are more values in*/
01785 /*  the list than there are rows the superfluous elements are*/
01786 /*  ignored. The matrix is not extended by this operation.*/
01787 /* */
01788 /*  Arguments:*/
01789 /*  name    Name of the matrix.*/
01790 /*  column  Index of the column to set.*/
01791 /*  values  Values to set into the column.*/
01792 /* */
01793 /*  Results:*/
01794 /*  None.*/
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 /*  ::struct::matrix::__set_rect --*/
01837 /* */
01838 /*  Takes a list of lists of cell values and writes them into the*/
01839 /*  submatrix whose top-left cell is specified by the two*/
01840 /*  indices. If the sublists of the outer list are not of equal*/
01841 /*  length the shorter sublists will be filled with empty strings*/
01842 /*  to the length of the longest sublist. If the submatrix*/
01843 /*  specified by the top-left cell and the number of rows and*/
01844 /*  columns in the "values" extends beyond the matrix we are*/
01845 /*  modifying the over-extending parts of the values are ignored,*/
01846 /*  i.e. essentially cut off. This subcommand expects its input in*/
01847 /*  the format as returned by "getrect".*/
01848 /* */
01849 /*  Arguments:*/
01850 /*  name    Name of the matrix object.*/
01851 /*  column  Column index of the topleft cell to set.*/
01852 /*  row Row index of the topleft cell to set.*/
01853 /*  values  Values to set.*/
01854 /* */
01855 /*  Results:*/
01856 /*  None.*/
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 /*  ::struct::matrix::__set_row --*/
01925 /* */
01926 /*  Sets the values in the cells identified by the row index to*/
01927 /*  the elements of the list provided as the third argument. Each*/
01928 /*  element of the list is assigned to one cell, with the first*/
01929 /*  element going into the cell in column 0 and then upward. If*/
01930 /*  there are less values in the list than there are columns the*/
01931 /*  remaining columns are set to the empty string. If there are*/
01932 /*  more values in the list than there are columns the superfluous*/
01933 /*  elements are ignored. The matrix is not extended by this*/
01934 /*  operation.*/
01935 /* */
01936 /*  Arguments:*/
01937 /*  name    Name of the matrix.*/
01938 /*  row Index of the row to set.*/
01939 /*  values  Values to set into the row.*/
01940 /* */
01941 /*  Results:*/
01942 /*  None.*/
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 /*  ::struct::matrix::__swap_columns --*/
01988 /* */
01989 /*  Swaps the contents of the two specified columns.*/
01990 /* */
01991 /*  Arguments:*/
01992 /*  name        Name of the matrix.*/
01993 /*  column_a    Index of the first column to swap*/
01994 /*  column_b    Index of the second column to swap*/
01995 /* */
01996 /*  Results:*/
01997 /*  None.*/
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 /*  ::struct::matrix::__swap_rows --*/
02042 /* */
02043 /*  Swaps the contents of the two specified rows.*/
02044 /* */
02045 /*  Arguments:*/
02046 /*  name    Name of the matrix.*/
02047 /*  row_a   Index of the first row to swap*/
02048 /*  row_b   Index of the second row to swap*/
02049 /* */
02050 /*  Results:*/
02051 /*  None.*/
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 /*  ::struct::matrix::_transpose --*/
02096 /* */
02097 /*  Exchanges rows and columns of the matrix*/
02098 /* */
02099 /*  Arguments:*/
02100 /*  name        Name of the matrix.*/
02101 /* */
02102 /*  Results:*/
02103 /*  None.*/
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 /*  ::struct::matrix::_unlink --*/
02190 /* */
02191 /*  Removes the link between the matrix and the specified*/
02192 /*  arrayvariable, if there is one.*/
02193 /* */
02194 /*  Arguments:*/
02195 /*  name    Name of the matrix.*/
02196 /*  avar    Name of the linked array.*/
02197 /* */
02198 /*  Results:*/
02199 /*  None.*/
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 /*  ::struct::matrix::ChkColumnIndex --*/
02224 /* */
02225 /*  Helper to check and transform column indices. Returns the*/
02226 /*  absolute index number belonging to the specified*/
02227 /*  index. Rejects indices out of the valid range of columns.*/
02228 /* */
02229 /*  Arguments:*/
02230 /*  matrix  Matrix to look at*/
02231 /*  column  The incoming index to check and transform*/
02232 /* */
02233 /*  Results:*/
02234 /*  The absolute index to the column*/
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 /*  ::struct::matrix::ChkRowIndex --*/
02268 /* */
02269 /*  Helper to check and transform row indices. Returns the*/
02270 /*  absolute index number belonging to the specified*/
02271 /*  index. Rejects indices out of the valid range of rows.*/
02272 /* */
02273 /*  Arguments:*/
02274 /*  matrix  Matrix to look at*/
02275 /*  row The incoming index to check and transform*/
02276 /* */
02277 /*  Results:*/
02278 /*  The absolute index to the row*/
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 /*  ::struct::matrix::ChkColumnIndexNeg --*/
02312 /* */
02313 /*  Helper to check and transform column indices. Returns the*/
02314 /*  absolute index number belonging to the specified*/
02315 /*  index. Rejects indices out of the valid range of columns*/
02316 /*  (Accepts negative indices).*/
02317 /* */
02318 /*  Arguments:*/
02319 /*  matrix  Matrix to look at*/
02320 /*  column  The incoming index to check and transform*/
02321 /* */
02322 /*  Results:*/
02323 /*  The absolute index to the column*/
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 /*  ::struct::matrix::ChkRowIndexNeg --*/
02354 /* */
02355 /*  Helper to check and transform row indices. Returns the*/
02356 /*  absolute index number belonging to the specified*/
02357 /*  index. Rejects indices out of the valid range of rows*/
02358 /*  (Accepts negative indices).*/
02359 /* */
02360 /*  Arguments:*/
02361 /*  matrix  Matrix to look at*/
02362 /*  row The incoming index to check and transform*/
02363 /* */
02364 /*  Results:*/
02365 /*  The absolute index to the row*/
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 /*  ::struct::matrix::ChkColumnIndexAll --*/
02396 /* */
02397 /*  Helper to transform column indices. Returns the*/
02398 /*  absolute index number belonging to the specified*/
02399 /*  index.*/
02400 /* */
02401 /*  Arguments:*/
02402 /*  matrix  Matrix to look at*/
02403 /*  column  The incoming index to check and transform*/
02404 /* */
02405 /*  Results:*/
02406 /*  The absolute index to the column*/
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 /*  ::struct::matrix::ChkRowIndexAll --*/
02431 /* */
02432 /*  Helper to transform row indices. Returns the*/
02433 /*  absolute index number belonging to the specified*/
02434 /*  index.*/
02435 /* */
02436 /*  Arguments:*/
02437 /*  matrix  Matrix to look at*/
02438 /*  row The incoming index to check and transform*/
02439 /* */
02440 /*  Results:*/
02441 /*  The absolute index to the row*/
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 /*  ::struct::matrix::MatTraceIn --*/
02466 /* */
02467 /*  Helper propagating changes made to an array*/
02468 /*  into the matrix the array is linked to.*/
02469 /* */
02470 /*  Arguments:*/
02471 /*  avar        Name of the array which was changed.*/
02472 /*  name        Matrix to write the changes to.*/
02473 /*  var,idx,op  Standard trace arguments*/
02474 /* */
02475 /*  Results:*/
02476 /*  None.*/
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 /*  ::struct::matrix::MatTraceOut --*/
02533 /* */
02534 /*  Helper propagating changes made to the matrix into the linked arrays.*/
02535 /* */
02536 /*  Arguments:*/
02537 /*  avar        Name of the array to write the changes to.*/
02538 /*  name        Matrix which was changed.*/
02539 /*  var,idx,op  Standard trace arguments*/
02540 /* */
02541 /*  Results:*/
02542 /*  None.*/
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 /*  ::struct::matrix::SortMaxHeapify --*/
02584 /* */
02585 /*  Helper for the 'sort' method. Performs the central algorithm*/
02586 /*  which converts the matrix into a heap, easily sortable.*/
02587 /* */
02588 /*  Arguments:*/
02589 /*  name    Matrix object which is sorted.*/
02590 /*  i   Index of the row/column currently being sorted.*/
02591 /*  key Index of the column/row to sort the rows/columns by.*/
02592 /*  rowCol  Indicator if we are sorting rows ('r'), or columns ('c').*/
02593 /*  heapSize Number of rows/columns to sort.*/
02594 /*  rev Boolean flag, set if sorting is done revers (-decreasing).*/
02595 /* */
02596 /*  Sideeffects:*/
02597 /*  Transforms the matrix into a heap of rows/columns,*/
02598 /*  swapping them around.*/
02599 /* */
02600 /*  Results:*/
02601 /*  None.*/
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 /*  ::struct::matrix::CheckSerialization --*/
02645 /* */
02646 /*  Validate the serialization of a matrix.*/
02647 /* */
02648 /*  Arguments:*/
02649 /*  ser Serialization to validate.*/
02650 /*  rvar    Variable to store the number of rows into.*/
02651 /*  cvar    Variable to store the number of columns into.*/
02652 /*  dvar    Variable to store the matrix data into.*/
02653 /* */
02654 /*  Results:*/
02655 /*  none*/
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 /*  ::struct::matrix::DeleteRows --*/
02707 /* */
02708 /*  Deletes n rows from the bottom of the matrix.*/
02709 /* */
02710 /*  Arguments:*/
02711 /*  name    Name of the matrix.*/
02712 /*  n   The number of rows to delete (no greater than the number of rows).*/
02713 /* */
02714 /*  Results:*/
02715 /*  None.*/
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 /*  ::struct::matrix::DeleteColumns --*/
02743 /* */
02744 /*  Deletes n columns from the right of the matrix.*/
02745 /* */
02746 /*  Arguments:*/
02747 /*  name    Name of the matrix.*/
02748 /*  n   The number of columns to delete.*/
02749 /* */
02750 /*  Results:*/
02751 /*  None.*/
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 /*  Ready*/
02781 
02782 namespace ::struct {
02783     /*  Get 'matrix::matrix' into the general structure namespace.*/
02784     namespace import -force matrix::matrix
02785     namespace export matrix
02786 }
02787 package provide struct::matrix 2.0.1
02788 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1