Since this program makes heavy use of the modulus operation, we'll start with a word on that. A modulus is the remainder of a division, typically of integers. Modulus is computed by the % operator in C, but usually just written as “mod” in mathematics. We will use modulus to remove cycles. For instance, if you run 10,000 feet around a circular track 3300 feet long, how far will you have moved from the start? Of course, the answer to that question, 100 ft, is the remainder of dividing 10,000 by 3300: 10000mod3300=100. The (integer) quotient is three, meaning that you completed three complete circuits, and the remainder (100=10000−3×3300) is how much further you traveled after completing the last one. So the modulus removes the complete circuits from the total distance.
For our purposes, the divisor will always be positive, but the dividend may not. Suppose you get turned around and run backwards around the circle. Then you will have run negative 10,000 ft, and will finish at position −10000mod3300=−100. That just means you were 100 feet away from the starting point in the backwards direction, which, on a 3300-ft circle, is also 3200 feet in the forwards direction. In our program, we will often want the positive version of the position, which simply means we add the divisor to the remainder when it is negative. I use the symbol rot to indicate this:
The second notion we will use is that of rotating (or shifting) a character through an alphabet. Suppose we we are using the alphabet of capital letters, and we want to code the word AXEL by shifting each letter right five places. This would produce FCJQ. For each letter, simply move five places to the right in the alphabet. Counting five down from A, you reach F. Five right from E reaches J. Since there aren't five spots to the right of X, just wrap around, letting A follow Z, so you wrap back around to C. To decrypt FCJQ, work the other way, moving left from each letter, treating Z as just before A. Then you can get your AXEL back.
Mathematically, shifting can be represented using modular arithmetic. For the above example, assign to the letters A-Z the numbers 0 to 25, respectively:
For the present assignment, our “alphabet” will be the 95 printable ASCII characters from space to tilde (codes 32 through 126), inclusive. The alphabet position for each character is simply its ASCII code minus 32. Encoding will map plaintext characters from that set into cyphertext characters drawn from the same set. In addition to the larger alphabet, we will also be using multiple shift counts. Instead of shifting each character the same amount, we will use a “key,” which is just a list of shift counts to be used at different times. Shift amounts are signed; a negative right shift is just a left shift, and vice versa.
To translate (either encrypt or decrypt), each character must be shifted by one of the shift amounts given in the key. Shift left for encryption and right for decryption. The shift amount for each character is determined by choosing a position in the key and shifting by the amount stored in that position. For the first character, the position is zero, so the first key value is used as the shift count. For succeeding characters, the key position is computed by this formula:
Since that explanation was so transparent and straightforward, I'm sure the following example is quite superfluous. Here we are encrypting the message “A Message!” using the key 18 34 -10 8 -3 7. Each row in the following table encrypts one character of the message.
|ASCII||Cipher||New Shift Pos|
So the encryption of “A Message!” by the key 18 34 -10 8 -3 7 is “/x+Sla?q]+”.
Decrypting the string is pretty much the same, except you shift the values right. Also, since the new shift position is computed using the plaintext character, you must use the ASCII code produced by the conversion, instead of the code going into the conversion. Like this:
|ASCII||Plain||New Shift Pos|
Each step (row in the table) starts with two inputs and must compute two outputs. The inputs are the untranslated character and the current shift position, and the outputs are the translated character and the next shift position. For the first step, the shift position is zero; the remaining steps use the position produced by the previous step. The character to translate and its ASCII code are given in the first two columns. The character is converted to its alphabet position using by subtracting 32 (third column), and the shift is the value from shift position in the key. Then the character position value is shifted (left to encode, right to decode) and converted back to an ASCII code by adding 32, giving the translated character. Then we compute the shift position for the next step by the above formula: add the plaintext ASCII code to the current shift position, then take that sum modulus the key size, which six in this case. For the encoding case, the plaintext ASCII code is the input character (before translation); when decoding, it is output character resulting from translation.
The assignment is to write a program which performs the decryption.
For instance, the message above could be input to your program by the file
For a larger example, the input:
Use an array of 100 integers to hold the key values, and keep another plain integer to record the number of values actually used. The first part of your program should read the key values into the key array, and stop when a zero is read. Also make sure to stop when the array is full, even if more values are present in the input. In this part of the program, read the integer values using >> which skips spaces between the input values.
In the second part of your program, you will want to read each character without skipping white space. For this you should declare a char variable and read it with get, like cin.get(ch). You can put that in a while loop test then provide a loop body which outputs the translation of ch.
Between the two parts, after reading the last integer key value but before taking up the character-reading loop, you will need to discard the rest of the line that contains the terminating 0. You can write a simple loop, also using cin.get, to read and discard characters until a newline is discarded. Alternatively, you can use getline to read the rest of the line into a string, which you then discard. (The istream argument to getline would be cin.)
With the above organization, the second loop will have one iteration for each line in the example table. During testing, you might add an output statement to print some or all of the information in a line of the table. Then you can run your program on the test input and detect any error when it occurs.
If you're casting around for a starting place, the very first version might just read the key and print it out again. Then follow that with a loop to read and echo the message characters. You can then work your translation code into the body of the second loop. If you take the advice above to print the information from the lines in the above table, you might work on generating the value for each column, working left to right. Whenever you get one column right, move on to the next. When you can generate the table correctly, remove the extra output and just print the translated characters to create the final version.
In C, character values are really small integers, and a character can be converted to its ASCII code simply by casting, or by assigning it to an intger value. Likewise, and ASCII code value held in an integer can be changed to a character in similar ways. Here is a simple C program that demonstrates some of those things, and also shows how to read a file one character at a time.