#ifndef ARRAY_H
#define ARRAY_H

const int def_array_size = 7;
template<class T>
class Array {  
public:
  Array(int def_size = def_array_size) { init((const T *) 0, def_size); }
  Array(const Array<T>& rhs) { init(rhs.ia, rhs.sz); }
  Array(const T* ay, int size) { init(ay, size); }
  virtual ~Array(void) { delete [] ia; }
  Array<T>& operator=(const Array<T>&);
  T& operator[](int index) { return ia[index]; }
  const T& operator[](int index) const { return ia[index]; }
  const T& operator()(int index) const { return ia[index]; }
  Array<T> operator()(int start, int end) const {
    return Array<T>(ia+start, end-start+1);
  }
  Array<T> operator()(int start, int end, int stride) const {
    Array<T> r((end-start+stride)/stride);
    for (int i=start, j=0; i<=end; i+=stride) r.ia[j++] = ia[i];
    return r;
  }
  int size(void) const { return sz; }
protected:
  T *ia;
  int sz;
private:
  void init(const T*, int);
};

template<class T>
Array<T>& Array<T>::operator=(const Array<T>& rhs) {
  if (this == &rhs)        // self-test
    return *this;          // ref returned for daisy-chaining
  delete [] ia;            // free up current memory
  init(rhs.ia, rhs.sz);    // copy rhs to lhs
  return *this;
}

#include <assert.h>

template<class T>
void Array<T>::init(const T *ay, int size) {
  assert(size >= 0);
  // initialize all class data members
  sz = size;
  ia = new T [size];
  assert(ia != 0); // quit if new() failed
  // initialize array values if specified
  for (int index=0; index<size; index++) {
    // did we receive an initialization array?
    ia[index] = (ay != (T *)0) ? ay[index] : 0;
  }
}

#include <iostream>
#include <iomanip>
template<class T>
std::ostream& operator<<(std::ostream& ostr, const Array<T>& a) {
  for (int i=0; i<a.size(); ++i) {
    if ( (i % 5) == 0 ) ostr << "[" << std::setw(3) << i << "] ";
    ostr << std::setw(10) << a[i];
    if ( (i % 5) == 4 ) ostr << endl;
  }
  return ostr;
}

#endif