// Copyright 1995 Barbara Liskov

#ifndef ARRAY_T
#define ARRAY_T

// Internals of generic extensible array implementation.
// You probably do not want to look in here.  All of the interface
// to arrays is in "array.h".

#include <assert.h>
#include "array.h"
#include <strstream.h>

template <class T>
inline array<T>::array() {
    alloc_ = 0;
    store_ = new T[alloc_];
    size_  = 0;
}

template <class T>
inline int array<T>::length() const {
    return size_;
}

template <class T>
inline T& array<T>::operator[](int index) {
    bound_check(index);
    return store_[index];
}

template <class T>
inline T const& array<T>::operator[](int index) const {
    bound_check(index);
    return store_[index];
}

template <class T>
inline T array<T>::fetch(int index) const {
    bound_check(index);
    return store_[index];
}

template <class T>
inline void array<T>::store(int index, T v) {
    bound_check(index);
    store_[index] = v;
}

template <class T>
inline T array<T>::top() const {
    bound_check(size_-1);
    return store_[size_-1];
}

template <class T>
inline void array<T>::append(T v) {
    if (size_ >= alloc_)
	enlarge_allocation_to(size_+1);
    store_[size_++] = v;
}

template <class T>
inline T array<T>::remove() {
    assert (size_ > 0);
    size_--;
    return store_[size_];
}

template <class T>
inline void array<T>::remove_many(int num) {
    assert((num >= 0) && (num <= size_));
    size_ -= num;
}

template <class T>
inline array<T>::array(int predict) {
    range_check(predict);
    alloc_ = predict;
    store_ = new T[predict];
    size_ = 0;
}

template <class T>
inline array<T>::~array() {
    delete [] store_;
}

template <class T>
inline void array<T>::clear() {
    size_ = 0;
}

template <class T>
inline void array<T>::bound_check(int val) const {
    assert((val >= 0) && (val < size_));
}

template <class T>
inline void array<T>::range_check(int val) const {
    assert(val >= 0);
}

// More expensive operations (do not inline?)

template <class T>
array<T>::array(array<T> const& d) {
    alloc_ = d.size_;
    store_ = new T[d.size_];
    size_  = d.size_;
    copy(d.store_, store_, size_);
}

template <class T>
array<T>& array<T>::operator=(array<T> const& d) {
    clear();
    enlarge_to(d.size_);
    copy(d.store_, store_, size_);
    return (*this);
}

template <class T>
void array<T>::enlarge_to(int newsize) {
    if (newsize > alloc_)
	enlarge_allocation_to(newsize);
   size_ = newsize;
}

template <class T>
void array<T>::enlarge_allocation_to(int newsize) {
    alloc_ = alloc_ * 2;
    if (newsize > alloc_) alloc_ = newsize;

    T* oldstore = store_;
    store_ = new T[alloc_];
    copy(oldstore, store_, size_);
    delete [] oldstore;
}

template <class T>
void array<T>::reclaim() {
    if (alloc_ > size_) {
	// Some free entries that can be reclaimed
	T* oldstore = store_;
	alloc_ = size_;
	store_ = new T[alloc_];
	copy(oldstore, store_, size_);
	delete [] oldstore;
    }
}

template <class T>
void array<T>::copy(T const* src, T* dst, int n) {
    for (int i = 0; i < n; i++)
	dst[i] = src[i];
}

template <class T>
array<T>* array<T>::copy() const {
    array<T> *temp = new array<T>();
    temp->enlarge_to(size_);
    copy(store_, temp->store_, size_);
    return temp;
}

#if 0
template <class T>
bool array<T>::equal(array<T> *a) const {
    if (size_ != a->size_) return false;
    for (int i = 0; i < size_; i++) {
        if (a->store_[i] != store_[i]) return false;
        }
    return true;
}
#endif

template <class T>
ostream& operator << (ostream & o, array<T> const* a) {
    o << "array" << endl;
    for (int i = 0; i < a->size(); i++) {
       	 o << i << "th element is "<< a->fetch(i) << endl;
       	 }
    o << endl;
    return o;
}

#if 0
template <class T>
string array<T>::unparse() const {
ostrstream out;

	out << "array" << endl;
	for (int i = 0; i < size_; i++) {
		out << i << "th elt = " << store_[i] << endl;
		}
	out << endl;
	out << ends;
	string result = string(out.str());
	return result;
}
#endif

#endif /* ARRAY_T */
