Complex math with TOOT

Richard Suchenwirth 2005-03-25 - TOOT is "transparent OO for Tcl" - a "pure-value" approach, where a value (a listable string) like

 {Foo | bar grill}

is interpreted to be of class Foo (so Foo's methods can be applied to it), with the "instance variable" values bar and ''grill'. (In fact, they aren't "variable" - values are immutable).

The "|" as second element just identifies this as a TOOT value. "Transparent" means that a TOOT has nothing to hide - its state is fully visible in the string representation. For a "constructor", list is sufficient:

set c [list Complex | $real $imag]

but a few keystrokes add "constructor sugar", so you might as well call

set c [Complex | $real $imag]

No garbage collection is necessary: like all Tcl values, TOOT values just go away when nobody wants them any more (e.g. on return when associated with a local variable).

Methods are called as usual, with the method name (which can also look like "+") after the object, and then possibly more arguments. The example with the multiplication method named, evidently, "*" shows that the appearance of infix arithmetics comes as a by-product:

set i {Complex | 0 1}         ;#-- imaginary unit i
$i * $i  ==> {Complex | -1 0} ;#-- such that i*i == -1

Reinventing Complex math made simple, here's my experiments. Binary operators take one argument, which can be another Complex, or a real number x, which then gets "upgraded" to [Complex | $x 0]. A single proc contains all methods for the Complex "class":

 proc Complex {| r i {method ""} args} {
    if {[llength $args]==1} {
        set operand [lindex $args 0]
        if [string is double -strict $operand] {
            set r2 $operand; set i2 0
        } else {foreach {Complex | r2 i2} $operand break}
    }
    switch -- $method {
        ""   {list Complex | $r $i ;#--constructor sugar} 
        real {set r}
        imag {set i}
        abs  {expr {hypot($i,$r)}}
        arg  {expr {atan2($i,$r)}}
        format {expr {$i ? "$r+i*$i" : $r}}
        ==   {expr {$r==$r2 && $i==$i2}}
        !=   {expr {!($r==$r2 && $i==$i2)}}
        +    {Complex | [+ $r $r2] [+ $i $i2]} 
        -    {Complex | [- $r $r2] [- $i $i2]}
        *    {Complex | [expr {$r*$r2 - $i*$i2}] \
                             [expr {$r*$i2 + $r2*$i}]
             }
        /    {set div [expr {$r2*$r2 + $i2*$i2}]
              Complex | [expr {($r*$r2 + $i*$i2) / $div}] \ 
                             [expr {($r2*$i - $r*$i2) / $div}]
             }
        default {error "unknown method $method"}
    }
 }
#-- For convenience, [expr] operators are exported as commands:
 foreach op {+ - * /} {proc $op {a b} "expr {\$a $op \$b}"}

To make this flavor of TOOT work, we let unknown know it shall auto-expand the first word if its second element is "|" :

 proc know what {proc unknown args $what\n[info body unknown]}
 know {
    if {[lindex $args 0 1] eq "|"} {
        return [uplevel 1 [lindex $args 0] [lrange $args 1 end]]
    }
 }
#-- This little tester reports the unexpected
 proc ? {cmd expected} {
    catch {uplevel 1 $cmd} res
    if {$res ne $expected} {puts "$cmd -> $res, expected $expected"}
 }
#-- The test suite passes silently when all goes well:
 set i {Complex | 0 1}     ;# flat value
 set b [Complex | 3 4] ;# constructor sugar
 ? {$b format} {3+i*4}
 ? {$b abs}     5.0
 ? {$b real}    3
 ? {$b imag}    4
 ? {$i == $i}   1
 ? {$i == $b}   0
 ? {$i != $b}   1
 ? {$i + $b}    {Complex | 3 5}
 ? {$b - $i}    {Complex | 3 3}
 ? {$b + 1}     {Complex | 4 4}
 ? {$i * $i}    {Complex | -1 0} ;# i**2 == -1?
 ? {[$i * $i] format} -1
 set r   [$b abs]
 set phi [$b arg]
 ? {[[Complex | [expr cos($phi)] [expr sin($phi)]] * $r] == $b} 1
 ? {$b / 0}               "divide by zero"
 ? {$b / {Complex | 0 0}} "divide by zero"
 ? {$a foo}               "unknown method foo"

See also Straightforward implementation of complex numbers which is included in recent Tcllib