def FisherJaikumar_Routing(graph, clusterAssignment, k_clusters, saveFolder):

    routes = []
    demand = graph.getDemand()
    capacity = graph.getCapacity()

    for k in range(len(k_clusters)):
        cluster = []
        for i in range(len(clusterAssignment)):
            if (clusterAssignment[i] == k):
                cluster.append(i + 1)

        appoRoute = Route(capacity)
        appoRoute.addCustomer(0, 0, False)

        while (len(cluster) > 0):
            prevnode = appoRoute.getCustomers()[len(appoRoute.getCustomers()) -
                                                1]
            distPrevNode = graph.getValue(prevnode, [c for c in cluster])
            nearestN = cluster[np.argmin(distPrevNode)]
            if nearestN not in appoRoute.getCustomers():
                appoRoute.addCustomer(nearestN, demand[nearestN], False)
                cluster.remove(nearestN)

        appoRoute.addCustomer(0, 0, False)
        appoRoute.printRoute("Route cluster:" + str(k))
        routes.append(appoRoute)

    return routes
def LocalSearch_FlippingPath(route: Route, graph: cvrpGraph, candidate1,
                             candidate2):

    node1 = candidate1 % len(route.getCustomers())
    node2 = candidate2 % len(route.getCustomers())
    if (node2 != 0):
        firstCandidate = route.getCustomers()[node1]
        prevfc = route.getCustomers()[node1 - 1]
        nextfc = route.getCustomers()[node1 + 1]

        secondCandidate = route.getCustomers()[node2]
        prevsc = route.getCustomers()[node2 - 1]
        nextsc = route.getCustomers()[node2 + 1]

        cost1 = graph.getValue(prevfc, firstCandidate) + graph.getValue(
            firstCandidate, nextfc)
        cost2 = graph.getValue(prevsc, secondCandidate) + graph.getValue(
            secondCandidate, nextsc)

        route.setCost(route.getCost() - (cost1 + cost2))
        a = route.getCustomers().index(firstCandidate)
        b = route.getCustomers().index(secondCandidate)
        route.getCustomers()[a] = secondCandidate
        route.getCustomers()[b] = firstCandidate

        cost3 = graph.getValue(prevfc, secondCandidate) + graph.getValue(
            secondCandidate, nextfc)
        cost4 = graph.getValue(prevsc, firstCandidate) + graph.getValue(
            firstCandidate, nextsc)

        route.setCost(route.getCost() + (cost3 + cost4))

    return route
def Crossover(winner1,
              winner2,
              graph: cvrpGraph,
              tabuSearch: bool = False,
              tabuLister: list = []):

    demand = graph.getDemand()
    capacity = graph.getCapacity()
    tabuList = tabuLister
    winner1Sequence = []
    winner2Sequence = []

    child1 = []
    child2 = []
    solution1 = []
    solution2 = []

    winner1Sequence += [p.getCustomers() for p in winner1]
    winner1Sequence = [y for x in winner1Sequence for y in x if y != 0]
    winner2Sequence += [p.getCustomers() for p in winner2]
    winner2Sequence = [y for x in winner2Sequence for y in x if y != 0]

    crossover_point1 = np.random.randint(int(len(winner1Sequence) / 4),
                                         (int(len(winner1Sequence) / 2)))
    crossover_point2 = np.random.randint(
        int(len(winner1Sequence) / 2),
        (int(len(winner1Sequence) / 2) + int(len(winner1Sequence) / 4)))

    #Form child one List
    for node in (winner1Sequence[crossover_point1:crossover_point2]):
        if node not in child1 and node != 0:
            child1.append(node)

    for node in (winner2Sequence[crossover_point2:]):
        if node not in child1 and node != 0:
            child1.append(node)

    for node in (winner2Sequence[:crossover_point2]):
        if node not in child1 and node != 0:
            child1.insert(0, node)

    #Build and check the child one Route

    i = 0
    while (i < len(child1)):
        route = Route(capacity)
        cost = 0
        route.setCost(0)
        route.addCustomer(0, demand[0], False)
        for node in child1[i:]:
            i = i + 1
            if (route.addCustomer(node, demand[node], False) < 0):
                if (route.checkCustomer(node) == -1):
                    route.addCustomer(0, demand[0], False)
                    i = i - 1
                break
            if (len(child1) == i):
                route.addCustomer(0, demand[0], False)
        for n in range(len(route.getCustomers()) - 1):
            cost += graph.getValue(route.getCustomers()[n],
                                   route.getCustomers()[n + 1])
        route.setCost(cost)
        solution1.append(route)

#Form child two  List
    for node2 in (winner2Sequence[crossover_point1:crossover_point2]):
        if node2 not in child2 and node2 != 0:
            child2.append(node2)

    for node2 in (winner1Sequence[crossover_point2:]):
        if node2 not in child2 and node2 != 0:
            child2.append(node2)

    for node2 in (winner1Sequence[:crossover_point2]):
        if node2 not in child2 and node2 != 0:
            child2.append(node2)

    #Build and check the child one Route

    i = 0
    while (i < len(child2)):
        route2 = Route(capacity)
        cost = 0
        route2.setCost(0)
        route2.addCustomer(0, demand[0], True)
        for node in child2[i:]:
            i = i + 1
            if (route2.addCustomer(node, demand[node], False) < 0):
                route2.addCustomer(0, demand[0], False)
                i = i - 1
                break
        if (len(child2) == i):
            route2.addCustomer(0, demand[0], False)
        for n in range(len(route2.getCustomers()) - 1):
            cost += graph.getValue(route2.getCustomers()[n],
                                   route2.getCustomers()[n + 1])
        route2.setCost(cost)
        solution2.append(route2)

    f1 = sum([c.getCost() for c in solution1])
    f2 = sum([c.getCost() for c in solution2])

    tabuState = []
    if (f1 > f2):
        tabuState = f1
    else:
        tabuState = f2

    if (tabuSearch == True and int(tabuState) in tabuList):
        #print("TABULISTED ==> " +str(int(tabuState)))
        return Crossover(winner1, winner2, graph, tabuList)
    else:
        tabuList.append(int(tabuState))

    if (f1 < f2):
        return solution1, tabuList, f1
    else:
        return solution2, tabuList, f2
def ClusterFirst_RouteSecond(graph, saveFolder):

    finalRoutes = []
    capacity = graph.getCapacity()
    demand = graph.getDemand()
    dimension = graph.getDimension()
    dist = [np.inf for i in range(dimension)]
    nodeQueue = []
    auxGraph = [(i, Route(capacity)) for i in range(dimension)]

    #Source Node has 0 cost, in this case route Depot - 1- Depot
    dist[0] = 0

    #nodeQueue = [(c+1,dist[c]) for c in range(len(dist))]
    route = Route(capacity)
    route.addCustomer(0, demand[0], False)
    route.addCustomer(1, demand[1], False)
    route.addCustomer(0, demand[0], False)
    route.setCost(graph.getValue(0, 1) + graph.getValue(1, 0))

    nodeQueue = [(1, route.getCost(), route)]
    node = 0
    nodeQueue.sort(key=lambda x: x[1], reverse=True)

    while len(nodeQueue) > 0:
        #Give the node in the auxiliary graph that has low cost
        nodeToexpand = nodeQueue.pop()
        node = nodeToexpand[0]
        cost = nodeToexpand[1]
        #Give the corresponding route, that contain node
        #auxGraph.pop()
        #Create each child of the node in the auxiliary graph
        for j in range(1, dimension):
            #j child
            if (j >= node):
                newRoute = Route(capacity)
                newRoute.setCost(0)
                newRoute.addCustomer(0, demand[0], False)
                control = -3
                #for each child that not exceed the truck capacity
                for i in range(node, j + 1):
                    #The not is not in the route and it not exceed the truck capacity
                    #Add Node
                    if (newRoute.checkCustomer(i) == -1):
                        control = newRoute.addCustomer(i, demand[i], False)
                        #Update Cost
                        if (control > 0):
                            newRoute.setCost(
                                newRoute.getCost() + graph.getValue(
                                    newRoute.getCustomers()[node - i], i))
                        else:
                            break
                #Close the route
                if (control > 0):
                    newRoute.setCost(newRoute.getCost() + graph.getValue(j, 0))
                    newRoute.addCustomer(0, demand[0], False)
                    if (dist[j] > newRoute.getCost() + cost):
                        dist[j] = newRoute.getCost() + cost
                        nodeQueue.append((j + 1, dist[j], newRoute))
                        auxGraph[j] = (node - 1, newRoute)
                        nodeQueue.sort(key=lambda x: x[1], reverse=True)
                else:
                    break

    u = len(auxGraph) - 1
    while (u != 0):
        node = auxGraph[u]
        u = node[0]
        finalRoutes.append(node[1])

    return finalRoutes
def Sequential_CW(routes, savings, graph: cvrpGraph):
    capacity = graph.getCapacity()
    demand = graph.getDemand()

    toDo, checked, _ = SearchaAndCompleteSequence(routes, graph, True)
    #no routes have been created yet
    while (SearchaAndCompleteSequence(routes, graph) == True
           and len(savings) > 0):
        routeSelected = Route(capacity)
        printable = savings.copy()
        k = 0
        saveNow = savings[0]
        l = saveNow[1]
        m = saveNow[2]
        if ((l not in checked) and (m not in checked)
                and (len(routeSelected.getCustomers()) == 0)):
            #No one served i and j so this route will be teh first
            routeSelected.addCustomer(l, demand[l], True)
            routeSelected.addCustomer(m, demand[m], False)
            savings.remove(saveNow)

            while k < len(savings):
                save = savings[k]
                k = k + 1
                i = save[1]
                j = save[2]
                customerI = -1
                customerJ = -1
                toprint = save
                if (toDo == True):
                    #Check if someone alraedy served i and j
                    if ((i not in checked) and (j not in checked)):
                        if (-2 < customerI < 0
                                and len(routeSelected.getCustomers()) > 0):
                            customerI = routeSelected.checkCustomer(i)

                        if (-2 < customerJ < 0
                                and len(routeSelected.getCustomers()) > 0):
                            customerJ = routeSelected.checkCustomer(j)

                        # case: i and j have not been served and they are not route first entry
                        #customer i is served from this route but j is not.
                        if (customerI >= 0 and customerJ == -1):
                            if (customerI == 0):
                                control = routeSelected.addCustomer(
                                    j, demand[j], True)
                                if (control != -1):
                                    savings.remove(save)
                                    k = 0
                            else:
                                control = routeSelected.addCustomer(
                                    j, demand[j], False)
                                if (control != -1):
                                    savings.remove(save)
                                    k = 0
                        #customer j is served from this route but i is not.
                        if (customerI == -1 and customerJ >= 0):
                            if (customerJ == 0):
                                control = routeSelected.addCustomer(
                                    i, demand[i], True)
                                if (control != -1):
                                    savings.remove(save)
                                    k = 0
                            else:
                                control = routeSelected.addCustomer(
                                    i, demand[i], False)
                                if (control != -1):
                                    savings.remove(save)
                                    k = 0
                print("Savings number :" + str(printable.index(toprint)))
            routes.append(routeSelected)
            routeCost = 0
            for i in range(len(routeSelected.getCustomers()) - 1):
                routeCost += graph.getValue(
                    routeSelected.getCustomers()[i],
                    routeSelected.getCustomers()[i + 1])
            routeSelected.setCost(routeCost)

            toDo, checked, _ = SearchaAndCompleteSequence(routes, graph, True)
            savings.append((float(
                graph.getValue(i, 0) + graph.getValue(0, j) -
                routeSelected.getCost()), i, j))
            savings.sort(key=lambda x: x[0], reverse=True)

        else:
            savings.remove(saveNow)

    return routes