/* Copyright (c) 1997-2024
   Ewgenij Gawrilow, Michael Joswig, and the polymake team
   Technische Universität Berlin, Germany
   https://polymake.org

   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.
--------------------------------------------------------------------------------
*/

#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(BigObject p)
{
   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;
   bool unbounded = !p.give("BOUNDED");

   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;

   std::string got_property;
   if (p.lookup_with_property_name("AFFINE_HULL | EQUATIONS", got_property)>>E) {
     if (got_property == "EQUATIONS") {
       E=E.minor(basis_affine(E).first,All);
     }
     equats=E;
     ah_eq=true;
  }


      
   if (unbounded) {
      const Vector<Coord> VEC1 = -v0.slice(range_from(1)) + ones_vector<Coord>(n);
      const Vector<Coord> VEC2 = v0.slice(range_from(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;      
      
      Matrix <Coord> A(0,n);
      if (equats.cols() != 0) A = dehomogenize(equats);
      //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
      BigObject p_new(p.type());

      // this should be improved...
      if (unbounded) {
         if (ah_eq)
            // we need to take care of the restriction that AFFINE_HULL may only be written if F contains the FACETS
            p_new.take( f=="FACETS" ? Str("AFFINE_HULL") : Str("EQUATIONS") ) << E;
         p_new.take(f) << F;
      } else {
         if (ah_eq)
            p_new.take("EQUATIONS") << E;
         p_new.take("INEQUALITIES") << F;
      }

      p_new.take("ONE_VERTEX") << v0;
      // Either it was bounded to start, or we forced it to be bounded
      p_new.take("BOUNDED") << true;

      // In the bounded case, we have a subset of constraints; in the
      // unbounded case we take care to preserve our one vertex
      p_new.take("FEASIBLE")<<1;

      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(range_from(1));
         if (rank(Mat)==n_verts+1)
            verts /= (M-v0).slice(range_from(1));
         else {
            const Vector<Coord> M2=p_new.give("LP.MAXIMAL_VERTEX");
            verts /= (M2-v0).slice(range_from(1));
         }
      }
   }

   p.take("CONE_DIM") << verts.rows()+1;
   p.take("REL_INT_POINT") << (v0+(0|average(rows(verts/zero_vector<Coord>(n)))));
}

FunctionTemplate4perl("rel_int_point<Coords>(Polytope<Coords>)");
} }

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