//----------------------------------------------------------------
//	vmatrix.h
//
//	Defines a dynamically allocated matrix of fractions.
//
//	programmer: ALLAN CRUSE
//	written on: 15 SEP 1992
//	revised on: 28 NOV 2002
//	revised on: 28 APR 2011 -- added public 'obtain()' method
//	revised on: 29 APR 2011 -- corrected input-prompt message 
//----------------------------------------------------------------

#include "fraction.h"

class FractionMatrix
{
	Fraction 	*tableau;
	unsigned int 	nrows, ncols;
	unsigned int	locn( unsigned int r, unsigned int k );
public:
	FractionMatrix( int r, int k );	// default constructor	
	~FractionMatrix( void ) { delete [] tableau; }

	Fraction obtain( unsigned int i, unsigned int j );
	void assign( unsigned int i, unsigned int j, Fraction f );
	void exchange_rows( int i, int j );
	void rescale_row( Fraction x, int i );
	void combine_rows( Fraction x, int i, int j );
	int row_reduced( Fraction& determinant );
	
	friend ostream& operator << ( ostream& s, FractionMatrix& a );
	friend istream& operator >> ( istream& s, FractionMatrix& a );
};


inline unsigned int FractionMatrix::locn( unsigned int r, unsigned int k )
{	
	return	k + ncols * r;
}	


FractionMatrix::FractionMatrix( int r, int k )
{
	unsigned int	size;	

	nrows = ( r > 0 ) ? r : 0;
	ncols = ( k > 0 ) ? k : 0;
	size = nrows * ncols;
	
	tableau = new Fraction[ size ];
}	


void FractionMatrix::exchange_rows( int i, int j )
{
	Fraction	temp;

	for (int k = 0; k < ncols; k++)
		{
		temp = tableau[ locn(i,k) ];
		tableau[ locn(i,k) ] = tableau[ locn(j,k) ];
		tableau[ locn(j,k) ] = temp;
		}
}	


void FractionMatrix::rescale_row( Fraction x, int i )
{
	if ( x == 0 ) return;
	for (int k = 0; k < ncols; k++) tableau[ locn(i,k) ] /= x;
}


void FractionMatrix::combine_rows( Fraction x, int i, int j )
{
	for (int k = 0; k < ncols; k++)
		tableau[ locn(j,k) ] -= x * tableau[ locn(i,k) ];
}


int FractionMatrix::row_reduced( Fraction& determinant )
{
	int	r = 0, k = 0;	// dimensions of row-reduced submatrix
	while (( r < nrows )&&( k < ncols ))
		{
		int	u = r;	// scan the next unreduced submatrix
		while (( tableau[ locn(u,k) ] == 0 )&&( ++u < nrows ));
		if (( u == nrows )&&( ++k < ncols )) continue;
		if ( k == ncols ) { determinant = 0; break; }
		
		Fraction	x = tableau[ locn(u,k) ];
		if ( x != 1 )
			{ rescale_row( x, u ); determinant *= x; return 0; }
		if ( u > r )
			{ exchange_rows( u, r ); determinant *= -1; return 0; }
		for (int i = 0; i < nrows; i++)
			if (( i != u )&&( (x = tableau[ locn(i,k) ] )!= 0 ))
				{ combine_rows( x, u, i ); return 0; }	
		++r;
		}
	return	1;
}

Fraction FractionMatrix::obtain( unsigned int i, unsigned int j )
{
	if (( i < nrows )&&( j < ncols )) 
		return	this->tableau[ locn(i,j) ];
	else	return 0;
}

void FractionMatrix::assign( unsigned int i, unsigned int j, Fraction f )
{
	if (( i < nrows )&&( j < ncols )) this->tableau[ locn(i,j) ] = f;
}


ostream& operator << ( ostream& s, FractionMatrix& a )
{
	for (int i = 0; i < a.nrows; i++)
		{
		s << endl;
		for (int j = 0; j < a.ncols; j++)
			s << a.tableau[ a.locn(i,j) ] << "\t";
	      	}
	s << endl;
	return	s;	
}
	

istream& operator >> ( istream& s, FractionMatrix& a )
{
	// input the matrix-entries by rows
	for (int i = 0; i < a.nrows; i++)
		{
		cout << "\nEnter vector-components for ";
		cout << "for row-vector #" << 1+i << ":\n" << endl;
		for (int j = 0; j < a.ncols; j++)
			{
			Fraction	x;
			cin >> x;
			a.assign( i, j, x );
			}
		cout << endl;
		}
	return	s;
}