//
// Created by Stephane on 10/03/2020.
//

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "decompose.h"
#include "components.h"
#include "improve.h"
#include "graph.h"
#include "main.h"
#include "separator.h"
#include "tree.h"
#include "utils.h"
#include "heap.h"

extern int trace;
extern char * last_name;


int *bestTree;
int bestHeight;
int bestEvalMode = NONE;


Separator separator;

Node * theNodes;

SET * theSets;
int nbAllocatedSets;

int ** components;
int ** componentsOf;
int nbAllocatedComponents;
int topComponents;



int nbCallsDecompose = 0;
int nbCallsDecomposeComponents = 0;

int mustVerifyDecomposition = 0;



void updateBestDecomposition(Node root, Graph g) {
    if ((root != NULL) && (root->height < bestHeight)) {
        bestHeight = root->height;
        saveToTable(bestTree, g);
        bestEvalMode = modeEvalSeparator;
    }
}



void allocAndInitializeDecomposition(Graph g) {
    separator = newSeparator(g->n, g);

    allocSeparation(g);
    allocNodes(g);
    nbAllocatedSets = 100;
    allocSets(g, nbAllocatedSets);

    nbAllocatedComponents = 100;
    allocComponents(g, nbAllocatedComponents);
    allocSearchConnectedComponents(g);

    bestTree = malloc(g->n * sizeof(int));
    bestHeight = g->n+1;
}

void initializeRun(Graph g, int S[], int pos[]) {
    topComponents = 0;

    for (int i = 0; i < g->n; i ++) {
        S[i] = i;
        pos[i] = i;
    }
}

void markRemoved(int S[], int n, int pos[]) {
    for (int i = 0; i < n; i ++)
        pos[S[i]] = NONE;
}


#ifdef STATS_SEPARATION
extern clock_t *timesSep;
extern int *nbSearches;
extern int *sumSizes;
#endif


void testDecompose(Graph g, int nbRuns, int algo) {
    int *S = malloc(g->n * sizeof(int)); // list of vertices
    int *posS = malloc(g->n * sizeof(int)); // booleans for separation
    int *nbCriticalNodes = malloc(g->n * sizeof(int)); // to calculate the numbers of critical nodes at each level
    int bestHDec = g->n+1;
    Node root;
    int h0;
    int nbEffectiveRuns = 0;
    int nbRunsAttempted = 0;
    int trace = 0;

    if (perForceChoiceNotInC == NONE)
        perForceChoiceNotInC = (g->n > 7600) ? 10 : 0;

    clock_t startTimeRun;
    clock_t startTimeImprove;
    clock_t totalTimeDecompose = 0;
    clock_t totalTimeImprove = 0;

    allocAndInitializeDecomposition(g);
    allocConnectionsHeap(g);

    if (preliminaryGreedyDecomposition && (algo != GREEDY_ALGORITHM) && (g->n < maxSizePrelimGreedy)) {
        nbEffectiveRuns ++;
        initConHeap(g);
        root = greedyDecompose(conHeap, 0, g, g->n+1, 1);
        if (trace) printf("greedy first decomp h=%d  %.1fs\n", root->height, (float) (clock()-startTime)/CLOCKS_PER_SEC);
        if (root != NULL) updateBestDecomposition(root, g);
        if (stopSearch) goto SAVE_AND_EXIT;
    }

    for (int i = 0; i < nbRuns; i ++) {

        startTimeRun = clock();
        if ((startTimeRun-startTime)/CLOCKS_PER_SEC >= time_limit)
            break;

        if (algo == SEPARE_AND_EXPLORE) {
            initializePriorities(g);
            initializeRun(g, S, posS);
        }

        int maxDepth = bestHDec*(100+depthThreshold)/100;
        if (noSwapsInFirstRuns) maxDepth = bestHDec+1;

        initConHeap(g);

        if (algo == GREEDY_ALGORITHM) {
            root = greedyDecompose(conHeap, 0, g, maxDepth, 1);
            if (trace) printf("greedy decomp h=%d  %.1fs\n", (root == NULL) ? -1 : root->height, (float) (clock()-startTime)/CLOCKS_PER_SEC);
        }
        else if (algo == SEPARE_AND_EXPLORE) {
            root = decompose(S, g->n, posS, 0, g, maxDepth, 0);
            if (trace) printf("decompose h=%d  %.1fs\n", (root == NULL) ? -1 : root->height, (float) (clock()-startTime)/CLOCKS_PER_SEC);
        }

        nbRunsAttempted ++;
        if (stopSearch) break;

        totalTimeDecompose += (clock()-startTimeRun);

        if (root == NULL) {
            if (nbRunsSeparation < 50) nbRunsSeparation ++;
            continue;
        }

        nbEffectiveRuns ++;
        h0 = root->height;
        if (root->height < bestHDec) bestHDec = root->height;

        if (printStats) printStatsOnCriticalNodes(root, nbCriticalNodes);

        updateBestDecomposition(root, g);

        if (noSwapsInFirstRuns) {
            if ((clock()-startTime) / CLOCKS_PER_SEC < timeWithNoSwaps * time_limit / 100) continue; // Shunt improve at the beginning
            root = buildTreeFromTable(bestTree, g);
            noSwapsInFirstRuns = 0;
            nbRuns = 1000000;
        }

        startTimeImprove = clock();
        if (pullUpIndependentSubtrees || searchEjectionsInCriticalBranch) {
            root = makeEjectionsInCriticalBranch(root, g);
            if (trace) printf("improve  %d --> %d  %.1fs\n", h0, root->height, (float) (clock()-startTime)/CLOCKS_PER_SEC);
            updateBestDecomposition(root, g); // useless
        }
        totalTimeImprove += (clock()-startTimeImprove);

        if (stopSearch) break;

        if (mustVerifyDecomposition) if ( ( ! verifyTree(root, 0)) || ( ! verifyDecomposition(root, g)) ) exit(0);
    }

#ifdef STATS_SEPARATION
    for (int i = 0; ; i ++) {
        if (nbSearches[i] == 0) break;
        printf("[%d]  %d x %.1fs   (size=%d)\n", i, nbSearches[i],
                (float) (timesSep[i] / nbSearches[i]) / CLOCKS_PER_SEC, sumSizes[i]/nbSearches[i]);
    }
#endif

SAVE_AND_EXIT:
    if (printResult) printf("%s %d %d   runs=%d/%d   calls=%d+%d+%d   h=%d    %.2fs + %.2fs   hdec=%d  mode=%d\n",
            last_name, g->n, g->m, nbEffectiveRuns, nbRunsAttempted,
            nbCallsDecompose, nbCallsDecomposeComponents, nbCallsEject,
            bestHeight,
            (float) totalTimeDecompose/CLOCKS_PER_SEC, (float) totalTimeImprove/CLOCKS_PER_SEC,
            bestHDec, bestEvalMode);
    if (printSolution) {
        PACEOutput(fileSolution, bestHeight, bestTree, g);
    }
}


//
// Decomposition : SEPARE_AND_EXPLORE
//

Node decompose(int *S, int n, int pos[], int depth, Graph g, int hmax, int connected) {

    nbCallsDecompose ++;

    if (trace) printf("[%d] call %d  n=%d   \n", depth, nbCallsDecompose, n);

    assert(depth <= g->n);

    if (stopSearch) return NULL;
    if (n == 0) return NULL;
    if (hmax <= 0) return NULL;

    if (n <= 3) { markRemoved(S, n, pos); return makeSmallTree(S, n, g); }

    SET set = NULL;
    if (n > 105) {
        if (theSets[depth] == NULL) reAllocSets(100, g);
        set = makeSet(S, n, theSets[depth]);
    }
    else
        Introsort(S, S, S + n - 1);

    // Search connected components
    if (depth > 0) {
        int nbComp = searchConnectedComponents(set, S, n, g);
        if (stopSearch) return NULL;
        if (nbComp > 1) {
            if (trace) printf("components: %d   ", nbComp);
            return decomposeConnectedComponents(iComp, nbComp, S, n, pos, depth, g, hmax);
        }
    }

    int nbEdges = initializeNbNeighbors(set, S, n, pos, g); // for searchSeparator()

    if (2*nbEdges == n*(n-1)) {
        markRemoved(S, n, pos);
        return makeSingleBranch(S, n, NULL, g);
    }

    //searchSeparator(S, n, set, g, separator, (n < 20) ? 1 : nbRunsSeparation, (n < 20) ? 1 : nbFlushes, NULL, 0);
    searchSeparator(S, n, set, g, separator, nbRunsSeparation, nbFlushes, depth);

    if (stopSearch) return NULL;

    //root = makeSingleBranchWithDisconnectedVertices(separator->C->val, separator->C->n, separator->nbABDV, g);
    Node root = makeSingleBranch(separator->C->val, separator->C->n, nbNInABCopy, g);

    Node theLastOfTheBranch = bottomNode;
    markRemoved(separator->C->val, separator->C->n, pos);

    int nA = separator->A->n;
    int nB = separator->B->n;
    int nC = separator->C->n;
    Node A, B;

    int *listA = S;
    int *listB = S+nA; // no need for S after here

    if (trace) printf("   sep: %d,%d,%d\n", nA, nB, nC);

    copyList(listA, separator->A->val, nA);
    copyList(listB, separator->B->val, nB);

    // Here nbNeighborsInA and nbNeighborsInB contain for each vertex in listA and listB
    // its number of neighbors in listA and listB. No need to recalculate in initSeparator.

    if (nA >= nB) {
        A = decompose(listA, nA, pos, depth + 1, g, hmax - nC, 0);
        if (stopSearch) return NULL;
        if ((A == NULL) && (nA > 0)) {
            root = NULL;
            goto FIN;
        }
        B = decompose(listB, nB, pos, depth + 1, g, hmax - nC, 0);
        if (stopSearch) return NULL;
        if ((B == NULL) && (nB > 0)) {
            root = NULL;
            goto FIN;
        }
    }
    else {
        B = decompose(listB, nB, pos, depth + 1, g, hmax - nC, 0);
        if (stopSearch) return NULL;
        if ((B == NULL) && (nB > 0)) {
            root = NULL;
            goto FIN;
        }
        A = decompose(listA, nA, pos, depth + 1, g, hmax - nC, 0);
        if (stopSearch) return NULL;
        if ((A == NULL) && (nA > 0)) {
            root = NULL;
            goto FIN;
        }
    }


    if (root == NULL) {
        if (A == NULL) { root = B; goto FIN; }
        if (B == NULL) { root = A; goto FIN; }
        //assert(A->next == NULL);
        //assert(B->next == NULL);
        root = A;
        while (A->next != NULL) A = A->next;
        A->next = B;
        assert((root->next == NULL) || (depth > 0));
        goto FIN;
    }


    if (A != NULL) {
        if (A->next == NULL)
            addChild(A, theLastOfTheBranch);
        else {
            while (A != NULL) {
                Node next = A->next;
                addChild(A, theLastOfTheBranch);
                A = next;
            }
        }
    }


    if (B != NULL) {
        if (B->next == NULL)
            addChild(B, theLastOfTheBranch);
        else {
            while (B != NULL) {
                Node next = B->next;
                addChild(B, theLastOfTheBranch);
                B = next;
            }
        }
    }

    if (theLastOfTheBranch->father != NULL)
        updateIncHeightOnBranch(theLastOfTheBranch->father, theLastOfTheBranch->height);

    //updateBranchUp(theLastOfTheBranch, root);
    FIN:

    //verifyTree(root, depth);
    return root;
}


Node decomposeConnectedComponents(int *iComp, int nbComp, int *S, int n, int pos[], int depth, Graph g, int hmax) {

    Node list = NULL, node;

    nbCallsDecomposeComponents ++;

    int *sizes = malloc(nbComp*sizeof(int)); // To memorize sizes after first explorations

    sortListByComponent(S, n, nbComp, sizes);
    int nbV = 0;

    for (int i = 0; i < nbComp; i ++) {
        if (stopSearch) { list = NULL; break; }

        node = decompose(S+nbV, sizes[i], pos, depth, g, hmax, 1);

        if ((node == NULL) || (stopSearch)) return NULL;

        node->next = list;
        if (list != NULL) list->prev = node;
        list = node;

        nbV += sizes[i];
    }

    return list;

#ifdef OLD_VERSION
    int *component, *componentOf;

    if (components[topComponents] == NULL) reAllocComponents(g, 100);
    component = components[topComponents];
    componentOf = componentsOf[topComponents];
    topComponents ++;

    // Memorize iComp[] that may be used in next calls
    for (int i = 0; i < n; i ++)
        componentOf[i] = iComp[S[i]];

    for (int i = 0; i < nbComp; i ++) {
        if (stopSearch) { list = NULL; break; }

        int size = extractList(i, S, n, componentOf, component);

        node = decompose(component, size, depth, g, hmax, 1);
        if (node == NULL) { topComponents --; return NULL; }

        node->next = list;
        if (list != NULL) list->prev = node;
        list = node;
    }

    topComponents --;
    return list;
#endif
}



//
// Greedy-decompose (V,E)
// 1. select vertex u with highest degree,
// 2. C:=disconnectedComponents(V-{u},E),
// 3. tree:=<u>,
// 4. for each c in C do
// 5.    addChild(Greedy-decompose(c,E), tree),
// 6. return tree,

int lastRemovedVertex = NONE;

Node greedyDecompose(Heap conHeap, int depth, Graph g, int hmax, int connected) {

    nbCallsDecompose ++;

    if (depth > maxDepthGreedy) return NULL;

    if (trace) printf("[%d] call %d  n=%d  max=%d ", depth, nbCallsDecompose, conHeap->n, maxNbN4Con);

    if (stopSearch) return NULL;
    if (conHeap->n == 0) return NULL;
    if (hmax == 0) return NULL;

    if (conHeap->n <= SIZE_SMALL_TREE) {
        Node root = makeSmallTree(conHeap->val, conHeap->n, g);
        heapRemoveAll(conHeap);
        return root;
    }

    // Isolated vertices
    if (nbUnconnectedVertices > 0) {
        //assert(connected == 0);
        if (trace) printf(" :: %d unconnected vertices :: %d --> %d\n", nbUnconnectedVertices, conHeap->n, conHeap->n - nbUnconnectedVertices);
        Node last;
        Node root =  makeListWithUnconnectedVertices(conHeap, g, &last);
        if (conHeap->n > 0) {
            nbUnconnectedVertices = 0;
            Node sibling = greedyDecompose(conHeap, depth + 1, g, hmax, 0);
            if (sibling == NULL)
                return NULL;
            last->next = sibling;
        }
        return root;
    }

    // Search disconnected components
    if (( ! connected) && (depth > 0) && (lastRemovedVertex != NONE) &&
            (nbN4Con[lastRemovedVertex] != 1)) { // added 7/04/2020
        int nbComp = searchConnectedComponentsGreedy(NULL, conHeap, g, lastRemovedVertex);
        if (nbComp > 1) {       //if (trace) { printf("components [%d]   ", nbComp); for (int i = 0; i < nbComp; i ++) printf("%d,", compSizes[i]); printf("\n");}
            return greedyDecomposeComponents(iComp, nbComp, conHeap, depth, g, hmax);
        }
    }

    if (stopSearch) return NULL;

    // Build a tree whose root is a node with highest degree

    int vertex = vertexWithManyConnections(conHeap, MAX_CONNECTED_BEST_AT_RANDOM);

    if (trace) printf("remove %d (nbN=%d)\n", vertex, nbN4Con[vertex]);

    //if (nbInListWithThisValue(maxNbN4Con, conHeap->val, conHeap->n, nbN4Con) != nbMax4Con) { printf("AAA max=%d  %d et non pas %d\n",maxNbN4Con, nbInListWithThisValue(maxNbN4Con, conHeap->val, conHeap->n, nbN4Con), nbMax4Con); exit(0); }

    heapRemove(vertex, conHeap);

    if (nbN4Con[vertex] == maxNbN4Con) {
        if (--nbMax4Con == 0) updateNbMaxConInHeap(conHeap);
    }

    if (nbN4Con[vertex] == 0) nbUnconnectedVertices --;

    //printf("1. max=%d nbMax=%d  -->  ", maxNbN4Con, nbMax4Con);
    //if (nbInListWithThisValue(maxNbN4Con, conHeap->val, conHeap->n, nbN4Con) != nbMax4Con) { printf("BBB max=%d  %d et non pas %d\n",maxNbN4Con, nbInListWithThisValue(maxNbN4Con, conHeap->val, conHeap->n, nbN4Con), nbMax4Con); exit(0); }

    updateHeapNbNAfterRemoval(vertex, conHeap, g);

    if (nbMax4Con == 0) updateNbMaxConInHeap(conHeap);

    //printf("2. max=%d nbMax=%d\n", maxNbN4Con, nbMax4Con);
    //if (nbInListWithThisValue(maxNbN4Con, conHeap->val, conHeap->n, nbN4Con) != nbMax4Con) { printf("CCC max=%d  %d et non pas %d\n",maxNbN4Con, nbInListWithThisValue(maxNbN4Con, conHeap->val, conHeap->n, nbN4Con), nbMax4Con); exit(0); }

    Node root = theNodes[vertex];
    resetNode(root);

    lastRemovedVertex = vertex;

    Node children = greedyDecompose(conHeap, depth+1, g, hmax, 0);

    if (children == NULL)
        return NULL;

    if (children->next == NULL)
        addChild(children, root);
    else {
        while (children != NULL) {
            Node child = children;
            children = children->next;
            addChild(child, root);
        }
    }

    //verifyTree(root, 0);

    return root;
}



Node greedyDecomposeComponents(int iComp[], int nbComp, Heap conHeap, int depth, Graph g, int hmax) {
// Normally there is no component of size 1
    nbCallsDecomposeComponents ++;
    int numCall = nbCallsDecomposeComponents;

    int *sizes = malloc(nbComp*sizeof(int)); // To memorize sizes after first explorations

    sortListByComponent(conHeap->val, conHeap->n, nbComp, sizes);

    if (trace) {
        printf(" :: split call %d : %d components : ", nbCallsDecomposeComponents, nbComp);
        for (int i = 0; i < nbComp; i ++) printf("%d ", sizes[i]);
        printf("\n");
    }

    // The number of neighbors of u in S[] is the number of neighbors of u in its component
    // No need to update.
    Node list = NULL, node;
    int nb = 0;
    int *val = conHeap->val;
    for (int i = 0; i < nbComp; i ++) {

        if (stopSearch) { conHeap->val = val; free(sizes); return NULL; }

        conHeap->n = sizes[i];
        conHeap->val = val+nb;
        nb += sizes[i];

        if (i == nbComp-1) free(sizes);

        lastRemovedVertex = NONE;

        if (conHeap->n <= SIZE_SMALL_TREE) {
            node = makeSmallTree(conHeap->val, conHeap->n, g);
            heapRemoveAll(conHeap);
        }
        else if (rebuildConHeap(conHeap)) {
            // clique
            node = makeSingleBranch(conHeap->val, conHeap->n, NULL, g);
            heapRemoveAll(conHeap);
        }
        else
            node = greedyDecompose(conHeap, depth, g, hmax, 1); // the component is connected

        if (0 && ! verifyDecomposition(node, g)) { printf("Error: decCompCall %d  depth=%d\n", numCall, depth); exit(0); }

        if (node == NULL) { conHeap->val = val; if (i != nbComp-1) free(sizes); return NULL; }

        node->next = list;
        if (list != NULL) list->prev = node;
        list = node;
    }

    conHeap->val = val;
    //free(sizes);

    return list;
}




Node makeListWithUnconnectedVertices(Heap heap, Graph g, Node *last) {
    // unconnected vertices are at the end of the heap
    // They are just removed from the heap, the degree of their already removed neighbors is not updated
    int firstUV = heap->n - nbUnconnectedVertices;
    Node node = NULL;
    *last = NULL;

    for (int i = heap->n - 1; i >= firstUV ; i --) {
        int u = heap->val[i];
        assert (nbN4Con[u] == 0);
        //heapRemove(heap->val[i], heap);
        heap->ind[u] = NONE;
        Node p = theNodes[u];
        resetNode(p);
        if (*last == NULL) *last = p;
        p->next = node;
        node = p;
    }
    heap->n = firstUV;
    return node;
}


Node makeSmallTree(int *S, int n, Graph g) {

    if (n == 0) return NULL;

    Node u = theNodes[S[0]];
    resetNode(u);

    if (n == 1) return u;

    Node v = theNodes[S[1]];
    resetNode(v);

    if (n == 2) {
        if (areNeighbours(u->vertex, v->vertex, g)) {
            addChild(v, u);
            return u;
        }
        //connectSiblings(u, v);
        u->next = v;
        return u;
    }

    Node w = theNodes[S[2]];
    resetNode(w);

    int status = 0;
    if (areNeighbours(u->vertex, v->vertex, g))
        status += 1;
    if (areNeighbours(u->vertex, w->vertex, g))
        status += 2;
    if (areNeighbours(v->vertex, w->vertex, g))
        status += 4;


    if (status == 7) { // Clique -> branch
        addChild(w, v);
        addChild(v, u);
        return u;
    }

    if (status == 6) { // (u,w) (v,w) -> w(u,v)
        addChild(u, w);
        addChild(v, w);
        return w;
    }

    if (status == 4) { // (v,w) -> w(v),u
        addChild(v, w);
        w->next = u; // addChild(u, w);
        return w;
    }

    if (status == 2) { // (u,w) -> w(u),v
        addChild(u, w);
        w->next = v; // addChild(v, w)
        //connectSiblings(w, v);
        return w;
    }

    if (status == 5) { // (u,v) (v,w) -> v(u,w)
        addChild(w, v);
        addChild(u, v);
        return v;
    }

    if (status == 1) { // (u,v) -> v(u),w
        addChild(u, v);
        v->next = w; // addChild(w, v);
        //connectSiblings(v, w);
        return v;
    }

    if (status == 3) { // (u,v) (u,w) -> u(v,w)
        addChild(v, u);
        addChild(w, u);
        return u;
    }

    //addChild(v, u);
    //addChild(w, u);
    //return u;

    // (status == 0) :: (u,v) (u,w) -> u(v,w)
    u->next = v;
    v->next = w;
    //connect3Siblings(u, v, w);

    return u;
}





//
// Utils
//



void allocNodes(Graph g) {
    theNodes = malloc(g->n*sizeof(Node));
    for (int i = 0; i < g->n; i ++)
        theNodes[i] = newNode(i, g);
}

void allocSets(Graph g, int max) {
    theSets = calloc((size_t) (g->n), sizeof(SET));
    for (int i = 0; i < max; i ++)
        theSets[i] = allocSet(g->n + 100);
}


void allocComponents(Graph g, int max) {
    components = calloc((size_t) (g->n), sizeof(int *));
    componentsOf = calloc((size_t) (g->n), sizeof(int *));
    for (int i = 0; i < max; i ++) {
        components[i] = malloc((size_t) (g->n) * sizeof(int));
        componentsOf[i] = malloc((size_t) (g->n) * sizeof(int));
    }
}



// Alloc new sets if necessary
void reAllocSets(int size, Graph g) {

    for (int i = nbAllocatedSets; i < nbAllocatedSets+size; i++) {
        theSets[i] = allocSet(g->n);
    }
    nbAllocatedSets += size;
}


void reAllocComponents(Graph g, int size) {
    for (int i = nbAllocatedComponents; i < nbAllocatedComponents+size; i ++) {
        components[i] = malloc((size_t) (g->n) * sizeof(int));
        componentsOf[i] = malloc((size_t) (g->n) * sizeof(int));
    }
    nbAllocatedComponents += size;
}




void PACEOutput(FILE *file, int height, int fathers[], Graph g) {

    if (file == NULL) file = stdout;

    fprintf(file, "%d\n", height);

    for (int i = 0; i < g->n; i ++) {
        fprintf(file, "%d\n", fathers[i]);
    }
}


void saveToTable(int T[], Graph g) {
    for (int i = 0; i < g->n; i ++) {
        T[i] = (theNodes[i]->father == NULL) ? 0 : (theNodes[i]->father->vertex+1);
    }
}

Node buildTreeFromTable(int T[], Graph g) {
    Node root = NULL;

    for (int i = 0; i < g->n; i ++)
        theNodes[i]->father = theNodes[i]->next = theNodes[i]->fbs = NULL;

    for (int i = 0; i < g->n; i ++) {
        Node father = (T[i] == 0) ? NULL : theNodes[T[i]-1];
        theNodes[i]->father = father;
        if (father != NULL) {
            theNodes[i]->next = father->fbs;
            father->fbs = theNodes[i];
        } else
            root = theNodes[i];
    }
    return root;
}


// Verify that the decomposition tree is correct for the edges of g
// Note : tree may be a partial decomposition of g (does not contain all the vertices of g)
int exploreAndSetFathers(Node p, int F[]) {
    Node q = p->fbs;
    if (F[p->vertex] != NONE) { printf("vertex %d occurs twice !\n", p->vertex); return 0; }
    while (q != NULL) {
        if ( ! exploreAndSetFathers(q, F)) return 0;
        F[q->vertex] = p->vertex;
        q = q->next;
    }
    return 1;
}

int areInSameBranch(int u, int v, int F[]) {
    int i = u;
    while ((i != NONE) && (i != v))
        i = F[i];
    if (i == v) return 1;
    i = v;
    while ((i != NONE) && (i != u))
        i = F[i];
    return (i == u);
}

int verifyDecomposition(Node tree, Graph g) {
    if (sizeTree(tree) != g->n) { printf("Error size %d nodes\n", sizeTree(tree)); return 0; }
    int F[g->n];
    for (int i = 0; i < g->n; i ++)
        F[i] = NONE;
    if ( ! exploreAndSetFathers(tree, F)) return 0;
    int nbNONE = 0;
    for (int u = 0; u < g->n; u ++) if (F[u] == NONE) nbNONE ++;
    if (nbNONE != 1) { printf("Error : %d nodes not in tree\n", nbNONE-1); return 0; }
    for (int u = 0; u < g->n; u ++) {
        if (F[u] != NONE) {
            for (int *p = g->lists[u]; *p != NONE; p ++) {
                if (F[*p] != NONE) {
                    // g contains edge (u,q->val) and u and q->val are in the tree
                    if ( ! areInSameBranch(u, *p, F)) {
                        printf("Error edge (%d,%d)\n", u, *p);
                        return 0;
                    }
                }
            }
        }
#ifdef NOTDEF
        if (F[u] != NONE) {
            LIST q = g->adj[u];
            while (q != NULL) {
                if ((u == 13280) && (q->val == 16624)) printf("Yahou\n");
                if ((u == 16624) && (q->val == 13280)) printf("Yahou\n");
                if (F[q->val] != NONE) {
                    // g contains edge (u,q->val) and u and q->val are in the tree
                    if ( ! areInSameBranch(u, q->val, F)) {
                        printf("Error edge (%d,%d)\n", u, q->val);
                        return 0;
                    }
                }
                q = q->suiv;
            }
        }
#endif
    }
    return 1;
}



void printStatsOnCriticalNodes(Node root, int *nbCriticalNodes) {
    makeStatsCriticalNodes(root, nbCriticalNodes);
    printf("[%d] %d\n", 0, nbCriticalNodes[0]);
    for (int i = 1; i < root->height; i++) if (nbCriticalNodes[i] != nbCriticalNodes[i-1]) printf("[%d] %d\n", i, nbCriticalNodes[i]);
    printf("[%d] %d\n", root->height-1, nbCriticalNodes[root->height-1]);
}