/* http://projecteuler.net/
 *
 * Problem 59
 * Decrypt the ciphertext (cipher1.txt). It is known that the key consists
 * of three lower case characters and that the plain text is in English.
 *
 * Solution by Melkor (Filip Niksic, fniksic@gmail.com)
 *
 **/

#include <iomanip>
#include <fstream>
#include <vector>

using namespace std;

double freq_eng_ascii[] = {
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
    0.000000, 0.000000, 0.020517, 0.000000, 0.000000, 0.020517, 0.000000, 0.000000,
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
    0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
    0.155835, 0.001198, 0.005472, 0.000001, 0.000000, 0.000001, 0.000000, 0.002291,
    0.000195, 0.000195, 0.000103, 0.000000, 0.012153, 0.001821, 0.009340, 0.000008,
    0.000071, 0.000105, 0.000051, 0.000017, 0.000008, 0.000015, 0.000016, 0.000012,
    0.000051, 0.000016, 0.000305, 0.000349, 0.000000, 0.000001, 0.000000, 0.000955,
    0.000001, 0.001888, 0.001107, 0.000543, 0.000615, 0.000568, 0.000581, 0.000380,
    0.001222, 0.002254, 0.000097, 0.000363, 0.000209, 0.001001, 0.001104, 0.000499,
    0.001862, 0.000011, 0.000823, 0.000906, 0.001968, 0.000085, 0.000286, 0.000882,
    0.000106, 0.000385, 0.000033, 0.000015, 0.000000, 0.000015, 0.000000, 0.000000,
    0.000000, 0.060585, 0.009431, 0.018029, 0.035354, 0.095122, 0.016083, 0.015184,
    0.049582, 0.050535, 0.000674, 0.005843, 0.029146, 0.017734, 0.054868, 0.058110,
    0.011829, 0.000700, 0.044129, 0.048629, 0.066696, 0.019790, 0.007897, 0.017110,
    0.001132, 0.013683, 0.000695, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000
};

int myFreq[3][128];
int howMany[3];
vector<int> ciphertext;

int main() {
    ifstream in("cipher1.txt");
    ofstream out("p59-temp.txt");
    int ind(0), letter, maxKey[3];
    double IC[3], maxIC[3] = {0.0};

    while (in >> letter) {
	++howMany[ind];
	++myFreq[ind][letter];
	ind = (ind + 1) % 3;
	ciphertext.push_back(letter);
    }

    for (int key = 'a'; key <= 'z'; ++key) {
	IC[0] = IC[1] = IC[2] = 0.0;
	for (int i = 0; i < 128; ++i)
	    for (int j = 0; j < 3; ++j)
		IC[j] += (static_cast<double>(myFreq[j][i]) / howMany[j]) * freq_eng_ascii[i ^ key];
	out << static_cast<char>(key) << "  :  " << setprecision(5) << fixed
	    << IC[0] << "  " << IC[1] << "  " << IC[2] << endl;
	for (int i = 0; i < 3; ++i)
	    if (IC[i] > maxIC[i]) {
		maxIC[i] = IC[i];
		maxKey[i] = key;
	    }
    }

    IC[0] = 0.0;
    for (int i = 0; i < 128; ++i)
	IC[0] += freq_eng_ascii[i] * freq_eng_ascii[i];
    out << "Normal IC: " << IC[0] << endl;

    int sum(0);
    for (int i(0); i < ciphertext.size(); ++i)
	sum += ciphertext[i] ^ maxKey[i % 3];

    out << "Result: " << sum << endl;

    in.close();
    out.close();

    return 0;
}
