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



with javascript_semantics
constant keyword = "Playfair example",
         option = 'Q' -- ignore Q
--       option = 'J' -- replace J with I

sequence table,
         findchar
 
procedure build_table()
    table = repeat(repeat(' ',5),5)
    findchar = repeat(0,26)
    findchar[option-'A'+1] = {0,0}
    -- (note any (J/Q) in keyword are simply ignored)
    string alphabet = upper(keyword) & tagset('Z','A')
    integer i=1, j=1
    for k=1 to length(alphabet) do
        integer c = alphabet[k]
        if c>='A' and c<='Z' then
            integer d = c-'A'+1
            if findchar[d]=0 then
                table[i][j] = c
                findchar[d] = {i,j}
                j += 1
                if j=6 then
                    i += 1
                    if i=6 then exit end if -- table filled
                    j = 1
                end if
            end if
        end if
    end for
end procedure
build_table() 

function clean_text(string plaintext)
--
-- get rid of any non-letters and insert X between duplicate letters
--
    plaintext = upper(plaintext)
    string cleantext = ""
    integer prevChar = -1
    for i=1 to length(plaintext) do
        integer nextChar = plaintext[i]
        if nextChar>='A' and nextChar<='Z'
        and (nextChar!='Q' or option!='Q') then
            if nextChar='J' and option='J' then nextChar = 'I' end if
            if nextChar=prevChar then
                cleantext &= 'X'
            end if
            cleantext &= nextChar
            prevChar = nextChar
        end if
    end for
    if odd(length(cleantext)) then
        -- dangling letter at end so add another letter to complete digraph
        cleantext &= iff(cleantext[$]='X'?'Z':'X')
    end if
    return cleantext
end function
 
function remove_x(string text)
    for i=2 to length(text)-1 do
        if text[i]='X'
        and ((text[i-1]=' ' and text[i-2]=text[i+1]) or
             (text[i+1]=' ' and text[i-1]=text[i+2])) then
            text[i] = '_'
        end if
    end for
    return text
end function
 
function playfair(string text, integer step, sequence d)
    --
    -- text may be the plaintext or the encoded version
    -- step is 2 for plaintext, 3 for encoded(/skip spaces)
    -- d is {2,3,4,5,1} instead of mod(+1,5), or
    --      {5,1,2,3,4}   --           -1
    --
    string res = ""
    for i=1 to length(text) by step do
        integer {row1, col1} = findchar[text[i]-'A'+1],
                {row2, col2} = findchar[text[i+1]-'A'+1]
        if i>1 then res &= " " end if
        if row1=row2 then
            {col1,col2} = {d[col1],d[col2]}
        elsif col1=col2 then
            {row1,row2} = {d[row1],d[row2]}
        else
            {col1,col2} = {col2,col1}
        end if
        res &= table[row1][col1] & table[row2][col2]
    end for
    return res
end function
 
function encode(string plaintext)
    return playfair(clean_text(plaintext),2,{2,3,4,5,1})
end function
 
function decode(string ciphertext)
    -- ciphertext includes spaces we need to skip, hence by 3
    return remove_x(playfair(ciphertext,3,{5,1,2,3,4}))
end function
 
printf(1,"Playfair keyword : %s\n",{keyword})
printf(1,"Option: J=I or no Q (J/Q) : %s\n",option)
printf(1,"The table to be used is :\n\n%s\n\n",{join(table,"\n")})
string plaintext = "Hide the gold...in the TREESTUMP!!!!",
       encoded = encode(plaintext),
       decoded = decode(encoded)
printf(1,"The plain text : %s\n\n",{plaintext})
printf(1,"Encoded text is : %s\n",{encoded})
printf(1,"Decoded text is : %s\n",{decoded})
