From 72d2269634fd70ee5db3e233246c94a5e9e794b1 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 00:36:56 +0300 Subject: [PATCH 01/12] implemented linked list --- soninrv/docs/data/lab1/phonebook.py | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 soninrv/docs/data/lab1/phonebook.py diff --git a/soninrv/docs/data/lab1/phonebook.py b/soninrv/docs/data/lab1/phonebook.py new file mode 100644 index 0000000..89243d4 --- /dev/null +++ b/soninrv/docs/data/lab1/phonebook.py @@ -0,0 +1,53 @@ +# 1. СВЯЗНЫЙ СПИСОК +def ll_create_node(name, phone): + return {'name': name, 'phone': phone, 'next': None} + + +def ll_insert(head, name, phone): + """Добавить или обновить запись. Возвращает голову списка.""" + node = head + while node is not None: + if node['name'] == name: + node['phone'] = phone # обновить + return head + node = node['next'] + # Вставка в начало — O(1) + new_node = ll_create_node(name, phone) + new_node['next'] = head + return new_node + + +def ll_find(head, name): + """Вернуть телефон или None.""" + node = head + while node is not None: + if node['name'] == name: + return node['phone'] + node = node['next'] + return None + + +def ll_delete(head, name): + """Удалить узел, вернуть новую голову.""" + if head is None: + return None + if head['name'] == name: + return head['next'] + prev, node = head, head['next'] + while node is not None: + if node['name'] == name: + prev['next'] = node['next'] + return head + prev, node = node, node['next'] + return head + + +def ll_list_all(head): + """Собрать все записи и вернуть отсортированный список (name, phone).""" + result = [] + node = head + while node is not None: + result.append((node['name'], node['phone'])) + node = node['next'] + result.sort(key=lambda x: x[0]) + return result \ No newline at end of file From fb36061d9a6ec6e8b57f9821305ee1aae906dd2d Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 00:42:24 +0300 Subject: [PATCH 02/12] implemented hash table --- soninrv/docs/data/lab1/phonebook.py | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/soninrv/docs/data/lab1/phonebook.py b/soninrv/docs/data/lab1/phonebook.py index 89243d4..ae7e231 100644 --- a/soninrv/docs/data/lab1/phonebook.py +++ b/soninrv/docs/data/lab1/phonebook.py @@ -50,4 +50,42 @@ def ll_list_all(head): result.append((node['name'], node['phone'])) node = node['next'] result.sort(key=lambda x: x[0]) + return result + +# 2. ХЕШ-ТАБЛИЦА (цепочки через связный список) + +HT_SIZE = 1024 # число корзин (степень двойки) + + +def ht_create(size=HT_SIZE): + return [None] * size + + +def _ht_hash(name, size): + h = 5381 + for ch in name: + h = ((h << 5) + h) ^ ord(ch) + return h % size + + +def ht_insert(buckets, name, phone): + idx = _ht_hash(name, len(buckets)) + buckets[idx] = ll_insert(buckets[idx], name, phone) + + +def ht_find(buckets, name): + idx = _ht_hash(name, len(buckets)) + return ll_find(buckets[idx], name) + + +def ht_delete(buckets, name): + idx = _ht_hash(name, len(buckets)) + buckets[idx] = ll_delete(buckets[idx], name) + + +def ht_list_all(buckets): + result = [] + for head in buckets: + result.extend(ll_list_all(head)) + result.sort(key=lambda x: x[0]) return result \ No newline at end of file From 77485a5318a75c716d4616edec5ab30b971e63fc Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 00:44:07 +0300 Subject: [PATCH 03/12] implemented bst --- soninrv/docs/data/lab1/phonebook.py | 73 +++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/soninrv/docs/data/lab1/phonebook.py b/soninrv/docs/data/lab1/phonebook.py index ae7e231..7e9d99d 100644 --- a/soninrv/docs/data/lab1/phonebook.py +++ b/soninrv/docs/data/lab1/phonebook.py @@ -88,4 +88,77 @@ def ht_list_all(buckets): for head in buckets: result.extend(ll_list_all(head)) result.sort(key=lambda x: x[0]) + return result + +# 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST) + +def bst_create_node(name, phone): + return {'name': name, 'phone': phone, 'left': None, 'right': None} + + +def bst_insert(root, name, phone): + """Вставить / обновить. Возвращает корень.""" + if root is None: + return bst_create_node(name, phone) + if name == root['name']: + root['phone'] = phone + elif name < root['name']: + root['left'] = bst_insert(root['left'], name, phone) + else: + root['right'] = bst_insert(root['right'], name, phone) + return root + + +def bst_find(root, name): + """Вернуть телефон или None.""" + while root is not None: + if name == root['name']: + return root['phone'] + elif name < root['name']: + root = root['left'] + else: + root = root['right'] + return None + + +def _bst_min(node): + while node['left'] is not None: + node = node['left'] + return node + + +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'] + if root['right'] is None: + return root['left'] + # Два потомка: заменить минимальным из правого поддерева + successor = _bst_min(root['right']) + root['name'] = successor['name'] + root['phone'] = successor['phone'] + root['right'] = bst_delete(root['right'], successor['name']) + return root + + +def bst_list_all(root): + """Центрированный (in-order) обход → отсортированный список.""" + result = [] + stack = [] + node = root + while stack or node is not None: + while node is not None: + stack.append(node) + node = node['left'] + node = stack.pop() + result.append((node['name'], node['phone'])) + node = node['right'] return result \ No newline at end of file From bf2dc504792d6845cdd9421797f16a588f0b7e60 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 00:51:26 +0300 Subject: [PATCH 04/12] added experiment --- soninrv/docs/data/lab1/phonebook.py | 143 +++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/soninrv/docs/data/lab1/phonebook.py b/soninrv/docs/data/lab1/phonebook.py index 7e9d99d..d33111a 100644 --- a/soninrv/docs/data/lab1/phonebook.py +++ b/soninrv/docs/data/lab1/phonebook.py @@ -161,4 +161,145 @@ def bst_list_all(root): node = stack.pop() result.append((node['name'], node['phone'])) node = node['right'] - return result \ No newline at end of file + return result + +""" +Экспериментальная часть: замер производительности трёх структур данных. +""" + +import time +import csv +import random +import os +import sys + +sys.setrecursionlimit(30000) # BST с отсортированными данными — глубокая рекурсия + + +# ── Параметры ────────────────────────────────────────────────────────────── +N = 10_000 # размер набора +REPEATS = 5 # повторений каждого замера +SEARCH_N = 100 # запросов на поиск (существующих) +SEARCH_MISS = 10 # запросов на поиск (отсутствующих) +DELETE_N = 50 # удалений + +random.seed(42) + +# ── Генерация данных ─────────────────────────────────────────────────────── +records_sorted = [(f"User_{i:05d}", f"+7-000-{i:07d}") for i in range(N)] +records_shuffled = records_sorted[:] +random.shuffle(records_shuffled) + +search_names_hit = [records_sorted[i][0] for i in random.sample(range(N), SEARCH_N)] +search_names_miss = [f"None_{i:04d}" for i in range(SEARCH_MISS)] +search_names = search_names_hit + search_names_miss + +delete_names = [records_sorted[i][0] for i in random.sample(range(N), DELETE_N)] + +# ── Вспомогательные функции ──────────────────────────────────────────────── + +def build_ll(records): + head = None + for name, phone in records: + head = ll_insert(head, name, phone) + return head + +def build_ht(records): + buckets = ht_create() + for name, phone in records: + ht_insert(buckets, name, phone) + return buckets + +def build_bst(records): + root = None + for name, phone in records: + root = bst_insert(root, name, phone) + return root + +STRUCTURES = { + 'LinkedList': { + 'build': build_ll, + 'find': ll_find, + 'delete': lambda ds, name: ll_delete(ds, name), # возвращает новый head + 'list_all': ll_list_all, + 'mutable': False, # ll_delete возвращает новую голову + }, + 'HashTable': { + 'build': build_ht, + 'find': ht_find, + 'delete': lambda ds, name: ht_delete(ds, name), # in-place, returns None + 'list_all': ht_list_all, + 'mutable': True, + }, + 'BST': { + 'build': build_bst, + 'find': bst_find, + 'delete': lambda ds, name: bst_delete(ds, name), # возвращает новый корень + 'list_all': bst_list_all, + 'mutable': False, + }, +} + +MODES = { + 'shuffled': records_shuffled, + 'sorted': records_sorted, +} + +# ── Замер ────────────────────────────────────────────────────────────────── + +def measure(fn, *args, repeats=REPEATS): + times = [] + for _ in range(repeats): + t0 = time.perf_counter() + fn(*args) + times.append(time.perf_counter() - t0) + return times + +rows = [["structure", "mode", "operation", "run", "time_sec"]] + +for struct_name, ops in STRUCTURES.items(): + for mode_name, records in MODES.items(): + print(f" {struct_name} / {mode_name} ...", flush=True) + + # ── А. Вставка ────────────────────────────────────────────────── + insert_times = [] + for run in range(REPEATS): + t0 = time.perf_counter() + ds = ops['build'](records) + insert_times.append(time.perf_counter() - t0) + rows.append([struct_name, mode_name, "insert", run + 1, insert_times[-1]]) + + # Строим структуру один раз для поиска и удаления + ds = ops['build'](records) + + # ── Б. Поиск ──────────────────────────────────────────────────── + def do_search(ds=ds): + for name in search_names: + ops['find'](ds, name) + + search_times = measure(do_search) + for run, t in enumerate(search_times, 1): + rows.append([struct_name, mode_name, "find", run, t]) + + # ── В. Удаление ───────────────────────────────────────────────── + # Удаление изменяет структуру, поэтому каждый раз пересобираем + delete_times = [] + for run in range(REPEATS): + ds2 = ops['build'](records) + t0 = time.perf_counter() + for name in delete_names: + result = ops['delete'](ds2, name) + if result is not None: # ll / bst возвращают новую голову/корень + ds2 = result + delete_times.append(time.perf_counter() - t0) + rows.append([struct_name, mode_name, "delete", run + 1, delete_times[-1]]) + + print(f" insert avg={sum(insert_times)/REPEATS:.4f}s " + f"find avg={sum(search_times)/REPEATS:.4f}s " + f"delete avg={sum(delete_times)/REPEATS:.4f}s") + + +with open("results.csv", "w", newline="", encoding="utf-8") as f: + csv.writer(f).writerows(rows) + +print("\nРезультаты сохранены в docs/data/results.csv") \ No newline at end of file From d5b077f8dd9f1ff1e5c24f17c7c75545a5eeef8a Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 00:55:18 +0300 Subject: [PATCH 05/12] added results.csv --- soninrv/docs/data/lab1/results.csv | 91 ++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 soninrv/docs/data/lab1/results.csv diff --git a/soninrv/docs/data/lab1/results.csv b/soninrv/docs/data/lab1/results.csv new file mode 100644 index 0000000..2a307cb --- /dev/null +++ b/soninrv/docs/data/lab1/results.csv @@ -0,0 +1,91 @@ +structure,mode,operation,run,time_sec +LinkedList,shuffled,insert,1,3.294921400000021 +LinkedList,shuffled,insert,2,2.92912730000171 +LinkedList,shuffled,insert,3,2.8146583999987342 +LinkedList,shuffled,insert,4,2.7935691000020597 +LinkedList,shuffled,insert,5,2.8566659999996773 +LinkedList,shuffled,find,1,0.03453739999895333 +LinkedList,shuffled,find,2,0.03489120000085677 +LinkedList,shuffled,find,3,0.034232199999678414 +LinkedList,shuffled,find,4,0.03294129999994766 +LinkedList,shuffled,find,5,0.03249359999972512 +LinkedList,shuffled,delete,1,0.016195199998037424 +LinkedList,shuffled,delete,2,0.016463700001622783 +LinkedList,shuffled,delete,3,0.016346699998393888 +LinkedList,shuffled,delete,4,0.016296699999656994 +LinkedList,shuffled,delete,5,0.016424599998572376 +LinkedList,sorted,insert,1,2.383058199997322 +LinkedList,sorted,insert,2,2.375423099998443 +LinkedList,sorted,insert,3,2.34873769999831 +LinkedList,sorted,insert,4,2.3596142000023974 +LinkedList,sorted,insert,5,2.3823104000002786 +LinkedList,sorted,find,1,0.027813299999252195 +LinkedList,sorted,find,2,0.02766450000126497 +LinkedList,sorted,find,3,0.027582700000493787 +LinkedList,sorted,find,4,0.02761159999863594 +LinkedList,sorted,find,5,0.02766390000033425 +LinkedList,sorted,delete,1,0.015935499999613967 +LinkedList,sorted,delete,2,0.01771329999974114 +LinkedList,sorted,delete,3,0.016032899999117944 +LinkedList,sorted,delete,4,0.01585219999833498 +LinkedList,sorted,delete,5,0.016385800001444295 +HashTable,shuffled,insert,1,0.06008769999971264 +HashTable,shuffled,insert,2,0.02979799999957322 +HashTable,shuffled,insert,3,0.02958039999793982 +HashTable,shuffled,insert,4,0.03261639999982435 +HashTable,shuffled,insert,5,0.03028959999937797 +HashTable,shuffled,find,1,0.00040919999810284935 +HashTable,shuffled,find,2,0.00025829999867710285 +HashTable,shuffled,find,3,0.000260199998592725 +HashTable,shuffled,find,4,0.00024839999969117343 +HashTable,shuffled,find,5,0.0002446999969833996 +HashTable,shuffled,delete,1,0.0007224000000860542 +HashTable,shuffled,delete,2,0.00018980000095325522 +HashTable,shuffled,delete,3,0.00014259999807109125 +HashTable,shuffled,delete,4,0.00020619999850168824 +HashTable,shuffled,delete,5,0.00014730000111740083 +HashTable,sorted,insert,1,0.02703069999915897 +HashTable,sorted,insert,2,0.0286950000008801 +HashTable,sorted,insert,3,0.029971800002385862 +HashTable,sorted,insert,4,0.028408000001945766 +HashTable,sorted,insert,5,0.028463399998145178 +HashTable,sorted,find,1,0.00038550000317627564 +HashTable,sorted,find,2,0.00026449999859323725 +HashTable,sorted,find,3,0.0002604000001156237 +HashTable,sorted,find,4,0.0002567999981692992 +HashTable,sorted,find,5,0.0002595000005385373 +HashTable,sorted,delete,1,0.00020910000239382498 +HashTable,sorted,delete,2,0.0002086000022245571 +HashTable,sorted,delete,3,0.00015020000137155876 +HashTable,sorted,delete,4,0.0001517000018793624 +HashTable,sorted,delete,5,0.00015150000035646372 +BST,shuffled,insert,1,0.026569400000880705 +BST,shuffled,insert,2,0.028130499998951564 +BST,shuffled,insert,3,0.02583809999850928 +BST,shuffled,insert,4,0.02573110000230372 +BST,shuffled,insert,5,0.02615979999973206 +BST,shuffled,find,1,0.00020509999740170315 +BST,shuffled,find,2,0.00017859999934444204 +BST,shuffled,find,3,0.00017999999909079634 +BST,shuffled,find,4,0.00017889999799081124 +BST,shuffled,find,5,0.00017719999959808774 +BST,shuffled,delete,1,0.00014940000255592167 +BST,shuffled,delete,2,0.0010156000025745016 +BST,shuffled,delete,3,0.000994199999695411 +BST,shuffled,delete,4,0.0011020999991160352 +BST,shuffled,delete,5,0.0011912000009033363 +BST,sorted,insert,1,10.031728599999042 +BST,sorted,insert,2,9.260749099998066 +BST,sorted,insert,3,9.739691700000549 +BST,sorted,insert,4,8.961757199998829 +BST,sorted,insert,5,9.583165900003223 +BST,sorted,find,1,0.041536599997925805 +BST,sorted,find,2,0.04151529999944614 +BST,sorted,find,3,0.04165329999887035 +BST,sorted,find,4,0.04157439999835333 +BST,sorted,find,5,0.0415880999971705 +BST,sorted,delete,1,0.04558349999933853 +BST,sorted,delete,2,0.041408099998079706 +BST,sorted,delete,3,0.041001800000231015 +BST,sorted,delete,4,0.041335800000524614 +BST,sorted,delete,5,0.041272599999501836 From 51f77e7a5aba05c31ce11cb308336d56c3c68db6 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 01:00:45 +0300 Subject: [PATCH 06/12] added plots --- soninrv/docs/data/lab1/plots.py | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 soninrv/docs/data/lab1/plots.py diff --git a/soninrv/docs/data/lab1/plots.py b/soninrv/docs/data/lab1/plots.py new file mode 100644 index 0000000..6c0df6e --- /dev/null +++ b/soninrv/docs/data/lab1/plots.py @@ -0,0 +1,49 @@ +import csv +import statistics +import matplotlib.pyplot as plt +import numpy as np + +rows = [] +with open("results.csv", encoding="utf-8") as f: + for r in csv.DictReader(f): + rows.append(r) + +STRUCTS = ["LinkedList", "HashTable", "BST"] +MODE_MAP = {"shuffled": "случайный", "sorted": "отсортированный"} +OPS = [("insert", "Вставка"), ("find", "Поиск"), ("delete", "Удаление")] + +def stats(structure, mode, operation): + vals = [float(r["time_sec"]) for r in rows + if r["structure"] == structure and r["mode"] == mode and r["operation"] == operation] + if not vals: + return 0.0, 0.0 + return statistics.mean(vals), statistics.stdev(vals) if len(vals) > 1 else 0.0 + +fig, axes = plt.subplots(1, 3, figsize=(15, 5)) +fig.suptitle("Среднее время операций (сек, лог-шкала, N=10 000, 5 повторений)") + +x = np.arange(len(STRUCTS)) +WIDTH = 0.35 + +for ax, (op_key, op_title) in zip(axes, OPS): + for i, mode in enumerate(["shuffled", "sorted"]): + avgs, stds = [], [] + for s in STRUCTS: + avg, std = stats(s, mode, op_key) + avgs.append(avg) + stds.append(std) + offset = (i - 0.5) * WIDTH + ax.bar(x + offset, avgs, WIDTH, label=MODE_MAP[mode]) + ax.errorbar(x + offset, avgs, yerr=stds, fmt="none", capsize=4) + ax.set_yscale("log") + ax.set_title(op_title) + ax.set_xticks(x) + ax.set_xticklabels(STRUCTS) + ax.set_ylabel("Время (сек)") + ax.yaxis.grid(True, which="both", linestyle="--", alpha=0.5) + ax.set_axisbelow(True) + ax.legend() + +plt.tight_layout() +plt.savefig("../../performance_comparison.png", dpi=150) +plt.show() \ No newline at end of file From a9deaa51ec669df2d57bfc3e394546548209060e Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 01:06:00 +0300 Subject: [PATCH 07/12] added report --- soninrv/docs/performance_comparison.png | Bin 0 -> 67367 bytes soninrv/docs/report1.md | 172 ++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 soninrv/docs/performance_comparison.png create mode 100644 soninrv/docs/report1.md diff --git a/soninrv/docs/performance_comparison.png b/soninrv/docs/performance_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca134f41399aae650bde414db9df4bbc10111cd GIT binary patch literal 67367 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 literal 0 HcmV?d00001 diff --git a/soninrv/docs/report1.md b/soninrv/docs/report1.md new file mode 100644 index 0000000..f2c3b2b --- /dev/null +++ b/soninrv/docs/report1.md @@ -0,0 +1,172 @@ +# Отчёт по лабораторной работе "Структуры данных" + + +## 1. Цель работы + +Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций: вставки, поиска и удаления. + +Структуры данных: + +Связный список (LinkedList) — узлы-словари, соединённые ссылками. + +Хеш-таблица (HashTable) — массив корзин (1024 элемента) с цепочками через связный список. + +Двоичное дерево поиска (BST) — рекурсивная / итеративная реализация через словари. + +N = 10 000 записей вида User_00001, +7-000-0000001. + +Два режима: случайный порядок (records_shuffled) и отсортированный (records_sorted). + +Поиск: 100 гарантированно существующих имён + 10 несуществующих = 110 запросов. + +Удаление: 50 случайных имён из набора. + +Каждый замер повторяется 5 раз; записываются все замеры и среднее. + + +## 2. Результаты экспериментов + +| structure | mode | operation | run | time_sec | +|---|---|---|---|---| +| LinkedList | shuffled | insert | 1 | 3.688133922999995 | +| LinkedList | shuffled | insert | 2 | 3.642716359000005 | +| LinkedList | shuffled | insert | 3 | 3.6362029409999934 | +| LinkedList | shuffled | insert | 4 | 3.5635424559999933 | +| LinkedList | shuffled | insert | 5 | 3.6936824539999975 | +| LinkedList | shuffled | find | 1 | 0.0404481799999985 | +| LinkedList | shuffled | find | 2 | 0.0415632419999951 | +| LinkedList | shuffled | find | 3 | 0.0408364839999961 | +| LinkedList | shuffled | find | 4 | 0.0409441910000083 | +| LinkedList | shuffled | find | 5 | 0.0409490519999877 | +| LinkedList | shuffled | delete | 1 | 0.020429828999994 | +| LinkedList | shuffled | delete | 2 | 0.0203125029999995 | +| LinkedList | shuffled | delete | 3 | 0.0205162980000039 | +| LinkedList | shuffled | delete | 4 | 0.0204522580000059 | +| LinkedList | shuffled | delete | 5 | 0.0204940820000132 | +| LinkedList | sorted | insert | 1 | 2.807388945 | +| LinkedList | sorted | insert | 2 | 2.6681887550000027 | +| LinkedList | sorted | insert | 3 | 2.7149360570000027 | +| LinkedList | sorted | insert | 4 | 2.586755936000003 | +| LinkedList | sorted | insert | 5 | 2.858489943000009 | +| LinkedList | sorted | find | 1 | 0.0301240860000007 | +| LinkedList | sorted | find | 2 | 0.0300124050000079 | +| LinkedList | sorted | find | 3 | 0.0301267250000023 | +| LinkedList | sorted | find | 4 | 0.0300742670000033 | +| LinkedList | sorted | find | 5 | 0.0304795409999769 | +| LinkedList | sorted | delete | 1 | 0.0176948809999828 | +| LinkedList | sorted | delete | 2 | 0.0186108259999855 | +| LinkedList | sorted | delete | 3 | 0.0183917109999924 | +| LinkedList | sorted | delete | 4 | 0.0183299800000042 | +| LinkedList | sorted | delete | 5 | 0.0202586389999908 | +| HashTable | shuffled | insert | 1 | 0.040671551999992 | +| HashTable | shuffled | insert | 2 | 0.0356988590000071 | +| HashTable | shuffled | insert | 3 | 0.034698187999993 | +| HashTable | shuffled | insert | 4 | 0.034897758999989 | +| HashTable | shuffled | insert | 5 | 0.0436747020000041 | +| HashTable | shuffled | find | 1 | 0.0003306420000228 | +| HashTable | shuffled | find | 2 | 0.0002776770000139 | +| HashTable | shuffled | find | 3 | 0.0002387590000125 | +| HashTable | shuffled | find | 4 | 0.0002413439999884 | +| HashTable | shuffled | find | 5 | 0.0002350800000101 | +| HashTable | shuffled | delete | 1 | 0.0009653390000039 | +| HashTable | shuffled | delete | 2 | 0.000182843999994 | +| HashTable | shuffled | delete | 3 | 0.000187277000009 | +| HashTable | shuffled | delete | 4 | 0.0001825169999847 | +| HashTable | shuffled | delete | 5 | 0.000182102999986 | +| HashTable | sorted | insert | 1 | 0.031514957000013 | +| HashTable | sorted | insert | 2 | 0.0317737780000015 | +| HashTable | sorted | insert | 3 | 0.0332209919999968 | +| HashTable | sorted | insert | 4 | 0.0438333349999879 | +| HashTable | sorted | insert | 5 | 0.0344081210000126 | +| HashTable | sorted | find | 1 | 0.0004218560000026 | +| HashTable | sorted | find | 2 | 0.0003256969999938 | +| HashTable | sorted | find | 3 | 0.0003048350000085 | +| HashTable | sorted | find | 4 | 0.000252023999991 | +| HashTable | sorted | find | 5 | 0.0002450770000166 | +| HashTable | sorted | delete | 1 | 0.0002077629999917 | +| HashTable | sorted | delete | 2 | 0.000197111999995 | +| HashTable | sorted | delete | 3 | 0.000204272000019 | +| HashTable | sorted | delete | 4 | 0.0001966060000029 | +| HashTable | sorted | delete | 5 | 0.0001917250000076 | +| BST | shuffled | insert | 1 | 0.0322367580000104 | +| BST | shuffled | insert | 2 | 0.0445325409999952 | +| BST | shuffled | insert | 3 | 0.0312052750000191 | +| BST | shuffled | insert | 4 | 0.0302206560000115 | +| BST | shuffled | insert | 5 | 0.0304544809999924 | +| BST | shuffled | find | 1 | 0.000256859999979 | +| BST | shuffled | find | 2 | 0.0001786029999948 | +| BST | shuffled | find | 3 | 0.0001869349999878 | +| BST | shuffled | find | 4 | 0.0001727730000027 | +| BST | shuffled | find | 5 | 0.0001574610000147 | +| BST | shuffled | delete | 1 | 0.0001869909999925 | +| BST | shuffled | delete | 2 | 0.0012688459999878 | +| BST | shuffled | delete | 3 | 0.0012691000000017 | +| BST | shuffled | delete | 4 | 0.001258899999982 | +| BST | shuffled | delete | 5 | 0.0013220630000034 | +| BST | sorted | insert | 1 | 12.957382101000007 | +| BST | sorted | insert | 2 | 12.10390555699999 | +| BST | sorted | insert | 3 | 12.698454105999986 | +| BST | sorted | insert | 4 | 12.181134653000017 | +| BST | sorted | insert | 5 | 12.952122806999997 | +| BST | sorted | find | 1 | 0.0432625550000125 | +| BST | sorted | find | 2 | 0.0455909260000169 | +| BST | sorted | find | 3 | 0.0434497109999938 | +| BST | sorted | find | 4 | 0.04326359800001 | +| BST | sorted | find | 5 | 0.0431787990000032 | +| BST | sorted | delete | 1 | 0.0546987289999947 | +| BST | sorted | delete | 2 | 0.0549414869999793 | +| BST | sorted | delete | 3 | 0.0549512879999838 | +| BST | sorted | delete | 4 | 0.0546492089999901 | +| BST | sorted | delete | 5 | 0.0542962790000274 | +Графическое представление результатов приведено на рисунке ниже. +[![Сравнение производительности](performance_comparison.png)] +## 3. Анализ результатов + + +### 3.1. Вставка + +Связный список: проход по всему списку для поиска дубликата перед вставкой даёт O(n) на каждый элемент. При N = 10 000 это ≈50 млн операций сравнения — отсюда 3.6 с. + +Хеш-таблица: хеш вычисляется за O(len(name)), поиск в корзине ≈ O(1). Итог — 0.037 с независимо от порядка. + +BST на случайных данных: дерево остаётся примерно сбалансированным, высота ≈ log₂(10000) ≈ 13. Итог — 0.034 с. На отсортированных данных каждый новый элемент добавляется в правое поддерево, высота достигает N = 10 000 — полная деградация до O(n²) суммарно. Итог — 12.6 с (×373 замедление). + + +### 3.2. Поиск + +Связный список: в среднем просматривает N/2 узлов. При 110 запросах — ≈550 000 сравнений. Время ≈ 0.04 с. + +Хеш-таблица: поиск в корзине из ~10 элементов — практически мгновенно. Время ≈ 0.0003 с — в 130 раз быстрее связного списка. + +BST: случайный порядок — log(N) шагов, ≈ 0.0002 с. Отсортированный — линейный поиск O(n), ≈ 0.044 с (сравнимо со связным списком). + + +### 3.3. Удаление + +Связный список: необходим проход до удаляемого элемента — O(n). При 50 удалениях ≈ 0.02 с. + +Хеш-таблица: O(1) в среднем — ≈ 0.0003 с. + +BST: случайные данные — O(log n) ≈ 0.001 с. Отсортированные — O(n) ≈ 0.055 с. + + +### 3.4. Получение отсортированного списка + +Связный список и хеш-таблица: сбор всех N элементов + Python sort — O(n log n). Практически одинаково для обеих структур. + +BST: in-order обход уже возвращает отсортированный список — O(n), без дополнительной сортировки. При случайном вводе BST является наиболее эффективным для list_all. + + +## 4. Выводы и рекомендации + +На основании экспериментов можно дать следующие практические рекомендации: + +Частые вставки и поиск без упорядочивания → Хеш-таблица. Константное среднее время O(1) для всех операций, нечувствительность к порядку данных. Практически всегда лучший выбор для справочников, кэшей, индексов. + +Данные нужны в отсортированном порядке (range queries, итерация по алфавиту) → Сбалансированное BST (AVL, красно-чёрное дерево) или B-дерево. Простая BST допустима только при случайном порядке вставки. На отсортированных данных деградирует до O(n). + +Очень мало элементов или требуется простота реализации → Связный список. При N < 100 разница в скорости незначительна, а код минимальный. + +BST (сбалансированный) vs Хеш-таблица: если нужно только find/insert/delete — хеш-таблица быстрее. Если нужны min/max, range-запросы, сортировка — BST предпочтительнее. + +Итог: для реального телефонного справочника с операциями insert/find/delete оптимальна хеш-таблица. Если требуется регулярный вывод списка по алфавиту — BST (сбалансированное). Связный список применим только как учебная модель или для очень маленьких N. From 7cbd75ee342c44df445e7313ba5b3d4881aeaef7 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 03:13:08 +0300 Subject: [PATCH 08/12] added main.py --- soninrv/docs/data/lab2/main.py | 611 +++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 soninrv/docs/data/lab2/main.py diff --git a/soninrv/docs/data/lab2/main.py b/soninrv/docs/data/lab2/main.py new file mode 100644 index 0000000..cd6e0c9 --- /dev/null +++ b/soninrv/docs/data/lab2/main.py @@ -0,0 +1,611 @@ +""" +maze_solver.py — Поиск выхода из лабиринта. + +Паттерны GoF: Builder, Strategy, Observer, Command. +Алгоритмы: BFS, DFS, A*, Dijkstra. + +Запуск: + python maze_solver.py --solve mazes/small_10x10.txt --algo bfs + python maze_solver.py --walk mazes/small_10x10.txt + python maze_solver.py --experiment +""" + +from __future__ import annotations + +import argparse +import csv +import heapq +import os +import statistics +import sys +import time +from abc import ABC, abstractmethod +from collections import deque +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +sys.setrecursionlimit(100_000) + + +# ЭТАП 1. МОДЕЛЬ ЛАБИРИНТА — Cell, Maze + +@dataclass +class Cell: + """Одна клетка лабиринта.""" + x: int + y: int + is_wall: bool = False + is_start: bool = False + is_exit: bool = False + weight: int = 1 # 1=асфальт 2=песок 3=болото + + def is_passable(self) -> bool: + return not self.is_wall + + def __repr__(self) -> str: + if self.is_wall: return "#" + if self.is_start: return "S" + if self.is_exit: return "E" + return " " + + # heapq требует сравнения объектов + def __lt__(self, other: Cell) -> bool: + return (self.x, self.y) < (other.x, other.y) + + def __hash__(self): + return hash((self.x, self.y)) + + def __eq__(self, other): + return isinstance(other, Cell) and self.x == other.x and self.y == other.y + + +class Maze: + """Двумерная сетка клеток.""" + + def __init__(self, width: int, height: int): + self.width = width + self.height = height + self._cells: List[List[Cell]] = [ + [Cell(x, y) for x in range(width)] for y in range(height) + ] + self.start: Optional[Cell] = None + self.exit: Optional[Cell] = None + + def get_cell(self, x: int, y: int) -> Optional[Cell]: + if 0 <= x < self.width and 0 <= y < self.height: + return self._cells[y][x] + return None + + def get_neighbors(self, cell: Cell) -> List[Cell]: + """Четыре соседа — только проходимые, в пределах границ.""" + result = [] + for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)): + nb = self.get_cell(cell.x + dx, cell.y + dy) + if nb and nb.is_passable(): + result.append(nb) + return result + + + +# ЭТАП 2. ПАТТЕРН BUILDER — загрузка лабиринта из файла + +_WEIGHT_MAP = {' ': 1, '.': 2, '~': 3} + + +class MazeBuilder(ABC): + """Интерфейс строителя лабиринта.""" + + @abstractmethod + def build_from_file(self, filename: str) -> Maze: ... + + @abstractmethod + def build_from_string(self, text: str) -> Maze: ... + + +class TextFileMazeBuilder(MazeBuilder): + """ + Строит Maze из текстового файла: + # — стена S — старт E — выход + ' '— путь (w=1) . — песок (w=2) ~ — болото (w=3) + """ + + def build_from_file(self, filename: str) -> Maze: + return self.build_from_string(Path(filename).read_text(encoding="utf-8")) + + def build_from_string(self, text: str) -> Maze: + lines = text.splitlines() + while lines and not lines[-1].strip(): + lines.pop() + if not lines: + raise ValueError("Пустой лабиринт") + + height = len(lines) + width = max(len(l) for l in lines) + maze = Maze(width, height) + + for y, line in enumerate(lines): + for x, ch in enumerate(line): + cell = maze.get_cell(x, y) + if cell is None: + continue + if ch == '#': + cell.is_wall = True + elif ch == 'S': + cell.is_start = True + maze.start = cell + elif ch == 'E': + cell.is_exit = True + maze.exit = cell + else: + cell.weight = _WEIGHT_MAP.get(ch, 1) + for x in range(len(line), width): # дополнить стенами + c = maze.get_cell(x, y) + if c: + c.is_wall = True + + if maze.start is None: + raise ValueError("Нет стартовой клетки 'S'") + if maze.exit is None: + raise ValueError("Нет выходной клетки 'E'") + return maze + + + +# ЭТАП 3. ПАТТЕРН STRATEGY — алгоритмы поиска пути + + +def _reconstruct(parent: Dict[Cell, Optional[Cell]], end: Cell) -> List[Cell]: + """Восстановить путь от старта до end по словарю предшественников.""" + path, node = [], end + while node is not None: + path.append(node) + node = parent.get(node) + path.reverse() + return path + + +class PathFindingStrategy(ABC): + """Интерфейс стратегии поиска пути.""" + + @property + @abstractmethod + def name(self) -> str: ... + + @abstractmethod + def find_path( + self, maze: Maze, start: Cell, exit_cell: Cell + ) -> Tuple[List[Cell], int]: + """Возвращает (path, visited_count). path пуст если пути нет.""" + ... + + +class BFSStrategy(PathFindingStrategy): + """Поиск в ширину — гарантирует кратчайший путь (по числу шагов).""" + + @property + def name(self) -> str: + return "BFS" + + def find_path(self, maze, start, exit_cell): + queue = deque([start]) + parent: Dict[Cell, Optional[Cell]] = {start: None} + visited = 0 + while queue: + cur = queue.popleft() + visited += 1 + if cur == exit_cell: + return _reconstruct(parent, exit_cell), visited + for nb in maze.get_neighbors(cur): + if nb not in parent: + parent[nb] = cur + queue.append(nb) + return [], visited + + +class DFSStrategy(PathFindingStrategy): + """Поиск в глубину — не гарантирует кратчайший путь.""" + + @property + def name(self) -> str: + return "DFS" + + def find_path(self, maze, start, exit_cell): + stack = [start] + parent: Dict[Cell, Optional[Cell]] = {start: None} + visited = 0 + while stack: + cur = stack.pop() + visited += 1 + if cur == exit_cell: + return _reconstruct(parent, exit_cell), visited + for nb in maze.get_neighbors(cur): + if nb not in parent: + parent[nb] = cur + stack.append(nb) + return [], visited + + +class AStarStrategy(PathFindingStrategy): + """A* с манхэттенской эвристикой — направленный поиск, учитывает веса.""" + + @property + def name(self) -> str: + return "A*" + + @staticmethod + def _h(a: Cell, b: Cell) -> int: + return abs(a.x - b.x) + abs(a.y - b.y) + + def find_path(self, maze, start, exit_cell): + heap: List[Tuple[int, int, Cell]] = [(0, 0, start)] + g: Dict[Cell, int] = {start: 0} + parent: Dict[Cell, Optional[Cell]] = {start: None} + visited = 0 + while heap: + _, g_cur, cur = heapq.heappop(heap) + visited += 1 + if cur == exit_cell: + return _reconstruct(parent, exit_cell), visited + if g_cur > g.get(cur, float('inf')): + continue + for nb in maze.get_neighbors(cur): + new_g = g_cur + nb.weight + if new_g < g.get(nb, float('inf')): + g[nb] = new_g + parent[nb] = cur + heapq.heappush(heap, (new_g + self._h(nb, exit_cell), new_g, nb)) + return [], visited + + +class DijkstraStrategy(PathFindingStrategy): + """Дейкстра — оптимален для взвешенных графов, без эвристики.""" + + @property + def name(self) -> str: + return "Dijkstra" + + def find_path(self, maze, start, exit_cell): + dist: Dict[Cell, int] = {start: 0} + parent: Dict[Cell, Optional[Cell]] = {start: None} + heap: List[Tuple[int, Cell]] = [(0, start)] + visited = 0 + while heap: + d, cur = heapq.heappop(heap) + visited += 1 + if cur == exit_cell: + return _reconstruct(parent, exit_cell), visited + if d > dist.get(cur, float('inf')): + continue + for nb in maze.get_neighbors(cur): + new_d = d + nb.weight + if new_d < dist.get(nb, float('inf')): + dist[nb] = new_d + parent[nb] = cur + heapq.heappush(heap, (new_d, nb)) + return [], visited + + + +# ПАТТЕРН OBSERVER + + +@dataclass +class SearchStats: + time_ms: float + visited_cells: int + path_length: int + strategy_name: str + + def __str__(self) -> str: + return (f"[{self.strategy_name}] " + f"время={self.time_ms:.2f} мс " + f"посещено={self.visited_cells} " + f"длина пути={self.path_length}") + + +class Observer(ABC): + @abstractmethod + def update(self, event: dict) -> None: ... + + +class ConsoleView(Observer): + """Выводит события и рисует лабиринт в консоли.""" + + def update(self, event: dict) -> None: + kind = event.get("type") + if kind == "maze_loaded": + print(f"[Лабиринт загружен] {event['width']}×{event['height']}") + elif kind == "search_start": + print(f"[Поиск] алгоритм={event['strategy']}") + elif kind == "path_found": + print(f"[Готово] {event['stats']}") + elif kind == "no_path": + print("[Результат] Путь не найден!") + + def render( + self, + maze: Maze, + player_pos: Optional[Cell] = None, + path: Optional[List[Cell]] = None, + ) -> None: + path_set = set(path) if path else set() + for y in range(maze.height): + row = [] + for x in range(maze.width): + cell = maze.get_cell(x, y) + if cell is None: + row.append("?") + elif player_pos and cell == player_pos: + row.append("@") + elif cell.is_wall: + row.append("█") + elif cell.is_start: + row.append("S") + elif cell.is_exit: + row.append("E") + elif cell in path_set: + row.append("*") + elif cell.weight == 3: + row.append("~") + elif cell.weight == 2: + row.append(".") + else: + row.append(" ") + print("".join(row)) + print() + + + +# ЭТАП 4. ОРКЕСТРАТОР MazeSolver + +class MazeSolver: + """ + Связывает Maze + PathFindingStrategy + список Observer. + Паттерны: Strategy (алгоритм подключается снаружи), + Observer (уведомления при завершении). + """ + + def __init__(self, maze: Maze, strategy: PathFindingStrategy): + self.maze = maze + self._strategy = strategy + self._observers: List[Observer] = [] + self._last_path: List[Cell] = [] + + def add_observer(self, obs: Observer) -> None: + self._observers.append(obs) + + def set_strategy(self, strategy: PathFindingStrategy) -> None: + self._strategy = strategy + + def _notify(self, event: dict) -> None: + for obs in self._observers: + obs.update(event) + + def solve(self) -> SearchStats: + if not self.maze.start or not self.maze.exit: + raise ValueError("Лабиринт не содержит старт или выход") + + self._notify({"type": "search_start", "strategy": self._strategy.name}) + + t0 = time.perf_counter() + path, visited = self._strategy.find_path( + self.maze, self.maze.start, self.maze.exit + ) + elapsed_ms = (time.perf_counter() - t0) * 1000 + + self._last_path = path + stats = SearchStats(elapsed_ms, visited, len(path), self._strategy.name) + + self._notify({"type": "path_found" if path else "no_path", "stats": stats}) + return stats + + @property + def last_path(self) -> List[Cell]: + return self._last_path + + + +# ЭТАП 5. ПАТТЕРН COMMAND — пошаговое управление игроком + + +_DIRECTIONS = {'W': (0, -1), 'S': (0, 1), 'A': (-1, 0), 'D': (1, 0)} + + +class Command(ABC): + @abstractmethod + def execute(self) -> bool: ... + @abstractmethod + def undo(self) -> None: ... + + +class Player: + def __init__(self, cell: Cell): + self.current_cell = cell + + def move_to(self, cell: Cell) -> None: + self.current_cell = cell + + +class MoveCommand(Command): + """Перемещение игрока в направлении direction (W/A/S/D).""" + + def __init__(self, player: Player, direction: str, maze: Maze, + observers: Optional[List[Observer]] = None): + self._player = player + self._direction = direction.upper() + self._maze = maze + self._observers = observers or [] + self._prev: Optional[Cell] = None + + def execute(self) -> bool: + dx, dy = _DIRECTIONS.get(self._direction, (0, 0)) + target = self._maze.get_cell( + self._player.current_cell.x + dx, + self._player.current_cell.y + dy, + ) + if target and target.is_passable(): + self._prev = self._player.current_cell + self._player.move_to(target) + for obs in self._observers: + obs.update({"type": "move", "cell": target}) + return True + return False + + def undo(self) -> None: + if self._prev: + self._player.move_to(self._prev) + self._prev = None + + +class CommandHistory: + """Стек выполненных команд (undo/redo).""" + + def __init__(self): + self._stack: List[Command] = [] + + def execute(self, cmd: Command) -> bool: + ok = cmd.execute() + if ok: + self._stack.append(cmd) + return ok + + def undo(self) -> bool: + if self._stack: + self._stack.pop().undo() + return True + return False + + + +# ЭТАП 6. ЭКСПЕРИМЕНТАЛЬНАЯ ЧАСТЬ + + +def run_experiment( + maze_files: List[Tuple[str, str]], + strategies: List[PathFindingStrategy], + repeats: int = 7, + out_csv: str = "results.csv", +) -> None: + builder = TextFileMazeBuilder() + rows = [["maze", "strategy", "run", "time_ms", "visited_cells", "path_length"]] + + for maze_name, maze_file in maze_files: + print(f"\n=== {maze_name} ===") + maze = builder.build_from_file(maze_file) + print(f" Размер: {maze.width}×{maze.height}") + + for strategy in strategies: + solver = MazeSolver(maze, strategy) + times, visits, lengths = [], [], [] + + for run in range(1, repeats + 1): + stats = solver.solve() + times.append(stats.time_ms) + visits.append(stats.visited_cells) + lengths.append(stats.path_length) + rows.append([maze_name, strategy.name, run, + round(stats.time_ms, 4), + stats.visited_cells, + stats.path_length]) + + print(f" {strategy.name:10} | " + f"t={statistics.mean(times):.3f} мс | " + f"посещено={statistics.mean(visits):.0f} | " + f"путь={statistics.mean(lengths):.0f}") + + os.makedirs(os.path.dirname(os.path.abspath(out_csv)), exist_ok=True) + with open(out_csv, "w", newline="", encoding="utf-8") as f: + csv.writer(f).writerows(rows) + print(f"\nСохранено: {out_csv}") + + + +# CLI + + +_ALGO_MAP = { + "bfs": BFSStrategy(), + "dfs": DFSStrategy(), + "astar": AStarStrategy(), + "dijkstra": DijkstraStrategy(), +} + +_MAZES = [ + ("small_10x10", "small_10x10.txt"), + ("medium_50x50", "medium_50x50.txt"), + ("large_100x100", "large_100x100.txt"), + ("empty_30x30", "empty_30x30.txt"), + ("no_exit_20x20", "no_exit_20x20.txt"), + ("weighted_40x40", "weighted_40x40.txt"), +] + + +def cmd_solve(maze_file: str, algo: str) -> None: + view = ConsoleView() + maze = TextFileMazeBuilder().build_from_file(maze_file) + view.update({"type": "maze_loaded", "width": maze.width, "height": maze.height}) + + strategy = _ALGO_MAP.get(algo.lower()) + if not strategy: + print(f"Неизвестный алгоритм '{algo}'. Доступны: {', '.join(_ALGO_MAP)}") + return + + solver = MazeSolver(maze, strategy) + solver.add_observer(view) + solver.solve() + view.render(maze, path=solver.last_path) + + +def cmd_walk(maze_file: str) -> None: + view = ConsoleView() + maze = TextFileMazeBuilder().build_from_file(maze_file) + player = Player(maze.start) + history = CommandHistory() + + solver = MazeSolver(maze, BFSStrategy()) + solver.add_observer(view) + solver.solve() + path = solver.last_path + + print("W=вверх S=вниз A=влево D=вправо Z=отмена Q=выход") + while True: + view.render(maze, player_pos=player.current_cell, path=path) + if player.current_cell == maze.exit: + print("Вы достигли выхода!") + break + key = input("Ход: ").strip().upper() + if key == "Q": + break + elif key == "Z": + if not history.undo(): + print("Нечего отменять.") + elif key in _DIRECTIONS: + if not history.execute(MoveCommand(player, key, maze, [view])): + print("Туда нельзя — стена.") + else: + print("Неизвестная клавиша.") + + +def cmd_experiment() -> None: + run_experiment(_MAZES, list(_ALGO_MAP.values())) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Maze solver") + parser.add_argument("--solve", metavar="FILE", help="Решить лабиринт") + parser.add_argument("--algo", metavar="ALGO", default="bfs", + help="bfs | dfs | astar | dijkstra") + parser.add_argument("--walk", metavar="FILE", help="Ручное управление (WASD)") + parser.add_argument("--experiment", action="store_true", + help="Запустить замеры и сохранить CSV") + args = parser.parse_args() + + if args.solve: + cmd_solve(args.solve, args.algo) + elif args.walk: + cmd_walk(args.walk) + elif args.experiment: + cmd_experiment() + else: + parser.print_help() \ No newline at end of file From 52cba853ddc36e31f984412c4c4e80ad9d5e6f05 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 03:14:03 +0300 Subject: [PATCH 09/12] added mazes --- soninrv/docs/data/lab2/empty_30x30.txt | 30 +++++++ soninrv/docs/data/lab2/large_100x100.txt | 100 ++++++++++++++++++++++ soninrv/docs/data/lab2/medium_50x50.txt | 50 +++++++++++ soninrv/docs/data/lab2/no_exit_20x20.txt | 20 +++++ soninrv/docs/data/lab2/small_10x10.txt | 10 +++ soninrv/docs/data/lab2/weighted_40x40.txt | 40 +++++++++ 6 files changed, 250 insertions(+) create mode 100644 soninrv/docs/data/lab2/empty_30x30.txt create mode 100644 soninrv/docs/data/lab2/large_100x100.txt create mode 100644 soninrv/docs/data/lab2/medium_50x50.txt create mode 100644 soninrv/docs/data/lab2/no_exit_20x20.txt create mode 100644 soninrv/docs/data/lab2/small_10x10.txt create mode 100644 soninrv/docs/data/lab2/weighted_40x40.txt diff --git a/soninrv/docs/data/lab2/empty_30x30.txt b/soninrv/docs/data/lab2/empty_30x30.txt new file mode 100644 index 0000000..386c2e4 --- /dev/null +++ b/soninrv/docs/data/lab2/empty_30x30.txt @@ -0,0 +1,30 @@ +############################## +#S # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# E# +############################## diff --git a/soninrv/docs/data/lab2/large_100x100.txt b/soninrv/docs/data/lab2/large_100x100.txt new file mode 100644 index 0000000..e12ffae --- /dev/null +++ b/soninrv/docs/data/lab2/large_100x100.txt @@ -0,0 +1,100 @@ +#################################################################################################### +#S# # # # # # # # # # # ## +# ##### # ### # # ### # ##### # ##### # # ########### # ### ### # ####### ####### # ### ### ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +##### # ### # # ### ##### ######### ####### # ### # ##### ### # ### # # ##### # ### ######### ### ## +# # # # # # # # # # # # # # # # # # # # # # ## +# ##### # ######### # # # ### # ##### ##### ### ### # ##### ##### ############# # # # # ####### #### +# # # # # # # # # # # # # # # # # # # # # # # # # ## +### # # # # ######### # ### ####### ### # ####### ##### # # # ##### # ### # # # ##### # # ### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### # ########### # ### # # ####### ############# ### # ##### # # # # # ### # # # ######### # # ## +# # # # # # # # # # # # # # # # # # # # # # ## +# ### # # # # ######### # ##### ####### ### ############# # ####### ### # ##################### # ## +# # # # # # # # # # # # # # # ## +### ### ### ##### ##### ######### ### ### ##### # # ### ##### ######### ####### ####### # ### ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ### ### ####### # # ##### ##### # ### # # ### # ##### ######### # ##### ##### # ### # ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### # # # # ### # ####### ### # # ##### # ### ########### ##### # ### # ### # # ######### # #### +# # # # # # # # # # # # # # # # # # # # # # # # ## +# ####### # # # # # ####### # # ### ### # ##### ### ### # # ### # # ######### ### ######### ### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### ### ##### ### # # # ##### ### # ### ##### ### ##### # ######### # # ##### # ### # ####### ## +# # # # # # # # # # # # # # # # # # # # # # # ## +# # # # ####### ####### # # ### # ####### # # ##### # # # ### ####### ################### # # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### # ### ########### ##### ### ### # ### # ##### # # # ### # # # ######### # ######### #### +# # # # # # # # # # # # # # # # # # # # # # # ## +##### # ### ### ### ####### ### # ### ### ##### ####### # ####### ########### # # # ### ### # ### ## +# # # # # # # # # # # # # # # # # # # # # ## +# # ##### ### ### ### ### ####### # # # ### ##### # # # ### # ##### # # ############# ########### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # # # # # ##### # # ######### ####### ##### # ##### # ### # # ######### # # ### # ### # # ###### +# # # # # # # # # # # # # # # # # # # # # ## +# ##### # # ### ##### ################# # ### ##### ##### # ############### # ### ########### # # ## +# # # # # # # # # # # # # # # # # # # # # # # ## +# # ##### # # ##### ### ##### ### # ####### ### ####### # # # ### # ##### # ### ##### ##### # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ##### # ### ### # # ##### ### # ### # # ####### # # # # # # # ### # ### ######### # ##### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +### ######### # ### ### # # # ### # ######### # ####### # # # # ##### # ### ### ### # ####### # #### +# # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ### ### ### ### ### # # # ####### # # # # ### ##### ### # # # ##### ####### # # ######### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### ### ### ####### ### ##### # ############# ##### ### ####### # # ### ### # # # # ### ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ####### ### # ### ### ### # # # ### ######### # ####### # # # # ####### # ### ##### ##### # ## +# # # # # # # # # # # # # # # # # # # # # # # ## +##### # ##### # # # # ### ### ##### # ######### ########### # # ####### ######### # # # # # ### # ## +# # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ##### # ### # # ##### ### ### # ##### ##### ##### # ##### ####### ##### ##### ##### # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +##### ### # # # ####### ### ### ####### # # ##### ### # # ##### # # ######### ### # # # # # # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### # ### ### # ##### ##### # ### ##### ####### # # # ####### ######### # ### # # ####### ## +# # # # # # # # # # # # # # # # # # # # # # ## +### ### # # ####### ### # # ### ### # # # ####### ########### ##### # # ### ####### ### ##### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ### # # # ##### ### ### ### # ### # ### # # # ### ### ##### # # ######### # # # ##### # ###### +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### # ### # # ### # # # # ### # ######### ### ### # # ########### # # ##### # ######### # # # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # ### # # ### # ### # # ##### ####### ### # ##### ### # # # ######### # # ### # ### # # # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +### ####### ### # ##### # ### ### # ##### # ### # ##### ### # ### ### ##### ### ##### ### ##### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # ### ### ####### ### # # # ### ####### # ### # # ### ####### # ### ####### # # # ########### ## +# # # # # # # # # # # # # # # # # # # # ## +# # ### ########### ### # # ######### ######### # # # # # # # # ### ############# ################## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # ### ### # # # # # # ##### ### ### # ### # # # ##### # ##### # # ### # # # ### # # ### # # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### ##### # ### ### ##### # # ### # ##### # # ### ### # ####### ##### # ### # # ### # # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +##### ### # # # # ### ### ### # ### # ### ##### ### ### ##### ##### # ########### # # # # ### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ####### ####### ### ### ### # # ### ### ##### ### # # # ### # # # ### ### ######### # # ######## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ####### # ####### # ##### # # # # ### ### # # # ##### ##### # ##### ### # # # ### ### ### ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ### ### # # # # ##### # # # # ########### ##### ##### # # # ### ### ##### # ##### ######### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### ####### ####### ##### # # ##### ##### ##### # ##### # ##### # # # # # ##### # ######### # ## +# # # # # # # # # # # # # # # # # # # # # # ## +# ### ### # # ### # # ##### ### ### ####### ##### ##### # # ##### # # # ### ### # ### ####### ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +### # # ##### # ### ##### # # ### ### # # # # # ##### ####### # # # # ####### ##### # # # # ### #### +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### # # ##### ### # # # ####### # ##### ##### ######### ### ##### # ### ### ##### ### ####### ## +# # # # # # # # # # # # # # # # # # # # ## +# ### ######### ### # ##### ######### # ### # ##### ### ######### # # # ####### ############# ### ## +# # # # # # # # # # # # # # # # # # # # ## +### ### # ########### # # ##### # ##### # ### # # ####### ### ####### # ##### ### ### ### ##### #### +# # # # # # # # # # # # # # # # # # # # # # ## +# # # ### ### ##### # ##### ####### # ### # ### ### # # ### ### ################# ##### ### ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### ### ### ### # ################### # # ### ####### ### ### # # # ### ### # # # # # # # ## +# # # # # # # # # # # E## +#################################################################################################### +#################################################################################################### diff --git a/soninrv/docs/data/lab2/medium_50x50.txt b/soninrv/docs/data/lab2/medium_50x50.txt new file mode 100644 index 0000000..e6a2bea --- /dev/null +++ b/soninrv/docs/data/lab2/medium_50x50.txt @@ -0,0 +1,50 @@ +################################################## +#S# # # # # ## +# ### ### ### ##### ##### # # ####### # ##### # ## +# # # # # # # # # # # # # ## +### # ### # ##### ######### ##### # ### # # ### ## +# # # # # # # # # # # # ## +# # # # # ####### ### # # # ############# ### # ## +# # # # # # # # # # # # # ## +# # # # # ##### ### # # # ########### # # # ###### +# # # # # # # # # # # # # # # ## +# # ### ### ########### # # # ### # # # ####### ## +# # # # # # # # # # # # # ## +# ### ### ####### ### # # # # # ### # ### # # # ## +# # # # # # # # # # # # # # # ## +# ### # ##### ##### ##### ##### # ##### ### # #### +# # # # # # # # # # # ## +### ##### # # # # ### # ##### ####### # ####### ## +# # # # # # # # # # # # ## +# ######### # # ### ##### # ##### ####### # # # ## +# # # # # # # # # # # # # # ## +# # # # # ##### # ### # # ##### ### ####### # # ## +# # # # # # # # # # # # ## +# ### ### ######### ### # ### # ### ### # ##### ## +# # # # # # # # # # # # ## +# ### # ####### # ### ####### ### ##### # # ### ## +# # # # # # # # # # # # ## +### ##### ### ##### ########### ### # ##### # #### +# # # # # # # # # # # ## +# ### ##### ##### # # # # ############# ##### # ## +# # # # # # # # # ## +# # ######### # # ########### ### # ########### ## +# # # # # # # # # # # ## +### ### # # # # ####### # # ### ########### # # ## +# # # # # # # # # # # # # ## +# ### ### ### # ### # ##### # ### # ######### # ## +# # # # # # # # # # # # ## +# # ### # # ### # ####### ### ### ##### # ##### ## +# # # # # # # # # # # # # # # ## +# ### # # # # # ### ### # ##### ### # # ### # # ## +# # # # # # # # # # # # # # ## +### # ##### # # # ##### ##### ########### ##### ## +# # # # # # # # # # # # ## +# ### # ##### ##### ##### # # # ##### # # # # # ## +# # # # # # # # # # # # # # ## +# ##### ### ### ##### # # # ##### # ##### ##### ## +# # # # # # # # # # # # # # ## +# ### ### ####### # # # # ##### ### # # # # # # ## +# # # # # # # # E## +################################################## +################################################## diff --git a/soninrv/docs/data/lab2/no_exit_20x20.txt b/soninrv/docs/data/lab2/no_exit_20x20.txt new file mode 100644 index 0000000..c0f82fc --- /dev/null +++ b/soninrv/docs/data/lab2/no_exit_20x20.txt @@ -0,0 +1,20 @@ +#################### +#S # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # # +# # E# +#################### diff --git a/soninrv/docs/data/lab2/small_10x10.txt b/soninrv/docs/data/lab2/small_10x10.txt new file mode 100644 index 0000000..d5c532f --- /dev/null +++ b/soninrv/docs/data/lab2/small_10x10.txt @@ -0,0 +1,10 @@ +########## +#S # # +# # ### # +# # # +##### # ## +# # # +# ### # +# #### # +## E# +########## diff --git a/soninrv/docs/data/lab2/weighted_40x40.txt b/soninrv/docs/data/lab2/weighted_40x40.txt new file mode 100644 index 0000000..201539f --- /dev/null +++ b/soninrv/docs/data/lab2/weighted_40x40.txt @@ -0,0 +1,40 @@ +######################################## +#S # #~~ # # ~~ # ..## +### # ### # ### # ### # #.# ##### # # ## +# # # # #~~ # # # # #.# # .. # # ## +# # # # ##### #.### ### ### ### ### # ## +# # #~ #. #~# # ~~#.. # # ## +#~#########~##### #~# ### ### #######.## +#~ # # # ..# # ~~ #.. #.## +######### # # # ##### # ######### ### ## +# # # # #~~ # # ~~... # ## +# ##### #.# ####### # #######.###.# #### +# # .# #.#.#. #.# #.. # #.# # ## +# #.#.# # #.#.### #.# ##### ### # # # ## +# #.# # # ~# ~~# #. # # ..## +# #############~# ###.##### #.###.### ## +# # # # .# # # # .# ## +# ######### # # #~#.### #.# # #####.#### +# # # # #~#. # #.# # #...## +# # # #########.### ### # ####### ###~## +# # #~~~~ # .# # # #~## +#~### # ### # ### ### ############### ## +#~#. # ..# # #~~ .. # .# ## +###.# #####~#######.####### # #.# ###### +#~ # # #~..#~~ .#.....#.. #.# # ..## +#~##### # #~### #####.#########.# # # ## +# # # #~# #.. ..... # # # # ## +# # #.### # # ###.#########.### # # #### +# #~~.#~~~# # ~~#. # # # # .## +#.#####~### ###.###~# ####### #######.## +#. # # # #.# ~ #. # # ## +#####~### # # # # #####.# # #~### ###~## +# ..~# # # # # # ~~..# #~.. # #~## +# ##### # ##### #.# ############### #~## +# ..# # ~# #.# #~# ..~~# ..~## +##### # ###~#~##### #~#.### #####~###### +#...# # #.#~#. # .# # #..~#~ # ## +#.#####.# #.# #.##### ### # #~#~### # ## +# . #~~ # # ~# E## +######################################## +######################################## From 55ff0515e9f0ebca31e8e4a60d41a7b7ef965446 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 03:14:54 +0300 Subject: [PATCH 10/12] added plots --- soninrv/docs/data/lab2/plots.py | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 soninrv/docs/data/lab2/plots.py diff --git a/soninrv/docs/data/lab2/plots.py b/soninrv/docs/data/lab2/plots.py new file mode 100644 index 0000000..42591c7 --- /dev/null +++ b/soninrv/docs/data/lab2/plots.py @@ -0,0 +1,63 @@ +import csv +import statistics +import matplotlib.pyplot as plt +import numpy as np + + +rows = [] +with open("results.csv", encoding="utf-8") as f: + for r in csv.DictReader(f): + rows.append(r) + +MAZES = ["small_10x10", "medium_50x50", "large_100x100", + "empty_30x30", "no_exit_20x20", "weighted_40x40"] +STRATS = ["BFS", "DFS", "A*", "Dijkstra"] +MAZE_RU = { + "small_10x10": "10×10", + "medium_50x50": "50×50", + "large_100x100": "100×100", + "empty_30x30": "30×30 пустой", + "no_exit_20x20": "20×20 без выхода", + "weighted_40x40":"40×40 взвешенный", +} + +def avg(maze, strat, metric): + vals = [float(r[metric]) for r in rows + if r["maze"] == maze and r["strategy"] == strat] + return statistics.mean(vals) if vals else 0.0 + +def std(maze, strat, metric): + vals = [float(r[metric]) for r in rows + if r["maze"] == maze and r["strategy"] == strat] + return statistics.stdev(vals) if len(vals) > 1 else 0.0 + + +fig, axes = plt.subplots(1, 3, figsize=(16, 5)) +fig.suptitle("Сравнение алгоритмов (среднее, 7 запусков)") + +x = np.arange(len(MAZES)) +W = 0.18 +offsets = np.linspace(-(len(STRATS)-1)/2, (len(STRATS)-1)/2, len(STRATS)) * W + +for ax, (metric, ylabel, title) in zip(axes, [ + ("time_ms", "Время (мс)", "Время выполнения"), + ("visited_cells", "Посещено клеток", "Посещённые клетки"), + ("path_length", "Длина пути", "Длина найденного пути"), +]): + for i, strat in enumerate(STRATS): + vals = [avg(m, strat, metric) for m in MAZES] + errs = [std(m, strat, metric) for m in MAZES] + ax.bar(x + offsets[i], vals, W * 0.95, label=strat, yerr=errs, capsize=3) + ax.set_title(title) + ax.set_xticks(x) + ax.set_xticklabels([MAZE_RU[m] for m in MAZES], fontsize=7, rotation=15) + ax.set_ylabel(ylabel) + ax.legend(fontsize=8) + ax.yaxis.grid(True, linestyle="--", alpha=0.5) + ax.set_axisbelow(True) + if metric == "time_ms": + ax.set_yscale("log") + +plt.tight_layout() +plt.savefig("../../performance_plot.png", dpi=150) +plt.close() \ No newline at end of file From 9c6804eea82b05f12931f77a8ac66e184155f525 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 03:17:42 +0300 Subject: [PATCH 11/12] added results.csv --- soninrv/docs/data/lab2/results.csv | 169 +++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 soninrv/docs/data/lab2/results.csv diff --git a/soninrv/docs/data/lab2/results.csv b/soninrv/docs/data/lab2/results.csv new file mode 100644 index 0000000..3ce53ef --- /dev/null +++ b/soninrv/docs/data/lab2/results.csv @@ -0,0 +1,169 @@ +maze,strategy,run,time_ms,visited_cells,path_length +small_10x10,BFS,1,0.0844,28,21 +small_10x10,BFS,2,0.0664,28,21 +small_10x10,BFS,3,0.0644,28,21 +small_10x10,BFS,4,0.0632,28,21 +small_10x10,BFS,5,0.7441,28,21 +small_10x10,BFS,6,0.0654,28,21 +small_10x10,BFS,7,0.0643,28,21 +small_10x10,DFS,1,0.0597,22,21 +small_10x10,DFS,2,0.0523,22,21 +small_10x10,DFS,3,0.0512,22,21 +small_10x10,DFS,4,0.0518,22,21 +small_10x10,DFS,5,0.0511,22,21 +small_10x10,DFS,6,0.0508,22,21 +small_10x10,DFS,7,0.1135,22,21 +small_10x10,A*,1,0.1172,24,21 +small_10x10,A*,2,0.0951,24,21 +small_10x10,A*,3,0.0935,24,21 +small_10x10,A*,4,0.0926,24,21 +small_10x10,A*,5,0.0935,24,21 +small_10x10,A*,6,0.0922,24,21 +small_10x10,A*,7,0.0945,24,21 +small_10x10,Dijkstra,1,0.1149,28,21 +small_10x10,Dijkstra,2,0.1066,28,21 +small_10x10,Dijkstra,3,0.1046,28,21 +small_10x10,Dijkstra,4,0.1036,28,21 +small_10x10,Dijkstra,5,0.1034,28,21 +small_10x10,Dijkstra,6,0.1031,28,21 +small_10x10,Dijkstra,7,0.1038,28,21 +medium_50x50,BFS,1,1.1695,493,257 +medium_50x50,BFS,2,1.1107,493,257 +medium_50x50,BFS,3,1.0981,493,257 +medium_50x50,BFS,4,1.2213,493,257 +medium_50x50,BFS,5,1.1256,493,257 +medium_50x50,BFS,6,1.0916,493,257 +medium_50x50,BFS,7,1.0941,493,257 +medium_50x50,DFS,1,0.7161,263,257 +medium_50x50,DFS,2,0.6265,263,257 +medium_50x50,DFS,3,0.6072,263,257 +medium_50x50,DFS,4,0.6024,263,257 +medium_50x50,DFS,5,0.6033,263,257 +medium_50x50,DFS,6,0.6594,263,257 +medium_50x50,DFS,7,0.654,263,257 +medium_50x50,A*,1,1.4393,357,257 +medium_50x50,A*,2,1.4165,357,257 +medium_50x50,A*,3,1.4844,357,257 +medium_50x50,A*,4,1.3735,357,257 +medium_50x50,A*,5,1.3595,357,257 +medium_50x50,A*,6,1.4585,357,257 +medium_50x50,A*,7,1.3453,357,257 +medium_50x50,Dijkstra,1,2.0358,493,257 +medium_50x50,Dijkstra,2,2.0877,493,257 +medium_50x50,Dijkstra,3,2.2691,493,257 +medium_50x50,Dijkstra,4,2.0743,493,257 +medium_50x50,Dijkstra,5,2.0684,493,257 +medium_50x50,Dijkstra,6,2.013,493,257 +medium_50x50,Dijkstra,7,2.04,493,257 +large_100x100,BFS,1,11.4561,4783,1953 +large_100x100,BFS,2,11.1618,4783,1953 +large_100x100,BFS,3,11.3113,4783,1953 +large_100x100,BFS,4,11.0404,4783,1953 +large_100x100,BFS,5,10.9312,4783,1953 +large_100x100,BFS,6,11.1477,4783,1953 +large_100x100,BFS,7,11.1166,4783,1953 +large_100x100,DFS,1,5.0315,2161,1953 +large_100x100,DFS,2,4.9678,2161,1953 +large_100x100,DFS,3,5.1106,2161,1953 +large_100x100,DFS,4,5.5327,2161,1953 +large_100x100,DFS,5,5.0265,2161,1953 +large_100x100,DFS,6,5.0804,2161,1953 +large_100x100,DFS,7,5.0136,2161,1953 +large_100x100,A*,1,19.9319,4741,1953 +large_100x100,A*,2,19.4914,4741,1953 +large_100x100,A*,3,19.39,4741,1953 +large_100x100,A*,4,19.4556,4741,1953 +large_100x100,A*,5,19.5936,4741,1953 +large_100x100,A*,6,19.3358,4741,1953 +large_100x100,A*,7,19.1552,4741,1953 +large_100x100,Dijkstra,1,20.4017,4783,1953 +large_100x100,Dijkstra,2,20.3607,4783,1953 +large_100x100,Dijkstra,3,20.1817,4783,1953 +large_100x100,Dijkstra,4,20.1812,4783,1953 +large_100x100,Dijkstra,5,20.1135,4783,1953 +large_100x100,Dijkstra,6,19.9753,4783,1953 +large_100x100,Dijkstra,7,20.1115,4783,1953 +empty_30x30,BFS,1,1.986,784,55 +empty_30x30,BFS,2,1.967,784,55 +empty_30x30,BFS,3,1.9533,784,55 +empty_30x30,BFS,4,1.9549,784,55 +empty_30x30,BFS,5,1.9965,784,55 +empty_30x30,BFS,6,2.0811,784,55 +empty_30x30,BFS,7,2.0084,784,55 +empty_30x30,DFS,1,1.2944,433,379 +empty_30x30,DFS,2,1.3148,433,379 +empty_30x30,DFS,3,1.2713,433,379 +empty_30x30,DFS,4,1.2671,433,379 +empty_30x30,DFS,5,1.3947,433,379 +empty_30x30,DFS,6,1.2743,433,379 +empty_30x30,DFS,7,1.2843,433,379 +empty_30x30,A*,1,4.9961,784,55 +empty_30x30,A*,2,4.9058,784,55 +empty_30x30,A*,3,4.8649,784,55 +empty_30x30,A*,4,4.8501,784,55 +empty_30x30,A*,5,4.8164,784,55 +empty_30x30,A*,6,4.8326,784,55 +empty_30x30,A*,7,4.7652,784,55 +empty_30x30,Dijkstra,1,4.5931,784,55 +empty_30x30,Dijkstra,2,4.5417,784,55 +empty_30x30,Dijkstra,3,4.648,784,55 +empty_30x30,Dijkstra,4,4.6928,784,55 +empty_30x30,Dijkstra,5,4.612,784,55 +empty_30x30,Dijkstra,6,4.597,784,55 +empty_30x30,Dijkstra,7,4.6834,784,55 +no_exit_20x20,BFS,1,0.3933,162,0 +no_exit_20x20,BFS,2,0.386,162,0 +no_exit_20x20,BFS,3,0.3831,162,0 +no_exit_20x20,BFS,4,0.3843,162,0 +no_exit_20x20,BFS,5,0.3814,162,0 +no_exit_20x20,BFS,6,0.3824,162,0 +no_exit_20x20,BFS,7,0.3838,162,0 +no_exit_20x20,DFS,1,0.3912,162,0 +no_exit_20x20,DFS,2,0.3901,162,0 +no_exit_20x20,DFS,3,0.3842,162,0 +no_exit_20x20,DFS,4,0.3855,162,0 +no_exit_20x20,DFS,5,0.3851,162,0 +no_exit_20x20,DFS,6,0.3844,162,0 +no_exit_20x20,DFS,7,0.3862,162,0 +no_exit_20x20,A*,1,0.8838,162,0 +no_exit_20x20,A*,2,0.8866,162,0 +no_exit_20x20,A*,3,0.8986,162,0 +no_exit_20x20,A*,4,0.8769,162,0 +no_exit_20x20,A*,5,0.9976,162,0 +no_exit_20x20,A*,6,0.8757,162,0 +no_exit_20x20,A*,7,1.003,162,0 +no_exit_20x20,Dijkstra,1,0.8448,162,0 +no_exit_20x20,Dijkstra,2,0.8276,162,0 +no_exit_20x20,Dijkstra,3,0.8252,162,0 +no_exit_20x20,Dijkstra,4,0.8274,162,0 +no_exit_20x20,Dijkstra,5,0.9752,162,0 +no_exit_20x20,Dijkstra,6,0.8365,162,0 +no_exit_20x20,Dijkstra,7,0.83,162,0 +weighted_40x40,BFS,1,1.2317,533,321 +weighted_40x40,BFS,2,1.2659,533,321 +weighted_40x40,BFS,3,1.1845,533,321 +weighted_40x40,BFS,4,1.5564,533,321 +weighted_40x40,BFS,5,1.2026,533,321 +weighted_40x40,BFS,6,1.169,533,321 +weighted_40x40,BFS,7,1.3397,533,321 +weighted_40x40,DFS,1,0.8644,361,321 +weighted_40x40,DFS,2,0.827,361,321 +weighted_40x40,DFS,3,0.8941,361,321 +weighted_40x40,DFS,4,0.9692,361,321 +weighted_40x40,DFS,5,0.8452,361,321 +weighted_40x40,DFS,6,0.8235,361,321 +weighted_40x40,DFS,7,0.8164,361,321 +weighted_40x40,A*,1,1.8278,452,321 +weighted_40x40,A*,2,1.7486,452,321 +weighted_40x40,A*,3,1.8236,452,321 +weighted_40x40,A*,4,1.9749,452,321 +weighted_40x40,A*,5,1.7385,452,321 +weighted_40x40,A*,6,1.7864,452,321 +weighted_40x40,A*,7,1.7326,452,321 +weighted_40x40,Dijkstra,1,2.0444,533,321 +weighted_40x40,Dijkstra,2,2.0199,533,321 +weighted_40x40,Dijkstra,3,2.0213,533,321 +weighted_40x40,Dijkstra,4,2.0246,533,321 +weighted_40x40,Dijkstra,5,2.1628,533,321 +weighted_40x40,Dijkstra,6,2.0323,533,321 +weighted_40x40,Dijkstra,7,2.1926,533,321 From 10dcc4307f25c284f6e75a2cce24506d938e4017 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 03:26:25 +0300 Subject: [PATCH 12/12] added report --- soninrv/docs/performance_plot.png | Bin 0 -> 85480 bytes soninrv/docs/report2.md | 105 ++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 soninrv/docs/performance_plot.png create mode 100644 soninrv/docs/report2.md diff --git a/soninrv/docs/performance_plot.png b/soninrv/docs/performance_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..81c8327f659e312e388a52043f75f4ebe58b4293 GIT binary patch literal 85480 zcmd43cUY6%7Ci_ks34*UA|OSP-V_BSbU}LWHJ}(ky7U&Rib@k9Akuqpp@(h*>Agb` zL^=T>L`ukYpQixN@a+gnfqx*Lk4!xEU2Q#m9=pRxG#`7qIk|c|J$u6L4RiN+=IU}!=-C zn{($rTwOlG?WW8JAfRWjTn-@3<1-SWYc zAMw3I1a2AYfro9yS>l)Mjt_!P8Z=WM49Oh+ylUR;Tjh*yox8*; zWzVUeScCG~FdNC&+(K=XSHGfWwmm2&lN+sbnpzC%d3#OJfmzI{^46obl?)!0(ahpz zeIG?R(%b9?(#2e68$2z!pVA!TFg~3z{c+=^rZxzEaR`x%XiQL+(B|2FTCyNx{(6&l@Z8xqUCl+{KS6j6OZA-lo_}2>DJo9(0+VeG2 zY>lnpwJb7$lidltHaK)}8x^uxzaU2V-uUn39!u|12CKlK)6-+unU;Y1o+xsXsSZpV z!DXz(*hgO6yxygDI8WW~fbS-A!|`_0*5OJ<{lk}(Q*6N}9R%q=o0CKze4h-+nIBhJQ>RPu#}UtORJuyGW!wAVag|=>e7v^Ivm{IJ+$M z9LO)^;tQ5m4rW~Iu^SWS0yUZyEe9W)2o=G2{;|Wh!(YrBVomeK)FrvUuZd@GP3z@m!kjQ>=MDn0Z~{3K({(-eqojOB{c? zcC1roCb}_Q>)1u$<#Q}>9>)wa+NJ(D2u~bwmFtXQ`OHcacjH`E5EayajT86bbRgoW z?6(X*=ED$9Y_jTPy`%z#4uU6|`M}wj#9f>|zoPaaFlQfH<8P&vRMjG@V%e|CP*^*98t`E9kS95`pPO8Yl}0E{|FCipu|R^I$7 z#Vqck;Q*2*5>DT6T0iG)t|w4q)iTkQAseihECLJcOsUWKHITt}Alke&;eMJ?!NtOz zbr6l4fAp44wJKFS&7)s}H<4MwJs0x_JAT)yHL$gfS=1q6zmqNa3tsZwbk!eBFhO$4 zZZTK@>NRF~6Q@~;&tJpi(Av*$_>J@?-wQAQ43;l`D@A9+A8a=+$@ChU-#@=E1(X_= z7^wym@IGDeO#D`v-Qn-$_$^uIiMRCp@3U*&m(|dYPID;HpMhYR)z?P~w2}BMn?i#k zy;kRmyI0o5SC?XCy*p9$<@0dN(S;RWA?`Y#y4eUe47aG$XB8~z~0b>15D zw}N*`bp5MPOH6{q{X@jG%h1>o@oPf=;3`Z^Z~Qz=TK`*j>_RyJjdyxk=+18+<=bnsBoB7)<~HyRX^Jo~?1en>G77AMEA@ zgdew&e~VTcWC|3J<$1g5nrGe%?W9R-wuDps!8c~t1_5aI_E=kRaF0s&W)?gVeAah+ z_Lhh&#pGuReJVXCdjqmAc+uz^62mYjT_Za9rJ-RNzaK9doIreTd--fm{t!{&ZF)Cv zoUtc5VX>4>^|i-eAME<485Q{|<7=nK7;O+*f|jWJQ_UHHzu(OHpyAun2rreX#O8tD zed6evCtu%AbtCxac3IX<-eM&$#~ftTiH_P82}oxh5Ccs0SXu|X*<6`SwRJ+8>ah}I z^ur;a{VBT)FC1cMJ6878?N~0OmUTj`H%)TFrSdqB$WGeK6^0!4F-d0iy7;VZ)s=-x#JZgX*VdmA!+`e|G^uRg zIJp0s*c#P#>3-j~;&mRvaV@F?=Z0NV)o2S6hrS1BnVz0lDWkPvWhXx( z*k;8}aBT#ynNm#bxRq&f&3w1e^$U1gsgs@0+dc}%x>G}BaNL|vt4KEk)Hmxmdfb2Y zhTO4oa(x0d5>ycvt8d3A^2x}AWDUKZ#J|8%#VwhIl!&xRx}-og-iNvT0lV8Kh=t(9 z(*5?Tfc{uXMeBNs-S_vLx-F;5Z1A6nB_QEVUcA;JV9{{r>93R;+jjrGKJmFF?E0Ac zbhVxKAS_AH{FjJ5Pe5m#^Nh&tGl|GQzKkj-)rV(o(TrkFA6$=PZ%EMY!bSGUOo4nr$@`IGK=&Jjt=r- zzWWB9`@|9nFoECO0TC0iI(sDjOGwQ6L&%V0d#Wy*VN`eo`0c->tXx&K1wxm!Wb z1av0&EfoH;fMkOBKm=RR2O+cqrb#bHDY`H=qUQ9PUvf6Z#p{>d{V;#F2D20_f|W;l zKGVoCrfd#wnTwm%M zjC|DfNu>uREaPaq33F>c-I~w?4cvWX#Z>{ek@!y}5m2OHe#UEix9Nx0utKj%Z`J#z z$^LPisVhra0Z8JtTjbxA^XBzGfG+&dZjQ$i9{`Uk}TBZ;|ed;p-=it8rK@4_Qf8^x)d;do^J z$^M9TtBuJ9g9@WTza&P=$7;$%WKlBHN$#F)Z>9TkcVn;k+7|eZr|E8j^LKO;rgcMP ziih8zBaA#;sN|w|w-=%|@4cBe>+!}|M zX-K@Jq*vSh!5F^vy*ei6eV&* zsMXeUBuLe(f<4-(XquUC37AQ?OK04J*rPYH>GO9ZzPtBNQ}>$LOHa&s`o8a(`#vtlP<1kOo$syil7{t8Q?x^^>H*F*Yw^cgFp6UC zjqQW2mv@xB-p~cwM$CuN)8V_<)i%b<;``VJFmmjOQ50@IU=H8O8qkYaPuiw)^|`f81O=b|A?2I z#`LF3jKIa9cXXvj%PnV|BZAHuhGEL;=iNJ#!&nTb zU)SAQv@`j%zkJKCRPauaaWk5cm@?)1=}!3m_U!D+I|Hkky`l&{L$#3&UMj@~xnxk` znr60U>bU~~K{4~*{mQ^i^+oq`D4ABX#>4ff0|-Vzx6`xI*{ z_+|1!#c9&={@>Q49XHD3C75WW_uutUx{nzeH)`3z3NCz;)sG0P8%CaH?PJhrhNqnC zrN)_|VH=91oJUfh;rURdJYSeS)mKR_eaTlX7mY$Y-OSp~bO5k=PiP|rN#J3>$-(nS zE%`0o&OWFrs1O12$v}_`gl06sl`05rILFsVS8adRK`X=Ljs}8HZH%FQump2#+osMB z8D`<8^e~$z^e!{u@PpkGR)123AIQKwu0-@LS7y49WxSR$ihB2iC7N5fOC@7!ImwDO zX=1c>mryo>TL8FrmpuPa-Q#pkGcD7 z_;%C5wBy`Tw`V^3Ha_tjMgE~(K@?Up7yZ#^-mjBIT?BsQc??Q9JhUYAq)q)tYrwo~ zKWH^)?;2-4eA$pD`#K5Rf1LI=8~)UI`jX4Td!4j(>$+F5e|(alN(DfV=Tk&rYF*0-Q=fvT+Ig=NklK7#3b1O< zl!l3#d0!v4)Tc)#sKxVo#374)r@|iYS_LEKBcI|C-}&_Tlvl{DiZ*9K+*@S6gtTFk z9eyJvUmk0KY_RUqg7lJj*-Aqxrl-^HF(A!Qll$0TcTKn=Ax{e6yYH;puAyjU;hd56 zu@Yy{3~Y;yTaUQO%M4LG{-z++7O^o=;ZuLa5^X5Z_Gn(rK3!$pD!x93cG4%r#j&2M|082UrFFUKJHi99^K-H%7ZjkjfkYOt0^uDD)a}El3g- zs6`K$tOdXJNj*%uY0&<`zNIBI-!~|Dyxel5E_kx?vCvGqPqX9kh&1vYTT_KN) zfAItKU*uHCjZ#=oVrzTM_jvAtL+ohPrd2u5I|h|J+VFIA&~cyfk$PSP=)&LMnu!ax zjdp8pXVs*v(K%PAR?n*t37z0a&=;!4bA3;wP7T{td-yfycA^^jD}PsMh6`Q>XaH}7 zJi6Klc#J{#9RtdqcvjAq`CtOvCjg)Zl#?1YvzW$}uFKz7))6dJhQmV8W*fcE%HXvY zIcTC7!RA33I@Effk?(v}0B*bdtf>+NJF0KlC`Ina^T%fK%NwH-qq59o{}aEN1heG9 z(SyeeINjsNps)1q6P;-I4ew4|$zbH6g&Oe)>)e|4xh04`N3b5!88~$HX>N@;It|F9 z)|T9=xri71T_@ie)Zpa3hL>a#=4&coXD6pC+P7$amSQM`sUDE=ScZT?V0sKNXFRNJ zxOEVdN7g~NncM4Gb)v07g*Y0O(V^fYS6bV{_FoM{kVa2bNdXMWeBb^Zzba_gxtk`| zXX;%C{qP|2gM6A-JWF48=U?AA3@2dJ%#Z_rqaG>yFD8%uoPK^pf0|^|F{bsnoS*!9 zk+rG#=rify)0205B{d&XK_B>?tWpY~P}@NmXz-n@cyzO0@P99`rfq36HsXVt`u{D~ z!JwT;oSOIF5S(~!1$y^@osM(*X;!3W(C_*CCczjFYHafQo{mI`8(nV|DCTsX8vZ*_ ztIkTF)K`+%2BdMbnFF1_S6KHs_y=_oNR$GXb6&wW<)*i)}u|(C^iafNRoh=+0jPlYQkMCf2 z-c+|_pJBD*E-I8>)#}8M}Jf->33XS zE7HwTYGr3(aXrl&k~)eaaz0kpdOSlx=T6q(i0>K+ z^x7D2rM1ZF{?c;tyGJNT!{PYOJbKx-@8e-irw(I(v%3eMzNzgWlqZ=&*4@Q=gzx;Za*}oH$mZ?yJ zG%pDHI)AK#f~P@_c99tX}@*&slO(ii&Erwm>1dFUeJ0`K_IN)Xn)NHAYA1BFIb8m z1wSJ7H}WITnNdkh^U_AMaei(Li{!U0$p!7sW@OM>0Aul{k&;fx%NTyd4dUA=1R0&~ zGx~)Z^Kln1Pz&43hhCv%+O1QSSFy{0dwmfnYZ{hd%JxG*Wx)sduWyCZ$he`gI{VU2pa&6tt;{&YrSV}QhFjPsmdtYLP};yP ziXp@cYN5k!Mbf6${lo=FPZir~>qIanMyM220Plp3_|XP_3h}m6!yc&1mW&UVF68 z_AI`?sAd85TXT2I&@bIWpZ(>|yYzM5%Q(XNhjHJCgdk7^K2RA%je=gyGNd#}y*cDg zFa(+~rnVw5T>wgedo2HXBT~4r#q*alxLU`FOJbJi}W;EA?oOG7WlT1r+ILfY;#tHC6dI+?92XX8#z|#!XzE?=n_pO?4f? zDV|P>3Z2S8Ux{xik2!}iahR!d9&QgkqrXW2X_i}Zo>Jg6U*5BCbsW(Li3m!F1S*WX}yc39{va$?_A%{ z(F`wHOApw7vo3BA2;x@skNSCk{;zvLwFyF^pRq{JpAH)U(jymWG;u3Z zprWpJ@Tk8*dh(pn+n#NlGXO!Cgnc*#4}%RqnOWn8PR!?hu4iga~(0 zLJCr1&Gjn{@7@SlQRns1BKaN@UdK1%s>yCORB}oO@rg5K}#_yjP-_r&j8> z+#9CLl>->!M&}7kL1Kw%b$*X~UavU{biY5Wg7Jt&R|0&65U>hw+xKyRk=fwcvYj-$ zU2@;Y9tj#B2yN-ZAfrBN$vFZ=j4H|L`Pv!VNK4-852xG?vOFG{-E+J)W=gvQ#n)9ScwxNy zMukKnu&nKr#<9susxUWGV*?6z( zzy<;FuoCuSQGWR+odp*oJXteu^`p4!_V`WOBe>CJ6F{U6Ta*iz=&+ACRZ~o^}LHm{ZBVSRc z>dDu_Ujsk^9qnPan`#Sg>k-ID#sE6Cx@jBLv)#Pc-wQ1hC;+sp%ipVPkLQ2$_zCgF z{`0sTKU*dEmp%H=Q~$Xo|2*^mLnFXHSN8vvBjNuvQ$#)bcMSe>VgIi?BmO_WA^!~# z5lcYF2>|ADdpxI2l$ufGw*CaM2Fdjg7U^?0t!78+NzKq^s5E=&4`6ks9#0v7+Ufk`)5`-uLQZYZH8~HYOK+cskyDidB09On zAGkyWTx}~zNS>0TGBHH`ervk6he%Z91FC(5)$ivOC5toQWmSfP&i?}KM4e6Nr30e` zF3pb#i&e6OlXyJn5JChveYR(ffFFe}mQBL_3;8B{K-0fn6Q}s&rtkX22E4c%BqUjv zfruwBv|jT0>F(lJZ-8hg>(N1u3K5rT0Z(fJWbR2T;s?EyWTrFtEtcFpXVGjyf3ief=V#TDgq(o{Tm^WDnY>~{r7}07h|m1MEi=vUSF%yI z;st&HY$Q9{*78%FKq8V)8nS01eEbdQVVc7uGhu>Xcyxapz>elk-dn_apC}uA+Avzq zCL0tuI58x~$KOtDV}KpvJjw|y+FUa#+MogEFj{!U(=tr+qc~l(|BydG9COYQdNll! z(}lV@n>#-~7nR%G#;`SHwBWD1w4BuI?nMweJ`iNphUFGhlyp1`<87dni+OLFBymYz zXIb*MWZBF5!xmozHzosA>i~;S5sRHw;O$J=qU$*yqbaS?GglHE6E}d4#;MG}`m9XyFl06i_e-xJ6K<245cWL|zaEZ-JDu%l3 zN0DYg@Yy6bv!-HqYdsoA(_{B7yg_`9w*>g0{pzNbR?>XZr2(TMKVuVD$2fX=VeFdP zhM#xCL<|fr1rZJg9Iz$DOC+oZwUh%y(D5vVb18RiUVY`8Jv=D4Y}y3S&1WD3kTEx7 zZ({Z?iNAzgdm=%>;r#if!`>@Ljw1Xx%F7YgJY7KSKMl4r^VE}2w2E2N@MD{R#0_+ZnBq`rvN%X}HNT30XywEU;^h zH$1nQm>-cbLWY1!Ki&!=D6>a#TI@|O4d?h0haj9M>*)QhDOZQ4LU^eL?5G*rSkm$w zbkZ{blnWXS)ySXB$niZYu8UzcCRjWOr=WJO)Kk2)LyS}Nc0XnweJbE;KmNs%=0<9y(r z<}q0BITS4S$LENd@iOzz{d>de!i^O1%GBasdPnC((jPp(WjS#bEH0Eq93nj8SLst; zx~Jwv!D9l|2R-FxwD`Q+R=8Y7@M&YyPPvD}_Z0C8^HY#9D!~O$m6}%19u`96PKsKH z@-tc@zW)&5GY4XW8{ZGZ&abC-shDLD@ikEn4bYI6fD9OgL~@nQ0C@dNXn`97f8{(= zho%0e%dEe$<-o6z1{u=?K9dxxnn;G)P@$H6J@ZEQJgQg2ck^xdZm^`BIxFdBrpnao zfUw0697;QUF6`SPUnlQzNMmZLpvW)OEblP(Sn0>Y+GAc!zgS)3m?t-MD+5Jbr(DjDO0nc$x!MS0Zy+6BQ%_2>G6iOoQ2b&xRVa z`^Cpxj=*ZnKzpMqk~WM%D3~Rqd17O9O_~6$s3`X)_Lz`}NcBjxG}I8$Qr9 z`n*~i&PrikorP0_rZr@b(B76XeA}U<)Us(@)4z=FQ?8u1X*r4jnVuwYjGHW34qDdnNd8^MX9}-~}g4?zs0J4pgoPjkhH1TSUW${OVIB?l5CZ zFT`mQsBOOMsL>qesV9BSGo9w;I4xsWh~gwc3&nI%*%QoZ#TYWd@&5 zg%f-EHN!8R2oob;-C_6ixHz11$R=YRtx}D@g}39hwV7b$3;Zlp4sL}b595^tiyoJDG6+MR#OHl; zV7a1>;|=)oSN=Rl(34{{!5cTmnA5H#{i_X=y(R$o=PnJRaMk2iUKgg+O|`We4%qC-wcB_!gl0Y^RztYrn$3_nt4getbSjBq%wyE#jydObp<`mTFdBpS|FxYpp`}|vjEL_1ECpxlX+#7 zX|4>P=^c?D$Dxd(`puMjxqORuD*&MSXH2mNgqng8;#o))2r8`IY-VV0 zCM72&QZFN-KWD5?$LdAcHp0?4e4Bu+2!4b|F*TP@M0ul?X3k~qJ*JM*xf3@n3(-EH zh%UjXIxCW+3+MjqcCwipU5%AVXm8DRL(|kUw^)lBo(vwg#3*kGV`qpQj?mJ$*MwzK zerAq!hK++WyX&3CX{IcsTak1HkVOy^!dExN<-^}gq1ZIr5@q3R%^MeMY(nST(uI}JJCgxS7XRXKIGx>;?KrR zeFX?>x5VrPZE385)lU>z{ODn(QoPV5Szy+@iMHZ)XS$N3VVZ@sdiKJT2l99iA_5J@ zdlA)P6dMCnZ5;T{0Bjl2mNhlPwV$DBFo!Yex2O1oLOIy>5BUpKuL+vbA)TUv-?XVH z!8#+ub`mr-JL@6ON4rMvu3O&G;qC;+yxG_MqO+q9zVjTh+PCp6+@bT5^Gmz4p4zio zN>r+Lvm;C9zv{xW43p1Rlx6i_B+p+BtL2&Gx}#n=eQsS&M}i!&slO1F#HqfL#1~#Z z^b54u&ykVC^N${r|J-=eGA(o3V?X%X#p}lrkTLoyX)G9@k8$a|GJ{zTOJp&e*TVZ^saY->*xhF!*1evE)VQVlDm1!!2DelS1koTET9L zOfiKA&=jtZ2O35Y3g8(5@OeXVX!W_*x3yzBYOmGRWBwo0cmLMVazak3}+4vr0{#?qv`f+VIsgwztW@Z&+yK`B^R7jE_}T zP~8H#-Y?0zZsoP}Q@0!2=DXnqSP8TNLy08@8N)>W6dQn*Up<+0#H>kKZYh^VzTk^b zn>*IY&g#BXd^-o8TWUt+M~_n#m&eBP^>}6ATu>`ZB?+K%SQg2W=a6qSUByzpJ+f;) z(DfhQ&Fir8mnAPHeNB|476=7_ST4lfy;~SjTH_AGxP+JbOSi7Wwsh^ngrYWHjI`y* z$6?-z(1@!|_B7{3e#%lh%Oe7nzYj6p%TV4ZziK9mYIcHl{E40XzWVzDR7X=O)gu3; z63kcr2}G58{EkuQOQj)SMyjw~Z5EaRgR!C^dk!SdF=QUOI$3|ri#{*$_H`z9(F!araMb-xXeE$j`$V7D7n6fW#rA#zYRB@Dx=|?ZdcG%Dg&U1!dcy4{_%Q* zPKK${Z=`k69J7g$L3nkImOF5|=O3f&2V0IVV^eZO;z)+iNgzqtRpcVHJVn<{rgA(< zEf`?HL;^=YBFItNO90>-E>l3Y1mT^y&=SljA8mwFLKE@P&y@vHy#z5s4yUkaB_aL% zDE^Gh_y>sh`VMBgTlRtl-;5hPvr*^EBab1Zz_eSyEPC0Qdf)z;xu@>jhG+NdtGzWl zGLl=$6>-<1)E@BJm=(T{V>(P@BsH1e&0m^w{6fAYDtEt_yQS!}GRL1s$*iAX$s?~l zz1|g$#;C#h^v!Qa170Vj^qZu%6mW6E8CB1$H3K($|ML~M(-j00rvU0*S+DU>I@S6U z--b|{b%U-#kB)Y)k*=R^8^5S$yTF3lqew8$8Qp^sJ+BtpWbY#(W|U-`)3qhSgR05( zC)H39uaysatJ4PvD&bQ2zR{D0&pYK(J^9((W10#<8c4FKkLfPz-T;m)}8 zfo;luK3Z+?r3pe>2cnht(9n7qkbmLc?{gE2M*`*vpGu8mbhxGBV)V|B>+H<>t)vN& zsKcndXbUn$M8iCh0X>5Cx77ofo$&WXl(?ZxSPQ^FPuv+?4e`}gU^hWof~xX0XbbK3 zEL_xYl*f?6n1z~h7FcKzdE%IA{qVaMIVPsLCo43$OW&6eMsGTUG@^Z)iws`no5YGn z?_WwP;$s?++KU3LTtB&9Q8@37tvz7do66%g%28u^@82tHuRgT_?mH5Kk-A$erdYYB zW?6&nSIyxKSWE*;-Ab_EG?`}E{Z*Di5o7<>YmRK@BBNLO)&PISO}>DvgM{uT$Ao$z z?+9i$_EXM{mQBhvtZ7j~UNGYyfLj`!$m600antvQYV44o?iPww5X3fIVKh2o^Y*wj z3gKsW-caZhom_JLnS?{Abu_`xs+5?pZ2_yX4JqlDrN7Nu1a0we$4TWdZSMQA;+TV7jp*Cw+Ne`HX zTV9RbZ`M7-FEy0vcvOFGc-k&u+~$7b5(gB-Kww03dz;&qBL=^?n?5w`qtN*iu1&$N6wCaoFAiRIcbrj?O<+G8VzBzNQ}QkWbSO)8ZOvB|qP27? z4Q|If7)E4=(B-)pk|30RI_$quODMb%wq8+?4<;MxfZ2>;VKJ@YILTInUzDV_Z2l*? z4{xC+hceNG?r!Qu_D6U_^pgyr_KlnH0Ke34J^@~`Z6of{swj;Mv$3J^#%QV0-TbR~ zl^9X-J@TBGRvk>)E2rH1pHwoM4#I^lzA$qQCQinv$Cq59>8r?mNU$-8217F;iq|$C znPMz=vU>>`Y8+D$~ zr>H;4{@z_jw?q*&o_kne<)_7Yfk6V(3E{Sr#U?c1GVZq|mOJ{Bnhd4tljmf*0p|ys z!b)n~u-u-`N^6z~mXBSPGlxDfk92cgc*xocwl|D5rM|Aqrcl_;5%(55Ye6bV*{a87 znigIX{AFgsOk9)CkYNqAhQfm0ZdGk&?s+GRF8SBPdfi*gXjUEDNtON-^BVg&hnd@M zZ=Vi666iel37B^zKRK=x613nJl$WD8FN-iyitTsbkpTjcF7+?6%hJRhP7fxAaC7L& z#|p(9OrNANRx*R?`lUncaZz=zYf{5__7EbE{gSJ0<%#gjtB^*I8eLBGxs_qWH?k_~ ze!u8(zB&0w;Z0BAy*Xmy3iXVe4>lF$F)lNM0Uz03^mxpDm|JP{fL4ZagVU3F|Fit` zYiJ$gJ89zUT!P1U-G=u%d^^p})ct(d+`~21zTV(Pd>wsWF~kO%=onuKnB54E2f2CC zm7-TIK2b-(#sF|5`G|XYZ^C*7QB`+y(M#w9>td}-s51pYRbFQeY+h~5QeVaUjiadh zBo7(Jyt3fHvGh0zCk%_9&p<#l4T5x7#GtA&h!HCKg=?jnIbt^65Q@F!9y=m~pWJHj zUa7-`vXJO#<}ib9>A<3|QIqijc@a_pfLE7V!r}L;>WntQz@>(z=8FE6%J;5+C0{FT zoos_GV6&<)ajem*(8Efa!GUcxP>WQH(!tntznzZ>IzC_X8asOex(uIsKrneTmS}-d zHo6Y8xbloJY>V3EX*NhfY{JB;&VteXaQnq6)*aWBvsw<4d7ZmgDr6E=cmq<6>!dBf z97AJGtDS(B1x ze0$Lt?uF2!Qp%AvXqxHKTh^8Kb~X4M#dvS=x2osX^cZ--bfjh0TpykC@qDY-n~c@4U!9~h~*z{aiF=|4j>jPpOH zRpIEy1}^yJ2~B?+eznt2J|?t_WPs7nqt>J#?}t67-gH;ade1m}&v{KWwlN4 zOZuFNE>{fhV1U%wy0TTj&zc7aLL@8ZV`aH|r`{C%r05d_(Fb6rQIYk&3+gA8Hj%)q z|7|$Mr#}~a$?$hylT12eGnNW*fv7WH!Es3aSqLw08E^Eka|ArE!A91m60IgWYE^24 zxh8FXE3|Yrf|hrSUzm9gScdfAvefyk6MD^geH0%cYwvXf^t> z5CyPyn@Y}PwbXP@bbQw>^FE3`F<}finYql=u>}i-gLw-JqbZfbgle_LqIjj1qaPx( z#VlDQl~L|X(c&n4R%ZIB8fxu^;Y!$)=KZZ}h?pzUQP9>vzmGGov(9=mIk3mX2*cL( z{El>*$X1(YjhBejQcIFdH}Dd@&Oh!?tPGN>OcR`!1qBh)7aTMO<~M z3?&XZw34o`qu+=%XysKXV6VK$IZHw~nrNGarv6(=7|IZ>%wEeT z`tH;5wyn`M^V$fy+eOIzqo^^VVxSV&0+)#;+SpmLHpkH45;jSiOYD2t&yx3CyzyW# z3APqSC3K!KAva#Euiy&zyuEc=SIN>BX2TM_5Tb{;E>&n2?4eOG>a+w62HgL5B{)AV z)HF+|_0N+HbX%}u214ytVN@6wxxphfmIr1u2wy6CjccwBxzB2Z>dy2BMC{SeEqpJBa)D`1_N<;R{ zt_gK9UjZ>FZu(u>YlrvDM~BdogbY1_f^&IAP{8=9m$8)Fa9&)spzEn*x$*g-M!d4{7p7 z^yhfVN)ELK0zNq&7OGYzU9hR)zyORG!|B1Ne#ZTSIbh&mpeEy^m@`v7*1#@yx`v6q z{8C5z6`TWGN!cC1A>w>;w?;|>FFKgl_Dy?7f0$^!^aw^t!z#5pJqaX%AXEhq#=9V% z^Kc?B=ix&BGndmQy59q9hPqU;DGfo#jsdUT&nDY-#A9_=q9IArM{fZ|(OE3p;TYk& zTz2oC4xY#?QZwm%Gj@Gf)erNMw%CeR5qbH{)H$c$&#O%8qs`YQiV6<`5Mm_Popmza zAH|Ih&oTwEw6qUD3p;Uv5jLWB@a1!&BVr(p$UIaZ;h-$ z%RwRSV$rWAp-<~)q4I{PhEh3Z;ZKD3>O#-c_(K!QO<1@fSMb24Gq=0x^FD)@-wt!)SWaV`DlV#$;}FO@F{ zTPBR>Mz%xP_N|0w8#B8*$(iqG={EmJ$C14@@Yfn}%xw#<<0rS~9aKA(XSw*%v$d2q z2!g}0Nu(j}(G=b5<7dpq7mxDl^+^xPYKwS5#YRK%H*0oaz2VKZ&91l2oE&|20H%1It+$t32^#%q~0h z%zDHiyMtlHP9d7NM-%-!EVt?d`-DUzUSqGjGNUqj1uqGOu5cNL*Y=iU_Ij@>qq5^f97c&^ju3Nv zooWU`CO|ZItdL6JZuUyc6_)ibop6qOs~<*J*${q1yZO32>jL_p%K)D*beya&okw?xbJWCHedgq163(=H4vHQ; zV8Z-)PRUqu4(%n-1$nGsArbn_iVVNaYaQtK!zzR{O8T-dmN8u@t&4zg(!I{UGQKWw zp{~@pRbJjGEBByTsA0p8T5-3)nwdQNS#Y7n0dcgHY;;st59rklmvg-14^(Xv)M68+ zLr-UN`$>csG~^B9>dxF>go9zKa{a{?+`LC;gi?FS(xXs|^-3LP9%(iA4G$gb1mt+J zbB@>G%K+;0dgG64+nPl_RH#qum7oohs~D zsfq*T!N-*`w|aNR3e24-`*)w^>tArK%MktH_r}AI(kxH^7n9KFdgu z-r8HZiEye-{uKXG)=ojJ^Q8;*%h?U$ZxH7=Hh+(TOVy+lpm$a zC-)Z{m92+eGeqj2DIvL?KAYU~*SZ6K`S0V63MU`v|1?axd&$}F{_i0km+kl_ijax! zVAi4WeJ%;D{(LVK1QYRbyA@=~DX?$Kz{v{#oRo1{9Grkq$DQu`2@D)D?1}&v0+K^H zW{3Co9Yl^);Gar7Rg zJ65FMDP4Z-Uj~j7X_^4W-dxvY;P^5Hjr11q3VMf{5NklJD^RC}P^-5X?6KgOhlXYR zngpy5jL*kX{do_(!=@nIox%LQ(A9Bpyv3i}|HKW6iZbAIh_~ng6Bzby0$O#Z@E^5= zqV>8Nmul&NgGnIaCpskqW@U`r$_sdH83H-Mi3OEJiG3~@2v+X}tDk(OosgTr%agkl-N+h;ffU9oV)rQAn`nEx^@#aQ4QS9w-s#UUK5bWHr)-7z@7vo}5Wk7M zkS4i)gEmfgyjXI(;qx|XLx@2!Psu7191HUG5}OQheXD_KCfE10w7TEDp5M4!W$4swE0MYx2R=0b8soZ~$7YbpTr6 zm8Dj!uz6iCuoFE6p4qpnEzO;^8fJsUh0ua}1;`QsxJhtE2n<|cJ+FTL zB=HoEC&^$s-5h*x3v|-NIeo5Q&bfbw3j0~EX#Zcty$4WKTemITsF(vP1`LP+6+uv< zs3Zd-7y(5jszy3W?wq4L!KTIVC!Zq5gmnVMS*H_%eJ!q%xddsm22l2> zy>4f|#Okr1m5mL1Sp$E#|Ydc{qvlYs>GS6uhf?g6^4F zv`+AA43&A<9xlGfPXy~ew?)kjle9# zoc{8qzn9z94Xrc;XX*ToN(R#pJPKN1^(EI+(EFa-5VwH@>P$Upc>(nhH*g7gWQIdp zYe9~$r=DG$)K((6{HqcAL`Z(#)(=tND*HAV<(yu;7}$)Z-;$yHkPFAjw4(3(k$HXf zW$~u>w4|8j$={z%DL&GSiKqJLhI^vni5Era3^#$yFDAta4Kc|UU)HEZ$gkm)Mp9!h zd8&FdJb)Zn_3z{N`J9fE#tCc7C&nBvU_m!u8Mk~RNc}Hq|F>(tPMKAlOl;W7;rI!d zmUfT*-0xWM>+2~-tgx$^rD*-c9wKT{wp{s54mE5ez&LPEQkv#9*xxmvxaQLH{$tJE%wvaobCX&uK> ze=z3w?$VA&dv2lCDk=L%_2dVtt8$P99zhgUia$p+sn#X?wxn!w6yhzm(ee`_FP_C4 zkmX$Y{`K<)+|TI!d^zYjI6}r%Zab!*I`QU}QWZQ^K9}=M4pC)z?O+y2@^uIt3W3(_%*_CIWrvV%MU)OYUV>JJC(C1V)1hrEeH&u= zJ;}21??rwQH@U1lLc~-b7fTHBSAIQXWo*c6;yBT#eRD>jb$({KDK;>#+Bm2oTaKJC(PUKNKOuU zXPa91dCrEw=>q#FDKN_%d&*9z8I*m{zrOz1tHhHBagHvTA*fI_0r!PN?-^0|P>xeo zfKNr!+H-#?g~m<9dk-kqG-nE>Ajh?0PLyOo}6UE z69(6z#i*)!`XX_>rZ0sTd|cxBIk>Oh-1%SEqb_#KlVg~m7j3Ec{-CvvCnP>?HQf;z zldq-7<2-hPg*vQ{JeR*uMxPAd_68{YozFfLS$<-J7luXb-*h7w%=gmfpHR&2agROz zLZKv3eu?8N1fnia=bf<|zwzwkSAhS;%R64JAcpTfdT4QBK^gC~8(Wr0nQ42NVE(yT zZ!CQ;9sG6C`}~p%FSc%4er)ya@H!+WSle3LhM~t2<_pzMvN#9}E$;l)YLQ{uNup)7 zfc=zu96mLo?`bO(Vm{$uUg0$6@lCY4c5>!Bdv;F#Zmc7Z^p2r%E%`ZqWrDP_b_dVa za22jw7Hz9Jp*v~7sRuul-`s!y)UEzC9d8SjoYou@CX0?!pFET~l%qZg?*CiYQ&}Uv z8eu*uTRq(zeUC@HyLdIOSh2${d<2}&KEf@@y{GSp%w;shF>EjS?I5CkXhLcIrmm8B;9ISuxl1fCmz-e%9Xug0LUEx2^dL zbFfPTX&yCKr7SsV|8b@4s+%(Ddj}Q2YZVR$e;sOky26j;^vzc(0W-9R^BZP-c2#L@ zmLX4p)0Y>M5R?qx*rxK?c<$Gjlq#Q>S;y@8&~;bDt@`=GuMxd(2RPp)+huLnwGNg) zSv;v%)9!vLa%l&is4+#o-451D!t(#f@SS&cB45GR#t(xtxgDXWIAKV~qd(UEc zxB{g_P+w}=F_BFM8MD}Sf_zJ_FOjPb4E}LneewfT8Is3dT{q|&U)Z>Nj^M%Wg2%B5 zr_%d_{Gb>*n0&^_=6n*83G3(6q&}jLPwdDP%o&D(csY;KUWgJ|$P+(8#tIHdmtCQB zg;3Iw9sFRm=Mwcx!Sm(^I)@_Dxu{cUpKABx+sq7&C?-z8wYYr#m1}H&7^T(Znzo!@ zJy}7b{==OlIQd}_HiC$LQ;h?tb?YqkA*5t=DK)`+m)JvF6o%!G&A{wmnladUZA!zt z%MYdqC-QAgpx!k3hKGLV&#E0~_hXTYvHAUz+i9J7f@b>r9zePKk~yxgIWj{#UWobE zceMW2Bvl|b@!M-Nr)Gd9>UF?Udt1f^t>6P-A;R>tT+AN04~wb=?dnK7{$O7_9=-F| zx382ov&Hb4PdYwz@2Z)-@V?=_hSE2jEs#!U_x4h=ut>?8w z()%7&Idm2D)C>QeLA^p@}_Y&`#u=z6*NXdHfnb96zZO;ngu!z z$mz78hZj4;ZYeWZi>nu*0JYnHb6FMYny@J+Cm(-td1jj}q)jRAx{&m^s!s3y1*26MVYX?bWR*?VLwB z#SC(|P(oc_Ad$zZC*Z7dUL)^rV66p8JmGa~?hyDOdNI#8jua8GfzD5&!Dg zu&4V70L2u3RFakryAo~!#J6nkLT4+#pbB-PeEc)I`5vYs>ph4jQX3}}T0N-kQr=C; zGee27s>T7(hxt6eUlN9I_%zpf<%z65t1{15q6%IfFxe5Dcjo{a(56;csEmTFdp{KB!H>!uGN z@}Gtc-T9D}3?)HMRr1S>H>)zv82V|lD{?;`X$qEzRxI#wkQh~<=syW)#bE=TVET4S z+8C&{`*K#K%YN+AE#me;^|tc}8B3y8$X z-yz|I>~-y?o!zZt-K^-c>I^zI&Twt)cR1*{mT{Te^m@w;M%YO+kSAuh0Hz&A>y4{w! zm#=emIWzBxow&KM^&E^kf{Pc7WpL>MPEq=*z;z|vk-=J7&Vv?1Vco{h9h>hLJ&ojK zxIehz{%fYQX924IJeb0L8%ROU;*%`Pm^E87lwq)n+_6vumr1F%l29-Zxbk-e_;sG$ z78%XVp9@B>i!2w(W=IR!bd?F!LGedlP9{~Z=UOdEzl3oZTp!Mw+i81M^=+#V8d(2g z9Kp3HDS;2VAWAm1bSOF!<%5>QH{fx9F=`bqrD5CCs9OlEH;+ zBh&$a*Kn(K7CNQrm+X@^nK7#pS*2s|6nMGf?#H05q#Q;}3pz}EE%F3}-;ky^j#^b= zHrAjU3QLuqJi*A1SgAesn9N|v4<1NwZoOC?-eq#zbC&$i$0)Gnf5wW%{}5b|prdta zS6X7)+}5d#rW=xqUzRci?+F4RySD-*o=@6F#w?}HJ8tv|`fjqhTQY^QW&gh9d3XFv zJU$U2A?x@vaT1tn;ZyFzDQ0dVd3E)jbDg`l(pT;_J>em;AVc;->dV?v$p?NMd}%0t zw6z~%U6%I}Sr5c)mTTUC;$vX_P4U7IK(YZJMy!~d8nuEc9r)$l9OF%+^Th%5jz799 z7dO?5{)zV=r$$e=rtgC?;9Pt|j&4`d-+J22Tq$_7ze z?v%_J?nW<5e?vv$>Bkrk`NpLYt-3MP^Ax5jW)EjZ(IbNF$h!hl0SDsYEZgOGDTTOl zO6kgL-LWxC?TZIzAvuXb4pojY(Mod+p!~a^J*_4P6|vx&s3YrE0YcazWUGd(>_WL= z2OA_thc_V!42R#cHM9nYF{e(Vr0BuKDI-%}WH7tCep0GENl-9${Y5r}=(d5iWzz-8 z@h&5cvy1MB5*fQ#dp|T_l%v<$_hIu4yq~Z6&(~si;L6{Ob{ZzjW(QXGZ{&Si(k?`wRxBVa zBtI1-dK(k%Z}&|grQS;`3_z?k%3XuZyEJifp8mPMk%Vf;M;7xMymd-RV0Fg}jn%Gx z@Su>?&t%-&Y|FB5@w2JE(sh^~f6ksi?;UB&mUdws$_@0^z1vW_5dz`LR)8MGgNN}_ z21{ggq{@$6_OpANY$B#Dk*)NcnhH#LnXVan!>vIxaJxGFYzl7c!F|5cY{&i_**~AN zSGNX?E`yH)3ZLia&=vqL;SLJ00;{X9?@Huj^q4`}L%6r%;9=au07pn1N)Yi)KpWV% z0Q8EX-c|zckb&;bUQ4sc?Eo7gW=C80BI0~elwAJq!3%rd3afIX;4-KZsY-w+LUf33 z^TSKLENFU45p7S9VfW(AXWx(`I61BnPA&YHp1t#n9iy@L;z$NHROFUcX1BMyrL1_j z`vOse%lDoc0eQZM7c=ToDT$`PvF@C#E<`{}v^dejW_gsqWH^rg8w= z?~C0#da+o?O-)L|qG1T3g<4GvXEInX!Ry<0Pt-z_p(GBQx$s}e&a7MGN|Q6@eAf+B zo})Kc$dixeX&98s)IhwcDd6@=2q=3hEsHdvoF8E#u-1wOwElcCdv9XaR^u4seftv6 zD^z!V4gv+juhoF+YU-w8&UxBgoxgyo9Q_TCgVNy)N1aZNSRcV2e@Uk6wpjC1l+E6r z_f_H{T%OBma_m$1_E7r!3y$!_HB6EpF;6reY{}wy0C44@GQan0|>Vl zgd;7b=;{&qx`PAjOsXVhfhy~!2#=V`#amZ+q?K0(*P@&22sIg|A+<4}z zKCtvy=v>NH-FNuH8;P8Fvz!!Tl90`i3+1V9YYX5RjpE~*u&n*O2V-}5tY+ljFD#=A zrH8LdMpT^}iL#hdsVbd?dM%*;hxlqo)U^kJirJI)5@2oJ-1FBWEIxT9z0VM(ldGRvH~`Jpi^{^P8XeJ{(~{vw+7WT3m#j6IN<*hHsY% zw_wykNuv~LZry5w{z45S{)k=5he->{EZK1|OXs}w* zLJ#ZOX60GB)@4k13%7B1MI4HLR^<?#|qdEsoWcvQvq?;9bj7Xt;g z4~IRKHK^sFv(?QEUkqb=#2h}q)ltSE+sJ5pyrXXYe25wv6}{+wU=|qI7}u=i=O<55 zGiIL0BPo1;ENMuQ|`L%07S?IObclSO%_gu%9{S?aQ=d{&NQ1MuO zWO%4I11gF*-{1#Fwu*^+xEWmyviCWW~uWkb*?;@Ly za|71D8?KijYv`tdJVED&`~OI-#@qLrVQYxrsB9TFN>P1H_7hT9O13w1hoBHi&s&`p zf64TVrE=^skf1R;m8* z{qEF+bDC(;GUCp^?1n-=-;>`1S!Fr0_>-iyU!}7k{MLd6YlkZm#9tU{+Zo1QG~;{G zJWlSQ_+ggy1)nMVq#G@-2E1jplrx|BPYv#!l)kEb_8@t+O6?6%%qQ*T%C{!PhPkrT z0KzcX8)_HEYS}C*BS9u>v)$f;uiZ2Nxna0L?yV9acMrNkj_%O^a1%>;8JUP6=0-St zhNIfoyxGXo3|47A5m|nC_NqO*tazEiA1=0BVuXobWp2xX{c0;nmpwiF#p-ExR;X!? z9oC=>oqeo0+avfx?5y8q_jk^Ll(fY)?6+SxtZGWPW&cH6y`%%d}^`-sL}8yB~H)Gw{LKv|P)2W4k9xKlpv2Q|vC|uCKnUM5sF0rmg)>ek5Os|&cfPfYD=n-nA z{ROhLP(|6Z8jCiyZ=Rx}jeXlu$A+f*Jw?2QAYu&-1+1- zL4qWbfPsuT4EV_uplSTMUv{JjnTGzQ>GpIRl{P~a^B8$ng^o>CeOnE>^?=85)L2p> zjhlNI@|+8j2J})oITy$)6u%RDBNT<(uiwAvk^c-tp5H z8OEF_iYI5aS35~Yh5sVl)xkX4qr1Ggb!;UFu6x^>oVvg+nGx(ia=cT_+I{!Y4|pXi zZf-{&*cx<*T;Z6{sLbM$9uu|Y`sPV>!-qcQ+{P3yCoUY-V`3UYpzt(v+!~#+%30a~ zEuy<0i9Z7n@Z(pw^B>I?`!jehFv2$6*h~Tjq&2T$WQvS2{Rgga8k#ZwbvRGm20%=M zWw29fC&D7!%Kr6B*hBK$kT=o44+9N$B;?ycr4!)gI(Aq&CR5k%18IIWXTvt+O z7T~ryU+h&&QxmhF(*o#HxyyuvHpV4kMl_I-uob5dcB&9WNc(-DkI5KViAC2m^eFH; ze9tn-9|8(OVf_rqM$n|JsaiO@{IdMXrc+0%_!T$9-k{byuRl^0Z%Kk*Y%5x8JATPf z!Hp7`(Bp{IPUQhSv@k$2*l|Qxs-Nxc$2np8BHEhEpvRc5x^H;oTVGxUdg{`wi};b! z+3>@0EVNsef#zyyO{lSUg&Wli&o$i|fPU1))LX05D#)5RR5kEw{vH0jSP*+6y?0y^ zshn+o^YDTxw41%jkle&07WONzh{p=3xpTIRP4;4&LjZ=WnlG1VxcU)FX&43*NNbQ7 z1G<#16PHCR8qz{X9$ry>rX{F?)3-xj@A(Z7qtXoknN`he_jM_ms|;M?~$nkG8jh?y}AYU%s@=MDPve z+LEZ?y*X6ia%fD|9pUSoOo8vlkdp|XZAOkBi&P`nHUn44xV(7exp!<^xO`w4@KLoB z)i5`8bP6q=j$aE1IMcpk$a<6SSi7>cbu4oEiOT;7!5@?M409EnudAR+J5vyWkgL4J z4hk7V`?+Jjfs8PLHgud5 zfA4cycaO#vsUCO3`S>)`2ruFC72QWj;kT+70M{nfsY&@8zAbU!Uo-6v;0iKy*Rg$e zNI+0SUs5eCen)$nW#gVr=q;x(P7vP9L0z1M@#WG8$aNNaAQ?i=pLFeJnu?Po(W0?o zIfnj3CUSW;;9=t?y8B7kgS_c*`I0WPL?IM~>fjx;NuTp3OPZ9tCTz)jz2E`~lagH> zcUY!RRvf{4TQ#uyf|scOn&uLutB3>V{PbrbWB20q|tx?M$X z=%@Xr7+xz|kMrxq9KS*PrI9*|;66WWml2N)?sCm@xeCulDAM9---KlAR%0^eV(U>& zN`$sg{_aAMbdf@h)oeIt@$UqcW}S%MgjS8Z9?TyawFPW7+j>3rSa3Z z+lOK=^urK-nU_610|U{WHKL>tfRgJiXQ=>}3R7fzwmZvTV9MQ_6V^6H0%{QMdkUd# z`YfM~Jyx5)ZBW;Iy>#o}0mZy{csbIe(|G10N1owJY_;hM54dcnR3M2+5j6|;v%X7Q z6=&LN!!_ZwKU)QyE8Bl?FDfRTnD1kK0Zx4^Ek)GrO*Vn7AIR2j%fN3RX^||Zz3o#D zblUEp8)gHuCFVX-`01GM6=HI`O9Du6t+Hzl7v^e%wbP7+&Jq@NN;aF!KgsKT_gt;^ zflw#NCZdVLWfpg)qJkBEei)k)I%H{}S(Ve~HMB`b`X}hG)nvW!6jEPzzoFyneD$ar z!UU>SnjdE4(Q<7X0d&7y0jHGf$$hAJT-iaCYn82dmXIhjwmo@up}3Y+Bq{q=(rjQT zR%*|+{N}K4(Rt^d+8Ar=HWL?^-^`G9SH8>MQEEsa*!PQa!%{fvg(iPm$@#qQV$r>> z>>L?1hYIHy;TyMY*e|W(c~^Z>W~MyF@ube%v`rWx|7Be;*R!eToa5Y!>h>pZX^o!a z56>xI)Q(FyL7I13^Nj(P;dCK0(qRfpoGsoh#VK>#(`Pr&r}Vxr*SLLR9%Y>#W3X2< z`iA?Q3IK-Au@C)b`uM&t{8Ty9{nG{xR%BuvF?_<2$F}wyPhw-WM|Uh-E-L-xhPz+k zk|x{yhMtzPVGyc;18YCpE2?+|oqrU%qT$*a>t5OiQtOe@Oj1 z(afsj^m1uyJ^S5GY*|&R9MjU|W7NZZm!MnT?n1NM(r~-oPLB>LcQGznu6=JR)|mW6 zGOiQ66u*Deh5Wk=Bl`<$o`4D0r?-qtSak{CyY1OOg^B#MP4dupPi_I#7inIinoqa` zL%KiV70-nmWg73?8Ols-}7++88vk5|4;HuM|g zDWX@6jRL;4O{n0FzfvUNelzXt&UVwQ2SR#xiZb)u>Pf9OLx>H*2BYUw- z&`r7w()(wG+P4n<*y{}%nJKJooN5o`Ic)5K&H+9|f_e~(;hwlP z%J)2Ol$bOPsi5;mX!GW^Yz%lXRgJO*@CgIsiQizj6Fpw0$$s14?6*M!fSCLR$=aE+ zF%-To_quvdYXEJ$w6Q~U=js8^VbN*}loETbe3YsMKZ<@1(@ooW^0<&ljKaFScU)94 z|4NXUKYV?`!?R_VpIuoocAs%`zT{u88a-rir>+c07PuDKHT^huwYYg(SK_6b%WwK_ z6-~BQ!edI0}Ushq3vbL-iYfYzTrP13*f2E*IYbVNG=ZWm31I8)} z``5^YOy4WAPMJdk?KDwP^(WV3TeAB{rQ3gi-f6Sn)gFf|zzw2%9=auTa5=FHgE|Ig zQ)iVN6m(A?-*Zn}yvh1?FR2VK`@00<-p=cm<~(X!WUHV_+VFvMD3$bt9wi>m-EvfKX8L^C56xZy|ON!z2u>8o@*O3XFYs- zOY^<^{MQiS=;V4L*{#5Tw`DYt$f6bAvj8lY?9Z>ukp`Y(H6jQ3WQM=pZ|R^-#~4B~ zHMl+8K8f6)K7d@Ecdb<~mPBtV1y`?JA$PfU^+z>X=qw9p86WI+##BY8u%;InXh z>Zh*vSZ?h{`Z2D{>nRqV7UCEXHJpY_w2lkal$FAwZ;Sd-HJ~##|(HFKh;v(<6w>WfT@jdOo#w~EmteF6Y--rAoTHR);V`x0SD~G+3t4b(a|9v=b z+dgT_=s(zhUY<#Q6beA9qO(ib;KRr}oL`WQ;EE8G_bUbl zP!S3E&+w(1X7tzwc=hBS9#e7KtU0!b%h+|>t?X})i9>x41L~aQ_7{ZdM{q?(OAR&u zb;gRcV-2hN{&mZ~&^4v^=57x>wi9%cVHFJ|EOFTZZ3(>;Xq zjUM}{vi=X{k@GGVphp8IY`{^9p2X$>dkho!CtvRL7=07g**hdApJY7jd=w0n(uv@- z(|6m4X2>{A+!X1w&P#kz%^Mr11Fi>}NU}!9#0GS6#i6r{JQpizM~;+itkW)qa!?&E z#P$zw`kmo>@S z&JND&ISZ#&tQZ#^bT8+Zcnkspsa1oh*hifOjQ@lRk{iqK>pu>RWO;M9kJDo{+ z>=RtsYZP4#ULVvS$e#}>rAk`q3ZRsvigHIMcQl5)2P%Y(JH$oShAYqH4juK~fE|g2# zc{nd>3$_b>#Vk{E8WPxJGO}Vg$KCAmu`Ew5) z-*;YX&5jMIs69U;b*dU?oUzWEQjXY95L2?Z=g(R3@ zWi2ShOi|_Zz*e&idS!hKsaO&K0RwjIqW%Y)3|Px6=*0jNXtM_RDd2VOxQ{t{ExEco z&@@P+`y?O`1++apY=(JFJd%J1`3N8R3F*i?_I9Lt^}4)_jr^za;eVvqJ|*Z^JeTZ{ z<}Z)-{&2CpC#>@@bw9MZ4(~1NvT^OY9M&PdSjO%>0@n%6#iMFId_zrjT)x^k^TB4l zVHPR?C~y3Z6Shw@p>X0SmtAa}mM?hu=q?br)AytyMIdpG@TL+#>W&p;>&zR?mZ!_ z>qDwqdhr6{vcWBuEldfsl!ol~xN}#FR5+_@1{KF;pRzR^bh_HMsljzP=}NDhl%8bH zIsW%Nf5Jk}-;@)XL6Gsyeiu85HSgW(Y1P52v2&|y-WbIc_Ol!wQ3T;jW86u`g16Mu z+~Pz#qo(`!0vo^sW%n8J;M zWtNz>RtaDUm7*(AvL8jZ%~BCgdck>wQ&jpMXJRzK@+(lb$_V$4VZMnIr&DDQnckDQ z^L-xxo*60JBVP_13>f3ZlDOuY8Y<-z=4qRsZ;vJq!RzXTfRj2LaJvGD7Fz>u@3BzO zTr(jNnI6TOe2W8#=ARAP_XCh4ZX_Jg5L7txOi+7JDo6W5{p7|Sm+d|D09dRhEwfOe z5#C0b7B=Gkw)$+=r#;Rp1d zP+dgWxfE9l1b8LLvr?ci4u_CXp(Yr#ID67kO6{fbzrDhVl&a@cm5RN&Oah7{(b8hns*|a>`Q;BsbMc4ig!oPiFk7R zu0N)zp8ub`4@)|5Udf90I)A7O4s&;L(9 zhW~BB?LSxczo-1IzhR|dFpW)rETVM}Q}w@Pw+5Mc4G79iVCZLxv1N`iNRQV@>i%{8 z42!8W?7+pBBl1_OLrEbBwdwK5S^s{K+c6>z{v*%&*RSPoZW-whD}(SvYuVjX;2_8& zDRAKD=&PK4Z;Z6Zmnjpqh=6dU(Y8IUm$nU&1P?J)>ks2My4f;c1v4`l+nTh%Pfe+# z5{?W^_${zNRaZc{(*j9oo(GvcjvUexM%H|mC&>}e6k-b8H3~YaKHg!{#g&ADbo@wO zUGQ@8q?RL}HSD!@`+X8u~j|IIW48j7)0bM))g`DPb{HP}G%%p>d^U-|u%Y+>VeLJr+kTV?f>L zeJvtY%}`r)+NLax(HHSO|9x2%WKcnS)T8(`MtL5O%6nFQ~zKsrh=W#oJeT^s(CeoA{s zp9Kl(AdY0y2tbE#f9c0A92Kv`Y;F+gU?$r z>K5`qDTbD;+-lp##V8`K>Od=Y@Uh~wUf(e>*ej#SB?{M4|6e{{X#;Z08^%L`D5(sQ zFWkaq{tElp#}?d|t1(OaagY>{)chvUUx~=Wke-5TtcAQZ7C45%o0LC=>O~A6=ZoLL;cu1g?d_<|9U7{y z><(mbA_baHS8 zdsYM-N8Kg~{oZBk_vq3`=k@Wna|EF7?15A*F*BT8oU0vsb!dN-)<@TbCnq~V)(UU& z>nYQM#Vul|-tuZ=0`P=|#u% z`kz*~g&iJtO{t1XE7(st;oJp@FK?VK>XVlENC9?}8k~{$v8EOac}Nb~+#6fM)kSsK{LW*~p}7T46{ZPg&yw3!aLNZ#ytzCQ-@pXwLk)A{;odCc zCzVHVxvfuv{X1p+f7|;wqRf{({(3%b3}sJAf9A0H-@XMuK&aeMa>r~!iMTF?Y4we@ z#LeuSeZ0!YMkl9^;d8fTJ`6ZL3+AZf!;ReVjVnaMRmh7j#Ug?>7D8(WiIjR!Y(rWb zw0Af|;WC!rz59M+1q)dR^XHylz|2V~-!=p|Ue#l&5L$;!BFR?*##2z3 zR)AD!DPM>Xe%i@Iwg{@nD%z;+`SFX@K-9K16Xf*3H4@J3aaw4|T_T>N(z+_{()*H0 zB&1LOabV$EH>$*`pB9AJBi2>I(H@jVt6}vch2V#{n5<^9eu>9m{nzldJ>G(TSYlO>@6^+j0IWU z$*C2tn^sQyrP&Yv>NO=~AFag(`60^%2`T796L2V%U6+^S1TiKtr>Pa=kD@_fr(iswS`F~1^}{xDcb!IB>jev;`vgW)75zu@+V?M}RP0d>59=csp!0I5G@RJ+Vyby1x_F;)gstsKt zM=i!q#U`~S7`}am+_2v4*VqA%q;C)t5WtcmFJu*T_VF`v7PE&WTieastE&_xUTM=i zwEg$`KYH5-@Q`#16}7H%X4K->>qzOa!3iwZNyPH_snj6P#ly}J+T^BST7tx?d|K*0 ztq5yM5bv^^5)6UKgo5Swd|sO zwU*u~zpWib@42pe-&pOtkIC~ZFh2dHnxVsz1opLF}BJlcFDFRuQRdfsxQ{H)<$Fn(=4Tl$5FqT}X~H}$8t z5Vz+-_$_Ypc`7cdqgg_%h(Y8J2!6}4xrHhEtiN;W+1{vPd!cE7WubnFTYo7xw-hX& zJdSM8OgwJ#h^N&WjqMJD4IZVVkg%W&6V>#MV7Yy|5koiL#Kx zr@U>FsycJKUHV2{IykKsR2@pCR^Jt?4P$R2%akJwz$SyV{SCdmYCCr7U)meO1fJMD zCeuSxC2;CkZzF(cSKszeMyta!B(y2BmgBP-&(xZ}la`gdbiJFN0549TRipV5Ptp`+ zl>dittx2!P z=1JLAE>wuffFg+8+NRw7yZ^sgwe8K)K}K*UO9QPzR}6j3*`iUtLE^#@k*xdC z$-SFJYmlHD!Gq1$4@w`iaq-H%obN8Tpr{MV5%-)+lDi-1ri)1bX*`e13YdMOyvPlC zYFfN^VpQ`W^-TNmdx;d%&=rB_kMPIWBnxhI38hGe zUWQ(%yBpSIMVuFL{Mp^X978GccRAa&iv=Xw5d+#4x91SFPLiWl9T>PVfU8sfzP@qc zdyWoNGelmrAlHZ@PuM3|Q{hlDu{4Q7--`;@grWI8{d#Q!{AD%MvZd;_<`^LISSjpU zlNG#6?cwT)=25)y|=1dUDjl8s}^pqEqg9wJ4@#^u9gTvQZ+D6jM z_Xh?H(f%x4TneSHOHT@!^Z(X*Ti1Vpl&oF!eA9etza*LWZrbCLvEu2OX_=}IJcZSV z8s&zgn$b~t9yNNZlM+w-hfI(=4^;2*CgZ+Z-CQE)@6QGn`0n+Q+X~W+BNrkA zpvH@+U8gJlPMdpk;LRyF&XwAci(wCp=CWHI@hJ?0FjOgt*Y`cyKmN&Ck7a{|vSOI; z>_{9wb~oF^>1=%oSvyE^KF(?%g37C0)pI7Z<0pDCbj*E4`w;c3n9&=7c3w!onHLJC zymI^u-jlEdjU{Cq2hZe*YFvkr%fCzgg-?@Trd-Mp3XCT9Asmh{kUd%~U9j^smZD^j z#6G8%stIS*KlJo<_+7ksi}8@3FRgdkhcoNap?!C+UY#iW z=9047{Tsr36E7>x_PD8Z$@#&oabs4*$~R>6*bZ+hTyb00$E*EBc*^mHcxlTb58q11 zUJQJB-4Llj-U%f$nDup+y*7$yCq`A=*NQoXuA#%}{S6B(nTh_FzXH#ZK7WMC^+vV1 zDOz-=VUHlw^q6U|ri)m`tJ5jn{`nsydEU+z<6*vHi7rfhq&ZXcuvuJbM_lrkJ5QF% z9F%-?-GG1WzT>U+5`nJgI$eAHQxbnk{1Vw|7tu6ionjZ^X$c#{vIQ{opMi>C790(=XVv~nGHj8zF znqOpR+p|b+%ou!uWHbOWSy7n1s*N#+e){+jl#IK1&a7S1i5*dMfr5xYR;T12Uk~_i zT>_RHsf5FwL>e>)Z4`>~IO&`pK3$%#IO8)iqur;Z=$1P#=`v)LphT?42TQD)@y@d2%l4Q; zaHuk6;&ip=wp{RqW_hVi<>^bX9o|p9#$tM7Z=yn62ISupWZrJH4=+1%>d|7i&XmG@@!Dc?v-s3L z>mtzL&*pT=IniUjDAz~qvHX0u}7|Y*rB)SH;zAD zfLb&uBw*+5l|eEHTJF$$Un@9%8#&iKKA{~*%{<^DyAv=BH7dP}H`VIWUSR0u=BDqG zd`8zeS)^c0P*I#ky|S49Pe^#{!G630W~z4e4>U7wAEN}2@9W%4uiuAhs=>L{-Zhq* zy_|;$J@x6#gGO-~+BRJTBodME&vppZj05m=bWEW5b9D^ZcPE4EXG-*uZfNCq<%GAH zVQ6-}m$_O4aLG#aAxQefP{+1Qy!Zp-OlOXF_|ci{J;<><3HLDgMn1vnaMjrC8lOV` zS8~6}XH4O19EafgnE%dBK|#R+<@)ARGw1;r1OPviE_}Uk;X;$3mN&Ot?n-aiBtGDE zoZS`UW_jhx6${iiLgAtFWa#zIrDLLyfUKl3p`B)TU^2J)ulLEjN{=vTh-P|%q+|AH zeto(4?ld+MD@svVjLE$IuyH-TKhuQc5JWv*Ko0XzAJ(CdP;{*4d&(C3k#z=wjQ2me zfLeX8)iOorv!{|H5AgAIwR25zz+IE017sj;ngar2< z6aH-;NFMTF`mqdEk*CwIW<5Hwh>EguUdhkTpFi7{fIj*(5UYOw<|;Z}=HfP}u6@u< zI)UAg-#$Sf!W316{go)uBeCPN8foH#>+Lx0(O9zUZVhG+trMw2%a$$EYCgeVi?wp1 zHo9`$ur-%$3J;x?efT4=V!C;v4$4JN$pl)^37LjZKN1iRAoxmnGW;u6T#TS1;Lq`? zYcsENpm^ptAj4v_86huTYzrRi?&iiv?#gnqXl~x7sAv(z$Sm4#K}^`lasHJcP+Yge zXU`@U*Vfh5iRY`qB<8!Ehwoo0@Sr{j2Nc1F4|T)@pgNyZ17&`^dpaUBB=j6%b*cFy;8;DF zUR70Q5?oP%_d8SonS5QhdiCm@%DJoe$rFTuI?)?7hy9bk=pqMqqJT=PVpAgLUO(nI zg!(=1K6ma_lTzQY>louAJn{v6WT&tB-rKvwJWJm-ZW`m0EM0%Zo`hFZusalPyv5IM zqkcSj11LCH2o^pV`KXL?cL5vxoJnI*Ndp@8U5tb1oDKj%t_lwHCi^zkh$w{kgk#!$ zxe+}6)8liFEu~Yv+ZU&nHl4n}Gp#dmyL|P?Ao2LTjqgF7P~~SYEwfznBrFx^3EPj} zZ4;J$8r)PCouzNn^bMUt4$x528Yt#P`>MgChSwcrg6CtppP9X5q977kLss302#S;E z2@4?^^#%@gLQoKwbROde1IR?4gTZIHG5sv^zIr6T>uP8GCMOI4sLq-n8>-HZVPO$V`0oeZzLb4 z66?&j_d7`sl7!~+q!8?D&yZh`_2r12XAb&?>+zo`>~oFYJUD-Sk+dq>O;3s%gv0o} zc=wo3V_l|I z0bSPOZcP(#qR`>ES{PcmfnW2vmiGZU?@HPyD-}%_oe=Wgz14^bRBg6~E=8ovZUK)B z&m)a%4uc)pM`Ox%trTJ~IxhqW#*3>d=5sKHi=j>}LmHBNX*oatOW$#CXY->Q?sF7< zRE`&B#=Wk&2>G7G>0@ydY@lp4V%JrLW!zYR4d za^0;+RG-@Zrz%IJv~9G7^6a&919C-gEI2xFY1s9hd_A0Zt<_7{p3J?P5BX9j&T`N8 z?rx3zyUhoh4)X>;AB#4xj63pc@lr-k4tlv)&@xKn93Z9;EoWp;0cU9!Dq;w^`iWD*!^e0~FPUwChO2{f$<6yPlxG^NR2o#<;K-jo0i_ zCNVyFpIOv_uX^XxQ!u*uloYj9CUv4n-H9k#FzFT+@~VmgTFgGJ0{$Z*07HPy-!sRq ztoT62MS-m(!YX7ZGR_*xTo|O~yLjFzPC^$aw6twovcA!k2uL8;7v;DL_ev;!j1d6G%u9(AoDJ?+RFOSEZUIeS({?+I=y9!OYWj0(j#%p?je)mJjhp{$KRNpE6h7#_}409^FvFq z)(<^~)t84Pjh9c7r%#_=w3|LIeWCLYuuv*k%~yzvi)Rlg?b+i6(tDr(*)Nk4;-}?A zK70PW*x{9`$lf$mvu&Lfo2taL&kd#>3eNVtuk|8p#{yq`H=NTfIyzbuY~FOtQ%LA0 zd2Q~-q*exqQxA~J6H>n8CKY{sXMtzf1Yinu!n=7VADM}xRpT%%-ZWd0MB1k8q$~S# zIIJBD`^3;X^rCn|E#Omu2ynnGseUfncaq*m=x9-^w`eQC|6HV;Oav}6sJ+)pX=0My zkEfMqYj1RdMAjJd*Z4f{^nj}#Px7q>a_0Sdhm3rmTw!&1MAwPg*W;R zx=h)gsEtPM>lA)-^w!9VRaCI=*~pa*u42&6b~Ivh!3xxYh`x=CC>PD@g2)A%%#OvB zz3xO7a%bvc+N*8ct>M8&xrQIOAu3n71&+GB!#)Q>5ag_6OyPC6sK3=!YcS9C!tjvs z#OZ?DiWrQ_zia4Gj~wIfZl0FOs>EzOe)Zb5+wh}R1dF&Fy(H_Hy@REn3Kv>VA<1cV} z>JU;3P?3~tOnXuUTlKb9uu=898c>y3hVsv!l*wirN59nIr5$6V9Q?-b0{hFSRiTak zRcv@C0(TrYXqa}wUUXMbB%WzLmc`c&*$1NN3J)%F79g4>jplWbvT?a zK#rM@ks>Oyl9F-w?cZEXzgRU+UplcV=w1L~@4Hl6OEIHY5l{=LD3Vc(OW2tTJhq+N zwq5j*1EIr%HgH7lRVo)GGu2$4Pd?2KC^$6V7y#_i*e<4~f3u@}^LXn4v z;3c96QNIs=xHbr*6}_qwl;Hb%3zPKmM>Necep%m^Xh!y-nrME+-n1V?vogklMKL8D zd^HoG!Fn zh+vdOpsd*n*SQr`KEXLZ2M!ag!Bk*=Hqyf=fK}1(A${hnTYmh-zy9)JUBS1sOiG)> z2(oIct*tEBJU%aA>*H@G?EMWASH`N?UEmUz=QN zf;0OHtR|S=Ptm;{*?C5ef7L1#L&K21g#}wGAT}?9scurpLx7$TCd`?Ab`DZQ@~t8h zIPHni=pzuoZe%jye4AADT6wXtHK*Y7Eq04y z_6B6<<+;0u8F=M}rKJXibr+kRzI6TPvK_x-U#%?=9k41A9IP3rIi2Be*Pe7OMcL=> zuRi_ZL_0_SZ>#ib3uP_lB{=OE_on^!@%r59_X--iA_eS!c}Qh!O|si9+tc51P_F-2 zk6<8k0vO!Y7Y}a+l1;?PbjV)AVcZ>CY+R1o!E~z-Q21@ z8Wo03t~fh)Y|3E_@5IfmX|h^0(d99H#mJ|v9;b~xZ>|ENwU%Ixe6dPcn}&F>!~&RA zt03xDHCK0c_d)?|b0PwFH~`G&^#+-N)|b5Ho;A{D?I&~#g5Yq@t62hKY=J-^Op+6js6{W(#Wp;C zZNrN7ns>as-gZ7py(6aBGYz9hORB$T8^Dh0O#SA~ zotqmZHbYL~%6E4Pb7z^k&(k5*ozNEd*I>;mVq;jKJ6ZiSW=Pdh&P~u;6tv{M*!J=U zfV;mPP<^~WLF`DS|64jzx8+o)e4Vm5+**5_cJam9zFQ*{#l6BCwQ+|^Vo+CMGWJWK zjmiM8YN0c8D-2b?7UwT*Q!r6Y$9-+Qm4E2>b^LH8u|w~gcXc?@brJN5=Q#xj|ACwA zPc%$&3dLbOvyq5`#eFRRf z@6GDn(>A5ZLe_pRk`C6W6iC!c${TU2vf(srDAcu`f>ScIjv)mX1B4e!pMDa7ISM86 zC()!5K}=~ZdlsJHrf@znN<(lHAqO`jDZ!b7~9E%FhJ&m1~=rY8hJYh#HIC z^8WCihEGXx3F%w&&w@vo<6)bI)?BQ?DmqyPr^P0a6g$_Dn)wC5T?wPKslb39ii+&D zxa(O>dN+H`dZQtL-Y&bqiC-)>H8hhq^OX_J`IM7K7CkWiMn1Hch?Vg@76F z3?Be%%)t$xE4e_csUVvN?geRC-##Fi6LwAL0kAyb(x(S5qg5l6Ck|qyo1bN(BF*oy zR+AT<2^+F3|KWEQCt#dw_;@c7lV@L%_8q4@Wo6&x)S>5*rT4%ST8a9Z_l(D^i5(AT zaEdkT;v%XvpI?1kOoSS&$#ds(a2zJIt5tBNw`PF~yC7xR3`W5H=L$ga#hz=0DbLa+ zlq(E9A53@VEy)iZr)Imi7TJ>*AU~c$rw|ldx2j>8qLc+Ki|TS0DgluX2uRXkHo3-pK!9tciKBt1fB_>V8{ct)n*vhT8Ij-O{v~7iU)BM(4n)i8&+ygXa2d^H_K~|&Et11}A4D+RTzb^k$Q`6*%%<+gNHs0j~iP#Te zOH$dkQa$l>+dZtlf(o4UGro0F>gVqvNH)xOg5Ug?<8gp{*K9QL)lm+=sXgvsu{fYw zHFrC6>=>=a$1lC9Y@c%~Wa)m&=;n7&s z)Ob`&`nENqhVD|)M9O&+?4>L$x+6-n4~KjLqcYv-3nv^!eMyrej8GlvP){Eec{Cm7fw_4%gw5;_y|N8u1j zmjb>%*l~C$B-Zw1)c)tCXcjIL7cZ`8`S_HF{J!K;Z}iv|4}E5)cTQ;q8ut7t?E)P0 zgLjE^Z1H7V7(7l36mD-gwD>l~*zG@DJVXwC9eSupRJ#-OESSM*#yD-fzzalkP4YwO z#W0atGypx3y65n?O8<#;#2q2SCQ5SLQW22?m2881L-^{q77ulWyE26xt{%^Bn%`6%deYs@Npi>Wa2(6>Z-!S zdZdx6x3}kUOxczDK!VZG#VpWeD7$YGY{4B-R4$+wza34~A2E5j>s3z7%ZouuvnnoG z9LK}$cDgA-@95DdXvcqQ2PT(^21O7Wnf2LM5gQBlsL~BpJte|@i4GH3|;$)c=5paKkWF4X74x@w3~yyuPuU^S#Ce+e%|Vf~Ypy+L(P%$4s}S zYUKpy@Wv~Z*g$gnTy)%k_+K`U0sZ>6U$kBFb+n&pgS_--YbG7SP!4)Y3xxFC z$iK8c+ZjR=CEyPjEsLAJ09xE5V$AMlU_rscQ1w7Bq&>dw%&N0G2S#Trq1R9mtvVL) zxaJV%-tdn0HTjW3O*ApR<289(LIDha=(fu8e}=MkUQF<~U0{V%|16;*1cb@@S`Rt-dkE00t}LO z9 zz^WgLo^(45oU9mh!1Rza^yDt`h?6qjGT|Ap;BQaGnLmY9*XjUx%T^`&r?QZHFPD@;!XD{EQgH6~58-@3@VgF`(;IsN(d!NN3H>Spa;G4$UnNljI+mC-(I-dP=0p@tV5_af z3{WEoPP#Ivm-ghv0a#~6J#*N`r+_1kI*i1jfRc&f*mrfX9j{HN)10|oPtQN67wLlu zGdc$fY@FK74zVv#Yu|p`xmRauw+3Cga>e}7k?7L)2w?Zz!&6yUJaJ)-6DwgE6bz^1 zC8kWK$FS$kn_rqnwg&6HEa_#i(is5UK)wAi2%04PzQlowkY{xJ3VqjjeMCT2)90x1 zLKhjbad8R5_`@9m;&ie-{c3t}J5KBBn70>M-rm!nCsE3cb1Y9%6n1bh?Rz0T%Sg5W~U#l^=*z6VM9 zI`n7uCa(Y~-5Yp=;=TL#>(K1Di8#Oh(fypxWm&M*SPn%?o6%2(k>)?Uv<$aB5QRMe zj-w35y)_2frc{7zXEKCL+ZZ+mQP5CQKwbHM5I4D1I^8)&G&7}CY7<%+pPA>QbuXhB zgeqEm9#G3=rwtDtp-4S(%6h)fiGW#yu)3n4OS)8=j@*HJ*|IIfMIuy@hX1uc zpTc#k7yHm!cOgf#Gf5bLhDmKxJ)QE_V+bgD#UD3DH`D(TCxE;l!!Ti)Rv_}*K6UiE zEp#xNg3pigrgYba*Nd^Sc+KJ2mI>%{nUIj0hK5EENc#ook6G_y79vKMUHD>@nE%;x zoZ;m)4);^m>K>(w7cXX^*px07!bdTN!aznLHqdB`ec&TVC;RxV7Oy|4;xIgTiXF7k zqDX_P@;o}YYI$i6Cz8PmOxkUFXEz#Z(gnPXQZW<hUh~F=3SscO6w}ZYfs4dcQP}#P-SrD@+B#hrukF0UpFCOA7v7a&n3S^wR^N4%5gKC8EGEa?Sz`44u!wOS3Y1htH?|# z2praS$ZwRbtfJ!l=xn5C*oaLirp+FU_9n7dIrw(X5WREE18e{gEZ3a(L4U5uU6r|~ z^Wpq@1$RdLNV2_g`Oh3WXHL0dhu+D7%3vZ?O&;E=CnE^)q}v2cI@T$(={4OGo`|&` zMoPomqonQ|1Ecv|16;u4jRji?T#`hDl}7Pg07DyfaTd8*%k-Zb_pK{hn!oq7wp1PY zVMi?$5)#V!J{h4BOk2rRd@3VqIWOmKEzXKU07A&a$O+ zfZ%yFvNiZdox)fxROo+LcP0kNksAX57`+!SUPymhmPWI_*`cu*JO(CGav2IvM!HtR zM>^dY$%8zhvFEu-wDI>5f0*8D855oe_=YJ?czk@*TT(vFoV9BNR&Z6aOI;zqnBIpo zZb06_2~PAePL7iefhuf;&nAD`@KBPC9c*sUWKsi_(w~hT*Q)V_`D&OA2=pz>Zb?NO+8x};{;Dl}}W=Gq=17C&V^_cv` z)y1jwb>cb=25Izs?PoSWO-dmTlP{ad=-p~(XBS)Sdi82{bSxIJ5Fu3UiBf+^QfH9> zJSVU!U=GbgVgqeNZWIxKPK>m0+{%DrL>=EwB^SsP>3N99(a%y%cqTH^ zOJ2WzZ8Lh}^kC}w1ABs1j$mB;%+#p)LVDWZ+4CT)=`t!DIZM^qSH3u^2UH*{V7+?% zx~GSShXAwBR{de7w(py6@b-NkpX&Od6Q=v@A^iw?I7LXVS$1wS<;8fM7G-<~PT&ck zNDW9U;)U3;1Q($_@WylITwgr0fRQ8c-uqM9SB1GPBihbb!jewhHRzS(u<`Jz%XE5F zz-v_lC{#2l@Y=CNvMA0|OTE_TU_jf*G57oh4J_A29|(wSbLZ~2v=H|r zFxEs#)tjd5skodwcdiyEZ;2TpX^bi{B4BNJ_jI$#aT$6dGZ#NG4;T_Z@_i2!%YV7J zc!A!NiBVk35bxL0AOlAF_i@_5h-xQN)#s##j8saV8DGkYndYAq4_s&&A72h|qgv2T zhzONMK@-OxN!}MXQ2;8S&AYChzB74|6WSYJvqEzgpghhEI&OH#n4Rvu@w-4+PaYu= zjeGSW;hKlXe4@QD`>JTn;~jwc_(Y~CD%!gU_> zp#`m(K2sn0rJ9upS)Zf$uYn4=UnH-r13hoQa@6eD32y*Wu)U`*4Wv5lD~)CY5B6?RK4>;^$Vrog!=*5gcuC{AUG zP3;`(G$j01fN*mN{^RzXEJ=2}_57z(-Yq*3c9gRl!Q^Aij`AeCUMn4p3(gIY*@K!e zKrBD_`F;X|AyZaBX%U2-wjFn_&$0JVju~N&rD-h9H?BBds2WmnZ*N5b%q9s0n>MSE zi34*V;l>Y7XCcj^*a8KWzzzCK9D4S-2;*6|{5n%cG42VAbCK{%Fgf=qW=kc=q84D>vrxD^4*^SM@ENWNdqg_gRw>Q|Ad8ik) zH*s7s_EeQV_T@qabY3Rra5>|PzJ34WN3;_=A=LHzx>L7LKS%g2>;?R}Io9mo^4NuL zv)P9_&Rz8X2myn>))(oj&;E1enPqN zw0sJ(gs$pU!9EZFEy(MWs|$nUr&A2V8X6ASU>WC0v%IjHnU0b&P$g?qUTW}nZL$7V zNc)AMu@}h?ZhFTg{|w0sYfPO!T}d-rny0wWHoWvGGg|Hl7+Nrdqo}{Iu(eIoIJ{d4IU@2Ybz{@R@+;RJG<3%7P#xB+Le0fmfVRTVgtB6c>|y(g_dsnYP!7$ z#@@HbKLHcUzggVZR9QLB%tTPo_(3;kTqCHbz9Q#2;)Cm^J`+StP<59iWGb_zqs1nL z@#lJs;LLwrL#Ird3!Nch_=)j5NlHpMPLNPNrJH^;wpIF^MXGw)G2uB*jWXJ@e{X~&^t zpU~hbXk7YzK3P0K5}&WeU4RbhvM?VGUL#|oD;~k@ryZ2K4h9^URbXU?4Z)bNs*X7>WeH>P|QD*NM9)0&i$<~)1J=F2Tp zQ*$`m`fDAl&J1vVUNq?8?{7Y|jjA)J0p5I3!^UZDc!;^5IQQy96y$*GBK;_BNq05v z3J0FBm5k2kxdF4Z-LIfNM3FN@AQSQGHeY@UGP_22;e9k0xB31kE(mw8O*^x)q!XDg zTWd2}KxXBQc^r2p4cC6lj(679s;3KCx&tYU&+2{WQGWS*Cuneov2!0$0_X zp*GHqvQW!pe>#&N7-i77brJQFB^T^Kf95Ag=L@1&`1~-Gs(i=Hu9KHI0hzezopjkX zyIJ57kFBFEImm0y<*G-xsTXNZSw6!)Ses z{wgp(*p%Nk(QQKsxQT`KLRzB;4cc(G(n3Xv_+kn#l^Bci-GH`o@6y?-PGOGSW>h!N zTTWaCVi^)o$ou;Mf@u5e1qPWQDhiNYMyE9|FK;%Oqbq5nKHgNei?CyFoikYWf|R|X z7WXyRL4;Q?JQ>V)Y_Tc(OSC5Z>cS>Gx#{k%ny@v6BMo#OwQ>O%UZmz16`Y|uk~{4T zhAa6CElacOjp|M6M?|3w^)FXfZvc|F$|0i6gtrUkzuDM?YyEBkQ?Se zQ!TV~>C(o%OCk6?Lv|*Fk|282#@M0HTQA=E;V0;#K*qegp@}ptC59?^;#959yHFRO zharYWsBJ^8bMzQXV(~B=Pi05~ca3&a4=tYXMC6{%;Svmi9;y$JI`NR*AU=^fnSgOl za^lgr*4EY$N-u`>y-UA`NT!5p5S<(EMk%=FZ+<+hnQ*|6M?h4SFw%uU;kkeO;q_S@ zb}Hg=M5+euyZq~LPmQaHarS)cHJk8TZpRXquyX4IOxu0jDwE`L`u{zcxNloI|D6ax*)-xCPL&71Wy6q!+mD%7<1wl+-0LOZA zRPo^7r9`R0P-V#-q1)cR&EG@(EU^`C+^?J3ic9H5?7kc?LDAf~6o0tCceFGkiSp6- z%HeF>_#u43Wo9u6H#V`Ib6&CO!U?dYo*$vhvG^JhrBFIY}xxO;IR>#PuEY)!L2QlB5?K*-K2q8WLu zDAI5sj2>KzcdIN$s!qJWcM5f&oD9N6%%ARwy}k%;?e|Ww z3XB%WHLl_g+=j`fg1`lD+YF&D%Zxu(V>M`tV{nptS&CwG_G+TWTJ%q6oncju>tlev zYb{*oZXz@ATlL(rzez$g4Y)rkzgB8JUVNGAZ@KgbnxQTNe+I~re}YeWNdqvs)!)q;z&W+eq#}gxGKFJ$&9K?P~+60yIt=968ds}{_JvQ z6N}-hprmvY@%>gmFzfA)yW=oL-ntx4Tw*!<_wNtF2pvLxQ8nF6pd+hx9Q{0R3cNn+ zj@<*skZ$S#jsxM%?TA!o9$DapjOuCNfv?@D4c1>u5k@oANx|d912C}9oiNe|&{l(Y zStm3jG0eljBh^BI88c?MV~r3bYJ^Bv08dYeG}3mZJC68LrAJcf1J05W1{^>;u}nmS zDH>6-0I}+o|I~$`i!qfokmc)nSJQDY41{HG?dS?u$B5v@HZ161ELjoIk0640l3&7X z+OO?iyFG{Pb|hwvc8w4OdPc3d2AGVb$L;71$0kD(jNKtrY`N%AXQZPKLr}l_X~vSD zx_c-A^QijPO^)^ZPB&_`shA>bYm7T{iNzwiW<46c8?}(r^5I4lFMUeEl{MW; zK>jq~0qf7ohj$lW#ddAdCyg)4UM=e7S(?t4ch&8^Rd|Yv#Ytw*UW8+hZo#Uoh859W z?Y;%_vc36d@=I=&JbME_z@`BG1nEjU`S>@dZ2Efavnvng&Phu++wHgL+mzDColoMJ z2LbapG%&oF7-%{d>05xc9#&KM1Z(ut%2YHPyF@Pi!4l68mRLgIUde@Ksa|3fPFp2L zWLQ0Hf`ajof0Dj*WYY1=pX%E@v962(bdC z&{!$UNQ1l&!XSoI`lw5H8gaiQ&vZqsT+@SmI#_224FSKo7olNwM~xgA!`uwpuR?%E z(*fn{raWrG6w!mtFmfm|)xpI~0Cs!Qx9J~UagsMM97iiB0~;7%)?$8w2N*p{*sn-3 z*6!*!@KVLrKz%YXup{m>-M!_w(XzxuFRLxY)<*e%D&Z}pGsf_3v>F<)38fw%#f2Q? zd3imGqK70=!u+lVcCfQcp2;ayRKX=;q!5ryrzky;t@R{ju<%$#r;_rpb{tD41YE0V zh>0Y-A@z==VSn5XeyPII)g}J)>Zt;h<|YinkHDw-7A6xb!hVSx`fqn+D$2-2RJMD0 z{UMEBER>MG0$O1?3!rb-K)m6FfXod=u`c<%h=$ zA`VR!9FysK0XV$X6sec+Cwg3HI1StZx{v&zQKbY! z=pO(B7SG5p1akoiuA)mE)ferHz8%BA{(i{Ky1^_kwE?TGgkjP+&gpW$TIx(67I_*o7b-=oEDFKh*Q3wq;<%J3m1&e zP`VN4H*4%A;};*~q5tlY23P!j)7aCDsH-E4+6{srM*k+BQCP5?`dWWK{^NK4{gVF& z2mb%-51C$jd~?sG3l}DehSk;@;qGla>HhZ#!5wLLex|kq)dK|Bieip{0brTBCfnLgZA9qz?^hxPmGC|_+S1Jnj}!Caj&pu8&9fd^`<#OB`7Frx_&`rv!|k!VRG)C z#wvqUJ6{{nuWu4L32~l@j5HQ3*GaWY5ZDu|Ld+AioYj3TsCoBq&~HVg{YKcJP=Cft zpP}5-N%l8i?sV2ejGbUXLbnUn7;g{cV;b#f@REKn&n_jhGzN`&3rk=OP38U@>~#@a zdBE5cJ*!O(s-Noq>y{E9HW~V5f(D|QJ+^}*uC&YxJe3=gT!}d0l#ZJ+JxZT|y%%6! z0_`0LvQid5M`^zQlA$L=Zq%l?7yK5+xaIVcPw$|fX)~%DnLI1AN&;u=h4&_}`(!bi zj`K!)d5JK^qt48smCR#cILJlsMk`YZ6rfApfr*zB(oqd%m}rICS2L!+#YouISo-%al79Ia$}e~a@&Ui{pq3Jera@a2AE!gI z@%gKEtI*umVpSCtvAC^Q=PRCa=>iD9*Q~%bCgVDH*b^j zMBoizcI4Jk&qp+_tbPm(y`pkHm%vbTbn^y*Pbh#-;(LMwcf7%)%B$tSPo+@JF&&zs%>*m*f zo|tFE;uYXpv;P9F?TyXU;ygW#3hmLyz;@iabs!h44jv}~VF)=J9OMN1WK&|fPAQ*Q zzh_kJ)7V01T^|c~AKa0~x7c&C&8x4PY|T8B@WCYGWYRA?pCB!-_dCY?d;+@zBG*Kk zU8j)@GD%i8$-OQ|ueY>!wDo(WB}~AP-zGcnV;$K-xh2wfX!zGd*NSZs?IcmHA<^K^u3Q z6%RxX?MBIwM}CjNW5mt^b5S@J{3nN;)g$X_G+kqUR`q?vEMd^Sya8rP=D9*<3S~x# z$NXeW>J0{?sLS}@18#I%gApaw1fxNRkolkSi%84;PvWLcmlT~q$KC1WEX=Q?e>j;@ zhXRawWjHaYdaYs!kg}@1LY#J^;D<2Ew*!Mgt^82PLoiFTVaO8>2B2z$rdqkg2z0|+j8|I zw(N|?cZ^W9b#utT3hF5Nka4PzI>H$V+r>>(Z#htU8e{X1vh6&KoN7(!L(nMvV5U?~@G#>?BhWpR=i41lcn9$kys}9 zzs(LlyGPfh6$m5=@A{a%e)=iWqwz2Ptk*Ew^-*AYkMu2VC~P&ME$?4%7Pcv)&V{G| zxJdXp*miZwc<+BDT$uT2mS=c*!;40dn(-JqbP3d9pSeuF)9y}2Zu|w(K^8jswP-VXp{aANrcR!T zg{KA_LjEdOtvtH5X0;x9i7a}a(=KQO`^DKU4i3tgPO>nrgP_3O2YYmqzatP{D!3QZ zCVR)*r12k{Y;>IDh7B9?tUgsvUszZzc3UrHiQuCL@IwCFex!Zwa-~~}rh8&k!$`(Y zWbhW!HaCRIkw7>0wLXm~JY#L((*Jq=xfUz=fepP?-Af$hd$x_Km~%SmEOEQdajIKR zOs1I_7+w*H5!`XUXBR&iT6tp^Xz(>m7CG~DqISK5diG|u;X_$E$7dHlaK!!r*+-ND zT=Gcv0cgt$`GXC*XhtT;_Sj8+H_4NXzQ(>-bS;#|+dQB&W@MAnxZ}P0P+O(-f>S0% zBBT4NYJb*SyBSq;8f(_SkWJ-j2*qm>&a~WM-suQ?tvlq%4F#aP622p^S`rRNDn1Di zR3*beUIXJgiDs*Z5yd2blXMt}moH=KE||By)JNoASPW-qQCM8((;6%MhFVtL5cCIq zw+_E8!hZasaJut9_hTK0(T*UU0;l9Tt?W-AOBI9_PG+$<2_LBaj=LcUjy~veEr2H) z`Is>c2nKQ=cWtcpVgUqs%GMtb6Ps^@dd==33eH&bZ-xZ$ot79o;8XQ9{88;EQT~8w zisyhe?p=Q*4}jHcz4`YRTwZ5+zh<#nU|4Xs9vu!n(g(+HU@)6He4H>Hvkbv7w@h>s zMJoU^tl`a6iql8;N#^uH&@x0YdCYwpnR0e@cJg1kWQoaNS zQj2q;kz#uy0YcG20YvB|v|?WBnO0&jfoLS+tqB(4!P4|aT`f6BZIU)mxxHjqH%ZN_ zoZ5m_QRoh67FV>LWBEWz=VUUQbKdz7%{h^y)pFDhAPGNwrp2T4$Wp_{OwjD{M(y8| zm|JbzeuLQDEws*$dgYacGrjUO|IzQO1BoL4CC^B`?KB`K%*b_l@Zf>8*$hUH$oMB3 zr6^$%z+?~0nP8HKt>8!)lB=9vn7N4=&+5FOd>HCLH>5Y#?R+P?_>=*q<~}lUvg+LY zu>8A25wfD(TFc$#otI0qH`IUYtnFG0v{NWAu`wkiVzP;`{N7|<^$z#gB;Q5d%QwUe z%RIK}tyGQ-w0q=g_smhclk@Y#K)dVfUCs7B%hDXI73$LwSelygscKGGucmD8ms9>p zjcw1y+eyDy=kQRw6y$tc+C~HF?^`CRHPZSS?*XEzw0fEpe@I`|w16eIKu(aw*^1f| zAv5ppnx)^rW5Pp%F9DQUC8$z|1s$z+Y^*t0z{Y5Rm`LBr|M_Bu@H)@slt1HP^{12+ zq=w(OUSYU#VNQKE?H(4kd=-y+;nkk?;cSV!m~QrV{rICBv3u}Pz4d$pH1G!>cU@0@ z7G@$iYYldW4Rhe_$E$80x_pwifX>ZF7QiHQL@h<{#h;1gJXS0SHlZUNGx&X3 z&8V#Exj%4xIt@*1L7SK?n}AyeMB=Yys^(lQD?$;?ruhM4! zqd@cCmT97c7IE?C!()w$EB%vs1?z71n)Q{NUhdy|?)>t&_AhG_+PlrNlZLWBq!z~? zF=)2AIK2L`OMmB1Sy(*uWoq?jXo8%Pe%roQh@JCmauZ*7^2>@|o#T8KC4rV@S|%Cm zTKcl$94_0n{XCGRR4j9N>+j`Fl=tj7|X-|k)C zNO|(FNBoxf!~BFo#G%}?wn>lZ)#WFS>#v90&^DY&jDr@BgNE)Hy#ivR?2!Rlzg|^c zGc{c)81x~HNnj)P3kPkf!2I=a%Eu5%&rutNgkf?mg739GO^qNA z7UC`|pf4(2uKDXN^dEL=qkAa5R5eX(CX6#!sC1^81xa2*`zc#}r(d9l;Xj0n>qbrnX2fYEA4XngKGUCh{ zK1PCmCGr+nKQM_a7}B2b%58Ucoren76t<~Rva^H~tk#WvGX2{~s2akALrS4P_~D#O z*|18gIRL({l9HzfY983I1Gqt3Rras!&Hi3zvH| ziL71@8DOcdEI+@}z$5+CJ`UzO%gi|z5$W=XJ@hNf_$SqcIPJHW@GV=W07C2!SB#P5 z#qO~85IghJPhm0{pVont{^0PgFjtiM4+ew)dq)KQqxyH+b`n@6whl zNvqZ1=5N@W@JqIv4Tf_z`?Xa4QE_QT==yPO<1GT+@^r7gq%r*_4~ zV~H)+21j15A}R&^cf~ak7pY&#}{ zq)zztgxsu=_Rp^;7?S@wG53G}ke&x{$H_!(MTWQ=&?_k2RGQXN`B}ppkku16zS5Fm^$$s3(AWV^yz+0^ z#^gmpwkw%Lj{*nj{Fbd-gG*y6qQEmm?5E#av-y z7ox%*8B2Hs^AWEOu$l^CsjEiESU~emr}2?ga<6DZ75Z{*{(-pH{ISt&U#%vc78YUUs!&4K%QJiLSsYk1bT95$&xhNyc7U@2lJfDjz@KL9} zXLYVR-T9RxBe|F#=EMbmJ-5N}O#nwibW(9V-pFp|k)HETAzUF0^3M@+;#!fHOFVu$P;|;#*>r6=7wqdehcCIKA)N!; zkLY9z+|R^<;$2^ie6VSpwoBbqSjBUr%b^_o$WxGjcpY2dHU9=PzUgT$w0qkv=UFdQoyURx{PXy&&P&g1hDo zN~s>G22m+oetEp`84~S=!s1(u0OA$hv`Ce`aBZao_h6_q`T0S{WOR;+rUq=M}mkJ zyw|+$!{OXV;{po!)%p8~FAL_7Z$F0N+%=#WxPbVCyehZ2L9L6C719iG)?!-I(2rwO z97H|Z6|oD7DKEZ&(=Y05vCD|&V63+hyxT;~Kdv@~!=%S5t+x}6O--Le`bQp9SysD( z6y}6BfrkH|5a)!2cHqgI5IMmqhByP%xC7(FIt_TJq>|(q2s5N0`qM}r#u7gjS6nv8%=AE)fxjpgrMeqh_L;McYb7CL#Iu_wBJoIEg63$Xv)x~lfhX6@NI z{*&)FSOGAT4msYjD3CCh8T;?wyEpLd%9OtE6VMglmev@rA2xnq5z|D>j+2KeF=4o^ z!q&q~4|_k%VeJ?jP04*~?6XRD8-Ls%XO4p44WH`}Ko3ObPcliLivGlr(<611f`E^z zn_|cDPX4HBCr70vGhM=;xyII1<|o+&FL);OP4(hWPg7^U6{pX2n0xG%)!W7r(5p4X z@nbkR{~gpTRxm5da&vJPa#>>ZtQkZ8ofvX&IMb6j-kLXfS)A%BYYh2=nJh|Q9D6r6 z!|m9VR!&Lc@Tz0t+v^~Bl*J^R-}kv86PtlND3mxw*S6RI7j&#y6Pp|5ib zTs^(R8ndz7;8|40<&7D{;Z4l1VK|-gUpV*q-H~jgb^O0&n&1=K!ya5QWLj6_f4B|) z{NGq6FO2`mocT9t;EoZ+E@PrU+KT3nxY*b{;c(9Anc_gC83sdg>u6p-b=}wnuG85A zR{Pm1W;B;m8{4kOZ+hv&@+qnbwIW?qr~E}bDrCxC)_wP?XMWPaC{>QZU0q}o=Mv`! zVwos1=uzn|L-y})nsl{In3W;dwPou>>&%hEcyZ-dKE!FD4(CG*J4(V?E?wlQ? ziDNa}e<}~$7UB0FWsF>ZMi1M+T=N6z#_39l`*S*u9cry|efjd7^anBeYPls>t{z=j zJNh;!uHTtPN_YM`J!p3+XH~L$L&wK1rKkV&iUpTXXcfhqUk{v>2`H$*P98O+ANvcp z{zSv)p%*Vt8kGo-JujEIV&m`CVPEMjRQ{?8id!~9bNJts5tFPEmyUkg#Kf`F?)kO9 z{k1srH;vMTnJ;N%p8BUxOU-Rkg$+g~p)h*cf7LhDci`%6St<_q-^?ptBxPe~m5WE{ zuFOa>1&%Q|gqzX$)a^vcO79wqe{b6yn4daz*{?_`Cx!S|WRX$-%57@gkI=VCCtm3n z?~ME$YmZ||m*_d{V`?Cf#lmPtMmc)WLEWrHtqO6Reprcki`<&-!98(1#|>@8b}{>q z-Lu>nVXxZBj8prL|H>e6_iOF96U-!6ol)6s+M7N@MVR$62q zn$WuS*YVMJ&gQPr*;anLVwaMiNEyR!#~7h=^mwou$71K$Gye@&jNh%0g?YX!PhpOV z4aTE0(Ot;mm9V4KVN$yrvR-9$Z&N-=(+4}OkaZRX?>hRK!_J*M16pzxlwP7`Km?y#LR9oTtgzBIJ ze~L^JVfXY-Slv0kGy6D~VB_--jrfSxwP!cHJ-U;7!13$uilzL2eiZCq3D_aDNr|m` zJ~u8DUt0;2%Gh%kSH%xTx_*gzmU$)aJVQbyS|R;%rn9x`HZDGPLJA*dx6y&)ApXs0 z9>oQE?&8MqEeCWJk2lGp``@$v)Lx(F#|7NYLaZ-Fj$l%+sXzSF<1j$zpxGdRZMziV zbc2bdA`e3uX&T5{zylT!J1b)`{MW1F-lgU%tvWl_^w*4EzDi-c+xO$iYwz=HQENV? zem}eD#`$SetW*5H-S|+~es+STsm61T%CLlh9m7VKg}YquxwhYNZ3wjMWPjM|eP(EW z_4>Et0+&_|&3rF=k_nba*9tce?R@sGZ`Me-+4}TsRy?z-TV^be)YNM zDq0|r*q@0Btmy3D^K9qRl}F#m9N2I0gQtn>T4;Y3r+w|)HJ2V8w~##E<)-d%X|c^i zypmJv(eZuLBvYyc`=xq@%YP^j%TEi_cx{|encU20(9dGk4RF3Mvv1Kx*T~SmM)Xr4 zyn3_nrE7w<|7WuU&T_*;hR&bM2Wy(sZfpwQ^|NQ;W^b;(@AvkU8Ho0@{JeDbqik{2 zlc9BQj&`qmsc%1j>VcWy;R3z%eI)+9<#Ed3^XDOXO64~_hx-%Cyu(Xk938vf2%-I; z)GZ;A)K83xjNy~2pVU*kPMs5WSrc*WQfPl8oEaR}yyc#-n=y@*)jKF?S)_Da@6xvl zrNj37OcZ^jEG2zbe4UjjxG-3u;NV#!jwkd_OL+lQqcqeY0fNkGCz| z_brDOg*o|Uu={kLP3q4o>C5~y$YUcmt7E5ZZ*5CU!u{ryLjtTB-vS!=;yQ2mrPj>q z%{rKgriWS8v7rG8^SvGJ>RqmWX*Ct4^2Lg4&@~zQl(}$1s??iI)+#RNHFimk&eA}zILEe;iZ)7qBr7U;UB{hq!~?|R`Bdr6lyB4<<^kJ$|sr6mlt#+iL{Xn&SA zuVRq%YvciELs^c4vz^%FXwKsiiRTBkwTgd~J_`2@HV7>1aYl;=0YEbtaP>*B^9gVZ~p2#QQS3-^-_7A8NDcU>*P0`V4mGH6j_^ zmyg_cw8PAb@&+z?)rP*z;dqG-W)f9BXB4?lP%A%@cWPHLvmLc?WVvr1+2(rgg4X0$ z4mKMt_ah)W$(Pf1mF~FaErkWEOCwov>t-^S1d%ESg}gC3VWwVSjFnfM#JJlTBNltE zBcCk!>y!U)0sQ2TBg)AO$B4G)COXL~o=HfZh0orb%xAr)`>Nqn$>6(wqr&ClcGmZB=pgnsksc`tM%kh zyou4X^bJALa$Wz}qw$R7eE-o+lU_y-?zG>?napSYPgB#(ElNt$Gcqr~mvwT)&?gcb zkc%}$&NiTr4-J1U_+ZblaZTH2zi4nm59=FtrxyIi)2xmeed2vnT3MafFscsxyl>5p z+(C%EpvdaW6urcbAElaT`z{9;e(@@bIF(3lA_-U)RHS4aJ~_z1s;gl>m>>0M2}t8**vdf-x2N zC#q9cZPDl^@KkkJB|zF|s7;ipFW_pW&)iFfi>C0@x5IRxO3Obg?6#JGkXj4TuLpHo z;G_}^2eY{_XLz-6a3Kqd%*nk?L`kO8aH94I6R@t0d|;s3me4B0C0{4!J~}uqsSOGn}g{5 z*C*}j*O&R3D;f*5;^*Mi^gC&HacQ0nd%fr;`7?$-+3&&YEWl|H`-#y4M(aU%iu0S? zBlu=v>h(3-stujTjr_ouvRKg-uE#DNIp9BM-NJ)%xRQ3xq67KMWY>Fd?3-2_KD8cU zKmoMmWiaIcW;Y2Iw6(IPmtHgHQ$E<0&d1>wGp#swPT1#ZCQmz>;M z7aNW|7*hN8$f1}RV(xcmr*72j2TjnCIE%I~c(bsVwPxn9)mp||dmx2GlbN#>hOQ+3;0kXk z_|B!V&DM3Y0UCZvx^b3*h^_8$2H#Bc@3L7A=bh;!!yCCoOhxNI2|UsRh8tA_PD8je z_u5U$6k@{JA!14s3 zP4j{VnP#N;7p!}^9?Y}qB5xXaI#4w{7zm^@FcOaXAe`@aI6uya^8gn3LY||m-(&_Z zJUCJO&B7tqW0T!ZEj&o8=aAWO)Upy4s_4!HpiPA2S$a;JU4 z1*9IX<`!eJ**G%ksNzqL_9!8o)A|H_@FuXrs8c7$-JWkF$I-$TH>wkJk-R#A^ zC2p^5vaIUfV7Pr9e~#zm=(ljBAwD|F5)qT!g{5&v0(C-4^Ez00@-7)oYCN=kFV<2p zj3zpwxt@yX>6|@T=1O5e7=0Lj>hIS7e0=sTTF)ZSwQY^h4Hdr2-hU+6ag3N+5`HqafXOT;&~3G7KrUWE&Bx*9ZrCAqtVAF z``W<}xm6Sib*3j+em^(7i#VC-7k*lx&(C{@o1ajjfIAuIxprEIu`lZk+@AQzOS{$CPvbOi-T&7)X zQ+mOj+wd>kg-L|#e}0q-GdHdvDE|k{U6wHR8+X3DqIf-|0)<)c_!8E{N-xkR(XFjR z&u{M;)w30{O3}+d!2g}xk~CAB!C;o)>j5vqRCt%yj$!!8`443s(EYN*wG*0c6~-kt zr@Ef=VOobwTQ6=XR+K$cCc*JEg1z=+Q<-tsUHeC-e|BCjyfv(d&DV&1pIV#> zmQkMEgXfPqd@MbeUFg;h6dgw?abj<3wfSk7Y@XwnL@dZq5tP{tN}j6l51h>J zKyJ|-e=H1YHbNN4HQVFsEI_ul+ zMh#`M6`p8*=n`k1W$@X>G@2m?%SC{r=dPZo(&lDj$9Zs`f$f~iog(NhRo$+$9SRov zsC2$I;*R911`MOQ6e8Ri9VT|@E8_Ku>~q0`A%Qxp-rZ0!sSLMI#W>fWrN!p*923LX z8)N+MYzv6ge!2gUDY)48#s|A*+$r6n_^5>qC(A$cwI zkWtQ)x*Ng1wBuDPID`K6XcK<&NazQ5tVy=s_lErWGdNVEdSj0z!rM(HS7r!nWDLD; zKfvDUY7%YrLDrWua4yFKDNKQ#XLLXC7|VwP(X( zt2NGt;?{kx1wO^veg*w|>{BOVwaz=>JaBoZc)B7Krz;C>P7mvu-$4jv*RNO>tGRFw=TN9v$BKesFNIly_dZFC1iU8zHz8?YBf% z&>Y;4D7F>b)4MQ2vW&9dGf;ntW8n&c}N=l_94bmVD z4bqUNI!k+yacNmCrLEGUJ!qkXc+}(9->MoCkT$bMn#SvI#9rUnVf(u zM77D)Cou8TkCpecBIDm6BI|?Ah_G2t9f${cl#*pGBoCWBj5>dO5P2VTkmj5VPv&-j zWmfu-BzJiQ`YwL*=alRMCdzqe0$O37dsPqxxixfYr$&j`us*)IHDrDAr*lirLow9E zOLowf2#Z@0^@bp-7uhx(n$GWX6=XBYT~-;F()F7D_U+qekHs;x45D7yI^BshY}*_v zP5f%E;>Gf3Udt<#s*7I8`f!KcjoHo0UYq|oaBbOQHIL%1dJc<7huX1P);d|Zl&3fU z#f6yboD8H7-uRzTOJ+7S=4Nwe9wF=K`C;9Ysao+{!x;qY7H&jOBr_=W%|*<=Ga`8b8J^JF$qjwyvv zX_6^PW*YC%gjav}eV!YPU_@B3u(9Q%Kx*VYwRTX{Sdp=IvK54`cTR*qDl^l=*bWd^ zx#xFg)Vx&W_K}!rZ%sK~8xrIjc>L{re(w!xF>v*tP zL{HJae7X`1-^LfxQ0K}cC7KSS@gFHKjO_b=p-=m4O9<1}kZFJM2eF;0stYQbdDxj5 zn9r!8O-Fcg;*?r=_NCRHL4dZxHbH3Pzy(>ax$rp>@7rn4DK)8(=^6bi$XNHabnZyY z_!4Y(+87I=N@6lxAAei7J0i(ZdQ_GnqBj$#0vYJlz%j> z3xzbU_->a!m*bw&<>gfm-jmN(ISQT5U1dty3OaJDn#El;C1s!9bVo;bitZg)@D~H^ zX(oerm+EIDl+B?o^Gry)=axy#8PCz^v?cn~s$vA=U281?LGMbvYnDd1;2zS|@=3Z{ zWGyN?kjh{e#;FY$>cfHZIRS zEx^hmRsgND&8G4D)QNO)!he zW-v|nR`TG}nY7)!DAPRt4R@-YaaV3Fl&)^PP8;{+UyICS?Ju&xhh*jRw-vr)?5F3Wbi@eiF>sFM@`TO@nMuBL0 z5e#NRLSn|twx!*s_%so^Z_l?q90UcY5$eTEgW_F8&I7bwO|l<_@y%~sVgw5^3hQ>C z2zWK~?H-Z9`&;}F4{Wj=LC*f9JEma|LH2N;erD15Ea1or!UpuX$isGzcls5LFKQaP zAn&2!_8rMFbbrZ=JhYQ{0Ps4kbw<2&Du}GA5<#D9Z`K_Ct3^ZzJ|N<>hZ5r~_YB_a zJ@kZfLTFnubA#KV)I(f6T3d#(e`eMCLhfUTkRZ8hB+C4T;O$4E0SroD?TaZVv}dQh zY}>NlEy>iGg0Xwhn&e{QaZ0>lKfqX3h#0357_nda;%oC9S*q;UlDfLOqcWC0<#VTo zL~>-J=M2y%M}NT5U3C_Y$dlSy{C#uYVe>I87xzzSg8_c%j4e!@ZM*m~Xem++wR%XT zrr2~+v#y)|OcXkhY6uHo8UL?#`-T@8f^DqUX#MV$$W4xgwpF#F2SdAlNfLWZs56qi z?unV@rSav(>V4<|w_Iu-SgD%KgR2b4>Vs7ufV+6fzrk#V@J z-zVk5sb;rtpv3jN5NlYx@`t2yTVNfW9wjd1P?;0!E;oB z*a2|YH`D(CGyx|Za^Gm;fAo8$u`qwt{Y*Vi^=#uSv)k^OjnrF7>j^9CZrUL_ajT}~ zw?*?*^C&SA_?V`vn4vyArL9f_aB4xNP1-)B#(9iJIbj0?Y-lk+Gf^a+Tl zA1vY$nZ@n}Jl={uKSh@YS-ldq>>P_Jj_MbeAB)_m9@4oQuC&-;apdIaW)E3eKmk*4 z5_Iiyj>emXA}SoLz7@AA^TLUz7~6JEMw^|h|8sDD#8S_AR8YtvaY9r-(4_S0t%IFG zW;H}sGg2jXYdR^nxTkghprdUg{j@&f<>?TK(R_jw_#cU&T3{LV*+N1{c#ckYs&-`@ ziXt))&vY=|aF`B0TG0-IeYuMpwKl z-@OZ;oqKWt8?^J?B6B=(i$w4sOaQWGmp6Y|eeS zuQqoXjJ*wAB$MC}1jSPkwY>ysbSO%_C3P^#$3Af9LXK@NTUyzcD!IT2h+M8JsBduk zNISl!uHJZyN4G_r(!G$3#)Fx2o72YEEiQfOi9w{VX6n@=wXaM!bd?V5bmO^^o)f_B zIIQzkRJOr$4$E;-45!78sW1kJ#n9g0UvBA&@^07pS<>#g)RD^PGSk{Xb(N%o*z>?$^!Qp@g#fC*aJ=HZ{HxL3M}0x~(j^0tbGf>u9%@iHY@o)c=^l>DP^e z*v`yjTNWGR_M#pURF64!08Eflk2{&_L6Z4{vqa*$LJ!Wy!Ulm$suUR%s9Ov0%Y9{W zHD)xkXX>YB!m7y9U<~;0_Pz%wp9J0~J}OG8@ugfQw0`kWj`yxeQpfY5P}0hKX1=j7 zc79J}(pu968||;9Y-2kWY9GI-D%8r~UVWz8oO)O*xN@Zc$ygJ=AuF$Dr_Y=tT1ipx zt95&=b~S_cS}U`+>NsoeR~pF&-V5zP(L-u(j~VCtp6M# zOVimP^ORnsu`5WJ z_A+Tc>@cY8FUEROa=dn3K+qJAOlgaqq5B%BXjdLF=3uVG0QYfbG&OIUU4Ct)m+a9PeZF z@Ms=-fydkXKRm4JgylBuqIAbVgtMO-9*C=y#B^*7$W(xX8-}`_kj?3YK!XKETV-?(2PPq=K*#Zx-5^pxg&-ra@24h6zH>_mC5N1L<`u2bp zB7r}?>Gd<(g}PCUV)7lngOnA!2+l&)A4EFdlR24ai3`Cwi>!)g#I?qG3cz_XQCNw? zBp0yG0I|FESk$`N9Q$8^$b(ISZZ# zR{4T!58$Ten^kk;@e8A2$q!pYzhk9GSQ?uju~R2XyTuupB#5t=i!tQuIV3Z127|y( zs5;2={l3S*g&{b)lynEu_g4HyeV4;F3BHF_?;2)C_OoXP<$L+~D(gJix9vf}2TG^G z9#LZ2r~C6*EN_sDlZ_WU;U#c}sjs62WixPtB9b?(a4{T{=eMhj?=o;lI8dy2kBZ#%uC_dO3>sqYlpwld)0g}qIJC6?O%zOuxcXohpxG!`1877Bam?qVn zOVg>ZIPJg=*_DnNWODXc*g$gfI^~;zQ=6&R zJY&}~Z@u^wH%nCewW4lo?3#=7Rrzl+$-E!29{?#O;9g*^p@SG_T#Iq_&PD5QHy%)w zuVBgQ`-WooYO$8qf(Z))9WM*q?3)$esFyJ*wT4?%;Nqjc4vuc_dML za^)SvCa(Jd+}m9=OcG8tZ4%%^)QPR3yR?C-csagC$q>JV6puiJoWP~QU#M?Kua|Z5 z)9?lRcN}?-prn-I3%$R#27SO119UqN2~k@h^iB#MIY7o%0y~9#ZnCI4e1jz)1?9>LcZ>(7LKn94V(-wz1ufrG$Q8xf9x0U4V3ki9s(2&R9B66;{ zt19)|$dYqIPdXKTY5WF&1s3ZTRGBfr z6Q?8vBbGrj)>6l=VcqW_y?zY`-SK!bIO}|t$nr2$~&u@xC9uf zcs7-HJexKhv%)j7Lf-a4`Gs4}hFh%|aR1=CGI@np^@2(bXl#rTGdX#WT~6;_EMsJA zz5SV_P&;Jf@180m?^Yfh11#QyvB_$Tor~+Nfs6hF?u=>qYFp3|IL$`TRz)rj9$&(m zEmMo3n{^#@y^Qf5HvY_4a$&EW!Sq93F2ZZBPn*3CGOd;eE>_$u?L6bd>UZ!Ol;SVf zM7H^A$z0zGI>u_K2J#I2!i1RvIM+u*zBjdgKda6S#w*?5CI^Y{Z%4cEf=6a=HWq3? zq&NmURwB|tZ>mO4Ij+X)A2-Mr+a)lmxRQU~PEjrbXKn=uOnE&|V!e2m+R|Os1Jj;pJZj()`jE~Yus@)^R?N89iF;<9+?`|Axrd- z!fq}#SbxU-$su#KVDw26CVwh7+cM$a=Ucx*&SRE#o67OHlCj^YWZ@{!Dmo;2JW5~>-C>>o(bUhLh^EO)8CjZ?6Z1rC{dCN>mD?!r0R^5ATb5Lg~L-a^{KnsQ<-}%TvyQ zoZPp6ddCMQ8SWN~Zkkmwctb2}3{skYoMIQvKq>9FljE9Jl^m5ztL(I#P(Y$&tH96a z^DdND0mr161YWJE5m2se+h;|lGI?G~^3r*~KTcAfxx#Ks|5jAHbY}JrR3uYL+;UyFVh4RHuMLULWcrKr8MRFAiiwzW{y^R}Faj#qM*0NJ6zl#+{ zonLVwUnmWag!IL07D+y&PsfVlZOI1$JK{4WG4iU=rEbH+<602 zgLf4|7YYs_4PO3a2Yw16DGU`5B3vUp76(pDs=ZGd%9Mij{zLh6EJ|p;c(%3#4&9|`~qinz3 zISFA!8wB`!-*Cm%9Hwa8+~4;sCV+v1Qs}oo==qN~5Y)Tsa~MJJT@y#uesd>D8UA<% zWfLqWRMlBiR*(wVce`HPsq+2QA4i`2U^uf;q0nz4;=01vF+A2rCSLkqZjagh-kmQ4*SFn`6!My9t$@0D3 zh5L?5B&vrJ9;Z3>MiTUp3d;q!=%_$>*Q_C@C zx6r~z=0w2IpY>PZ+F#)#`7MvRAN{6*{-b(TT z_Z^cD#cdE%w2A}uxu*lY^csAcC!*H5C5v2Qqu)n&$CzSbad#iJKvRMrRXb;iE1cB& z_SxP#bo*h>(MU-(fqn79M<^c#uhS$fmF4)-)MG9ow>q`0jK%`Jah1`DGuL(cai}?8 z5fhBjVZ?hD=`LBqLt}Pi|Gla^Npj0jp+IEfj~!Im1?TlNhD2dSy$UU#JW)w^^c!WTJGt& zQYx9$6kvrFSn(zE#=C;<^Inwj#2u8an4?YP7`*=0I2kAiL$HlDKZdXkN3Vceqa)MQ0?n4%P<|X|MoTpurKFZ3Y#%$RyaTZ{uKUhIbKI~ z-QM7bvR+Fu09wki8?-z%Ls{HYmdL70nZO7{I2oPp3ors5W^>mvHKz|kcj`rt7WNr@ zLEJc~gRwBrWsV`VmM=H-KAa!Aoak~C~zn7Q*Vw!LXqZB35w)3)g}Qq#?A5|)>oXCp$#x= zel|Fyzuz>}z--{5A>U-Sp%=$DMml~yb(0-DxcmwzHU3`yR{-=@plOY(QF2KZ3Fb=K zLdSoJ+)!y)>DWM%GXrT*7$x(E#YdV6q$MFfDg;U+{i~A!WE499F&J7RSuz?Jd**MpAD?IGr+wB0V0F! zgV}*gkP1{LT=g++dFgZg%ck=vQAv+ES_GPV;kFb179hd5n|c}5+n0WSpn3r#s|%>v zJF#Uy;wt+LSJWOB$Bvk=IpK}l<6Sur>DKBqqh4q*$6yd>7bHCuMuuGAXh&E7z$+|5 zd1LPpklSSOF^KLiA6C^o%PC(5-x3@RwzM zTA7YanSrT+(=teJ#n50a0^z0o<&?=@?jTfOt*<~cd$k!;$96FO15P72Sj%vN2`+r~ z6h_Kyn@*T8siV)6v!Ya&HG#s{qIdkOVB8FXLHrBZ=OX z+@7Uj!Y{Dco%D&0PCjAQb1Ur)ke<}(+C*9uFei?)v6T6yOl)d1?BY}sX9_|hz z7MUHmGX}R-ZyIssc!YtAGu~ZJa0qWa`Uo39D4ZSYdy;@t2m^o3z`M;0@RcYE^4)G8 zw^l`7j(x%Fm`eGMMU7Xmv_Fi!0;y*18<>FV6sXWA3ueA*2rRC=u$9N}#bkC~ZG>2A z@wa~kNr!E1#nJP7{+y0dUurw%U{|LhztLLwjnTd+(^r!Ce6ehzg7^i{`%|UPKnQOt zm@C3K2u`d-rc>n5Gs%96&TGiQaEgMM={(pve4Q714Gi}rE1f&+Z!@Q{KZSnb)9Rp^ z95@aC=pd+Gc|Jr;zk1?8_oGkX35Y5wAhbV{w-1H~_Hcg5EsOMh0Fur% zm@lwMhg3etbDqLN%+E8(4J!es$CI^~m)`$r(#0)TN3y;ArOZ!9SEKvlIS{#wV?GSc z$|rfKC5uz~#fCm>sD&D#+ZJt^G%OU@v*vIy`fg@6T^!1;m2n^}AC420QDU1Jz$^XL zSAxRi^3@YzdW<9nzfAX&F*;P?&e!he_b_)8Cv27rvlXIVOADOM8fE>+b>iBTZ+q6} zU8iNmrX) zF>8`Ic9vp!#qdfgrAlCJN``~vN=LPsi3+dt2W${2IAhrTgow@1uwqh%BhB?Nc1xVG z&SCVz1S?2!q{l_`WYdJ*8~zZKf2;^(CjxvXd{}kpyu)U<>-KU{6EvN9{a!%o_G<4M zp_R_Wt{A0%v*gqsopbPvU>UuWSKnaT+q*?*dvXSJNf%t1PqU9G4gTUN`St1JJC(x; zGZjJQZ40%*Gfe@{v8B+Qe#(dhG7zyzdP=~dp0zDTNe>GgYPiYodc;=G2lL7MV7gcj) ze!t(&>8;hH*3IFsWlW40^LNTFhFz`M57@5KU>b`}N|dHe9{>r>U0n@$04EPY;2?s4 zR}?V*iLK(Yi_COyxA1-Ef>QS|VA1qM84SaQmBe5rR($$Xbc)~u;Z&_a3J7*7p&UOyX7AY^{7T4GE%*%x@+T6FVk>0Z5O3$llGs%X6pG!7CbujbkxzgY@{v4En#W%pO^I{i#KE zQ7-jIKYMU}3zFh^o^3bpfDFa#Z?@ku>#Ie-$2v7ok z8YBh1pC{#n>YW2}jdmUI6uf9ONOt2+haY@bAqV38HAE^lzv6FFPsR-Ek5yTsedjcu z2wnWu4yAkOTqg;wOF06a%5QZ}=MAWjrBM6-qKpQc~qwG`4i!H!*8i?2d8| zS+|+GLTrVwC^|0huteEcO&rRqtDg5gPS$!5vXi(4fKoQR@6I1^u=IT#gs*y3`Tk7g z*dVEm=JN&XWy1g9(Wi22txdu(xcT+__5%C(VgS!Vk+560ELOY7&Fqpl2MZN4( zoAb95K;3u?24A~PHh)M+>I6>?)LhMYJ}-FGp0%=2mKHjZ5;qb}$$q4uN@vMR{Kuk> zy8QN^Uc;tK7-v{``7C5mB+i>ta3bJ-PVym5 zUdz=9T)1{3->_#^jyJmz4AR{*m=oPVGG75c3){0Z(N8s+8kGv40%{hR%j7vyqJT?567vzUZb_pdzL4 zdQ8?IlDQ2&8WV``tngFVG%!baA|+nfZI(mzas!1E6IwvFYXdbrt9g$~)OcL_aGTGQ zi+zte|9ey+b}$cKiM+`|SP%K&xqd9U`jlwb^s5!10AK<}Ki~fHiL95WI()aK_`c&l zvjIz74}s)8j(pX{;Ha!La{qN=P_kM%;1iP6=7GpnNgl6|Y|F9aa03kWUBBuPhm-(S zPoS(fCA|`zgfr&HY#3J^5Ab<(^Y5kGQ=pM&)fR&{DhI0~U)15uU^9KY^5$^S5^6-H z1FL=Bk@`+9<2R-T&Z0U317)Oa@^JgTLV!3I;uaHR+OlCjnUpTjp?{uELjI7;}QEkZD{ zPy+h>0`fa`iwj#CmTs}~a2I)k*T?CINzF++%n9^gsK#axSfWQYNQlx#Qb9S0!c?C? z+{I%Ju{BDrfO~@^k^OoWVk;$NF`V5jwjz!mP35uA640PMfOyT~mS#{aUKp*`q=OYF zW@j+J*lCY)Y`bNKZ+^k?t6OYR7O?GlRgQo4+f5(#)`lsX>yI}_OBfr0<@(~@>Peh5 zct{Yw1+d3rM?HQH07}gZt_4Umk_s4;Gf&HWH{aC1Cs09UQsGv@es&`2Ia#E_AV$E^ zh`LMl;jVz{AUjwonABmu!jHmF+zUG-TO4UkL$+1t`WnDZi$THByy|r4dk|ejv<5_& z^A`H{6Wle&efGB;?!dmw)2${!2HxAD)k9#32Y>OlzbBLS>3igRi~D;ELys}h z^~isCDqT#(n1{Pw7pG!SWYKBC{NVoe^B3&>2>IUt1=$7c8jco0m#Y9|)gmI<44&?% zj8CX5(U3mGT>HdeD-p`=Kr-eiGVHq#uyNi68th}pK#mv&*&7r)QL#|?v~wAYInH?C zS-D?L(R7RfDWejViL2cuuk{PBC6c(_0q;>7wpAzc2hytUP1%E>*xd=Z^8z`K*WZJw zXw^+kbG(4c!ZZ|4r)=l3@z;Wm_%JpArtuHlt{lWmcP3op%^>T$AqbJ;*8r32<5gdR z`rsQwr2R+*K5CD(#zQN^234vJID4E-4JM%aRXyIL@hzwJ$k(bx1Wp?anleyK+kpFTgK14{J1dXm z<=A8lT-r@#oxlLf`S}H9_()dZ{r-Cu+HiHrd2!Z&C`JSaDzc;l z>Bu_`;}zFzW*n^@Z?=7nU@}LJ){w_CT z{ojDp|5kox*O3{M2ytt@d|at~`#wc7aZo>>hI3v7TfQR1rwZZup1_K86s8oZ2fjx< zWhwoGzti)(EJHccG%YN!CT)WjRM>f}BerS`Zg3s~KQ{>SWGsCEAZ&nn7A`HS>O}##67SZ;y)Oq0e#1_Ww*ElRf<(z_c1`$@1hBs!r*{B6nAv#Ec0l z*e_pHe2$>J1sifW>`#Y7ZQJ##jl4A6w};gCM51f`Ju=-QXOo{>1GE&&)e75DbkOmC zn4u$HtDU(mhQn}y-!d>qSWHF6(x7sZS#s_$+5R7nNiYtYG5+%KjLZ<(AD4|j9kw^* z`1xMPg(e>>Br?tt&BM=0vyQxXYb;(}A7VDvx@92S-e$0UaKypP89T@N>+6Erk+`g{ z$O|#1ET*b9gV9bS6-apo4&bgvTL`LoTYy5dDftjy>e|zU7lp{Y4w0EI_%ge91jeq5 znfGC1^0v^B`7~C2t&T`i%Pdh{TE)^f?z)v`K-L%F~BTtrM&w zSAaQl5zhNtU2Z$1pS*;a8CDaDv4HUpDWh3dT~Y@BSiNYf94+&@7zoBcC8#}mUV(|Q zh19ZicR0yl2}ig;uB=K5e^zsUDa`^Jc+~iaL*{`(CQ0I1pqx6o{QkbG(-yaF3{4gl zBf)&()3lJpn6(coLE0UKa48Mk2-D)D7b}`fV&s72k~jZN@5Qpzz@>VU^8qo z|L5b7f<{vYUFDNi*HKH_KqS6-^f?fW3;;7W__F3971a%vg>pP@erc>*BzmAbItUDS z0MM1W>T#m!zbcyON(5g=DDfeogs%Q37&*XKxYw28Tr2m7n?HT|V?}OSOwsW zTX_IJ{P-N+SfNcRAgw~DQ@b^xNr=mMa#&W_;#=IgjPC}55BYAsl4#&e_`j|t-)$hE zXy2+&Z97YWFBamy>&@&%d;ejPXbE{M;2iAx&pB9ub8v&3QDD>G&mciZ`>zPM7dq7*+DMkfBgtnBH`q}e(>Lis`P*Tn(o-P zzn{YY{+74Gw*S_*!XMmK_+P)QzhAc=Jcj@M3;5ZN_5Yv0kPF`y_NTP&=9QAjmLZ>v N@(DGC40&VU{{x9m|7-vN literal 0 HcmV?d00001 diff --git a/soninrv/docs/report2.md b/soninrv/docs/report2.md new file mode 100644 index 0000000..d626e8e --- /dev/null +++ b/soninrv/docs/report2.md @@ -0,0 +1,105 @@ +# Отчёт по лабораторной работе «Поиск выхода из лабиринта» + +## 1. Цель работы + +Разработать гибкую расширяемую программу для загрузки лабиринта из текстового файла, поиска пути от старта до выхода с возможностью выбора алгоритма и экспериментального сравнения алгоритмов. В ходе работы применены паттерны проектирования GoF: **Builder**, **Strategy**, **Observer**, **Command**. + +Реализованные алгоритмы поиска пути: + +- **BFS** (поиск в ширину) — гарантирует кратчайший путь по числу шагов. +- **DFS** (поиск в глубину) — не гарантирует кратчайший путь, но быстрее при удачном порядке соседей. +- **A\*** (с манхэттенской эвристикой) — направленный поиск, учитывает веса клеток. +- **Dijkstra** — оптимален для взвешенных графов, без эвристики. + +Тестовые лабиринты: + +- Маленький 10×10 — простой путь. +- Средний 50×50 — с тупиками (алгоритм Прима). +- Большой 100×100 — запутанная структура. +- Пустой 30×30 — без стен, демонстрирует максимальную нагрузку. +- Без выхода 20×20 — старт и выход разделены глухой стеной. +- Взвешенный 40×40 — клетки с разным весом: асфальт (1), песок (2), болото (3). + +Каждый эксперимент повторялся 7 раз, результаты усреднены. + +## 2. Описание паттернов + +| Паттерн | Классы | Назначение | +|---|---|---| +| Builder | `MazeBuilder`, `TextFileMazeBuilder` | Скрывает парсинг файла; новый формат = новый класс | +| Strategy | `PathFindingStrategy`, BFS/DFS/A\*/Dijkstra | Смена алгоритма одной строкой без изменения остального кода | +| Observer | `Observer`, `ConsoleView` | Визуализация отделена от логики поиска | +| Command | `Command`, `MoveCommand`, `CommandHistory` | Пошаговое управление игроком с поддержкой undo | + +## 3. Результаты экспериментов + +Усреднённые значения (7 повторений) представлены в таблице: + +| Лабиринт | Алгоритм | Время, мс | Посещено клеток | Длина пути | +|---|---|---|---|---| +| 10×10 | BFS | 0.081 | 28 | 21 | +| 10×10 | DFS | 0.053 | 22 | 21 | +| 10×10 | A\* | 0.088 | 24 | 21 | +| 10×10 | Dijkstra | 0.672 | 28 | 21 | +| 50×50 | BFS | 1.150 | 493 | 257 | +| 50×50 | DFS | 0.614 | 263 | 257 | +| 50×50 | A\* | 1.220 | 357 | 257 | +| 50×50 | Dijkstra | 1.685 | 493 | 257 | +| 100×100 | BFS | 11.378 | 4783 | 1953 | +| 100×100 | DFS | 5.141 | 2161 | 1953 | +| 100×100 | A\* | 18.019 | 4741 | 1953 | +| 100×100 | Dijkstra | 17.489 | 4783 | 1953 | +| 30×30 пустой | BFS | 1.832 | 784 | 55 | +| 30×30 пустой | DFS | 1.151 | 433 | 379 | +| 30×30 пустой | A\* | 3.748 | 784 | 55 | +| 30×30 пустой | Dijkstra | 3.945 | 784 | 55 | +| 20×20 без выхода | BFS | 0.370 | 162 | — | +| 20×20 без выхода | DFS | 0.373 | 162 | — | +| 20×20 без выхода | A\* | 0.708 | 162 | — | +| 20×20 без выхода | Dijkstra | 0.677 | 162 | — | +| 40×40 взвешенный | BFS | 1.104 | 533 | 321 | +| 40×40 взвешенный | DFS | 0.774 | 361 | 321 | +| 40×40 взвешенный | A\* | 1.516 | 452 | 321 | +| 40×40 взвешенный | Dijkstra | 1.725 | 533 | 321 | + +Графическое представление результатов приведено на рисунке ниже. + +![Сравнение производительности](performance_comparison.png) + +## 4. Анализ результатов + +### 4.1. BFS + +Гарантирует кратчайший путь по числу шагов. Исследует все клетки на расстоянии d перед переходом к d+1, поэтому число посещённых клеток максимально среди всех алгоритмов — на лабиринте 100×100 это 4783 клетки. На пустом лабиринте 30×30 BFS находит оптимальный путь длиной 55 клеток, тогда как DFS даёт 379. Время растёт линейно с размером: от 0.08 мс (10×10) до 11.4 мс (100×100). + +### 4.2. DFS + +Самый быстрый алгоритм по времени: на лабиринте 100×100 — 5.1 мс против 11.4 мс у BFS. Посещает вдвое меньше клеток (2161 против 4783), так как уходит глубоко в одном направлении. Однако на пустом лабиринте DFS даёт путь в 7 раз длиннее оптимального (379 против 55) — алгоритм уходит в угол и обходит весь лабиринт по периметру. Оптимальная длина пути при этом совпадает с BFS только в лабиринтах-лабиринтах (50×50 и 100×100), где единственный путь — сам. + +### 4.3. A\* + +На невзвешенных лабиринтах находит тот же кратчайший путь, что BFS, но исследует на 20–30% меньше клеток благодаря манхэттенской эвристике (на 50×50: 357 против 493). Однако на большом лабиринте 100×100 оказывается медленнее BFS (18 мс против 11 мс) из-за накладных расходов на `heapq`. На взвешенном лабиринте A\* корректно учитывает стоимость клеток, в отличие от BFS. + +### 4.4. Dijkstra + +На невзвешенных лабиринтах полностью совпадает с BFS по посещённым клеткам и длине пути, но медленнее из-за `heapq` вместо `deque`. На взвешенном лабиринте 40×40 корректно минимизирует суммарный вес пути. Практически вытесняется A\* везде, где цель заранее известна. + +### 4.5. Лабиринт «без выхода» + +Все алгоритмы обходят все 162 доступные клетки левой секции и возвращают пустой путь. Реализация корректно обрабатывает этот случай через событие `no_path` для Observer. A\* и Dijkstra работают медленнее (0.7 мс против 0.37 мс у BFS/DFS) из-за накладных расходов `heapq` при полном обходе без нахождения цели. + +## 5. Выводы и рекомендации + +На основе полученных результатов можно сформулировать следующие рекомендации: + +- **Кратчайший путь в невзвешенном лабиринте → BFS.** Гарантированный результат, простая реализация на `deque`, линейное масштабирование. + +- **Максимальная скорость, длина пути не критична → DFS.** В 2 раза быстрее BFS на больших лабиринтах, посещает вдвое меньше клеток. Не использовать на открытых пространствах — путь может быть многократно длиннее оптимального. + +- **Взвешенный граф, цель известна → A\*.** Направленный поиск + учёт весов. На небольших и средних лабиринтах быстрее и экономнее BFS. На очень больших (100×100+) накладные расходы `heapq` могут перевесить выигрыш от эвристики. + +- **Взвешенный граф, нужны все кратчайшие расстояния → Dijkstra.** Оптимален без целевой точки. С целевой точкой предпочтительнее A\*. + +**Итог:** для навигации в лабиринте с одним выходом оптимален BFS (гарантия) или DFS (скорость). A\* предпочтителен при взвешенных клетках. Паттерн Strategy позволяет переключать алгоритмы без изменения остального кода — `solver.set_strategy(AStarStrategy())`. + +