/*----------------------------------------------*/

function diag (v)
{
	vd = Max (Rows(v), Columns(v));
	diagMx = {vd,vd};
	for (_v = 0; _v < vd; _v = _v + 1)
	{
		diagMx[_v][_v] = v[_v];
	}
	return diagMx;
}

/*----------------------------------------------*/

function mx_diag (v)
{
	vd = Min (Rows(v), Columns(v));
	diagMx = {vd,1};
	for (_v = 0; _v < vd; _v = _v + 1)
	{
		diagMx[_v] = v[_v][_v];
	}
	return diagMx;
}

/*-----------------------------------------------*/

function sum (mx)
{
	rd = Rows(mx);
	cd = Columns (mx);
	col_sum = {1,cd};
	for (_c = 0; _c < cd; _c = _c + 1)
	{
		_s = 0;
		for (_r = 0; _r < rd; _r = _r+1)
		{
			_s = _s + mx[_r][_c];
		}
		col_sum [_c] = _s;
	}
	return col_sum;
}

/*-----------------------------------------------*/

function table (vec1, vec2)
{
	if (Rows(vec1) == Rows(vec2) && Columns (vec1) == 1 && Columns (vec2) == 1)
	{
		maxV = 0;
		rd = Rows(vec1);
		for (_r = 0; _r < rd; _r = _r+1)
		{
			maxV = Max (maxV, Max(vec1[_r],vec2[_r]));
		}	
		tableOut = {maxV+1, maxV+1};
		for (_r = 0; _r < rd; _r = _r+1)
		{
			tableOut[vec1[_r]][vec2[_r]] = tableOut[vec1[_r]][vec2[_r]] + 1;
		}	
		return tableOut;
	}
	else
	{
		return 0;
	} 
}

/*-----------------------------------------------*/

function read_kernel_matrix (dummy)
{
	SetDialogPrompt ("Please locate a .csv with the kernel matrix");
	ExecuteAFile (HYPHY_LIB_DIRECTORY + "TemplateBatchFiles" + DIRECTORY_SEPARATOR + "Utility" + DIRECTORY_SEPARATOR + "ReadDelimitedFiles.bf");
	kern_AVL = ReadCSVTable ("",2);
	
	kernel_matrix = kern_AVL[1];
	names		  = kern_AVL[0];
	points		  = Rows(kernel_matrix);
	fprintf (stdout, "Read ", points, " points\n");
	return points;
}

/*-----------------------------------------------*/

function matrix_to_kernel (distances)
{
	/* subtract the diagonal */
	points		   = Rows (distances);
	norm_distances = distances-
					 distances["_MATRIX_ELEMENT_VALUE_ * (_MATRIX_ELEMENT_ROW_ == _MATRIX_ELEMENT_COLUMN_)"];
	
	/* convert to a kernel matrix */
	
	sigma 		  = 1;
	kernel_matrix = norm_distances["Exp (-(_MATRIX_ELEMENT_VALUE_^2)/(2*sigma))"];
	
	/* check for eigen negative eigenvalues */
	
	kernel_eig    = Eigensystem(kernel_matrix);
	min_ev		  = 1e100;
	for (k=0; k < points; k=k+1)
	{
		min_ev = Min (min_ev,(kernel_eig[0])[k]);
	}
	if (min_ev < 0)
	{
		scaled_diag = {points, points};
		
		for (k=0; k < points; k=k+1)
		{
			scaled_diag[k][k] = (kernel_eig[0])[k] - min_ev;
		}
		kernel_matrix = kernel_eig[1]*scaled_diag*Transpose(kernel_eig[1]);
	}
	return kernel_matrix;
}


/*-----------------------------------------------*/

function perform_kernel_PCA (kernel_matrix)
{
	points = Rows (kernel_matrix);

	one_matrix = {points,points} ["1/points__"];
	K_hat 	   = kernel_matrix - one_matrix*kernel_matrix-kernel_matrix * one_matrix 
							   + one_matrix*kernel_matrix*one_matrix;
							   
	for (r=0; r<points; r=r+1)
	{
		for (c=r+1; c<points; c=c+1)
		{
			m = 0.5(K_hat[r][c]+K_hat[c][r]);
			K_hat[r][c] = m;
			K_hat[c][r] = m;
		}
	}
							   
	pca  				   = Eigensystem (K_hat);
	eval 				   = pca[0];
	evect				   = pca[1];
	/* count meaningful components */

	k = 0;
	while (eval[k]>0.001 && k < points)
	{
		k=k+1;
	}
	components = k;
		
	/* normalize the eigenvectors */
	normEV 		= {components, points};
	normEigs	= {components, 1};
	
	evalSum = (((Transpose(eval))["1"])*eval)[0];
	
	for (k=0; k<components; k=k+1)
	{
		evk 		= (evect[-1][k])*(1/eval[k]);
		normEigs[k] = eval[k]/evalSum; 
		for (k2=0; k2<points; k2=k2+1)
		{
			normEV [k][k2] = evk[k2];
		}
	}
	
	kPCA_AVL = {};
	kPCA_AVL ["E-Values"]  = normEigs;
	kPCA_AVL ["E-Vectors"] = normEV;
	kPCA_AVL ["Rotated"]   = Transpose(normEV*K_hat);
	
	return    kPCA_AVL;
	
}
