#include <iostream>
#include <vector>
#include <set>
#include <assert.h>
#include <cmath>
#include <map>
#include <complex>
#include <gmpxx.h>
#include <algorithm>
#include <getopt.h>
#include "get_operators.hpp"
#include "X_from_operator.hpp"
//Compilation with:
//g++ main.cpp -lgmp -lgmpxx -O3

using namespace std;

mpq_class readRat(){//read the rational growth rate
  cout<<" What should the growth rate be ?"<<endl; 
  cout<<" Give a rational number: enter the numerator and the denominator separated by a blank space."<<endl; 
  int n,d; cin >>n>>d;  
  return mpq_class(n)/d;
}

Number readAlg(){//read the algrebraic growth rate
  cout<<"   What should the growth rate be ?"<<endl; 
  cout<<"   Give an irrational algrebraic real number. First give a rational multiple of the minimal polynomial (of degree at least 2) with integer coeficients."<<endl; 
  cout<<"   For that enter pairs of integers i j such that the coefficient of x^i is j. You need to start with the term of highest degree and to end with the pair 0 j (where j is the constant term of the polynomial). Every unspecified term is set to 0."<<endl;
  cout<<"   Finaly give a pair of rational numbers (each given as a pair of integers separated by a blank space) around the root that uniquely determines it."<<endl;
  cout<<"   For instance for any number of the form a^(1/b) (with a and b positive integers) one could chose the polynomial x^b-a and the interval [1,2] and write: b 1 0 -a 1 1 2 1. "<<endl; 
  int a,c,d,e; mpq_class b; 
  cin>>a>>b;
  vector<mpq_class> p(a+1,0);
  p[a]=b;
  while(true){
    cin>>a>>b;
    p[a]=b;
    if(a == 0) break;
  }
  cin >> a>>c>>d>>e;
  pair<mpq_class,mpq_class> approx(mpq_class(a)/c,mpq_class(d)/e);
  Number::setPoly(p, approx);
  Number coef(0);
  coef.val[1]=1;
  return coef;
}

int main(int argc, char *argv[]){// read the input of the user and call all the auxiliaries functions
  char graphClass = 0;
  int pathwidth = 0;
  int min_all_max = -1;
  bool maxIM=false;
  bool minD=false;
  bool rat=false;
  pair<int,int> graph_D;
  vector<bool> sigma;
  pair<int,int> graph_F;
  vector<bool> rho;
  int opt;
  while ((opt = getopt(argc, argv, "htfp:s:a:AmMIDR")) != -1) {
    switch (opt) {
      case 'h':
        cout<<"This program computes the set of (multi-)linear operators that correspond to the dynamic algorithm described in the joint article."<<endl;
        cout<<"Instead of writing this operator, it prints c++ code that can is used by the program that computes (or tries to) the set X for a given growth rate"<<endl;
        cout<<"This program must be called with some options."<<endl<<endl;
        cout<<"****     One of the  following options must specify the class of graph, we work on:"<<endl;
        cout<<"    -t to write the code for tree"<<endl;
        cout<<"    -f to write the code for forests"<<endl;
        cout<<"    -p n to write the code for pathwidth n (where n is either 1 or 2)"<<endl<<endl;
        cout<<"****     One of the following options must specify the (sigma,rho)-dominating set we work on (unless *):"<<endl;
        cout<<"    -s to use a predefined parameter:"<<endl;
        cout<<"        D for Dominating set"<<endl;
        cout<<"        I for Independant set"<<endl;
        cout<<"        i for Independant dominating set"<<endl;
        cout<<"        T for Total dominating set"<<endl;
        cout<<"        M for Induced matching"<<endl;
        cout<<"        S for Strong stable set"<<endl;
        cout<<"        C for Perfect Code"<<endl;
        cout<<"        P for Perfect dominating set"<<endl;
        cout<<"        p for Totat perfect dominating set"<<endl;
        cout<<"    -a to specify the (sigma,rho)-dominating set by giving the automaton. It should be followed by a string of the following format:"<<endl;
        cout<<"        pqup'q'u' where p,q,p',q' are one digit integers that correspond respectively to p(sigma), q(sigma), p(rho), q(rho) and"<<endl;
        cout<<"        u and u' are two sequence of 0 and 1 that give the values of 1(sigma) (resp 1(rho)) from 0 to p+q-1. Note that there should be no space in the string pqup'q'u'.";
        cout<<"        For instance, for dominating set the string would be: 0111101."<<endl<<endl; 
        cout<<"****     One of the  following options must be specified (unless *):"<<endl;
        cout<<"    -A if we count all (sigma,rho)-dominating sets"<<endl;
        cout<<"    -m if we count 1-minimal (sigma,rho)- dominating sets"<<endl;
        cout<<"    -M if we count 1-maximal (sigma,rho)- dominating sets"<<endl<<endl<<endl;
        cout<<"****     * There are two options that overides the two previous sets of options:"<<endl;
        cout<<"    -I computes maximum induced matching, the graph class still has to be specified."<<endl<<endl<<endl;
        cout<<"    -D computes minimal dominating sets in pathwidth 2."<<endl<<endl<<endl;
        cout<<"****  -R  allows you to use rational growth rate instead of algebraic."<<endl;
        cout<<"****     For instance calling with \"-p 1 -s M -A\" will generate the set of operators for the number of induced matchings in graphs of pathwidth 1."<<endl<<endl<<endl;
        return 0;
        break;
      case 'p':
        pathwidth=atoi(optarg);
        if(pathwidth!=1 && pathwidth!=2){
          cerr<< "The pathwidth should be 1 or 2. Call with -h to see usage."<<endl;
          exit(EXIT_FAILURE);
        }
      case 't':
      case 'f':
        if(graphClass!=0){
          cerr<< "More than one class of graph specified. Call with -h to see usage."<<endl;
          exit(EXIT_FAILURE);
        }
        graphClass=opt;
        break;
      case 's':
        if(sigma.size()!=0){
          cerr<< "More than one parameter is specified. Call with -h to see usage."<<endl;
          exit(EXIT_FAILURE);
        }
        switch (optarg[0]) {
          case 'D': //Dominating set
            graph_D=pair<int,int>(1,0);// size and loop
            sigma=vector<bool>{true};
            graph_F=pair<int,int>(2,1);
            rho=vector<bool>{false, true};
            break;
          case 'I':  //Independant
            graph_D=pair<int,int>(2,1);// size and loop
            sigma=vector<bool>{true, false};
            graph_F=pair<int,int>(1,0);
            rho=vector<bool>{true};
            break;
          case 'i': //Indep dominating
            graph_D =pair<int,int>(2,1);// size and loop
            sigma=vector<bool>{true, false};
            graph_F=pair<int,int>(2,1);
            rho=vector<bool> {false, true};
            break;
          case 'T': //Total dominating
            graph_D=pair<int,int> (2,1);// size and loop
            sigma=vector<bool> {false, true};
            graph_F=pair<int,int>(2,1);
            rho=vector<bool>{false, true};
            break;
          case 'M': //Induced matching
            graph_D=pair<int,int>(3,2);// size and loop
            sigma=vector<bool>{false, true, false};
            graph_F=pair<int,int>(1,0);
            rho=vector<bool> {true};
            break;
          case 'S'://Strong stable set
            graph_D=pair<int,int>(2,1);// size and loop
            sigma=vector<bool>{true, false};
            graph_F=pair<int,int> (3,2);
            rho=vector<bool>{true, true, false};
            break;
          case 'C': //Perfect Code
            graph_D=pair<int,int>(2,1);// size and loop
            sigma=vector<bool>{true, false};
            graph_F=pair<int,int>(3,2);
            rho=vector<bool> {false, true, false};
            break;
          case 'P': //Perfect dominating
            graph_D=pair<int,int>(1,0);// size and loop
            sigma=vector<bool>{true};
            graph_F=pair<int,int>(3,2);
            rho=vector<bool>{false, true, false};
            break;
          case 'p'://Totat perfect dominating set
            graph_D=pair<int,int>(3,2);// size and loop
            sigma=vector<bool>{false, true, false};
            graph_F=pair<int,int>(3,2);
            rho=vector<bool>{false, true, false};
            break;
        }
        break;
      case 'a':
        if(strlen(optarg)<6){ cerr<< "Arg of a too short. Call with -h to see usage."<<endl;  exit(EXIT_FAILURE);}
        graph_D=pair<int,int>((optarg[1]-'0')+(optarg[0]-'0'), (optarg[0]-'0'));
        if(strlen(optarg)<5+graph_D.first){ cerr<< "Arg of a too short. Call with -h to see usage."<<endl;  exit(EXIT_FAILURE);}
        for(int i=0; i<graph_D.first; i++) sigma.push_back(optarg[i+2]=='1');
        graph_F=pair<int,int>((optarg[3+graph_D.first]-'0')+(optarg[2+graph_D.first]-'0'), (optarg[2+graph_D.first]-'0'));
        if(strlen(optarg)!=4+graph_D.first+graph_F.first){ cerr<< "Arg of a is not of the right length. Call with -h to see usage."<<endl;  exit(EXIT_FAILURE);}
        for(int i=0; i<graph_F.first; i++) rho.push_back(optarg[i+4+graph_D.first]=='1');
        break;
      case 'A':
        if(min_all_max!=-1){
          cerr<< "Only one of A m M must appear. Call with -h to see usage."<<endl;
          exit(EXIT_FAILURE);
        }
        min_all_max=0;
        break;
      case 'M':
        if(min_all_max!=-1){
          cerr<< "Only one of A m M must appear. Call with -h to see usage."<<endl;
          exit(EXIT_FAILURE);
        }
        min_all_max=1;
        break;
      case 'm':
        if(min_all_max!=-1){
          cerr<< "Only one of A m M must appear. Call with -h to see usage."<<endl;
          exit(EXIT_FAILURE);
        }
        min_all_max=2;
        break;
      case 'I':
        maxIM= true;
        break;
      case 'D':
        minD= true;
        break;
      case 'R':
        rat= true;
        break;
      default: /* '?' */
        cerr<< "Call with -h to see usage."<<endl;
        exit(EXIT_FAILURE);
    }
  }
  if(!minD &&(graphClass == 0 ||(!maxIM && ( sigma.size() == 0 || min_all_max == -1)))){
    cerr<< "Something was not initialized in the options. Call with option -h to see usage."<<endl;
    exit(EXIT_FAILURE);
  }
  
  if(graphClass=='p' || minD){
    vector<vector<vector<int>>> operators;
    vector<int> baseVect;
    if(maxIM) get_operators_pw_mIM(pathwidth, operators, baseVect);
    else if(minD) get_operators_minD(operators, baseVect);
    else get_operators_pw(pathwidth, min_all_max, graph_D, sigma, graph_F, rho, operators, baseVect);
    if(rat){      
      mpq_class coef = readRat();
      vector<vector<mpq_class>> sep(1,vector<mpq_class>(baseVect.size()));
      for(size_t i=0; i<baseVect.size(); i++) sep[0][i] = baseVect[i];
      while(true){
        cout<<"Do ou whish to add a vector with positive integer coordinates to the set X at the start ? (This is usefull for instance for proposition 32.) If no enter -1.";
        cout<<"If yes enter the "<< baseVect.size()<<" entries of the vector separated by a blank space."<<endl;
        long int a;
        cin>>a;
        if(a==-1) break;
        vector<mpq_class> v(baseVect.size(),a);
        for(size_t i=1; i<v.size(); i++) cin>>v[i];
        sep.push_back(v);
      }
      findX(operators, sep, coef);
    }else{
      Number coef = readAlg();
      vector<vector<Number>> sep(1,vector<Number>(baseVect.size()));
      for(size_t i=0; i<baseVect.size(); i++) sep[0][i] = baseVect[i]/coef;
      while(true){
        cout<<"Do ou whish to add a vector with positive integer coordinates to the set X at the start ? (This is usefull for instance for proposition 32.) If no enter -1.";
        cout<<"If yes enter the "<< baseVect.size()<<" entries of the vector separated by a blank space."<<endl;
        long int a;
        cin>>a;
        if(a ==-1) break;
        vector<Number> v(baseVect.size(),a);
        for(size_t i=1; i<v.size(); i++){cin>>a; v[i]=a;}
        sep.push_back(v);
      }
      findX(operators, sep, coef);
    }
  }else{
    vector<vector<vector<pair<int,int>>>> operators;
    vector<int> baseVect;
    if(maxIM) get_operators_tf_mIM(graphClass, operators, baseVect);
    else get_operators_tf(graphClass, min_all_max, graph_D, sigma, graph_F, rho, operators, baseVect);    
    if(rat){
      mpq_class coef = readRat();
      vector<vector<mpq_class>> sep(1,vector<mpq_class>(baseVect.size()));
      for(size_t i=0; i<baseVect.size(); i++) sep[0][i] = baseVect[i]/coef;
      sep.push_back(vector<mpq_class>(baseVect.size(),0));
      sep.back()[7]=sep.back()[12]=1/coef;
      findX(operators, sep, coef);
    }else{
      Number coef = readAlg();
      vector<vector<Number>> sep(1,vector<Number>(baseVect.size()));
      for(size_t i=0; i<baseVect.size(); i++) sep[0][i] = baseVect[i]/coef;
      findX(operators, sep, coef);
    }
  }

	return 0;
}
