def glouton(k, graph, verbose, log):
    if log:
        print "Starting basic Glouton.."
        startTime = time.time()
    # initialisation
    initGraph = graph.copy()
    niList = []
    markingList = [] 
    potentialNodesList = []
    partitionList = []
    # initialisation du sommet de départ
    s0 = nodeWithLessNeighbor(graph, markingList)
    markingList.append(s0)
    potentialNodesList = setPotentialNodesList(graph,s0, markingList, potentialNodesList)    
    if verbose :
        print "Sommet de depart :", s0    
        print "Potential Nodes : ", potentialNodesList
    # initialisation de i : numéro de partition courante
    i = 1
    while i < k:
        # Calcul de ni : nombre de sommets pour la prochaine partition
        niList = calculateSizeListSubGraph(k, i, niList, initGraph)
        ni = niList[i-1]
        # j : nombre de sommets actuellement dans la partition courante
        j=1 	# on compte le sommet de départ
        while potentialNodesList != [] and j < ni:
            m = potentialNodesList[-1][0]
            markingList.append(m)
            potentialNodesList = setPotentialNodesList(graph,m, markingList, potentialNodesList)
            j = j+1
        # Affectation de la partition créée à la liste de partitions
        partitionList.append(markingList)
        # Réinitialisation des structures
        # Restructuration du graphe : suppression des sommets marqués
        graph.remove_nodes_from(markingList)
        markingList = [] 
        potentialNodesList = []
        i = i+1
        # Initialisation de la prochaine partition
        if i != k :
            s0 = nodeWithLessNeighbor(graph, markingList)
            markingList.append(s0)
            potentialNodesList = setPotentialNodesList(graph,s0, markingList, potentialNodesList)
    # partitionList ← sommets restants 
    partitionList.append(graph.nodes())
    graph = initGraph
    # probleme : si on ne return pas le graphe, ici il est bien égal
    # à tout le graphe mais sans le return il vaut une partition...
    
    stopTime = time.time()
    if verbose:
        print "Partition finale 1:", partitionList[0]
        print "Partition finale 2:", partitionList[1]
    if log:
        print "Optimum trouve:", objf.calculateCut(partitionList[0],partitionList[1],graph)
        print "Execution Time :", stopTime-startTime    
    
    return partitionList, graph
def calculateFictifGain(s1,s2,P1,P2,graph):
    # échange des sommets s1 et s2
    newP1 = list(P1)
    newP1.remove(s1)
    newP1.append(s2)
    newP2 = list(P2)
    newP2.remove(s2)
    newP2.append(s1)
    # newG : gain si on permute s1 et s2
    newG = objf.calculateCut(newP1, newP2, graph)
    #newG = objf.calculateGainNodesAB(s1, s2, newP1, newP2, graph)
    return newG
def calculateFictifGainUnique(s,P1,P2,graph):
    # déplacement du sommet s de P1 vers P2
    newP1 = list(P1)
    newP2 = list(P2)
    if s in newP1:
        newP1.remove(s)
        newP2.append(s)
    else :
        newP2.remove(s)
        newP1.append(s)
    # newG : gain si on déplace s de P1 vers P2
    newG = objf.calculateCut(newP1, newP2, graph)
    return newG
def kl(graph,verbose):
    print "Starting KL version 2.."
    startTime = time.time()
    ## Initialisation ##
    # Liste de partitions
    partitionsList = []
    # Initialisation des listes de sommets à étudier 
    # (une par partition)
    remainingNodesS1 = []
    remainingNodesS2 = []    
    
    ## Déroulement de l'algo ##    
    # bipartition
    partitionsList, graph = g.gloutonWithCut(2,graph,False,False)
    if verbose:
        print "Partition Init 1: ", partitionsList[0]
        print "Partition Init 2: ", partitionsList[1]

    deltaTime = -time.time()
    
    ratio = 20    
    
    # Mise a jour des listes des sommets à étudier
    remainingNodesS1 = list(partitionsList[0])[len(partitionsList[0])*(100-ratio)/100:]
    remainingNodesS2 = list(partitionsList[1])[len(partitionsList[1])*(100-ratio)/100:]

    # calcul du gain global initial
    globalMin = objf.calculateCut(remainingNodesS1,remainingNodesS2,graph)
    #  Initialisation du gain fictif
    GainGlobalFictif = globalMin
    if verbose:
        print "Global Min Initial:", globalMin
        print ""
        print "Recherche de sommets uniques à changer..."   
   
    nodesList = nx.nodes(graph)
    iterations = 100
    while iterations > 0 :
        improvement = False
        localMin = sys.maxint
        
        for node in nodesList:
            gain = calculateFictifGainUnique(node, partitionsList[0], partitionsList[1], graph)
            
            # recherche du meilleur gain local            
            if gain < localMin  and gain > 0 :
                improvement = True
                localMin = gain
                s = node
                GainGlobalFictif = globalMin - gain
                if verbose:
                    print "Nouveau gain local:", localMin, "Node: ", node
                    print "Gain Global Fictif =", GainGlobalFictif

        if improvement :
            nodesList.remove(s)        
        if (GainGlobalFictif < globalMin):
            globalMin = GainGlobalFictif
            if verbose:
                print "Nouveau gain global:", globalMin, "Node:", s
                print "Le noeud",s,"a été échangé de partition"
            partitionsList = switchNodesUnique(partitionsList,s)
        
        iterations = iterations - 1
 
    stopTime = time.time()
    deltaTime += time.time()
    print "Temps d'exécution total:", stopTime-startTime
    print "Temps d'exécution KL:", deltaTime
    if verbose:
        print "Partition Finale 1:", partitionsList[0]
        print "Partition Finale 2:", partitionsList[1]
    print "Global cut:", objf.calculateCut(partitionsList[0], partitionsList[1], graph)
def kl(graph,verbose):
    print "Starting KL version 3.."
    startTime = time.time()
    ## Initialisation ##

    # Sert à mémoriser les sommets échangés
    exchangedNodes = []
    # Liste de partitions
    partitionsList = []
    # Initialisation des listes de sommets à étudier 
    # (une par partition)
    remainingNodesS1 = []
    remainingNodesS2 = []
    
    #  Initialisation des listes de gain fictif
    GFictif = 0
    
    
    ## Déroulement de l'algo ##    
    # bipartition
    partitionsList, graph = g.glouton(2,graph,False,False)
    
    # Mise a jour des listes des sommets à étudier
    remainingNodesS1 = list(partitionsList[0])
    remainingNodesS2 = list(partitionsList[1])
    if verbose: 
        print "Partition Init 1: ", remainingNodesS1
        print "Partition Init 2: ", remainingNodesS2
    
    optimumEqu = False
    # boucle principale
    deltaTime = -time.time()
    
    # calcul du gain global initial
    globalMin = objf.calculateCut(remainingNodesS1,remainingNodesS2,graph)
    if verbose: 
        print "Global Min Initial:", globalMin

    while remainingNodesS1 != [] and remainingNodesS2 != [] and not optimumEqu:
        improvement = False
        P1 = list(partitionsList[0])
        P2 = list(partitionsList[1])
    
        localMin = sys.maxint

        # On inverse l'ordre de P1 et de P2 car les derniers sommets ajoutés
        # sont les plus susceptibles à ressortir en premier
        for nodeA in reversed(remainingNodesS1):
            # s1 : sommet candidat de S1 pour un échange
            s1 = nodeA

            for nodeB in reversed(remainingNodesS2):
                # on cherche le meilleur candidat pour un échange avec s1
                # Calcul du gain lié à l'échange de a et b : G(a,b)
                #gainAB = calculateFictifGain(s1, nodeB, P1, P2, graph)
                gainAB = objf.calculateGainNodesAB(s1, nodeB, P1, P2, graph)

                if gainAB + globalMin < localMin:
                    localMin = gainAB + globalMin
                    # s2 : sommet candidat de S2 pour un échange avec s1
                    s2 = nodeB
                    if verbose: 
                        print "Nouveau gain local:", localMin, "Nodes: ", s1, s2
                    #GFictif = gainAB
                    GFictif = localMin
                
                    
            # Mise a jour du gain global
            if (GFictif < globalMin):
                improvement = True
                globalMin = GFictif
                if verbose: 
                    print "Nouveau gain global:", globalMin, "Nodes:", s1, s2
                exchangedNodes.append(s1)
                exchangedNodes.append(s2)
                
                # P ← échanger les sommets s1 et s2
                partitionsList = switchNodes(partitionsList,s1,s2)                
                if verbose: 
                    print "Les noeuds",s1,"et",s2, "ont été échangés"                
                    print "Partition 1:", partitionsList[0]
                    print "Partition 2:", partitionsList[1]
                
                # Mise a jour des listes des sommets à étudier
                remainingNodesS1 = setRemainingNodes(partitionsList[0],exchangedNodes)
                remainingNodesS2 = setRemainingNodes(partitionsList[1],exchangedNodes)

  
        if (improvement == False):
            if verbose: 
                print "Optimum équilibré trouvé:", objf.calculateCut(partitionsList[0], partitionsList[1], graph)
            optimumEqu = True

    if verbose:
        print ""
        print "Recherche de sommets uniques à changer..."   
   
    while exchangedNodes != [] :
        localMin = sys.maxint
        
        for node in exchangedNodes:
            gain = calculateFictifGainUnique(node, partitionsList[0], partitionsList[1], graph)

            # recherche du meilleur gain local            
            if gain < localMin:
                localMin = gain
                if verbose:
                    print "Nouveau gain local:", localMin, "Node: ", node
                s = node
                GFictif = gain

        exchangedNodes.remove(s)        
        if (GFictif < globalMin):
            globalMin = GFictif
            if verbose: 
                print "Nouveau gain global:", globalMin, "Node:", s
                print "Le noeud",s,"a été échangé de partition"
            partitionsList = switchNodesUnique(partitionsList,s)
 
    stopTime = time.time()
    deltaTime += time.time()
    print "Temps d'exécution total:", stopTime-startTime
    print "Temps d'exécution KL:", deltaTime
    if verbose:
        print "Partition Finale 1:", partitionsList[0]
        print "Partition Finale 2:", partitionsList[1]
    print "Global cut:", objf.calculateCut(partitionsList[0], partitionsList[1], graph)     
def recuitSimule(graph, iterations, tempI, remonteeMax,verbose):
    print "Starting Recuit Simule with", iterations, "iterations,", tempI, "as initial temperature and", remonteeMax, "as max upgoing movements"

    deltaTime0 = -time.time()    
    
    ## Initialisation ##
    # Liste de partitions
    partitionsList = [] 
    
    # Appel du Glouton
    partitionsList, graph = g.gloutonWithCut(2,graph,False,False)
    P1Final = list(partitionsList[0])
    P2Final = list(partitionsList[1])
    if verbose :
        print "Partition P1 init :", P1Final
        print "Partition P2 init :", P2Final

    print "Number of nodes in graph:", nx.number_of_nodes(graph)
    # calcul du gain global initial
    globalMin = objf.calculateCut(partitionsList[0],partitionsList[1],graph)
    
    #  Initialisation du gain fictif
    GainGlobalFictif = globalMin
    print "Global Min Initial:", globalMin

    # Temps de départ
    deltaTime = -time.time()
    
    # Nombre d'iteration de l'algorithme
    iterationsC = 0

    # Nombre courant de remontée de temperature consecutives
    remontee = 0
    
    # Temperature courante
    tempC = tempI
    
    list_pourcentage = [0,10,20,30,40,50,60,70,80,90]
    
    nodesOnLimit,neighborsList = neighbors(partitionsList[0],graph)
    
    # Boucle principale
    while iterationsC < iterations :
        pourcentage = (iterationsC*100)/iterations
        if pourcentage in list_pourcentage:
            del(list_pourcentage[0])
            print '------------------',pourcentage, "% effectués", '------------------'            
            
        change = False

        # Candidat aleatoire de la partition P1
        nodeA = selectNode(nodesOnLimit)
        # Candidat aleatoire de la partition P2
        nodeB = selectNode(neighborsList)

        # Calcul du gain lié à l'échange de nodeA et nodeB : G(nodeA,nodeB)
        gainAB = calculateFictifGain(nodeA, nodeB, partitionsList[0], partitionsList[1], graph)
        
        if verbose:
            print "Gain entre",nodeA,"et",nodeB,"=",gainAB
        # Amelioration solution locale
        if gainAB >= 0 :
            change = True
            remontee = 0
            if verbose :
                print "Descente en temperature avec les noeuds", nodeA, "et", nodeB                
                
        else:
            # Acceptation de la solution dégradante 
            if random() < exp(gainAB/tempC) and remontee < remonteeMax :
                change = True
                remontee +=1
                if verbose :
                    print "Remontee en temperature avec les noeuds", nodeA, "et", nodeB                

        # Changement a effectuer                                        
        if change :
            # On echange les sommets nodeA et nodeB
            partitionsList = switchNodes(partitionsList,nodeA,nodeB)
            updateNeighbors(nodeA,nodeB,nodesOnLimit,neighborsList,partitionsList[0],partitionsList[1],graph)
            
            GainGlobalFictif = GainGlobalFictif - gainAB
            if verbose:
                print "Les noeuds",nodeA,"et",nodeB, "ont été échangés"
                print "Gain fictif =", GainGlobalFictif
            
            if GainGlobalFictif < globalMin and GainGlobalFictif > 0:
                globalMin = GainGlobalFictif
                print "Update globalMin =",globalMin
                P1Final = list(partitionsList[0])
                P2Final = list(partitionsList[1])
        
        # cas où la remontee de temperature n'a que fait dégrader la solution
        if remontee == remonteeMax and not change :
            if verbose:
                print "Degradation trop élevée --> récupération des anciennes partitions"
            # récupération de la meilleure partition
            partitionsList[0] = list(P1Final)
            partitionsList[1] = list(P2Final)
            nodesOnLimit,neighborsList = neighbors(partitionsList[0],graph)
        
        addRandomNode(nodesOnLimit, partitionsList[0])
        addRandomNode(neighborsList, partitionsList[1])        
        
        iterationsC +=1
        tempC = 0.99*tempC
    

    deltaTime += time.time()
    deltaTime0 += time.time()
    if verbose:
        print "Partition Finale 1:", P1Final
        print "Partition Finale 2:", P2Final    
    print "Temps d'exécution recuit-simule:", deltaTime
    print "Temps d'exécution global:", deltaTime0
    print "Global cut:", globalMin