/* linbox/vector/subiterator.h
 * Copyright (C) 2002 William J. Turner
 *
 * Written by William J. Turner <wjturner@acm.org>
 * Mods by -bds
 * ------------------------------------
 *
 *
 * ========LICENCE========
 * This file is part of the library LinBox.
 *
 * LinBox 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ========LICENCE========
 *.
 */

#ifndef __LINBOX_subiterator_H
#define __LINBOX_subiterator_H

#include <iterator>
#include <vector>

// namespace in which all LinBox code resides
namespace LinBox
{

	/** \brief Subvector iterator class provides striding iterators.
	  \ingroup vector

	 *  A Subiterator steps by a fixed stride thru the underlying container.
	 *  Subiter<Iterator> requires that Iterator be a random access iterator class
	 *  and then itself provides the full functionality of a random access iterator
	 *  class.  See STL documentation for that functionality.
	 *  Documented here is only the constructor from (1) an iterator of an
	 *  underlying container and (2) a stride amount.
	 */
	template <typename Iterator>
	class Subiterator {
	public:
		// Types

		typedef typename std::iterator_traits<Iterator>::iterator_category	iterator_category;
		typedef typename std::iterator_traits<Iterator>::value_type		value_type;
		typedef typename std::iterator_traits<Iterator>::difference_type	difference_type;
		typedef typename std::iterator_traits<Iterator>::pointer		pointer;
		typedef typename std::iterator_traits<Iterator>::reference		reference;


		// Basic constructors

		Subiterator ()
		{
		}

		/** Constructors.
		 *
		 *  Subiterator p (pp, 3) provides an iterator which initially  has
		 *  the same reference, but for which increments and offsets step by
		 *  the amount stride rather than 1.
		 *  Thus p+k is equivalent to pp+(stride*k).
		 *
		 *  Striding iterators are easily positioned beyond the bounds of the
		 *  underlying container.  It is up to the user to dereference the
		 *  iterator only when it has a valid reference.
		 */
		Subiterator (const Iterator &iter, const difference_type& stride = 1) :
			_iter (iter), _stride (stride)
		{
		}

		// copy-constructor
		Subiterator (const Subiterator & iter) :
			_iter (iter._iter), _stride (iter._stride)
		{
		}

		Subiterator (Iterator & iter) :
			_iter (iter._iter), _stride (iter._stride)
		{
		}

		// constructor
		template<class Iterator2>
		Subiterator (const Subiterator<Iterator2>& iter) :
			_iter (iter._iter), _stride (iter._stride)
		{
		}

		template<class Iterator2>
		Subiterator (Subiterator<Iterator2>& iter) :
			_iter (iter._iter), _stride (iter._stride)
		{
		}

		// copy-assign

		template<class Iterator2>
		Subiterator& operator = (const Subiterator<Iterator2>& sub)
		{
			_iter  =sub.showIterator();
			_stride=sub.showStride();
			return *this;
		}

		Subiterator& operator = (const Subiterator& sub)
		{
			_iter  =sub.showIterator();
			_stride=sub.showStride();
			return *this;
		}

		// Access operations
		const reference operator * () const
		{
			return *_iter;
		}

		reference operator * ()
		{
			return *_iter;
		}

		Iterator operator -> () const
		{
			return _iter;
		}

		reference operator [] (difference_type n) 
		{
			return _iter[n * _stride];
		}

		const reference operator [] (difference_type n) const
		{
			return _iter[n * _stride];
		}

		// Iteration operations

		Subiterator& operator ++ ()
		{ 
			_iter += _stride;
			return *this;
		}

		Subiterator operator ++ (int)
		{
			Subiterator tmp = *this;
			_iter += _stride;
			return tmp;
		}

		Subiterator& operator -- ()
		{
			_iter -= _stride;
			return *this;
		}

		Subiterator operator -- (int)
		{
			Subiterator tmp = *this;
			_iter -= _stride;
			return tmp;
		}

		Subiterator operator + (difference_type n) const
		{
			return Subiterator (_iter + (n * _stride), _stride);
		}

		Subiterator& operator += (difference_type n)
		{
			_iter += (n * _stride);
			return *this;
		}

		Subiterator operator - (difference_type n) const
		{
			return Subiterator (_iter - (n * _stride), _stride);
		}

		difference_type operator - (const Subiterator& x) const
		{
			return (_iter - x._iter)/_stride;
		}

		Subiterator& operator -= (difference_type n)
		{
			_iter -= (n * _stride);
			return *this;
		}

		// Comparison operations

		bool operator == (const Subiterator& i) const
		{
			return ( (this->_stride == i._stride) && (this->_iter == i._iter) );
		}

		bool operator != (const Subiterator& i) const
		{
			return !(*this == i);
		}

		bool operator < (const Subiterator& i) const
		{
			return (this->_iter < i._iter);
		}

		bool operator > (const Subiterator& i) const
		{
			return (this->_iter > i._iter);
		}

		bool operator <= (const Subiterator& i) const
		{
			return (this->_iter <= i._iter);
		}

		bool operator >= (const Subiterator& i) const
		{
			return (this->_iter >= i._iter);
		}

		// swap

		void swap (Subiterator& x)
		{
			std::swap (_iter, x._iter); std::swap (_stride, x._stride);
		}

		// view protected members

		difference_type showStride() const
		{
			return this->_stride ;
		}

		const Iterator & showIterator() const
		{
			return this->_iter ;
		}


	protected:

		Iterator          _iter;	//!< @internal wrapped iterator

		difference_type	_stride;	//!< @internal length between iterations

	}; // template <class Iterator> class Subiterator

} // namespace LinBox

#endif // __LINBOX_subiterator_H

// Local Variables:
// mode: C++
// tab-width: 4
// indent-tabs-mode: nil
// c-basic-offset: 4
// End:
// vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s
