/* Copyright (c) 1997-2004
   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.
*/

#ident "$Project: polymake $$Id: facets_from_incidence.cc,v 1.9 2005/09/22 17:12:09 gawrilow Exp $"

#include <Poly.h>
#include <Rational.h>
#include <IncidenceMatrix.h>
#include <linalg.h>
#include <argv_options.h>

namespace polymake { namespace polytope {

namespace {

template <typename E, typename Matrix1, typename Vector2> inline
void assign_facet_through_points(const GenericMatrix<Matrix1,E>& V, GenericVector<Vector2,E>& f)
{
   f=null_space(V)[0];
}

template <typename Matrix1, typename Vector2> inline
void assign_facet_through_points(const GenericMatrix<Matrix1,double>& V, GenericVector<Vector2,double>& f)
{
   const Matrix<double> F=null_space(V);
   if (F.rows() == 1) {
      f=F[0];
   } else {
      // least squares
      const int n=V.cols();
      f=inv(( 2*T(V)*V               | ones_vector<double>(n)) /
	    ( ones_vector<double>(n) | 0.0                   ) )[n].slice(sequence(0,n));
   }
}

template <typename Coord, typename IM>
Matrix<Coord> facets_from_incidence(const Matrix<Coord>& V, const Matrix<Coord>& AH,
				    const GenericIncidenceMatrix<IM>& VIF)
{
   Matrix<Coord> F(VIF.rows(), V.cols());
   typename Rows< Matrix<Coord> >::iterator Fi=rows(F).begin();

   for (typename Entire< Rows<IM> >::const_iterator vertex_list=entire(rows(VIF));
	!vertex_list.at_end();  ++vertex_list, ++Fi) {
      assign_facet_through_points(V.minor(*vertex_list,All) / AH, non_const(*Fi));
      int outer_vertex=(sequence(0,V.rows()) - *vertex_list).front();
      if (*Fi * V[outer_vertex] < 0) inv_sign(*Fi);
   }

   return F;
}

} // end anonymous namespace

template <typename Coord>
void facets_from_incidence(Poly& p)
{
   const Matrix<Coord> V=p.give("VERTICES");
   const IncidenceMatrix<> VIF=p.give("VERTICES_IN_FACETS");

   Matrix<Coord> AH=null_space(V);
   p.take("AFFINE_HULL") << AH;

   AH.col(0).fill(0);
   const Matrix<Coord> F=facets_from_incidence(V, AH, VIF);
   p.take("FACETS") << F;
}

template <typename Coord>
void vertices_from_incidence(Poly& p)
{
   const Matrix<Coord> F=p.give("FACETS"), AH=p.give("AFFINE_HULL");
   const IncidenceMatrix<> VIF=p.give("VERTICES_IN_FACETS");

   const Matrix<Coord> V=facets_from_incidence(F, AH, T(VIF));
   p.take("VERTICES") << V;
}

} }

using namespace polymake;

/** @file facets_from_incidence
 *
 *  Compute the facet description of a polyhedron from its vertices and its incidence matrix.
 *
 *  @synopsis facets_from_incidence <file> { -primal, -dual }
 *
 *  @reading  VERTICES, VERTICES_IN_FACETS
 *  @writing  FACETS, AFFINE_HULL
 */

int main(int argc, const char *argv[])
{
   argv_option options[]={ { "-primal" }, { "-dual" } };
   enum { primal, dual };
   if (argc!=3 || !extract_options(argc,argv,1,options) || argc!=2) {
      cerr << "usage: " << argv[0] << " <file> { -primal, -dual }" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      if (p.isa("RationalPolytope")) {
	 if (options[primal])
	    polytope::facets_from_incidence<Rational>(p);
	 else
	    polytope::vertices_from_incidence<Rational>(p);

      } else if (p.isa("FloatPolytope")) {
	 if (options[primal])
	    polytope::facets_from_incidence<double>(p);
	 else
	    polytope::vertices_from_incidence<double>(p);

      } else {
	 throw std::runtime_error("don't know how to proceed with " + p.type());
      }
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
