Playfair cipher

You are encouraged to solve this task according to the task description, using any language you may know.
Task

Implement a  Playfair cipher for encryption and decryption.



The user must be able to choose   J  =  I     or   no   Q   in the alphabet.

The output of the encrypted and decrypted message must be in capitalized digraphs, separated by spaces.



Output example

                HI DE TH EG OL DI NT HE TR EX ES TU MP



package require TclOO
 
oo::class create Playfair {
    variable grid lookup excluder
    constructor {{keyword "PLAYFAIR EXAMPLE"} {exclude "J"}} {
        # Tweaking according to exact operation mode
        if {$exclude eq "J"} {
            set excluder "J I"
        } else {
            set excluder [list $exclude ""]
        }
 
        # Clean up the keyword source
        set keys [my Clean [append keyword "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]]
 
        # Generate the encoding grid
        set grid [lrepeat 5 [lrepeat 5 ""]]
        set idx -1
        for {set i 0} {$i < 5} {incr i} {for {set j 0} {$j < 5} {} {
            if {![info exist lookup([set c [lindex $keys [incr idx]]])]} {
                lset grid $i $j $c
                set lookup($c) [list $i $j]
                incr j
            }
        }}
 
        # Sanity check
        if {[array size lookup] != 25} {
            error "failed to build encoding table correctly"
        }
    }
 
    # Worker to apply a consistent cleanup/split rule
    method Clean {str} {
        set str [string map $excluder [string toupper $str]]
        split [regsub -all {[^A-Z]} $str ""] ""
    }
 
    # These public methods are implemented by a single non-public method
    forward encode my Transform 1
    forward decode my Transform -1
 
    # The application of the Playfair cypher transform
    method Transform {direction message} {
        # Split message into true digraphs
        foreach c [my Clean $message] {
            if {![info exists lookup($c)]} continue
            if {![info exists c0]} {
                set c0 $c
            } else {
                if {$c0 ne $c} {
                    lappend digraphs $c0 $c
                    unset c0
                } else {
                    lappend digraphs $c0 "X"
                    set c0 $c
                }
            }
        }
        if {[info exists c0]} {
            lappend digraphs $c0 "Z"
        }
 
        # Encode the digraphs
        set result ""
        foreach {a b} $digraphs {
            lassign $lookup($a) ai aj
            lassign $lookup($b) bi bj
            if {$ai == $bi} {
                set aj [expr {($aj + $direction) % 5}]
                set bj [expr {($bj + $direction) % 5}]
            } elseif {$aj == $bj} {
                set ai [expr {($ai + $direction) % 5}]
                set bi [expr {($bi + $direction) % 5}]
            } else {
                set tmp $aj
                set aj $bj
                set bj $tmp
            }
            lappend result [lindex $grid $ai $aj][lindex $grid $bi $bj]
        }
 
        # Real use would be: return [join $result ""]
        return $result
    }
}