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

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1