/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   circuitfinder.h
 * Author: sparsa
 *
 * Created on 2 October, 2017, 11:14 AM
 */

#ifndef CIRCUITFINDER_H
#define CIRCUITFINDER_H
#include <algorithm>
#include <iostream>
#include <list>
#include <vector>
using namespace std;
typedef enum {z,les,leq,que} relation;
//extern relation comparison[4][4];
extern relation addition[4][4];
typedef list<int> NodeList;
//void pairWiseTightestRelation (relation **graph, int V); //for tightening the matrix
typedef struct{bool A; bool B;} bool2;
void pairWiseTightestRelation (relation **graph, int V); //for tightening the matrix
template<int N>
class CircuitFinder // defining a circuit finder class
{
  vector<NodeList> AK; //this is the matrix representaion using node list
  vector<int> Stack; //a vector used a stack
  vector<bool> Blocked; // a boolean array to indicate which nodes are blocked and which are not
  vector<NodeList> B; // special node.
  int S;

  void unblock(int U); //function to unblock the vertix U
  bool2 circuit(int V); // function to check if there is a circuit from note V
  bool cycleCheck(); //function to output the list of circuits in the graph
  relation **relmatrix;
public:
  CircuitFinder(int Array[N][N]) //convert associate matrix representation to list representation of grph
    : AK(N), Blocked(N), B(N) {
    for (int I = 0; I < N; ++I) {
      for (int J = 0; J < N; ++J) {
        if (Array[I][J]) {
          AK[I].push_back(Array[I][J]);
        }
      }
    }
  }
  CircuitFinder(relation **matrix) :AK(N), Blocked(N), B(N)
    {
      relmatrix = new relation*[N];
      for(int i = 0; i < N; i++)
          relmatrix[i] = new relation[N];
      for (int I = 0; I<N; I ++)
      {
          for(int J = 0 ; J < N ; J++)
          {
              //if(matrix[I][J] == leq || matrix[I][J] == les || matrix[I][J] == que)
              //{
              relmatrix[I][J] = matrix[I][J]; // copying the matrix information in the class
	      if(matrix[I][J] != z)
                  AK[I].push_back(J+1); //every one is linked with every one because its a complete graph
              //}
          }
      }
    }
  ~CircuitFinder()
  {
      for(int i = 0;i < N; i++)
          delete[] relmatrix[i];
          delete[] relmatrix;
  }
  bool run(); //start the run
};

template<int N>
void CircuitFinder<N>::unblock(int U)
{
  Blocked[U - 1] = false;

  while (!B[U - 1].empty()) {
    int W = B[U - 1].front();
    B[U - 1].pop_front();

    if (Blocked[W - 1]) {
      unblock(W);
    }
  }
}

template<int N>
bool2 CircuitFinder<N>::circuit(int V)
{
  bool2 F;
  F.A= false;
  F.B = false;
  Stack.push_back(V);
  Blocked[V - 1] = true;
  //bool check;
  for (int W : AK[V - 1]) {
    if (W == S) {
      F.B = cycleCheck();
      F.A = true;
    } else if (W > S && !Blocked[W - 1]) {
      F = circuit(W);
    }
  }

  if (F.A) {
    unblock(V);
  } else {
    for (int W : AK[V - 1]) {
      auto IT = std::find(B[W - 1].begin(), B[W - 1].end(), V);
      if (IT == B[W - 1].end()) {
        B[W - 1].push_back(V);
      }
    }
  }

  Stack.pop_back();
  return F;
}

template<int N>
bool CircuitFinder<N>::cycleCheck() //here we need to check if there exists any cycle
{ // with <+<=* regex. return 1 if there is a cycle and return 0 if not.
  //cout << "circuit: "<<endl;
  int countles=0;
  for (auto I = Stack.begin(), E = Stack.end(); I != E; ++I) {
      //checking of if the cycle have a < along with  other <=
    // cout << *I << " -> ";
    if(I+1 != E)
      {
	if(relmatrix[*I][*(I+1)] != leq || relmatrix[*I][*(I+1)] != les)
	  {
	    return false;
	  }
	else if(relmatrix[*I][*(I+1)] == les)
	  countles++;
      }
    else
      {
	if(relmatrix[*I][*Stack.begin()] != leq || relmatrix[*I][*Stack.begin()] != les)
	  return false;
	else if (relmatrix[*I][*Stack.begin()] == les)
	  countles ++;
	
      }
    if( countles == 1)
      return true;
    else
      return false;
    
  }
  // cout << *Stack.begin() << std::endl;
}

template<int N>
bool CircuitFinder<N>::run()
{
  Stack.clear();
  S = 1;
  bool2 returnval;
  while (S < N) {
    for (int I = S; I <= N; ++I) {
      Blocked[I - 1] = false;
      B[I - 1].clear();
    }
   returnval= circuit(S);
   if(returnval.B == true)
     return true;
    ++S;
  }
  return false;
}


#endif /* CIRCUITFINDER_H */

