// Triangle Matrix Helper
// Ben Axelrod
// 01/08/06


#include <iostream>
#include <cstdlib>
using namespace std;

//
// USAGE
//
// Trag_eq() and Trag_noeq() give you the indicies of a 1D array as if it
// where a triagular 2D matrix.  A good usage of this is when you have
// a large distance matrix or another sort of symetric similarity matrix.
// You can store it with half the space by using these techniques.  There
// are two cases one might be interested in, when the row is allowed to be
// equal to the column, and when they are not.  
//
// NOTE: max 1D array size for row = col case: ((N*N)-N)/2+N
// NOTE: max 1D array size for row != col case: ((N*N)-N)/2


//
// ABOUT DERIVATION
//
// The first key number on each row was noticed to be row*N-TriangleNumber
// Where: row is the row number (starting with 0)
//        N is the size of the matrix (elements per side)
//        and TriangleNumber is an integer sequence defined by: N(N+1)/2
// See: http://www.research.att.com/~njas/sequences/A000217 for more info.
//
// For example when N=4, and the row can equal the column, the key numbers 
// are 0, 4, 7, and 9.  Creating this diagram:
//
//   0 1 2 3
// 0 0 1 2 3
// 1   4 5 6
// 2     7 8
// 3       9
//
// From this equation: row*N-N(N+1)/2, it is easy to get the rest of the 
// indecies.
//


int Trag_noeq(int row, int col, int N)
{      
	//assert(row != col); //You can add this in if you like
	if (row<col)
		return row*(N-1) - (row-1)*((row-1) + 1)/2 + col - row - 1;
	else if (col<row)
		return col*(N-1) - (col-1)*((col-1) + 1)/2 + row - col - 1;
	else
		return -1;
}


int Trag_eq(int row, int col, int N)
{      
	if (row <= col)
		return row*N - (row-1)*((row-1) + 1)/2 + col - row;
	else if (col<row)
		return col*N - (col-1)*((col-1) + 1)/2 + row - col;
}

int main( int argc, char *argv[])
{
	int i, j, N;
	int maxcell_eq, maxcell_noeq;

	if (argc == 1)
		N = 5;
	else if (argc == 2)
		N = atoi(argv[1]);
	else
	{
		cout << "\n ERROR: Usage: trag N\n";
		cout << "where N = side of matrix\n\n";
		exit(1);
	}


	cout << "\nTrag_eq indicies:\n";
	for(j=0; j<N; j++)
	{
		for(i=0; i<N; i++)
		{
			cout.width(4);
			cout << Trag_eq(j,i,N);
		}
		cout << endl;
	}

	cout << "\nTrag_noeq indicies:\n";
	for(j=0; j<N; j++)
	{
		for(i=0; i<N; i++)
		{
			cout.width(4);
			cout << Trag_noeq(j,i,N);
		}
		cout << endl;
	}


	//
	// Trag_eq example usage
	//
	cout << "\nTrag_eq example:\n";
	cout << "cell (0,0) = 11\n";
	cout << "cell (0,1) = 12\n";
	cout << "cell (1,2) = 13\n";

	// declare halfsize 1D array
	maxcell_eq = ((N*N)-N)/2+N;
	int array1[maxcell_eq];

	//clear array
	for(i=0; i<maxcell_eq; i++)
		array1[i] = 0;

	//set some cells
	array1[Trag_eq(0,0,N)] = 11;
	array1[Trag_eq(0,1,N)] = 12;
	array1[Trag_eq(1,2,N)] = 13;

	//read a cell
	int firstcell1 = array1[Trag_eq(0,0,N)];

	cout << "\nThe full matrix:\n";

	//print entire matrix
	for(j=0; j<N; j++)
	{
		for(i=0; i<N; i++)
		{
			cout.width(4);
			cout << array1[Trag_eq(j,i,N)];
		}
		cout << endl;
	}


	//
	// Trag_noeq example usage
	//
	cout << "\nTrag_noeq example:\n";
	cout << "cell (0,1) = 11\n";
	cout << "cell (0,2) = 12\n";
	cout << "cell (1,2) = 13\n";

	// declare halfsize 1D array
	maxcell_noeq = ((N*N)-N)/2;
	int array2[maxcell_noeq];

	//clear array
	for(i=0; i<maxcell_noeq; i++)
		array2[i] = 0;

	//set some cells
	array2[Trag_noeq(0,1,N)] = 11;
	array2[Trag_noeq(0,2,N)] = 12;
	array2[Trag_noeq(1,2,N)] = 13;

	//read a cell
	int firstcell2 = array2[Trag_noeq(0,0,N)];

	cout << "\nTrag_noeq example:\n";

	//print entire matrix
	for(j=0; j<N; j++)
	{
		for(i=0; i<N; i++)
		{
			cout.width(4);
			if (Trag_noeq(j,i,N) < 0)
				cout << " ";
			else
				cout << array2[Trag_noeq(j,i,N)];
		}
		cout << endl;
	}

}


