'''simple docstring'''






from __future__ import annotations

from typing import Generic, TypeVar

SCREAMING_SNAKE_CASE_						 =      TypeVar('T')




class  a			(							Generic[T]					):



  """simple docstring"""







  def __init__(				self ,     snake_case_       ):

    '''simple docstring'''







    __UpperCAmelCase:       int		=			data
    __UpperCAmelCase:       Any		=			self
    __UpperCAmelCase:       Dict		=			0




class  a			(							Generic[T]					):



  """simple docstring"""







  def __init__(				self       ):

    '''simple docstring'''







    __UpperCAmelCase:       List[Any]		=			{}







  def  lowercase_	(				self ,     snake_case_       ):

    '''simple docstring'''







    __UpperCAmelCase:       Optional[Any]		=			DisjointSetTreeNode(A_       )







  def  lowercase_	(				self ,     snake_case_       ):

    '''simple docstring'''







    __UpperCAmelCase:       Optional[int]		=			self.map[data]
    if elem_ref != elem_ref.parent:
      __UpperCAmelCase:       str		=			self.find_set(elem_ref.parent.data       )
    return elem_ref.parent







  def  lowercase_	(				self ,     snake_case_ ,     snake_case_       ):

    '''simple docstring'''







    if nodea.rank > nodea.rank:
      __UpperCAmelCase:       str		=			nodea
    else:
      __UpperCAmelCase:       List[Any]		=			nodea
      if nodea.rank == nodea.rank:
        nodea.rank += 1







  def  lowercase_	(				self ,     snake_case_ ,     snake_case_       ):

    '''simple docstring'''







    self.link(self.find_set(A_       ) ,     self.find_set(A_       )       )




class  a			(							Generic[T]					):



  """simple docstring"""







  def __init__(				self       ):

    '''simple docstring'''







    __UpperCAmelCase:       Union[str, Any]		=			{}







  def  lowercase_	(				self ,     snake_case_       ):

    '''simple docstring'''







    if node not in self.connections:
      __UpperCAmelCase:       Tuple		=			{}







  def  lowercase_	(				self ,     snake_case_ ,     snake_case_ ,     snake_case_       ):

    '''simple docstring'''







    self.add_node(A_       )
    self.add_node(A_       )
    __UpperCAmelCase:       Dict		=			weight
    __UpperCAmelCase:       int		=			weight







  def  lowercase_	(				self       ):

    '''simple docstring'''







    __UpperCAmelCase:       Any		=			[]
    __UpperCAmelCase:       List[Any]		=			set()
    for start in self.connections:
      for end in self.connections[start]:
        if (start, end) not in seen:
          seen.add((end, start)       )
          edges.append((start, end, self.connections[start][end])       )
    edges.sort(key=lambda snake_case_       : x[2]       )

    # creating the disjoint set
    __UpperCAmelCase:       int		=			DisjointSetTree[T]()
    for node in self.connections:
      disjoint_set.make_set(A_       )

    # MST generation
    __UpperCAmelCase:       Dict		=			0
    __UpperCAmelCase:       Union[str, Any]		=			0
    __UpperCAmelCase:       Optional[Any]		=			GraphUndirectedWeighted[T]()
    while num_edges < len(self.connections       ) - 1:
      __UpperCAmelCase,			__UpperCAmelCase,			__UpperCAmelCase:       Union[str, Any]		=			edges[index]
      index += 1
      __UpperCAmelCase:       List[Any]		=			disjoint_set.find_set(A_       )
      __UpperCAmelCase:       List[str]		=			disjoint_set.find_set(A_       )
      if parent_u != parent_v:
        num_edges += 1
        graph.add_edge(A_ ,     A_ ,     A_       )
        disjoint_set.union(A_ ,     A_       )
    return graph

'''simple docstring'''
lowerCAmelCase					:    Optional[Any]           = {
    'A': ['B', 'C', 'E'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F', 'G'],
    'D': ['B'],
    'E': ['A', 'B', 'D'],
    'F': ['C'],
    'G': ['C'],
}







def 		A_(     A						:		dict		,     A						:		str		,     A						:		Optional[Any]):
							UpperCamelCase   			=     set()
							# keep track of all the paths to be checked
							UpperCamelCase   			=     [[start]]

							# return path if start is goal
							if start == goal:
														return [start]

							# keeps looping until all possible paths have been checked
							while queue:
														# pop the first path from the queue
														UpperCamelCase   			=     queue.pop(0)
														# get the last node from the path
														UpperCamelCase   			=     path[-1]
														if node not in explored:
																					UpperCamelCase   			=     graph[node]
																					# go through all neighbour nodes, construct a new path and
																					# push it into the queue
																					for neighbour in neighbours:
																												UpperCamelCase   			=     list(A)
																												new_path.append(A)
																												queue.append(A)
																												# return path if neighbour is goal
																												if neighbour == goal:
																																			return new_path

            # mark node as explored
																					explored.add(A)

    # in case there's no path between the 2 nodes
							return []







def 		A_(     A						:		dict		,     A						:		str		,     A						:		Tuple):
							if not graph or start not in graph or target not in graph:
														return -1
							if start == target:
														return 0
							UpperCamelCase   			=     [start]
							UpperCamelCase   			=     set(A)
							# Keep tab on distances from `start` node.
							UpperCamelCase   			=     {start: 0, target: -1}
							while queue:
														UpperCamelCase   			=     queue.pop(0)
														if node == target:
																					UpperCamelCase   			=     (
																					    dist[node] if dist[target] == -1 else min(dist[target]		,     dist[node])
																					)
														for adjacent in graph[node]:
																					if adjacent not in visited:
																												visited.add(A)
																												queue.append(A)
																												UpperCamelCase   			=     dist[node] + 1
							return dist[target]


if __name__ == "__main__":
						print(bfs_shortest_path(demo_graph, 'G', 'D'))  # returns ['G', 'C', 'A', 'B', 'D']
						print(bfs_shortest_path_distance(demo_graph, 'G', 'D'))  # returns 4




