Skip to content

Instantly share code, notes, and snippets.

@viraj-lakshitha
Created January 21, 2021 09:17
Show Gist options
  • Save viraj-lakshitha/a403b862f788b807dea7e4072de964a2 to your computer and use it in GitHub Desktop.
Save viraj-lakshitha/a403b862f788b807dea7e4072de964a2 to your computer and use it in GitHub Desktop.
decision-tree-algorithm.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "decision-tree-algorithm.ipynb",
"provenance": [],
"authorship_tag": "ABX9TyM03jZeatM2qfX/S/t5Xgs7",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/viraj-lakshitha/a403b862f788b807dea7e4072de964a2/decision-tree-algorithm.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "SiKvv3hOpPhP"
},
"source": [
"# Decision Tree Algorithm\r\n",
"From Scratch using Python"
]
},
{
"cell_type": "code",
"metadata": {
"id": "bcaPnL6d-Rfl"
},
"source": [
"# For Python 2 / 3 compatability\r\n",
"from __future__ import print_function"
],
"execution_count": 21,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "6H6u3G-6pIWB"
},
"source": [
"# First two rows are features and last column\r\n",
"training_data = [\r\n",
" ['Green', 3, 'Apple'],\r\n",
" ['Yellow', 3, 'Apple'],\r\n",
" ['Red', 1, 'Grape'],\r\n",
" ['Blue', 2, 'Blueberry'],\r\n",
" ['Red', 1, 'Grape'],\r\n",
" ['Yellow', 3, 'Lemon'],\r\n",
"]\r\n",
"\r\n",
"# Column Labels (Used to print the tree)\r\n",
"headers = [\"colors\",'diameter','label']"
],
"execution_count": 22,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "AeQ5ku_H4WKg",
"outputId": "00293419-0205-4eda-c4d4-aabe4942daf5"
},
"source": [
"#Define a function to find a unique value for the column in the dataset\r\n",
"def unique_vals(rows, col):\r\n",
" return set([row[col] for row in rows])\r\n",
"\r\n",
"#Test the function\r\n",
"print(unique_vals(training_data, 0)) # Unique value in the first column"
],
"execution_count": 23,
"outputs": [
{
"output_type": "stream",
"text": [
"{'Green', 'Yellow', 'Red', 'Blue'}\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "8u1_w8RM4i1K",
"outputId": "62a50c0a-1957-401c-c4fa-b8ac6b28f6b1"
},
"source": [
"#Define a function to count the number of each type of data in a dataset\r\n",
"def class_counts(rows):\r\n",
" counts = {} # a dictionary of label -> count.\r\n",
" for row in rows:\r\n",
" # in our dataset format, the label is always the last column\r\n",
" label = row[-1]\r\n",
" if label not in counts:\r\n",
" counts[label] = 0\r\n",
" counts[label] += 1\r\n",
" return counts\r\n",
"\r\n",
"\r\n",
"#Test the function\r\n",
"class_counts(training_data)"
],
"execution_count": 24,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"{'Apple': 2, 'Blueberry': 1, 'Grape': 2, 'Lemon': 1}"
]
},
"metadata": {
"tags": []
},
"execution_count": 24
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Re8KOtsh6TEP",
"outputId": "4f51d7f5-b7e2-4311-ea96-5efbc5485e8d"
},
"source": [
"#Define a function to check the value is interger or numeric\r\n",
"def is_numeric(value):\r\n",
" return isinstance(value, int) or isinstance(value, float)\r\n",
"\r\n",
"#Test the function\r\n",
"is_numeric(10)"
],
"execution_count": 25,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"True"
]
},
"metadata": {
"tags": []
},
"execution_count": 25
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "sm3f1uC-9dTW"
},
"source": [
"**Question class is used to partition a dataset.**\r\n",
"This class just records a 'column number' (e.g., 0 for Color) and a\r\n",
"'column value' (e.g., Green).The 'match' method is used to compare\r\n",
"the feature value in an example to the feature value stored in the\r\n",
"question. See the demo below"
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Il0ovtvd9HeG",
"outputId": "5fd29d96-3e4e-46c0-a2e6-ce17c49800dd"
},
"source": [
"class Question:\r\n",
"\r\n",
" def __init__(self, column, value):\r\n",
" self.column = column\r\n",
" self.value = value\r\n",
"\r\n",
"# Compare the feature value in an example to the\r\n",
"# feature value in this question.\r\n",
" def match(self, example):\r\n",
" val = example[self.column]\r\n",
" if is_numeric(val):\r\n",
" return val >= self.value\r\n",
" else:\r\n",
" return val == self.value\r\n",
"\r\n",
"# This is just a helper method to print\r\n",
"# the question in a readable format.\r\n",
" def __repr__(self):\r\n",
" condition = \"==\"\r\n",
" if is_numeric(self.value):\r\n",
" condition = \">=\"\r\n",
" return \"Is %s %s %s ?\" % (\r\n",
" headers[self.column], condition, str(self.value))\r\n",
" \r\n",
"\r\n",
"#Test the class\r\n",
"print(Question(1, 3))\r\n",
"print(Question(2, \"Apple\"))\r\n",
"print(Question(0, \"Blue\"))"
],
"execution_count": 32,
"outputs": [
{
"output_type": "stream",
"text": [
"Is diameter >= 3 ?\n",
"Is label == Apple ?\n",
"Is colors == Blue ?\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "pSVucmj4-74q",
"outputId": "9b85d4dd-a33f-4300-b83f-29fbebda850b"
},
"source": [
"#Check the Quection and Dataset working properly or not ?\r\n",
"# Let's create quection from the Question class\r\n",
"question1 = Question(0,\"Blue\")\r\n",
"question2 = Question(0,\"Red\")\r\n",
"\r\n",
"# Let's take a sample data from the training_data\r\n",
"sample_data = training_data[3] # ['Blue', 2, 'Blueberry']"
],
"execution_count": 36,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"True"
]
},
"metadata": {
"tags": []
},
"execution_count": 36
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "crc7Z8crAQJ-",
"outputId": "0c583588-69fc-456a-d39f-2385dd2a5961"
},
"source": [
"# Check\r\n",
"question1.match(sample_data) # should be TRUE"
],
"execution_count": 37,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"True"
]
},
"metadata": {
"tags": []
},
"execution_count": 37
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "YljlGpwZATRM",
"outputId": "c910b55a-27af-4bc1-f3cb-3835eabde064"
},
"source": [
"# Check\r\n",
"question2.match(sample_data) # should be FALSE, because the colour of blueberry should be BLUE"
],
"execution_count": 38,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"False"
]
},
"metadata": {
"tags": []
},
"execution_count": 38
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "-2m87nEqCAY7",
"outputId": "bdff1f08-13f5-400d-ee27-c87968e62951"
},
"source": [
"# Define a function to partion data in to TRUE and FALSE columns\r\n",
"def partition(rows, question):\r\n",
" true_rows, false_rows = [], [] # Define two list for FALSE and TRUE\r\n",
" for row in rows:\r\n",
" if question.match(row):\r\n",
" true_rows.append(row)\r\n",
" else:\r\n",
" false_rows.append(row)\r\n",
" return true_rows, false_rows\r\n",
"\r\n",
"# Test the function using Partition Function and Question Function\r\n",
"true_rows, false_rows = partition(training_data, Question(0, 'Blue'))\r\n",
"print('TRUE Dataset : ',true_rows)\r\n",
"print('FALSE Dataset : ',false_rows)"
],
"execution_count": 42,
"outputs": [
{
"output_type": "stream",
"text": [
"TRUE Dataset : [['Blue', 2, 'Blueberry']]\n",
"FALSE Dataset : [['Green', 3, 'Apple'], ['Yellow', 3, 'Apple'], ['Red', 1, 'Grape'], ['Red', 1, 'Grape'], ['Yellow', 3, 'Lemon']]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "JlEKy2KgERtP"
},
"source": [
"Gini impurity is a measure of how often a randomly chosen element from the set would be incorrectly labeled if it was randomly labeled according to the distribution of labels in the subset.\r\n",
"\r\n",
"Different between Gini and Entropy [Click](https://quantdare.com/decision-trees-gini-vs-entropy/)\r\n",
"\r\n",
"![1_gc1d1Sf8F7NoXAEnRlitbg.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABZQAAAEiCAIAAADh7cAdAACAAElEQVR42uzdd1gUV9g//MPSe28iElQsoIi9F8Su0aixo7FiIirGbtAYS9SIvcWoUbCj2IkNCwQURYoiFqRYAAWkS1m2vtfreZ757bMLy9Jnyffzh9c6e3ZmdubsYc49Z+6jJhaLCQAAAAAAAAAAW3FwCAAAAAAAAACAzRC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDU1HAIAAGCP/Pz8z58/C4VC8f9FCJH8l5J8XX3W1tYWFhY4BQBoRtCMAACwkErNNtkAAADV4enpeeDAgXrZ9ODBg2/evIlTAIBmBM0IAAALIXgBAAAs4ufnN336dKmFP//8c5MmTcr7iNTNVaFQKPpKKBQKBAI+n19SUpKWlvbmzZvExMSSkpJy/yKqqLx69aply5Y4CwBoRtCMAACwDYIXAADALhMnTvT395dc0qtXr+DgYFVV1WquWSwWp6SkvPnqxldCoVCygKen5759+3AKANCMKGMz8vDhw5MnT8bHx1tZWXXp0sXDw0NbWxuVAQAaDAQvAACAXfLz89u3b//27VvJhWvWrFm/fn3NbiglJeXIkSN///13WloaXaKnp5eammpoaIizAIBmRLmakXXr1vn7+8+YMUNbWzsgICAkJKRx48a3b99u3bo16gMANAwIXgAAAOs8fvy4V69eAoGAWcLhcO7evduvX78a35ZQKDx37pynp2dubi4hZNeuXV5eXjgFAGhGlKgZuXnz5uLFi8PCwkxMTAghIpFowoQJAQEB9vb2cXFxOjo6qA8A0ABgqlQAAGCdrl27btiwQXKJSCRyd3fPzs6u8W2pqqpOmjQpJiamS5cuhJD9+/cjrA+AZkS5mpFTp04lJCRs2rTpf67vORz6+u3bt1euXEFlAICGAcELAIC6dvXq1RcvXtTNth49enTnzh1lPErLly93c3OTXJKWljZjxoxa2pydnV1oaGjfvn0TEhJu376NCgm16uLFiy9fvsRxQDNSUzIyMgQCwbFjx5glDg4OBgYGhJB79+6hJgBAw4DgBQBAnVqzZo27u7umpmbdbI7H4w0bNmzv3r3K9/eJwzlx4oS5ubnkwmvXru3Zs6eWtqihoXHy5EljY2NlPFzKUiGBio+P7969e3h4OA4FmpEasXr16kGDBm3bto1ZQudJoTk4UBMAoGFAzgsAUDICgeDGjRtnz56Nj4/Pzc01MDDo1KnT+PHjXV1d1dTUaJktW7aoqaktXbqUbTt/6tQpd3f3M2fOTJw4sbwynz9/Li4u1tTUVFVVVflKLBbTSfvEYrG1tbVU+aysLLFYrPaVqqqqUCgsKSnR/YoW2Lp166pVqwICAkaPHq10p/v69esjRoyQ/FOlqakZHh7evn37WtrihQsXxo8fHx8f37x5cwU/wuVys7OzBQIBPUfM3nL+l+pXmpqahoaGKioqylUhlYuynAuxWDxixIiIiIhHjx41a9YMDTuakRqvus+ePXNxcaE7M2bMGFQDAGgIxAAASiIzM3PRokUWFhZMC2ZnZ9evX7/mzZtzOBxnZ+dXr16JxeL79+9zOBwvLy/ZNSQnJ//666979uz58uVLTe1VXl7evn37vL29P3z4IL9kZGSklpaWh4eH/GI2Njbyr4klCwcGBpZZbNmyZUwZkUg0fPhwHR2diIgIZTzvP//8s9S3a9GiRWFhYe1t8cqVK/Hx8YqX/+mnnxT8m+vg4LBz5868vDw2HFgFKyStQn/99dfjx4/ZX1uU6FxkZWXZ2tq2aNEiOztbGX+YNMMCmhHWNiP06ZgOHToIhUJcPwBAw4CRFwCgHGJiYr777rsPHz4QQnr37r1+/XoXFxcjIyP67osXL5YsWRIaGvrHH39s3LgxIyPDy8tr165dUitp165dbGwsIWTp0qU+Pj41smNeXl50BHKnTp2ePHlSXrGMjIxOnTppamo+f/5c/sT7Z8+ezcrKKiws3LRp05cvXwghjRs3njZtmpmZma6u7tSpUyU/npmZuW7dugMHDtD/2tjYdOvWrWvXrpMmTWrcuDFTLDs7u2XLlmpqanFxcWZmZsp16nk8Xvfu3aOjoyUXTp8+XfLp7voVGhoaFxdXUlJy9uxZWgcMDAx+/vlnIyMjNTU1sVgsEAgKCwsvX75Mv4Wuru4vX9XjPitYIcVicVBQ0ObNm4ODgzdu3Ojt7c3y2qJc5yIkJKRfv379+/e/e/euEv0kMzMz//rrLx8fn5KSEj6fj2aEhVU3Kiqqc+fOjRo1Cg8Pt7W1xSUEAGDkBQBAHTl9+jTtX+no6OzatYsZVSvlhx9+kIwpSL2bnp7OvNuxY8ea2rdWrVoxqy0oKCiv2IQJE5gU9Irg8XhMGgIfHx85JenFt62trZ+fX3lHRiwW027n0qVLlbECvHnzRvax7VOnTrFtP8eOHUv3berUqWUWePDgAfNw0+HDh+txV+VXyGPHjq1Zs2bSpEmSg943btyoRHVGWc5Fz549CSGBgYEsP57Pnz/fuXOnp6fnwIEDNTQ06HGjnWo0I2yrujk5OS1atGjevHlycjKuHwCgIUHwAgDYLjg4mD7fq6amFhoaKr/DT3sCZQYvxGJx9+7da7wbtmrVKrpONze38sqkpKSoqakZGxsrPkr50aNHzOV1ZGRkecW2b9+upqbm5eVVXFwsf4UfP37U0NDQ1tb+9OmTMlYDPz8/qV6Hvr5+YmIiq3ayUaNGdN+OHj1aXpnvvvuOeeipvoZzV1ghDQ0N1dTUnJycJk+e3KtXL2UMXijLuQgICKBj++VEHtlg586dhBBTU9P+/ft7eXkpY/DiP9KMlJaW9uvXz8XFJT09HdcPAIDgBQBA3SkqKmKy2f3+++8Vlg8LC5MTvCguLg4MDAwPD6/BPRSJRGFhYf/88w+Xyy2vDB3Zu3z5csVXu337dvpFjIyMyuxWFRYWTpw4UU9P79y5cwquc9q0aYSQBQsWKGllmDJlilTHo3PnzjwejyW7l5yczOxYUlJSecUkn1d68eJFvexqhRUyISGhpKSEvl6xYoXSBS+U6FwIBAJ7e3vZdDZsk52dnZaWRl+XlJQoafCiwTcjIpFo4sSJffr0YTJivHz5kv3jegAAFISpUgGA1by9vZOSkgghhoaGCxcurLB8z549+/TpU9672traw4cP79atWw3uoYqKSs+ePYcNG1beZJNcLvfQoUP0+WrFV/vvv//SF3369OFwpNvqxMTEbt26PX36NCIiYty4cQqukz5Wc/jw4eLiYmWsDH/++afUvAxPnjyp38wRZZ4yW1vbpk2blleM1ud6pEiFbN68uZaWlvK2G8pyLgghqqqq7u7uhJDdu3ez+ZCamJgwIwKUWsNuRpYuXVpYWHjr1i1DQ0O65NKlS3KSMQEAKBcELwCAvTIyMpjZ+KdOnargZPXMeFqWOHPmTFZWlqmpqWR2DPnEYnFoaCh97erqKvXutWvXOnXq1LJly4iIiNatWyu+J127dlVTU+Nyucz4FOWir69/9uxZdXV1yYXbt2+/desWq3od/fr1k1Ps2bNnzNf55ptvlKJCKm/wguXngqIPu4WHhxcVFaHZRzNS5aq7e/fu+/fv//rrr2/evHn27FlUVFRYWNjFixdbtmyJ8w4ACF4AANSumzdvikQi+rpv376Kd9FZ9S2uXLlCCOnevbsiM/NTcXFxOTk59HX//v2Z5SKRaM2aNWPHjvX29g4ICNDX16/Unujq6jo7OxNClGtqA0mdOnXavHmz5BKaqDUjI6Pe9y0kJKTCXkdaWhqTzWTy5Mk6OjpKUSGVjrKcC6pbt24cDofP5zMdV0AzUtmqGxAQsHjx4piYmC5durRr187FxaVTp069e/eOiopC8AIAELwAAKiL4EUVQhIdOnTQ19eXurF2+/btsWPHurm5derUyd7eftCgQcxbubm5kyZNGjp0aI8ePVq1amVqavr27VtCyI0bN37++efu3bubmpp27dp169atpaWlkutMSUmZOHHi4MGDu3Xr1qJFCyMjI4FAILs/dMhujx49FP/iwcHB9IWpqWnbtm3p65ycnOHDh+/du/fWrVvLli2r2iGlu6G8wQtCyOLFi4cMGSK5JCMjY9q0afU783dqaiozkFtOr4Nm4COEWFparlmzpl52tQoVUrko0bmgDA0NHR0dlf2HiWakfqvu5s2bmVi/lBYtWuCMA0ADgbQfAMBOQqHQxMSEtlSqqqqVSsUvW/iPP/6QbPp69uzJvPXmzRtm5j8qMTHR29vb1tZ27dq1R48edXd3p/PSDRw4UHLN4eHhqqqqkh/k8/lS2/348SN9686dO4rv/5gxY+inxo4dS5dER0fTscG6urpMJrYqOH78OCGEw+Hk5+crb93IyMiwsrKS+nO2devWetylEydO0N1o0qRJeWWePXtGE0loaWnJnzen9lShQipdwk5lOReSZs6cSUcEKMURVuqEnWhGAACUlxrCNwDATjlf0dcGBgaVGuIuW3j58uULFiw4d+6cbJJCBweHkpKSN2/eDB8+nGZ6nzt3rlAojI2NNTIyIoTMmDFDXV392LFjQUFBly5dYiIL3bp143K5MTExgwcPzs3NLXNPYmJi6AtLS0vFY8rMyGGa8MLX1/enn37icrmEkKKiIj8/P0Vyl5bJwsKCPn6Snp5uYGCg4KcePnxYzZRvgwcPrsEMCxYWFsePHx88eLDkbVJvb29XV9dOnTrVS3VlBstIPubDKC4u9vX1XbZsGZfLdXZ2PnXqVJs2baqwlbi4ODU1NVVVVRUVFRqkU1NTY7IPikSipKQkFRUVOuyIx+M1btxYW1u7mhVS6SjLuZD9YaalpSm+Azwe79ChQ0KhsDojPiqVRbiB+c82IwAAygvBCwBgKT6fz7yubHKHMmlra48fP77Mi3UOh9OqVavRo0fTCUqjoqJevHhBIxeUh4fHsWPHCCFBQUFM8ILeeOzcubODg0NERESZG83MzKQvjI2NFe8UZWdn09c9evT48ccfjx8/fvDgwW3btsXFxRFCDhw4sGDBgqolLGB2IysrS/GxxKdPn/b391ckZkTJvubxeDWbHnLgwIHLly+XHFDD5/MnTZoUHR1dI7Wlyr2OFy9ezJ07l04QIxAI+Hx+ampqWFhYaWmpiYnJggUL1q1bJzkxzb179wIDA9euXcvMDlCeDx8+fPfdd58/fy4oKGDOppWV1dOnT+nQodjY2E6dOjG9WWNj47t377Zv376aFZJRvyPqG965kP1hMj98RXz48GHTpk2S7aScX2WZP099ff3Ro0dX+GUbQJVo8M0IAACCFwAA9do8qf2/BiojI0MkEsnOGFpZ8md/ZNY/atQoqUkBmev7lJSUSq02Ly+vsn3F+/fv0xeampoeHh45OTnh4eHt2rXjcrk//vgjISQ+Pv7evXtubm7VDF4o/ql9X7GthmzcuDE4OPjx48fMksTERE9PT/poTF1KSUlhnlQvKSmJjIyk/UPVrzQ1NefNmzdq1KhevXpJPWdUXFw8atSowsLC7777Ts4Uv1STJk0SExNzcnJMTU3pc+8zZsyQLODi4jJv3rzDhw97eXnNmDGjRYsWshGuKlRI5aJE50L2h8nj8QoKChQcEtW8eXPmISD4LzcjAAAIXgAA1DNzc/PGjRunpqYSQkpLS9PS0mxtbWWLFRYWxsbG6urqqqur0yHchBChUMj/SiAQSGb6VFFR4XA45WU1Y64I6cyFZb5V5mcl4yxl9hU1NDQUn8uACV6UlpZaWVkFBQXRMSDu7u4rVqzIz8+ngy+qGbyo1D1elv4BU1M7c+ZM+/bt6TGhTpw4MWTIkMmTJ9flnjD3S3V1daOjo6WSxcqho6Pz/ffff/PNNxX2lhlML0sq3aBYLN6wYUNERMSLFy+aNm1agxVSchNVODiJiYlV+6CmpmaTJk0a8Lko74ep+PNc9a5qZzYtLa24uLhqW2zWrFn1o9gNrBkBAEDwAgCg/g0dOvTw4cP0dVJSUpnBi4SEBNlYA8Pa2lrq5qSc4AVzTSw7koIJXpR5sS7nYrqwsLBSj72IRCJmusRFixbt2LGDuWGrq6s7ffr03bt3E0KuXr2alpZmY2NT2UPK7AlzB16p2dvbHzp0aMKECZILly5dOmnSpLqcB5SJN/Xs2bOyXQ76OJLiQkNDCSEtW7a0trZmFubn50+dOrVRo0YhISHyB5NXtkJWs6f68OFDOT/PCnuVqamplc3NoUTnogH8MKtQJbhcrqOjI/O4TWUFBASMHTsWzUh11Gy6FgAABC8AAMoIXpQ5dVzTpk0vX74sFAq5XG5kZOTu3btpbKJly5arVq2STSYv52KUiUHIDspl3irzYl3OOunzJrTHqIjY2FgmTalsYot58+bt2bNHLBYLBIK//vpr/fr1lT2kzJ7o6uo2jEoyfvz4u3fvHjp0iDl3Bw8erMsuh2SvgyZYrVU0tiW5obi4uMmTJy9evFiR5IuVrZDV7Kl27979yZMnVbs/r62tXYWsokp0LhrAD7MKZ1ZLSysiIqLKwQs5eUP+y81IPaZrAQCo6z88AADsVFBQwNzwmTJliiIfYQbD//TTT2UWoHeTJKdKZTAT5p89e1bqrbdv39K3aGp6KQMHDqTvyk6Vun//fvpWcXGxIvtPM4YSQho3blxmgUGDBtEClpaWpaWllT2kCQkJ9OP+/v4Npp4UFxc7OTnR77Vv37463jpTNwgh4eHhin/wyZMnx48fz83NVfwjRUVFtINx7tw5uuT06dNOTk7R0dEKrqGyFVJyqtS1a9eyvCYo17mQxERpMzIylGiq1IZ0GamkzQitinp6eoSQkJAQBT/CPDZ49OhR2XcXLFigpaW1YsWK169fV2qScgAATJUKAP9d+vr669atW758OZ3wwsvLq3Pnzgp+Vk4eigrV4DPVZmZm9EVubq4iI2+Zm299+/Yts4Cnp+ft27dpJ+fcuXPu7u6V2h9mSlfJuVQqtGLFigsXLlTnOEydOnXt2rW1VE+4XC6PxyOELFu2zNPTs45r6b1795jqqvgMiwsWLHj+/DmPx1u4cGFKSgrte1QoPDycx+OpqKj069dPIBAsXbp09+7dGzZsUPy+aGUrpHJRrnNRzR/m27dvhw8fTmt+1Whqat67d68BT5rb4JsRqn7TtQAA1CUELwCA1RYvXhwQEBARESEWi728vB48eCB/KC8zcWAVAhDMQFnZsdDMksoOk5bsK0rNYFLmDjAJL8oLXowYMaJJkyYfPnwghOzevbvKwQtmxxQxYMCAao5m//bbb2uphvB4vDFjxiQkJEyYMEFyysO673X07t1bwZDZhQsXHj9+HBERsWXLlvDw8MDAwIkTJyryQZrSz8nJSSgUurm5vX//nhBy6NChVatWKTgBQaUqZHm/AvYHL5TiXMj+MPX19eloDkVYWVlNmTKFabWqwNDQsPqRC7FYXMePV6AZkVWP6VoAABC8AAD4H6qqqkePHu3YsWNpaWl4ePjevXsXLlxYXuGsrCwmeFGFDphAIJB6oWDwQs46mUvDzMxMZkxyeZ48ecI8h1xe8ILD4Xh4eKxevZoQEhkZ+fDhwx49eih+PD9//kwIUVdXr3BnJA38ip01ZM6cOcHBwX369PHz86uXThTT6ygzJ0uZdu7cSceh3Lhxg855qeAH6cAcHR0dNze3rVu36ujo9O/fPyUlJTAwcNSoUYqsoVIVUnmDF0pxLmR/mJUataGtre3t7V3vx7xhBC+UsRmpjhpM1wIAUJc4OAQAwHJOTk53796ldwi9vLymTJkiOaedJEUSWNIblWV2w5hZSGSDF8ySMu9zMu/KrrZVq1bm5uY0MFHhvt26dYu+sLCwaNGiRXnFRowYwbz28fGp1MGMjIwkhDg7OzeMG2tr1649fvx4q1atLl++XC/f6Pnz558+faKve/fureCnPDw8hg8fnpycHBoa2rp1awVHiRcXF0dERNC7xNeuXRs+fLirq6ujoyOdOlfBTVeqQkrV6vKm6WEJpTsXsj9MxR+Lq/eAhWyziWakjqsuU3NOnDhRqUlqiouL6c+/f//+dMmZM2cmTpzo5+eHyAUAsBxGXgCAEujZs2dUVNSYMWMiIiJOnz4dFha2cuXKjh07tm3bVltbu6CgIDQ09OTJk2fPnp0/f/67d+8CAwOl1nD27Nng4ODk5GR62f3o0aOJEydaWVmtWbNGJBJt2rQpJyfn8uXLtLC3t/e///5rbGzs4+Ozd+/e58+fP3r0iL4VGho6efJkExOTGTNmiMViX1/fnJycsLAw+u6gQYPatGnj7u7etWtXukRFRaV///7+/v4PHz4s86t9/vx5+/btubm5OTk5V65coQuLioqmTJliYmKir6+/adMmpnBiYqKPj09ISAiz5PLly46Ojl26dBk0aNDkyZMrPJJ0N5SljyTfsWPH1q9fb2VldePGDWNj47rc9K5duxISEgoLC5n7pYSQX3/9tVmzZlpaWvPmzXNwcJDz8WnTphFC/Pz8xGKx4r2FBw8e8Pl8Dodz9+5dExMTutDzq6CgoISEBPkbVbBCUnv37k1KSuLz+RkZGcyvadu2bXFxcTY2NhoaGq1bt/bw8GBDNVDec8H48uXL8+fPWf7DfPPmzZ9//ikUCktKSsLDw5nlzs7OXbt2pak6Fi1aZGdnh2akbqouS9K1AADUKeQsBQBlweVyDxw40L17d6YFU1VVtbe3p0+Ym5ub79+/XywWC4XCX375ZfXq1ZKfnTlzpr6+vrm5uaWlpbW1taWlpZmZmYaGRmJi4uvXrzU0NExNTS0sLKy+srCwMDEx0dbWFovFvXv3NjQ0NDc3t7KyYj6ora194sQJPz8/bW1tMzMzZp3m5ua6urpHjhyR3DSdfs/CwqLML3X+/HkdHR1dXV19fX1DQ0MjIyMDAwNdXV1NTU0Oh9OuXTvJwlu3btXS0jIxMbG0tKT7Y2pqqqurq6qq+uuvvyqSTp9OtnL9+nVlrwy3b99WU1PT1dWNjIys403n5+ebmJjo6Ojo6ekZGBgYGRkZGhrq6elpaWmpqqpqaWm9ePGiwpWIRCI7OztVVdW0tDQFt7tq1Sr6ZIHkwoKCAn19fTooScH1yK+QVK9evfT19WndpjXN2traysqK1n99ff2hQ4eyoRoo+7mg7ty5Qx8Dyc7OZu0vLigoSFNT09jYmGkMmVphbm5ubGysoaHx6NEjNCN1VnUDAgI6d+4sFos3b95MR08ouGn61GGbNm0+ffrUp08fGm+ytbUVCAS4zAAAlkPwAgCUT2Ji4ubNm2fNmjVs2LDp06dv3br12rVrBQUF7Nzb5ORkGmpJSEio3z2hQzYcHByUffa7Z8+eGRgYqKqqBgYGKulXuHv3LjPzbkxMzLFjxyr8CB3Os2jRIqnldGYEAwMDBX8C7KmQOBfUunXrCCGzZs3CuUAzoriePXvSPaeTjDx58kTxDxJCunTp4ujoGBgYyIz7uHz5MmoFACB4AQDwXzd8+HBCyJo1a+p3N+bMmUPHKiv1wUxJSbGxsSGEHDx4UHm/xcyZM+kEwGKxePz48UFBQXT5ixcvBgwYsHv3bqFQKFk+Pz+fjjC6dOmS1Kri4uJo32PPnj3KVSFZfi6KioomTJgwf/783Nzc2jsXIpGIJrh5+vQpzgWaEcXRZ52SkpJUVFRat26t4KeKioro+DsXF5ekpCS6kKZrGTRoECoGALAcEnYCANQ6mkn04MGDXC63vvYhKyvrxIkTenp6M2bMUN4jWVBQMGzYsLS0tFWrVs2dO1d5vwidFsfV1TU0NLS4uHjAgAF0+dGjR+/cuePl5fXs2TMmIcK7d+8OHjxIk8UaGRmlp6czOWL5fH5paamOjg4hZMeOHcHBwdnZ2UpRIdl/Lu7du+fv779v3z5mHsraOBf//PPPmzdvevfu3a5dO5wLNCOKq366lqZNm9KFdMQQTdeC6gEAyHkBAPBfN3r0aELI4cOH62sHaH/V09NTeY8hj8ejHcspU6Yo+5MvYWFhRkZGPXr0kLz/KRaLHz161LRpU2NjY+aGcJMmTbS0tGhiFC0tLXV1dVVV1YiICPrub7/9pqWlRZ+ZNzAw0NDQ6NGjh1JUSPafi7y8vG7dupmYmEycOLH2zgWd8eH8+fM4EWhGKqve07UAANQxBC8AAOpCXFyclpZW69ateTxe3W89Pz/f0tLSwcFBagC8cqF3Gl1dXUtLS2tvK9nZ2d27d6+Dx+C5XG5MTEyZ9WHt2rX79u1rwBWSbeSci/v373///fe1tN3Hjx8TQsaPH49TgGakCuo9XQsAQB3DYyMAAHXBycnp+PHjr1+/XrFiRd1vffbs2cXFxVeuXKEzGiqj1atXHz9+3MnJ6eLFixoaGrW3oXXr1sXExNCcdrVKU1PTxcWFPn8uJTQ01NXVtQFXSLapl3NRUFAwZcqUdu3aMY+lAJqRSjl16hQh5IcffiCEbN68uXHjxnT5y5cvBw4cuGfPHpFIJFXlIiMjCSF9+/aVWtVPP/1EC/j6+qKeAABrqeEQAADUjXHjxiUnJ69cudLV1fXbb7+ts+3u378/ICDg8uXLrVu3VtJDd+jQod9//93a2vr69eu1Gn+Jjo4+cODAmDFj6jHKExMTk5OTQ1PoNcgKqUS+fPly6tQpen+7xs2ePTsvLy8oKIimyQA0I5UlP3XOnTt3evfu3b59e1qTs7Ozz507J5muxczMTE1NTTJdS3Fx8Y4dO9p+ZWpqijoDAKyDwScAAHVp3rx5JiYmHz58qJvNPXnyRFNTc8uWLcp7xAIDA1VVVfX09KKjo2t1Q1evXtXT06M5FOvx+QVnZ+ewsLCGWiGVy+zZszdv3lwba96zZ4+2tvb9+/dxkNGMVFm9p84BAKhjKmKxGBEcAIC6dPz48W7dutH5EWtbcHBwTk7OmDFjlPRYPXnyhD6dfu3atSFDhtTehnx8fFauXCkSiSwtLVNTU+kNybqXn59/48aNiRMnNtQKqVyOHDkyY8YMOjFqjR9zJyenjh074iCjGamO0tLSV69eOTk5yT709Ntvv5mbm9NkFgAADQOCFwAAwFLJycndu3fPzMw8cuTIrFmzamkrUVFRf/zxx/nz5+l/Fy1atHPnThx8ADQjSt2MuLm57d27tw4eQAMAqDPIeQEAAGyUk5MzbNiwzMzMNWvW1EaXIz8///Tp04cPH46JiZFcTrPfAQCaEeVtRuosdQ4AQF3CyAsAAGCd0tLSgQMHhoaGTps2zc/Pr5pr4/F4nz9/zsrK+vz589u3b+Pi4l68ePHw4cOSkhKpku3atXv69CmOPwCaEeVtRkpLS7t06XLgwIE6mDUJAKAuYeQFAACwi1gsnjFjRmhoKCHkypUrFhYWKl/Rd5kXTGHJF5JZnURf8fl82d5FeaZPn47jD4BmRKmbES6Xu2rVKkQuAKDhwcgLAABgF29v702bNtX9dtXU1NLS0iwsLHAKANCMoBkBAGAbDg4BAACwh6+vb710OQghw4YNQ5cDAM0ImhEAAHbCyAsAAGCRjIyM1NRUoVBIB2zTkduSw7lpsQr/eNFh4bKjxOUst7W1bdSoEU4BAJoRNCMAACyE4AUAAAAAAAAAsBoeGwEAAAAAAAAAVkPwAgAAAAAAAABYDcELAAAAAAAAAGA1BC8AAAAAAAAAgNUQvAAAAAAAAAAAVkPwAgAAAAAAAABYTQ2HAAAAGry4uDihUEhfm5qaNm7cWPLd9PT0nJwcTU1NdXV1gUDA5/MdHBw4HMT3AQDNCAAAW6iIxWIcBQAAYKdDhw5lZ2evWrWqOit5+/Ztr169iouL8/LyCCGGhoZpaWm6urpMgRUrVmzbtk0kEtH/mpub379/38nJCccfAM0ImhEAAJZA8AIAAFgqNDS0T58+Ojo62dnZWlpa1VxbYWGhhYUFj8cTCoV//vnnjz/+KPXuoEGD+Hy+v79/06ZNcfAB0IygGQEAYBUMZgMAAJZq2rTpqFGj/Pz8qt/lIIQ8ePCgffv2o0aNIoTs27dP6l09PT2RSLR69Wp0OQDQjKAZAQBgIYy8AAAApfT58+dbt24FBQXFx8cLBAJm+blz58rsOaxatUpFRWXQoEGurq6EkHv37tEXVHFxsamp6cePH42NjXFsAdCMoBkBAGAbJOwEAADl8/fffy9atKh3796enp5WVlaampoaGhocDkdVVfWbb74p8yPBwcHr1q3r169fmzZt4uLi9u7dK9nrePToUevWrdHlAEAzgmYEAICdELwAAADWEQgEt27dEovFgwcPVldXl3p3+vTpCQkJ169f7927t4IrLCoqevbsWc+ePQkhCxYsmDt37tWrVz98+NCkSRNaICQkRLITAgBoRtCMAACwCnJeAAAAuxQVFbm6up47d27WrFnDhg2TevfkyZMfPnwICQlRvMtBn1R3cXGhUwO4u7sbGRnRfHtMgZCQkH79+uHgA6AZQTMCAMBOCF4AAAC7rFy5slu3bn5+fi4uLnfu3Pn06RPzVnFx8ZIlS06dOqWmVrmRg/fv32c6FTo6OjNnziSEHD58mMvlEkK4XO6TJ0/69OmDgw+AZgTNCAAAOyF4AQAALJKXl3fu3Llly5YVFhaGhYUZGxubmpoy7z5//rxPnz7W1taVXW1wcHDfvn2Z/3p6enI4nOzs7DNnzhBCHj586OjoaGhoiOMPgGYEzQgAADsh5wUAALAIn8/39fW1sLA4duxYcXHxDz/8oKGhwbz79OlTgUBw6NCh8j6uoqIyefJkOq6bUVhYyDypTjVt2nTYsGGBgYH79u2bMWOG5A1VAEAzgmYEAICFELwAAAAWMTc3Hzp0KCHE19eXJtWTfDc3Nzc7O/vNmzdy1lBUVCTV63jw4IGzs7Oenp7kwgULFgQGBkZHR4eHh9+/f/+XX37BwQdAM4JmBACAtRC8AAAA1klOTg4NDW3VqlWXLl0kl9vb2zdr1mzbtm2VWtu9e/dk74gOHDiwZcuW8fHxPj4+0dHRlcrbBwBoRtCMAADUMeS8AAAA1vH19RWLxT/88AMh5OzZs48ePaLLv/3221u3buXn51dqbffv35d8Up1SUVGZP38+IeTSpUtt27bV19fHYQdAM4JmBACAtRC8AAAA1jl16hSHw5k6dSqfz9+wYYODgwNdrqOjM2PGDE9PT8VXlZOTI/WkOuOHH36gnQ1XV1cccwA0I2hGAADYDMELAABgHT6f7+DgYGNjs23btmnTpknOFLB+/fqsrKx58+bR6QnlKCwsDAkJmTBhgkgkun79elpamlQBfX19elcWafYA0IygGQEAYDkVsViMowAAAKyya9euFStW9O7dW1tb+/z581paWpLvlpSU/PLLL7du3ZozZ86wYcOsra21tLQkZxOg7O3tMzIyOByOWCwWCoWmpqayHY/4+PgePXq8f/9eKg8fAKAZQTMCAMAqCF4AAAAbZWdnf/78uVWrVuUViImJOXv27M2bN9PT03k8nkgkon/RoqOjmzdvjgMIAGhGAAAaEgQvAAAAAAAAAIDVkPMCAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWQ/ACAAAAAAAAAFgNwQsAAAAAAAAAYDUELwAAAAAAAACA1RC8AAAAAAAAAABWU8MhAACA/5TU1NTnz5+npaUZGhqOGzcOB6RhuHnzZlZWlqWlpb29ffPmzXFAAAAAGhgVsViMowAAAP8FFy5c8PHxefz4MSFEQ0OjV69ed+/exWFpGKZMmXLmzBl6VePk5DRnzpyFCxeqqKjgyAAAADQM/+nghUAgSEtLEwqFzEHgcDjW1tZaWlqyhXk83sePHzW/0tDQUFFR4X+lrq5uZGSEmgQAaAlZ/k29vb03bdpECOnXr9+GDRu6du2qrq4uVYbP51+6dMnY2HjAgAG03ysQCNLT03V1ddXV1Tmc/3nWUiwWCwQC3lc2NjZSKykuLs7KytLS0uJwOCoq///fWZFIVFpaqqenZ2xsXHtfUHbnqywxMfHu3bujR4+2sLBgFr57904oFEqVtLW11dDQkF1DZmYmIYQeBA6HI/qKHrFGjRpVZ9/CwsLevXv3/fffy1bR3NzcsLCwixcv+vr6EkKGDh165coV2bMMAAAASkn8H3b48GHZA9KzZ88yC/v5+ZV5AHV0dHg8nhgAAC0hi8XExKiqqtIOrUgkKq/YyJEj6TeaNm0aXfL27Vs5f0MdHR1lV3L9+vUyC69du7ZWv6PszldNVFQUjX1oamq+fPmSWW5lZSX7pTw8PMpcyYQJE8o8CN26davOvu3YsYOup3nz5qWlpeUV27hxIy22f/9+/MYBAAAaBtXffvvtPxu40dTUbNOmjZubm7W19dOnT+nClJSUkSNHWltbSxU2NTW1sbExMDB4/fq1WCxWUVFxc3MbP3788uXLW7ZsiSgY/Adt2bKlVatWOjo6OBRoCdnfEm7YsCEiIoIQcvz4cVtb2zLLlJaWzpw5UyQSEUJiY2OnTJliYmKiqqpqY2MzYMAAR0fHN2/elJSU0MJ2dnZeXl6jR49u27at7CElhLx69aq0tJQQoq+v37dv34kTJ44cOVJ2mEZNKXPnq7aqgwcPhoSEEEKEQmFBQcHo0aPpcjMzswFfqampJSQk0IVRUVGdOnVq0aKF1EoMDAwaNWqkqqr6/v17uqRLly7jxo2bMGGCo6Njlb/mypUr6QpzcnLs7e3bt29fXohky5YtIpEoOzt79uzZZZZ5+vTpnTt3nJ2d0QgAAABg5IXS2LZtG4fDYcY8z5gxQ07hmTNnEkKOHTum4Mrj4uL8/PxycnJq+1vw+fzpX127dq1ujtv79+/pFp8+fcoszMjI8PX1ffPmDZvPuFAoTEhIuHr16r///puXl1f9Fe7evXv69Om//fbbf+dXc+nSJULIiBEjmCX5+fl5eXlfvnypy92oQrUPCgo6f/68QCBA01eXLWG9++GHH+j3evXqlZxiI0aMYP4+Xrx4UepdyQQZnTt3ljOCQywWe3t7E0LatGnz7t27uvmO8ndecZGRkcx6nJ2dZQscOHCAEGJubk7LWFlZZWVllbc2+qjO3Llza6qWMvu2cOFCOSV1dXXLGxojFouLi4s1NDT09PTS09Px2wcAAFAKCF78zwVfhw4dli5dSq+HtLS05FyHjRgxwt7eXsE1v3z5kj4M7OjoSB8prz30Fh8h5Pfff6+b4xYdHU23yPQbeTxe06ZN6RjypKQkdp7u8PBwJycn5vJ33bp11V8nHa3dsWPH/8hPhsfjOTg40MEXzEJDQ8Pqjwmv7Wr/999/0/I//vgjmr46awnZYMqUKfR7JScnyyn25cuXv/76y8fHp2nTpn/99ZdsgcmTJzOth6+vb3nrEQqF7du3NzY2zszMrLPvWOHOKy4qKmrNmjVeXl7W1tblBS/Onz/PHIpx48aVt6oXL15UM5gidWCvXbu2du3a4cOHT5o0SU5JAwMDQoiDg0N5Bfr37y/nsRcAAABgGw7GnohEotDQ0L59+86bN48mY+NyuUeOHClvoEp4eLirq6uCK4+KiuLxeISQly9f5uTk1MgOX7p0qWvXruzMkJ+RkZGcnEzz1TGhDcUlJSX17NnTx8en9vYwKSnJ1dX1xYsXBgYGEyZMWLx48XfffYdfQWUdPXo0ISFBU1Nz1qxZdbPFmqr2Dx8+pC9CQ0NxHuusJWQDJtMkzXxRHj09PQ8Pj4ULF378+LHMFA/btm3T19dnHmH48uVLmes5fPhwTEzMxo0bmeEJdaDCnVdchw4d1q9fb2pqKmc9Q4cOZZ7IOH/+/JkzZ+SssKZm/eBwOCNGjPjtt99KSkrkf0d6omUzjDLmzZtHCPn777+ZR2AAAACAzRC8IE+fPs3Pz+/Xr5+9vT0z5vbAgQNlXvG8fPkyOzu7X79+Cq58+PDhrVq1IoTMmjXLzMysRnb4+vXrERERNRUKqVmNGzemSdrat28/ePDgyn780aNHDx8+fPfuXe3t4fLly7lcroWFxevXr8+ePbt9+3Y88FwFhw4dIoSMHz++pmp1nVX72bNnGxoaqqurL1myBOexzlpCVgUvmBlD5IiOjuZyuWVm8bC2tl63bh19nZ6evn79etkyOTk53t7e7du3//HHH+v+m8rZ+cp68OCB/PXs2LHDzs6Ovp4/f/6nT5/q5jsKBILHjx/L3zd6omkSkDKNGjXKxsZGKBQePXoULQAAAACCF0ogODiYw+H06dOHELJgwQK68MOHD1evXpUtTO/WKn6/0djY+MWLF9nZ2eXdwKwC+anv693Zs2ezsrKioqKYm5Os+moPHjygT1/LZiIEBT1//pwOq6H3LetGTdWNbt26ZWRkZGdnz5gxA6eyzlpCNmA6sYoMAbh69Wr79u3L6xsvWLCgTZs29PXu3btfv34tVcDb2zsnJ2ffvn2KBEpqnPydV1x2dvbDhw8nTZokp4y+vv6xY8foIc3JySkvNWYNjryg7t27x+Pxxo4dW2HwQs588Gpqah4eHoSQEydOyIlxAAAAAIIXLLpkb9euHc1R5+bm1rp1a7p83759soVDQkKaNWvWuHHjShxiDqfKKd/LRJ/LYDNTU9OqXafW9lfLz8/PyMgghMgmxgfFHT9+nI4q79atW51ttAbrhqamZhUia2gJq9kSsid4UaGYmJht27YtW7ZMTqeXOSx8Pp+J9VDR0dGHDh2aOnVqjx49FNmcWCxOTEwsKCiosGReXl5ycrJAIKD//fz5M5/Pr+zOK27u3LlNmjQZOnSo/GKurq7z58+nr69fv86klals8EIkEiUlJWVnZ1e4Y1++fJk7d+6MGTPkj/yqMHhBCJkzZ466unpaWho7n8QEAAAABC/+z9VSaGgoM/hZRUXF09OTvr537x5NMyZ1fV/Zm42vXr2KjY1NTU2VvWCNj4+/cuXK+fPnX716xVyPyicQCFJSUhTcdF5e3s2bNy9dulRhx6+0tDQyMvLUqVMPHjxQ5BpajpSUlNjY2Pj4eNm30tLSgoKCTp48GRkZWVxcLFugOnfXMzIybt++7e/v//Lly/Ieci4qKqIvaBb6ShGLxW/evDl//vyNGzc+fvxYqc++e/fuypUrJ06cePbsWXknWsH6UOExZCp2fHy8v7//7du309PT5XypKlTCf//9lxDi7u6uSOHExMQ3b95I7v/ly5dPnz4dGxur4OZqttrzeLzYr/Ly8mR/BTExMWfPnv3nn38+fPhQ2RrC4/Gio6NPnToVGhqan59fncpQ4yeaDS1hvWOaBfm9aC6X6+7uPnr0aPkjDvr27csUuHPnDpO6UiwWz58/X09Pb+vWrYr8OhYuXNikSRMHBwdDQ0NbW9vJkyfLnsfi4uK1a9e2bt3axMSkWbNmhoaGffr08fb2btGiRUBAQBV2XhHHjx+/du3ayZMn1dXVKyy8ZcsWmsGXEPLzzz8zc6MqGLyIiIj48ccfbWxsmjdvbmZm9s0330yYMCExMbG88l5eXhwOZ/v27RVc31T02Ah9CIim7aTNGgAAALAa2zKInjp1qk+fPppf9enT59SpU3UwX8bly5cls7XTFOWy8xHQscGV3SVbW1v6pL3UhBcdO3aUPBFGRkYHDhyQM/HenTt3+vXr16RJE6a81f86fPiw1LQLNPOl5MXikCFDykx6n5WVNW7cODU1NclLTE9PT0UmvJSdbUQsFk+dOpUQ0rx5c8mSKSkp48ePl/y+qqqqCxcuLCgoEIvFnz59GjJkSMuWLWl+NR0dHearyZ+skbpx48Y333wjuXItLa2lS5dyuVymzJUrV6ytrS0sLGgBY2Nj6//l5+cnf/0CgWDt2rV6enqSm3BwcAgJCZE/24hQKNyxY4exsbHkB1u1ahUVFSU7AUqF9UH+MZS/t/3795edYaEKlZBWM01NTUJIYGCg1FtlzjbSvXt3Qoi/v/+nT5/GjRsnOYrewcFB9lDUdrVPSkqib508eVJyuZ+fn9STRC1atLh3754iv/Hs7Oxx48ZJ9fEGDhz4/v37KlSGGj/RLGkJ693w4cPp1/n48aOcYgEBAc2aNcvOzq5whWlpaczxb9y4cWFhoVgs9vX1JYTs3Lmzwo/fvn2bjnNZsmTJo0ePYmNj6e/R1NT09u3bTLHCwsJWrVqpqant3LkzOjq6tLQ0KSlpxYoVtLU8ffp01Xa+Qm3atNm1a1d579LZRuhXph4+fMhkQnV1dZVsRmjwq7zJjHfv3q2mpmZlZXXw4MGYmJiEhIT58+dramrq6OgcOHCgzMNubGz88OHDCr9Co0aNCCGNGjWSX2zhwoWEkMGDByOFOwAAAMuxK3hRZgq9JUuW1N4Wt2/fzuFwcnJyZC9l6P35vLw8ZvnBgwfpHdFqBi9evXpF7/zb29svWbJk7dq133//vZaWFiHkwoULcrro1tbWzChZOb04Nzc3IyMjVVXVzp07T506lUlI2bFjR6l+6ZMnT+jlnZmZ2Zw5czZv3uzh4WFpaUl7bpIXptUJXhQXF9Pnww0MDDw8PNavXz979mwaR1iwYIFYLM7MzLSzs7OysqI920oFLxYtWsTcQBs5cuSMGTM6dOhA19OmTZuUlBRaLD4+vlevXkxqegMDA2YT8oMXOTk5vXv3pp9q2bKlu7v77Nmz27dvT2/r3b9/v7zgRUpKSs+ePekHO3To4O7uPnLkSHretbW1mR1TsD5UeAyZXjQNFqirq0+YMGHDhg2LFy+mH9TV1Y2JialmJRSLxZGRkfRLvXz5UvHgxbhx4+gPoXnz5u7u7q6urjTEoKurK78nWePVvszgBX0QhhDSs2fP1atXr1y5ku6hhoaG/N2jvwL61eimPTw83N3d6RITE5Pc3NxKVYYaP9HsaQmpBw8e7KqeV69eVa21Z4aKVNi3LykpUXCdksMrli9fnpeXZ2lp2bZtWz6fL/+D169fp139vXv3SsYp3NzcCCE2NjZFRUV04S+//EIHOkmtwdvbWzZ4Uamdr85BkA1eiMXilStXMkdj9+7digQvfv75Z9oKJSYmSgUZdXR0pNZT2e9Ig57m5ubyi+3cuZPGjHBFCAAAgOCFok6dOlXe8JDau8X37bffuri4SC2Mj49nbt5K3kCbMGFCixYtKrsJ2eDFuHHjCCGdOnWSHBrw6dOnM2fOyL/pTfucdMfOnTsn9RbTi6Mz2H3+/Fm2hx8QEMAsFAgEtLfTp08fycKZmZmdO3cmhCxdurRGghf79++nnTHJe8IlJSWnT5+W7BGJxWIaN5k3b56Cx/aff/6hOzB37tzS0lJmeWBgIL1p/P3330uWT0tLo+UPHjyo4Ca8vLzoI+579uyRXH779m1vb2/mfMkGL7KysqysrLp06RIREcEsfP36Nd2x6dOnV6o+KHgM6dQGlpaWsbGxzEI+nz9nzhxCSJcuXYRCYTUr4cWLF+nwnOLiYsWDF7RKPH36lFl+6dIlunz+/PkVnoUarPZlBi/oZJaS0QGxWPzs2bO7d+/K3zGRSEQjWRYWFsHBwZI/roMHD/r6+la2MtT4iWZPS0h5enqaKcDc3NzCwsLyKysrK2tr60Zf2djYbN26tQrbffv2LT3abdq0qcG/IDwej84nRQNJdN5lyZpQptLS0ubNmxNCHB0dy2vTfv/9d7qEplCloTpJdHZP2eBF3SgzeFFaWsrkMdXW1o6Pj5cMXsiO1Xrw4AGtYH///bfsJlasWCG1nsqaO3cu3ZlHjx7JKXblyhVarMJ4PQAAACB4IZa8RCtTnz59amOLQqHQyMjIy8tL9q0hQ4YwPS6mL2dpaTl37tzqBy/oMw5r166twj4r0oubMGGCVNeFSTogGRfYu3cvHebw4cMHqVVFRUXRHrvUSPWqBS+mT59OHxGv8NtVKnjB4/HoU9Zdu3aVfZdJp8cMjqhC8CIhIYE+C7Bo0SL5Jct8bKTMu6+0R21ra1up+qDIMXz69CkdciI7dKKwsJCOOrl+/Xo1KyHNxmdtbS37lpzgRYsWLaTu6jODumX7b7Va7WWDF8zUvJJVRUF+fn70s5IPXJRJwcpQ4yeaPS1hPcrLy3N0dKSjpYKCgmp25UFBQZJ/rSZPnlzhR3bs2EELr1y5UuqtL1++0DaH+atHgyPm5uZSfXihUDh+/HhFnp6oveCF7NOF0dHRzPNT3bp1EwgETPDin3/+kdr/du3a0T80WVlZspugyZUV/NtRptTUVDpqqVWrVnKGUMXGxtINpaam4qIQAACAzViUsPPx48dVeKs6nj59mpeXx+Sok8TkTk9MTLx58yYdJJ+RkVFm4cqifbZz587V0pT4zs7OUvPzOXxFvw6zkN76HjBgAA2vSOrQoYO5ublAIHj58mVNfd/HX9Xg14yNjaX3HhcvXiz77syZM2lf+sKFC1XexMWLF/l8voqKytKlS6vwcclMIgwnJyd6lVxSUqJ4fVDkGF69elUkEhkbG48aNUrqLV1dXfrwC3OZXuVKSPNc2tvbV+pTdnZ2UukeCCE0BwEdYlBn1V6WhYUFHcB/8OBBHo9XqY2ePXuWPk9Eo1c1VRlq8ESzuSWsbTwe7/79+4sXL3ZxcXn58qWdnV1QUNCAAQNqdisDBgxgToSWlta2bdsq/Mjt27eZvCpF/5eKioqLiwshhImyTZw4kU4s0qZNmyFDhmzevDk4OLioqIjD4fj7+zMjm+orZ5bUkvbt269Zs4YZ7yAna+nr16+fPXtGCGndurWpqWmZP0yaUiQ0NJTJtVwpNjY2T5486dChw+vXr52dnX18fCIiImRXxbRmubm5yIMGAADAZv/p2Ubu37+voqJS5oiPoUOHNmvWjL6mIxTu379PCKmRS3Y6TcOrV69at249f/78sLCwOphhnn4dyYszGph4+/bt1LLQeRBqJHgxbtw4TU1NLpfbq1evsWPHXrp0iemqVQfNGkgIodf6UrS1tVu2bClZrArorXtra2sbG5uaOhF0kk6xWMxMSKFIfVDkGNKTpa6uPn36dNkT+vz5c8kTWuVKSKdmlMoTWZ06yeVya6Q+KFjty6wqY8eOpVlFW7ZsuWHDBsXrDK0hHTt2rNrcwLKVocZPNJtbwtq2adOm/v3779y58927d8uWLUtMTKTzStQ4GoRiMgEr3nbNnDlTT8aTJ08IIenp6bQR9vT0pLkb+Hz+rVu3fvnlF1dXVwsLi8WLF1c21lYHwQtCyKpVq+iDh4SQ3377jVZIWXFxcUzETc5vk04XUqlgnKQmTZpERkYeOnQoNzd3+fLlXbt2nTVrluxWaPhSdt5ZAAAAYBU19uxK165dy5urrGvXrrWxxeDg4LZt25qYmJQR1OFwPD096S39mzdvJiUlBQcHt2rVisn4WB0//fRTdnb2xo0b8/Pz939la2vr4eGxZMkSeq1WB4qKiuiEfO/evStzhkU1NTULCwv5s+gryMXF5dy5c3Pnzk1PT7/4lb6+/qRJk3799dfqBAWY2VhlR44wyyMiIsqctLVSm7Czs6vO109OTvb394+Pj09LS0tNTZWd8lOR+qDIMaTjC/Lz82/dulXmnpibmzOFq1wJ6XgW5lGLhuHIkSMcDufs2bPv3r379auOHTsuXrx48uTJcj7F4/HocVC8hlRYGWr8RLO5JaxtS5YscXFxuXbtWmBgoI+PT1JS0saNG1u3bl2/eyUQCJiZRP/6668ePXqUWUxDQ4OO1jEzM3v69OmsWbOYNDF08tSdO3eGhYVdv369RlrpGgxeqKmp+fn5dejQgcvl8ni8adOmHTt2TLYY8yyYnIFXzBMosbGxVRtjkpqaun79+mPHjuno6AwdOvTbb78dPHiwVJmUlBQ6jS5t3AAAAIC92PMESx0n7BQIBAYGBlIp+iTl5uYyd4QWL15sZmb2008/VWFDZU6VKhaLP378+Mcff3Tq1Im5Z+vo6CiVwLJqD/8zmd5kn13v3Lkzs4TePN+yZUuVj6HiU6WKxeKioiI/P78hQ4ZoaGjQT+nr60dHR1c558XGjRvpevLz8+XkoZDck8rmvKAjzDt06FBhyTJzXjx48ICZY8LAwMDZ2XnQoEHMkk+fPlW2Psg/hiNGjKCTgyp+BqtQCc+cOUN7VrIpIeXkvBg4cKDsqrZs2cKE0uqs2pc3VSrN0LlkyRLJaXdlp3iQemifzhq7ePHiCg91pSpDjZ9oNrSE9SgzM7Np06a0Myw5w3FNoROClJcLRhYT+jl//rziW0lISDh48ODUqVPpd6EqTKtcqzkv6GQ6Zdq+fTuzk6NHj5bNebF79276buvWrctbCTO5NZP7tlL4fD7NGKKvry+ZLVjKvXv36FZqZIpZAAAA+E/kvJg8eXJ5U6XKv/9ZNVFRUQUFBXIGPxsZGdF+OCFk//79WVlZzEx7NcLa2nr58uVPnjyJj4+fMmUKHekt5wnhGkev6phuYW3T0dGZNm3ajRs3Pn36tGHDBnV19S9fvjBP1Fd5/wkhHz58KLMAXc4UqwL64EnVRhncvHlzwIABDx48mDBhwr///pudnf3s2bNbt24tWLCgyvVB/jGswgmtQiWkE3PweLyPHz82sEius7Pztm3b3r59GxISQse9nzx5Uiodo9SoBDpnRIU1pLKVocZPNHtawhUrVjSvnnXr1lV2o+bm5tevX9fT0+Pz+cz8r7U9AEEOmj2UEJKVlVVhYV9f35iYGBqHnTt37vHjx5OSki5dukQjIH/++SfzzBFLRl5QixYtYh5EkhwzwmCGwJS3/wKBgDk+VRsvc+DAAfqEjr+/P00OWqbk5GQa2MLICwAAAJZTY9XebNu2rUOHDn/99RdNVte1a9e5c+fWRuSCEHLv3j0VFRWa3K488+fPP3jwICGktLRURUWlb9++tbEnDg4OJ0+eTElJ+ffffyMjI+syeBEZGRkSEsLn85nRuXXAxMRk9erVfD5//fr19GZy1fIFMFGJ0NBQZn4+RlZWFr1srU7wgn42Jyfn+fPnbdu2rWwnraSkZObMmXR6jpqtD2UeQ7q3Hz58ePPmTYsWLWqpEjJ9gOTk5MaNGzfIZrFPnz7379+3sLAoLi6OjIwcOHCgnBry4sWL8PDw0tJSOgqjZitDjZ/oem8JBwwYICfHgSK+/fbbKnyqZcuW7dq1e/DgwbNnz/Ly8oyMjOoxeDF8+HB6t//hw4d01lvZ5is4OHjs2LEqKirr1q1zdHRkplClvvvuu9zc3JkzZxYVFeXk5Cje6+ZyuVpaWnXwxTkcjq+vr7Ozc2FhYZkFOnfubGhomJ+fn5mZ+eXLF5r/RdKnT59oIh47OzsmiUal7Nmzhz4MMnToUDnF3r59Swhp27YtzXwBAAAACF4oavJXdbChO3futG7dmt5GLo+Tk5OrqytNUOfo6MgMYa0NNCVbhU+qM1dXBQUF1dzi/PnzT5069e7du/3799MpGyUJBAKRSMSMWq+l72ttbS0ZuaDfTsGv5uTk1KtXr7CwsO3bt8+ePVsq/rJ7924ul6umpjZz5swq7+SYMWNWrlxZVFS0bt26gIAAqXeLi4t1dHTK/GBJSQnNMCd7Q5t5dKX69UHqGI4bN2716tXp6enLly+/fPmybHk5O6z4Rs3MzOzt7d9+JWd645pVg9VeQbq6uiYmJsXFxfKPxrRp0y5cuPDp06fDhw/LDiOiB7ymKkMNnuh6bAkHflUvf1wMDAyYn2fNBi9oxgTJF/J5eHj8/vvvOTk5165dS09Pl00g8ttvv4WFhX3//fc0XeWtW7c+ffoklQqUTnGip6enYMqV3NzcSZMmBQUFubm5Xbp0qZohJEXY29vv2LHDw8OjzHeNjIxWrly5atUqgUAQHBwsG5O6ePEifbFw4cKqBbi5XK4i2YXpyIuqxUcAAACgLv1HZxvx9fUNDg5WU1OrMFs7M667Bp8Zmf+V5IDhZ8+e0U5IhdP42dra0su4c+fOcblcgUDg7+/PPPZfKV27dp0+fTohZNmyZevXr2dWUlxcfOTIkaZNm/bo0YNe/FX/aH/33XdMbnl6GU3viUl9X3oVfuPGjczMTEJIUFBQeY+EUHv37uVwOElJSW5ubqmpqXQhj8fb9BW96q1Ofr5GjRqtWrWKzrc6e/ZsZo69nJychQsXGhkZ0ckjZWlra9NO7/Xr1yV7ONu2bVu+fHkV6oMix9DAwOCPP/4ghFy5cmXcuHH0GNLOz82bN3v06GFjY0Mnl61mJezSpQs9O3X2g63Bai/ry5cvQ4cO3blzp2Rr8Pfff6empmpoaMgfkjBy5Eh6uBYvXnzgwAGm7xofHz9w4MCmTZumpqZWqjLU+Ilmc0tYx5ihMTUyNa9sJ5k2PoqU19PTO3HihKqqal5e3vDhw6XGJjx9+tTX11cyCZRQKFy/fr3USmg64c6dO0vNEFyekydP3rp1SyQSBQUF+fv7V+f7FhQUnD9/nj60Ir/knDlzhg0bVt67Xl5e7du3pz+fjIwMybdycnJ27dpFCHFzc5ONrSuIDtyQP56Chk6YZg0AAABY7T+V4ePChQvu7u6SvdkWLVpMnDgxICBATjY7ettTTplKJexMTU2lyeG1tbWHDh06Z86cIUOG0Kzyo0aNUmSFNAchHQ1LJwjYsGFDFTIXisXi7OxsmvyPpoh3dHRs2bIlM9pi0qRJpaWl1UzYKRKJaMpGFRWVnj17zpw5c+zYscbGxjRUIZXvjcngqK6uTo+bm5ub/KNx7Ngx5saara1tu3btmP2fOHHily9fJAtXNmGnWCwuKSkZN24cc4jatWvXpk0b2gsyMTF59OhReQk7165dSz/VvXv3RYsWTZ8+3cbGRlNTk3ZEmRyNitQHxY+hSCRauXIl05mxs7Nr164dMx7b2dk5NTW1+pWQDkLR0NDIzMysm4SdNVjtZRN2Xrt2jQ7bsbKyGjNmzKxZs5jpjXbu3FnhjiUnJzs7O9Pyurq6Xbp0adq0KT0FLi4uGRkZileGGj/RrGoJ6x0dyEBnoKj+2kQi0aJFi3766afRo0dLjnNxdXWdN2+el5cX0ziUx9fXl87s07Vr171798bGxqanp584ccLU1HT37t1MMXrYCSE//PDDq1evRCKRWCwODw93dHS0s7NLTk5WcIclH1ny8PCo2rdevXr1kCFDTE1NmVU1a9Zs4sSJkn8CpHz69ImWl0rYSeXl5dH4YNu2bX19fZOTk9+/f3/hwgV7e3tCyIgRIypMYi0HzQD9zTffyClz7tw52pplZWUhCxoAAADL/beCF507d9bV1dXX1zc0NDQyMjIwMNDV1eVwOD4+PnI+5ePjY2Fh8fnz5xoJXtCQwbx58yTH+jZu3HjPnj18Pl+RFb5//75Xr15Md3rkyJH0+rUKwQvq6NGjHTt2ZB6E1tbWHjx4cGBgYIV7ouBsI3w+f/v27Y6OjrR7TG8dL1y4UDa1e2lp6axZs5gRwi4uLsHBwYr0Hr///nsm/4Kenl737t3Pnj0rW7IKwQvmur9du3bM/ltZWc2fP//jx49MAdnghUAgWLlyJTPtqIaGxujRo1+9eiUWi2nKAGaCCUXqg+LHUCwWP3z4sH///rTTSx8+79Sp0/79+7lcbo1UwtLSUho+2LRpU50FL2qq2pc528iLFy9GjhzJdMk4HI6Li8uNGzcUrB5cLnfZsmWSc0C0atVq69atJSUlla0MNX6i2dMSNrDghUAgUFNTMzIyMjc3t7Kysv5flpaWpqamOjo6R44cqXAlr169GjlypOTTeUZGRj4+PjRCwQQvBg4ceOHCBZrrxNDQUEtLS0dHp2/fvm/fvlV8h4uLi2fOnGlqampra9u+ffuqfevGjRsbGxszX9nS0tLMzExPT2/FihVyPnXx4kVNTc07d+6UFyA+cOCA1BC5tm3bHjx4UPI4VAF9GKpp06ZyytCHucaMGYPLQQAAAPZTqfEBtKC4/Pz89+/f29nZVSHJeUZGxufPnx0cHOSkCawUoVCYlJSkoaHRqFGjWkp1UVpampCQYGZmJvuMt9SY5Hfv3lXhsOR+ZW9vX7UHpBUZHJ6cnGxoaNioUSMFN8Hj8ZKSksRisZ2dXYUPmStSHxQ8hlRKSgqXy7W2tpbz1HfVKuGCBQv27dtHb/wqOGq9RtR4tZfy8ePH3NzcZs2aVS2pYV5e3sePH2n3tZqVocZPNIwfP54+7JCSksKqXLN5eXlRUVHZ2dl2dnZOTk5SJzEoKKhv374aGhoikSg5OfnNmze2traOjo5Vzi65cePG8+fPP3v2jG2DQF+9evX+/XsOh2Nvb1/9TLT/H3v3HRfVte6Pf80wgPRmAcUKCCoCooggElGxiw00WLEQjAU5aowlMebEY7xHjSWaozlWbOjBEiti1BAVjRGlCErvKihdaQMzv9f1+Z195zsDwwaBoH7efw171uyy9p7NrGev9SxK0JOXl2dhYZGQkFBjgbi4uF69ejHGzp8/37BEsAAAANCcELwAgHpLS0uzsrKqqKjAj354X3h7ewcFBdFELdQh7uM0cuTI9u3bHzhw4IM/UkNDw4KCAktLS5p5StHixYt3795tZ2cXERHRnEFYAAAAaBj8twaAeuvSpcvSpUsZY4cPH0ZtwHuB66pQVVX10VZCdXV1eHg4DXP7GA5WScLOioqKI0eO0BztiFwAAAC8F0SoAgBogDVr1pw7d47LIwDQ0v/b/TeNCM85QT5IlAp0+PDhH8PBUvBCbhZtjqqqqo+PT3p6+tChQ/HtAAAAeD9+zqEKAKAB9PT0njx5gieW8L7gEqYWFhZ+nDXw5MmTtWvXbtu2TXZ6lA+VWCymbL615a8RCoU7duyg6VQBAADgvYCGBwA09PaByAW8PyZOnEgv/v3vf3+cNTB79uxBgwYtWLDgYzjY7du30/igOXPm4CYGAADwYUDCTgAA+Ch4eXkFBwczxjw9Pf38/FxcXBo2rcx76uTJk8OGDatxKpwPRnl5+b1794KDg3fv3s0YGzJkyLVr1xChAAAA+DAgeAEAAB+LH99KTEykAQXDhw//5ZdfUC0fBj8/v8OHD9Nokfbt22/cuHHWrFlNNHM2AAAAND8ELwAA4OMSFxf39OnTnJwcdXX1uXPnokI+DGfOnHn58mXXrl27devWpUsXLkUrAAAAfBgQvAAAAAAAAACAFg0DQQEAAAAAAACgRUPwAgAAAAAAAABaNAQvAAAAAAAAAKBFQ/ACAAAAAAAAAFo0BC8AAAAAAAAAoEVD8AIAAAAAAAAAWjQELwAAAAAAAACgRUPwAgAAAAAAAABaNAQvAAAAAAAAAKBFQ/ACAAAAAAAAAFo00cd88FVVVZmZmaqqqmpqakKhUCAQMMYk/6Wjo6OtrS1bvqKioqCgQFVVVfQWY0wsFpeXlxsbG+NKAgAAAAAAAGgiAqlU+tEe/L59+3x9fWt7t3fv3tHR0bJLHBwcHjx4IF+DAkFxcbFcmAMAAAAAAAAAGstH3fPCxcVl7969paWlN2/ePH/+PC309PTs27evjo6OnZ2dXPm1a9euXLkyMTGRMaaqqmpvb+/o6Ojm5obIBQAAAAAAAEDT+ah7XnC+++67devWMcZ0dXXz8/NVVFRqLFZWVmZvbx8fH+/t7b1x48bOnTvzWXlubu6VK1ecnZ0tLCya+kBOnz598eJFFRWVffv24bQCAAAAAADAh0GEKmCMhYeH0wtXV9faIhdpaWmTJk0qLy+/ffu2s7MzzzWLxWInJ6eUlBRNTc2YmJhu3bo16YFEREQcOnRIVVUVwQsAAAAAAAD4YGC2ESaRSO7evUuv3dzcaiwTGhrar18/Y2PjiIgI/pELxlhOTk5KSgpjrLS09OHDh42yw8nJyQMHDty8efOHekY++AMEAAAAAACAekHwgkVHRxcVFdFrxeCFVCr9/vvvx4wZs3jx4osXLxoaGtZr5aamplOnTmWM9enTZ8SIEY2yw/fu3QsPD09LS/tQz8gHf4AAAAAAAABQLxg2wsLCwuiFoaGhra2t7FvFxcWzZ88OCwv75ZdfRo8e3bD1BwUF7d6929DQkKZifXepqakf9hn54A8QAAAAAAAA6gXBC/b777/Ti08++UQo/L+uKHFxcRMnTtTQ0Hjw4ME75qowMjJqxB2mcSgfsA/+AAEAAAAAAKBePvbghVQq5XpeyI4ZOXXq1Lx588aMGXPw4EENDY132URmZmZBQYG6urqlpaXcW9nZ2XFxcTk5OVZWVj179tTU1OSzQv4dE6RSaVxcXFRUVJcuXWxtbbW0tGorKZFIEhMTIyMjDQwMbGxsjI2NG1CTiYmJUVFR2tratra27du3r7HYq1evIiMjMzIyLC0tbW1ta5xllucBZmdnP3z4UCwW29jYmJmZ1daxpays7PHjxwkJCUKh0MzMzMHBQXkXmJycnKioqIKCgt69e1taWtaYwDUpKamyspJ7986dO3FxcSYmJm5ubsnJyYwxMzOz2mr7xYsXubm5enp6PGerAQAAAAAAgP9tc7Yox44dc3V1VX/L1dX12LFjTbq56OhoripiYmKkUqlYLF62bBktWbt27btvYubMmYwxc3Nz2YWZmZlTpkyRPREqKir+/v7FxcW1ref58+cjR47kGsyamprG/zVnzhwqs3r1asaYqqqqVCrdvHmznp4et34dHZ0aK7Oqquqbb76RCyIMGTIkJSWF5wHWuAYLC4uwsDDZYiEhIebm5rJl9PX1ZXeJzwGS4OBgueBIt27dbty4IbdjEolk165dbdu2lS1pbm7u81+ff/65bPkrV6506dJFtnCrVq1WrFhRXl4ut2YnJyfG2IEDB8rLy4cOHcqVf/jwoampKWPM2dm5tpNIIardu3dLAQAAAAAAgJ+W1fNixYoVW7du5f78/a2HDx9u2bKlibb422+/0Ys2bdr06tUrJydn6tSpXF+Mffv2rVu3Tk1NrXE3WlZWNmrUqMePH+vq6n766aempqYZGRnnz5/fuXOnVCrduXNnjZ9SUVF58uRJRUWFVCpVvn6JRDJx4sRz5861adPG09OzoqLit99+KykpmT59ura2toeHB1cyPz9/7Nixd+/eVVVVnTp1qrW1dUFBQWho6I0bN3r37n379m07Ozvl2yooKBg/fvytW7cYY5aWlg4ODq1atYqIiHj06JGbm9v169cHDx4sFosXLlxIs7d27drV3t5eTU0tLCzs2bNn06dPb9Wq1aRJk/gfoJ+f388//0wRFkdHR1VV1fDw8F9//XXo0KF79uz57LPPuJKbNm1as2YN7ZiXl1dxcXFwcHDSW7S5CRMmcIX/9re/bd++nTFmYmLi4OBgZGQUFRUVGRm5ZcuWkJCQK1euUFRCVkZGxsqVK69fv05/CoXCTp06LV68eNWqVeHh4X/88Yejo6PcR/bt21daWqqvrz979mxETgEAAAAAAN6/nhfHjh2rbSebrv8FNZsZY15eXnfu3Gnfvr2bm9tPP/3Ebfr48eON3vNi9+7djDFdXV3Z3g1lZWXHjx8vLCysc4Xt2rVjjC1cuFDxLep5wRjT0tI6deqURCKh5SkpKQYGBowxa2tr2fILFixgjLVr1y46OppbKBaLfX19GWP9+/evrq5WvjNLly5ljIlEIoq8cEJDQ9euXcvtwPDhwzt06HDixAluSVFRkYODA42wqKqq4nmAQUFBFCYICgqSXX7o0CGq0uzsbFqSnZ1NPTi++eYbrlh1dTWdcTMzM7FYzC2/dOkSFxmh6Am5ePGirq4uY8zT01Ox54W1tbVQKFy9enVKSkpJSUlWVpZUKs3Ly6O+FTNmzFDsotKxY0fG2LJlyxA3BQAAAAAA4K8FBS9cXV1rC164uro2xRYlEgmXSrN///5qampffPFFVVWVWCzmHrO7uLg0evDCx8eH8oM2bIV1Bi+EQqFsMIJMmzaNjig3N5eWREZGUoLS06dPyxV+/fo1pb24fPmykj1JTExUVVVljAUEBNRZ1XIRCqlUeu7cOdql5ORkPgdYWlpKjf8lS5YobmLcuHGMsZUrV9Kfp0+fpu4VFFPgXL16lTEmEAhevXpFSyorKy0sLBhjjo6OiqvdtWsX7eTNmzflgheMsdWrVyt+hEJCampqL168kF1+9uxZOjtyxwsAAAAAAADKCVtOH5A//vijAW+9i+jo6Ly8PHr95MmTEydO/POf/1RRURGJRH5+frT89u3bMTExjbtdytfwx1tNcVwqKiq9e/eWWzhmzBh6QYMmGGPnz5+XSCQGBgbjx4+XK6ylpTVo0CCqIiUbOnPmjFgsFggEK1asUL5LAoFAMfNlr1696EVCQgKf43rw4EFmZiZjbM6cOYrvjhw5UnaHS0tLqao7dOggW6x///7U4aigoIC7DBITE6lDhOJq586dS6lDKBoiy8DAgIalyFm6dKlAIKisrKThLZx//etfjLGxY8e+4+Q1AAAAAAAAH5uPerYR2YQXt27dkp0NxNfX97vvvqusrKQ2p+xAknfn5eW1devW8vJyFxcXDw+PGTNmjBw58h3nNKmTmZkZveAa7XFxcZTdk3qCyKGQDZWpDQUdTExM5AIEPOno6NCLwsJCPuW5ndm6davijCEU1+DK0NwxWVlZCQkJ3bt354rdvHmT4g5chTx9+pRe1JjgQ0NDw9LS8v79+1wxTr9+/WqcLcXKymrEiBEhISF79+5dvXq1SCSimNG1a9cYY/7+/rjvAAAAAAAAvK/BC0dHx99//722t5pii9SOpYf2cvOYtmvXztPT8/jx44yxI0eOfP/997Izd7wjOzu7U6dO+fn5vXjx4sxbOjo63t7e69ata1gUoGGoC0ZRURGNpFDUpk0b5fsTHx/PGOM/5WdBQcGpU6cePnyYnZ2d9VYDdlgoFIaGhta2w9x0IR06dBg9evTly5cnT568Z88eR0fHqqqq0NDQhQsXMsZWrVrFhT/oKBhjNCZFUceOHe/fv88V4yMgICAkJCQ7O/vMmTM0rcy//vUvqVRqbW0tOzsJAAAAAAAA8NGCho1wIzXq9VaDSSQSblaRTz75RLHA4sWL6cXr168pH2Qj8vDwSE5OPnz48MiRI9XU1EpKSn7++ecePXo8evSo2Sqcslq4ubnl1m7jxo1K1tCqVSvGWEVFRZ3bev78uaenp7Gx8YIFCw4dOpScnGxgYODi4tKAHVZXV8/Jyalth7lzSqNaxo0b9/jxYxcXF01NTS0trfHjx798+dLf3/+LL77giqmrq9MLsVhc43ZpOVeMj+HDh/fo0YMx9uOPPzLGysvL6RJasmQJbjoAAAAAAADvcfBi2rRpy5cvV1y+fPlyLtlkI3r06BE3WqHG4IWTk1OfPn3o9a5du+qcoLS+NDU1Z82adeXKlefPn3/33XeqqqolJSVcxKQZWFlZUbKPBq+BuqukpaUpL5aamjpw4MDTp09bW1ufPXs2Ly/vyZMnN2/e3Lt3bwN2uKysLD09nU/5Z8+epaSkdOvWbcWKFePHj58+ffqGDRtiYmJ27NghO+qEVktTn9a4HlrOFeNDIBDQPCy3b9+OjIwMCgrKz883NDScMWMGbjoAAAAAAAD11bJyXmzZssXe3n7v3r2UydLR0dHPz68pIheMsRs3btCLDh06mJub11hm4cKFNGloUlLS5cuXuZyXjcvQ0PCrr74Si8V///vfHz58KJVKFRM6NF3wIiMjQy4rRH3XkJ+fHxMTo5gilLN9+/bU1NQePXqEh4fXq/9CjZtjjF27do3Oi3LTpk2LjY29deuW8i4e3Gpv3bplbW0t9+6rV68o20W9gheMsVmzZq1ZsyY/P3/fvn2RkZGMsXnz5tEsqgAAAAAAAFAvwpa2Q9OmTQsLCyt/KywsrIkiF4yx69ev0wslU7ROmzaNS3WxY8eOJj3wTp06UfLLOiMXNG1HcXHxO27Ry8vL2NhYKpWuXLmyxgI0YYcSkyZN0tLSYox9++23Sj5OoSgnJye5yEV2dna9DtDMzGz06NGMsfXr179580b5DtNcsBQ6+fPPP7k0pYp69epF0Y2tW7cqjhzZsWNHeXm5SCSaO3duvapXQ0ODhjsFBQWFh4erqKgsWrQIdxwAAAAAAIAPIXjRPMrKym7dukWvlTyW19TUpEkr6Gk/NYbf3aFDhyZMmPD48WNuSUFBwc6dOxljw4YNq/PjlCDzypUrubm5tGO1jXdQTldX93/+538YY7/88ouXlxetjbKBhISEODs7d+jQgeYQrU379u1Xr15N04jOnz+fCyjk5+f7+/vr6+uHhIQwxqhjS1hYmGxw4fLlyzS5ab0OcPv27Wpqas+ePXN2dpY9HdHR0TNmzNDV1T116hQtEQgEHh4etG/9+/c3NDRUUVHR19e3srIaPXr0zp078/PzuY//+OOPQqEwOTl56NChXBrRysrKjW/RFCGUw6JeFi1aJBKJ8vLypFKph4cH/8ymAAAAAAAA8P+QfkyCgoKWLFni4+MjOzrAwcHB19c3ICDgt99+ky28a9euCRMm0DyXREdHx93d3d/fn5qjPM2cOZMa8PSnRCJxcnKi1vXAgQPnzp07efJkAwMDarQXFBTUucJNmzbR/qiqqtIEGUOHDqW3KJSgqqqq+Kl79+7Rpy5dusQtlEgkq1atEgqFXNTA1taWm8HUxsYmKytL+c6UlZV5eXlReZFIZGtra21tTT0sDA0N7927J5VKr1+/Tpvo1q3bokWLFixYQJOSLl26lE7EiRMneB6gVCo9efKkvr4+FTAyMrK3t2/bti39aWBgEBYWxpVMS0szMjLS1tY2NTVVU1OTu/ItLCwyMzO5wgcPHuTmPe3YsaOtrS33kU8//bSkpER2D+kMuru713myvL29aSVyVxcAAAAAAADw93EFL+zs7DQ1NbW1tXV1dfX19fX09LS1tTU0NEQikYqKSnBwsGxhHR0dPT29Nm3aGBsbm5iYtGvXTk9PT11dXUdHp6ioqMHBC6lUKhaLt27d2rNnTy4yoquryz8mUlFRMW/ePG50iZ2dHdcwrm/wgoSHhw8ZMoQCKDQXab9+/Xbv3l1eXs7zGPfv329ra8sdjrGx8eLFi589e8YVOHr0qImJCRc4cHR0vHz5slQq/frrrwUCgVzwQskBkqysLG9vb1NTU26FZmZmX375ZU5ODlfm/Pnzurq6ffv2TUtL4+IsmZmZYWFhAQEBNDJlw4YNsqtNSUnx9PTkVqutre3k5BQUFKR4vPyDF2vXrmWM2dra4l4DAAAAAADQYIJGn0QD+KuoqEhMTGzdujVNAlovxcXFaWlpnTt35rJyvLvMzMzy8nITExOuD0K9lJeXp6Sk6OnptW/fXjFzh0QiycjIKCoqMjU1NTIyapQDzM/Pz8nJadu2reIK7ezsoqKibty4wQ38kTVu3LiLFy/OnDkzMDBQ8d2Ct7p27fqOmVNLS0s7deqUl5e3b9++efPm4YIHAAAAAABoGAQv4ANUXFxMIY+oqCgbGxu5d6VSqb29fWRk5E8//fT555833W7s2rVryZIlRkZGWVlZrVq1wnkBAAAAAABoGCGqAD48urq6ffr0oflKHz16JPtWenr6zJkzIyMjLSwspk6d2nT7UF1d/cMPPzDGfH19EbkAAAAAAAB4F+h5AR+mxMREV1fXFy9e0DS0pqamqqqqz58/T0xMlEqlFhYWly5dsrCwaLodCAoK8vb2FolEKSkplHYUAAAAAAAAGkaEKoAPkoWFxdOnTw8cOHD06NHU1NTw8HDKJDpo0CAfH5+ZM2fKziPTFK5fv963b99JkyYhcgEAAAAAAPCO0PMCPgqvX7+mCURQFQAAAAAAAO8dBC8AAAAAAAAAoEVDwk4AAAAAAAAAaNEQvAAAAAAAAACAFg3BCwAAAAAAAABo0RC8AAAAAAAAAIAWDcELAAAAAAAAAGjRELwAAAAAAAAAgBYNwQsAAAAAAAAAaNEQvAAAAAAAAACAFg3BCwAAAAAAAABo0RC8AAAAAAAAAIAWDcELAAAAAAAAAGjRELwAAAAAAAAAgBYNwQsAAAAAAAAAaNFEH/PBSySSFy9eVFVVSd6SSqW0XCAQCN9SeUtNTU1XV1ckEuFyAYAPQ1VVVWZmpqqqqpqamlAoFAgEdEskOjo62trasuUrKioKCgpUVVVFbzHGxGJxeXm5sbExKhMAAAAAmsFH3SAPDg6eOnUqn5KGhoZz585duHBh165dcdEAwPvu0KFDvr6+tb3bu3fv6Oho2SUuLi4PHjyQKyYQCIqLi+XCHPCRSElJyc7OHjRoEKoCAAAAmoeA627wEUpKSrp+/Xp5efndu3dPnjxJCxctWmRmZiYSiQQCQVVVVUVFxe3bt69cuVJdXS0UCj09PQMDA9XV1XHpAMD76+nTp7///ntpaenNmzfPnz9PCz09Pfv27aujo2NnZzdw4EDZ8ufOnVu5cmViYiJjTFVV1d7e3tHR0c3NbcKECajMj9COHTuWLVsmkUiOHTs2bdo0VAgAAAA0g486eMHZtWvXkiVLGGOdOnVKT09XLJCZmenu7h4fH88Ymz59+tGjR1vCbv/666+FhYUTJ05UUVFp0g3FxsZu2bKFMbZ27Vpzc/MP+2I4ffr0xYsXVVRU9u3b14irraqqogfdkydPHjt2bMPOb2ho6IkTJxhje/bs4SJosbGxERER48aNMzAwaOF1K5FIUlJSnjx5oq+vb2Njo6enh5vPX+67775bt24dY0xXVzc/P7+2m0lZWZm9vX18fLy3t/fGjRs7d+7MZ+X/+Mc/kpKSevXqtWLFir/qAHNzc69cueLs7GxhYdHCz0VGRsY333zDGAsICLC1tW3Ju2pqapqdnc0YO378uLe3N75HAAAA0BykIJVyD45mzZpVW5nt27f///EegSAxMfEv3+f9+/fT/ixYsKCpt3X16lXa1p07dz74i2H16tX0bLlxV1tRUcE15xp8fn/44QdaWFJSQkvi4uLU1NQYYz179qyurm7JFXv37t1evXpxd55vv/0Wd56WYOTIkXRGxo4dW1uZ1NTUPn36dOnSpb53ACcnJ8aYu7v7X3V0lZWV3bp1Y4xpamomJye38HPx8OFDOhcXLlxo4bs6d+5cVVVVHx+fqqoqfIkAAACgeWC2kf91+/ZtevHJJ5/UVobrRC2VSkNDQ//yfQ4PD6cXt27daqx1fv31146OjoWFhbgk3pfzGxERUVlZyRiLi4vLz89vsWczOTnZzc0tNjZWV1d36tSpy5Yta/7hBsnJyQMHDty8eTOuLo5EIrl79y69dnNzq7FMaGhov379jI2NIyIinJ2d368DzMnJSUlJYYyVlpZyoQF4d/v37y8qKjp48GBT9/sDAAAA4CB4wdLT0zMyMuj14MGDlbR8WtRuz58/X09PT1VVdfny5Y21zqCgoPv374vFYlwV78v5HTNmjJWVFWNs3rx5rVu3brFnc+XKleXl5W3btn369GlQUNDWrVttbGyaeR/u3bsXHh6elpaGq4sTHR1dVFRUW/BCKpV+//33Y8aMWbx48cWLFw0NDd+7AzQ1NaWszH369BkxYgTOeCPS0NBAJQAAAEBzwvSf7Pfff6cXHTt2pA7GNYqKiuJeN3+7S9GAAQNycnIqKyt1dHQaZYUSiaTGfB/Qks+vgYFBbGxsYWGhXMOypZ3NO3fuMMb8/PxMTEz+qn1ITU3FdSUnLCyMXhgaGsolWSguLp49e3ZYWNgvv/wyevTo9/cYg4KCdu/ebWhoSNPBAgAAAMB7CsGL//v5rqTbBeVxpBdWVlYuLi4tYc/V32qstWVlZaHPRYvC8/wKhULFR+It6mwWFRXl5OQwxrp37/4X7gYNHwBZXOj2k08+EQr/ryNeXFzcxIkTNTQ0Hjx4oCSk+74wMjLCuQYAAAB43yF4wX777Td6oSR4cevWrYSEBGoo7tixoyXsdmVl5dOnT2mGFH19fdm3CgsLY2NjU1JSOnXqZG1tzfOHe72eS2dmZv7xxx+6urp9+vRp06aNkpLZ2dkPHz4Ui8U2NjZmZmY8H34mJSVJJBKurVtVVRUREfH06VMbGxtra2tVVVXZwlKpNCYmRkdHp2vXroyxioqKX3/9NTs728bGZsCAAbIlS0pKoqOjU1NTu3fvbm1trampyWdnEhMTIyIiunbtamdnpySa8OrVq8jIyIyMDEtLS1tbW21tbeWrffPmzb1794qLix0cHExNTfmfXzlPnjwRi8WGhoayK6ntbMbFxVVVVRkbG7dt27bGAiUlJampqSKRqGfPnnwqh0+Vvnnzhl5oaWnV9zqXSqWJiYlRUVHa2tq2trbt27dvcOXzvMJ5XrFlZWWPHz9OSEgQCoVmZmYODg7Kr+2cnJyoqKiCgoLevXtbWlrWmCkgKSmpsrKSe/fOnTtxcXEmJiZubm40bM3MzKy2Onzx4kVubq6enh7PeUCobrnQreyYkVOnTs2bN2/MmDEHDx5s3KEBFRUVERER6enpvXr16tmzp0hU6z+gtLS0qKio4uJiGxubXr16yZbMzMwsKCgwMDDo2LFjjZ8Vi8VPnz6VSqW0CSqvrq5uaWkpe8cwMjLq0KEDLYmJiaHct7a2tkqCNXl5ebGxscnJyXp6etbW1g0LxhUXF0dGRmZlZfXo0aNnz578A9ClpaXR0dFxcXGmpqZ2dna1fYX53P8rKiri4uLi4+N1dHR69+7dqVMnnhfMgQMHAgMDX7165e3tPW/evL+wIxUAAAB8jFpaBtFjx465urrSM2dXV9djx4416eYyMzO5qkhJSamxTGFhYY8ePajM5s2bW0hFcTk4jh49yi0sKytbs2ZNq1atZE/xiBEjlKfZ37Zt24ABA7jfuG3btjX+r8ePH8vNNhIaGtqlSxdu5QKBYOXKlZWVlYqrDQ4OlmtqduvW7caNG/wnKTh58qRYLF60aJFsW7Rt27ZhYWE1TuSRkpKSmJjI7Z6BgQFXJj4+3sXFRbZ5KRQKx44d++zZMyWzjRw4cKBdu3bcR1q1arV3717FvQ0JCZGbQVZfX1/u0pWdbSQnJ2fkyJGy7TFnZ2e5Panx/CrONiKVSqkJN3/+fD5nk8b/6+jovHr1qsaat7e3Z4x5eXnVeY74VOkvv/xiYmLCtbIMDAxM/uvw4cPK119VVfXNN9/IhSEsLCzkzn6dlf/8+fORI0dyEQFNTU2uQubMmdOAK1YikezatUuu6Whubu7zX59//rls+StXrsh+ZehCWrFiRXl5eY2X/YEDB8rLy4cOHcqVf/jwIUWmnJ2da6yr58+fU9ho9+7d/O8h0dHR3CZiYmKkUqlYLF62bBktWbt27bvfprjZRl6/fj179myaGYersYiICLny1dXVP/zwg9ykv1ZWVrIl9+7dSxdbdHR0jRudP38+fYr+nDlzJm1O7svYqlWr9PT0Bw8e9OvXT3ZzI0eOzM3NVfwv8Le//U0u0DBw4EDupO/YsaPO2oiJienbt6/sV0YkEi1ZsuTNmzfKZxtJSEgYPHiwbNcYoVC4atUquRsvz/v/4cOH5YIO3bt353NnXrx4saam5ooVKzw8PBhjtra2EokEac8BAACg2bSs4EWNuQmXL1/edFs8cuQIbaVLly41Np9CQkLoSaaRkdGZM2daTl3V2LidO3cuY0xFRWXy5Mnffvvt0qVLKexiY2OjZFVff/21sbGxrq5uncGLiRMnCoVCTU1Nd3f3qVOnco/6ly1bJrfOzz77jN4aMmTI6tWr161bN2zYMAp21Nj+r7HN4+fnR58yNzefPn26i4sL9blQVVW9evWqYlyApkXgrhzuqI8fP05NOx0dnSFDhvj6+rq6utISQ0PD69evKwYvRCJRQEAA1YaXl9fw4cO53BPr16/nCldWVlJLiTHWtWvXyZMne3t7cw3g06dPK+7klClTOnbsqKKi0r9//+nTp3MdHNq3b5+dnf3uwQvlZ5ObWmLTpk2K1c7NbHL79m3lJ4hnlVKAw9jYmFarq6vL7Yzy4EV+fv6gQYPoU5aWljNmzJg/f36fPn2o2Xbz5k3+lZ+bm9u5c2djY2Nq+9UWvOB/xW7cuJHbsa+++srf31825EHfPq4wXUWMMRMTEw8Pjzlz5tjb29OeWFtbZ2ZmKl7269ev9/f3l22mvnr1atOmTfTnvXv3FKvru+++o6jN69ev+d9Ddu7cSets06aNRCJ58eKF7HRL7dq1q6ioaJTgBXUxoC/yjBkz3NzcqAGvpaUlG+rKzMzkJnWyt7efMWOGh4cH9TTR0NDg6qqkpIQu70WLFilusaCggHqLbNu2TXnwgjG2ePFidXV1FRUVBweHmTNncsmM+vbtK9ssl0gkNKGsQCBwcXHZtGmTt7e3bFhNQ0Nj69atyqsiMDCQwgra2tpDhgxZtGjRpEmTaCVubm5Kghe7d++mIzI0NBw9evTs2bOtra2p2JIlS2Q3wef+HxgYyEVevvrqq1WrVtHpUFNTU4zkytq7d6+GhkZsbKzsjaglzBoOAAAACF78BY4dO1Zb95Cm638xb9482oSpqen8+fP9/PwWLFjg6+s7Z86cCRMmUCoBdXV1Ly8v5T/sWkLwIjY2VrHNLJVKL1++TJ2olfvXv/5FH1d86sgFLxhja9as4Zo0r1+/psaGurp6VlYWVz4oKIgaXUFBQbLrOXToELVgZVvpSto8lEVV9vlqWFgYPZXt16+fYlPE2tpaX19///79L168yMvLe/nyJT2UpriDs7Pz8+fPuU8lJCTQPB2dOnUqLS2VC17QQQUHB3NtmIyMDMpo2KpVK9n1DB8+vEOHDidOnOBKFhUVOTg4UCf/qqoquZ1kjA0bNiwvL49bAzcQKSAg4N2DF3WeTRpK07lzZ27fON7e3tRuVH526lul2dnZtDN79uzheXkvXbqUQkg7d+6UXR4aGrp27VquqnlWPqFONAsXLlTcHP8rNjs7m3pwfPPNN1yx6urqSZMm0UbFYjG3/NKlS3Tgfn5+soGAixcvUvPb09NT8bK3trYWCoWrV69OSUkpKSmhb1ZeXh7FhmbMmKEYY6VrQDGGqBztM3W0uXPnTvv27d3c3H766SfuQj1+/HijBC8odhAZGcktP3v2LBc+4Ba+evXK2Ni4f//+9+/f5xY+ffqU6srHx4dbuHDhQgrWyF5m5Mcff6RoQn5+fp3BC8bYqFGj6EYhF2wKDg6WuzwYYxQ1I6WlpRS08vHxqbMDQnZ2Np2+fv36ZWRkcMsLCwu/+uorLiBVY/DiH//4h6am5rfffstFpqqrqxcsWEBfEC58wPP+T6P85KIeUVFRcjFcOdXV1ebm5hs2bKA/fXx8aFvp6en4FQUAAAAfY/DC1dW1tuCFq6trE22UG97cuXNn+/9ycHBwcnJydXWdNWtWcHCwbEOxYVJSUmJjYxMSEhLfio+Pf/LkiWyP3/T09ISEhNTU1PT09KSkJNkf0/yDF9TQEolENQ7iaJTgxa5du+Te+vnnn7lx8txvempKyf0+JuPGjaOJM/m0eTp06KD445geMss2JGSbIiEhIXLlZ8+eTc/bFQdKxMTE0KdkG6IUvBAIBL/++qtc+Xv37lH51atXcwslEoliFODcuXNUkuuwze2ku7u74ngBeq6roaFRUFDQ1MELriV29uxZ2eU5OTnUq//QoUPKz059q7S+wYvExETqYiMbzakRz8pXHryo1xVLiXtVVFRko3Xcd0QgEHB1UllZaWFhwRhzdHRUXO2uXbsU28NcU1/2AuNQe1VNTe3FixeyyykQIBQKlY8OU6w6bmxR//791dTUvvjii6qqKrFYzPWocnFxaZTgRffu3blQAoda/j179pRdKBv6kQsodOzYkVvCDXhR7L/Tu3dvuUiHkuDF1KlTq6urZT9OuY3krpMlS5bIjkORu1f07t27znqYM2eOYtxTUW3DRhSrJT8/n/rv7N+/n//9n5snWPaq4+PChQtCoZBCeKWlpXp6eoyxXr164ScUAAAANCdhy8m+8ccffzTgrXeRmZnJTUBw4sSJiP+6f/9+eHh4WFjY4cOHJ0+eXGPyv8TExGXLlnFNNeV8fHw++eST7t27W7zVt2/fYcOGcZ+trq52cHDo3r17165dO3fu3KtXL25mk3qhxkBVVZXss9PGRf32ZXFzKCYlJdGLBw8eUCYR+r0uh1rpsoPtlejZs6diJjlutX/++afcW+7u7iNGjJBbeObMGcbYrFmzFBPXWVtbu7u7y04lIzsWXTbpAHF0dKTe7w8ePJDN+qGYfLFXr170gmsLcQYPHqyYoo/6e5eVlcXFxTX1F23y5MnUOqVn1Jz9+/dXVla2bdv2008/Vb6GhlUpf2fOnBGLxQKBYMWKFcpL1rfya1SvK7a0tJS+a1yuR679TymECgoKaEl0dHRiYiJ1iFBc7dy5c6kFqFhRBgYGa9asUfzI0qVLBQJBZWUlFzEkFKgaO3ZsvaYFiY6OzsvLo9dPnjw5ceLEP//5TxUVFZFI5Ofnx40e4nmLU65z585yaSwYY2PGjOEyDcl+7xQ/Tic0KyurrKyMlvTu3Zv6fP373/+WLXn37l3aYQr01MnGxkY2kQQlVaGQE3dD40469ehRPOn5+fl1JpY6efIkfWW4IVT1olgtlD5G9iLnc/9v27YtfV/27NlTWVnJfwfU1NQ2btxImzh37lxRURH9X0PWMAAAAGhOwo/54G/evEkvtLS05HK21WnBggXbtm3jOj8rFxYW9vLlS3okOGvWLOoKTpkR6SkuNT8mT55M009wjYd6cXZ2ptZLQECAk5PTzz//nJub29R12KFDBxrIzbXZuOb31q1bZyo4deqUbJmGbZEa/+np6YpxAbkl2dnZJSUlNYZdZMMxiYmJEomEz9ZpQpM6563gEmQUFhbyX21953xpGJFItHjxYsbYjRs3uBMhkUgoD+Jnn32mfPqDRq9SRdQeMzExkQsQ8FTfyq/XFUuzcmRlZclFRuhmYmBgYGZmRktoshjGmJ2dneJGNTQ0aPILrhinX79+NQZMraysKDa3d+/eqqoqLmh47do1xphsmgw+uFmW2rRp8+eff3JDSBhjvr6+XGZNrgtPo6OKKi8v50ISyk8ojQmSvQNTeEW2Aims06dPH0dHx3fcMe6Gxp30O3fuyHby4k66YlBD8StD4Y/6/pfhUy3cRc7n/q+hoTF58mTKhWxpafndd98pXn41Gj58+JdffkmvDx48SP+2ZsyYgZ9QAAAA8JEGL5T83HyXX6J8ghdcJkj+PDw85s6dS0Ov+Xj9+jW1f+hBrqwLFy6sXr36119/DQ4OdnR0lE3IXy9aWloXL17s27cvZfXz8/Nr37792LFjm6jfSm3oiaVQKAwNDb2qIC4urk2bNnKTL9QXPXLMyMios2R8fDy9qG1WRVpeUVHBdahWjoYePH/+XG55QUHB3r17/fz8xo4da2dnxz3854mb00RxzU3B19eXRuBzgxcuXbqUnp6uqqr6+eefN3OV1rYJ/lN+vmPl1+uK7dChw+jRo6VS6eTJk+/cuVNVVVVeXn7+/Hm6FaxatYqbS4JnRXHF+KABFNnZ2dT5hYILUqnU2tpasaMQz7sfTcUidzV6enrS6yNHjsiGDJpHSkrK999/7+Pj4+7u3qNHDy4zkSwvLy/q+MN1vigqKqJIE89uF/yNHDmyU6dOKSkp3t7eNDansLDwhx9+2L17t0gkqrN/EBfn4n9JK6qsrDx37tzSpUsnT57s6OjYoUMHuSuH5/1/37591LUqLS1t3bp1PXr06Nev3/Hjx3nuRmZm5vXr1ymc0bBeJAAAAAAfQvBCSXeDhvVE4P/zXfGJfZ2WLl26f//+1q1b8ywfHh5eXV3NPcQjEolk3bp1//znP2/evFnftkeNevTocf/+/atXr86cOdPAwKC6uvrSpUtOTk5ynaubFP2iVVdXz8nJya1FWFjYu2zizZs3lEaxzpJcJwKxWFxjAW658u4Gsu1k7hi5cIOnp6exsfGCBQsOHTqUnJxsYGDg4uJSryPiHvM2T3vA0NBw1qxZsk1TesDu6ekpN1doM1SpIurLI/eUu0aNUvn1vWLPnDkzbty4x48fu7i4aGpqamlpjR8//uXLl/7+/l988UV9K6petTR8+HCaP4KG/JSXl1OmA0rKwJ9EIuGOSHaGEQ71zaGoK22ieYSHh7u4uJiZma1Zs+bs2bO5ubmdOnWqseuKuro6DfMJDAykERBHjx4tLS3V1dWdNm1a4+6VkZHRrVu3zM3Nz549a25urqGhYWBgsHz5ch0dnX379nEzpNT5leFzSdd4naxYscLExGTixIk7d+589OiRSCQaMGCA4g2Qz/1fR0fnxIkTUVFRy5cvp5BcRETE9OnTKTNInQIDA6lHFcaMAAAAwEcdvJg2bVptU6U2+o9RerjHjTsYMmRIUx/d77//Th2/udZpQUHB2LFjCwsLb9y4UWeLsR5nVCgcPnx4YGDgixcvjh071rZtW5qfnxr8zYDmmygrK1Mc1tEoysvLX758SaPTee6Mkm4atFxbW5vnCAXKPcklF0hNTR04cODp06etra3Pnj2bl5f35MmTmzdv0hAM/riUlvVKW/AuKIECNU1TUlJCQkJ4Dj1o9CpVRB0B6uy40ViVX98r9tmzZykpKd26dVuxYsX48eOnT5++YcOGmJiYHTt2cN0u+FcUV4wPgUBA87Dcvn07MjIyKCgoPz/f0NCwvh34Hz16xA03qDF44eTkxA0LojS9zXBNhoSEDBs27M6dO1OnTv3999/z8vKioqKuXr1aW2jms88+owyplKKVxoxMnz69xkE37yguLi4/P3/IkCFLliwZNWqUn5/f9u3bk5KSKHktn+uZzyVd4+3Ow8Nj69atKioqe/fuzc7OTklJuXPnzunTp6kDWsPu/zY2Nlu2bElNTQ0LC6NhL0ePHqXxR8pRJIy6B0sAABAKSURBVEtfX9/Dw4O+NTwzywAAAAB8UMELxtiWLVuOHTvm6uqq/parq+uxY8e2bNnSFNu6ceMGvdDV1aWutjylp6cfOHCgvj9D6TknFyWJjIx0dXWdPn36zp076ztihSc1NbVp06bR1BKVlZU8c2Q2VvCCMcbnp3CDw0A8W31GRkbUO+bWrVs1Frh9+zb/BmReXh6lA+TKb9++PTU1tUePHuHh4RMmTGhwq4k6AQmFQj4RmcY6TTSC6d///veBAwekUmn//v1pFtXmrFIll1B+fr7ybJGNVfn1vWKnTZsWGxt7+PDhzZs3/+c//wkMDFy7di11iKhxtTVW1KtXryjdQH0ratasWTSF8763GGPz5s2jQUANuPt16NDB3Ny8xjLcmLikpKTLly83wzX55ZdflpWVzZ07NygoaNCgQTUm75RlYWFBHdYOHDgQERFBt7hGHzNCX3wvL6/y8vIrV67s3Lnz7Nmze/bsWbp0qWLC2hq1bt2aSnJ5Rvi7cuVKSEiIUCi8f//+Z599xj/MzfP+7+rqevPmTbp+ZPMQ1/bVpjFWU6dOpe5RmzZtUhwICQAAAPBRBC+oYRAWFlb+VlhYWFP0uSA0cJcSXihOWFCbU6dODR8+PCQkpGvXrtRI46OsrOz+/fvcmJHAwEBnZ+d27dpNnz69qeuTm62jzsfgXCUUFxe/yxbNzMxoCpL169fX2N2Dctfx8fLlSy4xIWfTpk10OOPHj+ezEhpzdPr0aZr6Qe4aoN/riuOSJBJJTk6O3MJdu3aVlZUJhUKugUTjyZ2cnOQ6/3M9KRQ9e/ZMbklRURE9NJ4yZUqbNm0a5bzzOZuUQCE2Npa6KvAfetCwKuVv0qRJWlpajLFvv/1WyfVT38qnOlGskHpdsVKpNDIykkInf/75p2xaRzm9evWiASxbt25VHDmyY8eO8vJykUhEE83wp6GhQXUbFBQUHh6uoqKyaNGiBt/9lExQPW3aNJoPhfa2qe9UZWVl1MCuMe1ubZ+iFC03b96krA3Ozs42NjaNvm8JCQmvX78uLS3duHFjfHx8A3qx0Sits2fPKgYRxGKx4l2OQxd5l7dkl79+/ZpPLhI+938tLS0Kh9X5P4JLjUH9TSoqKvbs2fP111/jhxQAAAB8pMGL5lFdXc39fB80aBDPTz179mzBggW3bt3629/+Rv1seX4wPDy8srJSIBA4OzsvWrRo/fr1ZWVlN27caNwOt5GRkUOGDLlw4QK3pKqqasOGDfSIUnHOUTlcMrnAwEDKSMclBayv7du3q6mpPXv2zNnZmVp6JDo6esaMGbq6upRXj88RjR49+tWrV/RnSUmJt7c3dVL46quv6NFfndasWWNqalpdXT1kyJDw8HCuCXrx4kXKStivXz/FBmR1dfWAAQO4na+urt6yZcvf//53CjHQhKmMMXpqHRYWJtu+vXz5spKnkbt37161ahU3+j0pKcnV1bWoqEgoFK5bt66xLgY+Z9Pd3Z0O5NWrV8bGxlOmTOG58oZVKX/t27dfvXo1xUfmz5/PtRXz8/P9/f319fVpkEt9K5/q5MqVKzQLw7Vr17gBHfyvWIFAQB3mT58+3b9/f0NDQxUVFX19fSsrq9GjR+/cuVN24swff/xRKBQmJycPHTo0KyuLFlZWVm58i8bpKHbZqNOiRYtEIlFeXp5UKvXw8KhvGsiysjKuM4iS/CCamppcgp5r167JVktT0NDQoMazbC8P+t6tXLmytk95eHiYmJhw08c2RbcLup5pduFvv/3WyspKW1tbTU2tdevWdnZ2n3766YkTJyifkRLr1q1r3bq1VCodMWKEbP+La9euWVlZDRs2rLb4BV3k6enpsjM0PX36dPDgwXLJffnc/0tKSkaNGrVt2zbZeVL379+flZWlpqZW579CGlfVunVrJycnmnakXbt2fAbOAAAAADQO6cfk/Pnz/v7+c+bM4aYppdG/8+fPDwgIuHz5svKPr127dsWKFVKplBqZmzdv5rndtWvXUg5/Z2fnDRs2FBYWUhf3gICABh9LcnIy7f/Ro0dpyapVq2hJjx49KAEbzfmnqqr6xx9/1LnCoqKitm3b0hqMjY01NDRojIZUKr169Sotv3PnjuIHKYjwxRdfyC48efKkvr4+fcrIyMje3p5buYGBQVhYmPKdoR/HJiYm2traAoGge/fu1tbWXG+CpUuXVlVVcYW5QMA//vGPGtf2xx9/cLkkDA0N+/bty82m2adPn/j4eNnC1Gw2NzcfNmwY7UO/fv2oNijUlZubyxW+fv26UCikXBWLFi1asGABJRdcunSptbU1Y+zEiRNyO+nj49OqVSs1NbU+ffpwzU51dfWDBw8qP79SqfSHH37g4jjcQpq0Yv78+TzPpiwuPcT69evrdfnVq0q5J+d79uzhuf6ysjIvLy/6lEgksrW1tba2ph4WhoaG9+7dq1flE+qzQ98IqrShQ4c24IpNS0szMjLS1tY2NTVVnBvIwsIiMzOTK3zw4EFuPEvHjh1tbW25j3z66aey55G77N3d3eusH29vb1rJb7/9xrNKg4KClixZ4uPjQ5VDHBwcfH19AwIC5Naza9euCRMmyA7c0NHRcXd39/f3p6AJT0qOiDsdb968oSXffPMNLXFycgoICPDx8aF5kXfu3EnLnz9/rrge7sm/kZFRWVmZYgFKRWlubs7zjkHBLwcHB7n/HQKBoO1bij31xo4dKxaLlVfF1atXuWEmbdq0GTBgAJf/aNasWfTxhw8f0pILFy7Qp3Jycug61NXVnTNnjr+//7Bhw1RUVD755BNKdOLn58f//n/hwgUapWhsbDxp0qR58+ZxM3lt27atzrNJ86p07dpVIpEcOXJEJBKFhIRIAQAAAJrLxxW8cHNz09DQ0NbW1tXV1X9LW1tbQ0NDJBIJhcJ9+/Yp//ilS5devnwpkUi6dOmioqLy7NkzntuldPRGRkahoaG0hHo76+vrcz/c3z14Qb9NBwwYwA2AV1dXHzt27JMnT3iu8+bNm1wHDS0treXLl1PjqgHBC6lUmpWV5e3tTU8siZmZ2ZdffpmTk8O/zRMfHz9kyBBqe6urqw8YMODIkSNyhesMXkil0tevXwcEBFhZWVHDQ1VVtXfv3uvXr6+srJQrScELf3//ysrKJUuW0P4LBAJLS8svv/xSsfzRo0dlM+c5OjpSFOzrr78WCARywQtDQ8OkpKRbt25xgx10dXWHDh1KrfFGDF4oOZuyKMuDmppajc1C5fhXaQOCF2T//v22trZcE9rY2Hjx4sWy3zuelc+dgnnz5nE5Ne3s7ORa7Hyu2PPnz1OWnLS0NC7OkpmZGRYWFhAQQFWxYcMG2dWmpKR4enpyq9XW1nZycgoKCqpXU7/GeKitrS3/yrSzs9PU1OTufnp6etzdT0VFJTg4WLawjo6Onp5emzZtjI2NTUxM2rVrp6enp66urqOjU1RU1ETBi6qqqlWrVnGBQjU1tYkTJ9Lti7KK1niVZmRkULUvX768xn149+DFunXrhELhrFmzuF0tKSlJSUm5cOHC2LFjaVW3b9+uszays7OnTJnCBcVUVFQGDhwoW/OKwQupVHr//n3ZrEzdunXbsWNHVVVVSEiImpoaF7zgef+PjY318PDgwihCodDOzu7KlSt8zubLly8HDx4sEAi0tLTU1NT279+Pn1AAAACA4EWLRrnuRo0axb+NRw+7zp07xy18/Pgx/XasM2LSANXV1QkJCcnJybLdE/h/NiUlJSEhobq6urH2Jy8vLy4u7tWrVw1u81RVVSUmJlZUVLz7zpSWlsbHx9drVVlZWfn5+corLTU1NTIysl7HWFFRkZSU1Ij13ICzOWrUKMbYjBkzmrlK66WsrCw2NjYrK0sikbx75RcVFUVFRRUWFjbsirW1taWElzV+kJqyM2fOrPHd/Pz85OTkGo+iXt68eUONz6a4e/zlKioq4uLiYmNjX79+3RL2Jzc3VyQSaWlpFRcXK74rFoupw069WvKZmZmUR4P/R168ePHw4UPZTj3veP/Pzs5+/PhxjX1VlEtLS7t06VK9OuAAAAAANApB88yB9yGZPXt2YGBgUFDQ1KlT+ZQPDQ0dMWKEUCjMy8vj+qVT8s7ffvvN1ta2qQeTv4+cnZ3v3r3r7u4eGhqK2mgiMTExlN3w/v37NF0iKFdcXEw5LKOiohQTQ0qlUnt7+8jIyJ9++om6VjWRXbt2LVmyxMjIKCsri2fmF2iwCxcuUFeF3NxcGqYki/LFVFdXx8XFNSB9CQAAAADwJ0QV1EtJSUlwcLC+vv748ePfvHlDnbeVo54aNjY2spELyrpHrSBu7k+A5rR582bG2IABAxC54ElXV7dPnz6UpODRo0eyb6Wnp8+cOTMyMtLCwoJnWLNhqqurafSQr68vIhfNwMnJSU1NLS8vb+bMmXLznty/f3/EiBHV1dWTJ09G5AIAAACgqSF4UT/nzp0rLS2dMmVKq1atAgMDufHzjLG///3v48ePj4+Pl/sITWtCY7ZlTZgwgRK2NcM0hAByMjMzg4KCKL0laoO/kydPGhsbR0VF2dvbd+7ceeDAgYMHD7a0tOzateuxY8csLCwuXbpEE082kf/85z+pqakikWjhwoU4Hc2gdevWQUFBIpHo+PHjnTp16tGjh5ubm7Ozc8eOHR0dHR8+fDhq1KgjR46gogAAAACamghVUC9isZhGfDx//vzQoUO//vorLS8uLqb5Ghhjv/zyC82B//Lly8TERMrBZmxs/OzZM319fS6bWnp6eufOnV+8eHHu3LmgoKCBAwdS5kVgjFlaWlZUVFhYWKAqmsilS5d69+5tYWFBk5sCTxYWFk+fPj1w4MDRo0dTU1NpplhjY+NBgwb5+PjMnDlTdoaOpnD9+vW+fftOmjQJt4tmM3HixISEhN27d1+8eDEjI+Pp06cqKiqdOnUaN27csmXLBg8ejCoCAAAAaAbIeVE/JSUlffr0oQSc27Zto7z0xNfX98yZM1paWhkZGZS07z//+Y/wLYlEUl1dXVVVtXnz5mXLljHGwsLC3N3dNTQ0aBB1eXk5Yyw3N5ebbxIAWr7Xr1/TBCKoio9HQUEBTbeBqgAAAABoTghe1JtUKo2JienatatioKGiosLMzCwrKwu1BAAAAAAAANBYMGyk3gQCgeJEA+T+/fvOzs6oIgAAAAAAAIBGhISdjWnHjh1TpkxBPQAAAAAAAAA0IgQvGs2xY8fevHmD9IcAAAAAAAAAjQvBi0ajpaV16NAh1AMAAAAAAABA40LCTgAAAAAAAABo0dDzAgAAAAAAAABaNAQvAAAAAAAAAKBFQ/ACAAAAAAAAAFo0BC8AAAAAAAAAoEVD8AIAAAAAAAAAWjQELwAAAAAAAACgRUPwAgAAAAAAAABaNAQvAAAAAAAAAKBFQ/ACAAAAAAAAAFo0BC8AAAAAAAAAoEVD8AIAAAAAAAAAWjQELwAAAAAAAACgRfv/AgAA//+iIbrB8+l3JgAAAABJRU5ErkJggg==)"
]
},
{
"cell_type": "code",
"metadata": {
"id": "xi4VLvQrE4jV"
},
"source": [
"# There are various method to calculate the Gini Impurity for a list of rows\r\n",
"# Define a function to calculate Gini Impurity\r\n",
"def gini(rows):\r\n",
" counts = class_counts(rows)\r\n",
" impurity = 1\r\n",
" for lbl in counts:\r\n",
" prob_of_lbl = counts[lbl] / float(len(rows))\r\n",
" impurity -= prob_of_lbl**2\r\n",
" return impurity"
],
"execution_count": 43,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "6bXExSkmFX0K",
"outputId": "7c927e94-dc7c-4a38-a582-70b941a3d168"
},
"source": [
"# Test the gini() function with un-mixed list of data\r\n",
"test_gini_unmixed = [['Apple'],['Apple']]\r\n",
"gini(test_gini_unmixed) # Should be 0, because the no different data"
],
"execution_count": 44,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"0.0"
]
},
"metadata": {
"tags": []
},
"execution_count": 44
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "WTSUYqlCGU4q",
"outputId": "215d4500-2c38-4dd9-bff6-d3f707aa0ae3"
},
"source": [
"# Test the gini() function with mixed list of data\r\n",
"test_gini_unmixed = [['Lemon'],['Apple']]\r\n",
"gini(test_gini_unmixed) # Should be 0.5, because there two type of data"
],
"execution_count": 48,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"0.5"
]
},
"metadata": {
"tags": []
},
"execution_count": 48
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "1VUvfNY2HJvy"
},
"source": [
"Information gain is the reduction in entropy or surprise by transforming a dataset and is often used in training decision trees. Information gain is calculated by comparing the entropy of the dataset before and after a transformation\r\n",
"\r\n",
"Read More : Gini Index and Information Gain | Entropy and Information Gain [Click](http://www.learnbymarketing.com/481/decision-tree-flavors-gini-info-gain/#:~:text=Summary%3A%20The%20Gini%20Index%20is,of%20each%20class%20from%20one.&text=Information%20Gain%20multiplies%20the%20probability,2)\r\n",
"\r\n",
"The uncertainty of the starting node, minus the weighted impurity of two child nodes.\r\n",
" "
]
},
{
"cell_type": "code",
"metadata": {
"id": "8eShByF9HJcK"
},
"source": [
"# Define the function to calculate the Information Gain of the tree\r\n",
"def info_gain(left, right, current_uncertainty):\r\n",
" p = float(len(left)) / (len(left) + len(right))\r\n",
" return current_uncertainty - p * gini(left) - (1 - p) * gini(right)"
],
"execution_count": 51,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "1pcHOWuaIoqS",
"outputId": "d91ce0e3-c875-47fb-96ec-8fc96760d7df"
},
"source": [
"# Check the all function\r\n",
"current_uncertainty = gini(training_data)\r\n",
"print('Gini Impurities : ',current_uncertainty)"
],
"execution_count": 52,
"outputs": [
{
"output_type": "stream",
"text": [
"Gini Impurities : 0.7222222222222221\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "J5SV2tvuJ_Dy",
"outputId": "89926112-b41d-40be-d1e9-7fdfc282eb45"
},
"source": [
"# Data Gaining when partitioning on GREEN\r\n",
"true_rows, false_rows = partition(training_data, Question(0, 'Green'))\r\n",
"print('Information Gain of GREEN : ',info_gain(true_rows, false_rows, current_uncertainty))"
],
"execution_count": 53,
"outputs": [
{
"output_type": "stream",
"text": [
"Information Gain of GREEN : 0.12222222222222223\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Bf03cs94KBzg",
"outputId": "235fb6b3-5961-4a76-e2a8-1a35ec329fa7"
},
"source": [
"# Data Gaining when partitioning on RED\r\n",
"true_rows, false_rows = partition(training_data, Question(0, 'Red'))\r\n",
"print('Information Gain of RED : ',info_gain(true_rows, false_rows, current_uncertainty))"
],
"execution_count": 54,
"outputs": [
{
"output_type": "stream",
"text": [
"Information Gain of RED : 0.30555555555555536\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "OMeCB_vqKE4h",
"outputId": "85d2f4c5-29f5-4589-cf15-2f48dcd96f9d"
},
"source": [
"# It looks like we learned more using 'Red' (0.37), than 'Green' (0.14).\r\n",
"# Why? Look at the different splits that result, and see which one\r\n",
"# looks more 'unmixed' to you.\r\n",
"true_rows, false_rows = partition(training_data, Question(0,'Red'))\r\n",
"\r\n",
"print('TRUE Dataset : ',true_rows)\r\n",
"print('FALSE Dataset : ',false_rows)"
],
"execution_count": 55,
"outputs": [
{
"output_type": "stream",
"text": [
"TRUE Dataset : [['Red', 1, 'Grape'], ['Red', 1, 'Grape']]\n",
"FALSE Dataset : [['Green', 3, 'Apple'], ['Yellow', 3, 'Apple'], ['Blue', 2, 'Blueberry'], ['Yellow', 3, 'Lemon']]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "R095r-iEK0I3"
},
"source": [
"#Define a model to find the best question to ask by iterating over every feature / value and calculating the information gain\r\n",
"def find_best_split(rows):\r\n",
" best_gain = 0 # keep track of the best information gain\r\n",
" best_question = None # keep train of the feature / value that produced it\r\n",
" current_uncertainty = gini(rows)\r\n",
" n_features = len(rows[0]) - 1 # number of columns\r\n",
"\r\n",
" for col in range(n_features): # for each feature\r\n",
"\r\n",
" values = set([row[col] for row in rows]) # unique values in the column\r\n",
"\r\n",
" for val in values: # for each value\r\n",
"\r\n",
" question = Question(col, val)\r\n",
"\r\n",
" # try splitting the dataset\r\n",
" true_rows, false_rows = partition(rows, question)\r\n",
"\r\n",
" # Skip this split if it doesn't divide the\r\n",
" # dataset.\r\n",
" if len(true_rows) == 0 or len(false_rows) == 0:\r\n",
" continue\r\n",
"\r\n",
" # Calculate the information gain from this split\r\n",
" gain = info_gain(true_rows, false_rows, current_uncertainty)\r\n",
"\r\n",
" # You actually can use '>' instead of '>=' here\r\n",
" # but I wanted the tree to look a certain way\r\n",
" if gain >= best_gain:\r\n",
" best_gain, best_question = gain, question\r\n",
"\r\n",
" return best_gain, best_question"
],
"execution_count": 57,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "57Zz1xciLbxK",
"outputId": "001628a8-e1fa-46be-defb-d9801d10e94a"
},
"source": [
"#Test the Function\r\n",
"best_gain, best_question = find_best_split(training_data)\r\n",
"best_question"
],
"execution_count": 58,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"Is diameter >= 2 ?"
]
},
"metadata": {
"tags": []
},
"execution_count": 58
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "KzCJKJLWLo-g"
},
"source": [
"# Create two classes as Leaf and Decision_Node. Leaf classify the data and Decision_Node ask thwe quecction\r\n",
"\r\n",
"# Lead Class\r\n",
"class Leaf:\r\n",
" def __init__(self, rows):\r\n",
" self.predictions = class_counts(rows)\r\n",
"\r\n",
"# Decision_Node Class\r\n",
"class Decision_Node:\r\n",
" def __init__(self,\r\n",
" question,\r\n",
" true_branch,\r\n",
" false_branch):\r\n",
" self.question = question\r\n",
" self.true_branch = true_branch\r\n",
" self.false_branch = false_branch"
],
"execution_count": 59,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "wm0Yn3bJMIro"
},
"source": [
"# Define Function to build the tree\r\n",
"def build_tree(rows):\r\n",
"\r\n",
" # Try partitioing the dataset on each of the unique attribute,\r\n",
" # calculate the information gain,\r\n",
" # and return the question that produces the highest gain.\r\n",
" gain, question = find_best_split(rows)\r\n",
"\r\n",
" # Base case: no further info gain\r\n",
" # Since we can ask no further questions,\r\n",
" # we'll return a leaf.\r\n",
" if gain == 0:\r\n",
" return Leaf(rows)\r\n",
"\r\n",
" # If we reach here, we have found a useful feature / value\r\n",
" # to partition on.\r\n",
" true_rows, false_rows = partition(rows, question)\r\n",
"\r\n",
" # Recursively build the true branch.\r\n",
" true_branch = build_tree(true_rows)\r\n",
"\r\n",
" # Recursively build the false branch.\r\n",
" false_branch = build_tree(false_rows)\r\n",
"\r\n",
" # Return a Question node.\r\n",
" # This records the best feature / value to ask at this point,\r\n",
" # as well as the branches to follow\r\n",
" # dependingo on the answer.\r\n",
" return Decision_Node(question, true_branch, false_branch)"
],
"execution_count": 60,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "1uCt6HnFMfZF"
},
"source": [
"# Define functionto print the tree\r\n",
"def print_tree(node, spacing=\"\"):\r\n",
"\r\n",
" # Base case: we've reached a leaf\r\n",
" if isinstance(node, Leaf):\r\n",
" print (spacing + \"Predict\", node.predictions)\r\n",
" return\r\n",
"\r\n",
" # Print the question at this node\r\n",
" print (spacing + str(node.question))\r\n",
"\r\n",
" # Call this function recursively on the true branch\r\n",
" print (spacing + '--> True:')\r\n",
" print_tree(node.true_branch, spacing + \" \")\r\n",
"\r\n",
" # Call this function recursively on the false branch\r\n",
" print (spacing + '--> False:')\r\n",
" print_tree(node.false_branch, spacing + \" \")"
],
"execution_count": 65,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "rD-AxWKCMq-Z"
},
"source": [
"my_tree = build_tree(training_data)"
],
"execution_count": 62,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "nBBZ21UuM0JS",
"outputId": "a8f07c9e-427f-4439-f5c8-4d1bf5f2d531"
},
"source": [
"print_tree(my_tree)"
],
"execution_count": 66,
"outputs": [
{
"output_type": "stream",
"text": [
"Is diameter >= 2 ?\n",
"--> True:\n",
" Is diameter >= 3 ?\n",
" --> True:\n",
" Is colors == Yellow ?\n",
" --> True:\n",
" Predict {'Apple': 1, 'Lemon': 1}\n",
" --> False:\n",
" Predict {'Apple': 1}\n",
" --> False:\n",
" Predict {'Blueberry': 1}\n",
"--> False:\n",
" Predict {'Grape': 2}\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "gXMALkx-NSdp",
"outputId": "29a5feef-0f52-4966-d16a-912d37244814"
},
"source": [
"# Define a function to get the all rules of recursion\r\n",
"def classify(row, node):\r\n",
"\r\n",
" # Base case: we've reached a leaf\r\n",
" if isinstance(node, Leaf):\r\n",
" return node.predictions\r\n",
"\r\n",
" # Decide whether to follow the true-branch or the false-branch.\r\n",
" # Compare the feature / value stored in the node,\r\n",
" # to the example we're considering.\r\n",
" if node.question.match(row):\r\n",
" return classify(row, node.true_branch)\r\n",
" else:\r\n",
" return classify(row, node.false_branch)\r\n",
"\r\n",
"# Test\r\n",
"classify(training_data[0], my_tree)"
],
"execution_count": 67,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"{'Apple': 1}"
]
},
"metadata": {
"tags": []
},
"execution_count": 67
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "igWCuRQJNm8L",
"outputId": "fd8d4f61-9fc3-4231-fe16-b4265dce0925"
},
"source": [
"# Define function to get the probability of decision\r\n",
"def print_leaf(counts):\r\n",
" total = sum(counts.values()) * 1.0\r\n",
" probs = {}\r\n",
" for lbl in counts.keys():\r\n",
" probs[lbl] = str(int(counts[lbl] / total * 100)) + \"%\"\r\n",
" return probs\r\n",
"\r\n",
"# Test 1\r\n",
"print_leaf(classify(training_data[0], my_tree))"
],
"execution_count": 69,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"{'Apple': '50%', 'Lemon': '50%'}"
]
},
"metadata": {
"tags": []
},
"execution_count": 69
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "UVFh9XtjN-Uy",
"outputId": "d0d0f5d7-9a0b-4418-809b-26067b320895"
},
"source": [
"# Test 2\r\n",
"print_leaf(classify(training_data[1], my_tree))"
],
"execution_count": 70,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"{'Apple': '50%', 'Lemon': '50%'}"
]
},
"metadata": {
"tags": []
},
"execution_count": 70
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Tpq-btm8OJfB",
"outputId": "7dd804ea-a953-4f88-dccd-f890a378ce0a"
},
"source": [
"# Testing the Model \r\n",
"testing_data = [\r\n",
" ['Green', 3, 'Apple'],\r\n",
" ['Yellow', 4, 'Apple'],\r\n",
" ['Red', 2, 'Grape'],\r\n",
" ['Red', 1, 'Grape'],\r\n",
" ['Yellow', 3, 'Lemon'],\r\n",
"]\r\n",
"\r\n",
"for row in testing_data:\r\n",
" print (\"Actual: %s. Predicted: %s\" %\r\n",
" (row[-1], print_leaf(classify(row, my_tree))))"
],
"execution_count": 71,
"outputs": [
{
"output_type": "stream",
"text": [
"Actual: Apple. Predicted: {'Apple': '100%'}\n",
"Actual: Apple. Predicted: {'Apple': '50%', 'Lemon': '50%'}\n",
"Actual: Grape. Predicted: {'Blueberry': '100%'}\n",
"Actual: Grape. Predicted: {'Grape': '100%'}\n",
"Actual: Lemon. Predicted: {'Apple': '50%', 'Lemon': '50%'}\n"
],
"name": "stdout"
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment