Richard Suchenwirth 2002-06-01 - After function mapping, here is an application for matrix multiplications: encryption. We use map to transpose the second matrix in multiplication, and again to multiply elementwise:
proc mat* {A B} { set C {} set Bt [eval map list $B] foreach arow $A { set crow {} foreach bcol $Bt { lappend crow [sum [map * $arow $bcol]] } lappend C $crow } set C } proc map {fun args} { set cmd "foreach " set fargs {} for {set i 0} {$i<[llength $args]} {incr i} { append cmd "$i [string map [list @ $i] {[lindex $args @]}] " lappend fargs $$i } append cmd [string map [list @f $fun @a [join $fargs]] {{ lappend res [@f @a] }}] set res {} eval $cmd set res } proc * {a b} {expr {$a*$b}} proc sum list {expr [join $list +]}
if 0 {We can do matrix multiplication now - so what? Here's an interesting application in cryptography (from a German math book): given a string to encrypt, e.g.
This is matrix magic
convert each character to a number (e.g. decimal Unicode); group each four characters into a 2x2 matrix; multiply that with a same-sized encryption matrix; transmit the resulting sequence of matrix contents, which in the example look like
-20 208 -10 230 -73 210 83 64 12 194 2 228 -15 240 -77 218 -6 206 6 198
Decoding such a cryptogram goes backwards: group the numbers into 2x2 matrixes; multiply with the inverse of the encryption matrix (this is the really secret part); format into the corresponding characters.
This simple encryption has the property that same letters have unequal equivalents, depending on the context - e.g. "i" comes as -10, 210, -15, 6 above; but equal 4-grams of course have the same code:
% matrixEncrypt foolfool => -9 222 3 216 -9 222 3 216
}
proc matrixEncrypt {s {M {{1 0} {-1 2}}}} { if {[set ls [string length $s]]%4} { append s [string repeat " " [expr 4-$ls%4]] } set res "" foreach {a b c d} [map c2i [split $s ""]] { eval lappend res [mat* "{$a $b} {$c $d}" $M] } eval concat $res ;# flatten the list of rows } proc matrixDecrypt {numbers {M {{1 0} {.5 .5}}}} { foreach {a b c d} $numbers { eval lappend t [mat* "{$a $b} {$c $d}" $M] } join [map n2c [eval concat $t]] "" } #------------ little helper functions for mapping: proc c2i c {scan $c %c} proc n2c n {format %c [expr round($n)]}
if 0 {We can experiment how cryptic the output gets:
% matrixEncrypt a 65 64 0 64 % matrixEncrypt aa 0 194 0 64 % matrixEncrypt aaa 0 194 65 64 % matrixEncrypt aaaa 0 194 0 194 % matrixEncrypt "Hello, world" -29 202 0 216 67 88 -87 238 -3 228 8 200
One might also try larger matrix sizes - but what's always required is that sender and receiver agree on one matrix, and the inverse matrix is used in decoding - just I don't have matrix inversion algorithms handy ;-( }
Category Cryptography | Arts and crafts of Tcl-Tk programming