/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: GenericIncidenceMatrix.h 9494 2010-02-20 00:38:05Z gawrilow $
*/

#ifndef _POLYMAKE_GENERIC_INCIDENCE_MATRIX_H
#define _POLYMAKE_GENERIC_INCIDENCE_MATRIX_H

#include <matrix_methods.h>
#include <GenericIO.h>

namespace pm {

/* ------------------------
 *  GenericIncidenceMatrix
 * ------------------------ */

template <typename _symmetric=NonSymmetric> class IncidenceMatrix;

template <typename Matrix>
class GenericIncidenceMatrix
   : public Generic<Matrix>,
     public matrix_methods<Matrix, bool>,
     public operators::base {
protected:
   GenericIncidenceMatrix() {}
   GenericIncidenceMatrix(const GenericIncidenceMatrix&) {}
#ifdef POLY_DEBUG
   ~GenericIncidenceMatrix() { POLY_DEBUG_METHOD(GenericIncidenceMatrix,dump); }
   void dump() const { cerr << this->top() << std::flush; }
#endif

public:
   typedef bool element_type;
   static const bool is_symmetric=check_container_feature<Matrix,Symmetric>::value,
                     prefer_concat=false;
   typedef typename if_else<is_symmetric, Symmetric, NonSymmetric>::type symmetric;
   typedef IncidenceMatrix<symmetric> persistent_type;
   typedef GenericIncidenceMatrix generic_type;
   typedef bool value_type;
   typedef bool reference;
   typedef const bool const_reference;
protected:
   template <typename Matrix2>
   void assign(const GenericIncidenceMatrix<Matrix2>& M)
   {
      DIMENSION_CHECK((M.rows() != this->rows() || M.cols() != this->cols()), "operator= - matrix dimension mismatch");
      std::copy(pm::rows(M).begin(), entire(pm::rows(*this)));
   }

   template <typename Matrix2, typename Operation>
   void assign_op(const GenericIncidenceMatrix<Matrix2>& M, const Operation& op, True)
   {
      perform_assign(pm::entire(pm::rows(*this)), pm::rows(M).begin(), op);
   }

   template <typename Matrix2, typename Operation>
   void assign_op(const GenericIncidenceMatrix<Matrix2>& M, const Operation& op, False)
   {
      assign_op(M+T(M), op, True());
   }

   template <typename Matrix2>
   bool trivial_assignment(const GenericIncidenceMatrix<Matrix2>&) const { return false; }

   bool trivial_assignment(const GenericIncidenceMatrix& M) const { return this==&M; }

public:
   Matrix& operator= (const GenericIncidenceMatrix& M)
   {
      if (!trivial_assignment(M))
	 this->top().assign(M);
      return this->top();
   }

   template <typename Matrix2>
   typename enable_if<Matrix, !is_symmetric || Matrix2::is_symmetric>::type&
   operator= (const GenericIncidenceMatrix<Matrix2>& M)
   {
      this->top().assign(M);
      return this->top();
   }

   template <typename Matrix2>
   typename disable_if<Matrix, !is_symmetric || Matrix2::is_symmetric>::type&
   operator= (const GenericIncidenceMatrix<Matrix2>& M)
   {
      this->top().assign(M+T(M));
      return this->top();
   }

   template <typename Matrix2>
   Matrix& operator+= (const GenericIncidenceMatrix<Matrix2>& M)
   {
      DIMENSION_CHECK((this->rows() != M.rows() || this->cols() != M.cols()), "operator+= - matrix dimension mismatch");
      this->top().assign_op(M, BuildBinary<operations::add>(), bool2type<!is_symmetric || Matrix2::is_symmetric>());
      return this->top();
   }

   template <typename Matrix2>
   Matrix& operator-= (const GenericIncidenceMatrix<Matrix2>& M)
   {
      DIMENSION_CHECK((this->rows() != M.rows() || this->cols() != M.cols()), "operator-= - matrix dimension mismatch");
      this->top().assign_op(M, BuildBinary<operations::sub>(), bool2type<!is_symmetric || Matrix2::is_symmetric>());
      return this->top();
   }

   template <typename Matrix2>
   Matrix& operator*= (const GenericIncidenceMatrix<Matrix2>& M)
   {
      DIMENSION_CHECK((this->rows() != M.rows() || this->cols() != M.cols()), "operator*= - matrix dimension mismatch");
      this->top().assign_op(M, BuildBinary<operations::mul>(), bool2type<!is_symmetric || Matrix2::is_symmetric>());
      return this->top();
   }

   template <typename Matrix2>
   Matrix& operator^= (const GenericIncidenceMatrix<Matrix2>& M)
   {
      DIMENSION_CHECK((this->rows() != M.rows() || this->cols() != M.cols()), "operator^= - matrix dimension mismatch");
      this->top().assign_op(M, BuildBinary<operations::bitwise_xor>(), bool2type<!is_symmetric || Matrix2::is_symmetric>());
      return this->top();
   }

   template <typename Matrix2>
   void swap(GenericIncidenceMatrix<Matrix2>& M, typename enable_if<int, is_symmetric==Matrix2::is_symmetric>::type=0)
   {
      if (trivial_assignment(M)) return;
      DIMENSION_CHECK((this->rows() != M.rows() || this->cols() != M.cols()), "swap - matrix dimension mismatch");
      std::swap_ranges(entire(pm::rows(*this)), pm::rows(M).begin());
   }

   Matrix& transitive_closure();

   template <typename Result>
   struct rebind_generic {
      typedef GenericIncidenceMatrix<Result> type;
   };
};

struct is_incidence_matrix;

template <typename Matrix>
struct spec_object_traits< GenericIncidenceMatrix<Matrix> >
   : spec_or_model_traits<Matrix,is_container> {
private:
   typedef spec_or_model_traits<Matrix,is_container> _super;
public:
   static const int dimension=2,
                    is_symmetric=check_container_feature<Matrix,Symmetric>::value,
                    is_resizeable= _super::is_specialized ? _super::is_resizeable : 2-is_symmetric;
   typedef is_incidence_matrix generic_tag;

   typedef Rows<Matrix> serialized;
   static serialized& serialize(Matrix& M) { return rows(M); }
   static const serialized& serialize(const Matrix& M) { return rows(M); }
};

template <typename Matrix>
Matrix& GenericIncidenceMatrix<Matrix>::transitive_closure()
{
   DIMENSION_CHECK(this->rows() != this->cols(), "transitive_closure - matrix not square");
   bool changed;
   do {
      changed=false;
      for (typename Entire< Rows<Matrix> >::iterator r_i=entire(pm::rows(this->top()));  !r_i.at_end();  ++r_i) {
	 const int ones=r_i->size();
	 for (typename Matrix::row_type::iterator e=r_i->begin(); !e.at_end(); ++e)
	    if (e.index() != r_i.index())
	       (*r_i) += this->row(e.index());
	 if (r_i->size() > ones) changed=true;
      }
   } while (changed);
   return this->top();
}

/* ----------------------------------------
 *  SingleIncidenceRow, SingleIncidenceCol
 * ---------------------------------------- */

template <typename SetRef>
class SingleIncidenceRow
   : protected single_line_matrix<SetRef>,
     public GenericIncidenceMatrix< SingleIncidenceRow<SetRef> > {
   typedef single_line_matrix<SetRef> _super;
public:
   typedef bool value_type;
   typedef bool reference;
   typedef const bool const_reference;
   SingleIncidenceRow(typename _super::arg_type arg) : _super(arg) {}

   friend class Rows<SingleIncidenceRow>;
};

template <typename SetRef>
class SingleIncidenceCol
   : protected single_line_matrix<SetRef>,
     public GenericIncidenceMatrix< SingleIncidenceCol<SetRef> > {
   typedef single_line_matrix<SetRef> _super;
public:
   typedef bool value_type;
   typedef bool reference;
   typedef const bool const_reference;
   SingleIncidenceCol(typename _super::arg_type arg) : _super(arg) {}
};

template <typename SetRef>
struct spec_object_traits< SingleIncidenceRow<SetRef> >
   : spec_object_traits< single_value_container<SetRef> > {};

template <typename SetRef>
struct spec_object_traits< SingleIncidenceCol<SetRef> >
   : spec_object_traits< single_value_container<SetRef> > {};

class SingleElementIncidenceLine
   : public modified_container_impl< SingleElementIncidenceLine,
				     list( Container< single_value_container<int, true> >,
					   Operation< BuildUnaryIt<operations::index2element> > ) >,
     public GenericSet<SingleElementIncidenceLine, int, operations::cmp> {
protected:
   single_value_container<int, true> _index;
public:
   SingleElementIncidenceLine() {}
   SingleElementIncidenceLine(int i) : _index(i) {}

   const single_value_container<int, true>& get_container() const { return _index; }
   int dim() const { return 1; }
};

template <>
struct check_container_feature<SingleElementIncidenceLine, sparse_compatible> : True {};

template <typename Iterator1Ref, typename Iterator2Ref>
struct SingleElementIncidenceLine_factory {
   typedef Iterator1Ref first_argument_type;
   typedef Iterator2Ref second_argument_type;
   typedef SingleElementIncidenceLine result_type;

   result_type operator() (Iterator1Ref it, Iterator2Ref) const
   {
      return *it;
   }
   template <typename Iterator1, typename Iterator2>
   result_type operator() (operations::partial, const Iterator1&, const Iterator2&) const
   {
      return result_type();
   }
};

template <typename SetRef>
class IncidenceLines_across
   : public modified_container_pair_impl< IncidenceLines_across<SetRef>,
					  list( Container1< sequence >,
						Container2< SetRef >,
						IteratorCoupler< zipping_coupler<operations::cmp, set_union_zipper> >,
						Operation< BuildBinaryIt<SingleElementIncidenceLine_factory> >,
						Hidden< single_line_matrix<SetRef> > ) > {
public:
   sequence get_container1() const { return sequence(0, size()); }
   typename single_value_container<SetRef>::const_reference get_container2() const { return this->hidden().get_line(); }
   int size() const { return this->hidden().get_line().dim(); }
};

template <typename SetRef>
class Rows< SingleIncidenceRow<SetRef> >
   : public redirected_container< Rows< SingleIncidenceRow<SetRef> >,
				  list( Container< single_value_container<SetRef> >,
					MasqueradedTop ) > {
protected:
   ~Rows();
public:
   const single_value_container<SetRef>& get_container() const { return this->hidden()._line; }
};

template <typename SetRef>
class Cols< SingleIncidenceRow<SetRef> > : public IncidenceLines_across<SetRef> {
protected:
   ~Cols();
};

template <typename SetRef>
class Rows< SingleIncidenceCol<SetRef> > : public IncidenceLines_across<SetRef> {
protected:
   ~Rows();
};

template <typename SetRef>
class Cols< SingleIncidenceCol<SetRef> > : public Rows< SingleIncidenceRow<SetRef> > {
protected:
   ~Cols();
};

template <typename ContainerRef1, typename ContainerRef2>
class IncidenceLineChain
   : public modified_container_impl< IncidenceLineChain<ContainerRef1,ContainerRef2>,
				     list( Container< ContainerChain<ContainerRef1,ContainerRef2> >,
					   Operation< BuildUnaryIt<operations::index2element> >,
					   ExpectedFeatures< indexed > ) >,
     public GenericSet<IncidenceLineChain<ContainerRef1,ContainerRef2>, int, operations::cmp> {
   typedef modified_container_impl<IncidenceLineChain> _super;
protected:
   typename _super::container chain;
public:
   const typename _super::container& get_container() const { return chain; }

   IncidenceLineChain(typename _super::container::first_arg_type arg1,
		      typename _super::container::second_arg_type arg2)
      : chain(arg1,arg2) {}

   int dim() const
   {
      return chain.get_container1().dim() + chain.get_container2().dim();
   }
};

template <typename ContainerRef1, typename ContainerRef2>
struct check_container_feature<IncidenceLineChain<ContainerRef1, ContainerRef2>, sparse_compatible> : True {};

template <typename ContainerRef1, typename ContainerRef2>
struct spec_object_traits< IncidenceLineChain<ContainerRef1, ContainerRef2> >
   : spec_object_traits<is_container> {
   static const bool is_temporary=true, is_always_const=true;
};

/* ----------------------------
 *  SameElementIncidenceMatrix
 * ---------------------------- */

template <bool elem>
class SameElementIncidenceMatrix
   : public GenericIncidenceMatrix<SameElementIncidenceMatrix<elem> > {
public:
   SameElementIncidenceMatrix(int r_arg, int c_arg) : r(r_arg), c(c_arg) {}
protected:
   int r,c;

   friend class Rows<SameElementIncidenceMatrix>;
   friend class Cols<SameElementIncidenceMatrix>;
};

template <bool elem>
class SameElementIncidenceLine
   : public modified_container_impl< SameElementIncidenceLine<elem>,
				     list( Container< sequence >,
					   Operation< pair<nothing, operations::identity<int> > >,
					   ExpectedFeatures< end_sensitive > ) >,
     public GenericSet<SameElementIncidenceLine<elem>, int, operations::cmp> {
public:
   int dim() const { return reinterpret_cast<const int&>(*this); }
   int size() const { return elem ? dim() : 0; }
   bool empty() const { return !elem; }

   sequence get_container() const { return sequence(0, size()); }
};

template <bool elem>
struct check_container_feature< SameElementIncidenceLine<elem>, sparse_compatible> : True {};

template <bool elem>
class Rows< SameElementIncidenceMatrix<elem> >
   : public redirected_container< Rows< SameElementIncidenceMatrix<elem> >,
				  list( Container< constant_masquerade_container< SameElementIncidenceLine<elem> > >,
					MasqueradedTop ) > {
   typedef redirected_container<Rows> _super;
protected:
   ~Rows();
public:
   const typename _super::container& get_container() const
   {
      return reinterpret_cast<const typename _super::container&>(this->hidden().c);
   }
   int size() const { return this->hidden().r; }
   bool empty() const { return !size(); }
};

template <bool elem>
class Cols< SameElementIncidenceMatrix<elem> >
   : public redirected_container< Cols< SameElementIncidenceMatrix<elem> >,
				  list( Container< constant_masquerade_container< SameElementIncidenceLine<elem> > >,
					MasqueradedTop ) > {
   typedef redirected_container<Cols> _super;
protected:
   ~Cols();
public:
   const typename _super::container& get_container() const
   {
      return reinterpret_cast<const typename _super::container&>(this->hidden().r);
   }
   int size() const { return this->hidden().c; }
   bool empty() const { return !size(); }
};

template <typename Matrix1, typename Matrix2> inline
RowChain< ColChain<const Matrix1&, SameElementIncidenceMatrix<false> >,
	  ColChain<SameElementIncidenceMatrix<false>, const Matrix2&> >
diag(const GenericIncidenceMatrix<Matrix1>& m1, const GenericIncidenceMatrix<Matrix2>& m2)
{
   return RowChain< ColChain<const Matrix1&, SameElementIncidenceMatrix<false> >,
                    ColChain<SameElementIncidenceMatrix<false>, const Matrix2&> >
             ( ColChain<const Matrix1&, SameElementIncidenceMatrix<false> >
	          ( m1.top(), SameElementIncidenceMatrix<false>(m1.rows(), m2.cols()) ),
	       ColChain<SameElementIncidenceMatrix<false>, const Matrix2&>
	          ( SameElementIncidenceMatrix<false>(m2.rows(), m1.cols()), m2.top() )
	     );
}

/// the same, but with corner blocks filled with 1
template <typename Matrix1, typename Matrix2> inline
RowChain< ColChain<const Matrix1&, SameElementIncidenceMatrix<true> >,
	  ColChain<SameElementIncidenceMatrix<true>, const Matrix2&> >
diag_1(const GenericIncidenceMatrix<Matrix1>& m1, const GenericIncidenceMatrix<Matrix2>& m2)
{
   return RowChain< ColChain<const Matrix1&, SameElementIncidenceMatrix<true> >,
                    ColChain<SameElementIncidenceMatrix<true>, const Matrix2&> >
             ( ColChain<const Matrix1&, SameElementIncidenceMatrix<true> >
	          ( m1.top(), SameElementIncidenceMatrix<true>(m1.rows(), m2.cols()) ),
	       ColChain<SameElementIncidenceMatrix<true>, const Matrix2&>
	          ( SameElementIncidenceMatrix<true>(m2.rows(), m1.cols()), m2.top() )
	     );
}

/* ------------
 *  complement
 * ------------ */

template <typename Matrix>
class ComplementIncidenceMatrix
   : public GenericIncidenceMatrix< ComplementIncidenceMatrix<Matrix> > {
protected:
   ComplementIncidenceMatrix();
   ~ComplementIncidenceMatrix();
public:
   typedef Matrix hidden_type;
   const Matrix& hidden() const { return reinterpret_cast<const Matrix&>(*this); }
};

template <typename Matrix>
struct spec_object_traits< ComplementIncidenceMatrix<Matrix> >
   : spec_object_traits<is_container> {
   static const bool is_always_const=true;
   typedef Matrix masquerade_for;
};

template <typename Matrix>
struct check_container_feature< ComplementIncidenceMatrix<Matrix>, Symmetric >
   : check_container_feature<Matrix, Symmetric> {};

template <typename Matrix>
class matrix_random_access_methods< ComplementIncidenceMatrix<Matrix> > {
public:
   bool operator() (int i, int j) const
   {
      return ! reinterpret_cast<const Matrix&>(*this)(i,j);
   }
};

template <typename LineRef>
struct ComplementIncidenceLine_factory {
   typedef LineRef argument_type;
   typedef LazySet2<sequence, LineRef, set_difference_zipper> result_type;

   result_type operator() (typename function_argument<LineRef>::const_type l) const
   {
      return result_type(sequence(0,l.dim()), l);
   }
};

template <typename Matrix>
class Rows< ComplementIncidenceMatrix<Matrix> >
   : public modified_container_impl< Rows< ComplementIncidenceMatrix<Matrix> >,
				     list( Hidden< Rows<Matrix> >,
					   Operation< BuildUnary<ComplementIncidenceLine_factory> > ) > {
protected:
   ~Rows();
};

template <typename Matrix>
class Cols< ComplementIncidenceMatrix<Matrix> >
   : public modified_container_impl< Cols< ComplementIncidenceMatrix<Matrix> >,
				     list( Hidden< Cols<Matrix> >,
					   Operation< BuildUnary<ComplementIncidenceLine_factory> > ) > {
protected:
   ~Cols();
};

/* --------------------------------------------
 *  boolean operations with incidence matrices
 * -------------------------------------------- */

template <typename MatrixRef1, typename MatrixRef2, typename Controller>
class LazyIncidenceMatrix2
   : public modified_container_pair_base<MatrixRef1, MatrixRef2, Controller>,
     public GenericIncidenceMatrix< LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller> > {
   typedef modified_container_pair_base<MatrixRef1, MatrixRef2, Controller> _base;
public:
   LazyIncidenceMatrix2(typename _base::first_arg_type src1_arg, typename _base::second_arg_type src2_arg, const Controller& c_arg=Controller())
      : _base(src1_arg, src2_arg, c_arg) {}
};

template <typename MatrixRef1, typename MatrixRef2, typename Controller>
struct spec_object_traits< LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller> >
   : spec_object_traits<is_container> {
   static const bool is_lazy=true, is_temporary=true, is_always_const=true;
};

template <typename MatrixRef1, typename MatrixRef2, typename Controller>
struct check_container_feature< LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller>, Symmetric > {
   static const bool value= check_container_ref_feature<MatrixRef1,Symmetric>::value &&
                            check_container_ref_feature<MatrixRef2,Symmetric>::value;
};

template <typename MatrixRef1, typename MatrixRef2, typename Controller>
class matrix_random_access_methods< LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller> > {
   typedef LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller> master;
public:
   bool operator() (int i, int j) const
   {
      const master& me=static_cast<const master&>(*this);
      return me.get_operation().contains(me.get_container1()(i,j), me.get_container2()(i,j));
   }
};

template <typename MatrixRef1, typename MatrixRef2, typename Controller>
class Rows< LazyIncidenceMatrix2<MatrixRef1, MatrixRef2, Controller> >
   : public modified_container_pair_impl< Rows< LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller> >,
					  list( Container1< masquerade<pm::Rows,MatrixRef1> >,
						Container2< masquerade<pm::Rows,MatrixRef2> >,
						Operation< operations::construct_binary2_with_arg<LazySet2,Controller> >,
						MasqueradedTop ) > {
   typedef modified_container_pair_impl<Rows> _super;
protected:
   ~Rows();
public:
   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());
   }
   typename _super::operation get_operation() const
   {
      return this->hidden().get_operation();
   }
};

template <typename MatrixRef1, typename MatrixRef2, typename Controller>
class Cols< LazyIncidenceMatrix2<MatrixRef1, MatrixRef2, Controller> >
   : public modified_container_pair_impl< Cols< LazyIncidenceMatrix2<MatrixRef1,MatrixRef2,Controller> >,
					  list( Container1< masquerade<pm::Cols,MatrixRef1> >,
						Container2< masquerade<pm::Cols,MatrixRef2> >,
						Operation< operations::construct_binary2_with_arg<LazySet2,Controller> >,
						MasqueradedTop ) > {
   typedef modified_container_pair_impl<Cols> _super;
protected:
   ~Cols();
public:
   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());
   }
   typename _super::operation get_operation() const
   {
      return this->hidden().get_operation();
   }
};

/* ------------------------------------
 *  operations building block matrices
 * ------------------------------------ */

namespace operations {

template <typename OpRef>
struct bitwise_inv_impl<OpRef, is_incidence_matrix>
   : reinterpret_impl<OpRef, const ComplementIncidenceMatrix<typename deref<OpRef>::type>&> {};

template <typename Matrix>
struct bitwise_inv_impl<const ComplementIncidenceMatrix<Matrix>&, is_incidence_matrix> {
   typedef const ComplementIncidenceMatrix<Matrix>& argument_type;
   typedef const Matrix& result_type;

   result_type operator() (argument_type x) const
   {
      return x.hidden();
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct add_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef LazyIncidenceMatrix2<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const, set_union_zipper>
      result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      if (check) DIMENSION_CHECK((l.rows() != r.rows() ||
				  l.cols() != r.cols()), "operator+ - matrix dimension mismatch");
      return result_type(l,r);
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l+=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct sub_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef LazyIncidenceMatrix2<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const, set_difference_zipper>
      result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      if (check) DIMENSION_CHECK((l.rows() != r.rows() ||
				  l.cols() != r.cols()), "operator- - matrix dimension mismatch");
      return result_type(l,r);
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l-=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct mul_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef LazyIncidenceMatrix2<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const, set_intersection_zipper>
      result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      if (check) DIMENSION_CHECK((l.rows() != r.rows() ||
				  l.cols() != r.cols()), "operator- - matrix dimension mismatch");
      return result_type(l,r);
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l*=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct bitwise_xor_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef LazyIncidenceMatrix2<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const, set_symdifference_zipper>
      result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      if (check) DIMENSION_CHECK((l.rows() != r.rows() ||
				  l.cols() != r.cols()), "operator- - matrix dimension mismatch");
      return result_type(l,r);
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l^=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct concat_impl<LeftRef, RightRef, check, cons<is_set, is_set> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef IncidenceLineChain<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      return result_type(l, r);
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct div_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef RowChain<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      return result_type(l,r);
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r)
   {
      l/=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct div_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_set> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef Set_with_dim_helper<typename attrib<RightRef>::plus_const> right_helper;
   typedef SingleIncidenceRow<typename right_helper::container> Right;
   typedef RowChain<typename attrib<LeftRef>::plus_const, Right> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r)
   {
      if (check) DIMENSION_CHECK(!right_helper::check(r, l.cols()),
				 "concat_rows - column dimensions mismatch");
      return result_type(l, right_helper::create(r, l.cols()));
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r)
   {
      l/=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct div_impl<LeftRef, RightRef, check, cons<is_set, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef Set_with_dim_helper<typename attrib<LeftRef>::plus_const> left_helper;
   typedef SingleIncidenceRow<typename left_helper::container> Left;
   typedef RowChain<Left, typename attrib<RightRef>::plus_const> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r)
   {
      if (check) DIMENSION_CHECK(!left_helper::check(l, r.cols()),
				 "concat_rows - column dimensions mismatch");
      return result_type(left_helper::create(l, r.cols()), r);
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct bitwise_or_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef ColChain<typename attrib<LeftRef>::plus_const, typename attrib<RightRef>::plus_const> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r) const
   {
      return result_type(l,r);
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r)
   {
      l|=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct bitwise_or_impl<LeftRef, RightRef, check, cons<is_incidence_matrix, is_set> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef Set_with_dim_helper<typename attrib<RightRef>::plus_const> right_helper;
   typedef SingleIncidenceCol<typename right_helper::container> Right;
   typedef ColChain<typename attrib<LeftRef>::plus_const, Right> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r)
   {
      if (check) DIMENSION_CHECK(!right_helper::check(r, l.rows()),
				 "concat_cols - row dimensions mismatch");
      return result_type(l, right_helper::create(r, l.rows()));
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r)
   {
      l|=r;
   }
};

template <typename LeftRef, typename RightRef, bool check>
struct bitwise_or_impl<LeftRef, RightRef, check, cons<is_set, is_incidence_matrix> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef Set_with_dim_helper<typename attrib<LeftRef>::plus_const> left_helper;
   typedef SingleIncidenceCol<typename left_helper::container> Left;
   typedef ColChain<Left, typename attrib<RightRef>::plus_const> result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
			   typename function_argument<RightRef>::const_type r)
   {
      if (check) DIMENSION_CHECK(!Set_with_dim_helper<LeftRef>::check(l, r.rows()),
				 "concat_cols - row dimensions mismatch");
      return result_type(left_helper::create(l, r.rows()), r);
   }
};

} // end namespace operations
namespace operators {

template <typename Matrix1, typename Matrix2> inline
typename operations::add_impl<const Matrix1&, const Matrix2&, true>::result_type
operator+ (const GenericIncidenceMatrix<Matrix1>& l, const GenericIncidenceMatrix<Matrix2>& r)
{
   operations::add_impl<const Matrix1&, const Matrix2&, true> op;
   return op(l.top(), r.top());
}

template <typename Matrix1, typename Matrix2> inline
typename operations::sub_impl<const Matrix1&, const Matrix2&, true>::result_type
operator- (const GenericIncidenceMatrix<Matrix1>& l, const GenericIncidenceMatrix<Matrix2>& r)
{
   operations::sub_impl<const Matrix1&, const Matrix2&, true> op;
   return op(l.top(), r.top());
}

template <typename Matrix1, typename Matrix2> inline
bool operator== (const GenericIncidenceMatrix<Matrix1>& l, const GenericIncidenceMatrix<Matrix2>& r)
{
   if ((l.rows()==0 && r.rows()==0) || (l.cols()==0 && r.cols()==0)) return true;
   if (l.rows() != r.rows() || l.cols() != r.cols()) return false;
   operations::eq<const Matrix1&, const Matrix2&> op;
   return op(l.top(), r.top());
}

template <typename Matrix1, typename Matrix2> inline
bool operator< (const GenericIncidenceMatrix<Matrix1>& l, const GenericIncidenceMatrix<Matrix2>& r)
{
   if ((l.rows()==0 && r.rows()==0) || (l.cols()==0 && r.cols()==0)) return false;
   DIMENSION_CHECK((l.rows() != r.rows() ||
		    l.cols() != r.cols()),  "operator< - matrix dimension mismatch");
   operations::lt<const Matrix1&, const Matrix2&> op;
   return op(l.top(), r.top());
}

} // end namespace operators
} // end namespace pm

namespace polymake {
   using pm::GenericIncidenceMatrix;
}

namespace std {
   template <typename Matrix1, typename Matrix2> inline
   void swap(pm::GenericIncidenceMatrix<Matrix1>& M1, pm::GenericIncidenceMatrix<Matrix2>& M2)
   {
      M1.top().swap(M2.top());
   }
}

#ifdef _POLYMAKE_GENERIC_MATRIX_H
#  include <Incidence_and_SparseMatrix.h>
#endif
#endif // _POLYMAKE_GENERIC_INCIDENCE_MATRIX_H

// Local Variables:
// mode:C++
// c-basic-offset:3
// End:
