#include "parser.h"
#include "printer.h"
#include "polynomial.h"
#include "division.h"
#include "buchberger.h"
#include "wallideal.h"
#include "lp.h"
#include "reversesearch.h"
#include "termorder.h"
#include "ep_standard.h"
#include "ep_xfig.h"
#include "gfanapplication.h"
#include "polyhedralcone.h"
#include "polyhedralfan.h"
#include "tropical.h"
#include "tropical2.h"
#include "symmetry.h"

class TropicalIntersectionApplication : public GFanApplication
{

  SimpleOption optionTestIfTropicalBasis;
  SimpleOption optionIncidencePrinting;
  //  SimpleOption optionParseSymmetry;
  //  SimpleOption optionMinkowskiRefinement;
public:
  const char *helpText()
  {
    return "This program computes the intersection of a set of tropical hypersurfaces. The input is a list of polynomials with each polynomial defining a hypersurface. Considering tropical hypersurfaces as fans, the intersection can be computed as the common refinement of these. Thus the output is a fan whose support is the intersection of the tropical hypersurfaces. The fan will be presented as a list of some of its closed cones. If a cone is a face of another cone in the fan it is not guaranteed to be listed. But the support of the fan will be the union of the listed cones.\n";
  }
  TropicalIntersectionApplication():
    optionTestIfTropicalBasis("-t","Note that the input polynomials generate an ideal. This option will make the program choose a relative interior point for each listed output cone and check if its initial ideal contains a monomial. The actual check is done on a homogenization of the input ideal, but this does not affect the result.\n"),
    optionIncidencePrinting("--incidence","Print incidence information of the fan. Only faces of maximal dimensional cones will be printed, so this works best if the fan is pure.")
    //    optionParseSymmetry("--symmetryPrinting","Parse a group of symmetries after the input has been read. Used when printing with --incidence."),
    //    optionMinkowskiRefinement("--minkowski","Compute the normal fan of the  Minkowski sum of the Newton polytopes instead.")
  {
    registerOptions();
  }
    
  char *name()
  {
    return "_tropicalintersection";
  }
  int main()
  {
    LpSolver::printList(Stderr);
    lpSetSolver("cddgmp");
    
    FileParser P(Stdin);
    
    PolynomialSet theInput=P.parsePolynomialSet();
    int n=theInput.numberOfVariablesInRing();

    SymmetryGroup sym(n);
    //    if(optionParseSymmetry.getValue())sym.computeClosure(P.parseIntegerVectorList());

    PolyhedralFan F(n);
    /*    if(optionMinkowskiRefinement.getValue())
      {
	F=PolyhedralFan::fullSpace(n);
	for(PolynomialSet::const_iterator i=theInput.begin();i!=theInput.end();i++)
	  F=refinement(F,PolyhedralFan::normalFanOfNewtonPolytope(*i),n-1,false);
      }
      else*/

    F=tropicalPrincipalIntersection(n, theInput); // dimension of lineality space could be computed to speed up computations

    //    fprintf(Stderr,"---------------------------\n");
    AsciiPrinter(Stdout).printPolyhedralFan(F);
    //   fprintf(Stderr,"---------------------------\n");

    fprintf(Stdout,"\nA list of relative interior points:\n");
    AsciiPrinter(Stdout).printVectorList(F.getRelativeInteriorPoints());

    if(optionIncidencePrinting.getValue())
      {
	AsciiPrinter p(Stdout);
	PolyhedralFan a=F;
	a.makePure();
	a.printWithIndices(&p,&sym);
      }


    if(optionTestIfTropicalBasis.getValue())
      {
	PolynomialSet I=theInput;
	//	AsciiPrinter(Stderr).printPolynomialSet(I);
	
	fprintf(Stderr,"To check monomial containment for initial ideals we need to homogenize the input.\n");
	fprintf(Stderr,"Homogenizing...\n");
	IntegerVector grading=IntegerVector::allOnes(I.numberOfVariablesInRing());
	
	{
	  WeightReverseLexicographicTermOrder t(grading); 
	  buchberger(&I,t);
	}   
	/*	fprintf(Stderr,"Degree compatible basis\n");
	AsciiPrinter(Stderr).printPolynomialSet(I);
	*/
	PolynomialSet h;
	for(PolynomialSet::const_iterator i=I.begin();i!=I.end();i++)
	  {
	    h.push_back(i->homogenization(-1,-1,&grading));
	  }
	fprintf(Stderr,"The homogenized ideal:\n");
	AsciiPrinter(Stderr).printPolynomialSet(h);
	
	IntegerVectorList r=F.getRelativeInteriorPoints();
	
	
	IntegerVectorList trueRays;
	
	for(IntegerVectorList::const_iterator i=r.begin();i!=r.end();i++)
	  {
	    int n=h.numberOfVariablesInRing();
	    IntegerVector weight(n);
	    for(int j=0;j<n-1;j++)weight[j]=(*i)[j];
	    weight[n-1]=0;
	    PolynomialSet h2=h;
	    {
	      WeightReverseLexicographicTermOrder t(weight); 
	      fprintf(Stdout,"Computing the initial ideal with respect to:");
	      AsciiPrinter(Stdout).printVector(weight);
	      fprintf(Stdout,"\n");
	      buchberger(&h2,t);
	      fprintf(Stdout,"Done computing the initial ideal.\n");
	    }
	    
	    //	    AsciiPrinter(Stdout).printVector(weight);
	    //	    AsciiPrinter(Stdout).printPolynomialSet(h2);
	    PolynomialSet wall=initialForms(h2,weight);
	    //	    fprintf(Stdout,"Initial ideal:\n");
	    //	    AsciiPrinter(Stdout).printPolynomialSet(wall);
	    
	    
	    if(containsMonomial(wall))
	      {
		fprintf(Stdout,"The initial ideal contains a monomial: ");
		AsciiPrinter(Stdout).printPolynomial(Polynomial(computeTermInIdeal(wall)));
		fprintf(Stdout,"\n");
	      }
	    else
	      {
		fprintf(Stdout,"The initial ideal contains no monomial.\n");
		trueRays.push_back(*i);
	      }
	    fprintf(Stdout,"\n");
	  }
	fprintf(Stdout,"The set of tested interior points that are in the tropical variety of the ideal generated by the input:\n");
	AsciiPrinter(Stdout).printVectorList(trueRays);
	
      }
    return 0;
  }
};

static TropicalIntersectionApplication theApplication;
