// Header file for the Matrix class
// Created by:		Michael Edwards
// Date Started:	5/20/04
// Date Finished:
// Complier Used:	Microsoft Visual C++, Version 6.0 Enterprise Edition

#ifndef MCL_MATRIX_H
#define MCL_MATRIX_H

#include <cassert>
#include <memory.h>

namespace MCL						// namespace for the Math Component Library
{
	template <class NumberType = float>
	class Matrix
	{
		public:
			Matrix();
			// Default constructor.  Creates a [0, 0] matrix
			// PRECONDITION:  none.
			// POSTCONDITION: a matrix of size [0, 0] is created

			Matrix(unsigned int row, unsigned int column);
			// Creates a matrix of row and column with all elements = 0
			// PRECONDITION:  none.
			// POSTCONDITION: a matrix is created with size (row, column) and filled
			//				  with 0

			Matrix(unsigned int row, unsigned int column, const NumberType** const userData);
			// Converts a NumberType matrix to a matrix object
			// PRECONDITION:  for all (row, column), userData[row][column] is valid
			// POSTCONDITION: a matrix is created with size (row, column) and filled
			//				  with userData

			Matrix(unsigned int row, unsigned int column, const NumberType* const userData);
			// Fills a Matrix with an array of data, userData
			// PRECONDITION:  length of userData >= row * column
			// POSTCONDITION: a matrix is created with size (row, column) and filled
			//				  with userData

			Matrix(const Matrix& rhs);
			// Copy constructor

			virtual ~Matrix();
			// Default destructor

			Matrix operator + (const Matrix &rhs) const;
			// returns the matrix addition of this and rhs
			// NOTE: this and rhs must be the same size
			// PRECONDITION:  matrices must be of same dimension.
			// POSTCONDITION: returns this + rhs

			Matrix operator - (const Matrix &rhs) const;
			// returns the matrix substraction of this and rhs
			// NOTE: this and rhs must be the same size
			// PRECONDITION:  matrices must be of same dimension.
			// POSTCONDITION: returns this - rhs

			Matrix operator * (const Matrix &rhs) const;
			// returns the cross product of this and rhs
			// NOTE: this throws an exception if this->column != rhs.row
			// PRECONDITION:  this->column == rhs.row
			// POSTCONDITION: returns the cross product of this and rhs

			Matrix operator * (const NumberType scalar) const;
			// does the scalar multiplication of this and a scalar, scalar
			// PRECONDITION:  none.
			// POSTCONDITION: returns the scalar multiplication of this and rhs

			friend Matrix operator * (const NumberType scalar, const Matrix& rhs);
			// does the scalar multiplication of this and a scalar, scalar
			// PRECONDITION:  none.
			// POSTCONDITION: returns the scalar multiplication of this and rhs

			Matrix operator / (const NumberType scalar) const;
			// does the scalar division of this and a scalar, scalar
			// PRECONDITION:  scalar != 0.
			// POSTCONDITION: returns the scalar division of this and rhs

			Matrix& operator = (const Matrix &rhs);
			// assignment operator for the matrix class
			// PRECONDITION:  none.
			// POSTCONDITION: this = rhs

			Matrix& operator -= (const Matrix &rhs);
			// returns the matrix substraction of this and rhs
			// NOTE: this and rhs must be the same size
			// PRECONDITION:  matrices must be of same dimension.
			// POSTCONDITION: returns this - rhs

			Matrix& operator += (const Matrix &rhs);
			// returns the matrix addition of this and rhs
			// NOTE: this and rhs must be the same size
			// PRECONDITION:  matrices must be of same dimension.
			// POSTCONDITION: returns this + rhs

			Matrix& operator *= (const NumberType scalar);
			// does the scalar multiplication of this and a scalar, scalar
			// PRECONDITION:  none.
			// POSTCONDITION: returns the scalar multiplication of this and rhs

			Matrix& operator /= (const NumberType scalar);
			// does the scalar division of this and a scalar, scalar
			// PRECONDITION:  scalar != 0.
			// POSTCONDITION: returns the scalar division of this and rhs
			
			const NumberType* const operator[](unsigned int k) const;
			// grabs row k from the coordinate set
			// PRECONDITION:  k: [0, row)
			// POSTCONDITION: k is returned.
	
			NumberType* operator[](unsigned int k);
			// grabs row k from the coordinate set
			// PRECONDITION:  k: [0, row)
			// POSTCONDITION: row k is returned.
	
			// bool operator ==(const Matrix &rhs) const;
			// this == rhs iff this.Reduce() == rhs.Reduce()
			// PRECONDITION:  none.
			// POSTCONDITION:

			// NumberType Determinant() const;
			// Finds the determinant of the matrix
			// PRECONDITION:  none.
			// POSTCONDITION:
			
			unsigned GetColumn() const { return column; }
			// returns the number of columns of the Matrix
			// PRECONDITION:  none.
			// POSTCONDITION: returns the number of columns

			unsigned GetRow() const { return row; }
			// returns the number of rows of the Matrix
			// PRECONDITION:  none.
			// POSTCONDITION: returns the number of rows.

			Matrix Inverse() const;
			// returns an inverted form of the matrix
			// PRECONDITION:  the current matrix is square
			// POSTCONDITION: returns the inverted form of the current matrix; if no 
			//				  inverse exists, a zero matrix is returned.

			Matrix Reduce() const;
			// returns the Matrix in Row-Echelon Form 
			// PRECONDITION:  none.
			// POSTCONDITION: returns the reduced matrix of this

			const NumberType** const ToFloat() const	{ return (const NumberType**)data; }
			// Converts from a Matrix to a NumberType**
			// PRECONDITION:  none.
			// POSTCONDITION: returns the NumberType** representation of the Matrix
			
			Matrix Transpose() const;
			// returns the transposed matrix of this
			// PRECONDITION:  none.
			// POSTCONDITION: returns the transposition of the current matrix

		protected:
			NumberType** CreateMatrix(unsigned int row, unsigned int column) const;
			// creates a matrix that is row by column
			// PRECONDITION:  none.
			// POSTCONDITION: the matrix is returned
			
			void DestroyMatrix(unsigned int row, NumberType** myMatrix);
			// destroys a matrix pointed to by myMatrix with row rows
			// PRECONDITION:  myMatrix is valid and row indicates the correct number of rows
			// POSTCONDITION: myMatrix is deallocated properly

			Matrix GenerateIdentity(unsigned int userRow, unsigned int userColumn) const;		
			// creates an identity matrix of size [userRow, userColumn]
			// PRECONDITION:  userRow and userColumn is valid
			// POSTCONDITION: returns and identity matrix of [userRow, userColumn]

			void Initialize(unsigned int row, unsigned int column, const NumberType** const userData);
			// Fills a Matrix with an array of data, userData
			// NOTE:          kludge because MSVC++6 doesn't work well with chained constructors
			// PRECONDITION:  length of userData >= row * column
			// POSTCONDITION: a matrix is created with size (row, column) and filled
			//				  with userData

			unsigned int row, column;
			NumberType** data;
	};
}

#endif