#include <iostream>
#include <time.h>
#include "graph_holder.h"

extern void swap_int (int *num1, int *num2);
extern void permute  (int *array, int array_size);
void press_enter();
void bi_matrix(char *raman, int pts, char *bm);
int full(int *array, int size);
void graph_iter(int *left,int *right,char *raman,int pts,int prev,int idx, int lr);
int exis( int *array, int size, int elem);
extern void Hamming15_decode(int *input, int *output);
extern void BCH15_ML_decode(int *input, int *output);
extern void BCH15_alg_decode(int *input, int *output);
extern void hamm31decode(int *input, int *output);
extern void bch31decode_manual(int *input, int *output);
extern void bch31decode(int *input, int *output);





bipartite_graph::bipartite_graph(int l_poin, int r_point, int l_con, int r_con) : 
	left_points(l_poin), right_points(r_point), left_degree(l_con), 
		right_degree(r_con)	{

	try{
	
		int i;
		
		// allocating the matrix
		
		graph = new char* [left_points];

		for (i=0; i < left_points; i++)	{
			graph[i] = new char [right_points];
			memset (graph[i], 0, right_points*sizeof (char));
		}
	}
	catch (...)	{
		std::cout << "Out of memory";
	}
	std::cout << "\ngraph created\n";
	connect_graph();

}

	bipartite_graph::bipartite_graph(char *raman, int pts, int deg) {
		
			try{
	
		int i,j;
		left_points = pts/2;
		right_points = pts/2;
		left_degree = deg;
		right_degree=deg;
		
		// allocating the matrix
		
		graph = new char* [left_points];

		for (i=0; i < left_points; i++)	{
			graph[i] = new char [right_points];
			memset (graph[i], 0, right_points*sizeof (char));
		}



		
			 

			std::cout << "\ngraph created\n";
	char *bm = new char[pts*pts/4];
    bi_matrix(raman,pts,bm);
	 


//	  std::cout << "\n new matrix: ";
	  	   	 for (i=0;i<left_points;i++) {

		//	 std::cout << "\n";
				 for (j=0;j<right_points;j++) {
		//			graph[i][j]= bm[i*pts/2+j];
					 //std::cout << " " <<  bm[i*pts/2+j];
					 //std::cout << " " << (int) graph[i][j];
				 }
			 }

	


			
	delete[] bm;
		
	}
	catch (...)	{
		std::cout << "Out of memory";
	}

	}

bipartite_graph::~bipartite_graph()	{
		int i;

		for (i=0; i < left_points; i++)
			delete[]	graph[i];
		delete[] graph;	
	}

void bipartite_graph::connect_graph()	{
	
	int *socket_arr;
	int socket_arr_size;
	int *right_conn;
	int *indices;
	


	srand( (unsigned)time( NULL ) );
	
	int i, j, k, m, exists, number_repeated;

	socket_arr_size = left_points * left_degree;
	socket_arr = new int[socket_arr_size];
	right_conn = new int[socket_arr_size];
	indices = new int[socket_arr_size];
	memset (socket_arr, 0, socket_arr_size*sizeof (int));
	
	int *temp_perm;
	temp_perm = new int[socket_arr_size];
	
	 
	// std::cout << "socket array size = " << socket_arr_size;
	
	// generate permutation
	permute(socket_arr, socket_arr_size);

	

//	std::cout << "\npermutation: socket_arr: ";

	

	do {
		memset (indices, 0, socket_arr_size*sizeof (int));

	
		
//		std::cout << "\n socket_array";
		for( i = 0;   i < socket_arr_size; i++ ) {
//			std::cout << " " << socket_arr[i];
			right_conn[i]=socket_arr[i] / right_degree;
		}

	//	std::cout << "XXX";
	
	
//		for( i = 0;   i < socket_arr_size; i++ )	{
//			std::cout << "\n connect: left_ind =" << i / left_degree;
//			std::cout << " to right_ind =" << right_conn[i];
//		}
	

	

		m=0;

		for( i = 0; i <= socket_arr_size - left_degree; i=i+left_degree) {
			for (j=i+1; j < i+left_degree;j++) { 
				exists = 0;
				for (k=1; k <= j-i; k++) {
					if (right_conn[j] == right_conn[j-k]) {
						exists = 1;
						break;
					}
				}
				if (exists == 1) {
					indices[m]=j;
					m++;
			}
				}
		}

		number_repeated=m;

		       
	
//		std::cout << "the number repeated is " << number_repeated;

		
		permute(temp_perm,socket_arr_size);

		for (i=0;i<number_repeated;i++) {
		    swap_int( &socket_arr[indices[i]], &socket_arr[temp_perm[i]]);
			//std::cout << "\nindex  " << indices[i];
            //std::cout << "is being switched with index   " << temp_perm[i];
		}

		

		for( i = 0;   i < socket_arr_size; i++ ) {
			//std::cout << "\n indices[" << i;
			//std::cout << "] = " << indices[i];
    	}

	//std::cin >> temp;
	} while(number_repeated > 0);

	delete[] temp_perm;
	
//	std::cout << "enter 1";
	// press_enter();

//	std::cout << "\n right_conn: " ;

//	for (i=0;i < socket_arr_size; i++) 
//			std::cout << " " <<  right_conn[i];

//	std::cout << "\n i/left_degree: " ;
			
//	for (i=0;i < socket_arr_size; i++) 
//			std::cout << " " <<  i/left_degree;

//	std::cout << " \n socket_arr_size" << socket_arr_size;
//	std::cout << " \n left_degree" << left_degree;


	
//	std::cout << "\nsocket_arr_size=" << socket_arr_size;

	for( i = 0;   i < socket_arr_size; i++ )	{
//			std::cout << "\ni=" << i << ", i /left_degree=" << i / left_degree << " right_conn[i]=" << right_conn[i];
			graph [i / left_degree][right_conn[i]] = 1;
	}

//	std::cout << "\nbefore delete\n";

	delete[] right_conn;
	delete[] indices;
	delete[] socket_arr;
//	std::cout << "\ngraph connected\n";

//	std::cout << "\n enter 2";
//	press_enter();
	
	return;

}

void bipartite_graph::print_graph()	{

	int i, j;

	std::cout << "print_graph:\n";
	
	for (i=0; i < left_points; i++)	{
		for (j=0; j < right_points; j++)
			std::cout <<  (int )graph[i][j] << " ";
		std::cout << "\n";
	}
	return;
}

int bipartite_graph::find_edges()	{
	int i, j;
	int edges = 0;
	
	for (i=0; i < left_points; i++)	
		for (j=0; j < right_points; j++)
			if (graph[i][j]) edges++;

	return (edges);
}
/*
edge_vertex *bipartite_graph::create_edge_vertex() {
	
    int edges = find_edges();
	edge_vertex *p_edge_vrtx;

	p_edge_vrtx = new edge_vertex(left_points, right_points, edges);

	p_edge_vrtx->set_edge_graph(graph);

	return (p_edge_vrtx);

	

}*/


void bipartite_graph::decode( int *input_vec, int vec_len) {

// press_enter();
	
	int i,j,nones;
	// int k;

int  *vec_cpy = new int [vec_len];
int *left_indices = new int [left_degree];
int *right_indices = new int [right_degree];

//std::cout << "\n Recieved Vector: ";
//	for (i=0;i<vec_len;i++)
//		std::cout << " " << input_vec[i];
//press_enter();


while (1)	{

memcpy (vec_cpy, input_vec, vec_len);


for (i=0;i < left_points; i++) {
	//std::cout << "i = " << i;
	find_left_indices(left_indices,i);
    //std::cout << "\n i is " << i;
	//std::cout  << "; left_indices is ";
	//	for (k=0;k < left_degree;k++)
	//		std::cout << " " << left_indices[k];
	decode_left(input_vec, left_indices);
}

 std::cout << "\n After left decoding: ";
/*	for (i=0;i<vec_len;i++)
		std::cout << " " << input_vec[i]; 
			nones=0;
		for(i=0;i<vec_len;i++)
			nones = nones + input_vec[i];
			
		std::cout << "\n number of 1's in the codeword: " << nones; */
//press_enter();

for (j=0;j < right_points; j++) {
		//std::cout << "j = " << j;
	find_right_indices(right_indices,j);
	decode_right(input_vec, right_indices);
}

 std::cout << "\n After right decoding: ";
/*	for (i=0;i<vec_len;i++)
		std::cout << " " << input_vec[i]; 
	nones=0;
	for(i=0;i<vec_len;i++)
			nones = nones + input_vec[i];
			
		std::cout << "\n number of 1's in the codeword: " << nones;
	press_enter(); */
   

if (! memcmp(vec_cpy, input_vec, vec_len))
			break;

}

delete[]  vec_cpy; 
delete[] left_indices;
delete[] right_indices;


}


void bipartite_graph::decode_left(int *input_vec, int *left_indices) {

int i;

int *v = new int[left_degree];
int *o = new int[left_degree];

for (i=0;i<left_degree;i++) 
	v[i]=input_vec[left_indices[i]];
bch31decode_manual(v,o);
//BCH15_alg_decode(v, o);

for (i=0;i<left_degree;i++) 
	input_vec[left_indices[i]]=o[i];

delete[] v;
delete[] o;



}

void bipartite_graph::decode_right(int *input_vec, int *right_indices) {

int i;
int *v = new int[right_degree];
int *o = new int[right_degree];

for (i=0;i<right_degree;i++) 
	v[i]=input_vec[right_indices[i]];

//bch31decode_manual(v,o);
hamm31decode(v,o);

for (i=0;i<right_degree;i++) 
	input_vec[right_indices[i]]=o[i];



delete[] v;
delete[] o;
}

void bipartite_graph::find_left_indices(int *left_indices,int idx) {
int i,j,ln_indx;

	for (i=0; i < left_points; i++)	{
			ln_indx = 0;
			for (j=0;  j < right_points; j++)	{
				if (graph[i][j])	
					left_indices [ln_indx++]=j;
			}
		index_left(idx,left_indices,ln_indx);
					}
	
//int j;
//	int ln_index = 0;
//	for (j=0;j < right_points; j++) {
//		if ( graph[index][j] == 1) {
//			left_indices[ln_index]=j;
//			ln_index++;
//		}

//	}

}


//void bipartite_graph::index_left(int q, int *line, int ln_index) { 
//	int i,j;
//	int sum = 0;
//	for (i=0;i <= q-1; i++) {
//		for (j=0; j < right_points; j++) {
  //           sum = sum + graph[i][j];
//		}
//	}
//	std::cout << "\n i is " << q;
//	std::cout << "\n sum is " << sum;

//	for (i=0;i < ln_index;i++) {
//		line[i] = sum + i;
		
  //      std::cout << "\n line[" << i;
	//	std::cout << "] is " << line[i];
//	}



//	return;

//}


void bipartite_graph::find_right_indices(int *right_indices,int idx) {
	//int i;
	int j;
	int ln_indx;
	
			ln_indx = 0;
			for (j=0;  j < left_points; j++)	{
				if (graph[j][idx])	{
					right_indices [ln_indx]=j;
				ln_indx++;
				}
			}
			index_right(idx,right_indices,ln_indx);

			
	


//	int j;
//	int ln_index = 0;
//	for (j=0;j < left_points; j++) {
//		if ( graph[j][index] == 1) {
//			right_indices[ln_index]=j;
//			ln_index++;
//		}
//
//	}

}

void bipartite_graph::index_left(int q, int *line, int ln_index) { 
	int i,j;
	int sum = 0;
	for (i=0;i <= q-1; i++) {
		for (j=0; j < right_points; j++) {
             sum = sum + graph[i][j];
		}
	}
	//std::cout << "\n i is " << q;
	//std::cout << "\n sum is " << sum;

	for (i=0;i < ln_index;i++) {
		line[i] = sum + i;
		
      //  std::cout << "\n line[" << i;
		//std::cout << "] is " << line[i];
	}



	return;

}

void bipartite_graph::index_right(int q, int *line, int ln_index) { 
	int i,j,k;
	int idx2;
	int *sum = new int[ln_index];
	int *remainder = new int[ln_index];

//	std::cout << " \n ";
	for (i=0;i < ln_index;i++) {
		//std::cout << "|| initial line[" << i;
		//std::cout << "] is " << line[i];
	}
   
	for (i=0;i <ln_index;i++)
		sum[i]=0;

	for (k=0;k <ln_index; k++) {
	for (i=0;i <= line[k]-1 ; i++) {
		for (j=0; j < right_points; j++) {
             sum[k] = sum[k] + graph[i][j];
		}
	}
	}


	//std::cout << "\n i is " << q;
	//std::cout << "\n sum is ";
	//for (i=0;i < ln_index; i++)
	//	std::cout << " " << sum[i];

	for (k=0; k < ln_index;k++) {
		idx2=0;
		for (i=0; i < q; i++) {
			if (graph[line[k]][i] == 1)
				idx2++;
		}
		remainder[k]=idx2;

	}
//	std::cout << " \n ";
	for (i=0;i < ln_index;i++) {
		line[i] = sum[i] + remainder[i];
		//std::cout << "|| remainder[" << i;
		//std::cout << "] is " << remainder[i];
        //std::cout << "|| final line[" << i;
		//std::cout << "] is " << line[i];
	}


delete[] sum;
delete[] remainder;
	return;

}

void bi_matrix(char *raman, int pts,  char *bm) {

	
		std::cout << "\n bi_matrix! pts = " << pts;
			

	int i,j;

int *left = new int[pts/2];
int *right= new int[pts/2];
std::cout << "\n defined left and right";

for(i=0;i<pts/2;i++) {
	left[i]=-1;
	right[i]=-1;
}


std::cout << "\n set left and right with -1's";


memset (bm, 0, (pts*pts/4)*sizeof (char));

std::cout << "\n set bm with zeros";

std::cout << "\n calling graph iter!";
graph_iter(left,right,raman,pts,0,0,0);
std::cout << "\n finished all the graph_iters ";

std::cout << "\n LEFT";
int sss=0;
for(i=0;i<pts/2;i++) {
	std::cout << " " << left[i];
	sss = sss+left[i];
}

std::cout << "\n RIGHT";
for(i=0;i<pts/2;i++) {
	std::cout << " " << right[i];
	sss = sss+right[i];
}

//std::cout << "\n sum of the numbers is " << sss;
//press_enter();



for (i=0;i<pts/2;i++) {
	for (j=0;j<pts/2;j++) {
		if (raman[left[i]*pts+right[j]]==1)
			bm[i*pts/2+j]=1;


	}
}

/*std::cout << "\n left: ";
for(i=0;i<pts/2;i++)
std::cout << " " << left[i];

std::cout << "\n right: ";
for(i=0;i<pts/2;i++)
std::cout << " " << right[i];
*/

	
	
delete[] left;
delete[] right;	
}

int full(int *array, int size) {

	int i;
	int ret=1;
	for (i=size-1;i>=0;i--) {
		if (array[i]==-1) {
			ret=0;
			break;
		}



	}
return ret;
}

void add_arr( int *array, int size, int elem) {

int i;
int exists=0;

for (i=0;i<size;i++) {
	if (array[i]==elem)
		exists=1;
	if (array[i]==-1)
		break;

}
if (exists!=1)
	array[i]=elem;

}

int exis( int *array, int size, int elem) {
	int ret=0;
	int i;

	for (i=0;i<size;i++) {
		if (array[i]==elem) {
            ret=1;
			break;
		}


	}
return ret;
}

void graph_iter(int *left,int *right,char *raman,int pts,int prev,int idx, int lr) {
		int i;
		static int cnt=0;

		std::cout << "\n Calling graph_iter! idx = " << idx;
		if (idx==445)
			std::cout << "\n breakpoint!";
		
/*		std::cout << "\n \n";
	
	std::cout << "\n left: ";
	for(i=0;i<pts/2;i++)
std::cout << " " << left[i];
	std::cout << "\n right: ";
	for(i=0;i<pts/2;i++)
std::cout << " " << right[i];

	std::cout << "\n full(left) = " << full(left,pts/2);
	std::cout << "\n full(right) = " << full(right,pts/2);
	press_enter(); */
		try	{
	if ((full(left,pts/2)!=1) || (full(right,pts/2)!=1)) {
	//	std::cout << "\n Entering big if statement";

//	int *rr = new int[pts/2];
//	permute(rr,pts);


	if (lr == 0) {
		if (idx == 445 )	{
				cnt++;
		std::cout << " count =" <<cnt;
	}
		std::cout << "\n lr = 0! that means we are considering a point on the left";
		add_arr(left,pts/2,idx);
		for(i=0;i<pts;i++) { 
	/*		std::cout << "\n";
			std::cout << "\n i = " << i;
	std::cout << "\n prev = " << prev;
			std::cout << "\n raman[idx*pts+" << i << "]] = " << raman[idx*pts+i];
			std::cout << "\n exis = " << exis(right,pts/2,i); */
			
			
			if (idx == 445 )	{
				
				std::cout <<" i=" << i;
				if (i ==1318)	
					std::cout << "stop!";
			}
			
			if (raman[idx*pts+i]==1 ) 
				if (i!=prev )
					if (exis(right,pts/2,i)==0){
			
				
				std::cout << "\n Found out that at idx = " << idx << " there is a 1 at position " << i;
				graph_iter(left,right,raman,pts,idx,i,1); }
		}
	}

		if (lr == 1) {
			std::cout << "\n lr = 1! that means we are considering a point on the right";
		add_arr(right,pts/2,idx);
		for(i=0;i<pts;i++) { 
				if((idx ==445) && (i==1319)) {
					std::cout << "\n " << i;
				}
			if (raman[idx*pts+i]==1) {
				if (i!=prev) {
					if(exis(left,pts/2,i)==0) {
								/*	if (idx == 285 && ctn>1 )	{
										std::cout << "\n idx = " << idx;
										std::cout << "\n ctn = " << ctn;
										std::cout << "\n i = " << i;
										std::cout << "\n prev = " << prev;
										std::cout << "pts = " << pts;
	
			} */
				//std::cout << "\n Found out that at idx = " << idx << " there is a 1 at position " << i;
				graph_iter(left,right,raman,pts,idx,i,0); }
				} } }
	}


//delete[] rr;
	}
		}
		catch (...)	{
			std::cout << "\nproblem: idx="<<idx << "i=" <<i;
		}
}

 