// Hill Cipher

#include <iostream>
#include <vector>
#include <cctype>
using namespace std;

// Convert character to number (A=0, ..., Z=25)
int charToInt(char c) {
    return toupper(c) - 'A';
}

// Convert number to uppercase character
char intToChar(int n) {
    return 'A' + (n % 26);
}

// Modulo 26 (handles negative numbers too)
int mod26(int n) {
    return (n % 26 + 26) % 26;
}

// Multiply 3x3 matrix with 3x1 vector
vector<int> multiplyMatrix(const vector<vector<int>>& mat, const vector<int>& vec) {
    vector<int> res(3, 0);
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            res[i] = mod26(res[i] + mat[i][j] * vec[j]);
    return res;
}

// Calculate determinant of 3x3 matrix
int determinant(const vector<vector<int>>& m) {
    int det = m[0][0]*(m[1][1]*m[2][2] - m[1][2]*m[2][1]) -
              m[0][1]*(m[1][0]*m[2][2] - m[1][2]*m[2][0]) +
              m[0][2]*(m[1][0]*m[2][1] - m[1][1]*m[2][0]);
    return mod26(det);
}

// Find modular inverse of a number modulo 26
int modInverse(int det) {
    det = (det % 26 + 26) % 26; // Ensure positive mod
    for (int i = 1; i < 26; ++i) {
        if ((det * i) % 26 == 1) return i;
    }
    return -1; // No modular inverse exists

}

// Adjugate (cofactor transpose) of 3x3 matrix
vector<vector<int>> adjugate(const vector<vector<int>>& m) {
    vector<vector<int>> adj(3, vector<int>(3));

    adj[0][0] = mod26(m[1][1]*m[2][2] - m[1][2]*m[2][1]);
    adj[0][1] = mod26(-(m[0][1]*m[2][2] - m[0][2]*m[2][1]));
    adj[0][2] = mod26(m[0][1]*m[1][2] - m[0][2]*m[1][1]);

    adj[1][0] = mod26(-(m[1][0]*m[2][2] - m[1][2]*m[2][0]));
    adj[1][1] = mod26(m[0][0]*m[2][2] - m[0][2]*m[2][0]);
    adj[1][2] = mod26(-(m[0][0]*m[1][2] - m[0][2]*m[1][0]));

    adj[2][0] = mod26(m[1][0]*m[2][1] - m[1][1]*m[2][0]);
    adj[2][1] = mod26(-(m[0][0]*m[2][1] - m[0][1]*m[2][0]));
    adj[2][2] = mod26(m[0][0]*m[1][1] - m[0][1]*m[1][0]);

    // Transpose for adjugate
    swap(adj[0][1], adj[1][0]);
    swap(adj[0][2], adj[2][0]);
    swap(adj[1][2], adj[2][1]);

    return adj;
}

// Inverse of 3x3 matrix modulo 26
vector<vector<int>> inverseMatrix(const vector<vector<int>>& matrix) {
    int det = determinant(matrix);
    int detInv = modInverse(det);

    if (detInv == -1) {
        cout << "Matrix is not invertible under mod 26.\n";
        return {};
    }

    vector<vector<int>> adj = adjugate(matrix);
    vector<vector<int>> inv(3, vector<int>(3));
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            inv[i][j] = mod26(adj[i][j] * detInv);
    return inv;
}

// Encrypt plaintext using Hill Cipher
string encrypt(const string& plaintext, const vector<vector<int>>& key) {
    string cleanText = "";
    for (char c : plaintext)
        if (isalpha(c)) cleanText += toupper(c);

    // Pad with 'X' if not multiple of 3
    while (cleanText.size() % 3 != 0)
        cleanText += 'X';

    string cipher = "";
    for (size_t i = 0; i < cleanText.size(); i += 3) {
        vector<int> block = {
            charToInt(cleanText[i]),
            charToInt(cleanText[i+1]),
            charToInt(cleanText[i+2])
        };
        vector<int> res = multiplyMatrix(key, block);
        for (int val : res)
            cipher += intToChar(val);
    }

    return cipher;
}

// Decrypt cipher using Hill Cipher
string decrypt(const string& cipher, const vector<vector<int>>& key) {
    vector<vector<int>> invKey = inverseMatrix(key);
    if (invKey.empty()) return "";

    string plain = "";
    for (size_t i = 0; i < cipher.size(); i += 3) {
        vector<int> block = {
            charToInt(cipher[i]),
            charToInt(cipher[i+1]),
            charToInt(cipher[i+2])
        };
        vector<int> res = multiplyMatrix(invKey, block);
        for (int val : res)
            plain += intToChar(val);
    }

    return plain;
}

int main() {
    vector<vector<int>> key = {
        {6, 24, 1},
        {13, 16, 10},
        {20, 17, 15}
    }; // GYBNQKURP

    string text;
    cout << "Enter the plain text: ";
    getline(cin, text);

    string encrypted = encrypt(text, key);
    cout << "Encrypted: " << encrypted << endl;

    string decrypted = decrypt(encrypted, key);
    cout << "Decrypted: " << decrypted << endl;

    return 0;
}
