/* linbox/matrix/sparse-matrix.h
 * Copyright (C) 2001-2002 Bradford Hovinen
 *               1999-2001 William J Turner,
 *
 * Written by William J Turner <wjturner@math.ncsu.edu>,
 *            Bradford Hovinen <hovinen@cis.udel.edu>
 *
 * --------------------------------------------------------
 * 2003-01-11  Bradford Hovinen  <bghovinen@math.uwaterloo.ca>
 *
 * Move from blackbox/sparse-base.h to matrix/sparse-matrix.h
 * ------------------------------------
 * 2002-11-28  Bradford Hovinen  <bghovinen@math.uwaterloo.ca>
 *
 *   - Renamed ColOfRowsIterator to RowIterator
 *   - Named template argument _Row rather than Row; add a typedef to Row
 *   - Named template argument _Element rather than Row; add a typedef to Element
 *   - Renamed IndexIterator as IndexedIterator, and adjusted to match
 *     interface in DenseMatrixBase
 * ------------------------------------
 * 2002-08-06  Bradford Hovinen  <hovinen@cis.udel.edu>
 *
 * Renamed to sparse-base.h from sparse0-base.h
 * ------------------------------------
 * Modified by Bradford Hovinen <hovinen@cis.udel.edu>
 *
 * Refactoring:
 *   - Eliminated SparseMatrixAux and moved that functionality into Sparse0
 *   - Made SparseMatrix parameterized only on the element type
 *   - New read/write implementations for SparseMatrix, supporting multiple
 *     formats
 *   - Eliminated Gaussian elimination code
 *   - Added iterators, including ColOfRowsIterator, Iterator, and
 *     IndexIterator
 *   - Eliminated operator []; added getEntry; changed put_value to setEntry
 * ------------------------------------
 *
 *
 * ========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========
 *.
 */

/** @file linbox/matrix/sparsematrix/sparse-associative-vector.h
 * @ingroup sparsematrix
 * @brief
 */

#ifndef __LINBOX_matrix_sparsematrix_sparse_associative_vector_H
#define __LINBOX_matrix_sparsematrix_sparse_associative_vector_H


namespace LinBox { namespace Protected {

	/* Specialization for sparse associative vectors */
	template <class _Field, class _Row>
	class SparseMatrixGeneric<_Field, _Row, VectorCategories::SparseAssociativeVectorTag > {
	public:

		typedef         _Field          Field;
		typedef typename Field::Element Element;

		typedef _Row Row;
		typedef const Row ConstRow;
		typedef _SP_BB_VECTOR_<Row> Rep;
		typedef VectorCategories::SparseAssociativeVectorTag myTrait;
		typedef SparseMatrixGeneric<_Field, _Row, myTrait> Self_t;

#ifdef __LINBOX_PARALLEL
		BB_list_list sub_list;
#endif


		template<typename _Tp1, typename _R1 = typename Rebind<_Row,_Tp1>::other >
		struct rebind {
			typedef SparseMatrixGeneric<_Tp1, _R1> other;

			void operator() (other & Ap, const Self_t& A) {

				typename _Tp1::Element e;

				Hom<typename Self_t::Field, _Tp1> hom(A.field(), Ap.field());
				for( typename Self_t::ConstIndexedIterator
				     indices = A.IndexedBegin();
				     (indices != A.IndexedEnd()) ;
				     ++indices ) {
					// hom. image (e, A.getEntry(indices.rowIndex(),indices.colIndex()) );
					hom. image (e, indices.value() );
					if (!Ap.field().isZero(e))
						Ap.setEntry (indices.rowIndex(),
							     indices.colIndex(), e);
				}
			}

		};

		template<typename _Tp1, typename _Rw1>
		SparseMatrixGeneric (const SparseMatrixGeneric<_Tp1, _Rw1, myTrait> &Mat, const Field& F) :
			_field (F),
			_MD (F), _AT (*this)
			, _matA (Mat.rowdim()), _m (Mat.rowdim()), _n (Mat.coldim())
		{
			typename SparseMatrixGeneric<_Tp1,_Rw1, myTrait>::template rebind<Field,_Row>()(*this, Mat);
		}

		SparseMatrixGeneric (const Field & F, size_t m, size_t n) :
			_field(F),
			_MD(F),_AT(*this),
			_matA (m), _m (m), _n (n)
		{}

		SparseMatrixGeneric (const Field & F) :
			_field (F),
			_MD(F),_AT(*this),
			_matA(0), _m(0), _n(0)
		{};


		SparseMatrixGeneric (const SparseMatrixGeneric<Field, Row> &A) :
			_field(A.field()),
			_MD(A.field()),_AT(*this),
			_matA (A._matA), _m (A._m), _n (A._n)
		{}

		template<class VectorType>
		SparseMatrixGeneric (const SparseMatrixGeneric<Field, VectorType> &A) :
			_field(A.field()),
			_MD(A.field()),_AT(*this),
			_matA(A.rowdim()), _m (A.rowdim()), _n (A.coldim())
		{
			typename Rep::iterator meit = this->_matA.begin();
			typename SparseMatrixGeneric<Field, VectorType>::Rep::const_iterator copit = A._matA.begin();
			for( ; meit != this->_matA.end(); ++meit, ++copit)
				LinBox::RawVector<Element>::convert(*meit, *copit);
		}

		/** Constructor from a MatrixStream
		*/
		SparseMatrixGeneric ( MatrixStream<Field>& ms );

		template<class VectStream>
		SparseMatrixGeneric (const Field &F, VectStream &stream) :
			_field (F), _MD (F), _AT (*this)
			, _matA (stream.size()), _m (stream.size()), _n (stream.dim())
		{
			typename Self_t::RowIterator i;

			for (i = Self_t::rowBegin (); i != Self_t::rowEnd (); ++i)
				stream >> *i;
		}

		~SparseMatrixGeneric () {}

		size_t rowdim () const
		{
			return _m;
		}

		size_t coldim () const
		{
			return _n;
		}

		size_t size () const
		{
			size_t s(0);
			for(typename Rep::const_iterator it = _matA.begin(); it != _matA.end(); ++it)
				s+= LinBox::RawVector<Element>::size(*it);
			return s;
		}

		std::istream &read (std::istream &is
				    , Tag::FileFormat format  = Tag::FileFormat::Detect)
		{
			return SparseMatrixReadHelper<Self_t>::read (*this, is
									      , format);
		}


		std::ostream &write (std::ostream &os
				     , Tag::FileFormat format   = Tag::FileFormat::MatrixMarket) const
		{
			return SparseMatrixWriteHelper<Self_t>::write (*this, os, format);
		}

		const Element& appendEntry(size_t i, size_t j, const Element & value) { return setEntry(i,j,value) ;}
		void finalize(){}

		const Element&  setEntry (size_t i, size_t j, const Element &value)
		{
			return _matA[i][j] = value;
		}

		Element  &refEntry (size_t i, size_t j)
		{
			return _matA[i][j];
		}

		const Element &getEntry (size_t i, size_t j) const;

		Element       &getEntry (Element &x, size_t i, size_t j) const
		{
			return x = _matA[i][j];
		}

		typedef typename Rep::iterator RowIterator;
		typedef typename Rep::const_iterator ConstRowIterator;

		ConstRowIterator rowBegin () const
		{
			return _matA.begin ();
		}

		ConstRowIterator rowEnd () const
		{
			return _matA.end ();
		}

		RowIterator rowBegin ()
		{
			return _matA.begin ();
		}

		RowIterator rowEnd ()
		{
			return _matA.end ();
		}

		template <class RepIterator, class RowEltIterator, class _I_Element>
		class _Iterator {
		public:
			typedef _I_Element value_type;

			_Iterator (const RepIterator &i, const RowEltIterator &j, const RepIterator &A_end) :
				_i (i), _j (j), _A_end (A_end)
			{
				if( _i == _A_end ) return;
				while( _j == _i->end() ) {
					if( ++_i == _A_end ) return;
					_j = _i->begin();
				}
			}

			_Iterator (const _Iterator &iter) :
				_i (iter._i), _j (iter._j), _A_end (iter._A_end)
			{}

			_Iterator () {}

			_Iterator &operator = (const _Iterator &iter)
			{
				_i = iter._i;
				_j = iter._j;
				_A_end = iter._A_end;

				return *this;
			}

			bool operator == (const _Iterator &i) const
			{
				return (_i == i._i) && (_j == i._j);
			}

			bool operator != (const _Iterator &i) const
			{
				return (_i != i._i) || (_j != i._j);
			}

			_Iterator &operator ++ ()
			{
				while (++_j == _i->end ()) {
					if (++_i == _A_end ()) return *this;
					_j = _i->begin ();
				}
				return *this;
			}

			_Iterator operator ++ (int)
			{
				_Iterator tmp = *this;
				++(*this);
				return tmp;
			}

			_Iterator &operator -- ()
			{
				while (_j == _i->begin ())
					_j = (--_i)->end ();
				--_j;
				return *this;
			}

			_Iterator operator -- (int)
			{
				_Iterator tmp = *this;
				--(*this);
				return tmp;
			}

			value_type &operator * ()
			{
				return _j->second;
			}

			value_type *operator -> ()
			{
				return &(_j->second);
			}

		private:
			RepIterator _i;
			RowEltIterator _j;
			RepIterator _A_end;
		};

		typedef _Iterator<typename Rep::iterator, typename Row::iterator, Element> Iterator;
		typedef _Iterator<typename Rep::const_iterator, typename Row::const_iterator, const Element> ConstIterator;

		Iterator Begin ()
		{
			return Iterator (_matA.begin (), _matA.front ().begin (), _matA.end ());
		}
		Iterator End ()
		{
			return Iterator (_matA.end (), _matA.back ().end (), _matA.end ());
		}
		ConstIterator Begin () const
		{
			return ConstIterator (_matA.begin (), _matA.front ().begin (), _matA.end ());
		}
		ConstIterator End () const
		{
			return ConstIterator (_matA.end (), _matA.back ().end (), _matA.end ());
		}

		//! no doc
		template <class RepIterator, class RowIdxIterator, class _I_Element>
		class _IndexedIterator {
		public:
			// typedef std::pair<size_t, size_t> value_type;
			typedef _I_Element value_type;

			_IndexedIterator (size_t idx, const RepIterator &i, const RowIdxIterator &j, const RepIterator &A_end) :
				_i (i), _j (j), _A_end (A_end), _r_index (idx), _c_index (0)
			{
				if( _i == _A_end ) return;
				while( _j == _i->end() ) {
					++_r_index;
					if( ++_i == _A_end ) return;
					_j = _i->begin();
				}
				_c_index = _j->first;
			}

			_IndexedIterator (const _IndexedIterator &iter) :
				_i (iter._i), _j (iter._j), _A_end (iter._A_end), _r_index (iter._r_index), _c_index (iter._c_index)
			{}

			_IndexedIterator ()
			{}

			_IndexedIterator &operator = (const _IndexedIterator &iter)
			{
				_A_end = iter._A_end;
				_i = iter._i;
				_j = iter._j;
				_r_index = iter._r_index;
				_c_index = iter._c_index;

				return *this;
			}

			bool operator == (const _IndexedIterator &i) const
			{
				return (_i == i._i) && (_j == i._j);
			}

			bool operator != (const _IndexedIterator &i) const
			{
				return (_i != i._i) || (_j != i._j);
			}

			_IndexedIterator &operator ++ ()
			{
				++_j;
				while (_j == _i->end ()) {
					++_r_index;
					if (++_i == _A_end ) return *this;
					_j = _i->begin ();
				}

				_c_index = _j->first;

				return *this;
			}

			_IndexedIterator operator ++ (int)
			{
				_IndexedIterator tmp = *this;
				++(*this);
				return tmp;
			}

			_IndexedIterator &operator -- ()
			{
				while (_j == _i->begin ()) {
					_j = (--_i)->end ();
					--_r_index;
				}

				--_j;
				_c_index = _j->second;
				return *this;
			}

			_IndexedIterator operator -- (int)
			{
				_IndexedIterator tmp = *this;
				--(*this);
				return tmp;
			}

			value_type &operator * ()
			{
				return _j->second;
			}
			const value_type &operator * () const
			{
				return _j->second;
			}
			value_type *operator -> ()
			{
				return &(_j->second);
			}
			const value_type *operator -> () const
			{
				return &(_j->second);
			}

			size_t rowIndex () const
			{
				return _r_index;
			}
			size_t colIndex () const
			{
				return _c_index;
			}
			const value_type &value() const
			{
				return _j->second;
			}

		private:
			RepIterator _i;
			RowIdxIterator _j;
			RepIterator _A_end;

			mutable size_t _r_index;
			mutable size_t _c_index;
		};

		typedef _IndexedIterator<typename Rep::iterator, typename Row::iterator, Element> IndexedIterator;
		typedef _IndexedIterator<typename Rep::const_iterator, typename Row::const_iterator, Element> ConstIndexedIterator;

		IndexedIterator IndexedBegin ()
		{
			return IndexedIterator (0, _matA.begin (), _matA.front ().begin (), _matA.end ());
		}
		IndexedIterator IndexedEnd ()
		{
			return IndexedIterator (_m, _matA.end (), _matA.back ().end (), _matA.end ());
		}
		ConstIndexedIterator IndexedBegin () const
		{
			return ConstIndexedIterator (0, _matA.begin (), _matA.front ().begin (), _matA.end ());
		}
		ConstIndexedIterator IndexedEnd () const
		{
			return ConstIndexedIterator (_m, _matA.end (), _matA.back ().end (), _matA.end ());
		}

		Row &getRow (size_t i) {
			return _matA[i];
		}
		Row &operator [] (size_t i) {
			return _matA[i];
		}
		ConstRow &operator [] (size_t i) const
		{
			return _matA[i];
		}

		template <class Vector> Vector &columnDensity (Vector &v) const;
		SparseMatrixGeneric &transpose (SparseMatrixGeneric &AT) const;

		const Field & field() const
		{
			return _field ;
		}
		/** Matrix-vector product
		 * \f$y = A x\f$.
		 * @return reference to output vector y
		 * @param  x input vector
		 * @param y
		 */
		template <class OutVector, class InVector>
		OutVector &apply (OutVector &y, const InVector &x) const
		{
#ifdef __LINBOX_PARALLEL
			return BlackboxParallel (y, *this, x, BBBase::Apply);
#else
			return _MD.vectorMul (y, *this, x);
#endif
		}

		/** Transpose matrix-vector product
		 * \f$ y = A^T x\f$.
		 * @return reference to output vector y
		 * @param  x input vector
		 * @param y
		 */
		template <class OutVector, class InVector>
		OutVector &applyTranspose (OutVector& y, const InVector &x) const
		{
#ifdef __LINBOX_PARALLEL
			return BlackboxParallel (y, *this, x, BBBase::ApplyTranspose);
#else
			return _MD.vectorMul (y, _AT, x);
#endif
		}

		const Rep & getRep() const
		{
			return _matA;
		}

		Rep & refRep()
		{
			return _matA;
		}

		void resize( const size_t & m, const size_t & n, const size_t & nnz = 0)
		{
			_m = m ;
			_n = n ;
			_matA.resize(m);

		}


	protected:

		friend class SparseMatrixWriteHelper<Self_t>;
		friend class SparseMatrixReadHelper<Self_t>;

		const Field & _field;

		MatrixDomain<Field>       _MD; // Matrix domain for matrix operations
		TransposeMatrix<SparseMatrixGeneric<_Field, _Row> > _AT;
		Rep               _matA;
		size_t            _m;
		size_t            _n;

		// template<class F, class R, class T> friend class SparseMatrix;
	};

} // namespace Protected
} // namespace LinBox

#include "linbox/matrix/sparsematrix/sparse-associative-vector.inl"

namespace LinBox
{ /*  SparseMatrix for SparseMatrixFormat::SparseMap */

	template <class _Field /*, class _Row */  >
	class SparseMatrix<_Field, SparseMatrixFormat::SparseMap/* <_Row> */ > : public Protected::SparseMatrixGeneric<_Field,/*  _Row */ typename Vector<_Field>::SparseMap,VectorCategories::SparseAssociativeVectorTag> {
	public:
		typedef VectorCategories::SparseAssociativeVectorTag  myTrait ;
		typedef _Field                                       Field ; //!< Field
		typedef typename _Field::Element                   Element ; //!< Element
		typedef const Element                         constElement ; //!< const Element
		typedef typename Vector<_Field>::SparseMap             Row ;
		typedef SparseMatrixFormat::SparseMap              Storage ; //!< Matrix Storage Format
		typedef SparseMatrix<_Field,Storage>               Self_t ; //!< Self type
		typedef Protected::SparseMatrixGeneric<_Field,Row,myTrait >         Father_t ;

	public:
		template<class VectStream>
		SparseMatrix (const Field &F, VectStream &stream) :
			Father_t(F,stream)
		{}

		SparseMatrix(const Field & F, size_t m, size_t n) :
			Father_t(F, m, n)
		{}

		SparseMatrix(const Field & F) :
			Father_t(F)
		{}

		SparseMatrix ( MatrixStream<Field>& ms ) :
			Father_t(ms)
		{}

		template<class _Tp1, class _Storage>
		SparseMatrix(const SparseMatrix<_Tp1,_Storage> & Mat, const Field & F) :
			Father_t(F, Mat.rowdim(), Mat.coldim())
		{
			typename SparseMatrix<_Tp1,_Storage>::template rebind<Field,Storage>()(*this, Mat);

		}



		// using Father_t::RowIterator;

		template<typename _Tp1, typename _R1 = SparseMatrixFormat::SparseMap >
		struct rebind {
			typedef SparseMatrix<_Tp1, _R1> other;

			void operator() (other & Ap, const Self_t& A) {

				typename _Tp1::Element e;

				Hom<typename Self_t::Field, _Tp1> hom(A.field(), Ap.field());
				for( typename Self_t::ConstIndexedIterator
				     indices = A.IndexedBegin();
				     (indices != A.IndexedEnd()) ;
				     ++indices ) {
					hom. image (e, indices.value() );
					if (!Ap.field().isZero(e))
						Ap.setEntry (indices.rowIndex(),
							     indices.colIndex(), e);
				}
			}

		};


	} ; // SparseMatrix



} // namespace LinBox


#endif // __LINBOX_matrix_sparsematrix_sparse_associative_vector_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
