/*
 * Tiny Vector Matrix Library
 * Dense Vector Matrix Libary of Tiny size using Expression Templates
 *
 * Copyright (C) 2001, 2002 Olaf Petzold <opetzold@wit.regiocom.net>
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * "$Id:"
 */

#ifndef TVMET_VECTOR_H
#define TVMET_VECTOR_H

#include <iterator>					// reverse_iterator

#include <tvmet/tvmet.h>
#include <tvmet/Xpr.h>
#include <tvmet/TypePromotion.h>
#include <tvmet/NumericTraits.h>
#include <tvmet/CommaInitializer.h>
#include <tvmet/CompileTimeError.h>
#include <tvmet/RunTimeError.h>
#include <tvmet/meta/Vector.h>

namespace tvmet {

/* forwards */
template<class T, std::size_t Sz> class Vector;
template<class T, std::size_t Sz, std::size_t Stride> class VectorConstReference;

/**
 * \class VectorConstReference Vector.h "tvmet/Vector.h"
 * \brief Const value iterator for ET
 */
template<class T, std::size_t Sz, std::size_t Stride=1>
class VectorConstReference
  : public TvmetBase< VectorConstReference<T, Sz, Stride> >
{
public: // types
  typedef T 						value_type;
  typedef VectorConstReference<T, Sz, Stride>		this_type;

private:
  VectorConstReference();
  VectorConstReference& operator=(const this_type& rhs);

public:
  /** Constructor. */
  explicit VectorConstReference(const Vector<T, Sz>& rhs) : m_data(rhs.data()) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  VectorConstReference(const this_type& rhs) : m_data(rhs.m_data) { }
#endif

public: // access operators
  /** access by index. */
  value_type operator[](std::size_t i) const {
    RT_CONDITION(i < Sz, "[tvmet] Vector Bounce Violation")
    return m_data[i * Stride];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l)
       << "VectorConstReference<" << typeid(T).name() << ", "
       << Sz << ", " << Stride << ">,"
       << std::endl;
  }

private:
  const value_type* _tvmet_restrict 			m_data;
};

/**
 * \class VectorReference Vector.h "tvmet/Vector.h"
 * \brief Value iterator for ET
 */
template<class T, std::size_t Sz, std::size_t Stride=1>
class VectorReference  : public TvmetBase< VectorReference<T, Sz, Stride> >
{
public: // types
  typedef T 						value_type;
  typedef VectorReference<T, Sz, Stride>		this_type;

private:
  VectorReference();
  VectorReference& operator=(const this_type& rhs);

public:
  /** Constructor. */
  explicit VectorReference(Vector<T, Sz>& rhs) : m_data(rhs.data()) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  VectorReference(const this_type& rhs) : m_data(rhs.m_data) { }
#endif

public: // access operators
  /** access by index. */
  value_type& _tvmet_restrict operator[](std::size_t i) {
    RT_CONDITION(i < Sz, "[tvmet] Vector Bounce Violation")
    return m_data[i * Stride];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l)
       << "VectorReference<" << typeid(T).name() << ", "
       << Sz << ", " << Stride << ">,"
       << std::endl;
  }

private:
  value_type* _tvmet_restrict 				m_data;
};

/**
 * \class Vector Vector.h "tvmet/Vector.h"
 * \brief Compile time fixed length vector with evaluation on compile time.
 */
template<class T, std::size_t Sz>
class Vector
{
public:
  /** Data type of the tvmet::Vector. */
  typedef T     					value_type;

  /** Reference type of the tvmet::Vector data elements. */
  typedef T&     					reference;

  /** const reference type of the tvmet::Vector data elements. */
  typedef const T&     					const_reference;

  /** STL iterator interface. */
  typedef T*     					iterator;

  /** STL const_iterator interface. */
  typedef const T*     					const_iterator;

  /** STL reverse iterator interface. */
  typedef std::reverse_iterator<iterator> 		reverse_iterator;

  /** STL const reverse iterator interface. */
  typedef std::reverse_iterator<const_iterator> 	const_reverse_iterator;

  /** This type of tvmet::Vector, used to simplify member wrote. */
  typedef Vector<T, Sz>					this_type;

public: // STL  interface
  /** STL iterator interface. */
  iterator begin() { return m_data; }

  /** STL iterator interface. */
  iterator end() { return m_data + Sz; }

  /** STL const_iterator interface. */
  const_iterator begin() const { return m_data; }

  /** STL const_iterator interface. */
  const_iterator end() const { return m_data + Sz; }

  /** STL reverse iterator interface reverse begin. */
  reverse_iterator rbegin() { return reverse_iterator( end() ); }

  /** STL const reverse iterator interface reverse begin. */
  const_reverse_iterator rbegin() const {
    return const_reverse_iterator( end() );
  }

  /** STL reverse iterator interface reverse end. */
  reverse_iterator rend() { return reverse_iterator( begin() ); }

  /** STL const reverse iterator interface reverse end. */
  const_reverse_iterator rend() const {
    return const_reverse_iterator( begin() );
  }

  /** STL vector front element. */
  value_type front() { return m_data[0]; }

  /** STL vector const front element. */
  const_reference front() const { return m_data[0]; }

  /** STL vector back element. */
  value_type back() { return m_data[Sz-1]; }

  /** STL vector const back element. */
  const_reference back() const { return m_data[Sz-1]; }

  /** STL vector empty() - returns allways false. */
  static bool empty() { return false; }

  /** The size of the vector. */
  static std::size_t size() { return Sz; }

  /** STL vector max_size() - returns allways Sz. */
  static std::size_t max_size() { return Sz; }

public:
  /** Default Destructor */
  ~Vector() { }

  /** Default Constructor. The allocated memory region isn't cleared. If you want
   a clean use the constructor argument zero. */
  explicit Vector() { }

  /** Copy Constructor, not explicit!
   \internal If you write it explicit you will get error on missing copy ctor by
   using the current cross product implementation (which returns a new Vector<T, Sz>).
   Maybe this will be solved by a return of Xpr<>. */
  Vector(const this_type& rhs) {
    *this = XprVector<typename this_type::ConstReference, Sz>(rhs.cref());
  }

  /** Constructor with STL iterator interface. */
  template<class InputIterator>
  explicit Vector(InputIterator first, InputIterator last) {
    RT_CONDITION( std::distance(first, last) <= Sz, "Inputiterator doesn't fits in size" );
    for(std::size_t i = 0 ; first != last; ++first, ++i)
      m_data[i] = *first;
  }

  /** Constructor with STL iterator interface. */
  template<class InputIterator>
  explicit Vector(InputIterator first, std::size_t sz) {
    RT_CONDITION( sz <= Sz, "Inputiterator doesn't fits in size" );
    for(std::size_t i = 0; i < sz; ++first, ++i)
      m_data[i] = *first;
  }

  /** Constructor with initializer for all elements.
      \internal const problem
   */
  explicit Vector(value_type rhs) {
    //     typedef XprLiteral<value_type> expr_type;
    //     *this = XprVector<expr_type, Sz>(expr_type(rhs));
    for(std::size_t i = 0; i < Sz; ++i) m_data[i] = rhs;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1) {
    CT_CONDITION(2 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2) {
    CT_CONDITION(3 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3) {
    CT_CONDITION(4 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3,
		  value_type x4) {
    CT_CONDITION(5 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3; m_data[4] = x4;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3,
		  value_type x4, value_type x5) {
    CT_CONDITION(6 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3; m_data[4] = x4;
    m_data[5] = x5;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3,
		  value_type x4, value_type x5, value_type x6) {
    CT_CONDITION(7 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3; m_data[4] = x4;
    m_data[5] = x5; m_data[6] = x6;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3,
		  value_type x4, value_type x5, value_type x6, value_type x7) {
    CT_CONDITION(8 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3; m_data[4] = x4;
    m_data[5] = x5; m_data[6] = x6; m_data[7] = x7;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3,
		  value_type x4, value_type x5, value_type x6, value_type x7,
		  value_type x8) {
    CT_CONDITION(9 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3; m_data[4] = x4;
    m_data[5] = x5; m_data[6] = x6; m_data[7] = x7; m_data[8] = x8;
  }

  /** Default Constructor with initializer list. */
  explicit Vector(value_type x0, value_type x1, value_type x2, value_type x3,
		  value_type x4, value_type x5, value_type x6, value_type x7,
		  value_type x8, value_type x9) {
    CT_CONDITION(10 <= Sz, ArgumentList_is_too_long)
    m_data[0] = x0; m_data[1] = x1; m_data[2] = x2; m_data[3] = x3; m_data[4] = x4;
    m_data[5] = x5; m_data[6] = x6; m_data[7] = x7; m_data[8] = x8; m_data[9] = x9;
  }

  /** Construct a vector by expression. */
  template <class E>
  explicit Vector(XprVector<E, Sz> expr) {
    assign(expr, fcnl_Assign<value_type, typename E::value_type>());
  }

  /** Assign a value_type on array, this can be used for a single value
      or a comma separeted list of values. */
  CommaInitializer<this_type, Sz> operator=(value_type rhs) {
    return CommaInitializer<this_type, Sz>(*this, rhs);
  }

public: // access operators
  value_type* _tvmet_restrict data() { return m_data; }
  const value_type* _tvmet_restrict data() const { return m_data; }

public: // index access operators
  reference _tvmet_restrict operator[](std::size_t i) {
    RT_CONDITION(i < Sz, "[tvmet] Vector Bounce Violation")
    return m_data[i];
  }

  const_reference _tvmet_restrict operator[](std::size_t i) const {
    RT_CONDITION(i < Sz, "[tvmet] Vector Bounce Violation")
    return m_data[i];
  }

public: // ET interface
  typedef VectorConstReference<T, Sz, 1>    		ConstReference;
  typedef VectorReference<T, Sz, 1>    			Reference;

  /** Return a const Reference of the internal data */
  ConstReference cref() const { return ConstReference(*this); }

  /** Return a Reference of the internal data */
  Reference ref() { return Reference(*this); }

  /** Return the vector as const expression. */
  XprVector<ConstReference, Sz> as_expr() const {
    return XprVector<ConstReference, Sz>(this->cref());
  }

  /** Return the vector as expression. */
  XprVector<Reference, Sz> as_expr()  {
    return XprVector<Reference, Sz>(this->ref());
  }

private: // ET helper
  template<class E, class Op>
  void assign(E expr, Op op) {
    meta::Vector<Sz>::assign(*this, expr, op);
  }

private:
  template<class Obj, std::size_t LEN> friend class CommaInitializer;

  /** This is a helper for assigning a comma separated initializer
      list. It's equal to this_type& operator=(value_type) which does
      replace it. */
  this_type& assignValue(value_type rhs) {
    typedef XprLiteral<value_type> 			expr_type;
    *this = XprVector<expr_type, Sz>(expr_type(rhs));
    return *this;
  }

public: // math operators with scalars
  this_type& operator+=(value_type);
  this_type& operator-=(value_type);
  this_type& operator*=(value_type);
  this_type& operator/=(value_type);

  this_type& operator%=(std::size_t);
  this_type& operator^=(std::size_t);
  this_type& operator&=(std::size_t);
  this_type& operator|=(std::size_t);
  this_type& operator<<=(std::size_t);
  this_type& operator>>=(std::size_t);

public: // math operators with vectors
  template<class T2> this_type& operator=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator+=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator-=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator*=(const Vector<T2, Sz>&);
#ifdef KICK_MATH_SENSE // not inside regression tests
  template<class T2> this_type& operator/=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator%=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator^=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator&=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator|=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator<<=(const Vector<T2, Sz>&);
  template<class T2> this_type& operator>>=(const Vector<T2, Sz>&);
#endif // KICK_MATH_SENSE

public: // math operators with expressions
  template <class E> this_type& operator=(XprVector<E, Sz>);
  template <class E> this_type& operator+=(XprVector<E, Sz>);
  template <class E> this_type& operator-=(XprVector<E, Sz>);
  template <class E> this_type& operator*=(XprVector<E, Sz>);
  template <class E> this_type& operator/=(XprVector<E, Sz>);
  template <class E> this_type& operator%=(XprVector<E, Sz>);
  template <class E> this_type& operator^=(XprVector<E, Sz>);
  template <class E> this_type& operator&=(XprVector<E, Sz>);
  template <class E> this_type& operator|=(XprVector<E, Sz>);
  template <class E> this_type& operator<<=(XprVector<E, Sz>);
  template <class E> this_type& operator>>=(XprVector<E, Sz>);

public: // io
  std::ostream& print_on(std::ostream& os) const;

private:
  value_type 						m_data[Sz];
};

} // namespace tvmet

#include <tvmet/VectorImpl.h>
#include <tvmet/VectorFunctions.h>
#include <tvmet/VectorOperators.h>
#include <tvmet/VectorEval.h>

#endif // TVMET_VECTOR_H

// Local Variables:
// mode:C++
// End:
