

/*****************************************************************************\

MODULE: vector

SUMMARY:

Macros are defined providing template-like classes for
dynamic-sized arrays.

The macro vector_decl(T) declares a class vector(T),
whose implementation can be instantiated with vector_impl(T).
I/O operators << and >> can be made available using
vector_io_decl(T) and vector_io_impl(T).
The equality operators == and != can be made available using
vector_eq_decl(T) and vector_eq_impl(T).

The declaration

   vector(T) v;

creates a zero-length vector.  To grow this vector to length n, execute

   v.SetLength(n)

This causes space to be allocated for (at least) n elements,
and also causes the delault constructor for T to be called
to initialize these elements.

The current length of a vector is available as v.length().

The i-th vector element (counting from 0) is accessed as v[i].
If the macro RANGE_CHECK is defined, code is emitted to test
if 0 <= i < v.length().
This check is not performed by default.

For old-time FORTRAN programmers,
the i-th vector element (counting from 1) is accessed as v(i).

Calling v.SetLength(m) again with m <= n sets the current length
of v to m (but does not call any destructors or relinquish any space).
Calling v.SetLength(m) with m > n will allocate space and
call the default constructor for T m-n times to initialize the new 
elements.

v.MaxLength() indicates the maxumum length, which may
be larger than the current length.  
v.SetMaxLength(n) will allocate space for and initialize up to n elements,
without changing v.length().

When v's destructor is called, all constructed elements
will be destructed, and all space will be relinquished.

Space is managed using malloc, realloc, and free.
When a vector is grown, a bit more space may be allocated
than was requested for efficiency reasons.

Note that when a vector is grown, the space is reallocated using
realloc, and thus the addresses of vector elements may change,
possibly creating dangling references to vector elements.

\*****************************************************************************/

class vector(T) {  
public:  

   vector(T)();  // initially length 0
   vector(T)(const vector(T)& a);
   void operator=(const vector(T)& a);  
   ~vector(T)();  

   vector(T)(INIT_SIZE_TYPE, long n);
   // initial length is n.
   // Invoke as vector(T)(INIT_SIZE, n)

   void kill(); 
   // release space and set to length 0
  
   void SetLength(long n);  
   // set current length to n, growing vector if necessary

   void SetMaxLength(long n); 
   // allocates space and initializes up to n elements
   // Does not chane current length

   void FixLength(long n);
   // sets length to n and prohibits all future
   // length changes.  
   // FixLength may only be invoked immediately
   // after the default construction or kill.

   // The kill operation is also subsequently prohibited, 
   // and swap is allowed on fixed length vectors
   // of the same length.

   long fixed() const;
   // test if length has been fixed by FixLength().

   long length() const;
   // current length

   long MaxLength() const;
   // maximum length
  
   T& operator[](long i);
   const T& operator[](long i) const;
   // indexing operation, starting from 0
  
   T& operator()(long i);
   const T& operator()(long i) const;
   // indexing operation, starting from 1
  
   T* elts();
   const T* elts() const;
   // returns address of first vector element, or 0 if MaxLength() == 0
         
};   
   
void swap(vector(T)& x, vector(T)& y);
// swaps x & y by swapping pointers

void append(vector(T)& v, const T& a); 
// appends a to the end of v

void append(vector(T)& v, const vector(T)& w);
// appends w to the end of v



/*****************************************************************************\

                                     Input/Output

The I/O operators can be declared with vector_io_decl(T),
and implemented using vector_io_impl(T).
Elements are read and written using the underlying I/O operators 
<< and >> for T.

The I/O format for a vector v with n elements is:

   [v[0] v[1] ... v[n-1]]

\*****************************************************************************/

istream& operator>>(istream&, vector(T)&);  
ostream& operator<<(ostream&, const vector(T)&);  



/*****************************************************************************\

                              Equality Testing

The equality testing operators == and != can be declared
with vector_eq_decl(T) and implemented with vector_eq_impl(T).
The tests are performed using the underlying operator == for T.

\*****************************************************************************/


long operator==(const vector(T)& a, const vector(T)& b);  
long operator!=(const vector(T)& a, const vector(T)& b);


/*****************************************************************************\

                  Customized Constructors and Destructors

When new elements in a vector need to be constructed,
the routine 

   void BlockConstruct(T* p, long n);

is called, whose default implementation invokes the
default constructor for T n times.
Likewise, when a vector is destroyed, the routine

   void BlockDestroy(T* p, long n);

is called, whose default implementation invokes the
default destructor for T n times.

Both of these default implementations can be overridden as follows.
Instead of implementing vector(T) with vector_impl(T),
implement it with vector_impl_plain(T), and then write
your own BlockConstruct and BlockDestroy.

\*****************************************************************************/

