#include <iostream>
#include <vector>
#include <set>
#include <assert.h>
#include <cmath>
#include <map>
#include <complex>
#include <gmpxx.h>
#include <algorithm>
#include <getopt.h>

using namespace std;
#include "automaton.hpp"

/////////////////

//This class only purpose is to contain the primitiveSuboperator method that are usefull for the operators in bounded pathwidth
template<class T_auto>
class automaton_pw{
public:
  T_auto A;
  size_t size_Mat;
  vector<int> baseVect;
  vector<int> base;
  vector<vector<vector<int>>> transMat; 
  vector<int> evaluateVect;
  automaton_pw(pair<int,int>& tgD, vector<bool>& tfD, pair<int,int>& tgF, vector<bool>& tfF):  
  A(tgD, tfD, tgF, tfF){ }
  automaton_pw(): A() { }
  
  void primitiveSuboperator(){//This method removes states that are not accessible or not co-accessible
    if(baseVect.size()==0) return ;
    vector<vector<bool>> Mat(size_Mat, vector<bool>(size_Mat,false));
    for(size_t i=0; i<transMat[0].size(); i++) Mat[i][i]= true;
    for(size_t k=0; k<transMat.size(); k++)
      for(size_t i=0; i<transMat[k].size(); i++)
        for(size_t j=0; j<transMat[k][i].size(); j++)
          if(transMat[k][i][j]) Mat[i][j]=true;
   
    bool todo = true;
    while(todo){
      todo = false;
      for(size_t i=0; i<size_Mat; i++)
        for(size_t j=0; j<size_Mat; j++)
          for(size_t k=0; k<size_Mat; k++)
            if(!Mat[i][j] && Mat[i][k] && Mat[k][j]) todo = Mat[i][j]= true;
    }    
    
    vector<bool> coaccessible(size_Mat, false);
    for(size_t i=0; i<size_Mat ; i++){
      if(evaluateVect[i] == 0) continue;
      for(size_t j=0; j<size_Mat; j++) 
        coaccessible[j]= coaccessible[j] || Mat[i][j];
    }
    
    vector<bool> accessible(size_Mat, false);
    for(size_t i=0; i<size_Mat ; i++){
      if(baseVect[i] == 0) continue;
      for(size_t j=0; j<size_Mat; j++) 
        accessible[j]= accessible[j] || Mat[j][i];
    }

    for(size_t i=0; i<size_Mat; i++)
      coaccessible[i] = coaccessible[i] && accessible[i];
    
    vector<int> mappedTo(coaccessible.size());
    int t=0;
    for(size_t i=0; i<coaccessible.size(); i++)
      if(coaccessible[i]) mappedTo[i]=t++;
      else mappedTo[i]=-1;
    size_Mat=t;
    
    for(size_t k=0;k<transMat.size();k++){
      vector<vector<int>>transtemp(size_Mat,vector<int>(size_Mat,0));
      for(size_t i=0; i<transMat[k].size();i++){
        if(!coaccessible[i]) continue;
        for(size_t j=0;j<transMat[k][i].size();j++){
          if(!coaccessible[j]) continue;
          transtemp[mappedTo[i]][mappedTo[j]]=transMat[k][i][j];        
        }
      }
      transMat[k].swap(transtemp);
    }
    
    vector<int> baseVecttemp(size_Mat);
    for(size_t i=0; i<baseVect.size();i++){
      if(!coaccessible[i]) continue;
      else baseVecttemp[mappedTo[i]]=baseVect[i];
    }
    baseVect.swap(baseVecttemp);
    
    vector<int> evaluateVecttemp(size_Mat);
    for(size_t i=0; i<evaluateVect.size();i++){
      if(!coaccessible[i]) continue;
      else evaluateVecttemp[mappedTo[i]]=evaluateVect[i];
    }
    evaluateVect.swap(evaluateVecttemp);
    
    size_t i=0;
    while(i<transMat.size()){
      bool smaller = false;
      for(size_t j=0;j<transMat.size();j++){
        if(i==j) continue;
        smaller = true;
        for(size_t n=0;n<transMat[i].size();n++)
          for(size_t m=0;m<transMat[i].size();m++){
            if(transMat[i][n][m]>transMat[j][n][m]){smaller = false; break;}            
          }
        if(smaller) break;    
      }
      if(smaller){
       transMat[i].swap(transMat[transMat.size()-1]);
       transMat.pop_back();
      }else i++;
    }
  } 
};


//This class contains an automaton that computes (sigma,rho)-dominating sets and uses it to write explicitely the operators in pathwidth 1
template<class T_auto>
class automaton_pw1: public automaton_pw<T_auto>{
public:
  using automaton_pw<T_auto>::size_Mat;
  using automaton_pw<T_auto>::A;
  using automaton_pw<T_auto>::transMat;
  using automaton_pw<T_auto>::evaluateVect;
  using automaton_pw<T_auto>::baseVect;
  using automaton_pw<T_auto>::primitiveSuboperator;
  
  automaton_pw1(pair<int,int>& tgD, vector<bool>& tfD, pair<int,int>& tgF, vector<bool>& tfF):
  automaton_pw<T_auto>::automaton_pw(tgD, tfD, tgF, tfF){
    baseVect = A.baseVect;
    initevaluateVect(); 
    initmat(); 
    primitiveSuboperator();
  }  
  automaton_pw1(): automaton_pw<T_auto>::automaton_pw(){
    baseVect = A.baseVect;
    initevaluateVect(); 
    initmat(); 
    primitiveSuboperator();
  }

  void SameWithEdge(){
    vector<vector<int>> val(size_Mat, vector<int>(size_Mat,0));
    for(size_t i=0;i<A.size_Aut; i++){
      for(size_t j=0;j<A.base.size(); j++){
        int v = A.forgetSecondAfterOneEdge(i, A.base[j]);
        if(v!=-1) val[v][i]++;
      }
    }
    transMat.push_back(val);
  }  
  
  void newWithEdge(){
    vector<vector<int>> val(A.size_Aut, vector<int>(A.size_Aut,0));
    for(size_t i=0;i<A.size_Aut;i++){
      for(size_t j=0;j<A.base.size();j++){
        int v = A.forgetSecondAfterOneEdge(A.base[j], i);
        if(v!=-1) val[v][i]++;
      }
    }
    transMat.push_back(val);
  }
     
  void SameWithoutEdge(){
    vector<vector<int>> val(A.size_Aut, vector<int>(A.size_Aut,0));
    for(size_t i=0;i<A.size_Aut;i++)
      for(size_t j=0;j<A.base.size();j++)
        if(A.canBeForgoten(A.base[j]))
          val[i][i]++;
    transMat.push_back(val);
  }  
  
  void NewWithoutEdge(){
    vector<vector<int>> val(A.size_Aut, vector<int>(A.size_Aut,0));
    for(size_t i=0;i<A.size_Aut;i++)
      if(A.canBeForgoten(i))
        for(size_t j=0;j<A.base.size(); j++)
          val[A.base[j]][i]++;
    transMat.push_back(val);
  } 
  
  void initevaluateVect(){  
    evaluateVect=vector<int>(A.size_Aut,0);
    for(size_t i=0; i<A.size_Aut;i++)
      if(A.canBeForgoten(i)) evaluateVect[i]=1;
  }  
  
  void initmat(){
    size_Mat = A.size_Aut;
    SameWithEdge();
    newWithEdge();
    SameWithoutEdge();
    NewWithoutEdge();
  }
};


//This class contains an automaton that computes a (sigma,rho)-dominating sets and uses it to write explicitely the operators in pathwidth 2
template<class T_auto>
class automaton_pw2: public automaton_pw<T_auto>{
public:
  using automaton_pw<T_auto>::size_Mat;
  using automaton_pw<T_auto>::A;
  using automaton_pw<T_auto>::transMat;
  using automaton_pw<T_auto>::evaluateVect;
  using automaton_pw<T_auto>::baseVect;
  using automaton_pw<T_auto>::primitiveSuboperator;
  
  automaton_pw2(pair<int,int>& tgD, vector<bool>& tfD, pair<int,int>& tgF, vector<bool>& tfF):
  automaton_pw<T_auto>::automaton_pw(tgD, tfD, tgF, tfF){
    initbasevec();
    initevaluateVect(); 
    initmat(); 
    primitiveSuboperator();
  }  
  automaton_pw2():
  automaton_pw<T_auto>::automaton_pw(){
    initbasevec();
    initevaluateVect(); 
    initmat(); 
    primitiveSuboperator();
  }

  void initbasevec(){
    baseVect=vector<int>(A.size_Aut*A.size_Aut,0);
    for(int i=0; i<A.size_Aut; i++)
      for(int j=0; j<A.size_Aut; j++)
        baseVect[i*A.size_Aut+j]=A.baseVect[i]*A.baseVect[j];
  }
  
  void initevaluateVect(){  
    evaluateVect=vector<int>(A.size_Aut*A.size_Aut,0);
    for(int i=0; i<A.size_Aut; i++){ 
      for(int j=0; j<A.size_Aut; j++){ 
        int ti = A.forgetSecondAfterOneEdge(i, j);
        if((A.canBeForgoten(i) && A.canBeForgoten(j))|| (ti!=-1 && A.canBeForgoten(ti))) evaluateVect[i*A.size_Aut+j]=1;
      }
    }
  }
  void initmat(){
    size_Mat = A.size_Aut*A.size_Aut;
    for(int lost=0; lost<3; lost++){//lost is forgoten
      for(int edges=0; edges<4; edges++){
        vector<vector<int>> val(size_Mat, vector<int>(size_Mat,0));
        for(int i=0; i<A.size_Aut; i++)
          for(int j=0; j<A.size_Aut; j++)
            for(size_t k=0; k<A.base.size(); k++){
              vector<int> v(3,0);
              if(lost == 0){ v[0]=i; v[1]=A.base[k]; v[2]=j; }
              else if(lost == 1){ v[0]=i; v[1]=j; v[2]=A.base[k]; }
              else if(lost == 2){ v[0]=A.base[k]; v[1]=j; v[2]=i; }
              if(edges == 0){
                if(A.canBeForgoten(v[2])) val[v[0]*A.size_Aut+v[1]][i*A.size_Aut+j]++;                
              }else if(edges == 1){
                int nv = A.forgetSecondAfterOneEdge(v[1], v[2]);
                if(nv!=-1) val[v[0]*A.size_Aut+nv][i*A.size_Aut+j]++; 
              }else if(edges == 2){
                int nv= A.forgetSecondAfterOneEdge(v[0], v[2]);
                if(nv!=-1) val[nv*A.size_Aut+v[1]][i*A.size_Aut+j]++; 
              }else if(edges == 3){
                pair<int,int> nv= A.forgetThirdAfterTwoEdges(v[0], v[1], v[2]);
                if(nv.first != -1 && nv.second != -1) val[nv.first*A.size_Aut+nv.second][i*A.size_Aut+j]++; 
              }              
            }
        transMat.push_back(val);
      }     
    }
  }
};

//This class contains the automaton that computes minimal dominating sets in pathwidth 2 and uses it to write explicitely the operators in pathwidth 2
//Remark that there is a special 36th state that has to be handeld be the class
class automaton_pw2_minDom: public automaton_pw<automatonminDomPart>{
public:
  using automaton_pw<automatonminDomPart>::size_Mat;
  using automaton_pw<automatonminDomPart>::A;
  using automaton_pw<automatonminDomPart>::transMat;
  using automaton_pw<automatonminDomPart>::evaluateVect;
  using automaton_pw<automatonminDomPart>::baseVect;
  using automaton_pw<automatonminDomPart>::primitiveSuboperator;
  
  automaton_pw2_minDom(): automaton_pw<automatonminDomPart>::automaton_pw(){
    initbasevec();
    initevaluateVect(); 
    initmat(); 
    primitiveSuboperator();
  }  

  void initbasevec(){
    baseVect=vector<int>(A.size_Aut*A.size_Aut+1,0);
    for(int i=0; i<A.size_Aut; i++)
      for(int j=0; j<A.size_Aut; j++)
        baseVect[i*A.size_Aut+j]=A.baseVect[i]*A.baseVect[j];
  }
  
  void initevaluateVect(){  
    evaluateVect=vector<int>(A.size_Aut*A.size_Aut+1,0);
    for(int i=0; i<A.size_Aut; i++){ 
      for(int j=0; j<A.size_Aut; j++){ 
        int ti = A.forgetSecondAfterOneEdge(i, j);
        if((A.canBeForgoten(i) && A.canBeForgoten(j))|| (ti!=-1 && A.canBeForgoten(ti))) evaluateVect[i*A.size_Aut+j]=1;
      }
    }
    evaluateVect[36]=1;
  }
  
  void initmat(){
    size_Mat = A.size_Aut*A.size_Aut+1;
    for(int lost=0; lost<3; lost++){//lost is forgoten
      for(int edges=0; edges<4; edges++){
        vector<vector<int>> val(size_Mat, vector<int>(size_Mat,0));
        for(int i=0; i<A.size_Aut; i++){
          for(int j=0; j<A.size_Aut; j++){
            for(size_t k=0; k<A.base.size(); k++){
              vector<int> v(3,0);
              if(lost == 0){ v[0]=A.base[k]; v[1]=j; v[2]=i; }
              else if(lost == 1){ v[0]=i; v[1]=A.base[k]; v[2]=j; }
              else if(lost == 2){ v[0]=i; v[1]=j; v[2]=A.base[k]; }
              if(edges == 0){
                if(A.canBeForgoten(v[2])) val[v[0]*A.size_Aut+v[1]][i*A.size_Aut+j]++;                
              }else if(edges == 1){
                int nv = A.forgetSecondAfterOneEdge(v[1], v[2]);
                if(nv!=-1) val[v[0]*A.size_Aut+nv][i*A.size_Aut+j]++; 
              }else if(edges == 2){
                int nv= A.forgetSecondAfterOneEdge(v[0], v[2]);
                if(nv!=-1) val[nv*A.size_Aut+v[1]][i*A.size_Aut+j]++; 
              }else if(edges == 3){
                pair<int,int> nv= A.forgetThirdAfterTwoEdges(v[0], v[1], v[2]);
                if(v[0] == 5 && v[1]==5 && v[2] == 2) val[36][i*A.size_Aut+j]++;// this is the special case
                else if(nv.first != -1 && nv.second != -1) val[nv.first*A.size_Aut+nv.second][i*A.size_Aut+j]++; 
              }              
            }
          }
        }
        if(lost == 0){//the edge between i and j doesn't matter
          if(edges == 0 || edges == 1){ val[1*6+4][36]++; val[5*6+4][36]++;}       
          else if(edges == 2 || edges == 3){ val[1*6+3][36]++; val[5*6+4][36]++;}
        }else if(lost == 1){//the edge between i and j doesn't matter   
          if(edges == 0 || edges == 2){ val[4*6+1][36]++; val[4*6+5][36]++;}           
          else if(edges == 1 || edges == 3){ val[3*6+1][36]++; val[4*6+5][36]++;} 
        }else if(lost == 2){ //we lose the new node
          if(edges == 0) val[36][36]++;
          else if(edges == 1) val[3*6+4][36]++;
          else if(edges == 2) val[4*6+3][36]++;
        }
        transMat.push_back(val);
      }     
    }
  }
};

//This class contains an automaton that computes a (sigma,rho)-dominating sets and uses it to write explicitely the operators for trees or forests
template<class T_auto>
class automaton_tree{
public:
  T_auto A;
  size_t size_Mat;
  vector<int> baseVect;
  vector<vector<vector<pair<int,int>>>> transMat; 
  vector<int> evaluateVect;
  automaton_tree(bool forest): A() { 
    baseVect = A.baseVect;
    size_Mat = A.size_Aut;
    initevaluateVect(); 
    withEdge();
    if(forest) withoutEdge();
    primitiveSuboperator();  
  }

  automaton_tree(pair<int,int>& tgD, vector<bool> tfD, pair<int,int>& tgF, vector<bool> tfF, bool forest):  A(tgD, tfD, tgF, tfF){
    baseVect = A.baseVect;
    size_Mat = A.size_Aut;
    initevaluateVect(); 
    withEdge();
    if(forest) withoutEdge();
    primitiveSuboperator();
  }
  
  void primitiveSuboperator(){
    if(baseVect.size() == 0) return ;
    vector<bool> coaccessible(size_Mat, false);
    vector<bool> accessible(size_Mat, false);
    for(size_t i=0; i<size_Mat ; i++)
      if(evaluateVect[i] != 0) coaccessible[i]= true;
    for(size_t i=0; i<size_Mat ; i++)
      if(baseVect[i] != 0) accessible[i]=true;
    bool done = false;
    while(!done){
      done = true;
      for(size_t k=0;k<transMat.size();k++){
        for(size_t i=0; i<transMat[k].size(); i++){
          for(size_t j=0; j<transMat[k][i].size(); j++){
            if(coaccessible[i] && accessible[transMat[k][i][j].first]){ 
              if(!coaccessible[transMat[k][i][j].second]) done = false;
              coaccessible[transMat[k][i][j].second] = true;
            }
            if(coaccessible[i] && accessible[transMat[k][i][j].second]){ 
              if(!coaccessible[transMat[k][i][j].first]) done = false;
              coaccessible[transMat[k][i][j].first] = true;
            }
            if(accessible[transMat[k][i][j].first] && accessible[transMat[k][i][j].second]){
              if(!accessible[i]) done = false;
              accessible[i] = true;
            }
          }
        }
      } 
    }

    for(size_t i=0; i<size_Mat; i++)
      coaccessible[i] = coaccessible[i] && accessible[i];
    
    vector<int> mappedTo(coaccessible.size());
    size_Mat=0;
    for(size_t i=0; i<coaccessible.size(); i++){
      if(coaccessible[i]) mappedTo[i] = size_Mat++;
      else mappedTo[i]=-1;
    }
    
    for(size_t k=0; k<transMat.size(); k++){
      vector<vector<pair<int,int>>>transtemp(size_Mat);
      for(size_t i=0; i<transMat[k].size();i++){
        if(!coaccessible[i]) continue;
        for(size_t j=0;j<transMat[k][i].size();j++){
          if(coaccessible[transMat[k][i][j].first] && coaccessible[transMat[k][i][j].second]) 
            transtemp[mappedTo[i]].push_back(pair<int,int>(mappedTo[transMat[k][i][j].first], mappedTo[transMat[k][i][j].second]));        
        }
      }
      transMat[k].swap(transtemp);
    }
    
    vector<int> baseVecttemp(size_Mat);
    for(size_t i=0; i<baseVect.size();i++){
      if(!coaccessible[i]) continue;
      else baseVecttemp[mappedTo[i]]=baseVect[i];
    }
    baseVect.swap(baseVecttemp);
    
    vector<int> evaluateVecttemp(size_Mat);
    for(size_t i=0; i<evaluateVect.size();i++){
      if(!coaccessible[i]) continue;
      else evaluateVecttemp[mappedTo[i]]=evaluateVect[i];
    }
    evaluateVect.swap(evaluateVecttemp);
  } 
   
  void withEdge(){
    vector<vector<pair<int,int> > > val(A.size_Aut);
    for(size_t i=0; i<A.size_Aut; i++){
      for(size_t j=0; j<A.size_Aut; j++){
        int v = A.forgetSecondAfterOneEdge(i, j);
        if(v!=-1) val[v].push_back(pair<int,int>(i,j));
      }
    }
    transMat.push_back(val);
  }  
     
  void withoutEdge(){
    vector<vector<pair<int,int> > > val(A.size_Aut);
    for(size_t i=0; i<A.size_Aut; i++)
      for(size_t j=0; j<A.size_Aut; j++)
        if(A.canBeForgoten(j)) val[i].push_back(pair<int,int>(i,j));
    transMat.push_back(val);
  }
  
  void initevaluateVect(){  
    evaluateVect=vector<int>(A.size_Aut,0);
    for(size_t i=0; i<A.size_Aut;i++)
      if(A.canBeForgoten(i)) evaluateVect[i]=1;
  }  
};

 
// In the case of pathwidth write the requested operators in operators and the initial vector in basvect
void get_operators_pw(int pathwidth, int min_all_max, pair<int,int>& graph_D, vector<bool>& sigma, pair<int,int>& graph_F, vector<bool>& rho,
    vector<vector<vector<int>>>& operators, vector<int>& baseVect){  
  if(min_all_max == 0){
    if(pathwidth == 1){
      automaton_pw1<automaton_All> A(graph_D, sigma, graph_F, rho);
      baseVect = A.baseVect; operators = A.transMat;
    }else if(pathwidth == 2){
      automaton_pw2<automaton_All> A(graph_D, sigma, graph_F, rho);
      baseVect = A.baseVect; operators = A.transMat;
    }
  }else if(min_all_max == 1){
    if(pathwidth == 1){
      automaton_pw1<automaton1Max> A(graph_D, sigma, graph_F, rho);
      baseVect = A.baseVect; operators = A.transMat;
    }else if(pathwidth == 2){
      automaton_pw2<automaton1Max> A(graph_D, sigma, graph_F, rho);
      baseVect = A.baseVect; operators = A.transMat;
    }
  }else if(min_all_max == 2){
    if(pathwidth == 1){
      automaton_pw1<automaton1Min> A(graph_D, sigma, graph_F, rho);
      baseVect = A.baseVect; operators = A.transMat;
    }else if(pathwidth == 2){
      automaton_pw2<automaton1Min> A(graph_D, sigma, graph_F, rho);
      baseVect = A.baseVect; operators = A.transMat;
    }
  }else {
    cerr<< "Something was not initialized in the options. Call with option -h to see usage."<<endl;
    exit(EXIT_FAILURE);
  }
}

// In the case of trees and forests write the requested operators in operators and the initial vector in basvect
void get_operators_tf(char graphClass, int min_all_max, pair<int,int>& graph_D, vector<bool>& sigma, pair<int,int>& graph_F, vector<bool>& rho,
    vector<vector<vector<pair<int,int>>>>& operators, vector<int>& baseVect){   
  if(min_all_max == 0){
    automaton_tree<automaton_All> A(graph_D, sigma, graph_F, rho, graphClass == 'f');
    baseVect = A.baseVect; operators = A.transMat;
  }else if(min_all_max == 1){
    automaton_tree<automaton1Max> A(graph_D, sigma, graph_F, rho, graphClass == 'f');
    baseVect = A.baseVect; operators = A.transMat;
  }else if(min_all_max == 2){
    automaton_tree<automaton1Min> A(graph_D, sigma, graph_F, rho, graphClass == 'f');
    baseVect = A.baseVect; operators = A.transMat;
  }else {
    cerr<< "Something was not initialized in the options. Call with option -h to see usage."<<endl;
    exit(EXIT_FAILURE);
  }
}

//Similar functions for the special case of maximal induced matchings
void get_operators_pw_mIM(int pathwidth, vector<vector<vector<int>>>& operators, vector<int>& baseVect){  
  if(pathwidth == 1){
    automaton_pw1<automatonMaxInducedMatching> A;
    baseVect = A.baseVect; operators = A.transMat;
  }else{
    automaton_pw2<automatonMaxInducedMatching> A;
    baseVect = A.baseVect; operators = A.transMat;
  }
}

void get_operators_tf_mIM(char graphClass, vector<vector<vector<pair<int,int>>>>& operators, vector<int>& baseVect){  
  automaton_tree<automatonMaxInducedMatching> A(graphClass == 'f');
  baseVect = A.baseVect; operators = A.transMat;
}

// Similar function for the case of minimum dominating sets in pathwidth 2
void get_operators_minD(vector<vector<vector<int>>>& operators, vector<int>& baseVect){
    automaton_pw2_minDom A;
    baseVect = A.baseVect; operators = A.transMat;
}
