// Gate class implementation file

#include <stdafx.h>

#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "gate.h"

// constructors
CGate::CGate(){}

CGate::CGate(int n, char* name)
{
	n_gqbs = n;
	g_d = (int) pow(2,n_gqbs);
	
	gname = name;

	garray = new CComplex[g_d*g_d];
	assert(garray != 0);			// ensure allocation
}

// destructor

CGate::~CGate()
{
	delete[] garray;	// reclaim the memory
}

// accessor functions

int CGate::retn_gqbs(void)
{
	return n_gqbs;
}

char* CGate::retname(void)
{
	return gname;
}

// gate application functions

void CGate::apply(CHilbert* state_vec)
{	
	int n_qbs = state_vec->numbits();	// number of qubits in entire space
	int n_bs = state_vec->numbs();		// number of basis states spanning
										// the entire space
	
	CHilbert* state_vec_tmp = new CHilbert(n_qbs);	// temporary state_vector 
	*state_vec_tmp = *state_vec;

	CComplex* sum = new CComplex();

	for(int i = 0; i < n_bs; i++)
	{
		sum->set(0,0);

		for(int j = 0; j < n_bs; j++)
			*sum += garray[i * n_bs + j] * state_vec->retamp(j);

		state_vec_tmp->setamp(i,*sum);			
	}

	delete sum;
	
	*state_vec = *state_vec_tmp;
	delete state_vec_tmp;
}

void CGate::applysub(CHilbert* state_vec, int* gindex)
{
	int n_qbs = state_vec->numbits();	// number of qubits in entire space
	int n_bs = state_vec->numbs();		// number of basis states
										// in the entire space
	int r_d = (n_qbs - n_gqbs);			// dimensions of rest of space
										// i.e. the identity space

	int i, j = 0;

	int* rest = new int[r_d];	// array of qubit numbers for the identity space
	assert(rest != 0);			// ensure allocation

	// build array of idenitiy space qubit numbers
	for(i = 0; i < n_qbs; i++)
	{	
		if(!search(n_gqbs, i, gindex))
		{
			rest[j++] = i;
		}
	}

	CHilbert* state_vec_tmp = new CHilbert(n_qbs);	// temp state_vector
	*state_vec_tmp = *state_vec;				// copy current state_vector

	// the following are indexes used to determine
	// the elements required in the multiplication
	// i.e. in the tensor product of the gate and identity
	// space matrices
	// example:
	// entire space basis state |11001>
	// gate acts on qbits 0 and 2
	// -> corresponding basis state for the gate is |01>
	// index is then 1
	int giindex;	// gate input index
	int goindex;	// gate output index
	int idiindex;	// id input index
	int idoindex;	// id output index

	CBstate* ibasis = new CBstate(n_qbs);
	CBstate* obasis = new CBstate(n_qbs);

	CComplex* sum = new CComplex(); // running complex sum

	for(i = 0; i < n_bs; i++)
	{
		sum->set(0,0);

		ibasis->setbasis(i);
		giindex = ibasis->small_index(n_gqbs, gindex);
		idiindex = ibasis->small_index(r_d, rest);

		for(int j = 0; j < n_bs; j++)
		{
			obasis->setbasis(j);
			goindex = obasis->small_index(n_gqbs, gindex);
			idoindex = obasis->small_index(r_d, rest);

			// test if on leading diagonal of identity space
			// if not this element doesn't contribute to the multiplication
			if(idiindex == idoindex)
				*sum += garray[giindex * g_d + goindex] * state_vec->retamp(j);
		}

		state_vec_tmp->setamp(i,*sum);
	}

	delete sum;
	delete obasis;
	delete ibasis;

	*state_vec = *state_vec_tmp;
	delete state_vec_tmp;
	
	delete[] rest;
}

// serach functions
bool CGate::binsearch(int n, int v, int* array)
{
	int low, high, mid;
	
	low = 0;
	high = n - 1;

	while(low <= high)
	{
		mid = (low+high)/2;
		if(v < array[mid])
			high = mid - 1;
		else if (v > array[mid])
			low = mid + 1;
		else
			return true;	// match found
	}

	return false;	// no match found
}

bool CGate::search(int n, int v, int* array)
{
	for(int i = 0; i < n; i++)
	{
		if(v == array[i])
			return true;	// match found
	}

	return false;	// no match found
}

// operators

CGate& CGate::operator =(CGate& rhs)
{	
	this->n_gqbs = rhs.n_gqbs;
	this->g_d = rhs.g_d;
	this->gname = rhs.gname;
	this->garray = rhs.garray;

	return *this;
}

