/*
 * Userspace program that communicates with the VHE accelerator
 * through ioctls
 *
 * Acknowledgement (modified from):
 * Stephen A. Edwards
 * Columbia University
 * Lanxiang Hu, Liqin Zhang
 * May, 2022
 *
 */

#include <stdio.h>
#include "switching_matrix.h"
#include "encrypted_domain.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <stdio.h>

#include "limits.h"

#include "./library/mat.h"
#include "./client_functions.c"
#include "./server_functions.c"
#define M_1 4
#define N_1 4
#define M_2 2
#define N_2 2
#define L_1 4
#define L_2 8

int switching_matrix_fd;
int encrypted_domain_fd;

int weight;

// ciphertext
int c[] = { 0x1, 0x2, 0x3, 0x4,   // 0-3
			     0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8}; // 4-7

// bit-repr_ciphertext
int c_star[] = { 0x0, 0x0, 0x0, 0x0,      // 0-3
                 0x1, 0x0, 0x0, 0x0,                // 4-7
                 0x1, 0x0, 0x0, 0x0,                // 8-11
                 0x0, 0x1, 0x1, 0x0,                // 12-15
                 0x0, 0x1, 0x0, 0x0,                // 16-19
                 0x0, 0x0, 0x0, 0x0,                // 20-23
                 0xffffffff, 0x0, 0x0, 0x0,         // 24-27
                 0xffffffff, 0x0, 0x0, 0x0,         // 28-31
                 0xffffffff, 0x0, 0x0, 0x0,         // 32-35
                 0xffffffff, 0x0, 0x0, 0x0};        // 36-39

// secret key
int S[2][8] = { {0x6, 0x5, 0x0, 0x3, 0x9, 0x3, 0x3, 0x6},   // 0-7
			    {0x9, 0x7, 0x6, 0x8, 0x2, 0x0, 0x6, 0x1}}; // 8-15

// l = 4
int S_star[2][32] = { {0x30, 0x18, 0xc, 0x6,   // 0-3
                0x28, 0x14, 0xa, 0x5,               // 4-7
                0x0, 0x0, 0x0, 0x0,                 // 7-11
                0x18, 0xc, 0x6, 0x3,                // 12-15
                0x48, 0x24, 0x12, 0x9,               // 16-19
                0x18, 0xc, 0x6, 0x3,                // 20-23
                0x18, 0xc, 0x6, 0x3,                // 24-27
                0x30, 0x18, 0xc, 0x6},              // 28-31
			   {0x48, 0x24, 0x12, 0x9,               // 0-3
                0x38, 0x1c, 0xe, 0x7,               // 4-7
                0x30, 0x18, 0xc, 0x6,               // 7-11
                0x40, 0x20, 0x10, 0x8,              // 12-15
                0x10, 0x8, 0x4, 0x2,                // 16-19
                0x0, 0x0, 0x0, 0x0,                 // 20-23
                0x30, 0x18, 0xc, 0x6,               // 24-27
                0x8, 0x4, 0x2, 0x1}};               // 28-31

// encrypted data
int c_1[16] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,   // 0-7
			    0xffffffff, 0xfffffffe, 0xfffffffd, 0xfffffffc, 0xfffffffb, 0xfffffffa, 0xfffffff9, 0xfffffff8}; // 8-15

int c_2[16] = {0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,   // 0-7
			    0x1, 0x2, 0x3, 0x4, 0xffffffff, 0xfffffffe, 0xfffffffd, 0xfffffffc}; // 8-15

// expected result
int c_out[16] = {0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x11,   // 0-7
			    0x0, 0x0, 0x0, 0x0, 0xfffffffa, 0xfffffff8, 0xfffffff6, 0xfffffff4}; // 8-15

// encrypted data
int c[8] = {0x1, 0x2, 0x3, 0x4, 0xffffffff, 0xfffffffe, 0xfffffffd, 0xfffffffc};   // 0-7

// encrypted linear operator
int M[4][8] = { {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},   // 0-7
			    {0xfffffffe, 0xfffffffd, 0xfffffffc, 0xfffffffb, 0xfffffffa, 0xfffffff9, 0xfffffff8, 0xfffffff7},   // 8-15
                {0x3, 0x6, 0x9, 0xc, 0xfffffffd, 0xfffffffa, 0xfffffff7, 0xfffffff4},   // 16-23
                {0xfffffffe, 0xfffffffd, 0xfffffffc, 0xfffffffb, 0x6, 0x7, 0x8, 0x9}   // 24-31
               };

// result
int a[4] = {0xffffffd8, 0x28, 0xb4, 0xffffff88};    // 0-4

// encrypted data
int c_1_out[2] = {0x1, 0xfffffffe};   // 0-1

int c_2_out[2] = {0x5, 0xfffffffd};   // 0-1

// encrypted linear operator
int M_wi[4][4] = { {0x1, 0x2, 0x3, 0x4},   // 0-7
			    {0xfffffffe, 0xfffffffd, 0xfffffffc, 0xfffffffb},   // 8-15
                {0x3, 0x6, 0x9, 0xc},   // 16-23
                {0xfffffffc, 0xfffffffb, 0x9, 0xc}   // 24-31
               };

// reminder: vectorized result
int x[4] = {0x5, 0xfffffffd, 0xfffffff6, 0x6};    // 0-4

int w = 1;

// expected result
int a_out[4] = {0xfffffff9, 0x9, 0xffffffeb, 0xffffffe9};    // 0-4


int width_v, ell_v;
int width_m, length_m, ell_m;

int width_vd;
int width_l, length_l;
int width_w, length_w;

width_v = 8; // Default
ell_v = 5;
width_m = 8;
length_m = 2;
ell_m = 4;

width_vd = 16; // Default
width_l = 8;
length_l = 4;
width_w = 2;
length_w = 4;

int main() {
    key_switching_arg_t ksa;
    encrypted_domain_arg_t eda;

    printf("VHE Userspace program started\n");

    static const char filename[] = "/dev/key_switching";
    printf("key_switching device driver started\n");

    if ( (key_switching_fd = open(filename, O_RDWR)) == -1) {
        fprintf(stderr, "could not open %s\n", filename);
        return -1;
    }

    static const char filename[] = "/dev/encrypted_domain";

    printf("encrypted_domain device driver started\n");

    if ( (encrypted_domain_fd = open(filename, O_RDWR)) == -1) {
        fprintf(stderr, "could not open %s\n", filename);
        return -1;
    }

    srand(time(0));
    weight = rand() % 10;

    Mat *I = eye(M);
    Mat *S = scalermultiply(I, weight);
    Mat *x_1 = get_random_matrix(M, 1, INT_MIN>>26, INT_MAX>>26); // initialized variable range
    Mat *x_2 = get_random_matrix(M, 1, INT_MIN>>26, INT_MAX>>26); // initialized variable range
    printf("********** Client: Plaintext 1 *************\n");
    showmat(x_1);
    printf("********** Client: Plaintext 2 *************\n");
    showmat(x_2);
    Mat *c_1 = encryption(S, x_1); // ciphertext
    Mat *c_2 = encryption(S, x_2); // ciphertext
    printf("********** Client -> Server: Encrypted Text 1 *************\n");
    showmat(c_1);
    printf("********** Client -> Server: Encrypted Text 2 *************\n");
    showmat(c_2);
    printf("********** Server: Encrypted Addition *************\n");
    Mat *s_1 = serverAddition(c_1, c_2);
    showmat(s_1);
    printf("********** Server->Client: Decrypted Addition *************\n");
    Mat *addi = serverAddition(x_1, x_2);
    showmat(addi);
    printf("************** Linear Transformation ****************\n");
    printf("********** Clinet: Linear Transformation Key Switching Matrix *************\n");
    Mat *G = get_random_matrix(M, N, INT_MIN>>24, INT_MAX>>24);
    Mat *KSM = clientLinearTransform(G, S, c_1);
    showmat(KSM);
    printf("********** Server: Encrypted Linear Transform, (M_l, c_1) *************\n");
    Mat *s_2 = serverLinearTransform(KSM, c_1);
    showmat(s_2);
    printf("********** Server->Client: Decrypted Linear Transform *************\n");
    Mat *M_l = get_random_matrix(M+5, 1, INT_MIN>>24, INT_MAX>>24); // initialized variable range
    Mat *delin = serverLinearTransform(M_l, x_1);
    showmat(delin);
    printf("********** Server: Encrypted Weighted Inner Product *************\n");
    Mat *M_i = get_random_matrix(M, M, INT_MIN>>24, INT_MAX>>24); // initialized variable range
    Mat *s_3 = serverInnerProduct(c_1, transpose(c_2), M_i);
    showmat(s_3);
    printf("********** Server->Client: Decrypted Weighted Inner Product *************\n");
    Mat *dewin = serverInnerProduct(x_1, transpose(x_2), M_i);
    showmat(dewin);
    printf("********** Client: Decrypted Plaintext 1 *************\n");
    Mat *xx_1 = decryption(S, c_1);
    showmat(xx_1);
    printf("********** Client: Decrypted Plaintext 2 *************\n");
    Mat *xx_2 = decryption(S, c_2);
    showmat(xx_2);

}
