tvector.cpp

/*  tvector.cpp
    May 14 1997
    Chris Lacher

    Implementation of the class tvector

    History:

      05/14/97: initial development
      10/08/97: container protocol added
      03/30/98: memory efficiency, capacity decrease capability, PushBack runtime amortized O(1)
      07/30/00: nomenclature changes
      08/26/00: changed <class T> to <typename T> throughout
      12/05/00: subsumption of tvector.cpp
      04/19/01: namespace rcl
      04/19/01: index on size_t
      02/10/02: changed return type of Front() and Back() to reference
      12/13/02: namespace fsu
      01/01/04: template the iterator "pointer arithmetic" operations
      11/10/04: removed operator const T*

    Revised 10/8/97 as follows:

     1. container class protocol added

     2. design revised to lessen memory re-allocations:

    There are now 2 protected size_t (unsigned int) data fields: size_, capacity_

    capacity_ and size_ are independent (subject to size_ <= capacity_),
    and reductions in size_ do not re-allocate memory

    When capacity_ is increased in support of Insert() operations, it
    it is doubled. (This results in O(n) time for n pushback operations.)

    A file-scope constant defaultCapacity is used to set capacity_
    by the default constructor.

    NOTE that the implementations in this file are included into the
    file tvector.h, near the end of that file and inside the fsu
    namespace. Implementation is separated from class declaration here
    so that they can be released at different times. The intended use
    and effect is the same as if the implementations were actually in
    the file tvector.h.

    Copyright 1997 - 2007, R.C. Lacher
*/

namespace tvector
{
static const size_t  defaultCapacity = 10;
}

//----------------------------------
//     TVector<T>:: Implementations
//----------------------------------

// operator overloads

template <typename T>
std::ostream& operator << (std::ostream& os, const TVector<T>& v)
{
  v.Display(os);
  return os;
}  // end <<

template <typename T>
int operator == (const TVector<T>& v1, const TVector<T>& v2)
{
   if (v1.Size() != v2.Size())
      return 0;
   for (size_t i = 0; i < v1.Size(); ++i)
      if (v1[i] != v2[i])
         return 0;
   return 1;
}

template <typename T>
int operator != (const TVector<T>& v1, const TVector<T>& v2)
{
   return !(v1 == v2);
}

// public methods

template <typename T>
TVector<T>::TVector() : size_(0), capacity_(tvector::defaultCapacity), content_(0)
// Construct a vector of zero size and default capacity
{
  content_ = NewArray(capacity_);
}

template <typename T>
TVector<T>::TVector(size_t sz)
   : size_(sz), capacity_(sz), content_(0)
// Construct a vector of size and capacity sz
{
   content_ = NewArray(capacity_);
}

template <typename T>
TVector<T>::TVector(size_t sz, const T& Tval)
   : size_(sz), capacity_(sz), content_(0)
// Construct a vector with all elements initialized to the same value
{
   content_ = NewArray(capacity_);
   for (size_t i = 0; i < size_; ++i)
   {
      content_[i] = Tval;
   }
}

template <typename T>
TVector<T>::TVector(const TVector<T>& source)  
  : size_(source.size_), capacity_(source.capacity_)
// copy constructor      
{
   content_ = NewArray(capacity_);
   for (size_t i = 0; i < size_; ++i)
   {
      content_[i] = source.content_[i];
   }
}

template <typename T>
TVector<T>::~TVector()         
// destructor
{
   delete [] content_;
   content_ = 0;
   size_ = capacity_ = 0;
}

template <typename T>
TVector<T>& TVector<T>::operator = (const TVector<T>& source) 
// assignment operator
{
   if (this != &source)
   {
      // the NULL case
      if (source.capacity_ == 0)
      {
        if (capacity_ > 0)
          delete [] content_;
        size_ = capacity_ = 0;
        content_ = 0;
        return *this;
      }

      // set capacity 
      if (capacity_ != source.capacity_)
      {
        if (capacity_ > 0)
          delete [] content_;
        capacity_ = source.capacity_;
        content_ = NewArray(capacity_);
      }

      // set size_
      size_ = source.size_;

      // copy content
      for (size_t i = 0; i < size_; ++i)
      {
         content_[i] = source.content_[i];
      }
   }  // end if
   return *this;
}  // end assignment operator =

template <typename T>
TVector<T>& TVector<T>::operator += (const TVector<T>& V) 
{
  if (V.Empty())
    return *this;
  size_t i, s(Size());
  if (!SetSize(s + V.Size()))
  {
    std::cerr << "** TVector error: cannot expand vector in operator +=()\n";
    return *this;
  }
  for (i = 0; i < V.Size(); ++i)
    content_[i + s] = V[i];
  return *this;
}

template <typename T>
T& TVector<T>::operator [] (size_t i)
// element operator
{
   if (i >= size_)
   {
      std::cerr << "** TVector<T> Error: vector index out of range!\n";
      if (i >= capacity_)
         exit(EXIT_FAILURE);
   }
   return content_[i];
}

template <typename T>
const T& TVector<T>::operator [] (size_t i) const                 
// element operator
{
   if (i >= size_)
   {
      std::cerr << "** TVector<T> Error: vector index out of range!\n";
      if (i >= capacity_)
         exit(EXIT_FAILURE);
   }
   return content_[i];
}

template <typename T>
int TVector<T>::SetCapacity(size_t newcapacity)
// Reserve more (or less) space for vector growth;
// this is where memory is allocated. Note that this is 
// an expensive operation and should be used judiciuosly. 
// SetCapacity() is called by SetSize() only when increased capacity
// is required. If the client needs to reduce capacity, a call must be
// made specifically to SetCapacity.
{
  if (newcapacity == 0)
  {
    delete [] content_;
    content_ = 0;
    size_ = capacity_ = 0;
    return 1;
  }
  if (newcapacity != capacity_)
  {
    T* newcontent = NewArray(newcapacity);
    if (newcontent == 0)
      return 0;
    if (size_ > newcapacity)
      size_ = newcapacity;
    for (size_t i = 0; i < size_; ++i)
    {
      newcontent[i] = content_[i];
    }
    capacity_ = newcapacity;
    delete [] content_;
    content_ = newcontent;
  }
  return 1;
} // end SetCapacity()

template <typename T>
int TVector<T>::SetSize(size_t newsize)
// (re)set size
{
  if (newsize > capacity_)
    if (!SetCapacity(newsize))
      return 0;
  size_ = newsize;
  return 1;
}

template <typename T>
int TVector<T>::SetSize(size_t newsize, const T& Tval)      
// (re)set size_ with extra elements initialized to the same value
{
  size_t i, oldsize = size_;
  if (!SetSize(newsize))
    return 0;
  for (i = oldsize; i < newsize; ++i)
  {
    content_[i] = Tval;
  }
  return 1;
}

template <typename T>
size_t TVector<T>::Capacity() const 
// return capacity of vector (current memory reserved)
{
   return capacity_;
}

// Container class protocol implementation

template <typename T>
int TVector<T>::Empty() const
{
   return size_ == 0;
}

template <typename T>
size_t TVector<T>::Size() const
{
   return size_;
}

/*
template <typename T>
TVector<T>::operator const T* () const
// auto conversion of vector to array
{
  return content_;
}
*/

template <typename T>
int TVector<T>::PushBack(const T& Tval)
// grow by doubling capacity
{
  if (size_ >= capacity_) 
  {
    if (capacity_ == 0)
    {
      if (!SetCapacity(1))
        return 0;
    }
    else if (!SetCapacity(2 * capacity_))
      return 0;
  }
  content_[size_] = Tval;
  ++size_;
  return 1;
}

template <typename T>
int TVector<T>::PopBack()
{
  if (size_ == 0)
    return 0;
  --size_;
  return 1;
}

template <typename T>
void TVector<T>::Clear()
{
  SetSize(0);
}

template <typename T>
T&  TVector<T>::Front()
{
  if (size_ == 0)
  {
    std::cerr << "** TVector error: invalid Front() called on empty vector\n";
    exit (EXIT_FAILURE);
  }
  return content_[0];
}

template <typename T>
const T&  TVector<T>::Front() const
{
  if (size_ == 0)
  {
    std::cerr << "** TVector error: invalid Front() called on empty vector\n";
    exit (EXIT_FAILURE);
  }
  return content_[0];
}

template <typename T>
T&  TVector<T>::Back()
{
  if (size_ == 0)
  {
    std::cerr << "** TVector error: invalid Back() called on empty vector\n";
    exit (EXIT_FAILURE);
  }
  return content_[size_ - 1];
}

template <typename T>
const T&  TVector<T>::Back() const
{
  if (size_ == 0)
  {
    std::cerr << "** TVector error: invalid Back() called on empty vector\n";
    exit (EXIT_FAILURE);
  }
  return content_[size_ - 1];
}

// Iterator support

template <typename T>
TVectorIterator<T> TVector<T>::Begin () const
{
  Iterator I(*this);
  return I;
}

template <typename T>
TVectorIterator<T> TVector<T>::End() const
{
  Iterator I (*this);
  I.Tptr_ += size_;
  return I;
}

template <typename T>
TVectorIterator<T> TVector<T>::rBegin() const
{
  Iterator I(*this);
  I.Tptr_ += size_ - 1;
  return I;
}

template <typename T>
TVectorIterator<T> TVector<T>::rEnd() const
{
  Iterator I(*this);
  return --I;
}

template <typename T>
void TVector<T>::Display(std::ostream& os, char ofc) const
{
  size_t i;
  if (ofc == '\0')
    for (i = 0; i < size_; ++i)
      os << content_[i];
  else
    for (i = 0; i < size_; ++i)
      os << content_[i] << ofc;
}  // end Display()


template <typename T>
void TVector<T>::Dump(std::ostream& os) const
{
  size_t i;
  for (i = 0; i < capacity_; ++i)
  {
    if (i < 10)
      os << "    content_[" << i << "] == " << content_[i];
    else if (i < 100)
      os << "   content_[" << i << "] == " << content_[i];
    else if (i < 1000)
      os << "  content_[" << i << "] == " << content_[i];
    else if (i < 10000)
      os << " content_[" << i << "] == " << content_[i];
    else 
      os << "content_[" << i << "] == " << content_[i];
    if (i == 0) 
      os << " <- begin";
    if (i == size_) 
      os << " <- end";
    os << '\n';
  }
  i = capacity_;
  if (i < 10)
    os << "    content_[" << i << "] == <undefined>";
  else if (i < 100)
    os << "   content_[" << i << "] == <undefined>";
  else if (i < 1000)
    os << "  content_[" << i << "] == <undefined>";
  else if (i < 10000)
    os << " content_[" << i << "] == <undefined>";
  else 
    os << "content_[" << i << "] == <undefined>";
  if (i == size_) 
    os << " <- end";
  os << '\n';
} // end Dump()

// protected

template <typename T>
T* TVector<T>::NewArray(size_t newcapacity)
// safe memory allocator
{
   T* Tptr;
   if (newcapacity > 0)
   {
      Tptr = new T [newcapacity];
      if (Tptr == 0)
      {
         std::cerr << "** TVector error: unable to allocate memory for array!\n";
         exit (EXIT_FAILURE);
      }
   }
   else
   {
      Tptr = 0;
   }
   return Tptr;
}

//-----------------------------------------
//     TVectorIterator<T>:: Implementations
//-----------------------------------------

template <typename T>
TVectorIterator<T>::TVectorIterator () : Tptr_(0)
{}

template <typename T>
TVectorIterator<T>::TVectorIterator (const TVector<T>& V)
 : Tptr_(V.content_)
{}

template <typename T>
TVectorIterator<T>::TVectorIterator (const Iterator& I)
  :  Tptr_(I.Tptr_)
{}

template <typename T>
void  TVectorIterator<T>::Initialize (const TVector<T>& V)
{
  Tptr_ = V.content_;
}

template <typename T>
void  TVectorIterator<T>::rInitialize (const TVector<T>& V)
{
  Tptr_ = V.content_ + V.size_ - 1;
}

template <typename T>
T&  TVectorIterator<T>::Retrieve ()
{
  if (Tptr_ == 0)
  {
    std::cerr << "** TVectorIterator error: invalid Retrieve()\n";
    exit (EXIT_FAILURE);
  }
  return *Tptr_;
}

template <typename T>
const T&  TVectorIterator<T>::Retrieve () const
{
  if (Tptr_ == 0)
  {
    std::cerr << "** TVectorIterator error: invalid Retrieve()\n";
    exit (EXIT_FAILURE);
  }
  return *Tptr_;
}

template <typename T>
int TVectorIterator<T>::Valid () const
{
  return Tptr_ != 0;
}

template <typename T>
int TVectorIterator<T>::operator == (const Iterator& I2) const
{
  return Tptr_ == I2.Tptr_;
}

template <typename T>
int TVectorIterator<T>::operator != (const Iterator& I2) const
{
  return !(*this == I2);
}

template <typename T>
T&  TVectorIterator<T>::operator *  ()
{
  if (!Valid())
  {
    std::cerr << "** TVectorIterator error: invalid dereference\n";
    exit (EXIT_FAILURE);
  }
  return *Tptr_;
}

template <typename T>
const T&  TVectorIterator<T>::operator *  () const
{
  if (!Valid())
  {
    std::cerr << "** TVectorIterator error: invalid dereference\n";
    exit (EXIT_FAILURE);
  }
  return *Tptr_;
}

template <typename T>
T&  TVectorIterator<T>::operator [] (size_t i)
{
  if (!Valid())
  {
    std::cerr << "** TVectorIterator error: invalid dereference\n";
    exit (EXIT_FAILURE);
  }
  return *(Tptr_ + i);
}

template <typename T>
const T&  TVectorIterator<T>::operator [] (size_t i) const
{
  if (!Valid())
  {
    std::cerr << "** TVectorIterator error: invalid dereference\n";
    exit (EXIT_FAILURE);
  }
  return *(Tptr_ + i);
}

template <typename T>
TVectorIterator<T>& TVectorIterator<T>::operator = (const Iterator & I)
{
  Tptr_ = I.Tptr_;
  return *this;
}

template <typename T>
TVectorIterator<T>& TVectorIterator<T>::operator ++ ()
{
  ++Tptr_;
  return *this;
}

template <typename T>
TVectorIterator<T>  TVectorIterator<T>::operator ++ (int)
{
  Iterator I(*this);
  operator ++();
  return I;
}

template <typename T>
TVectorIterator<T>& TVectorIterator<T>::operator -- ()
{
  --Tptr_;
  return *this;
}

template <typename T>
TVectorIterator<T>  TVectorIterator<T>::operator -- (int)
{
  Iterator I(*this);
  operator --();
  return I;
}

template <typename T>
long TVectorIterator<T>::operator -  (const TVectorIterator<T> & I2) const
{
  return Tptr_ - I2.Tptr_;
}

template <typename T>
template <typename N>
TVectorIterator<T> TVectorIterator<T>::operator +  (N n) const
{
  Iterator I1 = *this;
  I1.Tptr_ += n;
  return I1;
}

template <typename T>
template <typename N>
TVectorIterator<T>& TVectorIterator<T>::operator += (N n)
{
  Tptr_ += n;
  return *this;
}

template <typename T>
template <typename N>
TVectorIterator<T>& TVectorIterator<T>::operator -= (N n)
{
  Tptr_ -= n;
  return *this;
}