{ "cells": [ { "cell_type": "markdown", "id": "2fac1987", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "# Genetic Algorithm Basics\n", "## Genetic Algorithm Implementation\n" ] }, { "cell_type": "markdown", "id": "6a10fbb9", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "Solve a 10-bit binary string optimization problem, in which you need to find the binary string with the largest total bit value." ] }, { "cell_type": "markdown", "id": "ac3d7450", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "## Population" ] }, { "cell_type": "code", "execution_count": 39, "id": "5af23611", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "import numpy as np\n", "\n", "# Set a seed so that the results are consistent over sessions\n", "np.random.seed(3)" ] }, { "cell_type": "code", "execution_count": 40, "id": "2f865c0d", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "def create_binary_population(pop_size, bit_length):\n", " '''\n", " Arguments:\n", " pop_size -- number of individuals inside population\n", " bit_length -- the number of bits of each individual (e.g. [[1010], [1101]])\n", " \n", " Return:\n", " binary_pop -- population array of shape (pop_size, bit_length)\n", " '''\n", " binary_pop = np.random.randint(2, size=(pop_size, bit_length))\n", " return binary_pop" ] }, { "cell_type": "code", "execution_count": 41, "id": "26ef1646", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Population:\n", " [[0 0 1 1 0 0 0 1 1 1]\n", " [0 1 1 1 0 1 1 0 0 0]\n", " [0 1 1 0 0 0 1 0 0 0]\n", " [0 1 0 1 1 0 1 0 0 1]\n", " [1 0 0 1 0 1 0 1 1 1]\n", " [1 0 1 0 0 1 1 1 0 0]\n", " [0 1 0 0 0 1 0 0 1 1]\n", " [0 0 1 1 1 0 1 1 1 1]\n", " [1 1 0 1 0 0 1 1 0 1]\n", " [0 0 0 0 0 1 1 0 1 1]\n", " [1 0 0 1 1 0 1 0 0 0]\n", " [0 0 0 0 0 0 1 0 0 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 0 0 0 0 0 0 1 1 0]\n", " [0 0 1 0 1 1 1 0 0 1]\n", " [0 1 0 1 1 0 0 1 0 0]\n", " [1 1 1 1 1 0 0 0 0 0]\n", " [1 1 1 0 0 0 0 0 0 1]\n", " [0 1 0 0 0 1 0 1 1 1]]\n" ] } ], "source": [ "pop_size, bit_length = 20, 10\n", "binary_population = create_binary_population(pop_size, bit_length)\n", "population_size = len(binary_population)\n", "print(\"Population:\\n\", binary_population)" ] }, { "cell_type": "markdown", "id": "c7d8e8ad", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "## Fitness" ] }, { "cell_type": "code", "execution_count": 42, "id": "074744c0", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "def fitness_binary_individual(individual):\n", " '''\n", " Return:\n", " fitness -- fitness values, based on number of 1s bits (e.g. [1001] -> 2)\n", " '''\n", " return np.sum(individual)" ] }, { "cell_type": "code", "execution_count": 43, "id": "34dec8ba", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "fitness = np.apply_along_axis(fitness_binary_individual, 1, binary_population)" ] }, { "cell_type": "code", "execution_count": 44, "id": "e51f4079", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "data": { "text/plain": [ "(20,)" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fitness.shape" ] }, { "cell_type": "code", "execution_count": 45, "id": "a347b902", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Population:\n", "[[0 0 1 1 0 0 0 1 1 1]\n", " [0 1 1 1 0 1 1 0 0 0]\n", " [0 1 1 0 0 0 1 0 0 0]\n", " [0 1 0 1 1 0 1 0 0 1]\n", " [1 0 0 1 0 1 0 1 1 1]\n", " [1 0 1 0 0 1 1 1 0 0]\n", " [0 1 0 0 0 1 0 0 1 1]\n", " [0 0 1 1 1 0 1 1 1 1]\n", " [1 1 0 1 0 0 1 1 0 1]\n", " [0 0 0 0 0 1 1 0 1 1]\n", " [1 0 0 1 1 0 1 0 0 0]\n", " [0 0 0 0 0 0 1 0 0 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 0 0 0 0 0 0 1 1 0]\n", " [0 0 1 0 1 1 1 0 0 1]\n", " [0 1 0 1 1 0 0 1 0 0]\n", " [1 1 1 1 1 0 0 0 0 0]\n", " [1 1 1 0 0 0 0 0 0 1]\n", " [0 1 0 0 0 1 0 1 1 1]]\n", "Fitness:\n", "[5 5 3 5 6 5 4 7 6 4 4 1 6 6 2 5 4 5 4 5]\n" ] } ], "source": [ "print(f'Population:\\n{binary_population}\\nFitness:\\n{fitness}')" ] }, { "cell_type": "markdown", "id": "e0574f01", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "## Selection" ] }, { "cell_type": "markdown", "id": "44f74ffb", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "Use `Roulette Wheel Selection` to select parents based on their fitness. The probability of selecting an individual is proportional to its fitness." ] }, { "cell_type": "code", "execution_count": 46, "id": "69c40fba", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "def rouletter_wheel_selection(population, fitness):\n", " '''\n", " Return:\n", " selected_population -- selected individuals based on fitness\n", " '''\n", " # fitness = fitness.ravel()\n", " fitness_sum = np.sum(fitness)\n", " if fitness_sum == 0:\n", " raise ValueError(\"Total fitness is zero, cannot perform selection.\")\n", " probabilities = fitness / fitness_sum\n", " selected_indices = np.random.choice(len(population), size=len(population), p=probabilities)\n", " selected_population = population[selected_indices]\n", " return selected_population" ] }, { "cell_type": "code", "execution_count": 47, "id": "80180de7", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "selected_population = rouletter_wheel_selection(binary_population, fitness)" ] }, { "cell_type": "code", "execution_count": 48, "id": "f0062a4d", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Population:\n", "[[0 0 1 1 0 0 0 1 1 1]\n", " [0 1 1 1 0 1 1 0 0 0]\n", " [0 1 1 0 0 0 1 0 0 0]\n", " [0 1 0 1 1 0 1 0 0 1]\n", " [1 0 0 1 0 1 0 1 1 1]\n", " [1 0 1 0 0 1 1 1 0 0]\n", " [0 1 0 0 0 1 0 0 1 1]\n", " [0 0 1 1 1 0 1 1 1 1]\n", " [1 1 0 1 0 0 1 1 0 1]\n", " [0 0 0 0 0 1 1 0 1 1]\n", " [1 0 0 1 1 0 1 0 0 0]\n", " [0 0 0 0 0 0 1 0 0 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 0 0 0 0 0 0 1 1 0]\n", " [0 0 1 0 1 1 1 0 0 1]\n", " [0 1 0 1 1 0 0 1 0 0]\n", " [1 1 1 1 1 0 0 0 0 0]\n", " [1 1 1 0 0 0 0 0 0 1]\n", " [0 1 0 0 0 1 0 1 1 1]]\n", "Fitness:\n", "[5 5 3 5 6 5 4 7 6 4 4 1 6 6 2 5 4 5 4 5]\n", "Selected Population:\n", "[[0 1 0 1 1 0 1 0 0 1]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 0 1 0 1 1 1 0 0 1]\n", " [0 1 0 0 0 1 0 1 1 1]\n", " [1 1 1 1 1 0 0 0 0 0]\n", " [1 0 0 1 1 0 1 0 0 0]\n", " [0 1 1 1 0 1 1 0 0 0]\n", " [1 1 0 1 0 0 1 1 0 1]\n", " [1 1 1 0 0 0 0 0 0 1]\n", " [0 0 1 0 1 1 1 0 0 1]\n", " [1 1 0 1 0 0 1 1 0 1]\n", " [1 1 0 1 0 0 1 1 0 1]\n", " [1 0 0 1 0 1 0 1 1 1]\n", " [0 1 0 1 1 0 1 0 0 1]\n", " [0 1 1 1 0 1 1 0 0 0]\n", " [1 1 1 1 1 0 0 0 0 0]\n", " [0 1 1 1 1 0 0 1 1 0]\n", " [0 1 0 1 1 0 1 0 0 1]\n", " [0 0 1 1 1 0 1 1 1 1]\n", " [0 0 1 1 0 0 0 1 1 1]]\n" ] } ], "source": [ "print(f'Population:\\n{binary_population}\\nFitness:\\n{fitness}\\nSelected Population:\\n{selected_population}')" ] }, { "cell_type": "markdown", "id": "5fab92bf", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "## Crossover" ] }, { "cell_type": "code", "execution_count": 49, "id": "27d90f1f", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "def one_point_crossover(parent1, parent2):\n", " '''\n", " Perform one-point crossover between two parents.\n", " \n", " Arguments:\n", " parent1 -- first parent binary array\n", " parent2 -- second parent binary array\n", " \n", " Return:\n", " child1 -- first child binary array\n", " child2 -- second child binary array\n", " '''\n", " point = np.random.randint(1, len(parent1)) # random integer from low(inclusive) to high(exclusive)\n", " child1 = np.concatenate((parent1[:point], parent2[point:]))\n", " child2 = np.concatenate((parent2[:point], parent1[point:]))\n", " return child1, child2" ] }, { "cell_type": "code", "execution_count": 50, "id": "370eefd5", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "parent_idx = np.array(np.random.choice(population_size, 2, replace=False))\n", "parent1, parent2 = selected_population[parent_idx]\n", "child1, child2 = one_point_crossover(parent1, parent2)" ] }, { "cell_type": "code", "execution_count": 51, "id": "3449ec1f", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Parent 1: [0 1 1 1 1 0 0 1 1 0]\n", "Parent 2: [0 1 0 0 0 1 0 1 1 1]\n", "Child 1: [0 1 1 1 0 1 0 1 1 1]\n", "Child 2: [0 1 0 0 1 0 0 1 1 0]\n" ] } ], "source": [ "print(f'Parent 1: {parent1}\\nParent 2: {parent2}\\nChild 1: {child1}\\nChild 2: {child2}')" ] }, { "cell_type": "markdown", "id": "9f587475", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "## Mutation" ] }, { "cell_type": "code", "execution_count": 52, "id": "e3e54e28", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "def bit_flip_mutation(individual, mutation_rate):\n", " random_probs = np.random.random(individual.shape)\n", " return np.where(random_probs < mutation_rate, 1-individual, individual)" ] }, { "cell_type": "code", "execution_count": 53, "id": "b2b629b1", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Child1: [0 1 1 1 0 1 0 1 1 1]\n", "Child1 Mutation: [1 0 0 0 1 0 1 0 0 0]\n" ] } ], "source": [ "mutation_rate = 0.9\n", "\n", "# child 1 mutation\n", "child1_mutation = bit_flip_mutation(child1, mutation_rate)\n", "# print(f'Random Probs used to mutate child: {random_probs}')\n", "print(f'Child1: {child1}\\nChild1 Mutation: {child1_mutation}')" ] }, { "cell_type": "markdown", "id": "748f92cc", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "source": [ "## Replacement and Termination" ] }, { "cell_type": "code", "execution_count": 54, "id": "465c6bc0", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "import numpy as np\n", "\n", "# Set a seed so that the results are consistent over sessions\n", "np.random.seed(3)\n", "\n", "def create_binary_population(pop_size, bit_length):\n", " '''\n", " Arguments:\n", " pop_size -- number of individuals inside population\n", " bit_length -- the number of bits of each individual (e.g. [[1010], [1101]])\n", " \n", " Return:\n", " binary_pop -- population array of shape (pop_size, bit_length)\n", " '''\n", " binary_pop = np.random.randint(2, size=(pop_size, bit_length))\n", " return binary_pop\n", "\n", "def fitness_binary_individual(individual):\n", " '''\n", " Return:\n", " fitness -- fitness values, based on number of 1s bits (e.g. [1001] -> 2)\n", " '''\n", " return np.sum(individual)\n", "\n", "def rouletter_wheel_selection(population, fitness):\n", " '''\n", " Return:\n", " selected_population -- selected individuals based on fitness\n", " '''\n", " # fitness = fitness.ravel()\n", " fitness_sum = np.sum(fitness)\n", " if fitness_sum == 0:\n", " raise ValueError(\"Total fitness is zero, cannot perform selection.\")\n", " probabilities = fitness / fitness_sum\n", " selected_indices = np.random.choice(len(population), size=len(population), p=probabilities)\n", " selected_population = population[selected_indices]\n", " return selected_population\n", "\n", "def one_point_crossover(parent1, parent2):\n", " '''\n", " Perform one-point crossover between two parents.\n", " \n", " Arguments:\n", " parent1 -- first parent binary array\n", " parent2 -- second parent binary array\n", " \n", " Return:\n", " child1 -- first child binary array\n", " child2 -- second child binary array\n", " '''\n", " point = np.random.randint(1, len(parent1)) # random integer from low(inclusive) to high(exclusive)\n", " child1 = np.concatenate((parent1[:point], parent2[point:]))\n", " child2 = np.concatenate((parent2[:point], parent1[point:]))\n", " return child1, child2\n", "\n", "def bit_flip_mutation(individual, mutation_rate):\n", " random_probs = np.random.random(individual.shape)\n", " return np.where(random_probs < mutation_rate, 1-individual, individual)" ] }, { "cell_type": "code", "execution_count": 55, "id": "e4bdf30b", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [], "source": [ "population_size = 20\n", "bit_length = 20\n", "n_generations = 70\n", "mutation_rate = 0.01\n", "\n", "fitnesses = []\n", "\n", "population = create_binary_population(population_size, bit_length)" ] }, { "cell_type": "code", "execution_count": 56, "id": "0d977f1b", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Best: 13\n", "Best: 14\n", "Best: 15\n", "Best: 15\n", "Best: 15\n", "Best: 17\n", "Best: 16\n", "Best: 14\n", "Best: 15\n", "Best: 15\n", "Best: 15\n", "Best: 15\n", "Best: 16\n", "Best: 15\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 17\n", "Best: 16\n", "Best: 16\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 16\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 18\n", "Best: 17\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 15\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 15\n", "Best: 15\n", "Best: 15\n", "Best: 16\n", "Best: 17\n", "Best: 17\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 16\n", "Best: 17\n", "Best: 17\n", "Best: 17\n", "Best: 16\n", "Best: 17\n", "Best: 15\n", "Best: 15\n", "Best: 15\n", "Best: 16\n" ] } ], "source": [ "for i in range(n_generations):\n", " fitness_values = np.apply_along_axis(fitness_binary_individual, 1, population)\n", " fitnesses.append(np.max(fitness_values))\n", " print(f'Best: {fitnesses[-1]}')\n", " \n", " selected_population = rouletter_wheel_selection(population, fitness_values)\n", " new_population = []\n", " \n", " while(len(new_population) < population_size):\n", " # Selection\n", " # Randomly choose 2 individuals from selected population\n", " parent_idx = np.array(np.random.choice(population_size, 2, replace=False))\n", " parent1, parent2 = selected_population[parent_idx]\n", " \n", " # Crossover\n", " child1, child2 = one_point_crossover(parent1, parent2)\n", " \n", " # Mutation\n", " child1_mutation = bit_flip_mutation(child1, mutation_rate)\n", " child2_mutation = bit_flip_mutation(child2, mutation_rate)\n", " \n", " # Add children to new population\n", " new_population.append(child1_mutation)\n", " new_population.append(child2_mutation)\n", " \n", " population = np.array(new_population[:population_size])" ] }, { "cell_type": "code", "execution_count": 57, "id": "906becb2", "metadata": { "tags": [ "hide-output", "scroll-output" ] }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGwCAYAAABcnuQpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAWl5JREFUeJzt3Xl8XGXZB/zfmTWZydJm6RKaLrRgKdBSFhFBKFKBwFtWWbTyFngQkaJIBQVRAbcCKqt9qAoC6vOCG1QWKVbtAihgsbUga2ulFVqSrpOZSWYyM+f9Y3KfmUlnOeuce5Lf9/PpB5KZJHcmc865zn1d93UrqqqqICIiIqpRHrcHQERERGQFgxkiIiKqaQxmiIiIqKYxmCEiIqKaxmCGiIiIahqDGSIiIqppDGaIiIiopvncHoDTMpkM3n//fTQ2NkJRFLeHQ0RERDqoqore3l50dHTA4yk/9zLsg5n3338fnZ2dbg+DiIiITNi6dSsmTJhQ9jnDPphpbGwEkH0xmpqaXB4NERER6RGJRNDZ2aldx8sZ9sGMSC01NTUxmCEiIqoxekpEWABMRERENY3BDBEREdU0BjNERERU0xjMEBERUU1jMENEREQ1jcEMERER1TQGM0RERFTTGMwQERFRTWMwQ0RERDWNwQwRERHVNFeDmTVr1mDevHno6OiAoihYtmxZwePRaBRXXXUVJkyYgPr6esyYMQNLly51Z7BEREQkJVeDmVgshlmzZmHJkiVFH1+0aBGWL1+OX/7yl3jjjTfwpS99CVdddRWeeOKJKo+UiIiIZOXqRpNdXV3o6uoq+fhf//pXLFiwAHPmzAEAXH755fjxj3+Ml19+GWeccUaVRklEI1H/QBoBrwceT+VN7ojIXVLXzHz0ox/FE088gffeew+qqmLlypV4++23cfLJJ5f8mkQigUgkUvCPiMiIeDKFj92+Ep++/0W3h0JEOkgdzNx7772YMWMGJkyYgEAggFNPPRVLlizB8ccfX/JrFi9ejObmZu1fZ2dnFUdMRMPBll1x9PQm8NLmXUilM24Ph4gqkD6YefHFF/HEE0/glVdewQ9/+EMsXLgQf/rTn0p+zQ033IC9e/dq/7Zu3VrFERPRcBBLpAEAqgrsjCVdHg0RVeJqzUw5fX19+NrXvobHH38cp59+OgBg5syZWL9+PX7wgx9g7ty5Rb8uGAwiGAxWc6hENMzEkynt/7sjCYxtqnNxNERUibQzMwMDAxgYGIDHUzhEr9eLTIbTvkTknFgiL5jp7XdxJESkh6szM9FoFBs3btQ+3rx5M9avX4+WlhZMnDgRJ5xwAq677jrU19dj0qRJWL16NX7+85/jjjvucHHURDTciTQTAPT0JlwcCRHp4Wows3btWpx44onax4sWLQIALFiwAA899BAeffRR3HDDDZg/fz527dqFSZMm4bvf/S6uuOIKt4ZMRCNALD/NxGCGSHquBjNz5syBqqolHx83bhwefPDBKo6IiKhwZoZpJiL5SVszQ0TkloKamQhnZohkx2CGiGgIppmIaguDGSKiIfJnZlgATCQ/BjNEREPEkoWrmcrV9hGR+xjMEBENkT8zk0xnsCc+4OJoiKgSBjNEREPE81YzAaybIZIdgxkioiHyC4ABLs8mkh2DGSKiIUSaqc6fPUWyCJhIbgxmiIiGEAXAk1vDAJhmIpIdgxkioiHEzMyUtsFgho3ziKTGYIaIKE8moyIuZmZEMMOaGSKpMZghIsoTH8itZNJmZphmIpIagxkiojzxwRSTRwEmtoQAsACYSHYMZoiI8kQHg5lwwIcxjUEAQHeEaSYimTGYISLKI+plwkEfxjTVAciubsrvCkxEcmEwQ0SURwQtoaAXDUEfQgEvANbNEMmMwQwRUR7R/Tcc8AEA2gdTTaybIZIXgxkiojyxhEgzZWdktLoZLs8mkhaDGSKiPLFE4czMmMZs3Qwb5xHJi8EMEVGeWF4BMJBLM7FmhkheDGaIiPJoMzMizdTENBOR7BjMEBHlGVoALNJMLAAmkheDGSKiPLml2SKYEY3zGMwQyYrBDBFRnvjgaqYGppmIagaDGSKiPGI7g9CQNNPu+ACSqYxr4yKi0hjMEBHlyW1nkJ2ZGVXvh9+rAAB6okw1EcmIwQwRUZ6hBcAej4K2BnYBJpIZgxkiojy5pdk+7XPcPZtIbgxmiIjy5LYzyAUz7aILMGdmiKTEYIaIKE8uzeTVPpdb0cRghkhGDGaIiPLEi8zMjNF2zmaaiUhGDGaIiAYlUxkk09nl16IAGOBmk0SyYzBDRDQoPphiAoBQMC/NxM0miaTGYIaIaJBomBfweeD35k6P7AJMJDcGM0REg7SGeXnFv0AuzbQjmkQ6o1Z9XERUHoMZIqJBxXrMAEBrQwCKAqQzKnbFkm4MjYjKYDBDRDRI6zETKAxm/F4PWkIBAOwCTCQjBjNERIO0HjNB7z6PtTeyboZIVgxmiIgGlUozAcCYJnYBJpIVgxkiokGxZPE0E5DfOI/BDJFsGMwQEQ0SMzOhImkmbjZJJC8GM0REg+KDwUxDsTQTG+cRSYvBDBHRoOjgaqZQsTQTa2aIpMVghohoULzIjtnCGK5mIpIWgxkiokHRcquZ8jabVFV2ASaSCYMZIqJB2nYGZfrMJFIZ9CZS+zxORO5hMENENKhcn5n6gBeNg5/vjrBuhkgmDGaIiAZpHYCLFAADQDt3zyaSEoMZIqJBcbE3U5GZGYCN84hkxWCGiGiQKAAOFVnNBBQWARORPBjMEBENEgXAxZrmAVyeTSQrBjNERABUVdVqZoptZwAAY5rYBZhIRgxmiIgA9A2kIdrHlCoAZpqJSE4MZoiIkKuXURSg3l+qZoZpJiIZMZghIkJuJVPI74XHoxR9DtNMRHJiMENEhLweMyWKfwGgvSGbZurtT6F/IF2VcRFRZQxmiIgAxCr0mAGApnofAr7saZO9Zojk4Wows2bNGsybNw8dHR1QFAXLli0reFxRlKL/vv/977szYCIatnIzM8XrZYDsOYl1M0TycTWYicVimDVrFpYsWVL08W3bthX8+9nPfgZFUXDuuedWeaRENNzFtIZ5pWdmgLwiYK5oIpJG+aPWYV1dXejq6ir5+Lhx4wo+/v3vf48TTzwR+++/f8mvSSQSSCRyJ5lIJGJ9oJJ64PnNWLbuPfz80g9jdDjg9nCGtR3RBC5+8GWce/gEXHLsFMvf73ev/Be3LX8TqYxqw+jKCwe9uPP8w3Dk5BZdz//n1j34wiPrtNU9svEowOeOn4rPHl/6PGCGKAAu1TBP0JZnM80kJVVVcdUj6/C3TTuLPl7v9+K2c2fiuAPaqjyyrCUrN+LBFzbD6KE/pjGIX/zP0dru7ZU89o//4qfPbcaPP3MEJraGTIy0trgazBjxwQcf4Omnn8bDDz9c9nmLFy/GLbfcUqVRues3a7fize29+OumnTh95ni3hzOsvbBxB157L4JUeqstwcxvXtlatYvhrhjw1IZtuoOZP7y2DVt2xR0elTWP/H2L7cFMpa0MhDHcbFJqPdEEnt6wrexznvjne64FM//34rvYEU0a/rpdsSRe2LgDZ83eT9fzf712K97YFsFzG3swv3WS4Z9Xa2ommHn44YfR2NiIc845p+zzbrjhBixatEj7OBKJoLOz0+nhuULk+HlSdZ5IKdgVgIjv84PzZmHWhGZbvmcxj697D/+7apOhYtWewd/1sx+bgvOPlOvY+e+ePlzy4N+1MdopXmHHbIFpJrmJv0trOIBHL/9IwWPLX9uOH65427VZNVVV0RPN/uz/77NHo71B3yzLbcvfxJ/e6DZ0rhe/Y0zSGVa71Uww87Of/Qzz589HXV1d2ecFg0EEg/reILVOTItzutt54iSyK5ZEMpXRVrSYJS7Gh3WOwrQxDZbHV8pB45sAGAt4xftp+rgmHDC20ZFxmTW2eXBpdCKFvmQa9RVmUYyI6ljNBDDNJDsRuI9tqtvn/bt1d3bG0a1AdHd8AAPpbH7pyEktus8jU9rCAIyNW5xjxCq94a4mlmY/99xzeOutt3DZZZe5PRSpaDMzvEN0XP6Fa0fU2uvdl0yjd/BuSaQsnJJbeaN/zCLwcXpsZjQGfajzZ09bds9IxnWsZgKAdjbOk1q596/bgagY2+iQ39ANkdFx559jxPt6uKuJYOaBBx7AEUccgVmzZrk9FGmk0hn0D2QAQJu2JOfkp2msngjFCa3O70FjhVkAq8Y0ZU+CRtJM4vcTJ1CZZJdGO3NBEjUzlWdmshdJ9pmRU4/2/i0WzGQ/tyuWQLoKxfdDiRtPo8eW0Tqt/PdmlDMzzotGo1i/fj3Wr18PANi8eTPWr1+PLVu2aM+JRCL4zW9+w1mZIeJ53Ue7I6yZcVr+hdPqRawnL1hQlOJt8+0iTt7xZFrX6qREKo098YGCr5WNU8GESNuGK6SuxGqSnbEEUumMrWMg68oF460NQXgUIKMCO124CdSOfYOznu0G3/P5QQ9nZqpg7dq1mD17NmbPng0AWLRoEWbPno1vfvOb2nMeffRRqKqKT33qU24NU0r5RV28Q3RefsBoNb3RXebO0W7hoE+7OOsJesUqi4DXg1Ehv6NjM0u7S7U5iNeznQEAtIazF0RVBXbGjK9KIWdpsx9FAgavR0Frg3tpQvEz9S6vFozORuY/b6QUALsazMyZMweqqu7z76GHHtKec/nllyMej6O52bkVH7Uov6hrZyyJAd4hOqZ/II1If+6EYLVGSVyEq1WTIlJNek6EYmztjUHHZ43McirNpLdpntejoK2BK5pkpdXMlAgY3OzgnBubuTST3j3B8gN9FgCT1IZG21aLUqm0oTNf1mtmqluT0m6gCNjsnWM1Gfl9jIgn9TXNA9hrRma593Dx48vNpfVmZ2ULCt91jLtgZoZpJpLZ0Dco7xCdM/SC1WNTmqlaAUPu5F153NVMgZllZoWWHlrTvAqrmbJj4PJsGamqWvE97ObfrqdMCqycwsJ3/ccxwDQTSW7o1CFPqs4ZGijaNTNTvWBG/4qmniqnwMxoNxCcGSFmZio1zQPYOE9Wkb4Ukqlsyr3U8eXmrJrZNBNgboYVYJqJJDe0Qp3T3c4RJ4aOwYZtttXMVCmYMZVmapBvWbZgJDgzIrc0W8/MDNNMMhJ/j6Y6H+r8xf+O7RKkmczcyBiaYc2vmWGaiWS2z8wM7xAdI06QB++XLULfEU0gY6FHRU+Va2aMXHi1KXqJZ2bE2OwsfB9IZ7Q7ej0zM+0GiqqpenLv39LHllNpykqiiZQ2+2fmRsbIuPMD/XgyDVWtfk+damMwU6OG5kHZOM854sQwY3wTFAVIZVTsiptbkjuQzmjLeau3mkl/j4pKK0Fk0BIKwOfJrrSyq/A9nndzUGlpNsDGebLS8/5td2hmrxIxWxIOeHW9x4bSuypxIJ0pOD+lMyoSqeG/2pXBTI0SU4fipM6ZGedoaaZRdWgJBbKfM/l6i4uvz6No38tpRgoezXYorSaPA0ujxfHk9yq62swzmJFTue6/Qv7frpozFnpmjcrRmy7eGU1CVbMtBISRUATMYKZGiTfnxJYQAOsrbKi0/Au81onT5IyAONm2NQTh8VSnj4s4ee+JDyCRKl0MmM6oVZ81MsvIbJMecZ0N84R2ly6IVF6uYV7pgEH87ZLpDPb2DVRlXEDuvWq28F9vAC1mp9oaAqgfrBsaCUXADGZqVGww9zpZ7KbKO0TH5BftaVO9JlfSlOtO6pRRIT8C3uyhXu5EuCuWRDqjQlGA1nB1Zo3MsrvuQdsxW0e9DFB4QRTbP5D79LQWqPN70VzvL3h+NVhte5ArfC9/7sm/+RLB+UgoAmYwU6PEzIzYGr6n11pRKhWXSmewM5YLQKxeRN3o46Ioiq4panFH1xoOwueV+9TQbqDnhh5xAyuZACDo82rbPfBGQh7i/VBp9sONpfVWlmUDhYXv5fYEyz/HiPcz00wkLTFtOKk1m2ZKZVTsNlmUSqXtjGXzzx4le5G3WiuRO9lWtyZFz3LUWmiYJ9g/M6NvK4PiY2CKVxZ6u2u70WvGbMM8oSUUgNejQFVze6gVowVNTUFtplHM5A9nDGZqlIi0m+v9aBlMCfAO0X7i4t/WEITXkz/DYTLN5NJ2AbkgrPS4rZ5sq8nuXiFGtjIQtMJqFt9LQ7yHK8/MVH9pvdWbhWzhuzjXlz6O87dz4MwMSU8ULDYEfa71TRgJ8u9yAOsXsFw+u8rBTJP+NFMtzczYVfiem5nRl2bKHwOPOzn0JdPoHfw7VgrIazHNlP+1ZWdYI/lppsGZGQYzJKv8aXGn2rtTfkfcwWBGR1BQTo9LAYPo6FsuPVYLm0wKRnYC18PoaiYAaOdmk1IRf4c6vweNFf6OVmdYzbDj+NITQIuVlmMa89JMDGZIVvnT4lqVOxvn2W5ot978OgkzS3Kt9powS9fMTA30mBHya5fsKHzXVjPpLADOjsGd5mtUXH69jKKUb3vQbrH2zahEKq2terNyI6On1ie3v1pemok1MySrWN4Ov27uNTLclUoz9Q9ktCltvTIZVVdTLyfoKVatpTSTaJpnV+G7tprJVAEwjzsZGDm2qh2Iip8T8Hq0VXBmtFeo9VFVVbupbW8MagXtQ/fyG44YzNQgVVW1SDsc8MHqChsqbWiNS33Aq01hGw0ed8eTSA3OIoiLcbXoyrXXwL5MQsDnsbXwPWYmzcTjTirdBnZ8t5ouNio/xVRp1qicSrU+u+MDGEhnzzHtDflLszkzQxJKpDJID14Uw0Gvq1vaD3f5KwOEdpPdZ8UdU0s4oKtlvp3Ee2RHNKG9d/KpqrpPSk12dgbx4mRvqgCYtWpS0LssO/uc7N8uu/mj87MWVrv/Ctp7vkRJgbgGjA75EfB5WABMcst/Y4YCuZoZTnfbr6fIbIXZ/iJurWQCsh19FQXIqNCaAOaL9Ke0zehqoQAY0L9XjR75qwP1EnVPsWR6RFwsZGekwLYh6NNa/VcjPW9XDyfxnuspEUAPrXvL9ZkZ/u9PBjM1SBT/1vu98HqUgqlH7hNjn8LZivxgxly+3c3VQj6vB63h0lPUYpVVU50PdX79sxNusnNFirY60EAw0xD0aTM5vJFwn5HjS1GUqqaaegykwMrJ3xuu2Ll+aKo4NzPDNBNJKDqk9bp44/YNpLXHyLo98QEk0/vOVpidEdDbat0p5dIyejbok42dTetyqwONBXJMNclDq5nReXxVs4OzkRRYOaJFxEBaxe4ie4INPceEA2yaRxIb2hMjFPBp0+O8Q7SPeC1HhfwI+nIXObMXMLeXPperraqlrQwEO2tmzGxnkB0DU7yyMFrzVc0OznYdXwGfB6O1PcGKHMdD00xBbmdAEotqxYq5Ey9XNNmv1FJPs9PTbi3LFsrOzNTQsmzBzsL3uMFdswWzxeBkr4F0Bjtj2SX6elM57RWKae00tMWDFeWCsPyGeQC4nQHJTfTEyJ8St7MYkrJKpYXM3o3beUIzo9x7pFvnnjYysXNWJGZw1+zcGHjcyWDH4EXc51HQEgro+ppq9ufSjq8G67Oy5W6mhu6vJmZm2GeGpCSmDPNnZrilgf1K5blNp5lcXvpc7o7O7bGZYVfhe7Zvk/E+M9kxiICKx52b8jeE9Xj09XGpVs1MOqNqwZYdNzLlCt+H7v+U286AaSaSULG7SLZWt1+ppdTitY70p9A/oO8koaqqq0uz839u2ZNgDTTME+wqfO8fyEC03jEezDDNJINiLRQq0ZY5O/y32xlLIKMCipJtkWCVnpsSEfCI1XZ9A+mi/aWGEwYzNUi7i8yvmalyR8uRoFSaqanepzW903sijCZS6BsMfNwKGMq9R2ppk0nBrsL3/B4cIYPL0rmViBzMFNhWK0Uo3hut4SB8XuuX3FIBdLYBYLrgOfnB+XBPNTGYqUG5mZl9C4A53W2fUptCKopi+EQoTjzZ3iTG7v7tkl9jMjQt01NDm0zms2NmJJ7X/VdvikL7+ey+LYXcjYf+96947+yKJZEcbBjphKFFuVaVes+JtHc44NWuDUGfB97B9/RwTzUxmKlBsSI7/FZzmeFIUW71Ue4iqu8iJsPMh/jZyVQGkb7cXVpfMq1tmllLaSYAaLPh7trssmwgd9ztjg84ekGk8swcX6NDAfgGL/Q7HFzRNLQo1yrRa2ZoAF/s5ktRlFyvGc7MkGxiRU6+TDPZr1wTLqOrx2QIZur8XjTVibRMLggTJ8U6v0fbRLNW2NG0LreVgfHOx6NDfvi92QtiNZb4UnFm6tE8HqUqq0DtbnsggpWhYy51jhkp+zMxmKlBuW6l+6aZ9vYN6C5KpdJiiZS2aqxYV1yjM2FGu5M6pdiJMH8FhJUdfd1gR+G7lZkZRVG0O2WuJHRPj8mAoRodnO1eKSjGHE8WFr6XOseMlC0NGMzUoNzJN3cn2Vzv14pSnZwyHSnExTEU8BbdfNBojZIsO1IXqzGpxe6/gh0zkuLmwGiPGaG9SqtiqLRS9W2ViBobJ2fVum1OM4WDPi11lB+E5WpzCl+DkbKlAYOZGlRsh9+CO0SeVC2rdIE3ehEdugGcW4oFYd02bYLnBjsK36NFCurNjYHHnRsymeIbwuqhHccO1ho60V272Axrqdqc3JYGDGZIMtp2BkNOvlwmap+hzaeGMpxmkmS7AO0kGCk2M1NbK5kAewrfRUdto1sZ5MbAYMZNu+NJpAZ7qLQ1mEwzOVozI2pZ7Du+itX6lLoBC42QxnkMZmqQttFkoHBa3OgKGyqtUnt/wwXAkix9LjZ7J0Nxsll2pJliFtNMubodHnduEOmVlnBAS7Xr1e7wOVNVVUfSuMVqfUrdgIn3NfvMkHRyS7ML7yS5osk+lS7w4rXeGUsgla68JFeaNFORHhU1HczYUPhebHWgEZwRdZeVztpO73oe6UtpS/btPL6KFb5XXs3EmRmSTKzEtDh7zdinUnv/1nAQHgVQVWi79ZbSP5DG3r6B7PdzOWAoOj0tyUorM5rr/Qh4jXVjHiqW2LcGzQimmdxlJRgf43AgKs4jTXU+1BnsLl3O0BvXRCqNPfHi5xj2mSEppTOq1hZ/6LQ4uwDbp9LqI69H0fLzlS6iYnVZwOtBc73fxlEap93R5Z28ZVlpZYai5HqFmF2Rom3cajbNxC7Ariq17Yge4m+3I5pAxoG9i3J7Rtl7bOXSxf0FPyfg9WBUqPAcwz4zJKX8vCfTTM7RM3Vdbvfagu+Vd+fodh8X8R7pTaTQl0xjIJ3Brniy4LFaYzXNU2x1oBEiCNwRTQ77zfxkZKUera0hCEUBUhlVOw7s5FTbA3GsiiCm3Dkmt3M2gxmSiOiJ4fUoCA4pdnM6/zuS6NlFWu8UdaVi4mpqDPpQ58++b7p7+7EzmoSqAj6PgpaQ9R193WC18F1bHWiyZqatIQBFyc6a7qqQciT7mV2WDQB+r0d73zuRanJqFePQc325c0xuaTZrZkgi+Q3zhkbg4oDZGU3wDtGCZCqD3Vr+ufTdnt7g0Wx3UidkN8nMFQ+Kk21bQ9DwJouysDojmVuabS7N5PN60BrOXhDZOK/69Nx4lGM1TVlOrmGevWkmcS7ZEx9AIpUuu5mlKEfgzAxJRezwW2xKvLUhW5SaUbOrbMgcUePi9yoYHSpd46K3VkKWlUxCfsGq3d1J3WC18N1q0zwg10OEdTPVZ7VPUq73kv1/O6fSTKNChYXvPWUaX2ppJs7MkEyKbWUgeD0KWsJcJmqVln9uKF/jYjTNJEuBba7raX9Nb2UgWC18t7qdQeEYeNxVk6qqlpZm53+dE387K8XJ5eQXvnf3JsoGdCHOzJCMtIZ5Je4ii+29Q8aIO7T2ClPD7TrTTLJ0/xXy02O5k60cgZYZVtNMMRtmZnjcuSOaSGmrO83OLjr5t3Oyu3Z+4Xu5mxIxMxNnMEMy0bqVlihW5DJR6/JnZsoZuqKg0veTJZVT7I5OhuJks6wWvseS1rYzAApnu6h6xLHXEPRZb3rowDmzx8Hi//zC93J1QyOlANj80UuuyN1FFp8Sd7oJ1EigN/jIv6NTVbVkSkq2vY/ygxmRd5dl1siMoYXvXgOFzKl0Bv0D2Q6tlmpmuMmrK+xIkzrVbLQvmUbv4PnaiRuZ/BlJbTVTw77nmPwC4HLnqVrHmZkaU2lKnMuzrdO7+kg0zUumM1r3zaHSGRU7o3LNfuTv6yLTSiuzWsLZpdEZFdprrVc8bwuEYnVoehXbxZicJ17vNivBjEP9ucRsSZ3fg0YLgXIpInDZvrdfW7RQbmYmlVGR1LH1Sq1iMFNjYhV6YjDNZJ3egt06v1fr6FtqWefOWAIZFVAUaMt33Va4NNuZpaPVlF0abe6CJFYH+or0bTKC3bfdYcdWHPl/O1W1r6VFfmdtJ2ZDxLn+ze29Zc8xobxtFIbz/kwMZmpMrltphTQT7xBNMzJ1XSmtJz7fGg7C55XjcMttkpm01HBMJmaLOMv1bTL283OpCjsviFSeHVtxiK/tH8hoaSE7OL1SUHzft7b3Aih9jvF5PVqjzOG8okmOsyvpFq2ww297kd1UyZgeAwW7lWbCZAwWWkIB+AbrSlKDzRXbKhQ7y87sjGSl1YFGf34iZe8Fkcqzo7i+PuDV0kB2nje7y/R+sYMIwkTqqNw5JtdrZvi+NxnM1BjRE6PUPjL5MzO8QzQuk1Fz+Wcdd3uVapSsdid1gidvk0wgW3MSsJBikYHZwnc7GuYB2ZRjY53P1BjIPLvaHrQ3mXv/lON04f/Qc0q5c0xus0mmmUgS2sxMiTSTKDJNpjKI9A3fKNwpu+JJpDIqFCW7504letNMMs3MAIUnPtnGZobZwndRM2N2K4PCMbBuptrsakjpxN/O6bYHrYOF70K541gUtzPNRNKIV+iJUef3okncIfKkaliuxiWgq8alUo8K2ZZlC/knPllWWVlhNs0UsynNBBQWVlN12NXDyYm/ndM1M/mF79mfU/ocI97fcaaZSBZih99yJ18uEzXPaEfcSq+1jGkmoPD3ky3QMsNs4Xul1YGGxuBAqoJKS6TS2NsnNoS1GszYv3AiVzPj3PGV/3vrSTNFmWYiWejZ4ZfT3eYZnRqutIpG1r2P2ofZzEy7ycZnYtq91OpAI3jcVZc45gJej9Yiwazc1gD2/e3E+Cp1ErdCb7pYXC84M0PSyG2KV2Zmhl2ATTO6+qjSSbDbwXbmVhTc0Uk2NjOGdmPWS6SZQjakmdoduLun0vJvPKz2cbG7cd5AOoOdsWTB93ZCfqBU7hzDAmCHrVmzBvPmzUNHRwcURcGyZcv2ec4bb7yBM844A83NzQiHwzjqqKOwZcuW6g9WEtEK2xkATDNZYbQJl3heLJnep7hOVVVb+mA4Qe/0dK3QCt/TGS31oEduZsa+mhneRFSHnTcKdndOFysifR4FLSHnmmUWzsyUqZlhAbCzYrEYZs2ahSVLlhR9fNOmTTjuuOMwffp0rFq1Chs2bMA3vvEN1NXJdWGoJj19MbiDr3lG00INQR/qBztsDn29I30prQeEdDMzTcOrZia/8N3I+15svmdlKwOBaabqsnMrjjE2p5nEe7CtIQiPgb3CjMo/dnXNzAzjNJOrG012dXWhq6ur5OM33ngjTjvtNNx+++3a56ZOnVr2eyYSCSQSuZNZJBKxPlBJJFJpDKSzU+jlChad3AXWDvet2gSPAnzuhPJ/y3w//9t/8Kc3uh0cVdaG/+4BoL9oT1EUjGkK4t2dcVzz6/VorMvl7vsHL5RNdT7U+a1fLO003NJMQPZvFumP4trfbtBdQ/HW9uz5wcqO2bmfn30dt+7qw//7s5ctfz8nTG4N4aZ5B+vejPPPb3yAX774LtIGW1a1hQO46YyDLdeylGPnbvQiKIj0p3DRAy9ZTlvtiTufYgJyx26lc0wuzWR/MPPIy1vwh1e3Yd7MDpx/VKft318vaXfNzmQyePrpp/GVr3wFp5xyCtatW4cpU6bghhtuwFlnnVXy6xYvXoxbbrmlegOtonhevrNcAbDIo+6MJh0fk1F74wO4bfmbAIBPHz2x4OJfSiqdwbeefF3rVlsN08Y06H7uAWMa8e7OONZt2VP88bGNNo3KPu2NQTTX+5HOqBjXXPszMwBw4NgGbOyO4p9b9xj+2s6Wess/v2NUPUIBL+LJNNa83WP5+zlhDYB5szpw1OQWXc//4R/fxuvbzN0QHjO1Fecd6dzFza4eMwDQVO/DmMYgunsTeO6dHZa/n2DkPGLGAWMbBv9b/hyjpZmS9tfM/Ov9vXjunR04rHOU7d/bCGmDme7ubkSjUdx66634zne+g9tuuw3Lly/HOeecg5UrV+KEE04o+nU33HADFi1apH0ciUTQ2eletGgnUS8T9HnK9kDJ9RSQr9hre940bndvQlcwsyOabWTnUYDvf3IWnN7BfsLoEA40EIDc/smZeO6dHqSLBFuKAnx0apudw7OF3+vB7z7/UWRUVbpZI7O+e9ahOOXgcUX/DuWMDgdw/AHtln9+KODDY1d+FK+/L+ds8P+u2oSN3VFs36t/xlYcr1859UMYp3O28tdrt+LFf+/CBzauDCrGru6/QHaG9defOwb/2LLb8vcSfF4PTjjQ+vuqnGljGvG7zx+DCaNDZZ8XcnBmRpbGoNIGM5lMttbgzDPPxDXXXAMAOOyww/DXv/4VS5cuLRnMBINBBIPDY9p8KD0rmbKPZy9OUQmLvfJTX92RBKa2V75zEV/T1hDEuUdMcGxsZrWEAzjzsP3cHoZhTt81VttoCf4O08c1Yfq4JlfHUMqf3+zGxu6o7iLXZCqDXYMrcs4/slP3/l2beqJ48d+7HF+AYGeaCQAmt4UxuS1sy/eqpiMmVZ5lE2nUuAOrmXqiohDb3RleaZdmt7W1wefzYcaMGQWfP+igg0bsaiY9K5myj8vb7TF/pYfemh4t8h8Gq26I3GK0QNnsipxqreqStbu2jJy8wZWl/YS0wUwgEMBRRx2Ft956q+Dzb7/9NiZNmuTSqNxVaSsDQRQHD6RVJFJypZpEFA/oX3XSY2DjRyIqzmjLfrMrcrTVlFHngpl0RsXOqBzpjVrg1A1uYfuJEZxmikaj2Lhxo/bx5s2bsX79erS0tGDixIm47rrrcMEFF+D444/HiSeeiOXLl+PJJ5/EqlWr3Bu0i2I6tjIACouD44k0gj55aiLy79b0nlRlyckS1TKjLRvMpnHM7pNlxM5YAhk1W5PWEnauj8twIW6A7S4A3ts3IE37CVdnZtauXYvZs2dj9uzZAIBFixZh9uzZ+OY3vwkAOPvss7F06VLcfvvtOPTQQ3H//ffjd7/7HY477jg3h+0aUbxVqSeGz+tB0Jf908pWN1NQM6P7pGpfoR/RSGV07yizx11+mslIN2YjchvCBnVtCDvSiTST3QXA4hzeXO93fSGBqzMzc+bMqfhmv/TSS3HppZdWaURyE1OEerqVNgR9SKSS0q1oyg9gdNfMiLblDm7YRjTc5brcGqtVM1rYKe7QE6kMIv0pR3rNyJLaqBX5K1wzGdW2Rn4yzZozpK0hUQM7/IZEJC5ZEXD+FLf+O0R5DhiiWiWOn93xASRTmYrPN3vcFXZjdibVJOtu9LLKr7OMD9h3gyvT34HBTA3JzcxUns7TcqSypZkixtNMPQb3SyKifY0K+eH3Zu/I9RTn9li4UGn7wzm0okmmGYFaUOf3QEzGxG28Jsi0oozBTA0R9S96dviVcZfUWCJVUIC2t28A/RXuElRVza1mYpqJyDRFUbTu4Hr2ILJyodJ+jkO9ZmS6iNYCRVG0G1w76yhlCioZzNQQ0fCo3FYGQkjCXVLFCSgU8CIwWKBcaWXF7viAth9VWwNXLRBZIerO9AQZVvqHOL2iSab0Rq0QpQd21lHmGua5/3dgMFNDojp2zBYaJGyc152XLtJ75yaCnVEhv1RLzIlqUa5xXvnjLpNRtaZ5Zu66c7tQOz0z4/5FtFaI64a9MzMiqHR/hozBTA0RuU49O/yGtClFedJM+VPD4o6qUoEgl2UT2UfrNVMhzbQ7ntQ2dtW7jUHhz9E/A2SG2ZVWI5m2pYGNN7jiZrPdxHvEbrYEM+l0GuvXr8fu3fZt0kX70ts0D8gVCcs0M6O98ZuCuu8Q7dwZl2ik0xtkiMdbwgEtJWzo52g3K/YHMwV1dLzJ0S23pYGdq5nk2WrGVDDzpS99CQ888ACAbCBzwgkn4PDDD0dnZ+eI7c5bDWKZdUjHaqaQA1OKVuVPDettrc7pZCL76A0yrB537Y3O1cxE+lLa0nIZajVqRW6zSXuuCfFkSru+yHB+NhXM/Pa3v8WsWbMAAE8++SQ2b96MN998E9dccw1uvPFGWwdIOaJwS2/TPMCZXVLNyqWM6nTn1MXXtEsQ+RPVOv0zooPHncmLlJNpJnFOaKrzud51tpZoK1xtKgAW5+56v1fXNclppoKZHTt2YNy4cQCAP/zhDzjvvPNw4IEH4tJLL8Wrr75q6wApJ6pzO4P850QlTDONaQzqXu3AJZhE9tHbBdjqcSeO797+VMX2C0blUhs8Jxhh95YG+SkmRbGno7AVpoKZsWPH4vXXX0c6ncby5cvxiU98AgAQj8fh9TJSdoqYHtQTBWvtq2VKM0Vyb369d249EvUxIKp1IsjYEU0inSm9lUyPxVqIxqAPdf7s5cXuFU1cFGBObrNJu4IZuf4OpoKZSy65BOeffz4OOeQQKIqCuXPnAgBeeuklTJ8+3dYBUlYmo2rTg3q2M3Bql1Qr8tNM7XqnuyU7YIhqWWs4AEUB0hkVu2LJks+zetwpiuJY3YxMjdpqSShob1d42RZnmEp03XzzzTjkkEOwdetWnHfeeQgGs28qr9eL66+/3tYBUlZf3lRtWM92Bg7tkmpWMpXB7vgAgOxJaCCTLeDbGU0gnVHhLbHxGaeUiezj83rQGg5gRzSJ7t7+kjUxdlyoxjTWYeuuPtvrZnhOMEc0W7WrjlLbAFiSoNJ01c4nP/nJgo/37NmDBQsWWB4QFSeCEkXJFlxVkr9LqgzEUkq/V8GokB8ZFfAoQEbNBjTFTkyxREobP+/CiOzR3lg3GMwkcHCJ5+S2EDF/3OWK/G2emeEKR1PsbppnNRVpN1Nppttuuw2/+tWvtI/PP/98tLa2YsKECdiwYYNtg6MckS4KB3y6iq20AmBJZma01REN2WIxr0dBa4UuwPnbH+jprUNEleUa5xU/7lRVzTWls9AMTe/KKaOsrrQaqcI2b2eQXzYgA1PBzNKlS9HZ2QkAWLFiBVasWIFnnnkGp556Kq699lpbB0hZYmZGT4oJyF+aLUkwozXMy73xx1TIqXdzt2wi21U67qKJlJbWtjQzY2AfKCN6uMLRFLs3muwZDmmm7du3a8HMU089hfPPPx8nn3wyJk+ejKOPPtrWAVJWzMBWBkCuSDiWTCOTUeEpUZNSLcWmhsc0BvEvlF7twGXZRPbLtUUof9w1BH26FhuUorfI3yiZus7WkrDN+/XJlu4zNTMzevRobN26FQCwfPlybTWTqqpIp+Wo0RhuYgY2mQQKl2/32dznwYyeosFM+S7AudkcOQ4WouGg4nFn02ohLZ1lYzCT33VWlhmBWqE1zbOhADiZymir4WQJZkyF3eeccw4+/elP44ADDsDOnTvR1dUFAFi3bh2mTZtm6wApS7wB9TTMA4A6v0crsI0lUq7XnPQUya9WvkNkmonIbpVqWbSu25aDGRE02VcALAKjOr8HjayjM0SsZrKjz4zYUd3nUTA6FLD8/exg6t1w5513YvLkydi6dStuv/12NDQ0AAC2bduGK6+80tYBUpaYGtTbNlpRFIQDPvQmUlL0mslvmCdUyt33SNbHgGg4qNR9u8empc/i5+yMJZFKZ+DzWt/XOD/1LEPX2VoStnGLm/xl2W6XMAimghm/31+00Peaa66xPCAqTux0GjJwNxIKerPBjARFwMXyq+0VugDLlpMlGg7aGwaPu0gCqqruExTYddy1hALwehSkMyp2RJMY12z9poQN88wT9ZbJdAbJVMbUbuiCjIszTP82v/jFL3Dccceho6MD7777LgDgrrvuwu9//3vbBkc5ca0AWP92EWGbOz5aUWwZn3aHWLIAuL/geURknTieEqkMIv37nhvsulB5PAraGrIpCLu6APOcYF4obyWs1SLg3MyMPLPmpoKZ++67D4sWLUJXVxf27NmjFf2OGjUKd911l53jo0FRgwXAQN6W7y6nmcSdGVA8zdTTm71DHIpLMInsV+f3orEue24oVs9iR8M8QduDzab9mbjC0Ty/16PNxlhdni1bwzzAZDBz77334qc//SluvPHGgo0ljzzySO6a7RCR5zQyMyNL47xdseymdoqS3RtGaBtsyJVMZ7C3b6Dga4Zuf0BE9sl15903yLBzzx27G+dpzfx4TjBF29LA4g2ujCUApoKZzZs3Y/bs2ft8PhgMIhaLWR4U7SvXNE//zEyDzX0FzBJTw63hQEERYJ3fi+Z6/+BzCk92Q7c/ICL7lNu13s49dyoVGxvFFY7W2LWlQbHVqW4zFcxMmTIF69ev3+fzy5cvx0EHHWR1TFSEWE5nrABYvHHdTTOVy6+WukMcuv0BEdmnVJDRP5DWZkntCBgqFfkbZddKq5FKKz2weE2QbZNJwORqpkWLFmHhwoXo7++Hqqp4+eWX8cgjj2Dx4sW4//777R4jIddnpkHndgb5z3V7S4OeMisQxjQF8U53dJ+TarHtD4jIHqVuIkSwEPB5tFlTJ36OWdp5wcKeUSOZ2A7H6syMjKvKTAUzl112Gerr6/H1r38d8Xgcn/70p9HR0YG7774bF154od1jJOTNzBhoLy6eG3U5zaQVFBYLZkp0I5UxJ0s0XGjHXbT4cWfXjKhW5B+1HswMpPO6zkpUeFpL7NjSIJNRtaZ5Mv0dTLdQnD9/PubPn494PI5oNIoxY8bYOS4aIq7NzBhYzWRjkyQrtKWeRd74pQoEeyTsY0A0XJRqi9Bj89JnkQ4Sx7MV+V1nWyTpOltrwnl79pm1K55EanBBR5tEM2SW+0GHQiGEQiE7xkJliGlBvdsZAHntq11OM5VbTllqMzouwSRyjkjTlErv2nUT0Z43M1OsQZ8RIvBqa5Cn62ytEb1mrFwTxN+hJRSA34auznYxNZIPPvgAF110ETo6OuDz+eD1egv+kf3iZvrMiKZ5rq9mKlczI/pQFJ5UZexjQDRclNoXze7eTiJoGkirWqsFs7hbtnW5AmALwYxNe3fZzdTMzMUXX4wtW7bgG9/4BsaPH8/VJlUgCoCNBTP29BSwqlzXzlI767Jmhsg5YpVRb38K/QNp1Pmz5wq7CzsDPg9Gh/zYHR9Ad28/WsLm00Nclm1d2IYVrrKuKDMVzDz//PN47rnncNhhh9k8HCommcogmc4AMLidQcCengJWqKpatglXqZqZYtsfEJE9mup8CPo8SKQy6I4kMLE1WyrgxHYBYxrrssFMJIHp48x/n1zDPJ4TzMo1zbMyMyPnjaapNFNnZ2fR9vPkjPw3npHVTDIUAEf6U0iksoFYsWlJEd1HEynt9yy1/QER2UNRlKK9ZpyoVSuV0jJK1otoLbGjaV6PpH8HU8HMXXfdheuvvx7/+c9/bB4OFSMqzwN5e2voIcN2BmJ1RGOdT5vKzhcOeFE/ZIq71PYHRGSfYl2AnWiGlivyt7aiye6VViORHaUHsqb7TKWZLrjgAsTjcUydOhWhUAh+f2FzpV27dtkyOMrKbWVgrLhahu0MKuXgxR3iuzvj6O5NYHJbuOT2B0Rkn1xDu+zxls6o2FmmJ5T5n2PPZpNc4WidHTMzsqb7TAUzd955J4t+qyiWMN4wD8htZxBzMc2k5wQ0plEEM/0FXyPbwUI0nAytV9sZTSCjAh4FaLWxf0ipIn+juMmkddpqJjtqZiSbITO9momqJ2aiYR4ANAy+cZPpDJKpjKEUlV30LLEe2gW43PYHRGQPraHd4HEnLlKtDUF4bezjIo59K8FMQddZnhdMC1u8wVVVVdo0k6mrm9frRXd39z6f37lzJ/vMOCC3yaSx1zb/+W6lmvS88Yc2zpP1YCEaTnKN85w97ko16DNi92DXWUCurrO1JmSxkWpvIoX+geyCDtnSfaaCmVIrmRKJBAIBFmzaTWuYZzDN5M8rGLbSvtoKXWmmIa3V2TCPyHntQ1YZObVKRWuMaWFmRnxtSzjgygzzcJHbm8nc9UCcoxuDPtQbaBNSDYaujvfccw+AbNHm/fffj4aGBu2xdDqNNWvWYPr06faOkLQGR0YLgIHsaqFkKuPalgZaAbCONNPQmhnZIn+i4SRXyzJ43JXpB2XHz4kn04gmUobT5QCXZdtFXENiyZSp7SVEwNsu4Y2moXfVnXfeCSA7M7N06dKClFIgEMDkyZOxdOlSe0dIWutpozMzQDYS3x0fcC+Y0dH6emiBIE9cRM4TQcvOWBKpdMaxws5w0IdwwItYMo3uSD8a2hsqf9EQYsUVi3+tEdcQVQX6BtKGF5XIXAJg6DfZvHkzAODEE0/EY489htGjRzsyKCqUW5ptIpgJWJtWtMpQmmlo7l7C6J9ouGgNB+D1KFqTSicvVGOa6rB5RwzdvQnsbyaY4WytLer9XihKNpiJJlKGgxm79+6yk6nk48qVKxnIVJGodzFaAAzkphXdaJzXP5BGb3/25+pJM+2KJZEcbK+e/3kisp/Ho6CtIVvj2N3b72hLhKFF/kaxjs4eHo+C0GCTUjOd4WWeNdcdli1atAjf/va3EQ6HsWjRorLPveOOOywPjHLEzEyDyTQT4M5qJhGUBH0eNJaZVRpV74fPoyCVUbF5R6zs9gdEZJ8xjXX4IJJA9+A/wJnjbmiDPqNkTm/UmnDQh9hg/ZJR4u8nY1Cp++q4bt06vPnmm5g9ezbWrVtX8nlspme/3MyM8WAmt6VB9dNM+emicu8Lj0dBe2MQ2/b247X39gIovf0BEdlHBAcf9PY7uufO0F5SRrFhnn3CQR/QmzBVeuDEdhd20X11XLlyJbxeL7Zt24aVK1cCyG5rcM8992Ds2LGODZDyZmZMpZnEZpMuzMwYyK+OEcHM+3u1j4nIWeIOe2N3FMm0czOiVjebZM2MfbQVTWZmZiT+OxiqmRnaX+aZZ55BLBazdUC0L7PbGQC5AmA3VjNpU5I6To4iT/+v9yKDXyPfwUI03IiGduK4a673OzIjaqVxnsxdZ2uRuI7ETJQeGDmnV5ul7kOlmueRvcR0oKk+M6J9tQurmXoMtB8Xd26vb4sUfExEzmkfbGinHXcOXaSsbGkQze86y/OCZeGAuQLg/oE0ImJBh4Q3m4aCGUVR9ql9YI2M82JW+sxYbF9tRa5hnr40E5BbdSVj5E803Oxz3DkULOQaYxoPZsTXNAR9pmanqZDZnbNFIBrwedBUL9/fwdCIVFXFxRdfjGAw+4bv7+/HFVdcgXA4XPC8xx57zL4RkjYdaKrPjIszM0aKxYZG+jJG/kTDzdCbBqeOO/Fz9sQHkEilEfTpn2Xu5saztjK7c3b+smwZJzEMXR0XLFhQ8PFnPvMZWwdDxcW07QzMBDNiStHNAmA9wcyQkyqnk4kcN3TW1KmAYVTIj4DXg2Q6g57eBCaMDun+Wj1dxEm/3MyMsRvcHsnrlgxdHR988EGnxkElqKqam5kxsbGX2SlFO+Te/DrSTEOCF564iJzX3lCd405Rsu0X3tvTh26DwUyuYR5na+2g3eCanpmR8+/A7Ucl1zeQhqizrqXtDFLpDHbGkgD0zbIwzURUfQGfB6NDfu1jJwMGrQtwxFjdjMxdZ2uR2RtcPZsGu4nBjORieVOB9SaWTIZcKgDeEU1CVQGvR0FLKFDx+a0NAeSnYWU9YIiGm/wbBycDhqG7dOsl83LgWmR2NZPsy+MZzEgut5LJC4/HeNFVrgC4usGMeOO3NQR0jdvv9WhBT6XtD4jIPvmpJSfTu2b3Z5K562wtMntNkP3v4Gows2bNGsybNw8dHR1QFAXLli0rePziiy/WloOLf6eeeqo7g3WJeMOZ2coAyHvjVnk7AzObRYqDpNL2B0Rkn/w7bWdnZgaXZ5tOMzH1bIeQyUaqsm8A7GowE4vFMGvWLCxZsqTkc0499VRs27ZN+/fII49UcYTu01YymSj+BfJaVydTVW1yaCbPLfL1sh4sRMNR+2BKt97vRYODM6K5LQ1MppmYerZFbjsDo2kmuWdmXJ3L7+rqQldXV9nnBINBjBs3rkojclb/QBq/fPFd7I4ndX/Nll19AMwV/wK5AmBVzRYTl2s69dg//otNPVFTP2eoV97dDcDYCUgEPrLmZImGI3Hz4PSMqDiuX98WwfeffVPX16gq8rrO8rxgB3Et2ba3T/ffAQB2xeQuAJa+MGHVqlUYM2YMRo8ejY9//OP4zne+g9bW1pLPTyQSSCRy05iRSKQaw9Tl6Q3b8J2n3zD1tS3hykW0xdT7vVCU7EkhligdzGzs7sWiX//T1M8ox8gSzM7B504YXW/7OIiouM7B483p466zJXt8fxBJYMnKTYa+tjHoQ3O9v/ITqaK2cDYYifSnDP8d6v1etIYZzBh26qmn4pxzzsGUKVOwadMmfO1rX0NXVxf+9re/westnnZZvHgxbrnlliqPVJ93d8UBAAeNb8JH9m/R/XU+j4Jzj5hg6md6PApCfi9iyTRiiVTJKcItg2Nrbwzi/5k53tTPGqox6MOnPzxR9/MvOmYSAj4Pzj18P1t+PhFVduL0MbjxtIPwsQPbHP05B45txHfOOsTU7O8JB7azjs4mE1tDuPWcQ/HWB72Gv/ZjB7TBa2IhSjVIHcxceOGF2v8feuihmDlzJqZOnYpVq1bhpJNOKvo1N9xwAxYtWqR9HIlE0NnZ6fhY9RBLEk+eMRbXfOLAqv3ccNCXDWbKVK+L4q5DOppw07yDqzW0Ai3hAD4/Z6orP5topPJ7Pfjs8ftX5Wd95iOTqvJzqLwLDdxk1oqaWpq9//77o62tDRs3biz5nGAwiKampoJ/snCr6ZDIkZZrnMcVA0REVKtqKpj573//i507d2L8eHvSINXWE3UnYBDV6+U6PuZahsuZDyUiIirF1TRTNBotmGXZvHkz1q9fj5aWFrS0tOCWW27Bueeei3HjxmHTpk34yle+gmnTpuGUU05xcdTmubX7qyj6LdfxUfbujkRERKW4GsysXbsWJ554ovaxqHVZsGAB7rvvPmzYsAEPP/ww9uzZg46ODpx88sn49re/jWCw9i64mYyKHVGX0kw6tjTI9RBgmomIiGqLq8HMnDlzyjZye/bZZ6s4GmftiieRyqhQFKCtwZ2aGT0FwEwzERFRrampmplaJoKFllAAfm91X/ZwhfbVqqrmamaYZiIiohrDYKZKRE2KG62gczMzxWtm9vYNIJnOAJC3VTUREVEpDGaqRFv63FT9mpTcXhzFZ2bE2Jrr/Qj6zO0BRURE5BYGM1XiZhqn0s7Zbq2yIiIisgODmSoRO7+6kmaqsJpJW5bN4l8iIqpBDGaqpFuGmZkSq5nY/ZeIiGoZg5kq6XExYNCa5pUoAOZKJiIiqmUMZqqk28XtAvQWAHMlExER1SIGM1Wgqqqr2wVUTDNFRM0M00xERFR7GMxUQW8ihf6BbB8XN9JMuaZ5TDMREdHww2CmCsTS58agD/WB6vdx0ZtmYjBDRES1iMFMFWjdf11a+ixmZhKpDFKDnX6FeDKF6GCQw5oZIiKqRQxmqsDtNI6omQH23dJAzBrV+71oCLq67ygREZEpDGaqINdh150C24DPA79XAbBvqil/lZWiKFUfGxERkVUMZqrAzU0mBTE7E08ODWbcW2VFRERkBwYzVSBDgW2pFU1uzxoRERFZxWCmCnpcbJgnlFrR1BNlwzwiIqptDGaqQIa9j8SWBqUKgLnJJBER1SoGM1Wgddh1tWam+MxMrmaGaSYiIqpNDGYc1j+QRqQ/G0C4GTBoNTNDCoDdXjZORERkFYMZh4lgIeDzoKnevT4u2v5MJZZms2aGiIhqFYMZh+UvfXazj0suzZSrmUmmMtgVSwLgzAwREdUuBjMOyy19djdYyC3Nzs3M7BhcyeTzKBgdCrgyLiIiIqsYzDhMhpVMQF6aKW81U36KyeNh918iIqpNDGYcJkP3XwAIBfZdzSTDKisiIiKrGMw4TJbVQg1FtjPIzcxwWTYREdUuBjMO65ag+y8AhIL7bmcgQ2diIiIiqxjMOEyWvY/CIs1UZGbG7VkjIiIiKxjMOEyWPi7F+sz0sPsvERENAwxmHJRKZ7AzJkcqp9iu2bIEWkRERFYwmHHQzlgSqgp4FKA17PbMTJE0kyQ9cIiIiKxwr7/+CCCChbaGILwu93HJTzOpqgpVzTXNc3vWiIiIyAoGMw7StjKQIFgQwUxGBRKpDKKJFFIZFYqSDbaIiIhqFdNMDpKl+y8AhPxe7f+jiZQ2a9QSCsDv5duAiIhqF69iDhIBQ7sEMx8ej6J1AY4n0tJ0JiYiIrKKwYyDeqLypJkAICRWNCVTeQ3z3J81IiIisoLBjINkWy3UEMztz8SGeURENFwwmHGQbHsf5WZm0trMDNNMRERU6xjMOEi2vY/CBTMz3DGbiIiGBwYzDlFVVZods4X8XjOy7BlFRERkFYMZh+yJDyCZzgCQJ5WT29IgJc1u3kRERFYxmHGICBZGhfwI+rwVnl0duS0N0kwzERHRsMFgxiEyBguiAPiDSD/6B7KzRkwzERFRrWMw4xAZa1IaBmtmNu+IAQAagz7UB+SYNSIiIjKLwYxDeqLyLX0ODaaZRDDTznoZIiIaBhjMOES2hnlAbmbmvT19AOQaGxERkVkMZhwi495HomZGVbMfy9LMj4iIyAoGMw7plnDvo/CQ+hjOzBAR0XDAYMYhsjXMA3JN8wSZxkZERGQWgxmHdEfkW5ot+swIbJhHRETDAYMZB8QSKcSSaQCSpZn2mZmRZ2xERERmMZhxgKiXCQW82goiGYjtDASZZo2IiIjMYjDjABlTTABnZoiIaHhiMOMAbSWTZMFCKG81U8DnQVO9PLNGREREZjGYcYBYySRTjxkACPo88HkUANlZI0VRXB4RERGRdQxmHNAtaTCjKIo2OyPb2IiIiMxyNZhZs2YN5s2bh46ODiiKgmXLlpV87hVXXAFFUXDXXXdVbXxmaTtmS7j0WRQky1bPQ0REZJarwUwsFsOsWbOwZMmSss97/PHH8eKLL6Kjo6NKI7OmR9KaGQAIacGMfGMjIiIyw9UK0K6uLnR1dZV9znvvvYcvfOELePbZZ3H66adXaWTWyLjJpCC2NJBxbERERGZIvZwlk8ngoosuwnXXXYeDDz5Y19ckEgkkEgnt40gk4tTwSpI5zdRU7wcAjJWomR8REZEVUhcA33bbbfD5fPjiF7+o+2sWL16M5uZm7V9nZ6eDI9xXMpXB7vgAADlTOZd9bH/Mm9WBkw8e6/ZQiIiIbCHtzMwrr7yCu+++G//4xz8MLSG+4YYbsGjRIu3jSCRS1YCmJ5qdFfJ7FYwO+av2c/U64cB2nHBgu9vDICIiso20MzPPPfccuru7MXHiRPh8Pvh8Prz77rv48pe/jMmTJ5f8umAwiKampoJ/1SS6/7Y3sI8LERFRNUg7M3PRRRdh7ty5BZ875ZRTcNFFF+GSSy5xaVSVaQ3zWJNCRERUFa4GM9FoFBs3btQ+3rx5M9avX4+WlhZMnDgRra2tBc/3+/0YN24cPvShD1V7qLppDfMa5Cv+JSIiGo5cDWbWrl2LE088UftY1LosWLAADz30kEujskbbl0nClUxERETDkavBzJw5c6Cqqu7n/+c//3FuMDbp6ZVzx2wiIqLhStoC4FqVa5jHmhkiIqJqYDBjMy3NxJkZIiKiqmAwYzOZu/8SERENRwxmbJTOqNgRTQJgmomIiKhaGMzYaFcsiXRGhaIAbQ0Bt4dDREQ0IjCYsZFomNcaDsDn5UtLRERUDbzi2kjUy7QzxURERFQ1DGZspHX/5UomIiKiqmEwY6MeLssmIiKqOgYzNhI7ZjOYISIiqh4GMzZiwzwiIqLqYzBjo9wmkywAJiIiqhYGMzbq5iaTREREVcdgxiaqquYVAHNmhoiIqFoYzNikN5FC/0AGAPdlIiIiqiYGMzbpjmRnZRrrfKjze10eDRER0cjBYMYmrJchIiJyB4MZm/Sw+y8REZErGMzYRKSZWPxLRERUXQxmbMI0ExERkTsYzNgk1zCPwQwREVE1MZixCdNMRERE7mAwY5OeKPdlIiIicgODGZtoO2YzzURERFRVDGZs0D+QRqQ/BQBoZ5qJiIioqhjM2ED0mAn6PGiq87k8GiIiopGFwYwNtGXZTUEoiuLyaIiIiEYWBjM2ECuZ2htYL0NERFRtDGZsoPWYYb0MERFR1TGYsUF+momIiIiqi8GMDXp62WOGiIjILQxmbMA0ExERkXsYzNhAKwBmmomIiKjqGMzYoJtpJiIiItcwmLEolc5gZ4xpJiIiIrcwmLFoZywJVQW8HgWt4YDbwyEiIhpxGMxYJOpl2hoC8HjY/ZeIiKjaGMxYJHrMtLNehoiIyBUMZizismwiIiJ3MZixiA3ziIiI3MVgxiJtKwMGM0RERK5gMGNRrmEe00xERERuYDBjERvmERERuYvBjEWsmSEiInIXgxkLVFXNBTNMMxEREbmCwYwFe+IDSKYzAID2Bs7MEBERuYHBjAWiXmZ0yI+Ajy8lERGRG3gFtoDdf4mIiNzHYMaCHnb/JSIich2DGQu4LJuIiMh9DGYsyDXMYzBDRETkFgYzFuS2MmCaiYiIyC0MZixgmomIiMh9DGYsYPdfIiIi9zGYsaA7MphmYvdfIiIi1zCYMSmWSCGWTAPgzAwREZGbXA1m1qxZg3nz5qGjowOKomDZsmUFj998882YPn06wuEwRo8ejblz5+Kll15yZ7BDiBRTOOBFOOhzeTREREQjl6vBTCwWw6xZs7BkyZKijx944IH40Y9+hFdffRXPP/88Jk+ejJNPPhk9PT1VHum+RPEvu/8SERG5y9Upha6uLnR1dZV8/NOf/nTBx3fccQceeOABbNiwASeddJLTwyuLy7KJiIjkUDP5kWQyiZ/85Cdobm7GrFmzSj4vkUggkUhoH0ciEUfGw4Z5REREcpC+APipp55CQ0MD6urqcOedd2LFihVoa2sr+fzFixejublZ+9fZ2enIuBKpDOr8Hhb/EhERuUxRVVV1exAAoCgKHn/8cZx11lkFn4/FYti2bRt27NiBn/70p/jLX/6Cl156CWPGjCn6fYrNzHR2dmLv3r1oamqydcyqqiKVUeH3Sh8TEhER1ZRIJILm5mZd12/pr8LhcBjTpk3DRz7yETzwwAPw+Xx44IEHSj4/GAyiqamp4J9TFEVhIENEROSymrsSZzKZgpkXIiIiGtlcLQCORqPYuHGj9vHmzZuxfv16tLS0oLW1Fd/97ndxxhlnYPz48dixYweWLFmC9957D+edd56LoyYiIiKZuBrMrF27FieeeKL28aJFiwAACxYswNKlS/Hmm2/i4Ycfxo4dO9Da2oqjjjoKzz33HA4++GC3hkxERESSkaYA2ClGCoiIiIhIDsOqAJiIiIioHAYzREREVNMYzBAREVFNYzBDRERENY3BDBEREdU0BjNERERU0xjMEBERUU1jMENEREQ1jcEMERER1TRXtzOoBtHgOBKJuDwSIiIi0ktct/VsVDDsg5ne3l4AQGdnp8sjISIiIqN6e3vR3Nxc9jnDfm+mTCaD999/H42NjVAUxdbvHYlE0NnZia1bt47YfZ/4GvA1EPg68DUA+BoIfB2svwaqqqK3txcdHR3weMpXxQz7mRmPx4MJEyY4+jOamppG7JtV4GvA10Dg68DXAOBrIPB1sPYaVJqREVgATERERDWNwQwRERHVNAYzFgSDQdx0000IBoNuD8U1fA34Ggh8HfgaAHwNBL4O1X0Nhn0BMBEREQ1vnJkhIiKimsZghoiIiGoagxkiIiKqaQxmiIiIqKYxmDFpyZIlmDx5Murq6nD00Ufj5ZdfdntIjlqzZg3mzZuHjo4OKIqCZcuWFTyuqiq++c1vYvz48aivr8fcuXPxzjvvuDNYhyxevBhHHXUUGhsbMWbMGJx11ll46623Cp7T39+PhQsXorW1FQ0NDTj33HPxwQcfuDRi+913332YOXOm1gTrmGOOwTPPPKM9Ptx//2JuvfVWKIqCL33pS9rnRsLrcPPNN0NRlIJ/06dP1x4fCa8BALz33nv4zGc+g9bWVtTX1+PQQw/F2rVrtceH+7lx8uTJ+7wPFEXBwoULAVTvfcBgxoRf/epXWLRoEW666Sb84x//wKxZs3DKKaegu7vb7aE5JhaLYdasWViyZEnRx2+//Xbcc889WLp0KV566SWEw2Gccsop6O/vr/JInbN69WosXLgQL774IlasWIGBgQGcfPLJiMVi2nOuueYaPPnkk/jNb36D1atX4/3338c555zj4qjtNWHCBNx666145ZVXsHbtWnz84x/HmWeeiX/9618Ahv/vP9Tf//53/PjHP8bMmTMLPj9SXoeDDz4Y27Zt0/49//zz2mMj4TXYvXs3jj32WPj9fjzzzDN4/fXX8cMf/hCjR4/WnjPcz41///vfC94DK1asAACcd955AKr4PlDJsA9/+MPqwoULtY/T6bTa0dGhLl682MVRVQ8A9fHHH9c+zmQy6rhx49Tvf//72uf27NmjBoNB9ZFHHnFhhNXR3d2tAlBXr16tqmr2d/b7/epvfvMb7TlvvPGGCkD929/+5tYwHTd69Gj1/vvvH3G/f29vr3rAAQeoK1asUE844QT16quvVlV15LwPbrrpJnXWrFlFHxspr8FXv/pV9bjjjiv5+Eg8N1599dXq1KlT1UwmU9X3AWdmDEomk3jllVcwd+5c7XMejwdz587F3/72NxdH5p7Nmzdj+/btBa9Jc3Mzjj766GH9muzduxcA0NLSAgB45ZVXMDAwUPA6TJ8+HRMnThyWr0M6ncajjz6KWCyGY445ZsT9/gsXLsTpp59e8PsCI+t98M4776CjowP7778/5s+fjy1btgAYOa/BE088gSOPPBLnnXcexowZg9mzZ+OnP/2p9vhIOzcmk0n88pe/xKWXXgpFUar6PmAwY9COHTuQTqcxduzYgs+PHTsW27dvd2lU7hK/90h6TTKZDL70pS/h2GOPxSGHHAIg+zoEAgGMGjWq4LnD7XV49dVX0dDQgGAwiCuuuAKPP/44ZsyYMWJ+fwB49NFH8Y9//AOLFy/e57GR8jocffTReOihh7B8+XLcd9992Lx5Mz72sY+ht7d3xLwG//73v3HffffhgAMOwLPPPovPf/7z+OIXv4iHH34YwMg7Ny5btgx79uzBxRdfDKC6x8Kw3zWbyAkLFy7Ea6+9VlAjMFJ86EMfwvr167F371789re/xYIFC7B69Wq3h1U1W7duxdVXX40VK1agrq7O7eG4pqurS/v/mTNn4uijj8akSZPw61//GvX19S6OrHoymQyOPPJIfO973wMAzJ49G6+99hqWLl2KBQsWuDy66nvggQfQ1dWFjo6Oqv9szswY1NbWBq/Xu0819gcffIBx48a5NCp3id97pLwmV111FZ566imsXLkSEyZM0D4/btw4JJNJ7Nmzp+D5w+11CAQCmDZtGo444ggsXrwYs2bNwt133z1ifv9XXnkF3d3dOPzww+Hz+eDz+bB69Wrcc8898Pl8GDt27Ih4HYYaNWoUDjzwQGzcuHHEvBfGjx+PGTNmFHzuoIMO0tJtI+nc+O677+JPf/oTLrvsMu1z1XwfMJgxKBAI4IgjjsCf//xn7XOZTAZ//vOfccwxx7g4MvdMmTIF48aNK3hNIpEIXnrppWH1mqiqiquuugqPP/44/vKXv2DKlCkFjx9xxBHw+/0Fr8Nbb72FLVu2DKvXYahMJoNEIjFifv+TTjoJr776KtavX6/9O/LIIzF//nzt/0fC6zBUNBrFpk2bMH78+BHzXjj22GP3ac/w9ttvY9KkSQBGzrkRAB588EGMGTMGp59+uva5qr4PbC0nHiEeffRRNRgMqg899JD6+uuvq5dffrk6atQodfv27W4PzTG9vb3qunXr1HXr1qkA1DvuuENdt26d+u6776qqqqq33nqrOmrUKPX3v/+9umHDBvXMM89Up0yZovb19bk8cvt8/vOfV5ubm9VVq1ap27Zt0/7F43HtOVdccYU6ceJE9S9/+Yu6du1a9ZhjjlGPOeYYF0dtr+uvv15dvXq1unnzZnXDhg3q9ddfryqKov7xj39UVXX4//6l5K9mUtWR8Tp8+ctfVletWqVu3rxZfeGFF9S5c+eqbW1tand3t6qqI+M1ePnll1Wfz6d+97vfVd955x31//7v/9RQKKT+8pe/1J4zEs6N6XRanThxovrVr351n8eq9T5gMGPSvffeq06cOFENBALqhz/8YfXFF190e0iOWrlypQpgn38LFixQVTW7BPEb3/iGOnbsWDUYDKonnXSS+tZbb7k7aJsV+/0BqA8++KD2nL6+PvXKK69UR48erYZCIfXss89Wt23b5t6gbXbppZeqkyZNUgOBgNre3q6edNJJWiCjqsP/9y9laDAzEl6HCy64QB0/frwaCATU/fbbT73gggvUjRs3ao+PhNdAVVX1ySefVA855BA1GAyq06dPV3/yk58UPD4Szo3PPvusCqDo71Wt94Giqqpq71wPERERUfWwZoaIiIhqGoMZIiIiqmkMZoiIiKimMZghIiKimsZghoiIiGoagxkiIiKqaQxmiIiIqKYxmCEiIqKaxmCGiAjAQw89hFGjRrk9DCIygcEMERmyfft2XH311Zg2bRrq6uowduxYHHvssbjvvvsQj8fdHp4ukydPxl133VXwuQsuuABvv/22OwMiIkt8bg+AiGrHv//9bxx77LEYNWoUvve97+HQQw9FMBjEq6++ip/85CfYb7/9cMYZZ7gyNlVVkU6n4fOZO63V19ejvr7e5lERUTVwZoaIdLvyyivh8/mwdu1anH/++TjooIOw//7748wzz8TTTz+NefPmAQD27NmDyy67DO3t7WhqasLHP/5x/POf/9S+z80334zDDjsMv/jFLzB58mQ0NzfjwgsvRG9vr/acTCaDxYsXY8qUKaivr8esWbPw29/+Vnt81apVUBQFzzzzDI444ggEg0E8//zz2LRpE84880yMHTsWDQ0NOOqoo/CnP/1J+7o5c+bg3XffxTXXXANFUaAoCoDiaab77rsPU6dORSAQwIc+9CH84he/KHhcURTcf//9OPvssxEKhXDAAQfgiSeesO31JiJ9GMwQkS47d+7EH//4RyxcuBDhcLjoc0RgcN5556G7uxvPPPMMXnnlFRx++OE46aSTsGvXLu25mzZtwrJly/DUU0/hqaeewurVq3Hrrbdqjy9evBg///nPsXTpUvzrX//CNddcg8985jNYvXp1wc+8/vrrceutt+KNN97AzJkzEY1Gcdppp+HPf/4z1q1bh1NPPRXz5s3Dli1bAACPPfYYJkyYgG9961vYtm0btm3bVvR3efzxx3H11Vfjy1/+Ml577TV87nOfwyWXXIKVK1cWPO+WW27B+eefjw0bNuC0007D/PnzC35PIqoC2/fhJqJh6cUXX1QBqI899ljB51tbW9VwOKyGw2H1K1/5ivrcc8+pTU1Nan9/f8Hzpk6dqv74xz9WVVVVb7rpJjUUCqmRSER7/LrrrlOPPvpoVVVVtb+/Xw2FQupf//rXgu/xP//zP+qnPvUpVVVVdeXKlSoAddmyZRXHfvDBB6v33nuv9vGkSZPUO++8s+A5Dz74oNrc3Kx9/NGPflT97Gc/W/Cc8847Tz3ttNO0jwGoX//617WPo9GoCkB95plnKo6JiOzDmhkisuTll19GJpPB/PnzkUgk8M9//hPRaBStra0Fz+vr68OmTZu0jydPnozGxkbt4/Hjx6O7uxsAsHHjRsTjcXziE58o+B7JZBKzZ88u+NyRRx5Z8HE0GsXNN9+Mp59+Gtu2bUMqlUJfX582M6PXG2+8gcsvv7zgc8ceeyzuvvvugs/NnDlT+/9wOIympibt9yCi6mAwQ0S6TJs2DYqi4K233ir4/P777w8AWvFsNBrF+PHjsWrVqn2+R35Nit/vL3hMURRkMhntewDA008/jf3226/gecFgsODjoSmva6+9FitWrMAPfvADTJs2DfX19fjkJz+JZDKp8zc1ptzvQUTVwWCGiHRpbW3FJz7xCfzoRz/CF77whZJ1M4cffji2b98On8+HyZMnm/pZM2bMQDAYxJYtW3DCCScY+toXXngBF198Mc4++2wA2cDoP//5T8FzAoEA0ul02e9z0EEH4YUXXsCCBQsKvveMGTMMjYeInMdghoh0+9///V8ce+yxOPLII3HzzTdj5syZ8Hg8+Pvf/44333wTRxxxBObOnYtjjjkGZ511Fm6//XYceOCBeP/99/H000/j7LPP3ictVExjYyOuvfZaXHPNNchkMjjuuOOwd+9evPDCC2hqaioIMIY64IAD8Nhjj2HevHlQFAXf+MY39pkpmTx5MtasWYMLL7wQwWAQbW1t+3yf6667Dueffz5mz56NuXPn4sknn8Rjjz1WsDKKiOTAYIaIdJs6dSrWrVuH733ve7jhhhvw3//+F8FgEDNmzMC1116LK6+8Eoqi4A9/+ANuvPFGXHLJJejp6cG4ceNw/PHHY+zYsbp/1re//W20t7dj8eLF+Pe//41Ro0bh8MMPx9e+9rWyX3fHHXfg0ksvxUc/+lG0tbXhq1/9KiKRSMFzvvWtb+Fzn/scpk6dikQiAVVV9/k+Z511Fu6++2784Ac/wNVXX40pU6bgwQcfxJw5c3T/DkRUHYpa7CgmIiIiqhHsM0NEREQ1jcEMERER1TQGM0RERFTTGMwQERFRTWMwQ0RERDWNwQwRERHVNAYzREREVNMYzBAREVFNYzBDRERENY3BDBEREdU0BjNERERU0/5/lT7Vxoq6/GkAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.plot(fitnesses)\n", "plt.xlabel('Generation')\n", "plt.ylabel('Fitness');" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.13 (WSL code-venv)", "language": "python", "name": "wsl-code-venv" }, "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.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }