//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published
//  by the Free Software Foundation; either version 3.0 of the License, or
//  any later version.
//
//  The GPSTk is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with GPSTk; if not, write to the Free Software Foundation,
//  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
//  
//  Copyright 2004, The University of Texas at Austin
//
//============================================================================

//============================================================================
//
//This software developed by Applied Research Laboratories at the University of
//Texas at Austin, under contract to an agency or agencies within the U.S. 
//Department of Defense. The U.S. Government retains all rights to use,
//duplicate, distribute, disclose, or release this software. 
//
//Pursuant to DoD Directive 523024 
//
// DISTRIBUTION STATEMENT A: This software has been approved for public 
//                           release, distribution is unlimited.
//
//=============================================================================

/**
 * @file Vector.hpp
 * Classes for Vector, both constant and modifiable
 */

#ifndef GPSTK_VECTOR_HPP
#define GPSTK_VECTOR_HPP

#include <limits>
#include <vector>
#include "VectorBase.hpp"

namespace gpstk
{
      /// @ingroup MathGroup
      //@{

      // forward declaration
   template <class T> class VectorSlice;

      /**
       * This class pretty much duplicates std::valarray<T> except it's fully
       * STL container compliant.  Remember that operators +=, -=, *= and /=
       * are provided by RefVectorBase.
       * 
       * @sa matvectest.cpp for examples
       */
   template <class T>
   class Vector : public RefVectorBase<T, Vector<T> >
   {
   public:
         /// STL value type
      typedef T value_type;
         /// STL reference type
      typedef T& reference;
         /// STL const reference type
      typedef const T& const_reference;
         /// STL iterator type
      typedef T* iterator;
         /// STL const iterator type
      typedef const T* const_iterator;

         /// Default constructor
      Vector() : v(NULL), s(0)
      {}
         /// Constructor given an initial size.
      Vector(size_t siz) : v(NULL), s(siz)
      {
         if (siz>0)
         {
            v = new T[siz];
            if(!v) {
               VectorException e("Vector(size_t) failed to allocate");
               GPSTK_THROW(e);
            }
         }
      }
         /**
          * Constructor given an initial size and default value for
          * all elements.
          */
      Vector(size_t siz, const T defaultValue) : v(NULL), s(siz)
      {
         if (siz>0)
         {
            v = new T[siz];
            if(!v) {
               VectorException e("Vector<T>(size_t, const T) failed to allocate");
               GPSTK_THROW(e);
            }
            this->assignFrom(defaultValue);
         }
      }
         /**
          * Copy constructor from a ConstVectorBase type.
          */
      template <class E>
      Vector(const ConstVectorBase<T, E>& r) : v(NULL), s(r.size())
      {
         if (r.size()>0)
         {
            v = new T[r.size()];
            if(!v) {
               VectorException e("Vector<T>(ConstVectorBase) failed to allocate");
               GPSTK_THROW(e);
            }
            this->assignFrom(r);
         }
      }
         /**
          * Copy constructor.
          */
      Vector(const Vector& r) : v(NULL), s(r.s)
      {
         if (r.s>0)
         {
            v = new T[r.s];
            if(!v) {
               VectorException e("Vector(Vector) failed to allocate");
               GPSTK_THROW(e);
            }
            this->assignFrom(r);
         }
      }
         /**
          * Valarray constructor
          */
      Vector(const std::valarray<T>& r) : v(NULL), s(r.size())
      {
         if (r.size())
         {
            v = new T[r.size()];
            if(!v) {
               VectorException e("Vector(valarray) failed to allocate");
               GPSTK_THROW(e);
            }
            this->assignFrom(r);
         }
      }

         /// subvector constructor
      template <class E>
      Vector(const ConstVectorBase<T, E>& vec,
             size_t top,
             size_t num) : v(NULL), s(0)
      {
            // sanity checks...
         if ( top >= vec.size() || 
              top + num > vec.size())
         {
            VectorException e("Invalid dimensions or size for Vector(VectorBase)");
            GPSTK_THROW(e);
         }
         if (num>0)
         {
            v = new T[num];
            if(!v) {
               VectorException e("Vector(subvector) failed to allocate");
               GPSTK_THROW(e);
            }
            size_t i;
            for(i = 0; i < num; i++)
               v[i] = vec(top+i);
            s = num;
         }
      }
   
         /// Destructor
      ~Vector()
      {
         if (v) delete [] v;
      }

         /// STL iterator begin
      iterator begin() { return v; }
         /// STL const iterator begin
      const_iterator begin() const { return v; }
         /// STL iterator end
      iterator end() { return v + s; }
         /// STL const iterator end
      const_iterator end() const { return v + s; }
         /// STL front
      value_type front() { return v[s-1]; }
         /// STL const front
      const_reference front() const { return v[s-1];}
         /// STL empty
      bool empty() const { return size() == 0; }
         /// STL size
      size_t size() const {return s; }
         /// STL max_size
      size_t max_size() const { return std::numeric_limits<size_t>().max(); }

         /// Non-const operator []
      T& operator[] (size_t i) 
      { return v[i]; }
         /// Const operator []
      T operator[] (size_t i) const
      { return v[i]; }
         /// Non-const operator ()
      T& operator() (size_t i) 
      { return v[i]; }
         /// Const operator ()
      T operator() (size_t i) const
      { return v[i]; }

         /// Like valarray, lets you do vec[slice] to get a VectorSlice.
      VectorSlice<T> operator[] (const std::slice& sli)
      { return VectorSlice<T>(*this, sli); }

         /// *this will be resized if it isn't as large as x.
      Vector& operator=(const Vector& x)
      { resize(x.s); return this->assignFrom(x); }

         /// *this will be resized if it isn't as large as x.
      template <class E>
      Vector& operator=(const ConstVectorBase<T, E>& x)
      { resize(x.size()); return this->assignFrom(x); }

         /// *this will be resized if it isn't as large as x.
      Vector& operator=(const std::valarray<T>& x)
      { resize(x.size()); return this->assignFrom(x); }
         /// Only (*this).size() elements will be assigned.
      Vector& operator=(const T x)
      { return this->assignFrom(x); }
         /// Only (*this).size() elements will be assigned.
      Vector& operator=(const T* x)
      { return this->assignFrom(x); }

         /// *this will be cleared and resized as necessary
      inline Vector& operator=(const std::vector<T>& x)
      {
         size_t i;
         size_t vs = x.size();
         (*this).resize(vs);

         for (i = 0; i < vs; i++) 
            (*this)[i] = x[i];

         return (*this); 
      }

         /// Resizes the vector.  if index > size, the vector will be
         /// erased and the contents destroyed.
      Vector& resize(const size_t index)
      { 
         if (index > s)
         {
            if (v)
               delete [] v;
            v = new T[index];
            if(!v) {
               VectorException e("Vector.resize(size_t) failed to allocate");
               GPSTK_THROW(e);
            }
         }
         s = index;
         return *this;
      }

         /// resize with new default value
      Vector& resize(const size_t index, const T defaultValue)
      {
         resize(index);
         for(size_t i = 0; i < s; i++)
            v[i] = defaultValue;
         return *this;
      }

         // convert the gpstk vector to std vector
      std::vector<T> toStdVector()
      {
         std::vector<T> v;
         for(size_t i = 0; i < s; i++)
            v.push_back(v[i] );
         return v;
      }

      inline Vector& operator<<(const Vector& b)
      {
         size_t i;
         size_t vs = this->size();
         size_t bs = b.size();
         size_t rows = vs + bs;
         Vector<T> toReturn(rows);

         for (i = 0; i < vs; i++)
            toReturn[i] = (*this)[i];

         for (i = 0; i < bs; i++)
            toReturn[i+vs] = b[i];

         (*this) = toReturn;

         return (*this);
      }

         /// Returns the concatenation of this Vector and a scalar of type T
      inline Vector& operator<<(const T &b) 
      {
         return (*this) << Vector(1,b);
      }

         /// Returns the concatenation of this Vector and Vector b
      inline Vector operator&&(const Vector &b) 
      {
         size_t i;
         size_t vs = this->size();
         size_t bs = b.size();
         size_t rows = vs + bs;
         Vector<T> toReturn(rows);

         for (i = 0; i < vs; i++)
            toReturn[i] = (*this)[i];

         for (i = 0; i < bs; i++)
            toReturn[i+vs] = b[i];

         return toReturn;
      }

         /// Returns the concatenation of this Vector and a scalar of type T
      inline Vector operator&&(const T &b) 
      {
         size_t i;
         size_t vs = this->size();
         size_t rows = vs + 1;
         Vector<T> toReturn(rows);

         for (i = 0; i < vs; i++)
            toReturn[i] = (*this)[i];

         toReturn[rows - 1] = b;

         return toReturn;
      }

   private:

         // a good optimizer will remove this function call
         // if RANGECHECK isn't defined.  remember that
         // range checking affects EVERY operation
      inline bool rangeCheck(const size_t index) const
      {
#ifdef RANGECHECK
         return (index < s);
#else
         return true;
#endif
      }
   
         /// The vector
      T* v;
         /// The size of the vector.
      size_t s;
   };
      // end class Vector<T>

      /**
       * A slice of Vector<T> that can be modified.  
       * @warning Remember that (VectorSlice = VectorSlice) will
       * assign elements to the VectorSlice, not copy the VectorSlice internal data!
       */
   template <class T>
   class VectorSlice : public RefVectorSliceBase<T, VectorSlice<T> >
   {
   public:
         /// Default constructor
      VectorSlice()
            : v(NULL), s(std::slice(0,0,0))
      {}

         /// Makes a slice of the whole vector
      VectorSlice(Vector<T>& vv)
            : v(&vv), s(std::slice(0,vv.size(),1))
      {}
            
         /// Makes a slice of the vector with the given std::slice.
      VectorSlice(Vector<T>& vv, const std::slice& ss)
            : v(&vv), s(ss)
      { vecSliceCheck(vv.size()); }

         /// Assign the elements of this slice from another vector.
      template <class V>
      VectorSlice& operator=(const ConstVectorBase<T, V>& x)
      { return this->assignFrom(x); }

         /// Assign the elements of this slice from a valarray.
      VectorSlice& operator=(const std::valarray<T>& x)
      { return this->assignFrom(x); }

         /// Assign all the elements of this slice to x.
      VectorSlice& operator=(const T x)
      { return this->assignFrom(x); }

         /// Assign (*this).size() elements from x to (*this).
      VectorSlice& operator=(const T* x)
      { return this->assignFrom(x); }

         /// Returns the modifiable i'th element of the slice.
      T& operator[] (size_t i) 
      { return (*v)[start() + i * stride()]; }
         /// Returns the const i'th element of the slice.
      T operator[] (size_t i) const
      { return (*v)[start() + i * stride()]; }
         /// Returns the modifiable i'th element of the slice.
      T& operator() (size_t i) 
      { return (*v)[start() + i * stride()]; }
         /// Returns the const i'th element of the slice.
      T operator() (size_t i) const
      { return (*v)[start() + i * stride()]; }

         /// returns the number of elements in the slice
      inline size_t size() const { return s.size(); }
         /// returns the index in the vector of the first element.
      inline size_t start() const { return s.start(); }
         /// returns the number of elements to skip between (*this)[i] and 
         /// (*this)[i+1]
      inline size_t stride() const { return s.stride(); }
   private:
         /// the vector used as a source for the slice
      Vector<T>* v;
         /// the slice specification.
      std::slice s;
   };

      /**
       * A Vector<T> slice that doesn't allow modification. 
       */
   template <class T>
   class ConstVectorSlice : public ConstVectorSliceBase<T, ConstVectorSlice<T> >
   {
   public:
         /// default constructor
      ConstVectorSlice()
            : v(NULL), s(std::slice(0,0,0))
      { }

         /// Makes a slice of the whole vector
      ConstVectorSlice(const Vector<T>& vv)
            : v(&vv), s(std::slice(0,vv.size(),1))
      { }
            
         /// Uses the given slice and vector.
      ConstVectorSlice(const Vector<T>& vv, const std::slice& ss)
            : v(&vv), s(ss)
      { vecSliceCheck(vv.size()); }

         /// Returns a const version of the i'th slice element.
      T operator[] (size_t i) const
      { return (*v)[start() + i * stride()]; }
         /// Returns a const version of the i'th slice element.
      T operator() (size_t i) const
      { return (*v)[start() + i * stride()]; }

         /// returns the number of elements in the slice
      inline size_t size() const { return s.size(); }
         /// returns the index in the vector of the first element.
      inline size_t start() const { return s.start(); }
         /// returns the number of elements to skip between (*this)[i] and 
         /// (*this)[i+1]
      inline size_t stride() const { return s.stride(); }

   private:
         /// Vectortor used as a source for this slice.
      const Vector<T>* v;
         /// the slice specification.
      std::slice s;
   };

      //@}

}  // namespace

#include "VectorOperators.hpp"

#endif
