From 2d53367e7467882d8974170821417a7f1ec7f08a Mon Sep 17 00:00:00 2001 From: abdelrahman wael Date: Mon, 17 Feb 2020 17:33:42 +0200 Subject: [PATCH] adding assignment 1 --- assigmenet 1/a1/README.txt | 11 + assigmenet 1/a1/exploring_word_vectors.ipynb | 951 +++++++++++++++++++ assigmenet 1/a1/imgs/inner_product.png | Bin 0 -> 16379 bytes assigmenet 1/a1/imgs/svd.png | Bin 0 -> 10428 bytes assigmenet 1/a1/imgs/test_plot.png | Bin 0 -> 34142 bytes 5 files changed, 962 insertions(+) create mode 100644 assigmenet 1/a1/README.txt create mode 100644 assigmenet 1/a1/exploring_word_vectors.ipynb create mode 100644 assigmenet 1/a1/imgs/inner_product.png create mode 100644 assigmenet 1/a1/imgs/svd.png create mode 100644 assigmenet 1/a1/imgs/test_plot.png diff --git a/assigmenet 1/a1/README.txt b/assigmenet 1/a1/README.txt new file mode 100644 index 0000000..1d8c799 --- /dev/null +++ b/assigmenet 1/a1/README.txt @@ -0,0 +1,11 @@ +Welcome to CS224N! + +We'll be using Python throughout the course. If you've got a good Python setup already, great! But make sure that it is at least Python version 3.5. If not, the easiest thing to do is to make sure you have at least 3GB free on your computer and then to head over to (https://www.anaconda.com/download/) and install the Python 3 version of Anaconda. It will work on any operating system. + +After you have installed conda, close any open terminals you might have. Then open a new terminal and run the following command: + +conda install gensim + +Homework 1 (only) is a Jupyter Notebook. With the above done you should be able to get underway by typing: + +jupyter notebook exploring_word_vectors.ipynb diff --git a/assigmenet 1/a1/exploring_word_vectors.ipynb b/assigmenet 1/a1/exploring_word_vectors.ipynb new file mode 100644 index 0000000..eaf87fb --- /dev/null +++ b/assigmenet 1/a1/exploring_word_vectors.ipynb @@ -0,0 +1,951 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CS224N Assignment 1: Exploring Word Vectors (25 Points)\n", + "\n", + "Welcome to CS224n! \n", + "\n", + "Before you start, make sure you read the README.txt in the same directory as this notebook. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# All Import Statements Defined Here\n", + "# Note: Do not add to this list.\n", + "# All the dependencies you need, can be installed by running .\n", + "# ----------------\n", + "\n", + "import sys\n", + "assert sys.version_info[0]==3\n", + "assert sys.version_info[1] >= 5\n", + "\n", + "from gensim.models import KeyedVectors\n", + "from gensim.test.utils import datapath\n", + "import pprint\n", + "import matplotlib.pyplot as plt\n", + "plt.rcParams['figure.figsize'] = [10, 5]\n", + "import nltk\n", + "nltk.download('reuters')\n", + "from nltk.corpus import reuters\n", + "import numpy as np\n", + "import random\n", + "import scipy as sp\n", + "from sklearn.decomposition import TruncatedSVD\n", + "from sklearn.decomposition import PCA\n", + "\n", + "START_TOKEN = ''\n", + "END_TOKEN = ''\n", + "\n", + "np.random.seed(0)\n", + "random.seed(0)\n", + "# ----------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Please Write Your SUNet ID Here: " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Word Vectors\n", + "\n", + "Word Vectors are often used as a fundamental component for downstream NLP tasks, e.g. question answering, text generation, translation, etc., so it is important to build some intuitions as to their strengths and weaknesses. Here, you will explore two types of word vectors: those derived from *co-occurrence matrices*, and those derived via *word2vec*. \n", + "\n", + "**Assignment Notes:** Please make sure to save the notebook as you go along. Submission Instructions are located at the bottom of the notebook.\n", + "\n", + "**Note on Terminology:** The terms \"word vectors\" and \"word embeddings\" are often used interchangeably. The term \"embedding\" refers to the fact that we are encoding aspects of a word's meaning in a lower dimensional space. As [Wikipedia](https://en.wikipedia.org/wiki/Word_embedding) states, \"*conceptually it involves a mathematical embedding from a space with one dimension per word to a continuous vector space with a much lower dimension*\"." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 1: Count-Based Word Vectors (10 points)\n", + "\n", + "Most word vector models start from the following idea:\n", + "\n", + "*You shall know a word by the company it keeps ([Firth, J. R. 1957:11](https://en.wikipedia.org/wiki/John_Rupert_Firth))*\n", + "\n", + "Many word vector implementations are driven by the idea that similar words, i.e., (near) synonyms, will be used in similar contexts. As a result, similar words will often be spoken or written along with a shared subset of words, i.e., contexts. By examining these contexts, we can try to develop embeddings for our words. With this intuition in mind, many \"old school\" approaches to constructing word vectors relied on word counts. Here we elaborate upon one of those strategies, *co-occurrence matrices* (for more information, see [here](http://web.stanford.edu/class/cs124/lec/vectorsemantics.video.pdf) or [here](https://medium.com/data-science-group-iitr/word-embedding-2d05d270b285))." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Co-Occurrence\n", + "\n", + "A co-occurrence matrix counts how often things co-occur in some environment. Given some word $w_i$ occurring in the document, we consider the *context window* surrounding $w_i$. Supposing our fixed window size is $n$, then this is the $n$ preceding and $n$ subsequent words in that document, i.e. words $w_{i-n} \\dots w_{i-1}$ and $w_{i+1} \\dots w_{i+n}$. We build a *co-occurrence matrix* $M$, which is a symmetric word-by-word matrix in which $M_{ij}$ is the number of times $w_j$ appears inside $w_i$'s window.\n", + "\n", + "**Example: Co-Occurrence with Fixed Window of n=1**:\n", + "\n", + "Document 1: \"all that glitters is not gold\"\n", + "\n", + "Document 2: \"all is well that ends well\"\n", + "\n", + "\n", + "| * | START | all | that | glitters | is | not | gold | well | ends | END |\n", + "|----------|-------|-----|------|----------|------|------|-------|------|------|-----|\n", + "| START | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |\n", + "| all | 2 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |\n", + "| that | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 |\n", + "| glitters | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |\n", + "| is | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |\n", + "| not | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 |\n", + "| gold | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |\n", + "| well | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |\n", + "| ends | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |\n", + "| END | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |\n", + "\n", + "**Note:** In NLP, we often add START and END tokens to represent the beginning and end of sentences, paragraphs or documents. In thise case we imagine START and END tokens encapsulating each document, e.g., \"START All that glitters is not gold END\", and include these tokens in our co-occurrence counts.\n", + "\n", + "The rows (or columns) of this matrix provide one type of word vectors (those based on word-word co-occurrence), but the vectors will be large in general (linear in the number of distinct words in a corpus). Thus, our next step is to run *dimensionality reduction*. In particular, we will run *SVD (Singular Value Decomposition)*, which is a kind of generalized *PCA (Principal Components Analysis)* to select the top $k$ principal components. Here's a visualization of dimensionality reduction with SVD. In this picture our co-occurrence matrix is $A$ with $n$ rows corresponding to $n$ words. We obtain a full matrix decomposition, with the singular values ordered in the diagonal $S$ matrix, and our new, shorter length-$k$ word vectors in $U_k$.\n", + "\n", + "![Picture of an SVD](imgs/svd.png \"SVD\")\n", + "\n", + "This reduced-dimensionality co-occurrence representation preserves semantic relationships between words, e.g. *doctor* and *hospital* will be closer than *doctor* and *dog*. \n", + "\n", + "**Notes:** If you can barely remember what an eigenvalue is, here's [a slow, friendly introduction to SVD](https://davetang.org/file/Singular_Value_Decomposition_Tutorial.pdf). If you want to learn more thoroughly about PCA or SVD, feel free to check out lectures [7](https://web.stanford.edu/class/cs168/l/l7.pdf), [8](http://theory.stanford.edu/~tim/s15/l/l8.pdf), and [9](https://web.stanford.edu/class/cs168/l/l9.pdf) of CS168. These course notes provide a great high-level treatment of these general purpose algorithms. Though, for the purpose of this class, you only need to know how to extract the k-dimensional embeddings by utilizing pre-programmed implementations of these algorithms from the numpy, scipy, or sklearn python packages. In practice, it is challenging to apply full SVD to large corpora because of the memory needed to perform PCA or SVD. However, if you only want the top $k$ vector components for relatively small $k$ — known as *[Truncated SVD](https://en.wikipedia.org/wiki/Singular_value_decomposition#Truncated_SVD)* — then there are reasonably scalable techniques to compute those iteratively." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plotting Co-Occurrence Word Embeddings\n", + "\n", + "Here, we will be using the Reuters (business and financial news) corpus. If you haven't run the import cell at the top of this page, please run it now (click it and press SHIFT-RETURN). The corpus consists of 10,788 news documents totaling 1.3 million words. These documents span 90 categories and are split into train and test. For more details, please see https://www.nltk.org/book/ch02.html. We provide a `read_corpus` function below that pulls out only articles from the \"crude\" (i.e. news articles about oil, gas, etc.) category. The function also adds START and END tokens to each of the documents, and lowercases words. You do **not** have perform any other kind of pre-processing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def read_corpus(category=\"crude\"):\n", + " \"\"\" Read files from the specified Reuter's category.\n", + " Params:\n", + " category (string): category name\n", + " Return:\n", + " list of lists, with words from each of the processed files\n", + " \"\"\"\n", + " files = reuters.fileids(category)\n", + " return [[START_TOKEN] + [w.lower() for w in list(reuters.words(f))] + [END_TOKEN] for f in files]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's have a look what these documents are like…." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "reuters_corpus = read_corpus()\n", + "pprint.pprint(reuters_corpus[:3], compact=True, width=100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 1.1: Implement `distinct_words` [code] (2 points)\n", + "\n", + "Write a method to work out the distinct words (word types) that occur in the corpus. You can do this with `for` loops, but it's more efficient to do it with Python list comprehensions. In particular, [this](https://coderwall.com/p/rcmaea/flatten-a-list-of-lists-in-one-line-in-python) may be useful to flatten a list of lists. If you're not familiar with Python list comprehensions in general, here's [more information](https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Comprehensions.html).\n", + "\n", + "You may find it useful to use [Python sets](https://www.w3schools.com/python/python_sets.asp) to remove duplicate words." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def distinct_words(corpus):\n", + " \"\"\" Determine a list of distinct words for the corpus.\n", + " Params:\n", + " corpus (list of list of strings): corpus of documents\n", + " Return:\n", + " corpus_words (list of strings): list of distinct words across the corpus, sorted (using python 'sorted' function)\n", + " num_corpus_words (integer): number of distinct words across the corpus\n", + " \"\"\"\n", + " corpus_words = []\n", + " num_corpus_words = -1\n", + " \n", + " # ------------------\n", + " # Write your implementation here.\n", + "\n", + "\n", + " # ------------------\n", + "\n", + " return corpus_words, num_corpus_words" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ---------------------\n", + "# Run this sanity check\n", + "# Note that this not an exhaustive check for correctness.\n", + "# ---------------------\n", + "\n", + "# Define toy corpus\n", + "test_corpus = [\"START All that glitters isn't gold END\".split(\" \"), \"START All's well that ends well END\".split(\" \")]\n", + "test_corpus_words, num_corpus_words = distinct_words(test_corpus)\n", + "\n", + "# Correct answers\n", + "ans_test_corpus_words = sorted(list(set([\"START\", \"All\", \"ends\", \"that\", \"gold\", \"All's\", \"glitters\", \"isn't\", \"well\", \"END\"])))\n", + "ans_num_corpus_words = len(ans_test_corpus_words)\n", + "\n", + "# Test correct number of words\n", + "assert(num_corpus_words == ans_num_corpus_words), \"Incorrect number of distinct words. Correct: {}. Yours: {}\".format(ans_num_corpus_words, num_corpus_words)\n", + "\n", + "# Test correct words\n", + "assert (test_corpus_words == ans_test_corpus_words), \"Incorrect corpus_words.\\nCorrect: {}\\nYours: {}\".format(str(ans_test_corpus_words), str(test_corpus_words))\n", + "\n", + "# Print Success\n", + "print (\"-\" * 80)\n", + "print(\"Passed All Tests!\")\n", + "print (\"-\" * 80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 1.2: Implement `compute_co_occurrence_matrix` [code] (3 points)\n", + "\n", + "Write a method that constructs a co-occurrence matrix for a certain window-size $n$ (with a default of 4), considering words $n$ before and $n$ after the word in the center of the window. Here, we start to use `numpy (np)` to represent vectors, matrices, and tensors. If you're not familiar with NumPy, there's a NumPy tutorial in the second half of this cs231n [Python NumPy tutorial](http://cs231n.github.io/python-numpy-tutorial/).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_co_occurrence_matrix(corpus, window_size=4):\n", + " \"\"\" Compute co-occurrence matrix for the given corpus and window_size (default of 4).\n", + " \n", + " Note: Each word in a document should be at the center of a window. Words near edges will have a smaller\n", + " number of co-occurring words.\n", + " \n", + " For example, if we take the document \"START All that glitters is not gold END\" with window size of 4,\n", + " \"All\" will co-occur with \"START\", \"that\", \"glitters\", \"is\", and \"not\".\n", + " \n", + " Params:\n", + " corpus (list of list of strings): corpus of documents\n", + " window_size (int): size of context window\n", + " Return:\n", + " M (numpy matrix of shape (number of corpus words, number of corpus words)): \n", + " Co-occurence matrix of word counts. \n", + " The ordering of the words in the rows/columns should be the same as the ordering of the words given by the distinct_words function.\n", + " word2Ind (dict): dictionary that maps word to index (i.e. row/column number) for matrix M.\n", + " \"\"\"\n", + " words, num_words = distinct_words(corpus)\n", + " M = None\n", + " word2Ind = {}\n", + " \n", + " # ------------------\n", + " # Write your implementation here.\n", + "\n", + "\n", + " # ------------------\n", + "\n", + " return M, word2Ind" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ---------------------\n", + "# Run this sanity check\n", + "# Note that this is not an exhaustive check for correctness.\n", + "# ---------------------\n", + "\n", + "# Define toy corpus and get student's co-occurrence matrix\n", + "test_corpus = [\"START All that glitters isn't gold END\".split(\" \"), \"START All's well that ends well END\".split(\" \")]\n", + "M_test, word2Ind_test = compute_co_occurrence_matrix(test_corpus, window_size=1)\n", + "\n", + "# Correct M and word2Ind\n", + "M_test_ans = np.array( \n", + " [[0., 0., 0., 1., 0., 0., 0., 0., 1., 0.,],\n", + " [0., 0., 0., 1., 0., 0., 0., 0., 0., 1.,],\n", + " [0., 0., 0., 0., 0., 0., 1., 0., 0., 1.,],\n", + " [1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,],\n", + " [0., 0., 0., 0., 0., 0., 0., 0., 1., 1.,],\n", + " [0., 0., 0., 0., 0., 0., 0., 1., 1., 0.,],\n", + " [0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,],\n", + " [0., 0., 0., 0., 0., 1., 1., 0., 0., 0.,],\n", + " [1., 0., 0., 0., 1., 1., 0., 0., 0., 1.,],\n", + " [0., 1., 1., 0., 1., 0., 0., 0., 1., 0.,]]\n", + ")\n", + "word2Ind_ans = {'All': 0, \"All's\": 1, 'END': 2, 'START': 3, 'ends': 4, 'glitters': 5, 'gold': 6, \"isn't\": 7, 'that': 8, 'well': 9}\n", + "\n", + "# Test correct word2Ind\n", + "assert (word2Ind_ans == word2Ind_test), \"Your word2Ind is incorrect:\\nCorrect: {}\\nYours: {}\".format(word2Ind_ans, word2Ind_test)\n", + "\n", + "# Test correct M shape\n", + "assert (M_test.shape == M_test_ans.shape), \"M matrix has incorrect shape.\\nCorrect: {}\\nYours: {}\".format(M_test.shape, M_test_ans.shape)\n", + "\n", + "# Test correct M values\n", + "for w1 in word2Ind_ans.keys():\n", + " idx1 = word2Ind_ans[w1]\n", + " for w2 in word2Ind_ans.keys():\n", + " idx2 = word2Ind_ans[w2]\n", + " student = M_test[idx1, idx2]\n", + " correct = M_test_ans[idx1, idx2]\n", + " if student != correct:\n", + " print(\"Correct M:\")\n", + " print(M_test_ans)\n", + " print(\"Your M: \")\n", + " print(M_test)\n", + " raise AssertionError(\"Incorrect count at index ({}, {})=({}, {}) in matrix M. Yours has {} but should have {}.\".format(idx1, idx2, w1, w2, student, correct))\n", + "\n", + "# Print Success\n", + "print (\"-\" * 80)\n", + "print(\"Passed All Tests!\")\n", + "print (\"-\" * 80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 1.3: Implement `reduce_to_k_dim` [code] (1 point)\n", + "\n", + "Construct a method that performs dimensionality reduction on the matrix to produce k-dimensional embeddings. Use SVD to take the top k components and produce a new matrix of k-dimensional embeddings. \n", + "\n", + "**Note:** All of numpy, scipy, and scikit-learn (`sklearn`) provide *some* implementation of SVD, but only scipy and sklearn provide an implementation of Truncated SVD, and only sklearn provides an efficient randomized algorithm for calculating large-scale Truncated SVD. So please use [sklearn.decomposition.TruncatedSVD](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.TruncatedSVD.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def reduce_to_k_dim(M, k=2):\n", + " \"\"\" Reduce a co-occurence count matrix of dimensionality (num_corpus_words, num_corpus_words)\n", + " to a matrix of dimensionality (num_corpus_words, k) using the following SVD function from Scikit-Learn:\n", + " - http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.TruncatedSVD.html\n", + " \n", + " Params:\n", + " M (numpy matrix of shape (number of corpus words, number of corpus words)): co-occurence matrix of word counts\n", + " k (int): embedding size of each word after dimension reduction\n", + " Return:\n", + " M_reduced (numpy matrix of shape (number of corpus words, k)): matrix of k-dimensioal word embeddings.\n", + " In terms of the SVD from math class, this actually returns U * S\n", + " \"\"\" \n", + " n_iters = 10 # Use this parameter in your call to `TruncatedSVD`\n", + " M_reduced = None\n", + " print(\"Running Truncated SVD over %i words...\" % (M.shape[0]))\n", + " \n", + " # ------------------\n", + " # Write your implementation here.\n", + " \n", + " \n", + " # ------------------\n", + "\n", + " print(\"Done.\")\n", + " return M_reduced" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ---------------------\n", + "# Run this sanity check\n", + "# Note that this not an exhaustive check for correctness \n", + "# In fact we only check that your M_reduced has the right dimensions.\n", + "# ---------------------\n", + "\n", + "# Define toy corpus and run student code\n", + "test_corpus = [\"START All that glitters isn't gold END\".split(\" \"), \"START All's well that ends well END\".split(\" \")]\n", + "M_test, word2Ind_test = compute_co_occurrence_matrix(test_corpus, window_size=1)\n", + "M_test_reduced = reduce_to_k_dim(M_test, k=2)\n", + "\n", + "# Test proper dimensions\n", + "assert (M_test_reduced.shape[0] == 10), \"M_reduced has {} rows; should have {}\".format(M_test_reduced.shape[0], 10)\n", + "assert (M_test_reduced.shape[1] == 2), \"M_reduced has {} columns; should have {}\".format(M_test_reduced.shape[1], 2)\n", + "\n", + "# Print Success\n", + "print (\"-\" * 80)\n", + "print(\"Passed All Tests!\")\n", + "print (\"-\" * 80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 1.4: Implement `plot_embeddings` [code] (1 point)\n", + "\n", + "Here you will write a function to plot a set of 2D vectors in 2D space. For graphs, we will use Matplotlib (`plt`).\n", + "\n", + "For this example, you may find it useful to adapt [this code](https://www.pythonmembers.club/2018/05/08/matplotlib-scatter-plot-annotate-set-text-at-label-each-point/). In the future, a good way to make a plot is to look at [the Matplotlib gallery](https://matplotlib.org/gallery/index.html), find a plot that looks somewhat like what you want, and adapt the code they give." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_embeddings(M_reduced, word2Ind, words):\n", + " \"\"\" Plot in a scatterplot the embeddings of the words specified in the list \"words\".\n", + " NOTE: do not plot all the words listed in M_reduced / word2Ind.\n", + " Include a label next to each point.\n", + " \n", + " Params:\n", + " M_reduced (numpy matrix of shape (number of unique words in the corpus , k)): matrix of k-dimensioal word embeddings\n", + " word2Ind (dict): dictionary that maps word to indices for matrix M\n", + " words (list of strings): words whose embeddings we want to visualize\n", + " \"\"\"\n", + "\n", + " # ------------------\n", + " # Write your implementation here.\n", + "\n", + "\n", + " # ------------------" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ---------------------\n", + "# Run this sanity check\n", + "# Note that this not an exhaustive check for correctness.\n", + "# The plot produced should look like the \"test solution plot\" depicted below. \n", + "# ---------------------\n", + "\n", + "print (\"-\" * 80)\n", + "print (\"Outputted Plot:\")\n", + "\n", + "M_reduced_plot_test = np.array([[1, 1], [-1, -1], [1, -1], [-1, 1], [0, 0]])\n", + "word2Ind_plot_test = {'test1': 0, 'test2': 1, 'test3': 2, 'test4': 3, 'test5': 4}\n", + "words = ['test1', 'test2', 'test3', 'test4', 'test5']\n", + "plot_embeddings(M_reduced_plot_test, word2Ind_plot_test, words)\n", + "\n", + "print (\"-\" * 80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Test Plot Solution**\n", + "
\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 1.5: Co-Occurrence Plot Analysis [written] (3 points)\n", + "\n", + "Now we will put together all the parts you have written! We will compute the co-occurrence matrix with fixed window of 4, over the Reuters \"crude\" corpus. Then we will use TruncatedSVD to compute 2-dimensional embeddings of each word. TruncatedSVD returns U\\*S, so we normalize the returned vectors, so that all the vectors will appear around the unit circle (therefore closeness is directional closeness). **Note**: The line of code below that does the normalizing uses the NumPy concept of *broadcasting*. If you don't know about broadcasting, check out\n", + "[Computation on Arrays: Broadcasting by Jake VanderPlas](https://jakevdp.github.io/PythonDataScienceHandbook/02.05-computation-on-arrays-broadcasting.html).\n", + "\n", + "Run the below cell to produce the plot. It'll probably take a few seconds to run. What clusters together in 2-dimensional embedding space? What doesn't cluster together that you might think should have? **Note:** \"bpd\" stands for \"barrels per day\" and is a commonly used abbreviation in crude oil topic articles." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# -----------------------------\n", + "# Run This Cell to Produce Your Plot\n", + "# ------------------------------\n", + "reuters_corpus = read_corpus()\n", + "M_co_occurrence, word2Ind_co_occurrence = compute_co_occurrence_matrix(reuters_corpus)\n", + "M_reduced_co_occurrence = reduce_to_k_dim(M_co_occurrence, k=2)\n", + "\n", + "# Rescale (normalize) the rows to make them each of unit-length\n", + "M_lengths = np.linalg.norm(M_reduced_co_occurrence, axis=1)\n", + "M_normalized = M_reduced_co_occurrence / M_lengths[:, np.newaxis] # broadcasting\n", + "\n", + "words = ['barrels', 'bpd', 'ecuador', 'energy', 'industry', 'kuwait', 'oil', 'output', 'petroleum', 'venezuela']\n", + "plot_embeddings(M_normalized, word2Ind_co_occurrence, words)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2: Prediction-Based Word Vectors (15 points)\n", + "\n", + "As discussed in class, more recently prediction-based word vectors have come into fashion, e.g. word2vec. Here, we shall explore the embeddings produced by word2vec. Please revisit the class notes and lecture slides for more details on the word2vec algorithm. If you're feeling adventurous, challenge yourself and try reading the [original paper](https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf).\n", + "\n", + "Then run the following cells to load the word2vec vectors into memory. **Note**: This might take several minutes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def load_word2vec():\n", + " \"\"\" Load Word2Vec Vectors\n", + " Return:\n", + " wv_from_bin: All 3 million embeddings, each lengh 300\n", + " \"\"\"\n", + " import gensim.downloader as api\n", + " wv_from_bin = api.load(\"word2vec-google-news-300\")\n", + " vocab = list(wv_from_bin.vocab.keys())\n", + " print(\"Loaded vocab size %i\" % len(vocab))\n", + " return wv_from_bin" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# -----------------------------------\n", + "# Run Cell to Load Word Vectors\n", + "# Note: This may take several minutes\n", + "# -----------------------------------\n", + "wv_from_bin = load_word2vec()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note: If you are receiving out of memory issues on your local machine, try closing other applications to free more memory on your device. You may want to try restarting your machine so that you can free up extra memory. Then immediately run the jupyter notebook and see if you can load the word vectors properly. If you still have problems with loading the embeddings onto your local machine after this, please follow the Piazza instructions, as how to run remotely on Stanford Farmshare machines.**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Reducing dimensionality of Word2Vec Word Embeddings\n", + "Let's directly compare the word2vec embeddings to those of the co-occurrence matrix. Run the following cells to:\n", + "\n", + "1. Put the 3 million word2vec vectors into a matrix M\n", + "2. Run reduce_to_k_dim (your Truncated SVD function) to reduce the vectors from 300-dimensional to 2-dimensional." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_matrix_of_vectors(wv_from_bin, required_words=['barrels', 'bpd', 'ecuador', 'energy', 'industry', 'kuwait', 'oil', 'output', 'petroleum', 'venezuela']):\n", + " \"\"\" Put the word2vec vectors into a matrix M.\n", + " Param:\n", + " wv_from_bin: KeyedVectors object; the 3 million word2vec vectors loaded from file\n", + " Return:\n", + " M: numpy matrix shape (num words, 300) containing the vectors\n", + " word2Ind: dictionary mapping each word to its row number in M\n", + " \"\"\"\n", + " import random\n", + " words = list(wv_from_bin.vocab.keys())\n", + " print(\"Shuffling words ...\")\n", + " random.shuffle(words)\n", + " words = words[:10000]\n", + " print(\"Putting %i words into word2Ind and matrix M...\" % len(words))\n", + " word2Ind = {}\n", + " M = []\n", + " curInd = 0\n", + " for w in words:\n", + " try:\n", + " M.append(wv_from_bin.word_vec(w))\n", + " word2Ind[w] = curInd\n", + " curInd += 1\n", + " except KeyError:\n", + " continue\n", + " for w in required_words:\n", + " try:\n", + " M.append(wv_from_bin.word_vec(w))\n", + " word2Ind[w] = curInd\n", + " curInd += 1\n", + " except KeyError:\n", + " continue\n", + " M = np.stack(M)\n", + " print(\"Done.\")\n", + " return M, word2Ind" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# -----------------------------------------------------------------\n", + "# Run Cell to Reduce 300-Dimensinal Word Embeddings to k Dimensions\n", + "# Note: This may take several minutes\n", + "# -----------------------------------------------------------------\n", + "M, word2Ind = get_matrix_of_vectors(wv_from_bin)\n", + "M_reduced = reduce_to_k_dim(M, k=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.1: Word2Vec Plot Analysis [written] (4 points)\n", + "\n", + "Run the cell below to plot the 2D word2vec embeddings for `['barrels', 'bpd', 'ecuador', 'energy', 'industry', 'kuwait', 'oil', 'output', 'petroleum', 'venezuela']`.\n", + "\n", + "What clusters together in 2-dimensional embedding space? What doesn't cluster together that you might think should have? How is the plot different from the one generated earlier from the co-occurrence matrix?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "words = ['barrels', 'bpd', 'ecuador', 'energy', 'industry', 'kuwait', 'oil', 'output', 'petroleum', 'venezuela']\n", + "plot_embeddings(M_reduced, word2Ind, words)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cosine Similarity\n", + "Now that we have word vectors, we need a way to quantify the similarity between individual words, according to these vectors. One such metric is cosine-similarity. We will be using this to find words that are \"close\" and \"far\" from one another.\n", + "\n", + "We can think of n-dimensional vectors as points in n-dimensional space. If we take this perspective L1 and L2 Distances help quantify the amount of space \"we must travel\" to get between these two points. Another approach is to examine the angle between two vectors. From trigonometry we know that:\n", + "\n", + "\n", + "\n", + "Instead of computing the actual angle, we can leave the similarity in terms of $similarity = cos(\\Theta)$. Formally the [Cosine Similarity](https://en.wikipedia.org/wiki/Cosine_similarity) $s$ between two vectors $p$ and $q$ is defined as:\n", + "\n", + "$$s = \\frac{p \\cdot q}{||p|| ||q||}, \\textrm{ where } s \\in [-1, 1] $$ " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.2: Polysemous Words (2 points) [code + written] \n", + "Find a [polysemous](https://en.wikipedia.org/wiki/Polysemy) word (for example, \"leaves\" or \"scoop\") such that the top-10 most similar words (according to cosine similarity) contains related words from *both* meanings. For example, \"leaves\" has both \"vanishes\" and \"stalks\" in the top 10, and \"scoop\" has both \"handed_waffle_cone\" and \"lowdown\". You will probably need to try several polysemous words before you find one. Please state the polysemous word you discover and the multiple meanings that occur in the top 10. Why do you think many of the polysemous words you tried didn't work?\n", + "\n", + "**Note**: You should use the `wv_from_bin.most_similar(word)` function to get the top 10 similar words. This function ranks all other words in the vocabulary with respect to their cosine similarity to the given word. For further assistance please check the __[GenSim documentation](https://radimrehurek.com/gensim/models/keyedvectors.html#gensim.models.keyedvectors.FastTextKeyedVectors.most_similar)__." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ------------------\n", + "# Write your polysemous word exploration code here.\n", + "\n", + "wv_from_bin.most_similar(\"\")\n", + "\n", + "# ------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.3: Synonyms & Antonyms (2 points) [code + written] \n", + "\n", + "When considering Cosine Similarity, it's often more convenient to think of Cosine Distance, which is simply 1 - Cosine Similarity.\n", + "\n", + "Find three words (w1,w2,w3) where w1 and w2 are synonyms and w1 and w3 are antonyms, but Cosine Distance(w1,w3) < Cosine Distance(w1,w2). For example, w1=\"happy\" is closer to w3=\"sad\" than to w2=\"cheerful\". \n", + "\n", + "Once you have found your example, please give a possible explanation for why this counter-intuitive result may have happened.\n", + "\n", + "You should use the the `wv_from_bin.distance(w1, w2)` function here in order to compute the cosine distance between two words. Please see the __[GenSim documentation](https://radimrehurek.com/gensim/models/keyedvectors.html#gensim.models.keyedvectors.FastTextKeyedVectors.distance)__ for further assistance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ------------------\n", + "# Write your synonym & antonym exploration code here.\n", + "\n", + "w1 = \"\"\n", + "w2 = \"\"\n", + "w3 = \"\"\n", + "w1_w2_dist = wv_from_bin.distance(w1, w2)\n", + "w1_w3_dist = wv_from_bin.distance(w1, w3)\n", + "\n", + "print(\"Synonyms {}, {} have cosine distance: {}\".format(w1, w2, w1_w2_dist))\n", + "print(\"Antonyms {}, {} have cosine distance: {}\".format(w1, w3, w1_w3_dist))\n", + "\n", + "# ------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solving Analogies with Word Vectors\n", + "Word2Vec vectors have been shown to *sometimes* exhibit the ability to solve analogies. \n", + "\n", + "As an example, for the analogy \"man : king :: woman : x\", what is x?\n", + "\n", + "In the cell below, we show you how to use word vectors to find x. The `most_similar` function finds words that are most similar to the words in the `positive` list and most dissimilar from the words in the `negative` list. The answer to the analogy will be the word ranked most similar (largest numerical value).\n", + "\n", + "**Note:** Further Documentation on the `most_similar` function can be found within the __[GenSim documentation](https://radimrehurek.com/gensim/models/keyedvectors.html#gensim.models.keyedvectors.FastTextKeyedVectors.most_similar)__." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run this cell to answer the analogy -- man : king :: woman : x\n", + "pprint.pprint(wv_from_bin.most_similar(positive=['woman', 'king'], negative=['man']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.4: Finding Analogies [code + written] (2 Points)\n", + "Find an example of analogy that holds according to these vectors (i.e. the intended word is ranked top). In your solution please state the full analogy in the form x:y :: a:b. If you believe the analogy is complicated, explain why the analogy holds in one or two sentences.\n", + "\n", + "**Note**: You may have to try many analogies to find one that works!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ------------------\n", + "# Write your analogy exploration code here.\n", + "\n", + "pprint.pprint(wv_from_bin.most_similar(positive=[], negative=[]))\n", + "\n", + "# ------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.5: Incorrect Analogy [code + written] (1 point)\n", + "Find an example of analogy that does *not* hold according to these vectors. In your solution, state the intended analogy in the form x:y :: a:b, and state the (incorrect) value of b according to the word vectors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ------------------\n", + "# Write your incorrect analogy exploration code here.\n", + "\n", + "pprint.pprint(wv_from_bin.most_similar(positive=[], negative=[]))\n", + "\n", + "# ------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.6: Guided Analysis of Bias in Word Vectors [written] (1 point)\n", + "\n", + "It's important to be cognizant of the biases (gender, race, sexual orientation etc.) implicit to our word embeddings.\n", + "\n", + "Run the cell below, to examine (a) which terms are most similar to \"woman\" and \"boss\" and most dissimilar to \"man\", and (b) which terms are most similar to \"man\" and \"boss\" and most dissimilar to \"woman\". What do you find in the top 10?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run this cell\n", + "# Here `positive` indicates the list of words to be similar to and `negative` indicates the list of words to be\n", + "# most dissimilar from.\n", + "pprint.pprint(wv_from_bin.most_similar(positive=['woman', 'boss'], negative=['man']))\n", + "print()\n", + "pprint.pprint(wv_from_bin.most_similar(positive=['man', 'boss'], negative=['woman']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.7: Independent Analysis of Bias in Word Vectors [code + written] (2 points)\n", + "\n", + "Use the `most_similar` function to find another case where some bias is exhibited by the vectors. Please briefly explain the example of bias that you discover." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ------------------\n", + "# Write your bias exploration code here.\n", + "\n", + "pprint.pprint(wv_from_bin.most_similar(positive=[], negative=[]))\n", + "print()\n", + "pprint.pprint(wv_from_bin.most_similar(positive=[,], negative=[]))\n", + "\n", + "# ------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Question 2.8: Thinking About Bias [written] (1 point)\n", + "\n", + "What might be the cause of these biases in the word vectors?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write your answer here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Submission Instructions\n", + "\n", + "1. Click the Save button at the top of the Jupyter Notebook.\n", + "2. Please make sure to have entered your SUNET ID above.\n", + "3. Select Cell -> All Output -> Clear. This will clear all the outputs from all cells (but will keep the content of ll cells). \n", + "4. Select Cell -> Run All. This will run all the cells in order, and will take several minutes.\n", + "5. Once you've rerun everything, select File -> Download as -> PDF via LaTeX\n", + "6. Look at the PDF file and make sure all your solutions are there, displayed correctly. The PDF is the only thing your graders will see!\n", + "7. Submit your PDF on Gradescope." + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/assigmenet 1/a1/imgs/inner_product.png b/assigmenet 1/a1/imgs/inner_product.png new file mode 100644 index 0000000000000000000000000000000000000000..09ad7df2b8850df46fafb4c23f13157a933f2ab0 GIT binary patch literal 16379 zcmaibcUV(R^EO=|^b$~zD!n9#G^tVrq<3in1eIO|q+?K|Dxe^Qt~8O}JCUMNMI>|x zElP(-Z{GoY-sgRP|9o6mE|PP0XLo1rxo6Iv*_XQ7YUHHXNO5p*$kp#E>*L_yJ_0^; z#6-XqBEn)f;2*A+zM3LVNk8*%;0KBOT{ABn91!jK2Nx$5!3Z=kdu(XxZK`$eww;@c zu+0NETYF(Y7k8jF4vvi9ZQ!emy|)dUpNq4r*KI#pj*Awzf$!&+AslQMO}w3CIZU;5 z*_7No?b*bHZwW&=nrRlD(vR@5F#Qa zB?W=rgxtI-1hf$H@^|&N@e^|O;=B~{j~r!tFFVi2?%t2xT-na$+St1Jc*}BdoDcM$ zzsq@gKX&+cBv-G$%K{b%IlluD5r#tkBOB-{bAI)Z&+&ndT>tL7RFy{=alA=1`iEzw8w52&uqGjQ{`t z!41Vf^Tn%`)uKFad{2XjYU!-#>264oU(RJ`C@Z5vN~Y=0#;UpKx8BMP9Ngb8q_;9E z1UlJppW2``!_|hUAHAjqolE!=9j1*ub}%{>K%bF_YAQKy36~IDN*V+ajU(?}FQpcy zrE+jD3tfyxK7Zl-`Fx(T4@1AZ%UYJd8)6^@J0Q*B`Jj}1$yCFdzjIh3W2@SE~( z`jcAQfc_ydnl(J)s-D9F*=j)joN18r{8#ZyopmLJJ?39iKtsY+QmDNG2>M#=2xDG* zsSe;9KA=6Rb_99v@O#DC0I3OeS)j=1=Bu?Ewrs2w zP~&15;9C3>=Q?h?0fRyHn0sg9g&~IgX7|tsc(%QC-Spv|S7%1>-s*&rQ;t0kgF@9A z6+&en_WY`h3%C_kI-}UEfZi)44Y}bre9Y2WTR8NP&vnI@$m3Me7owV2+4c{}4eEF9b8nwOCf4VegNTW@#5*tzZi z%cM=ijYlvTOT^L0ak0TO>0Rk-=r-MWn#}y}`n87olfGAvT^r%V7#XdW60#6P?zrMP z*i0L21w?Rpe0w^Um-qYau)(3jM(NB5gU9DK54cH^EZ_&BHGV>u(tEBd&fG z4bLYR{4`2=5?6QQ7#2N#_!YB;P^_pLkV=Bo1BCx$gY_ z*4%#eoLjWDv}Hvsf0-oqNrOD`@R8N}_0b^pzp-+YSWq9`s#eGD>L*9i}ch+x-Pc%?g%WqxS3jPph%WlB}G$p0M zncze`+C)(CRir}5=P3I~;>bBbtSGn+VNA2!@k!dbR z%GJjq@qbb1dAye^|p8h*jXeV{~NqMMbOU<8?9!lOB4^m~E*MF1K2lYPE6n{{<@H)y<|2vYqQ552UX5{Iudm@Rdc)dAIKxt7y{* zBET*itgfc4dsf$#r)K$W{BgamUpm5)ZJWe~MgI+MF8mOqQWjb&^^Vjcf|` z2oICHsSDaLF)HmmFzghX^vRGD7c#n>mM}ipN)_Er3dYRS?eR|eFGO8jM=HoOmTl5x zaGN4dQuW7YWjgHs^5_+!WBVCo(Ta3<=fP}|tB#vtiSh-F+GHh@`588)@YgGpNmhD( z278Fpl3d&gvTftUfY= z54tlw6_^Oz(#EV2#OPI`q$wpUNboOaKP@`@8vC?pnF7~iNys-i^OiM{Dndn3@SK1L zAw*P`@d$sR-&1&Vfvs(4B(~I;e<(i4E(?rF{%AbS87n!%=`09~^i}QuhnIg*;zyRC zpsn{kRC^<_tZ;I|Ud|QnHy1MkViE$k;~nI6Q4l`Z z|5@EYk#RKEKMEWCP*C&twf~kzY4izTVWLIPnogwK=Z@d%dv(-P zy}rm^26c#T9UIX1hAw$DHDkCi1Yu#$L+)ZGrYN8dAC;G zHo@t?L0EzDmlS?vB?^iqWV0sV=noY6!T?V(@w!!b8Ix?XFl21KV9a&85M>1fc3^^o z{UWxfSOb?cbb|Be;z=U0)kqDzx39fED_zEs7Li#!QTi4gg_QP13Fjev?IS2;wA;Z-2s z4Lf9kAD+JbHg_!4$sjnajL6>=OPGEEBn+&K1rfEUn6<>8;0-TH*3@MF3%eU6;({R7 zWEmWO3&%TA6qrJbf0zJrwQO^HU?Lv`#(XZ)NdI4G^(2SP& ztv>fw)JFqg4>pSUU&cB1Hrk~HjQ+i7`|t(-fA;}n9u|cQ0KjAUzkQmb_~-4_B1Iwc zPhW)oETwaiIZuAhd1~P5CPVmuu~sVs6($5EO~RPF6oa@@@P+$=ROE?dJeN8TYl$PV z@)0fsWH!43Dp#1t{wB#AS2w@BYkl9KseoXmIyG-IEu{8f$>K=65FEm&;4RcIO5bXt zfH+7*^%2AX%!$i?$z^|D0B&BZ`v%aXJm2Zi5J9wW$o-`I7hto2!IW27Q<_u*Wo|${ zTa-+7+GKd9-e2+qJS;=B?aSEQF?Z+5z(ky^(E~6&wZp5B>GcV|yDAry+ASI?IU$!7*?Ev0r6VAH(^FjOKctWCGpwzJ zd7PHd9bjBcUtR>3C4Bs9$&J!Op?-PYz?VZAf$Wm3+V_gcfONo7R5u#io`akVteFEb zINtmuQeFD`j~t-KgEj?OemRRBbHqVo(J^*6P?Ejy=DB`x0AZ{jt2z$|I#29v(qGv9 zeF7P@BNA-7XE_YG_s=4$|AVEQ5gvH}O=TN>*lF@!)V_5th>GZGXK1Ithmm^;%tNlM z2s&r+`t=2iWU-R@aj70<*2?P{v3QhxxqH!mkP@Jxo`Q-@5*DeDf}%-lGUNXWgQf@* zT`m)1FlO(KhQE_#NByh6)B*u|eJnuE0Jo+z@HzTVZqpX>oR6<70+5P<+!=Pam2ReG z!Dblo+KZj@JbSgtvL^GZCOT^hN^AR4e~NKwA$#zL`R{Xu+)bFrw64$T9R)>~?bpcb8rxhtB`^}n1`E!k>S zEz8T+UrPc&EVA;;WfpVz6=!bs4I^NJ&p?o@EPEU3FQ%6G?e7!J(|#P}>cN5~{hqvA z<+T4#{eXW-6>=W1TBZn9`)04%aN$NvN?hc{0KqW$Lm@d3px*e6anmG*=a)E!nOZ!^6H)F7}E=dv7WP% ziqRtvInAw34g5U52dQ=r>JKRz{$Y3lqUG zhjwd)nA{cCdYSbs$x8yr62%Aqw68v50G_P|_yZ*HRsbuXj_G%~cQh9X|K&*FkzruM z1W5e$42Qir`bWh8%oJ*35Vu0u@lN7Lvr!wBY4loXZggzV zQuE+FA3=+_>yP~{4bzJ+xZq`9s9HaOjaVR#NvGG+Lg7_b??h%-J?&nf!s8}tnVqbY zRk#8!mf{G7y|z%BYPJNBE4*3CJ8BwP*~9+ePheb`ILW&exBtwbEpl|O?)841xdLK? zp@t5+8~ZEojgbeX5i<%1b={X- z*^V8Ma0T_C!O8zv=Qu9Z+Eer!cGOi zVj`lb7|9uS=#^MAg)~FAnqY;^#4Srr`jdiMiDvb}i}64fxl?k5QFy!N!&AC_X8`(jNUd0wOqFkvsBub29R0{u66xRD>ICSxnAl{sxPJ z!r0$8ZqNgJ6mQxfMCHM;h_vz4Pio(tc)wcwN$k;%-`2c&K?^X?n*d#&6-9$Fnf(2Y z;$;Wy{aSrqsyo&Rb}#07dB<08oRjL%%~@*x>kBF>U<@l?OHyc`n~3)bQ-M%ESyjQ< z13Jq~mP?F2&l+<}%n$=cf#pL#52QTTJ9fW&x*9(*t&?7ihMc1mYIC)q^PRmj7_-6K zFP{sWSI3!p>oQ#Ondm;MSo|w*!zIgoZ4d2?n+`N8lN1e_4Wjre%F>Hnh%OSDp)ux* zEn>aZ?94-nH$MC1Pen=6bs$rqvu{I2mtBXLBI1oEbM-J+-RR6>-U;kA=C7?7ne(+; zs_|HtF6-2vJPgII1Z1C8@3nM&oo>x$WUj>*0zuew0X4ifiXXQ%Tua^1tBti5&A748 z?I}4z?j0nY%oV&t+z=bLA-{Pru0;Kh2AQ$L=D-*jgI|;&c=wHy zg{s!C#-9!GZ@_Ur6P7HTJ81-1A8Yzn>n#*<3Ra@fnTG=(JSRKsXG|(&cw5AbpcnPa zYq&Hm!btOM74lX6`abi3A*2;jZ2Z(o>m4M$R#IN8`b@cOG_cCCPx-dG>q@HQ+1K!| zVh2XGs&qb5G0?hK=oJoh1RiqEy5j%*nhYXBaH2gIHq*WP4T-XmbMkiCM zL3gNGcw(v*QYrI-XID&+1`*?uUl~YO&s?iNyL_~G8RtbLh!h>}XCe$Hi#V8mtba1& zDfC?&I_eV@F*{@EIPYj$<9i(%B;$nz5r z=$ea$$v;`paxAAsWq!a;Q}woRi&cE;@ynyg5xCY#}eSRdgAI={o9UWWQ^ynA(FXzQkFIG#6QU^DOzB)D^HEVn1cLZ$XrLZy4 zN*S!l7CB-c>6GmW(XuZ0`>bQhPFlKIA*#Dd;L>~_ow6De$0M~(mI%0_%Q9d zJs?mzfKug8^g6L1O&*(hzcgJ@D*OX=FX*21z$90%^x{gi1%xW)TV=h~i(N|k2A9D4 zAd9cgGrHYR$z5vCob&$v{m-Mi^r5k(Jln<=6 zjcE_`7u;>U-ajKev6%jz>>lp;?ds&9ew{!P@y=DAg=oJC*rH#Ew0AI4hm?3Zp0GqM z?WinE_cO)j@vL?D6D?Eh*Q11|Z56z| zCvN&vE>2e3sBHJ{QqSrvqZb@+BL_JzIbV)quW!X{c5I|>qR9` z<-7YnuI{!Rjy9Y=nLP0OGQ&w$&GBWYIJ*h4S4zwSDy1vaA`I4P+t~*mlW;#CP5BYc zG-z#@ygktrS1c*Vy!n%;QD?q&^kV{sYB_n-_7hJrwqAJ8$^T$~sCP!kzg0;UTNQEI zY~Wr`e2R%^Ca7y~aue+g5pWJSa0p3EO-pn8LXs(7>!c?&&Be@CZ1N=4;DzWy$dfV5 z{QmS>GTx7@l%wzOm)@i2wb8FC>}L0$Cf)bsGlOB+U?!Qfq*{c+xUQdiqt~}BO4=jBFyl1y={Ej_Y8PkzrN7Z zp5mSZk-FbaV*v|;#ld&IEIqA~sp^6^sI2$_S5kV1A~kJv^$YXbpH1w{!&7ZuYr>aS zZODcSi#v;=OYveCma^L!kGiXd!t^m4T@0(Lj$NptyqKy?gkVf`IH*m660N(RxP6b| zoR4LLrH6K9Ti&D74fdj?yo48&;688+=>}DuRUAVgxzyQtv;Fi8Sa63YAq09mr4Mtw~fo{2~_miT@Rmg-MMIU3;yM$MDWv(GmQv z=Zwv6tIk%?6&bINWG9^YYe*|$-7A#jy*|b7B~PU2@_SF5_4`Kr<+E>4hAw{VKiiH= zVDV5;lO%~JH7DUAwYXJD@a5p0g5_$abC-I1ov#0VPh5|7_I*4uNa@wAIIVV<1Wjv)ux{+4J){r?&55%?>Zf4Cg;fg3D z;%HBK*}naGZ6_zde8Ym@{YSHzUt``wtG()Q7Z;Tge#v`iA^Ywv z(pq|Px;j;t0Di^O>k&Z&CFbKNKDex?+E}?se#8wnTlnZS9tJ+|A|VhCp6YHLfE~zeIg2(fRsX#q{7;>So%wQrtWM3=I5q--wGN= z?*>|LbrU#)Y}nBc!scIomN-daM+gi)>0Oj4bXcirz5n$nqUW17$|#}o84tgzxd`UY z4IQew+Pd{e5Dbg-2NI(3keaz+=v!$6YSnHbV?LUK>FTRW){&g$#?zNDdHgd1VwnrF8T;!Cg3eTH^ z@Fun9!xv>0Z7LlkTZAQ#P`iwcqV2jGylru!j7>SPXa_pd_#AxU^cxANq6y{Fdt1XG^3C-jdf*0=HC_ zukr+@bakCk(k;mCmB+0$)`Cb{in4V03qt;91rI?fkOgirsoJGk+U4op578E(oaKk_ z-Idxf>PmD~)?a_Nu01Ub^&6XYBt$<6yK-HEu#0>n z7*Qf=(FAM_m`s3@U}u8p$M!GcxumoTR*vI_7DDnve_}v)O!eH;X7VKcDH+NdT)X36 zwFYsr)JX{~#Zrxp4l^k}vz zvR4T2fR!JeONyrS3hC{R+TNl%deFP@}k1GHeYLX+Ch2juO}R{;&q_{sv-kI{br0CD;xuX*V-}0s+1sq({h9}K8%xmi?P3m5k)o!PvBPwXIO`S*X2CB z=EBa}^@Yauu9uzYVT)B+Icrs{-D@X{r*Ti0g0zLS<)vv@G%i(lgxc?(zb2T~HK-BQyRijk(tB@tMBDdyzvORtXvl zZXY&J8Ab|PsF!=y9E!qkDakycQr>4$pIOeKVMxZp5@MmJ=CU8yQlo#|TS4nO;onmd zhwH!MrNw>t+;WHj9|O|fCy>&bWz3XUQF9Jv6Uw2n5}xF;u++dxjh4Ni0?Xfv3K$I= z__^vtlhYt3kuopG!E8!ecMr`3mnSt)NBnMq)JW4~@X$*sS`~9ssXa%96oGeV9fHF) zui(1!rM*f^ecdsRw>!_xxQ$dUuB)@+Smn^Qs$~%6C@XM}+Z1ZI=fABZeK$a!H&De- z?M>pTJM9p~UgpXViMpKYeQPa^tCouBM1uIDB?cji7*T;`y@EHmmc9u5&&9SbBpqIjGn}Iu(HY?#=`9g#;aRGJwCxe zpg-5oT#@$Sec_%3%(Wy;AQ?$*IwR>>d1ksBeRT|bGL9N386V)u;v`&V{d90>I~%cirZAh);U*_EnB4QC zzE8_>-+Y~C^_2Tp3qFE?xO*4a#g{ic{)sfCd*~CeOH!}qp^b36m1(i=?!7K7+SA$FWJYf%s>(@onoSG|MXU20JGbge5F>8F+$T?;QY-@bij9K{NO zA)+%ulYT8PCA+%6b1$nDPre1w$(yy!UTMEF`WP?g#aM0f93@DAG-V*2GEabG2D4h&gRlq@jC_>V4{;zb_)0 zdML0fffQ3SS3L*2m70~%52H7fNDDC_oZUSGn##j0)GrQm4b z8C*wBJoyd_(H&nBxs2QXjJmsecn&OqlN#F|Q-SZ&&mV^?ef+QR9=s~MH}<bl{)n_;iV~Ti78mYs>oyKLDS8(Or40W*4f!rra)mS`ZWY*tJzDsAWf-b~EeKCR8 zf=1N{`@1o4TKFfzUKfvdZb|Vstp~KTHpyv*9G0OmpTK{wd&8dAMv5x(cO6V z8-lR@l?Ic94G|kFJgw`a2Y8DUtG*3(zevXpe8ReLu05_;k@3_~7iEb2&}mcE( zYr0>c7JGu;HAxkZZDzJWqft#5!PAmWc|27n1nc)qu2-yH=~|BKLMI9`6VKhkzWtUn zE@98z5s@LT8)?yx^GuUeqa@u719oguKOOCearvjf(znF|mR8f)yL0g-ZFgj=;)Vps z{V>iM*}tA5=Q~6-fF-8kl>NyTD)3M!hR9^&?ggUD%kW1YBTdF zGVGyOpTEMaIMoOZWAhlI`}Y>(h>L|0`9e0xYI#y9JTF z__njKZg=IX;V+r0PLC&EiD$KCughMA{ybV?a(KSG^*y!X_lr;cl{x98D0(ZQUb?qb zWvL+6ZXDvR%AfwLURFa@11AO8gnaEp-@2uF-e=!$P{v}6uCNwJUQ?JUZ;*|BNa2Q% zX30a?Ia}>L>j~5OR$K6+`%s2g>ec#!@70BNit=`&@Z-J! z)v$v+y7;J4OQ(LK&V){?-2@xt8BzN|dNC@p&BAtN!QKt%afv1O@h*|{<fUJK>DSmQz*^99(m&^s!>$_9c-i6R9m# z?+el%c#SQt_>4LHp^+PuOtMps;yrm7){e7uxV$g=X8%(erM6jPy4;faxNo~cT%K%6 zW8D(PG2G_X=fux*fx2({2u(`(uJ62@&=XuWPhoV53w*85U|*~Ar0~h)6(9Lunn5B% zJckaf>@DI=){VEX9q=(xWhU!^eNs{7ylJ;Bf$g&VnpXqMjrL=SlGLv<7S; z^6nZ3l>S-YN0;^a$KSdmnVwCc8N0Xna!}3L&IUwD{Y)d&Vc!p4ZD`YD(T(%0k)q&&rGI0D$j!9& zR$~bl^WpI%GjwsZZ)u@>_3Gqq=GdOd6M;o2YOI{jnv84=LNC}KU{tR*l(M|3&Qz`7 zWp~};sgzCyj}Yze(YseS#X1UhJhOkOVP3zLO_-;mUiU4rQ#g9wkT>;Wk8h(h{1f=b z5fP{`H|_Y@8#{S>^4fFH^k#Nh=()2LWkT<~Uiu>)sqJH~Hj|4V%v;Bd+N<*v?Eg`P zNxpcehRM2O;-BX@OyI_iN5OX^W>H1l$UoijWH+`k`gU-|Z2SZ*`hL-b-|O+yFd%5S zB`v@~w|}z)?bI*VC<(_yQr}@&Ja4Q=?bmGI#`S2i-e<+lRcI0PZr``{5}l3SwD+tu z1bu{zo>!}+NZ?zDwz!(=&84AQ{8?o~_8Ma0G2+SmedQsiHLl*(KdyKWF-FTZn=Xoc zh%l2+HLR}wRqAok`4a!Jw1qfjyiVOup>05>rxq>%&y7G43&Pc!Sm{}4zc7@8 zN#6YIFj-gr+*WoK_NwD#ktz9)vgmk!Gn9H_nz95_Y?O?6VDPm?^gsifAO zuAL~++mnW-$_3=vyXu7}23+gZf56MonSEQa(pa9$qElkVN6{qT`tf#x;F%!s;}<4h zYg;!taBWIr{}|Fbf&MuC+#3MjoP}PLpD@xAQ9XwLw4GNpOX-7)uLP8W1&84A#L1+r z)Fa?=RWlb{H_SV-$6j3wACG`{G!el%&%Ki6;9(@)IJQ&j7G}GjyZ1j6wA?N3JY@ec zz%b1PePNl_V`n(V@(Fdo?Kp|ooOXNj`_N0`KgzOck9`?a6v)_$BDKuj_lI6F^ag6x zX%%!H%6%B1o8~WO8#a#iS_}^KYvn)oKh4T zsZ5enA2m)X?%6%w95)G?v})rR{9C5T%DDNZLDpWx(q&8qM$mV+0lYGibXwFlV0JYO zC7!a6^6zq*WM()8}N8M%{XQS2Xk!NCEqVDH9q_S z#9KT?7vWMc^6A-e9M&BP2FEcN(?NkO>9UH*qa(DlL6#wUcKgS6P{d*9J!0|JtiyMk z9yZsrg*=lwx8~K0A5)53O5WK7xsJQ==v-9ULDv}-bcITM<}->rx{w&c)`G8`PQhRO zUYEVtbk446XgUxY0skT&Zp$rF?caD}q@{jQZUL1cVl?A$1dFp)r*N1PTZ3Kc1c%|2 zr01KX4#X}>#~4)P&lA7g@wQv!|EZEA=~a-WDQVm?)qL*aO)g1KXlJ$Tp@p`rt;?1~L_oy-;)i@eO`AlxdZn@3 zR2ra1&2GhWKIR8Li+LYlEHvfkbqa0o?Qp(Vn$~vuaIH-kNt!<@EpJhmTMD)2l2HM` zu6oPq&ZoWjTkG2?8B%&FR-`#C7j+7-AyJrzi*FKy;s9^!3bGac^uY9$nGfk z!`_E4Z|}d84$+C%v%1u{{Z+<$#RsoJioK3e0ufIQf#%fiExk&E6VFGV2#kR3I^Owv z_U8OkAau`$NN@zKY0y60)kBbXdtV~ue(^h+dRyflx%>q?ocMwfv#%>7%u6no z&qn5L3xj$DHQaRLgf6FZ4cqozTX7?vAQ9a7NRC!XYOd(Oi!H;VU@3e5xRMPlgN&{C z*-J5bZ}b(&@#cr-u0x80IQsqe-XTSo$V#=|7XmH*V~@X=V7XZ?%MU+4XtSN_ms5(H zA`I5=*Xdb#y+f{nRiX=yT2E%@yS;gZAYUNx8i|?eU;m>bQtb@Q7nGqX0hue2v?w&0 zo?BD9MG4r5ZzRo_*7b&w!u7nDvTZmoIS8MKh}KE9V}-QoGzpb;sZt?9SwaNLzm?Q zP*}OE&NIQBo7Te2{Kwh=$!ut3LYmA?wF+Xlc?GPe#OZd2Z+q>nDfs%l09xhB9>#*wXc!IafKW+P#sf=GXnVRYr z#8%vX8IsPkby4xd_$wSYT5Rqs!dU+ z9e<_L0a9a%Nt^>#Uhg$%u6Xf>Ap_b`kqGE{A3jW3$s zXw;nWlw>J58@yuv<}_;4G0a+O4Ze@8iEf8ouE6WC0O7*uA zTq|Ptn$ti?ZI8joU+0N|;}X1i3>vJdUL4h|Gc2%`FcsSP)ew2@#MuOlqyk3IgQ@cI%e~ZapLqoIfTdd4y7I$zX+wlxo zfUymJgCh1w?!6ga+D^Iu(iGME7bk@Hkwy7J%eG*Xxi?5OQtJ|VC7TpB_Sr7LhHTg0 zO2imO1w-lBVzaotr)WR4b~f1Q?N*S%s8tI0@bgWzVkgzhmB--XvzXQ93sRx4!+*f# zLDi-+AO3jY*`69^i${m3j{FDj?KpU{DRm{qLB~nQ&e_dfsgGZC!i5O)G##;D8iY=L zGX7d9OOr^jWbO0FtN9aTC-SqRY{hBy&=nHV2p*DZJ(kFS0Z)ieybQBF30$*_71R8~ zLho4KqWJqHf`O!02eovt5$V&XaTyu(pwJXktQsB@$rWM~P_^!v`EN~v(^s?giW3|o zE~*K@fd)3Rm;~IVkFqX$4Ao^cX~f5f<61y5XS%6kBOCPNPQ{DMbrl357qkrc?iU_qqPtDcOnvhOXOwh$l^w54@HI^jPvo1i1qL5HX&jZ}VUZe0E#say`&Nz}Hc` zHXpxj38Q7QPaKD5dVCAY-jx;TGA)MH0C~p0pxjI%M-~rjM9f^PFMe?~sa$ zKs1Ocb~65J!tgL4Cbe7YjGF96=<0;Y=KV#8><$pXkUgSFyOe{3wn%(mpg4BC zy1r@mQRBbt0+3*oE)aeB4y**2g(iiEIh{sKL(E6jVV!qfSPE-N!77w#qFmuBkF|?1Rri{dby|BshUx*e`Q` z*~YXBKuzN}KEd>t+ALirdmozrG7TX<*r}shd}sxfNhQqSm<`x_0Nxzo1F!LL{y%Xd zzW4x@{0@0X5ztWULZj(^=q7Wv3*b#B>K7pb*+zU-p_m%An;ceLz7#?qPvr_;e!YXQ zk;&O)X{uh(H>czO&*2Uz5s=?z)D%RU-zOO{W#yb?AGSSt_(6x^2~7sy@c%LuAQW)y z{I9Le?y2tay3sr78mtg!AsMX#@^kk4Z$ejXzi@8j;g%_QWkf4UwP|J7A#a%2h$eA0 zIro1#EeJ?G0HfQUI3Mg%3DcdP5AXDW5-d&)20P{!7ZVg-RlneBC~#Epmp1k%-4Px- z7pW6*21GRL!at|TXo8M<@nqCF%z-+VM%g?AsjZ8;WA5wtfM?7ms76vR2*B?buo@le zD?rdV7?U#K+@Q|j8a7Tg ziS|f;fhrU>WFD3^Z8=Q2q#T1(+>rWFmN8*r;~Kg+I>6fxDqf3?xX~KZI98e(2rqd` z7$ZIe_v^@Q7~Hx@9fN0Vc0`u@ei|JPdS@71J z#(8}@W6C6%DU7;R8*elt?pFtqzhupirY*9A8TLROAhkJd4txPBrCJsniK!79YSjjpA)uzN-i@VhK6?|}G-*CDT;dEfF=kIGAYbQ-t<9|o~w$D7_k{SqYO7FOK1|m?d z{c+7{+HNf*wlLz@_5elYoOF3+@*Y2tpmV8yX&f!%m=(VTr(b4k%ETQx)$)4=uyoa~ zz6^gip%LMk`UB3eWYSF9{SUS$&luygwnU=xt`qMGhsQp7f*suSxU5B=kHgimDN|lg zLy~@oc*v`aBO6tt5FnQV*(j@N6Q5qMsS5MF{iZtcc#pWM6K~B=HZ{+(n7N&I;N+V7 z<=M-WNHRZyV`H%lJppm8I_o+sn6i_(DfNiNjL^PjlNAZtv&c>d?TEHPzs6JT57oP+ z@Lo2xMcu<{?t#|~LYwiz5W0XN-c7?;7$@{#Nez@FV~SeP&4}x#l^Urq!F<|BJy7bykmr-tzCeylmn<7!WlaKIid--WP>WMhw>ZFz1k{M3f zH`kXN3FHarVq<-Ymh;#A+?>W4hz{FBuLw1LpkV*=UNUrMXLu5F@I54VRcA2?*i0)>{w ztH>4zt2__$7{J*hfN6vpF>f%KK8xX%ztN-u^KQ=DWC*LDy2ly`zi>LaD)V2E}80&5OedR770~xDX|~ z_cEFak-kOzqec6>0&4v>4l?O$bCPeYDx*P|d@ zQNAepLCcxwID*xyUz+ro5uRxb5kT-G*kS#s^U7vfTQ;Nzudp` z%0VEsG8;)rRXIsXh^niTrH#D>2=pQ%A@Q-Mh7Dm)%h?YSFs{UVTpzp~JTV`PtYbz- zN;tNBIDBO9`!Y&25&;hLBPwZp(gZ3qajCBuZ!t<9aiGxe8Y7@HUIo(Z?PV+oUk%sQ zwZM*=t`4sLG{bH|vA+CNYG{f`ke44RF;6|rWMrf{U7#Sab0G3KoFOcg)fMB(@#FA~ zKStcYv2~-iGWy)g1MY5G@6tMFm z-y`6NhzwrqI;L*a>Nbm3&~i#@ z_kD*XAJ$4pVWG{~Yh07y+JK#oJB49B8x_nj=+7CeWSm9GSXTL!_3O;J-z^C#RLKot zgt+TcUsc#XJnPMvc%FYDr~9;q%QzaTG5Lfujs7=ks+nWG?=k5SAGtq_Sq>AHZWirz zt0#^u=oZ~&)Ar-GJwEsL$L()>hMs)i&;ggA)=1%=x`=EXSQ-P0kwDLOAEp11-F&Y5 z*XKF{u%Ky$%Xfv_M-?voM`zz(JUL0uq*NEcf-ZJw=n;j=O+|9d=LkzPz>kjdy%cu0 zlPQ1LamEtaN5}Cs!;kzBbFUr=1=9VJ6;XVy4`MQo26McWd3|~;h%rAV#czrr${xJ^ zHZ8@AR==0qptq8Z(+Yvj583K&^{k203f**h)2?q3G|hzo(jJ=f3I%cXgTx31rjGGI zv7Vr;sJ?`7PAj{uZRisSqOREE@4Xk3$jGhWh1RD0g(yC#V;Q9Yfv%Nd1tdP;6G`e3 z!7cQ6L49mwTEQpF(roA+AvepaUi<@L+HG~t3r2D)cVAf z_KOblqvDvVu1n+3U*x7IrX8k(r;+VRs!0R`$$D0tzw?=;QL}=2+ANm;xLmpPxDdH` zx&+s%c;Om8a)O_=%dUl-E1jEOvtJ`!8(d@ie-NYD!renf0FzUOeF;{Tv!*l`6Qv5!^$!gFWHUBz^J>;ok7gguAvQX9cWmbPxew(HxG7R34^nQNi(M95s+#9) zm>mN*-D019hu@9a&Co5`O(e}Nt=;9-Rr>bittWvfBa5o5+FNZ#Ra;e`tYhiq!o9+@ zl17C(-HT*2y{;EDFK85g#(pPA7)3fp!bGn{Mh$ ze6vW{WZ7imN8?xRU*?~FtMTY9yrPXU7%liJR7Wz=ii1XJ7Eo|AQ9kU$cjdUZFhR#+Mz(zee?JDpq zp0F6lnib!Pd_e4ww++{8SkU#-(z#ZmpXM!#6O$IR53`3flxPRNu z_dho@UFn_iV8w^k$iiil@*Ax_S`}`>pe)d4?ewXSHC1P}hddPTVlszWGs&xoQ~S#- zVlHMc%TD%xQD23eCtNoD;{8K(Rl^ETI%l0}THjV*$ZgLZM1Ta6eGy#N&GKI^YBdV} zA`Ta~5Qhd222X)KLq{aELP^n+QQXlNa4w$@KooH&$yrIr@eE0B+1sf<(SL%vs;$rV zrm8Wj1r;P(zjE_(dwG1$F>K+Rgwd_jv)&{0s;ouG_u_HWNVSDVYsAtZ*fB}qOTXO-%D+Fu6C-y z8?rQi@$}JcJOAie4_x0_FN_b557m5aYrmtL-N>dp{XQ=vF@2JSgYakMrPfUOY=f=h z9$lGy*pHLG^%r7@(TT;82{pnufNp@hIJvM=LZcGYQHDY)_ z67{-MO@hWjhYn{}zx@0avqga<>ful|Umvi0hG^IpB~j77+Fm|aJLh<9eyh=^qA`b8 z?YaMD#>JY>Bj0(m!(gbv_U-yx*GOa!=*&W8SY5vHa99)5nIW{AQKT`ZR^?lHd1p(G zMa$4_`c+FJZK4Q0jLD8K%g1;Z9`o4y@gcb@zh;2WzVpfoR21F+faQ$Usc58_UL;8P zgZH|Hg_p&q+T`dgV?t4Tk<&!`PIJm~O0?eG?5tT)kH>hM`-l4XYwSITX}|bJ)t%Ih z7F(_9g;15{m76bVPLsm&ua&DZEz`?HKl^ToQI~$rLbd&FLu{vPe_$73yDhToTirB~ zUG-ag%&2(SCWA?^+}XbeX*Hw~U4sY)jjoB9m4W_-VpNvf2e|V8`EwJU6-D9^|HfJR28|?o1B5!aoPk*w8IH2OT^lN`!@pkq8exjg$4xiF}~V|6^Bc&zZ{F7b$khH5@r|nYie9vk!6Tk6d8IgbYb41F)$WnHEz{4{4!Jf z+VR|1GE-^k!awF#A1Yxe^lTZk@s|1~raagi$^eZF;|jG6V+kFO@`{@GuG*Vewj15; z8@+DnyV-9g3FfuBb-Os4)LEZi*8e?M=sCB@)KY$He3I=a*=pBI=B?29Ue8qqp6i*Mb_5?w`5BRJ< z6R&9Y4{Z%t)uj-oCILlL*~}IpRUz=_n}*m^nGHnwUG8TCjRKI0MKQ1QPNR04^OY+)N-|4)%^P0WV>y z2MGb-`u;YQ3i2T0W+zOgqofLvbaJ(TaI=gcITmn0==~EYJ1ZOXU%G**LicwCRBgO0 z>~*AU94s7Rz#1Z4e7r&r^8eS9f3Nr-Gj;zxlbx66KWF~OlYeFkLGKUn9|!ttt%tjS zxkS)}p#L(x2wJ}X{y7Ll3Xzi%*YrZzPe+X<=(_0%fPBwhhO0S#m5S{^mFV z7_RBpKP3}9JQ;XhZ|k`d6eq6l)P=^IaT=LIkh+l5jzkBhAd;i>lbx`C?5tO z1Qb5Ra84pbFa)?1Yvn`?udu>wBSjH=kX{|&zP}Az|9S^n-Ak5ad;C{PARi3luLxL( zI*`wI1A71t=mvx#L$4nFqk0$^WblJw52``HGE;v={$DNw%fL+LhvgY~t%UIST^vDA>|u6T?z=; z9sMk;`cjn@I3!#Qb-eIJj+kAz^~zGfT~n3g!mP;kFUdK-vjyte@6J35%{GgTbw^4a zp%@)AC?%mFu&Gj(VTpdG>{`q9ABDL_?+U-u8LP=c4Z1h{SfS#X-^}}P1&>E0mD(Hq zt~^qOeXHFM=4bS(Yzf>Z;<11?kfjCKlHZt=*D-DXYY+Kkm7O*Tr{z1cfa~A{kF~RQDHe);zUz+L1AcYkT(u+;?i{iRVg029eMSYgaUN@x>FVa8v_e-x|abjPqx`RHkmZVbeJH`8$^(1Y?|UWkxNFDLf7- zMRGWEQ6#)PO*fGH;6g!1=;UY4Xjgs+m~adPL%W{t2`>DHh-sGqxnM@-f5CegnMy+3 z;$jO9V^es5|NF!xvrs*0zHSw)|QvB+_sFEXwE$9 zI5O{rz!sgz4c4|^kx`5PK@@ZQvv0-NE{PHKiGB`7IN0@!m4p&HRaA?ILT}weRI&c% zDad2QY+u)*YzOm@hm@j%Vokhm(1f9?O|x5mx6Ism@tE*6;0;1FSUb8!isBM1h~W>YKV#d`AXRS=NOT1l#-#-CShb78LhV$!vV85XG>0W>24oRb|#ow z+WCv=!OvUw=W0Bex(eG9n6w344n{wRMM9^251XR8Y#195UCIW6SsoH@O2;iQyJqQn zgSXH|hkJah;B&OA(Rt01e}hfoh=|{%m!K8or)61b_et-Jts_$;E=5S<5R~$3k@Ln41j2GgqzCywTeo<0y@kYMI`g9c%FYHgYd7=k9^O7PT58!Ztbn*ynfY_HqiRZz`y5A3IVE`I&J)|2Y^p{(WBXAan-MuJW4ZWob} zeZ5WnMv9&K`!m2rlztXoif`s8shxO{*5GXN`3SE&Os3cRJ2stY0r_?~;1;$!SL0Ol z@{(?fsJ9>5vKQL_Dm;!>)#<{ZX@D(fuCA#Ps$Ha1^9>Cw2k~M@MsmkP~2?E zo?agfH=M2sxnh5)zDx$)2gYC_w1G6=CO{g@U zqIS9nc|E<+j2k2_FQ(DszKHEs<8@jTa(|oXB)pjhoBz02Tvq=bwek%dpBdrCDc!jp zwG0<&yHKAp`S{{w%E;4+>%-M~svr>bo|b6{2q^gP0~bFOkVwK!c8Wf7*?iFd#%&Xn z@u|wACL;r9>v_WR9pgs@r$YSR+~r1dy+*IE+kFCK9HqOHr4?2g-rdskOn2jj8V;kM zr28~3Qe^_CW^LHcQ4mnhWyLz2xG;XI1YBzHzL7V&y;NgvYkN$whuCp>c6j(i1+DT@ zFu|*9pAGy+p~5MmJWpamm3{%jz(6bJHG}Fybd7GO4Xa zFN^IL8@=;H6xF>@tfRL8o6jHD>zR&J+Gtoc8oEP`n&*LhZ_jBt;CzLjRcSxNaVG!L zZ=`V*b8`4?thRQ?&P1`&ospig#I2gDPF_x<&!@yM`FG~uNzVMjVe{pIPpgbZoAlUi z22;4%ZQZ$2iu^Z>fQ(e_wyiFBu^#7eyfx|p)T+5rS&IFPynL0+1FD*npXJ#?JCA=7 zc>ZdhIyc9#(eT;MO`6AZYw^GFDDt|wJaJsbk@k-J2EY!ho`_fExCJ(KqDXmL{Q}9A zZlny2;6i)1lm!P*WbHQCE*f+zEX&^aY0nFZKi)mcd8*(A zyz{+!;-bj{N*%PVu>s?pJ)qLgxKJg9fw?r|f8=NSr;OI0E&2{)@~_9f>#5X$Ghhk* z3M8q}b)d!cl3cDB7J=FM?4n+7=Vw-jpc4tqeGt(Ku$;&tsRiJ`RGwn;YQ|m>xmANR zA^jBylmk53UAK2yEzQVXP(46D$%W& zPx=FYO#X2;^HzLwYg+;R2L+IVw!_Z%*x@}C0rN_0-hg!k;9B@+^L4OvtT>ecxuX+& zI<-l=DH5l~(J0@1ECIQEk2}U*g^Je}g1o1137F;;&FaNzEofDtM~~*%VG&1 z$C6aTRzirhIP~BqH47f8BmshMTdKUUw9BkGwfO{&3N$$q4$#xODqJ>YV zb0O=g4EeYhD2~jhQ)FU4sD>1UdwMzBsQfb{K3;W@k!U!2@#a12f!*NtI zKOW4U%<41ns|DpzJr79|Q_ng9muBS{-Rx+WMM`E)Ok^VwAM++ZA4wT8Zwo^5IBYtb z?R`%j7j{m9jo?nbO)FQ}*f`H}Q|SGJfXKP~nbk+c?iUHDkf8+cF9artzWY_$xs!!h zarhXB+lZy9rN`Ry+wE!grG~z{g?gMZ3;};+SiI9(suaZfuXZTMs-7m8bN8YvzOXY(UpilqdE8O-A_Yo6l#d?_bFHWZ7(F%A#r zXxFrPymernoEdOi7x{G3Lla5wrfz393Z{NV;EUb;hA(W?Zu$v49Jj!zrCA^IPxI9Q zQ*Fpw_L1jADOL@(jqpVN@yg6Td~Wz8li5kG=;hYuKHBa-qIYeK!$&!0_X80b| zsq)~tVCK%T5hG&vhKg+tr7zfP_MK#`Js+9*MH$lq#U zR$sZTYXZp^KNW;%V81%9O%K>bH7E@L<8OjMh-7HIChK>2Vrg~wKtC`I%rjft8R5lu z!E8de0U${%Is#aLNN=5m0GOt^UPuJM*=Gpgdh@M9K81T6p4E$tfFczMXkrPFDdi4_ zjSI>90;)<-AtSn{z)=_L14V72(5Krqnt%uJRWiE0Z+ zTEw@bk4OB|#}!CqY2+)TxDn9W6D9#VUM-FTGv2%b_J=DgJ+9cv|0$X!M^|sFxv^2G zoQjy&(H^L--s$~v*uSDci2JFst7bq*Qy5t-s7Ku6&~HPT8DC!(YlXuEutmZ$06jiL zq2Ca~Ve|`xC8Ke)N@&%H@?m1VD3B3Km=1VAI*oc+D&@WpQvty-GuXMW#Xv5%aHP!p z24wF82(d{)h_pl+Fda*K4n2w(%gn5HMcx-7trma3-RZBNOZBQ4 z18FtPmB557dgl5yj-?Fx-D?;mJe3Cv4OT+k$|zD1z)5JFE~gSdL>^T;%%$W;$Q{Vp(fw6 zY62!L9iaA?+KlC4&bNo4?{9sM!Nlk+cqU-h{jPvJ;#3U6223%wHZySU>(V_2{MHWW zVCDE%v%|>00cB?{k<~!H1@;z=0-r0$)KCK&D%BdP6Ro1t!+^Vn zZB&6|LS`M~U}TJMKl_vDJbwfJ7#KJO{($JWUp19N&SopWKU-D758Iel;$1KYT%nXf zu*XcM72RbrKBmd%L_WJ^C6r{oYqlleZd^pZJVrgbO zUMU#&9s?nPHv_L>!8*ZmFa^m8oBK^DXqA*$+k_riTn1R2oXt2eb!_EZKWoLtVCq~7 zx1Igo^j98>@A$qa6WSfqT=tl;fkdOT2-Pv4?w5>D148BY`l8yT19^8OJDA&gq_y5{ z+jPfja%2=pfARA&Pku#$REGR79@zm=8;%ya;T2)Z zZO`vb6M{YRt$|q7?>j?2zPjSp@ItN2XTTqs-Je1X;t0mxto2Sil>mYuFz^h>bcY6S;1Bc+HZKrM^FqqL50b6E0P%Ci z+Aj9DcO?NJi6eP_(}UzPP!eW*iA*35y;o-xU|QaoF~bMRUl{-`)D+2v{m|B~%79#= zQ+sTn50YLgAeYlc>8C&z1y14u;I*E?waR`7jF;lv#77r)e3DsZ?AXXwY=J^ zP)VTvJ#&4~aJUE1Tu~~)cq}YXXpP`u8?yl^F$u%>awD zJ5_EZKA0-vS6^j6^DT>`-Tw~&j%`27#$ft#erW+Ax-;Sy1|CKF?>we>Z49(>P@kse zmpb{%WB>?Dcd|sX9R6m}zgJw|j8<}e9F)p!7s{9asP8unU}GH0n@+#~E+Ly~x!53? z`(h$#xGA%r18_n5K&ALJ$vhiJ10RWiCGJy^{6hd#g12CprMqmT5AO5{An&4!tZaxL zp1w&0TvTt$gzsS_l>m%h4*y_%a99);z_lyOI-LJHIwLSD%E&eN8xndzNLU+$t^6Im z1V);|3JnkRC8QE8!Ru#bEoXMG75))Vr}=f=0`R{W6Y3*-uDtWkl2tuxZh_C*SJxe8 z(eCNCH{j<3!y;EDH$R~PUx5y68sRaiaYqYphl%2vwI!Fi zY6mSXEz5?(W?xd5gpxr$MVY&~Vy)6|kFbPBkTFTE0lJ}v#jtT&nQ!IIhnb&dT@-%D z?{79m@4nW%dmg{DjR=*ABA!u`B}?G&{P{hh8c0WD0wAF9RZ+(X{kR2KrFtY`y&A`K zy++xoIz9@3Zk()fG6D!p*dnYVpE0KOF+h^Jt)}tA_Qo|-s(?P0wuiHiTpLkuU?omP zIwmwCsWo^mDXePH_p6u=q@-9^Z+Gc#t4jeoe+>`Kjk6+=M3Nrx2K-U4X?A_xj z15X1Q1eM|P;maiypPzL_?mi8rFAHDH;PI`!v_XUU6jShv~Ulf+i<&2(*c>yfNH>54=jlxmdtKe&Klm6-%jqRLdao z7-+6A1J)4j#aFw#+@}Uod4~afp;-vPB%T?j@!ON0zkEGeMU`&DD`+mu) zo6K`wtuGBAJ!dX9Gc$LR%G_e3h=JV6O*Q*o{3aMMcx^gth~J}+CaE?(;A(D3@rE8K zIFNa8_q%6nmjVP&JjRrGXXNP8*Qp|8qe-`eI@%ZNjZ0g0ii%$$MNCcQHT%-|8SPxT z0A=2>VW%AMw5hVX4Ppg1n(0Iq{ks1{wZ=s!inXDh zidS*~><k4|p2B5g>LXmd;*hh0 z`pTt?;2^91mJ45gbZ#ex_9sxyhJw;VwXKaC5!)??Gxq%j+_sjxa~V-2)*y1&1rdRZ z)$|ETNmc_zy9#j}v2Rt3w??uP7$@+SlC3gqTCRUW7woj7#%Nxuh}{eUj2Jf(+y>E06tqynES5hm-gA3LhFu_Ud@x_Y14UY z=_FPIMqE6T`{!+VeCx52jJPVjf-iHjB_gJ(1^2CbhQPQ8CI^?wy=q<87e}5$tr=-3 zY<`=pj`m!Y5}bkztEzF5^sca$n^Tw-z4Q7#d$C*IvN-sT)9=#FZlWlGVJ8j@-$9`- zP*ow^Asp36Hhsz^r1BLVz;87-=RKlj2b!mkUt}3HcsOb?3g6fW$PNBxZkc!OXW_A% zWS+_;Fg|%te+lUHSb~!YdQ<}3h|DLYTzl|pBl;y!j6Sg#)Q>kcYaM4Jwv9t`pR-T$ zZ60s$y6welD9=bokOl?T*`QAdf071q;eBi#;9{B}_uWItz{MGfsx}-f)A`zt#8ViV zZPA~Y=q0G-psEp}cv3%GsF5!K-=M8<=A|48;miR)r1?OxQd|h6M9W3}>EH5^Aj`R5B78h~AhhGKCa>H-L{3aCA0 zFVpaWe(4?-)29LL6Q88~J@5rE*f>yO>i&y`uPGMh`?h2sr!l$&NyS7|2aB_?9JZqyWW*^&F7iVT!bhoNMWE7qTaZ114BkyT;;|Mc>EhT;1G~+ zflvI$s-J_Gn+_^cqBq|460d<@P;8~O9B$mer-uE%DWgKQ3ob~4szDqf@{f3pZLC-f zpV%0gu((>;f~#-b;CJN(Z>>xm4ar@tEUg`QT?Ht^m*w~x0-sj=r zVP$*3`rrXGxPsZi&Dzn>mD$>X^4~&!mLqQBU~CVyb%fejlf%k2G_rAW6riAhJ?PiJ zf9q-D3jO^gYlk1r0u5w^eZzX6g^l%B+2B@w*r&WoP*)R6h&a^B#M%KoLy(JyjsN@g z|J^sgpZLd}TEFk)xX=0Loqv4u<4%57SOflO(7(O){Zr6gf~fqgzq(!!HD}Gv5sU^g zR9sOFyu!hT1`HhRmG0kH*n93SBej}r@ERi{E~4gob93r6CT^tSdar~0AT{+F9qLCW ziqPC()iinrLZnApc&brqw~EY0Dr65-uYxn9W#eRCD91bw?`B3Kj^21-C*1sjoZ&e| zFnq@6hpkg7Jtt|my$QrptQBnVcXIOAwx?G3b51O5&I*q<=b&X=B%QuD;E>6m{rtf$ zmWmM~@$k@<;m=Q>+{8xXh5PdpcAaPK>0kX5Z`?${6#n@`nrRjOkFv=7EpK9nJ;kwy z`{No+^3v2hzn@3`Ot=hDnkioX1Iq7mFomDB`w9N46YT09eDYE=&U>`Ko&~!a8}008 zy}y_Ezip*j?@g8%K4;c%oaD3`f3h`ERaI#c*V6#!aT`T&ixu z--7fd3naL-YIt&n1Uwh=x$^YbEbX$KYYiAJ(&5_ZPkVkq7y3t^nI%7q4(&PYx&K$akZ)>@baiaDN-hQLNIxT-U5L8=xS1@Fe(XduqZBSK^P+VplZS8n38y z-7_NMw0tw=&?ms@eerEMsRS=Y-e_*NFP`JmcHK@RKc4yiVmGJfH`^6u+?mD=^EXb87+77ae?c$}K&(THZH=czq-Vp5XV@z>D;P1YRzBZ4)KSSrgeB`sjm zy~cSBrX_LtbDy)doa3{N;)yOYpTz6uiFTd;a6$(&FX~3Fb#@>9Tgtag*1&PphV!6T(RaYL^&&V*8|x=5D->hp4?RiVpo+X1apq zkAg$<#_#GCe?C?)9;3!0f@B8XN{Ub8(Z<-$v)6f7#@^rNf{s5w?E7+Ydf-qgVAPJB z@W3QA$z|FNi9gxq=gROM#iyfA)^74{@Y?UDJzh?*85g?RizJqUjs_dm>0itf@U)m& z6=XLGh6GS*m>Yqqd3&PTE`$E1&sx^&LgTIo=Yt-u1h~^z{D*`oUdL~lwcfYmGeHal z+z(4E-qm#W@@^Eu>ph*Qv~m+)xICGuBGC67z-M`Ec1F_2vqFp=2A}!u){k{Aiv{YE zKTjtg;C?tDW!0O=Q5h&5WZanCWPgB_W2!q zxC|=EzNY<;j1F)PgEF*BV=5{ck94}*=R3k2u+yJBIa(j#eDVPkI}A0h?}sr^iWR2c zQ(Ngxa;WEY*?#N+x_JxqYBzydR|JWL%Zyi*uIFBdL=8zdtC-Oo9bO#dq6y)i39_4Z zbl|6K#gt@%GrEOQvyKwOHhZ?>Oc612ZGyCpG2Zbin-qc@(8lRzU-*L)Sp_|>qme}P zRXbUh*k@+4BU!KY;+uRH!nrJ`>)qTMQ+f*qP{YMv5OMY1x}*xyQ-2lp_rW3G`-JZ$>d5@tEG*F}HHn=w98vf%OoHbu}n=v&s_F zy|+&5Pw1nomgt}PJY?SengYq}dLdoMim^H~~e!lNwj{F1vdg zNjyvaZfhd3S&(lQ)UJOxPxZj>p3|zr+J(ANnju%XGciM{l3%(qIyK$(*;vz~YZR86 z#ppy63)xube4}s)0fESsAN!+l7z!?_qB?R`VBHP-dl^6tuPs=3ei zjSGDUb^C{DnH8cVvzh2a`1aay4iw}G;f8|)utt1mJHzOS>kNuP6_Zd&J6jKC)c&9{s zM!|sDzt_~eFBg`MGuYTZzg}SFLHIx?#qV1U{i@Gd2yLP2__UKOVG=X}{p!`#LE$6S zm%R<^3`B3x*-2zrWW~$thQ=!_4l2vzKW6%iW{}DbvJa-xE6LuDU8Fzl$7S5&uP_@( z=O>Xh;hMA+Q~2VaeL$bquSR!jfh(?6l`SrCc{p^+Plb=PrFZeu3=`f{a^+XYQnRC_ z#E<)c<*hNBN8*qBhJkz}mUwHb!0J&XRcWp>;~)?Fewvt${n~ncG%hvcc(?RQ0>7L6 zR2{WCM&ZGXT^EVlyM#vT4{1El=nbVOn6yH#PN)g!6XZQ2z8;q^ZJd>}xm)M23|(K& zjMAG_s}2SSM&}DBrQjZ}Vv9s~M4#?V=$`m9Ix?DoUhJiuw>#)!ke%>8 zm%LT{S<|%zi7MQFn_AP<+Cn6hcevFtqrMS(Db~kspCK-KG@9#=ja0-pkn4wUXYoX| zP7um2W}(iR1yv(N8KetZK7uYvS!F4sxmk#ds*c3IS@k4*f4-rA9Y0q6D3SaAxkaoP z*M|g7EA45o^;bI9rD!bG9hS;K5PH<&U5EQkDB8P{ zSN@VP2K%{4cb2wsy0|b}S=*E^l|Oam=6 z4tX_@yf)p7DkMOXA=!OBkAF+%YciqaM3Ff|?Cq`V^SYT!7NpNr)9w~}N`*%^k?M@z zrD|(#6x{JD7Awxsjlq39ym~dQLqNHyUdx-_$mFxF@t`Y&wpy zJ^>wYe0}Kl^h__Ts=`U@`p~OlLY1L6Bo6yN4_%%THn|TqgDh`;6uG?BphH|PeM^7s zi!ps40U^ng(*EF}XdG&1ZN?0&JJi;^LA_ZX#=zVp{xmnCm|$N}K`;2GMi9(lr5h5! z@`8~?gDL;Z@%hZPcZ3jL*gD+1`RZ&vIvb2@jhaY_hLC_!jf=}2pFTcpDAs#?vpac(G9g|wHgc{f&=39nOXVLRaaKnQHwYEY-Ow~vG=oYnl z_T{+6)27R!Et!dzYumU7gIzO&T{%$y`ErN83O)L_tFhRAQ_v^bvUkmMe4q%TGQQEb zoSuxRy0{JVhBDCiA3SmDz|&lGw?H%a$ISdKX%6Ca?Ou{3Is1B=xTpme`^ck^(CCN% zJiue%Ku7w4&0;B)ApBY1REP-HDBfYCbQJrXe$wApbLiPt=Fh%+Jc+xoMp4T`Xn~~I zgG_(PosAuKGVQ?{X=_%eR)mLHojtGCuYG41IO{w}H8W?URrVS5Z!{`bNV>F*qByxL z*^T>OWk$Xp>>7-Pl*`?s%`)&2UQhKH3T}UI@@XG8+dPM9L?`4~v>!uhzo-jFm3l}( zG@AU|=&Z*a9gnoiC8^5KH8>vFxz}n2UQw!bKjy&ByvB{!&Kf{v&L2CAkcpvLx<+>Q zy8X+A96@M|g2ZAgdOa-7wbMJiOF7`{X`@v8UZ6ieM6poJoN}i zdm}wFotOP{Hq?Z0ZXh^lw8hLlMiAJLzOmpS?{{LD?=k8WsWIfj_{G5LEEu6i3`nRDSslWt{`C!m9%re5z9bTZT^vU zJ5#jdKx1Zw|Z!nTU{1a*hGuxtfs1^~rRNAt_CIID>wT7yL_r(GkS;9omx{#=L!R!>d zjn90lf|5r{7<&bMqy}B7w1wQ@{4MM-{Hw^;wI9Cq>|Jc`z=!>=o+Rf^jY#w1tv#ivt@4Y;nR1!$Akcplc1Ex=6`WtB zUQv|aYp0;yURY#Sp{^+)RlsE{n>vU=e>~;OH^oRX^|e<&R+m|JaG^ph25beo3Hu8h zdE9*>KOKp5WMaxawSulB<}cGl(GW8iT6v3n>*ee-Ge*@Z+O*odq}LESyMF0FNt$Wz z^R05{)3%F>N}dstETr4SZ=~4DSBgmswp2HGLd^}rAFXnKG1_dq^qeU>CHOfdxV;f? z6R?tOuShAs%dIYZdt_;5q%0pWh_TIHB;#VK0hg9>EQnIO7te(!%cVDzcFt$J4;`LJcB|HwxUkbI#Hi57CnUVnh7WxND z5_jSdi6VUcU;aFDEQ_$wKs&yQN?N4b!SdIPNI`Rp(ZW(R*82Wl{)5l_^Si)n1kD}V z$IT_Ou5OWxB(8je#tSBO6}6Mp*H0M@p6e z#K?@$pjI6qC~zR%2EeMB$MI(AgmoP=EkXRpa)TB)kHsjdedP8yO+DyJl8c@KZ~1ik zaV|C*HJ+^G+0lmSn4X7K^0C@^^|Cifo^OIh%favRBa(rmR(4M{t~Jx+7mehXDuH-C~E8VZi;#RSoFi-A${rlm_<-xG0{{v`r3p4_j&vhcuxk`L3#bkb-o!LxPXy*Ks%S=V2 z&=vR2VP;e)44g_#RGN9{AG;+gbkw$Z z%$r7cV4UGa-C9}WSem$W2Ax>Y?FtY;n_y}DHb0)T$MHuvm6-y}4RJ{;v;yT@)CA)! z8be9t(mE5Z>yR4ztezx3F4hmDy~ZBa*=^Q*`Kc+16KnsY?F+Za@HxURaV2f1R zNV#qb%nd;cZ35*!=d#*si8#pc_E{#qyn8rcqds|GpU>z7-6q)tTt*GFE7v{Ze^3`0 zCiVa*Fh)p|9;ZV-&N0tUUGdHnyRvh)U9(~I>;^5E-xysST_3l-sJS+`eH>$ zo@;c7vvmAkeljJfpQ=yt^I!4G{=2@?cBW#0&NLkCs&_lsC#f7O)QWlTJ(VMh;^*ua z4@k!3R<$~vSV?O3v;I6)+98r!rxWWbJDs8Ae6gXl!-nmEw1d@^oFwP?DaRps5Nh3B zH+j`YRvCn^1SQbhzd8sgQ%r=LHdRdcka`lrg0!Yox)NWuD4+b;B`=2A?D8lOG zvjgIvM9dA}PFS>+=oKUvnjeKY^;H|iPx7Bw+*RwZV1@U}w5$$fIK15kA_WE6+xhLH z6FsQhAuQihMRlFX3_sx7;@li8Kb$wXlaa1 zwMdHJ?G=SphYDXICv>=3;pk_O31C_}@qce|%>R4K-yZurVSYqk|E`z6=Hjol{4cOs zM8r4$kEKUu17pJ$FmMMTTAY3XP#%qD$r13)`Qz+wSk5#~jTYr`v~3XX}hQjh;31 zZ-mY_%ZfCM^_b7%Wgi3Y#1e?kN}M)Em)cF5pWPfSGwr+J{Ryr75YaY)uBCPCv4rBtxv)=VSrC zNb*N-*jjK8s}|G7AZ1U(>3#bxpyl%y2YprEmuE|_wQ3#n84u)yNiMYmt$h^&18xEtLHopv5H=aHvx^E;N z)|jm)hSC;IEw`U59!V;&8E^5f6aWUsCWSZ%lTIa5WISEo-G3E%eAkJ2O3dTLiRd>G z;_m@Jm#Wm3S2XjMbG{T7aFlF^5!gA_h7^T3E=j!R0toDtd@hg1CUTWCj*CtEbq}T%P@dpO^WMel5@Bn(-XM9qvoy8DjTK8wfRQ3tYm2r^e7eNOi)}_AC@@GxJS-C%PsNTt=d} zX|we|xCh||I3ziyTZ!JaNBn#37hFC-5P51-b)~SeQzgmD9&HOceR)xM2VNFGTOr9D zCKgFm%~3eV3ENxfS~&p1p8j*+b3#%$JUb&|&qLlm`}>etr|xq>-LFCF*!&w)i2+dAnvsJ4MiIvU0$gCm(`gjpX9U5j%jViv?9Z!lgsv4&sCe6-V7`>YSP#G-frFzdSZNh>Z)mndMaL? z4h*r9TZ(Sk#uDO z0y}4bLdla_CTxd2Kb28AekVU|A221$TzI-#gLL9s%3E^pxS}=n+^FwFZLGL23=nkz zP9Q$0nFIG-fl}>9^**dT-;F$>?{Tl&j4!uwqTb(>6YG2R08Y;51GB_Mr2yigEcSA5 zl7*tsWx12A&w0*cm!t~$C7<&ZAuE&GX2)%}m9Ne0Rx*^I&nAkL5x2H8(Ijg7GxHvu z)xBF&bKj|6jF#KSrE8#Fy%g;Zk}Qa$V$v?BQw&o1iorGcaz^T~Zlm7)#cf^JL62~3 zf}&5ZK&WUxXs353z)q6ytnA_qcMTI}Vc^23DXP+bqUVC;TQh*jiO;zY{h*^+#7O>t zd|Y0RI}mq|0Y*xC{gW}wiqfEEijz;#wGQG@%C&XA!e@niM7H#hWf{#G2)qoD022_M z%vwK3d@*vZb%oQz7YigU5_SQ+FB9n8OO&$EYm4{Q6W{Mv;lN#+!6NdAqBpk z&KLj0uu85qEP!g;1M$hqY9eobm>R>7M@Q53rOo;NC)NYHkIm)N^L}V79|*GJEt_#* z^})yd(danJaL&&_3ry;{+j1dkKVen1bXpGNL|JQ5Ql#BtGhk5^NCT+`5sA80HkUu( zlX_ld=P9{A93*O0*VK(8%jtOLPC1k=60UYJizM`6B`Enc<&O^R@-ACX!4K3As~L3%cX?#2UT?($ao0_@s*uI?FwAN)s+&5(7-#vdrz z@M>4>^^4tE;wt#g0@bdjU)s=Atnf_2#{CBqDI9~S(w|(Z^T1|9QwjSv zVc#fvLJisfESj~yL=`Ws$doVq!R?cl^e$PyQq=>oN=6Dqx%tfvzs$#@9FZ3gV- zX=4M!d8Wy3G)<9a%t4aUhu-N9_OMA`1lx`P?UHs{Nodla;1^iQL2x{(W7BgUUvQIS zxX4~W(!oeku`cLwoFoN1g|Y|TeVFR#AR#>PW853M(|)3;X*QS&_)Wtk=mSw8VY#MqvgqvHmX4L-``cdr~cUZGdV9ljJI6$%l2q-c|!)lMF z(@qdBG4yNfO-SGN2QY-Qm`y;a;k934`pxNu)AfZLBP#xTM6NGBPJjB=VU>Vlfcxpvbv0PI)7t}K+9@-PAN&$ET0CRH zqA1#yV_cr|Nd#yr{*w*!=xOeE4aej4s!~?)7U7*|gfwGB3P;Kq2^Kv;$BJWY=P+Ux=gs)c^M85lXyL67664R> zu8vsc_$=OVrszfs>dBJH4QYouLy7ic2D6FbRhpZ)!lliUo@cW`FLf)LwTDv5`BdW| zW*-Z#vNb~ju;IcVBt}UJl;0BLY8wimENXEDO74AAYGgt$uw)lS2;Rp+%r>;CY1|u~ z$%oB?^uj7Y3IR7*=OeIGlrIQC5?@P^eOf8&3X>M~tKZi}9ExKt8d36V{VtcAMOLFE zfEr_*Z`m-Lz@Liz&>WR7r}rp&8<7l7z30n`3dY@A@sL{;jmT!C|1y?FB_iU(;hrj< zS{r4~5hoKl*D9L|FOpU9(TLe{Chs(gSl&>1JP^X;c zWrgXx`nBPx@a1D!ofvRoqW&mZ>*k2JAK>L5--Gr>eXVM;dd0ce@5NHy%!ffsyo=cglbuAE zg1jA?O7AUaz_6ZMlIB0H`$|KxZwoVpRKDh!q9>)fT0qW3qBV}@JYy3-s+_4la2_2} z9;RRGkoT2mh0R`Bg>SzOI0*%4fH*EiDHm2ecx++wlFQ^H?#V5;@vV$nk>us>QPS~< zO4z%l+m6;RPb9~Wl@6Z9Rj=~@NkGoNgsY&$tBsWCaV~?_3CVF`jh`Hr7pRWkww(d$ zrX0Gd6$-1dt`8me>`w2&ArsqtSDS32W>E=~fi#IvE?-8i$;Jm{LVWSZp|rVBs1z3S zbiov}EZUxk$a zs|bg5h*-a@60++@G2d~B!Z%(SK&8f?IeV3L+5Q&WN&1O*@C~@Gupj1vHxPtZFvkUz zbZ#(U0q53ux9RH(A`Z^zdo0Cyos>!wI1Q4`!sqa=Ayk~yKuTK& z$qv1`JK95DSyS14c|Cg*w!Q9=!wfkHazy|Yq084%FAB#6O0OdSjN1EyAEGF&9LF~v zn*jLLtAjWSmVs$NCAyku8eft5q9`996st1&_LPz$7ylqpA-INh zC1zyjufz}VQiQ(m*2GcJ_#l~+oTevJkJb@^iDp>+(Kq%64jZn5BL{c>e?^iZ5{H7q zMpz92d1L_FZfE9+b9)I`GdGXdEYs_G8hTLB{`ITK0A@u`g zKBwd;Q5E)iCal- zC0P<~*B)lAh+~i)qD!4IR_lUVVumjJR^RcIr6Nure*U<2Eyt z;$3@w>MV5GIHbV|B&hdxeXdV@<`NxxL)>ywyxqBHpTTRm&G=A}-$aoXhQogO41ucu z4jjj6(hx4b4Od7!!?&%hhf@E30lOv@7xpD;*bDU$wU)Q@Z>fLZr-bYqf1wZ{(V1(W zKl^C!b2KRHN3nGN!t&lZtrW&;9O|~&u1{rtJPLe`E1F8vq-q#-KR=##mmUwFQS%+T4WOH0FsJolA3u&B$j>Y>>Ij8|15p4n5EB`G@jU2hyG3M)IjU_@ z#AY>K9-&_Ex;MWI^1V6_3IU8W1j3s=SHXndst$Y|f332oPW*lA(}j6{nwlCjuYd1| zcS*r!345qb{hlNQ1o&NdTaelSL*EL2haXeomU_^eFCh85Z9s~Q#CaT=Qvy=?B2WVM z@{f-vZ0^7$N7Xxx=dCmeme@~w6aTrRYntSg{^kvrjUM-Q^+M}yzD>4i`r41hd46>a zB}jklx99<2D_)63MbluwXV%7rWeD2#aE$o}pb3lw!~UrzXQ*T+_ZIBMPZXVSIrrnVdL8wr_mo?zYtW$z7vBxs$-ZU%h_ zjCVk8oIQDLo~qHvLV^CJIsc-Z{+Y_-tyM?Q;vKNPH$2#N_@hu&Mc{xq$cm*|Hyn-b zf@~$B?E-F)n7M%326!0~i`8!06!dzZuO_cMFmnO*XJv_KRL`IWc_G#5`G?z~OqE)) z`hFC~gzXmDcrn%kRnXkwkm8*nQg<_mO~cWAjy_1#Z9ZQ~bWoVRtq3D77S59t!b{BV z1k5X_SkBXAhGNWt^m1>0{hWis?S^;;S0|GruRoqKhXuTz#6i5Ujdyh{o(n`X$dk^; z4k_o4xi5CxG{4sOoP$}QQQrd->zvxQV@Wv{svko#dnPuE4UXUqTRL|dPNw-l9D&77 zjGMyvL0&d$GCGnFcH8{z3{>o=$4#$!*~9eu+pita2AOr zvpl$(kL@HTE#;AM(&Uq{8BxG72tKaIUrpOiS5nB@V(nY!#x065^g%N4LSEx;0Ba!y zzx~R}J&!eO6(XB@JI`Fw^q?In2ws{JNO_i>)hG{!OAa5P2XoRvIkTjjxU}_}inP5e zsSuL^Fn3S67hj6;CUu_t;R{gpw;EyrQqbcb`3DXlx+ipDH zuI5QVUF%x55-maYqL5D!6li=0DW#YMnSXO#LRWT=p~7mLK&fd6*8lAU>sl!P(|Y|t z(eVGAi6WSg1%HWRrE{k`Ee#q>y!!Vxei#!MyF#MRX+88E2P8oBMKAas1R0IfzE}bB zLh~J%Y4s1{S^t;@o-lV<*J^qj&utu`0qE~aK=7hz>!w@%^%vvFqT`!x!9h!(UQ6A( z4|1uiwOc|ZKIVw_^6k_&Uq!|!@X{08Hs3T%u&NBX2Wjv;iD#&Oy?_4CAFXbI`R|D; zhG3lmV_{uOZ|$#F>j-r`JORZ+AszL>T=WP+fGKuW6o z)FZkx!5BRtz9{^SW}1hw|LwylPR*u^{ZGnw$r&(-s5DqyM-kGWttgggi2n zjfR~1i{0a|Ub56Koh2-USe6e+?s+E0EYQXPq}FY_TlS|pl>2TD13HE^+J=)FfQ8_d zP1gF2lnmbg08tjXe?in=$oq$({)^=QSVn&dir<#%FNpfT1VsHM|Niv={uf$8bHw7u zH4o}fmlNhH#`MsN>-S~f044kvgXb8n-~go2 z&tuDVP*dNFgCC@o%z+dcbdZ022ZlaBuXsf)@bv&;*XKl@vJ=)hTIwIP?aO9dOuCMg z)*x-*n^l8nw)tGG_(-N~P?DWWzY+5BeskYg@-Sz5FTzE91~tV7H@cNVRW1t^jo4Kj zQQ@*R0IltCZKw_A{5ZeecBl8Oqp)eLA65{j0Sk=&vJP9h2$kFzPZ&$qdE(yaJPbnX8PJW zk^!bI0Y0uPv&KDuuA53mG;}&X*?JMSz_55g7w|ES1SR{{QH)lmcD!9YP4J$3#{4pG zbiTs$SHnO}CP2f)0vxgXhAsa03;qG+c)9j@OydARUBw16lp3XSA4%xaF+ETN)qr+y zf|MT=fD}w1;1GIb2$e<3vj6VeaH)7)qtM^{mYQN7cJQ*?wDVV-! zGY;7VsYnrXD%@vO`${$Rl940u8dOT%#GkxFV-BTcn;UzqV zkrSG-X0gbqFNo)wkG#Q|0|DSTJg$BvO#VW9+Y9%NqSq{Q1;2XoE`zVi1^or%vC1UK zeVGGi@97#7tj?Lw5xRsLP=G_TPQtIo0K0+7IqkZTX1}pKj~a%ar4k^4Dk9pE zZf&=%RRkkJCdZzcxdPop#+k_h2g-93`mGO?k?0`LC;XaH!TTU#rX3F@LhLcQuu>Zk z8@gUx!=R(fq@C2`fZ6&&4a^f4z)f^CVc|l>Tu_}T_!&{cR26tk9zm)@_>7-s*8D}#nk>&MunUWWtK>CYBY{5uSTNbviHWFY;Rkgutv zGM2~L*2d=pB_Jj`x2G|$NPTKZiU=vS8E#Tq@-y|gp9i_?fNY6imC# zxMw^r!Uibe;UkGbcxe7z%2FhFLTwz#TC!e83@5!DAig4r3BN~c$ zk=qW2cIqJRRsCwoT{d68%e2tAgpC5_#ZkMYQ%FQTihmWUktW1~Sa_|s?%H*W`K^O2j zb5R`MIE)TtVXFS5LU}8m`fWd*Uq3DzJE;$3G#E`BjuHN~o;&o-S2mub%bhzG9H#{A zRrDVDSb82D1qcoaaUme3k>47QVl$+UuQb~C24yUYhzK{{qdNkJ{SdMeKg#*%n2f{x zRWa1Wq4jsXcb`YuF!q)EwU_#fT<%5syco;$)TyMxK8miUT1al)!w&F>hqoX7>of zZZtQ%jQ_-j9b=`gCppZ6Xj+H8K!k_`4h|m~Su_?>L{1DH({;hv@!IiN@IXe>c!-HR ziT!E;uyYgDq5dLmhj)CwiAfoXTnYjOjWV0PU~69WC3ERBe|q3rz_;8j=H2urcZ93e$X-c3`YD6n={0YzP-e~O! zKPG45-hq)q#T`>&!CO*}n9)OWgtaARoCZPq*Szd<0%^+1HYi~W5on*{8!+Su@7a2k z?K^<8w6I6Lc3XBso>rJ5wx(-wR2RG1e0uuT(isOe@TCC%D)z`SqE4_elx!Gy^$Oh5m8gp%`x!)tm@{3P2}ls#T# ze6dvbQb8fgA6$#DK3F`8Ss@OsA9T2E7F;fOuCHtk%XWKqX9k~m>Q_ISlgUi4QK3PH49CMya{^txNE@^sYlE z2lCpz>!@XGsn3}QbceHg*gfyDnr<-ysTasiQ?qo>e=9KFov5| zH+(gHA@v(R9f?$WI!Y_WB~fjg32__8z1{cVCBE`tL<5-b{LNS?47`LO{iW8yVP zv}Jhrhv`rS%17e_vjGOK^f^O~c$ql7+-b+Ik*?0b|zS4Ss zJB#Jw#V?cehV7=m#M~9GNh?{~PW@_n*!&Z=zMIC_Z+3lXeB8Vdxuy*%xp11Ya-uNt zxvMw(3=X)`xN^C+dE3!>P$YuJJxboLnbMF*1ZBuc0PDlR^sUu$-1;N2Seo}yLFpyF z4X3DqL>a%cl(88ByNP$rf{!2Zmq84|t13x?1UaScaY2yqIN{0(6{n74RXryb}P5lCV5biP5~qM zV%@raM_N$!<%2A;D_Z4l{uSRHpU!_w+p8wC+%85#^+JSuOl%hg?`j$J!VZSmxRW)p$CM%BxK}?Xu)!My)Z?7p$UUpK z{gM#O^oWw?86&^fDE6c;gPINIopD7!1Kzw4p(Mz+wXFNKQoKS4-t)_Z%Ny;B@RU~R zEbR{eV5Z$^pgI9hNcP&pFq#m@Nfz_ zK=d)2R@hGGBHNYLeraI1z0(*Ois60rG^Gp%rcMUDmID_b0@1STv{L?LTdw0*K7X*Q z>jk)_ze9Xgf@C%w#+JzbdMJuMQk4=Tnwa~lsj-AVP znWv(dn9s*gW8?H#xriMkhOzlHG7`j;C<-zBG_w*2lIsB*>4=m9#ysS%gs#p;Jc5NV>Yoqn^~7Y>T++9~-E47leTBFpQDV!LeyGGr+VA`r2fO_Mr}`Xg z=O{W*uVsPX&zosDM6^P1#b_YCf3kOt+0JgWG!3Z3Sv!s&{-VKBN#Q8x6)~oEHjTsY zs6ogXs-;*d?uFbWo$j=%(4b|adnoFcV|kE>Lb}^IlgKD=!#Yyn7}x_yKek z?}d_CvI=R+-R?3DLagJ;p09L!hvG63Hl!*9TsCDhehc!yo@F3U!$xs6+<)0CIQ}6P zHSAc4PJ?n#Yy#O~p0oiOofIxwG}%5)iC@#V{q&1Pi-3>YZTJ;R>Zn#X*Qb)j_muXY z^c1IQ+EO;g2SMvALK1=|u?FMnk}St#+xH54ZR@m-;|}k_z52NG2VQae)jSEsypq+0 z6XMmp(0l_88tH{8NoS{>rX#42+)t^KFURZV^^2^L2BCn((0x8yEx?xCGWe@(`rl5l z1=E>(o=?o&mV3>D0Sgs=0WsGF zyAT@@tw}L(s;-c9n(49>EA8kVr)ny=weztcN&4z~kQ;V7^<16&9m~1-m{Gb9`eG?|?sBx*xnTvsLjd|!BF<5rZ6B^BGb2HfB~Co?`iFi`yMyy^Pf z_KRVVljcjfNEOHvh9wZPLx;okJEC@be!ssHkUUs7CTd%Udt^U0x%r_5~6ru$6(B5 zkX@R60Zl*9_e8MA!+$HQ4agrzwXLD+UI#%NIEtnfkW_Iz=DS*jsU>QXyuf?Tcu#Us zMRy%)zy2;_ahN2>F}5B%q_^09{WEO?V~%+GK|vz#0n|rRZ%cgt`gP|T09WWB$?>~F z;|(B`-UxU&Uxh^qziTx@V4(Nvi03@Xd?4RZkXW0+kh9I5m9ZH2V|3PZd8o0I^p}R; zDxTNvo6|;L>npgiTz*i896q0a)@vxXdb>OkRKYidBum$+OHKhaU)V(osyJmQE7i>y zwR*vj)9Xod0G{;V@YaPEk*G`a5>?JN@}o_HS0~Ak_A3_D4ZxZ3&g}9~k`_D2>HXF(66Q(bejppyK|7e@+d?TGn3y-UJJ#406>KJcw|qrvwRJjE@pH ziwbtw;*gZ_8xKIHCTxRpp-})ZFXxUK|H(2_>;-?jy0!NZ{3_W_Lq6v4424|tGZGxOb-I5+~ViWtmw#QaQ z*CrO9%w*LUqcAgC&!=je`TB_^@UI3+<>nosLx~Aw*v{fQoh)W@Y*Y>4EPILZA@TDl z$eT+2Sd!c21mkP_OP?}y`)Le>>-><8pOZwHsmi@#3h6rd=gnXx;Hw%+0LRmL71nh6 zNCx`8-ass+=MJZMjg0MF(AA4ehx*F>c(XcU&a!TMSu1oRHyFVyZ>ZSqeRr%R?=VL6 z`Or!U{ToBm%u#2Q3CD|EhgKwm#~Jmpc-xu7v;#szrgL{J0a0M*xp1jmmNR-4(?pMR zEL>9wxC1k!SPLimz4XgHuKHD%)g1eU)7-B^>mP-JPw?=cr(Rqai->t=)|3sw2vh62 zt;*HA+^5xr>jSmq|g}45@mRX>i(&;$Z0+lRP z_)rJdZX1j;g`Efe1*L*_e0Tz^$$voFG{M^i1PscfFx9i|su^z&?w$Grieh=ruT-}` z)CVQ?pQ=nRMDVd}mbBg8qs%bUyqu4{MYFjVNMs$nbrY(v}$I*HU7ZIx>5~Y0Y>%Qy@FFbn>t&7&&nU#zzy|wPV5LC=pxCJk2;( zuT@dg!eSI(;T(gUt&4l#l`G&J@Q@g}7vVCw3kXJ(rqgx~x@&%#X#d-2mM$;o%Ah z@da^=EfYYCqQeAVhx!_tDTnI2wIEE9;blweI&fS$<9)t*ZJ_u?g|JVmCt0J*0CR_t z>6XapwLUz&$qf@F5lH_YAmGFd!5@&ATOhl3byTVP`ZVEPtcwNwoabk@jOKq*%`DMp z*lE6k*!eDqp8M2YY~E z4f?Ez?C*DlBZkz!1-mv{?YUST!bFse(rvp9hjU^J)f z)T&d!^hHIGIE!sLLzKe^;I%-ILRE@ud^zr}F+$eFR0u>75&R}MoR5W`;uI(4_X7h191RE zIlus#&|q|O50I>y8nh)bZX_@6f#74H-G0JIWLwbqLWT2t)Te0fZ~^)GPI`9hIVMGl zJjE?|xFu6+Qo-Q}M0x3q&my>tFexz8VKsw{Z?nf$oS5LnOi6(pCR&5!vga$>6lwGH$l=!_h zQnX&!wu79+w5Uh6^uxxS zY+|w6=$l)ZjOtj7X+m@fj6ujiQ-z-QtGg*(rk(wox-@>_ITdZgIa5aR7v`pG-ZMAY z5uQc_R2(UrKMG2@8`EGo)mDY--1{1N^cA=Mf0EC@B<4Yx-jcso%}88e>$*g>9&gPf zCV1E`v|ELJYY9QrAh+qKiMX3&+?`v>99(fpIORHWDgwv4&QcQj`*I|=^cjx_jlJlq zh;2+>?xoX0R(Jv9bGIIrimIdxLGe&W1zpN;de?+QpU!ZQhP)%-1F#GgF&0XceZb2p z#p^8h#>cW2btMC@cgP2tzuYh$l)pj1avs_hOj+2@ud?+?`U|KP3O{vhtN zsot`GAuzA-(4|)IcyF;AyY_h8K^D3ZpNnx!v9NYKT$0vH4EIuRj~N_UnS-{)lYf`9 zhs&^Dnmij6s~4|>2ZYK2wbi93086UQNzOs!ZapFJxtt$rvYP~bkYMBxx+>6TjAiHs zD813)H^=*fk!OW5SO%mz9puSDlH4;~EH4sP|CTE3yk3IPfex;L^({W`v|q2P}|S5 zvI}`Sj-r%^+X{bwLR6x;4{X9!V9wKq*^i!9kD1ZUj^pPE+3$qM{X0viOIKlAQLZp! z;J_aM-BQ=oMqINN5Rosyg#g|2pa(C%aK8wgF=!ztU%n&bsfDwrcssfl%o$YLMbPuA z{Ytb66GNV|`;yrB(V)Sz*{KC_;UWs=l^$%CGyG+~8xemYi*L%`_zFL!R%Gc`LFjo( zXH9PSk6Gr6j=uhn9fAh|YM)#9cA6k}sO2ro4Mv(?dis0T`(RqY4if3hFV-!mI$jLBo^vLjRzYp|E&I>BDiG+2HJ<19ceXqw_7nrv$6BhfF2 z7S}}*qI+7;a* zT7K90nMf;qI;@(I)a3o92PmE^9R5rUMyLgGa_tv^7UY>0=lpfu>;wG47zsBue|i>S zt8&}^t{rvwW84dsMb&;-YQ$Ykt0!QK!g30)c&x{!@h32w6)lsfNAK?K*Q%&WnxtG~ zMnks+4j#k=$Xjv(2xU3cE;Vo(2zp$ofx4Pzk~`>`FwsNYjGpU&Z{LP4&Xyib0}5P- znkk7UO?5>Jh@Td!dCM(bJjm@v{)7sPM@F7$IH$OSjLUCV>T#jnR~2Km(8I{{|DsiCcScok@z1H7XDa5bF9OxsD3-Bx|H-iO$9Bx+SMw~H=3R3SMQ*>I)jZS34 zH>(UA2znKhNUv_ZN_dibzqzJwlebzz-e06p#?>Uj7TpEM{xx~c496bM(H`?ziC#PV z79xHVP$sxCgKtTeK41{>&?p(Kzio4(lQO1=+?XwH&dMnbN+6VNRzWLjt(hg~HDt+S zP9L1&7(R@oN9;c2V?~zsV;R)+olP*8Vpv4S>+Snw0TA__07K~Q6_x%IZXTT>oB9Z5 zTA^vkq{_D%+c7>xq^DQM&r2wn$RdR6Nr#BB*+;33#%_^L&V`<#MCGR)*5YtXph}>8 z-Tj1Cjmps~I%$>UD@5Nso^Cn6(1_>naS0N{_|1b(#Rw(eC&-m9berAbm6Adrl#n_|O8LE>#X~G=?rP=Am*9atzG1Ly}I?-(D782y#L8} z$8tVL6O|WLf8LwNDJT7e<6>Zc|1Jk7b_O)^4ESwLWrAxXZNhC*N-Y+A(4cqFfQV9R zVxKG-`6IY_ui}V#xwo^);e<-QKkTtCcSc0)ic8>|8+Z0rZ6Y;mcR! zjGt`LTI65Ug>S9f5jXXR#P?Pc_C!;YbYA8^zl=7R?Pq2Bz!qxFa@rMSv#4{-`1(r1 zEO0R(_~0DHn^FT9#vo+Jp)-1zF5HopnICiNW**zh69-Hcdv$Jf#pi$<4uHh|eZi3l zX=v+idvxgc$fF<5>7vesB2+E>UgJ z62iFgo*VJ%24;wy^I2AdcnI2%o)J;dvKl+jpdM#}D|f;%N+Aex zSkFnXEMo>W7_irUpR*gKf8Fz@NXm~`4?M;i{_ab@^CGZU#KJo$R0a~; z14Iv>NPC>Dod+|ODvK9-Sj%nYldHJ}@T9SQsf5hC%1w*LaIH^eU0Iq|KMn`8v_o%B z8X3RcSE%dGX-~WZf1Hh~)3;V4!0FtHL%3M5$pEEpKBBM8~s4VfP&tvqF~S zFW`^9Q7lW$Uo zh)p2}W*j@!E38|tPc#~5b<-sbdTnh{vKPXUx9_mO&6`opl7{bs`^S6 zOtcP*JoFa6ZzRsBxN1AqSn}x=+;e{v^X~IdSJQLdm_XkKgTk$eo8nUs^f# zF%RB+=?WZ;ptv&gBfp}pXeLZ-E-U#4!QN8Pbs_Ns?(?W>_%Agw@y2J=w)9QzP>em? z#~!JsBnxaezrQvvp@wW4y3Z)!f(n{KwCpxEpSD-DXiEpa0;*TW&9U(Il}K_!Y3D?s zpZe6%hT9cS7m=0R=p>p%>6~AaMUz_JAZ)pL6b{M$7?|9rs!N8k@tG*{&u*WPQIPiY zI=T>nxpQ(44hqYq8x=KRpe3)74YwNC0&qo}uo1Y%8%uP;&D;`^j(B&ZVET8q+auzq z{?Yr`Lb@1oxeM%FE6)c1oy82kq1TluS$oJp1+y#UTc}__db4n*V2d9$vTig z`Av;!f3j2_;*!6R;kU^-efdtIfR1n4d1k|HzUS9nLTcc<32Ho5{hxTM{4%;to;y}S z*!(a3Z$K%PfO{s(4TD0i7cq++-&J?6R^^^L#hO6v1%UAl2t)}8$1xQ?k}Vfnr3nd( zWMpgiGbb#Dw=<(?OMB>c3PM(b@^El9|LqPDqm`*5Qsun0XT3Jd`It+!g(xBYRnA)Q4%PqIiZ$s*_Bm&Dw{r= zJA}Zy>|Ay5B#(LNjVKiOC$U(}bF1~gf{v1-w8=*EBqf?GiWvSjJ-=Y{6g1-xAW+$~ zn^tax0)4BVA|~ssnfC9u1P@0r^BQK{IdGtWv6@Ft-&ayB|4DEfN9Y!{+zT;mPyjiCaEUOd*|-5bI?RH-$7`lT*@K$Se7OIX<@CJE z?&vu9{{1Ci+uAjWa?O8Me)?95ZbCrRha~?tGSgCkx@Om3& zfW-1@*+YpSVb@zOKp`TSb;sinF+f~ika68z_0K>Z(rK+{OnH-2Yxy)t_+Pjdm! zD_FVVQralT8I+KXn^{=eD$H6>)V(Qo#C$}{@}Nw+|A~XCp|d?o1e5$KhP0$3q)(Bi z@R8ZJmYgS9O3TmO_-pmi@Bmm0>pc>bJ!(E>;!mI@_KE3rSRXBQu4^0wfU9zT*c>m} zEG#jKC6nX0a5F zm$TfmELTb#S|u)dwUj(ldi)g#mAUvV{D&3Mms3*LbqpF`y%OdF(*8%3GIr6cQBVp5 ztFc_Eh`AMi&2e$XSk2g_nQQlj-nWZdyLQZ$a{2m$Zlm_|=)M4O z3ZMsWO>W}z`{*80dN<$(-V34~%M%m#i-VQ8&jIu32n00|?i&+ z2hfWSED;Pp=0@AWNtqqY>?p-D@d58SZp>qI-h^RwWtuO$&D9G+b~6mTtpdGPs=Bc zW-j$5EoS@5S?g?v&n3G=D}9yTWnkh=h+_LYc$DFqM+d%VzOsHUpnN*8aU4>t(hxfi z^8A}o@u(h`fd9muE?C9}1MmGf*TZIpEIxkfAcWL?HI9K!jSkKx%ynvWJ~$x5~qY*Thre)QBJzxeP?g;ir8o%k1U2hGgL(cF`V!6$L^8a~|10 zCAfU9$Pd!+b^`!$uAX(RMM0#*N{4R2sTht$$dRI}@PO(o)AlqzIWW&SZ;&ZQ=%zOD z8L5S|X$e5+02G9Y{wyH1=#kD$nJ$rWTr(Rk$s&}^*g87u@c*)Cg!<+US)gD}!wJLe z@PPR6Ep=T6wKyFmbe^%SVUEgeGNffUBl4U)BZ_9D+%^y&c&hRXTXwWcoc4AEHP~ZI z{PlzK(H4huX37&!*QYtC}gEUX7|f|Q1>Ft|9Hu^i;HcQw^(0RPgry| zf77Nyyw##@;!$l*^oyZk2`QYoX7-pt<<4m4=>87B=EE_i*Sy(3hNIh(2X~AtpBi4* zD~39CV7@3K$4?!~D5i-5j+dy)*Hq8ZH?t-p_3Y8qr4|G_w9$f54R2g94|z2RuI3*h zvc8iaE_u6QMhNfg-yUZ@V!c(lPKaYUVhzf0gnGDAc@H5Mfc~scGcVT}`LYKh=V7(F zUu#Zp8!;Rf$t$iixaYsPt1!v_D!XZ`h@x(6QlwHyrM8~*-l^C$3r0g5g$ME}SNN2W z!zUQ$Ixeko4l;4YC8ao%aGCNK1*RK&CO<>!<2e@lK8u5;T(Wt{6*O`&Qr4@;gqd5WOPi366tjU(LBba~5wafu6 z8-%B$a`WXsl^(!jY*_$uRFd0M>7NGzgD`7gI(p>K440ZtJ_k2>}&(Hs_;)}X0(U#0I^0GDN$)f)rL<3C( j{(m|DyKi#Ady%B~qr