I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Playfair cipher

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

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
```

## 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   iEqualsJEnd Enum Dim Shared pfo As PlayFairOption  '' set this before calling buildTableDim 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 kEnd 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 cleanTextEnd 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 iEnd 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 cipherTextEnd 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 decodedTextEnd Function Dim As String keyword, ignoreQ, plainText, encodedText, decodedTextLine 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 : " : PrintFor i As UInteger = 1 To 5  For j As UInteger = 1 To 5    Print Chr(table(i, j) + 64); " ";  Next j  PrintNext i Print Line Input "Enter plain text : "; plainTextPrintencodedText = encodePlayfair(plainText)Print "Encoded text is : "; encodedTextdecodedText = decodePlayFair(encodedText)Print "Decoded text is : "; decodedTextPrintPrint "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
```

 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 earray2D maxCoord = listArray ((1, 1), maxCoord) -- | Generates a playfair table starting with the specified string.---- >>> makeTable "hello"-- "HELOABCDFGIKMNPQRSTUVWXYZ"makeTable :: String -> StringmakeTable 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 amakeSquare = array2D (5, 5) -- | Displays a playfair square, formatted as a square.showSquare :: Square Char -> StringshowSquare d = unlines \$ chunksOf 5 (elems d) -- | Given a value and an association list of x-coordinate * y-coordinate * value, returns the coordinatesgetIndex' :: (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 characterspairHelper 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)] -> StringunparsePair = unwords . fmap (\(x, y) -> [x, y]) codeHelper :: (Square Char -> (Char, Char) -> Maybe (Char, Char))    -> String -> String -> Maybe StringcodeHelper subs key =    fmap unparsePair .    mapM (subs (makeSquare \$ makeTable key)) .    parsePair playfair, unplayfair :: String -> String -> Maybe Stringplayfair key = codeHelper encodePair key . formatEncodeunplayfair = codeHelper decodePair formatEncode :: String -> StringformatEncode =    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' [email protected]:=&'J'@]} ])`(-.&'Q')@[email protected]  '') 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  ;:inv ;/ 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'HI DE TH EG OL DI NT HE TR EX ES TU MP`

## 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], " ")    endend 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 translationssub 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.

`sequence table,         findchar type pfoption(integer option)    return find(option,"QJ")!=0end type pfoption pfo -- 'Q' or 'J' procedure build_table(string keyword, integer option)-- option should be 'Q' to ignore Q, or 'J' to replace Js with I    pfo = option    table = repeat(repeat(' ',5),5)    findchar = repeat(0,26)    findchar[pfo-'A'+1] = {0,0}    -- (note that any pfo (J/Q) in keyword are simply ignored)    string alphabet = upper(keyword) & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"    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 forend procedure 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 cleantextend 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 textend function function playfair(string text, integer step, sequence d)    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            res &= table[row1][d[col1]] & table[row2][d[col2]]        elsif col1=col2 then            res &= table[d[row1]][col1] & table[d[row2]][col2]        else            res &= table[row1][col2] & table[row2][col1]        end if    end for    return resend function constant p1 = {2,3,4,5,1},  -- easier than playing with mod(+1,5)         m1 = {5,1,2,3,4}   --              ""          mod(-1,5) function encode(string plaintext)    return playfair(clean_text(plaintext),2,p1)end function function decode(string ciphertext)    -- ciphertext includes spaces we need to skip, hence by 3    return remove_x(playfair(ciphertext,3,m1))end function string keyword = "Playfair example"build_table(keyword,'Q')printf(1,"Playfair keyword : %s\n",{keyword})printf(1,"Option: J=I or no Q (J/Q) : %s\n",pfo)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
```

## Python

Translation of: Raku
`from string import ascii_uppercasefrom itertools import productfrom 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:", origenc = encode(orig)print "Encoded:", encprint "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)

Works with: rakudo version 2016.07
`# 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";`
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."                                endif 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'        @cantif pos(omit, newKey) \==0  then call err 'cipher key'  @cantfill= 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' @ucharsfill= 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: '  newKeysay '     omit char: '  omitsay '   double char: '  xxsay ' original text: '  strip(text)padL= 14 + 2call 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' , eTextpText= .Playfair(eText     );          call show 'plain'     , pTextqText= 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',  qTextsay ' 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.Xcolerr:   say;   say '***error!***'  arg(1);           say;    exit 13LR:    rowL= row(left(__, 1)); colL= _; rowR= row(right(__,1)); colR= _; return length(__)row:   ?= pos(arg(1), grid);      _= (?-1) // 5  +  1;      return  (4+?) % 5show:  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.
```

## 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 IntegerEnd Type Private myTable() As String Sub Main()Dim keyw As String, boolQ As Boolean, text As String, test As LongDim 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 SubErrorHand:   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 IntegerDim strT As String, coll As New CollectionDim 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 = tempEnd Function Private Function Encode(s As String) As StringDim 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 StringDim r As Integer, c As Integer, d1 As String, d2 As String, Flag As BooleanDim 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 SelectEnd Function Private Function Decode(s As String) As StringDim 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 StringDim r As Integer, c As Integer, d1 As String, d2 As String, Flag As BooleanDim 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 SelectEnd 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```

## Wren

Translation of: Kotlin
Library: Wren-dynamic
Library: Wren-str
Library: Wren-trait
Library: Wren-ioutil
`import "/dynamic" for Enumimport "/str" for Str, Charimport "/trait" for Steppedimport "/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_Jvar 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
```