I made an encoder/decoder for the Vigenère cipher. This one is using a table, not the remainder technique. Please let me know what you think can be done to improve performance, etc.
import string
import itertools
class Vigenere:
"""
A class for vigenere encoding and decoding.
Doesn't preserve spaces, UPPERCASE letters.
Default alphabet is lowercase a-z.
"""
def __init__(self):
"""Initialize default values."""
self.__reset()
def __reset(self):
# Reset all attributes
self.alphabet = ""
self.key = ""
self.__looped_key = ""
self.text = ""
self.__dec_str = ""
self.__enc_str = ""
self.__matrix = []
def __create_matrix(self):
# create a vigenere matrix
tmp = 2 * self.alphabet
self.__matrix.append(list(self.alphabet))
for i in range(len(self.alphabet) - 1):
self.__matrix.append(list(tmp.split(tmp[i], 1)[1][:len(self.alphabet)]))
def __loop_key(self):
# loop the key and slice so it's length matches text's
looped = ''.join(list(itertools.repeat(self.key, int(len(self.text) / len(self.key)) + 1)))[:len(self.text)]
self.__looped_key = looped
def __set_key(self, key):
# remove spaces, lower, loop and set the key
self.key = key.replace(' ', '').lower()
self.__loop_key()
def __set_text(self, text):
# remove spaces, lower and set text
self.text = text.replace(' ', '').lower()
def __set_alphabet(self, alphabet):
# remove spaces and lower
alphabet = alphabet.replace(' ', '').lower()
self.__check_alphabet(alphabet) # check alphabet validity
self.alphabet = alphabet
self.__check_chars() # check key and text compability with alphabet
self.__create_matrix() # create a vigenere table
def __check_alphabet(self, alphabet):
# check alphabet for duplicates
if len(alphabet) != len(set(alphabet)):
raise ValueError('alphabet contains duplicate characters')
def __check_chars(self):
# check key and text compability with alphabet
t_key = ''.join(list(filter(lambda x: x not in self.alphabet, self.key)))
t_text = ''.join(list(filter(lambda x: x not in self.alphabet, self.text)))
if t_text != '':
raise ValueError('text includes characters not in alphabet')
if t_key != '':
raise ValueError('key includes characters not in alphabet')
def decode(self, text, key, alphabet=string.ascii_lowercase):
"""Decode text with key using given alphabet(default a-z)."""
# reset values
self.__reset()
# set attributes
self.__set_text(text)
self.__set_key(key)
self.__set_alphabet(alphabet)
# iterate through the looped key and decode string
for i in range(len(self.__looped_key)):
lst = self.__matrix[self.alphabet.index(self.__looped_key[i])]
dex = lst.index(self.text[i])
self.__dec_str += self.__matrix[0][dex]
return self.__dec_str
def encode(self, text, key, alphabet=string.ascii_lowercase):
"""Encode text with key using given alphabet(default a-z)."""
# reset values
self.__reset()
# set attributes
self.__set_text(text)
self.__set_key(key)
self.__set_alphabet(alphabet)
# iterate through the text and encode it
for i in range(len(self.text)):
lst = self.__matrix[self.alphabet.index(self.text[i])]
dex = self.__matrix[0].index(self.__looped_key[i])
self.__enc_str += lst[dex]
return self.__enc_str
vigenere = Vigenere()
encode = vigenere.encode
decode = vigenere.decode
if __name__ == '__main__':
# test
assert encode('d CO dE', 'KE y') == 'ngmni'
assert decode('NG mN i', ' ke Y') == 'dcode'
assert decode('1132xn5m dze HN j5rn9v4Mmzzx qpc7s', 'K ey', 'abcdEF ghijklM Nopqr sT uvwxyz123456789') == 'qwertyuiopasdfghjklzxcvbnm1234'
assert encode('qW ertYU iopasdfGhJk LzxcvbNM1234', 'KeY ', 'abcdEF ghijklM Nopqr sT uvwxyz123456789') == '1132xn5mdzehnj5rn9v4mmzzxqpc7s'