print "\nMost likely key lengths using Kasiski test:\n%s\n" % (mostLikelyKeysKasiski)
 
 # initialize the letter frequency class
 letFreq = LetterFrequency()
 
 # loop through the top 4 most likely key lengths according to the kasiski test
 for keyLen in mostLikelyKeysKasiski[:2]:
     # Split the message into keyLen columns        
     msgColumns = polyCi.msgSplit(msg, keyLen)
     # holds the potential keyword
     keyword = ""
     # perform frequency analysis on each column
     print "Using the Vigenere Square on the top 4 most frequent letters in each column assuming that each corresponds to 'e' results in the following options:"                
     for idx, column in enumerate(msgColumns):
         # get the most frequent letter in the column
         columnFrequencies = letFreq.getFrequencies(''.join(column))[1]
         mostFrequentLetTuple = columnFrequencies[0]
         mostFreqLet = mostFrequentLetTuple[0]
         # for tuple in columnFrequencies[:4]:
             # print vigCi.vigenereSquareDecrypt(tuple[0], 'e')
         # print "options above"
         print "Column", idx+1, [vigCi.vigenereSquareDecrypt(tuple[0], 'e') for tuple in columnFrequencies[:6]]
         # assuming this letter corresponds to 'e', use the vigenere square
         # to discover the original keyword letter
         keywordLet = vigCi.vigenereSquareDecrypt(mostFreqLet, 'e')
         keyword += keywordLet
     # I discovered this to be the keyword through analyzing the possible combinations across the columns
     # and put it in manually to make the output more interesting
     #keyword = 'funny'
     print "\nPotential keyword: %s" % keyword
     print "Message deciphered using keyword '%s':\n%s\n" % (keyword, vigCi.decrypt(msg,keyword))
    def decrypt(self, msg, mfc=None, mfp=None, verbose=False, showDigraphFrequencies=False, auto=False, bruteAmt=None):
        # remove spaces from the message
        msg = ''.join(msg.split())        
        if verbose: 
            print "Attempting to decipher:\n", msg, "\n"
        
        # get the message and default letter frequencies
        letFreq = LetterFrequency()
        (freqDict, freqTuples) = letFreq.getFrequencies(msg)
        (stdDict, stdTuples) = letFreq.getStandardFrequencies()
        
        # if the mfc or mfp are None, use the top two most frequent letters
        # in the ciphertext and english language respectively as defaults
        mfc = tuple(mfc) if mfc else (freqTuples[0][0], freqTuples[1][0])        
        mfp = tuple(mfp) if mfp else (stdTuples[0][0], stdTuples[1][0])      
        
        if verbose: 
            print "Ciphertext Letter Frequencies:\n%s\n" % freqTuples
            print "English Language Letter Frequencies:\n%s\n" % stdTuples 
            if showDigraphFrequencies:
                diFreq = DigraphFrequency()
                diFreqTuples = diFreq.getFrequencies(msg)[1]
                diStdTuples = diFreq.getStandardFrequencies()[1]
                print "Ciphertext Digraph Frequencies:\n%s\n" % diFreqTuples
                print "English Language Digraph Frequencies:\n%s\n" % diStdTuples 
                
            print "Using letter in ciphertext: %s" % mfc[0]
            print "\twith frequency: %.2f%%" % freqDict[mfc[0]]
            print "Using letter in ciphertext: %s" % mfc[1]
            print "\twith frequency: %.2f%%\n" % freqDict[mfc[1]]

            print "Assuming that", mfc[0], "is equivalent to", mfp[0]
            print "Assuming that", mfc[1], "is equivalent to", mfp[1], "\n"
        # get the affine key using the mfc letters and mfp letters
        key = self.getAffineKey(*tuple(map(self.letToInt, mfp+mfc)), verbose=verbose)   
        
        if auto:
            iEnd = jEnd = 10
            if bruteAmt:
                iEnd = int(bruteAmt[0])
                jEnd = int(bruteAmt[1])
            maxIdx = len(freqTuples)-1
            for i in range(0, iEnd):
                for j in range(0, jEnd):
                    if i != j and i <= maxIdx and j <= maxIdx:
                        self.decrypt(msg, mfc=[freqTuples[i][0], freqTuples[j][0]], mfp=mfp, verbose=False, showDigraphFrequencies=showDigraphFrequencies)
        
        if key is None:
            if verbose:
                print "Unable to decipher using '%s'->'%s' and '%s'->'%s'" % (mfc[0], mfp[0], mfc[1], mfp[1])
            return None
        
        # Use the key to decrypt
        plaintext = ""
        for ch in msg.lower():
            cipherInt = ((ord(ch) - 96)*self.multInv[key[0]] - key[1]) % 26
            # cover the 'z' case
            if cipherInt == 0:
                cipherInt = 26
            plaintext += chr(cipherInt + 96)
            
        if plaintext and not verbose:      
            print "%s %s %s  %s " % (str(key).ljust(8), mfc[0], mfc[1], plaintext)
        
        return plaintext