



import argparse
import copy

def       A__(		__lowerCAmelCase ):
				_snake_case :			Any  						=   {}

				with open(SCREAMING_SNAKE_CASE_ ) as f:
								for line in f:
												if line.split()[0] not in dict_of_neighbours:
																_snake_case :			Tuple  						=   []
																_list.append([line.split()[1], line.split()[2]] )
																_snake_case :			Dict  						=   _list
												else:
																dict_of_neighbours[line.split()[0]].append(
																    [line.split()[1], line.split()[2]] )
												if line.split()[1] not in dict_of_neighbours:
																_snake_case :			List[Any]  						=   []
																_list.append([line.split()[0], line.split()[2]] )
																_snake_case :			Union[str, Any]  						=   _list
												else:
																dict_of_neighbours[line.split()[1]].append(
																    [line.split()[0], line.split()[2]] )

				return dict_of_neighbours

def       A__(		__lowerCAmelCase	,			__lowerCAmelCase ):

				with open(SCREAMING_SNAKE_CASE_ ) as f:
								_snake_case :			Optional[Any]  						=   f.read(1 )
				_snake_case :			Any  						=   start_node

				_snake_case :			Dict  						=   []

				_snake_case :			Tuple  						=   start_node

				_snake_case :			Dict  						=   0
				while visiting not in first_solution:
								_snake_case :			List[Any]  						=   1_00_00
								for k in dict_of_neighbours[visiting]:
												if int(k[1] ) < int(SCREAMING_SNAKE_CASE_ ) and k[0] not in first_solution:
																_snake_case :			Dict  						=   k[1]
																_snake_case :			Tuple  						=   k[0]

								first_solution.append(SCREAMING_SNAKE_CASE_ )
								_snake_case :			Tuple  						=   distance_of_first_solution + int(SCREAMING_SNAKE_CASE_ )
								_snake_case :			int  						=   best_node

				first_solution.append(SCREAMING_SNAKE_CASE_ )

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

				_snake_case :			int  						=   (
				    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 ):
				_snake_case :			int  						=   []

				for n in solution[1:-1]:
								_snake_case :			List[str]  						=   solution.index(SCREAMING_SNAKE_CASE_ )
								for kn in solution[1:-1]:
												_snake_case :			Optional[Any]  						=   solution.index(SCREAMING_SNAKE_CASE_ )
												if n == kn:
																continue

												_snake_case :			str  						=   copy.deepcopy(SCREAMING_SNAKE_CASE_ )
												_snake_case :			Tuple  						=   kn
												_snake_case :			Union[str, Any]  						=   n

												_snake_case :			Optional[Any]  						=   0

												for k in _tmp[:-1]:
																_snake_case :			Optional[Any]  						=   _tmp[_tmp.index(SCREAMING_SNAKE_CASE_ ) + 1]
																for i in dict_of_neighbours[k]:
																				if i[0] == next_node:
																								_snake_case :			Optional[int]  						=   distance + int(i[1] )
												_tmp.append(SCREAMING_SNAKE_CASE_ )

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

				_snake_case :			Tuple  						=   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 ):
				_snake_case :			Union[str, Any]  						=   1
				_snake_case :			int  						=   first_solution
				_snake_case :			Optional[Any]  						=   []
				_snake_case :			int  						=   distance_of_first_solution
				_snake_case :			int  						=   solution

				while count <= iters:
								_snake_case :			str  						=   find_neighborhood(SCREAMING_SNAKE_CASE_	,			SCREAMING_SNAKE_CASE_ )
								_snake_case :			int  						=   0
								_snake_case :			Dict  						=   neighborhood[index_of_best_solution]
								_snake_case :			Optional[Any]  						=   len(SCREAMING_SNAKE_CASE_ ) - 1

								_snake_case :			Dict  						=   False
								while not found:
												_snake_case :			List[str]  						=   0
												while i < len(SCREAMING_SNAKE_CASE_ ):
																if best_solution[i] != solution[i]:
																				_snake_case :			Dict  						=   best_solution[i]
																				_snake_case :			Optional[Any]  						=   solution[i]
																				break
																_snake_case :			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] )
																_snake_case :			Optional[Any]  						=   True
																_snake_case :			Any  						=   best_solution[:-1]
																_snake_case :			Optional[int]  						=   neighborhood[index_of_best_solution][best_cost_index]
																if cost < best_cost:
																				_snake_case :			Dict  						=   cost
																				_snake_case :			Dict  						=   solution
												else:
																_snake_case :			List[Any]  						=   index_of_best_solution + 1
																_snake_case :			Tuple  						=   neighborhood[index_of_best_solution]

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

								_snake_case :			Union[str, Any]  						=   count + 1

				return best_solution_ever, best_cost

def       A__(		__lowerCAmelCase=None ):
				_snake_case :			int  						=   generate_neighbours(args.File )

				_snake_case :			Dict  						=   generate_first_solution(
				    args.File	,			SCREAMING_SNAKE_CASE_ )

				_snake_case :			str  						=   tabu_search(
				    SCREAMING_SNAKE_CASE_	,			SCREAMING_SNAKE_CASE_	,			SCREAMING_SNAKE_CASE_	,			args.Iterations	,			args.Size	,			)

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


if __name__ == "__main__":
		lowercase_		:				Union[str, 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())













import functools

def       A__(		__lowerCAmelCase	,			__lowerCAmelCase ):

				# Validation
				if not isinstance(__lowerCAmelCase	,			__lowerCAmelCase ) or not all(isinstance(__lowerCAmelCase	,			__lowerCAmelCase ) for day in days ):
								raise ValueError('The parameter days should be a list of integers' )

				if len(__lowerCAmelCase ) != 3 or not all(isinstance(__lowerCAmelCase	,			__lowerCAmelCase ) for cost in costs ):
								raise ValueError('The parameter costs should be a list of three integers' )

				if len(__lowerCAmelCase ) == 0:
								return 0

				if min(__lowerCAmelCase ) <= 0:
								raise ValueError('All days elements should be greater than 0' )

				if max(__lowerCAmelCase ) >= 3_66:
								raise ValueError('All days elements should be less than 366' )

				_snake_case :			Optional[int]  						=   set(__lowerCAmelCase )

				@functools.cache
				def dynamic_programming(__lowerCAmelCase ) -> int:
								if index > 3_65:
												return 0

								if index not in days_set:
												return dynamic_programming(index + 1 )

								return min(
								    costs[0] + dynamic_programming(index + 1 )	,			costs[1] + dynamic_programming(index + 7 )	,			costs[2] + dynamic_programming(index + 30 )	,			)

				return dynamic_programming(1 )


if __name__ == "__main__":
		import doctest

		doctest.testmod()






