// © Copyright1995, Joseph Bergin.  All rights reserved. 

#ifndef __Array__
#define __Array__

#include "Error.h"
#include <stdlib.h>
#include <iostream.h>
// Arrays are dynamically sized.  The user gives an initial size upon
// creation, but they may be extended with extendBy.  Array assignment is
// copying.  Subscripting yields references to the data stored,so that
// a subscript expression may be used on the left or right side of an 
// assignment.  
// They may be written if their elements may be.

const int ArrayDefaultLength = 10;

template <class T>
class Array{
	public:
	// constructors
		Array(unsigned int size = ArrayDefaultLength) // Uninitialized cells
		:	_length(size),
		 	_elements(new T[size])
		{	if(_elements == NULL)
				userERROR("Heap failure.");
		}

		Array(const T A[], int size) // Initialize from A. 
		:	_length(size),
			_elements(new T[size])
		{	if(_elements == NULL)
				userERROR("Heap failure.");
			for(int i=0; i< size; i++)
				_elements[i] = A[i];	
		}

		Array(unsigned int size, const T& t) // Initialized cells
		:	_length(size),
		 	_elements(new T[size])
		{	if(_elements == NULL)
				userERROR("Heap failure.");
			for(int j = 0; j<size; j++)_elements[j] = t;
		}

		Array(const Array<T>& a) {copy(a);} // copy constructor
		
	// destructor
		virtual ~Array(){free();}
		
	// assignment
		Array<T> & operator = (const Array<T>& a) // Copy semantics. 
		{	if(_elements != a._elements)
			{	free();
				copy(a);
			}
			return *this;
		}
		
	// access	
		T & operator [](unsigned int i) const // Subscripting.
		{	if(i >= _length)userERROR("Subscript out of bounds in array.");
			return _elements[i];
		}

//		T & operator ()(unsigned int i) const // Subscripting. 
//		{	if(i >= _length)userERROR ( "Subscript out of bounds in array.");
//			return _elements[i];
//		}
		
		unsigned int length()const{ return _length;}
		
		void swap(unsigned int i, unsigned int j)
			// swap contents of cells i and j.  
		{	T temp = (*this)[i]; // get rangechecks
			(*this)[i] = (*this)[j];
			(*this)[j] = temp;
		}
		
		void extendBy(unsigned int i)  // Uninitialized cells.
		{	if(i>0) 
			{	T* temp = new T [_length + i];
				if(temp == NULL)
					userERROR("Heap failure.");
				for(unsigned int j = 0; j < _length; j++) temp[j] = _elements[j];
				_length += i;
				delete [] _elements;
				_elements = temp;
			}
		}
		
		void extendBy(unsigned int i, const T& t) // Initialized cells.
		{	if(i>0) 
			{	T* temp = new T [_length + i];
				if(temp == NULL)
					userERROR("Heap failure.");
				unsigned int j;
				for(j = 0; j < _length; j++) temp[j] = _elements[j];
				for(j = _length; j < _length + i; j++) temp[j] = t;
				_length += i;
				delete [] _elements;
				_elements = temp;
			}
		}

		void extendBy(const T A[], int size)
		{	T *temp = new T[_length + size];
			if(temp == NULL) userERROR("Heap Failure.");
			unsigned int i;
			unsigned int newLength = _length + size;
			for( i= 0; i < _length; ++i) temp[i] = _elements[i];
			for(	 ; i < newLength; ++i) temp[i] = A[i - _length];
			delete [] _elements;
			_elements = temp;
			_length = newLength;
		}
		
	protected:
		unsigned int _length;
		T* _elements;

		void copy(const Array<T>& a) 
		//Assumes previous elements have been deleted
		{	_elements = new T [a._length];
			if(_elements == NULL)
				userERROR("Heap failure.");
			_length = a._length;
			for(int i = 0; i< _length; i++)
				_elements[i] = a._elements[i];
		}

		void free()
		{	 delete [] _elements;
		}

//	friend
//	ostream & operator<<(ostream & os, const Array<T> &A);

};

// Requires writeable elements. Pointer addresses will
// be written.
template <class T>
ostream & operator<<(ostream & os, const Array<T>& A)
{	for(int i = 0; i < A.length(); i++)
		os << A[i]<<' ';
	return os;
}

#endif

