#include <iostream>
#include <time.h>
#include <math.h>
#include "graph_holder.h"

extern void swap_int (int *num1, int *num2);
extern void permute  (int *array, int array_size);
void press_enter();


partitioned_graph::partitioned_graph(int l_poin, int l_degree, int *r_grps, int gr_num, 
				 int *r_degrees) : left_points(l_poin), left_degree(l_degree),  group_num (gr_num)	{

	 int i;
	 right_points = 0;
	
		for (i=0; i < group_num; i++)
			right_points+= r_grps[i];

		try{

		
			right_groups = new int [group_num];
			memcpy(right_groups, r_grps, group_num * sizeof(int));
			right_degrees = new int [group_num];
			memcpy(right_degrees, r_degrees,  group_num * sizeof(int));

			// 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();

}

partitioned_graph::~partitioned_graph()	{
		
	int i;
		
		delete[] right_groups;
		delete[] right_degrees;

		for (i=0; i < left_points; i++)
			delete[]	graph[i];
		delete[] graph;
	}

void partitioned_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];
			int r_pnt = socket_arr[i];
			int r_idx=0;
			for (j=0; j < group_num; j++)	{
				if (r_pnt > right_groups[j]*right_degrees[j])	{
						r_pnt -= right_groups[j]*right_degrees[j];
						r_idx += right_groups[j];
						continue;
			    }

				r_pnt /= right_degrees[j]; // index in a group
				right_conn[i]= r_pnt + r_idx;
				break;
			}
		}

	
//		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 partitioned_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 partitioned_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);
}


void partitioned_graph::decode( int *input_vec, int vec_len, int q, int *r_grps) {

press_enter();

	int i,j,k;

int  *vec_cpy = new int [vec_len];
int *left_indices = new int [left_degree];

int max_r_degree=right_degrees[0];

for (i=1; i < group_num; i++)
	max_r_degree = max_r_degree > right_degrees[i] ? max_r_degree : right_degrees[i];

int *right_indices = new int [max_r_degree];

//std::cout << "\n Recieved Vector: ";
//	for (i=0;i<vec_len;i++)
//		std::cout << " " << input_vec[i];
//press_enter();

int nv=0;
for(i=0;i<=q-1;i++)
	nv= nv + r_grps[i];

int sz;




	for (i=0;i < left_points; 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];
	press_enter();

	for (j=0;j < right_points; j++) {
		sz = find_right_indices(right_indices,j);
		//std::cout << "\n after find_right_indices";
		//std::cout << "\n j = " << j;
	//	for (i=0;i<max_r_degree;i++)
	//		std::cout << "\n right_indices[" << i << "] = " << right_indices[i];

		decode_right(input_vec, right_indices,sz);
	}

	std::cout << "\n After right decoding: ";
		for (i=0;i<vec_len;i++)
			std::cout << " " << input_vec[i];
		press_enter();

		std::cout << "\n Entering while loop!";

while (1)	{

	memcpy (vec_cpy, input_vec, vec_len);
	for (i=0;i < left_points; i++) {
		find_left_indices(left_indices,i,nv);
	//	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];
	press_enter();

	for (j=0;j < nv; j++) {
		sz=find_right_indices(right_indices,j);
		decode_right(input_vec, right_indices,sz);
	}

	std::cout << "\n After right decoding: ";
		for (i=0;i<vec_len;i++)
			std::cout << " " << input_vec[i];
		press_enter();


	if (! memcmp(vec_cpy, input_vec, vec_len))
				break;

}


}


void partitioned_graph::decode_left(int *input_vec, int *left_indices) {

int i;

for (i=0;i<left_degree;i++) {
	if (left_indices[i] != -1)
	input_vec[left_indices[i]] +=1;


}



}

void partitioned_graph::decode_right(int *input_vec, int *right_indices, int sz) {

int i;

int max_r_degree;

for (i=1; i < group_num; i++)
	max_r_degree = max_r_degree > right_degrees[i] ? max_r_degree : right_degrees[i];

for (i=0;i<sz;i++)
	input_vec[right_indices[i]] +=2;


}

void partitioned_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 partitioned_graph::find_left_indices(int *left_indices,int idx, int nv) {
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]) {
					if (j <= nv-1)
						left_indices[ln_indx]=j;
					else
						left_indices[ln_indx]=-1;
					ln_indx++;
			}
			}
	}
		index_left(idx,left_indices,ln_indx);
}



//void partitioned_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;

//}


int partitioned_graph::find_right_indices(int *right_indices,int idx) {
	int i,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);
return 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 partitioned_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++) {
		if (line[i] != -1)
			line[i] = sum + i;

      //  std::cout << "\n line[" << i;
		//std::cout << "] is " << line[i];
	}



	return;

}

void partitioned_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];
	}



	return;

}





