/* Copyright (c) 1997-2006
   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: steiner_point_all.cc,v 1.8 2006/03/09 16:21:35 gawrilow Exp $"

#include <ctime>
#include <cmath>
#include <Poly.h>
#include <Matrix.h>
#include <Vector.h>
#include <Graph.h>
#include <Rational.h>
#include <random_generators.h>
#include <HasseDiagram.h>
#include <argv_options.h>

namespace {
   enum { seed, acc };
}

namespace polymake { namespace polytope {

void steiner_point_all(Poly& p, const argv_option *options)
{
   const int dim=p.give("DIM");
   const Matrix<Rational> V=p.give("VERTICES");
   const Matrix<double> Vd(V);
   const Graph<> G=p.give("GRAPH");
   const HasseDiagram<> HDiagram=p.give("HASSE_DIAGRAM");

   int nop;
   Vector<Rational> weights(V.rows());
    
   const int n_stp = HDiagram.nodes() - HDiagram.nodes_of_dim(1).size() - HDiagram.nodes_of_dim(0).size() - 1;
   Matrix< Rational > steiner_points(n_stp,dim+1);

   double step_dist; // this could be Rational
   bool close = false;
   int i = 0; // to know where to put the STEINER POINT
   Vector<int> sum_up(V.rows());
   sum_up.fill(1);

   time_t t;
   const long s=options[seed] ? atol(options[seed]) : (time(&t), t);
   UniformRNG< Vector<double> > random_source(dim,s);

   const int accuracy=options[acc] ? atoi(options[acc]) : 1;
   const double eps=pow(10.0,-accuracy);

   for( int d = dim; d >= 2; d-- ) {
      nop = 10*(1<<d);

      //iterate over the faces of current dimension d
      for (Entire<sequence>::iterator fi=entire(HDiagram.nodes_of_dim(d)); !fi.at_end(); ++fi) {

	 weights.fill(0);
	 Set<int> face = HDiagram.node(*fi); // the combinatorial data of the face

	 //iterate over the vertices of the face
	 for ( Entire< Set <int> >::iterator vi=entire(face); !vi.at_end(); ++vi) {
	    Set<int> neighbors = G.neighbors(*vi)*face; // neighbors of *vi in actual face
	    Matrix<double> adjVert(Vd.minor(neighbors,All));
	  
	    // get the perpendicular vectors for actual vertex
	    for (  Entire< Rows< Matrix< double > > >::iterator ri = entire(rows(adjVert)); !ri.at_end(); ++ri ) {
	       *ri = Vd[*vi] - *ri;
	    }

	    close = false;
	    int countwhile = 0;
	    int old_out = 0;
	    int out = 0;

	    // check more points on the sphere until weights "converge"
	    while (!close){
	       //reinitilize some Values
	       step_dist = 0;
	       countwhile++;
	       Matrix<double> rand_points((countwhile == 1)?50*nop:nop, dim+1);
	       rand_points.col(0).fill(1);
	       std::copy(random_source.begin(), entire(rows(rand_points.minor(All, range(1,dim)))));

	       // check how many points are in- and outside the cone
	       old_out = out;
	       for( Entire< Rows< Matrix< double > > >::iterator ri1= entire(rows(rand_points)); !ri1.at_end(); ++ri1 ) {
		  for( Entire< Rows< Matrix< double > > >::iterator ri2= entire(rows(adjVert)); !ri2.at_end(); ++ri2 ) {
		     if ( (*ri1)*(*ri2) < 0) {
			out++;
			break;
		     }
		  }
	       }
	       step_dist = (countwhile!=1)?(double(out)/countwhile)/(double(old_out)/(countwhile-1)):2;

	       close = ( fabs(step_dist-1) < eps);
	    }//end while
	    weights[*vi] = (Rational)((countwhile+49)*nop-out)/((countwhile+49)*nop);
	 }//end verts
	 weights /= sum_up*weights;
	 steiner_points[i] = weights*V;
	 ++i;
      }//end face
   }//end dim

   p.take("STEINER_POINTS") << "# seed=" << s << ", acc=" << accuracy << '\n' << steiner_points;
}

} }

using namespace polymake;

/** @file steiner_point_all
 *
 *  Compute the Steiner points of all faces of a polytope using a
 *  randomized approximation of the angles.
 *
 *  @c -acc $eps$ is an accuracy parameter controlling the accuracy (
 *  $10^{-eps} ) of the angles computed.  The @c -seed $s$ parameter
 *  may be set to initialize the random calculation of the angles.
 *
 *  Polytope must be bounded.
 *
 *  @synopsis steiner_point_all <file> [ -seed <s> ] [ -acc <eps> ] 
 *
 *  @reading  DIM, VERTICES, GRAPH, HASSE_DIAGRAM
 *  @writing  STEINER_POINTS
 *
 *  @index clients Other
 *  @author Thilo Schröder <thilosch@math.tu-berlin.de>
 */

int main(int argc, const char *argv[])
{
   argv_option options[]={ { "-seed", true }, { "-acc", true } };
   if ( !extract_options(argc,argv,1,options) || argc != 2) {
      cerr << "usage: " << argv[0] << " <file> [ -acc <eps> ] [ -seed <s> ]" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      polytope::steiner_point_all(p, options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
