Playfair cipher: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Wren}}: Minor tidy)
 
(29 intermediate revisions by 15 users not shown)
Line 1: Line 1:
{{draft task}} [[Category:Encryption]]
[[Category:Encryption]]

{{task}}

;Task:
Implement a [[wp: Playfair cipher| Playfair cipher]] for encryption and decryption.
Implement a [[wp: Playfair cipher| Playfair cipher]] for encryption and decryption.



The user must be able to choose J = I or no Q in the alphabet.
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.
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.
;Output example:
HI DE TH EG OL DI NT HE TR EX ES TU MP
<br><br>

=={{header|11l}}==
{{trans|Python}}

<syntaxhighlight lang="11l">F uniq(seq)
[Char] seen
L(x) seq
I x !C seen
seen.append(x)
R seen

F partition(seq, n)
R (0 .< seq.len).step(n).map(i -> @seq[i .< i + @n])

F canonicalize(s)
R s.uppercase().filter(c -> c.is_uppercase()).join(‘’).replace(‘J’, ‘I’)

T Playfair
[String = String] dec, enc

F (key)
V m = partition(uniq(canonicalize(key‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)), 5)

L(row) m
L(i, j) cart_product(0.<5, 0.<5)
I i != j
.enc[row[i]‘’row[j]] = row[(i + 1) % 5]‘’row[(j + 1) % 5]

L(ci) 5
V c = [m[0][ci], m[1][ci], m[2][ci], m[3][ci], m[4][ci]]
L(i, j) cart_product(0.<5, 0.<5)
I i != j
.enc[c[i]‘’c[j]] = c[(i + 1) % 5]‘’c[(j + 1) % 5]

L(i1, j1, i2, j2) cart_product(0.<5, 0.<5, 0.<5, 0.<5)
I i1 != i2 & j1 != j2
.enc[m[i1][j1]‘’m[i2][j2]] = m[i1][j2]‘’m[i2][j1]

.dec = Dict(.enc.map((k, v) -> (v, k)))

F encode(txt)
V c = canonicalize(txt)
[String] lst
V i = 0
L i < c.len - 1
I c[i + 1] == c[i]
lst [+]= c[i]‘X’
i++
E
lst [+]= c[i]‘’c[i + 1]
i += 2
I i == c.len - 1
lst [+]= c.last‘X’
R lst.map(a -> @.enc[a]).join(‘ ’)

F decode(encoded)
R partition(canonicalize(encoded), 2).map(p -> @.dec[p]).join(‘ ’)

V playfair = Playfair(‘Playfair example’)
V orig = ‘Hide the gold in...the TREESTUMP!!!’
print(‘Original: ’orig)
V enc = playfair.encode(orig)
print(‘Encoded: ’enc)
print(‘Decoded: ’playfair.decode(enc))</syntaxhighlight>

{{out}}
<pre>
Original: Hide the gold in...the TREESTUMP!!!
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP
</pre>

=={{header|APL}}==
{{works with|Dyalog APL}}
<syntaxhighlight lang="apl">⍝⍝⍝⍝ Utility functions

⍝ convert to uppercase
UpCase ← { 1 ⎕C ⍵ }

⍝ remove non-letters
JustLetters ← { ⍵ /⍨ ⍵∊⎕A }

⍝ replace 'J's with 'I's
ReplaceJ ← { ('J' ⎕R 'I') ⍵ }

⍝ Insert an 'X' between repeated letters
SplitDouble ← { (' '⎕R'X') ⍵ \⍨ 1,~⍵=1⌽⍵ }

⍝ Append an 'X' if the message is not of even length
PadEven ← { ⍵,(2|≢⍵) ⍴ 'X' }

⍝ Split text up into letter pairs
Pairs ← { (1=2|⍳≢⍵) ⊂ ∊ ⍵ }

⍝ Group text into chunks of five letters
Groups ← { (1=5|⍳≢⍵) ⊂ ∊ ⍵ }

⍝ Shift within 1-5 based on left arg (0 for +1,1 for -1)
Shift ← { ⍺ ← 0 ⋄ 1+5|(⍺+1)⌷⍵,3+⍵ }

⍝⍝⍝⍝ Playfair implementation

⍝ All the things we have to do to the plaintext, chained together
PreparePlaintext ← { PadEven SplitDouble ReplaceJ JustLetters UpCase ∊ ⍵ }

⍝ Ditto for ciphertext
PrepareCiphertext ← { JustLetters UpCase ∊ ⍵ }

⍝ Create the grid from the key
PrepareKey ← { 5 5⍴ ∪ ReplaceJ (JustLetters UpCase ⍵),⎕A }

⍝ Encode or decode a single pair of letters
∇resultPair ← grid TransformPair args;mode;inPair;l;r;i1;j1;i2;j2
mode inPair ← args
l r ← inPair
i1 j1 ← ⊃⍸grid=l
i2 j2 ← ⊃⍸grid=r
:If i1=i2
j1 ← mode Shift j1
j2 ← mode Shift j2
:Else
:If j1=j2
i1 ← mode Shift i1
i2 ← mode Shift i2
:Else
j1 j2 ← j2 j1
:EndIf
:EndIf
resultPair ← grid[(i1 j1)(i2 j2)]

⍝ Encode or decode an entire message
∇resultText ← grid TransformText args; mode; inText
mode inText ← args
resultText ← Groups ∊ { grid TransformPair mode ⍵ } ¨ Pairs inText

⍝ Specific transforms for each direction including key and text preparation
∇cipher ← key EncodeText plain
cipher ← (PrepareKey key) TransformText 0 (PreparePlaintext plain)

∇plain ← key DecodeText cipher
plain ← (PrepareKey key) TransformText 1 (PrepareCiphertext cipher)

⍝ Demo
key ← 'Playfair example'
plain ← 'Hide the gold in the tree stump.'
⎕ ← cipher ← key EncodeText plain
⎕ ← key DecodeText cipher</syntaxhighlight>

{{Out}}<pre> ⎕ ← cipher ← key EncodeText plain
┌─────┬─────┬─────┬─────┬─────┬─┐
│BMODZ│BXDNA│BEKUD│MUIXM│MOUVI│F│
└─────┴─────┴─────┴─────┴─────┴─┘
</pre>
<pre> ⎕ ← key DecodeText cipher
┌─────┬─────┬─────┬─────┬─────┬─┐
│HIDET│HEGOL│DINTH│ETREX│ESTUM│P│
└─────┴─────┴─────┴─────┴─────┴─┘
</pre>


=={{header|C++}}==
=={{header|C++}}==
<lang cpp>#include <iostream>
<syntaxhighlight lang="cpp">#include <iostream>
#include <string>
#include <string>


Line 118: Line 288:
cout << "Enter the text: "; getline( cin, txt );
cout << "Enter the text: "; getline( cin, txt );
playfair pf; pf.doIt( key, txt, ij, e ); return system( "pause" );
playfair pf; pf.doIt( key, txt, ij, e ); return system( "pause" );
}</lang>
}</syntaxhighlight>
{{out}}<pre>
{{out}}<pre>
(E)ncode or (D)ecode? e
(E)ncode or (D)ecode? e
Line 142: Line 312:
=={{header|D}}==
=={{header|D}}==
{{trans|Python}}
{{trans|Python}}
<lang d>import std.stdio, std.array, std.algorithm, std.range, std.ascii,
<syntaxhighlight lang="d">import std.stdio, std.array, std.algorithm, std.range, std.ascii,
std.conv, std.string, std.regex, std.typecons;
std.conv, std.string, std.regex, std.typecons;


Line 212: Line 382:
writeln(" Encoded: ", enc);
writeln(" Encoded: ", enc);
writeln(" Decoded: ", pf.decode(enc));
writeln(" Decoded: ", pf.decode(enc));
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>Original: Hide the gold in...the TREESTUMP!!!
<pre>Original: Hide the gold in...the TREESTUMP!!!
Line 219: Line 389:


=={{header|FreeBASIC}}==
=={{header|FreeBASIC}}==
<lang freebasic>' FB 1.05.0 Win64
<syntaxhighlight lang="freebasic">' FB 1.05.0 Win64


Enum PlayFairOption
Enum PlayFairOption
Line 374: Line 544:
Print
Print
Print "Press any key to quit"
Print "Press any key to quit"
Sleep</lang>
Sleep</syntaxhighlight>
Sample input/output:
Sample input/output:
{{out}}
{{out}}
Line 396: Line 566:
=={{header|Go}}==
=={{header|Go}}==
{{trans|Kotlin}}
{{trans|Kotlin}}
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 603: Line 773:
decodedText := pf.decode(encodedText)
decodedText := pf.decode(encodedText)
fmt.Println("Deccoded text is :", decodedText)
fmt.Println("Deccoded text is :", decodedText)
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 627: Line 797:
{{incorrect|Haskell|TREESTUMP -> TREXSTUMPX, should be TREXESTUMP}}
{{incorrect|Haskell|TREESTUMP -> TREXSTUMPX, should be TREXESTUMP}}
(My guess is that map (\[x, y] -> if x == y then [x, 'x'] else [x, y]).chunksOf 2 is simply discarding the y. [[User:Petelomax|Pete Lomax]] ([[User talk:Petelomax|talk]]) 05:54, 13 October 2018 (UTC))
(My guess is that map (\[x, y] -> if x == y then [x, 'x'] else [x, y]).chunksOf 2 is simply discarding the y. [[User:Petelomax|Pete Lomax]] ([[User talk:Petelomax|talk]]) 05:54, 13 October 2018 (UTC))
<lang haskell>
<syntaxhighlight lang="haskell">
import Control.Monad (guard)
import Control.Monad (guard)
import Data.Array (Array, assocs, elems, listArray, (!))
import Data.Array (Array, assocs, elems, listArray, (!))
Line 729: Line 899:
| odd (length str) = str ++ "x"
| odd (length str) = str ++ "x"
| otherwise = str
| otherwise = str
</syntaxhighlight>
</lang>


<pre>
<pre>
Line 740: Line 910:


=={{header|J}}==
=={{header|J}}==
{{incorrect|J|TREESTUMP -> TREXSTUMPX, should be TREXESTUMP}}
Rather than implement two versions of the rules, one for encrypt, one for decrypt, let's just make a lookup table (mapping character pairs to character pairs). To decrypt we can look up in the other direction.
Rather than implement two versions of the rules, one for encrypt, one for decrypt, let's just make a lookup table (mapping character pairs to character pairs). To decrypt we can look up in the other direction.


Implementation:
'''Implementation:'''
<syntaxhighlight lang="j">choose=: verb define

sel=. 'Q' e. y
<lang J>choose=:3 :0
alph=: (sel { 'JQ') -.~ a. {~ 65 + i.26
sel=. 'Q'e.y
norm=: [: dedouble alph restrict ('I' I.@:=&'J'@]} ])`(-.&'Q')@.sel@toupper
alph=:(sel{'JQ')-.~a.{~65+i.26
norm=: alph restrict('I' I.@:=&'J'@]} ])`(-.&'Q')@.sel@toupper
''
''
)
)

restrict=: ] -. -.~
restrict=: ] -. -.~
choose 'Q'


splitDigraph=: ,`([,'X',])@.((= {.) *. 2 | #@])
setkey=:3 :0
dedouble=: splitDigraph/&.|. NB. progressively split digraphs in string

choose 'Q'
setkey=: verb define
key=. ~.norm y,alph
key=. ~.norm y,alph
ref=: ,/2{."1 ~."1 (,"0/~alph),"1 norm 'XQV'
ref=: ,/ 2{."1 ~."1 (,"0/~ alph) ,"1 norm 'XQV'
mode=. #.=/"2 inds=.5 5#:key i.ref
mode=. #. =/"2 inds=. 5 5#:key i. ref
inds0=. (0 3,:2 1)&{@,"2 inds
inds0=. (0 3,:2 1)&{@,"2 inds
inds1=.5|1 0+"1 inds NB. same column
inds1=. 5|1 0 +"1 inds NB. same column
inds2=.5|0 1+"1 inds NB. same row
inds2=. 5|0 1 +"1 inds NB. same row
alt=:key{~5 #.mode{"_1 inds0,"2 3 inds1,:"2 inds2
alt=: key {~ 5 #. mode {"_1 inds0 ,"2 3 inds1 ,:"2 inds2
i.0 0
i. 0 0
)
)

pairs=: 3 :0
pairs=: verb define
2{."1 -.&' '"1 ~."1 (_2]\ norm y),"1 'XQV'
2{."1 -.&' '"1 ~."1 (_2]\ norm y) ,"1 'XQV'
)
)

encrypt=:3 :0
encrypt=: verb define
,alt{~ref i. pairs y
;:inv ;/ alt{~ref i. pairs y
)
)
decrypt=: verb define
, ref{~alt i. pairs y
)</syntaxhighlight>


'''Example use:'''
decrypt=:3 :0
<syntaxhighlight lang="j"> choose 'IJ'
,ref{~alt i. pairs y
)</lang>

Example use:

<lang J> choose 'IJ'


setkey 'playfair example'
setkey 'playfair example'
encrypt 'Hide the gold in the tree stump'
encrypt 'Hide the gold in the tree stump'
BM OD ZB XD NA BE KU DM UI XM MO UV IF
BMODZBXDNABEKUDMUIXMKZZRYI
decrypt 'BMODZBXDNABEKUDMUIXMKZZRYI'
decrypt 'BM OD ZB XD NA BE KU DM UI XM MO UV IF'
HIDETHEGOLDINTHETREXESTUMP</syntaxhighlight>
HIDETHEGOLDINTHETREXSTUMPX</lang>


=={{header|Java}}==
=={{header|Java}}==
<lang java>import java.awt.Point;
<syntaxhighlight lang="java">import java.awt.Point;
import java.util.Scanner;
import java.util.Scanner;


Line 891: Line 1,063:
return text.toString();
return text.toString();
}
}
}</lang>
}</syntaxhighlight>


=== alternative version ===
=== alternative version ===


<lang java>import java.util.Scanner;
<syntaxhighlight lang="java">import java.util.Scanner;
public class PlayfairCipherEncryption
public class PlayfairCipherEncryption
Line 1,099: Line 1,271:
sc.close();
sc.close();
}
}
}</lang>
}</syntaxhighlight>



=={{header|Julia}}==
=={{header|Julia}}==
<lang julia>function playfair(key, txt, isencode=true, from = "J", to = "")
<syntaxhighlight lang="julia">function playfair(key, txt, isencode=true, from = "J", to = "")
to = (to == "" && from == "J") ? "I" : to
to = (to == "" && from == "J") ? "I" : to


Line 1,126: Line 1,297:
encod = Dict()
encod = Dict()


# Map pairs in same row of matrix m.
# Map pairs in same row or same column of matrix m.
for i in 1:5, j in 1:5, k in 1:5
for i in 1:5, j in 1:5, k in 1:5
if j != k
if j != k
encod[m[i, j] * m[i, k]] = m[i, mod1(j + 1, 5)] * m[i, mod1(k + 1, 5)]
encod[m[i, j] * m[i, k]] = m[i, mod1(j + 1, 5)] * m[i, mod1(k + 1, 5)]
end
end
end

# Map pairs in same column of matrix m.
for j in 1:5, i in 1:5, k in 1:5
if i != k
if i != k
encod[m[i, j] * m[k, j]] = m[mod1(i + 1, 5), j] * m[mod1(k + 1, 5), j]
encod[m[i, j] * m[k, j]] = m[mod1(i + 1, 5), j] * m[mod1(k + 1, 5), j]
end
end
# Map pairs not on same row or same column.
end
for l in 1:5

# Map pairs not on same row or same column.
if i != k && j != l
for (i1, j1, i2, j2) in [(a, b, c, d) for a in 1:5, b in 1:5, c in 1:5, d in 1:5]
encod[m[i, j] * m[k, l]] = m[i, l] * m[k, j]
if i1 != i2 && j1 != j2
end
encod[m[i1, j1] * m[i2, j2]] = m[i1, j2] * m[i2, j1]
end
end
end
end
Line 1,168: Line 1,334:


println("Decoded: ", playfair("Playfair example", encoded, false))
println("Decoded: ", playfair("Playfair example", encoded, false))
</lang>{{out}}
</syntaxhighlight>{{out}}
<pre>
<pre>
Original: Hide the gold in...the TREESTUMP!!!
Original: Hide the gold in...the TREESTUMP!!!
Line 1,177: Line 1,343:
=={{header|Kotlin}}==
=={{header|Kotlin}}==
{{trans|FreeBASIC}}
{{trans|FreeBASIC}}
<lang scala>// version 1.0.5-2
<syntaxhighlight lang="scala">// version 1.0.5-2


enum class PlayfairOption {
enum class PlayfairOption {
Line 1,309: Line 1,475:
val decodedText = playfair.decode(encodedText)
val decodedText = playfair.decode(encodedText)
println("Decoded text is : $decodedText")
println("Decoded text is : $decodedText")
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 1,328: Line 1,494:
Decoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP
Decoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP
</pre>
</pre>

=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">ClearAll[MakeTranslationTable, PlayfairCipher, PlayfairDecipher]
MakeTranslationTable[tt_List] := Module[{poss, in, out},
poss = Tuples[Tuples[Range[5], 2], 2];
Table[
If[p[[1, 1]] == p[[2, 1]],
(* same row *)
{in, out} = {p, {{p[[1, 1]], Mod[p[[1, 2]] + 1, 5, 1]}, {p[[2, 1]], Mod[p[[2, 2]] + 1, 5, 1]}}};
,
If[p[[1, 2]] == p[[2, 2]],
(* same column *)
{in, out} = {p, {{Mod[p[[1, 1]] + 1, 5, 1], p[[1, 2]]}, {Mod[p[[2, 1]] + 1, 5, 1], p[[2, 2]]}}};
,
(*rectangle*)
{in, out} = {p, {{p[[1, 1]], p[[2, 2]]}, {p[[2, 1]], p[[1, 2]]}}};
]
];
StringJoin[Extract[tt, in]] -> StringJoin[Extract[tt, out]]
,
{p, poss}
]
]
PlayfairCipher[txt_String, key_String, iisj_ : True] :=
Module[{text, tt},
text = RemoveDiacritics[ToUpperCase[txt]];
tt = RemoveDiacritics[ToUpperCase[key]] <> CharacterRange["A", "Z"];
text //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
tt //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
If[iisj,
tt //= StringReplace["J" -> "I"];
text //= StringReplace["J" -> "I"];
,
tt //= StringReplace["Q" -> ""];
text //= StringReplace["Q" -> ""];
];
tt //= Characters /* DeleteDuplicates;
text = FixedPoint[StringReplace[#, x_ ~~ x_ :> x ~~ "X" ~~ x, 1] &, text];
If[OddQ[StringLength[text]], text = text <> "X"];
If[Length[tt] == 25,
tt = Partition[tt, 5];
tt = MakeTranslationTable[tt];
text = StringPartition[text, 2];
StringRiffle[text /. tt, " "]
,
Print["Something went wrong!"]
]
]
PlayfairDecipher[txt_String, key_String, iisj_ : True] :=
Module[{text, tt},
text = RemoveDiacritics[ToUpperCase[txt]];
tt = RemoveDiacritics[ToUpperCase[key]] <> CharacterRange["A", "Z"];
text //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
tt //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
If[iisj,
tt //= StringReplace["J" -> "I"];
text //= StringReplace["J" -> "I"];
,
tt //= StringReplace["Q" -> ""];
text //= StringReplace["Q" -> ""];
];
tt //= Characters /* DeleteDuplicates;
If[OddQ[StringLength[text]], text = text <> "X"];
If[Length[tt] == 25,
tt = Partition[tt, 5];
tt = MakeTranslationTable[tt];
text = StringPartition[text, 2];
StringRiffle[text /. (Reverse /@ tt), " "]
,
Print["Something went wrong!"]
]
]
PlayfairCipher["Hide the gold in...the TREESTUMP!!!", "Playfair example"]
PlayfairDecipher[%, "Playfair example"]</syntaxhighlight>
{{out}}
<pre>BM OD ZB XD NA BE KU DM UI XM MO UV IF
HI DE TH EG OL DI NT HE TR EX ES TU MP</pre>

=={{header|Nim}}==
{{trans|Java}}
<syntaxhighlight lang="nim">import pegs, strutils

type
Position = tuple[x, y: int]
Playfair = object
positions: array['A'..'Z', Position]
table: array[5, array[5, char]]

const None: Position = (-1, -1) # Default value for positions.


proc initPlayfair(key: string; jti: bool): Playfair =

for item in result.positions.mitems: item = None

var alphabet = key & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alphabet = if jti: alphabet.replace('J', 'I')
else: alphabet.replace("Q", "")

var k = 0
for ch in alphabet:
if result.positions[ch] == None:
result.table[k div 5][k mod 5] = ch
result.positions[ch] = (k mod 5, k div 5)
inc k


proc codec(playfair: Playfair; text: string; direction: int): string =

result.setLen(text.len)

for i in countup(0, text.high, 2):
var
(col1, row1) = playfair.positions[text[i]]
(col2, row2) = playfair.positions[text[i + 1]]

if row1 == row2:
col1 = (col1 + direction) mod 5
col2 = (col2 + direction) mod 5
elif col1 == col2:
row1 = (row1 + direction) mod 5
row2 = (row2 + direction) mod 5
else:
swap col1, col2

result[i] = playfair.table[row1][col1]
result[i + 1] = playfair.table[row2][col2]


proc encode(playfair: Playfair; text: string): string =
var
text = text
i = 0

while i < text.len:
if i == text.high:
if (text.len and 1) != 0:
text.add 'X'
elif text[i] == text[i + 1]:
text.insert("X", i + 1)
inc i, 2

result = playfair.codec(text, 1)


proc decode(playfair: Playfair; text: string): string =
result = playfair.codec(text, 4)


proc prompt(msg: string): string =
stdout.write msg
try:
result = stdin.readLine()
except EOFError:
echo ""
quit getCurrentExceptionMsg(), QuitFailure


when isMainModule:

var key: string
while key.len <= 6:
key = prompt("Enter an encryption key (min letters 6): ").toUpperAscii().replace(peg"[^A-Z]")

var text: string
while text.len == 0:
text = prompt("Enter the message: ").toUpperAscii().replace(peg"[^A-Z]")

var answer: string
while answer notin ["y", "n"]:
answer = prompt("Replace J with I? y/n: ").toLowerAscii()
let jti = (answer == "y")

let playfair = initPlayfair(key, jti)
let enc = playfair.encode(text)
let dec = playfair.decode(enc)

echo "Encoded message: ", enc
echo "Decoded message: ", dec</syntaxhighlight>

{{out}}
<pre>Enter an encryption key (min letters 6): Playfair example
Enter the message: Hide the gold...in the TREESTUMP!!!
Replace J with I? y/n: y
Encoded message: BMODZBXDNABEKUDMUIXMMOUVIF
Decoded message: HIDETHEGOLDINTHETREXESTUMP</pre>


=={{header|NetRexx}}==
=={{header|NetRexx}}==
<lang NetRexx>/* NetRexx */
<syntaxhighlight lang="netrexx">/* NetRexx */
options replace format comments java crossref symbols nobinary
options replace format comments java crossref symbols nobinary


Line 1,495: Line 1,847:


return
return
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 1,518: Line 1,870:


=={{header|ooRexx}}==
=={{header|ooRexx}}==
<lang oorexx>/*---------------------------------------------------------------------
<syntaxhighlight lang="oorexx">/*---------------------------------------------------------------------
* REXX program implements a PLAYFAIR cipher (encryption & decryption).
* REXX program implements a PLAYFAIR cipher (encryption & decryption).
* 11.11.2013 Walter Pachl revamped, for ooRexx, the REXX program
* 11.11.2013 Walter Pachl revamped, for ooRexx, the REXX program
Line 1,715: Line 2,067:
d=d||substr(x,j,2)' '
d=d||substr(x,j,2)' '
End
End
Return strip(d)</lang>
Return strip(d)</syntaxhighlight>
Output (sample):
Output (sample):
<pre>old cipher key: this is my little key
<pre>old cipher key: this is my little key
Line 1,734: Line 2,086:
decoded text: TO BE OR NO TT OB EE Q
decoded text: TO BE OR NO TT OB EE Q
TOBEORNOTTOBEE
TOBEORNOTTOBEE
original text: TOBEORNOTTOBEE</pre>
original text: TOBEORNOTTOBEE</pre>


=={{header|Perl}}==
=={{header|Perl}}==
{{trans|Perl 6}}
{{trans|Raku}}
<lang perl>use Math::Cartesian::Product;
<syntaxhighlight lang="perl">use Math::Cartesian::Product;


# Pregenerate all forward and reverse translations
# Pregenerate all forward and reverse translations
Line 1,809: Line 2,161:
print " orig:\t$orig\n";
print " orig:\t$orig\n";
print "black:\t$black\n";
print "black:\t$black\n";
print " red:\t$red\n";</lang>
print " red:\t$red\n";</syntaxhighlight>
{{out}}
{{out}}
<pre> orig: Hide the gold in...the TREESTUMP!!!
<pre> orig: Hide the gold in...the TREESTUMP!!!
Line 1,815: Line 2,167:
red: HI DE TH EG OL DI NT HE TR EX ES TU MP
red: HI DE TH EG OL DI NT HE TR EX ES TU MP
</pre>
</pre>

=={{header|Perl 6}}==
{{Works with|rakudo|2016.07}}
<lang perl6># Instantiate a specific encoder/decoder.

sub playfair( $key,
$from = 'J',
$to = $from eq 'J' ?? 'I' !! ''
) {

sub canon($str) { $str.subst(/<-alpha>/,'', :g).uc.subst(/$from/,$to,:g) }

# Build 5x5 matrix.
my @m = canon($key ~ ('A'..'Z').join).comb.unique.map:
-> $a,$b,$c,$d,$e { [$a,$b,$c,$d,$e] }

# Pregenerate all forward translations.
my %ENC = gather {
# Map pairs in same row.
for @m -> @r {
for ^@r X ^@r -> (\i,\j) {
next if i == j;
take @r[i] ~ @r[j] => @r[(i+1)%5] ~ @r[(j+1)%5];
}
}

# Map pairs in same column.
for ^5 -> $c {
my @c = @m.map: *.[$c];
for ^@c X ^@c -> (\i,\j) {
next if i == j;
take @c[i] ~ @c[j] => @c[(i+1)%5] ~ @c[(j+1)%5];
}
}

# Map pairs with cross-connections.
for ^5 X ^5 X ^5 X ^5 -> (\i1,\j1,\i2,\j2) {
next if i1 == i2 or j1 == j2;
take @m[i1][j1] ~ @m[i2][j2] => @m[i1][j2] ~ @m[i2][j1];
}
}

# Generate reverse translations.
my %DEC = %ENC.invert;

return
anon sub enc($red) {
my @list = canon($red).comb(/(.) (.?) <?{ $1 ne $0 }>/);
~@list.map: { .chars == 1 ?? %ENC{$_~'X'} !! %ENC{$_} }
},
anon sub dec($black) {
my @list = canon($black).comb(/../);
~@list.map: { %DEC{$_} }
}
}

my (&encode,&decode) = playfair 'Playfair example';

my $orig = "Hide the gold in...the TREESTUMP!!!";
say " orig:\t$orig";

my $black = encode $orig;
say "black:\t$black";

my $red = decode $black;
say " red:\t$red";</lang>
{{out}}
<pre> orig: Hide the gold in...the TREESTUMP!!!
black: BM OD ZB XD NA BE KU DM UI XM MO UV IF
red: HI DE TH EG OL DI NT HE TR EX ES TU MP</pre>


=={{header|Phix}}==
=={{header|Phix}}==
Originally translated from Kotlin, now uses a combined routine (playfair) for encoding and decoding, and direct char lookups, and x removal.
Originally translated from Kotlin, now uses a combined routine (playfair) for encoding and decoding, and direct char lookups, and x removal.
<!--<syntaxhighlight lang="phix">(phixonline)-->
<lang Phix>sequence table,
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
findchar
<span style="color: #008080;">constant</span> <span style="color: #000000;">keyword</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Playfair example"</span><span style="color: #0000FF;">,</span>

<span style="color: #000000;">option</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'Q'</span> <span style="color: #000080;font-style:italic;">-- ignore Q
type pfoption(integer option)
-- option = 'J' -- replace J with I</span>
return find(option,"QJ")!=0
end type

pfoption pfo -- 'Q' or 'J'
<span style="color: #004080;">sequence</span> <span style="color: #000000;">table</span><span style="color: #0000FF;">,</span>
procedure build_table(string keyword, integer option)
<span style="color: #000000;">findchar</span>
-- option should be 'Q' to ignore Q, or 'J' to replace Js with I
pfo = option
<span style="color: #008080;">procedure</span> <span style="color: #000000;">build_table</span><span style="color: #0000FF;">()</span>
table = repeat(repeat(' ',5),5)
<span style="color: #000000;">table</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">),</span><span style="color: #000000;">5</span><span style="color: #0000FF;">)</span>
findchar = repeat(0,26)
<span style="color: #000000;">findchar</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">26</span><span style="color: #0000FF;">)</span>
findchar[pfo-'A'+1] = {0,0}
<span style="color: #000000;">findchar</span><span style="color: #0000FF;">[</span><span style="color: #000000;">option</span><span style="color: #0000FF;">-</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">}</span>
-- (note that any pfo (J/Q) in keyword are simply ignored)
<span style="color: #000080;font-style:italic;">-- (note any (J/Q) in keyword are simply ignored)</span>
string alphabet = upper(keyword) & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
<span style="color: #004080;">string</span> <span style="color: #000000;">alphabet</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">upper</span><span style="color: #0000FF;">(</span><span style="color: #000000;">keyword</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">&</span> <span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">)</span>
integer i=1, j=1
<span style="color: #004080;">integer</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span>
for k=1 to length(alphabet) do
<span style="color: #008080;">for</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">alphabet</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
integer c = alphabet[k]
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">alphabet</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span>
if c>='A' and c<='Z' then
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">c</span><span style="color: #0000FF;"><=</span><span style="color: #008000;">'Z'</span> <span style="color: #008080;">then</span>
integer d = c-'A'+1
<span style="color: #004080;">integer</span> <span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">-</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span>
if findchar[d]=0 then
<span style="color: #008080;">if</span> <span style="color: #000000;">findchar</span><span style="color: #0000FF;">[</span><span style="color: #000000;">d</span><span style="color: #0000FF;">]=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
table[i][j] = c
<span style="color: #000000;">table</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span>
findchar[d] = {i,j}
<span style="color: #000000;">findchar</span><span style="color: #0000FF;">[</span><span style="color: #000000;">d</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">j</span><span style="color: #0000FF;">}</span>
j += 1
<span style="color: #000000;">j</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
if j=6 then
<span style="color: #008080;">if</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">6</span> <span style="color: #008080;">then</span>
i += 1
if i=6 then exit end if -- table filled
<span style="color: #000000;">i</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">6</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- table filled</span>
j = 1
<span style="color: #000000;">j</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end for
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end procedure
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>

<span style="color: #000000;">build_table</span><span style="color: #0000FF;">()</span>
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 pfo!='Q') then
if nextChar='J' and pfo='J' then nextChar = 'I' end if
if nextChar=prevChar then
cleantext &= 'X'
end if
cleantext &= nextChar
prevChar = nextChar
end if
end for
if remainder(length(cleantext),2) then
-- dangling letter at end so add another letter to complete digram
cleantext &= iff(cleantext[$]='X'?'Z':'X')
end if
return cleantext
end function
<span style="color: #008080;">function</span> <span style="color: #000000;">clean_text</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">plaintext</span><span style="color: #0000FF;">)</span>
function remove_x(string text)
<span style="color: #000080;font-style:italic;">--
for i=2 to length(text)-1 do
-- get rid of any non-letters and insert X between duplicate letters
if text[i]='X'
--</span>
and ((text[i-1]=' ' and text[i-2]=text[i+1]) or
<span style="color: #000000;">plaintext</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">upper</span><span style="color: #0000FF;">(</span><span style="color: #000000;">plaintext</span><span style="color: #0000FF;">)</span>
(text[i+1]=' ' and text[i-1]=text[i+2])) then
<span style="color: #004080;">string</span> <span style="color: #000000;">cleantext</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
text[i] = '_'
<span style="color: #004080;">integer</span> <span style="color: #000000;">prevChar</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span>
end if
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">plaintext</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
end for
<span style="color: #004080;">integer</span> <span style="color: #000000;">nextChar</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">plaintext</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
return text
<span style="color: #008080;">if</span> <span style="color: #000000;">nextChar</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">nextChar</span><span style="color: #0000FF;"><=</span><span style="color: #008000;">'Z'</span>
end function
<span style="color: #008080;">and</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">nextChar</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'Q'</span> <span style="color: #008080;">or</span> <span style="color: #000000;">option</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'Q'</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>

<span style="color: #008080;">if</span> <span style="color: #000000;">nextChar</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'J'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">option</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'J'</span> <span style="color: #008080;">then</span> <span style="color: #000000;">nextChar</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'I'</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
function playfair(string text, integer step, sequence d)
<span style="color: #008080;">if</span> <span style="color: #000000;">nextChar</span><span style="color: #0000FF;">=</span><span style="color: #000000;">prevChar</span> <span style="color: #008080;">then</span>
string res = ""
<span style="color: #000000;">cleantext</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'X'</span>
for i=1 to length(text) by step do
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
integer {row1, col1} = findchar[text[i]-'A'+1],
<span style="color: #000000;">cleantext</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">nextChar</span>
{row2, col2} = findchar[text[i+1]-'A'+1]
<span style="color: #000000;">prevChar</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">nextChar</span>
if i>1 then res &= " " end if
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if row1=row2 then
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
res &= table[row1][d[col1]] & table[row2][d[col2]]
<span style="color: #008080;">if</span> <span style="color: #7060A8;">odd</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cleantext</span><span style="color: #0000FF;">))</span> <span style="color: #008080;">then</span>
elsif col1=col2 then
<span style="color: #000080;font-style:italic;">-- dangling letter at end so add another letter to complete digraph</span>
res &= table[d[row1]][col1] & table[d[row2]][col2]
<span style="color: #000000;">cleantext</span> <span style="color: #0000FF;">&=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cleantext</span><span style="color: #0000FF;">[$]=</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">?</span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">:</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">)</span>
else
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
res &= table[row1][col2] & table[row2][col1]
<span style="color: #008080;">return</span> <span style="color: #000000;">cleantext</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
end for
return res
<span style="color: #008080;">function</span> <span style="color: #000000;">remove_x</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">)</span>
end function
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">text</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>

<span style="color: #008080;">if</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">'X'</span>
constant p1 = {2,3,4,5,1}, -- easier than playing with mod(+1,5)
<span style="color: #008080;">and</span> <span style="color: #0000FF;">((</span><span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">and</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]=</span><span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">or</span>
m1 = {5,1,2,3,4} -- "" mod(-1,5)
<span style="color: #0000FF;">(</span><span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">and</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]))</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'_'</span>
function encode(string plaintext)
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
return playfair(clean_text(plaintext),2,p1)
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end function
<span style="color: #008080;">return</span> <span style="color: #000000;">text</span>

<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
function decode(string ciphertext)
-- ciphertext includes spaces we need to skip, hence by 3
<span style="color: #008080;">function</span> <span style="color: #000000;">playfair</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">text</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">step</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">d</span><span style="color: #0000FF;">)</span>
return remove_x(playfair(ciphertext,3,m1))
<span style="color: #000080;font-style:italic;">--
end function
-- text may be the plaintext or the encoded version
-- step is 2 for plaintext, 3 for encoded(/skip spaces)
string keyword = "Playfair example"
-- d is {2,3,4,5,1} instead of mod(+1,5), or
build_table(keyword,'Q')
-- {5,1,2,3,4} -- -1
printf(1,"Playfair keyword : %s\n",{keyword})
--</span>
printf(1,"Option: J=I or no Q (J/Q) : %s\n",pfo)
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
printf(1,"The table to be used is :\n\n%s\n\n",{join(table,"\n")})
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">text</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">by</span> <span style="color: #000000;">step</span> <span style="color: #008080;">do</span>
string plaintext = "Hide the gold...in the TREESTUMP!!!!",
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">row1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">col1</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">findchar</span><span style="color: #0000FF;">[</span><span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]-</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span>
encoded = encode(plaintext),
<span style="color: #0000FF;">{</span><span style="color: #000000;">row2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">col2</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">findchar</span><span style="color: #0000FF;">[</span><span style="color: #000000;">text</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]-</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
decoded = decode(encoded)
<span style="color: #008080;">if</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">" "</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
printf(1,"The plain text : %s\n\n",{plaintext})
<span style="color: #008080;">if</span> <span style="color: #000000;">row1</span><span style="color: #0000FF;">=</span><span style="color: #000000;">row2</span> <span style="color: #008080;">then</span>
printf(1,"Encoded text is : %s\n",{encoded})
<span style="color: #0000FF;">{</span><span style="color: #000000;">col1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">col2</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[</span><span style="color: #000000;">col1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[</span><span style="color: #000000;">col2</span><span style="color: #0000FF;">]}</span>
printf(1,"Decoded text is : %s\n",{decoded})</lang>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">col1</span><span style="color: #0000FF;">=</span><span style="color: #000000;">col2</span> <span style="color: #008080;">then</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">row1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">row2</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[</span><span style="color: #000000;">row1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[</span><span style="color: #000000;">row2</span><span style="color: #0000FF;">]}</span>
<span style="color: #008080;">else</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">col1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">col2</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">col2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">col1</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">table</span><span style="color: #0000FF;">[</span><span style="color: #000000;">row1</span><span style="color: #0000FF;">][</span><span style="color: #000000;">col1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">table</span><span style="color: #0000FF;">[</span><span style="color: #000000;">row2</span><span style="color: #0000FF;">][</span><span style="color: #000000;">col2</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">encode</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">plaintext</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">playfair</span><span style="color: #0000FF;">(</span><span style="color: #000000;">clean_text</span><span style="color: #0000FF;">(</span><span style="color: #000000;">plaintext</span><span style="color: #0000FF;">),</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">decode</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">ciphertext</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- ciphertext includes spaces we need to skip, hence by 3</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">remove_x</span><span style="color: #0000FF;">(</span><span style="color: #000000;">playfair</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ciphertext</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">}))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Playfair keyword : %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">keyword</span><span style="color: #0000FF;">})</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Option: J=I or no Q (J/Q) : %s\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">option</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"The table to be used is :\n\n%s\n\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">table</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">)})</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">plaintext</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Hide the gold...in the TREESTUMP!!!!"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">encoded</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">encode</span><span style="color: #0000FF;">(</span><span style="color: #000000;">plaintext</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">decoded</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">decode</span><span style="color: #0000FF;">(</span><span style="color: #000000;">encoded</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"The plain text : %s\n\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">plaintext</span><span style="color: #0000FF;">})</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Encoded text is : %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">encoded</span><span style="color: #0000FF;">})</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Decoded text is : %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">decoded</span><span style="color: #0000FF;">})</span>
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 2,016: Line 2,302:
Decoded text is : HI DE TH EG OL DI NT HE TR E_ ES TU MP
Decoded text is : HI DE TH EG OL DI NT HE TR E_ ES TU MP
</pre>
</pre>
The only difference when option is 'J' is the 4<small><sup>th</sup></small> line of table is KNOQS


=={{header|Python}}==
=={{header|Python}}==
{{trans|Perl 6}}
{{trans|Raku}}
<lang python>from string import ascii_uppercase
<syntaxhighlight lang="python">from string import ascii_uppercase
from itertools import product
from itertools import product
from re import findall
from re import findall
Line 2,080: Line 2,367:
enc = encode(orig)
enc = encode(orig)
print "Encoded:", enc
print "Encoded:", enc
print "Decoded:", decode(enc)</lang>
print "Decoded:", decode(enc)</syntaxhighlight>
{{out}}
{{out}}
<pre>Original: Hide the gold in...the TREESTUMP!!!
<pre>Original: Hide the gold in...the TREESTUMP!!!
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP</pre>
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP</pre>

=={{header|Raku}}==
(formerly Perl 6)
<syntaxhighlight lang="raku" line># Instantiate a specific encoder/decoder.

sub playfair( $key,
$from = 'J',
$to = $from eq 'J' ?? 'I' !! ''
) {

sub canon($str) { $str.subst(/<-alpha>/,'', :g).uc.subst(/$from/,$to,:g) }

# Build 5x5 matrix.
my @m = canon($key ~ ('A'..'Z').join).comb.unique.map:
-> $a,$b,$c,$d,$e { [$a,$b,$c,$d,$e] }

# Pregenerate all forward translations.
my %ENC = gather {
# Map pairs in same row.
for @m -> @r {
for ^@r X ^@r -> (\i,\j) {
next if i == j;
take @r[i] ~ @r[j] => @r[(i+1)%5] ~ @r[(j+1)%5];
}
}

# Map pairs in same column.
for ^5 -> $c {
my @c = @m.map: *.[$c];
for ^@c X ^@c -> (\i,\j) {
next if i == j;
take @c[i] ~ @c[j] => @c[(i+1)%5] ~ @c[(j+1)%5];
}
}

# Map pairs with cross-connections.
for ^5 X ^5 X ^5 X ^5 -> (\i1,\j1,\i2,\j2) {
next if i1 == i2 or j1 == j2;
take @m[i1][j1] ~ @m[i2][j2] => @m[i1][j2] ~ @m[i2][j1];
}
}

# Generate reverse translations.
my %DEC = %ENC.invert;

return
sub enc($red) {
my @list = canon($red).comb(/(.) (.?) <?{ $1 ne $0 }>/);
~@list.map: { .chars == 1 ?? %ENC{$_~'X'} !! %ENC{$_} }
},
sub dec($black) {
my @list = canon($black).comb(/../);
~@list.map: { %DEC{$_} }
}
}

my (&encode,&decode) = playfair 'Playfair example';

my $orig = "Hide the gold in...the TREESTUMP!!!";
say " orig:\t$orig";

my $black = encode $orig;
say "black:\t$black";

my $red = decode $black;
say " red:\t$red";</syntaxhighlight>
{{out}}
<pre> orig: Hide the gold in...the TREESTUMP!!!
black: BM OD ZB XD NA BE KU DM UI XM MO UV IF
red: HI DE TH EG OL DI NT HE TR EX ES TU MP</pre>


=={{header|REXX}}==
=={{header|REXX}}==
Quite a bit of the REXX code deals with error checking, accepting arguments, and displaying the options used, and displaying input and output.
Quite a bit of the REXX code deals with error checking, accepting arguments, and displaying the options used, and displaying input and output.

<br>For ease of viewing and comparing, the output is in capitalized digraphs (which are really ''digrams'') as well as the original input(s).
For ease of viewing and comparing, the output is in capitalized digraphs (which are really ''digrams'') as well as the original input(s).
<br>Thanks to Walter Pachl, this program is now sensitive of using a suitable ''double character'' when &nbsp; '''X''' &nbsp; is present in the cipher key.

Thanks to Walter Pachl, this program is now sensitive of using a suitable ''double character'' when &nbsp; '''X''' &nbsp; is present in the cipher key.
<br>Also, more thanks are due to Walter Pachl for finding that the cipher key can't contain the OMIT character.
<br>Also, more thanks are due to Walter Pachl for finding that the cipher key can't contain the OMIT character.
<br>A fair amount of code was added to massage the decrypted encryption to remove doubled &nbsp; '''X'''es &nbsp; so as to match the original text
<br>(this is the ''possible text'' part of the REXX code).
<lang rexx>/*REXX program implements a PLAYFAIR cipher (encryption & decryption).*/
@abc='abcdefghijklmnopqrstuvwxyz'; @abcU=@abc /*lower and upper ABC's.*/
parse arg omit key '(' text /*TEXT is the phrase to be used. */ ;oldK=key /*save old.*/
if key =='' | key ==',' then do; key='Playfair example.'; oldK=key " ◄───using the default."; end
if omit=='' | omit==',' then omit='J' /*the "omitted" character. */
if text='' then text='Hide the gold in the tree stump!!' /*default.*/
newKey =scrub(key , 1) /*scrub old cipher key──► newKey */
newText=scrub(text ) /* " " text ──► newText*/
if newText=='' then call err 'TEXT is empty or has no letters'
if length(omit)\==1 then call err 'OMIT letter must be only one letter'
if \datatype(omit,'M') then call err 'OMIT letter must be a Latin alphabet letter.'
upper omit @abcU /*uppercase OMIT char & alphabet.*/
cant='can''t contain the "OMIT" character: ' omit
if pos(omit,newText)\==0 then call err 'TEXT' cant
if pos(omit,newKey) \==0 then call err 'cipher key' cant
fill=space(translate(@abcU,, omit), 0) /*elide OMIT char from alphabet. */
xx='X'; if omit==xx then xx='Q' /*char used for double characters*/
if length(newKey)<3 then call err 'cipher key is too short, must be ≥3 unique characters.'
fill=space(translate(fill,,newKey),0) /*remove any cipher characters. */
grid=newKey || fill /*only first 25 chars are used.*/
say 'old cipher key: ' strip(oldK) ; padL=14+2; pad=left('',padL)
say 'new cipher key: ' newKey ; padX=left('',padL,"═")'Playfair'
say ' omit char: ' omit /* [↓] lowercase of double char.*/
say ' double char: ' xx ; Lxx=translate(xx, @abc, @abcU)
say ' original text: ' strip(text) /* [↓] doubled version of Lxx. */
call show 'cleansed', newText ; LxxLxx=Lxx || Lxx
#=0 /*number of grid characters used.*/
do row =1 for 5 /*build array of individual cells*/
do col=1 for 5; #=#+1; @.row.col=substr(grid,#,1)
if row==1 then @.0.col=@.1.col
if col==5 then do; @.row.6=@.row.1; @.row.0=@.row.5; end
if row==5 then do; @.6.col=@.1.col; @.0.col=@.5.col; end
end /*col*/
end /*row*/


A fair amount of code was added to massage the decrypted encryption to remove doubled &nbsp; '''X'''es &nbsp; so as to match the original text
eText=.Playfair(newText, 1); call show 'encrypted' , eText
<br>(this is the &nbsp; &nbsp; ''possible text'' &nbsp; &nbsp; part of the REXX code).
pText=.Playfair(eText ); call show 'plain' , pText
<syntaxhighlight lang="rexx">/*REXX program implements a PLAYFAIR cipher (encryption and decryption). */
qText=changestr(xx ||xx,pText,Lxx) /*change doubled doublechar─►sing*/
@abc= 'abcdefghijklmnopqrstuvwxyz'; @abcU= @abc /*literals for lower and upper ABC's.*/
qText=changestr(Lxx||xx,qText,LxxLxx) /*change Xx ──► lowercase dblChar*/
parse arg omit key '(' text /*TEXT is the phrase to be used. */
qText=space(translate(qText,,xx),0) /*remove char used for "doubles."*/
upper qText /*reinstate the use of upperchars*/
oldKey= key /*save the old key. */
if length(qText)\==length(pText) then call show 'possible', qText
if key =='' | key ==',' then do; key= 'Playfair example.'
oldKey= key " ◄───using the default."
say ' original text: ' newText; say /*··· and show the original text.*/
end
if omit=='' | omit==',' then omit= 'J' /*the "omitted" character string. */
if text='' then text= 'Hide the gold in the tree stump!!' /*default.*/
upper omit @abcU /*uppercase OMIT characters & alphabet.*/
@cant= 'can''t contain the "OMIT" character: ' omit /*literal used in error text.*/
@uchars= 'unique characters.' /*a literal used below in an error msg.*/
newKey = scrub(key, 1) /*scrub old cipher key ──► newKey */
newText= scrub(text ) /* " " text ──► newText */
if newText=='' then call err 'TEXT is empty or has no letters.'
if length(omit)\==1 then call err 'OMIT letter must be only one letter.'
if \datatype(omit, 'M') then call err 'OMIT letter must be a Latin alphabet letter.'
if pos(omit, newText)\==0 then call err 'TEXT' @cant
if pos(omit, newKey) \==0 then call err 'cipher key' @cant
fill= space( translate(@abcU, , omit), 0) /*elide OMIT characters from alphabet. */
xx= 'X' /*character used for double characters.*/
if omit==xx then xx= 'Q' /* " " " " " */
if length(newKey)<3 then call err 'cipher key is too short, must be ≥ 3' @uchars
fill= space( translate(fill, , newKey), 0) /*remove any cipher characters. */
grid= newKey || fill /*only first 25 characters are used. */
say 'old cipher key: ' strip(oldKey)
say 'new cipher key: ' newKey
say ' omit char: ' omit
say ' double char: ' xx
say ' original text: ' strip(text)
padL= 14 + 2
call show 'cleansed', newText
#= 0 /*number of grid characters used. */
do row =1 for 5 /*build array of individual cells. */
do col=1 for 5; #= # + 1; @.row.col= substr(grid, #, 1)
if row==1 then @.0.col= @.1.col
if col==5 then do; @.row.6= @.row.1; @.row.0= @.row.5; end
if row==5 then do; @.6.col= @.1.col; @.0.col= @.5.col; end
end /*col*/
end /*row*/
pad = left('', padL)
padX= left('', padL, "═")'Playfair'
Lxx = translate(xx, @abc, @abcU) /* [↓] lowercase of double character. */
LxxLxx= Lxx || Lxx /* [↓] doubled version of Lxx. */
eText= .Playfair(newText, 1); call show 'encrypted' , eText
pText= .Playfair(eText ); call show 'plain' , pText
qText= changestr(xx || xx, pText, Lxx) /*change doubled doublechar ──► single.*/
qText= changestr(Lxx || xx, qText, LxxLxx) /*change xx ──► lowercase dblCharacter*/
qText= space( translate( qText, , xx), 0) /*remove character used for "doubles". */
upper qText /*reinstate the use of upper characters*/
if length(qText)\==length(pText) then call show 'possible', qText
say ' original text: ' newText; say /*··· and also show the original text. */
if qtext==newText then say padx 'encryption──► decryption──► encryption worked.'
if qtext==newText then say padx 'encryption──► decryption──► encryption worked.'
exit /*stick a fork in it, we're done.*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
/*──────────────────────────────────one─line subroutines───────────────────────────────*/
@@: parse arg Xrow,Xcol; return @.Xrow.Xcol
@@: parse arg Xrow,Xcol; return @.Xrow.Xcol
err: say; say '***error!***' arg(1); say; exit 13
err: say; say '***error!***' arg(1); say; exit 13
LR: rowL=row(left(__,1)); colL=_; rowR=row(right(__,1)); colR=_; return length(__)
LR: rowL= row(left(__, 1)); colL= _; rowR= row(right(__,1)); colR= _; return length(__)
row: ?=pos(arg(1),grid); _=(?-1)//5+1; return (4+?)%5
row: ?= pos(arg(1), grid); _= (?-1) // 5 + 1; return (4+?) % 5
show: arg ,y; say; say right(arg(1) 'text: ',padL) digram(y); say pad space(y,0); return
show: arg ,y; say; say right(arg(1) 'text: ',padL) digram(y); say pad space(y, 0); return
/*──────────────────────────────────────────────────────────────────────────────────────*/
/*──────────────────────────────────SCRUB subroutine────────────────────*/
.Playfair: arg T,encrypt; i= -1; if encrypt==1 then i= 1; $=
scrub: procedure; arg xxx,unique; xxx=space(xxx,0) /*ARG caps all args*/
$=; do j=1 for length(xxx); _=substr(xxx,j,1)
do k=1 while i==1; _= substr(T, k, 1); if _==' ' then leave
if unique==1 then if pos(_,$)\==0 then iterate /*unique?*/
if _==substr(T, k+1, 1) then T= left(T, k) || Lxx || substr(T, k + 1)
if datatype(_,'M') then $=$||_ /*only use Latin letters. */
end /*k*/
end /*j*/
upper T
do j=1 by 2 to length(T); __= strip( substr(T, j, 2) )
return $
if LR()==1 then __= __ || xx; call LR /*append X or Q char, rule 1*/
/*──────────────────────────────────DIGRAM subroutine───────────────────*/
select /*rule*/
digram: procedure; parse arg x; $=; do j=1 by 2 to length(x)
$=$ || substr(x,j,2)' '
when rowL==rowR then __= @@(rowL, colL+i)@@(rowR, colR+i) /*2*/
end /*j*/
when colL==colR then __= @@(rowL+i, colL )@@(rowR+i, colR) /*3*/
otherwise __= @@(rowL, colR )@@(rowR, colL) /*4*/
return strip($)
end /*select*/
/*──────────────────────────────────.PLAYFAIR subroutine────────────────*/
.Playfair: arg T,encrypt; i=-1; if encrypt==1 then i=1; $=
$= $ || __
do k=1 while i==1; _=substr(T,k,1); if _==' ' then leave
end /*j*/
return $
if _==substr(T,k+1 ,1) then T=left(T,k) || Lxx || substr(T,k+1)
/*──────────────────────────────────────────────────────────────────────────────────────*/
end /*k*/
digram: procedure; parse arg x,,$; do j=1 by 2 to length(x)
upper T
do j=1 by 2 to length(T); __=strip(substr(T,j,2))
$= $ || substr(x, j, 2)' '
if LR()==1 then __=__ || xx; call LR /*append X or Q char, rule 1*/
end /*j*/
return strip($)
select
/*──────────────────────────────────────────────────────────────────────────────────────*/
when rowL==rowR then __=@@(rowL, colL+i)@@(rowR, colR+i) /*rule 2*/
scrub: procedure; arg xxx,unique; xxx= space(xxx, 0) /*ARG capitalizes all args*/
when colL==colR then __=@@(rowL+i,colL )@@(rowR+i,colR) /*rule 3*/
otherwise __=@@(rowL, colR )@@(rowR, colL) /*rule 4*/
$=; do j=1 for length(xxx); _= substr(xxx, j, 1)
if unique==1 then if pos(_, $)\==0 then iterate /*is unique?*/
end /*select*/
if datatype(_, 'M') then $= $ || _ /*only use Latin letters. */
$=$ || __
end /*j*/
end /*j*/
return $</lang>
return $</syntaxhighlight>
Some older REXXes don't have a '''changestr''' bif, so one is included here ──► [[CHANGESTR.REX]].
Some older REXXes don't have a '''changestr''' bif, so one is included here ──► [[CHANGESTR.REX]].
<br><br>
<br><br>
'''output''' when using the default inputs:
{{out|output|text=&nbsp; when using the default inputs:}}
<pre>
<pre>
old cipher key: Playfair example. ◄───using the default.
old cipher key: Playfair example. ◄───using the default.
Line 2,197: Line 2,566:
════════════════Playfair encryption──► decryption──► encryption worked.
════════════════Playfair encryption──► decryption──► encryption worked.
</pre>
</pre>
'''output''' when using the input of: &nbsp; <tt> x stuvw (myteest </tt>
{{out|output|text=&nbsp; when using the input of: &nbsp; &nbsp; <tt> x &nbsp; stuvw &nbsp; (myteest </tt>}}
<pre>
<pre>
old cipher key: stuvw
old cipher key: stuvw
Line 2,220: Line 2,589:
════════════════Playfair encryption──► decryption──► encryption worked.
════════════════Playfair encryption──► decryption──► encryption worked.
</pre>
</pre>

=={{header|Ruby}}==
Printing the cipher in pairs just advertises the mechanism of encoding; I've gone with the traditional grouping into sequences of five letters instead.

<syntaxhighlight lang="ruby">class Playfair
Size = 5
def initialize(key, missing)
@missing = missing.upcase
alphabet = ('A'..'Z').to_a.join.upcase.delete(@missing).split''
extended = key.upcase.gsub(/[^A-Z]/,'').split('') + alphabet
grid = extended.uniq[0...Size*Size].each_slice(Size).to_a
coords = {}
grid.each_with_index do |row, i|
row.each_with_index do |letter, j|
coords[letter] = [i,j]
end
end
@encode = {}
alphabet.product(alphabet).reject { |a,b| a==b }.each do |a, b|
i1, j1 = coords[a]
i2, j2 = coords[b]
if i1 == i2 then
j1 = (j1 + 1) % Size
j2 = (j2 + 1) % Size
elsif j1 == j2 then
i1 = (i1 + 1) % Size
i2 = (i2 + 1) % Size
else
j1, j2 = j2, j1
end
@encode["#{a}#{b}"] = "#{grid[i1][j1]}#{grid[i2][j2]}"
@decode = @encode.invert
end
end

def encode(plaintext)
plain = plaintext.upcase.gsub(/[^A-Z]/,'')
if @missing == 'J' then
plain = plain.gsub(/J/, 'I')
else
plain = plain.gsub(@missing, 'X')
end
plain = plain.gsub(/(.)\1/, '\1X\1')
if plain.length % 2 == 1 then
plain += 'X'
end
return plain.upcase.split('').each_slice(2).map do |pair|
@encode[pair.join]
end.join.split('').each_slice(5).map{|s|s.join}.join(' ')
end

def decode(ciphertext)
cipher = ciphertext.upcase.gsub(/[^A-Z]/,'')
return cipher.upcase.split('').each_slice(2).map do |pair|
@decode[pair.join]
end.join.split('').each_slice(5).map{|s|s.join}.join(' ')
end
end</syntaxhighlight>

{{Out}}
<pre>irb(main):001:0> cipher = (p=Playfair.new 'Playfair example','J').encode('hide the gold in the tree stump')
=> "BMODZ BXDNA BEKUD MUIXM MOUVI F"
irb(main):002:0> p.decode(cipher)
=> "HIDET HEGOL DINTH ETREX ESTUM P"</pre>


=={{header|Sidef}}==
=={{header|Sidef}}==
{{trans|Perl 6}}
{{trans|Raku}}
<lang ruby>func playfair(key, from = 'J', to = (from == 'J' ? 'I' : '')) {
<syntaxhighlight lang="ruby">func playfair(key, from = 'J', to = (from == 'J' ? 'I' : '')) {


func canon(str) {
func canon(str) {
Line 2,281: Line 2,714:


var red = decode(black)
var red = decode(black)
say " red:\t#{red}"</lang>
say " red:\t#{red}"</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 2,290: Line 2,723:


=={{header|SQL}}==
=={{header|SQL}}==
<lang sql>
<syntaxhighlight lang="sql">
--Clean up previous run
--Clean up previous run
IF EXISTS (SELECT *
IF EXISTS (SELECT *
Line 2,604: Line 3,037:
WHERE NAME = 'FairPlayTable')
WHERE NAME = 'FairPlayTable')
DROP TYPE FAIRPLAYTABLE
DROP TYPE FAIRPLAYTABLE
</syntaxhighlight>
</lang>


=={{header|Tcl}}==
=={{header|Tcl}}==
{{incorrect|Tcl|TREESTUMP -> TREXSTUMPZ, should be TREXESTUMP}}
(My guess is that lappend digraphs $c0 [expr {$c0 eq $c ? "X" : $c}] is simply discarding $c. [[User:Petelomax|Pete Lomax]] ([[User talk:Petelomax|talk]]) 05:54, 13 October 2018 (UTC))


{{works with|Tcl|8.6}}
{{works with|Tcl|8.6}}
<lang tcl>package require TclOO
<syntaxhighlight lang="tcl">package require TclOO


oo::class create Playfair {
oo::class create Playfair {
variable grid lookup excluder
variable grid lookup excluder
constructor {{keyword "PLAYFAIR EXAMPLE"} {exclude "J"}} {
constructor {{keyword "PLAYFAIR EXAMPLE"} {exclude "J"}} {
# Tweaking according to exact operation mode
# Tweaking according to exact operation mode
if {$exclude eq "J"} {
if {$exclude eq "J"} {
set excluder "J I"
set excluder "J I"
} else {
} else {
set excluder [list $exclude ""]
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
# Clean up the keyword source
if {[array size lookup] != 25} {
set keys [my Clean [append keyword "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]]
error "failed to build encoding table correctly"

}
# 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
# Worker to apply a consistent cleanup/split rule
method Clean {str} {
method Clean {str} {
set str [string map $excluder [string toupper $str]]
set str [string map $excluder [string toupper $str]]
split [regsub -all {[^A-Z]} $str ""] ""
split [regsub -all {[^A-Z]} $str ""] ""
}
}

# These public methods are implemented by a single non-public method
# These public methods are implemented by a single non-public method
forward encode my Transform 1
forward encode my Transform 1
forward decode my Transform -1
forward decode my Transform -1

# The application of the Playfair cypher transform
# The application of the Playfair cypher transform
method Transform {direction message} {
method Transform {direction message} {
# Split message into true digraphs
# Split message into true digraphs
foreach c [my Clean $message] {
foreach c [my Clean $message] {
if {![info exists lookup($c)]} continue
if {![info exists lookup($c)]} continue
if {[info exist c0]} {
if {![info exists c0]} {
set c0 $c
lappend digraphs $c0 [expr {$c0 eq $c ? "X" : $c}]
} else {
unset c0
} else {
if {$c0 ne $c} {
lappend digraphs $c0 $c
set c0 $c
unset c0
}
} else {
}
lappend digraphs $c0 "X"
if {[info exist c0]} {
set c0 $c
lappend digraphs $c0 "Z"
}
}
}
}
if {[info exists c0]} {
lappend digraphs $c0 "Z"
}


# Encode the digraphs
# Encode the digraphs
set result ""
set result ""
foreach {a b} $digraphs {
foreach {a b} $digraphs {
lassign $lookup($a) ai aj
lassign $lookup($a) ai aj
lassign $lookup($b) bi bj
lassign $lookup($b) bi bj
if {$ai == $bi} {
if {$ai == $bi} {
set aj [expr {($aj + $direction) % 5}]
set aj [expr {($aj + $direction) % 5}]
set bj [expr {($bj + $direction) % 5}]
set bj [expr {($bj + $direction) % 5}]
} elseif {$aj == $bj} {
} elseif {$aj == $bj} {
set ai [expr {($ai + $direction) % 5}]
set ai [expr {($ai + $direction) % 5}]
set bi [expr {($bi + $direction) % 5}]
set bi [expr {($bi + $direction) % 5}]
} else {
} else {
set tmp $aj
set tmp $aj
set aj $bj
set aj $bj
set bj $tmp
set bj $tmp
}
}
lappend result [lindex $grid $ai $aj][lindex $grid $bi $bj]
lappend result [lindex $grid $ai $aj][lindex $grid $bi $bj]
}
}

# Real use would be: return [join $result ""]
# Real use would be: return [join $result ""]
return $result
return $result
}
}
}</lang>
}</syntaxhighlight>
Demonstrating:
Demonstrating:
<lang tcl>Playfair create cypher "Playfair Example"
<syntaxhighlight lang="tcl">Playfair create cypher "Playfair Example"
set plaintext "Hide the gold in...the TREESTUMP!!!"
set plaintext "Hide the gold in...the TREESTUMP!!!"
set encoded [cypher encode $plaintext]
set encoded [cypher encode $plaintext]
Line 2,699: Line 3,135:
puts "Original: $plaintext"
puts "Original: $plaintext"
puts "Encoded: $encoded"
puts "Encoded: $encoded"
puts "Decoded: $decoded"</lang>
puts "Decoded: $decoded"</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Original: Hide the gold in...the TREESTUMP!!!
Original: Hide the gold in...the TREESTUMP!!!
Encoded: BM OD ZB XD NA BE KU DM UI XM KZ ZR FT
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded: HI DE TH EG OL DI NT HE TR EX ST UM PZ
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP
</pre>
</pre>


=={{header|VBA}}==
=={{header|VBA}}==
<syntaxhighlight lang="vb">
<lang vb>
Option Explicit
Option Explicit


Line 2,915: Line 3,351:
SwapPairsDecoding = myTable(resD1.Row, resD1.Column) & myTable(resD2.Row, resD2.Column)
SwapPairsDecoding = myTable(resD1.Row, resD1.Column) & myTable(resD2.Row, resD2.Column)
End Select
End Select
End Function</lang>
End Function</syntaxhighlight>
{{out}}
{{out}}
<pre>Enter your keyword : Playfair example
<pre>Enter your keyword : Playfair example
Line 2,938: Line 3,374:
Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR EE ST UM PX</pre>
Decoded text is : HI DE TH EG OL DI NT HE TR EE ST UM PX</pre>

=={{header|V (Vlang)}}==
{{trans|Go}}
<syntaxhighlight lang="v (vlang)">import os
import strings
type PlayfairOption = int
const (
no_q = 0
i_equals_j = 1
)
struct Playfair {
mut:
keyword string
pfo PlayfairOption
table [5][5]u8
}
fn (mut p Playfair) init() {
// Build table.
mut used := [26]bool{} // all elements false
if p.pfo == no_q {
used[16] = true // Q used
} else {
used[9] = true // J used
}
alphabet := p.keyword.to_upper() + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i, j, k := 0, 0, 0; k < alphabet.len; k++ {
c := alphabet[k]
if c < 'A'[0] || c > 'Z'[0] {
continue
}
d := int(c - 65)
if !used[d] {
p.table[i][j] = c
used[d] = true
j++
if j == 5 {
i++
if i == 5 {
break // table has been filled
}
j = 0
}
}
}
}
fn (p Playfair) get_clean_text(pt string) string {
// Ensure everything is upper case.
plain_text := pt.to_upper()
// Get rid of any non-letters and insert X between duplicate letters.
mut clean_text := strings.new_builder(128)
// Safe to assume null u8 won't be present in plain_text.
mut prev_byte := `\000`
for i in 0..plain_text.len {
mut next_byte := plain_text[i]
// It appears that Q should be omitted altogether if NO_Q option is specified;
// we assume so anyway.
if next_byte < 'A'[0] || next_byte > 'Z'[0] || (next_byte == 'Q'[0] && p.pfo == no_q) {
continue
}
// If i_equals_j option specified, replace J with I.
if next_byte == 'J'[0] && p.pfo == i_equals_j {
next_byte = 'I'[0]
}
if next_byte != prev_byte {
clean_text.write_u8(next_byte)
} else {
clean_text.write_u8('X'[0])
clean_text.write_u8(next_byte)
}
prev_byte = next_byte
}
l := clean_text.len
if l%2 == 1 {
// Dangling letter at end so add another letter to complete digram.
if clean_text.str()[l-1] != 'X'[0] {
clean_text.write_u8('X'[0])
} else {
clean_text.write_u8('Z'[0])
}
}
return clean_text.str()
}
fn (p Playfair) find_byte(c u8) (int, int) {
for i in 0..5 {
for j in 0..5 {
if p.table[i][j] == c {
return i, j
}
}
}
return -1, -1
}
fn (p Playfair) encode(plain_text string) string {
clean_text := p.get_clean_text(plain_text)
mut cipher_text := strings.new_builder(128)
l := clean_text.len
for i := 0; i < l; i += 2 {
row1, col1 := p.find_byte(clean_text[i])
row2, col2 := p.find_byte(clean_text[i+1])
if row1 == row2{
cipher_text.write_u8(p.table[row1][(col1+1)%5])
cipher_text.write_u8(p.table[row2][(col2+1)%5])
} else if col1 == col2{
cipher_text.write_u8(p.table[(row1+1)%5][col1])
cipher_text.write_u8(p.table[(row2+1)%5][col2])
} else {
cipher_text.write_u8(p.table[row1][col2])
cipher_text.write_u8(p.table[row2][col1])
}
if i < l-1 {
cipher_text.write_u8(' '[0])
}
}
return cipher_text.str()
}
fn (p Playfair) decode(cipher_text string) string {
mut decoded_text := strings.new_builder(128)
l := cipher_text.len
// cipher_text will include spaces so we need to skip them.
for i := 0; i < l; i += 3 {
row1, col1 := p.find_byte(cipher_text[i])
row2, col2 := p.find_byte(cipher_text[i+1])
if row1 == row2 {
mut temp := 4
if col1 > 0 {
temp = col1 - 1
}
decoded_text.write_u8(p.table[row1][temp])
temp = 4
if col2 > 0 {
temp = col2 - 1
}
decoded_text.write_u8(p.table[row2][temp])
} else if col1 == col2 {
mut temp := 4
if row1 > 0 {
temp = row1 - 1
}
decoded_text.write_u8(p.table[temp][col1])
temp = 4
if row2 > 0 {
temp = row2 - 1
}
decoded_text.write_u8(p.table[temp][col2])
} else {
decoded_text.write_u8(p.table[row1][col2])
decoded_text.write_u8(p.table[row2][col1])
}
if i < l-1 {
decoded_text.write_u8(' '[0])
}
}
return decoded_text.str()
}
fn (p Playfair) print_table() {
println("The table to be used is :\n")
for i in 0..5 {
for j in 0..5 {
print("${p.table[i][j].ascii_str()} ")
}
println('')
}
}
fn main() {
keyword := os.input("Enter Playfair keyword : ")
mut ignore_q := ''
for ignore_q != "y" && ignore_q != "n" {
ignore_q = os.input("Ignore Q when building table y/n : ").to_lower()
}
mut pfo := no_q
if ignore_q == "n" {
pfo = i_equals_j
}
mut table := [5][5]u8{}
mut pf := Playfair{keyword, pfo, table}
pf.init()
pf.print_table()
plain_text := os.input("\nEnter plain text : ")
encoded_text := pf.encode(plain_text)
println("\nEncoded text is : $encoded_text")
decoded_text := pf.decode(encoded_text)
println("Deccoded text is : $decoded_text")
}</syntaxhighlight>

{{out}}
Sample run:
<pre>
Enter Playfair keyword : Playfair example
Ignore Q when building table y/n : n
The table to be used is :

P L A Y F
I R E X M
B C D G H
K N O Q S
T U V W Z

Enter plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Deccoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP
</pre>

=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-str}}
{{libheader|Wren-iterate}}
{{libheader|Wren-ioutil}}
<syntaxhighlight lang="wren">import "./dynamic" for Enum
import "./str" for Str, Char
import "./iterate" for Stepped
import "./ioutil" for Input

var PlayfairOption = Enum.create("PlayfairOption", ["NO_Q", "I_EQUALS_J"])

class Playfair {
construct new(keyword, pfo) {
_pfo = pfo
// build_table
_table = List.filled(5, null)
for (i in 0..4) _table[i] = List.filled(5, "\0") // 5 * 5 char list
var used = List.filled(26, false)
if (_pfo == PlayfairOption.NO_Q) {
used[16] = true // Q used
} else {
used[9] = true // J used
}
var alphabet = Str.upper(keyword) + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var i = 0
var j = 0
for (k in 0...alphabet.count) {
var c = alphabet[k]
if (Char.isAsciiUpper(c)) {
var d = c.bytes[0] - 65
if (!used[d]) {
_table[i][j] = c
used[d] = true
j = j + 1
if (j == 5) {
i = i + 1
if (i == 5) break // table has been filled
j = 0
}
}
}
}
}

getCleanText_(plainText) {
var plainText2 = Str.upper(plainText) // ensure everything is upper case
// get rid of any non-letters and insert X between duplicate letters
var cleanText = ""
var prevChar = "\0" // safe to assume null character won't be present in plainText
for (i in 0...plainText2.count) {
var nextChar = plainText2[i]
// It appears that Q should be omitted altogether if NO_Q option is specified - we assume so anyway
if (Char.isAsciiUpper(nextChar) && (nextChar != "Q" || _pfo != PlayfairOption.NO_Q)) {
// If I_EQUALS_J option specified, replace J with I
if (nextChar == "J" && _pfo == PlayfairOption.I_EQUALS_J) nextChar = "I"
if (nextChar != prevChar) {
cleanText = cleanText + nextChar
} else {
cleanText = cleanText + "X" + nextChar
}
prevChar = nextChar
}
}
var len = cleanText.count
if (len % 2 == 1) { // dangling letter at end so add another letter to complete digram
if (cleanText[-1] != "X") {
cleanText = cleanText + "X"
} else {
cleanText = cleanText + "Z"
}
}
return cleanText
}

findChar_(c) {
for (i in 0..4) {
for (j in 0..4) if (_table[i][j] == c) return [i, j]
}
return [-1, -1]
}

encode(plainText) {
var cleanText = getCleanText_(plainText)
var cipherText = ""
var length = cleanText.count
for (i in Stepped.new(0...length, 2)) {
var pair = findChar_(cleanText[i])
var row1 = pair[0]
var col1 = pair[1]
pair = findChar_(cleanText[i + 1])
var row2 = pair[0]
var col2 = pair[1]
cipherText = cipherText +
((row1 == row2) ? _table[row1][(col1 + 1) % 5] +_table[row2][(col2 + 1) % 5] :
(col1 == col2) ? _table[(row1 + 1) % 5][col1] +_table[(row2 + 1) % 5][col2] :
_table[row1][col2] +_table[row2][col1])
if (i < length - 1) cipherText = cipherText + " "
}
return cipherText
}

decode(cipherText) {
var decodedText = ""
var length = cipherText.count
for (i in Stepped.new(0...length, 3)) { // cipherText will include spaces so we need to skip them
var pair = findChar_(cipherText[i])
var row1 = pair[0]
var col1 = pair[1]
pair = findChar_(cipherText[i + 1])
var row2 = pair[0]
var col2 = pair[1]
decodedText = decodedText +
((row1 == row2) ? _table[row1][(col1 > 0) ? col1-1 : 4] +_table[row2][(col2 > 0) ? col2-1 : 4] :
(col1 == col2) ? _table[(row1 > 0) ? row1-1 : 4][col1] +_table[(row2 > 0) ? row2-1 : 4][col2] :
_table[row1][col2] +_table[row2][col1])
if (i < length - 1) decodedText = decodedText + " "
}
return decodedText
}

printTable() {
System.print("The_table to be used is :\n")
for (i in 0..4) {
for (j in 0..4) System.write(_table[i][j] + " ")
System.print()
}
}
}

var keyword = Input.text("Enter Playfair keyword : ", 1)
var ignoreQ = Str.lower(Input.option("Ignore Q when building_table y/n : ", "yYnN"))
var pfo = (ignoreQ == "y") ? PlayfairOption.NO_Q : PlayfairOption.I_EQUALS_J
var playfair = Playfair.new(keyword, pfo)
playfair.printTable()
var plainText = Input.text("\nEnter plain text : ")
var encodedText = playfair.encode(plainText)
System.print("\nEncoded text is : %(encodedText)")
var decodedText = playfair.decode(encodedText)
System.print("Decoded text is : %(decodedText)")</syntaxhighlight>

{{out}}
<pre>
Enter Playfair keyword : Playfair example
Ignore Q when building_table y/n : n
The_table to be used is :

P L A Y F
I R E X M
B C D G H
K N O Q S
T U V W Z

Enter plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP
</pre>


=={{header|zkl}}==
=={{header|zkl}}==
<lang zkl>fcn genKeyTable(key,deadChr){ // deadChr=="Q" or "J"
<syntaxhighlight lang="zkl">fcn genKeyTable(key,deadChr){ // deadChr=="Q" or "J"
deadChr=deadChr.toUpper();
deadChr=deadChr.toUpper();
key=key.toUpper().unique() - " " - deadChr;
key=key.toUpper().unique() - " " - deadChr;
return(key + (["A".."Z"].pump(String) - deadChr - key), deadChr);
return(key + (["A".."Z"].pump(String) - deadChr - key), deadChr);
}</lang>
}</syntaxhighlight>
<lang zkl>fcn playfair(text,keytable){ // text is a-z only
<syntaxhighlight lang="zkl">fcn playfair(text,keytable){ // text is a-z only
keyTable,deadChr:=keytable;
keyTable,deadChr:=keytable;
text=text.toUpper();
text=text.toUpper();
Line 2,968: Line 3,775:
})
})
.pump(String,Void.Read,"".create.fp(" ")).strip(); // insert blanks
.pump(String,Void.Read,"".create.fp(" ")).strip(); // insert blanks
}</lang>
}</syntaxhighlight>
<lang zkl>fcn decodePF(text,keyTable){
<syntaxhighlight lang="zkl">fcn decodePF(text,keyTable){
keyTable,_=keyTable;
keyTable,_=keyTable;
text-=" ";
text-=" ";
Line 2,985: Line 3,792:
.pump(String,Void.Read,"".create.fp(" ")).strip(); // insert blanks
.pump(String,Void.Read,"".create.fp(" ")).strip(); // insert blanks
}
}
</syntaxhighlight>
</lang>
<lang zkl>msg:="Hide the gold in the tree stump!!!";
<syntaxhighlight lang="zkl">msg:="Hide the gold in the tree stump!!!";
keyTable:=genKeyTable("playfair example");
keyTable:=genKeyTable("playfair example");
msg.println();
msg.println();
e:=playfair(msg,keyTable); e.println();
e:=playfair(msg,keyTable); e.println();
decodePF(e,keyTable).println();
decodePF(e,keyTable).println();
playfair("XX",keyTable).println() : decodePF(_,keyTable).println();</lang>
playfair("XX",keyTable).println() : decodePF(_,keyTable).println();</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>

Latest revision as of 12:18, 25 January 2024


Task
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



11l

Translation of: Python
F uniq(seq)
   [Char] seen
   L(x) seq
      I x !C seen
         seen.append(x)
   R seen

F partition(seq, n)
   R (0 .< seq.len).step(n).map(i -> @seq[i .< i + @n])

F canonicalize(s)
   R s.uppercase().filter(c -> c.is_uppercase()).join(‘’).replace(‘J’, ‘I’)

T Playfair
   [String = String] dec, enc

   F (key)
      V m = partition(uniq(canonicalize(key‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)), 5)

      L(row) m
         L(i, j) cart_product(0.<5, 0.<5)
            I i != j
               .enc[row[i]‘’row[j]] = row[(i + 1) % 5]‘’row[(j + 1) % 5]

      L(ci) 5
         V c = [m[0][ci], m[1][ci], m[2][ci], m[3][ci], m[4][ci]]
         L(i, j) cart_product(0.<5, 0.<5)
            I i != j
               .enc[c[i]‘’c[j]] = c[(i + 1) % 5]‘’c[(j + 1) % 5]

      L(i1, j1, i2, j2) cart_product(0.<5, 0.<5, 0.<5, 0.<5)
         I i1 != i2 & j1 != j2
            .enc[m[i1][j1]‘’m[i2][j2]] = m[i1][j2]‘’m[i2][j1]

      .dec = Dict(.enc.map((k, v) -> (v, k)))

   F encode(txt)
      V c = canonicalize(txt)
      [String] lst
      V i = 0
      L i < c.len - 1
         I c[i + 1] == c[i]
            lst [+]= c[i]‘X’
            i++
         E
            lst [+]= c[i]‘’c[i + 1]
            i += 2
      I i == c.len - 1
         lst [+]= c.last‘X’
      R lst.map(a -> @.enc[a]).join(‘ ’)

   F decode(encoded)
      R partition(canonicalize(encoded), 2).map(p -> @.dec[p]).join(‘ ’)

V playfair = Playfair(‘Playfair example’)
V orig = ‘Hide the gold in...the TREESTUMP!!!’
print(‘Original: ’orig)
V enc = playfair.encode(orig)
print(‘Encoded: ’enc)
print(‘Decoded: ’playfair.decode(enc))
Output:
Original: Hide the gold in...the TREESTUMP!!!
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP

APL

Works with: Dyalog APL
⍝⍝⍝⍝ Utility functions

⍝ convert to uppercase
UpCase  { 1 ⎕C  }

⍝ remove non-letters
JustLetters  {  /⍨ ⎕A }

⍝ replace 'J's with 'I's
ReplaceJ  { ('J' ⎕R 'I')  }

⍝ Insert an 'X' between repeated letters
SplitDouble  { (' '⎕R'X')  \⍨ 1,~=1 }

⍝ Append an 'X' if the message is not of even length
PadEven  { ,(2|≢)  'X' }

⍝ Split text up into letter pairs
Pairs  { (1=2|⍳≢)    }

⍝ Group text into chunks of five letters
Groups  { (1=5|⍳≢)    }

⍝ Shift within 1-5 based on left arg (0 for +1,1 for -1)
Shift  {   0  1+5|(+1),3+ }

⍝⍝⍝⍝ Playfair implementation

⍝ All the things we have to do to the plaintext, chained together
PreparePlaintext  { PadEven SplitDouble ReplaceJ JustLetters UpCase   }

⍝ Ditto for ciphertext
PrepareCiphertext  { JustLetters UpCase   }

⍝ Create the grid from the key
PrepareKey  { 5 5  ReplaceJ (JustLetters UpCase ),⎕A }

⍝ Encode or decode a single pair of letters
resultPair  grid TransformPair args;mode;inPair;l;r;i1;j1;i2;j2
 mode inPair  args
 l r  inPair
 i1 j1  ⊃⍸grid=l
 i2 j2  ⊃⍸grid=r
 :If i1=i2
     j1  mode Shift j1
     j2  mode Shift j2
 :Else
     :If j1=j2
         i1  mode Shift i1
         i2  mode Shift i2
     :Else
         j1 j2  j2 j1
     :EndIf
 :EndIf
 resultPair  grid[(i1 j1)(i2 j2)]


⍝ Encode or decode an entire message
resultText  grid TransformText args; mode; inText
  mode inText  args
  resultText  Groups  { grid TransformPair mode  } ¨ Pairs inText


⍝ Specific transforms for each direction including key and text preparation
cipher  key EncodeText plain
  cipher  (PrepareKey key) TransformText 0 (PreparePlaintext plain)


plain  key DecodeText cipher
  plain  (PrepareKey key) TransformText 1 (PrepareCiphertext cipher)


⍝ Demo
key  'Playfair example'
plain  'Hide the gold in the tree stump.'
  cipher  key EncodeText plain
  key DecodeText cipher
Output:
      ⎕ ← cipher ← key EncodeText plain

┌─────┬─────┬─────┬─────┬─────┬─┐ │BMODZ│BXDNA│BEKUD│MUIXM│MOUVI│F│ └─────┴─────┴─────┴─────┴─────┴─┘

      ⎕ ← key DecodeText cipher
┌─────┬─────┬─────┬─────┬─────┬─┐
│HIDET│HEGOL│DINTH│ETREX│ESTUM│P│
└─────┴─────┴─────┴─────┴─────┴─┘

C++

#include <iostream>
#include <string>

using namespace std;

class playfair
{
public:
    void doIt( string k, string t, bool ij, bool e )
    {
	createGrid( k, ij ); getTextReady( t, ij, e );
	if( e ) doIt( 1 ); else doIt( -1 );
	display();
    }

private:
    void doIt( int dir )
    {
	int a, b, c, d; string ntxt;
	for( string::const_iterator ti = _txt.begin(); ti != _txt.end(); ti++ )
	{
	    if( getCharPos( *ti++, a, b ) )
		if( getCharPos( *ti, c, d ) )
		{
		    if( a == c )     { ntxt += getChar( a, b + dir ); ntxt += getChar( c, d + dir ); }
		    else if( b == d ){ ntxt += getChar( a + dir, b ); ntxt += getChar( c + dir, d ); }
		    else             { ntxt += getChar( c, b ); ntxt += getChar( a, d ); }
		}
	}
	_txt = ntxt;
    }

    void display()
    {
	cout << "\n\n OUTPUT:\n=========" << endl;
	string::iterator si = _txt.begin(); int cnt = 0;
	while( si != _txt.end() )
	{
	    cout << *si; si++; cout << *si << " "; si++;
	    if( ++cnt >= 26 ) cout << endl, cnt = 0;
	}
	cout << endl << endl;
    }

    char getChar( int a, int b )
    {
	return _m[ (b + 5) % 5 ][ (a + 5) % 5 ];
    }

    bool getCharPos( char l, int &a, int &b )
    {
	for( int y = 0; y < 5; y++ )
	    for( int x = 0; x < 5; x++ )
		if( _m[y][x] == l )
		{ a = x; b = y; return true; }

	return false;
    }

    void getTextReady( string t, bool ij, bool e )
    {
	for( string::iterator si = t.begin(); si != t.end(); si++ )
	{
	    *si = toupper( *si ); if( *si < 65 || *si > 90 ) continue;
	    if( *si == 'J' && ij ) *si = 'I';
	    else if( *si == 'Q' && !ij ) continue;
	    _txt += *si;
	}
	if( e )
	{
	    string ntxt = ""; size_t len = _txt.length();
	    for( size_t x = 0; x < len; x += 2 )
	    {
		ntxt += _txt[x];
		if( x + 1 < len )
		{
		    if( _txt[x] == _txt[x + 1] ) ntxt += 'X';
		    ntxt += _txt[x + 1];
		}
	    }
	    _txt = ntxt;
	}
	if( _txt.length() & 1 ) _txt += 'X';
    }

    void createGrid( string k, bool ij )
    {
	if( k.length() < 1 ) k = "KEYWORD"; 
	k += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; string nk = "";
	for( string::iterator si = k.begin(); si != k.end(); si++ )
	{
	    *si = toupper( *si ); if( *si < 65 || *si > 90 ) continue;
	    if( ( *si == 'J' && ij ) || ( *si == 'Q' && !ij ) )continue;
	    if( nk.find( *si ) == -1 ) nk += *si;
	}
	copy( nk.begin(), nk.end(), &_m[0][0] );
    }

    string _txt; char _m[5][5];
};

int main( int argc, char* argv[] )
{
    string key, i, txt; bool ij, e;
    cout << "(E)ncode or (D)ecode? "; getline( cin, i ); e = ( i[0] == 'e' || i[0] == 'E' );
    cout << "Enter a en/decryption key: "; getline( cin, key ); 
    cout << "I <-> J (Y/N): "; getline( cin, i ); ij = ( i[0] == 'y' || i[0] == 'Y' );
    cout << "Enter the text: "; getline( cin, txt ); 
    playfair pf; pf.doIt( key, txt, ij, e ); return system( "pause" );
}
Output:

(E)ncode or (D)ecode? e Enter a en/decryption key: playfair example I <-> J (Y/N): y Enter the text: Hide the gold in the tree stump

OUTPUT: ========= BM OD ZB XD NA BE KU DM UI XM MO UV IF


(E)ncode or (D)ecode? d Enter a en/decryption key: playfair example I <-> J (Y/N): y Enter the text: BMODZBXDNABEKUDMUIXMMOUVIF

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

D

Translation of: Python
import std.stdio, std.array, std.algorithm, std.range, std.ascii,
       std.conv, std.string, std.regex, std.typecons;

string unique(in string s) pure nothrow @safe {
    string result;
    foreach (immutable char c; s)
        if (!result.representation.canFind(c)) // Assumes ASCII string.
            result ~= c;
    return result;
}

struct Playfair {
    string from, to;
    string[string] enc, dec;

    this(in string key, in string from_ = "J", in string to_ = null)
    pure /*nothrow @safe*/ {
        this.from = from_;
        if (to_.empty)
            this.to = (from_ == "J") ? "I" : "";

        immutable m = _canonicalize(key ~ uppercase)
                      .unique
                      .chunks(5)
                      .map!text
                      .array;
        auto I5 = 5.iota;

        foreach (immutable R; m)
            foreach (immutable i, immutable j; cartesianProduct(I5, I5))
                if (i != j)
                    enc[[R[i], R[j]]] = [R[(i + 1) % 5], R[(j+1) % 5]];

        foreach (immutable r; I5) {
            immutable c = m.transversal(r).array;
            foreach (immutable i, immutable j; cartesianProduct(I5, I5))
                if (i != j)
                    enc[[c[i], c[j]]] = [c[(i + 1) % 5], c[(j+1) % 5]];
        }

        foreach (i1, j1, i2, j2; cartesianProduct(I5, I5, I5, I5))
            if (i1 != i2 && j1 != j2)
                enc[[m[i1][j1], m[i2][j2]]] = [m[i1][j2], m[i2][j1]];

        dec = enc.byKeyValue.map!(t => tuple(t.value, t.key)).assocArray;
    }

    private string _canonicalize(in string s) const pure @safe {
        return s.toUpper.removechars("^A-Z").replace(from, to);
    }

    string encode(in string s) const /*pure @safe*/ {
        return _canonicalize(s)
               .matchAll(r"(.)(?:(?!\1)(.))?")
               .map!(m => enc[m[0].leftJustify(2, 'X')])
               .join(' ');
    }

    string decode(in string s) const pure @safe {
        return _canonicalize(s).chunks(2).map!(p => dec[p.text]).join(' ');
    }
}

void main() /*@safe*/ {
    /*immutable*/ const pf = Playfair("Playfair example");
    immutable orig = "Hide the gold in...the TREESTUMP!!!";
    writeln("Original: ", orig);
    immutable enc = pf.encode(orig);
    writeln(" Encoded: ", enc);
    writeln(" Decoded: ", pf.decode(enc));
}
Output:
Original: Hide the gold in...the TREESTUMP!!!
 Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
 Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP

FreeBASIC

' FB 1.05.0 Win64

Enum PlayFairOption
  noQ 
  iEqualsJ
End Enum

Dim Shared pfo As PlayFairOption  '' set this before calling buildTable
Dim Shared table(1 To 5, 1 To 5) As UInteger

Sub buildTable(keyword As String)
  Dim used(1 To 26) As Boolean  '' all false by default
  If pfo = noQ Then
    used(17) = True
  Else
    used(10) = True
  End If
  Dim As String alphabet = UCase(keyword) + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  Dim As UInteger i = 1, j = 1, k
  Dim As Integer c
  For k = 0 To Len(alphabet) - 1
    c = alphabet[k] - 64
    If c < 1 OrElse c > 26 Then Continue For
    If Not used(c) Then
      table(i, j) = c
      used(c) = True
      j += 1
      If j = 6 Then
        i += 1
        If i = 6 Then Return  '' table has been filled 
        j = 1          
      End If
    End If
  Next k
End Sub

Function getCleanText(plainText As String) As String
  plainText = UCase(plainText) '' ensure everything is upper case
  ' get rid of any non-letters and insert X between duplicate letters
  Dim As String cleanText = "", prevChar = "", nextChar
  For i As UInteger = 1 To Len(plainText)
    nextChar = Mid(plainText, i, 1)
    ' It appears that Q should be omitted altogether if noQ option is specified - we assume so anyway
    If nextChar < "A" OrElse nextChar > "Z"  OrElse (nextChar = "Q" AndAlso pfo = noQ) Then Continue For
    ' If iEqualsJ option specified, replace J with I
    If nextChar = "J" AndAlso pfo = iEqualsJ Then nextChar = "I"
    If nextChar <> prevChar Then
      cleanText += nextChar
    Else
      cleanText += "X" + nextChar
    End If
    prevChar = nextChar
  Next
  If Len(cleanText) Mod 2 = 1 Then  '' dangling letter at end so add another letter to complete digram
    If Right(cleanText, 1) <> "X" Then
      cleanText += "X"
    Else
      cleanText += "Z"
    End If
  End If      
  Return cleanText
End Function    
 
Sub findChar(c As uInteger, ByRef row As UInteger, ByRef col As UInteger)
  For i As UInteger = 1 To 5
    For j As UInteger = 1 To 5
      If table(i, j) = c Then
        row = i
        col = j
        Return
      End If
    Next j
  Next i
End Sub    
  
Function encodePlayfair(plainText As String) As String
  Dim As String cleanText = getCleanText(plainText)
  Dim As String digram, cipherDigram, cipherText = ""
  Dim As UInteger length = Len(cleanText)
  Dim As UInteger char1, char2, row1, col1, row2, col2
  For i As UInteger = 1 To length Step 2
    digram = Mid(cleanText, i, 2)
    char1 = digram[0] - 64
    char2 = digram[1] - 64
    findChar char1, row1, col1
    findChar char2, row2, col2
    If row1 = row2 Then 
      cipherDigram =  Chr(table(row1, col1 Mod 5 + 1) + 64)
      cipherDigram += Chr(table(row2, col2 Mod 5 + 1) + 64)
    ElseIf col1 = col2 Then
      cipherDigram =  Chr(table(row1 Mod 5 + 1, col1) + 64)
      cipherDigram += Chr(table(row2 Mod 5 + 1, col2) + 64)
    Else
      cipherDigram =  Chr(table(row1, col2) + 64)
      cipherDigram += Chr(table(row2, col1) + 64)
    End If
    cipherText += cipherDigram
    If i < length Then cipherText += " "  
  Next i 
  Return cipherText
End Function

Function decodePlayfair(cipherText As String) As String
  Dim As String digram, cipherDigram, decodedText = ""
  Dim As UInteger length = Len(cipherText)
  Dim As UInteger char1, char2, row1, col1, row2, col2
  For i As UInteger = 1 To length Step 3  '' cipherText will include spaces so we need to skip them
    cipherDigram = Mid(cipherText, i, 2)
    char1 = cipherDigram[0] - 64
    char2 = cipherDigram[1] - 64
    findChar char1, row1, col1
    findChar char2, row2, col2
    If row1 = row2 Then 
      digram =  Chr(table(row1, IIf(col1 > 1, col1 - 1, 5)) + 64)
      digram += Chr(table(row2, IIf(col2 > 1, col2 - 1, 5)) + 64)
    ElseIf col1 = col2 Then
      digram =  Chr(table(IIf(row1 > 1, row1 - 1, 5), col1) + 64)
      digram += Chr(table(IIf(row2 > 1, row2 - 1, 5), col2) + 64)
    Else
      digram =  Chr(table(row1, col2) + 64)
      digram += Chr(table(row2, col1) + 64)
    End If
    decodedText += digram
    If i < length Then decodedText += " "  
  Next i 
  Return decodedText
End Function

Dim As String keyword, ignoreQ, plainText, encodedText, decodedText
Line Input "Enter Playfair keyword : "; keyword

Do
  Line Input "Ignore Q when buiding table  y/n : "; ignoreQ
  ignoreQ = LCase(ignoreQ)
Loop Until ignoreQ = "y" Or ignoreQ = "n"

pfo = IIf(ignoreQ = "y", noQ, iEqualsJ)
buildTable(keyword)
Print "The table to be used is : " : Print
For i As UInteger = 1 To 5
  For j As UInteger = 1 To 5
    Print Chr(table(i, j) + 64); " ";
  Next j
  Print
Next i

Print 
Line Input "Enter plain text : "; plainText
Print
encodedText = encodePlayfair(plainText)
Print "Encoded text is : "; encodedText
decodedText = decodePlayFair(encodedText)
Print "Decoded text is : "; decodedText
Print
Print "Press any key to quit"
Sleep

Sample input/output:

Output:
Enter Playfair keyword : ? Playfair example
Ignore Q when buiding table  y/n : ? n
The table to be used is :

P L A Y F
I R E X M
B C D G H
K N O Q S
T U V W Z

Enter plain text : ? Hide the gold in...the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP

Go

Translation of: Kotlin
package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

type playfairOption int

const (
    noQ playfairOption = iota
    iEqualsJ
)

type playfair struct {
    keyword string
    pfo     playfairOption
    table   [5][5]byte
}

func (p *playfair) init() {
    // Build table.
    var used [26]bool // all elements false
    if p.pfo == noQ {
        used[16] = true // Q used
    } else {
        used[9] = true // J used
    }
    alphabet := strings.ToUpper(p.keyword) + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    for i, j, k := 0, 0, 0; k < len(alphabet); k++ {
        c := alphabet[k]
        if c < 'A' || c > 'Z' {
            continue
        }
        d := int(c - 65)
        if !used[d] {
            p.table[i][j] = c
            used[d] = true
            j++
            if j == 5 {
                i++
                if i == 5 {
                    break // table has been filled
                }
                j = 0
            }
        }
    }
}

func (p *playfair) getCleanText(plainText string) string {
    // Ensure everything is upper case.
    plainText = strings.ToUpper(plainText)
    // Get rid of any non-letters and insert X between duplicate letters.
    var cleanText strings.Builder
    // Safe to assume null byte won't be present in plainText.
    prevByte := byte('\000')
    for i := 0; i < len(plainText); i++ {
        nextByte := plainText[i]
        // It appears that Q should be omitted altogether if NO_Q option is specified;
        // we assume so anyway.
        if nextByte < 'A' || nextByte > 'Z' || (nextByte == 'Q' && p.pfo == noQ) {
            continue
        }
        // If iEqualsJ option specified, replace J with I.
        if nextByte == 'J' && p.pfo == iEqualsJ {
            nextByte = 'I'
        }
        if nextByte != prevByte {
            cleanText.WriteByte(nextByte)
        } else {
            cleanText.WriteByte('X')
            cleanText.WriteByte(nextByte)
        }
        prevByte = nextByte
    }
    l := cleanText.Len()
    if l%2 == 1 {
        // Dangling letter at end so add another letter to complete digram.
        if cleanText.String()[l-1] != 'X' {
            cleanText.WriteByte('X')
        } else {
            cleanText.WriteByte('Z')
        }
    }
    return cleanText.String()
}

func (p *playfair) findByte(c byte) (int, int) {
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            if p.table[i][j] == c {
                return i, j
            }
        }
    }
    return -1, -1
}

func (p *playfair) encode(plainText string) string {
    cleanText := p.getCleanText(plainText)
    var cipherText strings.Builder
    l := len(cleanText)
    for i := 0; i < l; i += 2 {
        row1, col1 := p.findByte(cleanText[i])
        row2, col2 := p.findByte(cleanText[i+1])
        switch {
        case row1 == row2:
            cipherText.WriteByte(p.table[row1][(col1+1)%5])
            cipherText.WriteByte(p.table[row2][(col2+1)%5])
        case col1 == col2:
            cipherText.WriteByte(p.table[(row1+1)%5][col1])
            cipherText.WriteByte(p.table[(row2+1)%5][col2])
        default:
            cipherText.WriteByte(p.table[row1][col2])
            cipherText.WriteByte(p.table[row2][col1])
        }
        if i < l-1 {
            cipherText.WriteByte(' ')
        }
    }
    return cipherText.String()
}

func (p *playfair) decode(cipherText string) string {
    var decodedText strings.Builder
    l := len(cipherText)
    // cipherText will include spaces so we need to skip them.
    for i := 0; i < l; i += 3 {
        row1, col1 := p.findByte(cipherText[i])
        row2, col2 := p.findByte(cipherText[i+1])
        switch {
        case row1 == row2:
            temp := 4
            if col1 > 0 {
                temp = col1 - 1
            }
            decodedText.WriteByte(p.table[row1][temp])
            temp = 4
            if col2 > 0 {
                temp = col2 - 1
            }
            decodedText.WriteByte(p.table[row2][temp])
        case col1 == col2:
            temp := 4
            if row1 > 0 {
                temp = row1 - 1
            }
            decodedText.WriteByte(p.table[temp][col1])
            temp = 4
            if row2 > 0 {
                temp = row2 - 1
            }
            decodedText.WriteByte(p.table[temp][col2])
        default:
            decodedText.WriteByte(p.table[row1][col2])
            decodedText.WriteByte(p.table[row2][col1])
        }
        if i < l-1 {
            decodedText.WriteByte(' ')
        }
    }
    return decodedText.String()
}

func (p *playfair) printTable() {
    fmt.Println("The table to be used is :\n")
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            fmt.Printf("%c ", p.table[i][j])
        }
        fmt.Println()
    }
}

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    fmt.Print("Enter Playfair keyword : ")
    scanner.Scan()
    keyword := scanner.Text()
    var ignoreQ string
    for ignoreQ != "y" && ignoreQ != "n" {
        fmt.Print("Ignore Q when building table  y/n : ")
        scanner.Scan()
        ignoreQ = strings.ToLower(scanner.Text())
    }
    pfo := noQ
    if ignoreQ == "n" {
        pfo = iEqualsJ
    }
    var table [5][5]byte
    pf := &playfair{keyword, pfo, table}
    pf.init()
    pf.printTable()
    fmt.Print("\nEnter plain text : ")
    scanner.Scan()
    plainText := scanner.Text()
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
        return
    }
    encodedText := pf.encode(plainText)
    fmt.Println("\nEncoded text is :", encodedText)
    decodedText := pf.decode(encodedText)
    fmt.Println("Deccoded text is :", decodedText)
}
Output:

Sample run:

Enter Playfair keyword : Playfair example
Ignore Q when building table  y/n : n
The table to be used is :

P L A Y F 
I R E X M 
B C D G H 
K N O Q S 
T U V W Z 

Enter plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF 
Deccoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP 

Haskell

This example is incorrect. Please fix the code and remove this message.

Details: TREESTUMP -> TREXSTUMPX, should be TREXESTUMP

(My guess is that map (\[x, y] -> if x == y then [x, 'x'] else [x, y]).chunksOf 2 is simply discarding the y. Pete Lomax (talk) 05:54, 13 October 2018 (UTC))

import Control.Monad     (guard)
import Data.Array        (Array, assocs, elems, listArray, (!))
import Data.Char         (toUpper)
import Data.List         (nub, (\\))
import Data.List.Split   (chunksOf)
import Data.Maybe        (listToMaybe)
import Data.String.Utils (replace)

type Square a = Array (Int, Int) a

-- | Turns a list into an n*m-array.
array2D ::
       (Int, Int) -- ^ n * m
    -> [e] -> Square e
array2D maxCoord = listArray ((1, 1), maxCoord)

-- | Generates a playfair table starting with the specified string.
--
-- >>> makeTable "hello"
-- "HELOABCDFGIKMNPQRSTUVWXYZ"
makeTable :: String -> String
makeTable k = nub key ++ (alpha \\ key)
    where
      alpha = ['A' .. 'Z'] \\ "J"
      key = map toUpper =<< words k

-- | Turns a playfair table into a 5*5 alphabet square.
makeSquare :: [a] -> Square a
makeSquare = array2D (5, 5)

-- | Displays a playfair square, formatted as a square.
showSquare :: Square Char -> String
showSquare d = unlines $ chunksOf 5 (elems d)

-- | Given a value and an association list of x-coordinate * y-coordinate * value, returns the coordinates
getIndex' :: (Eq a) => a -> [((Int, Int), a)] -> Maybe (Int, Int)
getIndex' el = fmap fst . listToMaybe . filter ((== el) . snd)

encodePair, decodePair :: Eq a => Square a -> (a, a) -> Maybe (a, a)
encodePair = pairHelper (\x -> if x == 5 then 1 else x + 1)
decodePair = pairHelper (\x -> if x == 1 then 5 else x - 1)

pairHelper :: (Eq t)
    => (Int -> Int) -- ^ a function used for wrapping around the square
    -> Square t -- ^ a playfair square
    -> (t, t) -- ^ two characters
    -> Maybe (t, t) -- ^ the two resulting/encoded characters
pairHelper adjust sqr (c1, c2) =
    do let ps = assocs sqr
       -- assigns an association list of (x-coord * y-coord) * value to ps
       (x1, y1) <- getIndex' c1 ps
       (x2, y2) <- getIndex' c2 ps
       -- returns the coordinates of two values in the square
       -- these will later be swapped
       guard $ c1 /= c2
       -- the characters (and coordinates) cannot be the same
       let get x = sqr ! x
       -- a small utility function for extracting a value from the square
       Just $
           -- wrap the coordinates around and find the encrypted characters
           case () of
             () | y1 == y2 ->
                    (get (adjust x1, y1), get (adjust x2, y2))
                | x1 == x2 ->
                    (get (x1, adjust y1), get (x2, adjust y2))
                | otherwise ->
                    (get (x1, y2), get (x2, y1))

-- | Turns two characters into a tuple.
parsePair :: String -> [(Char, Char)]
parsePair = fmap (\[x, y] -> (x, y)) . words . fmap toUpper

-- | Turns a tuple of two characters into a string.
unparsePair :: [(Char, Char)] -> String
unparsePair = unwords . fmap (\(x, y) -> [x, y])

codeHelper :: (Square Char -> (Char, Char) -> Maybe (Char, Char))
    -> String -> String -> Maybe String
codeHelper subs key =
    fmap unparsePair .
    mapM (subs (makeSquare $ makeTable key)) .
    parsePair

playfair, unplayfair :: String -> String -> Maybe String
playfair key = codeHelper encodePair key . formatEncode
unplayfair = codeHelper decodePair

formatEncode :: String -> String
formatEncode =
    map toUpper .
    unwords .
    map (\[x, y] -> if x == y then [x, 'x'] else [x, y]) .
    chunksOf 2 .
    replace "j" "i" .
    concatMap adjustLength .
    words .
    filter (\n -> n `elem` (['A'..'Z'] ++ ['a'..'z']))
    where
      adjustLength str
          | odd (length str) = str ++ "x"
          | otherwise = str
>>> playfair "playfair example" "hide the gold in the tree stump"
Just "BM OD ZB XD NA BE KU DM UI XM KZ ZR YI"

>>> unplayfair "playfair example" "BM OD ZB XD NA BE KU DM UI XM KZ ZR YI"
Just "HI DE TH EG OL DI NT HE TR EX ST UM PX"

J

Rather than implement two versions of the rules, one for encrypt, one for decrypt, let's just make a lookup table (mapping character pairs to character pairs). To decrypt we can look up in the other direction.

Implementation:

choose=: verb define
  sel=. 'Q' e. y
  alph=: (sel { 'JQ') -.~ a. {~ 65 + i.26
  norm=: [: dedouble alph restrict ('I' I.@:=&'J'@]} ])`(-.&'Q')@.sel@toupper
  ''
)

restrict=: ] -. -.~

splitDigraph=: ,`([,'X',])@.((= {.) *. 2 | #@])
dedouble=: splitDigraph/&.|.                     NB. progressively split digraphs in string

choose 'Q'
 
setkey=: verb define
  key=. ~.norm y,alph
  ref=: ,/ 2{."1 ~."1 (,"0/~ alph) ,"1 norm 'XQV'
  mode=. #. =/"2 inds=. 5 5#:key i. ref
  inds0=. (0 3,:2 1)&{@,"2 inds
  inds1=. 5|1 0 +"1 inds NB. same column
  inds2=. 5|0 1 +"1 inds NB. same row
  alt=: key {~ 5 #. mode {"_1 inds0 ,"2 3 inds1 ,:"2 inds2
  i. 0 0
)
 
pairs=: verb define 
  2{."1 -.&' '"1 ~."1 (_2]\ norm y) ,"1 'XQV'
)
 
encrypt=: verb define
  ;:inv ;/ alt{~ref i. pairs y
)
 
decrypt=: verb define
  , ref{~alt i. pairs y
)

Example use:

   choose 'IJ'

   setkey 'playfair example'
   encrypt 'Hide the gold in the tree stump'
BM OD ZB XD NA BE KU DM UI XM MO UV IF
   decrypt 'BM OD ZB XD NA BE KU DM UI XM MO UV IF'
HIDETHEGOLDINTHETREXESTUMP

Java

import java.awt.Point;
import java.util.Scanner;

public class PlayfairCipher {
    private static char[][] charTable;
    private static Point[] positions;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        String key = prompt("Enter an encryption key (min length 6): ", sc, 6);
        String txt = prompt("Enter the message: ", sc, 1);
        String jti = prompt("Replace J with I? y/n: ", sc, 1);

        boolean changeJtoI = jti.equalsIgnoreCase("y");

        createTable(key, changeJtoI);

        String enc = encode(prepareText(txt, changeJtoI));

        System.out.printf("%nEncoded message: %n%s%n", enc);
        System.out.printf("%nDecoded message: %n%s%n", decode(enc));
    }

    private static String prompt(String promptText, Scanner sc, int minLen) {
        String s;
        do {
            System.out.print(promptText);
            s = sc.nextLine().trim();
        } while (s.length() < minLen);
        return s;
    }

    private static String prepareText(String s, boolean changeJtoI) {
        s = s.toUpperCase().replaceAll("[^A-Z]", "");
        return changeJtoI ? s.replace("J", "I") : s.replace("Q", "");
    }

    private static void createTable(String key, boolean changeJtoI) {
        charTable = new char[5][5];
        positions = new Point[26];

        String s = prepareText(key + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", changeJtoI);

        int len = s.length();
        for (int i = 0, k = 0; i < len; i++) {
            char c = s.charAt(i);
            if (positions[c - 'A'] == null) {
                charTable[k / 5][k % 5] = c;
                positions[c - 'A'] = new Point(k % 5, k / 5);
                k++;
            }
        }
    }

    private static String encode(String s) {
        StringBuilder sb = new StringBuilder(s);

        for (int i = 0; i < sb.length(); i += 2) {

            if (i == sb.length() - 1)
                sb.append(sb.length() % 2 == 1 ? 'X' : "");

            else if (sb.charAt(i) == sb.charAt(i + 1))
                sb.insert(i + 1, 'X');
        }
        return codec(sb, 1);
    }

    private static String decode(String s) {
        return codec(new StringBuilder(s), 4);
    }

    private static String codec(StringBuilder text, int direction) {
        int len = text.length();
        for (int i = 0; i < len; i += 2) {
            char a = text.charAt(i);
            char b = text.charAt(i + 1);

            int row1 = positions[a - 'A'].y;
            int row2 = positions[b - 'A'].y;
            int col1 = positions[a - 'A'].x;
            int col2 = positions[b - 'A'].x;

            if (row1 == row2) {
                col1 = (col1 + direction) % 5;
                col2 = (col2 + direction) % 5;

            } else if (col1 == col2) {
                row1 = (row1 + direction) % 5;
                row2 = (row2 + direction) % 5;

            } else {
                int tmp = col1;
                col1 = col2;
                col2 = tmp;
            }

            text.setCharAt(i, charTable[row1][col1]);
            text.setCharAt(i + 1, charTable[row2][col2]);
        }
        return text.toString();
    }
}

alternative version

import java.util.Scanner;
 
public class PlayfairCipherEncryption
{
    private String KeyWord        = new String();
    private String Key            = new String();
    private char   matrix_arr[][] = new char[5][5];
 
    public void setKey(String k)
    {
        String K_adjust = new String();
        boolean flag = false;
        K_adjust = K_adjust + k.charAt(0);
        for (int i = 1; i < k.length(); i++)
        {
            for (int j = 0; j < K_adjust.length(); j++)
            {
                if (k.charAt(i) == K_adjust.charAt(j))
                {
                    flag = true;
                }
            }
            if (flag == false)
                K_adjust = K_adjust + k.charAt(i);
            flag = false;
        }
        KeyWord = K_adjust;
    }
 
    public void KeyGen()
    {
        boolean flag = true;
        char current;
        Key = KeyWord;
        for (int i = 0; i < 26; i++)
        {
            current = (char) (i + 97);
            if (current == 'j')
                continue;
            for (int j = 0; j < KeyWord.length(); j++)
            {
                if (current == KeyWord.charAt(j))
                {
                    flag = false;
                    break;
                }
            }
            if (flag)
                Key = Key + current;
            flag = true;
        }
        System.out.println(Key);
        matrix();
    }
 
    private void matrix()
    {
        int counter = 0;
        for (int i = 0; i < 5; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                matrix_arr[i][j] = Key.charAt(counter);
                System.out.print(matrix_arr[i][j] + " ");
                counter++;
            }
            System.out.println();
        }
    }
 
    private String format(String old_text)
    {
        int i = 0;
        int len = 0;
        String text = new String();
        len = old_text.length();
        for (int tmp = 0; tmp < len; tmp++)
        {
            if (old_text.charAt(tmp) == 'j')
            {
                text = text + 'i';
            }
            else
                text = text + old_text.charAt(tmp);
        }
        len = text.length();
        for (i = 0; i < len; i = i + 2)
        {
            if (text.charAt(i + 1) == text.charAt(i))
            {
                text = text.substring(0, i + 1) + 'x' + text.substring(i + 1);
            }
        }
        return text;
    }
 
    private String[] Divid2Pairs(String new_string)
    {
        String Original = format(new_string);
        int size = Original.length();
        if (size % 2 != 0)
        {
            size++;
            Original = Original + 'x';
        }
        String x[] = new String[size / 2];
        int counter = 0;
        for (int i = 0; i < size / 2; i++)
        {
            x[i] = Original.substring(counter, counter + 2);
            counter = counter + 2;
        }
        return x;
    }
 
    public int[] GetDiminsions(char letter)
    {
        int[] key = new int[2];
        if (letter == 'j')
            letter = 'i';
        for (int i = 0; i < 5; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                if (matrix_arr[i][j] == letter)
                {
                    key[0] = i;
                    key[1] = j;
                    break;
                }
            }
        }
        return key;
    }
 
    public String encryptMessage(String Source)
    {
        String src_arr[] = Divid2Pairs(Source);
        String Code = new String();
        char one;
        char two;
        int part1[] = new int[2];
        int part2[] = new int[2];
        for (int i = 0; i < src_arr.length; i++)
        {
            one = src_arr[i].charAt(0);
            two = src_arr[i].charAt(1);
            part1 = GetDiminsions(one);
            part2 = GetDiminsions(two);
            if (part1[0] == part2[0])
            {
                if (part1[1] < 4)
                    part1[1]++;
                else
                    part1[1] = 0;
                if (part2[1] < 4)
                    part2[1]++;
                else
                    part2[1] = 0;
            }
            else if (part1[1] == part2[1])
            {
                if (part1[0] < 4)
                    part1[0]++;
                else
                    part1[0] = 0;
                if (part2[0] < 4)
                    part2[0]++;
                else
                    part2[0] = 0;
            }
            else
            {
                int temp = part1[1];
                part1[1] = part2[1];
                part2[1] = temp;
            }
            Code = Code + matrix_arr[part1[0]][part1[1]]
                    + matrix_arr[part2[0]][part2[1]];
        }
        return Code;
    }
 
    public static void main(String[] args)
    {
        PlayfairCipherEncryption x = new PlayfairCipherEncryption();
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter a keyword:");
        String keyword = sc.next();
        x.setKey(keyword);
        x.KeyGen();
        System.out
                .println("Enter word to encrypt: (Make sure length of message is even)");
        String key_input = sc.next();
        if (key_input.length() % 2 == 0)
        {
            System.out.println("Encryption: " + x.encryptMessage(key_input));
        }
        else
        {
            System.out.println("Message length should be even");
        }
        sc.close();
    }
}

Julia

function playfair(key, txt, isencode=true, from = "J", to = "")
    to = (to == "" && from == "J") ? "I" : to

    function canonical(s, dup_toX=true)
        s = replace(replace(uppercase(s), from => to), r"[^A-Z]" => "")
        a, dupcount = [c for c in s], 0
        for i in 1:2:length(a)-1
            if s[i] == s[i + 1]
                dup_toX && splice!(a, i+1+dupcount:i+dupcount, 'X')
                dupcount += 1
            end
        end
        s = String(a)
        return isodd(length(s)) ? s * "X" : s
    end

    # Translate key into an encoding 5x5 translation matrix.
    keyletters = unique([c for c in canonical(key * "ABCDEFGHIJKLMNOPQRSTUVWXYZ", false)])
    m = Char.((reshape(UInt8.(keyletters[1:25]), 5, 5)'))

    # encod is a dictionary of letter pairs for encoding.
    encod = Dict()

    # Map pairs in same row or same column of matrix m.
    for i in 1:5, j in 1:5, k in 1:5
        if j != k
            encod[m[i, j] * m[i, k]] = m[i, mod1(j + 1, 5)] * m[i, mod1(k + 1, 5)]
        end
        if i != k
            encod[m[i, j] * m[k, j]] = m[mod1(i + 1, 5), j] * m[mod1(k + 1, 5), j]
        end
        # Map pairs not on same row or same column.
        for l in 1:5
            if i != k && j != l
                encod[m[i, j] * m[k, l]] = m[i, l] * m[k, j]
            end
        end
    end

    # Get array of pairs of letters from text.
    canontxt = canonical(txt)
    letterpairs = [canontxt[i:i+1] for i in 1:2:length(canontxt)-1]

    if isencode
        # Encode text
        return join([encod[pair] for pair in letterpairs], " ")
    else
        # Decode text
        decod = Dict((v, k) for (k, v) in encod)
        return join([decod[pair] for pair in letterpairs], " ")
    end
end

orig = "Hide the gold in...the TREESTUMP!!!"
println("Original: ", orig)

encoded = playfair("Playfair example", orig)
println("Encoded: ", encoded)

println("Decoded: ", playfair("Playfair example", encoded, false))
Output:
Original: Hide the gold in...the TREESTUMP!!!
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP

Kotlin

Translation of: FreeBASIC
// version 1.0.5-2

enum class PlayfairOption {
    NO_Q, 
    I_EQUALS_J
}

class Playfair(keyword: String, val pfo: PlayfairOption) {
    private val table: Array<CharArray> = Array(5, { CharArray(5) })  // 5 x 5 char array

    init {
        // build table
        val used = BooleanArray(26)  // all elements false  
        if (pfo == PlayfairOption.NO_Q) 
            used[16] = true  // Q used
        else
            used[9]  = true  // J used
        val alphabet = keyword.toUpperCase() + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        var i = 0
        var j = 0
        var c: Char
        var d: Int
        for (k in 0 until alphabet.length) {
            c = alphabet[k]
            if (c !in 'A'..'Z') continue
            d = c.toInt() - 65
            if (!used[d]) {
                table[i][j] = c
                used[d] = true
                if (++j == 5) { 
                    if (++i == 5) break // table has been filled 
                    j = 0
                }
            }          
        }
    }
    
    private fun getCleanText(plainText: String): String {
        val plainText2 = plainText.toUpperCase()  // ensure everything is upper case
        // get rid of any non-letters and insert X between duplicate letters
        var cleanText = ""
        var prevChar = '\u0000'  // safe to assume null character won't be present in plainText
        var nextChar: Char
        for (i in 0 until plainText2.length) {
            nextChar = plainText2[i]
            // It appears that Q should be omitted altogether if NO_Q option is specified - we assume so anyway
            if (nextChar !in 'A'..'Z' || (nextChar == 'Q' && pfo == PlayfairOption.NO_Q)) continue
            // If I_EQUALS_J option specified, replace J with I
            if (nextChar == 'J' && pfo == PlayfairOption.I_EQUALS_J) nextChar = 'I'
            if (nextChar != prevChar)
                cleanText += nextChar
            else
                cleanText += "X" + nextChar
            prevChar = nextChar
        }        
        val len = cleanText.length
        if (len % 2 == 1)  {  // dangling letter at end so add another letter to complete digram
            if (cleanText[len - 1] != 'X')
                cleanText += 'X'
            else 
                cleanText += 'Z'
        }
        return cleanText    
    }

    private fun findChar(c: Char): Pair<Int, Int> {
       for (i in 0..4)
           for (j in 0..4)
               if (table[i][j] == c) return Pair(i, j)
       return Pair(-1, -1)
    }

    fun encode(plainText: String): String {
        val cleanText = getCleanText(plainText)
        var cipherText = ""
        val length = cleanText.length
        for (i in 0 until length step 2) {
            val (row1, col1) = findChar(cleanText[i])
            val (row2, col2) = findChar(cleanText[i + 1])  
            cipherText += when {
                row1 == row2 -> table[row1][(col1 + 1) % 5].toString() + table[row2][(col2 + 1) % 5]
                col1 == col2 -> table[(row1 + 1) % 5][col1].toString() + table[(row2 + 1) % 5][col2]
                else         -> table[row1][col2].toString() + table[row2][col1]
            }
            if (i < length - 1) cipherText += " "  
        }
        return cipherText
    }

    fun decode(cipherText: String): String {
        var decodedText = ""
        val length = cipherText.length
        for (i in 0 until length step 3) {  // cipherText will include spaces so we need to skip them
            val (row1, col1) = findChar(cipherText[i])
            val (row2, col2) = findChar(cipherText[i + 1])  
            decodedText += when {
                row1 == row2 -> table[row1][if (col1 > 0) col1 - 1 else 4].toString() + table[row2][if (col2 > 0) col2 - 1 else 4]
                col1 == col2 -> table[if (row1 > 0) row1- 1 else 4][col1].toString() + table[if (row2 > 0) row2 - 1 else 4][col2]
                else         -> table[row1][col2].toString() + table[row2][col1]
            }
            if (i < length - 1) decodedText += " "
        }
        return decodedText
    }   

    fun printTable() {
        println("The table to be used is :\n")
        for (i in 0..4) {
            for (j in 0..4) print(table[i][j] + " ")
            println()
        }
    }
}

fun main(args: Array<String>) {
    print("Enter Playfair keyword : ")
    val keyword: String = readLine()!!
    var ignoreQ: String
    do {
         print("Ignore Q when buiding table  y/n : ")
         ignoreQ = readLine()!!.toLowerCase() 
    }
    while (ignoreQ != "y" && ignoreQ != "n")
    val pfo = if (ignoreQ == "y") PlayfairOption.NO_Q else PlayfairOption.I_EQUALS_J
    val playfair = Playfair(keyword, pfo)
    playfair.printTable()
    print("\nEnter plain text : ")
    val plainText: String = readLine()!!
    val encodedText = playfair.encode(plainText)
    println("\nEncoded text is : $encodedText") 
    val decodedText = playfair.decode(encodedText)
    println("Decoded text is : $decodedText")
}
Output:
Enter Playfair keyword : Playfair example
Ignore Q when buiding table  y/n : n
The table to be used is :

P L A Y F
I R E X M
B C D G H
K N O Q S
T U V W Z

Enter plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP

Mathematica/Wolfram Language

ClearAll[MakeTranslationTable, PlayfairCipher, PlayfairDecipher]
MakeTranslationTable[tt_List] := Module[{poss, in, out},
  poss = Tuples[Tuples[Range[5], 2], 2];
  Table[
   If[p[[1, 1]] == p[[2, 1]],
    (* same row *)
    {in, out} = {p, {{p[[1, 1]], Mod[p[[1, 2]] + 1, 5, 1]}, {p[[2, 1]], Mod[p[[2, 2]] + 1, 5, 1]}}};
    ,
    If[p[[1, 2]] == p[[2, 2]],
     (* same column *)
     {in, out} = {p, {{Mod[p[[1, 1]] + 1, 5, 1], p[[1, 2]]}, {Mod[p[[2, 1]] + 1, 5, 1], p[[2, 2]]}}};
     ,
     (*rectangle*)
     {in, out} = {p, {{p[[1, 1]], p[[2, 2]]}, {p[[2, 1]], p[[1, 2]]}}};
     ]
    ];
   StringJoin[Extract[tt, in]] -> StringJoin[Extract[tt, out]]
   ,
   {p, poss}
   ]
  ]
PlayfairCipher[txt_String, key_String, iisj_ : True] := 
 Module[{text, tt},
  text = RemoveDiacritics[ToUpperCase[txt]];
  tt = RemoveDiacritics[ToUpperCase[key]] <> CharacterRange["A", "Z"];
  text //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
  tt //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
  If[iisj,
   tt //= StringReplace["J" -> "I"];
   text //= StringReplace["J" -> "I"];
   ,
   tt //= StringReplace["Q" -> ""];
   text //= StringReplace["Q" -> ""];
   ];
  tt //= Characters /* DeleteDuplicates;
  text = FixedPoint[StringReplace[#, x_ ~~ x_ :> x ~~ "X" ~~ x, 1] &, text];
  If[OddQ[StringLength[text]], text = text <> "X"];
  If[Length[tt] == 25,
   tt = Partition[tt, 5];
   tt = MakeTranslationTable[tt];
   text = StringPartition[text, 2];
   StringRiffle[text /. tt, " "]
   ,
   Print["Something went wrong!"]
   ]
  ]
PlayfairDecipher[txt_String, key_String, iisj_ : True] := 
 Module[{text, tt},
  text = RemoveDiacritics[ToUpperCase[txt]];
  tt = RemoveDiacritics[ToUpperCase[key]] <> CharacterRange["A", "Z"];
  text //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
  tt //= StringReplace[Except[Alternatives @@ CharacterRange["A", "Z"]] -> ""];
  If[iisj,
   tt //= StringReplace["J" -> "I"];
   text //= StringReplace["J" -> "I"];
   ,
   tt //= StringReplace["Q" -> ""];
   text //= StringReplace["Q" -> ""];
   ];
  tt //= Characters /* DeleteDuplicates;
  If[OddQ[StringLength[text]], text = text <> "X"];
  If[Length[tt] == 25,
   tt = Partition[tt, 5];
   tt = MakeTranslationTable[tt];
   text = StringPartition[text, 2];
   StringRiffle[text /. (Reverse /@ tt), " "]
   ,
   Print["Something went wrong!"]
   ]
  ]
PlayfairCipher["Hide the gold in...the TREESTUMP!!!", "Playfair example"]
PlayfairDecipher[%, "Playfair example"]
Output:
BM OD ZB XD NA BE KU DM UI XM MO UV IF
HI DE TH EG OL DI NT HE TR EX ES TU MP

Nim

Translation of: Java
import pegs, strutils

type
  Position = tuple[x, y: int]
  Playfair = object
    positions: array['A'..'Z', Position]
    table: array[5, array[5, char]]

const None: Position = (-1, -1)   # Default value for positions.


proc initPlayfair(key: string; jti: bool): Playfair =

  for item in result.positions.mitems: item = None

  var alphabet = key & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  alphabet = if jti: alphabet.replace('J', 'I')
             else: alphabet.replace("Q", "")

  var k = 0
  for ch in alphabet:
    if result.positions[ch] == None:
      result.table[k div 5][k mod 5] = ch
      result.positions[ch] = (k mod 5, k div 5)
      inc k


proc codec(playfair: Playfair; text: string; direction: int): string =

  result.setLen(text.len)

  for i in countup(0, text.high, 2):
    var
      (col1, row1) = playfair.positions[text[i]]
      (col2, row2) = playfair.positions[text[i + 1]]

    if row1 == row2:
      col1 = (col1 + direction) mod 5
      col2 = (col2 + direction) mod 5
    elif col1 == col2:
      row1 = (row1 + direction) mod 5
      row2 = (row2 + direction) mod 5
    else:
      swap col1, col2

    result[i] = playfair.table[row1][col1]
    result[i + 1] = playfair.table[row2][col2]


proc encode(playfair: Playfair; text: string): string =
  var
    text = text
    i = 0

  while i < text.len:
    if i == text.high:
      if (text.len and 1) != 0:
        text.add 'X'
    elif text[i] == text[i + 1]:
      text.insert("X", i + 1)
    inc i, 2

  result = playfair.codec(text, 1)


proc decode(playfair: Playfair; text: string): string =
  result = playfair.codec(text, 4)


proc prompt(msg: string): string =
  stdout.write msg
  try:
    result = stdin.readLine()
  except EOFError:
    echo ""
    quit getCurrentExceptionMsg(), QuitFailure


when isMainModule:

  var key: string
  while key.len <= 6:
    key = prompt("Enter an encryption key (min letters 6): ").toUpperAscii().replace(peg"[^A-Z]")

  var text: string
  while text.len == 0:
    text = prompt("Enter the message: ").toUpperAscii().replace(peg"[^A-Z]")

  var answer: string
  while answer notin ["y", "n"]:
    answer = prompt("Replace J with I? y/n: ").toLowerAscii()
  let jti = (answer == "y")

  let playfair = initPlayfair(key, jti)
  let enc = playfair.encode(text)
  let dec = playfair.decode(enc)

  echo "Encoded message: ", enc
  echo "Decoded message: ", dec
Output:
Enter an encryption key (min letters 6): Playfair example
Enter the message: Hide the gold...in the TREESTUMP!!!
Replace J with I? y/n: y
Encoded message: BMODZBXDNABEKUDMUIXMMOUVIF
Decoded message: HIDETHEGOLDINTHETREXESTUMP

NetRexx

/* NetRexx */
options replace format comments java crossref symbols nobinary

-- input arguments:
--   encipher/decipher IQ-switch key text
--   encipher/decipher -
--     a character E, D (default E; . is an alias for E)
--   IQ-switch         -
--     a character I, Q (default I for I==J, Q for exclude Q; . is an alias for I)
--   key               -
--     the cipher key - default plugh_xyzzy (really just PLUGHXYZ)
--   text              -
--     the text to encipher/decipher

runSample(arg)
return

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method cipher(d_, km, digraphs) public static
  if d_.upper() = 'D' then d_ = -1 -- encode or decode adjustment
  else                     d_ = +1
  cipherText = ''
  loop dw = 1 to digraphs.words()
    -- process the digraphs one at a time
    digraph = digraphs.word(dw)
    cl = ''
    cr = ''
    -- get each letter from the digraph
    parse digraph dl +1 dr
    loop r_ = 1 to km[0] while (cl cr).words() < 4
      -- locate the row/col of each letter in the cipher matrix
      clx = km[r_].wordpos(dl)
      crx = km[r_].wordpos(dr)
      if clx > 0 then cl = r_ clx
      if crx > 0 then cr = r_ crx
      end r_

    -- process the digraph's rows, columns or rectangles
    select
      when cl.word(1) = cr.word(1) then do
        -- a row
        rx  = cl.word(1)
        clx = cl.word(2) + d_
        crx = cr.word(2) + d_
        if clx > 5 then clx = 1 -- wrap
        if crx > 5 then crx = 1
        if clx < 1 then clx = 5
        if crx < 1 then crx = 5
        cy = km[rx].word(clx) || km[rx].word(crx)
        cipherText = cipherText cy
        end
      when cl.word(2) = cr.word(2) then do
        -- a column
        cx  = cl.word(2)
        rlx = cl.word(1) + d_
        rrx = cr.word(1) + d_
        if rlx > 5 then rlx = 1 -- wrap
        if rrx > 5 then rrx = 1
        if rlx < 1 then rlx = 5
        if rrx < 1 then rrx = 5
        cy = km[rlx].word(cx) || km[rrx].word(cx)
        cipherText = cipherText cy
        end
      otherwise do
        -- a rectangle
        r1  = cl.word(1)
        r2  = cr.word(1)
        cy = km[r1].word(cr.word(2)) || km[r2].word(cl.word(2))
        cipherText = cipherText cy
        end
      end
    end dw
  return cipherText.strip()

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method encipher(km, digraphs) public static
  return cipher('E', km, digraphs)

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method decipher(km, digraphs) public static
  return cipher('D', km, digraphs)

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method getDigraphs(text, IorQ, ED) private static
  a2z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  if ED.upper() \= 'D' then eks = 'X'
  else                      eks = ''
  tp = text.upper().translate('', a2z)
  text = text.upper().translate(' '.copies(tp.length()), tp).space(0)
  rtext = ''
  ll = ''

  loop while text \= ''
    parse text lc +1 text
    if ll \= lc then rtext = rtext || lc
    else             rtext = rtext || eks || lc
    ll = lc
    end

  -- I == J or remove Q fixup
  if IorQ \= 'Q' then
    parse 'J I' IorQ sc .
  else
    sc = ''

  rtext = rtext.changestr(IorQ, sc)
  if rtext.length() // 2 \= 0 then rtext = rtext || eks
  digraphs = ''
  loop while rtext \= ''
    parse rtext digraph +2 rtext
    digraphs = digraphs digraph
    end
  return digraphs.space()

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method getKey(key, IorQ) private static
  a2z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  kp = (key || a2z).upper()
  kr = ''
  loop while kp \= ''
    parse kp xx +1 kp
    if \xx.datatype('u') then iterate
    kr = kr xx
    kp = kp.changestr(xx, '')
    end

  if IorQ = 'I' | IorQ = 'J' | IorQ = '' then
    kr = kr.changestr('J', '')
  else
    kr = kr.changestr('Q', '')

  return kr.space()

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method getKeyMatrix(kr) private static
  km = ''
  loop r_ = 1 while kr \= ''
    parse kr kp +10 kr
    km[0]  = r_
    km[r_] = kp
    end r_
  return km

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method runSample(arg) private static
  parse arg ciph IorQ key text
  if ciph = '' | ciph = '.' then ciph = 'E'
  if IorQ = '' | IorQ = '.' then IorQ = 'I'
  if key  = '' | key  = '.' then key  = 'plugh_xyzzy'
  if text = '' | text = '.' then text = 'NetRexx; not just a programing language for kings & queens!'

  kr = getKey(key.space(0), IorQ)
  km = getKeyMatrix(kr)

  digraphs = getDigraphs(text, IorQ, ciph)
  -- fixup for a Q in the text when Q is excluded (substitute K for Q)
  if IorQ.upper = 'Q' then digraphs = digraphs.changestr('Q', 'K')
  if ciph = 'E' then say text
  say digraphs
  if ciph.upper() = 'E' then
    say encipher(km, digraphs)
  else 
    say decipher(km, digraphs)

  return
Output:
$ java -cp .:.. RPlayfairCipher00
NetRexx; not just a programing language for kings & queens!
NE TR EX XX NO TI US TA PR OG RA MI NG LA NG UA GE FO RK IN GS QU EX EN SX
TN VS CZ YY OQ WE LT VZ XP VA VX QD OU GY OU GZ UF OV PR EQ LV NH CZ NT RY

$ java -cp .:.. RPlayfairCipher00 d . . TN VS CZ YY OQ WE LT VZ XP VA VX QD OU GY OU GZ UF OV PR EQ LV NH CZ NT RY
TN VS CZ YY OQ WE LT VZ XP VA VX QD OU GY OU GZ UF OV PR EQ LV NH CZ NT RY
NE TR EX XX NO TI US TA PR OG RA MI NG LA NG UA GE FO RK IN GS QU EX EN SX

$ java -cp .:.. RPlayfairCipher00 e q playfair Hide the gold in the tree stump\!\!
Hide the gold in the tree stump!!
HI DE TH EG OL DI NT HE TR EX ES TU MP
EB IK OK GH NA IR OM JG ND JU JM MZ UI

$ java -cp .:.. RPlayfairCipher00 d q playfair EB IK OK GH NA IR OM JG ND JU JM MZ UI
EB IK OK GH NA IR OM JG ND JU JM MZ UI
HI DE TH EG OL DI NT HE TR EX ES TU MP

ooRexx

/*---------------------------------------------------------------------
* REXX program implements a PLAYFAIR cipher (encryption & decryption).
* 11.11.2013 Walter Pachl revamped, for ooRexx, the REXX program
*                the logic of which was devised by Gerard Schildberger
* Invoke as rexx pf O abcd efgh ( phrase to be processed
* Defaults:           'Playfair example.'
*                   J
*                                 'Hide the gold in the tree stump'
* Major changes: avoid language elements not allowed in ooRexx
*                show use of a.[expr1,expr2]
*                allow key to be more than one word
*                add restriction of using X or Q in text
* 12.11.2013 change order of arguments
*            and comment the use of a.[expr1,expr2]
*            Program should run on all Rexxes that have changestr-bif
*--------------------------------------------------------------------*/
  Parse Upper Version v
  oorexx=pos('OOREXX',v)>0

  Parse Arg omit oldk '(' text
  If omit='' Then omit='J'
  If oldk='' Then oldk='Playfair example.'
  If text=''            Then text='Hide the gold in the tree stump!!'

  newkey=scrub(oldk,1)
  newtext=scrub(text)
  If newtext==''        Then Call err 'TEXT is empty or has no letters'
  If length(omit)\==1   Then Call err 'OMIT letter must be only one letter'
  If\datatype(omit,'M') Then Call err 'OMIT letter must be a Latin alphabet letter.'
  omit=translate(omit)
  cant='must not contain the "OMIT" character: ' omit
  If pos(omit,newtext)\==0 Then Call err 'TEXT' cant
  If pos(omit,newkey)\==0  Then Call err 'cipher key' cant
  abc='abcdefghijklmnopqrstuvwxyz'
  abcu=translate(abc)                 /* uppercase alphabet           */
  abcx=space(translate(abcu,,omit),0) /*elide OMIT char from alphabet */
  xx='X'                            /* char used for double characters*/
  If omit==xx Then
    xx='Q'
  If pos(xx,newtext)>0 Then
    Call err 'Sorry,' xx 'is not allowed in text'
  If length(newkey)<3 Then
    Call err 'cipher key is too short, must be at least 3 different letters'
  abcx=space(translate(abcx,,newkey),0) /*remove any cipher characters   */
  grid=newkey||abcx                     /* only first  25  chars are used*/
  Say 'old cipher key: ' strip(oldk)
  padl=14+2
  pad=left('',padl)
  Say 'new cipher key: ' newkey
  padx=left('',padl,"-")'Playfair'
  Say '     omit char: ' omit       /* [?]  lowercase of double char. */
  Say '   double char: ' xx
  lxx=translate(xx,abc,abcu)
  Say ' original text: ' strip(text)/* [?]  doubled version of  Lxx.  */
  Call show 'cleansed',newtext
  lxxlxx=lxx||lxx
  n=0                               /* number of grid characters used.*/
  Do row=1 For 5                    /* build array of individual cells*/
    Do col=1 For 5
      n=n+1
      a.row.col=substr(grid,n,1)
      If row==1 Then
        a.0.col=a.1.col
      If col==5 Then Do
        a.row.6=a.row.1
        a.row.0=a.row.5
        End
      If row==5 Then Do
        a.6.col=a.1.col
        a.0.col=a.5.col
        End
      End
    End

  etext=playfair(newtext,1)
  Call show 'encrypted',etext
  ptext=playfair(etext,-1)
  Call show 'plain',ptext
  qtext=changestr(xx||xx,ptext,lxx)     /*change doubled doublechar-?sing*/
  qtext=changestr(lxx||xx,qtext,lxxlxx) /*change Xx --? lowercase dblChar*/
  qtext=space(translate(qtext,,xx),0)   /*remove char used for "doubles."*/
  qtext=translate(qtext)                /*reinstate the use of upperchars*/
  Call show 'decoded',qtext
  Say ' original text: ' newtext        /* ·· and show the original text.*/
  Say ''
  Exit

playfair:
/*---------------------------------------------------------------------
* encode (e=1) or decode (e=-1) the given text (t) using the grid
*--------------------------------------------------------------------*/
  Arg t,e
  d=''
  If e=1 Then Do
    Do k=1 By 1 Until c=''
      c=substr(t,k,1)
      If substr(t,k+1,1)==c Then
        t=left(t,k)||lxx||substr(t,k+1)
      End
    End
  t=translate(t)
  Do j=1 By 2 To length(t)
    c2=strip(substr(t,j,2))
    If length(c2)==1 Then
      c2=c2||xx                         /* append X or Q char, rule 1*/
    Call LR
    Select
      /*- This could/should be used on ooRexx -------------------------
      When rowl==rowr Then c2=a.[rowl,coll+e]a.[rowr,colr+e] /*rule 2*/
      When coll==colr Then c2=a.[rowl+e,coll]a.[rowr+e,colr] /*rule 3*/
      *--------------------------------------------------------------*/
      When rowl==rowr Then c2=aa(rowl,coll+e)aa(rowr,colr+e) /*rule 2*/
      When coll==colr Then c2=aa(rowl+e,coll)aa(rowr+e,colr) /*rule 3*/
      Otherwise            c2=a.rowl.colr||a.rowr.coll       /*rule 4*/
      End
    d=d||c2
    End
  Return d

aa:
/*---------------------------------------------------------------------
* ooRexx allows to use a.[rowl,coll+e]
* this function can be removed when ooRexx syntax is used to access a.
*--------------------------------------------------------------------*/
  Parse Arg xrow,xcol
  Return a.xrow.xcol

err:
/*---------------------------------------------------------------------
* Exit with an error message
*--------------------------------------------------------------------*/
  Say
  Say '***error!***' arg(1)
  Say
  Exit 13

lr:
/*---------------------------------------------------------------------
* get grid positions of the 2 characters
*--------------------------------------------------------------------*/
  Parse Value rowcol(left(c2,1)) with rowl coll
  Parse Value rowcol(right(c2,1)) with rowr colr
  Return

rowcol: procedure Expose grid
/*---------------------------------------------------------------------
* compute row and column of the given character in the 5x5 grid
*--------------------------------------------------------------------*/
  Parse Arg c
  p=pos(c,grid)
  col=(p-1)//5+1
  row=(4+p)%5
  Return row col

show:
/*---------------------------------------------------------------------
* Show heading and text
*--------------------------------------------------------------------*/
  Arg,y
  Say
  Say right(arg(1) 'text: ',padl) digram(arg(2))
  result=space(arg(2),0)
  If arg(1)='decoded' Then Do
    result=strip(result,'T',xx)
    End
  Say pad result
  Return

scrub: Procedure
/*---------------------------------------------------------------------
* Remove all non-letters from the given string, uppercase letters
* and, if unique=1 remove duplicates
* 'aB + c1Bb' -> 'ABCBB' or 'ABC', respectively
*--------------------------------------------------------------------*/
  Arg xxx,unique                    /* ARG caps all args             */
  d=''
  used.=0
  Do While xxx<>''
    Parse Var xxx c +1 xxx
    If datatype(c,'U') Then
      If (unique=1 & pos(c,d)=0) |,
          unique<>1 Then
        d=d||c
    End
  Return d

digram: Procedure
/*---------------------------------------------------------------------
* Return the given string as character pairs separated by blanks
* 'ABCDEF' -> 'AB CD EF'
* 'ABCDE'  -> 'AB CD E'
*--------------------------------------------------------------------*/
  Parse Arg x
  d=''
  Do j=1 By 2 To length(x)
    d=d||substr(x,j,2)' '
    End
  Return strip(d)

Output (sample):

old cipher key:  this is my little key
new cipher key:  THISMYLEK
     omit char:  X
   double char:  Q
 original text:  to be or not to bee

 cleansed text:  TO BE OR NO TT OB EE
                 TOBEORNOTTOBEE

encrypted text:  IJ DY JV OP MJ IJ DY OA JJ
                 IJDYJVOPMJIJDYOAJJ

    plain text:  TO BE OR NO TQ TO BE QE QQ
                 TOBEORNOTQTOBEQEQQ

  decoded text:  TO BE OR NO TT OB EE Q
                 TOBEORNOTTOBEE
 original text:  TOBEORNOTTOBEE

Perl

Translation of: Raku
use Math::Cartesian::Product;

# Pregenerate all forward and reverse translations
sub playfair {
    our($key,$from) = @_;
    $from //= 'J';
    our $to = $from eq 'J' ? 'I' : '';
    my(%ENC,%DEC,%seen,@m);

    sub canon {
        my($str) = @_;
        $str =~ s/[^[:alpha:]]//g;
        $str =~ s/$from/$to/gi;
        uc $str;
    }

    my @uniq = grep { ! $seen{$_}++ } split '', canon($key . join '', 'A'..'Z');
    while (@uniq) { push @m, [splice @uniq, 0, 5] }

    # Map pairs in same row.
    for my $r (@m)  {
        for my $x (cartesian {@_} [0..4], [0..4]) {
        my($i,$j) = @$x;
        next if $i == $j;
        $ENC{ @$r[$i] . @$r[$j] } =  @$r[($i+1)%5] . @$r[($j+1)%5];
        }
    }

    # Map pairs in same column.
    for my $c (0..4) {
        my @c = map { @$_[$c] } @m;
        for my $x (cartesian {@_} [0..4], [0..4]) {
        my($i,$j) = @$x;
        next if $i == $j;
        $ENC{ $c[$i] . $c[$j] } = $c[($i+1)%5] . $c[($j+1)%5];
        }
    }

    # Map pairs with cross-connections.
    for my $x (cartesian {@_} [0..4], [0..4], [0..4], [0..4]) {
        my($i1,$j1,$i2,$j2) = @$x;
        next if $i1 == $i2 or $j1 == $j2;
        $ENC{ $m[$i1][$j1] . $m[$i2][$j2] } = $m[$i1][$j2] . $m[$i2][$j1];
    }

    # Generate reverse translations.
     while (my ($k, $v) = each %ENC) { $DEC{$v} = $k }

    # Return code-refs for encoding/decoding routines
    return
    sub { my($red) = @_; # encode
        my $str = canon($red);

        my @list;
        while ($str =~ /(.)(?(?=\1)|(.?))/g) {
            push @list, substr($str,$-[0], $-[2] ? 2 : 1);
        }
        join ' ', map { length($_)==1 ? $ENC{$_.'X'} : $ENC{$_} } @list;
    },
    sub { my($black) = @_; # decode
        join ' ', map { $DEC{$_} } canon($black) =~ /../g;
    }
}

my($encode,$decode) = playfair('Playfair example');

my $orig  = "Hide the gold in...the TREESTUMP!!!";
my $black = &$encode($orig);
my $red   = &$decode($black);
print " orig:\t$orig\n";
print "black:\t$black\n";
print "  red:\t$red\n";
Output:
 orig:  Hide the gold in...the TREESTUMP!!!
black:  BM OD ZB XD NA BE KU DM UI XM MO UV IF
  red:  HI DE TH EG OL DI NT HE TR EX ES TU MP

Phix

Originally translated from Kotlin, now uses a combined routine (playfair) for encoding and decoding, and direct char lookups, and x removal.

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})
Output:
Playfair keyword : Playfair example
Option: J=I or no Q (J/Q) : Q
The table to be used is :

PLAYF
IREXM
BCDGH
JKNOS
TUVWZ

The plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM ND ZB XD KY BE JV DM UI XM MN UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR E_ ES TU MP

The only difference when option is 'J' is the 4th line of table is KNOQS

Python

Translation of: Raku
from string import ascii_uppercase
from itertools import product
from re import findall

def uniq(seq):
    seen = {}
    return [seen.setdefault(x, x) for x in seq if x not in seen]

def partition(seq, n):
    return [seq[i : i + n] for i in xrange(0, len(seq), n)]


"""Instantiate a specific encoder/decoder."""
def playfair(key, from_ = 'J', to = None):
    if to is None:
        to = 'I' if from_ == 'J' else ''

    def canonicalize(s):
        return filter(str.isupper, s.upper()).replace(from_, to)

    # Build 5x5 matrix.
    m = partition(uniq(canonicalize(key + ascii_uppercase)), 5)

    # Pregenerate all forward translations.
    enc = {}

    # Map pairs in same row.
    for row in m:
        for i, j in product(xrange(5), repeat=2):
            if i != j:
                enc[row[i] + row[j]] = row[(i + 1) % 5] + row[(j + 1) % 5]

    # Map pairs in same column.
    for c in zip(*m):
        for i, j in product(xrange(5), repeat=2):
            if i != j:
                enc[c[i] + c[j]] = c[(i + 1) % 5] + c[(j + 1) % 5]

    # Map pairs with cross-connections.
    for i1, j1, i2, j2 in product(xrange(5), repeat=4):
        if i1 != i2 and j1 != j2:
            enc[m[i1][j1] + m[i2][j2]] = m[i1][j2] + m[i2][j1]

    # Generate reverse translations.
    dec = dict((v, k) for k, v in enc.iteritems())

    def sub_enc(txt):
        lst = findall(r"(.)(?:(?!\1)(.))?", canonicalize(txt))
        return " ".join(enc[a + (b if b else 'X')] for a, b in lst)

    def sub_dec(encoded):
        return " ".join(dec[p] for p in partition(canonicalize(encoded), 2))

    return sub_enc, sub_dec


(encode, decode) = playfair("Playfair example")
orig = "Hide the gold in...the TREESTUMP!!!"
print "Original:", orig
enc = encode(orig)
print "Encoded:", enc
print "Decoded:", decode(enc)
Output:
Original: Hide the gold in...the TREESTUMP!!!
Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP

Raku

(formerly Perl 6)

# Instantiate a specific encoder/decoder.

sub playfair( $key,
              $from = 'J',
	      $to = $from eq 'J' ?? 'I' !! ''
) {

    sub canon($str) { $str.subst(/<-alpha>/,'', :g).uc.subst(/$from/,$to,:g) }

    # Build 5x5 matrix.
    my @m = canon($key ~ ('A'..'Z').join).comb.unique.map: 
     -> $a,$b,$c,$d,$e { [$a,$b,$c,$d,$e] }

    # Pregenerate all forward translations.
    my %ENC = gather {
	# Map pairs in same row.
	for @m -> @r {
	    for ^@r X ^@r -> (\i,\j) {
		next if i == j;
		take @r[i] ~ @r[j] => @r[(i+1)%5] ~ @r[(j+1)%5];
	    }
	}

	# Map pairs in same column.
	for ^5 -> $c {
	    my @c = @m.map: *.[$c];
	    for ^@c X ^@c -> (\i,\j) {
		next if i == j;
		take @c[i] ~ @c[j] => @c[(i+1)%5] ~ @c[(j+1)%5];
	    }
	}

	# Map pairs with cross-connections.
	for ^5 X ^5 X ^5 X ^5 -> (\i1,\j1,\i2,\j2) {
	    next if i1 == i2 or j1 == j2;
	    take @m[i1][j1] ~ @m[i2][j2] => @m[i1][j2] ~ @m[i2][j1];
	}
    }

    # Generate reverse translations.
    my %DEC = %ENC.invert;

    return
        sub enc($red) {
	    my @list = canon($red).comb(/(.) (.?) <?{ $1 ne $0 }>/);
	    ~@list.map: { .chars == 1 ?? %ENC{$_~'X'} !! %ENC{$_} }
	},
	sub dec($black) {
	    my @list = canon($black).comb(/../);
	    ~@list.map: { %DEC{$_} }
	}
}

my (&encode,&decode) = playfair 'Playfair example';

my $orig = "Hide the gold in...the TREESTUMP!!!";
say " orig:\t$orig";

my $black = encode $orig;
say "black:\t$black";

my $red = decode $black;
say "  red:\t$red";
Output:
 orig:	Hide the gold in...the TREESTUMP!!!
black:	BM OD ZB XD NA BE KU DM UI XM MO UV IF
  red:	HI DE TH EG OL DI NT HE TR EX ES TU MP

REXX

Quite a bit of the REXX code deals with error checking, accepting arguments, and displaying the options used, and displaying input and output.

For ease of viewing and comparing, the output is in capitalized digraphs (which are really digrams) as well as the original input(s).

Thanks to Walter Pachl, this program is now sensitive of using a suitable double character when   X   is present in the cipher key.
Also, more thanks are due to Walter Pachl for finding that the cipher key can't contain the OMIT character.

A fair amount of code was added to massage the decrypted encryption to remove doubled   Xes   so as to match the original text
(this is the     possible text     part of the REXX code).

/*REXX program implements a   PLAYFAIR cipher   (encryption  and  decryption).          */
@abc= 'abcdefghijklmnopqrstuvwxyz';  @abcU= @abc /*literals for  lower and upper  ABC's.*/
parse arg omit key  '('  text                    /*TEXT  is the phrase to be used.      */
oldKey= key                                      /*save the old key.                    */
if key =='' | key ==','    then do;       key= 'Playfair example.'
                                       oldKey= key "   ◄───using the default."
                                end
if omit=='' | omit==','    then omit= 'J'        /*the  "omitted"  character string.    */
if text=''                 then text= 'Hide the gold in the tree stump!!'     /*default.*/
upper omit @abcU                                 /*uppercase OMIT characters & alphabet.*/
@cant= 'can''t contain the "OMIT" character: '  omit       /*literal used in error text.*/
@uchars= 'unique characters.'                    /*a literal used below in an error msg.*/
newKey = scrub(key, 1)                           /*scrub old cipher key  ──►  newKey    */
newText= scrub(text  )                           /*  "    "     text     ──►  newText   */
if newText==''             then call err 'TEXT is empty or has no letters.'
if length(omit)\==1        then call err 'OMIT letter must be only one letter.'
if \datatype(omit, 'M')    then call err 'OMIT letter must be a Latin alphabet letter.'
if pos(omit, newText)\==0  then call err 'TEXT'        @cant
if pos(omit, newKey) \==0  then call err 'cipher key'  @cant
fill= space( translate(@abcU, , omit),  0)       /*elide OMIT characters from alphabet. */
xx= 'X'                                          /*character used for double characters.*/
if omit==xx  then xx= 'Q'                        /*    "       "   "     "       "      */
if length(newKey)<3        then call err 'cipher key is too short, must be ≥ 3' @uchars
fill= space( translate(fill, , newKey), 0)       /*remove any cipher characters.        */
grid= newKey || fill                             /*only first  25  characters are used. */
say 'old cipher key: '  strip(oldKey)
say 'new cipher key: '  newKey
say '     omit char: '  omit
say '   double char: '  xx
say ' original text: '  strip(text)
padL= 14 + 2
call show 'cleansed', newText
#= 0                                             /*number of grid characters used.      */
         do row   =1  for 5                      /*build array of individual cells.     */
            do col=1  for 5;                 #= # + 1;       @.row.col= substr(grid, #, 1)
            if row==1  then            @.0.col= @.1.col
            if col==5  then do;        @.row.6= @.row.1;     @.row.0= @.row.5;    end
            if row==5  then do;        @.6.col= @.1.col;     @.0.col= @.5.col;    end
            end   /*col*/
         end      /*row*/
pad = left('', padL)
padX= left('', padL, "═")'Playfair'
Lxx = translate(xx, @abc, @abcU)                 /* [↓]  lowercase of double character. */
LxxLxx= Lxx || Lxx                               /* [↓]  doubled version of  Lxx.       */
eText= .Playfair(newText, 1);          call show 'encrypted' , eText
pText= .Playfair(eText     );          call show 'plain'     , pText
qText= changestr(xx  || xx, pText, Lxx)          /*change doubled doublechar ──► single.*/
qText= changestr(Lxx || xx, qText, LxxLxx)       /*change  xx ──► lowercase dblCharacter*/
qText= space( translate( qText, , xx), 0)        /*remove character used for "doubles". */
upper qText                                      /*reinstate the use of upper characters*/
if length(qText)\==length(pText)  then call show 'possible',  qText
say ' original text: '  newText;       say       /*··· and also show the original text. */
if qtext==newText  then say padx 'encryption──► decryption──► encryption worked.'
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
@@:    parse arg Xrow,Xcol;                         return  @.Xrow.Xcol
err:   say;   say '***error!***'  arg(1);           say;    exit 13
LR:    rowL= row(left(__, 1)); colL= _; rowR= row(right(__,1)); colR= _; return length(__)
row:   ?= pos(arg(1), grid);      _= (?-1) // 5  +  1;      return  (4+?) % 5
show:  arg ,y; say; say right(arg(1) 'text: ',padL) digram(y); say pad space(y, 0); return
/*──────────────────────────────────────────────────────────────────────────────────────*/
.Playfair: arg T,encrypt;         i= -1;     if encrypt==1  then i= 1;          $=
                    do k=1  while  i==1;     _= substr(T, k, 1);     if _==' '  then leave
                    if _==substr(T, k+1, 1)  then T= left(T, k) || Lxx || substr(T, k + 1)
                    end     /*k*/
           upper T
                    do j=1  by 2  to length(T);     __= strip( substr(T, j, 2) )
                    if LR()==1  then __= __ || xx;  call LR /*append X or Q char, rule 1*/
                      select                                                      /*rule*/
                      when rowL==rowR  then __= @@(rowL,   colL+i)@@(rowR,   colR+i) /*2*/
                      when colL==colR  then __= @@(rowL+i, colL  )@@(rowR+i, colR)   /*3*/
                      otherwise             __= @@(rowL,   colR  )@@(rowR,   colL)   /*4*/
                      end   /*select*/
                    $= $ || __
                    end     /*j*/
           return $
/*──────────────────────────────────────────────────────────────────────────────────────*/
digram: procedure; parse arg x,,$;          do j=1  by 2  to length(x)
                                            $= $  ||  substr(x, j, 2)' '
                                            end   /*j*/
        return strip($)
/*──────────────────────────────────────────────────────────────────────────────────────*/
scrub:  procedure; arg xxx,unique;    xxx= space(xxx, 0)      /*ARG capitalizes all args*/
        $=;            do j=1  for length(xxx);    _= substr(xxx, j, 1)
                       if unique==1  then  if  pos(_, $)\==0  then iterate  /*is unique?*/
                       if datatype(_, 'M')  then $= $  ||  _  /*only use Latin letters. */
                       end   /*j*/
        return $

Some older REXXes don't have a changestr bif, so one is included here ──► CHANGESTR.REX.

output   when using the default inputs:
old cipher key:  Playfair example.    ◄───using the default.
new cipher key:  PLAYFIREXM
     omit char:  J
   double char:  X
 original text:  Hide the gold in the tree stump!!

 cleansed text:  HI DE TH EG OL DI NT HE TR EE ST UM P
                 HIDETHEGOLDINTHETREESTUMP

encrypted text:  BM OD ZB XD NA BE KU DM UI XM MO UV IF
                 BMODZBXDNABEKUDMUIXMMOUVIF

    plain text:  HI DE TH EG OL DI NT HE TR EX ES TU MP
                 HIDETHEGOLDINTHETREXESTUMP

 possible text:  HI DE TH EG OL DI NT HE TR EE ST UM P
                 HIDETHEGOLDINTHETREESTUMP
 original text:  HIDETHEGOLDINTHETREESTUMP

════════════════Playfair encryption──► decryption──► encryption worked. 
output   when using the input of:     x   stuvw   (myteest
old cipher key:  stuvw
new cipher key:  STUVW
     omit char:  X
   double char:  Q
 original text:  myteest

 cleansed text:  MY TE ES T
                 MYTEEST

encrypted text:  NR WB ZB TU
                 NRWBZBTU

    plain text:  MY TE QE ST
                 MYTEQEST

 possible text:  MY TE ES T
                 MYTEEST
 original text:  MYTEEST

════════════════Playfair encryption──► decryption──► encryption worked.

Ruby

Printing the cipher in pairs just advertises the mechanism of encoding; I've gone with the traditional grouping into sequences of five letters instead.

class Playfair
  Size = 5
  def initialize(key, missing)
    @missing = missing.upcase
    alphabet = ('A'..'Z').to_a.join.upcase.delete(@missing).split''
    extended = key.upcase.gsub(/[^A-Z]/,'').split('') + alphabet
    grid = extended.uniq[0...Size*Size].each_slice(Size).to_a
    coords = {}
    grid.each_with_index do |row, i|
      row.each_with_index do |letter, j|
       coords[letter] = [i,j]
      end
    end
    @encode = {}
    alphabet.product(alphabet).reject { |a,b| a==b }.each do |a, b|
      i1, j1 = coords[a]
      i2, j2 = coords[b]
      if i1 == i2 then
         j1 = (j1 + 1) % Size
         j2 = (j2 + 1) % Size
      elsif j1 == j2 then
         i1 = (i1 + 1) % Size
         i2 = (i2 + 1) % Size
      else
         j1, j2 = j2, j1
      end
      @encode["#{a}#{b}"] = "#{grid[i1][j1]}#{grid[i2][j2]}"
      @decode = @encode.invert
    end
  end

  def encode(plaintext) 
    plain = plaintext.upcase.gsub(/[^A-Z]/,'')
    if @missing == 'J' then
      plain = plain.gsub(/J/, 'I')
    else
      plain = plain.gsub(@missing, 'X')
    end
    plain = plain.gsub(/(.)\1/, '\1X\1')
    if plain.length % 2 == 1 then
      plain += 'X'
    end
    return plain.upcase.split('').each_slice(2).map do |pair|
      @encode[pair.join]
    end.join.split('').each_slice(5).map{|s|s.join}.join(' ')
  end

  def decode(ciphertext) 
    cipher = ciphertext.upcase.gsub(/[^A-Z]/,'')
    return cipher.upcase.split('').each_slice(2).map do |pair|
      @decode[pair.join]
    end.join.split('').each_slice(5).map{|s|s.join}.join(' ')
  end
end
Output:
irb(main):001:0> cipher = (p=Playfair.new 'Playfair example','J').encode('hide the gold in the tree stump')
=> "BMODZ BXDNA BEKUD MUIXM MOUVI F"
irb(main):002:0> p.decode(cipher)
=> "HIDET HEGOL DINTH ETREX ESTUM P"

Sidef

Translation of: Raku
func playfair(key, from = 'J', to = (from == 'J' ? 'I' : '')) {

    func canon(str) {
        str.gsub(/[^[:alpha:]]/, '').uc.gsub(from, to)
    }

    var m = canon(key + ('A'..'Z' -> join)).chars.uniq.slices(5)

    var :ENC = gather {
        m.each { |r|
            for i,j in (^r ~X ^r) {
                i == j && next
                take(Pair("#{r[i]}#{r[j]}", "#{r[(i+1)%5]}#{r[(j+1)%5]}"))
            }
        }

        ^5 -> each { |k|
            var c = m.map {|a| a[k] }
            for i,j in (^c ~X ^c) {
                i == j && next
                take(Pair("#{c[i]}#{c[j]}", "#{c[(i+1)%5]}#{c[(j+1)%5]}"))
            }
        }

        cartesian([^5, ^5, ^5, ^5], {|i1,j1,i2,j2|
            i1 == i2 && next
            j1 == j2 && next
            take(Pair("#{m[i1][j1]}#{m[i2][j2]}", "#{m[i1][j2]}#{m[i2][j1]}"))
        })
    }.map { (.key, .value) }...

    var DEC = ENC.flip

    func enc(red) {
        gather {
            var str = canon(red)
            while (var m = (str =~ /(.)(?(?=\1)|(.?))/g)) {
                take("#{m[0]}#{m[1] == '' ? 'X' : m[1]}")
            }
        }.map { ENC{_} }.join(' ')
    }

    func dec(black) {
        canon(black).split(2).map { DEC{_} }.join(' ')
    }

    return(enc, dec)
}

var (encode, decode) = playfair('Playfair example')

var orig = "Hide the gold in...the TREESTUMP!!!"
say " orig:\t#{orig}"

var black = encode(orig)
say "black:\t#{black}"

var red = decode(black)
say "  red:\t#{red}"
Output:
 orig:	Hide the gold in...the TREESTUMP!!!
black:	BM OD ZB XD NA BE KU DM UI XM MO UV IF
  red:	HI DE TH EG OL DI NT HE TR EX ES TU MP

SQL

--Clean up previous run  
IF EXISTS (SELECT * 
           FROM   SYS.TYPES 
           WHERE  NAME = 'FairPlayTable') 
  DROP TYPE FAIRPLAYTABLE 

--Set Types  
CREATE TYPE FAIRPLAYTABLE AS TABLE (LETTER VARCHAR(1), COLID INT, ROWID INT) 

GO 

--Configuration Variables  
DECLARE @KEYWORD VARCHAR(25) = 'CHARLES' --Keyword for encryption  
DECLARE @INPUT VARCHAR(MAX) = 'Testing Seeconqz' --Word to be encrypted  
DECLARE @Q INT = 0 -- Q removed?  
DECLARE @ENCRYPT INT = 1 --Encrypt?  
--Setup Variables  
DECLARE @WORDS TABLE 
  ( 
     WORD_PRE  VARCHAR(2), 
     WORD_POST VARCHAR(2) 
  ) 
DECLARE @T_TABLE FAIRPLAYTABLE 
DECLARE @NEXTLETTER CHAR(1) 
DECLARE @WORD VARCHAR(2), 
        @COL1 INT, 
        @COL2 INT, 
        @ROW1 INT, 
        @ROW2 INT, 
        @TMP  INT 
DECLARE @SQL     NVARCHAR(MAX) = '', 
        @COUNTER INT = 1, 
        @I       INT = 1 
DECLARE @COUNTER_2 INT = 1 

SET @INPUT = REPLACE(@INPUT, ' ', '') 
SET @KEYWORD = UPPER(@KEYWORD) 

DECLARE @USEDLETTERS VARCHAR(MAX) = '' 
DECLARE @TESTWORDS VARCHAR(2), 
        @A         INT = 0 

WHILE @COUNTER_2 <= 5 
  BEGIN 
      WHILE @COUNTER <= 5 
        BEGIN 
            IF LEN(@KEYWORD) > 0 
              BEGIN 
                  SET @NEXTLETTER = LEFT(@KEYWORD, 1) 
                  SET @KEYWORD = RIGHT(@KEYWORD, LEN(@KEYWORD) - 1) 

                  IF CHARINDEX(@NEXTLETTER, @USEDLETTERS) = 0 
                    BEGIN 
                        INSERT INTO @T_TABLE 
                        SELECT @NEXTLETTER, 
                               @COUNTER, 
                               @COUNTER_2 

                        SET @COUNTER = @COUNTER + 1 
                        SET @USEDLETTERS = @USEDLETTERS + @NEXTLETTER 
                    END 
              END 
            ELSE 
              BEGIN 
                  WHILE 1 = 1 
                    BEGIN 
                        IF CHARINDEX(CHAR(64 + @I), @USEDLETTERS) = 0 
                           AND NOT ( CHAR(64 + @I) = 'Q' 
                                     AND @Q = 1 ) 
                           AND NOT ( @Q = 0 
                                     AND CHAR(64 + @I) = 'J' ) 
                          BEGIN 
                              SET @NEXTLETTER = CHAR(64 + @I) 
                              SET @USEDLETTERS = @USEDLETTERS + CHAR(64 + @I) 
                              SET @I = @I + 1 

                              BREAK 
                          END 

                        SET @I = @I + 1 
                    END 

                  -- SELECT 1 AS [T] 
                  --BREAK 
                  INSERT INTO @T_TABLE 
                  SELECT @NEXTLETTER, 
                         @COUNTER, 
                         @COUNTER_2 

                  SET @COUNTER = @COUNTER + 1 
              END 
        END 

      SET @COUNTER_2 = @COUNTER_2 + 1 
      SET @COUNTER = 1 
  END 

--Split word into Digraphs  
WHILE @A < 1 
  BEGIN 
      SET @TESTWORDS = UPPER(LEFT(@INPUT, 2)) 

      IF LEN(@TESTWORDS) = 1 
        BEGIN 
            SET @TESTWORDS = @TESTWORDS + 'X' 
            SET @A = 1 
        END 
      ELSE IF RIGHT(@TESTWORDS, 1) = LEFT(@TESTWORDS, 1) 
        BEGIN 
            SET @TESTWORDS = RIGHT(@TESTWORDS, 1) + 'X' 
            SET @INPUT = RIGHT(@INPUT, LEN(@INPUT) - 1) 
        END 
      ELSE 
        SET @INPUT = RIGHT(@INPUT, LEN(@INPUT) - 2) 

      IF LEN(@INPUT) = 0 
        SET @A = 1 

      INSERT @WORDS 
      SELECT @TESTWORDS, 
             '' 
  END 

--Start Encryption 
IF @ENCRYPT = 1 
  BEGIN 
      --Loop through Digraphs amd encrypt  
      DECLARE WORDS_LOOP CURSOR LOCAL FORWARD_ONLY FOR 
        SELECT WORD_PRE 
        FROM   @WORDS 
        FOR UPDATE OF WORD_POST 

      OPEN WORDS_LOOP 

      FETCH NEXT FROM WORDS_LOOP INTO @WORD 

      WHILE @@FETCH_STATUS = 0 
        BEGIN 
            --Find letter positions  
            SET @ROW1 = (SELECT ROWID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = LEFT(@WORD, 1)) 
            SET @ROW2 = (SELECT ROWID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = RIGHT(@WORD, 1)) 
            SET @COL1 = (SELECT COLID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = LEFT(@WORD, 1)) 
            SET @COL2 = (SELECT COLID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = RIGHT(@WORD, 1)) 

            --Move positions according to encryption rules  
            IF @COL1 = @COL2 
              BEGIN 
                  SET @ROW1 = @ROW1 + 1 
                  SET @ROW2 = @ROW2 + 1 
              --select 'row'  
              END 
            ELSE IF @ROW1 = @ROW2 
              BEGIN 
                  SET @COL1 = @COL1 + 1 
                  SET @COL2 = @COL2 + 1 
              --select 'col'  
              END 
            ELSE 
              BEGIN 
                  SET @TMP = @COL2 
                  SET @COL2 = @COL1 
                  SET @COL1 = @TMP 
              --select 'reg'  
              END 

            IF @ROW1 = 6 
              SET @ROW1 = 1 

            IF @ROW2 = 6 
              SET @ROW2 = 1 

            IF @COL1 = 6 
              SET @COL1 = 1 

            IF @COL2 = 6 
              SET @COL2 = 1 

            --Find encrypted letters by positions  
            UPDATE @WORDS 
            SET    WORD_POST = (SELECT (SELECT LETTER 
                                        FROM   @T_TABLE 
                                        WHERE  ROWID = @ROW1 
                                               AND COLID = @COL1) 
                                       + (SELECT LETTER 
                                          FROM   @T_TABLE 
                                          WHERE  COLID = @COL2 
                                                 AND ROWID = @ROW2)) 
            WHERE  WORD_PRE = @WORD 

            FETCH NEXT FROM WORDS_LOOP INTO @WORD 
        END 

      CLOSE WORDS_LOOP 

      DEALLOCATE WORDS_LOOP 
  END 
--Start Decryption 
ELSE 
  BEGIN 
      --Loop through Digraphs amd decrypt  
      DECLARE WORDS_LOOP CURSOR LOCAL FORWARD_ONLY FOR 
        SELECT WORD_PRE 
        FROM   @WORDS 
        FOR UPDATE OF WORD_POST 

      OPEN WORDS_LOOP 

      FETCH NEXT FROM WORDS_LOOP INTO @WORD 

      WHILE @@FETCH_STATUS = 0 
        BEGIN 
            --Find letter positions  
            SET @ROW1 = (SELECT ROWID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = LEFT(@WORD, 1)) 
            SET @ROW2 = (SELECT ROWID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = RIGHT(@WORD, 1)) 
            SET @COL1 = (SELECT COLID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = LEFT(@WORD, 1)) 
            SET @COL2 = (SELECT COLID 
                         FROM   @T_TABLE 
                         WHERE  LETTER = RIGHT(@WORD, 1)) 

            --Move positions according to encryption rules  
            IF @COL1 = @COL2 
              BEGIN 
                  SET @ROW1 = @ROW1 - 1 
                  SET @ROW2 = @ROW2 - 1 
              --select 'row'  
              END 
            ELSE IF @ROW1 = @ROW2 
              BEGIN 
                  SET @COL1 = @COL1 - 1 
                  SET @COL2 = @COL2 - 1 
              --select 'col'  
              END 
            ELSE 
              BEGIN 
                  SET @TMP = @COL2 
                  SET @COL2 = @COL1 
                  SET @COL1 = @TMP 
              --select 'reg'  
              END 

            IF @ROW1 = 0 
              SET @ROW1 = 5 

            IF @ROW2 = 0 
              SET @ROW2 = 5 

            IF @COL1 = 0 
              SET @COL1 = 5 

            IF @COL2 = 0 
              SET @COL2 = 5 

            --Find decrypted letters by positions  
            UPDATE @WORDS 
            SET    WORD_POST = (SELECT (SELECT LETTER 
                                        FROM   @T_TABLE 
                                        WHERE  ROWID = @ROW1 
                                               AND COLID = @COL1) 
                                       + (SELECT LETTER 
                                          FROM   @T_TABLE 
                                          WHERE  COLID = @COL2 
                                                 AND ROWID = @ROW2)) 
            WHERE  WORD_PRE = @WORD 

            FETCH NEXT FROM WORDS_LOOP INTO @WORD 
        END 

      CLOSE WORDS_LOOP 

      DEALLOCATE WORDS_LOOP 
  END 

--Output 
DECLARE WORDS CURSOR LOCAL FAST_FORWARD FOR 
  SELECT WORD_POST 
  FROM   @WORDS 

OPEN WORDS 

FETCH NEXT FROM WORDS INTO @WORD 

WHILE @@FETCH_STATUS = 0 
  BEGIN 
      SET @SQL = @SQL + @WORD + ' '

      FETCH NEXT FROM WORDS INTO @WORD 
  END 

CLOSE WORDS 

DEALLOCATE WORDS 

SELECT @SQL 

--Cleanup  
IF EXISTS (SELECT * 
           FROM   SYS.TYPES 
           WHERE  NAME = 'FairPlayTable') 
  DROP TYPE FAIRPLAYTABLE

Tcl

Works with: Tcl version 8.6
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
    }
}

Demonstrating:

Playfair create cypher "Playfair Example"
set plaintext "Hide the gold in...the TREESTUMP!!!"
set encoded [cypher encode $plaintext]
set decoded [cypher decode $encoded]
puts "Original: $plaintext"
puts "Encoded:  $encoded"
puts "Decoded:  $decoded"
Output:
Original: Hide the gold in...the TREESTUMP!!!
Encoded:  BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded:  HI DE TH EG OL DI NT HE TR EX ES TU MP

VBA

Option Explicit

Private Type Adress
   Row As Integer
   Column As Integer
End Type

Private myTable() As String

Sub Main()
Dim keyw As String, boolQ As Boolean, text As String, test As Long
Dim res As String
   keyw = InputBox("Enter your keyword : ", "KeyWord", "Playfair example")
   If keyw = "" Then GoTo ErrorHand
   Debug.Print "Enter your keyword : " & keyw
   boolQ = MsgBox("Ignore Q when buiding table  y/n : ", vbYesNo) = vbYes
   Debug.Print "Ignore Q when buiding table  y/n : " & IIf(boolQ, "Y", "N")
   Debug.Print ""
   Debug.Print "Table : "
   myTable = CreateTable(keyw, boolQ)
   On Error GoTo ErrorHand
   test = UBound(myTable)
   On Error GoTo 0
   text = InputBox("Enter your text", "Encode", "hide the gold in the TRRE stump")
   If text = "" Then GoTo ErrorHand
   Debug.Print ""
   Debug.Print "Text to encode : " & text
   Debug.Print "-------------------------------------------------"
   res = Encode(text)
   Debug.Print "Encoded text is : " & res
   res = Decode(res)
   Debug.Print "Decoded text is : " & res
   text = InputBox("Enter your text", "Encode", "hide the gold in the TREE stump")
   If text = "" Then GoTo ErrorHand
   Debug.Print ""
   Debug.Print "Text to encode : " & text
   Debug.Print "-------------------------------------------------"
   res = Encode(text)
   Debug.Print "Encoded text is : " & res
   res = Decode(res)
   Debug.Print "Decoded text is : " & res
   Exit Sub
ErrorHand:
   Debug.Print "error"
End Sub

Private Function CreateTable(strKeyword As String, Q As Boolean) As String()
Dim r As Integer, c As Integer, temp(1 To 5, 1 To 5) As String, t, cpt As Integer
Dim strT As String, coll As New Collection
Dim s As String

   strKeyword = UCase(Replace(strKeyword, " ", ""))
   If Q Then
      If InStr(strKeyword, "J") > 0 Then
         Debug.Print "Your keyword isn't available with your choice : Not Q (==> J) !"
         Exit Function
      End If
   Else
      If InStr(strKeyword, "Q") > 0 Then
         Debug.Print "Your keyword isn't available with your choice : Q (==> Not J) !"
         Exit Function
      End If
   End If
   strT = IIf(Not Q, "ABCDEFGHIKLMNOPQRSTUVWXYZ", "ABCDEFGHIJKLMNOPRSTUVWXYZ")
   t = Split(StrConv(strKeyword, vbUnicode), Chr(0))
   For c = LBound(t) To UBound(t) - 1
      strT = Replace(strT, t(c), "")
      On Error Resume Next
      coll.Add t(c), t(c)
      On Error GoTo 0
   Next
   strKeyword = vbNullString
   For c = 1 To coll.Count
      strKeyword = strKeyword & coll(c)
   Next
   t = Split(StrConv(strKeyword & strT, vbUnicode), Chr(0))
   c = 1: r = 1
   For cpt = LBound(t) To UBound(t) - 1
      temp(r, c) = t(cpt)
      s = s & " " & t(cpt)
      c = c + 1
      If c = 6 Then c = 1: r = r + 1: Debug.Print "   " & s: s = ""
   Next
   CreateTable = temp
End Function

Private Function Encode(s As String) As String
Dim i&, t() As String, cpt&
   s = UCase(Replace(s, " ", ""))
   'insert "X"
   For i = 1 To Len(s) - 1
      If Mid(s, i, 1) = Mid(s, i + 1, 1) Then s = Left(s, i) & "X" & Right(s, Len(s) - i)
   Next
   'Do the pairs
   For i = 1 To Len(s) Step 2
      ReDim Preserve t(cpt)
      t(cpt) = Mid(s, i, 2)
      cpt = cpt + 1
   Next i
   If Len(t(UBound(t))) = 1 Then t(UBound(t)) = t(UBound(t)) & "X"
   Debug.Print "[the pairs : " & Join(t, " ") & "]"
   'swap the pairs
   For i = LBound(t) To UBound(t)
      t(i) = SwapPairsEncoding(t(i))
   Next
   Encode = Join(t, " ")
End Function

Private Function SwapPairsEncoding(s As String) As String
Dim r As Integer, c As Integer, d1 As String, d2 As String, Flag As Boolean
Dim addD1 As Adress, addD2 As Adress, resD1 As Adress, resD2 As Adress
   d1 = Left(s, 1): d2 = Right(s, 1)
   For r = 1 To 5
      For c = 1 To 5
         If d1 = myTable(r, c) Then addD1.Row = r: addD1.Column = c
         If d2 = myTable(r, c) Then addD2.Row = r: addD2.Column = c
         If addD1.Row <> 0 And addD2.Row <> 0 Then Flag = True: Exit For
      Next
      If Flag Then Exit For
   Next
   Select Case True
      Case addD1.Row = addD2.Row And addD1.Column <> addD2.Column
         'same row, different columns
         resD1.Column = IIf(addD1.Column + 1 = 6, 1, addD1.Column + 1)
         resD2.Column = IIf(addD2.Column + 1 = 6, 1, addD2.Column + 1)
         SwapPairsEncoding = myTable(addD1.Row, resD1.Column) & myTable(addD2.Row, resD2.Column)
      Case addD1.Row <> addD2.Row And addD1.Column = addD2.Column
         'same columns, different rows
         resD1.Row = IIf(addD1.Row + 1 = 6, 1, addD1.Row + 1)
         resD2.Row = IIf(addD2.Row + 1 = 6, 1, addD2.Row + 1)
         SwapPairsEncoding = myTable(resD1.Row, addD1.Column) & myTable(resD2.Row, addD2.Column)
      Case addD1.Row <> addD2.Row And addD1.Column <> addD2.Column
         'different rows, different columns
         resD1.Row = addD1.Row
         resD2.Row = addD2.Row
         resD1.Column = addD2.Column
         resD2.Column = addD1.Column
         SwapPairsEncoding = myTable(resD1.Row, resD1.Column) & myTable(resD2.Row, resD2.Column)
   End Select
End Function

Private Function Decode(s As String) As String
Dim t, i&, j&, e&
   t = Split(s, " ")
   e = UBound(t) - 1
   'swap the pairs
   For i = LBound(t) To UBound(t)
      t(i) = SwapPairsDecoding(CStr(t(i)))
   Next
   'remove "X"
   For i = LBound(t) To e
      If Right(t(i), 1) = "X" And Left(t(i), 1) = Left(t(i + 1), 1) Then
         t(i) = Left(t(i), 1) & Left(t(i + 1), 1)
         For j = i + 1 To UBound(t) - 1
            t(j) = Right(t(j), 1) & Left(t(j + 1), 1)
         Next j
         If Right(t(j), 1) = "X" Then
            ReDim Preserve t(j - 1)
         Else
            t(j) = Right(t(j), 1) & "X"
         End If
      ElseIf Left(t(i + 1), 1) = "X" And Right(t(i), 1) = Right(t(i + 1), 1) Then
         For j = i + 1 To UBound(t) - 1
            t(j) = Right(t(j), 1) & Left(t(j + 1), 1)
         Next j
         If Right(t(j), 1) = "X" Then
            ReDim Preserve t(j - 1)
         Else
            t(j) = Right(t(j), 1) & "X"
         End If
      End If
   Next
   Decode = Join(t, " ")
End Function

Private Function SwapPairsDecoding(s As String) As String
Dim r As Integer, c As Integer, d1 As String, d2 As String, Flag As Boolean
Dim addD1 As Adress, addD2 As Adress, resD1 As Adress, resD2 As Adress
   d1 = Left(s, 1): d2 = Right(s, 1)
   For r = 1 To 5
      For c = 1 To 5
         If d1 = myTable(r, c) Then addD1.Row = r: addD1.Column = c
         If d2 = myTable(r, c) Then addD2.Row = r: addD2.Column = c
         If addD1.Row <> 0 And addD2.Row <> 0 Then Flag = True: Exit For
      Next
      If Flag Then Exit For
   Next
   Select Case True
      Case addD1.Row = addD2.Row And addD1.Column <> addD2.Column
         'same row, different columns
         resD1.Column = IIf(addD1.Column - 1 = 0, 5, addD1.Column - 1)
         resD2.Column = IIf(addD2.Column - 1 = 0, 5, addD2.Column - 1)
         SwapPairsDecoding = myTable(addD1.Row, resD1.Column) & myTable(addD2.Row, resD2.Column)
      Case addD1.Row <> addD2.Row And addD1.Column = addD2.Column
         'same columns, different rows
         resD1.Row = IIf(addD1.Row - 1 = 0, 5, addD1.Row - 1)
         resD2.Row = IIf(addD2.Row - 1 = 0, 5, addD2.Row - 1)
         SwapPairsDecoding = myTable(resD1.Row, addD1.Column) & myTable(resD2.Row, addD2.Column)
      Case addD1.Row <> addD2.Row And addD1.Column <> addD2.Column
         'different rows, different columns
         resD1.Row = addD1.Row
         resD2.Row = addD2.Row
         resD1.Column = addD2.Column
         resD2.Column = addD1.Column
         SwapPairsDecoding = myTable(resD1.Row, resD1.Column) & myTable(resD2.Row, resD2.Column)
   End Select
End Function
Output:
Enter your keyword : Playfair example
Ignore Q when buiding table  y/n : N

Table : 
    P L A Y F
    I R E X M
    B C D G H
    K N O Q S
    T U V W Z

Text to encode : hide the gold in the TRRE stump
-------------------------------------------------
[the pairs : HI DE TH EG OL DI NT HE TR XR ES TU MP]
Encoded text is : BM OD ZB XD NA BE KU DM UI ME MO UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR RE ST UM PX

Text to encode : hide the gold in the TREE stump
-------------------------------------------------
[the pairs : HI DE TH EG OL DI NT HE TR EX ES TU MP]
Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF
Decoded text is : HI DE TH EG OL DI NT HE TR EE ST UM PX

V (Vlang)

Translation of: Go
import os
import strings
type PlayfairOption = int
 
const (
    no_q = 0
    i_equals_j = 1
)
 
struct Playfair {
mut:
    keyword string
    pfo     PlayfairOption
    table   [5][5]u8
}
 
fn (mut p Playfair) init() {
    // Build table.
    mut used := [26]bool{} // all elements false
    if p.pfo == no_q {
        used[16] = true // Q used
    } else {
        used[9] = true // J used
    }
    alphabet := p.keyword.to_upper() + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    for i, j, k := 0, 0, 0; k < alphabet.len; k++ {
        c := alphabet[k]
        if c < 'A'[0] || c > 'Z'[0] {
            continue
        }
        d := int(c - 65)
        if !used[d] {
            p.table[i][j] = c
            used[d] = true
            j++
            if j == 5 {
                i++
                if i == 5 {
                    break // table has been filled
                }
                j = 0
            }
        }
    }
}
 
fn (p Playfair) get_clean_text(pt string) string {
    // Ensure everything is upper case.
    plain_text := pt.to_upper()
    // Get rid of any non-letters and insert X between duplicate letters.
    mut clean_text := strings.new_builder(128)
    // Safe to assume null u8 won't be present in plain_text.
    mut prev_byte := `\000`
    for i in 0..plain_text.len {
        mut next_byte := plain_text[i]
        // It appears that Q should be omitted altogether if NO_Q option is specified;
        // we assume so anyway.
        if next_byte < 'A'[0] || next_byte > 'Z'[0] || (next_byte == 'Q'[0] && p.pfo == no_q) {
            continue
        }
        // If i_equals_j option specified, replace J with I.
        if next_byte == 'J'[0] && p.pfo == i_equals_j {
            next_byte = 'I'[0]
        }
        if next_byte != prev_byte {
            clean_text.write_u8(next_byte)
        } else {
            clean_text.write_u8('X'[0])
            clean_text.write_u8(next_byte)
        }
        prev_byte = next_byte
    }
    l := clean_text.len
    if l%2 == 1 {
        // Dangling letter at end so add another letter to complete digram.
        if clean_text.str()[l-1] != 'X'[0] {
            clean_text.write_u8('X'[0])
        } else {
            clean_text.write_u8('Z'[0])
        }
    }
    return clean_text.str()
}
 
fn (p Playfair) find_byte(c u8) (int, int) {
    for i in 0..5 {
        for j in 0..5 {
            if p.table[i][j] == c {
                return i, j
            }
        }
    }
    return -1, -1
}
 
fn (p Playfair) encode(plain_text string) string {
    clean_text := p.get_clean_text(plain_text)
    mut cipher_text := strings.new_builder(128)
    l := clean_text.len
    for i := 0; i < l; i += 2 {
        row1, col1 := p.find_byte(clean_text[i])
        row2, col2 := p.find_byte(clean_text[i+1])
        if row1 == row2{
            cipher_text.write_u8(p.table[row1][(col1+1)%5])
            cipher_text.write_u8(p.table[row2][(col2+1)%5])
        } else if col1 == col2{
            cipher_text.write_u8(p.table[(row1+1)%5][col1])
            cipher_text.write_u8(p.table[(row2+1)%5][col2])
        } else {
            cipher_text.write_u8(p.table[row1][col2])
            cipher_text.write_u8(p.table[row2][col1])
        }
        if i < l-1 {
            cipher_text.write_u8(' '[0])
        }
    }
    return cipher_text.str()
}
 
fn (p Playfair) decode(cipher_text string) string {
    mut decoded_text := strings.new_builder(128)
    l := cipher_text.len
    // cipher_text will include spaces so we need to skip them.
    for i := 0; i < l; i += 3 {
        row1, col1 := p.find_byte(cipher_text[i])
        row2, col2 := p.find_byte(cipher_text[i+1])
        if row1 == row2 {
            mut temp := 4
            if col1 > 0 {
                temp = col1 - 1
            }
            decoded_text.write_u8(p.table[row1][temp])
            temp = 4
            if col2 > 0 {
                temp = col2 - 1
            }
            decoded_text.write_u8(p.table[row2][temp])
        } else if col1 == col2 {
            mut temp := 4
            if row1 > 0 {
                temp = row1 - 1
            }
            decoded_text.write_u8(p.table[temp][col1])
            temp = 4
            if row2 > 0 {
                temp = row2 - 1
            }
            decoded_text.write_u8(p.table[temp][col2])
        } else {
            decoded_text.write_u8(p.table[row1][col2])
            decoded_text.write_u8(p.table[row2][col1])
        }
        if i < l-1 {
            decoded_text.write_u8(' '[0])
        }
    }
    return decoded_text.str()
}
 
fn (p Playfair) print_table() {
    println("The table to be used is :\n")
    for i in 0..5 {
        for j in 0..5 {
            print("${p.table[i][j].ascii_str()} ")
        }
        println('')
    }
}
 
fn main() {
    keyword := os.input("Enter Playfair keyword : ")
    mut ignore_q := ''
    for ignore_q != "y" && ignore_q != "n" {
        ignore_q = os.input("Ignore Q when building table  y/n : ").to_lower()
    }
    mut pfo := no_q
    if ignore_q == "n" {
        pfo = i_equals_j
    }
    mut table := [5][5]u8{}
    mut pf := Playfair{keyword, pfo, table}
    pf.init()
    pf.print_table()
    plain_text := os.input("\nEnter plain text : ")
    encoded_text := pf.encode(plain_text)
    println("\nEncoded text is : $encoded_text")
    decoded_text := pf.decode(encoded_text)
    println("Deccoded text is : $decoded_text")
}
Output:

Sample run:

Enter Playfair keyword : Playfair example
Ignore Q when building table  y/n : n
The table to be used is :

P L A Y F 
I R E X M 
B C D G H 
K N O Q S 
T U V W Z 

Enter plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF 
Deccoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP 

Wren

Translation of: Kotlin
Library: Wren-dynamic
Library: Wren-str
Library: Wren-iterate
Library: Wren-ioutil
import "./dynamic" for Enum
import "./str" for Str, Char
import "./iterate" for Stepped
import "./ioutil" for Input

var PlayfairOption = Enum.create("PlayfairOption", ["NO_Q", "I_EQUALS_J"])

class Playfair {
    construct new(keyword, pfo) {
        _pfo = pfo
        // build_table
        _table = List.filled(5, null)
        for (i in 0..4) _table[i] = List.filled(5, "\0") // 5 * 5 char list
        var used = List.filled(26, false)
        if (_pfo == PlayfairOption.NO_Q) {
            used[16] = true // Q used
        } else {
            used[9]  = true // J used
        }
        var alphabet = Str.upper(keyword) + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        var i = 0
        var j = 0
        for (k in 0...alphabet.count) {
            var c = alphabet[k]
            if (Char.isAsciiUpper(c)) {
                var d = c.bytes[0] - 65
                if (!used[d]) {
                    _table[i][j] = c
                    used[d] = true
                    j = j + 1
                    if (j == 5) {
                        i = i + 1
                        if (i == 5) break // table has been filled
                        j = 0
                    }
                }
            }
        }
    }

    getCleanText_(plainText) {
        var plainText2 = Str.upper(plainText)  // ensure everything is upper case
        // get rid of any non-letters and insert X between duplicate letters
        var cleanText = ""
        var prevChar = "\0"  // safe to assume null character won't be present in plainText
        for (i in 0...plainText2.count) {
            var nextChar = plainText2[i]
            // It appears that Q should be omitted altogether if NO_Q option is specified - we assume so anyway
            if (Char.isAsciiUpper(nextChar) && (nextChar != "Q" || _pfo != PlayfairOption.NO_Q)) {
                // If I_EQUALS_J option specified, replace J with I
                if (nextChar == "J" && _pfo == PlayfairOption.I_EQUALS_J) nextChar = "I"
                if (nextChar != prevChar) {
                    cleanText = cleanText + nextChar
                } else {
                    cleanText = cleanText + "X" + nextChar
                }
                prevChar = nextChar
            }
        }
        var len = cleanText.count
        if (len % 2 == 1)  {  // dangling letter at end so add another letter to complete digram
            if (cleanText[-1] != "X") {
                cleanText = cleanText + "X"
            } else {
                cleanText = cleanText + "Z"
            }
        }
        return cleanText
    }

    findChar_(c) {
       for (i in 0..4) {
           for (j in 0..4) if (_table[i][j] == c) return [i, j]
       }
       return [-1, -1]
    }

    encode(plainText) {
        var cleanText = getCleanText_(plainText)
        var cipherText = ""
        var length = cleanText.count
        for (i in Stepped.new(0...length, 2)) {
            var pair = findChar_(cleanText[i])
            var row1 = pair[0]
            var col1 = pair[1]
            pair = findChar_(cleanText[i + 1])
            var row2 = pair[0]
            var col2 = pair[1]
            cipherText = cipherText +
                ((row1 == row2) ? _table[row1][(col1 + 1) % 5] +_table[row2][(col2 + 1) % 5] :
                 (col1 == col2) ? _table[(row1 + 1) % 5][col1] +_table[(row2 + 1) % 5][col2] :
                                  _table[row1][col2] +_table[row2][col1])
            if (i < length - 1) cipherText = cipherText + " "
        }
        return cipherText
    }

    decode(cipherText) {
        var decodedText = ""
        var length = cipherText.count
        for (i in Stepped.new(0...length, 3)) {  // cipherText will include spaces so we need to skip them
            var pair = findChar_(cipherText[i])
            var row1 = pair[0]
            var col1 = pair[1]
            pair = findChar_(cipherText[i + 1])
            var row2 = pair[0]
            var col2 = pair[1]
            decodedText = decodedText +
                ((row1 == row2) ? _table[row1][(col1 > 0) ? col1-1 : 4] +_table[row2][(col2 > 0) ?  col2-1 : 4] :
                 (col1 == col2) ? _table[(row1 > 0) ? row1-1 : 4][col1] +_table[(row2 > 0) ? row2-1 : 4][col2] :
                                  _table[row1][col2] +_table[row2][col1])
            if (i < length - 1) decodedText = decodedText + " "
        }
        return decodedText
    }

    printTable() {
        System.print("The_table to be used is :\n")
        for (i in 0..4) {
            for (j in 0..4) System.write(_table[i][j] + " ")
            System.print()
        }
    }
}

var keyword = Input.text("Enter Playfair keyword : ", 1)
var ignoreQ = Str.lower(Input.option("Ignore Q when building_table y/n : ", "yYnN"))
var pfo = (ignoreQ == "y") ? PlayfairOption.NO_Q : PlayfairOption.I_EQUALS_J
var playfair = Playfair.new(keyword, pfo)
playfair.printTable()
var plainText = Input.text("\nEnter plain text : ")
var encodedText = playfair.encode(plainText)
System.print("\nEncoded text is : %(encodedText)")
var decodedText = playfair.decode(encodedText)
System.print("Decoded text is : %(decodedText)")
Output:
Enter Playfair keyword : Playfair example
Ignore Q when building_table y/n : n
The_table to be used is :

P L A Y F 
I R E X M 
B C D G H 
K N O Q S 
T U V W Z 

Enter plain text : Hide the gold...in the TREESTUMP!!!!

Encoded text is : BM OD ZB XD NA BE KU DM UI XM MO UV IF 
Decoded text is : HI DE TH EG OL DI NT HE TR EX ES TU MP 

zkl

fcn genKeyTable(key,deadChr){ // deadChr=="Q" or "J"
   deadChr=deadChr.toUpper();
   key=key.toUpper().unique() - " " - deadChr;
   return(key + (["A".."Z"].pump(String) - deadChr - key), deadChr);
}
fcn playfair(text,keytable){  // text is a-z only
   keyTable,deadChr:=keytable;
   text=text.toUpper();
   text-=text - keyTable;	// remove unencodable characters
   if(deadChr=="Q") text-=deadChr; else text=text.replace("J","I");

   text=text.pump(String,T(Void.Read,1,False),
			 fcn(a,b=""){ if(a==b) a+"X"+b else a+b });
   if(text.len().isOdd) text+="Z";

   row:='wrap(c){ keyTable.index(c)/5 };
   col:='wrap(c){ keyTable.index(c)%5 };
   ltrRight:='wrap(c){ keyTable[(keyTable.index(c) + 1)%25] };
   ltrBelow:='wrap(c){ keyTable[(keyTable.index(c) + 5)%25] };
   ltrAt:='wrap(r,c) { keyTable[r*5 + c] };

   text.pump(String, Void.Read,  //-->digraph
      'wrap(a,b){
	 if((ra:=row(a))==(rb:=row(b))) return(ltrRight(a) + ltrRight(b));
	 if((ca:=col(a))==(cb:=col(b))) return(ltrBelow(a) + ltrBelow(b));
	 return(ltrAt(ra,cb) + ltrAt(rb,ca));
      })
   .pump(String,Void.Read,"".create.fp(" ")).strip(); // insert blanks
}
fcn decodePF(text,keyTable){
   keyTable,_=keyTable;
   text-=" ";
   row:='wrap(c){ keyTable.index(c)/5 };
   col:='wrap(c){ keyTable.index(c)%5 };
   ltrLeft:='wrap(c){ keyTable[(keyTable.index(c) - 1)%25] };
   ltrUp:='wrap(c){ n:=keyTable.index(c) - 5; if(n<0)n+=25; keyTable[n%25] };
   ltrAt:='wrap(r,c){ keyTable[r*5 + c] };
   text.pump(String,Void.Read,  //-->digraph
      'wrap(a,b){
	 if((ra:=row(a))==(rb:=row(b))) return(ltrLeft(a) + ltrLeft(b));
	 if((ca:=col(a))==(cb:=col(b))) return(ltrUp(a)   + ltrUp(b));
	 return(ltrAt(ra,cb) + ltrAt(rb,ca));
      })
   .pump(String,Void.Read,"".create.fp(" ")).strip(); // insert blanks
}
msg:="Hide the gold in the tree stump!!!";
keyTable:=genKeyTable("playfair example");
msg.println();
e:=playfair(msg,keyTable); e.println();
decodePF(e,keyTable).println();
playfair("XX",keyTable).println() : decodePF(_,keyTable).println();
Output:
Hide the gold in the tree stump!!!
BM OD ZB XD NA BE KU DM UI XM MO UV IF
HI DE TH EG OL DI NT HE TR EX ES TU MP
MM MW
XX XZ