/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include "_cvaux.h"

#if 0

#include <float.h>
#include <limits.h>
#include <stdio.h>


#include "_cvutils.h"
#include "_cvwrap.h"

/*typedef struct CvCliqueFinder
{   
    CvGraph* graph;
    int**    adj_matr;
    int N; //graph size

    // stacks, counters etc/
    int k; //stack size
    int* current_comp;
    int** All;
    
    int* ne;
    int* ce;
    int* fixp; //node with minimal disconnections
    int* nod;
    int* s; //for selected candidate
    int status;
    int best_score;

} CvCliqueFinder;
*/

#define GO 1
#define BACK 2
#define PEREBOR 3
#define NEXT PEREBOR
#define END 4


#define  CV_GET_ADJ_VTX( vertex, edge ) \
(                                       \
    assert(edge->vtx[0]==vertex||edge->vtx[1] == vertex ), \
    (edge->vtx[0] == vertex)?edge->vtx[1]:edge->vtx[0]     \
)


#define NUMBER( v ) ((v)->flags >> 1 )

void _MarkNodes( CvGraph* graph )
{
    //set number of vertices to their flags
    for( int i = 0; i < graph->total; i++ )
    {
        CvGraphVtx* ver = cvGetGraphVtx( graph, i );
        if( ver )
        {
            ver->flags = i<<1;
        }
    }  
}

void _FillAdjMatrix( CvGraph* graph, int** connected, int reverse )
{
    //assume all vertices are marked
    for( int i = 0; i < graph->total; i++ )
    {
        for( int j = 0; j < graph->total; j++ )
        {
            connected[i][j] = 0|reverse;
        } 
        //memset( connected[i], 0, sizeof(int)*graph->total );
        CvGraphVtx* ver = cvGetGraphVtx( graph, i );
        if( ver )
        {
            connected[i][i] = 1;
            for( CvGraphEdge* e = ver->first; e ; e = CV_NEXT_GRAPH_EDGE( e, ver ) )
            {
               CvGraphVtx* v = CV_GET_ADJ_VTX( ver, e );
               connected[i][NUMBER(v)] = 1^reverse;
            }
        }
    }
}


void cvStartFindCliques( CvGraph* graph, CvCliqueFinder* finder, int reverse, int weighted, int weighted_edges )
{
    int i;

    if (weighted)
    {
        finder->weighted = 1;
        finder->best_weight = 0;
        finder->vertex_weights = (float*)malloc( sizeof(float)*(graph->total+1));
        finder->cur_weight = (float*)malloc( sizeof(float)*(graph->total+1));
        finder->cand_weight = (float*)malloc( sizeof(float)*(graph->total+1));

        finder->cur_weight[0] = 0;
        finder->cand_weight[0] = 0;
        for( i = 0 ; i < graph->total; i++ )
        {
            CvGraphWeightedVtx* ver = (CvGraphWeightedVtx*)cvGetGraphVtx( graph, i );
            assert(ver);
            assert(ver->weight>=0);
            finder->vertex_weights[i] = ver->weight;
            finder->cand_weight[0] += ver->weight;             
        }
    }
    else finder->weighted = 0;

    if (weighted_edges)
    {
        finder->weighted_edges = 1;
        //finder->best_weight = 0;
        finder->edge_weights = (float*)malloc( sizeof(float)*(graph->total)*(graph->total));
        //finder->cur_weight = (float*)malloc( sizeof(float)*(graph->total+1));
        //finder->cand_weight = (float*)malloc( sizeof(float)*(graph->total+1));

        //finder->cur_weight[0] = 0;
        //finder->cand_weight[0] = 0;
        memset( finder->edge_weights, 0, sizeof(float)*(graph->total)*(graph->total) );
        for( i = 0 ; i < graph->total; i++ )
        {
            CvGraphVtx* ver1 = cvGetGraphVtx( graph, i );
            if(!ver1) continue;
            for( int j = i ; j < graph->total; j++ )
            {
                CvGraphVtx* ver2 = cvGetGraphVtx( graph, j );
                if(!ver2) continue;
                CvGraphEdge* edge = cvFindGraphEdgeByPtr( graph, ver1, ver2 );
                if( edge )
                {
                    assert( ((CvGraphWeightedEdge*)edge)->weight >= 0 );
                    finder->edge_weights[ i * graph->total + j ] = 
                    finder->edge_weights[ j * graph->total + i ] = ((CvGraphWeightedEdge*)edge)->weight;
                }
            }
        }        
    }
    else finder->weighted_edges = 0;
                               

    //int* Compsub; //current component (vertex stack)
    finder->k = 0; //counter of steps
    int N = finder->N = graph->total;
    finder->current_comp = new int[N];
    finder->All = new int*[N];
    for( i = 0 ; i < finder->N; i++ )
    {
        finder->All[i] = new int[N];
    }

    finder->ne = new int[N+1];
    finder->ce = new int[N+1];
    finder->fixp = new int[N+1]; //node with minimal disconnections
    finder->nod = new int[N+1];
    finder->s = new int[N+1]; //for selected candidate
    
    //form adj matrix
    finder->adj_matr = new int*[N]; //assume filled with 0
    for( i = 0 ; i < N; i++ )
    {
        finder->adj_matr[i] = new int[N];
    }

    //set number to vertices
    _MarkNodes( graph );
    _FillAdjMatrix( graph, finder->adj_matr, reverse );

    //init all arrays
    int k = finder->k = 0; //stack size
    memset( finder->All[k], 0, sizeof(int) * N );
    for( i = 0; i < N; i++ )  finder->All[k][i] = i;
    finder->ne[0] = 0;
    finder->ce[0] = N;

    finder->status = GO;
    finder->best_score = 0;

}   

void cvEndFindCliques( CvCliqueFinder* finder )
{
    int i;
    
    //int* Compsub; //current component (vertex stack)
    delete finder->current_comp;
    for( i = 0 ; i < finder->N; i++ )
    {
        delete finder->All[i];
    }
    delete finder->All;
    
    delete finder->ne;
    delete finder->ce;
    delete finder->fixp; //node with minimal disconnections
    delete finder->nod;
    delete finder->s; //for selected candidate
    
    //delete adj matrix
    for( i = 0 ; i < finder->N; i++ )
    {
        delete finder->adj_matr[i];
    }  
    delete finder->adj_matr;
    
    if(finder->weighted)
    {
        free(finder->vertex_weights);
        free(finder->cur_weight);
        free(finder->cand_weight); 
    }
    if(finder->weighted_edges)
    {
        free(finder->edge_weights);
    }
}

int cvFindNextMaximalClique( CvCliqueFinder* finder ) 
{
    int**  connected = finder->adj_matr;
//    int N = finder->N; //graph size

    // stacks, counters etc/
    int k = finder->k; //stack size
    int* Compsub = finder->current_comp;
    int** All = finder->All;
    
    int* ne = finder->ne;
    int* ce = finder->ce;
    int* fixp = finder->fixp; //node with minimal disconnections
    int* nod = finder->nod;
    int* s = finder->s; //for selected candidate   
    
    //START
    while( k >= 0)
    {
        int* old = All[k];
        switch(finder->status)
        {    
        case GO://Forward step
        /* we have all sets and will choose fixed point */ 
            {   
                //check potential size of clique
                if( (!finder->weighted) && (k + ce[k] - ne[k] < finder->best_score) ) 
                {
                    finder->status  = BACK;
                    break;
                } 
                //check potential weight
                if( finder->weighted && !finder->weighted_edges &&
                    finder->cur_weight[k] + finder->cand_weight[k] < finder->best_weight )
                {
                    finder->status  = BACK;
                    break;
                }

                int minnod = ce[k];
                nod[k] = 0;

                //for every vertex of All determine counter value and choose minimum
                for( int i = 0; i < ce[k] && minnod != 0; i++) 
                {   
                    int p = old[i]; //current point
                    int count = 0;  //counter
                    int pos = 0;

                    /* Count disconnections with candidates */
                    for (int j = ne[k]; j < ce[k] && (count < minnod); j++) 
                    {
                        if ( !connected[p][old[j]] ) 
                        {
                            count++;
                            /* Save position of potential candidate */
                            pos = j;
                        }
                    }
                    
                    /* Test new minimum */
                    if (count < minnod) 
                    {
                        fixp[k] = p;     //set current point as fixed
                        minnod = count;  //new value for minnod
                        if (i < ne[k])      //if current fixed point belongs to 'not'
                        {
                            s[k] = pos;     //s - selected candidate
                        } 
                        else 
                        {
                            s[k] = i;        //selected candidate is fixed point itself
                            /* preincr */
                            nod[k] = 1;      //nod is aux variable, 1 means fixp == s
                        }
                    }
                }//for

                nod[k] = minnod + nod[k];
                finder->status = NEXT;//go to backtrackcycle 
            }
            break;
        case NEXT:
            //here we will look for candidate to translate into not    
            //s[k] now contains index of choosen candidate
            {
                int* new_ = All[k+1];
                if( nod[k] != 0 )
                {
                    //swap selected and first candidate
                    int i, p = old[s[k]];
                    old[s[k]] = old[ne[k]];
                    int sel = old[ne[k]] = p;

                    int newne = 0;
                    //fill new set 'not'
                    for ( i = 0; i < ne[k]; i++) 
                    {
                        if (connected[sel][old[i]]) 
                        {
                            new_[newne] = old[i];
                            newne++;
                            
                        }
                    }
                    //fill new set 'candidate'
                    int newce = newne;
                    i++;//skip selected candidate
                    
                    float candweight = 0;
                    
                    for (; i < ce[k]; i++) 
                    {
                        if (connected[sel][old[i]]) 
                        {
                            new_[newce] = old[i];
                            
                            if( finder->weighted ) 
                                candweight += finder->vertex_weights[old[i]];                            
                            
                            newce++; 
                        }
                    }

                    nod[k]--;

                    //add selected to stack 
                    Compsub[k] = sel;

                    k++;
                    assert( k <= finder->N );
                    if( finder->weighted )
                    {
                        //update weights of current clique and candidates
                        finder->cur_weight[k] = finder->cur_weight[k-1] + finder->vertex_weights[sel]; 
                        finder->cand_weight[k] = candweight;
                    }       
                    if( finder->weighted_edges )
                    {
                        //update total weight by edge weights
                        float added = 0;
                        for( int ind = 0; ind < k-1; ind++ )
                        {
                            added += finder->edge_weights[ Compsub[ind] * finder->N + sel ];
                        }
                        finder->cur_weight[k] += added;
                    }                    
                                        
                    //check if 'not' and 'cand' are both empty
                    if( newce == 0 )
                    {
                        finder->best_score = MAX(finder->best_score, k );

                        if( finder->weighted )
                            finder->best_weight = MAX( finder->best_weight, finder->cur_weight[k] );
                        /*FILE* file  = fopen("cliques.txt", "a" );

                        for (int t=0; t<k; t++)
                        {
                          fprintf(file, "%d ", Compsub[t]);
                        }
                        fprintf(file, "\n");

                        fclose(file);
                        */
                        
                        //output new clique//************************
                        finder->status = BACK;
                        finder->k = k;

                        return CLIQUE_FOUND;

                    }
                    else //check nonempty set of candidates
                    if( newne < newce )
                    {
                        //go further
                        ne[k] = newne;
                        ce[k] = newce;
                        finder->status  = GO;
                        break;
                    }
                    
                }
                else 
                    finder->status  = BACK;

            }
            break;

        case BACK:
            {         
                //decrease stack
                k--;         
                old = All[k];
                if( k < 0 ) break;

                //add to not
                ne[k]++;

                if( nod[k] > 0 )
                {
                    //select next candidate
                    for( s[k] = ne[k]; s[k] < ce[k]; s[k]++ )
                    {
                        if( !connected[fixp[k]][old[s[k]]])
                            break;
                    }
                    assert( s[k] < ce[k] );
                    finder->status = NEXT;
                }
                else
                    finder->status = BACK;

            }
            break;
        case END: assert(0);       

        }
    }//end while

    finder->status = END;
    return CLIQUE_END;
}


                                     

void cvBronKerbosch( CvGraph* graph )
{
    int* Compsub; //current component (vertex stack)
    int k = 0; //counter of steps
    int N = graph->total;
    int i;
    Compsub = new int[N];
    int** All = new int*[N];
    for( i = 0 ; i < N; i++ )
    {
        All[i] = new int[N];
    }

    int* ne = new int[N];
    int* ce = new int[N];
    int* fixp = new int[N]; //node with minimal disconnections
    int* nod = new int[N];
    int* s = new int[N]; //for selected candidate
    
    //form adj matrix
    int** connected = new int*[N]; //assume filled with 0
    for( i = 0 ; i < N; i++ )
    {
        connected[i] = new int[N];
    }



    //set number to vertices
    _MarkNodes( graph );
    _FillAdjMatrix( graph, connected, 0 );

    //init all arrays
    k = 0; //stack size
    memset( All[k], 0, sizeof(int) * N );
    for( i = 0; i < N; i++ )  All[k][i] = i;
    ne[0] = 0;
    ce[0] = N;

    int status = GO;
    int best_score = 0;

    //START
    while( k >= 0)
    {
        int* old = All[k];
        switch(status)
        {    
        case GO://Forward step
        /* we have all sets and will choose fixed point */ 
            {   

                if( k + ce[k] - ne[k] < best_score ) 
                {
                    status  = BACK;
                    break;
                } 

                int minnod = ce[k];
                nod[k] = 0;

                //for every vertex of All determine counter value and choose minimum
                for( int i = 0; i < ce[k] && minnod != 0; i++) 
                {   
                    int p = old[i]; //current point
                    int count = 0;  //counter
                    int pos = 0;

                    /* Count disconnections with candidates */
                    for (int j = ne[k]; j < ce[k] && (count < minnod); j++) 
                    {
                        if ( !connected[p][old[j]] ) 
                        {
                            count++;
                            /* Save position of potential candidate */
                            pos = j;
                        }
                    }
                    
                    /* Test new minimum */
                    if (count < minnod) 
                    {
                        fixp[k] = p;     //set current point as fixed
                        minnod = count;  //new value for minnod
                        if (i < ne[k])      //if current fixed point belongs to 'not'
                        {
                            s[k] = pos;     //s - selected candidate
                        } 
                        else 
                        {
                            s[k] = i;        //selected candidate is fixed point itself
                            /* preincr */
                            nod[k] = 1;      //nod is aux variable, 1 means fixp == s
                        }
                    }
                }//for

                nod[k] = minnod + nod[k];
                status = NEXT;//go to backtrackcycle 
            }
            break;
        case NEXT:
            //here we will look for candidate to translate into not    
            //s[k] now contains index of choosen candidate
            {
                int* new_ = All[k+1];
                if( nod[k] != 0 )
                {
                    //swap selected and first candidate
                    int p = old[s[k]];
                    old[s[k]] = old[ne[k]];
                    int sel = old[ne[k]] = p;

                    int newne = 0;
                    //fill new set 'not'
                    for ( i = 0; i < ne[k]; i++) 
                    {
                        if (connected[sel][old[i]]) 
                        {
                            new_[newne] = old[i];
                            newne++;
                            
                        }
                    }
                    //fill new set 'candidate'
                    int newce = newne;
                    i++;//skip selected candidate
                    for (; i < ce[k]; i++) 
                    {
                        if (connected[sel][old[i]]) 
                        {
                            new_[newce] = old[i];
                            newce++;
                        }
                    }

                    nod[k]--;

                    //add selected to stack 
                    Compsub[k] = sel;
                    k++;

                    //check if 'not' and 'cand' are both empty
                    if( newce == 0 )
                    {
                        best_score = MAX(best_score, k );

                        FILE* file  = fopen("cliques.txt", "a" );

                        for (int t=0; t<k; t++)
                        {
                          fprintf(file, "%d ", Compsub[t]);
                        }
                        fprintf(file, "\n");

                        fclose(file);

                        /*for( int t = 0; t < k; t++ )
                        {
                            printf("%d ", Compsub[t] );
                        }
                        printf("\n"); */

                        //printf("found %d\n", k);

                        //output new clique//************************
                        status = BACK;
                    }
                    else //check nonempty set of candidates
                    if( newne < newce )
                    {
                        //go further
                        ne[k] = newne;
                        ce[k] = newce;
                        status  = GO;
                        break;
                    }
                    
                }
                else 
                    status  = BACK;

            }
            break;

        case BACK:
            {         
                //decrease stack
                k--;         
                old = All[k];
                if( k < 0 ) break;

                //add to not
                ne[k]++;

                if( nod[k] > 0 )
                {
                    //select next candidate
                    for( s[k] = ne[k]; s[k] < ce[k]; s[k]++ )
                    {
                        if( !connected[fixp[k]][old[s[k]]])
                            break;
                    }
                    assert( s[k] < ce[k] );
                    status = NEXT;
                }
                else
                    status = BACK;

            }
            break;
       

        }
    }//end while

}//end cvBronKerbosch

#endif

