/* 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: canonical_coord.cc 10228 2011-05-24 18:10:25Z paffenho $
*/

#include "polymake/client.h"
#include "polymake/linalg.h"
#include "polymake/permutations.h"

namespace polymake { namespace polytope {

template <typename Iterator> inline
void canonicalize_oriented(Iterator e)
{
   if (!e.at_end() && !abs_equal(*e,1)) {
      const typename Iterator::value_type leading=abs(*e);
      do *e /= leading; while (!(++e).at_end());
   }
}

template <typename Vector>
void canonicalize_point_configuration(GenericVector<Vector>& V,
                         typename pm::enable_if<void*, pm::check_container_feature<Vector,pm::sparse>::value>::type=0)
{
   typename Vector::iterator it=V.top().begin();
   if (!it.at_end()) {
      if (it.index()==0) {
         if (*it != 1) {
            const typename Vector::element_type first=*it;
            V.top() /= first;
         }
      } else {
         canonicalize_oriented(it);
      }
   }
}

template <typename Vector>
void canonicalize_point_configuration(GenericVector<Vector>& V,
                         typename pm::disable_if<void*, pm::check_container_feature<Vector,pm::sparse>::value>::type=0)
{
   if (!V.top().empty() && V.top().front() != 1) {
      conv<typename Vector::element_type, bool> non_zero;
      if (non_zero(V.top().front())) {
         const typename Vector::element_type first=V.top().front();
         V.top() /= first;
      } else {
         canonicalize_oriented(find_if(entire(V.top()), non_zero));
      }
   }
}

template <typename Vector>
void canonicalize_rays(GenericVector<Vector>& V,
                         typename pm::enable_if<void*, pm::check_container_feature<Vector,pm::sparse>::value>::type=0)
{
   typename Vector::iterator it=V.top().begin();
   if (!it.at_end())
         canonicalize_oriented(it);
}

template <typename Vector>
void canonicalize_rays(GenericVector<Vector>& V,
                         typename pm::disable_if<void*, pm::check_container_feature<Vector,pm::sparse>::value>::type=0)
{
   if (!V.top().empty()) {
      conv<typename Vector::element_type, bool> non_zero;
      canonicalize_oriented(find_if(entire(V.top()), non_zero));
   }
}

template <typename Vector, typename E> inline
void canonicalize_facets(GenericVector<Vector,E>& V)
{
   canonicalize_oriented(find_if(entire(V.top()), conv<typename Vector::element_type, bool>()));
}

template <typename Vector> inline
void canonicalize_facets(GenericVector<Vector,double>& V)
{
   V.top() /= sqrt(sqr(V.top()));
}

template <typename Matrix> inline
void canonicalize_rays(GenericMatrix<Matrix>& M)
{
   for (typename Entire< Rows<Matrix> >::iterator r=entire(rows(M)); !r.at_end();  ++r)
      canonicalize_rays(r->top());
}

template <typename Matrix> inline
void canonicalize_point_configuration(GenericMatrix<Matrix>& M)
{
   Set<int> neg;
   int i = 0;
   for (typename Entire< Rows<Matrix> >::iterator r=entire(rows(M)); !r.at_end();  ++r, ++i) {
      if ( r->top()[0] < 0 )
         neg.push_back(i);
      else
      canonicalize_point_configuration(r->top());
   }
   M = M.minor(~neg,All);
}

template <typename Matrix> inline
void canonicalize_facets(GenericMatrix<Matrix>& M)
{
   for (typename Entire< Rows<Matrix> >::iterator r=entire(rows(M)); !r.at_end();  ++r)
      canonicalize_facets(r->top());
}

// assumes that the first coordinate is in 0/+1/-1
// i.e. typically canonicalize_rays has been called before
// polytope model: the INPUT_RAYS define a cone, 
// and the polytope is the intersection with x_0=1
// the provided rays should be adjusted in a way that 
// they define the cone with the additional inequality (1,0,0,...)
// we do a step of Fourier-Motzkin elimination to achieve this
template <typename Matrix> inline
void canonicalize_polytope_generators(GenericMatrix<Matrix>& M)
{
   // collect the rays with non-zero first entry
   Set<int> pos, neg;
   for (int i = 0; i < M.top().rows(); ++i) {
      if ( M.top()(i,0) > 0 ) 
         pos.push_back(i);
      else {
         if ( M.top()(i,0) < 0 ) 
            neg.push_back(i);
      }
   }
   if ( pos.size() == 0 ) 
      //   throw std::runtime_error("not a polytope or illegal empty polytope");
      // the polytope is empty if no positive points are found: truncate matrix
      // initial rule will set FEASIBLE to false
      M.top().clear();
   else {

      // Fourier-Motzkin elimination: 
      // we add all pairs of a ray with a positive and negative first coordinate
      // note that this requires that the first coordinates are all 0/+1/-1
      // FIXME a more clever implementation would avoid duplicates, either only among the added inequalities, or among all.
      for ( Entire<Set<int> >::const_iterator nit = entire(neg); !nit.at_end(); ++nit )
         for ( Entire<Set<int> >::const_iterator pit = entire(pos); !pit.at_end(); ++pit )
            M.top() /= M.top()[*nit] + M.top()[*pit];
      
      // now remove the inequalities with negative first coordinates
      // we want to keep the order of the rays otherwise
      M.top() = M.top().minor(~neg,All);
   }
}

template <typename Matrix> inline
void add_extra_polytope_ineq(GenericMatrix<Matrix>& M)
{
   const int d=M.cols();
   typedef typename Matrix::element_type E;
   const Vector<E> extra_ineq(unit_vector<E>(d,0));
   for (typename Entire< Rows<Matrix> >::iterator r=entire(rows(M)); !r.at_end();  ++r)
      if (*r == extra_ineq) return;
   M /= extra_ineq;
}

template <typename Matrix> inline
void orthogonalize_subspace(GenericMatrix<Matrix>& M)
{
   orthogonalize_affine(entire(rows(M)));
}

FunctionTemplate4perl("canonicalize_point_configuration(Vector&) : void");
FunctionTemplate4perl("canonicalize_point_configuration(Matrix&) : void");
FunctionTemplate4perl("canonicalize_rays(Vector&) : void");
FunctionTemplate4perl("canonicalize_rays(Matrix&) : void");
FunctionTemplate4perl("canonicalize_facets(Vector&) : void");
FunctionTemplate4perl("canonicalize_facets(Matrix&) : void");
FunctionTemplate4perl("canonicalize_polytope_generators(Matrix&) : void");
FunctionTemplate4perl("add_extra_polytope_ineq(Matrix&) : void");
FunctionTemplate4perl("orthogonalize_subspace(Matrix&) : void");
FunctionTemplate4perl("dehomogenize(Vector)");
FunctionTemplate4perl("dehomogenize(Matrix)");


template <typename Matrix2, typename E>
void orthogonalize_facets(Matrix<E>& F, const GenericMatrix<Matrix2,E>& AH)
{
   for (typename Entire< Rows<Matrix2> >::const_iterator a=entire(rows(AH)); !a.at_end(); ++a) {
      const E s=sqr(a->slice(1));
      for (typename Entire< Rows< Matrix<E> > >::iterator f=entire(rows(F)); !f.at_end(); ++f) {
         const E x= f->slice(1) * a->slice(1);
         if (x) *f -= (x/s) * (*a);
      }
   }
}

template <typename E>
struct coord_comparator {
   typedef operations::cmp type;
};

template <>
struct coord_comparator<double> {
   typedef operations::cmp_epsilon<double> type;
};

template <typename Matrix1, typename Matrix2, typename E>
Array<int> find_vertex_permutation(const GenericMatrix<Matrix1,E>& Vertices, const GenericMatrix<Matrix2,E>& otherVertices)
{
   if (Vertices.rows() != otherVertices.rows() || Vertices.cols() != otherVertices.cols())
      throw no_match("find_vertex_permutation: dimension mismatch");
   return find_permutation(rows(Vertices), rows(otherVertices), typename coord_comparator<E>::type());
}

template <typename Matrix1, typename Matrix2, typename Matrix3, typename E>
Array<int> find_facet_permutation(const GenericMatrix<Matrix1,E>& Facets, const GenericMatrix<Matrix2,E>& otherFacets,
                                  const GenericMatrix<Matrix3,E>& AH)
{
   if ((Facets.rows()==0 || Facets.cols()==0) && (otherFacets.rows()==0 || otherFacets.cols()==0))
      return Array<int>();
   if (Facets.rows() != otherFacets.rows() || Facets.cols() != otherFacets.cols())
      throw no_match("find_facet_permutation: dimension mismatch");
   Matrix<E> F1(Facets), F2(otherFacets);
   if (AH.rows()) {
      orthogonalize_facets(F1, AH);
      orthogonalize_facets(F2, AH);
   }
   canonicalize_facets(F1);
   canonicalize_facets(F2);
   return find_permutation(rows(F1), rows(F2), typename coord_comparator<E>::type());
}

FunctionTemplate4perl("find_vertex_permutation(Matrix, Matrix)");
FunctionTemplate4perl("find_facet_permutation(Matrix, Matrix, Matrix)");


// rotation matrix: move the hyperplane with the given normal vector to (0 0 ... last_sign)
// CAUTION: resulting matrix is applicable to affine coordinates (from the right)!

template <typename _Vector>
Matrix<double> rotate_hyperplane(const GenericVector<_Vector>& F, int last_sign)
{
   Matrix<double> R(T(null_space_oriented(F.slice(1), last_sign)));
   orthogonalize(entire(cols(R)));
   normalize(entire(cols(R)));
   return R;
}

FunctionTemplate4perl("rotate_hyperplane(Vector; $=1)");

} }

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