
import argparse
import copy







def     A__   (  lowerCamelCase  )					->		Any:
				UpperCamelCase_:							Tuple		=  {}

				with open(__lowerCamelCase  ) as f:
								for line in f:
												if line.split()[0] not in dict_of_neighbours:
																UpperCamelCase_:							int		=  []
																_list.append([line.split()[1], line.split()[2]]  )
																UpperCamelCase_:							Optional[Any]		=  _list
												else:
																dict_of_neighbours[line.split()[0]].append(
																    [line.split()[1], line.split()[2]]  )
												if line.split()[1] not in dict_of_neighbours:
																UpperCamelCase_:							Any		=  []
																_list.append([line.split()[0], line.split()[2]]  )
																UpperCamelCase_:							Tuple		=  _list
												else:
																dict_of_neighbours[line.split()[1]].append(
																    [line.split()[0], line.split()[2]]  )

				return dict_of_neighbours







def     A__   (  lowerCamelCase       ,      lowerCamelCase  )					->		Optional[int]:

				with open(__lowerCamelCase  ) as f:
								UpperCamelCase_:							Dict		=  f.read(1  )
				UpperCamelCase_:							Union[str, Any]		=  start_node

				UpperCamelCase_:							Dict		=  []

				UpperCamelCase_:							int		=  start_node

				UpperCamelCase_:							Optional[int]		=  0
				while visiting not in first_solution:
								UpperCamelCase_:							Dict		=  1_00_00
								for k in dict_of_neighbours[visiting]:
												if int(k[1]  ) < int(__lowerCamelCase  ) and k[0] not in first_solution:
																UpperCamelCase_:							Optional[int]		=  k[1]
																UpperCamelCase_:							str		=  k[0]

								first_solution.append(__lowerCamelCase  )
								UpperCamelCase_:							List[str]		=  distance_of_first_solution + int(__lowerCamelCase  )
								UpperCamelCase_:							Any		=  best_node

				first_solution.append(__lowerCamelCase  )

				UpperCamelCase_:							Any		=  0
				for k in dict_of_neighbours[first_solution[-2]]:
								if k[0] == start_node:
												break
								position += 1

				UpperCamelCase_:							str		=  (
				    distance_of_first_solution
				    + int(dict_of_neighbours[first_solution[-2]][position][1]  )
				    - 1_00_00
				)
				return first_solution, distance_of_first_solution







def     A__   (  lowerCamelCase       ,      lowerCamelCase  )					->		Tuple:
				UpperCamelCase_:							Optional[Any]		=  []

				for n in solution[1:-1]:
								UpperCamelCase_:							List[Any]		=  solution.index(__lowerCamelCase  )
								for kn in solution[1:-1]:
												UpperCamelCase_:							Optional[int]		=  solution.index(__lowerCamelCase  )
												if n == kn:
																continue

												UpperCamelCase_:							str		=  copy.deepcopy(__lowerCamelCase  )
												UpperCamelCase_:							List[str]		=  kn
												UpperCamelCase_:							List[Any]		=  n

												UpperCamelCase_:							Tuple		=  0

												for k in _tmp[:-1]:
																UpperCamelCase_:							Optional[int]		=  _tmp[_tmp.index(__lowerCamelCase  ) + 1]
																for i in dict_of_neighbours[k]:
																				if i[0] == next_node:
																								UpperCamelCase_:							Union[str, Any]		=  distance + int(i[1]  )
												_tmp.append(__lowerCamelCase  )

												if _tmp not in neighborhood_of_solution:
																neighborhood_of_solution.append(_tmp  )

				UpperCamelCase_:							Optional[Any]		=  len(neighborhood_of_solution[0]  ) - 1

				neighborhood_of_solution.sort(key=lambda lowerCamelCase  : x[index_of_last_item_in_the_list]  )
				return neighborhood_of_solution







def     A__   (  lowerCamelCase       ,      lowerCamelCase       ,      lowerCamelCase       ,      lowerCamelCase       ,      lowerCamelCase  )					->		List[str]:
				UpperCamelCase_:							str		=  1
				UpperCamelCase_:							Dict		=  first_solution
				UpperCamelCase_:							Dict		=  []
				UpperCamelCase_:							List[str]		=  distance_of_first_solution
				UpperCamelCase_:							int		=  solution

				while count <= iters:
								UpperCamelCase_:							Tuple		=  find_neighborhood(__lowerCamelCase       ,      __lowerCamelCase  )
								UpperCamelCase_:							List[str]		=  0
								UpperCamelCase_:							Tuple		=  neighborhood[index_of_best_solution]
								UpperCamelCase_:							List[Any]		=  len(__lowerCamelCase  ) - 1

								UpperCamelCase_:							List[Any]		=  False
								while not found:
												UpperCamelCase_:							int		=  0
												while i < len(__lowerCamelCase  ):
																if best_solution[i] != solution[i]:
																				UpperCamelCase_:							Optional[Any]		=  best_solution[i]
																				UpperCamelCase_:							Optional[Any]		=  solution[i]
																				break
																UpperCamelCase_:							int		=  i + 1

												if [first_exchange_node, second_exchange_node] not in tabu_list and [
												    second_exchange_node,
												    first_exchange_node,
												] not in tabu_list:
																tabu_list.append([first_exchange_node, second_exchange_node]  )
																UpperCamelCase_:							Optional[int]		=  True
																UpperCamelCase_:							Union[str, Any]		=  best_solution[:-1]
																UpperCamelCase_:							Union[str, Any]		=  neighborhood[index_of_best_solution][best_cost_index]
																if cost < best_cost:
																				UpperCamelCase_:							Optional[Any]		=  cost
																				UpperCamelCase_:							Optional[Any]		=  solution
												else:
																UpperCamelCase_:							Optional[Any]		=  index_of_best_solution + 1
																UpperCamelCase_:							Tuple		=  neighborhood[index_of_best_solution]

								if len(__lowerCamelCase  ) >= size:
												tabu_list.pop(0  )

								UpperCamelCase_:							List[Any]		=  count + 1

				return best_solution_ever, best_cost







def     A__   (  lowerCamelCase=None  )					->		Tuple:
				UpperCamelCase_:							List[str]		=  generate_neighbours(args.File  )

				UpperCamelCase_:							List[str]		=  generate_first_solution(
				    args.File       ,      __lowerCamelCase  )

				UpperCamelCase_:							str		=  tabu_search(
				    __lowerCamelCase       ,      __lowerCamelCase       ,      __lowerCamelCase       ,      args.Iterations       ,      args.Size       ,      )

				print(f'''Best solution: {best_sol}, with total distance: {best_cost}.'''  )


if __name__ == "__main__":
							lowerCamelCase_  :	Optional[Any] 				=					argparse.ArgumentParser(description="""Tabu Search""")
							parser.add_argument(
							    """-f""",
							    """--File""",
							    type=str,
							    help="""Path to the file containing the data""",
							    required=True,
							)
							parser.add_argument(
							    """-i""",
							    """--Iterations""",
							    type=int,
							    help="""How many iterations the algorithm should perform""",
							    required=True,
							)
							parser.add_argument(
							    """-s""", """--Size""", type=int, help="""Size of the tabu list""", required=True
							)

							# Pass the arguments to main method
							main(parser.parse_args())








def     A__   (  lowerCamelCase       ,      lowerCamelCase  )					->		list:
				UpperCamelCase_:							Optional[int]		=  word.split()

				def justify(lowerCamelCase       ,      lowerCamelCase       ,      lowerCamelCase  ) -> str:
								UpperCamelCase_:							Tuple		=  max_width - width
								UpperCamelCase_:							Optional[Any]		=  len(lowerCamelCase  )
								if len(lowerCamelCase  ) == 1:
												# if there is only word in line
												# just insert overall_spaces_count for the remainder of line
												return line[0] + " " * overall_spaces_count
								else:
												UpperCamelCase_:							List[Any]		=  words_count - 1
												# num_spaces_between_words_list[i] : tells you to insert
												# num_spaces_between_words_list[i] spaces
												# after word on line[i]
												UpperCamelCase_:							Optional[Any]		=  spaces_to_insert_between_words * [
												    overall_spaces_count // spaces_to_insert_between_words
												]
												UpperCamelCase_:							List[str]		=  (
												    overall_spaces_count % spaces_to_insert_between_words
												)
												# distribute spaces via round robin to the left words
												for i in range(lowerCamelCase  ):
																num_spaces_between_words_list[i] += 1
												UpperCamelCase_:							Dict		=  []
												for i in range(lowerCamelCase  ):
																# add the word
																aligned_words_list.append(line[i]  )
																# add the spaces to insert
																aligned_words_list.append(num_spaces_between_words_list[i] * """ """  )
												# just add the last word to the sentence
												aligned_words_list.append(line[-1]  )
												# join the aligned words list to form a justified line
												return "".join(lowerCamelCase  )

				UpperCamelCase_:							Optional[int]		=  []
				UpperCamelCase_:							list[str]		=  []
				UpperCamelCase_:							List[str]		=  0
				for word in words:
								if width + len(lowerCamelCase  ) + len(lowerCamelCase  ) <= max_width:
												# keep adding words until we can fill out max_width
												# width = sum of length of all words (without overall_spaces_count)
												# len(word) = length of current word
												# len(line) = number of overall_spaces_count to insert between words
												line.append(lowerCamelCase  )
												width += len(lowerCamelCase  )
								else:
												# justify the line and add it to result
												answer.append(justify(lowerCamelCase       ,      lowerCamelCase       ,      lowerCamelCase  )  )
												# reset new line and new width
												UpperCamelCase_,				UpperCamelCase_:							List[str]		=  [word], len(lowerCamelCase  )
				UpperCamelCase_:							List[str]		=  max_width - width - len(lowerCamelCase  )
				answer.append(""" """.join(lowerCamelCase  ) + (remaining_spaces + 1) * """ """  )
				return answer


if __name__ == "__main__":
							from doctest import testmod

							testmod()






