/* Copyright (c) 1997-2005
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program 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 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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 General Public License for more details.
*/

#ifndef _POLYMAKE_MATRIX_METHODS_H
#define _POLYMAKE_MATRIX_METHODS_H "$Project: polymake $$Id: matrix_methods.h,v 1.17 2006/02/01 17:39:16 gawrilow Exp $"

#ifndef _POLYMAKE_INDEXED_SUBSET_H
#  include <IndexedSubset.h>
#endif
#ifndef _POLYMAKE_CONTAINER_CHAIN_H
#  include <ContainerChain.h>
#endif

// OSF/1 defines this name to something odd that we don't need at all
#ifdef minor
#undef minor
#endif

namespace pm {

/* --------------------------------
 *  Matrix masquerades: Rows, Cols
 * -------------------------------- */

template <typename _Matrix> class Rows;
template <typename _Matrix> class Cols;

template <typename _Matrix>
struct spec_object_traits< Rows<_Matrix> >
   : spec_object_traits<is_container> {
   static const IO_separator_kind IO_separator=IO_sep_enforce;
};
template <typename _Matrix>
struct spec_object_traits< Cols<_Matrix> >
   : spec_object_traits< Rows<_Matrix> > { };

template <template <typename> class Masquerade, typename _Matrix,
	  bool _is_concrete=Concrete<typename deref<_Matrix>::type>::answer>
struct masquerade_matrix_helper {
   typedef typename deref<_Matrix>::type matrix_type;
   typedef typename inherit_const<Masquerade<matrix_type>, _Matrix>::type apparent_type;
   typedef typename assign_const<apparent_type, effectively_const<_Matrix>::answer>::type result_type;
};
template <template <typename> class Masquerade, typename _Matrix>
struct masquerade_matrix_helper<Masquerade, _Matrix, false> {
   typedef typename Concrete<typename deref<_Matrix>::type>::type matrix_type;
   typedef typename inherit_const<Masquerade<matrix_type>, _Matrix>::type apparent_type;
   typedef apparent_type result_type;
};

template <template <typename> class Masquerade, typename _Matrix> inline
typename masquerade_matrix_helper<Masquerade, _Matrix>::result_type&
masquerade_matrix(_Matrix& m)
{
   return const_cast<typename masquerade_matrix_helper<Masquerade, _Matrix>::result_type&>
                    (reinterpret_cast<typename masquerade_matrix_helper<Masquerade, _Matrix>::apparent_type&>
		                     (Concrete<_Matrix>::get(m)));
}

template <typename _Matrix> inline
typename masquerade_matrix_helper<Rows, _Matrix>::result_type&
rows(_Matrix& m)
{
   return masquerade_matrix<Rows>(m);
}

template <typename _Matrix> inline
typename masquerade_matrix_helper<Rows, const _Matrix>::result_type&
rows(const _Matrix& m)
{
   return masquerade_matrix<Rows>(m);
}

template <typename _Matrix> inline
typename masquerade_matrix_helper<Cols, _Matrix>::result_type&
cols(_Matrix& m)
{
   return masquerade_matrix<Cols>(m);
}

template <typename _Matrix> inline
typename masquerade_matrix_helper<Cols, const _Matrix>::result_type&
cols(const _Matrix& m)
{
   return masquerade_matrix<Cols>(m);
}

template <typename _Matrix> inline
int empty_rows(const _Matrix& m)
{
   int cnt=0;
   for (typename Entire< Rows<_Matrix> >::const_iterator r=entire(rows(m)); !r.at_end(); ++r)
      if (!r->size()) ++cnt;
   return cnt;
}

template <typename _Matrix> inline
int empty_cols(const _Matrix& m)
{
   int cnt=0;
   for (typename Entire< Cols<_Matrix> >::const_iterator c=entire(cols(m)); !c.at_end(); ++c)
      if (!c->size()) ++cnt;
   return cnt;
}

/* -------------------------------
 *  Matrix masquerade: Transposed
 * ------------------------------- */

template <typename _Matrix>
class Transposed
   : public inherit_generic<Transposed<_Matrix>, _Matrix>::type {
protected:
   ~Transposed();
public:
   typedef typename _Matrix::value_type value_type;
   typedef typename _Matrix::reference reference;
   typedef typename _Matrix::const_reference const_reference;

   _Matrix& hidden() { return reinterpret_cast<_Matrix&>(*this); }
   const _Matrix& hidden() const { return reinterpret_cast<const _Matrix&>(*this); }
};

template <typename _Matrix>
struct spec_object_traits< Transposed<_Matrix> >
   : spec_object_traits<is_container> {
   static const bool is_temporary=false;
};

template <typename _Matrix>
struct check_container_feature<Transposed<_Matrix>, sparse>
   : check_container_feature<_Matrix, sparse> { };

template <typename _Matrix>
struct check_container_feature<Transposed<_Matrix>, pure_sparse>
   : check_container_feature<_Matrix, pure_sparse> { };

template <typename _Matrix> inline
typename masquerade_matrix_helper<Transposed, _Matrix>::result_type&
T(_Matrix& m)
{
   return masquerade_matrix<Transposed>(m);
}

template <typename _Matrix> inline
typename masquerade_matrix_helper<Transposed, const _Matrix>::result_type&
T(const _Matrix& m)
{
   return masquerade_matrix<Transposed>(m);
}

template <typename _Matrix>
class Rows< Transposed<_Matrix> > : public Cols<_Matrix> { };

template <typename _Matrix>
class Cols< Transposed<_Matrix> > : public Rows<_Matrix> { };

/* ------------------------------------------------
 *  Methods defined for all matrices,
 *  depending on Rows::category and Cols::category
 * ------------------------------------------------ */
template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef> class MatrixMinor;

template <typename Matrix, typename RowCategory=typename container_traits< Rows<Matrix> >::category>
class matrix_row_methods {
public:
   typedef typename deref<typename container_traits< Rows<Matrix> >::reference>::type row_type;
   typedef typename deref<typename container_traits< Rows<Matrix> >::const_reference>::type const_row_type;

   int rows() const
   {
      return pm::rows(static_cast<const Matrix&>(*this)).size();
   }
};

template <typename Matrix>
class matrix_row_methods<Matrix, random_access_iterator_tag>
   : public matrix_row_methods<Matrix, forward_iterator_tag> {
public:
   typename container_traits< Rows<Matrix> >::reference
   row(int i)
   {
      return pm::rows(*static_cast<Matrix*>(this))[i];
   }
   typename container_traits< Rows<Matrix> >::reference
   operator[] (int i)
   {
      return pm::rows(*static_cast<Matrix*>(this))[i];
   }
   typename container_traits< Rows<Matrix> >::const_reference
   row(int i) const
   {
      return pm::rows(*static_cast<const Matrix*>(this))[i];
   }
   typename container_traits< Rows<Matrix> >::const_reference
   operator[] (int i) const
   {
      return pm::rows(*static_cast<const Matrix*>(this))[i];
   }
};

template <typename Matrix, typename ColCategory=typename container_traits< Cols<Matrix> >::category>
class matrix_col_methods {
public:
   typedef typename deref<typename container_traits< Cols<Matrix> >::reference>::type col_type;
   typedef typename deref<typename container_traits< Cols<Matrix> >::const_reference>::type const_col_type;

   int cols() const
   {
      return pm::cols(static_cast<const Matrix&>(*this)).size();
   }
};

template <typename Matrix>
class matrix_col_methods<Matrix, random_access_iterator_tag>
   : public matrix_col_methods<Matrix, forward_iterator_tag> {
public:
   typename container_traits< Cols<Matrix> >::reference
   col(int i)
   {
      return pm::cols(*static_cast<Matrix*>(this))[i];
   }
   typename container_traits< Cols<Matrix> >::const_reference
   col(int i) const
   {
      return pm::cols(*static_cast<const Matrix*>(this))[i];
   }
};

template <typename Matrix, typename E=typename Matrix::element_type,
          typename RowCategory=typename container_traits< Rows<Matrix> >::category,
          typename ColCategory=typename container_traits< Cols<Matrix> >::category>
class matrix_methods
   : public matrix_row_methods<Matrix>, public matrix_col_methods<Matrix> {
public:
   typedef E element_type;

   template <typename RowIndexSet, typename ColIndexSet>
   MatrixMinor<Matrix&,
	       typename prevent_lazy<const typename Concrete<RowIndexSet>::type&>::type,
	       typename prevent_lazy<const typename Concrete<ColIndexSet>::type&>::type>
   minor(const RowIndexSet& row_indices, const ColIndexSet& col_indices)
   {
      INDEX_CHECK(!complement_helper<typename attrib<typename Concrete<RowIndexSet>::type>::plus_const_ref>::dimension_check
		  (Concrete<RowIndexSet>::get(row_indices), this->rows()),
		  "minor - row indices out of range");
      INDEX_CHECK(!complement_helper<typename attrib<typename Concrete<ColIndexSet>::type>::plus_const_ref>::dimension_check
		  (Concrete<ColIndexSet>::get(col_indices), this->cols()),
		  "minor - column indices out of range");
											    
      return MatrixMinor<Matrix&,
                         typename prevent_lazy<const typename Concrete<RowIndexSet>::type&>::type,
                         typename prevent_lazy<const typename Concrete<ColIndexSet>::type&>::type>
                        (*static_cast<Matrix*>(this),
			 Concrete<RowIndexSet>::get(row_indices),
			 Concrete<ColIndexSet>::get(col_indices));
   }

   template <typename RowIndexSet, typename ColIndexSet>
   const MatrixMinor<const Matrix&,
		     typename prevent_lazy<const typename Concrete<RowIndexSet>::type&>::type,
		     typename prevent_lazy<const typename Concrete<ColIndexSet>::type&>::type>
   minor(const RowIndexSet& row_indices, const ColIndexSet& col_indices) const
   {
      INDEX_CHECK(!complement_helper<typename attrib<typename Concrete<RowIndexSet>::type>::plus_const_ref>::dimension_check
		  (Concrete<RowIndexSet>::get(row_indices), this->rows()),
		  "minor - row indices out of range");
      INDEX_CHECK(!complement_helper<typename attrib<typename Concrete<ColIndexSet>::type>::plus_const_ref>::dimension_check
		  (Concrete<ColIndexSet>::get(col_indices), this->cols()),
		  "minor - column indices out of range");
											    
      return MatrixMinor<const Matrix&,
                         typename prevent_lazy<const typename Concrete<RowIndexSet>::type&>::type,
                         typename prevent_lazy<const typename Concrete<ColIndexSet>::type&>::type>
                        (*static_cast<const Matrix*>(this),
			 Concrete<RowIndexSet>::get(row_indices),
			 Concrete<ColIndexSet>::get(col_indices));
   }
};

template <typename Matrix> class matrix_random_access_methods { };

template <typename Matrix, typename E>
class matrix_methods<Matrix, E, random_access_iterator_tag, random_access_iterator_tag>
   : public matrix_methods<Matrix, E, forward_iterator_tag, forward_iterator_tag>,
     public matrix_random_access_methods<Matrix> { };

template <typename _Matrix>
class matrix_random_access_methods< Transposed<_Matrix> > {
public:
   typename _Matrix::reference
   operator() (int i, int j)
   {
      return static_cast<Transposed<_Matrix>*>(this)->hidden()(j,i);
   }
   typename _Matrix::const_reference
   operator() (int i, int j) const
   {
      return static_cast<const Transposed<_Matrix>*>(this)->hidden()(j,i);
   }
};

template <typename _Matrix>
class Transposed< Transposed<_Matrix> > : public _Matrix {
protected:
   Transposed();
   ~Transposed();
};

#if 0
template <class _Matrix>
class Dimensions {
protected:
   int dims[2];
public:
   Dimensions(const _Matrix& m) {
      dims[0]=m.rows(); dims[1]=m.cols();
   }

   typedef const int* iterator;
   typedef iterator const_iterator;

   int size() const { return 2; }

   iterator begin() const { return dims; }
   iterator end() const { return dims+2; }
};

template <class _Matrix> inline
const Dimensions<typename Concrete<_Matrix>::type>
dimensions(const _Matrix& m) { return Concrete<_Matrix>::get(m); }
#endif

/* -------------
 *  MatrixMinor
 * ------------- */

struct all_selector {
   int operator[] (int i) const { return i; }
};
#define All all_selector()

template <>
class alias<all_selector> : public all_selector {
public:
   typedef const all_selector& arg_type;
   alias(arg_type) { }
};

template <>
struct complement_helper<const all_selector&, false> : complement_helper<all_selector> {
   static bool dimension_check(const all_selector&, int) { return true; }
};

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
class minor_base {
public:
   typedef typename deref<MatrixRef>::type matrix_type;
   typedef typename deref<RowIndexSetRef>::type row_set_type;
   typedef typename deref<ColIndexSetRef>::type col_set_type;

protected:
   alias<MatrixRef> matrix;
   alias<RowIndexSetRef> rset;
   alias<ColIndexSetRef> cset;

   typedef typename alias<MatrixRef>::arg_type matrix_arg_type;
   typedef typename alias<RowIndexSetRef>::arg_type row_set_arg_type;
   typedef typename alias<ColIndexSetRef>::arg_type col_set_arg_type;

   minor_base(matrix_arg_type matrix_arg, row_set_arg_type rset_arg, col_set_arg_type cset_arg)
      : matrix(matrix_arg), rset(rset_arg), cset(cset_arg) { }

public:
   typename inherit_const<matrix_type, MatrixRef>::type& get_matrix() { return matrix; }
   const matrix_type& get_matrix() const { return matrix; }
   const row_set_type& get_subset(int2type<1>*) const { return rset; }
   const col_set_type& get_subset(int2type<2>*) const { return cset; }
};

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
class MatrixMinor
   : public minor_base<MatrixRef, RowIndexSetRef, ColIndexSetRef>,
     public inherit_generic<MatrixMinor<MatrixRef,RowIndexSetRef,ColIndexSetRef>, typename deref<MatrixRef>::type>::type {
   typedef minor_base<MatrixRef, RowIndexSetRef, ColIndexSetRef> _base;
public:
   typedef typename _base::matrix_type matrix_type;
   typedef typename container_elem_traits<MatrixRef>::value_type value_type;
   typedef typename container_elem_traits<MatrixRef>::reference reference;
   typedef typename container_elem_traits<MatrixRef>::const_reference const_reference;
   static const bool prefer_concat=matrix_type::prefer_concat;

   typedef typename inherit_generic<MatrixMinor, matrix_type>::type generic_type;

   MatrixMinor(typename _base::matrix_arg_type matrix_arg,
	       typename _base::row_set_arg_type rset_arg,
	       typename _base::col_set_arg_type cset_arg)
      : _base(matrix_arg,rset_arg,cset_arg) { }

   /// Assignment operator should copy elements instead of alias pointers
   MatrixMinor& operator= (const MatrixMinor& m) { return generic_type::operator=(m); }
   using generic_type::operator=;

protected:
   void clear(True*)
   {
      for (typename Entire< Cols<MatrixMinor> >::iterator c=entire(pm::cols(*this)); !c.at_end(); ++c)
	 c->fill(0);
   }
   void clear(False*)
   {
      for (typename Entire< Rows<MatrixMinor> >::iterator r=entire(pm::rows(*this)); !r.at_end(); ++r)
	 r->fill(0);
   }
public:
   /// fill with zeroes (if dense), delete elements (if sparse)
   void clear()
   {
      clear((bool2type<identical<typename _base::row_set_type,all_selector>::answer>*)0);
   }
};

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
struct spec_object_traits< MatrixMinor<MatrixRef, RowIndexSetRef, ColIndexSetRef> >
   : spec_object_traits<is_container> {
   static const bool is_temporary=true,
                     is_always_const=effectively_const<MatrixRef>::answer;
};

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
struct check_container_feature<MatrixMinor<MatrixRef, RowIndexSetRef, ColIndexSetRef>, sparse>
   : check_container_ref_feature<MatrixRef, sparse> { };

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
struct check_container_feature<MatrixMinor<MatrixRef, RowIndexSetRef, ColIndexSetRef>, pure_sparse>
   : check_container_ref_feature<MatrixRef, pure_sparse> { };

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
class matrix_random_access_methods< MatrixMinor<MatrixRef, RowIndexSetRef, ColIndexSetRef> > {
   typedef MatrixMinor<MatrixRef,RowIndexSetRef,ColIndexSetRef> master;
public:
   typename inherit_const<typename deref<MatrixRef>::type::reference, MatrixRef>::type
   operator() (int i, int j)
   {
      master& me=static_cast<master&>(*this);
      INDEX_CHECK((i<0 || i>=me.rows()), "operator() - row index out of range");
      INDEX_CHECK((j<0 || j>=me.cols()), "operator() - column index out of range");
      return me.get_matrix()(me.get_subset((int2type<1>*)0)[i], me.get_subset((int2type<2>*)0)[j]);
   }

   typename deref<MatrixRef>::type::const_reference
   operator() (int i, int j) const
   {
      const master& me=static_cast<const master&>(*this);
      INDEX_CHECK((i<0 || i>=me.rows()), "operator() - row index out of range");
      INDEX_CHECK((j<0 || j>=me.cols()), "operator() - column index out of range");
      return me.get_matrix()(me.get_subset((int2type<1>*)0)[i], me.get_subset((int2type<2>*)0)[j]);
   }
};

template <typename Minor, int dir>
struct RowCol_helper;

template <typename Minor>
struct RowCol_helper<Minor,1> {
   typedef masquerade<Rows, typename extract_template_type_param<Minor,0>::type> type;
};

template <typename Minor>
struct RowCol_helper<Minor,2> {
   typedef masquerade<Cols, typename extract_template_type_param<Minor,0>::type> type;
};

template <typename Minor, typename _Renumber, int dir, typename Selector=typename extract_template_type_param<Minor,dir>::type>
class RowColSubset
   : public indexed_subset_impl< RowColSubset<Minor,_Renumber,dir,Selector>,
               cons< Container1< typename RowCol_helper<Minor,dir>::type >,
               cons< Container2< Selector >,
	       cons< Renumber< _Renumber >,
                     Hidden< Minor > > > >
            > {
   typedef indexed_subset_impl<RowColSubset> _super;
public:
   typename _super::container1& get_container1()
   {
      return reinterpret_cast<typename _super::container1&>(this->hidden().get_matrix());
   }
   const typename _super::container1& get_container1() const
   {
      return reinterpret_cast<const typename _super::container1&>(this->hidden().get_matrix());
   }
   const typename _super::container2& get_container2() const
   {
      return this->hidden().get_subset((int2type<dir>*)0);
   }
};

template <typename Minor, typename _Renumber, int dir>
class RowColSubset<Minor, _Renumber, dir, const all_selector&>
   : public redirected_container< RowColSubset<Minor,_Renumber,dir,const all_selector&>,
               cons< Container< typename RowCol_helper<Minor,dir>::type >,
		     Hidden< Minor > >
            > {
   typedef redirected_container<RowColSubset> _super;
public:
   typename _super::container& get_container()
   {
      return reinterpret_cast<typename _super::container&>(this->hidden().get_matrix());
   }
   const typename _super::container& get_container() const
   {
      return reinterpret_cast<const typename _super::container&>(this->hidden().get_matrix());
   }
};

template <typename Minor, typename _Renumber, int dir, typename Operation,
	  typename CrossSelector=typename extract_template_type_param<Minor,3-dir>::type>
class RowsCols
   : public modified_container_pair_impl< RowsCols<Minor,_Renumber,dir,Operation,CrossSelector>,
	       cons< Container1< RowColSubset<Minor,_Renumber,dir> >,
	       cons< Container2< unlimited_constant_container<typename attrib<CrossSelector>::plus_const_ref> >,
	       cons< Hidden< Minor >,
		     Accessor< Operation > > > >
            > {
   typedef modified_container_pair_impl<RowsCols> _super;
protected:
   ~RowsCols();
public:
   const typename _super::container2& get_container2() const
   {
      return constant(this->hidden().get_subset((int2type<3-dir>*)0));
   }
};

template <typename Minor, typename _Renumber, int dir, typename Operation>
class RowsCols<Minor, _Renumber, dir, Operation, const all_selector&>
   : public RowColSubset<Minor, _Renumber, dir> {
protected:
   ~RowsCols();
};

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
class Rows< MatrixMinor<MatrixRef, RowIndexSetRef, ColIndexSetRef> >
   : public RowsCols< minor_base<MatrixRef, RowIndexSetRef, ColIndexSetRef>, True, 1,
		      operations::construct_binary<IndexedSlice> > {
protected:
   ~Rows();
};

template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef>
class Cols< MatrixMinor<MatrixRef, RowIndexSetRef, ColIndexSetRef> >
   : public RowsCols< minor_base<MatrixRef, RowIndexSetRef, ColIndexSetRef>, True, 2,
		      operations::construct_binary<IndexedSlice> > {
protected:
   ~Cols();
};

/* ----------
 *  RowChain
 * ---------- */

template <typename MatrixRef1, typename MatrixRef2>
class RowChain
   : public container_pair_base<MatrixRef1, MatrixRef2, true>,
     public inherit_generic< RowChain<MatrixRef1,MatrixRef2>,
			     cons<typename deref<MatrixRef1>::type, typename deref<MatrixRef2>::type> >::type {
   typedef container_pair_base<MatrixRef1, MatrixRef2, true> _base;
public:
   typedef typename deref<MatrixRef1>::type matrix1_type;
   typedef typename deref<MatrixRef2>::type matrix2_type;
   typedef typename inherit_generic<RowChain, cons<matrix1_type, matrix2_type> >::type
      generic_type;
   typedef typename identical<typename container_elem_traits<MatrixRef1>::value_type,
			      typename container_elem_traits<MatrixRef2>::value_type>::type
      value_type;
   typedef typename compatible<typename container_elem_traits<MatrixRef1>::reference,
			       typename container_elem_traits<MatrixRef2>::reference>::type
      reference;
   typedef typename compatible<typename container_elem_traits<MatrixRef1>::const_reference,
			       typename container_elem_traits<MatrixRef2>::const_reference>::type
      const_reference;
   static const bool prefer_concat=matrix1_type::prefer_concat && matrix2_type::prefer_concat;

   RowChain(typename _base::first_arg_type matrix1_arg, typename _base::second_arg_type matrix2_arg)
      : _base(matrix1_arg, matrix2_arg) { }

   RowChain& operator= (const RowChain& m) { return generic_type::operator=(m); }
   using generic_type::operator=;
};

template <typename MatrixRef1, typename MatrixRef2>
struct spec_object_traits< RowChain<MatrixRef1, MatrixRef2> >
   : spec_object_traits<is_container> {
   static const bool is_temporary=true,
                     is_always_const= effectively_const<MatrixRef1>::answer || effectively_const<MatrixRef2>::answer;
};

template <typename MatrixRef1, typename MatrixRef2>
struct check_container_feature< RowChain<MatrixRef1, MatrixRef2>, sparse> {
   static const bool answer=check_container_ref_feature<MatrixRef1, sparse>::answer ||
                            check_container_ref_feature<MatrixRef2, sparse>::answer;
};

template <typename MatrixRef1, typename MatrixRef2>
struct check_container_feature< RowChain<MatrixRef1, MatrixRef2>, pure_sparse> {
   static const bool answer=check_container_ref_feature<MatrixRef1, pure_sparse>::answer &&
                            check_container_ref_feature<MatrixRef2, pure_sparse>::answer;
};

template <typename MatrixRef1, typename MatrixRef2>
class matrix_random_access_methods< RowChain<MatrixRef1, MatrixRef2> > {
   typedef RowChain<MatrixRef1,MatrixRef2> master;
public:
   typename compatible<typename container_elem_traits<MatrixRef1>::reference,
		       typename container_elem_traits<MatrixRef2>::reference>::type
   operator() (int i, int j)
   {
      master& me=static_cast<master&>(*this);
      const int r1=me.get_container1().rows();
      if (i < r1) return me.get_container1()(i,j);
      return me.get_container2()(i-r1,j);
   }
   typename compatible<typename container_elem_traits<MatrixRef1>::const_reference,
		       typename container_elem_traits<MatrixRef2>::const_reference>::type
   operator() (int i, int j) const
   {
      const master& me=static_cast<const master&>(*this);
      const int r1=me.get_container1().rows();
      if (i < r1) return me.get_container1()(i,j);
      return me.get_container2()(i-r1,j);
   }
};

template <typename MatrixRef1, typename MatrixRef2>
class Rows< RowChain<MatrixRef1, MatrixRef2> >
   : public container_chain_impl< Rows< RowChain<MatrixRef1,MatrixRef2> >,
               cons< Container1< masquerade<pm::Rows,MatrixRef1> >,
               cons< Container2< masquerade<pm::Rows,MatrixRef2> >,
		     Hidden< RowChain<MatrixRef1,MatrixRef2> > > >
            > {
   typedef container_chain_impl<Rows> _super;
protected:
   ~Rows();
public:
   typename _super::container1& get_container1()
   {
      return rows(this->hidden().get_container1());
   }
   typename _super::container2& get_container2()
   {
      return rows(this->hidden().get_container2());
   }
   const typename _super::container1& get_container1() const
   {
      return rows(this->hidden().get_container1());
   }
   const typename _super::container2& get_container2() const
   {
      return rows(this->hidden().get_container2());
   }
   int size() const
   {
      return get_container1().size() + get_container2().size();
   }
};

template <typename MatrixRef1, typename MatrixRef2>
class Cols< RowChain<MatrixRef1,MatrixRef2> >
   : public modified_container_pair_impl< Cols< RowChain<MatrixRef1,MatrixRef2> >,
               cons< Container1< masquerade<pm::Cols,MatrixRef1> >,
               cons< Container2< masquerade<pm::Cols,MatrixRef2> >,
               cons< Accessor< BuildBinary<operations::concat> >,
                     Hidden< RowChain<MatrixRef1,MatrixRef2> > > > >
            > {
   typedef modified_container_pair_impl<Cols> _super;
protected:
   ~Cols();
public:
   typename _super::container1& get_container1()
   {
      return cols(this->hidden().get_container1());
   }
   typename _super::container2& get_container2()
   {
      return cols(this->hidden().get_container2());
   }
   const typename _super::container1& get_container1() const
   {
      return cols(this->hidden().get_container1());
   }
   const typename _super::container2& get_container2() const
   {
      return cols(this->hidden().get_container2());
   }
   int size() const
   {
      int c=get_container1().size();
      if (!c) c=get_container2().size();
      return c;
   }
};

/* ----------
 *  ColChain
 * ---------- */

template <typename MatrixRef1, typename MatrixRef2>
class ColChain
   : public container_pair_base<MatrixRef1, MatrixRef2, true>,
     public inherit_generic<ColChain<MatrixRef1,MatrixRef2>,
			    cons<typename deref<MatrixRef1>::type, typename deref<MatrixRef2>::type> >::type {
   typedef container_pair_base<MatrixRef1, MatrixRef2, true> _base;
public:
   typedef typename inherit_generic<ColChain, cons<typename deref<MatrixRef1>::type, typename deref<MatrixRef2>::type> >::type
      generic_type;
   typedef typename identical<typename container_elem_traits<MatrixRef1>::value_type,
			      typename container_elem_traits<MatrixRef2>::value_type>::type
      value_type;
   typedef typename compatible<typename container_elem_traits<MatrixRef1>::reference,
			       typename container_elem_traits<MatrixRef2>::reference>::type
      reference;
   typedef typename compatible<typename container_elem_traits<MatrixRef1>::const_reference,
			       typename container_elem_traits<MatrixRef2>::const_reference>::type
      const_reference;

   ColChain(typename _base::first_arg_type matrix1_arg, typename _base::second_arg_type matrix2_arg)
      : _base(matrix1_arg, matrix2_arg) { }

   ColChain& operator= (const ColChain& m) { return generic_type::operator=(m); }
   using generic_type::operator=;
};

template <typename MatrixRef1, typename MatrixRef2>
struct spec_object_traits< ColChain<MatrixRef1, MatrixRef2> >
   : spec_object_traits<is_container> {
   static const bool is_temporary=true,
                     is_always_const= effectively_const<MatrixRef1>::answer || effectively_const<MatrixRef2>::answer;
};

template <typename MatrixRef1, typename MatrixRef2>
struct check_container_feature<ColChain<MatrixRef1, MatrixRef2>, sparse> {
   static const bool answer=check_container_ref_feature<MatrixRef1, sparse>::answer ||
                            check_container_ref_feature<MatrixRef2, sparse>::answer;
};

template <typename MatrixRef1, typename MatrixRef2>
struct check_container_feature<ColChain<MatrixRef1, MatrixRef2>, pure_sparse> {
   static const bool answer=check_container_ref_feature<MatrixRef1, pure_sparse>::answer &&
                            check_container_ref_feature<MatrixRef2, pure_sparse>::answer;
};

template <typename MatrixRef1, typename MatrixRef2>
class matrix_random_access_methods< ColChain<MatrixRef1, MatrixRef2> > {
   typedef ColChain<MatrixRef1,MatrixRef2> master;
public:
   typename compatible<typename container_elem_traits<MatrixRef1>::reference,
		       typename container_elem_traits<MatrixRef2>::reference>::type
   operator() (int i, int j)
   {
      master& me=static_cast<master&>(*this);
      const int c1=me.get_container1().cols();
      if (j < c1) return me.get_container1()(i,j);
      return me.get_container2()(i,j-c1);
   }
   typename compatible<typename container_elem_traits<MatrixRef1>::const_reference,
		       typename container_elem_traits<MatrixRef2>::const_reference>::type
   operator() (int i, int j) const
   {
      const master& me=static_cast<const master&>(*this);
      const int c1=me.get_container1().cols();
      if (j < c1) return me.get_container1()(i,j);
      return me.get_container2()(i,j-c1);
   }
};

template <typename MatrixRef1, typename MatrixRef2>
class Rows< ColChain<MatrixRef1,MatrixRef2> >
   : public modified_container_pair_impl< Rows< ColChain<MatrixRef1,MatrixRef2> >,
               cons< Container1< masquerade<pm::Rows,MatrixRef1> >,
               cons< Container2< masquerade<pm::Rows,MatrixRef2> >,
               cons< Accessor< BuildBinary<operations::concat> >,
		     Hidden< ColChain<MatrixRef1,MatrixRef2> > > > >
            > {
   typedef modified_container_pair_impl<Rows> _super;
protected:
   ~Rows();
public:
   typename _super::container1& get_container1()
   {
      return rows(this->hidden().get_container1());
   }
   typename _super::container2& get_container2()
   {
      return rows(this->hidden().get_container2());
   }
   const typename _super::container1& get_container1() const
   {
      return rows(this->hidden().get_container1());
   }
   const typename _super::container2& get_container2() const
   {
      return rows(this->hidden().get_container2());
   }
   int size() const
   {
      int r=get_container1().size();
      if (!r) r=get_container2().size();
      return r;
   }
};

template <typename MatrixRef1, typename MatrixRef2>
class Cols< ColChain<MatrixRef1,MatrixRef2> >
   : public container_chain_impl< Cols< ColChain<MatrixRef1,MatrixRef2> >,
               cons< Container1< masquerade<pm::Cols,MatrixRef1> >,
               cons< Container2< masquerade<pm::Cols,MatrixRef2> >,
		     Hidden< ColChain<MatrixRef1,MatrixRef2> > > >
            > {
   typedef container_chain_impl<Cols> _super;
protected:
   ~Cols();
public:
   typename _super::container1& get_container1()
   {
      return cols(this->hidden().get_container1());
   }
   typename _super::container2& get_container2()
   {
      return cols(this->hidden().get_container2());
   }
   const typename _super::container1& get_container1() const
   {
      return cols(this->hidden().get_container1());
   }
   const typename _super::container2& get_container2() const
   {
      return cols(this->hidden().get_container2());
   }
   int size() const
   {
      return get_container1().size() + get_container2().size();
   }
};

} // end namespace pm

namespace polymake {
   using pm::Rows;
   using pm::Cols;
   using pm::Transposed;
   using pm::all_selector;
}

namespace std {
   // due to silly overloading rules
   template <typename _Matrix> inline
   void swap(pm::Transposed<_Matrix>& m1, pm::Transposed<_Matrix>& m2)
   {
      m1.swap(m2);
   }

   template <typename MatrixRef, typename RowIndexSetRef, typename ColIndexSetRef> inline
   void swap(pm::MatrixMinor<MatrixRef,RowIndexSetRef,ColIndexSetRef>& m1,
	     pm::MatrixMinor<MatrixRef,RowIndexSetRef,ColIndexSetRef>& m2)
   {
      m1.swap(m2);
   }

   template <typename MatrixRef1, typename MatrixRef2> inline
   void swap(pm::RowChain<MatrixRef1,MatrixRef2>& m1, pm::RowChain<MatrixRef1,MatrixRef2>& m2)
   {
      m1.swap(m2);
   }

   template <typename MatrixRef1, typename MatrixRef2> inline
   void swap(pm::ColChain<MatrixRef1,MatrixRef2>& m1, pm::ColChain<MatrixRef1,MatrixRef2>& m2)
   {
      m1.swap(m2);
   }
}

#endif // _POLYMAKE_GENERIC_MATRIX_H

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