/*********************************************************************************************************************************************
Purpose:To understand the complexity of a quantum erasure channel, and with a QBER as high as 25%.
Authors: Sharansh R(EE19B116),Vibhhu Sharma(EE19B128),Adithyan CG(EE19B003)
Date   : 4th October,2019
Input  : The User must enter three inputs 
 	     N: the total number of bits ~ 1 million (more the better, but start small)
	     P: the fraction of bits that reach the receiver ~ 0.1
	     Q: the fraction of bits that have an error ~ 0.25
Sample Input: ./a.out {N~1000000} {Q~0.1} {R~0.25}
Output : The random offset and the calculated offset and the Quantum Bit Error Rate.
Sample I/O:     "./a.out 500000 0.1 0.25

		Number of Bits Sent is: 500000
		Number of Bits Received is: 50000
		The Randomly Choosen Offset is : 476916   
		The Calculated Offset is : 476916
		The Minimum Hamming Distance is: 2476
		Number of bits used to calculate Offset: 9883
		Quantum Bit Error Rate(QBER)(in %): 25.053123"
**********************************************************************************************************************************************/

#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <limits.h>

int main(int argc, char* argv[])
{

    /* Reading in data from command line */
    if (argc != 4)
    {
        fprintf(stderr, "Three inputs required (N: The total number of bits ~ 1 million,"
                        "P: The fraction of bits that reach the receiver ~ 0.1,"
                        "Q: The fraction of bits that have an error ~ 0.25)\n");
        exit(1);
    }

    srand(time(0)); // seeding the random number generator
    FILE* gnuplotfile = popen("gnuplot -persistent", "w"); //file pointer
	
	fprintf(gnuplotfile, "set title 'Hamming distance vs Offset' \n set xlabel 'Offset' \n "
                              "set ylabel 'Hamming Distance'\n ");//setting the format of plot 
        fprintf(gnuplotfile, "plot [][]'-' with l ls 1 title ' Distance'\n");

        FILE* gnuplotPipe = popen("gnuplot -persistent", "w");

    FILE* fp=fopen("data.dat","w");//file storing the generated random bits
    int N;
    double P, Q;
    N = atoi(argv[1]); // the total number of bits
    P = atof(argv[2]); // the fraction of bits that reach the receiver
    Q = atof(argv[3]); // the fraction of bits that have an error

    int hamdist = 0, calcoff; // variable to store hamming distance and calculated offset
    int minsum = INT_MAX; // to find the minimum hamming distance
    int R = P * N; // number of random bits to be selected
    int R1 = Q * R; // number of random bits that are to be flipped
    int T = 0.9 * N + 1; // temporary variable
    int M = rand() % T + (0.1 * N); // generating a random offset

    int* n = (int*)malloc(N * sizeof(int)); // dynamic memory allocation of an array of N random bits of 0 and 1
    int* arr1 =(int*)malloc(N*sizeof(int));
    for (int i = 0; i < N; i++)
    {
        n[i] = rand() % 2; // allocating either 0 or 1 using random number generator
        fprintf(fp,"%d\n",n[i]);
    }
    for(int i = 0; i < N; i++)
    {
	arr1[i] = n[(i+M)%N];
		
    }
    int** r = (int**)malloc(2 * sizeof(int*));
    for (int i = 0; i < 2; i++)
        r[i] = (int*)malloc(R * sizeof(int));
 

    int* check1 = (int*)malloc((R+1) * sizeof(int)); int count1=0;//array used to check that a random element is not repeated
    for (int i = 0; i < N; i++) // to select R locations from arr and store those bit values in a 2xP separate array (location, bit_value)
    {
    	int x=rand()%N;
    	if(i>0)
    	{
    	int counter=0;//counter variable stores number of mismatches of randomly generated index to previous indices
    	for(int j=0;j<count1-1;j++)
    	{
	    	if(check1[j]!=x)/*making sure the random element has not already been generated by comparing it with previously generated 					elements*/
	    	{
		counter++;
		}
	}
		if(counter==count1-1)//checks that randomly generated index has not matched any previous index
		{
		
		check1[count1]=x;
		r[0][count1] = x;//element is added to 2d array only if it wasn't already there
		r[1][count1] = arr1[r[0][count1]];
		count1++;
		}
	}
        if(i==0)
        {
        check1[count1]=x;
	r[0][count1] = x;
	r[1][count1] = arr1[r[0][count1]];
	count1++;
		
        }
        if(count1>=R)//taking exactly R elements
        break;
    }

	int* check2 = (int*)malloc((R1+1) * sizeof(int));
	int count2=0;
    for (int i = 0; i < N; i++) // flipping some random bits with a certain probablity
    {
        int tem = rand() % R;
        if(i>0)
    	{
    	int counter=0;//counter variable stores number of mismatches of randomly generated index to previous indices
    	for(int j=0;j<count2-1;j++)
    	{
	    	if(check2[j]!=tem)
	    	{
		counter++;
		}
	}
		if(counter==count2-1)//checks that randomly generated index has not matched any previous index
		{
		count2++;
		check2[count2]=tem;
		r[1][tem] = !r[1][tem];
		}
	}
        if(i==0)
        {
        count2++;
        check2[count2]=tem;
        r[1][tem] = !r[1][tem];// flipping using NOT operator
        }
       if(count2>=R1)//taking exactly R1 elements
       break;  
    }

	int hamdistance[N];
	for(int i=0;i<N;i++)
	hamdistance[i]=0;//setting initial hamming distance for each offset as zero
	int redsize=0;//counts number of bits used to calculate Hamming distance
    for (int i = 0; i < N;i++) // moving the randomly generated array over the initial array
    {
        int redsizetemp=0;//temporary variable
        for (int k = 0; k < R; k++)
        {
            int x=rand();
            if(x%5==1)//sending only approximately 1/5th of the total received bits
            {
            redsizetemp++;
            if (r[1][k] ^ n[(r[0][k]+i)%N]) // computing hamming distance
                hamdistance[i]++;
            }
        }
        if (hamdistance[i] < minsum) // calculating the minimum hamming distance
        {
            minsum = hamdistance[i];
            calcoff = i;
            redsize=redsizetemp;
        }
    }
    for(int i=0;i<N;i++)
    {
    	fprintf(gnuplotfile,"%d %d\n",i,hamdistance[i]); //writing offset and hamming distance to a file 
    }
    
    printf("\nNumber of Bits Sent is: %d\nNumber of Bits Received is: %d\nThe Randomly Choosen Offset is : %d   \nThe Calculated Offset is : %d\nThe Minimum Hamming Distance is: %d\nNumber of bits used to calculate Offset: %d\nQuantum Bit Error Rate(QBER)(in %%): %f\n",N,R, M,calcoff,minsum,redsize,(float)minsum/redsize*100.0); // printing the output

    fflush(gnuplotfile);//plotting in gnuplot
    // freeing up the used dynamically allocated
    for (int i = 0; i < 2; i++)
        {free(r[i]);
        }
    free(check2);
    free(check1);
    free(r);
    free(n);

    return 0;
}
