/* 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: rel_int_point.cc 10057 2011-03-11 17:29:30Z herr $
*/

#include "polymake/client.h"
#include "polymake/linalg.h"
#include "polymake/Matrix.h"
#include "polymake/ListMatrix.h"

namespace polymake { namespace polytope {

template <typename Coord>
void rel_int_point(perl::Object p, bool unbounded, bool affine_hull)
{
   std::string f;
   Matrix<Coord> F=p.give_with_property_name("FACETS | INEQUALITIES",f);
   Matrix<Coord>  E;
   const Vector<Coord> v0=p.give("ONE_VERTEX");      
   int n = v0.dim()-1;

   ListMatrix<Vector <Coord> > verts(0,n); //the linear space corresponding to the affine space spanned by the vertices
   ListMatrix<Vector <Coord> > equats(0,n+1);//equations for the polytope, linear independent
   bool ah_eq=false;

   if (affine_hull) {
      p.give("AFFINE_HULL")>>E;
      equats=E;
      ah_eq=true;
   } else {
      if (p.lookup("EQUATIONS")>>E) {
         equats=E.minor(basis_affine(E).first,All);
         ah_eq=true;
      }
   }

      
   if (unbounded) {
      const Vector<Coord> VEC1=-v0.slice(1)+ones_vector<Coord>(n);
      const Vector<Coord> VEC2=v0.slice(1)+ones_vector<Coord>(n);
      F/=(VEC1|unit_matrix<Coord>(n))/(VEC2|-unit_matrix<Coord>(n));
   }
  
   int n_verts,n_equats; 
   while ((n_verts=verts.rows())+(n_equats=equats.rows())<n) {
      Matrix<Coord> ort=null_space(verts);

      bool dep=true;
      const Matrix <Coord> A=equats.minor(All,sequence(1,n));
      //we try to find a vector orthogonal to the space of vertices and not yet contained in equats
      int i=0;
      for (; dep;++i) {
         if (rank(A/ort.row(i))==n_equats+1) dep=false;
      }
      i--;
      //we solve the linear program for this direction
      perl::Object p_new(p.type());
      if (ah_eq)
         p_new.take(affine_hull ? "AFFINE_HULL" : "EQUATIONS")<<E;
      if (unbounded)
         p_new.take(f)<<F;
      else
         p_new.take("INEQUALITIES")<<F;
      p_new.take("LP.LINEAR_OBJECTIVE")<<(0|ort.row(i));
      const Coord min=p_new.give("LP.MINIMAL_VALUE");
      const Coord max=p_new.give("LP.MAXIMAL_VALUE");

      //if min and max are equal we have found an equation
      if (min==max)
         equats/=(-min|ort.row(i));
      //else we get a new vertex
      else {
         const Vector<Coord> M=p_new.give("LP.MINIMAL_VERTEX");
         const Matrix<Coord> Mat=verts/(M-v0).slice(1);
         if (rank(Mat)==n_verts+1)
            verts/=(M-v0).slice(1);
         else {
            const Vector<Coord> M2=p_new.give("LP.MAXIMAL_VERTEX");
            verts/=(M2-v0).slice(1);
         }
      }
   }
   if (!affine_hull)
      p.take("AFFINE_HULL")<<equats;
   p.take("CONE_DIM")<<verts.rows()+1;
   p.take("REL_INT_POINT") << (v0+(0|average(rows(verts/zero_vector<Coord>(n)))));
}

UserFunctionTemplate4perl("# @category Linear optimization"
                          "# Computes a relative interior point of a polyhedron //P// and stores it in //P//->[[REL_INT_POINT]]."
                          "# The //unbounded// flag needs to be set to true if the polyhedron could be unbounded."
                          "# @param Polytope P"
                          "# @param Bool unbounded needs to be set to true if //P// could be unbounded; default value: 0"
                          "# @param Bool affine_hull indicates that the affine hull of //P// is already computed; default value: 0"
                          "# @author Sven Herrmann",
                          "rel_int_point<Coords>(Polytope<Coords>; $=0, $=0) : void");
} }

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