






from __future__ import annotations

import math
import random
from collections.abc import Collection
from typing import overload

class 							SCREAMING_SNAKE_CASE__		:





					"""simple docstring"""






					def __init__(      self  :						str							,	__A  :						Collection[float] | None = None ):
						if components is None:
							snake_case__				:   Dict     =						[]
						snake_case__				:   Dict     =						list(__A )



					def __len__(      self  :						Any ):
						return len(self.__components )



					def __str__(      self  :						List[str] ):
						return "(" + ",".join(map(__A							,	self.__components ) ) + ")"



					def __add__(      self  :						List[Any]							,	__A  :						Vector ):
						snake_case__				:   Any     =						len(self )
						if size == len(__A ):
							snake_case__				:   Dict     =						[self.__components[i] + other.component(__A ) for i in range(__A )]
							return Vector(__A )
						else:
							raise Exception("must have the same size" )



					def __sub__(      self  :						List[str]							,	__A  :						Vector ):
						snake_case__				:   Union[str, Any]     =						len(self )
						if size == len(__A ):
							snake_case__				:   Optional[int]     =						[self.__components[i] - other.component(__A ) for i in range(__A )]
							return Vector(__A )
						else:  # error case
							raise Exception("must have the same size" )



					@overload
					def __mul__(      self  :						List[str]							,	__A  :						float ):
						...



					@overload
					def __mul__(      self  :						Dict							,	__A  :						Vector ):
						...



					def __mul__(      self  :						int							,	__A  :						float | Vector ):
						if isinstance(__A							,	(float, int) ):
							snake_case__				:   List[Any]     =						[c * other for c in self.__components]
							return Vector(__A )
						elif isinstance(__A							,	__A ) and len(self ) == len(__A ):
							snake_case__				:   int     =						len(self )
							snake_case__				:   Tuple     =						[self.__components[i] * other.component(__A ) for i in range(__A )]
							return sum(__A )
						else:  # error case
							raise Exception("invalid operand!" )



					def  _lowercase  (      self  :						str ):
						return Vector(self.__components )



					def  _lowercase  (      self  :						str							,	__A  :						int ):
						if isinstance(__A							,	__A ) and -len(self.__components ) <= i < len(self.__components ):
							return self.__components[i]
						else:
							raise Exception("index out of range" )



					def  _lowercase  (      self  :						Union[str, Any]							,	__A  :						int							,	__A  :						float ):
						assert -len(self.__components ) <= pos < len(self.__components )
						snake_case__				:   Optional[Any]     =						value



					def  _lowercase  (      self  :						int ):
						if len(self.__components ) == 0:
							raise Exception("Vector is empty" )
						snake_case__				:   List[Any]     =						[c**2 for c in self.__components]
						return math.sqrt(sum(__A ) )






					def  _lowercase  (      self  :						int							,	__A  :						Vector							,	__A  :						bool = False ):
						snake_case__				:   Union[str, Any]     =						self * other
						snake_case__				:   Union[str, Any]     =						self.euclidean_length() * other.euclidean_length()
						if deg:
							return math.degrees(math.acos(num / den ) )
						else:
							return math.acos(num / den )







def   SCREAMING_SNAKE_CASE       (				snake_case_      :							int     ):
	assert isinstance(snake_case_  ,						snake_case_     )
	return Vector([0] * dimension     )







def   SCREAMING_SNAKE_CASE       (				snake_case_      :							int  ,						snake_case_      :							int     ):
	assert isinstance(snake_case_  ,						snake_case_     ) and (isinstance(snake_case_  ,						snake_case_     ))
	snake_case__				:   List[Any]     =						[0] * dimension
	snake_case__				:   List[Any]     =						1
	return Vector(snake_case_     )







def   SCREAMING_SNAKE_CASE       (				snake_case_      :							float  ,						snake_case_      :							Vector  ,						snake_case_      :							Vector     ):
	assert (
	    isinstance(snake_case_  ,						snake_case_     )
	    and isinstance(snake_case_  ,						snake_case_     )
	    and (isinstance(snake_case_  ,						(int, float)     ))
	)
	return x * scalar + y







def   SCREAMING_SNAKE_CASE       (				snake_case_      :							int  ,						snake_case_      :							int  ,						snake_case_      :							int     ):
	random.seed(snake_case_     )
	snake_case__				:   Union[str, Any]     =						[random.randint(snake_case_  ,						snake_case_     ) for _ in range(snake_case_     )]
	return Vector(snake_case_     )

class 							SCREAMING_SNAKE_CASE__		:





					"""simple docstring"""






					def __init__(      self  :						Optional[Any]							,	__A  :						list[list[float]]							,	__A  :						int							,	__A  :						int ):
						snake_case__				:   List[Any]     =						matrix
						snake_case__				:   Any     =						w
						snake_case__				:   Any     =						h



					def __str__(      self  :						Union[str, Any] ):
						snake_case__				:   int     =						""
						for i in range(self.__height ):
							ans += "|"
							for j in range(self.__width ):
								if j < self.__width - 1:
									ans += str(self.__matrix[i][j] ) + ","
								else:
									ans += str(self.__matrix[i][j] ) + "|\n"
						return ans



					def __add__(      self  :						Tuple							,	__A  :						Matrix ):
						if self.__width == other.width() and self.__height == other.height():
							snake_case__				:   Dict     =						[]
							for i in range(self.__height ):
								snake_case__				:   Union[str, Any]     =						[
								    self.__matrix[i][j] + other.component(__A							,	__A )
								    for j in range(self.__width )
								]
								matrix.append(__A )
							return Matrix(__A							,	self.__width							,	self.__height )
						else:
							raise Exception("matrix must have the same dimension!" )



					def __sub__(      self  :						Optional[Any]							,	__A  :						Matrix ):
						if self.__width == other.width() and self.__height == other.height():
							snake_case__				:   List[str]     =						[]
							for i in range(self.__height ):
								snake_case__				:   int     =						[
								    self.__matrix[i][j] - other.component(__A							,	__A )
								    for j in range(self.__width )
								]
								matrix.append(__A )
							return Matrix(__A							,	self.__width							,	self.__height )
						else:
							raise Exception("matrices must have the same dimension!" )



					@overload
					def __mul__(      self  :						List[str]							,	__A  :						float ):
						...



					@overload
					def __mul__(      self  :						Tuple							,	__A  :						Vector ):
						...



					def __mul__(      self  :						List[str]							,	__A  :						float | Vector ):
						if isinstance(__A							,	__A ):  # matrix-vector
							if len(__A ) == self.__width:
								snake_case__				:   Tuple     =						zero_vector(self.__height )
								for i in range(self.__height ):
									snake_case__				:   List[str]     =						[
									    self.__matrix[i][j] * other.component(__A )
									    for j in range(self.__width )
									]
									ans.change_component(__A							,	sum(__A ) )
								return ans
							else:
								raise Exception(
								    "vector must have the same size as the "
								    "number of columns of the matrix!" )
						elif isinstance(__A							,	(int, float) ):  # matrix-scalar
							snake_case__				:   Tuple     =						[
							    [self.__matrix[i][j] * other for j in range(self.__width )]
							    for i in range(self.__height )
							]
							return Matrix(__A							,	self.__width							,	self.__height )
						return None



					def  _lowercase  (      self  :						str ):
						return self.__height



					def  _lowercase  (      self  :						Union[str, Any] ):
						return self.__width



					def  _lowercase  (      self  :						Optional[Any]							,	__A  :						int							,	__A  :						int ):
						if 0 <= x < self.__height and 0 <= y < self.__width:
							return self.__matrix[x][y]
						else:
							raise Exception("change_component: indices out of bounds" )



					def  _lowercase  (      self  :						str							,	__A  :						int							,	__A  :						int							,	__A  :						float ):
						if 0 <= x < self.__height and 0 <= y < self.__width:
							snake_case__				:   Optional[int]     =						value
						else:
							raise Exception("change_component: indices out of bounds" )



					def  _lowercase  (      self  :						str							,	__A  :						int							,	__A  :						int ):
						if self.__height != self.__width:
							raise Exception("Matrix is not square" )
						snake_case__				:   Tuple     =						self.__matrix[:x] + self.__matrix[x + 1 :]
						for i in range(len(__A ) ):
							snake_case__				:   Dict     =						minor[i][:y] + minor[i][y + 1 :]
						return Matrix(__A							,	self.__width - 1							,	self.__height - 1 ).determinant()



					def  _lowercase  (      self  :						Union[str, Any]							,	__A  :						int							,	__A  :						int ):
						if self.__height != self.__width:
							raise Exception("Matrix is not square" )
						if 0 <= x < self.__height and 0 <= y < self.__width:
							return (-1) ** (x + y) * self.minor(__A							,	__A )
						else:
							raise Exception("Indices out of bounds" )






					def  _lowercase  (      self  :						List[str] ):
						if self.__height != self.__width:
							raise Exception("Matrix is not square" )
						if self.__height < 1:
							raise Exception("Matrix has no element" )
						elif self.__height == 1:
							return self.__matrix[0][0]
						elif self.__height == 2:
							return (
							    self.__matrix[0][0] * self.__matrix[1][1]
							    - self.__matrix[0][1] * self.__matrix[1][0]
							)
						else:
							snake_case__				:   Dict     =						[
							    self.__matrix[0][y] * self.cofactor(0							,	__A ) for y in range(self.__width )
							]
							return sum(__A )







def   SCREAMING_SNAKE_CASE       (				snake_case_      :							int     ):
	snake_case__				:   list[list[float]]     =						[[0] * n for _ in range(snake_case_     )]
	return Matrix(snake_case_  ,						snake_case_  ,						snake_case_     )







def   SCREAMING_SNAKE_CASE       (				snake_case_      :							int  ,						snake_case_      :							int  ,						snake_case_      :							int  ,						snake_case_      :							int     ):
	random.seed(snake_case_     )
	snake_case__				:   list[list[float]]     =						[
	    [random.randint(snake_case_  ,						snake_case_     ) for _ in range(snake_case_     )] for _ in range(snake_case_     )
	]
	return Matrix(snake_case_  ,						snake_case_  ,						snake_case_     )











from __future__ import annotations

import time

__lowerCamelCase      :    str   		=   list[tuple[int, int]]

__lowerCamelCase      :    Optional[int]   		=   [
    [0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0],  # 0 are free path whereas 1's are obstacles
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0],
    [1, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0],
]

__lowerCamelCase      :    Tuple   		=   [[-1, 0], [0, -1], [1, 0], [0, 1]]  # up, left, down, right



class 							SCREAMING_SNAKE_CASE__		:





					"""simple docstring"""






					def __init__(      self  :						Union[str, Any]							,	__A  :						int							,	__A  :						int							,	__A  :						int							,	__A  :						int							,	__A  :						Node | None ):
						snake_case__				:   Optional[int]     =						pos_x
						snake_case__				:   Dict     =						pos_y
						snake_case__				:   int     =						(pos_y, pos_x)
						snake_case__				:   Optional[int]     =						goal_x
						snake_case__				:   Tuple     =						goal_y
						snake_case__				:   str     =						parent



class 							SCREAMING_SNAKE_CASE__		:





					"""simple docstring"""






					def __init__(      self  :						List[Any]							,	__A  :						tuple[int, int]							,	__A  :						tuple[int, int] ):
						snake_case__				:   Tuple     =						Node(start[1]							,	start[0]							,	goal[1]							,	goal[0]							,	__A )
						snake_case__				:   Tuple     =						Node(goal[1]							,	goal[0]							,	goal[1]							,	goal[0]							,	__A )

						snake_case__				:   int     =						[self.start]
						snake_case__				:   Union[str, Any]     =						False



					def  _lowercase  (      self  :						Dict ):
						while self.node_queue:
							snake_case__				:   Optional[Any]     =						self.node_queue.pop(0 )

							if current_node.pos == self.target.pos:
								snake_case__				:   Optional[Any]     =						True
								return self.retrace_path(__A )

							snake_case__				:   int     =						self.get_successors(__A )

							for node in successors:
								self.node_queue.append(__A )

						if not self.reached:
							return [self.start.pos]
						return None



					def  _lowercase  (      self  :						Union[str, Any]							,	__A  :						Node ):
						snake_case__				:   str     =						[]
						for action in delta:
							snake_case__				:   str     =						parent.pos_x + action[1]
							snake_case__				:   Union[str, Any]     =						parent.pos_y + action[0]
							if not (0 <= pos_x <= len(grid[0] ) - 1 and 0 <= pos_y <= len(__A ) - 1):
								continue

							if grid[pos_y][pos_x] != 0:
								continue

							successors.append(
							    Node(__A							,	__A							,	self.target.pos_y							,	self.target.pos_x							,	__A ) )
						return successors






					def  _lowercase  (      self  :						Optional[Any]							,	__A  :						Node | None ):
						snake_case__				:   Tuple     =						node
						snake_case__				:   Any     =						[]
						while current_node is not None:
							path.append((current_node.pos_y, current_node.pos_x) )
							snake_case__				:   Tuple     =						current_node.parent
						path.reverse()
						return path



class 							SCREAMING_SNAKE_CASE__		:





					"""simple docstring"""






					def __init__(      self  :						Dict							,	__A  :						str							,	__A  :						int ):
						snake_case__				:   str     =						BreadthFirstSearch(__A							,	__A )
						snake_case__				:   int     =						BreadthFirstSearch(__A							,	__A )
						snake_case__				:   Tuple     =						False



					def  _lowercase  (      self  :						Optional[Any] ):
						while self.fwd_bfs.node_queue or self.bwd_bfs.node_queue:
							snake_case__				:   Any     =						self.fwd_bfs.node_queue.pop(0 )
							snake_case__				:   List[str]     =						self.bwd_bfs.node_queue.pop(0 )

							if current_bwd_node.pos == current_fwd_node.pos:
								snake_case__				:   List[str]     =						True
								return self.retrace_bidirectional_path(
								    __A							,	__A )

							snake_case__				:   Union[str, Any]     =						current_bwd_node
							snake_case__				:   Dict     =						current_fwd_node

							snake_case__				:   List[Any]     =						{
							    self.fwd_bfs: self.fwd_bfs.get_successors(__A ),
							    self.bwd_bfs: self.bwd_bfs.get_successors(__A ),
							}

							for bfs in [self.fwd_bfs, self.bwd_bfs]:
								for node in successors[bfs]:
									bfs.node_queue.append(__A )

						if not self.reached:
							return [self.fwd_bfs.start.pos]
						return None






					def  _lowercase  (      self  :						Any							,	__A  :						Node							,	__A  :						Node ):
						snake_case__				:   List[str]     =						self.fwd_bfs.retrace_path(__A )
						snake_case__				:   Optional[Any]     =						self.bwd_bfs.retrace_path(__A )
						bwd_path.pop()
						bwd_path.reverse()
						snake_case__				:   List[Any]     =						fwd_path + bwd_path
						return path


if __name__ == "__main__":
	# all coordinates are given in format [y,x]
	import doctest

	doctest.testmod()
	__lowerCamelCase      :    str   		=   (0, 0)
	__lowerCamelCase      :    List[str]   		=   (len(grid) - 1, len(grid[0]) - 1)
	for elem in grid:
		print(elem)

	__lowerCamelCase      :    Any   		=   time.time()
	__lowerCamelCase      :    Optional[Any]   		=   BreadthFirstSearch(init, goal)
	__lowerCamelCase      :    str   		=   bfs.search()
	__lowerCamelCase      :    Optional[Any]   		=   time.time() - start_bfs_time

	print("""Unidirectional BFS computation time : """, bfs_time)

	__lowerCamelCase      :    Optional[Any]   		=   time.time()
	__lowerCamelCase      :    Optional[int]   		=   BidirectionalBreadthFirstSearch(init, goal)
	__lowerCamelCase      :    str   		=   bd_bfs.search()
	__lowerCamelCase      :    Optional[Any]   		=   time.time() - start_bd_bfs_time

	print("""Bidirectional BFS computation time : """, bd_bfs_time)



