https en wikipedia orgwikiplayfaircipherdescription the playfair cipher was developed by charles wheatstone in 1854 it s use was heavily promotedby lord playfair hence its name some features of the playfair cipher are 1 it was the first literal diagram substitution cipher 2 it is a manual symmetric encryption technique 3 it is a multiple letter encryption cipher the implementation in the code below encodes alphabets only it removes spaces special characters and numbers from the code playfair is no longer used by military forces because of known insecurities and of the advent of automated encryption devices this cipher is regarded as insecure since before world war i prepare the plaintext by upcasing it and separating repeated letters with x s i and j are used interchangeably to allow us to use a 5x5 table 25 letters we re using a list instead of a 2d array because it makes the math for setting up the table and doing the actual encodingdecoding simpler copy key chars into the table if they are in alphabet ignoring duplicates fill the rest of the table in with the remaining alphabet chars encode the given plaintext using the playfair cipher takes the plaintext and the key as input and returns the encoded string encodehello monarchy cfsupm encodeattack on the left flank emergency dqzsbyfsdzfmfnlohfdrsg encodesorry special avxetx encodenumber 1 number umbenf encodephotosynthesis the sun oemhqhvchesuke decode the input string using the provided key decodebmzfazrzdh hazard firehazard decodehnbwbpqt automobile drivingx decodeslyssaqs castle atxtackx prepare the plaintext by up casing it and separating repeated letters with x s i and j are used interchangeably to allow us to use a 5x5 table 25 letters we re using a list instead of a 2d array because it makes the math for setting up the table and doing the actual encoding decoding simpler copy key chars into the table if they are in alphabet ignoring duplicates fill the rest of the table in with the remaining alphabet chars encode the given plaintext using the playfair cipher takes the plaintext and the key as input and returns the encoded string encode hello monarchy cfsupm encode attack on the left flank emergency dqzsbyfsdzfmfnlohfdrsg encode sorry special avxetx encode number 1 number umbenf encode photosynthesis the sun oemhqhvchesuke rectangle decode the input string using the provided key decode bmzfazrzdh hazard firehazard decode hnbwbpqt automobile drivingx decode slyssaqs castle atxtackx rectangle
import itertools
import string
from collections.abc import Generator, Iterable


def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...], None, None]:
    it = iter(seq)
    while True:
        chunk = tuple(itertools.islice(it, size))
        if not chunk:
            return
        yield chunk


def prepare_input(dirty: str) -> str:
    

    dirty = "".join([c.upper() for c in dirty if c in string.ascii_letters])
    clean = ""

    if len(dirty) < 2:
        return dirty

    for i in range(len(dirty) - 1):
        clean += dirty[i]

        if dirty[i] == dirty[i + 1]:
            clean += "X"

    clean += dirty[-1]

    if len(clean) & 1:
        clean += "X"

    return clean


def generate_table(key: str) -> list[str]:
    
    
    alphabet = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
    
    
    table = []

    
    for char in key.upper():
        if char not in table and char in alphabet:
            table.append(char)

    
    for char in alphabet:
        if char not in table:
            table.append(char)

    return table


def encode(plaintext: str, key: str) -> str:
    

    table = generate_table(key)
    plaintext = prepare_input(plaintext)
    ciphertext = ""

    for char1, char2 in chunker(plaintext, 2):
        row1, col1 = divmod(table.index(char1), 5)
        row2, col2 = divmod(table.index(char2), 5)

        if row1 == row2:
            ciphertext += table[row1 * 5 + (col1 + 1) % 5]
            ciphertext += table[row2 * 5 + (col2 + 1) % 5]
        elif col1 == col2:
            ciphertext += table[((row1 + 1) % 5) * 5 + col1]
            ciphertext += table[((row2 + 1) % 5) * 5 + col2]
        else:  
            ciphertext += table[row1 * 5 + col2]
            ciphertext += table[row2 * 5 + col1]

    return ciphertext


def decode(ciphertext: str, key: str) -> str:
    

    table = generate_table(key)
    plaintext = ""

    for char1, char2 in chunker(ciphertext, 2):
        row1, col1 = divmod(table.index(char1), 5)
        row2, col2 = divmod(table.index(char2), 5)

        if row1 == row2:
            plaintext += table[row1 * 5 + (col1 - 1) % 5]
            plaintext += table[row2 * 5 + (col2 - 1) % 5]
        elif col1 == col2:
            plaintext += table[((row1 - 1) % 5) * 5 + col1]
            plaintext += table[((row2 - 1) % 5) * 5 + col2]
        else:  
            plaintext += table[row1 * 5 + col2]
            plaintext += table[row2 * 5 + col1]

    return plaintext


if __name__ == "__main__":
    import doctest

    doctest.testmod()

    print("Encoded:", encode("BYE AND THANKS", "GREETING"))
    print("Decoded:", decode("CXRBANRLBALQ", "GREETING"))