#include <iostream>
#include <time.h>
#include <math.h>
#include "graph_holder.h"
#include <list>

using namespace std ;

extern void find_xy(int p, int *x, int *y);
extern int reduceq(int e, int q);
int normal_mtwo_equals(mtwo a, mtwo b);
int embed(decomposition *d_ptr,  int q, mtwo **m_ptr, int up_lim);
//mtwo::print_matrix();


mtwo::mtwo() {
	q[0]=0; q[1]=0; q[2]=0; q[3]=0;

}

mtwo::mtwo(int n1, int n2, int n3, int n4) {
			q[0] = n1;
			q[1] = n2;
			q[2] = n3;
			q[3] = n4;
}

void mtwo::print_matrix() {
	std::cout << "\n";
		std::cout << "[ " << q[0] << "   " << q[1] << "]";
	std::cout << "\n[ " << q[2] << "   " << q[3] << "]";
	std::cout << "\n";

}

void mtwo::reduce_q(int qq) {
	int i;
	for(i=0;i<4;i++) {
		if (q[i] > 0) {
//			std::cout << "\n q[" << i << "] is bigger than zero!";
//			std::cout << "\n old q[" << i << "] is " << q[i];
			q[i] = q[i]%qq;
//			std::cout << "\n new q[" << i << "] is " << q[i];
		}
		if (q[i] < 0) {
//			std::cout << "\n q[" << i << "] is less than zero!";
//			std::cout << "\n old q[" << i << "] is " << q[i];
			q[i] = qq-abs((q[i]%qq));
//				std::cout << "\n new q[" << i << "] is " << q[i];
		}
	}
}


int embed(decomposition *d_ptr,   int q, mtwo **m_ptr, int up_lim) {
int x,y;
std::cout << "\n q =" << q;
 find_xy (q, &x, &y);
std::cout << "\n x = " << x;
std::cout << "\n y = " << y;
list <mtwo> m1;
list <mtwo>::iterator m1_Iter;
int l;

for (l=0; l < up_lim; l++)   {

//	d_ptr[l].array[0]+=1; 
//	std::cout << "\n l = " << l;
//	std::cout << "\n 1,1 entry is " << d_ptr[l].array[0]+d_ptr[l].array[1]*x+d_ptr[l].array[3]*y;
//	std::cout << "\n 1,2 entry is " << -d_ptr[l].array[1]*y+d_ptr[l].array[2]+d_ptr[l].array[3]*x;
//	std::cout << "\n 2,1 entry is " << -d_ptr[l].array[1]*y-d_ptr[l].array[2]+d_ptr[l].array[3]*x;
//	std::cout << "\n 2,2 entry is " << d_ptr[l].array[0]-d_ptr[l].array[1]*x-d_ptr[l].array[3]*y;
	
 mtwo x(d_ptr[l].array[0]+d_ptr[l].array[1]*x+d_ptr[l].array[3]*y,-d_ptr[l].array[1]*y+
		d_ptr[l].array[2]+d_ptr[l].array[3]*x,-d_ptr[l].array[1]*y-d_ptr[l].array[2]+d_ptr[l].array[3]*x,
		d_ptr[l].array[0]-d_ptr[l].array[1]*x-d_ptr[l].array[3]*y);
		m1.push_back(x);
	//	x.print_matrix();
		
		 }
		 

*m_ptr = new mtwo[m1.size()]; 



int i =0;
for (m1_Iter =  m1.begin(); m1_Iter != m1.end(); ++m1_Iter)     {
                 (*m_ptr)[i++]= *m1_Iter;
				 (*m_ptr)[i-1].reduce_q(q);
}


  return (m1.size());





}

mtwo mult(mtwo x,int a, int p) {
	mtwo ret;
	ret.q[0]=reduceq(x.q[0]*a,p);
	ret.q[1]=reduceq(x.q[1]*a,p);
	ret.q[2]=reduceq(x.q[2]*a,p);
	ret.q[3]=reduceq(x.q[3]*a,p);
	return ret;

}

int normal_mtwo_equals(mtwo a, mtwo b) {

	int ret=0;
	if (a.q[0]==b.q[0]&&a.q[1]==b.q[1] && a.q[2]==b.q[2] && a.q[3] == b.q[3] )
		ret = 1;

	return ret;


}

mtwo mmultiply(mtwo a, mtwo b, int q) {

	mtwo ret;
	ret.q[0] = reduceq(a.q[0]*b.q[0] + a.q[1]*b.q[2],  q);
	ret.q[1] = reduceq(a.q[0]*b.q[1]+a.q[1]*b.q[3],q);
	ret.q[2] = reduceq(a.q[2]*b.q[0]+a.q[3]*b.q[2],q);
	ret.q[3] = reduceq(a.q[2]*b.q[1]+a.q[3]*b.q[3],q);



return ret;

}