/* Copyright (c) 1997-2005
   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: bounded_embedder.cc,v 1.2 2005/02/21 11:18:33 gawrilow Exp $"

#include <Poly.h>
#include <Array.h>
#include <Set.h>
#include <Graph.h>
#include <Matrix.h>
#include <Vector.h>
#include <linalg.h>

#include <list>
#include <cmath>


namespace polymake { namespace graph {

void bounded_embedder(Poly& p, const char *graph_section, const char *vertex_section,
		      const char *fixed_indices, const char *fixed_coord, const char *embedding_section)
{
   typedef Graph<nothing,double> w_graph;

   w_graph BG;
   p.give(graph_section) >> ignore_attrs<int,nothing>(BG);
   const int n_nodes = BG.nodes();
   const Matrix<double> V = p.give(vertex_section);
   Matrix<double> GR(n_nodes,3);
#ifdef POLY_DEBUG
   cout << "reading ... " << endl;
   cout << "graph ... \n" << BG << endl;
   cout << "V ... " << endl;
   cout << "n_nodes ... " << n_nodes << endl;
#endif

   // compute metric distances
   for (Entire<w_graph::edge_container>::iterator e=entire(edges(BG)); !e.at_end(); ++e)
      *e = std::sqrt(sqr(V[e.from_node()]-V[e.to_node()]));
#ifdef POLY_DEBUG
   cout << "done compute metric distances" << endl;
   cout << "graph ... \n" << BG << endl;
#endif

   // compute fixed nodes
   const Set<int> fixed_nodes = p.give(fixed_indices);
   if (fixed_nodes.size()<3)
      throw std::runtime_error("bounded_embedder: Less than three fixed nodes.");
   
   p.give(fixed_coord) >> non_const(GR.minor(fixed_nodes,All));

   // compute coordinates of remaining node
   const Set<int> inner_nodes = sequence(0,n_nodes) - fixed_nodes;
   const int n_inner_nodes = inner_nodes.size();
   Matrix<double> stress_matrix(n_nodes, n_nodes);
   
   Vector<double> rhs_x(n_inner_nodes);
   Vector<double> rhs_y(n_inner_nodes);
   Vector<double> rhs_z(n_inner_nodes);
   
   int m=0;
   for (Entire< Set <int> >::const_iterator n=entire(inner_nodes); !n.at_end(); ++n,++m) {
      //      cout << "testing node " << *n << endl;
      //      cout << "neighbors: " << BG.neighbors(*n) << endl;
      //      cout << "stress_matrix\n" << stress_matrix << endl;
      for (Entire<w_graph::out_edge_list>::const_iterator e=entire(BG.out_edges(*n)); !e.at_end(); ++e) {
	 //	 cout << "nn: " << e.to_node() << endl;
	 //	 cout << "stress_matrix(*n,*n) = " << stress_matrix(*n,*n) << " + " << 1/(*e) << endl;
	 stress_matrix(*n,*n) += 1/(*e);  // spring constant
	 //	 cout << "stress_matrix(*n,*n) = " << stress_matrix(*n,*n) << endl;
	 //	 cout << "stress_matrix\n" << stress_matrix << endl;

	 const int nn = e.to_node();
	 if ( inner_nodes.contains(nn) ) {
	    stress_matrix(*n,nn) = -1/(*e);  // spring constant
	    //	    cout << "stress_matrix(*n,nn) = " << stress_matrix(*n,nn) << endl;
	    //	    cout << "stress_matrix\n" << stress_matrix << endl;
	 }
	 else {
	    rhs_x[m] += GR(nn,0)/(*e);
	    rhs_y[m] += GR(nn,1)/(*e);
	    rhs_z[m] += GR(nn,2)/(*e);
	 }
      }
   }
   
   //   cout << "stress_matrix\n" << stress_matrix << endl;
   
   const Matrix<double> stress_matrix_inv = inv(stress_matrix.minor(inner_nodes,inner_nodes));
#ifdef POLY_DEBUG
   cout << "done inverting matrix" << endl;
#endif

   GR.minor(inner_nodes,All).col(0) = stress_matrix_inv*rhs_x;
   GR.minor(inner_nodes,All).col(1) = stress_matrix_inv*rhs_y;
   GR.minor(inner_nodes,All).col(2) = stress_matrix_inv*rhs_z;
   
   p.take(embedding_section) << GR;
}
   
} }

using namespace polymake;

/*  @file bounded_embedder
 *
 *  @synopsis bounded_embedder <file> <graph_section> <vertex_section> <fixed_indices> <fixed_coord> <embedding_section>
 *
 */

int main(int argc, const char* argv[])
{
   if (argc != 7) {
      cerr << "usage: " << argv[0] << " <file> <graph_section> <vertex_section> <fixed_indices> <fixed_coord> <embedding_section>" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      graph::bounded_embedder(p, argv[2], argv[3], argv[4], argv[5], argv[6]);
   }
   catch (const std::exception& e) {
     cerr << e.what() << endl;
     return 1;
   }
   return 0;
}


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