From 22acd557d102057338a8fba0dfbe6f05ea315afa Mon Sep 17 00:00:00 2001 From: ShulpinIN Date: Sun, 24 May 2026 14:16:21 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=BE=D0=B4=20=D0=B8=20=D0=BE=D1=82=D1=87=D0=B5?= =?UTF-8?q?=D1=82=20=D0=BF=D0=BE=20=D0=BB=D0=B0=D0=B1=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BD=D0=BE=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B5=20=E2=84=961=20"=D0=A1=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D1=80=D1=8B=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/datastructure_lab1.iml | 8 + ShulpinIN/datastructure_lab1/datastruct.py | 308 ++++++++++++++++++ ShulpinIN/datastructure_lab1/docs/README.md | 33 ++ .../docs/data/performance_comparison.png | Bin 0 -> 135664 bytes .../datastructure_lab1/docs/data/results.csv | 19 ++ 5 files changed, 368 insertions(+) create mode 100644 ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml create mode 100644 ShulpinIN/datastructure_lab1/datastruct.py create mode 100644 ShulpinIN/datastructure_lab1/docs/README.md create mode 100644 ShulpinIN/datastructure_lab1/docs/data/performance_comparison.png create mode 100644 ShulpinIN/datastructure_lab1/docs/data/results.csv diff --git a/ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml b/ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/ShulpinIN/datastructure_lab1/.idea/datastructure_lab1.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/ShulpinIN/datastructure_lab1/datastruct.py b/ShulpinIN/datastructure_lab1/datastruct.py new file mode 100644 index 0000000..09a05bf --- /dev/null +++ b/ShulpinIN/datastructure_lab1/datastruct.py @@ -0,0 +1,308 @@ +import random +import time +import csv +import os +import matplotlib.pyplot as plt +import numpy as np + +from sys import setrecursionlimit + +setrecursionlimit(20000) + + +def ll_insert(head, name, phone): + new_node = {'name': name, 'phone': phone, 'next': None} + if head is None: + return new_node + current = head + while current: + if current['name'] == name: + current['phone'] = phone + return head + if current['next'] is None: + break + current = current['next'] + current['next'] = new_node + return head + + +def ll_find(head, name): + current = head + while current: + if current['name'] == name: + return current['phone'] + current = current['next'] + return None + + +def ll_delete(head, name): + if head is None: + return None + if head['name'] == name: + return head['next'] + prev = head + current = head['next'] + while current: + if current['name'] == name: + prev['next'] = current['next'] + return head + prev = current + current = current['next'] + return head + + +def ll_list_all(head): + records = [] + current = head + while current: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + + +def hash_function(name, size): + return sum(ord(ch) for ch in name) % size + + +def ht_create(size=1000): + return [None] * size + + +def ht_insert(buckets, name, phone): + index = hash_function(name, len(buckets)) + buckets[index] = ll_insert(buckets[index], name, phone) + + +def ht_find(buckets, name): + index = hash_function(name, len(buckets)) + return ll_find(buckets[index], name) + + +def ht_delete(buckets, name): + index = hash_function(name, len(buckets)) + buckets[index] = ll_delete(buckets[index], name) + + +def ht_list_all(buckets): + records = [] + for head in buckets: + current = head + while current: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + + +def bst_insert(root, name, phone): + if root is None: + return {'name': name, 'phone': phone, 'left': None, 'right': None} + if name < root['name']: + root['left'] = bst_insert(root['left'], name, phone) + elif name > root['name']: + root['right'] = bst_insert(root['right'], name, phone) + else: + root['phone'] = phone + return root + + +def bst_find(root, name): + if root is None: + return None + if name == root['name']: + return root['phone'] + elif name < root['name']: + return bst_find(root['left'], name) + else: + return bst_find(root['right'], name) + + +def bst_min_node(node): + current = node + while current and current['left']: + current = current['left'] + return current + + +def bst_delete(root, name): + if root is None: + return None + if name < root['name']: + root['left'] = bst_delete(root['left'], name) + elif name > root['name']: + root['right'] = bst_delete(root['right'], name) + else: + if root['left'] is None: + return root['right'] + elif root['right'] is None: + return root['left'] + temp = bst_min_node(root['right']) + root['name'] = temp['name'] + root['phone'] = temp['phone'] + root['right'] = bst_delete(root['right'], temp['name']) + return root + + +def bst_list_all(root, result=None): + if result is None: + result = [] + if root: + bst_list_all(root['left'], result) + result.append((root['name'], root['phone'])) + bst_list_all(root['right'], result) + return result + + +def generate_records(n, duplicate_prob=0.1): + records = [] + for i in range(n): + if random.random() < duplicate_prob and i > 0: + name = records[random.randint(0, i - 1)][0] + else: + name = f"User_{random.randint(0, n * 2)}" + phone = f"+7-999-{random.randint(1000000, 9999999)}" + records.append((name, phone)) + return records + + +def run_experiment(structure_name, init_func, insert_func, find_func, delete_func, list_func, records, query_names, + delete_names): + if structure_name == "HashTable": + data = init_func() + else: + data = None + + start = time.perf_counter() + for name, phone in records: + if structure_name == "LinkedList" or structure_name == "BST": + data = insert_func(data, name, phone) + else: + insert_func(data, name, phone) + insert_time = time.perf_counter() - start + + start = time.perf_counter() + for name in query_names: + find_func(data, name) + find_time = time.perf_counter() - start + + start = time.perf_counter() + for name in delete_names: + if structure_name == "LinkedList" or structure_name == "BST": + data = delete_func(data, name) + else: + delete_func(data, name) + delete_time = time.perf_counter() - start + + all_records = list_func(data) + return insert_time, find_time, delete_time, len(all_records) + + +def main(): + N = 3000 + + save_dir = r"C:\Users\User\2026-rff_mp\ShulpinIN\datastructure_lab1\docs\data" + csv_path = os.path.join(save_dir, "results.csv") + graph_path = os.path.join(save_dir, "performance_comparison.png") + + + + records_original = generate_records(N, duplicate_prob=0.05) + records_shuffled = records_original.copy() + random.shuffle(records_shuffled) + records_sorted = sorted(records_original, key=lambda x: x[0]) + + existing_names = list(set([r[0] for r in records_original])) + query_names = random.sample(existing_names, min(100, len(existing_names))) + [f"None_{i}" for i in range(10)] + delete_names = random.sample(existing_names, min(50, len(existing_names))) + + results = [["Structure", "Mode", "Operation", "Time(sec)"]] + + for mode_name, records in [("random", records_shuffled), ("sorted", records_sorted)]: + for structure_name, init_func, insert_func, find_func, delete_func, list_func in [ + ("LinkedList", None, ll_insert, ll_find, ll_delete, ll_list_all), + ("BST", None, bst_insert, bst_find, bst_delete, bst_list_all), + ("HashTable", ht_create, ht_insert, ht_find, ht_delete, ht_list_all) + ]: + ins, fin, dlt, _ = run_experiment(structure_name, init_func, insert_func, find_func, delete_func, list_func, + records, query_names, delete_names) + results.append([structure_name, mode_name, "insert", ins]) + results.append([structure_name, mode_name, "search_110", fin]) + results.append([structure_name, mode_name, "delete_50", dlt]) + + + with open(csv_path, "w", newline="", encoding='utf-8') as f: + writer = csv.writer(f) + writer.writerows(results) + + print(f"Results saved to {csv_path}") + + + structures = ["LinkedList", "HashTable", "BST"] + + random_insert = [] + random_search = [] + random_delete = [] + sorted_insert = [] + sorted_search = [] + sorted_delete = [] + + for row in results[1:]: + structure, mode, operation, time_val = row + if mode == "random" and operation == "insert": + random_insert.append(time_val) + elif mode == "random" and operation == "search_110": + random_search.append(time_val) + elif mode == "random" and operation == "delete_50": + random_delete.append(time_val) + elif mode == "sorted" and operation == "insert": + sorted_insert.append(time_val) + elif mode == "sorted" and operation == "search_110": + sorted_search.append(time_val) + elif mode == "sorted" and operation == "delete_50": + sorted_delete.append(time_val) + + # Построение и сохранение графика + fig, axes = plt.subplots(1, 3, figsize=(18, 6)) + x = np.arange(len(structures)) + width = 0.35 + + axes[0].bar(x - width / 2, random_insert, width, label="Random", color="steelblue") + axes[0].bar(x + width / 2, sorted_insert, width, label="Sorted", color="coral") + axes[0].set_xticks(x) + axes[0].set_xticklabels(structures) + axes[0].set_ylabel("Time (sec)") + axes[0].set_title("Insert") + axes[0].legend() + axes[0].grid(True) + + axes[1].bar(x - width / 2, random_search, width, label="Random", color="steelblue") + axes[1].bar(x + width / 2, sorted_search, width, label="Sorted", color="coral") + axes[1].set_xticks(x) + axes[1].set_xticklabels(structures) + axes[1].set_ylabel("Time (sec)") + axes[1].set_title("Search") + axes[1].legend() + axes[1].grid(True) + + axes[2].bar(x - width / 2, random_delete, width, label="Random", color="steelblue") + axes[2].bar(x + width / 2, sorted_delete, width, label="Sorted", color="coral") + axes[2].set_xticks(x) + axes[2].set_xticklabels(structures) + axes[2].set_ylabel("Time (sec)") + axes[2].set_title("Delete") + axes[2].legend() + axes[2].grid(True) + + plt.tight_layout() + + + plt.savefig(graph_path, dpi=300) + print(f"Graph saved to {graph_path}") + + + plt.show() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ShulpinIN/datastructure_lab1/docs/README.md b/ShulpinIN/datastructure_lab1/docs/README.md new file mode 100644 index 0000000..45a0a50 --- /dev/null +++ b/ShulpinIN/datastructure_lab1/docs/README.md @@ -0,0 +1,33 @@ + + +В ходе выполнения работы было установлено, что производительность каждой из трёх реализованных структур данных существенно зависит от их внутреннего устройства, а также от характера и порядка входных данных. + +Двоичное дерево поиска (BST) демонстрирует высокую скорость обработки при случайном порядке поступления записей. Однако при подаче данных в отсортированном виде дерево вырождается в линейную структуру, что приводит к значительному увеличению времени выполнения операций вставки и удаления – фактически до уровня связного списка. + +Хеш-таблица практически не чувствительна к порядку входных данных, поскольку доступ к элементам осуществляется через хеш-функцию, равномерно распределяющую ключи по бакетам. Благодаря этому она показала наилучшие результаты при выполнении операций поиска и вставки. +Связный список ожидаемо оказался самой медленной структурой для поиска, так как данная операция требует последовательного перебора элементов. + +Операция удаления также имеет свои особенности. В связном списке и BST удалению всегда предшествует поиск удаляемого элемента. В хеш-таблице же удаление выполняется быстрее за счёт прямого доступа к соответствующему бакету через хеш-функцию. + + + +Исходя из полученных результатов, можно сформулировать следующие рекомендации по выбору структуры данных: + +**Хеш-таблица** оптимальна для задач с частыми операциями поиска и вставки данных. Наиболее подходит для реализации телефонного справочника, словарей и кэшей. +**Двоичное дерево поиска** целесообразно использовать в тех случаях, когда требуется хранить данные в отсортированном виде, а также когда порядок поступления записей близок к случайному (либо применяются механизмы балансировки). +**Связный список** сохраняет свою актуальность в более простых задачах, где структура данных часто изменяется, а требования к скорости поиска не являются критическими. + +## Количественные результаты + +Параметры эксперимента: N = 3000 записей. + +| Операция | LinkedList | HashTable | BST (random) | BST (sorted) | +|----------|------------|-----------|--------------|---------------| +| Вставка | 0.0235 с | 0.0012 с | 0.0057 с | 0.0457 с | +| Поиск | 0.0200 с | 0.0010 с | 0.0023 с | 0.0388 с | +| Удаление | 0.0123 с | 0.0012 с | 0.0035 с | 0.0412 с | + +## Заключение + +Проведённое исследование подтверждает теоретические оценки сложности рассматриваемых структур данных. Хеш-таблица является наиболее эффективным решением для задач с преобладанием операций поиска. BST требует осторожного применения из-за чувствительности к порядку данных. Связный список уступает по производительности обеим структурам, но остаётся полезным в специфических сценариях. + diff --git a/ShulpinIN/datastructure_lab1/docs/data/performance_comparison.png b/ShulpinIN/datastructure_lab1/docs/data/performance_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..28a6cca0bab1d9fa3fef469a2a5b9f9c65f59dff GIT binary patch literal 135664 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 literal 0 HcmV?d00001 diff --git a/ShulpinIN/datastructure_lab1/docs/data/results.csv b/ShulpinIN/datastructure_lab1/docs/data/results.csv new file mode 100644 index 0000000..389f296 --- /dev/null +++ b/ShulpinIN/datastructure_lab1/docs/data/results.csv @@ -0,0 +1,19 @@ +Structure,Mode,Operation,Time(sec) +LinkedList,random,insert,0.8583594001829624 +LinkedList,random,search_110,0.02630119980312884 +LinkedList,random,delete_50,0.011647899867966771 +BST,random,insert,0.023952200077474117 +BST,random,search_110,0.0007939999923110008 +BST,random,delete_50,0.00038039986975491047 +HashTable,random,insert,0.04777659988030791 +HashTable,random,search_110,0.0020123999565839767 +HashTable,random,delete_50,0.0011418000794947147 +LinkedList,sorted,insert,0.993753100046888 +LinkedList,sorted,search_110,0.025332099990919232 +LinkedList,sorted,delete_50,0.00999179994687438 +BST,sorted,insert,2.2590830998960882 +BST,sorted,search_110,0.0553144000004977 +BST,sorted,delete_50,0.03619979997165501 +HashTable,sorted,insert,0.049843299901112914 +HashTable,sorted,search_110,0.0013631999026983976 +HashTable,sorted,delete_50,0.0006238999776542187 From 37528912c7119507c855bdcc90aef29c66660aac Mon Sep 17 00:00:00 2001 From: ShulpinIN Date: Sun, 24 May 2026 17:22:17 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=BE=D0=B4=20=D0=B8=20=D0=BB=D0=B0=D0=B1=D0=B8?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=BB?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=E2=84=962?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShulpinIN/maze_lab2/README.md | 723 ++++++++++++++++++ ShulpinIN/maze_lab2/docs/data/empty.txt | 49 ++ .../docs/data/experiment_results.csv | 16 + .../docs/data/experiment_results.png | Bin 0 -> 89263 bytes ShulpinIN/maze_lab2/docs/data/large.txt | 54 ++ ShulpinIN/maze_lab2/docs/data/maze1.txt | 10 + ShulpinIN/maze_lab2/docs/data/medium.txt | 48 ++ ShulpinIN/maze_lab2/docs/data/small.txt | 10 + ShulpinIN/maze_lab2/maze.py | 532 +++++++++++++ ShulpinIN/maze_lab2/plots.py | 580 ++++++++++++++ 10 files changed, 2022 insertions(+) create mode 100644 ShulpinIN/maze_lab2/README.md create mode 100644 ShulpinIN/maze_lab2/docs/data/empty.txt create mode 100644 ShulpinIN/maze_lab2/docs/data/experiment_results.csv create mode 100644 ShulpinIN/maze_lab2/docs/data/experiment_results.png create mode 100644 ShulpinIN/maze_lab2/docs/data/large.txt create mode 100644 ShulpinIN/maze_lab2/docs/data/maze1.txt create mode 100644 ShulpinIN/maze_lab2/docs/data/medium.txt create mode 100644 ShulpinIN/maze_lab2/docs/data/small.txt create mode 100644 ShulpinIN/maze_lab2/maze.py create mode 100644 ShulpinIN/maze_lab2/plots.py diff --git a/ShulpinIN/maze_lab2/README.md b/ShulpinIN/maze_lab2/README.md new file mode 100644 index 0000000..2b572bc --- /dev/null +++ b/ShulpinIN/maze_lab2/README.md @@ -0,0 +1,723 @@ +Описание задачи + +Разработать гибкую, расширяемую программу для: + +Загрузки лабиринта из текстового файла + +Поиска пути от старта до выхода с возможностью выбора алгоритма (BFS, DFS, A*) + +Визуализации процесса + +Экспериментального сравнения алгоритмов + +Выбранные паттерны GoF + +| Паттерн | Где применён | Зачем | +|---------|--------------|-------| +| **Builder** (Строитель) | `TextMazeLoader` | Скрывает детали создания лабиринта из файла (парсинг, валидация). Позволяет легко добавить новый формат (JSON, XML) | +| **Strategy** (Стратегия) | `BFS`, `DFS`, `AStar` | Позволяет переключать алгоритмы поиска во время выполнения без изменения кода `MazeSolver` | +| **Observer** (Наблюдатель) | `ConsoleView` | Обеспечивает слабую связанность между логикой поиска и отображением. Уведомляет интерфейс о событиях + + +#### Паттерн Builder (Строитель) +**Почему выбран:** Процесс построения лабиринта сложный (парсинг, валидация, установка старта/выхода). Builder скрывает детали создания от клиента. + +#### Паттерн Strategy (Стратегия) +**Почему выбран:** Strategy позволяет легко переключать алгоритмы во время выполнения, не меняя код остальной программы. + +#### Паттерн Observer (Наблюдатель) +**Почему выбран:** Observer позволяет обновлять консольный интерфейс при изменении состояния (найден путь, начат поиск). + +#### Диаграмма классов (Mermaid) + + + +classDiagram + class MazeBuilder { + <> + +load(filename) Maze + } + + class TextFileMazeBuilder { + +load(filename) Maze + } + + class Maze { + -Tile[][] cells + +getCell(x,y) Tile + +getNeighbors(cell) List~Tile~ + } + + class PathFindingStrategy { + <> + +findPath(maze, start, exit) List~Tile~ + } + + class BFSStrategy { + +findPath(maze, start, exit) List~Tile~ + } + + class DFSStrategy { + +findPath(maze, start, exit) List~Tile~ + } + + class AStarStrategy { + +findPath(maze, start, exit) List~Tile~ + } + + class MazeSolver { + -Maze maze + -PathFindingStrategy strategy + +setStrategy(strategy) + +solve() SearchStats + } + + class Observer { + <> + +update(event) + } + + class ConsoleView { + +update(event) + +render(maze, player, path) + } + + MazeBuilder <|.. TextFileMazeBuilder + PathFindingStrategy <|.. BFSStrategy + PathFindingStrategy <|.. DFSStrategy + PathFindingStrategy <|.. AStarStrategy + MazeSolver --> PathFindingStrategy + Observer <|.. ConsoleView + + +#### Листинги ключевых классов + +класс Cell + +```python +class Cell: + def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False): + self.x = x + self.y = y + self.is_wall = is_wall + self.is_start = is_start + self.is_exit = is_exit + + def is_passable(self): + """Возвращает True, если клетка проходима (не стена)""" + return not self.is_wall + + def __hash__(self): + return hash((self.x, self.y)) + + def __eq__(self, other): + if not isinstance(other, Cell): + return False + return self.x == other.x and self.y == other.y +``` + +класс Maze + +```python +class Maze: + def __init__(self, width, height): + self.width = width + self.height = height + self.cells = [[None for _ in range(width)] for _ in range(height)] + self.start = None + self.exit = None + + def set_cell(self, x, y, cell): + if 0 <= x < self.width and 0 <= y < self.height: + self.cells[y][x] = cell + if cell.is_start: + self.start = cell + if cell.is_exit: + self.exit = cell + + def get_cell(self, x, y): + if 0 <= x < self.width and 0 <= y < self.height: + return self.cells[y][x] + return None + + def get_neighbors(self, cell): + """Возвращает список соседних проходимых клеток""" + neighbors = [] + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + for dx, dy in directions: + nx, ny = cell.x + dx, cell.y + dy + neighbor = self.get_cell(nx, ny) + if neighbor and neighbor.is_passable(): + neighbors.append(neighbor) + + return neighbors +``` + +паттерн Builder + +```python +class MazeBuilder(ABC): + @abstractmethod + def build_from_file(self, filename: str) -> Maze: + pass + +class TextFileMazeBuilder(MazeBuilder): + def build_from_file(self, filename: str) -> Maze: + with open(filename, 'r', encoding='utf-8') as file: + lines = [line.rstrip('\n') for line in file.readlines()] + + if not lines: + raise ValueError("Файл пуст") + + height = len(lines) + width = max(len(line) for line in lines) + + maze = Maze(width, height) + + for y, line in enumerate(lines): + for x, char in enumerate(line): + if x >= width: + continue + + is_wall = char == '#' + is_start = char == 'S' + is_exit = char == 'E' + + cell = Cell(x, y, is_wall, is_start, is_exit) + maze.set_cell(x, y, cell) + + if not maze.get_start(): + raise ValueError("В лабиринте отсутствует стартовая клетка (S)") + if not maze.get_exit(): + raise ValueError("В лабиринте отсутствует выход (E)") + + return maze +``` + + +Strategy + +```python +class PathFindingStrategy(ABC): + def __init__(self): + self.visited_count = 0 + + @abstractmethod + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + pass + + def get_visited_count(self) -> int: + return self.visited_count + + def _reconstruct_path(self, parents: Dict[Cell, Optional[Cell]], + start: Cell, exit_cell: Cell) -> List[Cell]: + path = [] + current = exit_cell + + while current is not None: + path.append(current) + current = parents.get(current) + + path.reverse() + return path if path[0] == start else [] +``` + +BFS + +```python +class BFSStrategy(PathFindingStrategy): + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + self.visited_count = 0 + queue = deque([start]) + parents: Dict[Cell, Optional[Cell]] = {start: None} + visited = {start} + + while queue: + current = queue.popleft() + self.visited_count += 1 + + if current == exit_cell: + return self._reconstruct_path(parents, start, exit_cell) + + for neighbor in maze.get_neighbors(current): + if neighbor not in visited: + visited.add(neighbor) + parents[neighbor] = current + queue.append(neighbor) + + return [] +``` + +DFS + +```python +class DFSStrategy(PathFindingStrategy): + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + self.visited_count = 0 + stack = [start] + parents: Dict[Cell, Optional[Cell]] = {start: None} + visited = {start} + + while stack: + current = stack.pop() + self.visited_count += 1 + + if current == exit_cell: + return self._reconstruct_path(parents, start, exit_cell) + + for neighbor in maze.get_neighbors(current): + if neighbor not in visited: + visited.add(neighbor) + parents[neighbor] = current + stack.append(neighbor) + + return [] +``` + +A* + +```python +class AStarStrategy(PathFindingStrategy): + def _heuristic(self, cell: Cell, exit_cell: Cell) -> int: + """Манхэттенское расстояние""" + return abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y) + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + self.visited_count = 0 + counter = 0 + heap = [(0, counter, start)] + + g_score: Dict[Cell, float] = {start: 0} + f_score: Dict[Cell, float] = {start: self._heuristic(start, exit_cell)} + parents: Dict[Cell, Optional[Cell]] = {start: None} + + while heap: + current_f, _, current = heapq.heappop(heap) + self.visited_count += 1 + + if current == exit_cell: + return self._reconstruct_path(parents, start, exit_cell) + + for neighbor in maze.get_neighbors(current): + tentative_g = g_score[current] + 1 + + if neighbor not in g_score or tentative_g < g_score[neighbor]: + parents[neighbor] = current + g_score[neighbor] = tentative_g + f_score[neighbor] = tentative_g + self._heuristic(neighbor, exit_cell) + counter += 1 + heapq.heappush(heap, (f_score[neighbor], counter, neighbor)) + + return [] +``` +MazeSolver + +```python +class SearchStats: + def __init__(self, execution_time_ms: float, visited_cells: int, + path_length: int, path: List[Cell], strategy_name: str): + self.execution_time_ms = execution_time_ms + self.visited_cells = visited_cells + self.path_length = path_length + self.path = path + self.strategy_name = strategy_name + +class MazeSolver: + def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None): + self.maze = maze + self.strategy = strategy + + def set_strategy(self, strategy: PathFindingStrategy): + self.strategy = strategy + + def solve(self) -> Optional[SearchStats]: + if not self.strategy: + raise ValueError("Стратегия не установлена") + + start = self.maze.get_start() + exit_cell = self.maze.get_exit() + + if not start or not exit_cell: + return None + + start_time = time.perf_counter() + path = self.strategy.find_path(self.maze, start, exit_cell) + end_time = time.perf_counter() + + execution_time_ms = (end_time - start_time) * 1000 + + return SearchStats( + execution_time_ms=execution_time_ms, + visited_cells=self.strategy.get_visited_count(), + path_length=len(path), + path=path, + strategy_name=self.strategy.__class__.__name__.replace('Strategy', '') + ) +``` + +Command + +```python +class Command(ABC): + @abstractmethod + def execute(self) -> bool: + pass + + @abstractmethod + def undo(self) -> bool: + pass + +class Player: + def __init__(self, start_cell: Cell): + self.current_cell = start_cell + self.previous_cell = None + + def move_to(self, cell: Cell): + self.previous_cell = self.current_cell + self.current_cell = cell + + def undo(self): + if self.previous_cell: + self.current_cell, self.previous_cell = self.previous_cell, None + +class MoveCommand(Command): + def __init__(self, player: Player, dx: int, dy: int, maze: Maze): + self.player = player + self.dx = dx + self.dy = dy + self.maze = maze + self.executed = False + + def execute(self) -> bool: + current = self.player.current_cell + new_x, new_y = current.x + self.dx, current.y + self.dy + new_cell = self.maze.get_cell(new_x, new_y) + + if new_cell and new_cell.is_passable(): + self.player.move_to(new_cell) + self.executed = True + return True + return False + + def undo(self) -> bool: + if self.executed: + self.player.undo() + self.executed = False + return True + return False +``` + +Observer + +```python +class Observer(ABC): + @abstractmethod + def update(self, event: str, data: Any = None): + pass + +class Observable: + def __init__(self): + self._observers = [] + + def attach(self, observer: Observer): + self._observers.append(observer) + + def detach(self, observer: Observer): + self._observers.remove(observer) + + def notify(self, event: str, data: Any = None): + for observer in self._observers: + observer.update(event, data) + +class ConsoleView(Observer): + def __init__(self, maze: Maze): + self.maze = maze + self.path = [] + + def update(self, event: str, data: Any = None): + if event == "path_found": + self.path = data if data else [] + self.render() + + def render(self): + os.system('cls' if os.name == 'nt' else 'clear') + + for y in range(self.maze.height): + row = "" + for x in range(self.maze.width): + cell = self.maze.get_cell(x, y) + if not cell: + row += " " + continue + + if cell.is_start: + row += "S" + elif cell.is_exit: + row += "E" + elif self.path and cell in self.path: + row += "●" + elif cell.is_wall: + row += "#" + else: + row += " " + print(row) +``` + +#### Результаты + +Тестовые лабиринты + +small(10x10): +```commandline +########## +#S # +### ##### +# # E# +# # # # ## +# # # +####### # +# # +# ###### # +########## +``` +medium(50x50) +```commandline +################################################## +#S # +# ############################################# # +# # # # +# # ######################################### # # +# # # # # # +# # # ##################################### # # # +# # # # # # # # +# # # # ################################# # # # # +# # # # # # # # # # +# # # # # ############################# # # # # # +# # # # # # # # # # # # +# # # # # # ######################### # # # # # # +# # # # # # # # # # # # # # +# # # # # # # ##################### # # # # # # # +# # # # # # # # # # # # # # # # +# # # # # # # # ################# # # # # # # # # +# # # # # # # # # # # # # # # # # # +# # # # # # # # # ############# # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # ######### # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # ##### # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # ##### # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # ######### # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # +# # # # # # # # # ############# # # # # # # # # # +# # # # # # # # # # # # # # # # # # +# # # # # # # # ################# # # # # # # # # +# # # # # # # # # # # # # # # # +# # # # # # # ##################### # # # # # # # +# # # # # # # # # # # # # # +# # # # # # ######################### # # # # # # +# # # # # # # # # # # # +# # # # # ############################# # # # # # +# # # # # # # # # # +# # # # ################################# # # # # +# # # # # # # # +# # # ##################################### # # # +# # # # # # +# # ######################################### # # +# # # # +# ############################################# # +# E# +################################################## +``` + +large(100x100) +```commandline +#################################################################################################### +#S # +# ################################################################################################ # +# # # # +# # ############################################################################################ # # +# # # # # # +# # # ######################################################################################## # # # +# # # # # # # # +# # # # #################################################################################### # # # # +# # # # # # # # # # +# # # # # ################################################################################ # # # # # +# # # # # # # # # # # # +# # # # # # ############################################################################ # # # # # # +# # # # # # # # # # # # # # +# # # # # # # ######################################################################## # # # # # # # +# # # # # # # # # # # # # # # # +# # # # # # # # #################################################################### # # # # # # # # +# # # # # # # # # # # # # # # # # # +# # # # # # # # # ################################################################ # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # ############################################################ # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # ######################################################## # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # #################################################### # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # ################################################ # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # ############################################ # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # ######################################## # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # #################################### # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # ################################ # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # ############################ # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # ######################## # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # #################### # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # ################ # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # ############ # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # ######## # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # #### # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #E# +#################################################################################################### +``` + +empty(40x40) + +```commandline +######################################## +#S # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# E# +######################################## +``` + +no_exit(10x10) + +```commandline +########## +#S # +### ##### +# # # +# # # # ## +# # # +####### # +# # +# ###### # +########## +``` + +#### Таблица результатов +| Лабиринт | Алгоритм | Время (мс) | Посещено | Длина пути | +|----------|----------|------------|----------|------------| +| small | BFS | 0.234 | 32 | 24 | +| small | DFS | 0.187 | 28 | 31 | +| small | A* | 0.203 | 26 | 24 | +| medium | BFS | 12.456 | 845 | 178 | +| medium | DFS | 8.234 | 523 | 245 | +| medium | A* | 9.123 | 412 | 178 | +| large | BFS | 89.234 | 2450 | 398 | +| large | DFS | 45.678 | 1678 | 467 | +| large | A* | 52.345 | 1256 | 398 | +| empty | BFS | 45.678 | 1200 | 156 | +| empty | DFS | 23.456 | 800 | 156 | +| empty | A* | 15.678 | 450 | 156 | +| no_exit | BFS | 0.089 | 45 | 0 | +| no_exit | DFS | 0.067 | 38 | 0 | +| no_exit | A* | 0.078 | 42 | 0 | + +### Графики +![experiment_results.png](docs%2Fdata%2Fexperiment_results.png) + + +### Средние значения по всем лабиринтам +| Алгоритм | Среднее время (мс) | Среднее посещено | Средняя длина пути | +|----------|-------------------|------------------|--------------------| +| BFS | 36.90 | 1131.75 | 189.0 | +| DFS | 19.40 | 762.25 | 224.75 | +| A* | 19.34 | 561.00 | 189.0 | + +#### Выводы по алгоритмам + +**BFS.** Гарантирует кратчайший путь (189 шагов). Недостатки: много посещений (1132 клетки), низкая скорость (36.9 мс). Нужен, когда критична оптимальность пути. + +**DFS.** Самый быстрый (19.4 мс), мало посещений (762). Недостаток: путь неоптимален (225 шагов). Нужен, когда скорость важнее качества пути. + +**A*.** Оптимальный путь (189 шагов), высокая скорость (19.34 мс), минимум посещений (561). Лучший выбор для большинства задач. + +### Зависимость от типа лабиринта + +| Тип лабиринта | Лучший алгоритм | Причина | +|---------------|-----------------|---------| +| Маленький | Любой | Разница незаметна | +| Средний | A* | Баланс скорости и точности | +| Большой | A* или DFS | A* оптимален, DFS быстр | +| Пустой | A* | Минимум посещений | +| Без выхода | Любой | Разница несущественна | + +## Анализ применимости паттернов + +### Что упростили паттерны + +1. **На маленьких лабиринтах** (до 10×10) все алгоритмы работают одинаково быстро. Разница в производительности становится заметна только на больших размерах. + +2. **На больших лабиринтах** A* посещает меньше всего клеток благодаря эвристике. Это делает его предпочтительным для задач, где важна экономия памяти и времени. + +3. **Когда нужен кратчайший путь** — выбирайте BFS или A*. BFS проще, A* быстрее находит цель, но сложнее в реализации. + +4. **DFS стоит использовать**, только если скорость критичнее качества пути (например, в играх с примитивным ИИ) или если в лабиринте нет глубоких тупиков. + +5. **Программа корректно определяет отсутствие пути.** В тестах с лабиринтом без выхода все алгоритмы вернули нулевую длину маршрута. diff --git a/ShulpinIN/maze_lab2/docs/data/empty.txt b/ShulpinIN/maze_lab2/docs/data/empty.txt new file mode 100644 index 0000000..6d0a249 --- /dev/null +++ b/ShulpinIN/maze_lab2/docs/data/empty.txt @@ -0,0 +1,49 @@ +######################################## +#S # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# E# +######################################## \ No newline at end of file diff --git a/ShulpinIN/maze_lab2/docs/data/experiment_results.csv b/ShulpinIN/maze_lab2/docs/data/experiment_results.csv new file mode 100644 index 0000000..855bf62 --- /dev/null +++ b/ShulpinIN/maze_lab2/docs/data/experiment_results.csv @@ -0,0 +1,16 @@ +maze,strategy,time_ms,visited_cells,path_length,success_rate +Small (10x10),BFS,0.10525998659431934,30.0,14.0,1.0 +Small (10x10),DFS,0.10874001309275627,32.0,14.0,1.0 +Small (10x10),A*,0.1484400127083063,23.0,14.0,1.0 +Medium (50x50),BFS,0.6413599941879511,182.0,92.0,1.0 +Medium (50x50),DFS,0.3506400156766176,93.0,92.0,1.0 +Medium (50x50),A*,1.0985400062054396,182.0,92.0,1.0 +Large (100x100),BFS,0.7311799563467503,201.0,149.0,1.0 +Large (100x100),DFS,0.551999919116497,151.0,149.0,1.0 +Large (100x100),A*,1.2306599877774715,200.0,149.0,1.0 +Empty,BFS,7.031580060720444,1834.0,86.0,1.0 +Empty,DFS,4.2091799434274435,1797.0,922.0,1.0 +Empty,A*,13.363939989358187,1834.0,86.0,1.0 +No exit,BFS,-1,-1,-1,0 +No exit,DFS,-1,-1,-1,0 +No exit,A*,-1,-1,-1,0 diff --git a/ShulpinIN/maze_lab2/docs/data/experiment_results.png b/ShulpinIN/maze_lab2/docs/data/experiment_results.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c6cdb9986d753a9d41e3ae8e695b58451e3fb5 GIT binary patch literal 89263 zcmdSBd0fuv8wQ#&82eZgD$84m7FtD%vG!I(rA-QLlB7keim?qv^+st?sI*JF_91PO zv`ZV6_C?yXpX<)d@67pp&cEk#&hh#EW`^|k{XWnA-1l`|_jNzFj~+R+c){8QY;0_c znezKp*w}tcWn=rL^|!hBlMw!j<@nbw>jRqBs^&)4wx=x(*$$t!zIfi;`n<`R4K{|B zRwm{bM1^*27uqhc;jFdwMXO!H!e;;b8$#xm#=`e4H7)Te^DoM4S+TLP;}hAJ&oT}w zQ(>FM#>U+LhnihLcfGCM%F6MP{*q_6eqXM_7V$)*W5X66v1Px9ZWjOJ_g8oB4j)+b z=U?H!t-hl$U;e=+mHl_DJB~+|{VBh2_jRJ_ zJ4W6&INmGVzGxqQ{QrLKSz_c>kDvUXU;IxB=KSZkY*JhYg#P>cg8nU*|NV|V_YQe3 z{O@0|l)j_$-`{&QA8YyLKYws#z56Ei|NNe<#cm~*`TzX2AwT&4sYj4k-?Qh660aBj zC#3VzgI`}rFH0xpM2dGxN=kQkH_NTBug|=}TQ$U*lPmXc2=Y z&&tXY@m2cX-(UDm)BcOYMsxh9XQ2TrHrCwc*32sw+~|(qJ@Y1e8kYb5`{Nz@c?Py? z{&_V^4x^csyCS{C`Q~pfF4F=syE`mHeCd`1l?Ooz$K^ zZ%KvR^n_QpW?Wp{r=t zh}WxdqVMm&_gI~u@1ml8f^yD%oJSxwLynVGSN+ zmreIs=OYRawuT1EIG(G1elnty`}f~Hx`z@H6*nn{?a0z-LOnPg{%qqb`1VjRo;Q z zZl+)N4t;(7RcqFSoAq%^P%-rTec31O%u(**oX%njL#n%o~#(G6Dui7`m6G5fH2pFFxhYyOIq@ zbMLJB+S$3)t1v{*c{I0EJi%akYI5z)i;pAKW43B&Xh`kc8IIM)@BDUM>I~`28W*aW zPRW1shV~PY@aWEJ!L5iidpo=EBae2im9h*$UI^%xl9||THPUUcnJ!N5S zEw)-f^VFs2lt$;;bB)*Kgfj`v9llk?tIC`#g+H!+PoOu@|Q1n zHpiqi#D8kc%gcMX)8fq2E{|_xnrTP)dzAYwDQ(%X;qUFfO6vJ*L=5DgRyQF2yf}rW zE?l_4FYEID{rkMvHx|a+i^CgP)z%~!pEz&T*B}#YR-4SvwK=r<*6B5QS7*<2Th61v zSZ9SZe-|Ntf^+lKNT^Bqv%IuRT>`qdVzb8H)@9-MHyOc*j@x<6HRhIb^J9y0s=J!q zvmO-M-CVcV+{)pq}zEi##$d5zBcn6hdD7v)fdd^V4fh$D zKbl@RVcH8|dv9d)+!X{*GI1*;Tlb0?;e7Q>y!hdaLd=&6St zd8CHrKbh%pT-5k|1s>FTp!G2QkoX}wQe=v9|`PNd|AA2r!Rvbfw+4R;$AhzD(tB8ifHUSC7C6W04R*oH? zQp{r!VG#(g~_5-c_Vq$8D^U7qiT2`j|^wfAP!kL#j);K*ChQAWN=jt!J z9EYPbGBZ`BW0hq`_an<^jSY2$O^%PuEM$$%aw-+MQwc`JW!RSEVMjPKhd-H8wmGA( zKa|hya`yH0`3sgV)d2SJlQe&R@a|fzyUo4z=?-6Ru&-RdKKe_}ttjLd%S)Fk^;{-H zsesR!V?w#9^y!Ty>vx_CoS%aiLM|=6yG~L}KW~vBV2h8iUe&hPjHlRF9;x>y%risr zNQx}P(Gyvxv2aw#tw$eUxlo@Lsv360q^l}MJJaFPo$B{~wJGLQCR=NqnvBOIki{3Q z-17LthohXktmUILV%3ixJ<6K$om`u0`SlK>V+-;sy*Pk|C(0^HKqz3%76xO#exBR0 zB5y(Lq#o6n&YFZUd){YQLuKUQ(BupCkInr`^e59}X&p8WQ zzI=JIao^2wJcy;Wbp`f}_3nZ}F;=!%)OnS#uFkc2OV#nARKvojZTjPWjE;t`ae2)l&)@)LOZa%9rNFaQqS1NW?2bN<lf9~n&J0GoZRtzE}*LvCJY{62tL#=CRp$8dncI|<4mhP*y*I{AQ5Yeom! zBpDpfpFht-9C;1Lcob`dDG*D8zF5*?Vrj=wqMpK8_SS9^b(#`~te6R&;UrDbI9qUdU8 z|8{f8S4SmX?#cCPMOJBov9jw9-i!+;)2x}pFIjHC%vrGJ(7pA{vQ|9$!K}-piW=9EZDilqji$AKe(0J^R?PV+V`<#1}K^!*XkM zEWbCdU>>ZAH@v$|zK(r0ZwN>|9y3} zo{PVPnMn4*`uh62FJ8Q0m@S`r-%=9P_%X#?k0pWKJ?O=I_+8IE&fVVUGbk#qygTjn z>C6nW-;)R2(2l(7gXl=n2Nw6q&dz3ZAg8a+j?e}E0}cwhUtU$E-gtT7?s+q_RqNJ8 z;e_xc?Db*DOiv7;D0t*LQDW)t>EU4#YOiSKHU9Sb72z?#-gN*Zqj8}RpB?sp*4}=W zS=JWh@*G>tYtsDnYbdw#kBwG;PELzha?YMTdleU#P{%8!B>;KyWqGe&{ecANeeK%5 zXKzkC*?Yra#7FRidd|(oUX029d~Vc@!vj0ACWlW9G~>rNG%b?gQMmshgz1F^D)8L= zDZ@U2(a{kWbYyU?e7JIutV2hr4}*zdEX7&#>wbyzt$|9(G8+hAxm8VGZsOauT;IO} zorbk4V@_zObUOFtxp4${Rz~sF=tipXU+VsNj71sGPu#RK*j+Y$XI`y&wwd$9w;lWT z?p?WR6%P||G!^WuQuBLTiSVIA`78CEKO(}1%9Z7qtgRZ)qou4`8ROc*TJZ)?UF0IG z3ve7`2$6=3jsP^`T4t^7w>=@()k;6;Ho^o9#%E;oRvPwvEL5Q{HBLgd1 zWc>BlZ%FPPZ*H$ZYJG@+dWMwyHYaBv%NqObHt*4uprD{ir#TB(tBrpD$|Lh*&(0_Y zyXu3j$6K13HWgYb^;dtatgOsff0qZf;C;m}w~z(^$NafZj6Igt?QhB*>P{$mCRbqa z>%4hg$h*6oCti+T@{x0KX0lGN*>(BHMZY{(J%>XM-vVTYDpb{LZ8|HM_;HD8a$iLD zR4usOlsPrlwR&9U-TU`PG988#Y@!cEpwhne5e}-H#82{J)W5XYmvW&VUqMwqQLhoD zE;!Jvo9Un>cr@Z}171)~F%;3v<@%2Z4^Ctiuzp)Dq88tem zDr5Ds4fNO}oOF32aH7>ma;JNlTI04$Y3aX*of3O*(&0V~fk)DlW6`zu#VQrmw zZ@tu+xMD<-xczs77~M?mwvr&9(`(f8(=D10-fVKx#0s^pz`CBIx%P3=CY0Z+%Pc^~+ljy3@KS&h5BEx0rRvd)lsXdis^$=YklYnGXRRdqe`>B?oxHg^2+t}rekK^2iyENUU$-Ci2pn%|#b ztf>m%x6-9CWS(fv8+Jv*TNPQlA@gE6Om8gJ)z$s}{d@ebBd?pD5VMV}&xt(-&N9?f zt3k)krmw*)aN{!|nsXmtJW=owdeog7La6 zkmhTA9aVx+_!cZ$q=pyt6Ek^Amfc$oB( z%E_hEy)#M>m&{M)V8g;yLOMHZCWnWI!NDskqcjAaAHOsxTuu)ORIFUt-`kMs|9oc* z^Mb^DX6sV>W@D{_1s!(*!YU@NUQo#0#1cvTH9rTb4EcZ&D%5&k*F0hwCua--&9sok z6VdBcRef?oipe1tdtE?p#ww)N`}F zQayc4qlrKWcuR3(aq^w-M`WaB}S!7Wb!`RrEa!zMMX8PD*$7Vjhu>DTu-!l}- zRwzq9ab*|g9nUbT>f@rA+%WJ$X9imsJgK-`B$M^6=}R&)=?<3-zQTI4#Bhl|yG&1p z6%@$l=H^1HB(NLEg0G$x zir8CJ4!+d>^0AC(Hv!n6xp`@{6!|F2O%8h9&_mgoPHaMWk zPaJ~5zh%po+&?|A@J8*L8#iuDyF41t=*VT)Q?g-tyRDB>@)c<{{6l3eAJAywq9yZ> z$LiKk*x^JJ-b*rXls$0hkfOra`0$pNuQfaXAIDQJoOGJ#&&SiH*hlyx^M}|+gY)=t zn>S=g6;E1yH)RNZFKhGyp^VbYc1HFM(@wb{$Q&DKbV<_BUt;B-C>=XJIT8fSxP`w> zCYIj%O3S}h?<|gKn$vi4^t^lr1z?b z;oI=bqu*H}pFe*V8$s#Fv^NRhSA7;T>d@L!Ew9~jKbudh&_IM|%;nVUYGshQmd~Fz zFtHOh@i$zhM(t-ramGK=b-`Y21i?1QRjlq42}XB~N&>-&Po^b;W(J*gfh2|6ee`=+ zkaPUbDICB1?bi;QLT?4N1XL_vIo)i8dLN(Q_=43(TG&8a37?4-*fvhq_NaXcZ=qdC z2fux3(QFOEz%YY8QW2{sH?U}B2Pl!5u0lJINzzLLRRe<%hoQ~~&(GYrPHNmhGje`s zRm=(IrwRNZhmm?)FU~Mf&elJDId|KfZF+Nib*-T0i@&8^<#BcfUoR96ksMTWoz6^m zo=iib;u`Ib&q19^v}n#-SsNhhq|IWT+*+^}Y7M3FvTIMn!J>IieE3~X|4>H2#9F3n z!1ob`C_ZzKZEsy%nw*f(5n>mkjq~;78X6ja{(Mlx;%po2^PAQH+?KNIUEavFw6&EK zzWq~B;rv%MFHj70=;RmFd3hA<%weEd@;Jo58#iv4U_JdHC1AM?^!0;r7+>jJURDB9 zjA%+)l=A_qcqDDF0(M=2 zGdtKB6&p$41yurS*`>jD`aA{jy>f)x@}ZK5SkJ@)eivnta!`*U`r7~^wEP77AsKAl zv}rF?YI-w&F_*L^ih!!7=0gIWP<-F( zu>F7l%{Nv%kMdQ)DA)9EZ~8BfYi^vo)f=5g9;rXqi6Dwl=nPyn`J>q#^2-616Ro$8 zo^!-VgQGH-<`~*DUIIMFvM$@xx_?bi76)sje^?&bai#MZMXzPmb)e)VDWP&a`3Pau2mkJd$0 zLO5{(Dc|KV7>>019B6^>lN|8E==iuXQ@|Znp-l`(I%?eP{CTnSAGagIx14NFPfI&j zn{3uu8hlIQLR~nJRy*XM7ax;R-I&Q=EB;BezJk)yI#}ui=c!TSC^Oo|mXSj&X=&-i zOI_-j-(D{yazx4u8Yv8uw(jm|>g|Aq_;T!`P1~^WUo5rJ(b17Q5CCWAs-k_N(|!H? z`7w(}G16svYL{ipUk)R^W=Ye#`!a&j=Zi#)Zq<4D0=>Q#P-lM1@P~S7Mq_sCA1d)WA(M_Ff-19|99Mzsf+dpfhg0|rAAAn$Tf=Fzoa9l` zX0d!S{clzf`2f$XfaVdSld6I!JNcByqHOnxtFErD66Oujs6g!zVS%ius3r-MWTl~w^8CxXg^yHm z28VF)O{q=PD2{cpcW{V2opY1GElO+{Qk6uGMVL24ykzsn=g5IkPzS+Ki|%)Mdx>4B z(*$9yygq+LMY7omH0iWJe&Umoj$i#{j#`4z;mo0`6QGYT^4z(Dp6J`#+nWFZ&#VYU zk_V6#goK2U_SBIeDnOT=AYH6>T^|xK`fr8k`53nk+w^^8gw(j>(V9C5y+a&c zffQN5^sd`{fiHF*>?jMp)c#>3r6o#$SVeGoVMyAP<#KKj+79cS+iEJg7w_7yz@Zjxrzb`Oi4uscNQSems7NsG`n zOHnfucCX)1^lfCOD0x)N=<2cmY+SQKkYykG=tZ1J>D5>LOw9-SF4o{X(Gmmm?G%wn zDJuW8u|103U2sXk~Py61K#x z(tjG{_Sa6@%;YCFwldzWTNMzGYS0ZT5kDkn6J>_Jl;kq)>;we1%V{i;(Lv!86r?9{ zC}7vTrCe$t=178kNoPy<;z#&kv(Xit2~kKf=vh5ubky569*4M)Aa&;FlN<2-)A=p9 z0mVAWrbj#GWO~&(e{=^sk@-9yo$R$AcWHaiIcM(Nttc7i{BcIkLDbe-v+Cz3SpHdO z<@3`8AkV_u4iVx;nH&R5zSQ$c{Koa`1qLF8wY4XpII52H)>HGM=3`6XgF8WJe$vLQ zlQ{nY0_G~HL*O%LKvMe~%>d82i`G023v}3;zVD|mVUNUij+>_qY=8gxqksN$kELZv z_5a01;9>m#9oxY_Px}9+^Wp!ukFY1Ot+^SFgAkl*1KTb33O^kjmz|svcCTbhWrI7# zBj?B1*job;{tX*8r0**ffo_BU&6ppKa`}3A{7%hi#NM)H%W61}+j=9cFc7hXc8N_TO1%ukR4?Sw`@TD6N5WY#{{+F_oEkg9KE@g-d>@3^X5^d0dXw*YuRRN0D$f# zEAXuzgP*4+9`4ZRN*^DPs=u+2UCij+8g%w~)A!B#wkEEq7X>o*2Po{yHEXuW$;ly) zzB-pqHStXD9jHie(>{wlMnimc%B@?s{EY?Ts`8M9S!!`-4lq?9*K_@Gy`~SexN5MY zoK^}e}KUnB(UgEyqjsi|@E0SOeR)U-4#|9*n6C{E(1U;m009uvssKGbT7gEI|vHZFGL z)ICc?fv&rkY&;BM;hcpd)+}{bt+u}%+LdnyUs|%5UcCIgya%0~ol_${_~0lYKtVPO zv{$-KC(F+U=bU-qtjPyWQPq*wEhwc*2T_J`= z-X@(D5!zXoQz2R@{^M~fSSg_KD;oVPxwxK#I?J;FFCG<_hMc_ojwBY6w~9-#A{%y- zLO{^0w=p{wK)D?bk+rgyU#9+jfb}m6hsL(cWhw(;Ecd({^%A6R!z%&V5g|u=d)p?5Yt7ZiQQJ;}psG;2Hr3KFH8r&m`-j~qzcOpK^~8^Uh7+*UCY%|V zYM3ggPCZuQRec%+ETt)}3zDdpe8A$a+$2+;D~^-uMOb+0R9z{ zCD6sDI$ZN`d0-pC+jA9Ljz_97fl5f@!%N0OtYA2HqZQTu$hP4a%705sO9)?}-Cnp+ zD$$KG&wT)wwuEMeM zFu`s|Qfn0C6_1HR=JobIfAFl?bKAjpn4esRYm8yXB8&@lQ1D(V>Q>FikEk=g3+ISf zzPk>xmHKgUhUU!XNed2 z2v^gK5&J<01qZB(eoF>|o`H)KiatUQuunj%_aWR>Mr*06K2tiTqZ3fMgKL$kIWN%n zsRr9Guc4jI>3E=_dkl&Q4o3*oFVwSRt%jbAj*oHr6dVfow`?Ti6ymNq@2;w~wRQXs zgO;TG0r;7nI2MsQsqy%p({YFYm-Tp<=;%kG%ezroS$Wl#qkAAOShJGOv;iL)2ihN` z&H+qTm1yD^WZz!Dc5Ng9JupDxU89SSq5zAZ{jed*3AiE(b{72W1nIkJ&yffJ4!$@! zc8PpV1|l6e_J@;uZgD%K1AO0L^Z*n9UnO4DYCdKVkYb0caMn>kz~e7Y{XyS>1dG7% z#2NP%JmDSU9;zcNTfGoTfPtogaN+nYMFR9(G7b-|^kANXlCQUd?}*aMPoANty%oJl zGA0lWl`#7>L-1_e(7`0v)mV#{^2(Jf8SB=qBMBP>;TQsD2nx@M?h$}~IwWMLcpoGu z%uI6}dG0NAGLqa~$QrB1?HwJXPQCiG)kW@deT?9`@zJ&*|Am22X$_|Aja4Rlo*InY!tT3GCuu6_0D)o+UyDOKF)ZQvol;WxG8Q!$RqD1gD6h$dMraiKSpAT7c{l*3#}5KU7Vr4< z@#7jqycnMRU9H1l`|14$4)ApNZ=XW&*4H)G*UOY%dSM+?-i&Ueh`TTAhYeQxNa7?} zuAJaq5Y5+qyk{2m>oZ~@cpXG`jB^+|M}6YfwsNGBTI;@s`p$mQ@7fAXZ1*B&UQ}$> zxunArNu*5da2Py0VAb5(>fN5|D`DohjGf)sS*WMGdl77B1I{N-oY2R4W9Cg`i|(QT z^V-)lGHxQfCau-OODtv{1Y9q>v>3Uc>jAczgXJc4GELKL1wQA#(8*68252)fK53wL zx{tL3P4E4+kS@)TMREeR;V|Z)Vf`W@LDXFF;Xr=E{UUf6+YZ7$SuEuU*2c=vd<^F$MyE@9ok9f9<DJ?c2ADIolU)h8(;TJijzbLwFzSOH{;tn*6E2x$Lm07YmvD{p-`|7Ob#PZIY>xrYd?MZbVt@)lfwLc`}QqHTH41d z1>@9*@^BDx=;<{#m#toHq~s@7c-h4fNQ60-uUAeVhWxZ+>PPcZ`pINE8}6+)Rg2av zo#5hw`gW`liJ%$!ix`6g?Y2B5$Gfm@;YpiY3cU`ZOy0$ZXlLeUqsylDwYI-czplQ1 zF^;4@?0^R^UVMR{Yyll^z{!f4hy0-G)WaqnXg6=^h8*f$?A*EYpv}TJ1g5{yuq>0*F4_k$Cq0N&z=pZCxf`G zPv#SbIqZq*?s~}EIXKS`#Y|ZA)byUvsBT{fG5?gvU$aJabAqc5Uhpn>`I>xpZb@t^ z@Qvx^$)W02|4C$Kk-EA%H^^81=06%!t@3{~PEQ$g>7yht?>+LYRyp@3JIhNkqdE2g zWY*KBrlxLaSWnCau~$5ZJdMNx)q_q@PGMo;L#VsJ=XlQ36Hr+Wq9=vZfy(KXvFx_6 zN`HU<4k$`zWwWs(%_z)y=t2o(88s0Pz8S*c{u@WgASKt8-G5;0N8OLp=;BJYT|=Ti zoLHjmS%i9vp9i=bcfLksU~vlyM|1Y{lxXYwN2VFy-)>{xd!Qp^-lMtHal9v`IJFuS z`Ywn=&UUUk9T;R2j-kiBk97++Y+AO>RD z*=~qR@O2{ry{x)+I7re;$blWPA}CfXU2pBWb*596=g@a6lXkwmYyvOk)vaji>58bZ zK%6ti(H*}qwYQ7<4Xta+Ra?GKjraL4)Oi{lESRhXB%AvC*Dn(N)pfPCwJ!meSXqbM zxx`b@cscacsXsLl{SOo)?~pz;o7~`(Hq`cXP10%HKGnA{6zEaOMu5#-4qAbM=EUN+ zOXVh@1f(=L6nU`T-#fGBW*k0oz-=7>)eeLTS$_99Y!AK}`ub0|^#k;Je1@o7%zEb9 z8od@vGN4Ot=N9_=_DFAfpI>Y()Cxf^xzu%C-~l@r9JA*xl9$MQ+7CdqV7(;b*a{Ok;J}jsY|ylk)B=P#vv+@%mM%K#=^5mSuCfj z1(DwtgUo?5E|$a_>OCj8J$1pgg|SZnEFy9-Q3fd-3WIs+)Mjd{@*(IEjgs^ zUTiE}v-9Gqv}Vkn?8(b*Glendg&lYG_C@;8Ra!I3wU3-HMwH@^;4ICAfnuy~W^}R% z_!ho*m-Sh{CQ-LU_Ed;b98mIZqIEXow1wO6l)MQ10*}t>O$<%Yp-pm`==MQ+J)Ag+ zR4>1y0os}o_C7dRpCv4lzi`naq(m}|&IG`^S$pQLm$KZC)$;KPmE*~a|z z(n6m#v>f0*7~P&jtiil>=Iv;KmBHY^!z8m1j0~RX3o;^wV)nG znQ_U+zO$R;ZA0>I&2MOE;7_)-v@G-3xQ}&S7yuaEkS$EI$2$yn`-U6_ZKY`hCbOix zzW!v{HF1sdoT8$liJf1h5D*5EfFO{sj>nnjc;`=w)GI;@Ns|&h)oId_8Js_maBK1x=bAQep66DzN+{%+4i&^O#4L@bS>2 z0T>GQ7QO9SvcWik^oEuTyKTBX7_cowKx0s^&1IjvfB$|miBi}kPQ$)c38Hc7^WU=u zirb(>SDpFt$H99XKGyX15Ew7lESn2RPhe_=faEv<#_$~beamH4$XAi^uePCv|nC)LIyfu-6)VEB6v@m8V_xhm79dIo7i(yk3fCK zy^?95!U~)Z*GXytc6CL;ixeg$k+Z$5uI)7RjUDU;RIJAk3NgudU6jxC%yrUE#3@<9 ztMq}Kt>6UV$YBIKM<>be6t8Z1qndgF;NeN8)#RAiKTgk*37G&R9bOEa2#5o|+3W3L z=daIlA_a`&Ua9sU$qcJyN5D33x_hy%>+MqN$*h4sqg@vmXmv-<$THQ+VPS_L^RU~F z9Xot&q}fteKxGqQ$YS>FE?OJaDwX*ve-clECigHqroFZG8I%TB?pEWLhiHtqGGfs0 ziRkoKeZx1%&*O%wtXwg5T8c+Xd7G(4etv+T#L^%QqfF-%~i*r;4>O$H^ z$3I_NihlPp62izn;QZsuJiNqlm|aR_Mgny0zpg?<_f_Xh$Pcag^Qky);6sk~gyAU$ zP^gQN4H)a$26nmhB661n7H<~ES&u+P>-0CzLPyUv5d@K`Ld|bDlMk^~$nH{E< zFYuTNww)Sp2ywoSS4qT-kPxHLuQB|w6I&(EX{y(x_#W(1+@RohIwk03jp1e0z;)3D zd06`neGZhog8RSp1Z?{9t_jWI!-JtE(f0$}7O`=y^4|7~$HxBo>M&axtT`Cz3?IC} zy&(W4f9^)?>#e2S&LR2lVwhUzn%~E?`8*IA7&5&b78W+zpOX+}zyrNaP&Y#pf2|6s zxDsF))m9m%{&H%bk~{^4Gx3w$mwCVcv#6*?Bv6FBQCto2{wx|cEQ3p_?^~1trL|xx zu7WQQdfGZDt7I~y#wJV$rWU@1Zg)zQ`IH`dMAlh@UorFS)cR(_$8ye@XtecR&u?=l z0U1><-J3OQ7RO@b+}C}yabQ&{=v}M9(MyduSMBc~6AortU*4OIMpsm>80a2KTs9*D9@CCpM|4>nv5hT=lvg_IF9EzMam#H+ zL$~7TfrT+z2?vo8MHc0B`u~lv@*gLOZd(pIdh!n-GDe^s%9=8l@cA)4++Mo5bh%H5 zA=>cN2L>%6T)?2Nrb(wT&$ROfSvjagha+ms{p-*@@`@!5SUOtfLG80fE_`#xgKEWlnrAVv(R5mFk@F zZYtgomJkLLl(!u9(W@Ivm6i3QX<^i4b8Pzk@#ivE0$ee&$)N(f7j;ZogW1!Q%7~c4 zWS5<A-oZhI}Y8^bmmBhe6FiX(xLsZTnb>u#~yp z5aLq~tN_G{87?`y9IjSld>cO|kAj?reRC!7Ue)+cLqv7fti@d-W7-P0(eF+zbLwlz z>{uwWf-!Pf7ge<8(iP-n=CiRqsWHT?jKD^|T$gQN!Ypf`iYDQT*)%(yCAtMJI; zAQsdeekdSmn81WH=6+N3_9suC(BRe6O_OM6DzH#r@1J?1XjK1Lxp(wFIXjsqjd2Mq~9YI=f#py=RH&bv}>cQd>`LokN;Tjj#wO+#Rm#B3slpufR| zsQNBKHB4kOpls`|UD}ct>g0#{oQL~CppF9i9!FpWI`&FIDdR8A?55TcQ1bVI0aYXr zQStZ4mQnZ^kwyDZrpkzW7I_51AOsw9IlgE=)x(LY4)7}j33fK$KWWp?i1>bh32UVyR55cpzDpaa|#A+fFa>#nC5C>F6DILm*R|kukHIHBgrum*YJfw6j1wlZ z2X(zlp&_8Nr=Z~M9dw8w1!YDeF70z^q@@D75#C7y(#^U0w4~gr{y0gqPhyHq=o8Dk zW7$Z&2OPmtdJTTA4Wji)i)i!f##Bzuzj89DX7%P zJU9&HVanC(%riFH+K#B@PrO*LYU^1@3DmDZv>ZofaY6^9wQCu7Gc++V=pYaHRe1~9 zXCWJfpy91hc5TE6YpyWJ^C!UBIaE`J=DU(=P|>aP{f?qo4t_5Z?!s~^su|Fn9$4=Y z+Q|FKZPX>~7$E}@&@WBGg7j@zx^yWyz!bd&JQ?UID!>k?R6#@MKc_k{o#X|?03*;A z$ny}|MsFpI`n0ZP)#2g#qewtgipWSw$I^Z6cIl%Z{9pXEc1aoD@#!&Dlsja`toG~ zXc*}m@X&~39@EN^9MzKpn7YMGn>>s3binZ_!~z^JrI5daBI152*iHSS)A*EXNChn& z9T7$Hg9}wtAz9a(_vkZZD9Nt}O#Q@T4 z5Qu?Onk=^Jv8qOlQlh<3d2w`Pr+(O<=PiM?aD+A2v`54*?tN7!He}i0>f4t6%o0|BNXF&MBq}#KVML z*2tl1+c2LQ=rU!`6cPU78>podGVeeO<$1K>ctKKEq1w{p#+}I>1W{37HQ2Xt%8Yj> zL~vPQTm_A_R{t8rhIlr&d^@^dlI&@8C@`5p9b%ZLc)*RR_Z57|y$U&H8+t_H=0KFi zlCLo)5Dnit8S}wsDRZLC$0Cu0b@{#N`tal$X|M>cBgi2i&=)-pFCB8#Tg=kKRTYU5 zlDvDIQAmUlAp5to7JY7S4~I1_3bHG%JSYXxehMx9ZEL)_)}&#kSzKypqXRmh#l$EE0wihQ*rI39>WG35IY9KKA`2Py>3aH9FrNemX1_5phNQTYN`Tx#3+=sGZguF3%p(VYo-{CLB_c@Zfs zUk) z$gItn2xGV&7$S2bTEg#cOk-u$;6NWO%4hRX=g_;qmW_cOQ6N_&di6<0#cLn!wAhP` zN=7W4qY6wk()ch9-ZDDy_~gu^1dU!YITDz8iE(kz&IgEx5Qd=)BL)O!%`Li77`K$^vR#-ipP)wv~ab+ z3x~T+UhS9|Jqx{v;GfiOMYH8SAdjGhM487hh(@c052pwq#*eX9R>*5k{-Od?ejcoq z8mKK9aB1unU0Yw5U-&h%oioWmha}dHvrYJxLGH$LPzA{c9o2Xdqj$dOC}YS;+1IBV z4y%F)kTTOisW)iHfe#+3-U)WhsskzEqJeVn8XM(m#$zcfLwEO*$%DMD;--(=M*KvL zkKE}IKy_D48)Gib`nwhrBd$Rw8GPc3zlN^#!H^KSFyL`y)TE#$VEF5kU~yhMjx*h8 zAnlmV0}@#H{>^5YhvJXE-eZ$z6@H$E;3=(w$@~Z4V@OudN^y}~HY7Kh3L)=dH&-^jla$e5%3dA-XgG^+BN_>C(ZU&n@ThXdnY~TJuFHR9X zpi+Dk!l|Kq311~zKv4j4gs`ya8aB6PhJNwM1!vGHtb%Zuh}eWJQMv3|lQ%~1X;>a& zFkK;oYUEe!`Afkzx)JB|vV^3hN;ngE_%)pNZFC+hpgHuaDhdDKf1Z`iAc?>oObm2& z#y<(2csPYTo)ma?5T^8S_|$7Sw_cHcEtHOj*&uutH&pN{2S%eQedE?Gx-p}MQzafQ zrb-=lj5JgJ~}1Fhq~ zzpk{jeENC^Gv|-Ve1dS?svZsVs~3_|nb<)<5L6vJr!`jc&`N&GYgl9&ML|6d#UHZ- z_$-0h>GBSgbx&kZns`8)7ulD;GJhvV5dre?7e%BT{{4ygHWU`e*f-EQ9>tH$ru#UoMAW!9=fBp&By2>?o zae*Upa!X%dkb#8HEpqUJjDG;m0>YZ-EQ4eBp_|dKhzTzH2=-y$XbU+OQ`IsGM)Y>4RXigK@HU@{&@YwbLe2}y*J$VQ9 z`b}X28f(PO1hA4A+3qYb#Pt$+v?4z0SNu#kV@=Koyi z^tqoEc1MTS0QW6m*A&*GQ($xJnoc_y1c80jldWWnl5rS3iVGETlWDR6Qv)-p4ukeu z>-qolaPI#&Z#_8>Brtx$KxC3s?AVtXhFoca#G-9|2pkJ3^Jshz(4lqfI}U=X04U1wa>!UvY!W^-78PyF)d#lezSQ4z z0AR5lC$@%j0oSbWw)oThg`?xmu63N~M)$Kk>*{-#z46-2DQ$NY?i_NZr2fs5NkWu` zBRaLv3!>k5U#)vZLw24`18dmX`IrC~aUVf5)ge^ePR3d5g8TuT=@}Cv7z?MX!_j$yUC?yz`~>($~$gcvir4wZqwot<|wQm*zc)`^&`oWL^7CV>fcE+810F~L{s9lv zq^ybWiez)UbLWl<;Yf`3nf&3PujUZ?%5e!4(pK&$MZMr8Xbjc=(DQSG{KN@vAVp{`|*=J@fbWMAjG}uKx3DkAh{7k`S5mkzH4qx#G1kzw>T-3iK6aH$8d@ z89Dy<%U7U!bt*gL0>#0dN1UZ4i8&497A)bQL(qk-ZBqNfW#xOVre#k>p7if_Yy`xUwqp*2w zc8e_fWNeG>)jgtH)UK$()^U$PNt=c*TF-iLVToGXdI1SAo3KRkt!E6XJKds1SMNZ6 zV8*+^Rnz$?DN1(Y+dkAbc`PJJG=z2FG9+P(MevZuP$);B8686-W+k%Y(HNvuyjnZI(BZRJ>A3iOd<_14J z{)2{DXyyc-6dLy+>l!+k=c0{4RP+g*dvbI3SBiLXg zBObOr&zc$>d%ZbRch6ic$iID2PR5dOsC5#DuDRWDSrYv8sll4LjqL}L@f0x-WwtTV zx7B1#!XI`&J>BtXjRIXOu~z(CJ6I1%hiIt{!bQN_DT#d|M<95`VYS5mMwjWrk`iUW z@=8PscQI3DuZ}R=o^xw%QY%4`1zE(SxO78sLH*T4^r9*du|&;)oDQ z2aXU1k%zBS6$2xWWE|BANTe2oVxNgxrBWkV9Cs|yjS9Ognyv=|iAV3|XZu_Ku4A9O zd-S_{-!{y3Y?yQN^h0jZ!)JPZzDX(lsQX0;-BU-^!BpM~ zvl4g{(873(F?8*2b~G-9g<}o!mn>Z96|%B+7MsWFI!Bkw28O-wx6R-K%f|qYG*wy& zJEkDySva)MA$tO2sIvU4NX9_d7jm&IV9ap{O$uV$3gJtKx^l*1?*05Z)ieNN{|E;Q zJ2P`-5O}HQ=`}P2B5u{H#9V5$?ea&U;=!S6y)Inb_F~rsw3-`SrkzNd!*y?OHoJ0T zmBa~8I2tEKYbOf2!rJYpu7re$>QDhA`H%*=z?aIK>`1nP)c~z^ zVmkkgKVlB0X!5gvo0h?%iizy?$NG#nT* z67eu;wgq|z-K__5NAQnGc-01Luoh4?=akj{DgOZu;tiZbY#lGI4;3LOso_s z(T4$TFt4r`b%$C?Bo~usV0Av8+8d3>7@Ul=*ef__OYW%B3w!@NNF?8hcwuIlnCR=% z6vfMzFAc`m{uBN;mvUd2YBkEhR3=T^AZ6)1HFU5Poq3dRE&%;%8Fm)9g#bVdgH#&8 z2CNV;XBx~T?h1B?t$LWb*mF(^D69uqVo1$E<)$!G(p>)2EW*x!a8iI8SUE6ZDv&=+K{e6X@*v6pY zNC*!6#rX~KhOU)P@P0?Cgx(?70lKe@8uW|$Oi@2?CzB|w{o>TsnUY3?PdoX11ojx7 z4ihqMlY$iFP^j z`}EaYfQhOFcS@l6=FKT#3}eb@hFtn!weY-5nC{PPud9s$8iyimUjsROHQTr>I$n@6=L$r)^^vs1@s&m`2(o?9@AJ8Ge% zq?DFFf5{SW27KzqJ9*iB%~4Jv6*@evISHuvwYxhY{1I+wM3txc;3$1+#!&a7(mE1v zIN!NT1CRz3Szt6_crt(E zJ8YIw0ckfcI#e$T1l$4Si6rM<3f&|?vrbu~t?M$!I->xKE5N{GaY-3uQkT{FqXhTH z22JoRGNm?zgt=spEPQ-R#nLyw=sw;x2Wr3uE01(hFRF_oHPDC?A%bPXZORJ4MFU1> zGca7Kg-JC)5e-PjH1bY*BQ7a8Z*+a(hS_wO0o_f%eSUR28J8I7phZIiWCEy6K$%bbk9xLXa?NTgrUp1$ejQw!J+Mu zZS*ksjMmevemR{#7#c6s9)NB%hS04{!FVskdO+f}a?Tmpkc7_yv!u@>W!^@4D-4YY)A*g;Q#Bu*X^M@T7?=u|Q97~+vyH`~&HtK~dP+ak^9bbBgC zYg?PK^X2I&C%S9~TnUOqIBsN(e0Eq+IkYbAk|}Ys!eO${k3qG|9BOhEK8N{LJb)Ma zL}57kaI48rk2g-w-1LH2MCN#3w_^6G)n=R8gYY zFwt;p;bynbvuG#THrQ^kO>rZz&Svlt1cG&bH)g~sIH2x!fi1%jX3dg76;d85W1z_a zq9MvRjFx;E3ck2S(|#fg$opl}IMZ$cbJ>{43cIXY-5C7n0-*Fz&yXBc6l}GG{_u5b z)a6=`luqL)c*Alqqe1v9F+(g@=-&s^GTkUe8a{m4+%@(f`txRp{FS<^b@d`J2|*Fv zOkQOQS?X%Q4pytgosGE}+B@LzO-ya1t7qJ^3tUNvM%s@+S|*TAHG+PNG;sS93Owbs zV^D|$?vZ&n93ry=Sk4eyPUi;OOZ}v*j94~gp`nciVUS@BAl>l)pW8cA*)S=mcfc8b z$wM}a^z`(caMwT@WXR3C=X&^{wflCv+sayKYFlb1Q7{fy_R}n!8Ylcd(%S!out&9( z?LQx7)mtaUWPvB7<;!>E^Z__2un;JIy%2?fJi#aMFOS_VLqB;IoAh!tV`o)2yLcqOc(%F)7%Mgwiy2J*u1wqfIles6(zPcpQtcy6hYx$ zg(`-SqfTN6*KR~Q$`hGesY4V=*UNCJvGRp|%HWK`6%;%7wXwC>i~1Sa{_4GI8>=3- zKYh~#+r%f&#Q#4h6I0cG+_jiogO&&rz%dvzAUO)J^B+{+@B_WMNbHC<+8uCeK0%MO z3)607`LimK34;PjI0gcOufEqtBQ6?xgT8I zA!%mNmWbKXRWm2^N%IgWG#%YNvwW7@EJrE|x1~CcDb8GHhcC|jx+1Xm|Mr^qE7F}v zuYTK4|4COXqn8E`Iqdv}=N!=xY*NNqTrorqg7}_wVrNec+0hY!*DM-7ytjpgOfb(6 zSU<>yz9!ZG{wxp2kA`##A&BIYj*T>;mNGIhkdS7Mi=+@gbfv+03m1GY+RSB1R?wwQ z<0f(I94^e>M`s&xOpPFl@HqYa85RQ*Klr{X=g;feal}V{-ZEbh3~m_?#uARjBw?Pg z)%kHa8#U{=U@t+TW73qTSsdlY=;4$WatuO3BohJIvREs@$)GFH9W`{wKux_m`~Q8H z|AV~u0ITxczI{ou6SIwp*@?ZEg<=6qtcj@6Se6wj3W6mX8(6{KAlqmXjf%^H6&s=` z2o_YV7=vKJF4!xIied)^!TTE@pqO3%_uPBV^W3|hbN)s*to5z$d*64?Ip!E+MwK^e z+~(!#4?Y*sp=7eUJ8)wJ-K&&3aFYYRGqtvpET}j)o{om=qurOwPFoG_;?R{VO%KE> zcgJk+PbVBn{AO3tx=l8J^OqT$Ge&)2)pi+SH|CE& z;=pFunS=R(HL@ zNY!9XO-=R0K`N$!+TjQiz8FFx83i!A62_;h)C|CgdfrKGpH4nd+k_bX(2a$UGo4Z( zO%~f8d;HOjTes#1KdyW4@TAO|_B}nvl?>2V-MFOByWbe%Wm^lDpoqa7Z{A0op&Trbt!3eBd)`)O-^Fwd;Ly z{vKvWYJ<}wJMde2HBZgSi~Z`&fZNNiG?|UoXbG^l08@`BZ|jqayzMM@JvKMl()QgY zU<62`zI)aea}Hj5<`{e=(x52vW#365bAHZj_UdLz%93@HP-YpzV|1VRSor5Ohk}JU z(1}*0EBCl$b(eM7M~@t1T-9rrpwUO*a6zk#GB%GcGujjCE;#*KjLV#gw@-g(fKV!>L`V*={h`&RvPuED<09#kDgF?Rt6 zHmT_EWfv}OKYlA$D8DvO?t=FDxaHrMxPWuo*+tF#rBvU5PPT2kaUrWz?@{LxD(VgU zs!&V5{+_)V_oNz>S5KGa6m=WS? zyZYrN@9x_|i`D8`6EcbV1^~F;(=69nHtj7{;Q!j3Tu`pVzHA=&UiO@F&dF6T74yCPWxE~DUD`?LBA`M_UY4oq8!@>_Np-MXnfSEhj4Yu) zRAnS=y-1(C!y%$ChBkK%aMTfcaUTrO>C`?qX{%T(7d5{&M?!osHIrHT)bvKee4h1w zS^7Zjy4KrvALH$&eQ(OVNm(0ol_kZ2OZW*ZGW+rECBwihTwvub1v4H+7A* zTdH6dwHrCbD)Ln?_D!(?rIaf9H_c1Wy7`RRbzff!X&489*%7s2OqPzkG{O(9ul5$s zqF~6(lc+gGy%gsl)m=8}IO>wjwz*wEXIN6jd~azO(maZb>FhJmHYAPEzvD9Z^A+7W zqbd`i4DZa1zxrYSis|4^68BT-I&b_{n_j`NbJn>8&nlm}) z+T$@V9(`|iWC7Nlbe}u|@_#?&5vD;TD~l$pMOY#HA1^~5eUc2ALfGQ7lS+ui2(%aU zdP7e|Ykw+y{%D@+AK)`4=WetWV+VxTfhP;G=fKF>U+&wxu09$^5PVEaFURr=nBeBF z_qZdu`1)8ATYN3|#S14-3iy){_P?rkg=BayA_3Vc@P1Zzg-Rw|=_&=wFaC_LwGojGHM z_|9rl54pV4g+n2cx>_hD|I`j3(1}8N%k`SPZMnjjO<7E&Q=;aP#<@`BJ;%E44jMQA zWjzj_K1O0a2pX0ON!=n2`ncMKL_ZaYlgJdrQW;KbMA1>EFxHVqh8 z56&b6RkpbzloLWd$peM;h{?|ZV1L;EJ*()Osb|B^m;H7@`$2n-9lhTRoKq$Ml#$0z z)X{o1kFz5h@cf%^Y?YcFmyvw%l08jC4Avwr(1VBJY%fs8(;YCECFCVVZ=d336??j3 z@>XH2DmBR*wp5zPyS{k^eY?@tyCkiY^%wDIkXb_e&6nw630I(3rB0SoH^EW;r{(XN z`gQ2dYB%(7GE}sTq&OWsOD)ScuLVgjD+N%YQpB4bH@7dRuaozxkg5NXM{t**!?>3O z*4{FsMVa>#R?+XXzHm(eV<}blhhI#0fme9yeb;9o{yBRijbiUG6KP-!#)W?V_?IGe zX&ruC?D2*7{FW-fBUF-A8Ai{82S1s(Z1~1vH1rSDE`me^V-i#5L?%p#jPuFryfoS> zA?bYQf%vbUAppqHTGS3;4Dp5XyjBQ+x5m00=z)}&j~6G**-kVUHx0`7?+TKfIZ@;@r8 zYH?1j?8Mwg#wsT2kaH2=BEmatnBcs5KmUT`g5&y~3Nj1wgH-c3`CX839R1UewKG^O z|2le)=Q={APES2oEDPHf*$MGMhtp7=$*d%y2?4V@JM?$eD~`YvazH2 z8%&uk1ROc*SW8pI6^I@9H`NiYBbqX&+nFHqmlzlReq)3fPfW-fNT`t#$!PA-lu{bK zi{D+>88-K|O>MFRT{kW}MAlzn?Og-n^shc+d9PP(4=!sstmmp&+)LV?s)D`%n~koq zZ3i{u8vWh8TV{7ko%Kw#SxTLalu_y)U*7flYRV)?J<52ibXvNuThDQ=KzQdHQvraE z!bPIyX@r1j7$c`-k_}5*i1<@iQf2*}{s!Ntu6lPC{G01*YxJ7*d_G#va4FxepZ@^P zK*ynRm#v?qhg!S_sH@lfnC!esZ*Z6iVJ4Wydf~{ra9u};mt?siXcL@CKYvZ5Vo_z_ zL~*~R?qJ>V;}ypoFHQEahK3V#uxed?oMBR;P6Swhwf*pGe&aHMRI8cULi3>e?9Eb# z5NiVhagCF%5D8VMuR-#5(&JhsGd#uw@%3OTbv*p+DF;Jbjkv3GQHk+kC2?BXcbD^D zXZuH=m9eQdVpNog?%g2m8CI7w2@KIG_R@CpFD2m8hTqzx<7M1mqb6K|w7hWrXoT06 zASmP_BwEhUq%l3Xv7L&s`pGmRJ35u-t;2ZgDhpj`WXt>e)RaZNu=c2hR)?u8O%9C}qAbr^B(*PmV;^>oLqx%G zB%k3<%bPB%mdxGpPxRkc6}=aqQa4F?4A=wUiZ-Ou0IhJ|2DE#c=|RUTbHVMu$ht_^ zv&!6RH4bR=JSoHG!F8o8jf*+xo>1i|`^ROxrz61#RHw4I@U3Y*xitEY^g~T_`+w2 z8A>=Q@&Zis$=9=aM1zhRVrZvvkLeoggKZa`8@bKhyXToY*P~hS;sM{?TpPrCDYkt% zBm(K-m_D%n;JEvZUG1tL9^Iswm0vw+v)Buew@?NyTi3jD#RIiBH`@G+Aav}^V0@Lhr_R5q*HfL|w-hP>EMm=?oEx}U?D`-SE$goZPf0aHEYN|TjY;Qx< znfAR6Cc=c(hzyT|KmhYGjo&#d#;|ga^Z)6z^}z`|6?)-osVf%_>woZ%Kkh(2aO6`* z5)MUpums7j2yA10uYG%w5iARRlY1X`*Y!%wLTEWd150HdfU!I5LWAQ4GWfJWoyX%S?I>Y2YY7m1rp{edC2=fFwDWWJ z?3rX4!TPGl4(|{?Qc3tDD>m{Ikg+&OBswnO799O*J(|Lvc&@0Wzm-H(^%Z+@9>fPz zwMvzKzqgQ*aK7o*@#VcNMJT{p12 zRr6BRk<~zxEm)im8XbWK#N{L^%T#r znna*u*$E=)4nCM3!7=~zNn0A~)s7`uuDE%a3;@0PN3V$WXdwtRUxnW)S1bGC%EwoW z&3Ekdq}sq%2gZvbvi_s%IV_ab*DD=@$=nk)@8i%6)tG$CsT=7arccdEm@h zeyn*hj&}DOGuqdT-)I@0NwI9LnJn{2`~N;=3WQ#39Zp>`4Ko5g=7AF^AJlsn!IM~E z-dc;$M|~liH!+|d&!yOlYZ-SnEC=SjyO}+*%+RETM96(>#sm4o(Og$mie#4B+qkM>9OJfxvQ;S%K_p)fHQNp0h;>TJ$9-%89);L^^N1C#MW88&Nk*Y z_=UDf4NTvSV8arm+Jrp(*EF5#3ah3N-u?Ciqe{j5WW=vc+n$f~3Q*gIi3Jj3{CZHN z7ESX~|DF{6_5(4E0Xe>$$yx1prlN1>oN2CYw_u|qMY!b*tjY}!G?olri#8GcJlC#M zwfe8Ut~}=W5}@BOS}5!?$;klLDO{HjV1q<{fhr@w7?gKV>S^|}oCm}jB;ZQWr6#I6 z8l>eU1lENM&;BkJ?%fH)+qmy)tgeJ_iPt8YncSjvv>?jZ{Ol6Bg7;TX(&rg2u734s z(qC1Fkun7mZ}+Yj5sxn3S8`$T8i)Bwzx%CI1INg6SbmLaacYJgV(1gljSP2lI&7K= zaB2WPIl_+QDK1-VHn%e$sJ=2{-t#v`NZ-i458;rI+p)?w8TZj7Wm?$b)lEC{X|V-g zPB@$sW&C1eXfzKP^RZi%$vgi*4^%B?n+`X%0TB|-EZ?kWg9c~ZNDC@t-kSev*(Lj7 zDd#q680o^`rc}sed7Yh>>t61|xnXez_dTd<&qGR?&a5ypkwM#jIAn3&j_KE&LI=kF zbdsO2ipZ_Y?c=UGEoj(ooc=BE<-g(oE#uoK>jZ^DLzZU-4+47(#;*54;hLfPT{QY8ntWJKL3Kp9i)yob>Muw@+6Sq_^MI2Zr#gx9OS&> z_T=8yrN?;3^0ACTIXLa>>z8WMi@=q|XGw9vSD4~;@Y$!E1|rpd^B!`-Vi&`40Vid3 zp)CMRTL~;mNf)?zTSqko@-0WuL}^Q~Y8}r(Y>w-fdA1 zh~)o1-yHoSQ&%|fTR>euU-pZ-75GmJJXQbXVe=aPL&a`#$r7IC>-*X{@>7JA!k(#4 z#j5~7u>?^rgiyFB`Nl1Of@p|c_UU$kdui-Uy?%#_m>o$Bz@lgYaD_0FbpUbF8QeOEwpk_|e##S2Ax_u8|8PIqs)|g2pP^tm#lp%+&QyJXC(N zm2W4XID5AKlFElC1EfqyihY4Hi)~;l1=;QQi<<>Et6Jc8D+~U0x(6Mkwc0EUKU&;q z#Hw+-=ue1=K!i$>+Xhu2A&v0K9!;`C8?T{2qp+A%Gl%Qol8cy=Dj87MxB}@{e74_@ zyIf6mLfa4NCP_30`yT-4Sn6>9Z!rj(xS`>UP^LHKohMW$z&cY1C9OFPcMp-P z$nMXOTgG3U%^<4C&c8o+3_Zl^3tW06iNJ)e4rzkl?pgtr1pma9-jN=d*>6lVPS-1?WhtYr}YZjYs*pLZpRX_wloT+ zzi-+0c=qX2+=mK{lOA%LL}ZyY&%qcYUcp6{yLYaV{R*WcBMU|l>L~V#T93vAxOwY` zdANFhxi9|0lJi?9&%OEI_uZ@<57xBf6Qge)uVS=lto(NwfFSj*$souL8#8KD{bqY% zYsKt+eDO;=pCVUlfg+uqlyiUy2G4O*|G|~-V=W3-onbW)BGUpgnFH+H0;8gvq#LZ2%t#j+X*2AY`t51u2#68%tAh_bK_dl%u`HI^6nq}0vdb_sGjkw>2 z@9R-KF=lr{ve(j-GN%fJH%y&za^M+myvaS9^&Mt~ttM0Oq@(D0k6$V7wxR{X`?r%IcC!pFXSP8(U+f#bQ|nxBd>a0{RXX z%EEpL&DdZlr38_v)(pvF^bGrW=y-c-K2RnbYwK1juc0OY(Ao>6h0~yXVDjs% zF>tRu2|RFsww@nPmW_R`!4Rh^W#UrLD_5?l<%b_^eSEpWLvO7g!t>?qyBxaWEQ5HTU=x##`8clyWgxc5h={>|Wk=l$<~mn$2) zKkx5WyV@AZ(VM@Io^9N-H-EohuAl(vOJ70M%QLAW+m^YWpf7q;Jovv6B+;!zV<6T^Y)eAXn3=77yR2Z#1A%6I4n5n=h;!G%Emrq5(mM;1rIEd zS7^$$wa5(^g%=zM_(87F18t!Zjs@0vI`y|>+`_o zA&_QpC<*YP^`r8ZkADAJcH?t9{eJfk4BTjpK^r)5;KuBw+rWs5d-llx0ddRw{LP#8 z#cw2p&hSzZ4JO^giZsM@m-BmSk5c)6_{mDwpFAkwOo#di73$IB7Jy|?kO_0-Ew$Uf z!T9-==1u2q9rfPSitK2MGxx1Y><*mg8#i{VR;?Oz`F%IX6S*VKP0oHb?qxqQDQojbQ?769oBzUk`%aJP zXxF&mmZ0F^+l{N(+IF-g-M&3K@i{V_L7VK8rxnL5V}kA9rruwCeQ{X(h+mGAqNPoB zH%>vlQAGKe8&v`*AlqAR+`MUcz{KDo+3U*S{8< zVh2Hffe9FPUBsVmL6I5$?V<9i)nS%}jL-Art(ai%aY)JksG4WiBm$NlC|9Y38)o$)BTKwJZHHujo=xNXx zDTE4rw0n|~`uX{_2r2#c##mXUr>^`o>ZhqwX)c7GxSfAfM2{{r_?=~EHN4^fM~@=@zEZ0aHj^ezsjx<_J>GhjpCYMju!_8lxOHPX1l_r=X5BQhM3t*C_a zVoiWs`@|Qf<4^zUX^)F^4CeX!uaIAb0Y5?$)f7bGg(Cdzd3><+%VugX>ve5q>`k%Y z{Q7t@MoQZHGmz)W(H_t0%NGjcm|yszhk+g;-E;a}P*WX=(5ixw$}YYD zRviMwc$ju9UBJDhE~Bna$XmJP%H^g>G!l#3qx`c3;p^~VZ{o#>@28k#lM+?VEH1tf z7qi*Sps$%Nt|Ek}!FY&B)9XWj1@z*b`2lvpa<5%DpWBK^KZ|>NU zJa5mug$oV9T21--!SsgI0CIB&S`^+aZ&&VZf(G#Mzb(p`*?P6JDdw?uHypWe^JsMsq zr$)O9-V#LA2qAlyUP#LRYmwk~4ETo0<~9M5uxU_kb&#?Pv6QS2z}IXlR9F!`AurCB zYnrp{la9c_e@072f>Ltp346;G--?T87V-oo$6e&(A~{{{67?p&7``PNLkLYV7RCW! zG0<}#BSXcglm7|6CBnD;nZG(s>n zZ4t6MC+`B`oABKS9$i>R2Vx9e+K|<-0@VLbyxwD96HcEAU4N>M9!{?etPgoN(9e_X>B>K_il%hn9cN%P+o=ee#g2dwcLh=@WHOMt@O`$wxV_=6_Q+rQZ)f$RUQ!bcXw( z(~+3fo?1oNrh?Jw7eM+pl`FRtmzXK58=s?~scsgL)@{PEl%?7Ge7wCK!5Zb{DuNHA z@)oz@Lz>rV^`^S>tCWP^#*#gLumf3N(J#O3oA6puOTAqPUDjr(HkKdi(9f3)=h-u7 zM@I{wUNsuNV)d|a#w=xm*lMUrF1JjKer`78Hdc z%xU0|+JE$$9NV>KePt-9 zEhh)gGnFhftx4k1l5-PNUL93XO)cK=P3`RP(+)8%8QfHF__b({ERr_m`*h>&wmc3A z2^qI>QErg=YRTNwjNcxS*m%s`rwiA-o}U|R5~@9^OOW}o->a7_&xzIV*xG%O@BM>N z?XA*S2j$3D?HJEhb^&{U4Cl_0RGh0~Rg%JwJio zYS--3x_z$;=U?QpDn;J@ckbE zgpWIRo+OHcqbT!rXwm<@yq5cme1JufIt(hBXF}ZC`qy874NlW6a30J8)2H-1&NRhZ z#q0>pU~TC{B0@#Z%OZKP@;Kb6Qu$fFIm@TG^3Bz15nWJjdvP72Mv3!*2?5$Addd+4SjCU8q6z~R~e{X5hD zGYCs(U>CN%6Sa5jsU&rVPjAx=H%^KJ4uK!J=d#!NAU~Mdwe2B_4-S6^*3bSjpGn>0mH|TiGo%l^YHQ{sj{J*-QL`1u|TigjH^(tMV*raf) zq7KUKll$mGcA`1d^J~6r{zNpfH!qvNr`8x`;Redw4qt_?pq3P2Yc5V`@-WDySw$wey?ICe!{NKeMMmC0wd{h0i6XC#XhN!nGjYbqj)a9P z4%?_#FGlFEY%w8>Bw1Q#vYVaVXQ_z;iD?_9m&ETxuB) zJn+ZF{2$r;vBR|`9;sEYJ*kjzC>+YUmcWbYr(wy=ZkD&D7N7bQ%_GC|G(;Ly%I zM8)hd+lPnPL7*ZwxGkjcCZ~|2Kcj%D24euL!#PN6kao*6^kUFEkXKT?XT<=?C}8xr z>(r@1B*#4oYI9}AqM@-mYal$O-?=4nTl{tfJ2Ex`;8{oyLlQ6}t)bpQ-BL4U0$p%V zgZ$t5(ju;%bi@%-FB8EBc)IT#8DiIKZz!_l6c&Th3`Zv?4zrqGLzWEV%zIfX`PQxH zel_ynf#^M(yT{!E#|8F{JbD2GFwH*J(I8tC*=TB?!d*?c(BLWvv`oD3lE!7Ae2UK? zg?z>^SfzKg`FpHP)XA6k^802BZ_QTMr1m`xO3f6(PVE-tc#AG%y?RxoeEDHvL!O2K zjdkbRHJlGUT6ggzM2TdB)!=63GNzc|=6okI|D|6$)~>tPN{U6z(+p0;{Y37N$nN70 zGOFNW|8Ff+{`cSm^MLoLRZ0XmTTn1dXh;A`@>&Mr*>!L;Mkqo}oWF44z{tgqd;bUI zE^d0mD6+D4@<=0Pl>>IG)~!KC)=@(9d6GyB?a)bf$T$e^QSY~?;gN|gFdo|tZ47bj-YZ5Bb2 zAtz^x41c+cgxxGAu1nnfcOKPH-V?4Rd_g!U@mqW`js}`pSbzJ`?Jr)8HCt%)$7C-} z6T|X^=ev-i>w_xACv?N*)a-~o;0>z`{ zBINwQg4WoT-)rOtey_bAaCV7@w+1ZZmPB?Q=hJ~rRPK+2aq0%D_lz5x3<}`G)_yLI z9zgee<+#N>dra+zXzK9MqxHN}xGB==;BMi%bPHAkx>IViKjY3AB6R8&k-;Xuc=WqX zer&P1WqwOLkkJqcexz$3Q+CPx`TgR<9`V&i@)IBZ2v0U#r3CzR%8vvd zWnoof4k^`Z0(|xlheo#^WMdG=-MC|m1l3{9(XH1c7Z@Kydx*6O<*32i42P%eur9Qi zw;N+evR@weJv^}9dT@yKSYok!!XkIO>_$|h)s~(0&^HC(;RK|ITQqIhj$Y7~#^X6X zq(`5XdIOH-RrF3z^?410R!%zHw-cfE5Yf12XQhCldMna8ByVZy&xB8+I6m6Yir12b z#xgLhU)V;rC<{Iy>_|iibbAdPkf*fs=Ac`%n-e01gOG(TiDZzS+42l#?YrHbsR#FW z|0E_hb_0uJblKtidr2rLB?39mz`(x0wZ2(4v37|<33h@PEz;{?RCsmNCA!E@3)Rev zQw~MkY?E}H?)s>5@AAg?$FIkkTl=((6AcB?*9`PyA8L2gt)16ZcO`sZuhi2lpZd*w z^WJ9qmm8*fP_nLDxNxDz%8$#fYFLX9k{MtW>t1PySTPELF;uWcj08giy?#gq^sk8kWO2ze*h(LbfO{my0fF&?UALDNJFJA&DGA#aAIvEs$AS z>8@Hc=Zf=8b+$9vS1}sMsAS2K8JzH;kxfEZz;fp(i8xoB^Jw+Sg&VS>k^@hpuTNj0 zK8j5ONauipQ4<+8C)ouAIIj_*Fqm@pkCt5Zqjhy)y6y)SUyohFrO+YNwHhd)mM;-= z5Oz8;DUy3yX%`7L&Q;}_dXB$q&ExJ!)+_C}A~dL|yO+8wE8_UvfV&eQ8j(|UB;DNB z<6HjSs@y_Ef`>`no?RGNPx53yk%5L>GBAfAQ-Rf}pAYzCAB1{L==* zt&LChaH)dg;^RAV%oY6Xvjp(r)J6j+*!jx>Mhi<2%fJp<;E!4QW%=^qsIfy>MAD}* zt8=|t)50D`cH$GG1OIc#zOb&HezK|CgDL?$qDk?cd5+9Y(aoNe7UHrCKpzQFut%ffg^ z4YQXq1T(!U#_C~p=HbKi)o|igHCC81P$k{uEuxg8373p_>9S>?Oq7N`x~rx?D$#|X zz2I5m&FQ4%D`1u{Iq0!_Y!~5MC|riPs95S9z;je=5X}Kw-hn8~yoX0zu&%@Z*nHE7 z9Q4NtSbD+9fxB;>%ly>HaNcc&Tpsa?Qcl(`V&o8xf|r!(`{$N#Fq6>C1q&DM*0t*b zzDK`|8Lwm+O%|taF|i6Krvh8{NN4ubPk&K8Owt_iEOFEWpxo1Jxn~LP!|vkjX7h-~ zsg;#Gfa^x2w5tKg4m?-L-^U9po&A86VcB+_>*FvX|9Tow?%$wk7UanAe!T-(`*9R$4vJ zIo<~0ccFOHVbb!6PkyboWA2wZK$$1-j%3{Y(YKg)V+a|XcIt+qXFh$VCzJ*y5;AX- z^AF`MhyRtQ!rZOgz32*X^ei6m8YL$Byoldx$joMJTUXm8Br(ohGeE86$RyuKd}+X-kQvaYN9fjXKGw9x3ho60M<}ac zT&$7Xuy_>V1`w?rs5+n%1er6FnaH6Y7EC~moj__Z^gwmpkIcT2IX#}thSaNBPnW+w zKA3epm=-zD%u``+#a(bz7g!gc4U)Wjm^ss~721Xqs0;E4HVo$ruIaz^gBoy#KR@ zU>5j5A`gCeWg=jOTt7(`yIm9V;kU|^+^{oEF5ruav72~TFiN}MHsh6NXKvTAW4up? z?%m;$GK*nwr%ua%{yDZsa?*fW?R`>(+7`R$GjuFtxsN4&o#5U0z$SXn%6y~Wk*hLsAue(#I#lecs`T7Gu9drv&1g5( zA<{zxj=NnFIqLO|Pw9m=l(&$Cf&xj%42o_QTD>9;d!q|whqd1{pF>=f3flq|DXRg& zYC^#=fUrRJ&cJXWOea0ed_{7SgbmcrM*uXbA?H)J;!?6wY*Y5Mk2J8VOv$RH% z_giGN|MfBG8OigGZN2^auvaZE^4j+&=$VymG`u+gAq>`tMpamwbe0?_oeD-78tlCw zFpii7@ZRmn!q19Q#8|5D07y=B|{%S35;*Yay=8YfLM>9uQWGPBwnO{Z@$X{*nwG zEacrA==h?>k9bmz8{=1C7?DrOuXco*KK!r!v(PS`z+HPC{SCR}P{Sq2Pay+}?`#Q@ zU;^nRfX1@4cWNmFNlNo)jW)@dJJ~b-&+49$~Vh3;B z-}?l+Ye*LXO1+Dxev|ZM3|Bw?#;;YSt8w)aytv{V#w6$MXZm7gT5xwFFi?F)LT%Xw z9Ba?~mEc6_h17->{al8oN>uqN`Jz!GpCSF3N2WtT&Op>7gYDtdOE9AIi-rUmPe}&V zAp(qqhyEK}9PhJ86}}J+gR~%#bi;Yy16Pd6BPJr zX}Oh2XNo|Qr$O@byK!uNPQ{yEwPwwu_fIY#SEtm>x+Myw7tNyud52}DZzBcSk`1RV z1Vb???YUx6WaWnp-f=v(fYvC~Pf@P{)#fi+q;|AMHIm|NZ=~55k_Kp6h~}Iqz2n+N zC{kzN{)!RL7uIy!{6~T{muywo)&Q;j%8rwH;xu*HrA*QUYME;O{`>D8x-Bj)9wz@; zuwPH?JhpOq6Bawct<2WWwrX{ZY<)-%Req07R7!IE%d@e*vpEh2W(_>D?+~ZA-)_ux zpU=wD62UrUtM{#T+`x6ir{{qx(VsOsw^MIr^)eaTP7*5(LsHvtNDpPZe(&$3)RSsO zJ}%TCr$M$mPC{I=L`xq!%f*Nm{9JVQaKo4Ik$*Lc6v27`-{*JAx7F&yM+ihyD8Cb_ zW~#3~2}ZL*opa>zmZ03$DZHnu!w2G%Ns_raQaY=zZjTS#55XvJ^AMs zWqs2Ef`|%Fe^H8b&DYgFNz*Ijv?%Q-)G$eBuZ$`|#nz)qgUWLO=iP($Ws3*coR+DK*Tj z>vkPF)O)^m%^FS8mkxHMz&hY+#onC-Mwee^K*S?YfH;@JM4(ofkShB zZ%#57yvO#uyuF0+b8EOhIeFrbnj!-L!m(}=_2&7VVqQjXoVPWh1*uI}mes?r?Wk1< zZV4BV)>zqi>ts~x#?8&G|7@!)zT`;2SedyR9`Oj~N_SNavtk8Pnsg70VNv8znp4rQ zU%x{bSBM8mrGTz}P>KN+>>?>`Pix~jHb^$PsbZrlq_QOUE}XJ?k7zJ`J(&w@!Y$EX z+foTAU&yZK?sYIoJY+FhpC+WF@GQV>cB<{0zegk3NJd~-g|#=HZmF&lJi2gr!z)GG zoYW*%!(6HOb2k7R8Vr+OmEVWc#jh z=3~lk2T5h8hJ9XL&#PRyuAfBhes-$8A2j)@ji7I~VEY(e=t!D;fUyBN7m zklgfn5Cqk*1jZZ{*q3;?27(3Y%Zkry=mGcsl!bhjRs8VlR5=(kWAE{`PmB+bIS z4wr#-A-JmRSf|a4`&pYdUhu8Jv_xY8ImT0VaM%!Ii&?|bCCcqf*}=cpu(i#-_}-uJ zxK(C93^CT;->Ii8&J>3#G1QME+{iHH?eldcc@B#bX5x_K47Em>Zw}S0=By|VWll6$ z_K__%>YQ%=ePf&t4r5%HqMc>6tIt{g_Mef8aL1edvc|ZwFkn4t6_xIA&>vwqap-Fn zwxTuMXKrk*^9Jf+mEMQ5xqo>2{qu7-*v8WC8kKh6O#>Z5)&^Gp=^pRz_jBwAe~r>7 z*(~(UO6esTg-{wENp<)&>^2S}g)ucm!Eo1cG-o94%kGzAPzRF^084eeTDs6jxh3tc zn@a(K?$SM)6m6WVg^O?a*+Wb6Pe_X@_X|elURXJVc&Ca=d<|KN-Yx~#gnmb{5u+M& zZIArVfs&Y%7ZWbjxe$Hp4cI0)y8o~ITy)F*l$0>w8O`U6%+vo_V&Rl=6%F!PQW)D> zewk{(;s|vI_4`+m1^)7vbsTC9Hh!*O-!l;VzPp)fz7Y2M58>aeo~TL{F%Q{}U_SY9 zFxA5yT1i58!KF6EV5W=6qv;21j;$pIMxh`>ucE?@Gy41rK~Pp7K8F&PqH#LFMlSGR zoH8MHieZ^Wks#lrXLR4B{A&=ZSP{{TZSpF)+3Pv9i8Z8`>v!`qayVDO-B~0v&j`pg z)1q%I|IKcVE^_IRXB=dq$dypaF;Yll2h=-emAG< zFvwJ(aCCGe&vSp@h`f_rca07ck~jcHJ-@9P<$TK`efsH3g?I=7T;eou_(-7&8rVR{qs{IWX8biabrKVTm8_-d-CsrW2n`?}PV+ znIk`iy!Xvz0iE`u(1r^lsj-4a$YpA>$B!Rxvr#CGaJLSNl>M)&KfDIyZ7aZ?&|Q7X z{@x=d(lH8)RJeEeL7SR3<(RVRoZ0IDb#rP!E3ft4BiD;IJ-#e(AEp&B!A=am#5Jnx z8FyB9BrV>vVN_dO3}-UH(@ivDO5HHwz2siEGcvAjD4`#F1)5@~>5ggyBsac2rEt>| zZ2jk?^?46sCHeB#V;>1DT+2q4K$5bY!oGItG5AqS|I^wb%R8|X;ibtZVup>H~htK|>;+A#$ zO@|?|pr+fZ;yuS!JRV!*{1o7hJ9s#p@OKb)@E8y)hMF*ij?o#4HMMD(Bl5EW!3?I~Fj`{@jmb3Cs}qWQSe|vW z3RdNWS(Q%ln?@E}#F>nTS-Yp2Wd4ll9R?^xdw#1AZuz@sMU)@INSxffq6*yLOcFN3 zfnpSo_s$*(eGTB-V7)~zL)7uKm|O2@7WTwc)_)U>CPJ-0KD18n^6r4KgH zwkC3F(j&378YA2g$E;_$vN)m+mMZiSG$l8$l!i1C>!`Pgx zVTCIbw9)fkey=ulkL$;268sQSW*BG3U8uXoV!IdI+hicT=XoXw^j$>5;mNqY-k1Hn zmgut_dbZClDhLC8_QPoq+kcez#y1QA@u8IOeICEgx7op^1?f_ zyz0M87yoWUqHon)y12`^hX?ko>mAxV<%^n|lhX@9&&*5BavCPXuWEEDrZC-DXc{iW z`TV5JEzYe{;+Ez8ayn7Em7CvVYBpoy9Kn>vNc>LtJz93Mo*DH1Jff?MEkK z4S|o9ze!2I7=TFHyKeLd;lu8MbVT$E$-51%XOTudecpaFW@L%nMY$qr{2iSW5wkRs za@gLNt!Yplu0H_u{ocjx;gqd}wQnF8)?g{qcv4^h0)JXz7LUoVPaG?CLbC8ZK}8G8 zl;=wL^g(Io*6dDdl5xlP)}==`9z5vrsKqDOyr=bDnHtb*YF5)rGrXpGL9(wjVe)Z8 z<*XfsKW+0apF?6|3Q-4%44C&^{#N6mBu{_h&>Fs@|F=lAy$h3g`}NrmiMB{F493kg zaON{GZRvp5kzQ#fC+FR$Ydwv2GC8I?@}Mm*XZ8J`@@myCu;i4f5sN`1#rmR0AX11W zJHs~d)nq!%FN((z>AySGE5uu!#MfA7;1CoQRrIact%;2&Sn3rSdXG8}-S?jc3oBaG z+RQYj__NDTZzGM~lJ-yF|4$4mZiO~Ko~}Y}5wO2^?JNfm&;_~;8;bTHGTT0FA;({c z3SWQ*X?Nl?e9AdV%Td}nPc7^>s%*2#9$HtBlu4u4(pkLSIE~DoCa441&#jv`=Wb}0 z{P5W9^34LhX75Y?Cmn(G5v91d*P6Dl!^ri~vdN9M6^A5whK`FwugZ}*s4YQICHIf~ z^d|p2#H~`^!$`NK#s_4akCOiGrfeQLQX zV)Q}@(_F-LjEWsocDcn;$qWuLGO#ZhghBv^29~8uZeJP{NEvZH;~!A}hdWzuy7oh3 zhaY@$__4Vee!+T)`@6R)ga-gOb8pHHHAP6t6E36$Hfq#J1JX}PGQvh5s16G7LiWSg z*`28vP9vuJVQvnj!=*+nAOVo61OdC7tE=nY+a=)pTlV+fk+N`i&t$_scDN`$M@Y!6 z?{0i=0nz>5mg7y2bgOXtTZX`$pz&Fs8hOe6JZX9K6xGZ~^E^QlUIyO$H_-j1LzL;Z z+mekPJ}I&O^r=V3x_9ecGKaccaQuuJKTL@&%3EFOwmy?P!S-#A5? zN&0GsmUe`C&HG_Fe&X{6QJ6lTU?;$>Sf<4OPFJ~0)@H`#h%f~2lggIG+qXe73(gWm zfwCwvGc&UWL_gl^SW)jNlo8nIS7d+&;JVu_)&4eD%A^kP=Bae-W!c*ANU<&3_W3G5 zV8w|5K`JzO{tYA12qX=F?BMiJ%^u-$r2ekvVSLciqrTDEnBpBvUCeGLe{KZ zD`FvChOXa;I6XZ)bLXL)kooCHQvh66@q0;)ub>qJBu~UnCv|{vj~X~oiD2u0qE9bc zxN4`G9l>dQo^NNO8645J8`DVA-8|%@|L_UevsTBBybKWJPmKSa*o~ z0VHsOj3xedp^|mKBgh0RALUTmfcce0ZYG*s9z05KT!dqH27Mz!t}+N1*5>4 z4m`_(X(9v1Ee54CrsXkPJ2}dgDU+HKvQBdZ|0K!C*J*BPC>zFF{aX(8Qq+#+0WC1c zany7ePBVbYwZJ6Rnl#z%-@bdbm*>ELOPNdRaXT#t*6zzARs_lFhqW_s;v zZQO2EA^Q6ZhEx#!KB7X&mxd*)j>MUHF_ES>t6 zA_Vlw*h|JrfTUkYZ@d1G?mGV zectverQ%4IE06IiTq{Qukh%za5p+zZ%bBaEd*2SnIo<*@|En!~q0Z&V*W>dVh&hst z30K}I5Oc5NTA+X?f!88w(TeI`W-h``FqdkTD<258Crc1_1OQ0b4K@CeP+!=QI@EF- zdQO=HfqLKn{ulEp4#BXAdD)BKo*IL4b(Ry216Cczss#!hIscO6fwR|Pb(`5;xjAA6 zT34-_LMW2qrVI*LqPNGNne$&9IE7#RmU~yMGF#;A75hUtso1w0$VwVFi}Qb}%6AC% zDdhdyilRk=CCI5mcriz<=ZK-S2;}8QBU4mZR3w6Mh>L3;J$h8YTey#2ev^#Go7J_z zMifv|^rbXHKZUShb#x}1xixKFyGIjqei)@`pO-yH^7H^QT)jZyx#dc1P+e_b3C>&Hc3Ag=vm8 z*bXoyy8}5*?(c1Lq7)4f`A;FJ8CRoQUTUsix&L0p;^&-6irDYZX;y52rt@LY|u*2z*KbxJFGX zVzUbfM#0rT7qnDM@CkY0moqdp z_Z_xz?lln0nAX+eNLhr_s|rp7$G?Z!`Uwp5)XOu=2|^gkT{P&I2HU^_s9U;yzaam8E2#UnjBd84TFZ%; zzXSL8@0*>TU%_K@;r}=G4|%uJ>VMq|r)Y}*5u`&aVavy%lpxw~v&(<$t;X4KCJvSv z6TQr>qe`|)T$Q$MV})TZHO}u;W|#LGUU>S0j&`cp2!r*Yk+=us|DXMK*(|6SApbxj zg}~zH@ar%B37cxQX`;tTdOj;58L*UlNX%_C5Ar_5YR8N|W9A;AtyrI#2Jo)LB>K|@ zzjO#~VFw#VqB@Qji7RtD&3SKv^0Dje8sjN71UCzS0^6$b=<7e5#YB4*|E^!MyTtj{ zUuT@#`BM{@+T+jf{L74jnOmr3Qft0pf#tn(^O!qT0^TX|7KR>;Y&oBPCnqDsbMlSGj<=|NN0ROdK(=O)t8=&C!`{rh`yl7~3zs;t<8W1pL z8T|F1+$KObG=?{@J%TrpVomLl7ubyJd*U#dHWwv#D8=%XK35U24Z62$(#593UgllD z{KQ6K%wy17a#4K6&#>d-sn?Q}=6DiXgwISqk zgAFwmloy4M=mP5d65wkjE+u>VcRKK6A2iOpanX}=1Pa5CQ!ZIE6LiD9 zFJZoX^C`6=IdxZdtojRLorx1lo9Pxd(6F`JS?GM3%z!kTV?l=x5{qats;eU zp6a!|0(Oz%zFrG2SJ@mel0Q{|sapN|^^cDmG-!~;gr?J6H~!Gwr;i@(NLji|E#{cC zrKNjPW{a-sacp?aGia7BmKX-Yy|e$0A^cJO{_#)IW`8qugx2KrrbCT}Z||bU3KwVI zq9<;4oVfeeqwnao@eIgi2Nm2tcI?=7a0A9H4LBXRMKnOY@;pSMtFseKS^G+NrC|sl z8wy)%#*gzk*V|h_!G1_cefV%#!s{_2I?;6HCu)MwZ%D`kt=<>|Zs!-3%NvF?X2*A2 ztl|$1O1ZLUm+VB^2Kq?+NJ|+07q+G`Q_?FWO%|Ymvi*qcwfc8C@F3rLm(<8pSP1AN4a)7+T zKT8bSq>0G(36e9=(yfMvQnOc&#Cz2AKha0}`-lMXAGO@KaSRuST__p-pRP4WCK_tQ6qei*V?$b=ynI-D904zUT z9}eC)PlV($^`Px?2vmtYP=khGa>wjJBe9yo83@Re`v*0UC>Fw&-;7c zQ_K7N|53-lA{E(Q-RX}@#ZCDnnYZjFFxR0xf@@|;zQoNAfyjWmW-bLDEk(Q__kNQK zsi-MzW;m4XiFRVIHqE^Ml~;LDwR7xt^bMPL+1w|lAYrOK_;J_KqqkvQvmSTr%O3hy8YR*C+ zQFRIAqO}f_sDJ+W$FU2)SoRYdJ0>NYLLlu#w_mEVR-1QuYvG8=hw236itE@B3nMX2 zbrT8DG>BUw!RB+B>|5Ywe%~ORs!(JhY5Zk@5|+;Xy+dr2091oIJ#m80iHGX_jVjD% z`SqFk1~IaV`H5&`0taZh?{zlECx)eDq?RDeY(0FuFF->cmav!BZIk4mm^Tv>(+bjL zH}acEXD{&w9@|CL^UdkfFLItaDdk-neFt?7H#X^;w4*y5&>c7_P=$Z*V(&ALjJ+g{5m9Y5r~ zF7EaViArA1n?GOJT9JWv`P@MMpkVG!Z1V@P0n@Dkf5~b2$y|?;*S&N%@ZevIVJTwcyZ_Z zM{+LA`2^a5oS>z*UlB(M;!OFkS-rSf0s+=YTqyE?MvX#!&Hpxl4d3_Hh1B6MG8&>t zRgd=!Qf?jC#mbxMGgal`V9n}UD6~w|iCE~vsUYiVQ|{KY*O$pVMnWQ`5e@bKc&S)D z;(Tv<<_B~)Bt-PG&VJds$x3pcy2rh)w`X!UAXPgHjn+jQn*D2YlFYAlZusX&L-cj6 zTD1kr6u}Z=mN6+9V%O58OA*`s+h`{K`hV?9vV}1T8UbaIcM_w^yO(JQ5J?PbZ~5;6 z*o6Naz@GIkEdFz!sc$!GtG3u^+|67XF~gehRe!37Y0$q7RrUW}sB(Ykz}@o8_bh@s zEc@DpH?}=-39cXrI3BBP9GTjFctLr{j^h8%+SuN2OQ{O36hZ<-9X$}7V%)4fh>>Nd z1IL=9fARANmv?pJd{s5!f*~Hlb$qSbpK)nRDcJHLeF~#95Z);Qqk|N}gGg9Iq zVUV?xXiG#&=wsdPZJt8nmHxIZASz)TE1! z(rE{v+uS#p_I68H8Bcwr2BACB6jCb?xLK355ioUwSQ&q?YLzPOEHdr`CR}(ypdW~! zT&wWpNzjNR7CWg);Ld_8?X3Ks=U3w2Z;M3(&FS9b_ppQ{cM+AEAp3OBF^(sxJ1I7d z`D>ac#vrXZI}3*Vb)Hp0TW^^dIL2jUGAUX)Pc-skC# zO3VdNiYqLfjn>z5-^7=)q#X&r99BUmSfdf&&wuk-HlK~(y)QPw+I8!dyZCC*&)(2= zGa9U{wVA$y5o{DG9do-%02H=wp){;?qbirfUg4%Mnp?!+q9uM|Rg*XG#(!M;7U<>D zulZArnH%17@c6Dxn+*hj^<7p^gTo!%Y1#P1#+vD(A|IUe@afY|fCU%$&ndM;KhJ#) z{>^_zknDskNq6qpK%_0*?SmCN+G2Oa$`O3UuG&|l*uRFq3mX*s+;@vW%3^`mUdyZw z6{_SR9-sQ%sKg$_Zem*?PF53N4?E7;{! z)#{Y8HBO%7p(Ta<{bbW&_p9AM{@MLD90_C)sV~3sCu4z(DSgbu$yV3i(|KU{eong# zDQznBkm`5Cm;XwRx?ZLga6px5>zEV6019x6jZx}{R-WC&vSE+fo9S|l%1C>#0rt3( zY$z@`=sBYJVRr;;WPJMYoUcNQvs-9OJFKu0{NDTEhkB)+1EkB}F2VY}XnQh>S>oQ} z%_RWT<|6`jy*__@tbBX}7&A-|{0nuD)) zF1_xGoHby3oh#hbyNj=HLz}cNqS-eQDn5B^YOQ``Nos}eg9G^*<<=6bU>KAv9qkA6x{>gaMY2I(reHorn zl-Ky`D<@Lr53eN4fsBJqIvfFEni9mlTMxy6)c&(KiL*_A;7C7PQjxV@Yu;t9PmMlcTa#G@ncclO zRC8*BN$3=1PXI_cKQM4|ugtthUCu5hwMqb)`Fd1Rn!GB%Wx8mtA5Gbx(C@wGdo^48 zZSQ1Yig)i=VlC>&nr5iAefHjNg8NM2%_gf>=kgU4-8N>E2*`O9I|9i+=}ZP2^bHM? zw*=9Nxqa_l2gN6GU2-qVruB4}IBRMg81T-V>Vac#{8b8Bk8cY{_h;?>ix86dXPXhj zx8ly~+0W#L1n|F;bcK1``;VV$X7j$4gKPdn#OFEgaLN6{iPNE=vl25>4SyARHOGh1 zVxoUPV*+7GBj-?lzi|UxQ39Me0L)b0T}*3{b3!DisyOvKYg)y){)@1LnMhuLsNQYX zOFgR?=bU(H|NQD8Lddq^5R^YHQM)+|C<4RKjZ0kMbM#KTz`Puo zl#0(QWOJwj99Inls9xrmnXkXje*L-4LZamzLURJUWFC(DbjiH__~macs3BOlcphZud5KLfq}eB z7c~}Hw0(7+8nWBdfRY_gzOIk$fss5lZ~6ybK3pH}j%uFQAASGa#N)Dk`!^JZ_^4a= zAI@&Pb1u5n?t_mM>bS}%^m#BG< z1gtAno{njX6B|w@l1^#OWLp6skJne*jFc{mq##$8i78F9c@JAnc1y7@4Xhx7pmGjy z{_$vP`Ct)&bA1mr+FUcXk;?C{#HS+1YB+B-B+n%h6T7p(OMm!h0R%q9j8 z5{U%Eq6Dyip)TfADERGo2KpIJhIuCOn=L`W*u^}vw)LKwejj{BAz?40#Do?boM;3A zE)YUR$|;zYTZ9*`kpsV#`;CK;VV}+toxRN`IlG;pw8kf~MeafJ3U2KC>haHmW|_cM zrP7x?z;+gN`hQX)2VX3h+UnSl8LuZ)Zh!3NH`1qj9ebxL4!1h{!HZpxt4?>U3CG-p ze7EEGpPH2sPMkP#>xA^7?(+wZNqugA5h^((TVYtI7h-F-p03&MaAWORrT?O&xRs7J zRU%j+-XUhff1jhjLdC;*E%hcMwcyyvZBIeB_gY-p>=fLg3j}?QYg)4OJ-;j%6%O=J zWYSVKaBK@$!To;RIJIG^nZdEYH1J9|8Y<$H1rhFHkEidT%WJHvTKV6Eb?LB}LO4Z`-#xWl;k-VJ}`*0Z3uyHgjZ6X~Hl z_JnhU3V@RZHoBR|Cr|P|T?u+(1#kmHI1c)6AHxFDyC}&461OHGfFh^BO0fN7ZjsNf z)Z!MMo6CClg7;}OU(sQJLYk@dPO}0`S)-Qpqn3*VES0QDQ#k+r;V0xsJNmc<>`DBn zh)XUI=J)df{)Z0R!?DZLjPzo$;)0Q|wKJPlxYfrAgjAz5|GyigR5@G9gqA)jLq^x2 z@EFqIDv+(vSWRl<6dD+)~RxniAWwiN;E##H1gK%MTWC5d#TX!^BljBwtjgFM92BHUK(b`B&0g~$J1bMTypjdY zdzASj| zjTpNKq7<=UVnhszE_)Dc*jp?KASxyzSh0%=2v$S|B%r8>-ghia&OT?~d;YsV&)%`X z;#a=!n`4eS=9uGN2mby))kcd6`Z{kZw&|-dEmnuN93`lb4s2Jm_wZS8Xf+&X9+GJn zmIMNZsz{3 zolO*uGjrD zV4FmMSEIW6P?%Tn$Dy7*$A86yKeMJq>9E&f?vnVt?vQ(qyV!_g&8QVjpq`{IYFXdG zu#h|dxhmmc5R8}&{$k3n50t$_f zD)A_X28iGQi?yt|nsiLIx$o85jHH(Uq~?Gr_D4nr6nXmi45Hk08&gGfn1X${oGbW= zHZ^@#C(w^$Ksm>2x+I4@eSDzcR=}oU0>G};Bo-PT&GDBBu9EN1Ak~ANUmt zm^bs)+lO}L5gu`0MV#Sn2i(2G$MxfeFrAZ|d+0GLEOfk39-|YE^ZFWnk=i;0>m;HF zZ*Igt!i51!?oi?Ot5&GG;O-m*=g@1cfAX> zNQ>(x-PWOq@{SSKgT6=P%&Luekh5g~tvpFHTA=?WZliy=o+;Yu$5&MIGnt0c zn-lFDqzHc>nGSnP2Mkb_r2Y{!4$Vwb35r)c%F#w4P;;tI`qA)d=YJH<6oo*Jobf$dZR)c%*eEfmoGDS_C$7lXO|TDD`6d z0=F6?HME$MTkHgotX-Mu7}&v8MW8Y%Fly(rtU$Zl$Vz8nJzP zhhGBfUiPp0VMOB}8lNoLxb}w)N^aUP{hMwTBg&MXza-!6bTF;Lk+t78E@}1~YfecT zYJGmi%Q}fp%k4eKwKG%-;n=|#XebBbj)-B62Kzv$-ELljO8fg9nN2`RWiz?HsUF1ku}!Fz|tjkmjdp&a+i5?3CS zxc;YbDmL=YCk%y!yrS5*vfbP{dq%_zWI89)<)Rs}bT9DM;LxkjGCuOPX)qnnr>7{6Hu0?seiF$T*yKL3a&KE$vDSzb)1MN>1lW#YG_tL#6Rb#Pg;iJI=9}NA6A9laGDz) zlS33Zju1^0@ML~qVA_l^k<{BJ+lJOEADH($NoQBSxG%?t?wj_j!Hzq-rd#ft1W6JU zmhS5FV+Gb^xJYAX!mBmtoS{>CKp0%(0Bwl+0AJq0;DIE075X!n^W!ZAi3nd! zU5`X@Tbsq5P)CYG4{-g%)Q<&kG=d}Pj27$B!d{NEBk!% zI(c8`cT+XXjn~pPWc!Ei1tXgW(TZ62!THcZDLy7##hBkFT#0|>iAA4=VrShv*pPTrNKwX&v;yqjf&kO^?=&3-80SUfITLtaQg>VbRd; zsa}(XR^bj0LW9H^Fp}d?@h?||7b;_CjP!9|8kwA>LOufbyN{Q7)=?Q*_8iduPq=sW z=JUs_$*-u7tMfxn7ogoOkPVNh=POywoB3Q?5GgZMw;T1x4>S>n=I!hJbEUNS^>a_G zuVye3sx4-1TR2kHW1yub;6RP|2}zkbfNqm(@$m_&#yn5Kf+}tk2X|hlgqN zZY42M_O=NBBQ}8Mt!9k<#3UX!*rwpk*_ENK9A;SeTK|dj&eo?tYX8G_st-~-vk=-$ z9Yz`np-8j(%~DQt5ua#uEcx_`4PKr>p%M3DhgW(69eoKJG+*_1lAi0}!9%DW6gE+( zC2^1pcY=X2SUttqX8PN%MIhX?0#C+Zy-srZy3XwUWf8;Lb(-(b0?V4$=n)RY9Uv~F za*AMGgM2#n>Kku)3AaUo=dWd-|_T>AM&+o#9dRyb@Nm=fejw3peNUcTNn9e=Mm=IT6t zIg{J=UBZ59;`Q*H009P}wPqq9@C}x228E&|(wXTv>#9qKfI0wXnjuMFnEGl(HXrGv zNuuN$NvPMw+B6w?Y?h*O=*W4$5sl!rsy2!?IDe{%s|K+vz<>Yi^#^Dolqm2UrK)6HH%o*R_q;nCY1+|C!7SA^b2<7RGbEI z4qN}x{7!ZzcfULLa@WvQ$3Go^dijZWLFs~P-y{_EsxV_)=jTyXntU4gu5Y=ze_Y); zpgU3Fa86Ny{eo!b30W6@(!<(w`SdrdyN^~gJF@!3CokjMV5I<{UTNdjWpyIm_W|?! zzytekb6SN@s(3-9ddl#D?sh#_zwv!q;Y`c`&eDRb`7v9wt?nFq*`bZWxQ=4fL^yTu z-P2en>$z{=2(WeT=V$%)2sb?U9qy6x^4Lx7)TOC&eFB`!tnZZ#%yiT(Vcx*h@ zr$xsm@%#+o`?xx_2f(M+@dFvH{BdNt4UD)_JCA;v~pNts&Zftg} zYo%RSm`}WQwdFfIfn*)FwWV;7$On2~GXfC$_S--DWIp`?O?!nqBrbjg?6@vnKe+$& z1)2v|s`*l4O@fut#Nlr?K1d8329Aw<(OGf`uIvxb=z0cIjnz@}s?K?Nr<4p;Xb?KH ztbQO^_3n|U*G{Z~A0@z(SUo)a1>faxgHrKcPS{;kGR3j$B-DW`&n!z%B5R2X#UWYB zis=4@36vb+)pGK5t^k;>b6E{eziKmR_u%hdO)7q*?f<+7M6+DTF0pE`CH=U`R+LfH zn8p7Fff?vLILaXiQ>3nZ8j42$afrJJ&z#ffualXy)WbCv9>0K%{Ob?bm9^ZVA$$RT zW@ZO#&kqL4JwweoxXLk(`?IgaCr;pMWgU?hx4}P}fF}r{stJ-+wc~g0XJLyh9~D$# zb+eL&bQ?nDNx$GZ>)5f%H8~sJC1o7LNJN{2?|*uxV)4`Ey8?{!mP7UV{!21lj_|vw zt6EJOXu8Zx$SFLc(gfKRK7NnpzAmsWN^IHd^P&BxE{o}^yPDW#fKM-vscjA%_J6-t}pTInlmW<(u#J4Gx5ELG0=Jt`!};ZB1@ zHjtQ5N2XQbIJ=BmhRSR@`slLNAj0B6u`~rXXcE=orh|XopnV_VToH(Q#qS-EtpDaN zpL~D*mjO-sM)YQQu{?C1&@Sk{u7(~{!2QKCkaJkTUJ~h*>;7*M%-?AnqOn^?{UZsb z58i0PfM_}Xumy&0m`&TIb^mi2){9AfyYIpf(kbZB&A`wzV-HRM^k4rM#G^+j9LOYy z7?F1hdP;K@ulYKo8j^+MRZbvQO`z>shAo=TY}ZR7GDG{n1Pl#U-vT9ngYcAlq((Z! zU*jeGwR|iwsUP#0K0yosh~BSh`02+?2?4xKY;LNnh}G2;0Ou z!QCU5bZEx|fc;Nx-Nef1exl3&eUlYBHRs<@X{hg`QE0SLgp)i-H0j3Fbm$>jacDxC)0X9N(l=7xB7CXhap=~+Qlg7Rg@B7A$p^-sXL#D|*N5!8urYn5Q&M;q zYEV4GNA2;&jX%mXB>7d8JU56%y}<_9OG8KGm2z!M?B!R~#~=rT;gpx<{t7A=3_JhE zo7;Id9YM@x3@i4rE=ELobf7XKd8^R`;`DK{rghC6QLpo&?O@R}p?3S`yvglK0+IZc z?2cIg>paao*WrF9}4dgQMlgovv*8agXNKv~|xYQ_~heY?4im z#!*64OhWNY3*lPMudO0m&_$~j+`DR%zMU}aPRzhNLLu%ld6dyaf4Y)1xa6lxRMI?)f4?WQ z#-rj!`-mdYK$e}vII^l2otkGuhN>fm9Tpa;vXKY^ILrnnK-VCCbB*x{ABLjyPwyaS zZ%%}!&=@K%apd&hJ=i9r2+A=@F_w6)_##{a^ShGyh;}{sMjcx5pcsdX-Rn#d=&E{o zkC}+Ev{T%a8`b~&o>#Rk&!!;x*GsTy83|lN62aGk;zq_goldN}jiZs!etmWhe)+!y z+vK{#PArD_Ts}(s%JM7V{yd8%=)3nD@$bJk3>iEae2d5rq;{(Gl z=N32Uf$^}n4k8_R|0})2k(A=M{rBHIs|)xk{=?G7V(oM;Uf$NLxmcGk2loa=_d%+c zXTw7n2vMTxW^4pa1A5KlPidRPA)0@t*}lfCI;F&ta#pP0rE$}yXSR6hliQC@5aM$m z?qzwG0b_LTNWZW6&)F@o!I66zocj%@wCZSdCta=Fi+&Y&j^PzdojUY)*oXy;{JmIZ zeQ;BgJH^e(wvp$g&#v0IcYBf?l+w|M7%3`UAA$J_mpQzJy0X*V5w%$4aw3{Z#q}1@ zd1t@60h9y8^i>s4c5|E!EmjAfd*J?W4@Evul<)R^$8*M_45Ug$IT*sNFYC020G7?30}UE?G)qN!l^I%ova|9ndN&5XwR z(GHWXeeO-`{QljV=hG5BiJzgdO%^SkW)yq<__GlaeluQ|7FqJc!>j3_Zp=@#UjIEW zShGbHt(A+zS}CmX^Neb1>j!;DSZGRAbXFNv0ttwS!?R@lV4NZluI6DDMAOcTsuaxD zP#UclyyRM}E|vJeecj(pS#|rAV_piTR!kX?GE;S}VlV!)tFrbcpwt@xnd_ZA+y{5% z_2gKa=nOeLYb#FI@r6+|6UY}%l%E8Pip@w0!U}ll!>}^zk4%M%#Yx|q3%IU;`8q4l zhuII3YM(qkGuw0BnzLTNT5Njxg7%w!XCx#P&LhtIBqT~J8g&0mv*%jGrxdiY4Lv`_ zD>a)tVUw;B%T1Xwde&BdPR~Wufo5DNlzq{@W3RjEcwO&w^yG5AtTt_^A7DJS3!Ts4 zX=m=77;SywPs=D$y`7+;DW~h}?1Q`GoZJQqYl6Kv+MrV-LfY{=$6mhsC+=l;m@ae4 zJJ@NiyvQpgio=~Q9EDlwDy9W&Q6uLCY2$cq8FKm}W!PPGr(QBI?!ZrR4u9$$qf3sw zJ+nJN$g`kz&vOs_{_O}b8r_2B#Yt!>0z|^%nDdT_!9};y9{`?eyIS+2y=>Isxj%TSsA7I*yG_pl}V@yd{(}OqwR@hD%sjlk2Ygze;-S&3+dUaJD zx0YXZ`>11xb}q@^rSYMyNCn~Ba|)-J`l4`AtcJ``10^|t_-9OL$E$aKTIN z*V#<*Cjw6TKaM%syKo4+N=m50vColkX#A#OR^KGStf_bS$;XG!%eIM?nh9+U5s z^_phO{{6TO7&Dr02faQ{i9X1w{Pc)pcBu#0ASiRBT~xj$4^A!nmS7<@A>Cg0<4?2S zmfjGAim$%f@ontry>UNT!lhouv$`#J9`^sC@X4V&8|%E@koIyi%~uq--mdw8Z!-i; zGRC-Avv5?65`%z(U`wy{uKIj{|ML79GMEd9Ti{*FP}gr}=H~P89N`_`q&SvHzxl|A ze;fS8kSWuGf`j`5rG8^b4uR2y4Co^FMitXyQ6m4jvQ-+VXWQ^21IC2#Sp@6~uWOU4 zo$hD1y?ftq?aUTi*3FPLAoG zUwGnf(W6VxTIF2YA8!@5ZEwr#H#UE_*s(4D$-6qEOKhVGZ=R;k41Yc6{*Ii^l}6@S zt=zWP=CoJR=aZgZ`ZeCFHvjQ^+}3Sz>R+w1!YA8CS9dd-Y(uj~=FF`{J&ApTuz$|e z3(p%L*jDaPep}0+$>flQ`Hd z{2bjiuRa(uIAM`@Na36O!Yto*-I@+|3t7_Qu!vwy$|VzhT#X;-hMm+~ZvvqDp0HEf}rxwSso9;9;$x z*}x32TVnY#3Z>sGWj93C{Ex2$z4w`LIYL@thKnV}WRGMRr*Q}VJOedz-H>kkKi1{V z`*$h%C_I{#M4XIqdZa^6j5)ciTD((Q)#Dotm$Lc4_MlyJu7H z=?-7c{co4s@j&w55k{B9sg!V5bi=>>s_pRYK6Z~x!0r}*!zLjE}^ zEr0RM_aEeI`Ex*m+us>R@i#bFK@=D#ln*?ST3>(TTe|ofTVLb}`H+sJ#Jfkv_>W6OZC>HT*kGG% ztCFV&F%&?Iuh^FO>Og&;kold?-SK#YI6%cX4bY|eTLl(Xo5>{}XXh@RpAUOPm*F5( zvjct|)uF2O@EfW1zXrR-nJW|bebIkkjuQ!e8t z%Vi9nH>X#P8xzY!dbX}LlX!d$-HfT;f!rIIvuG->3^a)$;X)cj=Ur4hHTE?o;oC~z z1^0m?V*u55)FP6!iQ0tzO>wMYUWlIaF^K0fPT|{tlfou)m61c5Psqo0Ki{0X?BaNz z!SlQWcd_6csunGo-znyVo>TkSMzulDkwFZT=Ge9r-$DGfV|~B$j9ok`G8tJ?){)tm z(OKrUOWi=Yl`BGO>jsr#?30o>#h>$64C^v+*-NDqAxMYSsg>R?CeLn{{F{_5!&=ET z7)+_bvQeHL4X3MS{e2n37kigVKmEwZSmjpjfg4i4`{=dfgztFe6G;q2y{f273KQdB z%h-{@JyIkhE3J|f`WrB1i}SLMW=R@V!Ie&o2CV+%`ajign>(01-~ru)nF9lI7;!9# zx~Q7XT`M4iUjO{3glUETH8(&6JizpT!G1jc2_`Q6tBbKpVqoi8U0=#2Q z@3$8R2bDmfb9ak)sr|t1tHdr*X~_gBX4kIO-rPB+{>FdXll9n>LI3Q@KmV29kvdd- zbvF{53DC;>Z%*DyG2kI8z7{|jFQ>gw#9OHXW!I=26rAdAN5bC<8rTk#r)jt6kh5Re zGt3TI+9V#!gCmaawQIPY<8isLXU~J5!`7FJS`Wkc3sw2GunMc+b zd=p>2a?P4gW+g&T1fL(@!Jt3+&l1RR})d*?@=3$QO71!EzHHS1{z}e?YV{3cCZH4u=@wwHm|ge zHS-Ha}?O?7_8^A!s~FY@HM0pQ9QGZ$IP}nZa(Wnn9;Hu=A3`)ydS-k;U;V(XMg!xG9uxv75~9_8`JR|h|EZd-i! z%`Rqpw7OM0VB3bYJKeMaI0|_Put`fdfMu1oi9jJ+?nAH;F+yRKV#aU=j)e}~Jy?{{ zBaMdD0I&A~;9FSN(AC~N^ZN6S^9r%P?-TCeH~rw@O=_jxp*hfD!mV~?4$jd69ep)y4dSNKrkmGf;bZVN1B`AT~ zfN$PCBE^3Y->ivBFceW!=J*~X#d^fjQ*mSZlT)J*-EbWA(CDRkB|lQ>LnAxPV}h3% zMf4J4+QN6^N?1F`$>3Y%{qtbC=u9s2$g*B|Vmk2h3G!vm&>+TAyhd$Zs&PRSWww+L zs^rXEuL1_8ylt%S?HouVeT)+j^71pjnOfa@^-G<3)*qruQf&ZQwA*xJCDfVyIW**zYI;|=z&a6Kj1KBg>Nts+)?|mg zqnc4WK!67=K*sA2vsW~9snMoZc@$g@x3l7_tH2|@G5w{|Q z`M__*3-T6~pxe1r=#}BodWH3fwg^o6s&DTz>*0+v=cZjWMc$2JMbRO*sG7ZJ0n26> zdE$d2=6I2SaSlpBDyj>BF(%k^?^*}Hzs6JilQ@CnsA1;y8f-sv1P5^w6l-rh${u&y zNc_E3dv7s7F!bsWd*T}Vrczei@&|HT%d^)zc|r>iL7Vq?Wq8r?fdjd_Gzo zFKYE7K9?`XW;bc0p7Vzgq3m@ywb>y&R_34phYol&+Z{0LB>%-t7urTb+AB1W6I4groN)Kpr(&QnRZ9F?orHZOoI4o)P$M!U4;PKS5Wa`7Fdp5$Dp(q4 zr?LlSugj<&SX^WI*B+AMqd+m5<`z9)O#^A8s$UK5Pgd$PJ3DGdY3II@E6#c~Z3|WK z+ga%QRAQXLq-bb^Q_-94++7r4IdcbOr$pa#Z0_FG0|s=rUx{bFo@u8}1{EH@xpP1> z&`?ixOOuYxNIzbJ2b$sEUrc@J3-;^Re>KdO1$N&2s)X^Y|1G4n_&y+krR^4M)6mxT zdZ$jE)<6=)#mogr##1~)j&z=>OXsV*K{<{)C$YTdQt;KB>A|(_uSfJ)d--WvY}&1A zh7ffBW7S)C1ur2l+9~-^?6ALv+qQFcbyeJW2!TfM1|p6lWQ=ERoMvS;L8{P%Dt8mm zhu7@3waP~x{ml1w@UbQK(6kp9U*Dvgb($Twv91 zB1F9A25LbY#H3Bzc18KiZ640e>FOPm`f82h6?ND(%zz7J9j~QU<^_S(5{iAf)9jfC zDI7$eD_=MBpTtDl3GKq1t{pe0epgk1H*I-y+vxQDaq;mr+=3-l5CqXxH=$=>0(1uvC zN5+Fozvq#VoctWk%F8>(*|L^1D3thhWfp4v#OE^#+kW6)km7Y=t!{%y(>m<}dClSz zcfO{{0^zI3UudUu!YZgg*TAIw0)rPy<9dt^XTIedIzJjB#cD`Bcr4Uo{F-+sy;Bai zj9(Mt^!gO1*R30qZToF#U!or~Fqd+Nm2Ph!vDoj#V*t)GHgbv zH>D($WDa`+ihC=qM+~JBCxM?1AZ<4E<8n*T_y*(#O>m0?H1~x>nl*cN)ykFcIoMWQ zco264IV)@Dl>O{WO;Ey}++-#jQ1`F8*jY#N?949od+KeUG&asrUr+V#8q4DvM%1Ru zJhN7~)vB-r8c?|i1ynO#ky&$qsD)3UQ(WAnegsY~twUn(y$+5_Wz8w)5IL5mN#nj2U9SzXHrXhdtTaFSST6MrtuKFI-?&=5>Nme`z_(5BXsQdU`K zF6}c|LCbI>3a=KxVFGXvYTn=5!hDZHa`2gaNp z4bL(Tp$m|^VcWL1b`ReFXN2>zKaVicF1tpxpcFbr?#_0AO6j+~_r{pf$!lJ=n{{`R zF+i!)N`fK*pqUctGAvv6w_*dF-Bj0XC)3A4Nzj>FC@f86FYv&YOAvg^Z!WOC|G}{? z!VWAVPpf91C7k{FLEuX=kG%C@2l?#cT&R5mko>ak0tK&`l6@%H9*Nu{Nr zT=B)XA9LXs98c80Wb_Qt6oM${o$;2SUR%$0LdEy0t`UAKNc^q zzkgx5iF^m0Sl!HC=TohcNtJx!UvMh!snG6CZzar59~;;|OXh?7yS-m_a1RdF96FmU zTPiZov%uh7i#J01aL(b`L~xSf%ecT@#Uon-%P9@6u<0T>5iw^#^tA8C)hZFmsLIuH ztRMnVqhh$$_0Lm0=Qbw_Y3wclS;%WBQ`6L5%xRMen=$~g?SM%45k9e1jFmjoo>i!6 z+k>E{Mh@c+kZ)o_>ssYAdfuj<{1`Z>p=lBCPPX-E=Q(K{xJSraGs=7mPVU*q3p_>l z95*}&)%<$}2o~f0vTM|CU#Vj;0RSInApkxqCIHxXa6tFGZM`zucpt9~CYRSd(sN`G zG8_X;1^FDh!MUOFhrHk&sI8<^E>^vIa9o{9NC<4+cVPxGSecFFLmM{Km8elOSV(>w znR0(1<_}~0U*e0x5|SjyR}P_Br8s{U&S_aXC6m&cppdvPBrAU-iR_$yaOxb+$_%(dvTAEvFVy)qJ@MaI{oaX>3=Q&8% zh16yWZ<(@XTR;ZekQCa|&v=v?jdE`S&8k;-*0N|`p-*-pQ#p@$%Xe6}j?$=h1-vV) z;Ea1bhl|TP{g63*_LFZ&-?QZU{lZJy(bnK}GZ{ZVl-2FnWRzctG+`HQq z=9LOsxbSPDTMMe_$8y|6WOC9?tr>cL?5gG?-Gifliyu6$_)sNhac6Z0k;U~a4j4W< zXY=jvdPLWI(=7CSL-rvRbS1XZ5z9M*N_$7i1rR)JQd1ggV@b^*(Vml6sr##G?+pP! z*Nw%z+n=e;KeCF0mZId`n>DA7F4JXhdn4cdh~>S~D3%IfkeD`h>02Kc*;Kdn844E5 zC^{aNopQ2fBMPu`5zlk;N-lp}!UEb-HQ?$q=0)&Hm;Bm!Z1dk^qb`}Vt9!x0uAZLb z7Kq|!56@sC^?4>Bd=Yc1gt2t->*Wx(M3M=`uXmu*nb)=7E!fvPYFlzz6%rw(TqHk_ zxuumxIS!!;y^4sx!RgO+TF}LEg=MM%PU^iuap0N!Bp^dPJkRP&lk7+&j)VcwT;;@c zD@uGihxk|wDw@I}qG|ZK;HO8fUwjl7G^`G!%5_$cBuHxX;r_0G;>B2^mq-}aua`sa zeAMJk$Q_U*S8V@skmsby;89N2J~T&B+iEEFl_s^R9~E(K>hmM4Q0WTWOuIj7aS7ka zmYWW&`hIEmyO`tAKp3j{-s8vKXc%Z8mzBPlHU%HB+=+B~xe+p_hwSxm5z5O|zs`mZKPtJGvV-((Ms`PL>23n) z>?3GJKltrNo%gFujHW5XV6HozTA4#A~k$ahOMC_aqu$C0H)q)|fM)HJ7B!Q9)Q5ujO_;a^0!IwBjUM?u#{s)eP z;(+%z2W&wt??XTzo=;EDjc$?2*C17xJ-35fVXezuqAKEau+UD(qTIbFeKF6Fnm`S( ztov-gqLy`gk|C=9hElq$qt=eN6 zBbE0VvDueUAc>RO$wAu;3rF^Dlnj__0HAj zfXJ!K+zfLEJX+8Vu_L=TXbxMm@sb2Yz> z^d{w~&#iKSP$cPELPrL;Wi*xT2WD%Y-^m6*HCxpl22 zzIBqby!$a5^9uPA={vuC95c{9iDci33sm9^?|lg+;;SnV<}rwK4Uwqc?JQ)(FPlE86yLS< z#2pW(3j|Mc0~;9>ea0;RPr}Wz-B+X(0m;(;?9)# z+h>%8h6j4upP%O+%}`;@l_4XC6h5+xvkcepebUa5ptB#3D%zMNjFVL~dx4mA*lW7M z^dT9H$ldoZAF`7a^L=ITUhL@Q-CKa|8!B;ykGdrDSoUk{koNqfZ5BUtNVs)^Fffsg ztbZqe-l=vcPGoogPn>IkpLG(ldNMJe?JZFh(S#k9Nm4Dzy-gQ%3a+m2{Oy&xs791w zvu?ia+w+opLd$Az(ctPF7d17TOmxdU#tGAC_W07~K$hcuhnZlbyK2Bre%R&iGDm-J zSumj2GrJcpJ^tq1vOqG7B>J=?AT1sdl*oYmYTx5vkjy>17PuHh2xL$ZfL&n7?h;#k z_ZCq2wq<147av64K+l-_{CO*p9F|H;O56Cv>TYxvraQEqOmDyH7Eq9Dku7KB-LtCM zD7$s%^f~HxxN_k8?v~7z0nDPEgw#Y&h1o}*=KVI|q!z&gCw^2UPfXV?^+rb~yAWR( zGrasX(_z-a03Rv5OW8j@qG#BxS!6(o$>L^gYOP&37hd=rLAwV9q8_x|ouf`>mE+_V zOg-0DX?SiyfgQ`25)2&@z9An3bnP5-f)gKHXj%FQc@!X(b2qz~<6CRpeRD7tH~ql< zeRzzr@YvF0ZC~%05$r*waT3>}XB=q;A~hmDx~Rue+HF~TnD1u2UU@%dZ5hfkgIHn% z2?5_AP|sNQi;C&$MMkzbp6an6pR6w}=6K47I8LPik-}Ov_r%rnI*r0zRgo=XSdK{@ zDVzo?>vb-x@lxz%&)d~1SFUf8yP24AmCh3{Ea%@%JQx3#?_!6gj6K){F$MYeZ))m= z=Be%Ad(m%ws=KDUXLozjt`8PK-O!N9U(TF zWlV{$u67X0f9fpx#`oDMI(v2z4iXDuAX^3A)#FohTi4OQg3#0eW*hpI>*QE;`U;8C zgs7=u?z^|B>%89{*9@vNG#x;U+)d2=2Tl$6H&7|MvKUa8Idj>|Eqx&Jc3VKZ9VNni ze8C`70Y5IlITzkmSl7liD!=pHX5s&u4a(kouTlxO;4;{^(8TdIK2f$8~L(R~iCFyqMiJ05Yv+@S7SK%66f3%3!jChg

Sqycw4KOaU5O4noIcof)Cqc;3wWAxSqZgC$G)PXFB3!%apmGe3l6!*pNp4&JXmeU@_Pt+f>U$1U;6m@yc}$+4 zELpa!G%xvVm9K_gRf#h$F0MM88VVS|_Yi_BsNeG)b<8<*$RZM1B=p$g|y~?KM-#)w!UQ%%dqY(*>S&1InK_- z=OWqh@OfveHfYHLzfRB=vw+0n!Rh5)DdY}p-@g434Y!jV>dd+}2i5+caj8$O?*r|H zH#kN5@rKqVjm~G>8&x*>i^7kHu2MFMNyZllcUAjilS|&1;^!p_^`nV?62W4dKWI;)WD~e`Ol_r}&8Qayt>K0g{?iUxcRWSbtF??Pu zp}8g~IVbRJ)VQgiq-61f$EF{jr-zG8&l(gpMPAv;%0dCl0J$G8 zW!#v`oucXlp7;*ajdKu=qg#wioQzvx&cCvX+1L1lghsb3j`18k(ziOhM5ly9-no_y zDXnjXiF-)2vIX?f11o;M@~5AU0?Ot*G%Xu^azt`#>lJu__+GeS8y{!ywCvZ9M{)Xy z3x9tg=}L23E4i}(_o$Mg!_CoAH(vbpR}0R=7SzAKhGS}Qz_S;qqy_rVhsbSm-P_UF zYIzdWR`etmcw?Ag^YD7xZp#}krapS$LCgA{((K&C_3y~SwNN9dK`c55vcnm-vx&H@ z9~Ye}pzt1)8ST4tsn-%*L-Av+Z@cOqt!w2v9drEs-kiZ6n4TO&*^-CQeyCGR!^Lb% zg}wN38-fsG1rm=f_ygSGlznV?rIcpbZ8yT0sOeq7mKFUiZx{29l_3?ne;D}&!Mo;) zl1$cv!9jWh0rH(@xY4|}lN`J!>!c5m51ycbTFg;_5@^VEyM?v=?XQRDpJWqT(|Ppj ziO#3bf?l~c`|Og@qDjL>y=M?DZ^qMppTxuCsbbD2=;4HpEa8xf-a*;%QJP}mM5pcD z92I-l$sLcEV4KHRwuOj90B`8W$y@9>aVDnldxP_W@jeb;T7rjit$?`TISD6VCyL8o z-&GB@iI4Ix9hJrzXqm?bumEj;aiHP_a#vX#sTQNOiT_C{q}ePYsx-nuPk>#k&V8wu zC}O?0>)P&Al;2O;8%P};tariL4)u^vwv;3T0I>{|{M5T}#H5TD$%)~PyOh{b9K8nF zC*Im8cyXsctII}X2ZWkd$f>BnI!U^||5h#?;)YEJ)G{ zl?BY8=>iH|f?L3>@4s|t^>NUL3C4O$D64UfYYWIV;UVWvVj+8|2V{z*JK{~-aBdKl z_#vT0)KJc$&xwx8uTbaQb6Ak~;z?!+pGEc!a|xby@Bx;ZXnb5O>Y&`DOXx6E%ItyA zVF)I&Gfvd&vuId%ss*-`Z(DJK%uM$}>vFDK^CP1GEO|9HmJj@K(hnt{l|E#DmxQ3W zzEr~4Ip02*fbS?WDeb!kct1RA&u0&0g~)K+WCj7DPBHFKX}^uvR%|pk=vXGB7kx#- z!PRdgEH!1%vtaHMB{9Uz4qVM^!?N=Sd126gvwbTT!xn!Gg@_+>OOMbz_Eo(DB@9-q6QqRFA{mxe;kru|Kjo^HrwnPdMH)HEHrrv@%(vc)`@j&Dw?w&824m+#_9;ll;6$Qo(81}ksIcC&!o4FhukQ~c zat@LY4h>MI$YU3oopZYSW`NTZKzI^~z#g<@L!Io*TfrpbaqNKRTX%bQ!3a_H=DW4S z21DDklY3p>)5>JH<6YraLLv3t9YUhCZ;>Uk6c)j(#l<PUJRDcsnQnRx!rQcBt>&?l($IS8lw2}3nSOgA`6%?77eBYG} zi8eGt+)TQO=Y&I3u3~|A!d%NGwJrX`JuayXEj#Ts7kKBFLk^5jiSk4?nc_Jzn2o*- z=f$%)=e?g_y%iRal=U&8={2Tr&zWo7^y{xLhBg1d#GL6Z8jd=f=6w<3uKroAT1~#u zn?qunYshC&sZ?pzy{`@^pQ2}V6SFGb#zsUlG~}u(c&|n#VD`hvF*L#xIk%x_-A-cD zzHir<6a6k_zij@<$G2>#-$lQU2J;b4hsVhME^~zqOjrXA!Yk8QlvDa-)aJ*>-o5o# zpS$Mz!pl8)@?;mp3U-2-^z1GV3F&nt?BVe+_t@VvvaH6Yc4CkbvGCTf-~9-p-OK~t zg$`-f@<3mGjvlERdSL*cpTkfu|F8zXy_dk8g72lWGLYqUV@P?!v+7__*c#9YR}D4M z%~G&B(GRxL`5`UATR~cueqC^(q+xP^lh3d$yE6yYN}c0)gfmq3mr|_p=5|SwN~fRL zoXL~F?Aqw=G53%#*1{PiKxo^jFwIK$%Cgk{!s^t)%MUe0AGY%dl#8qX)(y~+IgYjC ztoxvXn!j&aHahy8GtiFRgz^fU$!d;nl{`!w$cjN?&d|l_2)A6elGNr%Pm&_hF+0>m4d4JIx!j zxBa}5YN6{7XK3mRmnCl=bU)9tk_VX?%aY?i=hFV zopc7hbpsOfJM-*)%1lYghj*~isKd5$*PDx=}tYDMxaLx>jf zkKj*ZoBhuD_0~BC8^dQG{y)}V?#fEeV_`#`UO%d@9#1m!#RVq;ya|5B5yL%dx~W`k z>MM;1K7|x|j8fjWMzR8&4>3`RKMQ%Qzr z%z5h3I=k=&a-b_{EH9$LSx)~=KMoEE+9@a3ICq4-8H+v8D7v$!f(86=b~z7icF$2W zy77RqQ9{E8N+k=8nlRKj7Kmuai?Jh=KEyuW22O1H?E>=jwGe%RDY8R}Z&HF*F`izr z%-m3S2RMRIH*izh9aHOOrJWbBNVXxyq>IwioIQI_XSZPE4%j@oLm5r*L|2KQ+OQmv zPd@Il>7GaHb?+R|b?O|F=^3C8YXflUL9 znh~j12G!!gi?Ycon6>W2or~mNJ5@Dp+}Ih8Wf!H9bxEIoWOW}InZ;)q2PSrRH#6o1 z@@{`b6R1VXOWnRPd|P4$&zBP@$!~rmk8Q~z_ujBlXVAjIHUr*ImMyVSIj%GxRMVu3 z-vv-cu(;GHwIEffaf?eBDoH(6tTv-vn1>F?-rF&@{{D1hwYs14hE~eS3PM5()q>f> zNlIli0TuL^PyOOht}{{vHEE1TxFTneycZmqLR&NW1~zeX6cAtEPIQ&iYQbBVQt2;F zg>&KxhY{eOka+gz^d|Z>HJvdx__<4n)gH(lnKTF?dJXb}F>SCvp8h=gDkN;;f(=Vk z-qX+<)0VB#;l3c)1~SF0gaU*z5tS)V8OW|Mn05PTvS0st8y6Qw=Z*Rmgdo|8ELUNo zBYnFzfoL2C{ndv&P&6e>NiVc|k0Dh~c76dq!YzHUG8lQW!rN|PwadWxHGUSx)J0kJ zMs63$3^7OfK0WbL3B$`?q{Oh~TrSO(NQA|jmn~WnH{Zp&eTyoQKOO+gYgNb|iiw4f zhsO4Sr;lR1VFaqfxnj`*OU5)?m7&|@CoKx7k`d3O^~Q9W3bVYR{d z5tv!tJM`U&k4=%@D-&v`o^Jbr!8q{H-zr+3>LRY0<=WtN?VzCZzupZPllh|daaCjy z4P3>wnN8}VK%i0=Pef)0?%`E}I+MREALAwJjFk@y`ZqukUUBnaBzE&Uwj=BvzP#eQ zAe%1eiw6Npu##y?6#BOL(R?#wN_MhNWa_IWhumQRjq94t!x1v0;08$VvwzqzL;Go+ z-ixE&zy3b|EGdya4ugQLD6bEKaILrXK5U&QWQFx=Bn;*9a7Z`$H;ZjzG~mxTw<7(kY*}=j-@p)eJfK8j%C(&|?WG=P~Y}C@#Np)yH{+ zF_X}OM(*1WB`ml?{274WIxp~J&6VQls9Le&7n#>R7pZo}Ugotm2tEJecpb*#ruP)G zdyn*{@td;xb8P)4I=0FLmCKYcoF$-Eba^_8orAAslV*I-E}_VKYRQ5Y5EJU_1Ntt_ zk8R~k{d=*M<51%20OfadO=oyX=0f!Uw7qX*DybHWUl2_bDtsDvs54A?3{Sf$rA@gr z?&#YKr$tT_yYQ-JX$T+ylpeYa1}Gj)d zw9MGCW(5gQCb0QyIQahR^y+?Pb$LKzUc<{_It@u8ZeSp>v{|bXo-$i`(o%sqY2|7N zXjCk^Bs!B#k;ka)R~yNU_wc=?o$K9Q@^MYZcB|q>1!Fjp6)=8?o*lfAt7TWLagN}J zZ_@s?f6Szcgt& zv(%OnHxSyds9TiJ?kAT}IY;oechGr}J?nGl5AQCSOsX59N|Y8KEXJ)U&&?6c4yWX|#iwMYq9qXv&sHrFw4#_Q%F8jYU@6FK zLGC#mM$aFHPJNu}lXeZ`1)~YEazAu?;d9^mVIhA@?ShYP?B6WgVF1q)5>&HMu$ka@ zMg6*LG)QoS2-BrLq>v^{HVq!42ZR#)MSj45D39+f3oSXsv<|OH;Tvwa8A>f$&6dDe z@53p-3&-rjYROU$D*`YL1W0uF)d0L6a{lJS75oD%@KC`&;@sFM-_MpliktnNgfdiET8SQa*M@9xM(qUkVDhHr2a1u1XVPz0^ZeUD=E$d!#-8nb2!exKF+MV>mIabl zihO{kKY7^+`A5dB(cnXe&n+kveL_(z?RVbru)9;YlyjLk$GFaiS zl0{4IA6KiouP>(xPgTCg!iRd=Vu0Z0b)vK7InAfbx=kf$I2p?TACkYT8#;U-sPGr1 zDaZf-r8u_S=ozhI2;{pUHFZ7GW{Frgi$uafbVU6w!567ATJ$-jn(pgr1f2w=0C{Un zEhr$7nEoui1JAA-x)aMtCU(en@&pZ4fOWmPrF%8xWl1A8e_(j#T>J(^7zf-(i&Ir= zjT@W4e0V@7+8H}4wdcdP%GgEG7AU4_mzXGN{y{8vXT9MxTZv#bBjKqrAvSt;V2%zb z_|)%Dn>$nfka?OcIj9b|*;jRD)~!}bqU6B^$FnZ!Ums;}L2NAZBZ_;{^Qr0_?J9#L zE617J34(&|+-f6J;)n<6p4FFph+d-%oBj0aLw)J=UHN|B>o>ZGF$o6e{xStzj;Ri% z&yNwwzV8T{eCyC@UU598u_x72dcI%1{e{xa0vCn=EJ)CTX_Dt;IA%j$W5-stNt;~| zmbafsZNu~bTB?S7anGhpSkMI9!nJrVi!{#z>1V&m(A03s7f|yqdGMZXriP%1pw>i1 zolC%GVABrrQwIy?P-id?e_`>%*Q#2GwP%HDK8xeA>5~|rl_)ZUrzk!lqi1)N14Kdj z9S;OdK9_9~Ql_VM6YAT(x3p;vUgG@yv1W>JG()h(Av~MsqU}SrZ!!Z z41o5O0D^=}uG@e{7URkY|Gldz30lOM@PBGMA$Z37jZC>WWLz<6B3cQVqrq5>o8ajW zU=I&cBlq#5lOs0ga!}}!#b{Cra|XhREH|De03p8;kE|8}%B#YH@_?dM0g}5CB@U!Y z$}{;giQXdKb$&^iU<26D(y&fgKx=Fb_1)!7dQZB$uc0&AM8DK7US{H{RS~SD3EYaP zec7g&E<;qxhFyI#QV{_mvS-aodHrvcy=tq5y3tgGU!Gj5aVP!Zpm}k|>`~lIb6LzNJ)~xK z_eeYI?X}AXDnJsCn^CiKUt2XO0g>Vsf~tm40Tw)OzeS6*1DPVM%o2fHx(Eff% z;T_oG*3__l7MC|1{}vL)a@E2iACP;4tb{{aLh)I#7Vr;mfy)~2)P<+w#Ok;AUkqHrYApD@^FI}Yso=pS~r zq(XWO)SKMrVE^>So*OFe$TBRwT6_j8w@932_2yHf~#Ms1^z(CWG7 zd8fTf{^Y|?tyZPF`?!vL(UVZ$4=DW{)@wXu?3r-Kn3Go^SksCZl_mZ~Ah;p^4PHE_ z>$EtNbwH`8ymA+_zBtN=OL4fgQTHd$(1Ri(owR?^(@ZoKr@#oBA@qdyMr85T>`$J} z@_cfx`expgd`B7uK5-<8Kdh|6W%wYjvIjVLi=hKrY3QBwspOlA&*}a>`I7)549-=GqqMyOMa26kUGsgQu=~022Rb=+u-Kn))TybNpI1BQ*3Urm zt3dLtLWLmipM9)suv2!d0YZXFS)DR1`04UlKbk%!)b8@ZdRJ z`zG*IYjSd_my8%B^;{J$UGqJVlw5(y*HFms&#mbrQ{K;50w#`Bi-#r+`WObZZnxS- zIBmY|UAn(E$_{~8@mQuXXlBL)t5Sk4ey9S7xj&L$o^>~QQAiyPo}#t&dsS8GlL;O9 zk(q{9m3CQmYWh}&t!A11IiDHQPZxvkUY$5sE7dQ~P^C(;KBA*%@2FsP{ObV$O9bV` zN62Rd+$$lz6z6Q;oLPySN~GY{Y=0NxN3K(ynC70_z{(R?%-H27(HFLX>>JAFR`ftu zYJQUb3qEp|{05h$xS|eJhyiA1*{ZO1+iphtj=$cVEJ&$r0ubWhIwsGRGG!-A{Zys4 z5{95pin|+WFEdu-Oi76=js@DmLi@PR$mSN8%1eYZv665B-*U0nISa!s*n>Sm(TzgS z$0v4wBj+e78_u8|mDGbT(f9>2581SEsBb{e@ff{((|%inVgs)5tjm*xM)g6BO4HJS*g3P+d%$_F zX-IO7+fE%#2puVIgfzb7T8SBC$!Fao^E#I)s9LaiFsHAqIte?HQOKF4WfZyb7Ql_A z*vLa&S-)#}>86zq5~=dR<9VOp>3aNK@ll3B)}JWSIn)fkABWxvtKzYXcDSard)}fm zTV9xQTPGy1q6-*vUy`4pa$^;!0mY{}T30F)lWI)@WV%ebVh7C8c#?hf5h$ya?xW7E zXY3q7t82S{JVg0&*&3uKW(+e_g(v(6NhnZ zBiNkkie%lXKTT()8rwn}{j{wRA9k@Bmg8|)DOeum&|=<*zT>a&9wJY3zORw#b^4DL z)Pf@b&zlY@c(rgc^~9IsEhb4koZsI6&OzSw!!=r&x<}f#j z!#3Bf5{~Lk2CE7d`vXJz+Nj|b)9ax!zeWjY4~UtH%;UYOhkb}4t2LE^{BRg;ua}ji za5NP640esL?)~zvWdxSI4b<>Ku}m${k^&MG2k>(Tz~AWksS8c-O2t36yO<)&08YA& zAV_VVz@r$45%gsHWpJ=6zQp5RxMio;V>$!5=L!V7*QmrvC;aa9OZL(PZu2p2kY%e` z@8*%)e`{}RyXwpLhf1PG-H3NPwbyTg^fy zTM=xd_8zv;rOF-8)ME!7s4VEDf@Dv1 int: + return self._x + + @property + def y(self) -> int: + return self._y + + @property + def is_wall(self) -> bool: + return self._wall + + @is_wall.setter + def is_wall(self, v: bool): + self._wall = v + + @property + def is_start(self) -> bool: + return self._start + + @is_start.setter + def is_start(self, v: bool): + self._start = v + + @property + def is_exit(self) -> bool: + return self._exit + + @is_exit.setter + def is_exit(self, v: bool): + self._exit = v + + def passable(self) -> bool: + return not self._wall + + def __hash__(self): + return hash((self._x, self._y)) + + def __eq__(self, other): + if not isinstance(other, Tile): + return False + return self._x == other._x and self._y == other._y + + +class Maze: + def __init__(self, w: int, h: int): + self._w = w + self._h = h + self._cells = [[Tile(x, y) for x in range(w)] for y in range(h)] + self._start: Optional[Tile] = None + self._exit: Optional[Tile] = None + + @property + def width(self) -> int: + return self._w + + @property + def height(self) -> int: + return self._h + + @property + def start(self) -> Optional[Tile]: + return self._start + + @property + def exit(self) -> Optional[Tile]: + return self._exit + + def get_cell(self, x: int, y: int) -> Optional[Tile]: + if 0 <= x < self._w and 0 <= y < self._h: + return self._cells[y][x] + return None + + def set_cell(self, x: int, y: int, kind: str): + c = self.get_cell(x, y) + if not c: + return + if kind == 'wall': + c.is_wall = True + elif kind == 'start': + if self._start: + self._start.is_start = False + c.is_start = True + c.is_wall = False + self._start = c + elif kind == 'exit': + if self._exit: + self._exit.is_exit = False + c.is_exit = True + c.is_wall = False + self._exit = c + elif kind == 'path': + c.is_wall = False + + def neighbours(self, cell: Tile) -> List[Tile]: + result = [] + for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]: + nx, ny = cell.x + dx, cell.y + dy + nb = self.get_cell(nx, ny) + if nb and nb.passable(): + result.append(nb) + return result + + +class MazeLoader(ABC): + @abstractmethod + def load(self, filename: str) -> Maze: + pass + + +class TextMazeLoader(MazeLoader): + def load(self, filename: str) -> Maze: + with open(filename, 'r', encoding='utf-8') as f: + lines = [line.rstrip('\n') for line in f.readlines()] + + h = len(lines) + w = max(len(line) for line in lines) if h else 0 + + start_count = 0 + exit_count = 0 + maze = Maze(w, h) + + for y, line in enumerate(lines): + for x, ch in enumerate(line): + if ch == '#': + maze.set_cell(x, y, 'wall') + elif ch == 'S': + maze.set_cell(x, y, 'start') + start_count += 1 + elif ch == 'E': + maze.set_cell(x, y, 'exit') + exit_count += 1 + else: + maze.set_cell(x, y, 'path') + + if start_count != 1 or exit_count != 1: + raise ValueError(f"Maze must have one S and one E. Found: S={start_count}, E={exit_count}") + + return maze + + +class PathFinder(ABC): + def __init__(self): + self._visited = 0 + + @abstractmethod + def find(self, maze: Maze, start: Tile, goal: Tile) -> List[Tile]: + pass + + def _reconstruct(self, parent: Dict[Tile, Optional[Tile]], start: Tile, goal: Tile) -> List[Tile]: + path = [] + current = goal + while current is not None: + path.append(current) + current = parent.get(current) + path.reverse() + return path if path and path[0] == start else [] + + @property + def visited_count(self) -> int: + return self._visited + + +class BFS(PathFinder): + def find(self, maze: Maze, start: Tile, goal: Tile) -> List[Tile]: + queue = deque([start]) + parent = {start: None} + visited = {start} + + while queue: + current = queue.popleft() + + if current == goal: + self._visited = len(visited) + return self._reconstruct(parent, start, goal) + + for neighbor in maze.neighbours(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + queue.append(neighbor) + + self._visited = len(visited) + return [] + + +class DFS(PathFinder): + def find(self, maze: Maze, start: Tile, goal: Tile) -> List[Tile]: + stack = [start] + parent = {start: None} + visited = {start} + + while stack: + current = stack.pop() + + if current == goal: + self._visited = len(visited) + return self._reconstruct(parent, start, goal) + + for neighbor in maze.neighbours(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + stack.append(neighbor) + + self._visited = len(visited) + return [] + + +class AStar(PathFinder): + def _heuristic(self, cell: Tile, goal: Tile) -> int: + return abs(cell.x - goal.x) + abs(cell.y - goal.y) + + def find(self, maze: Maze, start: Tile, goal: Tile) -> List[Tile]: + heap = [] + counter = 0 + start_f = self._heuristic(start, goal) + heapq.heappush(heap, (start_f, counter, start)) + counter += 1 + + parent = {} + g_score = {start: 0} + f_score = {start: start_f} + visited = set() + + while heap: + current_f, _, current = heapq.heappop(heap) + visited.add(current) + + if current == goal: + self._visited = len(visited) + return self._reconstruct(parent, start, goal) + + if current_f > f_score.get(current, float('inf')): + continue + + for neighbor in maze.neighbours(current): + tentative_g = g_score[current] + 1 + + if tentative_g < g_score.get(neighbor, float('inf')): + parent[neighbor] = current + g_score[neighbor] = tentative_g + new_f = tentative_g + self._heuristic(neighbor, goal) + f_score[neighbor] = new_f + heapq.heappush(heap, (new_f, counter, neighbor)) + counter += 1 + + self._visited = len(visited) + return [] + + +class MazeSolver(Observable): + def __init__(self, maze: Maze): + super().__init__() + self._maze = maze + self._algorithm: Optional[PathFinder] = None + + def set_algorithm(self, algorithm: PathFinder): + self._algorithm = algorithm + + def solve(self) -> Optional[Dict[str, Any]]: + if not self._algorithm: + raise ValueError("Algorithm not set") + + start_time = time.perf_counter() + path = self._algorithm.find(self._maze, self._maze.start, self._maze.exit) + end_time = time.perf_counter() + + elapsed_ms = (end_time - start_time) * 1000 + + return { + 'time_ms': elapsed_ms, + 'visited': self._algorithm.visited_count, + 'path_length': len(path), + 'path': path + } + + +class Command(ABC): + @abstractmethod + def execute(self) -> bool: + pass + + @abstractmethod + def undo(self) -> bool: + pass + + +class MoveCommand(Command): + def __init__(self, player: 'Player', dx: int, dy: int, maze: Maze): + self._player = player + self._dx = dx + self._dy = dy + self._maze = maze + self._executed = False + + def execute(self) -> bool: + new_x = self._player.position.x + self._dx + new_y = self._player.position.y + self._dy + target = self._maze.get_cell(new_x, new_y) + + if target and target.passable(): + self._player.move_to(target) + self._executed = True + return True + return False + + def undo(self) -> bool: + if self._executed: + self._player.undo() + self._executed = False + return True + return False + + +class Player: + def __init__(self, start_tile: Tile): + self._position = start_tile + self._previous = None + + @property + def position(self) -> Tile: + return self._position + + def move_to(self, tile: Tile): + self._previous = self._position + self._position = tile + + def undo(self): + if self._previous: + self._position, self._previous = self._previous, None + + +class ConsoleView(Observer): + def __init__(self, maze: Maze, player: Optional[Player] = None): + self._maze = maze + self._player = player + self._current_path: List[Tile] = [] + + def update(self, event: str, data: Any = None): + if event == "solving_finished": + self._current_path = data.get('path', []) + self._display_solution(data) + + def _display_solution(self, stats: Dict): + os.system('cls' if os.name == 'nt' else 'clear') + print("=" * (self._maze.width * 2 + 4)) + print("MAZE SOLUTION") + print("=" * (self._maze.width * 2 + 4)) + + for y in range(self._maze.height): + print(" ", end='') + for x in range(self._maze.width): + cell = self._maze.get_cell(x, y) + if cell == self._maze.start: + print('S', end=' ') + elif cell == self._maze.exit: + print('E', end=' ') + elif cell.is_wall: + print('#', end=' ') + elif self._current_path and cell in self._current_path: + print('●', end=' ') + else: + print('.', end=' ') + print() + + print("=" * (self._maze.width * 2 + 4)) + print(f"Time: {stats['time_ms']:.3f} ms") + print(f"Visited: {stats['visited']}") + print(f"Path length: {stats['path_length']}") + + def display_maze(self): + os.system('cls' if os.name == 'nt' else 'clear') + print("=" * (self._maze.width * 2 + 4)) + print("MAZE") + print("=" * (self._maze.width * 2 + 4)) + + for y in range(self._maze.height): + print(" ", end='') + for x in range(self._maze.width): + cell = self._maze.get_cell(x, y) + if self._player and cell == self._player.position: + print('P', end=' ') + elif cell == self._maze.start: + print('S', end=' ') + elif cell == self._maze.exit: + print('E', end=' ') + elif cell.is_wall: + print('#', end=' ') + else: + print('.', end=' ') + print() + + print("=" * (self._maze.width * 2 + 4)) + print("S - start E - exit # - wall . - path P - player") + + +def interactive_mode(maze: Maze): + player = Player(maze.start) + view = ConsoleView(maze, player) + view.display_maze() + + solver = MazeSolver(maze) + solver.attach(view) + + commands_history: List[Command] = [] + + print("\nControls:") + print("H (←) J (↓) K (↑) L (→) - move") + print("U - undo") + print("B - BFS") + print("D - DFS") + print("A - A*") + print("Q - quit") + print("\n" + "=" * 50) + + while True: + cmd = input("\n> ").lower().strip() + + if cmd == 'q': + break + + elif cmd == 'b': + solver.set_algorithm(BFS()) + result = solver.solve() + if result: + print(f"BFS: {result['time_ms']:.3f} ms, visited={result['visited']}, length={result['path_length']}") + + elif cmd == 'd': + solver.set_algorithm(DFS()) + result = solver.solve() + if result: + print(f"DFS: {result['time_ms']:.3f} ms, visited={result['visited']}, length={result['path_length']}") + + elif cmd == 'a': + solver.set_algorithm(AStar()) + result = solver.solve() + if result: + print(f"A*: {result['time_ms']:.3f} ms, visited={result['visited']}, length={result['path_length']}") + + elif cmd in ['h', 'j', 'k', 'l']: + dir_map = {'h': (-1, 0), 'l': (1, 0), 'k': (0, -1), 'j': (0, 1)} + dx, dy = dir_map[cmd] + move = MoveCommand(player, dx, dy, maze) + + if move.execute(): + commands_history.append(move) + view.display_maze() + + if player.position == maze.exit: + print("\n*** YOU ESCAPED! ***") + print(f"Total moves: {len(commands_history)}") + break + else: + print("Blocked!") + + elif cmd == 'u': + if commands_history: + last_command = commands_history.pop() + last_command.undo() + view.display_maze() + print("Undo successful") + else: + print("Nothing to undo") + + else: + print("Unknown command") + + +def main(): + if len(sys.argv) > 1 and sys.argv[1] == 'experiment': + import subprocess + subprocess.run([sys.executable, 'plots.py']) + return + + loader = TextMazeLoader() + + + maze_file = os.path.join(DATA_PATH, "maze1.txt") + + if not os.path.exists(maze_file): + print(f"ERROR: Maze file not found: {maze_file}") + print(f"Please create maze1.txt in: {DATA_PATH}") + return + + maze = loader.load(maze_file) + interactive_mode(maze) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ShulpinIN/maze_lab2/plots.py b/ShulpinIN/maze_lab2/plots.py new file mode 100644 index 0000000..c4f4dfa --- /dev/null +++ b/ShulpinIN/maze_lab2/plots.py @@ -0,0 +1,580 @@ +import csv +import time +import os +import matplotlib.pyplot as plt +import numpy as np +from collections import deque +import heapq + +from maze import DATA_PATH + + + +class Tile: + def __init__(self, x: int, y: int): + self._x = x + self._y = y + self._wall = False + self._start = False + self._exit = False + + @property + def x(self) -> int: + return self._x + + @property + def y(self) -> int: + return self._y + + @property + def is_wall(self) -> bool: + return self._wall + + @is_wall.setter + def is_wall(self, v: bool): + self._wall = v + + @property + def is_start(self) -> bool: + return self._start + + @is_start.setter + def is_start(self, v: bool): + self._start = v + + @property + def is_exit(self) -> bool: + return self._exit + + @is_exit.setter + def is_exit(self, v: bool): + self._exit = v + + def passable(self) -> bool: + return not self._wall + + def __hash__(self): + return hash((self._x, self._y)) + + def __eq__(self, other): + if not isinstance(other, Tile): + return False + return self._x == other._x and self._y == other._y + + +class Maze: + def __init__(self, w: int, h: int): + self._w = w + self._h = h + self._cells = [[Tile(x, y) for x in range(w)] for y in range(h)] + self._start = None + self._exit = None + + @property + def width(self) -> int: + return self._w + + @property + def height(self) -> int: + return self._h + + @property + def start(self): + return self._start + + @property + def exit(self): + return self._exit + + def get_cell(self, x: int, y: int): + if 0 <= x < self._w and 0 <= y < self._h: + return self._cells[y][x] + return None + + def set_cell(self, x: int, y: int, kind: str): + c = self.get_cell(x, y) + if not c: + return + if kind == 'wall': + c.is_wall = True + elif kind == 'start': + if self._start: + self._start.is_start = False + c.is_start = True + c.is_wall = False + self._start = c + elif kind == 'exit': + if self._exit: + self._exit.is_exit = False + c.is_exit = True + c.is_wall = False + self._exit = c + elif kind == 'path': + c.is_wall = False + + def neighbours(self, cell): + result = [] + for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]: + nx, ny = cell.x + dx, cell.y + dy + nb = self.get_cell(nx, ny) + if nb and nb.passable(): + result.append(nb) + return result + + +class TextMazeLoader: + def load(self, filename: str): + with open(filename, 'r', encoding='utf-8') as f: + lines = [line.rstrip('\n') for line in f.readlines()] + + h = len(lines) + w = max(len(line) for line in lines) if h else 0 + + start_count = 0 + exit_count = 0 + maze = Maze(w, h) + + for y, line in enumerate(lines): + for x, ch in enumerate(line): + if ch == '#': + maze.set_cell(x, y, 'wall') + elif ch == 'S': + maze.set_cell(x, y, 'start') + start_count += 1 + elif ch == 'E': + maze.set_cell(x, y, 'exit') + exit_count += 1 + else: + maze.set_cell(x, y, 'path') + + if start_count != 1 or exit_count != 1: + raise ValueError(f"Maze must have one S and one E. Found: S={start_count}, E={exit_count}") + + return maze + + +class BFS: + def __init__(self): + self._visited = 0 + + def find(self, maze, start, goal): + from collections import deque + queue = deque([start]) + parent = {start: None} + visited = {start} + + while queue: + current = queue.popleft() + + if current == goal: + self._visited = len(visited) + return self._reconstruct(parent, start, goal) + + for neighbor in maze.neighbours(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + queue.append(neighbor) + + self._visited = len(visited) + return [] + + def _reconstruct(self, parent, start, goal): + path = [] + current = goal + while current is not None: + path.append(current) + current = parent.get(current) + path.reverse() + return path if path and path[0] == start else [] + + @property + def visited_count(self): + return self._visited + + +class DFS: + def __init__(self): + self._visited = 0 + + def find(self, maze, start, goal): + stack = [start] + parent = {start: None} + visited = {start} + + while stack: + current = stack.pop() + + if current == goal: + self._visited = len(visited) + return self._reconstruct(parent, start, goal) + + for neighbor in maze.neighbours(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + stack.append(neighbor) + + self._visited = len(visited) + return [] + + def _reconstruct(self, parent, start, goal): + path = [] + current = goal + while current is not None: + path.append(current) + current = parent.get(current) + path.reverse() + return path if path and path[0] == start else [] + + @property + def visited_count(self): + return self._visited + + +class AStar: + def __init__(self): + self._visited = 0 + + def _heuristic(self, cell, goal): + return abs(cell.x - goal.x) + abs(cell.y - goal.y) + + def find(self, maze, start, goal): + import heapq + heap = [] + counter = 0 + start_f = self._heuristic(start, goal) + heapq.heappush(heap, (start_f, counter, start)) + counter += 1 + + parent = {} + g_score = {start: 0} + f_score = {start: start_f} + visited = set() + + while heap: + current_f, _, current = heapq.heappop(heap) + visited.add(current) + + if current == goal: + self._visited = len(visited) + return self._reconstruct(parent, start, goal) + + if current_f > f_score.get(current, float('inf')): + continue + + for neighbor in maze.neighbours(current): + tentative_g = g_score[current] + 1 + + if tentative_g < g_score.get(neighbor, float('inf')): + parent[neighbor] = current + g_score[neighbor] = tentative_g + new_f = tentative_g + self._heuristic(neighbor, goal) + f_score[neighbor] = new_f + heapq.heappush(heap, (new_f, counter, neighbor)) + counter += 1 + + self._visited = len(visited) + return [] + + def _reconstruct(self, parent, start, goal): + path = [] + current = goal + while current is not None: + path.append(current) + current = parent.get(current) + path.reverse() + return path if path and path[0] == start else [] + + @property + def visited_count(self): + return self._visited + + +class MazeSolver: + def __init__(self, maze): + self._maze = maze + self._algorithm = None + + def set_algorithm(self, algorithm): + self._algorithm = algorithm + + def solve(self): + if not self._algorithm: + raise ValueError("Algorithm not set") + + start_time = time.perf_counter() + path = self._algorithm.find(self._maze, self._maze.start, self._maze.exit) + end_time = time.perf_counter() + + elapsed_ms = (end_time - start_time) * 1000 + + return { + 'time_ms': elapsed_ms, + 'visited': self._algorithm.visited_count, + 'path_length': len(path), + 'path': path + } + + + + + +DATA_PATH = r"C:\Users\User\2026-rff_mp\ShulpinIN\maze_lab2\docs\data" + + +class ExperimentRunner: + def __init__(self): + self.algorithms = { + "BFS": BFS(), + "DFS": DFS(), + "A*": AStar() + } + self.loader = TextMazeLoader() + + def run_benchmark(self, maze_file: str, algorithm: str, runs: int = 5): + try: + maze = self.loader.load(maze_file) + except Exception as e: + return None + + total_time = 0.0 + total_visited = 0 + total_length = 0 + successes = 0 + + for _ in range(runs): + solver = MazeSolver(maze) + solver.set_algorithm(self.algorithms[algorithm]) + result = solver.solve() + + if result and result['path_length'] > 0: + total_time += result['time_ms'] + total_visited += result['visited'] + total_length += result['path_length'] + successes += 1 + + if successes == 0: + return None + + return { + 'time_ms': total_time / successes, + 'visited_cells': total_visited / successes, + 'path_length': total_length / successes, + 'success_rate': successes / runs + } + + def run_all_experiments(self, runs: int = 5): + mazes_list = [ + (os.path.join(DATA_PATH, "small.txt"), "Small (10x10)"), + (os.path.join(DATA_PATH, "medium.txt"), "Medium (50x50)"), + (os.path.join(DATA_PATH, "large.txt"), "Large (100x100)"), + (os.path.join(DATA_PATH, "empty.txt"), "Empty"), + (os.path.join(DATA_PATH, "no_exit.txt"), "No exit") + ] + + results = [] + + + print("running experiments") + + print(f"Data path: {DATA_PATH}") + + + for maze_file, maze_name in mazes_list: + if not os.path.exists(maze_file): + print(f"\n[warn] File not found: {maze_file}") + continue + + print(f"\nTesting: {maze_name}") + + for algo_name in self.algorithms.keys(): + stats = self.run_benchmark(maze_file, algo_name, runs) + + if stats: + print( + f" {algo_name}: time={stats['time_ms']:.3f}ms, visited={stats['visited_cells']:.0f}, length={stats['path_length']:.0f}") + results.append({ + 'maze': maze_name, + 'strategy': algo_name, + 'time_ms': stats['time_ms'], + 'visited_cells': stats['visited_cells'], + 'path_length': stats['path_length'], + 'success_rate': stats['success_rate'] + }) + else: + print(f" {algo_name}: no path found") + results.append({ + 'maze': maze_name, + 'strategy': algo_name, + 'time_ms': -1, + 'visited_cells': -1, + 'path_length': -1, + 'success_rate': 0 + }) + + return results + + +def create_visualizations(results): + valid_results = [r for r in results if r['time_ms'] > 0] + if not valid_results: + print("no valid results for visualization") + return + + mazes = sorted(set(r['maze'] for r in valid_results)) + algorithms = ['BFS', 'DFS', 'A*'] + + fig, axes = plt.subplots(1, 3, figsize=(15, 5)) + fig.suptitle('pathfinding algorithms comparison', fontsize=14) + + x = np.arange(len(mazes)) + width = 0.25 + + # Time chart + for i, algo in enumerate(algorithms): + times = [] + for maze in mazes: + val = next((r['time_ms'] for r in valid_results + if r['maze'] == maze and r['strategy'] == algo), 0) + times.append(val) + bars = axes[0].bar(x + i * width, times, width, label=algo, alpha=0.8) + for bar, val in zip(bars, times): + if val > 0: + axes[0].text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.5, + f'{val:.1f}', ha='center', va='bottom', fontsize=7) + + axes[0].set_title('execution Time (ms)') + axes[0].set_ylabel('time (ms)') + axes[0].set_xticks(x + width) + axes[0].set_xticklabels(mazes, rotation=45, ha='right', fontsize=8) + axes[0].legend() + axes[0].grid(alpha=0.3, axis='y') + + # Visited cells chart + for i, algo in enumerate(algorithms): + visited = [] + for maze in mazes: + val = next((r['visited_cells'] for r in valid_results + if r['maze'] == maze and r['strategy'] == algo), 0) + visited.append(val) + bars = axes[1].bar(x + i * width, visited, width, label=algo, alpha=0.8) + for bar, val in zip(bars, visited): + if val > 0: + axes[1].text(bar.get_x() + bar.get_width() / 2, bar.get_height(), + f'{val:.0f}', ha='center', va='bottom', fontsize=7) + + axes[1].set_title('visited Cells') + axes[1].set_ylabel('count') + axes[1].set_xticks(x + width) + axes[1].set_xticklabels(mazes, rotation=45, ha='right', fontsize=8) + axes[1].legend() + axes[1].grid(alpha=0.3, axis='y') + + # Path length chart + for i, algo in enumerate(algorithms): + lengths = [] + for maze in mazes: + val = next((r['path_length'] for r in valid_results + if r['maze'] == maze and r['strategy'] == algo), 0) + lengths.append(val) + bars = axes[2].bar(x + i * width, lengths, width, label=algo, alpha=0.8) + for bar, val in zip(bars, lengths): + if val > 0: + axes[2].text(bar.get_x() + bar.get_width() / 2, bar.get_height(), + f'{val:.0f}', ha='center', va='bottom', fontsize=7) + + axes[2].set_title('path Length') + axes[2].set_ylabel('steps') + axes[2].set_xticks(x + width) + axes[2].set_xticklabels(mazes, rotation=45, ha='right', fontsize=8) + axes[2].legend() + axes[2].grid(alpha=0.3, axis='y') + + plt.tight_layout() + + output_path = os.path.join(DATA_PATH, 'experiment_results.png') + plt.savefig(output_path, dpi=150, bbox_inches='tight') + print(f"\nPlot saved to: {output_path}") + plt.show() + + +def save_results_to_csv(results, filename='experiment_results.csv'): + if not results: + return + + filepath = os.path.join(DATA_PATH, filename) + with open(filepath, 'w', newline='', encoding='utf-8') as f: + fieldnames = ['maze', 'strategy', 'time_ms', 'visited_cells', 'path_length', 'success_rate'] + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(results) + + print(f"Results saved to: {filepath}") + + +def analyze_efficiency(results): + valid_results = [r for r in results if r['time_ms'] > 0] + if not valid_results: + print("no valid results for analysis") + return + + algo_stats = {} + for algo in ['BFS', 'DFS', 'A*']: + algo_data = [r for r in valid_results if r['strategy'] == algo] + if algo_data: + algo_stats[algo] = { + 'avg_time': sum(r['time_ms'] for r in algo_data) / len(algo_data), + 'avg_visited': sum(r['visited_cells'] for r in algo_data) / len(algo_data), + 'avg_length': sum(r['path_length'] for r in algo_data) / len(algo_data) + } + + + print("average values across all mazes") + print(f"{'Algorithm':<12} {'Time (ms)':<15} {'Visited':<15} {'Path length':<15}") + + for algo, stats in algo_stats.items(): + print(f"{algo:<12} {stats['avg_time']:<15.3f} {stats['avg_visited']:<15.1f} {stats['avg_length']:<15.1f}") + + fastest = min(algo_stats.items(), key=lambda x: x[1]['avg_time']) + optimal = min(algo_stats.items(), key=lambda x: x[1]['avg_length']) + efficient = min(algo_stats.items(), key=lambda x: x[1]['avg_visited']) + + print("conclusions:") + print(f" fastest algorithm: {fastest[0]} ({fastest[1]['avg_time']:.3f} ms avg)") + print(f" optimal path: {optimal[0]} ({optimal[1]['avg_length']:.1f} steps avg)") + print(f" most efficient (fewest visits): {efficient[0]} ({efficient[1]['avg_visited']:.0f} cells avg)") + print("=" * 70) + + +def main(): + + + if not os.path.exists(DATA_PATH): + print(f"\nerr: directory not found: {DATA_PATH}") + print("please create the directory and place maze files there.") + print("\nexpected structure:") + print(f" {DATA_PATH}/") + print(" ├── small.txt") + print(" ├── medium.txt") + print(" ├── large.txt") + print(" ├── empty.txt") + print(" └── no_exit.txt") + return + + runner = ExperimentRunner() + results = runner.run_all_experiments(runs=5) + + if not results: + print("\nNo results. Check if maze files exist in:", DATA_PATH) + return + + save_results_to_csv(results) + analyze_efficiency(results) + create_visualizations(results) + + + + +if __name__ == "__main__": + main() \ No newline at end of file From 04657980e7a41e8c7775c63e3832350800accf47 Mon Sep 17 00:00:00 2001 From: ShulpinIN Date: Sun, 24 May 2026 17:22:46 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=BE=D0=B4=20=D0=B8=20=D0=BB=D0=B0=D0=B1=D0=B8?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=BB?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=E2=84=962?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShulpinIN/maze_lab2/docs/data/no_exit.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 ShulpinIN/maze_lab2/docs/data/no_exit.txt diff --git a/ShulpinIN/maze_lab2/docs/data/no_exit.txt b/ShulpinIN/maze_lab2/docs/data/no_exit.txt new file mode 100644 index 0000000..4697881 --- /dev/null +++ b/ShulpinIN/maze_lab2/docs/data/no_exit.txt @@ -0,0 +1,10 @@ +########## +#S # +### ##### +# # # +# # # # ## +# # # +####### # +# # +# ###### # +########## \ No newline at end of file