def main():
    print '[Initialization]: Building Graphs and Links...'

    #read file and build graph and link object by the data in it
    G1 = Graph_builder()
    G1.build_graph_from('G1.txt')
    G2 = Graph_builder()
    G2.build_graph_from('G2.txt')
    L = Link_builder()
    L.build_link_from('L.txt')

    SortedByDegG1 = max(nx.degree(G1.Graph).values()) #max degree for node in G1
    SortedByDegG2 = max(nx.degree(G2.Graph).values()) #max degree for node in G2
    MaxMinDeg = min(SortedByDegG1, SortedByDegG2) #Minimum of the maximum of degree for G1 and G2
    log_D = math.floor(math.log(MaxMinDeg, 2)) #parameter in Korula Algorithm
    max_node_num = G1.max_node + 1 #to avoid 0-based, remains 0 when index = 0
    #N x N matrix (N is the # of nodes in G1 or G2, assume # of nodes in G1 = # of nodes in G2)
    print 'Allocate Matrix for Score of Similarity...'
    matrix = Matrix = [[0 for x in xrange(max_node_num)] for x in xrange(max_node_num)] #initialize all value as 0

    #parameter for matching algorithm
    it = 100 #iteration
    threshold = 1 #
    weight_unit = 1 #similarity unit

    #write file
    f = open('output.txt','w')
    for element in L.content:
        f.write(str(element[0]) + ' ' + str(element[1]) + '\n')

    #count similarity for all pairs at beginning by scaning existing links stored in link object
    print 'Initializing the Scores of Similarity for each pair...'
    for pair in L.content: #links currently exist
        for nb1 in G1.Graph.neighbors(pair[0]): # for each neighbor of node with id = pair[0]
            for nb2 in G2.Graph.neighbors(pair[1]): # for each neighbor of node with id = pair[1]
                matrix[nb1][nb2] += weight_unit #add score of similarity

    print 'Processing Korula Algorithm...'
    #Korula Algorithm
    for i in range(it):
        print '-------------------------'
        print 'Iteraion:' + str(1+i)
        for j in range(int(log_D)): #iterate from logD to 1
            degree = 2**(log_D - j)
            #map reduce
            G1_nodes, G2_nodes = G1.Graph.nodes(), G2.Graph.nodes()
            G1_pool, G2_pool = [], []#nodes that have degree > degree threshold
            size = len(G1_nodes)#G1 and G2 should have the same size in # of nodes

            for idx in range(size):
                #filter the nodes that could satistify the condition we set
                if  not L.outlink_fromG1_hash.has_key(G1_nodes[idx]) and len(G1.Graph.neighbors(G1_nodes[idx])) > degree:
                    G1_pool.append(G1_nodes[idx])
                if  not L.outlink_fromG2_hash.has_key(G2_nodes[idx]) and len(G2.Graph.neighbors(G2_nodes[idx])) > degree:
                    G2_pool.append(G2_nodes[idx])

            #select a best pair and insert to L
            candidate_pairs_from_G1 = []
            for u1 in G1_pool: #for each candidate in G1_pool
                max_value = threshold #set threshold
                candidate_u2 = []
                for u2 in G2_pool: #for each candidate in G2_pool
                    if matrix[u1][u2] >= threshold: #if node's weight >= threshold we set
                        if matrix[u1][u2] == max_value:
                            if len(candidate_u2) == 0:
                                candidate_u2.append(u2)
                            else: #!= 0
                                if (len(G1.Graph.neighbors(u1)) + len(G2.Graph.neighbors(u2))) > (len(G1.Graph.neighbors(u1)) + len(G2.Graph.neighbors(candidate_u2[0]))):
                                    candidate_u2= [] #reset
                                    candidate_u2.append(u2)
                                else:
                                    pass
                        elif matrix[u1][u2] > max_value:
                            max_value = matrix[u1][u2]#update
                            candidate_u2 = []#reset
                            candidate_u2.append(u2)
                        else:
                            pass
                if len(candidate_u2) > 0:
                    for candidate in candidate_u2:
                        candidate_pairs_from_G1.append((u1,candidate))
                else:#all scores of similarity are lower than threshold
                    #no update for candidate_pairs
                    continue

            #Vice versa
            candidate_pairs_from_G2 = []
            for u2 in G2_pool:
                max_value = threshold
                candidate_u1 = []
                for u1 in G1_pool:
                    if matrix[u1][u2] >= threshold:
                        if matrix[u1][u2] == max_value:
                            if len(candidate_u1) == 0:
                                candidate_u1.append(u1)
                            else:
                                if (len(G1.Graph.neighbors(u1)) + len(G2.Graph.neighbors(u2))) > (len(G1.Graph.neighbors(candidate_u1[0])) + len(G2.Graph.neighbors(u2))):
                                    candidate_u1= []
                                    candidate_u1.append(u1)
                                else:
                                    pass
                        elif matrix[u1][u2] > max_value:
                            max_value = matrix[u1][u2]#update
                            candidate_u1 = []#clear
                            candidate_u1.append(u1)
                        else:
                            pass
                if len(candidate_u1) > 0:
                    for candidate in candidate_u1:
                        candidate_pairs_from_G2.append((candidate,u2))
                else:#all scores of similarity are lower than threshold
                    #no update for candidate_pairs
                    continue

            candidate_pairs = []
            for candidate_pair in candidate_pairs_from_G1:
                #node in candidate_pairs_from G1 is the node in G1 with maximum similarity score to u2
                #and u2 is node in G2 with maximum similarity cscore to u1
                if candidate_pair in candidate_pairs_from_G2:
                    candidate_pairs.append(candidate_pair)

            if len(candidate_pairs) == 0:
                candidate_pairs_from_G1.extend(candidate_pairs_from_G2)
                candidate_pairs = candidate_pairs_from_G1

            #Choose best candidate
            best_candidate = []
            if len(candidate_pairs) == 0: #happen in last iteration
                print 'No Candidate Pairs'
                continue

            elif len(candidate_pairs) == 1:
                # if candidate_pairs[0] in L.content:
                #     print 'duplicate'
                #else:
                best_candidate.append(candidate_pairs[0])

            else:#len(candidate_pairs) > 1:
                max_value = threshold #minimum value
                for candidate in candidate_pairs:
                    if matrix[candidate[0]][candidate[1]] > max_value:
                        best_candidate = [] #clear
                        best_candidate.append(candidate)
                        max_value = matrix[candidate[0]][candidate[1]]#update
                        #best_candidate.append(candidate)
                    elif matrix[candidate[0]][candidate[1]] == max_value:
                            best_candidate.append(candidate)
                            #best_candidate.append(candidate)
                    else: # <
                        pass

            for candidate in best_candidate:
                print '(' + str(candidate[0]) + ',' + str(candidate[1]) + ')'
                # if not candidate in Ans.content:
                #     print 'incorrect output'

            #print 'Updating Scores of similarity'
            #update similarity score and mew links
            for candidate in best_candidate:
                L.add_link_from(candidate)
                f.write(str(candidate[0]) + ' ' + str(candidate[1]) + '\n')

                for u1 in G1.Graph.neighbors(candidate[0]):
                    for u2 in G2.Graph.neighbors(candidate[1]):
                        matrix[u1][u2] += weight_unit #add score of similarity

    print 'Total # of new links found:'
    print len(L.content) - L.ori_size
    print 'Done'
    f.close()
def main():
    try:
        response = requests.get(start_q) #sending start-query to the server
    except requests.exceptions.RequestException as e:
        print e
        time.sleep(5)
        exit(1)

    dic = response.json() #catch the contetnt returned in json format
    task, start_node, dest_node = dic['task'], str(dic['problem'][0]), str(dic['problem'][1])
    #task: 1-3 responding to different requirement to reach the destination given(in README file)

    path_str = '' + start_node
    current_node = '' + start_node
    visited_nodes = [start_node]
    dijkstra_path = []
    lock_nodes = {} #positions that can't move anymore (circle, one neighbor only...etc)
    visieted_nodes = {}
    path_list = [] #list for current path from start_node to end_node
    #showing infomation
    print 'Token number: ' + uni
    print 'Find the way from node ' + start_node + ' to ' + dest_node + ' in task ' + str(task)

    #load model packed by pickle
    fn = 'model.pkl'
    with open(fn, 'r') as f:
        train_model = pickle.load(f)

    userID_table = train_model.ID2Coordinate #key:userID; value: coordinate

    #get neighbors of start node
    response = requests.get(friend_list_q + start_node)
    print 'Processing...'
    friend_list = response.json()['neighbors'] #list of neighbor
    #building graph
    gb = Graph_builder(start_node, dest_node, friend_list, userID_table)
    gb.build_graph_from(current_node, friend_list)
    visited_nodes[current_node] = True
    #exploring the social network

    while True:
        print 'current_position = ' + current_node
        #previous_node = '' + current_node
        #if succeed thne commit the path, break loop and report.
        if current_node == dest_node:
            print path_str
            payload = {'path': path_str }
            response = requests.post(commit_path_q ,data=payload)
            result = response.text
            print result
            print 'Succeed :) !!'
            print 'Quesry: ' + str(result['queries'])
            print 'Cost: ' + str(result['cost'])
            print 'Target_distance: ' + str(result['target_distance'])
            break
        #graph update
        if current_node not in visieted_node and current_node not in lock_nodes:
            try:
                response = requests.get(friend_list_q + current_node)
            except requests.exceptions.RequestException as e:
                print e
                time.sleep(10)
                continue

            result = response.json()
            #if error (reach the query limit), breal loop and report.
            if 'error' in result: #reach query limit
                print path_str
                payload = {'path': path_str }
                response = requests.post(commit_path_q ,data=payload)
                result = response.text
                print result
                print 'fail :( !!'
                print 'Step exceeds limit'
                print 'Quesry: ' + str(result['queries'])
                print 'Cost: ' + str(result['cost'])
                print 'Target_distance: ' + str(result['target_distance'])
                break
            else: #exploring
                friend_list = response.json()['neighbors']
                gb.build_graph_from(current_node, friend_list)
                visited_nodes[current_node] = True

        #stop_list: can't extend anymore, roll back
        if (current_node != start_node and len(gb.Graph.neighbors(current_node)) <= 1) or DetectBlockOrCircle(current_node, gb.Graph, visited_nodes, lock_nodes):
            print 'block point!!'
            lock_nodes[path_list.pop()] = True #add new lock to the dictionary
            path_str = path_str.replace(',' + current_node, '') #remove the last node in current path
            current_node = '' + path_list[-1] #roll back to previous node
            print "lock_nodes: "
            print lock_nodes.keys()
            time.sleep( 3 )
            continue

        #path exploration
        if task == 1: #task 1: minimum number of query.

            deg = -999999 #set a small weight for default
            next_node = ''
            for neighbor in gb.Graph.neighbors(current_node): #scan the neighbors of current node
                #print neighbor
                if neighbor not in path_list and neighbor not in lock_nodes: #prevent from formimg a circle
                    if neighbor == dest_node:
                        current_node = '' + dest_node #update current node
                        next_node = '' + dest_node
                        break
                    else:
                        if neighbor in train_model.Graph.nodes() and dest_node in train_model.G.nodes():
                            if nx.has_path(train_model.G, neighbor, dest_node):
                                next_node = '' + neighbor
                                deg = 999999 #set a large weight for priority
                                #upadate
                        else:
                            degree = gb.Graph.node[neighbor]['deg']
                            if  degree >= deg:
                                deg = degree
                                next_node = '' + neighbor

            if next_node != '':
                current_node = '' + next_node
            path_list.append(current_node)
            path_str += ',' + current_node
            print path_str


        else: #minimum geography distance of path.
            #greedy rounting. With query limit, the recursion method seems to be relatively unstable

            distance_t = 999999
            next_node = ''
            for neighbor in gb.Graph.neighbors(current_node): #scan the neighbors of current node
                #print neighbor
                if neighbor not in path_list and neighbor not in block_nodes: #prevent from formimg a circle
                    if neighbor == dest_node:
                        print 'Dijkstra Algorithm processing...'
                        current_node = '' + dest_node #update current node
                        next_node = '' + dest_node
                        #don't need to updat
                        dijkstra_path = nx.dijkstra_path(gb.Graph, start_node, dest_node)
                        path_str = dijkstra_path[0]
                        for i in range(len(dijkstra_path)-2):
                            path_str += ',' + dijkstra_path[i+1]
                        break
                    else:
                        if neighbor in train_model.Graph.nodes() and dest_node in train_model.Graph.nodes():
                            if nx.has_path(train_model.Graph, neighbor, dest_node):
                                next_node = '' + neighbor
                                distance_t = -999999
                        else:
                            dis = distance(neighbor, current_node, userID_table)
                            if  dis <= distance_t:
                                distance_t = dis
                                next_node = '' + neighbor

            if next_node != '':
                current_node = '' + next_node
            path_list.append(current_node)
            path_str += ',' + current_node
            print path_str