Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicholsonjf/7025174c68a028b48beb27d9eeb5d593 to your computer and use it in GitHub Desktop.
Save nicholsonjf/7025174c68a028b48beb27d9eeb5d593 to your computer and use it in GitHub Desktop.
Implementation and Comparison of Navigation Algorithms.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"gpuClass": "standard"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/nicholsonjf/7025174c68a028b48beb27d9eeb5d593/implementation-and-comparison-of-navigation-algorithms.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"# Dependencies"
],
"metadata": {
"id": "wCMf3DNJacxr"
}
},
{
"cell_type": "code",
"source": [
"%matplotlib inline \n",
"\n",
"import numpy as np\n",
"from heapdict import heapdict\n",
"from math import floor, sqrt\n",
"\n",
"# for running experiments\n",
"import itertools\n",
"from multiprocessing import Pool\n",
"import pandas as pd\n",
"from google.colab import files\n",
"\n",
"# for plotting\n",
"from matplotlib import pyplot as plt\n",
"from matplotlib import colors\n",
"import networkx as nx"
],
"metadata": {
"id": "wFXeyLy3PWIt"
},
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Source Code"
],
"metadata": {
"id": "GyyLCsZUXwr3"
}
},
{
"cell_type": "code",
"source": [
"# TYPE_MAPPING is used to colour the tile according to the type\n",
"# e.g. all obstacles are in black colour. these tiles will be inaccessible\n",
"TYPE_MAPPING = {\n",
" 'obstacle' : 'black',\n",
" 'source' : 'royalblue',\n",
" 'target' : 'green',\n",
" 'path' : 'peachpuff',\n",
" 'others' : 'white',\n",
" 'visited' : 'yellow'\n",
"}\n",
"\n",
"# COLOUR_MAPPING is used to assign a value in numpy array so that matplotlib knows which value will be mapped to which colour\n",
"# e.g. if coordinate (1,2) has value 0, the tile will be of black colour\n",
"COLOUR_MAPPING = {\n",
" 'black' : 0,\n",
" 'royalblue' : 1,\n",
" 'green' : 2,\n",
" 'peachpuff' : 3,\n",
" 'white' : 4,\n",
" 'yellow' : 5\n",
"}"
],
"metadata": {
"id": "GAEBLmFrthFB"
},
"execution_count": 2,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"**Graph Implementation**"
],
"metadata": {
"id": "shOaPfcU8uCt"
}
},
{
"cell_type": "code",
"source": [
"class Graph:\n",
" \"\"\"\n",
" The graph implementation that all the algorithms in our project are designed\n",
" to search (with the exception of RRT). The graph is 2d. The height, width\n",
" and obstacle density can be set by the caller. The graph can be searched by\n",
" using its adjacency list, and easily plotted by calling the plot method.\n",
" \"\"\"\n",
"\n",
" def __init__(self, w, h, seed=0):\n",
" \"\"\"\n",
" Args:\n",
" w: The width of the graph (number of nodes)\n",
" h: The height of the graph (number of nodes)\n",
" seed: Using a seed value allows the same random number sequence to \n",
" be recalled, i.e. so the same obstacle layout can be used in\n",
" different trials.\n",
" \"\"\"\n",
" self.COLOUR_MAPPING = COLOUR_MAPPING\n",
" self.TYPE_MAPPING = TYPE_MAPPING\n",
"\n",
" # set the colour based on values in graph array (self.g). the mapping is determined by COLOUR_MAPPING\n",
" self.cmap = colors.ListedColormap( list( self.COLOUR_MAPPING.keys() ) )\n",
" self.bounds = list( self.COLOUR_MAPPING.values() ) + [ max(self.COLOUR_MAPPING.values()) +1 ]\n",
" self.norm = colors.BoundaryNorm(self.bounds, self.cmap.N)\n",
"\n",
" self.w = w\n",
" self.h = h\n",
" # initialise with others type of tiles\n",
" self.g = np.full(\n",
" (h, w),\n",
" self.COLOUR_MAPPING[self.TYPE_MAPPING['others']],\n",
" dtype=np.uint8\n",
" )\n",
" self.seed = seed\n",
"\n",
" self.adjacency_list = {}\n",
" self.source = None\n",
" self.target = None\n",
" self.obstacles = []\n",
" \n",
" def add_obstacle(self, x, y):\n",
" \"\"\"\n",
" args:\n",
" x: The x coordinate for where to add the obstacle.\n",
" y: The y coordinate for where to add the obstacle.\n",
" \"\"\"\n",
" self.g[y,x] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['obstacle'] ]\n",
" self.obstacles += [(x,y)]\n",
" \n",
" def add_source(self, x, y):\n",
" \"\"\"\n",
" args:\n",
" x: The x coordinate for where to add the obstacle.\n",
" y: The y coordinate for where to add the obstacle.\n",
" \"\"\"\n",
" # reset previous source to ensure that there is only one source\n",
" if self.source is not None:\n",
" self.g[self.source[1],self.source[0]] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['others'] ]\n",
"\n",
" # update new source\n",
" self.g[y,x] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['source'] ]\n",
" self.source = (x,y)\n",
" \n",
" def add_target(self, x, y):\n",
" \"\"\"\n",
" args:\n",
" x: The x coordinate for where to add the obstacle.\n",
" y: The y coordinate for where to add the obstacle.\n",
" \"\"\"\n",
" # reset previous target to ensure that there is only one target\n",
" if self.target is not None:\n",
" self.g[self.target[1],self.target[0]] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['others'] ]\n",
" \n",
" # update new target\n",
" self.g[y,x] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['target'] ]\n",
" self.target = (x,y)\n",
"\n",
" def generate_random_graph(self, probability_obstacle=0.2):\n",
" \"\"\"\n",
" args:\n",
" probability_obstacle: The probability that any given node in the grid\n",
" will be an obstacle.\n",
" \"\"\"\n",
" np.random.seed(self.seed)\n",
"\n",
" # generate list of all nodes\n",
" nodes = [ (x,y) for x in range(self.w) for y in range(self.h) ]\n",
" nodes_idx = list( range(self.w * self.h) )\n",
" # print(f\"nodes : {nodes}\")\n",
" \n",
" # pick one node as the source and remove node from the list\n",
" source_idx = np.random.choice(nodes_idx)\n",
" nodes_idx.remove(source_idx)\n",
" # print(f\"source : {source_idx}\\n nodes : {nodes_idx}\")\n",
" self.add_source(nodes[source_idx][0], nodes[source_idx][1])\n",
" \n",
" # pick one node as the target and remove node from the list\n",
" target_idx = np.random.choice(nodes_idx)\n",
" nodes_idx.remove(target_idx)\n",
" # print(f\"target : {target_idx}\\n nodes : {nodes_idx}\")\n",
" self.add_target(nodes[target_idx][0], nodes[target_idx][1])\n",
"\n",
" # pick a few nodes as obstacles\n",
" number_obstacles = floor(probability_obstacle * self.w * self.h)\n",
" obstacles_idx = np.random.choice(nodes_idx, size=number_obstacles, replace=False)\n",
" # print(f\"obstacles : {obstacles_idx}\")\n",
" for obstacles_idx in obstacles_idx:\n",
" self.add_obstacle(nodes[obstacles_idx][0], nodes[obstacles_idx][1])\n",
" \n",
" def add_path(self, x, y):\n",
" \"\"\"\n",
" args:\n",
" x: The x coordinate for where to add the obstacle.\n",
" y: The y coordinate for where to add the obstacle.\n",
" \"\"\"\n",
" # only add path if target or source is not (x,y)\n",
" if (x,y) != self.target and (x,y) != self.source:\n",
" self.g[y,x] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['path'] ]\n",
"\n",
" def add_paths(self, paths):\n",
" \"\"\"\n",
" args:\n",
" paths: A list of nodes to add to the found path.\n",
" \"\"\"\n",
" for x,y in paths:\n",
" self.add_path(x,y)\n",
"\n",
" # Add a single visited node to the list of visited nodes.\n",
" def add_visited_node(self, x, y):\n",
" \"\"\"\n",
" args:\n",
" x: The x coordinate for where to add the obstacle.\n",
" y: The y coordinate for where to add the obstacle.\n",
" \"\"\"\n",
" if (x,y) != self.target and (x,y) != self.source:\n",
" self.g[y,x] = self.COLOUR_MAPPING[ self.TYPE_MAPPING['visited'] ]\n",
" \n",
" # Add a list of visited nodes to the list of visited nodes.\n",
" def add_visited(self, paths):\n",
" \"\"\"\n",
" args:\n",
" paths: A list of nodes to add to the visited nodes list.\n",
" \"\"\"\n",
" for x,y in paths:\n",
" self.add_visited_node(x,y)\n",
"\n",
" def plot(self, plot_type, distance_fn=None):\n",
" \"\"\"\n",
" args:\n",
" plot_type: The type of matplotlib.pyplot to use to plot the graph.\n",
" distance_fn: A functiom which can be used to calculate distances\n",
" between vertices in the plot.\n",
" \n",
" \"\"\"\n",
" if plot_type == 'grid':\n",
" plt.figure( figsize=(self.w, self.h) )\n",
"\n",
" plt.imshow(\n",
" self.g,\n",
" interpolation = 'none',\n",
" cmap = self.cmap,\n",
" norm = self.norm,\n",
" extent=[0, self.w, self.h, 0] # to ensure that the coordinate plot is adjusted\n",
" )\n",
" plt.ylim((0, self.h))\n",
" plt.xlim((0, self.w))\n",
" plt.xticks(np.arange(0., self.w + 1., 1.))\n",
" plt.yticks(np.arange(0., self.h + 1., 1.))\n",
" plt.grid(which='major', axis='both', linestyle='-', color='k', linewidth=2)\n",
" plt.show()\n",
"\n",
" elif plot_type == 'graph' and distance_fn is not None:\n",
" # source : https://stackoverflow.com/questions/28372127/add-edge-weights-to-plot-output-in-networkx\n",
" plt.figure( figsize=(self.w, self.h) )\n",
" \n",
" self.g_nx = nx.Graph()\n",
" self.adjacency_list = self.get_adjacency_list(distance_fn)\n",
"\n",
" nodes = [ (x,y) for x in range(self.w) for y in range(self.h) ]\n",
"\n",
" for node in nodes:\n",
" self.g_nx.add_node(node, pos=node)\n",
"\n",
" for node in self.adjacency_list.keys():\n",
" for neighbour, distance in self.adjacency_list[node].items():\n",
" self.g_nx.add_edge(node, neighbour, weight=distance)\n",
"\n",
" pos = nx.get_node_attributes(self.g_nx, 'pos')\n",
" nx.draw(self.g_nx, pos)\n",
" labels = nx.get_edge_attributes(self.g_nx, 'weight')\n",
" nx.draw_networkx_edge_labels(self.g_nx, pos, edge_labels=labels)\n",
"\n",
"\n",
" def is_valid_coordinate(self, x, y):\n",
" \"\"\"\n",
" args:\n",
" x: The x coordinate to check if the coordinate is valid.\n",
" y: The y coordinate to check if the coordinate is valid.\n",
" \"\"\"\n",
" return (x >= 0) and (x < self.w) and (y >= 0) and (y < self.h)\n",
"\n",
" def add_neighbour(self, current, neighbour, distance_fn):\n",
" \"\"\"\n",
" args:\n",
" current: The node currently being expanded.\n",
" neighbor: The neighbor of the current node to be added to the adjacency list.\n",
" distance_fn : the distance fn to be used to calculate the distance between the current and neighbouring node\n",
" \"\"\"\n",
" x, y = neighbour\n",
" if self.is_valid_coordinate(x, y):\n",
" if self.g[y, x] != self.COLOUR_MAPPING[ self.TYPE_MAPPING['obstacle'] ]:\n",
" self.adjacency_list[current][neighbour] = distance_fn(current, neighbour)\n",
" \n",
" def get_adjacency_list(self, distance_fn):\n",
" '''\n",
" distance_fn needs to be a function that expects 2 inputs and returns float / int\n",
" return adjacency list with the following format\n",
" { (x_source, y_source) :\n",
" { \n",
" (x_neighbour, y_neighbour) : distance_to_neighbour,\n",
" (x_neighbour, y_neighbour) : distance_to_neighbour\n",
" }\n",
" }\n",
" '''\n",
"\n",
" self.adjacency_list = {}\n",
" np.random.seed(self.seed)\n",
" for x in range(self.w):\n",
" for y in range(self.h):\n",
" # obstacle node is not added in the adjacency list\n",
" if self.g[y,x] != self.COLOUR_MAPPING[ self.TYPE_MAPPING['obstacle'] ]:\n",
" self.adjacency_list[ (x,y) ] = {}\n",
"\n",
" self.add_neighbour((x,y), (x-1, y-1), distance_fn) # add bottom left\n",
" self.add_neighbour((x,y), (x, y-1), distance_fn) # add bottom\n",
" self.add_neighbour((x,y), (x+1, y-1), distance_fn) # add bottom right\n",
" self.add_neighbour((x,y), (x+1, y), distance_fn) # add right\n",
" self.add_neighbour((x,y), (x+1, y+1), distance_fn) # add top right\n",
" self.add_neighbour((x,y), (x, y+1), distance_fn) # add top\n",
" self.add_neighbour((x,y), (x-1, y+1), distance_fn) # add top left\n",
" self.add_neighbour((x,y), (x-1, y), distance_fn) # add left\n",
" return self.adjacency_list"
],
"metadata": {
"id": "Lq8tnx5UPD9k"
},
"execution_count": 3,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"**Distance Functions**"
],
"metadata": {
"id": "IPm-LUxH81rs"
}
},
{
"cell_type": "code",
"source": [
"def manhattan_distance(current, target):\n",
" \"\"\"\n",
" args:\n",
" current: The node currently being expanded.\n",
" target: The neighboring node to calculate the distance to. \n",
" \"\"\"\n",
" return abs(current[0] - target[0]) + abs(current[1] - target[1])\n",
"\n",
"def diagonal_distance(current, target, lateral_cost=1, diag_cost=sqrt(2)):\n",
" \"\"\"\n",
" args:\n",
" current: The node currently being expanded.\n",
" target: The neighboring node to calculate the distance to.\n",
" lateral_cost: The cost to travel laterally in a 2d grid.\n",
" diag_cost: The cost to travel diagonally in a 2d grid.\n",
" \"\"\"\n",
" # heuristic function for euclidean distance\n",
" dx = abs(current[0] - target[0])\n",
" dy = abs(current[1] - target[1])\n",
" h = lateral_cost*(dx+dy) + (diag_cost-2*lateral_cost)*min(dx,dy)\n",
" return h\n",
"\n",
"def euclidian_distance(current, target):\n",
" \"\"\"\n",
" args:\n",
" current: The node currently being expanded.\n",
" target: The neighboring node to calculate the distance to. \n",
" \"\"\"\n",
" return sqrt( (current[0]-target[0])**2 + (current[1]-target[1])**2 )\n",
"\n",
"def random_distance(current, target, upper_bound=10):\n",
" \"\"\"\n",
" args:\n",
" current: The node currently being expanded.\n",
" target: The neighboring node to calculate the distance to.\n",
" upper_bound: The exclusive upper bound of the random number that will be\n",
" used as the distance.\n",
" \"\"\"\n",
" return np.random.randint(upper_bound)"
],
"metadata": {
"id": "VEjOKcgRyzH0"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"**A* Algorithm**"
],
"metadata": {
"id": "0Isj-Ssf85UY"
}
},
{
"cell_type": "code",
"source": [
"class Astar:\n",
"\n",
" def __init__(self, adjacency_list):\n",
" \"\"\"\n",
" args:\n",
" adjacency_list: A nested dict of all nodes, their neighbors, and\n",
" the distances to each neighbor.\n",
" \"\"\"\n",
" self.adjacency_list = adjacency_list\n",
" self.visited = None\n",
" self.path_len = None\n",
"\n",
" def initialize_scores(self, init_value=float('inf')):\n",
" \"\"\"\n",
" args:\n",
" init_value: the starting g value for all nodes in the adjacency list.\n",
" \"\"\"\n",
" # initialize scores for all nodes as inf\n",
" scores = {}\n",
" for node in self.adjacency_list.keys():\n",
" scores[node] = init_value\n",
" return scores\n",
"\n",
" def reconstruct_path_and_cost(self, closed_set, current):\n",
" \"\"\"\n",
" args:\n",
" closed_set: The list of nodes that have already been explored.\n",
" current: A dict that maps nodes to their immediate predecessors on\n",
" the optimal path from start to goal.\n",
" \"\"\"\n",
" # to reconstruct shortest path and calculate cost of shortest path\n",
" path = [current]\n",
" cost = 0\n",
" while current in closed_set.keys():\n",
" cost += self.adjacency_list[current][ closed_set[current] ]\n",
" current = closed_set[current]\n",
" path = [current] + path\n",
" self.path_len = len(path)\n",
" return path, cost, len(self.visited)\n",
"\n",
" def search(self, source, target, heuristic_func):\n",
" \"\"\"\n",
" args:\n",
" source: The starting node of the search.\n",
" target: The goal of the search.\n",
" heuristic_func: The function used to calculate h(n) in the f function.\n",
" \"\"\"\n",
" self.visited = []\n",
"\n",
" # return source if source is target\n",
" if source == target:\n",
" return [target], 0\n",
"\n",
" open_set = heapdict()\n",
" open_set[source] = 0\n",
" closed_set = {}\n",
"\n",
" gscores = self.initialize_scores()\n",
" gscores[source] = 0\n",
" fscores = self.initialize_scores()\n",
" fscores[source] = heuristic_func(source, target)\n",
" \n",
" while len(open_set) != 0:\n",
"\n",
" current, f_score = open_set.popitem()\n",
" if current == target:\n",
" return self.reconstruct_path_and_cost(closed_set, current)\n",
" # Add current to visited nodes list.\n",
" if current not in self.visited:\n",
" self.visited.append(current)\n",
" for neighbour, distance in self.adjacency_list[current].items():\n",
" # Add neighbour to visited nodes list\n",
" if neighbour not in self.visited:\n",
" self.visited.append(neighbour)\n",
" neighbour_gscore = gscores[current] + distance\n",
" # print(f'Neighbour : {neighbour}, gscore : {neighbour_gscore}')\n",
" if neighbour_gscore < gscores[neighbour]:\n",
" closed_set[neighbour] = current\n",
" gscores[neighbour] = neighbour_gscore\n",
" fscores[neighbour] = neighbour_gscore + heuristic_func(neighbour, target)\n",
" if neighbour not in open_set.keys():\n",
" open_set[neighbour] = fscores[neighbour]\n",
" return [], float('Inf')"
],
"metadata": {
"id": "iG2lWBE0JkDt"
},
"execution_count": 5,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"**Variations of A\\***"
],
"metadata": {
"id": "5tbiBOnf5fLa"
}
},
{
"cell_type": "code",
"source": [
"class Weighted_Astar(Astar):\n",
" \"\"\"\n",
" Traditional A* will find the optimal solution to a graph search problem. Often\n",
" though, it's too expensive to find the optimal solution and a sub-optimal\n",
" solution is good enough.\n",
" Weighted A* is a variation of traditional A* that uses a constant scalar to\n",
" \"weight\" the importance of the heuristic value. The f-value is computed\n",
" as follows:\n",
"\n",
" f(n) = g(n) + epsilon * h(n)\n",
"\n",
" where epsilon >= 1\n",
" \"\"\"\n",
"\n",
" def __init__(self, adjacency_list, epsilon=2):\n",
" \"\"\"\n",
" args:\n",
" adjacency_list: A nested dict of all nodes, their neighbors, and\n",
" the distances to each neighbor.\n",
" epsilon: An integer that will be used to multiply the heuristic value\n",
" in the f function.\n",
" \"\"\"\n",
" self.epsilon = epsilon\n",
" super().__init__(adjacency_list)\n",
"\n",
" def search(self, source, target, heuristic_func):\n",
" \"\"\"\n",
" args:\n",
" source: The starting node of the search.\n",
" target: The goal of the search.\n",
" heuristic_func: The function used to calculate h(n) in the f function.\n",
" \"\"\"\n",
" self.visited = []\n",
" if source == target:\n",
" return [target], 0\n",
"\n",
" open_set = heapdict()\n",
" open_set[source] = 0\n",
" closed_set = {}\n",
"\n",
" gscores = self.initialize_scores()\n",
" gscores[source] = 0\n",
" fscores = self.initialize_scores()\n",
" fscores[source] = heuristic_func(source, target)\n",
" \n",
" while len(open_set) != 0:\n",
" current, f_score = open_set.popitem()\n",
" if current == target:\n",
" return self.reconstruct_path_and_cost(closed_set, current)\n",
" # Add current to visited nodes list.\n",
" if current not in self.visited:\n",
" self.visited.append(current)\n",
" for neighbour, distance in self.adjacency_list[current].items():\n",
" # Add neighbour to visited nodes list\n",
" if neighbour not in self.visited:\n",
" self.visited.append(neighbour)\n",
" neighbour_gscore = gscores[current] + distance\n",
" if neighbour_gscore < gscores[neighbour]:\n",
" closed_set[neighbour] = current\n",
" gscores[neighbour] = neighbour_gscore\n",
" fscores[neighbour] = neighbour_gscore + (self.epsilon * heuristic_func(neighbour, target))\n",
" if neighbour not in open_set.keys():\n",
" open_set[neighbour] = fscores[neighbour]\n",
" return [], float('Inf')\n",
"\n",
"\n",
"class Beam_Search(Astar):\n",
" \"\"\"\n",
" With traditional A*, the open set can grow unbounded as the grid is searched\n",
" for the lowest cost path from the source node to the goal node. With beam\n",
" search, at every iteration of the main loop, the open set is redefined to be\n",
" only the highest priority beam_size nodes in the open set. This is particularly\n",
" useful for search problems where storing the entire search tree in memory\n",
" is prohibitive, like in some machine language translation applications.\n",
" \"\"\"\n",
"\n",
" def __init__(self, adjacency_list, beam_size=3):\n",
" \"\"\"\n",
" args:\n",
" adjacency_list: A nested dict of all nodes, their neighbors, and\n",
" the distances to each neighbor.\n",
" beam_size: An integer that defines the maximum number of nodes that\n",
" will be explored at each iteration of the search.\n",
" \"\"\"\n",
" self.beam_size = beam_size\n",
" super().__init__(adjacency_list)\n",
"\n",
" def search(self, source, target, heuristic_func):\n",
" \"\"\"\n",
" args:\n",
" source: The starting node of the search.\n",
" target: The goal of the search.\n",
" heuristic_func: The function used to calculate h(n) in the f function.\n",
" \"\"\"\n",
" self.visited = []\n",
" if source == target:\n",
" return [target], 0\n",
"\n",
" open_set = heapdict()\n",
" open_set[source] = 0\n",
" closed_set = {}\n",
"\n",
" gscores = self.initialize_scores()\n",
" gscores[source] = 0\n",
" fscores = self.initialize_scores()\n",
" fscores[source] = heuristic_func(source, target)\n",
" \n",
" while len(open_set) != 0:\n",
" current, f_score = open_set.popitem()\n",
" if current == target:\n",
" return self.reconstruct_path_and_cost(closed_set, current)\n",
" # Add current to visited nodes list.\n",
" if current not in self.visited:\n",
" self.visited.append(current)\n",
" for neighbour, distance in self.adjacency_list[current].items():\n",
" # Add neighbour to visited nodes list\n",
" if neighbour not in self.visited:\n",
" self.visited.append(neighbour)\n",
" neighbour_gscore = gscores[current] + distance\n",
" if neighbour_gscore < gscores[neighbour]:\n",
" closed_set[neighbour] = current\n",
" gscores[neighbour] = neighbour_gscore\n",
" fscores[neighbour] = neighbour_gscore + heuristic_func(neighbour, target)\n",
" if neighbour not in open_set.keys():\n",
" open_set[neighbour] = fscores[neighbour]\n",
" # Enforce beam size.\n",
" if len(open_set) > self.beam_size:\n",
" new_hd = heapdict()\n",
" for i in range(self.beam_size):\n",
" item, priority = open_set.popitem()\n",
" new_hd[item] = priority\n",
" open_set = new_hd\n",
" return [], float('Inf')\n",
"\n"
],
"metadata": {
"id": "CGBBBthj5wLW"
},
"execution_count": 6,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"**Best First Search**"
],
"metadata": {
"id": "GjkMwrf9gNVa"
}
},
{
"cell_type": "code",
"source": [
"class BFS:\n",
" '''\n",
" This is the class for Best First Search (BFS). \n",
" BFS visits next state based on heuristics function f(n) = h with lowest heuristic value. \n",
" It doesn't take cost of the path into consideration for that specific state. \n",
" It only evaluates which next state from the current state has lowest heuristics. \n",
" Thus it is expected to be least efficient algorithm across our choices and serve as a base to others.\n",
" '''\n",
" # initialize the BFS object with adjacency_list property\n",
" def __init__(self, adjacency_list):\n",
" \"\"\"\n",
" args:\n",
" adjacency_list: A nested dict of all nodes, their neighbors, and\n",
" the distances to each neighbor.\n",
" \"\"\"\n",
" self.adjacency_list = adjacency_list\n",
" \n",
" # this is to keep scores and return them eventually\n",
" def initialize_scores(self, init_value=float('inf')):\n",
" \"\"\"\n",
" args:\n",
" init_value: the starting g value for all nodes in the adjacency list.\n",
" \"\"\"\n",
" scores = {}\n",
" for node in self.adjacency_list.keys():\n",
" scores[node] = init_value\n",
" return scores\n",
"\n",
" # since we are using a custom graph this method is related with it and nothing to do with actual algorithm\n",
" def reconstruct_path_and_cost(self, closed_set, current):\n",
" \"\"\"\n",
" args:\n",
" closed_set: The list of nodes that have already been explored.\n",
" current: A dict that maps nodes to their immediate predecessors on\n",
" the optimal path from start to goal.\n",
" \"\"\"\n",
" path = [current]\n",
" cost = 0\n",
" while current in closed_set.keys():\n",
" cost += self.adjacency_list[current][ closed_set[current] ]\n",
" current = closed_set[current]\n",
" path = [current] + path\n",
" self.path_len = len(path)\n",
" return path, cost, len(self.visited)\n",
"\n",
" # this is the actual search method where we visit the nodes and return the result if we find the target\n",
" def search(self, source, target, heuristic_func):\n",
" \"\"\"\n",
" args:\n",
" source: The starting node of the search.\n",
" target: The goal of the search.\n",
" heuristic_func: The function used to calculate h(n) in the f function.\n",
" \"\"\"\n",
" self.visited = []\n",
"\n",
" if source == target:\n",
" return [target], 0\n",
"\n",
" open_set = heapdict()\n",
" open_set[source] = 0\n",
" closed_set = {}\n",
" #this is different from the A* in a way it ignores the gscores and take only fscores into considerati\n",
" fscores = self.initialize_scores()\n",
" fscores[source] = heuristic_func(source, target)\n",
" \n",
" while len(open_set) != 0:\n",
"\n",
" current, f_score = open_set.popitem()\n",
" if current == target:\n",
" return self.reconstruct_path_and_cost(closed_set, current)\n",
" if current not in self.visited:\n",
" # Add current to visited nodes list.\n",
" self.visited.append(current)\n",
" for neighbour, distance in self.adjacency_list[current].items():\n",
" if neighbour not in self.visited:\n",
" # Add neighbour to visited nodes list.\n",
" self.visited.append(neighbour)\n",
" neighbour_fscore = fscores[current] \n",
" if neighbour_fscore < fscores[neighbour]:\n",
" closed_set[neighbour] = current\n",
" fscores[neighbour] = neighbour_fscore + heuristic_func(neighbour, target)\n",
" if neighbour not in open_set.keys():\n",
" open_set[neighbour] = fscores[neighbour] \n",
" return [], float('Inf')"
],
"metadata": {
"id": "rbatiQWcm9aj"
},
"execution_count": 7,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Experiment #1\n",
"\n",
"In the experiment below, each algorithm (Astar, BFS, Weighted_Astar, Beam_Search) is used to search each grid size (10, 20, 50) at each obstacle density (.1, .2, .4). This results in 36 separate trials, as can be seen in the table of results below.\n",
"\n",
"The RRT algorthm is run in a separate environment and source code and demos will be submitted separately."
],
"metadata": {
"id": "f-ty9fEnYVUn"
}
},
{
"cell_type": "code",
"source": [
"size_list = [10, 20, 50]\n",
"probability_obstacle_list = [0.1, 0.2, 0.4]\n",
"distance_fn_list = [euclidian_distance]\n",
"heuristic_func_list = [euclidian_distance]\n",
"search_algo_list = [Astar, BFS, Weighted_Astar, Beam_Search]\n",
"# generate all permutations based on the above parameters\n",
"all_exps = itertools.product(\n",
" size_list,\n",
" probability_obstacle_list,\n",
" distance_fn_list,\n",
" heuristic_func_list,\n",
" search_algo_list\n",
")\n",
"\n",
"def run_exp(parameters):\n",
" \"\"\"\n",
" args:\n",
" parameters: Values for the following parameters neededed to conduct a\n",
" search: graph sizes, probability densities, distance functions,\n",
" heuristic functions, search algorithms.\n",
" \"\"\"\n",
" # generate graph and run search algorithm based on the parameters\n",
" size, probability_obstacle, distance_fn, heuristic_func, search_algo = parameters\n",
" g = Graph(w=size, h=size)\n",
" g.generate_random_graph(probability_obstacle=probability_obstacle)\n",
" algo = search_algo( g.get_adjacency_list(distance_fn) )\n",
" search_result = algo.search(g.source, g.target, heuristic_func)\n",
" # record the approximate_shortest distance, paths, minimum cost, number of visited nodes\n",
" approx_shortest_distance = heuristic_func(g.source, g.target)\n",
" paths = search_result[0]\n",
" cost = search_result[1]\n",
" visited = search_result[2] if len(search_result) > 2 else None\n",
" return (size, probability_obstacle, distance_fn.__name__, heuristic_func.__name__, search_algo.__name__), approx_shortest_distance, paths, cost, visited\n",
"\n",
"def run_all_exps(all_exps, pool_size):\n",
" \"\"\"\n",
" args:\n",
" all_exps: A cross product of the following values for each trial: graph sizes, \n",
" probability densities, distance functions, heuristic functions,\n",
" search algorithms.\n",
" pool_size: The number of concurrent processes to use to run the experiment.\n",
" \"\"\"\n",
" # run exp in parallel based on the pool_size\n",
" with Pool(pool_size) as p:\n",
" return p.map(run_exp, all_exps)\n",
"\n",
"def process_exps_results(results):\n",
" \"\"\"\n",
" args:\n",
" results: A list of the return values from each trial.\n",
" \"\"\"\n",
" # convert results from list of tuples to list of dictionaries\n",
" # list of dictionaries can then be converted to pandas dataframe\n",
" output = []\n",
" for result in results:\n",
" parameters, approx_shortest_distance, paths, cost, visited = result\n",
" size, probability_obstacle, distance_fn, heuristic_func, search_algo = parameters\n",
" output += [{\n",
" 'size' : size,\n",
" 'probability_obstacle' : probability_obstacle,\n",
" 'distance_fn' : distance_fn,\n",
" 'heuristic_fn' : heuristic_func,\n",
" 'search_algo' : search_algo,\n",
" 'approx_shortest_distance' : approx_shortest_distance,\n",
" 'paths_length' : len(paths),\n",
" 'cost' : cost,\n",
" 'nodes_visited' : visited \n",
" }]\n",
" return output\n",
"\n",
"# run all experiments in parallel based on the defined parameters\n",
"results = run_all_exps(all_exps, 8)\n",
"results = process_exps_results(results)\n",
"results = pd.DataFrame.from_records(results)\n",
"# save results as csv file and download it\n",
"results.to_csv('experiment1_results.csv')\n",
"files.download('experiment1_results.csv')\n",
"# display results in pivot table\n",
"results[\"env\"] = list(zip(\n",
" results['size'],\n",
" results['probability_obstacle'],\n",
" results['distance_fn'],\n",
" results['heuristic_fn'],\n",
" results['approx_shortest_distance'] ))\n",
"results.pivot(index=['env', 'search_algo'],\n",
" columns=[],\n",
" values=['paths_length', 'cost', 'nodes_visited'])"
],
"metadata": {
"id": "NygiSOH-YZGU",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"outputId": "f2b29939-08dd-4a2c-85dd-a45737bed109"
},
"execution_count": 8,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Javascript object>"
],
"application/javascript": [
"\n",
" async function download(id, filename, size) {\n",
" if (!google.colab.kernel.accessAllowed) {\n",
" return;\n",
" }\n",
" const div = document.createElement('div');\n",
" const label = document.createElement('label');\n",
" label.textContent = `Downloading \"${filename}\": `;\n",
" div.appendChild(label);\n",
" const progress = document.createElement('progress');\n",
" progress.max = size;\n",
" div.appendChild(progress);\n",
" document.body.appendChild(div);\n",
"\n",
" const buffers = [];\n",
" let downloaded = 0;\n",
"\n",
" const channel = await google.colab.kernel.comms.open(id);\n",
" // Send a message to notify the kernel that we're ready.\n",
" channel.send({})\n",
"\n",
" for await (const message of channel.messages) {\n",
" // Send a message to notify the kernel that we're ready.\n",
" channel.send({})\n",
" if (message.buffers) {\n",
" for (const buffer of message.buffers) {\n",
" buffers.push(buffer);\n",
" downloaded += buffer.byteLength;\n",
" progress.value = downloaded;\n",
" }\n",
" }\n",
" }\n",
" const blob = new Blob(buffers, {type: 'application/binary'});\n",
" const a = document.createElement('a');\n",
" a.href = window.URL.createObjectURL(blob);\n",
" a.download = filename;\n",
" div.appendChild(a);\n",
" a.click();\n",
" div.remove();\n",
" }\n",
" "
]
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Javascript object>"
],
"application/javascript": [
"download(\"download_cc87e554-eef6-41f2-88fc-cfbeafb6c8b8\", \"experiment1_results.csv\", 3400)"
]
},
"metadata": {}
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
" paths_length \\\n",
"env search_algo \n",
"(10, 0.1, euclidian_distance, euclidian_distanc... Astar 5.0 \n",
" BFS 5.0 \n",
" Weighted_Astar 5.0 \n",
" Beam_Search 5.0 \n",
"(10, 0.2, euclidian_distance, euclidian_distanc... Astar 5.0 \n",
" BFS 5.0 \n",
" Weighted_Astar 5.0 \n",
" Beam_Search 5.0 \n",
"(10, 0.4, euclidian_distance, euclidian_distanc... Astar 5.0 \n",
" BFS 5.0 \n",
" Weighted_Astar 5.0 \n",
" Beam_Search 5.0 \n",
"(20, 0.1, euclidian_distance, euclidian_distanc... Astar 7.0 \n",
" BFS 7.0 \n",
" Weighted_Astar 7.0 \n",
" Beam_Search 7.0 \n",
"(20, 0.2, euclidian_distance, euclidian_distanc... Astar 7.0 \n",
" BFS 7.0 \n",
" Weighted_Astar 7.0 \n",
" Beam_Search 7.0 \n",
"(20, 0.4, euclidian_distance, euclidian_distanc... Astar 7.0 \n",
" BFS 7.0 \n",
" Weighted_Astar 7.0 \n",
" Beam_Search 7.0 \n",
"(50, 0.1, euclidian_distance, euclidian_distanc... Astar 33.0 \n",
" BFS 33.0 \n",
" Weighted_Astar 33.0 \n",
" Beam_Search 33.0 \n",
"(50, 0.2, euclidian_distance, euclidian_distanc... Astar 33.0 \n",
" BFS 33.0 \n",
" Weighted_Astar 33.0 \n",
" Beam_Search 33.0 \n",
"(50, 0.4, euclidian_distance, euclidian_distanc... Astar 33.0 \n",
" BFS 33.0 \n",
" Weighted_Astar 35.0 \n",
" Beam_Search 38.0 \n",
"\n",
" cost \\\n",
"env search_algo \n",
"(10, 0.1, euclidian_distance, euclidian_distanc... Astar 4.000000 \n",
" BFS 4.000000 \n",
" Weighted_Astar 4.000000 \n",
" Beam_Search 4.000000 \n",
"(10, 0.2, euclidian_distance, euclidian_distanc... Astar 4.000000 \n",
" BFS 4.000000 \n",
" Weighted_Astar 4.000000 \n",
" Beam_Search 4.000000 \n",
"(10, 0.4, euclidian_distance, euclidian_distanc... Astar 4.828427 \n",
" BFS 4.828427 \n",
" Weighted_Astar 4.828427 \n",
" Beam_Search 4.828427 \n",
"(20, 0.1, euclidian_distance, euclidian_distanc... Astar 8.071068 \n",
" BFS 8.071068 \n",
" Weighted_Astar 8.071068 \n",
" Beam_Search 8.071068 \n",
"(20, 0.2, euclidian_distance, euclidian_distanc... Astar 8.071068 \n",
" BFS 8.071068 \n",
" Weighted_Astar 8.071068 \n",
" Beam_Search 8.071068 \n",
"(20, 0.4, euclidian_distance, euclidian_distanc... Astar 8.071068 \n",
" BFS 8.071068 \n",
" Weighted_Astar 8.071068 \n",
" Beam_Search 8.071068 \n",
"(50, 0.1, euclidian_distance, euclidian_distanc... Astar 39.041631 \n",
" BFS 42.355339 \n",
" Weighted_Astar 39.870058 \n",
" Beam_Search 39.041631 \n",
"(50, 0.2, euclidian_distance, euclidian_distanc... Astar 39.041631 \n",
" BFS 40.698485 \n",
" Weighted_Astar 40.698485 \n",
" Beam_Search 39.041631 \n",
"(50, 0.4, euclidian_distance, euclidian_distanc... Astar 39.870058 \n",
" BFS 41.526912 \n",
" Weighted_Astar 41.870058 \n",
" Beam_Search 42.798990 \n",
"\n",
" nodes_visited \n",
"env search_algo \n",
"(10, 0.1, euclidian_distance, euclidian_distanc... Astar 16.0 \n",
" BFS 29.0 \n",
" Weighted_Astar 16.0 \n",
" Beam_Search 16.0 \n",
"(10, 0.2, euclidian_distance, euclidian_distanc... Astar 14.0 \n",
" BFS 24.0 \n",
" Weighted_Astar 14.0 \n",
" Beam_Search 14.0 \n",
"(10, 0.4, euclidian_distance, euclidian_distanc... Astar 11.0 \n",
" BFS 12.0 \n",
" Weighted_Astar 10.0 \n",
" Beam_Search 11.0 \n",
"(20, 0.1, euclidian_distance, euclidian_distanc... Astar 31.0 \n",
" BFS 58.0 \n",
" Weighted_Astar 32.0 \n",
" Beam_Search 32.0 \n",
"(20, 0.2, euclidian_distance, euclidian_distanc... Astar 27.0 \n",
" BFS 50.0 \n",
" Weighted_Astar 28.0 \n",
" Beam_Search 28.0 \n",
"(20, 0.4, euclidian_distance, euclidian_distanc... Astar 21.0 \n",
" BFS 39.0 \n",
" Weighted_Astar 21.0 \n",
" Beam_Search 21.0 \n",
"(50, 0.1, euclidian_distance, euclidian_distanc... Astar 350.0 \n",
" BFS 834.0 \n",
" Weighted_Astar 124.0 \n",
" Beam_Search 161.0 \n",
"(50, 0.2, euclidian_distance, euclidian_distanc... Astar 309.0 \n",
" BFS 695.0 \n",
" Weighted_Astar 109.0 \n",
" Beam_Search 149.0 \n",
"(50, 0.4, euclidian_distance, euclidian_distanc... Astar 231.0 \n",
" BFS 448.0 \n",
" Weighted_Astar 92.0 \n",
" Beam_Search 126.0 "
],
"text/html": [
"\n",
" <div id=\"df-7474c3be-1009-48f0-9679-e727167dca7e\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th></th>\n",
" <th>paths_length</th>\n",
" <th>cost</th>\n",
" <th>nodes_visited</th>\n",
" </tr>\n",
" <tr>\n",
" <th>env</th>\n",
" <th>search_algo</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(10, 0.1, euclidian_distance, euclidian_distance, 4.0)</th>\n",
" <th>Astar</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>16.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>29.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>16.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>16.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(10, 0.2, euclidian_distance, euclidian_distance, 4.0)</th>\n",
" <th>Astar</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>14.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>24.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>14.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>5.0</td>\n",
" <td>4.000000</td>\n",
" <td>14.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(10, 0.4, euclidian_distance, euclidian_distance, 4.0)</th>\n",
" <th>Astar</th>\n",
" <td>5.0</td>\n",
" <td>4.828427</td>\n",
" <td>11.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>5.0</td>\n",
" <td>4.828427</td>\n",
" <td>12.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>5.0</td>\n",
" <td>4.828427</td>\n",
" <td>10.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>5.0</td>\n",
" <td>4.828427</td>\n",
" <td>11.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(20, 0.1, euclidian_distance, euclidian_distance, 7.810249675906654)</th>\n",
" <th>Astar</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>31.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>58.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>32.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>32.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(20, 0.2, euclidian_distance, euclidian_distance, 7.810249675906654)</th>\n",
" <th>Astar</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>27.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>50.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>28.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>28.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(20, 0.4, euclidian_distance, euclidian_distance, 7.810249675906654)</th>\n",
" <th>Astar</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>21.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>39.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>21.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>7.0</td>\n",
" <td>8.071068</td>\n",
" <td>21.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(50, 0.1, euclidian_distance, euclidian_distance, 36.235341863986875)</th>\n",
" <th>Astar</th>\n",
" <td>33.0</td>\n",
" <td>39.041631</td>\n",
" <td>350.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>33.0</td>\n",
" <td>42.355339</td>\n",
" <td>834.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>33.0</td>\n",
" <td>39.870058</td>\n",
" <td>124.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>33.0</td>\n",
" <td>39.041631</td>\n",
" <td>161.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(50, 0.2, euclidian_distance, euclidian_distance, 36.235341863986875)</th>\n",
" <th>Astar</th>\n",
" <td>33.0</td>\n",
" <td>39.041631</td>\n",
" <td>309.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>33.0</td>\n",
" <td>40.698485</td>\n",
" <td>695.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>33.0</td>\n",
" <td>40.698485</td>\n",
" <td>109.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>33.0</td>\n",
" <td>39.041631</td>\n",
" <td>149.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(50, 0.4, euclidian_distance, euclidian_distance, 36.235341863986875)</th>\n",
" <th>Astar</th>\n",
" <td>33.0</td>\n",
" <td>39.870058</td>\n",
" <td>231.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>33.0</td>\n",
" <td>41.526912</td>\n",
" <td>448.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>35.0</td>\n",
" <td>41.870058</td>\n",
" <td>92.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>38.0</td>\n",
" <td>42.798990</td>\n",
" <td>126.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-7474c3be-1009-48f0-9679-e727167dca7e')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-7474c3be-1009-48f0-9679-e727167dca7e button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-7474c3be-1009-48f0-9679-e727167dca7e');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
]
},
"metadata": {},
"execution_count": 8
}
]
},
{
"cell_type": "markdown",
"source": [
"# Experiment #2\n",
"\n",
"For this experiment, we wanted to see how the results differ when the grid sizes are larger and the obstacle density is smaller (compared to experiment #1).\n",
"\n",
"The RRT algorthm is run in a separate environment and source code and demos will be submitted separately."
],
"metadata": {
"id": "Y4LAroVPp4v0"
}
},
{
"cell_type": "code",
"source": [
"size_list = [75, 100, 125]\n",
"probability_obstacle_list = [0.05]\n",
"distance_fn_list = [euclidian_distance]\n",
"heuristic_func_list = [euclidian_distance]\n",
"search_algo_list = [Astar, BFS, Weighted_Astar, Beam_Search]\n",
"# generate all permutations based on the above parameters\n",
"all_exps = itertools.product(\n",
" size_list,\n",
" probability_obstacle_list,\n",
" distance_fn_list,\n",
" heuristic_func_list,\n",
" search_algo_list\n",
")\n",
"\n",
"def run_exp(parameters):\n",
" \"\"\"\n",
" args:\n",
" parameters: Values for the following parameters neededed to conduct a\n",
" search: graph sizes, probability densities, distance functions,\n",
" heuristic functions, search algorithms.\n",
" \"\"\"\n",
" # generate graph and run search algorithm based on the parameters\n",
" size, probability_obstacle, distance_fn, heuristic_func, search_algo = parameters\n",
" g = Graph(w=size, h=size)\n",
" g.generate_random_graph(probability_obstacle=probability_obstacle)\n",
" algo = search_algo( g.get_adjacency_list(distance_fn) )\n",
" search_result = algo.search(g.source, g.target, heuristic_func)\n",
" # record the approximate_shortest distance, paths, minimum cost, number of visited nodes\n",
" approx_shortest_distance = heuristic_func(g.source, g.target)\n",
" paths = search_result[0]\n",
" cost = search_result[1]\n",
" visited = search_result[2] if len(search_result) > 2 else None\n",
" return (size, probability_obstacle, distance_fn.__name__, heuristic_func.__name__, search_algo.__name__), approx_shortest_distance, paths, cost, visited\n",
"\n",
"def run_all_exps(all_exps, pool_size):\n",
" \"\"\"\n",
" args:\n",
" all_exps: A cross product of the following values for each trial: graph sizes, \n",
" probability densities, distance functions, heuristic functions,\n",
" search algorithms.\n",
" pool_size: The number of concurrent processes to use to run the experiment.\n",
" \"\"\"\n",
" # run exp in parallel based on the pool_size\n",
" with Pool(pool_size) as p:\n",
" return p.map(run_exp, all_exps)\n",
"\n",
"def process_exps_results(results):\n",
" \"\"\"\n",
" args:\n",
" results: A list of the return values from each trial.\n",
" \"\"\"\n",
" # convert results from list of tuples to list of dictionaries\n",
" # list of dictionaries can then be converted to pandas dataframe\n",
" output = []\n",
" for result in results:\n",
" parameters, approx_shortest_distance, paths, cost, visited = result\n",
" size, probability_obstacle, distance_fn, heuristic_func, search_algo = parameters\n",
" output += [{\n",
" 'size' : size,\n",
" 'probability_obstacle' : probability_obstacle,\n",
" 'distance_fn' : distance_fn,\n",
" 'heuristic_fn' : heuristic_func,\n",
" 'search_algo' : search_algo,\n",
" 'approx_shortest_distance' : approx_shortest_distance,\n",
" 'paths_length' : len(paths),\n",
" 'cost' : cost,\n",
" 'nodes_visited' : visited \n",
" }]\n",
" # number of visited nodes\n",
" # approximate shortest distance\n",
" return output\n",
"\n",
"# run all experiments in parallel based on the defined parameters\n",
"results = run_all_exps(all_exps, 8)\n",
"results = process_exps_results(results)\n",
"results = pd.DataFrame.from_records(results)\n",
"# save results as csv file and download it\n",
"results.to_csv('experiment2_results.csv')\n",
"files.download('experiment2_results.csv')\n",
"# display results in pivot table\n",
"results[\"env\"] = list(zip(\n",
" results['size'],\n",
" results['probability_obstacle'],\n",
" results['distance_fn'],\n",
" results['heuristic_fn'],\n",
" results['approx_shortest_distance'] ))\n",
"results.pivot(index=['env', 'search_algo'],\n",
" columns=[],\n",
" values=['paths_length', 'cost', 'nodes_visited'])"
],
"metadata": {
"id": "qNDUI5AXsvwc",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 457
},
"outputId": "7885694c-e0a3-4db0-c9d3-d5c03a042b82"
},
"execution_count": 9,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Javascript object>"
],
"application/javascript": [
"\n",
" async function download(id, filename, size) {\n",
" if (!google.colab.kernel.accessAllowed) {\n",
" return;\n",
" }\n",
" const div = document.createElement('div');\n",
" const label = document.createElement('label');\n",
" label.textContent = `Downloading \"${filename}\": `;\n",
" div.appendChild(label);\n",
" const progress = document.createElement('progress');\n",
" progress.max = size;\n",
" div.appendChild(progress);\n",
" document.body.appendChild(div);\n",
"\n",
" const buffers = [];\n",
" let downloaded = 0;\n",
"\n",
" const channel = await google.colab.kernel.comms.open(id);\n",
" // Send a message to notify the kernel that we're ready.\n",
" channel.send({})\n",
"\n",
" for await (const message of channel.messages) {\n",
" // Send a message to notify the kernel that we're ready.\n",
" channel.send({})\n",
" if (message.buffers) {\n",
" for (const buffer of message.buffers) {\n",
" buffers.push(buffer);\n",
" downloaded += buffer.byteLength;\n",
" progress.value = downloaded;\n",
" }\n",
" }\n",
" }\n",
" const blob = new Blob(buffers, {type: 'application/binary'});\n",
" const a = document.createElement('a');\n",
" a.href = window.URL.createObjectURL(blob);\n",
" a.download = filename;\n",
" div.appendChild(a);\n",
" a.click();\n",
" div.remove();\n",
" }\n",
" "
]
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Javascript object>"
],
"application/javascript": [
"download(\"download_9abd27be-4274-4068-97e9-e0cdee88bec1\", \"experiment2_results.csv\", 1333)"
]
},
"metadata": {}
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
" paths_length \\\n",
"env search_algo \n",
"(75, 0.05, euclidian_distance, euclidian_distan... Astar 26.0 \n",
" BFS 26.0 \n",
" Weighted_Astar 26.0 \n",
" Beam_Search 26.0 \n",
"(100, 0.05, euclidian_distance, euclidian_dista... Astar 72.0 \n",
" BFS 76.0 \n",
" Weighted_Astar 72.0 \n",
" Beam_Search 72.0 \n",
"(125, 0.05, euclidian_distance, euclidian_dista... Astar 66.0 \n",
" BFS 66.0 \n",
" Weighted_Astar 66.0 \n",
" Beam_Search 66.0 \n",
"\n",
" cost \\\n",
"env search_algo \n",
"(75, 0.05, euclidian_distance, euclidian_distan... Astar 25.828427 \n",
" BFS 31.627417 \n",
" Weighted_Astar 25.828427 \n",
" Beam_Search 25.828427 \n",
"(100, 0.05, euclidian_distance, euclidian_dista... Astar 76.798990 \n",
" BFS 95.710678 \n",
" Weighted_Astar 76.798990 \n",
" Beam_Search 76.798990 \n",
"(125, 0.05, euclidian_distance, euclidian_dista... Astar 88.610173 \n",
" BFS 89.438600 \n",
" Weighted_Astar 88.610173 \n",
" Beam_Search 88.610173 \n",
"\n",
" nodes_visited \n",
"env search_algo \n",
"(75, 0.05, euclidian_distance, euclidian_distan... Astar 135.0 \n",
" BFS 680.0 \n",
" Weighted_Astar 82.0 \n",
" Beam_Search 95.0 \n",
"(100, 0.05, euclidian_distance, euclidian_dista... Astar 1108.0 \n",
" BFS 5363.0 \n",
" Weighted_Astar 231.0 \n",
" Beam_Search 336.0 \n",
"(125, 0.05, euclidian_distance, euclidian_dista... Astar 785.0 \n",
" BFS 3897.0 \n",
" Weighted_Astar 298.0 \n",
" Beam_Search 333.0 "
],
"text/html": [
"\n",
" <div id=\"df-0a953794-fe0f-463b-8db2-cbe287502081\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th></th>\n",
" <th>paths_length</th>\n",
" <th>cost</th>\n",
" <th>nodes_visited</th>\n",
" </tr>\n",
" <tr>\n",
" <th>env</th>\n",
" <th>search_algo</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(75, 0.05, euclidian_distance, euclidian_distance, 25.079872407968907)</th>\n",
" <th>Astar</th>\n",
" <td>26.0</td>\n",
" <td>25.828427</td>\n",
" <td>135.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>26.0</td>\n",
" <td>31.627417</td>\n",
" <td>680.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>26.0</td>\n",
" <td>25.828427</td>\n",
" <td>82.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>26.0</td>\n",
" <td>25.828427</td>\n",
" <td>95.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(100, 0.05, euclidian_distance, euclidian_distance, 72.3671196055225)</th>\n",
" <th>Astar</th>\n",
" <td>72.0</td>\n",
" <td>76.798990</td>\n",
" <td>1108.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>76.0</td>\n",
" <td>95.710678</td>\n",
" <td>5363.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>72.0</td>\n",
" <td>76.798990</td>\n",
" <td>231.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>72.0</td>\n",
" <td>76.798990</td>\n",
" <td>336.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th rowspan=\"4\" valign=\"top\">(125, 0.05, euclidian_distance, euclidian_distance, 86.45229898620394)</th>\n",
" <th>Astar</th>\n",
" <td>66.0</td>\n",
" <td>88.610173</td>\n",
" <td>785.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BFS</th>\n",
" <td>66.0</td>\n",
" <td>89.438600</td>\n",
" <td>3897.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Weighted_Astar</th>\n",
" <td>66.0</td>\n",
" <td>88.610173</td>\n",
" <td>298.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beam_Search</th>\n",
" <td>66.0</td>\n",
" <td>88.610173</td>\n",
" <td>333.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-0a953794-fe0f-463b-8db2-cbe287502081')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-0a953794-fe0f-463b-8db2-cbe287502081 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-0a953794-fe0f-463b-8db2-cbe287502081');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
]
},
"metadata": {},
"execution_count": 9
}
]
},
{
"cell_type": "markdown",
"source": [
"# Quickstart"
],
"metadata": {
"id": "Hw8R_vRTX1_S"
}
},
{
"cell_type": "markdown",
"source": [
"**Manual Graph Creation**"
],
"metadata": {
"id": "2EYyMJhCO0kh"
}
},
{
"cell_type": "code",
"source": [
"# initialize grid with width=4 and height=2\n",
"# note that x can range from 0 (inclusive) to 3 (inclusive)\n",
"# note that y can range from 0 (inclusive) to 1 (inclusive)\n",
"g = Graph(4,2)\n",
"\n",
"# add obstacle at coordinate x=1, y=1\n",
"g.add_obstacle(1,1)\n",
"\n",
"# add obstacle at coordinate x=0, y=1\n",
"g.add_obstacle(0,1)\n",
"\n",
"# add source at coordinate x=0, y=0\n",
"g.add_source(0,0)\n",
"\n",
"# add target at coordinate x=3, y=1\n",
"g.add_target(3,1)\n",
"\n",
"# one of the shortest path\n",
"g.add_path(1,0)\n",
"g.add_path(2,0)\n",
"\n",
"# source : blue, obstacle : black, path : cream, target : green\n",
"g.plot('grid')"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 161
},
"id": "WTBDMU2gZJn3",
"outputId": "aed2a665-5293-4d3f-86cd-93d65128e637"
},
"execution_count": 10,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 288x144 with 1 Axes>"
],
"image/png": "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\n"
},
"metadata": {
"needs_background": "light"
}
}
]
},
{
"cell_type": "code",
"source": [
"from pprint import pprint\n",
"# generate adjacency list of the graph\n",
"# note that obstacles coordinates are excluded (0,1) and (1,1)\n",
"'''\n",
"input :\n",
"distance_fn needs to be a function that expects 2 inputs and returns float / int\n",
" for constant distance : lambda current, neighbour : 3\n",
" for linear distance : lambda current, neighbour : abs(current[0] - neighbour[0]) + abs(current[1] - neighbour[1])\n",
" for random distance : lambda current, neighbour : np.random.randint(10)\n",
"\n",
"output format\n",
"{ (x_source, y_source) : [ \n",
" (distance_to_neighbour, (x_neighbour, y_neighbour)),\n",
" (distance_to_neighbour, (x_neighbour, y_neighbour))\n",
"] }\n",
"'''\n",
"\n",
"distance_fn = lambda current, neighbour : abs(current[0] - neighbour[0]) + abs(current[1] - neighbour[1])\n",
"\n",
"pprint( g.get_adjacency_list(distance_fn = distance_fn) )\n",
"\n",
"# to visualize adjacency list as a graph\n",
"g.plot(\n",
" plot_type = 'graph',\n",
" distance_fn = distance_fn\n",
")"
],
"metadata": {
"id": "m_fBxpRbhWzb",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 282
},
"outputId": "7ffedc3f-f6c6-4df4-a97c-bcc19c09fcdf"
},
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"{(0, 0): {(1, 0): 1},\n",
" (1, 0): {(0, 0): 1, (2, 0): 1, (2, 1): 2},\n",
" (2, 0): {(1, 0): 1, (2, 1): 1, (3, 0): 1, (3, 1): 2},\n",
" (2, 1): {(1, 0): 2, (2, 0): 1, (3, 0): 2, (3, 1): 1},\n",
" (3, 0): {(2, 0): 1, (2, 1): 2, (3, 1): 1},\n",
" (3, 1): {(2, 0): 2, (2, 1): 1, (3, 0): 1}}\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 288x144 with 1 Axes>"
],
"image/png": "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\n"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": [
"# Qucickstart Example: Weighted A*, epsilon=2\n",
"Performance Note:\n",
"\n",
"Compared to regular Astar, using weighted Astar with an epsilon of 2 delivers\n",
"a 79% decrease in nodes visited for only a 5% increase in path length. See how regular Astar performed on the same grid below."
],
"metadata": {
"id": "oPm16p2eolhJ"
}
},
{
"cell_type": "code",
"source": [
"zxi = Graph(50,50)\n",
"# generate graph with probability of obstacle = 0.3\n",
"zxi.generate_random_graph(0.3)\n",
"\n",
"distance_fn = lambda current, neighbour : abs(current[0] - neighbour[0]) + abs(current[1] - neighbour[1])\n",
"a = Weighted_Astar(zxi.get_adjacency_list(distance_fn), epsilon=2)\n",
"\n",
"paths, cost, visited = a.search(zxi.source, zxi.target, diagonal_distance)\n",
"zxi.add_visited(a.visited)\n",
"zxi.add_paths(paths)\n",
"# source : blue, obstacle : black, path : cream, target : green\n",
"# visualize sample problem\n",
"print(\"Visited: \", len(a.visited))\n",
"print(\"Path length: \", a.path_len)\n",
"print(\"Cost :\", cost)\n",
"zxi.plot('grid')"
],
"metadata": {
"id": "Rg7Lei6gom0e",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"outputId": "0aea3433-5909-4237-dcef-7d7737b55f09"
},
"execution_count": 12,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Visited: 107\n",
"Path length: 41\n",
"Cost : 49\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 3600x3600 with 1 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAACsYAAArBCAYAAADSQcgvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADxW0lEQVR4nOzcf8yu910X8PenPZMusvJzG5MDqWHgouhmPBJkEseQH651LDChSJepzEYSpGGRjiYExWSZBCNVopBmMJtsIAQ2wAYJZKURjGGes25kpLAQ3AwDrBjIWICRbl//6H3CcbTnvu/T52mf693XKznpc9/PdT33993P97rO88f7XLPWCgAAAAAAAAAAAABs3XVP9QIAAAAAAAAAAAAA4CQoxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVzh1y0My8L8nvJ/lIkkfWWhdm5pOT/HCSm5K8L8lXr7V+93SWCQAAAAAAAAAAAABXd8wTY79orfWitdaF3etvTfL2tdZnJ3n77jUAAAAAAAAAAAAAPCWOKcZ+rK9Icu/u63uTvOIJrwYAAAAAAAAAAAAArtGhxdiV5Gdm5tLM3L5777lrrd/aff3bSZ574qsDAAAAAAAAAAAAgAOdO/C4v7nW+sDMPCfJz87Mr1z5zbXWmpn1WCfuirSXy7R/7dqXCgAAAAAAAAAAAMDTyO+stZ59zAkHFWPXWh/Y/ffhmXlbks9L8r9n5nlrrd+ameclefhxzr0nyT1JMjNrrcfsz27WzCRJ2nIlf5KtVdvM2ufVzF7cntaZteVKerPJtT2t2dpzNWudmVzb0X6dNc+sLZu9uD3te1Gu7WjN1n5fbNa6F+Xajvb7R/PM2rLJtT2t2dwXt8de3Ka2eSX9e7EtV9J/nbVq3ott2Xa53n/sedcd8IP/7Mw86/LXSb40yXuS/GSSV+8Oe3WSnzj2wwEAAAAAAAAAAADgpBzyxNjnJnnbrnl7LskPrrV+emb+R5IfmZmvz6ON3K8+vWUCAAAAAAAAAAAAwNXtLcautX49yQsf4/3/m+SLT2NRAAAAAAAAAAAAAHCs657qBQAAAAAAAAAAAADASVCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqHFyMnZnrZ+bBmblv9/qlM/POmXnPzNw7M+dOb5kAAAAAAAAAAAAAcHXHPDH2jiQPJcnMXJfk3iS3rrU+N8n7k7z65JcHAAAAAAAAAAAAAIc56CmvM3M+yc1JXp/ktUk+Jckfr7XeuzvkZ5PcleT7D/hZ17bSM641VzMz46ywF7endWatuZLebHJtT2u21lzNWmcmF2dF88yaszVqnldrNrm2pzkb29K6F+XirGieWWs2ubanOVuj5nk1Z2vUPK/WbK252J7mvdic7RiHPjH27iR3Jvno7vXvJDk3Mxd2r1+Z5DMe68SZuX1mLs7MxSeyUAAAAAAAAAAAAAC4mr1PjJ2ZW5I8vNa6NDMvSZK11pqZW5N898x8XJKfSfKRxzp/rXVPknt2P2ud0LrhCVurazs+Hdr+rTNrzdWsdWZtuZLebO25mpnZtrTNK3H/2Kq2eSX9MwNOX9u90d/RnDWte7EtV9KbTa7tac32dPi7rHVmcm1Haza5tqc1m1zb05rN71Xb074X23Ilvdmu9f6xtxib5MVJXj4zL0tyQ5IbZ+bNa63bknzh7sO/NMnnXNMKAAAAAAAAAAAAAOAEXLfvgLXWXWut82utm5LcmuT+tdZtM/OcJNk9MfZ1Sb7vVFcKAAAAAAAAAAAAAFextxh7Fd8yMw8l+aUk/3mtdf8JrQkAAAAAAAAAAAAAjjZrrSfvw2aevA+DPZ7Mvf9kmJmnegmnrnVmrbmatc6sLVfSm609VzMz25a2eSXuH1vVNq+kf2bA6Wu7N/o7mrOmdS+25Up6s8m1Pa3Zng5/l7XOTK7taM0m1/a0ZpNre1qz+b1qe9r3YluupDfbLteltdaFY857Ik+MBQAAAAAAAAAAAIAzQzEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFQ4uxs7M9TPz4Mzct3v9xTPzzpl518z8wsw8//SWCQAAAAAAAAAAAABXd8wTY+9I8tAVr783ydettV6U5AeTfNsJrgsAAAAAAAAAAAAAjnJQMXZmzie5Ockbr3h7Jblx9/UnJPnNk10aAAAAAAAAAAAAABzu3IHH3Z3kziTPuuK91yT5qZn5wyQfTPL5J7s0OF0z81QvgSO1zqw1V7PWmbXmSnqzteZqZmbb0jyv5myNzAvgT2u9N7bmYnta92JrrqQ3m1zb05ytVevM5Nqe1mxybU9rNrm2pzlbq9aZybU9zdmOsfeJsTNzS5KH11qXPuZb35zkZWut80nelOTfPM75t8/MxZm5+IRXCwAAAAAAAAAAAACPY9ZaVz9g5g1JXpXkkSQ3JLkxyc8lecFa67N2x3xmkp9ea/3FPT/r6h+2Yfv+P27R5fZ4Wza5tqc1m1zb05qtNVfiX0JtVfNebMsm1/a0ZmvP1ax1Zm25kt5s7ddZ27yS/r0o13a0Zmu/LzZr3YtybYf7x3a17cfW66w1V9KbTa7taf+7rG1mT4e92Jat/RpLemfWmovNubTWunDMCXufGLvWumutdX6tdVOSW5Pcn+QrknzCzHzO7rAvSfLQkYsFAAAAAAAAAAAAgBNz7lpOWms9MjP/OMmPzcxHk/xukn90oisDAAAAAAAAAAAAgCMcVYxdaz2Q5IHd129L8raTXxIAAAAAAAAAAAAAHO+6p3oBAAAAAAAAAAAAAHASFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQ4dyhB87M9UkuJvnAWuuWmfn5JM/affs5Sd6x1nrFyS8RAAAAAAAAAAAAAPY7uBib5I4kDyW5MUnWWl94+Rsz82NJfuJklwYAAAAAAAAAAAAAh7vukINm5nySm5O88TG+d2OSlyb58RNdGQAAAAAAAAAAAAAc4dAnxt6d5M4kz3qM770iydvXWh88oTVt0sw81Us4Na3Z5Nqe1mxybU9rttZcbE/zXmzNJtf2tGZrzdWsdWatuZLubI2a59WaTa7tac7GtrTuRbng9LXuR7m2pzWbXJwVrTNrzZV0Z2vVOrPWXPTb+8TYmbklycNrrUuPc8jXJvmhq5x/+8xcnJmL17hGAAAAAAAAAAAAANhr1lpXP2DmDUleleSRJDckuTHJW9dat83Mpyb51SSfvtb6o70fNrP2fd7WXG7Ft+VKerPJtT3t//qkbWb24nY1z6wtW/tebGYvbkvbvBL3xa1qm1fSP7Nmbfux/b7YlivpzSbX9rRma8/VrHVmcm1Ha7bWXEn/vbFtZvbidrXN7OmwF9uyuca2p30vtuVKerO152pWOrNLa60Lx5y394mxa6271lrn11o3Jbk1yf1rrdt2335lkvsOKcUCAAAAAAAAAAAAwGnaW4zd49YkP3QSCwEAAAAAAAAAAACAJ+LcMQevtR5I8sAVr19ysssBAAAAAAAAAAAAgGvzRJ8YCwAAAAAAAAAAAABngmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFDh4GLszFw/Mw/OzH271zMzr5+Z987MQzPzTae3TAAAAAAAAAAAAAC4unNHHHtHkoeS3Lh7/Q+SfEaSF6y1PjozzznhtQEAAAAAAAAAAADAwQ4qxs7M+SQ3J3l9ktfu3v6GJH9/rfXRJFlrPXzgz7qGZZ59rbmS3mxycVa0zqw1V7PmmTVnY1vsxW1pnldztkbmxVnSuh/l2p7WbHJtT2u21lzNWmcm1/a0ZmvN1ax1Zq25mrXOrDVX0p2tUfO8WrO15kp6s7XmamZmj7ruwOPuTnJnko9e8d5nJfmambk4M/9lZj77sU6cmdt3x1x8YksFAAAAAAAAAAAAgMe394mxM3NLkofXWpdm5iVXfOvjkvzRWuvCzHxlkh9I8oUfe/5a654k9+x+1lprncS6z4zLDeu2XIn2OGdP23XWev9ozZX0ZmvNlfRmk2t7WrP5fXG7Wvdia65mrTNry5X0ZpNre54O98ZGzXuxLVv7NdY2r6R/L7bmgrPEdbYtbfNK+u/5cm2H+8e2tM8r6Z1ZW66kN5tc29Oa7Vrv+XuLsUlenOTlM/OyJDckuXFm3pzkN5K8dXfM25K86ZpWAAAAAAAAAAAAAAAn4Lp9B6y17lprnV9r3ZTk1iT3r7VuS/LjSb5od9jfSvLe01okAAAAAAAAAAAAAOxzyBNjH8+/SvKWmfnmJB9K8pqTWRIAAAAAAAAAAAAAHO+oYuxa64EkD+y+/r0kN5/4igAAAAAAAAAAAADgGlz3VC8AAAAAAAAAAAAAAE6CYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqHFyMnZnrZ+bBmblv9/o/zsz/nJl37f686NRWCQAAAAAAAAAAAAB7nDvi2DuSPJTkxive+5a11o+e7JIAAAAAAAAAAAAA4HgHPTF2Zs4nuTnJG093OQAAAAAAAAAAAABwbQ59YuzdSe5M8qyPef/1M/PtSd6e5FvXWh/e94Nm5qgFbkVrLjhLWq8zubanNVtrrqQ3m1zb05yNbWndi625mrXOrDVX0ptNLjhdzXuxOVuj5nm1ZmvNBWeJ62xbmufVmk0uzgoz257WmbXmSnqzybU9zdmOsfeJsTNzS5KH11qXPuZbdyV5QZK/nuSTk7zucc6/fWYuzszFJ7pYAAAAAAAAAAAAAHg8s9a6+gEzb0jyqiSPJLkhyY1J3rrWuu2KY16S5J+ttW7Z87PWvs/bmssN67ZcSW+29lZ827yS/r0o13a4f2xP636Ua3tas7XnatY6M7m2ozVba66kN1t7rmatM2vN1ax1ZnJtR/t11jazp8NebMvWmivpzdZ+X2R7Wq+xtlyJ+wdnT9t15v7BWdO8F9uy7XJdWmtdOOa8vU+MXWvdtdY6v9a6KcmtSe5fa902M8/bffAkeUWS9xy7aAAAAAAAAAAAAAA4KeeewLlvmZlnJ5kk70ryT05kRQAAAAAAAAAAAABwDY4qxq61HkjywO7rl57CegAAAAAAAAAAAADgmlz3VC8AAAAAAAAAAAAAAE6CYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqHFyMnZnrZ+bBmbnvY97/dzPzoZNfGgAAAAAAAAAAAAAc7pgnxt6R5KEr35iZC0k+6URXBAAAAAAAAAAAAADX4KBi7MycT3Jzkjde8d71Sb4ryZ2nszQAAAAAAAAAAAAAONy5A4+7O48WYJ91xXvfmOQn11q/NTMHf+Axx25Ja66kO1uj5nm1ZpOLs6J5Zq3Z5Nqe1mytuZq1zkyu7WnN1por6c3WmqtZ68xaczVrnZlcnBWtM2vNlfRma82VdGeDs6D1GmvNBWdJ63XWmovtad6LzdmOsfeJsTNzS5KH11qXrnjvzyX5e0m+54Dzb5+ZizNz8QmtFAAAAAAAAAAAAACuYtZaVz9g5g1JXpXkkSQ3JLkxyYd3f/5od9hnJvn1tdbz9/yste/ztuZyw7otV9KbTa7tac3WnovtaduLSf9+bJtZ630x6c3Wfo01sxe3pW1eifvilrXOTK7taM0m1/a0ZpNre1qzPR1+r2plLwLXqvX+0ZqrWevMWnMBXKu2+2JSf2+8tNa6cMwJe58Yu9a6a611fq11U5Jbk9y/1vqktdanrbVu2r3/B/tKsQAAAAAAAAAAAABwmvYWYwEAAAAAAAAAAABgC84dc/Ba64EkDzzG+x9/QusBAAAAAAAAAAAAgGviibEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqHFyMnZnrZ+bBmblv9/r7Z+bdM/NLM/OjM/Pxp7dMAAAAAAAAAAAAALi6Y54Ye0eSh654/c1rrReutf5Kkv+V5BtPdGUAAAAAAAAAAAAAcIRzhxw0M+eT3Jzk9UlemyRrrQ/uvjdJnplkHfizrmmhZ11rrqQ3m1zb05qtNRfbYy9uT+vMWnMl3dnYFntxW5rn1ZytVevM5Nqe1mxybU9rNrm2pzkb22IvAteq9f7RmqtZ68xacwFcK/fFfoc+MfbuJHcm+eiVb87Mm5L8dpIXJPmexzpxZm6fmYszc/EJrBMAAAAAAAAAAAAArmrvE2Nn5pYkD6+1Ls3MS6783lrrH87M9Xm0FPs1Sd70seevte5Jcs/uZ621Dnqw7GZcbo+35Up6s7U3/tvmldiLW9U2r8TMtqj9/iHXdrRma88FZ0XbNZb03z/aciX998a2mT0d9mJbNrm2pzVbe65mrTOTaztas7XmSnqzybU9rdnk2p6nw++MjZr3Ylu21lxJ//2jbWbt8+JPHPLE2BcnefnMvC/Jf0ry0pl58+VvrrU+snv/q05lhQAAAAAAAAAAAABwgL3F2LXWXWut82utm5LcmuT+JK+amecnyTxao355kl85zYUCAAAAAAAAAAAAwNWcu8bzJsm9M3Pj7ut3J/mGE1sVAAAAAAAAAAAAABzpqGLsWuuBJA/sXr74pBcDAAAAAAAAAAAAANfquqd6AQAAAAAAAAAAAABwEhRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUOHgYuzMXD8zD87MfbvXb5mZX52Z98zMD8zMM05vmQAAAAAAAAAAAABwdcc8MfaOJA9d8fotSV6Q5C8neWaS15zgugAAAAAAAAAAAADgKAcVY2fmfJKbk7zx8ntrrZ9aO0nekeT86SwRAAAAAAAAAAAAAPY7d+Bxdye5M8mzPvYbM/OMJK/Ko0+U3WtmDl3bprTmSrqzNWqeV3O2Rua1Pc0za80m1/a0ZmvNBWdF8zXWmq01V7PWmbXmSnqzybU9rdlaczVrnZlc29OarTVX0ptNru1pzSYXnK7mvdiarTVXMzNjq/Y+MXZmbkny8Frr0uMc8h+S/Ne11s8/zvm3z8zFmbn4BNYJAAAAAAAAAAAAAFc1a62rHzDzhjz6RNhHktyQ5MYkb11r3TYz/zzJX03ylWutj+79sJmrf9iG7fv/uEUa/9vUvBfbsrXnatY6s7ZcSW82ubanNVt7rmZmti1t80r67x9tuZLebHJtT2s2ubbH7x/b0j6vpHdmcm1Ha7bWXElvNrm2pzVbe65mrTOTazueDtdZq7b92L4X2+aV9N4bd7kurbUuHHPe3ifGrrXuWmudX2vdlOTWJPfvSrGvSfJlSb72kFIsAAAAAAAAAAAAAJymvcXYq/i+JM9N8t9n5l0z8+0ntCYAAAAAAAAAAAAAONq5Yw5eaz2Q5IHd10edCwAAAAAAAAAAAACn6Yk8MRYAAAAAAAAAAAAAzgzFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFQ4uBg7M9fPzIMzc9/u9TfOzK/NzJqZTz29JQIAAAAAAAAAAADAfsc8MfaOJA9d8fq/JfnbSd5/oisCAAAAAAAAAAAAgGtwUDF2Zs4nuTnJGy+/t9Z6cK31vlNaFwAAAAAAAAAAAAAc5dyBx92d5M4kzzq9pWzbzDzVS4Ak3XuxNVtrrmatM2vNlfRmk2t7WrO15mpmZtvSPK/WbK25kt5scm1Paza5OCvMbHtaZybX9rRma82V9GaTa3tas7XmatY6M7ng9NmP29I8r+Zsx9j7xNiZuSXJw2utS9fyATNz+8xcnJmL13I+AAAAAAAAAAAAABzikCfGvjjJy2fmZUluSHLjzLx5rXXbIR+w1ronyT1JMjPrmld6xq3VF+1ye7wtW3uuZq0za83F9rTtxaT/OpNrO1qzybU9rdnk2p723xmbZ9aWzV7cHjPbltZ7R9KbTa7tcV/clvZ5JWa2Ra0zk2s7WrPJtT2t2dpzwVnSep215mpmZo/a+8TYtdZda63za62bktya5P5DS7EAAAAAAAAAAAAA8GTZW4x9PDPzTTPzG0nOJ/mlmXnjyS0LAAAAAAAAAAAAAI4zT+ajc2em6zm9V2h7BHHS/0js1lzNWmfWmovtaduLSf91Jtd2tGaTa3tas8m1Pe2/MzbPrC2bvbg9ZrYtrfeOpDebXNvjvrgt7fNKzGyLWmcm13a0ZpNre1qzteeCs6T1OmvN1ax0ZpfWWheOOe+anxgLAAAAAAAAAAAAAGeJYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUOHgYuzMXD8zD87MfbvXf35mfnFmfm1mfnhm/szpLRMAAAAAAAAAAAAAru6YJ8bekeShK15/Z5LvXms9P8nvJvn6k1wYAAAAAAAAAAAAABxj1lr7D5o5n+TeJK9P8tokfzfJ/0nyaWutR2bmbyT5F2utL9vzc/Z/GAAAAAAAAAAAAAAkl9ZaF4454dAnxt6d5M4kH929/pQkv7fWemT3+jeSfPpjnTgzt8/MxZm5eMzCAAAAAAAAAAAAAOAY5/YdMDO3JHl4rXVpZl5y7Aeste5Jcs/uZ9U+MfaQJ+9uzcwk6csm1/ZcztaqbWZPh73Ylq39Gkt6ZybXdrRma8/F9tiLcPpar7PWXM1aZ9aai+1p3YttuZLebHJtT/s938y2p21mT4f7R1s2ubanNZv7/fa078W2XElvNveP7bEX/397i7FJXpzk5TPzsiQ3JLkxyb9N8okzc2731NjzST5wTSsAAAAAAAAAAAAAgBNw3b4D1lp3rbXOr7VuSnJrkvvXWl+X5OeSvHJ32KuT/MSprRIAAAAAAAAAAAAA9thbjL2K1yV57cz8WpJPSfL9J7MkAAAAAAAAAAAAADjeuWMOXms9kOSB3de/nuTzTn5JAAAAAAAAAAAAAHC8J/LEWAAAAAAAAAAAAAA4MxRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUGFvMXZmbpiZd8zMu2fml2fmO3bvv3Rm3jkz75mZe2fm3OkvFwAAAAAAAAAAAAAe2yFPjP1wkpeutV6Y5EVJvnxmviDJvUluXWt9bpL3J3n1qa0SAAAAAAAAAAAAAPbYW4xdj/rQ7uUzdn8+kuSP11rv3b3/s0m+6nSWCAAAAAAAAAAAAAD7nTvkoJm5PsmlJM9P8u+TvCPJuZm5sNa6mOSVST7j1Fa5ATPzVC/h1LRmk4uzonVmrbmS7mytWmcm1/a0ZmvNxfbYi3D6Wq+z1lzNWmfWmovtad2LrbmS3mxycVaY2fa0zqw1V9KbTa7tac7WqHlerdlacyXd2Ro1z6s52zH2PjE2SdZaH1lrvSjJ+SSfl+QvJbk1yXfPzDuS/H4efYrsnzIzt8/MxZm5eDJLBgAAAAAAAAAAAIA/bdZax50w8+1J/mCt9a+veO9Lk7xmrfXVe85dx37eWXe5Yd2WK+nNJtf2tGaTa3v8q5rtatuPrdeZa2y7WvdiW66kN5tc29OarTVX0v/3dNvM2ueVmNnWtM0r6b3nt+dqZmbb0javxP2Ds6d1L7blSnqzybU9rdnk2p723z+aZ9aWzV7cnvKZXVprXTjmhL1PjJ2ZZ8/MJ+6+fmaSL0nyKzPznN17H5fkdUm+7+jlAgAAAAAAAAAAAMAJOXfAMc9Lcu/MXJ9Hi7Q/sta6b2a+a2Zu2b33vWut+09zoQAAAAAAAAAAAABwNfNkPhZ4ZlbbY4hbH4ed9GaTa3tas8m1PeWPna/Wth9brzPX2Ha17sW2XElvNrm2pzVba66k/+/ptpm1zysxs61pm1fSe89vz9XMzLalbV6J+wdnT+tebMuV9GaTa3tas8m1Pe2/fzTPrC2bvbg95TO7tNa6cMwJ153WSgAAAAAAAAAAAADgyaQYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABUUIwFAAAAAAAAAAAAoIJiLAAAAAAAAAAAAAAVFGMBAAAAAAAAAAAAqKAYCwAAAAAAAAAAAEAFxVgAAAAAAAAAAAAAKijGAgAAAAAAAAAAAFBBMRYAAAAAAAAAAACACoqxAAAAAAAAAAAAAFRQjAUAAAAAAAAAAACggmIsAAAAAAAAAAAAABUUYwEAAAAAAAAAAACooBgLAAAAAAAAAAAAQAXFWAAAAAAAAAAAAAAqKMYCAAAAAAAAAAAAUEExFgAAAAAAAAAAAIAKirEAAAAAAAAAAAAAVFCMBQAAAAAAAAAAAKCCYiwAAAAAAAAAAAAAFRRjAQAAAAAAAAAAAKigGAsAAAAAAAAAAABABcVYAAAAAAAAAAAAACooxgIAAAAAAAAAAABQQTEWAAAAAAAAAAAAgAqKsQAAAAAAAAAAAABU2FuMnZkbZuYdM/PumfnlmfmO3ftfPDPvnJl3zcwvzMzzT3+5AAAAAAAAAAAAAPDYDnli7IeTvHSt9cIkL0ry5TPz+Um+N8nXrbVelOQHk3zbaS0SAAAAAAAAAAAAAPY5t++AtdZK8qHdy2fs/qzdnxt3739Ckt885ANn5vhVbkBrrqQ3m1zb05pNLjh9rfuxNRfb07oXW3Mlvdnk2p7WbK25mpnZ9pjZtjTPqzVba65mZrYtzfNqzsa2tO7F1lxJbza5tqc1m1ycFc0za87WyLz67S3GJsnMXJ/kUpLnJ/n3a61fnJnXJPmpmfnDJB9M8vmPc+7tSW4/ofUCAAAAAAAAAAAAwGOaRx8Ie+DBM5+Y5G1J/mmSf5nkO3cl2W9J8hfWWq/Zc/7hHwan7Ji9vwWX/yVDW66k/19ptM3s6bAX27K1X2NJ78zk2o7266xtZvbidrXNrH1eiZltkZltS9u8EjPbGr9XbVfbzJ4Oe7EtW3uuZq0za8uV9O/Htpm1z6tZ615sy5X0ZpNre9zzt6ttP7bvxbZ5Jb33xl2uS2utC8ecd90xB6+1fi/JzyX5O0leuNb6xd23fjjJFxzzswAAAAAAAAAAAADgJO0txs7Ms3dPis3MPDPJlyR5KMknzMzn7A67/B4AAAAAAAAAAAAAPCXO/T927j3WsvM+6/jzG8+0MUqDgbpgmqAAkSmoEFuYKBAVikVwaBEUCShFRNxaiwh6o+KiChWCqMS1qBWRkNu0GCkCrFwoGIIVwFyMwGamOG4cG5eLQKWBAsJNTVWjJC9/zJ7ElBnvfZyzc8565vORjjyz99pnvz+/a69zZH29DjjmjiQPzswtuRrSPrTWenhmvi7J+2bmU0n+V5Lfd8R1AgAAAAAAAAAAAMDL2hvGrrWeSnL3dR7/QJIPHGNRAAAAAAAAAAAAAHBSF856AQAAAAAAAAAAAABwGoSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABUu7jtgZl6V5J8l+fzd8e9da/3JmfnnSb5gd9gXJXlirfVVx1ooAAAAAAAAAAAAALycvWFskheT3LvWemFmLiV5bGY+uNb6smsHzMz7knz/sRYJAAAAAAAAAAAAAPvsDWPXWivJC7u/Xtp9rWvPz8xrktyb5PceY4FwLDNz1ks4ita5mrXuWetcSfdsrVr3zFycF6171jpXM3u2PfZse+zZttiv7Wnds9a5mrXuWetcSe9srXM1a92z1rma2TPOi9ZzsXWupHc2c8HxOR+3pXm/mmc7iQuHHDQzt8zMk0l+NMmH1lqPv+Tpr0ryj9ZaH7/Ba++fmcszc/mzXSwAAAAAAAAAAAAA3MhcvSHsgQfP3JbkA0m+fq31kd1jH0zyPWut9x3w+nWS99uCa4V121xJ72zm2p72/5Ohbc/a9wvOk9brR9tcSe9s5tqez8x2xgs5Zdd+/TDXdrT/yti8Z22z9c9VNlh6f06ba3va//tH257dDOdi22ztn7Fmredi21xJ72ztczVr3TNzbUfrbO1zNWvds7a5kt7ZzLU9rbPt5rqy1rrnJK876I6x16y1nk/yaJK37d70C5O8KcnfO8n3AQAAAAAAAAAAAIDTtjeMnZnbd3eKzczcmuStSZ7dPf1bkzy81vrJo60QAAAAAAAAAAAAAA5w8YBj7kjy4Mzckqsh7UNrrYd3z/2OJH/2WIsDAAAAAAAAAAAAgEPtDWPXWk8lufsGz335aS8IAAAAAAAAAAAAAF6JC2e9AAAAAAAAAAAAAAA4DcJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgAp7w9iZedXMPDEzH56Zp2fmnbvHZ2a+fWaem5lnZuYbjr9cAAAAAAAAAAAAALi+iwcc82KSe9daL8zMpSSPzcwHk/ziJK9L8iVrrU/NzBcdc6EAAAAAAAAAAAAA8HL2hrFrrZXkhd1fL+2+VpJ3JPmda61P7Y770WMtEgAAAAAAAAAAAAD2OeSOsZmZW5JcSfKGJO9aaz0+M78wyVfPzG9J8t+TfMNa64cO+F6fzXrPrda5kt7ZzMV5Yc+AV6r1+tE6V9I7m7m2p3U0c3FeNO9Z62y9c5UOlt7ZzMV50bpnrXMl3bOxLa3nYutcSe9srXM1a90zc21P62ytczVr3bPWuZLe2cy1Pc2zncSFQw5aa31yrXVXktcmedPMfGmSz0/yk2ute5J8d5Lvvd5rZ+b+mbk8M5dPac0AAAAAAAAAAAAA8P+ZtdbJXjDzbUl+IsnXJvkNa63/OFcz4+fXWj99z2vXSd/vvLtWWLfNlfTX4217djOci22ztc/VzJ5xXrSei21zJb2zmWt7Pn3N/1NnuozT96eu/mM998iZLuO0zZ33JembK+mdrXWupHe2T89Vdsm/drlvmyt56Wxdw7X+/tE6V9I7m7m2p3U2c21P+39jtGfb07ZnN8P1o202c21P+3WxVfO52DbbzfAZa90zc21H+efsyu4Grgfbe8fYmbl9Zm7b/fnWJG9N8mySv53k1+4O+zVJnjvJGwMAAAAAAAAAAADAabp4wDF3JHlwZm7J1ZD2obXWwzPzWJL3zMw3J3khV+8gCwAAAAAAAAAAAABnYm8Yu9Z6Ksnd13n8+SRfeYQ1AQAAAAAAAAAAAMCJXTjrBQAAAAAAAAAAAADAaRDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQYW8YOzOvmpknZubDM/P0zLxz9/hfm5n/ODNP7r7uOvpqAQAAAAAAAAAAAOAGLh5wzItJ7l1rvTAzl5I8NjMf3D33R9Za7z3e8gAAAAAAAAAAAADgMLPWOvzgmZ+W5LEk79h9PXySMHZmDn8zAAAAAAAAAAAAAG5mV9Za95zkBRcOOWhmbpmZJ5P8aJIPrbUe3z317TPz1Mz85Zn5/Bu89v6ZuTwzl0+yMAAAAAAAAAAAAAA4iZPeMfa2JB9I8vVJ/meS/5rk85I8kOTfr7X+9J7Xr5O83xbMTJKkba6kdzZzbU/rbO1zNWvds7a5kt7Z2udie1rPxba5kpfOdsYLOWUuH5w3bZ+xpP9ztp575KyXcKrmzvvOeglH1/ZzuvX3j9a5kt7ZzLU9rbOZa3taZ/Pfqzhv2j5jievHVrXtV9J/LrbO1ax1z9rmSm6O87GRc3FzjnPH2GvWWs8neTTJ29ZaH1tXvZjk+5K86STfCwAAAAAAAAAAAABO094wdmZu390pNjNza5K3Jnl2Zu7YPTZJvirJR463TAAAAAAAAAAAAAB4eRcPOOaOJA/OzC25GtI+tNZ6eGb+8czcnmSSPJnkDxxvmQAAAAAAAAAAAADw8vaGsWutp5LcfZ3H7z3KigAAAAAAAAAAAADgFbhw1gsAAAAAAAAAAAAAgNMgjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACosDeMnZlXzcwTM/PhmXl6Zt75U57/rpl54XhLBAAAAAAAAAAAAID9Lh5wzItJ7l1rvTAzl5I8NjMfXGv9q5m5J8nPOO4SAQAAAAAAAAAAAGC/vXeMXVdduyPspd3XmplbkvyFJH/0iOsDAAAAAAAAAAAAgIMccsfY7CLYK0nekORda63HZ+Ybk/ydtdbHZubgNzzJsVvSOlfSO5u5tqd1tta5mrXuWetcSe9srXOxPa3nYutcSVI8GpwLPmPbM3fed9ZL4IRaf06ba3taZzPX9rTOZq7taZ4NzoPmz1jzbI2a96t1tta5mrXuWetcbI9zsd9BYexa65NJ7pqZ25J8YGZ+dZLfluTL9712Zu5Pcv9nsUYAAAAAAAAAAAAA2GvWWid7wcy3JZkk70jyk7uHf16S/7DWesOe157szTbkpP8et6C9jG/bs/b9YnvaPmPJZz5nbbPdDNeP1j0z13a0ztZ+/Wjbr6T/XDTXdrRfP9ie9dwjZ72EU3XtDriFl49P35G57drYes1vnSvpna39Z3TbfiX956K5tqN1tta5kt7Z/CzbHnu2La3XjqR3tva5mrXuWdtcSf/52LZnN8O52Dbbbq4ra617TvK6Cwd849t3d4rNzNya5K27N/o5a63Xr7Ven+Qn9kWxAAAAAAAAAAAAAHBMFw845o4kD87MLbka0j601nr4uMsCAAAAAAAAAAAAgJPZG8autZ5KcveeY159aisCAAAAAAAAAAAAgFfgwlkvAAAAAAAAAAAAAABOgzAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoMLeMHZmXjUzT8zMh2fm6Zl55+7xd+8ee2pm3jszrz7+cgEAAAAAAAAAAADg+g65Y+yLSe5da70xyV1J3jYzb07yzWutN661flmS/5zkDx1vmQAAAAAAAAAAAADw8i7uO2CttZK8sPvrpd3XWmt9PElmZpLcmmQda5EAAAAAAAAAAAAAsM9c7V73HDRzS5IrSd6Q5F1rrT+2e/z7knxFko8m+cq11k/s+T7iWQAAAAAAAAAAAAAOcWWtdc9JXnDhkIPWWp9ca92V5LVJ3jQzX7p7/Pcm+blJnkny1dd77czcPzOXZ+bySRYGAAAAAAAAAAAAACdx0B1j/58XzHxbkp9Ya/3Flzz2q5P80bXWb9zz2nXS9zvvZiZJ0jZX8pnZ4Lxo+5y1Xj9a50p6Z3O957xp+4wl/Z+ztj1rvd4nzsWtcS5uV+GWpXzLsp575KyXcKrmzvuSOBe3qO2afzP8LGubrf9ndNd+Jf3norm2w/Vje1rPR+fi9rSfi+bajtbZXBe3p/1cbJsr6f+ctXIubs7p3zF2Zm6fmdt2f741yVuT/NuZecPusUnym5I8e+LlAgAAAAAAAAAAAMApuXjAMXckeXBmbsnVkPahJH8vyT+fmdckmSQfTvKOo60SAAAAAAAAAAAAAPbYG8autZ5Kcvd1nnrL6S8HAAAAAAAAAAAAAF6ZC2e9AAAAAAAAAAAAAAA4DcJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAq7A1jZ+ZVM/PEzHx4Zp6emXfuHn/PzPzbmfnIzHzvzFw6/nIBAAAAAAAAAAAA4PoOuWPsi0nuXWu9McldSd42M29O8p4kX5Lklya5NcnXHmuRAAAAAAAAAAAAALDPxX0HrLVWkhd2f720+1prrb9/7ZiZeSLJaw95w5l5Bcs8/1rngvOk9XNmru1png3OA5+x7Wnds9a5mrXuWetczWzZ9syd9531Eo7Cubg9rdf81rmS7tkaNe9X62zm4rxo3rPm2Ro171frbObanubZGjXvV+tsrXOxPc7FfofcMTYzc8vMPJnkR5N8aK31+Eueu5Tk7Un+wQ1ee//MXJ6Zy6ewXgAAAAAAAAAAAAC4rrl6Q9gDD565LckHknz9Wusju8e+O8n/Xmt90wGvP/zNNuYk/x634loZ3zabubandbb2uZq17lnbXEnvbO2fs7b9SpyLW9W2X0n/nrVyLm5P4ZZ9+s6jbbN9eq7nHjnbhZyy1jvgvlTtuVg2WOvvwcnN8LOsa8+ci9vVtmc3w7nYNlvrXEnvbK6L29O+Z62az8W22XzGtsu5yHnRei62zZX0zrab68pa656TvO6gO8Zes9Z6PsmjSd62e9M/meT2JH/4JN8HAAAAAAAAAAAAAE7b3jB2Zm7f3Sk2M3NrkrcmeXZmvjbJfUm+Zq31qaOuEgAAAAAAAAAAAAD2uHjAMXckeXBmbsnVkPahtdbDM/OJJP8pyb/c3a72/WutP328pQIAAAAAAAAAAADAje0NY9daTyW5+zqPHxLVAgAAAAAAAAAAAMDnxIWzXgAAAAAAAAAAAAAAnAZhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAECFvWHszLxqZp6YmQ/PzNMz887d439oZv7dzKyZ+cLjLxUAAAAAAAAAAAAAbuziAce8mOTetdYLM3MpyWMz88Ek/yLJw0n+yRHXBwAAAAAAAAAAAAAH2RvGrrVWkhd2f720+1prrX+TJDNzvNUBAAAAAAAAAAAAwIEOuWNsZuaWJFeSvCHJu9Zajx91VRvUHAi3zmau7WmdrXWuZq171jpX0j1bo+b9ap6tkf3ivHAubk/zlrXONnfed9ZL4IRqz8XSwVrnata6Z61zNWvds9a5kt7ZWudKumdrZL84L5rPxebZ2BbnIudF67nYOlfSPdtJXDjkoLXWJ9dadyV5bZI3zcyXHvoGM3P/zFyemcuvcI0AAAAAAAAAAAAAsNdBd4y9Zq31/Mw8muRtST5y4GseSPJAkszMOvEKOXNrdW3btSreXNvROlv7/6HRtl+Jc5Hzp/VcbJsr6Z3NXNvTOpu5tsfvH/C5sZ575KyXcOqu3d237dJ47bLYds33s2y72vbsZjgX22Yz1/a4Lm5P6/loru1x/diW9v1K7NnWtO1X0nvNb50r6Z2tfa5m9uyqvXeMnZnbZ+a23Z9vTfLWJM++oncDAAAAAAAAAAAAgCPZG8YmuSPJozPzVJJ/neRDa62HZ+YbZuaHk7w2yVMz8z3HXCgAAAAAAAAAAAAAvJyL+w5Yaz2V5O7rPP5dSb7rGIsCAAAAAAAAAAAAgJM65I6xAAAAAAAAAAAAAHDuCWMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKuwNY2fmVTPzxMx8eGaenpl37h7/+TPz+Mz8u5n5WzPzecdfLgAAAAAAAAAAAABc3yF3jH0xyb1rrTcmuSvJ22bmzUn+XJK/vNZ6Q5L/leT3H22VAAAAAAAAAAAAALDH3jB2XfXC7q+Xdl8ryb1J3rt7/MEkX3WMBQIAAAAAAAAAAADAIS4ectDM3JLkSpI3JHlXkn+f5Pm11id2h/xwki8+ygo5czNz1ks4CnNtT/NsjZr3q3k2tqX1XGydK+mdzVzb0zqbuQD+X3PnfWe9hKNpvTS2XvNb52rWumetcyW9s5mL86J5z1pnMxfnhT3bHnu2Lc371Tpb61xJ72ytczWzZ1ftvWNskqy1PrnWuivJa5O8KcmXHPoGM3P/zFyemcuvbIkAAAAAAAAAAAAAsN9Bd4y9Zq31/Mw8muRXJrltZi7u7hr72iT/5QaveSDJA0kyM2ut9Vku+Xy5Vli3zZX0ztY+F9vjXNye1j1rmyu5Oc7HRs3nYtts5tqe9uti257dDOdi22ytcyU3w/XjrFdwuq5t13rukbNdyBFcuwtu7Z7VzlU2WG6G62LXnrXvV9K7Z+bajpvhc9aq7Xxs/Zy1zpX0ztY+VzN7ti1t+5XYsy1yzYfzae8dY2fm9pm5bffnW5O8NckzSR5N8lt3h/3uJN9/pDUCAAAAAAAAAAAAwF6H3DH2jiQPzswtuRrSPrTWenhmPprkb87Mn0nyb5K8+4jrBAAAAAAAAAAAAICXtTeMXWs9leTu6zz+H5K86RiLAgAAAAAAAAAAAICTunDWCwAAAAAAAAAAAACA0yCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACgwt4wdmZeNzOPzsxHZ+bpmfnG3eNvnJl/OTM/ODN/d2Zec/zlAgAAAAAAAAAAAMD1HXLH2E8k+Za11i9J8uYkf3BmfkmS70nyx9davzTJB5L8keMtEwAAAAAAAAAAAABe3qy1TvaCme9P8leSvDfJbWutNTOvS/LILp59udee7M0AAAAAAAAAAAAAuFldWWvdc5IXHHLH2E+bmdcnuTvJ40meTvKbd0/9tiSvu8Fr7p+ZyzNz+STvBQAAAAAAAAAAAAAncfAdY2fm1Un+aZJvX2u9f2a+JMl3JflZSf5Okm9Ya/2sPd9jnfQOtefdzCRJ2uZKemcz1/a0zmau7Wmd7dpczezZtrTtV+L6AZ8rrZ+xtrmS3tlcF7er7FTMtVNxPffI2S7kCObO+5L07hnb42fZtrTtV+L3qq1q26/EubhlrXtmru1onc1c29M6m7m2p3W21rmS3tnMtT2ts+3mOvEdYy8e+M0vJXlfkvestd6fJGutZ5P8+t3zdyb5ypO8MQAAAAAAAAAAAACcpgv7Dpirye27kzyz1vqOlzz+Rbt/XkjyJ5L81WMtEgAAAAAAAAAAAAD22RvGJnlLkrcnuXdmntx9fUWSr5mZ55I8m+RHknzfEdcJAAAAAAAAAAAAAC/r4r4D1lqPJZkbPP2dp7scAAAAAAAAAAAAAHhlDrljLAAAAAAAAAAAAACce8JYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgAp7w9iZed3MPDozH52Zp2fmG3eP3zUz/2pmnpyZyzPzpuMvFwAAAAAAAAAAAACu7+IBx3wiybestX5gZr4gyZWZ+VCSP5/knWutD87MV+z+/uXHWyoAAAAAAAAAAAAA3NjeMHat9bEkH9v9+cdn5pkkX5xkJXnN7rCfnuRHjrVIAAAAAAAAAAAAANhn1lqHHzzz+iT/LMmX5moc+0iSSXIhya9aa/2nPa8//M0AAAAAAAAAAAAAuJldWWvdc5IXXDj0wJl5dZL3JfmmtdbHk7wjyTevtV6X5JuTvPsGr7t/Zi7PzOWTLAwAAAAAAAAAAAAATuKgO8bOzKUkDyd5ZK31HbvHfizJbWutNTOT5MfWWq/Z833cMXaDTnJX4S24erqaa0taZzPX9rTOdm2uZq17Zq7taJ2t/frRtl9J/7loru1ona11rqT/mt+q8FTMtVNxPffI2S7klM2d9yXp27NP71fbYOm95rvec960fsba5kp6Z3NdhM+d1utH61xsT+u52DZX0jtb61yJayPnT9vnbPcZO/07xu6i13cneeZaFLvzI0l+ze7P9yb5oZO8MQAAAAAAAAAAAACcposHHPOWJG9P8oMz8+TusW9N8nVJvnNmLib5yST3H2WFAAAAAAAAAAAAAHCAvWHsWuuxJDe65/MvP93lAAAAAAAAAAAAAMArc+GsFwAAAAAAAAAAAAAAp0EYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFDh4r4DZuZ1Sf56kp+dZCV5YK31nTPzt5L8ot1htyV5fq1115HWCQAAAAAAAAAAAAAva28Ym+QTSb5lrfUDM/MFSa7MzIfWWl997YCZ+UtJfuxYiwQAAAAAAAAAAACAffaGsWutjyX52O7PPz4zzyT54iQfTZKZmSS/Pcm9R1wnAAAAAAAAAAAAALysQ+4Y+2kz8/okdyd5/CUPf1mS/7bW+qFTXBfnyNX2uY+5tqd1NnNtT/NsrVr3zFzb0zxbo+b9ap3NXNvTOlvrXGxP86k4d9531ks4itY9a74uNs8G50HrZ6x1rqR7NuC4Wq8frXOxPa3nYutcSe9srXPBeeJzdtXBYezMvDrJ+5J801rr4y956muS/I2Xed39Se5/xSsEAAAAAAAAAAAAgAPMWmv/QTOXkjyc5JG11ne85PGLSf5Lkl++1vrhA77POuT9tuRmKKxb98xc23EzfM4aNZ+LbbO1zpX0zmau7Wn/Wda2Z+37lfTumbm2o3U214/t+cy5eMYLOWU3wamY9dwjZ72EU9V6B9ybQev1w1zb8ZnZuoZr/32xba6k/3fh5j1rm81c29M6m+vi9jgXt6ltvxJ7tkXt14/WuZqV7tmVtdY9J3ndhQO+8SR5d5JnXhrF7vy6JM8eEsUCAAAAAAAAAAAAwDHtDWOTvCXJ25PcOzNP7r6+Yvfc70jyN462OgAAAAAAAAAAAAA40MV9B6y1Hkty3XsIr7V+z2kvCAAAAAAAAAAAAABeiUPuGAsAAAAAAAAAAAAA554wFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACnvD2Jl53cw8OjMfnZmnZ+YbX/Lc18/Ms7vH//xxlwoAAAAAAAAAAAAAN3bxgGM+keRb1lo/MDNfkOTKzHwoyc9O8puTvHGt9eLMfNExFwoAAAAAAAAAAAAAL2fWWid7wcz3J/krSb4uyQNrrX94gtee7M0AAAAAAAAAAAAAuFldWWvdc5IXXDjJwTPz+iR3J3k8yZ1JvmxmHp+Zfzozv+IGr7l/Zi7PzOWTvBcAAAAAAAAAAAAAnMTFQw+cmVcneV+Sb1prfXxmLib5mUnenORXJHloZn7B+im3oF1rPZDkgd33+KlPb97MJEna5ko+Mxvb0nwuts1mru1xXdyutvOx/Vxs26+k99poru1x/diW9v1Kevesba6k/3xs27P2/UqSsi3LtS1bzz1ytgs5ZXPnfUn65kp6Z6ufq+zakXzm+tHKz2jOi7ZzMek/H9v2rH2/mrWei21zJb2zmWt7XPPhc6P5+tE22yu9Lh50x9iZuZSrUex71lrv3z38w0nev656IsmnknzhK1oFAAAAAAAAAAAAAHyW9oaxczW5fXeSZ9Za3/GSp/52kl+7O+bOJJ+X5H8cYY0AAAAAAAAAAAAAsNfFA455S5K3J/nBmXly99i3JvneJN87Mx9J8n+S/O7Vdh9eAAAAAAAAAAAAADZjbxi71nosydzg6d91ussBAAAAAAAAAAAAgFfmwlkvAAAAAAAAAAAAAABOgzAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwliA/8vO/Ydant/1HX+9h0mFNrEqqW0at6wVJ5CmGnEbQoMYf5AN9o9Ui7YLTSOGjlotSbut2FjUIoL4I8G20Hba3cbSsGhJ1JoGxkUCSyDa7i6r+yus/0TZuHUNigkUK9t8+sfcidNlZs65N+f2zvc1jwcMe+fc77nn857P93zn7PLcLwAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBhZxg7M3fMzIdn5qmZeXJm3nH0+A/PzCdm5rGjX990+ssFAAAAAAAAAAAAgOs7v8cxLyS5d6316My8LMkjM/Pg0ffes9b6ydNbHgAAAAAAAAAAAADsZ2cYu9Z6LslzR19/emaeTvLK014YAAAAAAAAAAAAABzHrLX2P3jmziQPJXlNkn+c5NuTfCrJw7lyV9k/2PH8/V8MAAAAAAAAAAAAgNvZI2utu47zhHP7HjgzL03y/iTvXGt9Ksm/SfJlSV6bK3eU/akbPO/izDw8Mw8fZ2EAAAAAAAAAAAAAcBx73TF2Zl6S5INJLq+13n2d79+Z5INrrdfs+DnrOHeo3YKZSZK0zZX8yWyt2vasfb+S3j0z13a0ztY6V9I7W/s1v22/Enu2Na3XjqR3tva5mrXuWdtcSf/52LZn7fuVJGVblttgy2o5F7dlPXP5rJdwcHPh7rNeAifQ9tkj6f0s3DpX0v+ZsW3PbodzsW02c21P62zm2p7W2VrnSnpna5+rWemeHf6OsXPlJ9+X5Olro9iZecU1h31zkieO88IAAAAAAAAAAAAAcEjn9zjmDUnemuTxmXns6LF3JblnZl6bZCX5eJLvPIX1AQAAAAAAAAAAAMBedoaxa62PJLnePYQ/dPjlAAAAAAAAAAAAAMDJnDvrBQAAAAAAAAAAAADAIQhjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACosDOMnZk7ZubDM/PUzDw5M+940ffvnZk1My8/vWUCAAAAAAAAAAAAwM2d3+OYF5Lcu9Z6dGZeluSRmXlwrfXUzNyR5E1JfvtUVwkAAAAAAAAAAAAAO+wMY9dazyV57ujrT8/M00lemeSpJO9J8n1JfnHfF5yZk630Ftc6VzN7tj2te2au7WmdrXWupHu2RvZre1r3rHWupHe21rmate5Z61zN7Nn22DJuFc7FbZkLd5/1EiBJ92eP1tla52rWumetcyW9s5lre1pnM9f2tM7WOlfSO1vrXM3s2RX73DH2s2bmziRfleTXZuYtST6x1vr1m/1hzszFJBc/l0UCAAAAAAAAAAAAwC57h7Ez89Ik70/yziQvJHlXkjftet5a61KSS0c/Y51olRuwVt9oV4Pnttnaq/i2/Ur6z0VzbUfrbK1zJb2zmWt7WmfzuWp72s9Fc21H62ytcyW9s/m7bHvs2ba0XjuS3tnq32PPXD7rJRzc1bvglp2K7sa8Ya6LcLra3mNJ/+cqc21H62zm2p7W2VrnSnpnM9f2+PeX/9e5fQ6amZfkShT7vrXWB5J8WZIvTfLrM/PxJF+S5NGZ+QuntVAAAAAAAAAAAAAAuJmdd4ydKynxfUmeXmu9O0nWWo8n+eJrjvl4krvWWp88pXUCAAAAAAAAAAAAwE3tc8fYNyR5a5Kvn5nHjn590ymvCwAAAAAAAAAAAACOZecdY9daH0kyO46581ALAgAAAAAAAAAAAICT2OeOsQAAAAAAAAAAAABwyxPGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFTYGcbOzB0z8+GZeWpmnpyZdxw9/iMz8xsz89jM/PLM/MXTXy4AAAAAAAAAAAAAXN8+d4x9Icm9a61XJ3l9ku+ZmVcn+Ym11lestV6b5INJfvD0lgkAAAAAAAAAAAAAN7czjF1rPbfWevTo608neTrJK9dan7rmsD+TZJ3OEgEAAAAAAAAAAABgt1lr/551Zu5M8lCS16y1PjUzP5rk7yX5wyRft9b6vR3PF88CAAAAAAAAAAAAsI9H1lp3HecJO+8Ye9XMvDTJ+5O88+rdYtdaP7DWuiPJ+5J87w2ed3FmHp6Zh4+zMAAAAAAAAAAAAAA4jr3uGDszL0nywSSX11rvvs73/1KSD621XrPj59TeMfY4d97diplJ0jebubandbarc7Vq26/Enm1R+/XDXNvh+rEtt8O52DZb+1xsT9u5mPS/z8y1Ha2zmWt7Wmfz+WO71jOXz3oJBzUX7k6SlL3FcvUt1nbtSPqvH/Zse9r2rH2/kt49M9d2tM7WPlcze7Y9rXtmru1one1orsPfMXau/OT7kjx9bRQ7M19+zWFvSfKx47wwAAAAAAAAAAAAABzS+T2OeUOStyZ5fGYeO3rsXUnePjOvSvKZJL+V5LtOZYUAAAAAAAAAAAAAsIedYexa6yNJrnc/6w8dfjkAAAAAAAAAAAAAcDLnznoBAAAAAAAAAAAAAHAIwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACjvD2Jm5Y2Y+PDNPzcyTM/OOo8d/YmY+NjO/MTM/PzNfcOqrBQAAAAAAAAAAAIAb2OeOsS8kuXet9eokr0/yPTPz6iQPJnnNWusrkjyT5J+d3jIBAAAAAAAAAAAA4OZ2hrFrrefWWo8eff3pJE8neeVa65fXWi8cHfarSb7k9JYJAAAAAAAAAAAAADc3a639D565M8lDuXKn2E9d8/gvJfnZtdZ/3vH8/V8MAAAAAAAAAAAAgNvZI2utu47zhPP7HjgzL03y/iTvfFEU+wNJXkjyvhs872KSi8dZFAAAAAAAAAAAAAAc1153jJ2ZlyT5YJLLa613X/P4tyf5ziTfsNb6X3v8nHWcO9RuwcwkSdrmSnpnM9f2XJ2tVdue3Q7nYttsrXMlrh9b075fiT3bmrb9Snqv+ebantbZWudKemcz1/a0zmau7WmdrX+uM17IKbj6r2Xrmctnu5ADmwt3J+nbs8/uV9tg6b9+NGvds9a5mrXumbm2o3W29utH234l9myL2q8f5tqO1tmO5jr8HWPnyk++L8nTL4pi35zk+5J87T5RLAAAAAAAAAAAAACcpp1hbJI3JHlrksdn5rGjx96V5F8m+bwkDx5Vub+61vqu01gkAAAAAAAAAAAAAOyyM4xda30kyfXu0/2hwy8HAAAAAAAAAAAAAE7m3FkvAAAAAAAAAAAAAAAOQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAECFnWHszNwxMx+emadm5smZecfR49969PvPzMxdp79UAAAAAAAAAAAAALix83sc80KSe9daj87My5I8MjMPJnkiybck+XenuUAAAAAAAAAAAAAA2MfOMHat9VyS546+/vTMPJ3klWutB5NkZo71gsc9fita50p6ZzMXt4rWPWudK+mdrXWuZvZse+zZtjTvV+ts5tqe1tla50p6ZzPX9rTOZq7taZ2td66zXsHpmQt3n/USTkXrnrW+x5Lu2Vq17lnrXM1a98xc29M8WyP7tT3Ne9Y6m7m2p3m249jnjrGfNTN3JvmqJL92jOdcTHLxeMsCAAAAAAAAAAAAgOPZO4ydmZcmeX+Sd661PrXv89Zal5JcOvoZa6117EXeym6Hwrp1z1rngltF23ss6b9+tM2V9M7Wfs1v26+kf89aORe3p23PWv8eS3pna50r6Z3NXNvTOpu5tqd1tv65znghp+DqR/z1zOWzXciBXb0DbtuefXa/2gbL7XD96Jor6Z3Nf/vYnvZz0Vzb4fqxLbfDudg2W+tcSe9s7dfFZs7FK87t+cNfkitR7PvWWh840SsBAAAAAAAAAAAAwCnaGcbOleT2viRPr7XeffpLAgAAAAAAAAAAAIDjO7/HMW9I8tYkj8/MY0ePvSvJ5yX5V0n+XJL/NjOPrbXuPpVVAgAAAAAAAAAAAMAOO8PYtdZHkswNvv3zh10OAAAAAAAAAAAAAJzMubNeAAAAAAAAAAAAAAAcgjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoMLOMHZm7piZD8/MUzPz5My84+jxL5qZB2fmN4/++YWnv1wAAAAAAAAAAAAAuL597hj7QpJ711qvTvL6JN8zM69O8v1JfmWt9eVJfuXo9wAAAAAAAAAAAABwJnaGsWut59Zajx59/ekkTyd5ZZK3JPmZo8N+JsnfPKU1AgAAAAAAAAAAAMBOs9ba/+CZO5M8lOQ1SX57rfUFR49Pkj+4+vubPH//FwMAAAAAAAAAAADgdvbIWuuu4zzh/L4HzsxLk7w/yTvXWp+60sJesdZaN4peZ+ZikovHWRQAAAAAAAAAAAAAHNdeYezMvCRXotj3rbU+cPTw787MK9Zaz83MK5I8f73nrrUuJbl09HPWce5QuwXXBsKtWvfMXNvROlv7XM1a96xtrqR3ttvhfdaq9Vw013a0zmau7Wmdzd/R3Gra3mNJ//XDXNvROlv/XGe8kFNw9ePHeuby2S7kwObC3We9hFPV9h5L+q8fcKtoe48l/e+ztj1rvd4nvbOZa3taZ2udK+mdzVzb0/656rjO7TpgrvyJ3Zfk6bXWu6/51n9N8rajr9+W5BcPvzwAAAAAAAAAAAAA2M8+d4x9Q5K3Jnl8Zh47euxdSX4syc/NzNuT/FaSbzuVFQIAAAAAAAAAAADAHnaGsWutjyS50X12v+GwywEAAAAAAAAAAACAkzl31gsAAAAAAAAAAAAAgEMQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABU2BnGzsz9M/P8zDxxzWNfOTMfnZnHZ+aXZubzT3eZAAAAAAAAAAAAAHBz+9wx9r1J3vyix/5Dku9fa/3VJD+f5J8eeF0AAAAAAAAAAAAAcCw7w9i11kNJfv9FD19I8tDR1w8m+VsHXhcAAAAAAAAAAAAAHMv5Ez7vySRvSfILSb41yR37PnFmTviSnJXWPTPX9rTO1jpXs9Y9a50r6Z6NbWk9F821Pa2zmWt7mmeDW0Hze6x1NnNtT+tsvXOd9QpOz1y4+6yXwDG0vseS7tngVuA9tj2te9Y6V9I7m7m2p3W21rmS3tnMxVbtvGPsDXxHkn8wM48keVmSP77RgTNzcWYenpmHT/haAAAAAAAAAAAAALDTie4Yu9b6WJI3JcnMXEjyN25y7KUkl46OXSd5vS1Yq2+0q2V822zm2p7W2cy1Pe3/x5A9g9PX9j5rvea7dmyXc3F77Nn2tO6ZubajdTZzbc/tcM1vVHgqfvYuuOuZy2e7kANrvwNu87kIt4q2zx+3w+eqttnMtT2ts5lre1pna50r6f9vBG17djuci22znfQ9dqI7xs7MFx/981ySf57k357o1QEAAAAAAAAAAADgQHaGsTPzQJKPJnnVzDw7M29Pcs/MPJPkY0l+J8l/PN1lAgAAAAAAAAAAAMDNnd91wFrrnht866cPvBYAAAAAAAAAAAAAOLGdd4wFAAAAAAAAAAAAgC0QxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUGFnGDsz98/M8zPzxDWPvXZmfnVmHpuZh2fmdae7TAAAAAAAAAAAAAC4uX3uGPveJG9+0WM/nuRfrLVem+QHj34PAAAAAAAAAAAAAGfm/K4D1loPzcydL344yecfff1nk/zOgde1OTNz1ks4Na2zmWt7WmczF7cKewanr/V91joX2+Nc3B57tj2te2au7WmdzVxwuppPxblw91kvgWNoPhfhVtH6+aN1rqR3NnNtT+ts5tqe1tla52rWumetcyXdsx3HzjD2Bt6Z5PLM/GSu3HX2r9/owJm5mOTiCV8HAAAAAAAAAAAAAPZy0jD2u5P8o7XW+2fm25Lcl+Qbr3fgWutSkktJMjPrhK93y1urb7T2erxtz67uV9tcSe9s3mPb034uts2V9L/PWjkXt6dtz26H62LbbO1zsT1t52LS/z5rnauZPduWtv1K+q8fvXOd8UJOwdXLR9tsn53rmctnu5ADu3pn37b9Svrvgtt2XUz6r/mt2vYr6T8XzbUd7dcP4PS1XRtbr/mu97ePcyd83tuSfODo6/+S5HWHWQ4AAAAAAAAAAAAAnMxJw9jfSfK1R19/fZLfPMxyAAAAAAAAAAAAAOBkzu86YGYeSPLGJC+fmWeT/FCSv5/kp2fmfJI/SnLxNBcJAAAAAAAAAAAAALvsDGPXWvfc4FtffeC1AAAAAAAAAAAAAMCJnTvrBQAAAAAAAAAAAADAIQhjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACrsDGNn5v6ZeX5mnrjmsZ+dmceOfn18Zh471VUCAAAAAAAAAAAAwA7n9zjmvUn+dZL/dPWBtdbfvvr1zPxUkj88+MoAAAAAAAAAAAAA4Bh2hrFrrYdm5s7rfW9mJsm3Jfn6A68LAAAAAAAAAAAAAI5lnzvG3szXJPndtdZvHmIxW3alEWZLWvesda6ke7ZGzfvVOlvrXGyPc3F7Wvesda6kd7bWudie5nOxdbbWuZrZs21p3q/W2XrnOusVnJ7W2ebC3We9hFPRul/NWq+LSfdsjZr3q3U2cwHcPlqvja1z0e9zDWPvSfLAzQ6YmYtJLn6OrwMAAAAAAAAAAAAAN3XiMHZmzif5liRffbPj1lqXklw6es5aa530JW9JV6v4trmS3tnMtT2ts5lre1pna50r6Z2tfS64VbS9x5L+64e5tqN1ttvh77LWPWudq1nrnplrO1pna5+rWdmWffaOquuZy2e7kANrvQPutVw/tseebUvbfiX2bGtaPy8m/ediq+ZzsW221rmS3tnMtT2ts5307+hzn8NrfmOSj621nv0cfgYAAAAAAAAAAAAAHMTOMHZmHkjy0SSvmplnZ+btR9/6O0keOM3FAQAAAAAAAAAAAMC+zu86YK11zw0e//aDrwYAAAAAAAAAAAAATmjnHWMBAAAAAAAAAAAAYAuEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVdoaxM3P/zDw/M0+86PF/ODMfm5knZ+bHT2+JAAAAAAAAAAAAALDbPneMfW+SN1/7wMx8XZK3JPnKtdZfSfKTh18aAAAAAAAAAAAAAOxvZxi71nooye+/6OHvTvJja63/fXTM86ewNgAAAAAAAAAAAADY2/kTPu9Ckq+ZmR9N8kdJ/sla63/s88SZOeFL3tpa50p6ZzPX9rTOZq7taZ2tda6kd7bWueBW0fwea53NXNvTPFur1j1rnatZ656Za3taZ2udq1nrls2Fu896CRyT68f22LNtsV/b07pnrXOxPc3nYutsrXMlvbOZa3uaZzuOk4ax55N8UZLXJ/lrSX5uZv7yWmu9+MCZuZjk4smXCAAAAAAAAAAAAAC7nTSMfTbJB45C2P8+M59J8vIkv/fiA9dal5JcSpKZuV47u2lXC+u2uZLe2drnata6Z+bajtbZXD+2p33P2vYr6d+zVs7F7Wnbs/b9atZ2LiY+C29V234lzsWtatuvxLm4VW37lVx7Lp7xQg7s6qm4nrl8tgs5sNvhDrht77P262Kz1nOxba6kd7b2uZq17pm5tqN1ttvh+tGq9VxsmyvxPnuxcyd83i8k+bokmZkLSf5Ukk8eaE0AAAAAAAAAAAAAcGw77xg7Mw8keWOSl8/Ms0l+KMn9Se6fmSeS/HGSt9XdChYAAAAAAAAAAACATdkZxq617rnBt/7ugdcCAAAAAAAAAAAAACd27qwXAAAAAAAAAAAAAACHIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKDCzjB2Zu6fmedn5olrHvvhmfnEzDx29OubTneZAAAAAAAAAAAAAHBz+9wx9r1J3nydx9+z1nrt0a8PHXZZAAAAAAAAAAAAAHA853cdsNZ6aGbuPNQLzsyhftQtpXWupHe21rmate6ZubanebZW9mxb7Be3Cufi9tgzbhXN52LzbI2a96t5tkbN+9U8W6Pm/WodbS7cfdZL4Jia32dsS+u52DpX0jtb61zNWvfMXNvTPBvb0nouts7Fn9jnjrE38r0z8xszc//MfOGNDpqZizPz8Mw8/Dm8FgAAAAAAAAAAAADc1Ky1dh905Y6xH1xrvebo938+ySeTrCQ/kuQVa63v2OPn7H6xjdrnz3FrrpbxbbOZa3v8Xxrb5FzcnuY9a5vNXNvTOlv7XM1a96x1LuD0tV4/2uZKXBu3yrm4PW175rq4XeuZy2e9hINyB1xuRW3XxtZrfutcSe9s5tqe1tnMtT2ts7XOlfTOZq7tKf9vBI+ste46zhNOdMfYtdbvrrX+z1rrM0n+fZLXneTnAAAAAAAAAAAAAMChnCiMnZlXXPPbb07yxGGWAwAAAAAAAAAAAAAnc37XATPzQJI3Jnn5zDyb5IeSvHFmXptkJfl4ku88vSUCAAAAAAAAAAAAwG47w9i11j3Xefi+U1gLAAAAAAAAAAAAAJzYubNeAAAAAAAAAAAAAAAcgjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoMLOMHZm7p+Z52fmiet8796ZWTPz8tNZHgAAAAAAAAAAAADsZ587xr43yZtf/ODM3JHkTUl++8BrAgAAAAAAAAAAAIBj2xnGrrUeSvL71/nWe5J8X5J16EUBAAAAAAAAAAAAwHGdP8mTZuYtST6x1vr1mTnwkrap+c+hdTZzwelyLm5P8561zmau7WmdrXWuZq171joXcPparx+tc7E9zsXtad2z1rmazYW7z3oJUK/12miu7WmdzVzb0zqbubandbbWuZLe2czFVh07jJ2ZP53kXUnetOfxF5NcPO7rAAAAAAAAAAAAAMBxnOSOsV+W5EuTXL1b7JckeXRmXrfW+p8vPnitdSnJpSSZmbXW+hyWe+u5Wo+3zZX0zmau7WmdzVzb0zpb61xJ72zt//da234l/XvWqvlcbJvNXNvTfl1s3rO22cy1Pa2zmWt7/F22Le37lSRlW5arW7aeuXy2Czkwd8CF/39a/y5rmyvpnc1c29M6W/tcbE/buZj0n49te9a+X/yJY4exa63Hk3zx1d/PzMeT3LXW+uQB1wUAAAAAAAAAAAAAx3Ju1wEz80CSjyZ51cw8OzNvP/1lAQAAAAAAAAAAAMDx7Lxj7Frrnh3fv/NgqwEAAAAAAAAAAACAE9p5x1gAAAAAAAAAAAAA2AJhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAPxfdu4/VPs8r+v4633vUVY3SbO0VasJdIZo0DUG0cxS123GHySZgUOW1tJNEbVWYErgFrFRKFFQWTe4bKBNhLoUEzq7mDYIqzH3NtnszjqSP2r8NQXV6irZ6qc/5tzrvcPMfc65ruvsub6v83jAsPd97us63897Pp/re2bheX8BACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgApnhrEz89aZeWFmnrnra39nZn5sZp6emXfMzKdc7jIBAAAAAAAAAAAA4N7O88TYtyV55CVf+9a11meutV6X5PEk33LgdQEAAAAAAAAAAADAhZyc9YK11pMzc99Lvvb+u377miTrvBecmXMvbkta50p6ZzPX9rTOZq7taZ2tda6ke7ZG9otj0XwWW2czF8eiec9aZzPX9rTOZi6OhT3bntYtm/sfvuolABvV+rOsda6kdzZzbU/rbK1zsT3O4vbYM7bqzDD2lczMW5L8mST/J8kX3eN1N5Pc3PU6AAAAAAAAAAAAAHAes9bZD3s9fWLs42utB1/mz745yavXWm8+x/dZ57neltyp4tvmSnpna/+bDG37ldizrWm9dyS9s7XOlfTfP1o1n8W22do/Y237lfTvWStncXua96xtNnNtT/v9o5WzuD1te3Yd7otto935iK3nnrjahRzYdXgCbutZbNZ2b2z/Gd2s9Sy2ztWsdc/MtR3tnzN7tj1te9a+X8Vur7Ueusgbbhzgot+V5E8c4PsAAAAAAAAAAAAAwM52CmNn5jPu+u1XJnnfYZYDAAAAAAAAAAAAALs5OesFM/NYki9M8ttn5vkkb07yZTPzQJLfSPIzSf7CZS4SAAAAAAAAAAAAAM5yZhi71nr0Zb78HZewFgAAAAAAAAAAAADY2Y2rXgAAAAAAAAAAAAAAHIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKDCmWHszLx1Zl6YmWfu+tq3zsz7ZubHZubtM/Pxl7pKAAAAAAAAAAAAADjDeZ4Y+7Ykj7zka+9M8uBa6zOTPJfkmw+8LgAAAAAAAAAAAAC4kJOzXrDWenJm7nvJ195x129/JMlXn/eCM3PuxW1J61xJ92yN7Nf2tO5Z61xJ72ytc7E9zWexebZG9otj4SxuT/Oetc5mLrhczuL2tO5Z61xJ0jra3P/wVS+BC2o9i82a741sS+tZbJ2rWeuemYtjYc+2x56xVed5YuxZ/lyS73ulP5yZmzPz1Mw8dYBrAQAAAAAAAAAAAMDLOvOJsfcyM38zyQeTfNcrvWatdSvJrdPXr32ud8zW6hvtTvHfNlv732Ro26+k/yy2zgXHpPVzZq7tcG/cpuaz2DZb+1zN7Nn2tO6Zubaj/XPWtmfX4Sy2zdY+F9tTdhQ/9DTV9dwTV7uQS3Dn6b6te9bMPX9b2vYr6d+zVs1nsW02c21P62zu99vVehbb5kp6Z9v1/rFzGDszX5/kK5K8frX92wQAAAAAAAAAAABgc3YKY2fmkSTfmOSPrLV+5bBLAgAAAAAAAAAAAICLu3HWC2bmsSTvSvLAzDw/M29M8o+TfFySd87M0zPzzy55nQAAAAAAAAAAAABwT2c+MXat9ejLfPk7LmEtAAAAAAAAAAAAALCzM58YCwAAAAAAAAAAAABbIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqHBmGDszb52ZF2bmmbu+9idn5j0z8xsz89DlLhEAAAAAAAAAAAAAznaeJ8a+LckjL/naM0m+KsmTh14QAAAAAAAAAAAAAOzi5KwXrLWenJn7XvK1Z5NkZi5pWQAAAAAAAAAAAABwMWeGsZxPcyTcPFuj5v1qna11LjgmrZ8zc8Hlaj6LrbO1ztXMnm1P656Zi2PRumetcyW9s7XOxfa0HsW5/+GrXsKlad2zZu7522K/OBbNZ7F1NnNtT/NsbEvrWWydK+me7SIuPYydmZtJbl72dQAAAAAAAAAAAAC43i49jF1r3UpyK0lmZl329a7KWn2j3anH22Yz1/a0zmau7WmdrXWupHe29rma2bNtaduvxJ5tTft+JfZsi1r3zFzb0Tpb+/2jbb8Se7Y17fuV9O5Z2Vgfeppq21zJXbM998TVLuTA7jzdt3nPWvXeF7vmSnpna//vj7b9SpzFrWrbr6T/LLbNlfTO1n7/aOYsvujGgdcBAAAAAAAAAAAAAFfizDB2Zh5L8q4kD8zM8zPzxpn54zPzfJLPS/LvZqbrr38CAAAAAAAAAAAAsDknZ71grfXoK/zR2w+8FgAAAAAAAAAAAADY2ZlPjAUAAAAAAAAAAACALRDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQ4cwwdmbeOjMvzMwzd33tt83MO2fmJ07/9xMud5kAAAAAAAAAAAAAcG/neWLs25I88pKvfVOSH1hrfUaSHzj9PQAAAAAAAAAAAABcmVlrnf2imfuSPL7WevD09z+e5AvXWj8/M69N8kNrrQfO8X3OvhgAAAAAAAAAAAAAJLfXWg9d5A0nO17ok9daP3/6619I8smv9MKZuZnk5o7XAQAAAAAAAAAAAIBz2TWM/ZC11rrXk2DXWreS3Eq6nxh7nifvbs3MJOmbzVzbc2c2OBZtn7Pr8Blr3bPWuZq17pm5tqN1NnNtT/s9355xLJrPYtts5tqe9vti255dh7PYNlv/XFe8kEtw57a4nnviahdyYHP/w0nK96xsuPb7RzN7ti1t+5XYs61pvd8nvbO1zpX03z9gq27s+L5fnJnXJsnp/75wuCUBAAAAAAAAAAAAwMXtGsb+2yRfd/rrr0vybw6zHAAAAAAAAAAAAADYzZlh7Mw8luRdSR6Ymedn5o1J/l6SN8zMTyT5ktPfAwAAAAAAAAAAAMCVOTnrBWutR1/hj15/4LUAAAAAAAAAAAAAwM7OfGIsAAAAAAAAAAAAAGyBMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACgwl5h7My8aWaemZn3zMw3HGhNAAAAAAAAAAAAAHBhO4exM/Ngkj+f5HOSfFaSr5iZTz/UwgAAAAAAAAAAAADgIvZ5YuzvS/Kja61fWWt9MMl/SPJVh1kWAAAAAAAAAAAAAFzMyR7vfSbJW2bmE5P8apIvS/LUQVa1QTNz1Uu4NK2zmQvYlc/Z9rTuWetczVr3zFzb0zqbuTgW9oxj0XwWW2czF8eidc9a50p6Z+ud66pXcHnm/oevegmXonrPSodrnauZPdsW+7U9rXvWOlfSO1vrXMDx2TmMXWs9OzN/P8k7knwgydNJfv2lr5uZm0lu7nodAAAAAAAAAAAAADiPWWsd5hvN/N0kz6+1/uk9XrMOdb1jcedvMrTNlfTOZq7taZ3NXNvTOtt1+Ft59mxb2vYrcf/g+LSeRXNtR+tsrXMlvbOZa3taZ2v/76q2/Urs2da03juS3tna52q2nnviqpdwUK1PwL2bzxnHovUsts7VrHXPzLUdrbO1zpVcj3tjo+az2Dbb6Vy311oPXeR9Oz8x9vSin7TWemFmfneSr0ryuft8PwAAAAAAAAAAAADY1V5hbJLvmZlPTPL/kvyltdb/3n9JAAAAAAAAAAAAAHBxe4Wxa60vONRCAAAAAAAAAAAAAGAfN656AQAAAAAAAAAAAABwCMJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgAp7hbEz81dn5j0z88zMPDYzrz7UwgAAAAAAAAAAAADgInYOY2fmU5P8lSQPrbUeTPKqJF9zqIUBAAAAAAAAAAAAwEXs9cTYJCdJPmZmTpJ8bJKf239JAAAAAAAAAAAAAHBxs9ba/c0zb0ryliS/muQda60/dcbrd78YAAAAAAAAAAAAANfJ7bXWQxd5w85PjJ2ZT0jylUl+b5JPSfKamfnal3ndzZl5amae2vVaAAAAAAAAAAAAAHCWkz3e+yVJfmqt9T+SZGa+N8kfTPKdd79orXUrya3T16x9nlB7jGbmqpcAH6btM5b85uesbTZzbU/rbNfhZ5k925a2/Urs2da071fSu2etczVr3bO2uZLrcR4bNZ/FttnMtT2ts7XPxfa0nsWysZIkdz5m67knrnYhBzb3P5ykfM/Khmv/WdY2V9I7W/t/f7TtV9J/Fs21He33j2Zt57H1c9Y6V9I72673xZ2fGJvkvyX53Jn52Hnx6q9P8uwe3w8AAAAAAAAAAAAAdrZzGLvW+tEk353k3Un+y+n3unWgdQEAAAAAAAAAAADAhZzs8+a11puTvPlAawEAAAAAAAAAAACAne38xFgAAAAAAAAAAAAAOCbCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKghjAQAAAAAAAAAAAKggjAUAAAAAAAAAAACggjAWAAAAAAAAAAAAgArCWAAAAAAAAAAAAAAqCGMBAAAAAAAAAAAAqCCMBQAAAAAAAAAAAKCCMBYAAAAAAAAAAACACsJYAAAAAAAAAAAAACoIYwEAAAAAAAAAAACoIIwFAAAAAAAAAAAAoIIwFgAAAAAAAAAAAIAKwlgAAAAAAAAAAAAAKuwcxs7MAzPz9F3/vH9mvuGAawMAAAAAAAAAAACAczvZ9Y1rrR9P8rokmZlXJfnZJG8/zLIAAAAAAAAAAAAA4GJ2DmNf4vVJ/uta62fOeuHMHOiSwMtp/oy1zmau7WmerZU92xb7tT32bHta96x1rmate9Y6F9vTfBZbZzPX9rTO1joX29N6FkvHSpLM/Q9f9RIuRfWelQ5nru1pnq1R8361zmYuuHyt59Fc29M820UcKoz9miSPvdwfzMzNJDcPdB0AAAAAAAAAAAAAeFmz1trvG8x8dJKfS/L711q/eMZr97vYEdv33+MxulOPt82mit+u1rNoru1ona11rqR3NnNtT+ts5tqe1tna52pmzzgWrWexba7E52yrnEWOhbPIsSg8ih96oup67omrXciBtT4B925t90b3xe1qPYvm2o7W2drvi237lfSfxba5kt7ZzLU9rbOdznV7rfXQRd534wDX/tIk7z4rigUAAAAAAAAAAACAy3SIMPbRJI8d4PsAAAAAAAAAAAAAwM72CmNn5jVJ3pDkew+zHAAAAAAAAAAAAADYzck+b15rfSDJJx5oLQAAAAAAAAAAAACws72eGAsAAAAAAAAAAAAAx0IYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBhrzB2Zj5+Zr57Zt43M8/OzOcdamEAAAAAAAAAAAAAcBEne77/HyX5/rXWV8/MRyf52AOsCQAAAAAAAAAAAAAubOcwdmZ+a5I/nOTrk2St9WtJfu0wywIAAAAAAAAAAACAi5m11m5vnHldkltJ3pvks5LcTvKmtdYH7vGe3S4GAAAAAAAAAAAAwHVze6310EXecGOPi50k+QNJvn2t9dlJPpDkm176opm5OTNPzcxTe1wLAAAAAAAAAAAAAO5pnyfG/s4kP7LWuu/091+Q5JvWWl9+j/esXa93rGYmSdI2V9I7m7m2p3W2O3O1atuvxJ5tUfv9w1zb4f6xLc7idrXt2XU4i22ztc6V9M5mru1pnc3P6O1pP4utczWzZ9tStl1Jkjtbtp574moXcmBz/8NXvYRL13r/aJ2rWeuemWs72j9nbXvWvl9J7561zZVcj/PYqPksts12OtdH7omxa61fSPLfZ+aB0y+9Psl7d/1+AAAAAAAAAAAAALCPkz3f/5eTfNfMfHSSn0zyZ/dfEgAAAAAAAAAAAABc3F5h7Frr6SQXekQtAAAAAAAAAAAAAFyGG1e9AAAAAAAAAAAAAAA4BGEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQIWTfd48Mz+d5JeS/HqSD661HjrEogAAAAAAAAAAAADgovYKY0990Vrrfx7g+wAAAAAAAAAAAADAzm5c9QIAAAAAAAAAAAAA4BBmrbX7m2d+Ksn/SrKS/PO11q0zXr/7xQAAAAAAAAAAAAC4Tm6vtR66yBtO9rzgH1pr/ezMfFKSd87M+9ZaT979gpm5meTmntcBAAAAAAAAAAAAgHva64mxH/aNZv5Wkl9ea33bPV6zDnW9YzEzSZK2uZLfnK1V256171ez1rPYNlfSO9t1uH+07pm5tqN1tvb7R9t+Jf1n0Vzb0X7/aNZ2HtvPYtt+JfZsa67Dz7K22cy1Pe33xVaFRzF3juJ67omrXciBzf0PX/US4MM0/yxrm639Z3TbfiX2bGta7x1J72ytcyXuH1vTvl9J7Z5d+ImxN/a44Gtm5uPu/DrJH03yzK7fDwAAAAAAAAAAAAD2cbLHez85ydtPi9yTJP9yrfX9B1kVAAAAAAAAAAAAAFzQzmHsWusnk3zWAdcCAAAAAAAAAAAAADu7cdULAAAAAAAAAAAAAIBDEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBh7zB2Zl41M/9pZh4/xIIAAAAAAAAAAAAAYBeHeGLsm5I8e4DvAwAAAAAAAAAAAAA7O9nnzTPzaUm+PMlbkvy1c75nn0serda5mtkzjkXrWWydK+merVXrnplre5pna9S8X62zmQsun/O4LfZre1r3rHWupHc2c8Hlaj6Kc//DV70EqNb8s6x5tkb2a3ta96x1rqR3tta5mtmz7bFnL9r3ibH/MMk3JvmNV3rBzNycmadm5qk9rwUAAAAAAAAAAAAAr2jnJ8bOzFckeWGtdXtmvvCVXrfWupXk1ul71lpr10sepetQWLfumbm24zp8ztiWts+ZzxjHpu0zlvT+nG6/f7TtV+IsblXbfiXOIsen9Sy2zZX0ztY+V7PWPTPXdrTO1j/XFS/kEty55X/RX/yZq13Igf3gt/+eJN171vs5M9dWtM5mru25Dv//pVHzWWybrXWupP/+0bZnzuL1sc8TYz8/yR+bmZ9O8q+SfPHMfOdBVgUAAAAAAAAAAAAAF7RzGLvW+ua11qette5L8jVJ/v1a62sPtjIAAAAAAAAAAAAAuIB9nhgLAAAAAAAAAAAAAEfj5BDfZK31Q0l+6BDfCwAAAAAAAAAAAAB24YmxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABU2DmMnZlXz8x/nJn/PDPvmZm/fciFAQAAAAAAAAAAAMBFnOzx3v+b5IvXWr88Mx+V5Idn5vvWWj9yoLUBAAAAAAAAAAAAwLntHMautVaSXz797Ued/rMOsSgAAAAAAAAAAAAAuKh5sW/d8c0zr0pyO8mnJ/kna62/ccbrhbMAAAAAAAAAAAAAnMfttdZDF3nDjX2uttb69bXW65J8WpLPmZkHX/qambk5M0/NzFP7XAsAAAAAAAAAAAAA7mWvJ8Z+2Dea+ZYkv7LW+rZ7vGYd6nrHYmauegmXrnXPzLUd1+Fz1shZ5Ji0ncfWe77P2Ha1nsW2uZLe2cy1Pe33fHvGsWg+i22ztc/VrHXPzLUd7Z+ztj37zbN4xQu5BHeOYttsrXMld8/WNVzrPb/9fp/07lnrXM1a98xc29E6W+tcSe9s7ff8tv1K6s/iR+6JsTPzO2bm409//TFJ3pDkfbt+PwAAAAAAAAAAAADYx8ke731tkn8xM6/Ki4Htv15rPX6YZQEAAAAAAAAAAADAxewcxq61fizJZx9wLQAAAAAAAAAAAACwsxtXvQAAAAAAAAAAAAAAOARhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAECFncPYmfldM/ODM/PemXnPzLzpkAsDAAAAAAAAAAAAgIs42eO9H0zy19da756Zj0tye2beudZ674HWBgAAAAAAAAAAAADntvMTY9daP7/Wevfpr38pybNJPvVQCwMAAAAAAAAAAACAi9jnibEfMjP3JfnsJD96jtce4pJ8BLXumbngcjmLHJPW89g6F9vTehZb50p6ZzMXx8KecSyaz2LrbK1zNWvdM3NxLFr3rHSsJL2ztc6VNH/OOudq1rpnrXM1a90zc21P62ytcyXdszVq3q/m2S5i7zB2Zn5Lku9J8g1rrfe/zJ/fTHJz3+sAAAAAAAAAAAAAwL3MWmv3N898VJLHkzyx1voH53j97hc7cvv8ezxWd+rxttnMtT3tf5Ohbc+uw1lsm611rsT9Y2ucxe1q27PrcBbbZmv/jDVzFrendc9a52rWumetczVr3bPWudie1rPYNlfS/zlr3rO22cy1Pa2zmWt7Wmdrn4vtaTuLSf95bNuz9v1Kavfs9lrroYu878YeF5wk35Hk2fNEsQAAAAAAAAAAAABwmXYOY5N8fpI/neSLZ+bp03++7EDrAgAAAAAAAAAAAIALOdn1jWutH07S/2xhAAAAAAAAAAAAADZhnyfGAgAAAAAAAAAAAMDREMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBhrzB2Zt46My/MzDOHWhAAAAAAAAAAAAAA7GLfJ8a+LckjB1gHAAAAAAAAAAAAAOzlZJ83r7WenJn7DrSWTZuZq17CpWmdzVwci9Y9a50r6Z2tda5mrXvWOlez1j1rnSvpno1tcRa3p3XPWudq1rpnrXM1a92z1rnYntaz2DpXs+Y9a53NXNvTOpu5tqd1tta52B5ncXvs2fbYsxftFcaex8zcTHLzsq8DAAAAAAAAAAAAwPV26WHsWutWkltJMjNrrXXZl/yIulNYt82V9M7WPlcze7YtbfuV9N8/2uZKemcz1/a0zmau7WmdrX2uZq171jZXcj3OYyNncXva9qx9vxJ7xvFwFrelbb+S3v8Wbp0r6Z3NXNvTOpu5tsd/f2xL+34l9myL7BnHwll80Y0DrwMAAAAAAAAAAAAAroQwFgAAAAAAAAAAAIAKe4WxM/NYkncleWBmnp+ZNx5mWQAAAAAAAAAAAABwMSf7vHmt9eihFgIAAAAAAAAAAAAA+9jribEAAAAAAAAAAAAAcCyEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAAAAUEEYCwAAAAAAAAAAAEAFYSwAAAAAAAAAAAAAFYSxAAAAAAAAAAAAAFQQxgIAAAAAAAAAAABQQRgLAAAAAAAAAAAAQAVhLAAAAAAAAAAAAAAVhLEAAAAAAAAAAAAAVBDGAgAAAAAAAAAAAFBBGAsAAAAAAAAAAABABWEsAAAAAAAAAAAAABWEsQAAAAAAAAAAAABUEMYCAAAAAAAAAMD/b+/eYy0ryzuOfx9mBAEvKOCNoZEIingBb9RLoQpegBrHWi8QarwhLRUVaqigRqutCRVbRhuLoYJjUhQsAqU4KkSNaIMg4KCDXERFGETQqEVLBJGnf6x1cHNmrzWcfdacNe/L95OYmXOY43l+WXuv/dtrvWsvSZJUBRfGSpIkSZIkSZIkSZIkSZIkqQoujJUkSZIkSZIkSZIkSZIkSVIVXBgrSZIkSZIkSZIkSZIkSZKkKrgwVpIkSZIkSZIkSZIkSZIkSVVwYawkSZIkSZIkSZIkSZIkSZKq4MJYSZIkSZIkSZIkSZIkSZIkVcGFsZIkSZIkSZIkSZIkSZIkSaqCC2MlSZIkSZIkSZIkSZIkSZJUBRfGSpIkSZIkSZIkSZIkSZIkqQoujJUkSZIkSZIkSZIkSZIkSVIVXBgrSZIkSZIkSZIkSZIkSZKkKrgwVpIkSZIkSZIkSZIkSZIkSVVwYawkSZIkSZIkSZIkSZIkSZKq4MJYSZIkSZIkSZIkSZIkSZIkVcGFsZIkSZIkSZIkSZIkSZIkSaqCC2MlSZIkSZIkSZIkSZIkSZJUBRfGSpIkSZIkSZIkSZIkSZIkqQoujJUkSZIkSZIkSZIkSZIkSVIVFrUwNiIOiIhrIuK6iDh2qKEkSZIkSZIkSZIkSZIkSZKkhZp5YWxELAM+BhwI7AEcEhF7DDWYJEmSJEmSJEmSJEmSJEmStBCL+cTYvYHrMvOHmXkncDqwcpixJEmSJEmSJEmSJEmSJEmSpIVZvoif3Qm4ceLr9cAfb+yHImIRv3LzVWsuqDdbrblq5jYrS83bq9ZsteaCerOZqzy1ZjNXeWrNVmuumtW6zWrNpfL4WCyP26w8bjNtLnwslqXm7VVrtlpzQb3ZzFWeWrOZS5sLt1l53GblcZtpc+FjsbGYhbH3SUQcDhzefnkHsG5T/84R7AD8fOwhNpFas5mrPLVmM1d5as1Way6oN5u5ylNrNnOVp9Zs5ipPrdlqzQX1ZjNXeWrNZq7y1JrNXOWpNZu5ylNrtlpzQb3ZzFWeWrOZqzy1ZjNXeWrNVmsuqDebucpTa7YnLPQHFrMw9iZg54mvV7Tfu5fMPBk4GSAiLs3MZy7id26Was0F9WYzV3lqzWau8tSardZcUG82c5Wn1mzmKk+t2cxVnlqz1ZoL6s1mrvLUms1c5ak1m7nKU2s2c5Wn1my15oJ6s5mrPLVmM1d5as1mrvLUmq3WXFBvNnOVp9ZsEXHpQn9mi0X8vm8Bu0XELhGxJXAwcO4i/v8kSZIkSZIkSZIkSZIkSZKkmc38ibGZeVdEHAl8CVgGnJqZVw42mSRJkiRJkiRJkiRJkiRJkrQAMy+MBcjMNcCaBfzIyYv5fZuxWnNBvdnMVZ5as5mrPLVmqzUX1JvNXOWpNZu5ylNrNnOVp9ZsteaCerOZqzy1ZjNXeWrNZq7y1JrNXOWpNVutuaDebOYqT63ZzFWeWrOZqzy1Zqs1F9SbzVzlqTXbgnNFZm6KQSRJkiRJkiRJkiRJkiRJkqQltcXYA0iSJEmSJEmSJEmSJEmSJElDWJKFsRFxQERcExHXRcSxS/E7l0JEnBoRt0bEurFnGVJE7BwRX42I70XElRHx9rFnGkpEPDAiLomIK9ps7x97piFFxLKI+HZEnDf2LEOJiOsj4rsRsTYiLh17niFFxHYRcWZEXB0RV0XEc8aeabEi4gnttpr7320RcdTYcw0hIo5u9xvrIuIzEfHAsWcaSkS8vc11Zcnba9rrckQ8PCIuiIjvt38+bMwZZ9WR7VXtNrs7Ip455nyz6sh1Qrtf/E5EnB0R24044kw6cv1Dm2ltRJwfEY8Zc8ZZ9fXfiHhHRGRE7DDGbIvRsc3+PiJumnhNO2jMGWfRtb0i4q3t8+zKiPjQWPMtRsc2O2Nie10fEWtHHHEmHbn2iohvzvXhiNh7zBln1ZFtz4i4qO37/x0RDxlzxll0vX8uvYP05Cq6f/TkqqF/dGUruoN05Zr470X2j57tVUP/6NxmJXeQnm1WdP/oyVV8/+jJVnT/iI7j2xGxS0RcHM05mDMiYsuxZ12onmxHtrmK299Db67Tojlvti6arvyAsWddiJ5cp7Tf+040x74fNPasC9WVbeK/fzQifjPWfLPq2WarI+JHE69ne4086oL05IqI+GBEXBvN+Ze3jT3rQvVk+/rE9vpJRJwz8qgL0pNr/4i4vM31jYjYdexZF6In135trnUR8amIWD72rLOKeeeha+gfMDVX0d1jzpRcRXePSVOyFd8/oHutR6ndY86U7VV095g0JVvx/QOm5iq6e8yZkqvo7jFpSrbi+0dMWSsWM5x32eQLYyNiGfAx4EBgD+CQiNhjU//eJbIaOGDsITaBu4B3ZOYewLOBt1S0ze4A9svMPYG9gAMi4tnjjjSotwNXjT3EJvCCzNwrM4s78boRHwG+mJm7A3tSwbbLzGvabbUX8AzgduDscadavIjYCXgb8MzMfDKwDDh43KmGERFPBt4M7E3zOHxpwaVvNRu+Lh8LfDkzdwO+3H5dotVsmG0d8ArgwiWfZjir2TDXBcCTM/OpwLXAcUs91ABWs2GuEzLzqe3+8TzgvUs91EBWM6X/RsTOwIuBG5Z6oIGsZnqvP3HudS0z1yzxTENYzbxcEfECYCWwZ2Y+CfjwCHMNYTXzsmXmayZ6yOeAs0aYa7FWs+Fj8UPA+9tc722/LtFqNsz2CeDYzHwKTWc8ZqmHGkDX++fSO0hXrtL7R1euGvpHV7bSO0jnMarC+0ffsbfS+8fUbBV0kKm5KugfXY/FGvpHV7bS+0fX8e1/otl/7Ar8EnjTeCPOrCvb/wAvBH484myL0ZXrNGB34CnA1sBho004m65cR2fmnm2vugE4csQZZ9V5Himai8OKuuhtQt/5sWMm+sfasQacUVeu1wM7A7tn5hOB00ebcHZTs2XmPhP94yLK6x9d2+wk4NA216eB94w24Wym5Xou8Cng4Pbc0o+B14034qLNPw9dQ/+ADXOV3j3mzM9VeveYND9bDf0Dpqz1KLx7zJm2hqXk7jFpfrbXU37/gHm5Kugec+Zvr9K7x6R7skXEFtTTP+avFVvweZel+MTYvYHrMvOHmXknzRN/5RL83k0uMy8EfjH2HEPLzJsz8/L277+mefLsNO5Uw8jG3NU0D2j/lyOONJiIWAH8Gc3BXW3mIuKhwL7AKQCZeWdm/mrUoYa3P/CDzCz9jeOc5cDW7dU02wA/GXmeoTwRuDgzb8/Mu4Cv0Sx2KE7H6/JKmuJH++fLl3KmoUzLlplXZeY1I400iI5c57ePRYBvAiuWfLBF6sh128SX21Jo/+jpvycCf0d9uYrWkesI4PjMvKP9N7cu+WAD6NtmERHAq4HPLOlQA+jIlcDcJ5k9lEI7SEe2x/OHBZYXAH+xpEMNoOf9c9EdpCtX6f2jJ1cN/aMrW9EdZCPHqIrtH5Ufe+vKVnQH2dg2K7V/9OQqvn/0ZCu6f/Qc394POLP9fnHdA7qzZea3M/P68SZbnJ5ca9r/lsAlFNY/enLdBvfsF7emzNfpqdnaDwI6gaZ/FKfW82M9uY4APpCZd7f/rqjuARvfZtF86vl+wDlLP93senIV3T86cv0euDMzr22/X1z3mDP/PHS7ny++f0w7v15694DOXEV3jzkd2YrvH9Nyld49oO41LB3Ziu8ffdus1O4BnbmK7h5zpmTbnkr6xxQLPu+yFAtjdwJunPh6PZUc6L0/iIjHAk8DLh55lMG0HyG9FrgVuCAza8m2iqYU3T3yHENL4PyIuCwiDh97mAHtAvwM+GT7keafiIhtxx5qYAdT2AmhLpl5E82n2dwA3Az8b2aeP+5Ug1kH7BMR20fENsBBNFey1eKRmXlz+/efAo8ccxgt2BuBL4w9xFDa26fcCBxKeZ/W1ikiVgI3ZeYVY8+yCRwZze2XTr0vt+MoxONp9vsXR8TXIuJZYw+0CewD3JKZ3x97kIEcBZzQ7j8+TJmfZNnlSv5w4eyrKLyDzHv/XE0HqfG4APTmKr5/zM9WSweZzFVT/5jyWKymf8zLVk0H6dh/FN8/5uU6ior6x7xsxfeP+ce3gR8Av5q4yKPYczC1HrvvyxXNbYxfC3xxpPFm1pUrIj5J04F3B/51vAln15HtSODciZ5fnJ7H4gfb/nFiRGw13oSz6cj1OOA1EXFpRHwhInYbdcgZbWS/+HKaT8y6bdrPbs46ch0GrImI9TT7xeNHHHEmU16jLwGWt5/4CPBKCuwerVXc+zz09tTRP1ZR5/n1VXTkKrl7tFYxJVsF/WMVG+YqvnvQ/Vgsunu0VrFhthr6xyq694svp9DuwfRcxXeP1irune3n1NE/pq0VW/B5l6VYGKtCRcSDaG4BdlShO7apMvP32XwU9gpg72huI160iHgpcGtmXjb2LJvAn2Tm04EDaW55tu/YAw1kOfB04KTMfBrwf5R3e9VOEbEl8DLgP8eeZQjtyciVNAuaHwNsGxF/Oe5Uw8jMq2hud3M+zZvgtTRXMVenvQq2uKtE768i4t00t748bexZhpKZ787MnWkylXorn3tpF9S/i4IX2fQ4ieYgxl40F0X886jTDGc58HCa28keA3y2vZK+JodQycU5rSNobge2M3A07R0HKvFG4G8i4jLgwcCdI88zs773zyV3kFqPC3TlqqF/TMtWQweZzEWzjaroH1O2VzX9Y0q2KjpIz36x6P4xJVc1/WNKtuL7x/zj2zQn/6tQ47F72GiufwMuzMyvjzLcInTlysw30BxDvQp4zXgTzm5Ktn1pFtOXuNDmHh3b7Dia/cizaF6r3znehLPpyLUV8Ntsbrv678CpI444s43sP4rtHx25jgYOyswVwCeBfxlxxJlMeY1+Es2HyJwYEZcAv6bA8y+1noe+H+cqtnv0ZSu5f0zLFRGPofDu0bO9iu8ePdmK7h/3Yf9RZPfoyVV895iWrT0fUXz/YCNrxe7reZelWBh7E/deebyi/Z42Y+2VQp8DTsvMs8aeZ1PI5rb1XwUOGHmUITwPeFlEXA+cDuwXEf8x7kjDaD+pc+5j5s+meSNZg/XA+omre8+kWShbiwOByzPzlrEHGcgLgR9l5s8y83fAWcBzR55pMJl5SmY+IzP3BX4JXLuxnynILRHxaID2z+JuWXF/FBGvB14KHNqW2tqcRj23rHgczUUDV7Q9ZAVweUQ8atSpBpCZt7QHs++mOYBRUwc5q71r1iU0V5DuMPJMg4mI5cArgDPGnmVAr6PpHtBcdFTLY5HMvDozX5yZz6A5oPaDsWeaRcf75+I7SK3HBbpy1dA/7sM2K7KDTMlVRf+Ytr1q6R8dj8XiO0jP/qPo/tGRq4r+0fE8q6J/wL2Obz8H2K59LEIF52AqO3Z/j/m5IuJ9wI7A34441qJN216Z+XuacxXFdY9JE9leAOwKXNf2j20i4roRR1uUyW2WmTe3r8930CwIKHKfDxs8Ftfzh9eys4GnjjTWIKbsP3ag2VafH3GsRZvIdSCw58Q5szMo+BzMvOfYRZm5T2buDVxImedfNjgPDXyE8vtHrefXO3NV0D16t1nB/WPac+xKyu8eU7dXJd2j67FYev/o23+U3D2m5fo8dXSPrudZ8f2jY63Ygs+7LMXC2G8Bu0XELu0nCB4MnLsEv1czaj+t4RTgqswsbkV8n4jYMSK2a/++NfAi4OpRhxpAZh6XmSsy87E0z7GvZGbxn2YZEdtGxIPn/g68mOa278XLzJ8CN0bEE9pv7Q98b8SRhlbk1UI9bgCeHRHbtPvI/WmuOKxCRDyi/fOPaE7mfXrciQZ1Ls0JPdo//2vEWXQfRMQBNLd7eFlm3j72PEOZd7uUlVTQPwAy87uZ+YjMfGzbQ9YDT29f54o298aq9edU0kGAc2hO6BERjwe2pLmtSi1eCFydmevHHmRAPwH+tP37fkCxt2ieb6KDbAG8B/j4uBMtXM/756I7SK3HBbpy1dA/erIV3UGm5aqhf/Rsr+L7R8/+4xwK7iAb2S8W2z96chXfP3qeZ0X3j47j21fRLL55ZfvPiuseUO+x+65cEXEY8BLgkPaCiKJ05LomInZtvxc0dxQrbht2ZLssMx810T9uz8xdRxxzwXoei3MnloPm9rhF9Y+efcc5tN2D5jWtuMUAG9kvvhI4LzN/O9J4M+t5LXto2xOZ+F4xep5jc91jK5pPRSyqe0DneehDKbx/1Hp+vStX6d0DpmcDXlt6/+jYZg8rvXv0PBaL7h7Qu/84h4L7x0b2i8V2j459x0oK7x7Q+zwrun/0rBVb8HmX5Rv7B4uVmXdFxJHAl4BlwKmZeeWm/r1LISI+Azwf2CEi1gPvy8xibys14XnAa4HvRsTa9nvvysw14400mEcDn4qIZTQLwz+bmeeNPJO6PRI4u+lELAc+nZlfHHekQb0VOK29aOCHwBtGnmcQ7QvTi4C/GnuWoWTmxRFxJnA5zW07vw2cPO5Ug/pcRGwP/A54S3s1c3GmvS4Dx9PcovNNwI+BV4834ew6sv2C5hYqOwKfj4i1mfmS8aZcuI5cx9HcauSCdv//zcz869GGnEFHroPaiyHupnksFpVpTq39t2ObPT8i9qK5Dcf1FPi61pHrVODUiFhHc9vY12WW98mIPY/Fgyn44pyObfZm4CPRfALHb4HDx5twdh3ZHhQRb2n/yVk0nxBQmqnvnym/g3Tl2oqy+0dXro9SeP+gO9ubCu8gtR6j6tpeh5TeP+jOVnoH6Xssltw/urZXDf2jK9tuhfePqce3I+J7wOkR8Y80x61KfJ/Wle1tNBewPAr4TkSsyczDxhx0gbpy3UXz2nxR2z/OyswPjDjnQm2Qi+bTo74eEQ8BArgCOGK8EWdW63mkrsfiVyJiR5pttpby+mJXrm/QnH85GvgNUNJ+Y07fY/FgmvedJeraZm+mOVdxN82d7d445pAz6Mp1QjS3Od4COCkzvzLqlMN6J+X3jw1U0D26fJyyu0eXoHnuld4/7k9OK7x79Dme8vtHl5K7xwbatYyld48+xxTeP6auFYuIb7HA8y5R1vFHSZIkSZIkSZIkSZIkSZIkabotxh5AkiRJkiRJkiRJkiRJkiRJGoILYyVJkiRJkiRJkiRJkiRJklQFF8ZKkiRJkiRJkiRJkiRJkiSpCi6MlSRJkiRJkiRJkiRJkiRJUhVcGCtJkiRJkiRJkiRJkiRJkqQquDBWkiRJkiRJkiRJkiRJkiRJVXBhrCRJkiRJkiRJkiRJkiRJkqrgwlhJkiRJkiRJkiRJkiRJkiRV4f8BvoGi6TQFT1QAAAAASUVORK5CYII=\n"
},
"metadata": {
"needs_background": "light"
}
}
]
},
{
"cell_type": "markdown",
"source": [
"# Qucickstart Example: A*"
],
"metadata": {
"id": "ZzAqolvv9YEA"
}
},
{
"cell_type": "code",
"source": [
"zxi = Graph(50,50)\n",
"# generate graph with probability of obstacle = 0.3\n",
"zxi.generate_random_graph(0.3)\n",
"\n",
"distance_fn = lambda current, neighbour : abs(current[0] - neighbour[0]) + abs(current[1] - neighbour[1])\n",
"a = Astar(zxi.get_adjacency_list(distance_fn))\n",
"\n",
"paths, cost, visited = a.search(zxi.source, zxi.target, diagonal_distance)\n",
"zxi.add_visited(a.visited)\n",
"zxi.add_paths(paths)\n",
"# source : blue, obstacle : black, path : cream, target : green\n",