Bifid cipher

From Rosetta Code
Task
Bifid cipher
You are encouraged to solve this task according to the task description, using any language you may know.
Description

The Bifid cipher is a polygraphic substitution cipher which was invented by Félix Delastelle in around 1901. It uses a 5 x 5 Polybius square combined with transposition and fractionation to encrypt a message. Any 5 x 5 Polybius square can be used but, as it only has 25 cells and there are 26 letters of the (English) alphabet, one cell needs to represent two letters - I and J being a common choice.

Operation

Suppose we want to encrypt the message "ATTACKATDAWN".

We use this archetypal Polybius square where I and J share the same position.

x/y 1 2 3 4 5
-------------
1 | A B C D E
2 | F G H I K
3 | L M N O P
4 | Q R S T U 
5 | V W X Y Z

The message is first converted to its x, y coordinates, but they are written vertically beneath.

A T T A C K A T D A W N
1 4 4 1 1 2 1 4 1 1 5 3
1 4 4 1 3 5 1 4 4 1 2 3

They are then arranged in a row.

1 4 4 1 1 2 1 4 1 1 5 3 1 4 4 1 3 5 1 4 4 1 2 3

Finally, they are divided up into pairs which are used to look up the encrypted letters in the square.

14 41 12 14 11 53 14 41 35 14 41 23
D  Q  B  D  A  X  D  Q  P  D  Q  H

The encrypted message is therefore "DQBDAXDQPDQH".

Decryption can be achieved by simply reversing these steps.

Task

Write routines in your language to encrypt and descrypt a message using the Bifid cipher.

Use them to verify (including subsequent decryption):

1. The above example.

2. The example in the Wikipedia article using the message and Polybius square therein.

3. The above example but using the Polybius square in the Wikipedia article to illustrate that it doesn't matter which square you use as long, of course, as the same one is used for both encryption and decryption.

In addition, encrypt and decrypt the message "The invasion will start on the first of January" using any Polybius square you like. Convert the message to upper case and ignore spaces.

Bonus

Suggest a way in which the cipher could be modified so that ALL 26 letters can be uniquely encrypted.

Related task

Playfair cipher

AArch64 Assembly

Works with: as version Raspberry Pi 3B version Buster 64 bits
or android 64 bits with application Termux
/* ARM assembly AARCH64 Raspberry PI 3B */
/*  program bifid64.s   */

/*******************************************/
/* Constantes                              */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"

.equ MESSSIZE,   2000
.equ BUFFERSIZE, 2000
/*******************************************/
/*   Macros                              */
/*******************************************/
//.include "../../ficmacros64.inc"            // for developer debugging


/*******************************************/
/* Initialized data */
/*******************************************/
.data
szMessDebutPgm:   .asciz "Program 64 bits start. \n"
szCarriageReturn: .asciz "\n"
szMessFinOK:      .asciz "Program normal end. \n"
szMessError:      .asciz "\nError  Buffer too small!!!\n"
szMessString:     .asciz "String :\n"
szMessEncrip:     .asciz "\nEncrypted :\n"
szMessDecrip:     .asciz "\nDecrypted :\n"
//szString1:        .asciz "ABCDEFGHIJKLMNOPQRSTUVWXYZ!.?abcdefghijklmnopqrstuvwxyz"
//szString1:         .asciz "attackatdawn"
szString1:         .asciz "ATTACKATDAWN"
//szString1:         .asciz "ABCD"
szString2:         .asciz "FLEEATONCE"
szString3:         .asciz "The invasion will start on the first of January" 

szPolybe1:        .ascii "ABCDE"
                  .ascii "FGHIK"
                  .ascii "LMNOP"
                  .ascii "QRSTU"
                  .asciz "VWXYZ"
                  
szPolybe2:        .ascii "BGWKZ"
                  .ascii "QPNDS"
                  .ascii "IOAXE"
                  .ascii "FCLUM"
                  .asciz "THYVR"

/*******************************************/
/* UnInitialized data */
/*******************************************/
.bss 
.align 4
tabiposX:                .skip 8 * MESSSIZE  
tabiposY:                .skip 8 * MESSSIZE 
sBuffex1:                .skip  BUFFERSIZE
sBuffex2:                .skip  BUFFERSIZE
/*******************************************/
/*  code section */
/*******************************************/
.text
.global main 
main: 
    ldr x0,qAdrszMessDebutPgm
    bl affichageMess
    ldr x0,qAdrszMessString       // display message
    bl affichageMess
    ldr x0,qAdrszString1          // display string
    bl affichageMess
    ldr x0,qAdrszString1          // string address
    ldr x1,qAdrszPolybe1          // Polybe key
    ldr x2,qAdrsBuffex1           // encrypted buffer result
    bl encrypt
    cmp x0,#0
    ble 99f
    ldr x0,qAdrszMessEncrip
    bl affichageMess
    ldr x0,qAdrsBuffex1           // display encrypted buffer
    bl affichageMess 
  

    ldr x0,qAdrsBuffex1           // encrypted buffer
    ldr x1,qAdrszPolybe1          // Polybe key
    ldr x2,qAdrsBuffex2           // decrypted buffer 
    bl decrypt
    ldr x0,qAdrszMessDecrip
    bl affichageMess
    ldr x0,qAdrsBuffex2           // display decrypted buffer 
    bl affichageMess 
    ldr x0,qAdrszCarriageReturn
    bl affichageMess 
    
    ldr x0,qAdrszMessString       // display message
    bl affichageMess
    ldr x0,qAdrszString2          // display string
    bl affichageMess
    ldr x0,qAdrszString2          // string address
    ldr x1,qAdrszPolybe2          // Polybe key
    ldr x2,qAdrsBuffex1           // encrypted buffer result
    bl encrypt
    cmp x0,#0
    ble 99f
    ldr x0,qAdrszMessEncrip
    bl affichageMess
    ldr x0,qAdrsBuffex1           // display encrypted buffer
    bl affichageMess 
  

    ldr x0,qAdrsBuffex1           // encrypted buffer
    ldr x1,qAdrszPolybe2          // Polybe key
    ldr x2,qAdrsBuffex2           // decrypted buffer 
    bl decrypt
    ldr x0,qAdrszMessDecrip
    bl affichageMess
    ldr x0,qAdrsBuffex2           // display decrypted buffer 
    bl affichageMess 
    ldr x0,qAdrszCarriageReturn
    bl affichageMess 
    
    ldr x0,qAdrszMessString       // display message
    bl affichageMess
    ldr x0,qAdrszString1          // display string
    bl affichageMess
    ldr x0,qAdrszString1          // string address
    ldr x1,qAdrszPolybe2          // Polybe key
    ldr x2,qAdrsBuffex1           // encrypted buffer result
    bl encrypt
    cmp x0,#0
    ble 99f
    ldr x0,qAdrszMessEncrip
    bl affichageMess
    ldr x0,qAdrsBuffex1           // display encrypted buffer
    bl affichageMess 
  

    ldr x0,qAdrsBuffex1           // encrypted buffer
    ldr x1,qAdrszPolybe2          // Polybe key
    ldr x2,qAdrsBuffex2           // decrypted buffer 
    bl decrypt
    ldr x0,qAdrszMessDecrip
    bl affichageMess
    ldr x0,qAdrsBuffex2           // display decrypted buffer 
    bl affichageMess 
    ldr x0,qAdrszCarriageReturn
    bl affichageMess 
    
    ldr x0,qAdrszMessString       // display message
    bl affichageMess
    ldr x0,qAdrszString3          // display string
    bl affichageMess
    ldr x0,qAdrszString3          // string address
    ldr x1,qAdrszPolybe1          // Polybe key
    ldr x2,qAdrsBuffex1           // encrypted buffer result
    bl encrypt
    cmp x0,#0
    ble 99f
    ldr x0,qAdrszMessEncrip
    bl affichageMess
    ldr x0,qAdrsBuffex1           // display encrypted buffer
    bl affichageMess 
  

    ldr x0,qAdrsBuffex1           // encrypted buffer
    ldr x1,qAdrszPolybe1          // Polybe key
    ldr x2,qAdrsBuffex2           // decrypted buffer 
    bl decrypt
    ldr x0,qAdrszMessDecrip
    bl affichageMess
    ldr x0,qAdrsBuffex2           // display decrypted buffer 
    bl affichageMess 
    ldr x0,qAdrszCarriageReturn
    bl affichageMess 
    
    ldr x0,qAdrszMessFinOK
    bl affichageMess
    b 100f
99:
    ldr x0,qAdrszMessError        // error
    bl affichageMess
    mov x0, #1    
100:                              // standard end of the program
    mov x0, #0                    // return code
    mov x8,EXIT 
    svc 0                         // perform system call
qAdrszMessString:         .quad szMessString
qAdrszMessDecrip:         .quad szMessDecrip
qAdrszMessEncrip:         .quad szMessEncrip
qAdrszString1:            .quad szString1
qAdrszPolybe1:            .quad szPolybe1
qAdrszString2:            .quad szString2
qAdrszString3:            .quad szString3
qAdrszPolybe2:            .quad szPolybe2
qAdrsBuffex1:             .quad sBuffex1
qAdrsBuffex2:             .quad sBuffex2
qAdrszMessDebutPgm:       .quad szMessDebutPgm
qAdrszMessFinOK:          .quad szMessFinOK
qAdrszCarriageReturn:     .quad szCarriageReturn
qAdrszMessError:          .quad szMessError
/******************************************************************/
/*     encrypt strings                         */ 
/******************************************************************/
/* r0 contains the address of the string1 */
/* r1 contains Polybe area address
/* r2 contains the address of the encrypted string */
/* r0 return buffer lenght  */
encrypt:
    stp x1,lr,[sp,-16]!       // save registers
    stp x2,x3,[sp,-16]!       //
    stp x4,x5,[sp,-16]!       //
    stp x6,x7,[sp,-16]!       //
    stp x8,x9,[sp,-16]!       //
    stp x10,x11,[sp,-16]!     //
    mov x3,#0                 // counter byte string 1
    mov x5,#0                 // counter byte buffer
1:
    ldrb w7,[x0,x3]           // load byte string 1
    cmp x7,#0                 // zero final ?
    beq 4f
    cmp x7,#65                // < A ?
    cinc x3,x3,lt
    blt 1b
    cmp x7,#90                // > Z
    ble 2f

    cmp x7,#97                // < a ?
    cinc x3,x3,lt
    blt 1b
    cmp x7,#122               //> z
    cinc x3,x3,gt
    bgt 1b
    sub x7,x7,#32                // convert minuscul to majuscule 
2:
    mov x11,'I'
    cmp x7,#'J'               // change J to I
    csel x7,x11,x7,eq
    ldr x11,qAdrtabiposX
    ldr x12,qAdrtabiposY
    mov x6,#0
    mov x9,#5
3:                            // loop to search char in polybe square
    ldrb w8,[x1,x6]           // load byte polybe
    cmp x8,x7                 // equal ?
    cinc x6,x6,ne
    bne 3b
    udiv x10,x6,x9            // compute coordonnés
    msub x8,x10,x9,x6
    strb w10,[x11,x5]         // save X position
    strb w8,[x12,x5]          // save Y position
    add x5,x5,#1              // increment indice
    add x3,x3,#1
    b 1b                      // and loop
    
4:
    mov x6,#0
    mov x8,x5
    mov x5,#0
    mov x3,#5
5:                            // convert position line 1 in char
    ldrb w9,[x11,x6]
    add x6,x6,#1
    cmp x6,x8                 // first line end ?
    csel x6,xzr,x6,ge
    bge 7f                    // jmp for second line
    
    ldrb w7,[x11,x6]          // compute rank with position X and Y
    mul x9,x3,x9
    add x9,x9,x7
    ldrb w9,[x1,x9]           // load polybe char
    strb w9,[x2,x5]
    add x5,x5,#1
 
    add x6,x6,#1
    cmp x6,x8
    blt 5b

    mov x6,#0
6:                            // convert position line 2 in char
    ldrb w9,[x12,x6]
    add x6,x6,#1
7:
    ldrb w7,[x12,x6]          // compute rank with position X and Y
    mul x9,x3,x9
    add x9,x9,x7
    ldrb w9,[x1,x9]           // load polybe char
    strb w9,[x2,x5]
    add x5,x5,#1
    add x6,x6,#1
    cmp x6,x8
    blt 6b

    mov x9,#0                 // zero final
    strb w9,[x2,x5]
    mov x0,x5
100:
    ldp x10,x11,[sp],16       // restau registers
    ldp x8,x9,[sp],16         //
    ldp x6,x7,[sp],16         //
    ldp x4,x5,[sp],16         //
    ldp x2,x3,[sp],16         //
    ldp x1,lr,[sp],16         //
    ret
qAdrtabiposX:     .quad tabiposX
qAdrtabiposY:     .quad tabiposY
/******************************************************************/
/*     decrypt strings                         */ 
/******************************************************************/
/* r0 contains the address of the encrypted string1 */
/* r1 contains Polybe area address
/* r2 contains the address of the decrypted string */
/* r0 return buffer lenght  */
decrypt:
    stp x1,lr,[sp,-16]!       // save registers
    stp x2,x3,[sp,-16]!       //
    stp x4,x5,[sp,-16]!       //
    stp x6,x7,[sp,-16]!       //
    stp x8,x9,[sp,-16]!       //
    stp x10,x11,[sp,-16]!     //
    mov x3,#0                 // counter byte string 1
    mov x5,#0                 // counter byte result buffer
1:
    ldrb w7,[x0,x3]           // load byte string 1
    cmp x7,#0                 // zero final ?
    csel x0,x5,x0,eq
    beq 4f
    
    cmp x7,#65                // < A ?
    cinc x3,x3,lt
    blt 1b
    cmp x7,#90                // > Z
    ble 2f

    cmp x7,#97                // < a ?
    cinc x3,x3,lt
    blt 1b
    cmp x7,#122               //> z
    cinc x3,x3,gt
    bgt 1b
    sub x7,x7,#32                // convert minuscul to majuscule 
2:
    ldr x11,qAdrtabiposX
    mov x6,#0
    mov x9,#5
3:                            // loop to search char in polybe square
    ldrb w8,[x1,x6]           // load byte polybe
    cmp x8,x7
    cinc x6,x6,ne
    bne 3b

    udiv x10,x6,x9
    msub x8,x10,x9,x6
    strb w10,[x11,x5]
    add x5,x5,#1
    strb w8,[x11,x5]
    add x5,x5,#1
    
    add x3,x3,#1
    b 1b
    
4:
    mov x6,#0           // start indice first line
    lsr x8,x5,#1
    mov x10,x8          // start indice second line
    mov x5,#0
    mov x3,#5

5:
    ldrb w9,[x11,x6]    // load position
    ldrb w7,[x11,x10]
    mul x9,x3,x9        // compute rank
    add x9,x9,x7
    ldrb w9,[x1,x9]     // load polybe byte
    strb w9,[x2,x5]     // and store to result 
    add x5,x5,#1
    add x10,x10,#1
    add x6,x6,#1
    cmp x6,x8           // end ?
    blt 5b

    mov x9,#0            // 0 final
    strb w9,[x2,x5]
    mov x0,x5
100:
    ldp x10,x11,[sp],16       //  restaur registers
    ldp x8,x9,[sp],16         //
    ldp x6,x7,[sp],16         //
    ldp x4,x5,[sp],16         //
    ldp x2,x3,[sp],16         //
    ldp x1,lr,[sp],16         //
    ret
    
/***************************************************/
/*      ROUTINES INCLUDE                 */
/***************************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeARM64.inc"
Output:
Program 64 bits start.
String :
ATTACKATDAWN
Encrypted :
DQBDAXDQPDQH
Decrypted :
ATTACKATDAWN
String :
FLEEATONCE
Encrypted :
UAEOLWRINS
Decrypted :
FLEEATONCE
String :
ATTACKATDAWN
Encrypted :
EYFENGIWDILA
Decrypted :
ATTACKATDAWN
String :
The invasion will start on the first of January
Encrypted :
RBPDHPHOQTNRBITMFODYPSAOSIAOBTOPDHTDCVI
Decrypted :
THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY
Program normal end.

Ada

-- Bifid cipher
-- J. Carter     2023 May
-- The Bifid cipher is included as part of the PragmAda Reusable Components (https://github.com/jrcarter/PragmARC)

with Ada.Text_IO;
with PragmARC.Encryption.Bifid;

procedure Bifid_Test is
   package Bifid renames PragmARC.Encryption.Bifid;

   Key_1 : constant Bifid.Square_Layout := ("ABCDE", "FGHIK", "LMNOP", "QRSTU", "VWXYZ");
   Key_2 : constant Bifid.Square_Layout := ("BGWKZ", "QPNDS", "IOAXE", "FCLUM", "THYVR");

   Msg_1 : constant String := "ATTACKATDAWN";
   Msg_2 : constant String := "FLEEATONCE";
   Msg_3 : constant String := "THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY";

   Crypt_1 : String (Msg_1'Range);
   Crypt_2 : String (Msg_2'Range);
   Crypt_3 : String (Msg_3'Range);
begin -- Bifid_Test
   Crypt_1 := Bifid.Encrypt (Msg_1, Key_1);
   Ada.Text_IO.Put_Line (Item => Msg_1 & " => " & Crypt_1 & " => " & Bifid.Decrypt (Crypt_1, Key_1) );
   Crypt_2 := Bifid.Encrypt (Msg_2, Key_2);
   Ada.Text_IO.Put_Line (Item => Msg_2 & " => " & Crypt_2 & " => " & Bifid.Decrypt (Crypt_2, Key_2) );
   Crypt_1 := Bifid.Encrypt (Msg_1, Key_2);
   Ada.Text_IO.Put_Line (Item => Msg_1 & " => " & Crypt_1 & " => " & Bifid.Decrypt (Crypt_1, Key_2) );
   Crypt_3 := Bifid.Encrypt (Msg_3, Key_2);
   Ada.Text_IO.Put_Line (Item => Msg_3 & " => " & Crypt_3 & " => " & Bifid.Decrypt (Crypt_3, Key_2) );
end Bifid_Test;
Output:
ATTACKATDAWN => DQBDAXDQPDQH => ATTACKATDAWN
FLEEATONCE => UAEOLWRINS => FLEEATONCE
ATTACKATDAWN => EYFENGIWDILA => ATTACKATDAWN
THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY => RASOAQXCYRORXESXADETSWLTNIATEGISBRGBALY => THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY

ALGOL 68

Using additional test cases and 5x5 squares from the EasyLang and other samples, plus using 6x6 and 8x8 squares for the bonus.

BEGIN # Bifid cipher                                                         #

    # mode to hold the details of a Bifid cipher                             #
    MODE BIFID = STRUCT( STRING key        # the Polybius square, linearised #
                       , STRING pre from    # pre-translation, "from" string #
                       , STRING pre to        # pre-tramslatiion "to" string #
                       );

    # string utilities                                                       #

    # returns the length of s                                                #
    OP   LENGTH = ( STRING s )INT: ( UPB s - LWB s ) + 1;
    # returns the index of c in s, or LWB s - 1 if it is not present         #
    PRIO INDEXOF = 1;
    OP   INDEXOF = ( STRING s, CHAR c )INT:
         BEGIN
            INT index := LWB s - 1;
            VOID( char in string( c, index, s ) );      # discard the result #
            index
         END # INDEXOF # ;
    # returns text with the characters in from chars replaced with the       #
    #         corresponding characters in to chars                           #
    PROC translate = ( STRING text, from chars, to chars )STRING:
         IF   LWB from chars /= LWB to chars OR UPB from chars /= UPB to chars
         THEN """from"" and ""to"" have different bounds (translate)"
         ELIF from chars = ""
         THEN text
         ELSE # the from and to strings have the same bounds                 #
              STRING result := text;
              FOR r pos FROM LWB result TO UPB result DO
                  INT t pos = from chars INDEXOF result[ r pos ];
                  IF  t pos >= LWB from chars THEN result[ r pos ] := to chars[ t pos ] FI
              OD;
              result
         FI # translate # ;
    # end string utilities                                                   #    

    # returns text encrypted according to cipher                             #
    PRIO ENCRYPT = 9;
    OP   ENCRYPT = ( STRING text, BIFID cipher )STRING:
         IF   STRING key     = ( key OF cipher )[ AT 1 ];
              INT key length = LENGTH key;
              INT sq width   = ENTIER sqrt( key length );
              sq width * sq width /= key length
         THEN "**** Bifid key is not a square"
         ELSE # the key is a square                                          #
              STRING msg1  = translate( text, pre from OF cipher, pre to OF cipher )[ AT 1 ];
              STRING msg  := "";
              FOR m pos FROM LWB msg1 TO UPB msg1 DO
                  IF msg1[ m pos ] /= " " THEN msg +:= msg1[ m pos ] FI
              OD;
              INT    m max = LENGTH msg;
              [ 1 : 2 * m max ]INT coordinates;
              FOR m pos TO m max DO
                  INT c index := key INDEXOF msg[ m pos ];
                  IF  c index >= LWB key THEN c index -:= 1 FI;
                  coordinates[ m pos         ] := c index OVER sq width;
                  coordinates[ m pos + m max ] := c index MOD  sq width
              OD;
              STRING result := "";
              FOR c pos BY 2 TO UPB coordinates DO
                  result +:= key[ ( coordinates[ c pos     ] * sq width )
                                +   coordinates[ c pos + 1 ]
                                + 1
                                ]
              OD;
              result
         FI # ENCRYPT # ;

    # returns text decrypted according to cipher                             #
    PRIO DECRYPT = 9;
    OP   DECRYPT = ( STRING text, BIFID cipher )STRING:
         IF   STRING key     = ( key OF cipher )[ AT 1 ];
              INT key length = LENGTH key;
              INT sq width   = ENTIER sqrt( key length );
              sq width * sq width /= key length
         THEN "**** Bifid key is not a square"
         ELSE # the key is a square                                          #
              STRING msg   = text[ AT 1 ];
              INT    m max = LENGTH msg;
              [ 1 : 2 * m max ]INT coordinates;
              INT c pos := 0;
              FOR m pos TO m max DO
                  INT c index := key INDEXOF msg[ m pos ];
                  IF  c index >= LWB key THEN c index -:= 1 FI;
                  coordinates[ c pos +:= 1 ] := c index OVER sq width;
                  coordinates[ c pos +:= 1 ] := c index MOD  sq width
              OD;
              STRING result := "";
              FOR i TO m max DO
                  result +:= key[ ( coordinates[ i         ] * sq width )
                                +   coordinates[ i + m max ]
                                + 1
                                ]
              OD;
              result
         FI # DECRYPT # ;

    # tests the ENCRYPT and DECRYPT operators                                #
    PROC test bifid = ( BIFID cipher, STRING text )VOID:
         BEGIN
            STRING code = text ENCRYPT cipher;
            print( ( text, " -> ", code,                newline ) );
            print( ( code, " -> ", code DECRYPT cipher, newline ) )
         END # text bifid # ;

    # Bifid ciphers for tests                                                #

    BIFID bifid 5 abc = ( "ABCDEFGHIKLMNOPQRSTUVWXYZ"
                        , "Jabcdefghijklmnopqrstuvwxyz"
                        , "IABCDEFGHIIKLMNOPQRSTUVWXYZ"
                        );
    BIFID bifid 5 bgw = ( "BGWKZQPNDSIOAXEFCLUMTHYVR"
                        , "Jabcdefghijklmnopqrstuvwxyz"
                        , "IABCDEFGHIIKLMNOPQRSTUVWXYZ"
                        );
    BIFID bifid 6 abc = ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
                        , "abcdefghijklmnopqrstuvwxyz"
                        , "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                        );
    BIFID bifid 8 abc = ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\/"
                        , " "
                        , "/"
                        );

    # basic task - using 5x5 Polybius squares                                #
    test bifid( bifid 5 abc, "ATTACKATDAWN" );
    print( ( newline ) );
    test bifid( bifid 5 bgw, "FLEEATONCE" );
    test bifid( bifid 5 bgw, "ATTACKATDAWN" );
    test bifid( bifid 5 bgw, "The invasion will begin on the first of January" );
    print( ( newline ) );

    # bonus - using a 6x6 square                                             #
    test bifid( bifid 6 abc, "The invasion will begin on the first of January" );
    print( ( newline ) );

    # bonus - using an 8x8 square                                            #
    test bifid( bifid 8 abc, "The invasion will begin on the first of January" )

END
Output:
ATTACKATDAWN -> DQBDAXDQPDQH
DQBDAXDQPDQH -> ATTACKATDAWN

FLEEATONCE -> UAEOLWRINS
UAEOLWRINS -> FLEEATONCE
ATTACKATDAWN -> EYFENGIWDILA
EYFENGIWDILA -> ATTACKATDAWN
The invasion will begin on the first of January -> RASOAQXFIOORXESXADETSWLTNIAZQOISBRGBALY
RASOAQXFIOORXESXADETSWLTNIAZQOISBRGBALY -> THEINVASIONWILLBEGINONTHEFIRSTOFIANUARY

The invasion will begin on the first of January -> TBPDIPHGBIOTAIVMGPCZKNSCN09KCIHK64I7BM4
TBPDIPHGBIOTAIVMGPCZKNSCN09KCIHK64I7BM4 -> THEINVASIONWILLBEGINONTHEFIRSTOFJANUARY

The invasion will begin on the first of January -> Ufkrss\knbkns9j7lt9fLldzO6/UQ/Ct7wX4/p36cvH5Xya
Ufkrss\knbkns9j7lt9fLldzO6/UQ/Ct7wX4/p36cvH5Xya -> The/invasion/will/begin/on/the/first/of/January

AppleScript

(* The "square" here is only notional, the characters' coordinates being calculated
   from their offsets in a linear text key. But a square key is accepted if the
   signaller's careless enough to keep or transmit one in this form. *)
on bifidEncipher(message, square)
    return transcipher(message, square, 1)
end bifidEncipher

on bifidDecipher(message, square)
    return transcipher(message, square, 2)
end bifidDecipher

on transcipher(message, square, code) -- code: 1 = encipher, 2 = decipher.
    -- Check and massage the input.
    set message to join(message's words, "")
    if (message contains ".") then set message to replaceText(".", "", message)
    set square to join(join(square, "")'s words, "")
    set squarea to (count square)
    set sqrt to (squarea ^ 0.5) as integer
    if (sqrt * sqrt  squarea) then error "Invalid key."
    ignoring case
        if ((sqrt < 6) and (message contains "J")) then ¬
            set message to replaceText("J", "I", message)
    end ignoring
    -- Calculate coordinates from the message characters' offsets in the "square" text
    -- and either split and recombine them (when enciphering) or not (deciphering).
    set list1 to {}
    set list2 to {{}, list1}'s item code
    set astid to AppleScript's text item delimiters
    ignoring case and diacriticals
        repeat with chr in message
            set AppleScript's text item delimiters to chr
            set |offset - 1| to (count square's first text item)
            set list1's end to |offset - 1| div sqrt + 1
            set list2's end to |offset - 1| mod sqrt + 1
        end repeat
    end ignoring
    set AppleScript's text item delimiters to astid
    set coords to {list1 & list2, list1}'s item code
    -- Calculate new offsets from the appropriate numbers in the coords list
    -- and get the characters offset by those amounts in the "square" text.
    set chrs to {}
    set nCoords to (count coords)
    set indexDifference to {1, nCoords div 2}'s item code
    repeat with i from 1 to (nCoords div code) by (3 - code)
        set |offset| to ((coords's item i) - 1) * sqrt + (coords's item (i + indexDifference))
        set chrs's end to square's character |offset|
    end repeat
    return join(chrs, "")
end transcipher

on join(lst, delim)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to delim
    set txt to lst as text
    set AppleScript's text item delimiters to astid
    return txt
end join

on replaceText(searchText, replacement, mainText)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to searchText
    set textItems to mainText's text items
    set AppleScript's text item delimiters to replacement
    set mainText to textItems as text
    set AppleScript's text item delimiters to astid
    return mainText
end replaceText

on task()
    set output to {}
    repeat with this in {{"Given example:", "ATTACKATDAWN", "ABCDEFGHIKLMNOPQRSTUVWXYZ"}, ¬
        {"Wikipedia example:", "FLEEATONCE", {"BGWKZ", "QPNDS", "IOAXE", "FCLUM", "THYVR"}}, ¬
        {"A 6 x 6 square allows all 36 letters & digits to be encoded:", ¬
            "The invasion will start on the 1st of January", ¬
            "THEQUI
                    9CKBR0
                    1OWNF8
                    7XJMP2
                    3SVLA6
                    5ZYDG4"}}
        set {heading, message, square} to this
        set encrypted to bifidEncipher(message, square)
        set decrypted to bifidDecipher(encrypted, square)
        set output's end to heading
        set output's end to message & " --> " & encrypted & " --> " & decrypted
    end repeat
    
    return join(output, linefeed)
end task

task()
Output:
"Given example:
ATTACKATDAWN --> DQBDAXDQPDQH --> ATTACKATDAWN
Wikipedia example:
FLEEATONCE --> UAEOLWRINS --> FLEEATONCE
A 6 x 6 square allows all 36 letters & digits to be encoded:
The invasion will start on the 1st of January --> TTFAEWUAU9WTE3WP1S5KDF0B8M9AH7KHHVLAV --> THEINVASIONWILLSTARTONTHE1STOFJANUARY"

ARM Assembly

Works with: as version Raspberry Pi
or android 32 bits with application Termux
/* ARM assembly Raspberry PI  */
/*  program bifid.s   */
/* REMARK 1 : this program use routines in a include file 
   see task Include a file language arm assembly 
   for the routine affichageMess conversion10 
   see at end of this program the instruction include */

/*******************************************/
/* Constantes                              */
/*******************************************/
.include "../constantes.inc"

.equ MESSSIZE,   2000
.equ BUFFERSIZE, 2000
/*******************************************/
/*   Macros                              */
/*******************************************/
//.include "../../ficmacros32.inc"            @ for developer debugging


/*******************************************/
/* Initialized data */
/*******************************************/
.data
szMessDebutPgm:   .asciz "Program 32 bits start. \n"
szCarriageReturn: .asciz "\n"
szMessFinOK:      .asciz "Program normal end. \n"
szMessError:      .asciz "\nError  Buffer too small!!!\n"
szMessString:     .asciz "String :\n"
szMessEncrip:     .asciz "\nEncrypted :\n"
szMessDecrip:     .asciz "\nDecrypted :\n"
//szString1:        .asciz "ABCDEFGHIJKLMNOPQRSTUVWXYZ!.?abcdefghijklmnopqrstuvwxyz"
//szString1:         .asciz "attackatdawn"
szString1:         .asciz "ATTACKATDAWN"
//szString1:         .asciz "ABCD"
szString2:         .asciz "FLEEATONCE"
szString3:         .asciz "L'invasion commencera le premier janvier" 

szPolybe1:        .ascii "ABCDE"
                  .ascii "FGHIK"
                  .ascii "LMNOP"
                  .ascii "QRSTU"
                  .asciz "VWXYZ"
                  
szPolybe2:        .ascii "BGWKZ"
                  .ascii "QPNDS"
                  .ascii "IOAXE"
                  .ascii "FCLUM"
                  .asciz "THYVR"

/*******************************************/
/* UnInitialized data */
/*******************************************/
.bss 
.align 4
tabiposX:                .skip 4 * MESSSIZE  
tabiposY:                .skip 4 * MESSSIZE 
sBuffer1:                .skip  BUFFERSIZE
sBuffer2:                .skip  BUFFERSIZE
/*******************************************/
/*  code section */
/*******************************************/
.text
.global main 
main: 
    ldr r0,iAdrszMessDebutPgm
    bl affichageMess
    ldr r0,iAdrszMessString       @ display message
    bl affichageMess
    ldr r0,iAdrszString1          @ display string
    bl affichageMess
    ldr r0,iAdrszString1          @ string address
    ldr r1,iAdrszPolybe1          @ Polybe key
    ldr r2,iAdrsBuffer1           @ encrypted buffer result
    bl encrypt
    cmp r0,#0
    ble 99f
    ldr r0,iAdrszMessEncrip
    bl affichageMess
    ldr r0,iAdrsBuffer1           @ display encrypted buffer
    bl affichageMess 
  

    ldr r0,iAdrsBuffer1           @ encrypted buffer
    ldr r1,iAdrszPolybe1          @ Polybe key
    ldr r2,iAdrsBuffer2           @ decrypted buffer 
    bl decrypt
    ldr r0,iAdrszMessDecrip
    bl affichageMess
    ldr r0,iAdrsBuffer2           @ display decrypted buffer 
    bl affichageMess 
    ldr r0,iAdrszCarriageReturn
    bl affichageMess 
    
    ldr r0,iAdrszMessString       @ display message
    bl affichageMess
    ldr r0,iAdrszString2          @ display string
    bl affichageMess
    ldr r0,iAdrszString2          @ string address
    ldr r1,iAdrszPolybe2          @ Polybe key
    ldr r2,iAdrsBuffer1           @ encrypted buffer result
    bl encrypt
    cmp r0,#0
    ble 99f
    ldr r0,iAdrszMessEncrip
    bl affichageMess
    ldr r0,iAdrsBuffer1           @ display encrypted buffer
    bl affichageMess 
  

    ldr r0,iAdrsBuffer1           @ encrypted buffer
    ldr r1,iAdrszPolybe2          @ Polybe key
    ldr r2,iAdrsBuffer2           @ decrypted buffer 
    bl decrypt
    ldr r0,iAdrszMessDecrip
    bl affichageMess
    ldr r0,iAdrsBuffer2           @ display decrypted buffer 
    bl affichageMess 
    ldr r0,iAdrszCarriageReturn
    bl affichageMess 
    
    ldr r0,iAdrszMessString       @ display message
    bl affichageMess
    ldr r0,iAdrszString1          @ display string
    bl affichageMess
    ldr r0,iAdrszString1          @ string address
    ldr r1,iAdrszPolybe2          @ Polybe key
    ldr r2,iAdrsBuffer1           @ encrypted buffer result
    bl encrypt
    cmp r0,#0
    ble 99f
    ldr r0,iAdrszMessEncrip
    bl affichageMess
    ldr r0,iAdrsBuffer1           @ display encrypted buffer
    bl affichageMess 
  

    ldr r0,iAdrsBuffer1           @ encrypted buffer
    ldr r1,iAdrszPolybe2          @ Polybe key
    ldr r2,iAdrsBuffer2           @ decrypted buffer 
    bl decrypt
    ldr r0,iAdrszMessDecrip
    bl affichageMess
    ldr r0,iAdrsBuffer2           @ display decrypted buffer 
    bl affichageMess 
    ldr r0,iAdrszCarriageReturn
    bl affichageMess 
    
    ldr r0,iAdrszMessString       @ display message
    bl affichageMess
    ldr r0,iAdrszString3          @ display string
    bl affichageMess
    ldr r0,iAdrszString3          @ string address
    ldr r1,iAdrszPolybe1          @ Polybe key
    ldr r2,iAdrsBuffer1           @ encrypted buffer result
    bl encrypt
    cmp r0,#0
    ble 99f
    ldr r0,iAdrszMessEncrip
    bl affichageMess
    ldr r0,iAdrsBuffer1           @ display encrypted buffer
    bl affichageMess 
  

    ldr r0,iAdrsBuffer1           @ encrypted buffer
    ldr r1,iAdrszPolybe1          @ Polybe key
    ldr r2,iAdrsBuffer2           @ decrypted buffer 
    bl decrypt
    ldr r0,iAdrszMessDecrip
    bl affichageMess
    ldr r0,iAdrsBuffer2           @ display decrypted buffer 
    bl affichageMess 
    ldr r0,iAdrszCarriageReturn
    bl affichageMess 
    
    ldr r0,iAdrszMessFinOK
    bl affichageMess
    b 100f
99:
    ldr r0,iAdrszMessError        @ error
    bl affichageMess
    mov r0, #1    
100:                              @ standard end of the program
    mov r0, #0                    @ return code
    mov r7, #EXIT                 @ request to exit program
    svc 0                         @ perform system call
iAdrszMessString:         .int szMessString
iAdrszMessDecrip:         .int szMessDecrip
iAdrszMessEncrip:         .int szMessEncrip
iAdrszString1:            .int szString1
iAdrszPolybe1:            .int szPolybe1
iAdrszString2:            .int szString2
iAdrszString3:            .int szString3
iAdrszPolybe2:            .int szPolybe2
iAdrsBuffer1:             .int sBuffer1
iAdrsBuffer2:             .int sBuffer2
iAdrszMessDebutPgm:       .int szMessDebutPgm
iAdrszMessFinOK:          .int szMessFinOK
iAdrszCarriageReturn:     .int szCarriageReturn
iAdrszMessError:          .int szMessError
/******************************************************************/
/*     encrypt strings                         */ 
/******************************************************************/
/* r0 contains the address of the string1 */
/* r1 contains Polybe area address
/* r2 contains the address of the encrypted string */
/* r0 return buffer lenght  */
encrypt:
    push {r3-r12,lr}          @ save  registers 
    mov r3,#0                 @ counter byte string 1
    mov r5,#0                 @ counter byte buffer

1:
    ldrb r7,[r0,r3]           @ load byte string 1
    cmp r7,#0                 @ zero final ?
    beq 4f
    cmp r7,#65                @ < A ?
    addlt r3,#1
    blt 1b
    cmp r7,#90                @ > Z
    ble 2f

    cmp r7,#97                @ < a ?
    addlt r3,#1
    blt 1b
    cmp r7,#122               @> z
    addgt r3,#1
    bgt 1b
    sub r7,#32                @ convert minuscul to majuscule 
2:
    cmp r7,#'J'               @ change J to I
    moveq r7,#'I'
    ldr r11,iAdrtabiposX
    ldr r12,iAdrtabiposY
    mov r6,#0
    mov r9,#5
3:                            @ loop to search char in polybe square
    ldrb r8,[r1,r6]           @ load byte polybe
    cmp r8,r7                 @ equal ?
    addne r6,#1
    bne 3b
    udiv r10,r6,r9            @ compute coordonnés
    mls r8,r10,r9,r6
 
    strb r10,[r11,r5]         @ save X position
    strb r8,[r12,r5]          @ save Y position
    add r5,r5,#1              @ increment indice
    add r3,r3,#1
    b 1b                      @ and loop
    
4:
    mov r6,#0
    mov r8,r5
    mov r5,#0
    mov r3,#5
5:                            @ convert position line 1 in char
    ldrb r9,[r11,r6]
    add r6,r6,#1
    cmp r6,r8                 @ first line end ?
    movge r6,#0
    bge 7f                    @ jmp for second line
    
    ldrb r7,[r11,r6]          @ compute rank with position X and Y
    mul r9,r3,r9
    add r9,r9,r7
    ldrb r9,[r1,r9]           @ load polybe char
    strb r9,[r2,r5]
    add r5,r5,#1
 
    add r6,r6,#1
    cmp r6,r8
    blt 5b

    mov r6,#0
6:                            @ convert position line 2 in char
    ldrb r9,[r12,r6]
    add r6,r6,#1
7:
    ldrb r7,[r12,r6]          @ compute rank with position X and Y
    mul r9,r3,r9
    add r9,r9,r7
    ldrb r9,[r1,r9]           @ load polybe char
    strb r9,[r2,r5]
    add r5,r5,#1
    add r6,r6,#1
    cmp r6,r8
    blt 6b

    mov r9,#0                 @ zero final
    strb r9,[r2,r5]
    mov r0,r5
100:
    pop {r3-r12,pc}            @ restaur registers
iAdrtabiposX:     .int tabiposX
iAdrtabiposY:     .int tabiposY
/******************************************************************/
/*     decrypt strings                         */ 
/******************************************************************/
/* r0 contains the address of the encrypted string1 */
/* r1 contains Polybe area address
/* r2 contains the address of the decrypted string */
/* r0 return buffer lenght  */
decrypt:
    push {r3-r11,lr}           @ save  registers 
    mov r3,#0                 @ counter byte string 1
    mov r5,#0                 @ counter byte result buffer
1:
    ldrb r7,[r0,r3]           @ load byte string 1
    cmp r7,#0                 @ zero final ?
    moveq r0,r5
    beq 4f
    
    cmp r7,#65                @ < A ?
    addlt r3,#1
    blt 1b
    cmp r7,#90                @ > Z
    ble 2f

    cmp r7,#97                @ < a ?
    addlt r3,#1
    blt 1b
    cmp r7,#122               @> z
    addgt r3,#1
    bgt 1b
    sub r7,#32                @ convert minuscul to majuscule 
2:
    ldr r11,iAdrtabiposX
    mov r6,#0
    mov r9,#5
3:                            @ loop to search char in polybe square
    ldrb r8,[r1,r6]           @ load byte polybe
    cmp r8,r7
    addne r6,#1
    bne 3b

    udiv r10,r6,r9
    mls r8,r10,r9,r6
    strb r10,[r11,r5]
    add r5,r5,#1
    strb r8,[r11,r5]
    add r5,r5,#1
    
    add r3,#1
    b 1b
    
4:
    mov r6,#0           @ start indice first line
    lsr r8,r5,#1
    mov r10,r8          @ start indice second line
    mov r5,#0
    mov r3,#5

5:
    ldrb r9,[r11,r6]    @ load position
    ldrb r7,[r11,r10]
    mul r9,r3,r9        @ compute rank
    add r9,r9,r7
    ldrb r9,[r1,r9]     @ load polybe byte
    strb r9,[r2,r5]     @ and store to result 
    add r5,r5,#1
    add r10,r10,#1
    add r6,r6,#1
    cmp r6,r8           @ end ?
    blt 5b

    mov r9,#0            @ 0 final
    strb r9,[r2,r5]
    mov r0,r5
100:
    pop {r3-r11,pc}            @ restaur registers

    
/***************************************************/
/*      ROUTINES INCLUDE                 */
/***************************************************/
.include "../affichage.inc"
Output:
Program 32 bits start.
String :
ATTACKATDAWN
Encrypted :
DQBDAXDQPDQH
Decrypted :
ATTACKATDAWN
String :
FLEEATONCE
Encrypted :
UAEOLWRINS
Decrypted :
FLEEATONCE
String :
ATTACKATDAWN
Encrypted :
EYFENGIWDILA
Decrypted :
ATTACKATDAWN
String :
L'invasion commencera le premier janvier
Encrypted :
MPDHLNLLDCCQMDFPFQSAOSOGXPFEWWUICDW
Decrypted :
LINVASIONCOMMENCERALEPREMIERIANVIER
Program normal end.

BASIC

Applesoft BASIC

 100 A$ = "ATTACKATDAWN": GOSUB 160"REPORT
 110 K$ = "BGWKZQPNDSIOAXEFCLUMTHYVR"
 120 A$ = "FLEEATONCE": GOSUB 160"REPORT
 130 K$ = " .'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123"
 140 A$ = "THE INVASION WILL START ON THE FIRST OF JANUARY 2023.": GOSUB 160"REPORT
 150 END

 REM REPORT
 160 GOSUB 200"ENCRYPT
 165 PRINT M$M$"FOR "W" X "W" POLYBIUS:":M$ =  CHR$ (13): FOR I = 1 TO W: PRINT , MID$ (K$,(I - 1) * W + 1,W): NEXT 
 170 PRINT "ENCRYPTED: "E$
 180 GOSUB 300"DECRYPT
 190 PRINT "DECRYPTED: "U$;: RETURN

 REM ENCRYPT A$ RETURNS E$
 200 GOSUB 400:L =  LEN (A$):E$ = "":U$ = "": IF  NOT L THEN  RETURN
 210 FOR I = 1 TO L
 220     C =  ASC ( MID$ (A$,I,1)): IF X(C) AND Y(C) THEN U$ = U$ +  CHR$ (C)
 230 NEXT I
 240 L =  LEN (U$): IF  NOT L THEN  RETURN
 250 FOR I = 1 TO L:C =  ASC ( MID$ (U$,I,1)):A(I) = X(C):A(I + L) = Y(C): NEXT I
 260 FOR I = 1 TO L * 2 STEP 2:E$ = E$ +  MID$ (K$,(A(I) - 1) * W + A(I + 1),1): NEXT I
 270 RETURN

 REM DECRYPT E$ RETURNS U$
 300 GOSUB 400:L =  LEN (E$):U$ = "": IF  NOT L THEN  RETURN
 310 FOR I = 1 TO L:C =  ASC ( MID$ (E$,I)):B(I * 2 - 1) = X(C):B(I * 2) = Y(C): NEXT I
 320 FOR I = 1 TO L:U$ = U$ +  MID$ (K$,(B(I) - 1) * W + B(L + I),1): NEXT I
 330 RETURN

 REM POLYBIUS K$ RETURNS X(255),Y(255)
 400 IF K$ = P$ AND  LEN (K$) THEN  RETURN
 410 IF XY THEN  FOR I = 0 TO 255:X(I) = 0:Y(I) = 0: NEXT I
 420 IF  NOT XY THEN  DIM X(255),Y(255),A(512),B(512):XY = 1
 430 IF K$ = "" THEN  FOR I = 1 TO 25:K$ = K$ +  CHR$ (I + 64 + (I > 9)): NEXT I
 440 L =  LEN (K$):W =  INT ( SQR (L - 1) + 1):I = 1:N = 1:K =  ASC ("0")
 450 FOR X = 1 TO W
 460     FOR Y = 1 TO W
 470         C$ =  MID$ (K$,I,1): IF C$ = "" THEN  FOR C = K TO 255: IF X(C) THEN  NEXT C: STOP 
 480         IF C$ = "" THEN C$ =  CHR$ (C):K$ = K$ + C$:K = C + 1
 490         C =  ASC (C$):Y(C) = Y:X(C) = X:I = I + 1: IF C$ = "J" THEN N = 0
 500 NEXT Y,X
 510 IF N THEN Y( ASC ("J")) = Y( ASC ("I")):X( ASC ("J")) = X( ASC ("I"))
 520 P$ = K$
 530 RETURN
Output:
FOR 5 X 5 POLYBIUS:
                ABCDE
                FGHIK
                LMNOP
                QRSTU
                VWXYZ
ENCRYPTED: DQBDAXDQPDQH
DECRYPTED: ATTACKATDAWN

FOR 5 X 5 POLYBIUS:
                BGWKZ
                QPNDS
                IOAXE
                FCLUM
                THYVR
ENCRYPTED: UAEOLWRINS
DECRYPTED: FLEEATONCE

FOR 6 X 6 POLYBIUS:
                 .'ABC
                DEFGHI
                JKLMNO
                PQRSTU
                VWXYZ0
                123456
ENCRYPTED: QDFVQLBFJSAPLAE.GS'DJMAV56BWCVS6VILAYNCVZDOMV3 T4M.2K
DECRYPTED: THE INVASION WILL START ON THE FIRST OF JANUARY 2023.

C#

Translation of: Java
using System;
using System.Collections.Generic;
using System.Drawing;

public class BifidCipher
{
    public static void Main(string[] args)
    {
        string message1 = "ATTACKATDAWN";
        string message2 = "FLEEATONCE";
        string message3 = "The invasion will start on the first of January".ToUpper().Replace(" ", "");

        Bifid bifid1 = new Bifid(5, "ABCDEFGHIKLMNOPQRSTUVWXYZ");
        Bifid bifid2 = new Bifid(5, "BGWKZQPNDSIOAXEFCLUMTHYVR");

        RunTest(bifid1, message1);
        RunTest(bifid2, message2);
        RunTest(bifid2, message1);
        RunTest(bifid1, message2);

        Bifid bifid3 = new Bifid(6, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        RunTest(bifid3, message3);
    }

    private static void RunTest(Bifid bifid, string message)
    {
        Console.WriteLine("Using Polybius square:");
        bifid.Display();
        Console.WriteLine("Message:   " + message);
        string encrypted = bifid.Encrypt(message);
        Console.WriteLine("Encrypted: " + encrypted);
        string decrypted = bifid.Decrypt(encrypted);
        Console.WriteLine("Decrypted: " + decrypted);
        Console.WriteLine();
    }
}

public class Bifid
{
    private char[,] grid;
    private Dictionary<char, Point> coordinates = new Dictionary<char, Point>();

    public Bifid(int n, string text)
    {
        if (text.Length != n * n)
        {
            throw new ArgumentException("Incorrect length of text");
        }

        grid = new char[n, n];
        int row = 0;
        int col = 0;

        foreach (char ch in text)
        {
            grid[row, col] = ch;
            coordinates[ch] = new Point(row, col);
            col += 1;
            if (col == n)
            {
                col = 0;
                row += 1;
            }
        }

        if (n == 5)
        {
            coordinates['J'] = coordinates['I'];
        }
    }

    public string Encrypt(string text)
    {
        List<int> rowOne = new List<int>();
        List<int> rowTwo = new List<int>();

        foreach (char ch in text)
        {
            Point coordinate = coordinates[ch];
            rowOne.Add(coordinate.X);
            rowTwo.Add(coordinate.Y);
        }

        rowOne.AddRange(rowTwo);
        var result = new System.Text.StringBuilder();

        for (int i = 0; i < rowOne.Count - 1; i += 2)
        {
            result.Append(grid[rowOne[i], rowOne[i + 1]]);
        }

        return result.ToString();
    }

    public string Decrypt(string text)
    {
        List<int> row = new List<int>();

        foreach (char ch in text)
        {
            Point coordinate = coordinates[ch];
            row.Add(coordinate.X);
            row.Add(coordinate.Y);
        }

        int middle = row.Count / 2;
        List<int> rowOne = row.GetRange(0, middle);
        List<int> rowTwo = row.GetRange(middle, row.Count - middle);
        var result = new System.Text.StringBuilder();

        for (int i = 0; i < middle; i++)
        {
            result.Append(grid[rowOne[i], rowTwo[i]]);
        }

        return result.ToString();
    }

    public void Display()
    {
        for (int i = 0; i < grid.GetLength(0); i++)
        {
            for (int j = 0; j < grid.GetLength(1); j++)
            {
                Console.Write(grid[i, j] + " ");
            }
            Console.WriteLine();
        }
    }
}
Output:
Using Polybius square:
A B C D E 
F G H I K 
L M N O P 
Q R S T U 
V W X Y Z 
Message:   ATTACKATDAWN
Encrypted: DQBDAXDQPDQH
Decrypted: ATTACKATDAWN

Using Polybius square:
B G W K Z 
Q P N D S 
I O A X E 
F C L U M 
T H Y V R 
Message:   FLEEATONCE
Encrypted: UAEOLWRINS
Decrypted: FLEEATONCE

Using Polybius square:
B G W K Z 
Q P N D S 
I O A X E 
F C L U M 
T H Y V R 
Message:   ATTACKATDAWN
Encrypted: EYFENGIWDILA
Decrypted: ATTACKATDAWN

Using Polybius square:
A B C D E 
F G H I K 
L M N O P 
Q R S T U 
V W X Y Z 
Message:   FLEEATONCE
Encrypted: HADNAAZDSP
Decrypted: FLEEATONCE

Using Polybius square:
A B C D E F 
G H I J K L 
M N O P Q R 
S T U V W X 
Y Z 0 1 2 3 
4 5 6 7 8 9 
Message:   THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY
Encrypted: TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY


C++

#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string_view>
#include <unordered_map>
#include <vector>

typedef std::pair<int32_t, int32_t> point;

class Bifid {
public:
	Bifid(const int32_t n, std::string_view text) {
		if ( text.length() != n * n ) {
			throw std::invalid_argument("Incorrect length of text");
		}

		grid.resize(n);
		for ( uint64_t i = 0; i < grid.size(); i++ ) {
			grid[i].resize(n);
		}

		int32_t row = 0;
		int32_t col = 0;
		for ( const char& ch : text ) {
			grid[row][col] = ch;
			coordinates[ch] = point(row, col);
			col += 1;
			if ( col == n ) {
				col = 0;
				row += 1;
			 }
		 }

		 if ( n == 5 ) {
			 coordinates['J'] = coordinates['I'];
		 }
	}

	std::string encrypt(std::string_view text) {
		std::vector<int32_t> row_one, row_two;
		for ( const char& ch : text ) {
			point coordinate = coordinates[ch];
			row_one.push_back(coordinate.first);
			row_two.push_back(coordinate.second);
		 }

		 row_one.insert(row_one.end(), row_two.begin(), row_two.end());
		 std::string result;
		 for ( uint64_t i = 0; i < row_one.size() - 1; i += 2 ) {
			 result += grid[row_one[i]][row_one[i + 1]];
		 }
		 return result;
	}

	std::string decrypt(std::string_view text) {
		std::vector<int32_t> row;
		for ( const char& ch : text ) {
			point coordinate = coordinates[ch];
			row.push_back(coordinate.first);
			row.push_back(coordinate.second);
		}

		const int middle = row.size() / 2;
		std::vector<int32_t> row_one = { row.begin(), row.begin() + middle };
		std::vector<int32_t> row_two = { row.begin() + middle, row.end() };
		std::string result;
		for ( int32_t i = 0; i < middle; i++ ) {
			result += grid[row_one[i]][row_two[i]];
		}
		return result;
	}

	void display() const {
		for ( const std::vector<char>& row : grid ) {
			for ( const char& ch : row ) {
				std::cout << ch << " ";
			}
			std::cout << std::endl;
		}
	}
private:
	std::vector<std::vector<char>> grid;
	std::unordered_map<char, point> coordinates;
};

void runTest(Bifid& bifid, std::string_view message) {
	std::cout << "Using Polybius square:" << std::endl;
	bifid.display();
	std::cout << "Message:   " << message << std::endl;
	std::string encrypted = bifid.encrypt(message);
	std::cout << "Encrypted: " << encrypted << std::endl;
	std::string decrypted = bifid.decrypt(encrypted);
	std::cout << "Decrypted: " << decrypted << std::endl;
	std::cout << std::endl;
}

int main() {
	const std::string_view message1 = "ATTACKATDAWN";
	const std::string_view message2 = "FLEEATONCE";
	const std::string_view message3 = "THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY";

	Bifid bifid1(5, "ABCDEFGHIKLMNOPQRSTUVWXYZ");
	Bifid bifid2(5, "BGWKZQPNDSIOAXEFCLUMTHYVR");

	runTest(bifid1, message1);
	runTest(bifid2, message2);
	runTest(bifid2, message1);
	runTest(bifid1, message2);

	Bifid bifid3(6, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
	runTest(bifid3, message3);
}
Output:
Using Polybius square:
A B C D E 
F G H I K 
L M N O P 
Q R S T U 
V W X Y Z 
Message:   ATTACKATDAWN
Encrypted: DQBDAXDQPDQH
Decrypted: ATTACKATDAWN

Using Polybius square:
B G W K Z 
Q P N D S 
I O A X E 
F C L U M 
T H Y V R 
Message:   FLEEATONCE
Encrypted: UAEOLWRINS
Decrypted: FLEEATONCE

Using Polybius square:
B G W K Z 
Q P N D S 
I O A X E 
F C L U M 
T H Y V R 
Message:   ATTACKATDAWN
Encrypted: EYFENGIWDILA
Decrypted: ATTACKATDAWN

Using Polybius square:
A B C D E 
F G H I K 
L M N O P 
Q R S T U 
V W X Y Z 
Message:   FLEEATONCE
Encrypted: HADNAAZDSP
Decrypted: FLEEATONCE

Using Polybius square:
A B C D E F 
G H I J K L 
M N O P Q R 
S T U V W X 
Y Z 0 1 2 3 
4 5 6 7 8 9 
Message:   THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY
Encrypted: TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY

EasyLang

proc prepare . s$ .
   for e$ in strchars s$
      h = strcode e$
      if h >= 97
         h -= 32
      .
      if h >= 65 and h <= 91
         if h = 74
            h = 73
         .
         r$ &= strchar h
      .
   .
   swap s$ r$
.
func$ encr msg$ key$ .
   prepare msg$
   len h[] len msg$ * 2
   for i to len msg$
      c$ = substr msg$ i 1
      j = strpos key$ c$ - 1
      h[i] = j div 5
      h[len msg$ + i] = j mod 5
   .
   for i = 1 step 2 to len h[] - 1
      j = h[i] * 5 + h[i + 1] + 1
      r$ &= substr key$ j 1
   .
   return r$
.
func$ decr msg$ key$ .
   for i to len msg$
      c$ = substr msg$ i 1
      j = strpos key$ c$ - 1
      h[] &= j div 5
      h[] &= j mod 5
   .
   mid = len h[] div 2
   for i = 1 to mid
      j = h[i] * 5 + h[i + mid] + 1
      r$ &= substr key$ j 1
   .
   return r$
.
key$ = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
h$ = encr "ATTACKATDAWN" key$
print h$
print decr h$ key$
print ""
# 
key$ = "BGWKZQPNDSIOAXEFCLUMTHYVR"
h$ = encr "FLEEATONCE" key$
print h$
print decr h$ key$
print ""
h$ = encr "ATTACKATDAWN" key$
print h$
print decr h$ key$
print ""
h$ = encr "The invasion will start on the first of January" key$
print h$
print decr h$ key$
Output:
DQBDAXDQPDQH
ATTACKATDAWN

UAEOLWRINS
FLEEATONCE

EYFENGIWDILA
ATTACKATDAWN

RASOAQXCYRORXESXADETSWLTNIATEGISBRGBALY
THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY

EMal

type PolybiusSquare
model
  Map map
  List grid
  new by int side, text symbols
    me.map ← text%Pair[]
    me.grid ← List[].with(side, <int y|text[].with(side, <int x|""))
    int y, x ← 0
    for each text symbol in symbols
      me.grid[y][x] ← symbol
      me.map[symbol] ← int%int(y => x).named("y", "x")
      x++
      if x æ side
        x ← 0
        y++
      end
    end
  end
  fun asText = text by block
    text result = ""
	for int y ← 0; y < me.grid.length; y++
	  for int x ← 0; x < me.grid[y].length; x++
	    result.append(" " + me.grid[y][x])
	  end
	  result.appendLine()
	end
	return result
  end
end
type BifidCipher
model
  PolybiusSquare square
  new by PolybiusSquare ←square do end
  fun encrypt ← text by text value
    List row ← int[].with(value.length * 2)
    int y ← 0
    int x ← value.length
    for each text char in value
      Pair p ← me.square.map[char]
      row[y++] ← p.y
      row[x++] ← p.x
    end
    text encrypted
    for int i ← 0; i < row.length; i += 2
      encrypted.append(me.square.grid[row[i]][row[i + 1]])
    end
    return encrypted
  end
  fun decrypt ← text by text value
    List row ← int[]
    for each text char in value
      Pair p ← me.square.map[char]
      row.append(p.y)
      row.append(p.x)
    end
    int y ← 0
    int x ← value.length
    text decrypted
    for int i ← 0; i < value.length; i++
      decrypted.append(me.square.grid[row[y++]][row[x++]])
    end
    return decrypted
  end
end
type Main
List pairs ← Pair[
  text%PolybiusSquare("ATTACKATDAWN" => PolybiusSquare(5, "ABCDEFGHIKLMNOPQRSTUVWXYZ")).named("sample", "square"),
  text%PolybiusSquare("FLEEATONCE" => PolybiusSquare(5, "BGWKZQPNDSIOAXEFCLUMTHYVR")).named("sample", "square"),
  text%PolybiusSquare("ATTACKATDAWN" => PolybiusSquare(5, "BGWKZQPNDSIOAXEFCLUMTHYVR")).named("sample", "square"),
  text%PolybiusSquare("The invasion will start on the first of January".upper().replace(" ", "") => 
    PolybiusSquare(6, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")).named("sample", "square")]
for each Pair pair in pairs
  writeLine("Using Polybius square:")
  write(pair.square)
  BifidCipher cipher ← BifidCipher(pair.square)
  write("Encrypting '" + pair.sample + "'")
  text encrypted ← cipher.encrypt(pair.sample)
  write(" => '" + encrypted + "'")
  text decrypted ← cipher.decrypt(encrypted)
  writeLine(" => '" + decrypted + "'")
  writeLine()
end
Output:
Using Polybius square:
 A B C D E
 F G H I K
 L M N O P
 Q R S T U
 V W X Y Z
Encrypting 'ATTACKATDAWN' => 'DQBDAXDQPDQH' => 'ATTACKATDAWN'

Using Polybius square:
 B G W K Z
 Q P N D S
 I O A X E
 F C L U M
 T H Y V R
Encrypting 'FLEEATONCE' => 'UAEOLWRINS' => 'FLEEATONCE'

Using Polybius square:
 B G W K Z
 Q P N D S
 I O A X E
 F C L U M
 T H Y V R
Encrypting 'ATTACKATDAWN' => 'EYFENGIWDILA' => 'ATTACKATDAWN'

Using Polybius square:
 A B C D E F
 G H I J K L
 M N O P Q R
 S T U V W X
 Y Z 0 1 2 3
 4 5 6 7 8 9
Encrypting 'THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY' => 'TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4' => 'THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY'

F#

// Bifid cipher. Nigel Galloway: September 17th., 2024
let polybius alphabet n g=let ng=Seq.allPairs [1..n] [1..g] in (Seq.zip ng alphabet|>Map.ofSeq,Seq.zip alphabet ng|>Map.ofSeq)
let fN (i,g) (e:Map<(int*int),char>)=i@g|>List.chunkBySize 2|>List.map(fun n->e[(n[0],n[1])])|>Array.ofList|>System.String
let fG (n:Map<char,int*int>) g=g|>Seq.map(fun g->n[g])|>List.ofSeq|>List.unzip
let encrypt (n:Map<(int * int),char>,g:Map<char,(int * int)>) text=fN (fG g text) n
let decrypt (n:Map<(int * int),char>,g:Map<char,(int * int)>) text=
  let p,q=let p,q=fG g text in List.map2(fun p q->[p;q]) p q|>List.concat|>List.splitAt p.Length
  List.zip p q |>List.map(fun g->n[g])|>Array.ofList|>System.String

let p1=polybius "abcdefghiklmnopqrstuvwxyz" 5 5
let s="attackatdawn" in let e=encrypt p1 s in printfn $"%s{s} -> %s{e} -> %s{decrypt p1 e}"
let s="fleeatonce" in let e=encrypt p1 s in printfn $"%s{s} -> %s{e} -> %s{decrypt p1 e}"
let p2=polybius "bgwkzqpndsioaxefclumthyvr" 5 5
let s="attackatdawn" in let e=encrypt p2 s in printfn $"%s{s} -> %s{e} -> %s{decrypt p2 e}"
let s="fleeatonce" in let e=encrypt p2 s in printfn $"%s{s} -> %s{e} -> %s{decrypt p2 e}"
let p3=polybius "abcdefghijklmnopqrstuvwxyz0123456789" 6 6
let s="theinvasionwillstarton1january2025" in let e=encrypt p3 s in printfn $"%s{s} -> %s{e} -> %s{decrypt p3 e}"
Output:
attackatdawn -> dqbdaxdqpdqh -> attackatdawn
fleeatonce -> hadnaazdsp -> fleeatonce
attackatdawn -> eyfengiwdila -> attackatdawn
fleeatonce -> uaeolwrins -> fleeatonce
theinvasionwillstarton1january2025 -> tbpdiphjspozcsq23h0jaokr4g5nvbm40z -> theinvasionwillstarton1january2025

FutureBasic

clear local fn recode( t as CFStringRef, code as CFStringRef ) as CFStringRef
  CFStringRef s = @""
  Short i, k, w = sqr( len( code ) )
  
  for i = 0 to len( t ) - 1 step 2
    k = intval( mid( t, i, 2 ) ) // Get ‘coordinates’ of char in code string
    k = w * ( k / 10 ) + k mod 10 
    s = fn StringByAppendingString( s, mid( code, k, 1 ) )
  next
  
end fn = s

//

clear local fn encode( s as CFStringRef, code as CFStringRef ) as CFStringRef
  CFStringRef a = @"", b = @"", c
  Short i, k, w = sqr( len( code ) )
  if w == 5 then s = fn StringByReplacingOccurrencesOfString( s, @"J", @"I" )
  print s
  
  for i = 0 to len( s ) - 1
    c = mid( s, i, 1 )
    k = instr( 0, code, c ) // Put row in one string, column in the other
    a = fn StringByAppendingString( a, fn StringWithFormat( @"%d", k / w ) )
    b = fn StringByAppendingString( b, fn StringWithFormat( @"%d", k mod w ) )
  next
  
  a = fn StringByAppendingString( a, b ) // Combine the two strings, and recode
  
end fn = fn recode( a, code )

//

clear local fn decode( s as CFStringRef, code as CFStringRef ) as CFStringRef
  CFStringRef a = @"", b = @"", c
  Short i, k, w = sqr( len( code ) )
  
  for i = 0 to ( len( s ) - 1 )
    c = mid( s, i, 1 )
    k = instr( 0, code, c ) // Put row and columm in one long string
    a = fn StringByAppendingString( a, fn StringWithFormat( @"%d%d", k / w, k mod w ) )
  next
  
  for i = 0 to len( a ) / 2 - 1 // Take row from first half of string, column from second
    c = fn StringByAppendingString( mid( a, i, 1 ), mid( a, i + len( a ) / 2 , 1 ) )
    b = fn StringByAppendingString( b , c ) // Combine, and recode
  next
  
end fn = fn recode( b, code )

//

print fn encode( @"ATTACKATDAWN", @"ABCDEFGHIKLMNOPQRSTUVWXYZ" )
print fn decode( @"DQBDAXDQPDQH", @"ABCDEFGHIKLMNOPQRSTUVWXYZ" )
print
print fn encode( @"FLEEATONCE", @"BGWKZQPDNSIOAXEFCLUMTHYVR" )
print fn decode( @"UAEOLWRINS", @"BGWKZQPDNSIOAXEFCLUMTHYVR" )
print
print fn encode( @"HAPPY40THDAD", @"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" )
print fn decode( @"GO31GAGVANJD", @"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" )

handleevents
Output:
ATTACKATDAWN
DQBDAXDQPDQH
ATTACKATDAWN

FLEEATONCE
UAEOLWRINS
FLEEATONCE

HAPPY40THDAD
GO31GAGVANJD
HAPPY40THDAD

J

Implementation:

alpha=: a.{~65+i.26
normalize=: {{ rplc&'JI'(toupper y)([-.-.)alpha }}
bifid=: {{ m{~_2 (t&#.)\,|:(t,t=.%:#m)#:m i.y([-.-.)m }}
difib=: {{ m{~t#.|:(|.@$$,)(t,t=.%:#m)#:m i.y([-.-.)m }}

This is pretty much a literal implementation of the algorithm, except that our indices range from 0..4 instead of 1..5 (or, for letter indices, they range from 0..24 instead of 1..25).

Much of the implementation is about converting between a two digit base 5 representation and a single digit numeric representation. The rest is simple array manipulations to make the rest come out right.

Task examples:

ref1=: ~.normalize alpha
ref2=: 'BGWKZQPNDSIOAXEFCLUMTHYVR'
ref3=: 'PLAYFIREXMBCDGHKNOQSTUVWZ'

   ref1 bifid normalize 'attack at dawn'
DQBDAXDQPDQH
   ref1 difib ref1 bifid normalize 'attack at dawn'
ATTACKATDAWN
   ref2 bifid normalize 'flee at once'
UAEOLWRINS
   ref2 difib ref2 bifid normalize 'flee at once'
FLEEATONCE
   ref2 bifid normalize 'attack at dawn'
EYFENGIWDILA
   ref2 difib ref2 bifid normalize 'attack at dawn'
ATTACKATDAWN
   ref3 bifid normalize 'The invasion will start on the first of January'
VRSYXSIYTMQVIRSKISLPVLDTCKRTCAIVTMATCEX
   ref3 difib ref3 bifid normalize 'The invasion will start on the first of January'
THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY
   (_81{.123{.a.)bifid 'The invasion will start on the first of January'
TgqhpqpqxzpxfqzoKqhxw/O3eWH53BYw`+Be8F1
   (_81{.123{.a.)difib(_81{.123{.a.)bifid 'The invasion will start on the first of January'
TheinvasionwillstartonthefirstofJanuary

Java

import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class BifidCipher {

	public static void main(String[] aArgs) {
		final String message1 = "ATTACKATDAWN";
		final String message2 = "FLEEATONCE";
		final String message3 = "The invasion will start on the first of January".toUpperCase().replace(" ", "");
		
		Bifid bifid1 = new Bifid(5, "ABCDEFGHIKLMNOPQRSTUVWXYZ");
		Bifid bifid2 = new Bifid(5, "BGWKZQPNDSIOAXEFCLUMTHYVR");
		
		runTest(bifid1, message1);
		runTest(bifid2, message2);
		runTest(bifid2, message1);
		runTest(bifid1, message2);
		
		Bifid bifid3 = new Bifid(6, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
		runTest(bifid3, message3);
	}	
	
	private static void runTest(Bifid aBifid, String aMessage) {
		System.out.println("Using Polybius square:");
		aBifid.display();
		System.out.println("Message:   " + aMessage);
		String encrypted = aBifid.encrypt(aMessage);
		System.out.println("Encrypted: " + encrypted);
		String decrypted = aBifid.decrypt(encrypted);
		System.out.println("Decrypted: " + decrypted);
		System.out.println();
	}	
	
}
	
final class Bifid {
	
	public Bifid(int aN, String aText) {
		if ( aText.length() != aN * aN ) {
			throw new IllegalArgumentException("Incorrect length of text");
		}
		
		grid = new char[aN][aN];			
		int row = 0;
		int col = 0;		 
		for ( char ch : aText.toCharArray() ) {
		    grid[row][col] = ch;
		    coordinates.put(ch, new Point(row, col) );
		    col += 1;
		    if ( col == aN ) {
		    	col = 0;
		    	row += 1;
		     }
		 }
		
		 if ( aN == 5 ) {
		     coordinates.put('J', coordinates.get('I'));
		 }
	}	
	
	public String encrypt(String aText) {		 
		List<Integer> rowOne = new ArrayList<Integer>();
		List<Integer> rowTwo = new ArrayList<Integer>();
		for ( char ch : aText.toCharArray() ) {
			Point coordinate = coordinates.get(ch);
			rowOne.add(coordinate.x);
			rowTwo.add(coordinate.y);
		 }
		  
		 rowOne.addAll(rowTwo);
		 StringBuilder result = new StringBuilder();
		 for ( int i = 0; i < rowOne.size() - 1; i += 2 ) {
			 result.append(grid[rowOne.get(i)][rowOne.get(i + 1)]);
		 }
		 return result.toString();
	}
	
	public String decrypt(String aText) {
		List<Integer> row = new ArrayList<Integer>();
		for ( char ch : aText.toCharArray() ) {
			Point coordinate = coordinates.get(ch);
		    row.add(coordinate.x);
		    row.add(coordinate.y);
		}
		
		final int middle = row.size() / 2;
		List<Integer> rowOne = row.subList(0, middle);
		List<Integer> rowTwo = row.subList(middle, row.size());
		StringBuilder result = new StringBuilder();
		for ( int i = 0; i < middle; i++ ) {
			result.append(grid[rowOne.get(i)][rowTwo.get(i)]);
		}
		return result.toString();
	}

    public void display() {
		Arrays.stream(grid).forEach( row -> System.out.println(Arrays.toString(row)) );
	}
	
	private char[][] grid;
	private Map<Character, Point> coordinates = new HashMap<Character, Point>();
	
}
Output:
Using Polybius square:
[A, B, C, D, E]
[F, G, H, I, K]
[L, M, N, O, P]
[Q, R, S, T, U]
[V, W, X, Y, Z]
Message:   ATTACKATDAWN
Encrypted: DQBDAXDQPDQH
Decrypted: ATTACKATDAWN

Using Polybius square:
[B, G, W, K, Z]
[Q, P, N, D, S]
[I, O, A, X, E]
[F, C, L, U, M]
[T, H, Y, V, R]
Message:   FLEEATONCE
Encrypted: UAEOLWRINS
Decrypted: FLEEATONCE

Using Polybius square:
[B, G, W, K, Z]
[Q, P, N, D, S]
[I, O, A, X, E]
[F, C, L, U, M]
[T, H, Y, V, R]
Message:   ATTACKATDAWN
Encrypted: EYFENGIWDILA
Decrypted: ATTACKATDAWN

Using Polybius square:
[A, B, C, D, E]
[F, G, H, I, K]
[L, M, N, O, P]
[Q, R, S, T, U]
[V, W, X, Y, Z]
Message:   FLEEATONCE
Encrypted: HADNAAZDSP
Decrypted: FLEEATONCE

Using Polybius square:
[A, B, C, D, E, F]
[G, H, I, J, K, L]
[M, N, O, P, Q, R]
[S, T, U, V, W, X]
[Y, Z, 0, 1, 2, 3]
[4, 5, 6, 7, 8, 9]
Message:   THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY
Encrypted: TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY

jq

Adapted from Wren

Works with: jq

Also works with gojq, the Go implementation of jq.

Preliminaries

# _nwise is for gojq
def _nwise($n):
  def n: if length <= $n then . else .[0:$n] , (.[$n:] | n) end;
  n;

# Input: a string
# Output: a stream of strings
def chars: explode[] | [.] | implode;

Bifid

# Input: the message to be encrypted
def encrypt($polybius):
  (ascii_upcase | gsub("J"; "I") ) as $m
  | {rows: [], cols : [] }
  | reduce ($m|chars) as $c (.;
      ($polybius|index($c)) as $ix
      | if $ix
        then .rows += [($ix/5)|floor + 1]
        | .cols += [($ix%5) + 1]
        else .
        end )
  | reduce (.rows + .cols | _nwise(2)) as $pair ("";
      (($pair[0] - 1) * 5 + $pair[1] - 1) as $ix
      | . + $polybius[$ix:$ix+1] ) ;

# Input: the message to be decrypted
def decrypt($polybius):
  reduce chars as $c ( {rows: [], cols : [] };
       ($polybius|index($c)) as $ix
       | .rows += [($ix/5)|floor + 1]
       | .cols += [($ix%5) + 1] )
  | ([.rows, .cols] | transpose | flatten) as $lines
  | ($lines|length/2) as $count
  | $lines[:$count] as $rows
  | $lines[$count:] as $cols
  | [$rows, $cols] as $d
  | reduce range(0; $count) as $i ("";
       (($rows[$i] - 1) * 5 + $cols[$i] - 1) as $ix
       | . + $polybius[$ix:$ix+1] ) ;

def polys:
  def p1: "ABCDEFGHIKLMNOPQRSTUVWXYZ";
  def p2: "BGWKZQPNDSIOAXEFCLUMTHYVR";
  def p3: "PLAYFIREXMBCDGHKNOQSTUVWZ";
  [p1, p2, p2, p3];

def messages: [
  "ATTACKATDAWN",
  "FLEEATONCE",
  "ATTACKATDAWN",
  "The invasion will start on the first of January"
  ];

def task:
  range(0; messages|length) as $i
  |  messages[$i]
  | encrypt(polys[$i]) as $encrypted
  | ($encrypted | decrypt(polys[$i] )) as $decrypted
  | "Message   : \(.)",
    "Encrypted : \($encrypted)",
    "Decrypted : \($decrypted)"
    "" ;

task
Output:

Same as for #Wren.

Julia

Using the Raku example's test messages.

polybius(text) = Char.(reshape(Int.(collect(text)), isqrt(length(text)), :)')

function encrypt(message, poly)
    positions = [findall(==(c), poly)[1] for c in message]
    numbers = vcat([c[1] for c in positions], [c[2] for c in positions])
    return String([poly[numbers[i], numbers[i+1]] for i in 1:2:length(numbers)-1])
end

function decrypt(message, poly)
   n = length(message)
   positions = [findall(==(c), poly)[1] for c in message]
   numbers = reduce(vcat, [[c[1], c[2]] for c in positions])
   return String([poly[numbers[i], numbers[i+n]] for i in 1:n])
end


for (key, text) in [("ABCDEFGHIKLMNOPQRSTUVWXYZ", "ATTACKATDAWN"), ("BGWKZQPNDSIOAXEFCLUMTHYVR", "FLEEATONCE"),
   ([' '; '.'; 'A':'Z'; 'a':'z'; '0':'9'], "The invasion will start on the first of January 2023.")]
    poly = polybius(key)
    encrypted = encrypt(text, poly)
    decrypted = decrypt(encrypted, poly)
    println("Using polybius:")
    display(poly)
    println("\n  Message: $text\n    Encrypted: $encrypted\n    Decrypted: $decrypted\n\n")
end
Output:
Using polybius:
5×5 Matrix{Char}:
 'A'  'B'  'C'  'D'  'E'
 'F'  'G'  'H'  'I'  'K'
 'L'  'M'  'N'  'O'  'P'
 'Q'  'R'  'S'  'T'  'U'
 'V'  'W'  'X'  'Y'  'Z'

  Message: ATTACKATDAWN
    Encrypted: DQBDAXDQPDQH
    Decrypted: ATTACKATDAWN


Using polybius:
5×5 Matrix{Char}:
 'B'  'G'  'W'  'K'  'Z'
 'Q'  'P'  'N'  'D'  'S'
 'I'  'O'  'A'  'X'  'E'
 'F'  'C'  'L'  'U'  'M'
 'T'  'H'  'Y'  'V'  'R'

  Message: FLEEATONCE
    Encrypted: UAEOLWRINS
    Decrypted: FLEEATONCE


Using polybius:
8×8 Matrix{Char}:
 ' '  '.'  'A'  'B'  'C'  'D'  'E'  'F'
 'G'  'H'  'I'  'J'  'K'  'L'  'M'  'N'
 'O'  'P'  'Q'  'R'  'S'  'T'  'U'  'V'
 'W'  'X'  'Y'  'Z'  'a'  'b'  'c'  'd'
 'e'  'f'  'g'  'h'  'i'  'j'  'k'  'l'
 'm'  'n'  'o'  'p'  'q'  'r'  's'  't'
 'u'  'v'  'w'  'x'  'y'  'z'  '0'  '1'
 '2'  '3'  '4'  '5'  '6'  '7'  '8'  '9'

  Message: The invasion will start on the first of January 2023.
    Encrypted: SejxqrEierbmrDiCjrDeJsbu89DWCHkgGS9E6tAG5 Ks2PBfCq uH
    Decrypted: The invasion will start on the first of January 2023.

Kotlin

import kotlin.math.sqrt

data class Square(private val square: String) {
    private val dim: Int = 
        sqrt(square.length.toDouble()).toInt()
    fun encode(ch: Char): Pair<Int, Int> = 
        square.indexOf(ch).let { idx -> Pair(idx / dim, idx.mod(dim)) }
    fun decode(pair: List<Int>): Char = 
        square[pair[0] * dim + pair[1]]
    fun decode(row: Int, col: Int): Char = 
        square[row * dim + col]
}

class Bifid(private val square: Square) {
    fun encrypt(str: String): String {
        fun expandAndScatter(str: String): IntArray {
            val buffer = IntArray(str.length * 2)
            str.forEachIndexed { i, ch ->
                with(square.encode(ch)) {
                    buffer[i] = first
                    buffer[str.length + i] = second
                }
            }
            return buffer
        }

        val buffer = expandAndScatter(str)

        val characters: List<Char> = buffer.asIterable()
            .windowed(size = 2, step = 2)
            .map { square.decode(it) }

        return String(characters.toCharArray())
    }

    fun decrypt(str: String): String {
        fun expand(str: String): IntArray {
            val buffer = IntArray(str.length * 2)
            for (i in buffer.indices step 2) {
                with(square.encode(str[i / 2])) {
                    buffer[i] = first
                    buffer[1 + i] = second
                }
            }
            return buffer
        }

        val buffer = expand(str)

        val characters = str.toCharArray()
        for (i in characters.indices) {
            characters[i] = square.decode(buffer[i], buffer[characters.size + i])
        }
        return String(characters)
    }
}

fun main() {
    with (Bifid(Square("ABCDEFGHIKLMNOPQRSTUVWXYZ"))) {
        println("\n### ABC... 5x5")
        encrypt("ATTACKATDAWN").also { println("ATTACKATDAWN -> $it") }
        decrypt("DQBDAXDQPDQH").also { println("DQBDAXDQPDQH -> $it") }
    }
    with(Bifid(Square("BGWKZQPNDSIOAXEFCLUMTHYVR"))) {
        println("\n### BGW... 5x5")
        encrypt("FLEEATONCE").also { println("FLEEATONCE -> $it") }
        decrypt("UAEOLWRINS").also { println("UAEOLWRINS -> $it") }

        encrypt("ATTACKATDAWN").also { println("ATTACKATDAWN -> $it") }
        decrypt("EYFENGIWDILA").also { println("EYFENGIWDILA -> $it") }
    }
    with(Bifid(Square("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))) {
        println("\n### ABC... 6x6")
        encrypt("THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY").also { println("$it") }
        decrypt("TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4").also { println("$it") }
    }
}
Output:
### ABC... 5x5
ATTACKATDAWN -> DQBDAXDQPDQH
DQBDAXDQPDQH -> ATTACKATDAWN

### BGW... 5x5
FLEEATONCE -> UAEOLWRINS
UAEOLWRINS -> FLEEATONCE
ATTACKATDAWN -> EYFENGIWDILA
EYFENGIWDILA -> ATTACKATDAWN

### ABC... 6x6
TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4
THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY

Lua

The "In addition" task and the bonus task are both solved using a 6x6 Polybius square containing every captial letter and all ten digits.

I refer to this combined solution as 'Task 4'.

function transcipher (cipher, message, decipher)
    local message = message:gsub("%s+", ""):upper()
    local xStr, yStr, s, char = "", "", ""
    for pos = 1, #message do
        char = message:sub(pos, pos)
        for x = 1, #cipher do
            for y = 1, #cipher[x] do
                if cipher[x][y] == char then
                    s = s .. x .. y
                    xStr = xStr .. x
                    yStr = yStr .. y
                end
            end
        end
    end
    if decipher then
        xStr, yStr = s:sub(1, #s/2), s:sub(#s/2 + 1, #s)
    else
        s = xStr .. yStr
    end
    local result, x, y = ""
    local limit = decipher and #s/2 or #s
    local step = decipher and 1 or 2
    for pos = 1, limit, step do
        x = tonumber(s:sub(pos, pos))
        y = decipher and
            tonumber(s:sub(pos + #s/2, pos + #s/2)) or
            tonumber(s:sub(pos + 1, pos + 1))
        result = result .. cipher[x][y]
    end
    return result
end

local RCbifid = {
    {"A", "B", "C", "D", "E"},
    {"F", "G", "H", "I", "K"},
    {"L", "M", "N", "O", "P"},
    {"Q", "R", "S", "T", "U"},
    {"V", "W", "X", "Y", "Z"}
}

local wikibifid = {
    {"B", "G", "W", "K", "Z"},
    {"Q", "P", "N", "D", "S"},
    {"I", "O", "A", "X", "E"},
    {"F", "C", "L", "U", "M"},
    {"T", "H", "Y", "V", "R"}
}

local mybifid = {
    {"A", "B", "C", "D", "E", "F"},
    {"G", "H", "I", "J", "K", "L"},
    {"M", "N", "O", "P", "Q", "R"},
    {"S", "T", "U", "V", "W", "X"},
    {"Y", "Z", "1", "2", "3", "4"},
    {"5", "6", "7", "8", "9", "0"}
}

local testCases = {
    {RCbifid, "ATTACKATDAWN"},
    {wikibifid, "FLEEATONCE"},
    {wikibifid, "ATTACKATDAWN",},
    {mybifid, "The invasion will start on the first of January"}
}

local msg
for task, case in pairs(testCases) do
    print("\nTask " .. task)
    msg = transcipher(case[1], case[2])
    print("Encoded message: " .. msg)
    msg = transcipher(case[1], msg, true)
    print("Decoded message: " .. msg)
end
Output:
Task 1
Encoded message: DQBDAXDQPDQH
Decoded message: ATTACKATDAWN

Task 2
Encoded message: UAEOLWRINS
Decoded message: FLEEATONCE

Task 3
Encoded message: EYFENGIWDILA
Decoded message: ATTACKATDAWN

Task 4
Encoded message: TBPDIPHJSPOTAIVMGPCZKNSCN10BFIHK75I8BM5
Decoded message: THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY

Nim

To add the letter J, we use a 6x6 Polybius square containing the 26 letters and the 10 digits. To build the square, we use a generic type parameterized with the side length.

import std/[sequtils, strutils, tables]

# Description of a Bifid cipher.
type Bifid[N: static Positive] = object
  grid: array[1..N, array[1..N, char]]
  coords: Table[char, (int, int)]

proc initBifid(N: static Positive; text: string): Bifid[N] =
  # Initialize a Bifid cipher.
  assert text.len == N * N
  var row, col = 1
  for c in text:
    result.grid[row][col] = c
    result.coords[c] = (row, col)
    inc col
    if col > N:
      col = 1
      inc row
  if N == 5:
    result.coords['J'] = result.coords['I']

func encrypt(bifid: Bifid; text: string): string =
  ## Encrypt "text" using the given cipher.
  var row1, row2: seq[int]
  for ch in text:
    let coords = bifid.coords[ch]
    row1.add coords[0]
    row2.add coords[1]
  let row = row1 & row2
  for i in countup(0, row.high, 2):
    result.add bifid.grid[row[i]][row[i+1]]

func decrypt(bifid: Bifid; text: string): string =
  ## Decrypt "text" using the given cipher.
  var row: seq[int]
  for ch in text:
    let coords = bifid.coords[ch]
    row.add [coords[0],  coords[1]]
  let m = row.len shr 1
  let row1 = row[0..<m]
  let row2 = row[m..^1]
  for i in 0..<m:
    result.add bifid.grid[row1[i]][row2[i]]

func `$`(bifid: Bifid): string =
  ## Display the Polybius square of a Bifid cipher.
  result = "  " & toSeq(1..bifid.N).join(" ") & '\n'
  for row in 1..bifid.N:
    result.add alignLeft($row, 2) & bifid.grid[row].join(" ") & '\n'

proc runTest(bifid: Bifid; message: string) =
  ## Run the test with given cipher and message.
  echo "Using Polybius square:"
  echo bifid
  echo "Message:   ", message
  let encrypted = bifid.encrypt(message)
  echo "Encrypted: ", encrypted
  let decrypted = bifid.decrypt(encrypted)
  echo "Decrypted: ", decrypted
  echo("\n─────────────────────────")

const
  Message1 = "ATTACKATDAWN"
  Message2 = "FLEEATONCE"
  Message3 = "The invasion will start on the first of January".toUpperAscii.replace(" ")

# Using 5x5 Polybius squares.
const
  Bifid1 = initBifid(5, "ABCDEFGHIKLMNOPQRSTUVWXYZ")
  Bifid2 = initBifid(5, "BGWKZQPNDSIOAXEFCLUMTHYVR")
for (bifid, message) in [(Bifid1, Message1), (Bifid2, Message2),
                         (Bifid2, Message1), (Bifid1, Message3)]:
  runTest(bifid, message)

# Using a 6x6 Polybius square with 26 letters and 10 digits.
const Bifid3 = initBifid(6, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
runTest(Bifid3, Message3)
Output:
Using Polybius square:
  1 2 3 4 5
1 A B C D E
2 F G H I K
3 L M N O P
4 Q R S T U
5 V W X Y Z

Message:   ATTACKATDAWN
Encrypted: DQBDAXDQPDQH
Decrypted: ATTACKATDAWN

─────────────────────────
Using Polybius square:
  1 2 3 4 5
1 B G W K Z
2 Q P N D S
3 I O A X E
4 F C L U M
5 T H Y V R

Message:   FLEEATONCE
Encrypted: UAEOLWRINS
Decrypted: FLEEATONCE

─────────────────────────
Using Polybius square:
  1 2 3 4 5
1 B G W K Z
2 Q P N D S
3 I O A X E
4 F C L U M
5 T H Y V R

Message:   ATTACKATDAWN
Encrypted: EYFENGIWDILA
Decrypted: ATTACKATDAWN

─────────────────────────
Using Polybius square:
  1 2 3 4 5
1 A B C D E
2 F G H I K
3 L M N O P
4 Q R S T U
5 V W X Y Z

Message:   THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY
Encrypted: RBPDHPHOQTNRBITMFODYPSAOSIAOBTOPDHTDCVI
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY

─────────────────────────
Using Polybius square:
  1 2 3 4 5 6
1 A B C D E F
2 G H I J K L
3 M N O P Q R
4 S T U V W X
5 Y Z 0 1 2 3
6 4 5 6 7 8 9

Message:   THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY
Encrypted: TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY

─────────────────────────

PascalABC.NET

Translation of: F#
// Bifid cipher. Nigel Galloway: September 18th., 2024
function polybius(alphabet:string;n,g:integer):(Dictionary<char,(integer,integer)>,Dictionary<(integer,integer),char>);
begin
  var ng:=Cartesian(1..n,1..g);
  Result:=(Dict(Zip(alphabet,ng)),Dict(Zip(ng,alphabet)));
end;
function fN(e:Dictionary<char,(integer,integer)>;n:string):(sequence of integer,sequence of integer):=(n.Substring(1).Scan(e[n[1]],(n,g)->e[g])).UnZipTuple;
function encrypt(e:(Dictionary<char,(integer,integer)>,Dictionary<(integer,integer),char>);text:string):string;
begin
  var fG:((sequence of integer,sequence of integer),Dictionary<(integer,integer),char>)->string:=(n,e)->(n[0]+n[1]).Batch(2).Aggregate('',(n,g)->n+e[(g[0],g[1])]);
  Result:=fG(fN(e[0],text),e[1]);
end;
function decrypt(e:(Dictionary<char,(integer,integer)>,Dictionary<(integer,integer),char>);text:string):string;
begin
  var fG:(sequence of (integer,integer),Dictionary<(integer,integer),char>)->string:=(n,e)->n.Aggregate('',(n,g)->n+e[(g[0],g[1])]);
  var t:=fN(e[0],text); var z:=t[0].Interleave(t[1]).SplitAt(t[0].count);
  Result:=fG(z[0].ZipTuple(z[1]),e[1]);
end;
begin
  var p:=polybius('abcdefghiklmnopqrstuvwxyz',5,5); var s,e:string;
  s:='fleeatonce'; e:=encrypt(p,s); println($'{s} -> {e} -> {decrypt(p,e)}');
  s:='attackatdawn'; e:=encrypt(p,s); println($'{s} -> {e} -> {decrypt(p,e)}');
  p:=polybius('bgwkzqpndsioaxefclumthyvr',5,5);
  e:=encrypt(p,s); println($'{s} -> {e} -> {decrypt(p,e)}');
  s:='fleeatonce'; e:=encrypt(p,s); println($'{s} -> {e} -> {decrypt(p,e)}');
  p:=polybius('abcdefghijklmnopqrstuvwxyz0123456789',6,6);
  s:='theinvasionwillstarton1january2025'; e:=encrypt(p,s); println($'{s} -> {e} -> {decrypt(p,e)}');
end.
Output:
fleeatonce -> hadnaazdsp -> fleeatonce
attackatdawn -> dqbdaxdqpdqh -> attackatdawn
attackatdawn -> eyfengiwdila -> attackatdawn
fleeatonce -> uaeolwrins -> fleeatonce
theinvasionwillstarton1january2025 -> tbpdiphjspozcsq23h0jaokr4g5nvbm40z -> theinvasionwillstarton1january2025

Perl

use v5.36;
use builtin <indexed floor>;
use experimental qw(builtin for_list);
use List::Util 'max';

sub table ($c, @V) { my $t = $c * (my $w = 2 + length max map { length } @V); ( sprintf( ('%'.$w.'s')x@V, @V) ) =~ s/.{1,$t}\K/\n/gr }

sub polybius ($text) {
    my %p;
    my $n = floor sqrt length $text;
    for my($k,$v) (indexed split '', $text) {
        $p{$v} = join ' ', $k%$n, int $k/$n
    }
    %p;
}

sub encrypt ($text, %P) {
    my(%I, @c, $encrypted);
    for my($k,$v) (%P) { $I{$v} = $k }
    for my ($n,$char) (indexed split '', ($text =~ s/\s//gr)) {
        for my($m,$i) (indexed split ' ', $P{$char}) { $c[$m][$n] = $i }
    }
    for my($i,$j) ($c[1]->@*, $c[0]->@*) { $encrypted .= $I{"$j $i"} }
    $encrypted
}

sub decrypt ($text, %P) {
    my($decrypted, $l, %I, @c) = ('', length($text));
    for my($k,$v) (%P) { $I{$v} = $k }
    for (split '', $text) {
        for my($i,$j) (split ' ', $P{$_}) { unshift @c, $i, $j }
    }
    substr $decrypted, 0, 0, $I{ "$c[$_] $c[$_+$l]" } for 0 .. $l-1;
    $decrypted;
}

for my($polybius,$message) (
  join('','A'..'Z') =~ s/J//r,                 'ATTACK AT DAWN',
  'BGWKZQPNDSIOAXEFCLUMTHYVR',                 'FLEE AT ONCE',
  join('','_.', 'A'..'Z', 'a'..'z', '0'..'9'), 'The_invasion_will_start_on_the_first_of_January_2023.',
  ) {
    my %Ptable = polybius $polybius;
    say "\nUsing polybius:\n" . table sqrt length $polybius, split '', $polybius;
    say 'Message   : ' .  $message;
    say 'Encrypted : ' .  (my $encrypted = encrypt $message, %Ptable);
    say 'Decrypted : ' .  decrypt $encrypted, %Ptable;
}
Output:
Using polybius:
  A  B  C  D  E
  F  G  H  I  K
  L  M  N  O  P
  Q  R  S  T  U
  V  W  X  Y  Z

Message   : ATTACK AT DAWN
Encrypted : DQBDAXDQPDQH
Decrypted : ATTACKATDAWN

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

Message   : FLEE AT ONCE
Encrypted : UAEOLWRINS
Decrypted : FLEEATONCE

Using polybius:
  _  .  A  B  C  D  E  F
  G  H  I  J  K  L  M  N
  O  P  Q  R  S  T  U  V
  W  X  Y  Z  a  b  c  d
  e  f  g  h  i  j  k  l
  m  n  o  p  q  r  s  t
  u  v  w  x  y  z  0  1
  2  3  4  5  6  7  8  9

Message   : The_invasion_will_start_on_the_first_of_January_2023.
Encrypted : SejxqrEierbmrDiCjrDeJsbu89DWCHkgGS9E6tAG5_Ks2PBfCq_uH
Decrypted : The_invasion_will_start_on_the_first_of_January_2023.

Phix

with javascript_semantics
enum encrypt,decrypt
function bifid(string msg, polybius, sequence p, integer n, ed)
    string res = ""
    sequence dx = {}, dy = {}, dxy
    for i=1 to length(msg) do
        integer k = find(msg[i],polybius)-1
        dx &= floor(k/n)+1
        dy &= remainder(k,n)+1
    end for
    if ed=encrypt then
        dxy = split_by(dx&dy,2)
    else
        -- "simply reversing these steps" - yeah, right...
        dxy = columnize(split_by(flatten(columnize({dx,dy})),length(dx)))
    end if
    for i=1 to length(dxy) do
        integer {x,y} = dxy[i]
        res &= p[x][y]
    end for
    return res
end function

procedure test(string msg, polybius)
    integer n = sqrt(length(polybius))
    if n=5 then msg = substitute(upper(msg),"J","I") end if
    sequence p = split_by(polybius,n)
    string enc = bifid(msg,polybius,p,n,encrypt),
           dec = bifid(enc,polybius,p,n,decrypt)
    printf(1,"For %dx%d polybius %s\n\n",{n,n,join(p,"\n                 ")})
    printf(1,"Message  : %s\nEncrypted: %s\nDecrypted: %s\n\n",{msg,enc,dec})
end procedure
    
constant polybii = {tagstart('A','J'-'A')&tagstart('K','Z'-'J'),
                    "BGWKZQPNDSIOAXEFCLUMTHYVR",
                    " ."&tagstart('A',26)&tagstart('a',26)&tagstart('0',10)},
         messages = {"ATTACKATDAWN","FLEEATONCE",
                     "The invasion will start on the first of January 2023."}
for t=1 to 3 do test(messages[t],polybii[t]) end for
Output:
For 5x5 polybius ABCDE
                 FGHIK
                 LMNOP
                 QRSTU
                 VWXYZ

Message  : ATTACKATDAWN
Encrypted: DQBDAXDQPDQH
Decrypted: ATTACKATDAWN

For 5x5 polybius BGWKZ
                 QPNDS
                 IOAXE
                 FCLUM
                 THYVR

Message  : FLEEATONCE
Encrypted: UAEOLWRINS
Decrypted: FLEEATONCE

For 8x8 polybius  .ABCDEF
                 GHIJKLMN
                 OPQRSTUV
                 WXYZabcd
                 efghijkl
                 mnopqrst
                 uvwxyz01
                 23456789

Message  : The invasion will start on the first of January 2023.
Encrypted: SejxqrEierbmrDiCjrDeJsbu89DWCHkgGS9E6tAG5 Ks2PBfCq uH
Decrypted: The invasion will start on the first of January 2023.

Python

"""Bifid cipher. Requires Python >=3.7."""
import math
import pprint
import string

from itertools import chain
from itertools import zip_longest

from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import Tuple
from typing import TypeVar


T = TypeVar("T")


def group(it: Iterable[T], n: int) -> Iterator[Tuple[T, ...]]:
    """Return the input iterable split in to `n` equal chunks, padded with `None`."""
    return zip_longest(*[iter(it)] * n)


Square = Tuple[Tuple[str, ...], ...]


def polybius_square(alphabet: str) -> Square:
    """Return the given alphabet as a tuple of tuples, representing a Polybius square."""
    return tuple(group(alphabet, math.ceil(math.sqrt(len(alphabet)))))


def polybius_map(square: Square) -> Dict[str, Tuple[int, int]]:
    """Return a reverse lookup for the given Polybius square."""
    return {
        square[i][j]: (i + 1, j + 1)
        for i in range(len(square))
        for j in range(len(square))
    }


def encrypt(message: str, square: Square) -> str:
    """Encrypt a plaintext message using a bifid cipher with the given Polybius square."""
    _map = polybius_map(square)
    return "".join(
        square[x - 1][y - 1]
        for x, y in group(
            chain.from_iterable(zip(*(_map[c] for c in message if c in _map))),
            2,
        )
    )


def decrypt(message: str, square: Square) -> str:
    """Decrypt a ciphertext message using a bifid cipher with the given Polybius square."""
    _map = polybius_map(square)
    return "".join(
        square[x - 1][y - 1]
        for x, y in zip(
            *group(
                chain.from_iterable((_map[c] for c in message if c in _map)),
                len(message),
            )
        )
    )


def normalize(message: str) -> str:
    """Normalize a message for the typical Polybius square."""
    return message.upper().replace("J", "I")


TYPICAL_POLYBIUS_SQUARE = polybius_square(
    alphabet="".join(c for c in string.ascii_uppercase if c != "J")
)


EXAMPLE_POLYBIUS_SQUARE = polybius_square(
    alphabet="BGWKZQPNDSIOAXEFCLUMTHYVR",
)


def main() -> None:
    test_cases = [
        ("ATTACKATDAWN", TYPICAL_POLYBIUS_SQUARE),  # 1
        ("FLEEATONCE", EXAMPLE_POLYBIUS_SQUARE),  # 2
        ("FLEEATONCE", TYPICAL_POLYBIUS_SQUARE),  # 3
        (
            normalize("The invasion will start on the first of January"),
            polybius_square(alphabet="PLAYFIREXMBCDGHKNOQSTUVWZ"),
        ),
        (
            "The invasion will start on the first of January".upper(),
            polybius_square(alphabet=string.ascii_uppercase + string.digits),
        ),
    ]

    for message, square in test_cases:
        pprint.pprint(square)
        print("Message  :", message)
        print("Encrypted:", encrypt(message, square))
        print("Decrypted:", decrypt(encrypt(message, square), square))
        print("")


if __name__ == "__main__":
    main()
Output:
(('A', 'B', 'C', 'D', 'E'),
 ('F', 'G', 'H', 'I', 'K'),
 ('L', 'M', 'N', 'O', 'P'),
 ('Q', 'R', 'S', 'T', 'U'),
 ('V', 'W', 'X', 'Y', 'Z'))
Message  : ATTACKATDAWN
Encrypted: DQBDAXDQPDQH
Decrypted: ATTACKATDAWN

(('B', 'G', 'W', 'K', 'Z'),
 ('Q', 'P', 'N', 'D', 'S'),
 ('I', 'O', 'A', 'X', 'E'),
 ('F', 'C', 'L', 'U', 'M'),
 ('T', 'H', 'Y', 'V', 'R'))
Message  : FLEEATONCE
Encrypted: UAEOLWRINS
Decrypted: FLEEATONCE

(('A', 'B', 'C', 'D', 'E'),
 ('F', 'G', 'H', 'I', 'K'),
 ('L', 'M', 'N', 'O', 'P'),
 ('Q', 'R', 'S', 'T', 'U'),
 ('V', 'W', 'X', 'Y', 'Z'))
Message  : FLEEATONCE
Encrypted: HADNAAZDSP
Decrypted: FLEEATONCE

(('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'))
Message  : THE INVASION WILL START ON THE FIRST OF IANUARY
Encrypted: VRSYXSIYTMQVIRSKISLPVLDTCKRTCAIVTMATCEX
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY

(('A', 'B', 'C', 'D', 'E', 'F'),
 ('G', 'H', 'I', 'J', 'K', 'L'),
 ('M', 'N', 'O', 'P', 'Q', 'R'),
 ('S', 'T', 'U', 'V', 'W', 'X'),
 ('Y', 'Z', '0', '1', '2', '3'),
 ('4', '5', '6', '7', '8', '9'))
Message  : THE INVASION WILL START ON THE FIRST OF JANUARY
Encrypted: TBPDIPHJSPOTAIVMGPCZKNSCN09BFIHK64I7BM4
Decrypted: THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY

Quackery

transpose is defined at Matrix transposition#Quackery.

  [ $ "" swap
    witheach
      [ upper
        dup char I > if [ 1 - ]
        dup char A char Z
        within iff
          [ char A - join ]
        else drop ] ]               is ->0..24    (         $ --> [ )

  [ $ "" swap
    witheach
      [ char A +
        dup char I > if 1+
       join ] ]                     is ->A..Z     (         [ --> $ )

  [ [] 5 times
     [ dip ->0..24 join ] ]         is makesquare ( $ $ $ $ $ --> [ )

  [ dup witheach
      [ i^ unrot poke ] ]           is makeindex  (         [ --> [ )

  [ dup temp put
    makeindex temp put
    ->0..24
    [] swap witheach
      [ temp share swap peek
        5 /mod join
        nested join ]
    temp release
    transpose
    unpack join
    [] swap
    dup size 2 / times
      [ 2 split dip
          [ nested join ] ]
    drop
    $ "" swap
    witheach
      [ unpack swap 5 * +
        temp share swap peek
        join ]
    ->A..Z
    temp release ]                  is encrypt    (       $ [ --> $ )

  [ dup temp put
    makeindex temp put
    ->0..24
    [] swap witheach
      [ temp share swap peek
        5 /mod join join ]
    temp release
    dup size 2 / split
    2 pack
    transpose
    [] swap witheach
      [ unpack swap 5 * +
        temp share swap peek
        join ]
     ->A..Z
    temp release ]                  is decrypt    (       $ [ --> $ )

  [ $ "ABCDE"
    $ "FGHIK"
    $ "LMNOP"
    $ "QRSTU"
    $ "VWXYZ"
    makesquare ]           constant is tasksquare (           --> [ )

  [ $ "BGWKZ"
    $ "QPNDS"
    $ "IOAXE"
    $ "FCLUM"
    $ "THYVR"
    makesquare ]           constant is wikisquare (           --> [ )

  [ $ "QUACK"
    $ "DEPTH"
    $ "LYING"
    $ "FORMS"
    $ "BVWXZ"
    makesquare ]           constant is ducksquare (           --> [ )


  say "Using tasksquare:" cr
  $ "Attack at dawn."                                  dup echo$ say " -> "
  tasksquare encrypt                                   dup echo$ say " -> "
  tasksquare decrypt                                       echo$
  cr cr
  say "Using wikisquare:" cr
  $ "Flee at once."                                    dup echo$ say " -> "
  wikisquare encrypt                                   dup echo$ say " -> "
  wikisquare decrypt                                       echo$
  cr cr
  say "Using ducksquare:" cr
  $ "The invasion will start on the first of January." dup echo$ cr say " -> "
  ducksquare encrypt                                   dup echo$ cr say " -> "
  ducksquare decrypt                                       echo$
Output:
Using tasksquare:
Attack at dawn. -> DQBDAXDQPDQH -> ATTACKATDAWN

Using wikisquare:
Flee at once. -> UAEOLWRINS -> FLEEATONCE

Using ducksquare:
The invasion will start on the first of January.
 -> EPGCNGINDORETNOMLLCNVNPWTIQXIOMVAGOANPY
 -> THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY

Raku

Technically incorrect as the third part doesn't "Convert ... to upper case and ignore spaces".

sub polybius ($text) {
    my $n = $text.chars.sqrt.narrow;
    $text.comb.kv.map: { $^v => ($^k % $n, $k div $n).join: ' ' }
}

sub encrypt ($message, %poly) {
    %poly.invert.hash{(flat reverse [Z] %poly{$message.comb}».words).batch(2)».reverse».join: ' '}.join
}

sub decrypt ($message, %poly) {
   %poly.invert.hash{reverse [Z] (reverse flat %poly{$message.comb}».words».reverse).batch($message.chars)}.join
}


for 'ABCDEFGHIKLMNOPQRSTUVWXYZ', 'ATTACKATDAWN',
    'BGWKZQPNDSIOAXEFCLUMTHYVR', 'FLEEATONCE',
    (flat '_', '.', 'A'..'Z', 'a'..'z', 0..9).pick(*).join, 'The invasion will start on the first of January 2023.'.subst(/' '/, '_', :g)
 -> $polybius, $message {
    my %polybius = polybius $polybius;
    say "\nUsing polybius:\n\t" ~ $polybius.comb.batch($polybius.chars.sqrt.narrow).join: "\n\t";
    say "\n  Message : $message";
    say "Encrypted : " ~ my $encrypted = encrypt $message, %polybius;
    say "Decrypted : " ~ decrypt $encrypted, %polybius;
}
Output:
Using polybius:
	A B C D E
	F G H I K
	L M N O P
	Q R S T U
	V W X Y Z

  Message : ATTACKATDAWN
Encrypted : DQBDAXDQPDQH
Decrypted : ATTACKATDAWN

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

  Message : FLEEATONCE
Encrypted : UAEOLWRINS
Decrypted : FLEEATONCE

Using polybius:
	H c F T N 5 f i
	_ U k R B Z V W
	3 G e v s w j x
	q S 2 8 y Q . O
	m 0 E d h D r u
	M p 7 Y 4 A 9 a
	t X l I 6 g b z
	J P n 1 K L C o

  Message : The_invasion_will_start_on_the_first_of_January_2023.
Encrypted : NGiw3okfXj4XoVE_NjWcLK4Sy28EivKo3aeNiti3N3z6HCHno6Fkf
Decrypted : The_invasion_will_start_on_the_first_of_January_2023.

RPL

« DUP SIZE √ "" → in key n out
 « { } DUP 
   1 in SIZE FOR j 
      key in j DUP SUB 
      POS 1 - n MOD LASTARG / IP 
      SWAP ROT SWAP + ROT ROT + SWAP
   NEXT
   + 'in' STO 
   1 in SIZE FOR j 
     'out' key 
     in j DUP 1 + SUB 
     OBJ→ DROP SWAP n * + 1 + 
     DUP SUB STO+
   2 STEP 
   out
» » '→BIFCOD' STO

« DUP SIZE √ "" → in key n out
 « { } 
   1 in SIZE FOR j 
      key in j DUP SUB POS
      1 - n MOD LASTARG / IP 
      ROT SWAP + SWAP +
   NEXT
   DUP 'in' STO SIZE 2 / 
   1 OVER FOR j 
      'out' key 
      in j GET n * in 5 PICK j + GET + 1 + 
      DUP SUB STO+
   NEXT
   DROP out
» » '→BIFDEC' STO

« { "ATTACKATDAWN" "ABCDEFGHIKLMNOPQRSTUVWXYZ" 
    "FLEEATONCE" "BGWKZQPNDSIOAXEFCLUMTHYVR"
    "ATTACKATDAWN" "BGWKZQPNDSIOAXEFCLUMTHYVR" 
    "THE INVASION WILL START ON THE FIRST OF JANUARY" "ABCDEFGHIJKLMNOPQRSTUVWXYZ 123456789" }
  → examples
 « 1 examples SIZE FOR j 
      examples j GETI ROT ROT GET
      →BIFCOD DUP examples j 1 + GET
      →BIFDEC 2 →LIST
   2 STEP
» » 'TASK' STO
Output:
4: { "DQBDAXDQPDQH" "ATTACKATDAWN" }
3: { "UAEOLWRINS" "FLEEATONCE" }
2: { "EYFENGIWDILA" "ATTACKATDAWN" } 
1: { "TEISTO1HKVCWO1GYIV EGPCZKOJAOI 9MG5OIH 64IRPBM4" "THE INVASION WILL START ON THE FIRST OF JANUARY" }

Ruby

def cleanMsg(msg, square)
  msg.upcase!
  msg.delete!(' ')
  msg.delete!('J') if square.index('J') == nil
end

def encrypt(msg, square)
  cleanMsg msg, square
  sq_size = (square.length ** 0.5).to_i
  rows = [0] * msg.length
  cols = [0] * msg.length
  (0...msg.length).each do |i|
    p = square.index(msg[i])
    rows[i], cols[i] = p / sq_size, p % sq_size
  end
  result = ""
  rows.concat(cols).each_slice(2) do |coord|
    result += square[coord[0]*sq_size + coord[1]]
  end
  return result
end

def decrypt(msg, square)
  msg.upcase!; square.upcase!
  sq_size = (square.length ** 0.5).to_i
  coords = []
  result = ""
  (0...msg.length).each do |i| 
    p = square.index(msg[i])
    coords << p / sq_size
    coords << p % sq_size
  end
  for i in (0...coords.length/2) do
    row, col = coords[i], coords[i+coords.length/2]
    result += square[row*sq_size + col]
  end
  return result 
end

def printSquare(square)
  sq_size = (square.length ** 0.5).to_i
  (0..square.length).step(sq_size).each do |i|
    print square[i...(i+sq_size)], "\n"
  end
end

tests = [["ATTACKATDAWN" , "ABCDEFGHIKLMNOPQRSTUVWXYZ"],
         ["FLEEATONCE"   , "BGWKZQPNDSIOAXEFCLUMTHYVR"],
         ["ATTACKATDAWN" , "ABCDEFGHIKLMNOPQRSTUVWXYZ"],
         ["the invasion will start on the first of january", "BGWKZQPNDSIOAXEFCLUMTHYVR"],
         ["THIS MESSAGE HAS NUMBERS 2023", "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"],
]

for test in tests
  message = test[0]; square = test[1];
  encrypted = encrypt(message, square)
  decrypted = decrypt(encrypted, square)

  puts "using the polybius:"
  printSquare(square)
  puts "the plain message:", message 
  puts "encrypted:", encrypted 
  puts "decrypted:", decrypted 
  puts "===================================================="
end
Output:
using the polybius:
ABCDE
FGHIK
LMNOP
QRSTU
VWXYZ

the plain message:
ATTACKATDAWN
encrypted:
DQBDAXDQPDQH
decrypted:
ATTACKATDAWN
====================================================
using the polybius:
BGWKZ
QPNDS
IOAXE
FCLUM
THYVR

the plain message:
FLEEATONCE
encrypted:
UAEOLWRINS
decrypted:
FLEEATONCE
====================================================
using the polybius:
ABCDE
FGHIK
LMNOP
QRSTU
VWXYZ

the plain message:
ATTACKATDAWN
encrypted:
DQBDAXDQPDQH
decrypted:
ATTACKATDAWN
====================================================
using the polybius:
BGWKZ
QPNDS
IOAXE
FCLUM
THYVR

the plain message:
THEINVASIONWILLSTARTONTHEFIRSTOFANUARY
encrypted:
RASOAQXCYRORXESXOLRGTXEGAWEWTNGTZTQALY
decrypted:
THEINVASIONWILLSTARTONTHEFIRSTOFANUARY
====================================================
using the polybius:
ABCDEF
GHIJKL
MNOPQR
STUVWX
YZ1234
567890

the plain message:
THISMESSAGEHASNUMBERS2023
encrypted:
TJMVBBDPMCW9ZIAYAEGBMK5XW
decrypted:
THISMESSAGEHASNUMBERS2023
====================================================

Uiua

Standard

The following just uses the standard version of the cipher.

S ← "ABCDEFGHIKLMNOPQRSTUVWXYZ"
P ← ↯⊟.√⧻.S
Prep ← ▽∊:S.∧⍜⊡⋅@I⊚⌕@J.⌵
Bifid ← setinv(
  ⊡:⟜(↯∞_2/⊂⇌⍉≡(⊢⊚⌕)Prep:¤)P
| ⊡:⟜(⍉⇌↯2_∞/⊂≡(⊢⊚⌕):¤)P
)
°Bifid.Bifid. "This is it"
°Bifid.Bifid. "The invasion will start on the first of January"
Output:
"This is it"
"SSSTRIII"
"THISISIT"
"The invasion will start on the first of January"
"SYLCTMQCQISSVROQQPBTFHVRNWNTDSOFGTSGCQU"
"THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY"

Extended

26 letters and 10 digits = 36 chars, so that's the obvious extension. Plus super-security is guaranteed by adding the use of a codeword in the grid  :-)

S ← "GLYPHABCDEFIJKMNOQRSTUVWXZ0123456789"
P ← ↯⊟.√⧻.S
Prep ← ▽∊:S.⌵
Bifid ← setinv(
  ⊡:⟜(↯∞_2/⊂⇌⍉≡(⊢⊚⌕)Prep:¤)P
| ⊡:⟜(⍉⇌↯2_∞/⊂≡(⊢⊚⌕):¤)P
)
°Bifid.Bifid. "This is it"
°Bifid.Bifid. "The invasion will start at 0600 on the 1st of January"
Output:
"This is it"
"O556REEE"
"THISISIT"
"The invasion will start at 0600 on the 1st of January"
"OWV58W5CQY6MM1OUD2AU4NLDRSMSGUPRV80NL1TDYRR"
"THEINVASIONWILLSTARTAT0600ONTHE1STOFJANUARY"

Wren

One way of enabling all 26 letters to be encrypted uniquely would be to use a 6 x 6 Polybius square including the 10 digits. We could then encrypt text using numerals as well.

However, the following just uses the standard version of the cipher.

import "./str" for Str
import "./seq" for Lst

class Bifid {
    static encrypt(polybius, message) {
        message = Str.upper(message).replace("J", "I")
        var rows = []
        var cols = []
        for (c in message) {
            var ix = polybius.indexOf(c)
            if (ix == -1) continue
            rows.add((ix/5).floor + 1)
            cols.add((ix%5) + 1)
        }
        var s = ""
        for (pair in Lst.chunks(rows + cols, 2)) {
            var ix = (pair[0] - 1) * 5 + pair[1] - 1
            s = s + polybius[ix]
        }
        return s
    }

    static decrypt(polybius, message) {
        var rows = []
        var cols = []
        for (c in message) {
            var ix = polybius.indexOf(c)
            rows.add((ix/5).floor + 1)
            cols.add((ix%5) + 1)
        }
        var lines = Lst.flatten(Lst.zip(rows, cols))
        var count = lines.count/2
        rows = lines[0...count]
        cols = lines[count..-1]
        var s = ""
        for (i in 0...count) {
            var ix = (rows[i] - 1) * 5 + cols[i] - 1
            s = s + polybius[ix]
        }
        return s
    }
}
var poly1 = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
var poly2 = "BGWKZQPNDSIOAXEFCLUMTHYVR"
var poly3 = "PLAYFIREXMBCDGHKNOQSTUVWZ"
var polys = [poly1, poly2, poly2, poly3]
var msg1  = "ATTACKATDAWN"
var msg2  = "FLEEATONCE"
var msg3  = "The invasion will start on the first of January"
var msgs  = [msg1, msg2, msg1, msg3]
for (i in 0...msgs.count) {
    var encrypted = Bifid.encrypt(polys[i], msgs[i])
    var decrypted = Bifid.decrypt(polys[i], encrypted)
    System.print("Message   : %(msgs[i])")
    System.print("Encrypted : %(encrypted)")
    System.print("Decrypted : %(decrypted)")
    if (i < msgs.count-1) System.print()
}
Output:
Message   : ATTACKATDAWN
Encrypted : DQBDAXDQPDQH
Decrypted : ATTACKATDAWN

Message   : FLEEATONCE
Encrypted : UAEOLWRINS
Decrypted : FLEEATONCE

Message   : ATTACKATDAWN
Encrypted : EYFENGIWDILA
Decrypted : ATTACKATDAWN

Message   : The invasion will start on the first of January
Encrypted : VRSYXSIYTMQVIRSKISLPVLDTCKRTCAIVTMATCEX
Decrypted : THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY

XPL0

Translation of: Wren
string 0;               \use zero-terminated strings
def  MaxLen = 100;      \more than length of longest message

func StrLen(A);         \Return number of characters in an ASCIIZ string
char A;
int  I;
for I:= 0 to MaxLen do
    if A(I) = 0 then return I;

proc StrUpper(S);       \In string S, convert lowercase characters to uppercase
char S;
int  I;
for I:= 0 to StrLen(S)-1 do
    if S(I)>=^a & S(I)<=^z then S(I):= S(I) & $DF;

proc StrReplace(S, C1, C2);     \In string S, convert character C1 to C2
char S, C1, C2;
int  I;
for I:= 0 to StrLen(S)-1 do
    if S(I) = C1 then S(I):= C2;

func IndexOf(S, C);     \Return offset of character C in string S
char S, C;
int  I;
[for I:= 0 to StrLen(S)-1 do
    if S(I) = C then return I;
return -1;
];

char Encrypted(MaxLen), Decrypted(MaxLen);

proc Encrypt(Polybius, Message);        \Copy encrypted Message into Encrypted
char Polybius, Message;
char Rows(MaxLen*2), Cols(MaxLen);
int  C, IX, I, J, Len;
[Len:= StrLen(Message);
for I:= 0 to Len do                     \copy Message into Encrypted
    Encrypted(I):= Message(I);
StrUpper(Encrypted);
StrReplace(Encrypted, ^J, ^I);
I:= 0;  J:= 0;
loop    [C:= Encrypted(I);
        I:= I+1;
        if C = 0 then quit;
        IX:= IndexOf(Polybius, C);
        if IX >= 0 then                 \ignores spaces, etc.
            [Rows(J):= IX/5;
            Cols(J):= rem(0);
            J:= J+1;
            ];
        ];
Len:= J;                                \length of encrypted message
for I:= 0 to Len-1 do                   \append Cols onto end of Rows
    Rows(I+Len):= Cols(I);
for I:= 0 to Len-1 do
    [IX:= Rows(I*2)*5 + Rows(I*2+1);
    Encrypted(I):= Polybius(IX);
    ];
Encrypted(I):= 0;                       \terminate string
];

proc Decrypt(Polybius, Message);        \Copy decrypted Message into Decrypted
char Polybius, Message;
char Rows(MaxLen*2), Cols(MaxLen);
int  C, IX, I, J, Len;
[I:= 0;  J:= 0;
loop    [C:= Message(I);
        if C = 0 then quit;
        I:= I+1;
        IX:= IndexOf(Polybius, C);
        Rows(J):= IX/5;
        Rows(J+1):= rem(0);
        J:= J+2;
        ];
Len:= I;
Cols:= Rows + Len;
for I:= 0 to Len-1 do
    [IX:= Rows(I)*5 + Cols(I);
    Decrypted(I):= Polybius(IX);
    ];
Decrypted(I):= 0;
];

int  Polys, Msgs, I;
[Polys:= ["ABCDEFGHIKLMNOPQRSTUVWXYZ",
          "BGWKZQPNDSIOAXEFCLUMTHYVR",
          "BGWKZQPNDSIOAXEFCLUMTHYVR",
          "PLAYFIREXMBCDGHKNOQSTUVWZ"];
Msgs:=   ["ATTACKATDAWN",
          "FLEEATONCE",
          "ATTACKATDAWN",
          "The invasion will start on the first of January"];
for I:= 0 to 4-1 do
    [Encrypt(Polys(I), Msgs(I));
     Decrypt(Polys(I), Encrypted);
    Text(0, "Message   : ");  Text(0, Msgs(I));    CrLf(0);
    Text(0, "Encrypted : ");  Text(0, Encrypted);  CrLf(0);
    Text(0, "Decrypted : ");  Text(0, Decrypted);  CrLf(0);
    if I < 4-1 then CrLf(0);
    ];
]
Output:
Message   : ATTACKATDAWN
Encrypted : DQBDAXDQPDQH
Decrypted : ATTACKATDAWN

Message   : FLEEATONCE
Encrypted : UAEOLWRINS
Decrypted : FLEEATONCE

Message   : ATTACKATDAWN
Encrypted : EYFENGIWDILA
Decrypted : ATTACKATDAWN

Message   : The invasion will start on the first of January
Encrypted : VRSYXSIYTMQVIRSKISLPVLDTCKRTCAIVTMATCEX
Decrypted : THEINVASIONWILLSTARTONTHEFIRSTOFIANUARY