The Vigenère cipher is a classic cipher originally developed by Italian cryptographer Giovan Battista Bellaso and published in 1553. It is named after a later French cryptographer Blaise de Vigenère, who had developed a stronger autokey cipher (a cipher that incorporates the message of the text into the key). 

The cipher is easy to understand and implement, but survived three centuries of attempts to break it, earning it the nickname "le chiffre indéchiffrable" or "the indecipherable cipher."

[From Wikipedia](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher):

> The Vigenère cipher is a method of encrypting alphabetic text by using a series of different Caesar ciphers based on the letters of a keyword. It is a simple form of polyalphabetic substitution.
>
> . . .
>
> In a Caesar cipher, each letter of the alphabet is shifted along some number of places; for example, in a Caesar cipher of shift `3`, `A` would become `D`, `B` would become `E`, `Y` would become `B` and so on. The Vigenère cipher consists of several Caesar ciphers in sequence with different shift values.

Assume the key is repeated for the length of the text, character by character. Note that some implementations repeat the key over characters only if they are part of the alphabet -- **this is not the case here.**

The shift is derived by applying a Caesar shift to a character with the corresponding index of the key in the alphabet.

Visual representation:
Write a class that, when given a key and an alphabet, can be used to encode and decode from the cipher.

## Example
Any character not in the alphabet must be left as is. For example (following from above):

Difficulty: interview

Starter code:

def __init__(self, key, alphabet):
	

Solutions:
from itertools import cycle

class VigenereCipher (object):
    def __init__(self, key, alphabet):
        self.key = key.decode('utf-8')
        self.alphabet = alphabet.decode('utf-8')
    
    def cipher(self, mode, str):
        return ''.join(self.alphabet[(self.alphabet.index(m) +
                  mode * self.alphabet.index(k)) % len(self.alphabet)]
                  if m in self.alphabet else m for m, k in zip(str.decode('utf-8'),
                  cycle(self.key))).encode('utf-8')
    
    def encode(self, str): return self.cipher(1, str)
    def decode(self, str): return self.cipher(-1, str)
    


class VigenereCipher (object):
    def __init__(self, key, alphabet):
        self.key = key.decode('utf-8')
        self.alphabet = alphabet.decode('utf-8')
        self.maps = {c:(self.alphabet[i:]+self.alphabet[:i]) for i, c in enumerate(self.alphabet) if c in self.key}   

    def code(self, f, s):
        return ''.join([f(self.maps[self.key[i % len(self.key)]], c) for i, c in enumerate(s.decode('utf-8'))]).encode('utf-8')
            
    def encode(self, s):    
        return self.code(lambda l, c: l[self.alphabet.index(c)] if c in self.alphabet else c, s)
        
    def decode(self, s):
        return self.code(lambda l, c: self.alphabet[l.index(c)] if c in self.alphabet else c, s)        

from itertools import cycle, islice
from unicodedata import normalize

def chunks(lst, chunk_size):
    return [lst[i:i+chunk_size] for i in range(0, len(lst), chunk_size)] if chunk_size != 1 else lst
    

class VigenereCipher (object):
    def __init__(self, key, alphabet):
        self.charsize = len(key) / len(key.decode('utf-8'))
        seq = chunks(list(islice(cycle(key), len(alphabet))), self.charsize)
        seq = [''.join(s) for s in seq]
        self.ab = chunks(alphabet, self.charsize)
        self.key = [self.ab.index(x) for x in seq]
        
    def caesar(self, c, shift):        
        try:
            return self.ab[(self.ab.index(c) + shift) % len(self.ab)]
        except ValueError:
            return c   
        
    def encode(self, str):
        seq = [self.caesar(c, self.key[index]) for index, c in enumerate(chunks(str, self.charsize))]  
        return ''.join(seq)
            
    def decode(self, str):
        seq = [self.caesar(c, -self.key[index]) for index, c in enumerate(chunks(str, self.charsize))]  
        return ''.join(seq)
        


class VigenereCipher(object):
    def __init__(self, key: str, alphabet: str):
        self.alphabet = list(alphabet)
        self.key = [alphabet.index(i) for i in key]

    def encode(self, text):
        return "".join([self.alphabet[(self.alphabet.index(text[i]) + self.key[i % len(self.key)]) % len(self.alphabet)]
                        if text[i] in self.alphabet else text[i] for i in range(len(text))])

    def decode(self, text):
        return "".join([self.alphabet[(self.alphabet.index(text[i]) - self.key[i % len(self.key)]) % len(self.alphabet)]
                        if text[i] in self.alphabet else text[i] for i in range(len(text))])


class VigenereCipher(object):

    def __init__(self, key, alphabet):
        self.key = key * 10
        self.alphabet = alphabet * 2
        
    
    def encode(self, text):
        encrypted = ""
        a = 0
        for i in text:
            if i in self.alphabet:
                encrypted += self.alphabet[self.alphabet.index(i) + self.alphabet.index(self.key[a])]
                a += 1
            else:
                encrypted += i
                a += 1
        return encrypted
        
              
    def decode(self, text):
        decrypted = ""
        a = 0
        for i in text:
            if i in self.alphabet:
                decrypted += self.alphabet[self.alphabet.index(i) - self.alphabet.index(self.key[a])]
                a += 1
            else:
                decrypted += i
                a += 1
        return decrypted

class VigenereCipher(object):
    cipherMap = {}
    invCipherMap = {}
    alphabet = ''
    lenAlphabet = -1
    key = ''
    lenKey = -1

    def __init__(self, key, alphabet):
        self.key = key
        self.lenKey = len(self.key)
        self.alphabet = alphabet
        self.lenAlphabet = len(self.alphabet)
        self.cipherMap = dict(list(enumerate(self.alphabet)))    #slick way learned @ stackExchange :)    
        self.invCipherMap = {val: key for key, val in list(self.cipherMap.items())}   #invert the cipher map
    #-----end constructor



    #helper function to perform the cipher. Note, if a char (in the provided text) is not
    # in the provided alphabet, we are to leave it be; otherwise, based on the alphabet 
    # and the key, we are to encode the character using the Vigenere Cipher    
    def __performVigenereCipher__(self, text, performEncode):
        lenText = len(text)
        encodedText = ''

        for i in range(lenText):
            curChar = text[i]
            keyChar = self.key[i%self.lenKey]

            if curChar in self.alphabet:        #Scenario: Perform Vigenere Cipher
                charVal = self.invCipherMap[curChar]
                keyVal  = self.invCipherMap[keyChar]

                if performEncode:
                    valEncoded = (charVal + keyVal)%self.lenAlphabet
                else:
                    valEncoded = (charVal - keyVal)%self.lenAlphabet

                encodedChar = self.cipherMap[valEncoded] 
            else:   #Scienario: The char is not in the alphabet; leave it be
                encodedChar = curChar
            
            encodedText += encodedChar
        #---end for loop

        return encodedText
    #-----end function

    def encode(self, text):
        performEncode = True
        encodedText = self.__performVigenereCipher__(text, performEncode)        
        return encodedText
    #-----end function encode    
    
    def decode(self, text):
        performEncode = False
        decodedText = self.__performVigenereCipher__(text, performEncode)
        return decodedText 
    #-----end function decode

#-----end class


class VigenereCipher(object):
    def __init__(self, key, alphabet):
        self.alphabet = alphabet
        self.key = key
    
    def encode(self, text):
        mx = []
        for i in range(len(text)):
            try:
                shift = self.alphabet.index(self.key[i%(len(self.key))]) 
                start = self.alphabet.index(text[i])
                mx.append(self.alphabet[(start+shift)%len(self.alphabet)])
            except:
                mx.append(text[i])
        return ''.join(mx)
            
    
    def decode(self, text):
        mx = []
        for i in range(len(text)):
            try:
                shift = self.alphabet.index(self.key[i%(len(self.key))]) 
                start = self.alphabet.index(text[i])
                mx.append(self.alphabet[(start-shift)%len(self.alphabet)])
            except:
                mx.append(text[i])
        return ''.join(mx)
        


from itertools import cycle
#cycle => cycle(abc) = abcabcabc

class VigenereCipher(object):
    def __init__(self, key:str, alphabet:str)->None:
        self.key = key
        self.alphabet = alphabet

    def cipher(self, m:int, s:str)->str:
        r = ''
        for i, j in zip(s,cycle(self.key)):
            if i in self.alphabet:
                r += self.alphabet[(self.alphabet.index(i) + m * self.alphabet.index(j)) % len(self.alphabet)]
            else:
                r += i
        return r

    def encode(self, s:str)->str:
        return self.cipher(1, s)

    def decode(self, s:str)->str:
        return self.cipher(-1, s)

from itertools import cycle

class VigenereCipher:
    def __init__(self, key, alphabet):
        self.key = key
        self.alphabet = alphabet
    
    def encode(self, text):
        return "".join(self.caesar(ch, key_ch, 1) if ch in self.alphabet else ch for ch, key_ch in zip(text, cycle(self.key)))

    def decode(self, text):
        return "".join(self.caesar(ch, key_ch, -1) if ch in self.alphabet else ch for ch, key_ch in zip(text, cycle(self.key)))

    def caesar(self, char, key_char, mode=1):
        char_position, move = self.alphabet.find(char), self.alphabet.find(key_char)
        return self.alphabet[(char_position + mode * move) % len(self.alphabet)] # Probably should store len(alphabet) instead of recalculating.

class VigenereCipher(object):
    def __init__(self, key, alphabet):
        self.alphabet=alphabet
        self.key=key
    
    def encode(self, text):
        clé=self.key
        while(len(clé)<len(text)):
            clé+=clé
            clé=clé[:len(text)]
        chiffre=''    
        for i,(v,w) in enumerate(zip(text,clé)):
            if v in self.alphabet:
                chiffre+=self.alphabet[(self.alphabet.index(v)+self.alphabet.index(w))%len(self.alphabet)]
            else:
                chiffre+=v
        return chiffre        
                
        
    
    def decode(self, text):
        clé=self.key
        while(len(clé)<len(text)):
            clé+=clé
            clé=clé[:len(text)]
        dechiffre=''    
        
        for i,(v,w) in enumerate(zip(text,clé)):
            if v in self.alphabet:
                dechiffre+=self.alphabet[(self.alphabet.index(v)-self.alphabet.index(w))%len(self.alphabet)]
            else:
                dechiffre+=v
                
        return dechiffre