// -*- c++ -*- (enables emacs c++ mode)
//===========================================================================
//
// Copyright (C) 2000-2008 Yves Renard
//
// This file is a part of GETFEM++
//
// Getfem++  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 program  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 program;  if not, write to the Free Software Foundation,
// Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
//===========================================================================


#ifndef GMM_REF_H__
#define GMM_REF_H__

/** @file gmm_ref.h
   @author  Yves Renard <Yves.Renard@insa-lyon.fr>
   @date August 26, 2000.
 *  @brief Provide some simple pseudo-containers.
 *  
 *  WARNING : modifiying the container infirm the validity of references.
 */


#include <iterator>
#include "gmm_except.h"

namespace gmm {

  /* ********************************************************************* */
  /* Simple reference.                                                     */
  /* ********************************************************************* */

  template<typename ITER> class tab_ref {

    protected :

      ITER begin_, end_;

    public :

      typedef typename std::iterator_traits<ITER>::value_type  value_type;
      typedef typename std::iterator_traits<ITER>::pointer     pointer;
      typedef typename std::iterator_traits<ITER>::pointer     const_pointer;
      typedef typename std::iterator_traits<ITER>::reference   reference;
      typedef typename std::iterator_traits<ITER>::reference   const_reference;
      typedef typename std::iterator_traits<ITER>::difference_type
	                                                       difference_type;
      typedef ITER                            iterator;
      typedef ITER                            const_iterator;
      typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
      typedef std::reverse_iterator<iterator> reverse_iterator;
      typedef size_t size_type;
    
      bool empty(void) const { return begin_ == end_; }
      size_type size(void) const { return end_ - begin_; }

      const iterator &begin(void) { return begin_; }
      const const_iterator &begin(void) const { return begin_; }
      const iterator &end(void) { return end_; }
      const const_iterator &end(void) const { return end_; }
      reverse_iterator rbegin(void) { return reverse_iterator(end()); }
      const_reverse_iterator rbegin(void) const
      { return const_reverse_iterator(end()); }
      reverse_iterator rend(void) { return reverse_iterator(begin()); }
      const_reverse_iterator rend(void) const
      { return const_reverse_iterator(begin()); }

      reference front(void) { return *begin(); }
      const_reference front(void) const { return *begin(); }
      reference back(void) { return *(--(end())); }
      const_reference back(void) const { return *(--(end())); }
      void pop_front(void) { ++begin_; }

      const_reference operator [](size_type ii) const { return *(begin_ + ii);}
      reference operator [](size_type ii) { return *(begin_ + ii); }

      tab_ref(void) {}
      tab_ref(const ITER &b, const ITER &e) : begin_(b), end_(e) {}
  };


  /* ********************************************************************* */
  /* Reference with index.                                                 */
  /* ********************************************************************* */

//   template<typename ITER> struct tab_ref_index_iterator_
//     : public dynamic_array<size_t>::const_iterator
//   {
//     typedef typename std::iterator_traits<ITER>::value_type  value_type;
//     typedef typename std::iterator_traits<ITER>::pointer     pointer;
//     typedef typename std::iterator_traits<ITER>::reference   reference;
//     typedef typename std::iterator_traits<ITER>::difference_type  
//     difference_type;
//     typedef std::random_access_iterator_tag iterator_category;
//     typedef size_t size_type;
//     typedef dynamic_array<size_type>::const_iterator dnas_iterator_;
//     typedef tab_ref_index_iterator_<ITER> iterator;
    

//     ITER piter;
    
//     iterator operator ++(int)
//     { iterator tmp = *this; ++(*((dnas_iterator_ *)(this))); return tmp; }
//     iterator operator --(int)
//     { iterator tmp = *this; --(*((dnas_iterator_ *)(this))); return tmp; }
//     iterator &operator ++()
//     { ++(*((dnas_iterator_ *)(this))); return *this; }
//     iterator &operator --()
//     { --(*((dnas_iterator_ *)(this))); return *this; }
//     iterator &operator +=(difference_type i)
//     { (*((dnas_iterator_ *)(this))) += i; return *this; }
//     iterator &operator -=(difference_type i)
//     { (*((dnas_iterator_ *)(this))) -= i; return *this; }
//     iterator operator +(difference_type i) const
//     { iterator it = *this; return (it += i); }
//     iterator operator -(difference_type i) const
//     { iterator it = *this; return (it -= i); }
//     difference_type operator -(const iterator &i) const
//     { return *((dnas_iterator_ *)(this)) - *((dnas_iterator_ *)(&i)); }
	
//     reference operator *() const
//     { return *(piter + *((*((dnas_iterator_ *)(this))))); }
//     reference operator [](int ii)
//     { return *(piter + *((*((dnas_iterator_ *)(this+ii))))); }
    
//     bool operator ==(const iterator &i) const
//     { 
//       return ((piter) == ((i.piter))
//        && *((dnas_iterator_ *)(this)) == *((*((dnas_iterator_ *)(this)))));
//     }
//     bool operator !=(const iterator &i) const
//     { return !(i == *this); }
//     bool operator < (const iterator &i) const
//     { 
//       return ((piter) == ((i.piter))
// 	 && *((dnas_iterator_ *)(this)) < *((*((dnas_iterator_ *)(this)))));
//     }

//     tab_ref_index_iterator_(void) {}
//     tab_ref_index_iterator_(const ITER &iter, const dnas_iterator_ &dnas_iter)
//       : dnas_iterator_(dnas_iter), piter(iter) {}
//   };


//   template<typename ITER> class tab_ref_index
//   {
//     public :

//       typedef typename std::iterator_traits<ITER>::value_type value_type;
//       typedef typename std::iterator_traits<ITER>::pointer    pointer;
//       typedef typename std::iterator_traits<ITER>::pointer    const_pointer;
//       typedef typename std::iterator_traits<ITER>::reference  reference;
//       typedef typename std::iterator_traits<ITER>::reference  const_reference;
//       typedef typename std::iterator_traits<ITER>::difference_type
// 	                                                       difference_type;
//       typedef size_t size_type; 
//       typedef tab_ref_index_iterator_<ITER> iterator;
//       typedef iterator                          const_iterator;
//       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
//       typedef std::reverse_iterator<iterator> reverse_iterator;
    
//     protected :

//       ITER begin_;
//       dynamic_array<size_type> index_;

//     public :

//       bool empty(void) const { return index_.empty(); }
//       size_type size(void) const { return index_.size(); }


//       iterator begin(void) { return iterator(begin_, index_.begin()); }
//       const_iterator begin(void) const
//       { return iterator(begin_, index_.begin()); }
//       iterator end(void) { return iterator(begin_, index_.end()); }
//       const_iterator end(void) const { return iterator(begin_, index_.end()); }
//       reverse_iterator rbegin(void) { return reverse_iterator(end()); }
//       const_reverse_iterator rbegin(void) const
//       { return const_reverse_iterator(end()); }
//       reverse_iterator rend(void) { return reverse_iterator(begin()); }
//       const_reverse_iterator rend(void) const
//       { return const_reverse_iterator(begin()); }


//       reference front(void) { return *(begin_ +index_[0]); }
//       const_reference front(void) const { return *(begin_ +index_[0]); }
//       reference back(void) { return *(--(end())); }
//       const_reference back(void) const { return *(--(end())); }
   
//       tab_ref_index(void) {}
//       tab_ref_index(const ITER &b, const dynamic_array<size_type> &ind)
//       { begin_ = b; index_ = ind; }

//     // to be changed in a const_reference ?
//       value_type operator [](size_type ii) const
//       { return *(begin_ + index_[ii]);}
//       reference operator [](size_type ii) { return *(begin_ + index_[ii]); }

//   };


  /// iterator over a gmm::tab_ref_index_ref<ITER,ITER_INDEX>
  template<typename ITER, typename ITER_INDEX>
    struct tab_ref_index_ref_iterator_
    {
      typedef typename std::iterator_traits<ITER>::value_type value_type;
      typedef typename std::iterator_traits<ITER>::pointer    pointer;
      typedef typename std::iterator_traits<ITER>::reference  reference;
      typedef typename std::iterator_traits<ITER>::difference_type
                                                              difference_type;
      typedef std::random_access_iterator_tag iterator_category;
      typedef tab_ref_index_ref_iterator_<ITER, ITER_INDEX> iterator;
      typedef size_t size_type;

      ITER piter;
      ITER_INDEX iter_index;
      
      iterator operator ++(int)
      { iterator tmp = *this; ++iter_index; return tmp; }
      iterator operator --(int)
      { iterator tmp = *this; --iter_index; return tmp; }
      iterator &operator ++() { ++iter_index; return *this; }
      iterator &operator --() { --iter_index; return *this; }
      iterator &operator +=(difference_type i)
      { iter_index += i; return *this; }
      iterator &operator -=(difference_type i)
      { iter_index -= i; return *this; }
      iterator operator +(difference_type i) const
      { iterator it = *this; return (it += i); }
      iterator operator -(difference_type i) const
      { iterator it = *this; return (it -= i); }
      difference_type operator -(const iterator &i) const
      { return iter_index - i.iter_index; }
	
      reference operator *() const
      { return *(piter + *iter_index); }
      reference operator [](int ii) const
      { return *(piter + *(iter_index+ii)); }
      
      bool operator ==(const iterator &i) const
      { return ((piter) == ((i.piter)) && iter_index == i.iter_index); }
      bool operator !=(const iterator &i) const { return !(i == *this); }
      bool operator < (const iterator &i) const
      { return ((piter) == ((i.piter)) && iter_index < i.iter_index); }

      tab_ref_index_ref_iterator_(void) {}
      tab_ref_index_ref_iterator_(const ITER &iter, 
				  const ITER_INDEX &dnas_iter)
	: piter(iter), iter_index(dnas_iter) {}
      
    };

  /** 
      convenience template function for quick obtention of a indexed iterator
      without having to specify its (long) typename
  */
  template<typename ITER, typename ITER_INDEX>
  tab_ref_index_ref_iterator_<ITER,ITER_INDEX>
  index_ref_iterator(ITER it, ITER_INDEX it_i) {
    return tab_ref_index_ref_iterator_<ITER,ITER_INDEX>(it, it_i);
  }

  /** indexed array reference (given a container X, and a set of indexes I, 
      this class provides a pseudo-container Y such that
      @code Y[i] = X[I[i]] @endcode
  */
  template<typename ITER, typename ITER_INDEX> class tab_ref_index_ref {
  public :
    
    typedef std::iterator_traits<ITER>            traits_type;
    typedef typename traits_type::value_type      value_type;
    typedef typename traits_type::pointer         pointer;
    typedef typename traits_type::pointer         const_pointer;
    typedef typename traits_type::reference       reference;
    typedef typename traits_type::reference       const_reference;
    typedef typename traits_type::difference_type difference_type;
    typedef size_t                                size_type;
    typedef tab_ref_index_ref_iterator_<ITER, ITER_INDEX>   iterator;
    typedef iterator                              const_iterator;
    typedef std::reverse_iterator<const_iterator>     const_reverse_iterator;
    typedef std::reverse_iterator<iterator>           reverse_iterator;
    
  protected :

    ITER begin_;
    ITER_INDEX index_begin_, index_end_;

  public :
    
    bool empty(void) const { return index_begin_ == index_end_; }
    size_type size(void) const { return index_end_ - index_begin_; }
    
    iterator begin(void) { return iterator(begin_, index_begin_); }
    const_iterator begin(void) const
    { return iterator(begin_, index_begin_); }
    iterator end(void) { return iterator(begin_, index_end_); }
    const_iterator end(void) const { return iterator(begin_, index_end_); }
    reverse_iterator rbegin(void) { return reverse_iterator(end()); }
    const_reverse_iterator rbegin(void) const
    { return const_reverse_iterator(end()); }
    reverse_iterator rend(void) { return reverse_iterator(begin()); }
    const_reverse_iterator rend(void) const
    { return const_reverse_iterator(begin()); }
    
    reference front(void) { return *(begin_ + *index_begin_); }
    const_reference front(void) const { return *(begin_ + *index_begin_); }
    reference back(void) { return *(--(end())); }
    const_reference back(void) const { return *(--(end())); }
    void pop_front(void) { ++index_begin_; }
    
    tab_ref_index_ref(void) {}
    tab_ref_index_ref(const ITER &b, const ITER_INDEX &bi,
		      const ITER_INDEX &ei)
      : begin_(b), index_begin_(bi), index_end_(ei) {}
    
    // to be changed in a const_reference ?
    const_reference operator [](size_type ii) const
    { return *(begin_ + index_begin_[ii]);}
    reference operator [](size_type ii)
    { return *(begin_ + index_begin_[ii]); }

  };


  /* ********************************************************************* */
  /* Reference on regularly spaced elements.                               */
  /* ********************************************************************* */

  template<typename ITER> struct tab_ref_reg_spaced_iterator_ {
    
    typedef typename std::iterator_traits<ITER>::value_type value_type;
    typedef typename std::iterator_traits<ITER>::pointer    pointer;
    typedef typename std::iterator_traits<ITER>::reference  reference;
    typedef typename std::iterator_traits<ITER>::difference_type
                                                            difference_type;
    typedef typename std::iterator_traits<ITER>::iterator_category
                                                            iterator_category;
    typedef size_t size_type;
    typedef tab_ref_reg_spaced_iterator_<ITER> iterator;
    
    ITER it;
    size_type N, i;
    
    iterator operator ++(int) { iterator tmp = *this; i++; return tmp; }
    iterator operator --(int) { iterator tmp = *this; i--; return tmp; }
    iterator &operator ++()   { i++; return *this; }
    iterator &operator --()   { i--; return *this; }
    iterator &operator +=(difference_type ii) { i+=ii; return *this; }
    iterator &operator -=(difference_type ii) { i-=ii; return *this; }
    iterator operator +(difference_type ii) const 
    { iterator itt = *this; return (itt += ii); }
    iterator operator -(difference_type ii) const
    { iterator itt = *this; return (itt -= ii); }
    difference_type operator -(const iterator &ii) const
    { return (N ? (it - ii.it) / N : 0) + i - ii.i; }

    reference operator *() const { return *(it + i*N); }
    reference operator [](int ii) const { return *(it + (i+ii)*N); }

    bool operator ==(const iterator &ii) const
    { return (*this - ii) == difference_type(0); }
    bool operator !=(const iterator &ii) const
    { return  (*this - ii) != difference_type(0); }
    bool operator < (const iterator &ii) const
    { return (*this - ii) < difference_type(0); }

    tab_ref_reg_spaced_iterator_(void) {}
    tab_ref_reg_spaced_iterator_(const ITER &iter, size_type n, size_type ii)
      : it(iter), N(n), i(ii) { }
    
  };

  /** 
      convenience template function for quick obtention of a strided iterator
      without having to specify its (long) typename
  */
  template<typename ITER> tab_ref_reg_spaced_iterator_<ITER> 
  reg_spaced_iterator(ITER it, size_t stride) {
    return tab_ref_reg_spaced_iterator_<ITER>(it, stride);
  }

  /**
     provide a "strided" view a of container
  */
  template<typename ITER> class tab_ref_reg_spaced {
  public :

    typedef typename std::iterator_traits<ITER>::value_type value_type;
    typedef typename std::iterator_traits<ITER>::pointer    pointer;
    typedef typename std::iterator_traits<ITER>::pointer    const_pointer;
    typedef typename std::iterator_traits<ITER>::reference  reference;
    typedef typename std::iterator_traits<ITER>::reference  const_reference;
    typedef typename std::iterator_traits<ITER>::difference_type
            difference_type;
    typedef size_t size_type;
    typedef tab_ref_reg_spaced_iterator_<ITER> iterator;
    typedef iterator                          const_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
    typedef std::reverse_iterator<iterator> reverse_iterator;
    
  protected :

    ITER begin_;
    size_type N, size_;
    
  public :
    
    bool empty(void) const { return size_ == 0; }
    size_type size(void) const { return size_; }
    
    iterator begin(void) { return iterator(begin_, N, 0); }
    const_iterator begin(void) const { return iterator(begin_, N, 0); }
    iterator end(void) { return iterator(begin_, N, size_); }
    const_iterator end(void) const { return iterator(begin_, N, size_); }
    reverse_iterator rbegin(void) { return reverse_iterator(end()); }
    const_reverse_iterator rbegin(void) const
    { return const_reverse_iterator(end()); }
    reverse_iterator rend(void) { return reverse_iterator(begin()); }
    const_reverse_iterator rend(void) const
    { return const_reverse_iterator(begin()); }
    
    reference front(void) { return *begin_; }
    const_reference front(void) const { return *begin_; }
    reference back(void) { return *(begin_ + N * (size_-1)); }
    const_reference back(void) const { return *(begin_ + N * (size_-1)); }
    void pop_front(void) { begin_ += N; }
    
    tab_ref_reg_spaced(void) {}
    tab_ref_reg_spaced(const ITER &b, size_type n, size_type s)
      : begin_(b), N(n), size_(s) {}
    
    
    const_reference operator [](size_type ii) const
    { return *(begin_ + ii * N);}
    reference operator [](size_type ii) { return *(begin_ + ii * N); }
    
  };

  /// iterator over a tab_ref_with_selection
  template<typename ITER, typename COND> 
  struct tab_ref_with_selection_iterator_ : public ITER {
    typedef typename std::iterator_traits<ITER>::value_type value_type;
    typedef typename std::iterator_traits<ITER>::pointer    pointer;
    typedef typename std::iterator_traits<ITER>::reference  reference;
    typedef typename std::iterator_traits<ITER>::difference_type
                                                              difference_type;
    typedef std::forward_iterator_tag iterator_category;
    typedef tab_ref_with_selection_iterator_<ITER, COND> iterator;
    const COND cond;
    
    void forward(void) { while (!(cond)(*this)) ITER::operator ++(); }
    iterator &operator ++()
    { ITER::operator ++(); forward(); return *this; }
    iterator operator ++(int)
    { iterator tmp = *this; ++(*this); return tmp; }
    
    tab_ref_with_selection_iterator_(void) {}
    tab_ref_with_selection_iterator_(const ITER &iter, const COND c)
      : ITER(iter), cond(c) {}
    
  };

  /**
     given a container X and a predicate P, provide pseudo-container Y
     of all elements of X such that P(X[i]).
  */
  template<typename ITER, typename COND> class tab_ref_with_selection {
    
  protected :
    
    ITER begin_, end_;
    COND cond;
    
  public :
    
    typedef typename std::iterator_traits<ITER>::value_type value_type;
    typedef typename std::iterator_traits<ITER>::pointer    pointer;
    typedef typename std::iterator_traits<ITER>::pointer    const_pointer;
    typedef typename std::iterator_traits<ITER>::reference  reference;
    typedef typename std::iterator_traits<ITER>::reference  const_reference;
    typedef size_t  size_type;
    typedef tab_ref_with_selection_iterator_<ITER, COND> iterator;
    typedef iterator   const_iterator;
    
    iterator begin(void) const
    { iterator it(begin_, cond); it.forward(); return it; }
    iterator end(void) const { return iterator(end_, cond); }
    bool empty(void) const { return begin_ == end_; }
    
    value_type front(void) const { return *begin(); }
    void pop_front(void) { ++begin_; begin_ = begin(); }
    
    COND &condition(void) { return cond; }
    const COND &condition(void) const { return cond; }
    
    tab_ref_with_selection(void) {}
    tab_ref_with_selection(const ITER &b, const ITER &e, const COND &c)
      : begin_(b), end_(e), cond(c) { begin_ = begin(); }
    
  };

}

#endif /* GMM_REF_H__  */
