// Header file for the Vector class
// Created by:		Michael Edwards
// Date Started:	5/21/04
// Date Finished:	
// Compiler Used:	Microsoft Visual C++, Version 6.0 Enterprise Edition

#ifndef MCL_VECTOR_H
#define MCL_VECTOR_H

#include <cassert>
#include "Matrix.cpp"
#include "Point.cpp"

namespace MCL						// namespace for the Math Component Library
{
	// NOTE:	Vectors could be either row or column vectors
	// ASSUME:	row or column is one
	template <class NumberType = float>	
	class Vector: virtual public Matrix<NumberType>
	{
		public:
			Vector(): Matrix<NumberType>()	{}
			// default constructor
			// PRECONDITION:  none.
			// POSTCONDITION:

			Vector(unsigned dimension): Matrix<NumberType>(1, dimension)	{}
			// creates a row vector with a certain dimension
			// PRECONDITION:  none.	
			// POSTCONDITION:

			Vector(unsigned dimension, NumberType* data): Matrix<NumberType>(1, dimension, data)	{}
			// creates a row vector with a certain dimension and filled with data
			// PRECONDITION:  data contains at least dimension elements
			// POSTCONDITION: 
			
			Vector(const Point<NumberType>& begin, const Point<NumberType> &end);
			// creates a row vector from two points begin and end 
			// PRECONDITION:  begin.dimension == end.dimension
			// POSTCONDITION: vector = <end - begin>

			Vector(const Matrix<NumberType>& rhs);
			// type casts rhs to a Vector
			// PRECONDITION:  rhs.row == 1
			// POSTCONDITION: the converted rhs, a Vector, is stored in this
			
			Vector(const Vector& rhs);
			// copy constructor
			// PRECONDITION:  none.
			// POSTCONDITION: rhs has been copied

			virtual ~Vector()		{}
			// default destructor
			// PRECONDITION:  none.
			// POSTCONDITION: Vector object is destroyed

			NumberType operator[](unsigned k) const;
			// grabs member pk from the coordinate set
			// PRECONDITION:  k: [0, dimension)
			// POSTCONDITION: pk is returned.

			NumberType& operator[](unsigned k);
			// grabs member pk from the coordinate set
			// PRECONDITION:  k: [0, dimension)
			// POSTCONDITION: pk is returned.

			NumberType Dot(const Vector& rhs);
			// returns the dot product between two Vectors
			// PRECONDITION:  the size of rhs == the size of this
			// POSTCONDITION:

			NumberType Magnitude() const;
			// returns the magnitude of the Vector
			// PRECONDITION:  none.
			// POSTCONDITION: the magnitude is returned

			Vector Normalize() const;
			// returns the normalized Vector
			// PRECONDITION:  this cannot be the zero vector
			// POSTCONDITION: the normalized Vector is returned

			Vector Transpose() const;
			// Transposes a Vector and returns a Vector
			// PRECONDITION:  none.
			// POSTCONDITION: the transposed Vector is returned

		private:
			class Iterator
			{
				friend class Vector<NumberType>;			
			
				public:				
					Iterator(): myVector(0), row(0), column(0)	{}
					// PRECONDITION:  none.
					// POSTCONDITION:

					Iterator(const Iterator& rhs): myVector(rhs.myVector), 
						row(rhs.row), column(rhs.column), rowIterator(rhs.rowIterator)	{}
					// copy constructor
					// PRECONDITION:  none.
					// POSTCONDITION:

					Iterator& operator++()
					// Prefix increment operator
					// PRECONDITION:  none.
					// POSTCONDITION:
					{
						if (rowIterator)
							++row;
						else
							++column;
						return *this;
					}

					Iterator operator++(int)
					// Postfix increment operator
					// PRECONDITION:  none.
					// POSTCONDITION:
					{
						Iterator temp(*this);
						if (rowIterator)
							++row;
						else
							++column;
						return temp;
					}

					Iterator& operator--()
					// Prefix decrement operator
					// PRECONDITION:  none.
					// POSTCONDITION:
					{
						if (rowIterator)
							--row;
						else
							--column;
						return *this;
					}

					Iterator operator--(int)			
					// Postfix decrement operator
					// PRECONDITION:  none.
					// POSTCONDITION:
					{
						Iterator temp(*this);
						if (rowIterator)
							--row;
						else
							--column;
						return temp;
					}

					NumberType operator*() const
					// Dereference operator
					// PRECONDITION:  the Iterator is at a valid position
					// POSTCONDITION:
					{
						if (rowIterator)
						{
							assert(myVector->row > row);
							return myVector->data[row][0];
						}
						else
						{
							assert(myVector->column > column);
							return myVector->data[0][column];
						}
					}

					NumberType& operator*()
					// Dereference operator
					// PRECONDITION:  the Iterator is at a valid position
					// POSTCONDITION:
					{
						if (rowIterator)
						{
							assert(myVector->row > row);
							return myVector->data[row][0];
						}
						else
						{
							assert(myVector->column > column);
							return myVector->data[0][column];
						}
					}

					Iterator& operator=(const Iterator& rhs)
					// Assignment operator
					// PRECONDITION:  none.
					// POSTCONDITION: this = rhs
					{
						myVector	= rhs.myVector;
						row			= rhs.row;
						column		= rhs.column;
						rowIterator = rhs.rowIterator;

						return *this;
					}

					bool operator==(const Iterator& rhs) const
					// Equality operator
					// PRECONDITION:  none.
					// POSTCONDITION: true if this == rhs; otherwise false
					{
						return ((myVector == rhs.myVector ) && (row == rhs.row) && (column == rhs.column));
					}

					bool operator!=(const Iterator& rhs) const
					// Inequality operator
					// PRECONDITION:  none.
					// POSTCONDITION: true if this != rhs; otherwise false
					{
						return !((myVector == rhs.myVector ) && (row == rhs.row) && (column == rhs.column));
					}

				private:
					Iterator(const Vector* userVector, unsigned userRow, unsigned userColumn):
						myVector(userVector), row(userRow), column(userColumn)
					{
						// NOTE:  Must be >= so that end iterator could be created
						assert((userVector->row >= row) && (userVector->column >= column));
						rowIterator = (userVector->row != 1) ? true: false;
					}					
				
					bool rowIterator;		// true = rowIterator, false = columnIterator
					const Vector* myVector;	// pointer to Vector that this iterator belongs to
					unsigned row, column;	// pointer to element in Matrix
			};

			friend class Vector::Iterator;

			Iterator Begin() const; 
			// Returns an iterator to the beginning of the Vector
			// PRECONDITION:  none.
			// POSTCONDITION: an iterator to the beginning is returned

			Iterator End() const;
			// Returns an iterator to the end of the Vector
			// NOTE:		  End iterators do not point to valid data
			// PRECONDITION:  none.
			// POSTCONDITION: an iterator to the end is returned
	};
}

#endif