Search code examples
c++stringfunctionfor-loopvigenere

C++ function for vigenere cipher only sometimes working (works for some inputs, skips shifts for others)


For an in class assignment I had to write a function that puts a user input string through the vigenere cipher with the optional help from helper function. I was able to reach a partial solution using a helper function that gets the correct shift for a character based on its place in the alphabet as well as another function that shifts the characters in the string by this place if they are letters.

The program only occasionally works with one example being "Hello, world!" with a keyword of "cake" being properly encrypted into "Jevpq, Wyvnd!", however the auto-graders' input of "hDcT+T EtL5V71" and keyword of "vpkbncc" returns "cSmU+V OuY5Q71" instead of "cSmU+G GvG5K71". I know why this is happening; first character is being skipped over every iteration so "cake" ends up being "ake", however I have no idea how to fix this. Here is my code:

/*Author: Xavier f.*/

#include <iostream> 
#include <string>  
#include <cctype>  

using namespace std;

int getShift(char c); // helper function that converts chars into their place in the alphabet as an int 
char shiftChar(char c, int rshift); // this function handles the character value shifting part of the problem
string encryptVigenere(string plaintext, string keyword); //implemntation of Vigenere cypher  , needs to loop around keyword


int main(){ 

    string text, key, debug; 
    cout << "Enter a sentence: ";
    getline(cin, text); 
    cout << "Enter the keyword  : "; 
    getline(cin, key); 

    debug = encryptVigenere(text, key);
    cout << "Ciphertext     : " << debug;  

    return 0; 

}

int getShift(char c) { 

    if (isupper(c)) { 

        return (int)c-(int)'A';
    }
    else { 

        return (int)c-(int)'a';

    }


}  

char shiftChar(char c, int rshift) { 

    char shifted;


    if (isalpha(c)) { //if it is a letter

        if (isupper(c)){

            shifted = ((int)c + rshift - 'A')%26 + 'A';        //65-90 for uppercase , 97-122 for lowercase
        } 
        else { //dont have to put condition since if its not uppercase its obviously lowercase  

            shifted = ((int)c + rshift - 'a')%26 + 'a'; 

        } 

        return shifted;
    } 
    else { 

        return c; 

    } 

} 

string encryptVigenere(string plaintext, string keyword){ 

    char encrypted;
    string vigenere; 
    int ciphercount = 0;

        for(int i = 0; i < plaintext.length(); ++i) {

            if(isalpha(plaintext[i])) {

                encrypted = shiftChar(plaintext[i], getShift(keyword[ciphercount])); 
                vigenere += encrypted; 
                ciphercount ++;

            } 
            else { 
                ciphercount -= keyword.length();
                vigenere += plaintext[i]; 
            }
            ciphercount = ciphercount % keyword.length();
        }  

    return vigenere;    

}

Like I mentioned before the "Hello, world!" example works even though the first character in cake is skipped in the for loop for some reason:

Enter a sentence: Hello, world!
Enter the keyword  : cake
Ciphertext     : Jevpq, wyvnd! 

debug: og ciphercount: c/ debug: plaintext[i]: H / debug: keyword[ciphercount]: a / debug: cyphercount isalpha: 1 / debug: encrypted: J / debug: vigenere: J
debug: cyphercount loop through: 1

However with autograders' input this problem becomes more disastrous:

Enter a sentence: hDcT+T EtL5V71
Enter the keyword  : vpkbncc
Ciphertext     : cSmU+V OuY5Q71 

debug: og ciphercount: v / debug: plaintext[i]: h / debug: keyword[ciphercount]: p / debug: cyphercount isalpha: 1 / debug: encrypted: c / debug: vigenere: c
debug: cyphercount loop through: 1

The output is supposed to be "cSmU+G GvG5K71" but since the first shift character is skipped the shift the text is put through is pkbn_bn_c_p__ which is wrong.

Does anyone know how I can go about fixing this?


Solution

  • Your problem here is not with the first letter of the keyword being ignored (try fake instead of cake and confirm that it does change the output).

    It appears that your real problem lays with the way the encryptVigenere function is written.

    I'm no expert in cryptography and I can't tell you whether or not this is the right way to implement vigenere cipher but I couldn't make much sense of this line:

                ciphercount -= keyword.length();
    

    and as it turned out you'll get the result you want if you remove it.

    So, here's your new encryptVigenere function:

    string encryptVigenere(string plaintext, string keyword){ 
    
        char encrypted;
        string vigenere; 
        int ciphercount = 0;
    
            for(int i = 0; i < plaintext.length(); ++i) {
    
                if(isalpha(plaintext[i])) {
                    encrypted = shiftChar(plaintext[i], getShift(keyword[ciphercount])); 
                    vigenere += encrypted; 
                    ciphercount ++;
                } 
                else { 
                    //ciphercount -= keyword.length();
                    vigenere += plaintext[i]; 
                }
                ciphercount = ciphercount % keyword.length();
            }  
    
        return vigenere;    
    
    }