-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCaesar.cpp
More file actions
261 lines (218 loc) · 8.28 KB
/
Caesar.cpp
File metadata and controls
261 lines (218 loc) · 8.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// Winter Thomas and Ricardo Romanach
// Caeser Cipher Statistical Attack
#include <iostream>
#include <string>
#include "Caesar.h"
#include "DataManipulation.h"
/////////////////////////
// Main User Functions //
/////////////////////////
// Does all steps for decrypting a caesar cypher automatically
// numberOfResults can be 1 to 26
void Caesar::solveCaesar(std::string encryptedText, int numberOfResults){
// Remove all non letters from the encrypted text
std::string cleanEncryptedText = cleanText(encryptedText);
// Get the correlation Frequencies for the clean text
double* correlationFrequencies = getCorrelationOfFrequencies(cleanEncryptedText);
// Get the indexs of the most likely results
int* topShifts = getTopShifts(correlationFrequencies, numberOfResults);
// print the solution(s)
printResults(topShifts, correlationFrequencies, numberOfResults, encryptedText);
delete[] correlationFrequencies;
delete[] topShifts;
}
// Returns unshifted string using the top result from the statistical attack
std::string Caesar::getMostLikelyDecryption(std::string encryptedText){
// Remove all non letters from the encrypted text
std::string cleanEncryptedText = cleanText(encryptedText);
// Get the correlation Frequencies for the clean text
double* correlationFrequencies = getCorrelationOfFrequencies(cleanEncryptedText);
// Get the indexs of the most likely results
int* topShifts = getTopShifts(correlationFrequencies, 1);
// Get the solution
std::string result = decrypt(encryptedText, topShifts[0]);
delete[] correlationFrequencies;
delete[] topShifts;
return result;
}
// When provided an text and a shift, it unshifts the text by that amount
// Primarily used for decrypting
std::string Caesar::decrypt(std::string ciphertext, int shift){
int storeForShift;
std::string result = "";
for (char i : ciphertext){
// If there is a non letter, no shift is needed
if (!isalpha(i)){
result += i;
continue;
}
// Shift letter
storeForShift = convertLetterToNumber(toupper(i));
storeForShift -= shift;
// Account for shift overflowing
if (storeForShift < 0){
storeForShift += LANGUAGE_LETTER_COUNT;
}
// If letter is capital
if (isupper(i)){
result += convertNumberToLetter(storeForShift);
}
// If letter is lowercase
else {
result += tolower(convertNumberToLetter(storeForShift));
}
}
return result;
}
// Prompts for a text and a shift amount and then shifts the text by that amount
std::string Caesar::encrypt(){
std::string text;
int key;
text = DataManipulation::getUserInput("Please enter the text to encrypt:");
std::cout << "\nHow much would you like to shift the text?\n";
key = DataManipulation::getIntegerInput(0, 25);
return encrypt(text, key);
}
// When provided a text and a shift, it shifts the text by that amount
// Primarily used for encrypting
std::string Caesar::encrypt(std::string text, int shift){
int storeForShift;
std::string result = "";
for (char i : text){
// If there is a non letter, no shift is needed
if (!isalpha(i)){
result += i;
continue;
}
// Shift letter
storeForShift = convertLetterToNumber(toupper(i));
storeForShift += shift;
// Account for shift overflowing
if (storeForShift > 25){
storeForShift -= LANGUAGE_LETTER_COUNT;
}
// If letter is capital
if (isupper(i)){
result += convertNumberToLetter(storeForShift);
}
// If letter is lowercase
else {
result += tolower(convertNumberToLetter(storeForShift));
}
}
return result;
}
////////////////////
// Core Functions //
////////////////////
// Returns an array of frequencies. For each possible Caesar shift, this will calculate how
// closely the letter frequencies match English letter frequencies. (e.g. Index 0 will hold
// how closely the frequencies match for a shift of 0.)
double* Caesar::getCorrelationOfFrequencies(std::string text){
int* letterFrequencies = getFrequencyOfLetters(text);
// Gets number of letters in text
int numberOfLetters = text.length();
double total;
double* correlationFrequencies = new double [LANGUAGE_LETTER_COUNT];
// For each possible shift
for (int i = 0; i < LANGUAGE_LETTER_COUNT; i++){
total = 0;
for (int j = 0; j < LANGUAGE_LETTER_COUNT; j++){
// Calculates the correlation of a letter in the cipher text with its frequency of use in the English language
total += ((double) letterFrequencies[j] / numberOfLetters) * ENGLISH_ALPHABET_FREQUENCIES[((LANGUAGE_LETTER_COUNT + j - i) % LANGUAGE_LETTER_COUNT)];
}
correlationFrequencies[i] = total;
}
delete[] letterFrequencies;
return correlationFrequencies;
}
// Returns an array of INDICES. The indices represent the positions of the highest values
// in the input array. The amount of top results to be returned is stored in topAmount.
// topAmount can be 1 to 26
int* Caesar::getTopShifts(double* frequencies, int topAmount){
// Make and initialize an array for the top values
int* topResults = new int[topAmount];
for (int i = 0; i < topAmount; i++){
topResults[i] = -1;
}
double max;
int maxIndex;
bool isInTopList;
// For each value to be in top list
for (int i = 0; i < topAmount; i ++){
max = 0.0;
// Check if the value is the largest value in the array
for (int j = 0; j < LANGUAGE_LETTER_COUNT; j++){
// If its the largest value
if(frequencies[j] > max){
// Check if its already in the top list
isInTopList = false;
for (int k = 0; k < topAmount; k++){
if (j == topResults[k]){
isInTopList = true;
}
}
// If its not, set it to the new largest
if (!isInTopList){
max = frequencies[j];
maxIndex = j;
}
}
}
// Store the current largest value in the top results array
topResults[i] = maxIndex;
}
return topResults;
}
// Prints the top most likely shift values, the frequency it matched
// with English letter frequency using that shift, and the plaintext
// translation for each shift.
void Caesar::printResults(int* topShifts, double* correlationFrequencies, int numberOfResults, std::string encryptedText){
std::cout << "\nTop " << numberOfResults << " Shifts:\n";
for (int i = 0; i < numberOfResults; i++)
{
std::cout << "Shift = " << topShifts[i] << "\tFrequency = " << correlationFrequencies[topShifts[i]] << "\n";
// For each letter in the encrypted text, shift and print
std::cout << decrypt(encryptedText, topShifts[i]);
std::cout << "\n\n";
}
}
//////////////////////
// Helper Functions //
//////////////////////
// Removes all non letter characters and capitalizes lowercase letters
std::string Caesar::cleanText(std::string rawEncryptedText){
std::string cleanText = "";
for (char i : rawEncryptedText){
// Capitalize the lower letters
if (islower(i)){
cleanText += toupper(i);
}
// Include capital letters
else if (isupper(i)){
cleanText += i;
}
}
return cleanText;
}
// Returns an array with the frequency of each letter in a given string
int* Caesar::getFrequencyOfLetters(std::string text){
// Create and initialize the return array
int* letterFrequency = new int[LANGUAGE_LETTER_COUNT];
for (int i = 0; i < LANGUAGE_LETTER_COUNT; i++){
letterFrequency[i] = 0;
}
// Count the amount of each letter
for (char i : text){
letterFrequency[convertLetterToNumber(toupper(i))]++;
}
return letterFrequency;
}
// Transforms a letter into its number in the alphabet 0 - 25
int Caesar::convertLetterToNumber(char letter){
return letter - 65;
}
// Transforms a number 0 - 25 into its letter in the alphabet
char Caesar::convertNumberToLetter(int number){
return number + 65;
}