For keyword: hello H E L O A B C D F G I J K M N P Q R S T U V W X Y Z and map vertically  mixedkeywordcollege, UNIVERSITY, True   doctest: NORMALIZEWHITESPACE 'A': 'C', 'B': 'A', 'C': 'I', 'D': 'P', 'E': 'U', 'F': 'Z', 'G': 'O', 'H': 'B', 'I': 'J', 'J': 'Q', 'K': 'V', 'L': 'L', 'M': 'D', 'N': 'K', 'O': 'R', 'P': 'W', 'Q': 'E', 'R': 'F', 'S': 'M', 'T': 'S', 'U': 'X', 'V': 'G', 'W': 'H', 'X': 'N', 'Y': 'T', 'Z': 'Y' 'XKJGUFMJST'  mixedkeywordcollege, UNIVERSITY, False   doctest: NORMALIZEWHITESPACE 'XKJGUFMJST' create a list of unique characters in the keyword  their order matters it determines how we will map plaintext characters to the ciphertext the number of those unique characters will determine the number of rows create a shifted version of the alphabet create a modified alphabet by splitting the shifted alphabet into rows map the alphabet characters to the modified alphabet characters going 'vertically' through the modified alphabet  consider columns first if current row the last one is too short, break out of loop map current letter to letter in modified alphabet create the encrypted text by mapping the plaintext to the modified alphabet example use
from string import ascii_uppercase


def mixed_keyword(
    keyword: str, plaintext: str, verbose: bool = False, alphabet: str = ascii_uppercase
) -> str:
    """
    For keyword: hello

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

    >>> mixed_keyword("college", "UNIVERSITY", True)  # doctest: +NORMALIZE_WHITESPACE
    {'A': 'C', 'B': 'A', 'C': 'I', 'D': 'P', 'E': 'U', 'F': 'Z', 'G': 'O', 'H': 'B',
     'I': 'J', 'J': 'Q', 'K': 'V', 'L': 'L', 'M': 'D', 'N': 'K', 'O': 'R', 'P': 'W',
     'Q': 'E', 'R': 'F', 'S': 'M', 'T': 'S', 'U': 'X', 'V': 'G', 'W': 'H', 'X': 'N',
     'Y': 'T', 'Z': 'Y'}
    'XKJGUFMJST'

    >>> mixed_keyword("college", "UNIVERSITY", False)  # doctest: +NORMALIZE_WHITESPACE
    'XKJGUFMJST'
    """
    keyword = keyword.upper()
    plaintext = plaintext.upper()
    alphabet_set = set(alphabet)

    # create a list of unique characters in the keyword - their order matters
    # it determines how we will map plaintext characters to the ciphertext
    unique_chars = []
    for char in keyword:
        if char in alphabet_set and char not in unique_chars:
            unique_chars.append(char)
    # the number of those unique characters will determine the number of rows
    num_unique_chars_in_keyword = len(unique_chars)

    # create a shifted version of the alphabet
    shifted_alphabet = unique_chars + [
        char for char in alphabet if char not in unique_chars
    ]

    # create a modified alphabet by splitting the shifted alphabet into rows
    modified_alphabet = [
        shifted_alphabet[k : k + num_unique_chars_in_keyword]
        for k in range(0, 26, num_unique_chars_in_keyword)
    ]

    # map the alphabet characters to the modified alphabet characters
    # going 'vertically' through the modified alphabet - consider columns first
    mapping = {}
    letter_index = 0
    for column in range(num_unique_chars_in_keyword):
        for row in modified_alphabet:
            # if current row (the last one) is too short, break out of loop
            if len(row) <= column:
                break

            # map current letter to letter in modified alphabet
            mapping[alphabet[letter_index]] = row[column]
            letter_index += 1

    if verbose:
        print(mapping)
    # create the encrypted text by mapping the plaintext to the modified alphabet
    return "".join(mapping[char] if char in mapping else char for char in plaintext)


if __name__ == "__main__":
    # example use
    print(mixed_keyword("college", "UNIVERSITY"))
