/*
 * Copyright (C) 2010-2011 Dynare Team
 *
 * This file is part of Dynare.
 *
 * Dynare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Dynare 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _VECTOR_HH
#define _VECTOR_HH

#include <algorithm>
#include <iostream>

#include <cstdlib>
#include <cassert>
#include <cmath>

/*
  This header defines three vector classes, which implement a "vector concept"
  (much like the concepts of the Standard Template Library or of Boost
  Library). The first class is a vector owning the data space for its elements,
  and the other two are vector "views" of something else (either a subvector of
  another vector, or a row or column of a matrix). This design philosophy is
  close to the design of the GNU Scientific Library, but here using the
  syntactic power of C++ class and templates, while achieving very high
  efficiency.

  These classes can be used with various templated functions, including
  wrappers around the BLAS primitives.

  The expressions required to be valid for a class V implementing the "vector concept" are:
  - V.getSize(): return number of elements
  - V.getStride(): return the stride, i.e. the offset between two elements in the data space
  - V.getData(): return the pointer to the data space
  - V(i): get an element of the vector

  The expressions required to be valid for a class V implementing the "mutable vector concept" are (in addition to those of "vector concept"):
  - V = X: assignment operator
  - V(i) = d: assign an element of the vector
  - V.setAll(d): set all the elements of the vector

*/

//! A full vector, implements the "mutable vector concept"
/*! Owns the data space for the elements */
class Vector
{
private:
  double *data;
  const size_t size;
public:
  Vector(size_t size_arg);
  Vector(const Vector &arg);
  virtual ~Vector();
  inline size_t getSize() const { return size; }
  inline size_t getStride() const { return 1; }
  inline double *getData() { return data; }
  inline const double *getData() const { return data; }
  inline void setAll(double val) { std::fill_n(data, size, val); }
  inline double &operator() (size_t i) { return data[i]; }
  inline const double &operator() (size_t i) const { return data[i]; }
  //! Assignment operator, only works for vectors of same size
  template<class Vec>
  Vector &operator=(const Vec &arg)
  {
    assert(size == arg.getSize());
    const double *p2 = arg.getData();
    for (double *p1 = data; p1 < data + size; p1++)
      {
        *p1 = *p2;
        p2 += arg.getStride();
      }
    return *this;
  }
  //! The copy assignment operator, which is not generated by the template assignment operator
  /*! See C++ standard, §12.8.9, in the footnote */
  Vector &operator=(const Vector &arg);
};

//! A vector view (i.e. a subvector of another vector, or a row or column of a matrix), implements the "mutable vector concept"
/*! Does not own the data space for the elements, so depends on another vector or matrix */
class VectorView
{
private:
  double *const data;
  const size_t size, stride;
public:
  VectorView(double *data_arg, size_t size_arg, size_t stride_arg);
  /* Can't use a template for the 2 constructors below: this would override the
  constructor which uses a pointer, because the argument list is the same */
  VectorView(Vector &arg, size_t offset, size_t size_arg);
  VectorView(VectorView &arg, size_t offset, size_t size_arg);
  virtual ~VectorView(){};
  inline size_t getSize() const { return size; }
  inline size_t getStride() const { return stride; }
  inline double *getData() { return data; }
  inline const double *getData() const { return data; }
  inline void setAll(double val)
  {
    for (double *p = data; p < data + size*stride; p += stride)
      *p = val;
  }
  inline double &operator() (size_t i) { return data[i*stride]; }
  inline const double &operator() (size_t i) const { return data[i*stride]; }
  //! Assignment operator, only works for vectors of same size
  template<class Vec>
  VectorView &
  operator= (const Vec &arg)
  {
    assert(size == arg.getSize());
    double *p1;
    const double *p2;
    for (p1 = data, p2 = arg.getData(); p1 < data + size*stride; p1 += stride, p2 += arg.getStride())
      *p1 = *p2;
    return *this;
  }
  //! The copy assignment operator, which is not generated by the template assignment operator
  /*! See C++ standard, §12.8.9, in the footnote */
  VectorView &operator=(const VectorView &arg);
};

//! Like a VectorView, but the data cannot be modified (implements the "vector concept")
class VectorConstView
{
private:
  const double *const data;
  const size_t size, stride;
public:
  VectorConstView(const double *data_arg, size_t size_arg, size_t stride_arg);
  /* Can't use a template for the 3 constructors below: this would override the
     constructor which uses a pointer, because the argument list is the same */
  VectorConstView(const Vector &arg, size_t offset, size_t size_arg);
  VectorConstView(const VectorView &arg, size_t offset, size_t size_arg);
  VectorConstView(const VectorConstView &arg, size_t offset, size_t size_arg);

  virtual ~VectorConstView() {};
  inline size_t getSize() const { return size; }
  inline size_t getStride() const { return stride; }
  inline const double *getData() const { return data; }
  inline const double &operator() (size_t i) const { return data[i*stride]; }
};

std::ostream &operator<<(std::ostream &out, const Vector &V);
std::ostream &operator<<(std::ostream &out, const VectorView &V);
std::ostream &operator<<(std::ostream &out, const VectorConstView &V);

namespace vec
{
  template<class Vec>
  void
  print(std::ostream &out, const Vec &V)
  {
    for (size_t i = 0; i < V.getSize(); i++)
      out << V(i) << " ";
    out << std::endl;
  }

  //! Computes v1 = v1 + v2
  template<class Vec1, class Vec2>
  void
  add(Vec1 &v1, const Vec2 &v2)
  {
    assert(v1.getSize() == v2.getSize());
    double *p1 = v1.getData();
    const double *p2 = v2.getData();
    while (p1 < v1.getData() + v1.getSize() * v1.getStride())
      {
        *p1 += *p2;
        p1 += v1.getStride();
        p2 += v2.getStride();
      }
  }

  //! Computes v1 = v1 - v2
  template<class Vec1, class Vec2>
  void
  sub(Vec1 &v1, const Vec2 &v2)
  {
    assert(v1.getSize() == v2.getSize());
    double *p1 = v1.getData();
    const double *p2 = v2.getData();
    while (p1 < v1.getData() + v1.getSize() * v1.getStride())
      {
        *p1 -= *p2;
        p1 += v1.getStride();
        p2 += v2.getStride();
      }
  }

  //! Does v = -v
  template<class Vec>
  void
  negate(Vec &v)
  {
    double *p = v.getData();
    while (p < v.getData() + v.getSize() * v.getStride())
      {
        *p = -*p;
        p += v.getStride();
      }
  }

  // Computes the infinite norm of a vector
  template<class Vec>
  double
  nrminf(const Vec &v)
  {
    double nrm = 0;
    const double *p = v.getData();
    while (p < v.getData() + v.getSize() * v.getStride())
      {
        if (fabs(*p) > nrm)
          nrm = fabs(*p);
        p += v.getStride();
      }
    return nrm;
  }

  // Computes the sum, min and max of a vector
  // and returns double mean=sum/size
  template<class Vec>
  double
  meanSumMinMax(double &sum, double &min, double &max, const Vec &v)
  {
    sum = 0;
    min = max = v(0);
    const double *p = v.getData();
    while (p < v.getData() + v.getSize() * v.getStride())
      {
        if ((*p) > max)
          max = (*p);
        if ((*p) < min)
          min = (*p);
        sum += *p;
        p += v.getStride();
      }
    return sum/v.getSize();
  }
} // End of namespace

#endif
