




import math


class        UpperCAmelCase__				:



     '''simple docstring'''
     def __init__(				self ,    _lowerCAmelCase ,    _lowerCAmelCase ,    _lowerCAmelCase ,    _lowerCAmelCase	):
            a			   =x
            a			   =y
            a			   =step_size
            a			   =function_to_optimize
     def        lowerCAmelCase__  (				self	):
            return self.function(self.x ,    self.y	)
     def        lowerCAmelCase__  (				self	):
            a			   =self.step_size
            return [
                SearchProblem(_lowerCAmelCase ,    _lowerCAmelCase ,    _lowerCAmelCase ,    self.function	)
                for x, y in (
                    (self.x - step_size, self.y - step_size),
                    (self.x - step_size, self.y),
                    (self.x - step_size, self.y + step_size),
                    (self.x, self.y - step_size),
                    (self.x, self.y + step_size),
                    (self.x + step_size, self.y - step_size),
                    (self.x + step_size, self.y),
                    (self.x + step_size, self.y + step_size),
                )
            ]
     def __hash__(				self	):
            return hash(str(self	)	)
     def __eq__(				self ,    _lowerCAmelCase	):
            if isinstance(_lowerCAmelCase ,    _lowerCAmelCase	):
                   return hash(str(self	)	) == hash(str(_lowerCAmelCase	)	)
            return False






     def __str__(				self	):
            return F'''x: {self.x} y: {self.y}'''





def 							lowerCamelCase    (							UpperCAmelCase_	:      Any   ,       UpperCAmelCase_	:      bool = True   ,       UpperCAmelCase_	:      float = math.inf   ,       UpperCAmelCase_	:      float = -math.inf   ,       UpperCAmelCase_	:      float = math.inf   ,       UpperCAmelCase_	:      float = -math.inf   ,       UpperCAmelCase_	:      bool = False   ,       UpperCAmelCase_	:      int = 1_0000   ,       )->			SearchProblem:

       """simple docstring"""




       a			   =search_prob
       a			   =[]  # list to store the current score at each iteration
       a			   =0
       a			   =False
       a			   =set()
       while not solution_found and iterations < max_iter:
              visited.add(UpperCAmelCase_      )
              iterations += 1
              a			   =current_state.score()
              scores.append(UpperCAmelCase_      )
              a			   =current_state.get_neighbors()
              a			   =-math.inf
              a			   =math.inf
              a			   =None  # to hold the next best neighbor
              for neighbor in neighbors:
                     if neighbor in visited:
                            continue  # do not want to visit the same state again
                     if (
                         neighbor.x > max_x
                         or neighbor.x < min_x
                         or neighbor.y > max_y
                         or neighbor.y < min_y
                     ):
                            continue  # neighbor outside our bounds
                     a			   =neighbor.score() - current_score
                     if find_max:  # finding max
                            # going to direction with greatest ascent
                            if change > max_change and change > 0:
                                   a			   =change
                                   a			   =neighbor
                     else:  # finding min
                            # to direction with greatest descent
                            if change < min_change and change < 0:
                                   a			   =change
                                   a			   =neighbor
              if next_state is not None:
                     # we found at least one neighbor which improved the current state
                     a			   =next_state
              else:
                     # since we have no neighbor that improves the solution we stop the search
                     a			   =True

       if visualization:
              from matplotlib import pyplot as plt

              plt.plot(range(UpperCAmelCase_      )   ,       UpperCAmelCase_      )
              plt.xlabel("""Iterations"""      )
              plt.ylabel("""Function values"""      )
              plt.show()

       return current_state


if __name__ == "__main__":
    import doctest

    doctest.testmod()





    def 							lowerCamelCase    (							UpperCAmelCase_	:      str   ,       UpperCAmelCase_	:      Dict      )->			Any:

           """simple docstring"""




           return (x**2) + (y**2)

    # starting the problem with initial coordinates (3, 4)
    _lowerCamelCase							=      SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_fa)
    _lowerCamelCase							=      hill_climbing(prob, find_max=False)
    print(
        '''The minimum score for f(x, y) = x^2 + y^2 found via hill climbing: '''
        f"""{local_min.score()}"""
    )

    # starting the problem with initial coordinates (12, 47)
    _lowerCamelCase							=      SearchProblem(x=12, y=47, step_size=1, function_to_optimize=test_fa)
    _lowerCamelCase							=      hill_climbing(
        prob, find_max=False, max_x=100, min_x=5, max_y=50, min_y=-5, visualization=True
    )
    print(
        '''The minimum score for f(x, y) = x^2 + y^2 with the domain 100 > x > 5 '''
        f"""and 50 > y > - 5 found via hill climbing: {local_min.score()}"""
    )





    def 							lowerCamelCase    (							UpperCAmelCase_	:      Any   ,       UpperCAmelCase_	:      List[Any]      )->			Union[str, Any]:

           """simple docstring"""




           return (3 * x**2) - (6 * y)

    _lowerCamelCase							=      SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_fa)
    _lowerCamelCase							=      hill_climbing(prob, find_max=True)






    print(
        '''The maximum score for f(x, y) = x^2 + y^2 found via hill climbing: '''
        f"""{local_min.score()}"""
    )






from __future__ import annotations





def 							lowerCamelCase    (							UpperCAmelCase_	:      str   ,       UpperCAmelCase_	:      dict[str, str]      )->			str:

       """simple docstring"""




       a   ,      a   ,      a			   ="""""", """""", """"""
       a			   =[]

       for character in message_part:
              tmp.append(character_to_number[character]      )

       for each in tmp:
              one += each[0]
              two += each[1]
              three += each[2]

       return one + two + three





def 							lowerCamelCase    (							UpperCAmelCase_	:      str   ,       UpperCAmelCase_	:      dict[str, str]      )->			tuple[str, str, str]:

       """simple docstring"""




       a   ,      a			   ="""""", """"""
       a			   =[]

       for character in message_part:
              this_part += character_to_number[character]

       for digit in this_part:
              tmp += digit
              if len(UpperCAmelCase_      ) == len(UpperCAmelCase_      ):
                     result.append(UpperCAmelCase_      )
                     a			   =""""""

       return result[0], result[1], result[2]





def 							lowerCamelCase    (							UpperCAmelCase_	:      str   ,       UpperCAmelCase_	:      str      )->			tuple[str, str, dict[str, str], dict[str, str]]:

       """simple docstring"""




       a			   =alphabet.replace(""" """   ,       """"""      ).upper()
       a			   =message.replace(""" """   ,       """"""      ).upper()

       # Check length and characters
       if len(UpperCAmelCase_      ) != 27:
              raise KeyError("""Length of alphabet has to be 27."""      )
       for each in message:
              if each not in alphabet:
                     raise ValueError("""Each message character has to be included in alphabet!"""      )

    # Generate dictionares
       a			   =(
           """111""",
           """112""",
           """113""",
           """121""",
           """122""",
           """123""",
           """131""",
           """132""",
           """133""",
           """211""",
           """212""",
           """213""",
           """221""",
           """222""",
           """223""",
           """231""",
           """232""",
           """233""",
           """311""",
           """312""",
           """313""",
           """321""",
           """322""",
           """323""",
           """331""",
           """332""",
           """333""",
       )
       a			   ={}
       a			   ={}
       for letter, number in zip(UpperCAmelCase_   ,       UpperCAmelCase_      ):
              a			   =number
              a			   =letter

       return message, alphabet, character_to_number, number_to_character





def 							lowerCamelCase    (							UpperCAmelCase_	:      str   ,       UpperCAmelCase_	:      str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ."   ,       UpperCAmelCase_	:      int = 5      )->			str:

       """simple docstring"""




       a   ,      a   ,      a   ,      a			   =__prepare(
           UpperCAmelCase_   ,       UpperCAmelCase_      )
       a   ,      a			   ="""""", """"""

       for i in range(0   ,       len(UpperCAmelCase_      ) + 1   ,       UpperCAmelCase_      ):
              encrypted_numeric += __encrypt_part(
                  message[i : i + period]   ,       UpperCAmelCase_      )

       for i in range(0   ,       len(UpperCAmelCase_      )   ,       3      ):
              encrypted += number_to_character[encrypted_numeric[i : i + 3]]

       return encrypted





def 							lowerCamelCase    (							UpperCAmelCase_	:      str   ,       UpperCAmelCase_	:      str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ."   ,       UpperCAmelCase_	:      int = 5      )->			str:

       """simple docstring"""




       a   ,      a   ,      a   ,      a			   =__prepare(
           UpperCAmelCase_   ,       UpperCAmelCase_      )
       a			   =[]
       a			   =""""""

       for i in range(0   ,       len(UpperCAmelCase_      ) + 1   ,       UpperCAmelCase_      ):
              a   ,      a   ,      a			   =__decrypt_part(message[i : i + period]   ,       UpperCAmelCase_      )

              for j in range(0   ,       len(UpperCAmelCase_      )      ):
                     decrypted_numeric.append(a[j] + b[j] + c[j]      )

       for each in decrypted_numeric:
              decrypted += number_to_character[each]

       return decrypted


if __name__ == "__main__":
    _lowerCamelCase							=      '''DEFEND THE EAST WALL OF THE CASTLE.'''
    _lowerCamelCase							=      encrypt_message(msg, '''EPSDUCVWYM.ZLKXNBTFGORIJHAQ''')
    _lowerCamelCase							=      decrypt_message(encrypted, '''EPSDUCVWYM.ZLKXNBTFGORIJHAQ''')
    print(f"""Encrypted: {encrypted}\nDecrypted: {decrypted}""")
