/* Copyright (c) 1997-2004
   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: check_inc.cc,v 1.17 2004/06/22 16:23:10 gawrilow Exp $"

#include <Rational.h>
#include <Poly.h>
#include <Matrix.h>
#include <sstream>

namespace polymake { namespace polytope {

bool check_inc(Poly& p,const char* points_section, const char* hyperplanes_section, const char* sign_arg, bool verbose)
{
   bool ok=true,
        minus=false, equal=false, plus=false;  // what is allowed
   int n_minus=0, n_equal=0, n_plus=0;         // number of point/hyperplane pairs

   // analyze sign argument
   char c;
   for (int i=0; (c = sign_arg[i]); ++i) {
      switch (c) {
      case '-' : minus = true; break;
      case '0' : equal = true; break;
      case '+' : plus  = true; break;
      default :
	 std::ostringstream err_msg;
	 err_msg << "check_inc: unrecognized sign argument " << sign_arg;
	 throw std::runtime_error(err_msg.str());
      }
   }

   // read the points and hyperplanes
   // matrices of points and hyperplanes, rsp.
   const Matrix<Rational> P = p.lookup(points_section);
   const Matrix<Rational> H = p.lookup(hyperplanes_section); 

   if (!P.rows() || !H.rows())   // nothing to do
      return true;
   if (P.cols() != H.cols()) {
      std::ostringstream err_msg;
      err_msg << "check_inc: " << points_section << " and " << hyperplanes_section
	      << " must match in dimension";
      throw std::runtime_error(err_msg.str());
   }

   // check all pairs
   Rational scp;
   for (int i=0; i<P.rows(); ++i) {
      for (int j=0; j<H.rows(); ++j) {
	 scp = P[i] * H[j];
	 int s=sign(scp);
	 if (s<0) {
	    n_minus++;
	    if (!minus) {
	       if (verbose)
		  cout << "<" << i << "," << j << ">   ( "
		       << P[i] << " ) * [ " << H[j] << " ] == " << scp << endl;
	       ok=false;
	    }
	 } else if (s==0) {
	    n_equal++;
	    if (!equal) {
	       if (verbose)
		  cout << "<" << i << "," << j << ">   ( "
		       << P[i] << " ) * [ " << H[j] << " ] == " << scp << endl;
	       ok=false;
	    }
	 } else {
	    n_plus++;
	    if (!plus) {
	       if (verbose)
		  cout << "<" << i << "," << j << ">   ( "
		       << P[i] << " ) * [ " << H[j] << " ] == " << scp << endl;
	       ok=false;
	    }
	 }
      }
   }

   // print statistics if data not plausible
   if (!ok && verbose)
      cout << "#points==" << P.rows() << ", #hyperplanes==" << H.rows()
	   << ", -:" << n_minus << ", 0:" << n_equal
	   << ", +:" << n_plus << ", total:" << P.rows()*H.rows() << endl;
   return ok;
}

} }

using namespace polymake;

/** @file check_inc
 *
 *  Check the coordinate data of a polytope.
 *  For each pair of vectors from two given sections their inner product must satisfy the
 *  given relation.
 *  
 *  $sign$ is a string composed of one or two characters from [-+0], representing the
 *  allowed domain of the vector inner products.
 *
 *  @synopsis check_inc <file> <points_section> <hyperplanes_section> <sign>
 *
 *  @reading  <points_section>, <hyperplanes_section>
 *
 *  @index clients Consistency check
 */

int main(int argc, const char* argv[])
{
   if (argc<5 || argc>6 || (argc==6 && strcmp(argv[5], "-v") && strcmp(argv[5], "-q"))) {
      cerr << "usage: " << argv[0] << " <file> <points> <hyperplanes> {-0+} [-vq]\n";
      return 1;
   }

   bool ok;
   bool verbose= argc==5 || argv[5][1]=='v';
   try {
      Poly p(argv[1]);
      ok=polytope::check_inc(p,argv[2],argv[3],argv[4],verbose);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 2;
   }
   return !ok;
}
