/* 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_graph.cc,v 1.6 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 <argv_options.h>
   
namespace {
   enum { seed, acc };
}

namespace polymake { namespace polytope {

void steiner_point_graph(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");

   int nop = 10*(1<<dim);

   Vector<Rational> weights(V.rows());
   Vector<Rational> steiner_point(dim+1);

   double step_dist;
   bool close = false;
   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 i = 0; i < G.nodes(); i++ ) {

      Set<int> neighbors = G.neighbors(i);
      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[i] - *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 = (double(out)/countwhile)/(double(old_out)/(countwhile-1));
	 close = ( fabs(step_dist-1) < eps);
      }//end while
      //      totalwhile += countwhile;
      weights[i] = (Rational)((countwhile+49)*nop-out)/((countwhile+49)*nop);
   }//end verts
   weights /= sum_up*weights;
   //	cout << "\n weights: " << weights << "\n --------------------"<< endl;
   steiner_point = weights*V;

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

} }

using namespace polymake;

/** @file steiner_point_graph
 *
 *  Compute the Steiner point 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.
 *
 *  @synopsis steiner_point_graph <file> [ -seed <s> ] [ -acc <eps> ]
 *
 *  @reading  DIM, VERTICES, GRAPH
 *  @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_graph(p, options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
