diff --git a/Lab-1/ejemyr_lab1.ipynb b/Lab-1/ejemyr_lab1.ipynb
index bdccbcf..adacf45 100644
--- a/Lab-1/ejemyr_lab1.ipynb
+++ b/Lab-1/ejemyr_lab1.ipynb
@@ -3,8 +3,10 @@
"nbformat_minor": 0,
"metadata": {
"colab": {
- "name": "template-report-lab-X.ipynb",
+ "name": "ejemyr_lab1.ipynb",
"provenance": [],
+ "collapsed_sections": [],
+ "toc_visible": true,
"include_colab_link": true
},
"kernelspec": {
@@ -20,7 +22,7 @@
"colab_type": "text"
},
"source": [
- ""
+ ""
]
},
{
@@ -30,8 +32,8 @@
"colab_type": "text"
},
"source": [
- "# **Lab X: Title**\n",
- "**Johan Hoffman**"
+ "# **Lab 1: Title**\n",
+ "**Christoffer Ejemyr**"
]
},
{
@@ -44,32 +46,6 @@
"# **Abstract**"
]
},
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "6UFTSzW7P8kL",
- "colab_type": "text"
- },
- "source": [
- "\n",
- "\n",
- "```\n",
- "# This is formatted as code\n",
- "```\n",
- "\n",
- "Short summary of the lab report. State the objectives, methods used, main results and conlusions. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "yJipbXtnjrJZ",
- "colab_type": "text"
- },
- "source": [
- ""
- ]
- },
{
"cell_type": "markdown",
"metadata": {
@@ -80,22 +56,12 @@
"#**About the code**"
]
},
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "HmB2noTr1Oyo",
- "colab_type": "text"
- },
- "source": [
- "A short statement on who is the author of the file, and if the code is distributed under a certain license. "
- ]
- },
{
"cell_type": "code",
"metadata": {
"id": "Pdll1Xc9WP0e",
"colab_type": "code",
- "outputId": "f74fa781-413b-41e7-a2ec-1bba2288ad4f",
+ "outputId": "ce1a945e-2dae-4530-cb4d-1236274284c0",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
@@ -106,7 +72,7 @@
"\"\"\"DD2363 Methods in Scientific Computing, \"\"\"\n",
"\"\"\"KTH Royal Institute of Technology, Stockholm, Sweden.\"\"\"\n",
"\n",
- "# Copyright (C) 2019 Johan Hoffman (jhoffman@kth.se)\n",
+ "# Copyright (C) 2019 Christoffer Ejemyr (ejemyr@kth.se)\n",
"\n",
"# This file is part of the course DD2363 Methods in Scientific Computing\n",
"# KTH Royal Institute of Technology, Stockholm, Sweden\n",
@@ -114,12 +80,9 @@
"# This is free software: you can redistribute it and/or modify\n",
"# it under the terms of the GNU Lesser General Public License as published by\n",
"# the Free Software Foundation, either version 3 of the License, or\n",
- "# (at your option) any later version.\n",
- "\n",
- "# This template is maintained by Johan Hoffman\n",
- "# Please report problems to jhoffman@kth.se"
+ "# (at your option) any later version."
],
- "execution_count": 1,
+ "execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
@@ -131,7 +94,7 @@
"metadata": {
"tags": []
},
- "execution_count": 1
+ "execution_count": 8
}
]
},
@@ -145,16 +108,6 @@
"# **Set up environment**"
]
},
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "D2PYNusD08Wa",
- "colab_type": "text"
- },
- "source": [
- "To have access to the neccessary modules you have to run this cell. If you need additional modules, this is where you add them. "
- ]
- },
{
"cell_type": "code",
"metadata": {
@@ -168,11 +121,24 @@
"\n",
"import time\n",
"import numpy as np\n",
+ "import unittest\n",
+ "import sys\n",
"\n",
"from matplotlib import pyplot as plt\n",
"from matplotlib import tri\n",
"from matplotlib import axes\n",
- "from mpl_toolkits.mplot3d import Axes3D"
+ "from mpl_toolkits.mplot3d import Axes3D\n",
+ "\n",
+ "class Tests(unittest.TestCase):\n",
+ " @staticmethod\n",
+ " def check_accuracy(est, true, decimal):\n",
+ " np.testing.assert_almost_equal(est, true, decimal=decimal)\n",
+ "\n",
+ " @staticmethod\n",
+ " def check_accuracy_multiple_random(num_of_tests, generating_func, decimal):\n",
+ " for i in range(num_of_tests):\n",
+ " est, true = generating_func()\n",
+ " Tests.check_accuracy(est, true, decimal)\n"
],
"execution_count": 0,
"outputs": []
@@ -187,26 +153,6 @@
"# **Introduction**"
]
},
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "l5zMzgPlRAF6",
- "colab_type": "text"
- },
- "source": [
- "Give a short description of the problem investigated in the report, and provide some background information so that the reader can understand the context. \n",
- "\n",
- "Briefly describe what method you have chosen to solve the problem, and justify why you selected that method. \n",
- "\n",
- "Here you can express mathematics through Latex syntax, and use hyperlinks for references.\n",
- "\n",
- "[Hyperlink to DD2363 course website.](https://kth.instructure.com/courses/7500)\n",
- "\n",
- "$\n",
- "{\\displaystyle \\frac{\\partial u}{\\partial t}} + u\\cdot \\nabla u +\\nabla p = f, \\quad \\nabla \\cdot u=0$\n",
- "\n"
- ]
- },
{
"cell_type": "markdown",
"metadata": {
@@ -220,84 +166,75 @@
{
"cell_type": "markdown",
"metadata": {
- "id": "zF4iBj5VURZx",
+ "id": "uwVrMhGQ4viZ",
"colab_type": "text"
},
"source": [
- "Describe the methods you used to solve the problem. This may be a combination of text, mathematical formulas (Latex), algorithms (code), data and output. "
+ "### Scalar product"
]
},
{
- "cell_type": "markdown",
- "metadata": {
- "id": "SsQLT38gVbn_",
- "colab_type": "text"
- },
- "source": [
- "# **Results**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "RLwlnOzuV-Cd",
- "colab_type": "text"
- },
- "source": [
- "Present the results. If the result is an algorithm that you have described under the *Methods* section, you can present the data from verification and performance tests in this section. If the result is the output from a computational experiment this is where you present a selection of that data. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "_4GLBv0zWr7m",
- "colab_type": "text"
- },
- "source": [
- "# **Discussion**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "6bcsDSoRXHZe",
- "colab_type": "text"
- },
- "source": [
- "Summarize your results and your conclusions. Were the results expected or surprising. Do your results have implications outside the particular problem investigated in this report? "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "1vcIILuQYsEA",
- "colab_type": "text"
- },
- "source": [
- "# **APPENDIX**"
- ]
- },
- {
- "cell_type": "markdown",
+ "cell_type": "code",
"metadata": {
- "id": "NlJ-Y9KAYt4Z",
- "colab_type": "text"
+ "id": "jqrNhl6Np2Ze",
+ "colab_type": "code",
+ "colab": {}
},
"source": [
- "In this appendix some examples are given to express and visualizing mathematical concepts such as vectors, matrices, meshes and functions. \n",
+ "def scalar_product(x, y):\n",
+ " if type(x) != np.ndarray:\n",
+ " try:\n",
+ " x = np.array(x)\n",
+ " except:\n",
+ " raise Exception(\"Vector format error.\\n\" + \"x: \" + str(x) + \"\\ny: \" + str(y))\n",
+ " if type(y) != np.ndarray:\n",
+ " try:\n",
+ " y = np.array(y)\n",
+ " except:\n",
+ " raise Exception(\"Vector format error.\\n\" + \"x: \" + str(x) + \"\\ny: \" + str(y))\n",
+ "\n",
+ " if x.ndim != 1 or y.ndim != 1:\n",
+ " raise Exception(\"Vector dimentions error.\\n\" + \"x: \" + str(x) + \"\\ny: \" + str(y))\n",
+ " if x.size != y.size:\n",
+ " raise Exception(\"Vector formats don't agree.\\n\" + \"x: \" + str(x) + \"\\ny: \" + str(y))\n",
+ " \n",
+ " sum = 0\n",
+ " for i in range(len(x)):\n",
+ " sum += x[i] * y[i]\n",
"\n",
- "This is not part of the template report for the course."
- ]
+ " return sum"
+ ],
+ "execution_count": 0,
+ "outputs": []
},
{
"cell_type": "code",
"metadata": {
- "id": "Y-enWfBvcbRZ",
+ "id": "CDxXDYtrrQ-a",
"colab_type": "code",
"colab": {}
},
"source": [
- ""
+ "class Tests(Tests):\n",
+ " def test_scalar_product(self):\n",
+ " with self.assertRaises(Exception):\n",
+ " scalar_product([1,2], [1,2,3])\n",
+ " with self.assertRaises(Exception):\n",
+ " scalar_product([[0,0],[0,0]], [[0,0],[0,0]])\n",
+ " with self.assertRaises(Exception):\n",
+ " scalar_product([\"s\",2], [1,3])\n",
+ " with self.assertRaises(Exception):\n",
+ " scalar_product(\"Hej\", [1,2,3])\n",
+ "\n",
+ " min_length = 1\n",
+ " max_length = 100\n",
+ " def genetator():\n",
+ " n = np.random.randint(min_length, max_length)\n",
+ " a = np.random.rand(n)\n",
+ " b = np.random.rand(n)\n",
+ " return scalar_product(a, b), a.dot(b)\n",
+ "\n",
+ " Tests.check_accuracy_multiple_random(1000, genetator, 7)"
],
"execution_count": 0,
"outputs": []
@@ -305,758 +242,207 @@
{
"cell_type": "markdown",
"metadata": {
- "id": "I5Kf3qRyY5J5",
+ "id": "FNWnhrnD4jmh",
"colab_type": "text"
},
"source": [
- "# **Vector**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "SBKa3PEj0N9D",
- "colab_type": "text"
- },
- "source": [
- "In this cell some basic commands are introduced for how to define and process a vector, where the NumPy array data type is used. "
+ "### Matrix-vector product"
]
},
{
"cell_type": "code",
"metadata": {
+ "id": "j7rPWYYP4iUV",
"colab_type": "code",
- "outputId": "47fb584b-290b-4c38-d419-44bb1af8d79c",
- "id": "PbnT724f5mfQ",
- "colab": {
- "base_uri": "https://localhost:8080/",
- "height": 571
- }
+ "colab": {}
},
"source": [
- "# Just a test\n",
- "# Create a vector as a NumPy array\n",
- "x = np.array([1,2,3,4])\n",
- "\n",
- "# Print various attributes of the vector\n",
- "print('x =',x)\n",
- "print('x[2] =',x[2])\n",
- "print('x[0:2] =',x[0:2])\n",
- "print('x.shape =',x.shape)\n",
- "print('x.ndim =',x.ndim)\n",
- "print('x.size =',x.size)\n",
- "print('x.dtype =',x.dtype)\n",
- "\n",
- "# Print the elements of vector x\n",
- "for i in range(x.size):\n",
- " print(\"x[\",i,\"] =\",x[i])\n",
- "\n",
- "# Assign a float value to the int vector x\n",
- "x[1] = 2.1\n",
- "print('x =',x)\n",
- "\n",
- "# Copy the int vector x to a float vector xf\n",
- "xf = x.astype(float)\n",
- "xf[1] = 2.1\n",
- "print('xf =',xf)\n",
- "print('xf.dtype =',xf.dtype)\n",
- "\n",
- "# Create a complex vector \n",
- "y = np.array([2,3,4], dtype=complex)\n",
- "print('y =',y)\n",
- "print('y.dtype =',y.dtype)\n",
- "\n",
- "# Create a vector z by defining a range through an increment\n",
- "z = np.arange(12, 28, 4.5)\n",
- "print('z =',z)\n",
- "\n",
- "# Overwrite the vector z by a vector of uniformly spaced elements in a range\n",
- "pi=np.pi\n",
- "pi\n",
- "z = np.linspace(0, 2*pi, 5)\n",
- "print('z =',z)\n",
- "\n",
- "# Visualize the vector in a plot. \n",
- "plt.figure()\n",
- "plt.plot(x,2*x,'ro-')\n",
- "plt.show()"
+ "def matrix_vector_product(M, x):\n",
+ " if type(M) != np.ndarray:\n",
+ " try:\n",
+ " M = np.array(M)\n",
+ " except:\n",
+ " raise Exception(\"Matrix format error.\\n\" + \"M: \" + str(M))\n",
+ " if type(x) != np.ndarray:\n",
+ " try:\n",
+ " x = np.array(x)\n",
+ " except:\n",
+ " raise Exception(\"Vector format error.\\n\" + \"x: \" + str(x))\n",
+ "\n",
+ " if x.ndim != 1 or M.ndim != 2:\n",
+ " raise Exception(\"Matrix or vector dimentions error.\\n\" + \"M: \" + str(M) + \"\\nx: \" + str(x))\n",
+ " if M.shape[1] != x.size:\n",
+ " raise Exception(\"Matrix and vector formats don't agree.\\n\" + \"M: \" + str(M) + \"\\nx: \" + str(x))\n",
+ "\n",
+ " b = np.zeros(M.shape[0])\n",
+ " for i in range(M.shape[0]):\n",
+ " for j in range(M.shape[1]):\n",
+ " b[i] += M[i, j] * x[j]\n",
+ "\n",
+ " return b"
],
- "execution_count": 3,
- "outputs": [
- {
- "output_type": "stream",
- "text": [
- "x = [1 2 3 4]\n",
- "x[2] = 3\n",
- "x[0:2] = [1 2]\n",
- "x.shape = (4,)\n",
- "x.ndim = 1\n",
- "x.size = 4\n",
- "x.dtype = int64\n",
- "x[ 0 ] = 1\n",
- "x[ 1 ] = 2\n",
- "x[ 2 ] = 3\n",
- "x[ 3 ] = 4\n",
- "x = [1 2 3 4]\n",
- "xf = [1. 2.1 3. 4. ]\n",
- "xf.dtype = float64\n",
- "y = [2.+0.j 3.+0.j 4.+0.j]\n",
- "y.dtype = complex128\n",
- "z = [12. 16.5 21. 25.5]\n",
- "z = [0. 1.57079633 3.14159265 4.71238898 6.28318531]\n"
- ],
- "name": "stdout"
- },
- {
- "output_type": "display_data",
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAWoAAAD4CAYAAADFAawfAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjIsIGh0\ndHA6Ly9tYXRwbG90bGliLm9yZy8li6FKAAAcRklEQVR4nO3de7zVc77H8denXHMf7WFo2M6cx6Ci\nZGtKLkM4GdfHmHEQgw4Z16EZxtExZgxn3DJEp9qVazu3lEhKJEmUXdFVNEmlRjuXXCLV/pw/Ptsg\ne7fXrrX277fWej8fj/3Yl7XU5+ent0/f3/di7o6IiKRXk6QLEBGRDVNQi4iknIJaRCTlFNQiIimn\noBYRSbnNcvGLNm/e3EtLS3PxS4uIFKSpU6eucPeS2l7LSVCXlpZSWVmZi19aRKQgmdm7db2moQ8R\nkZRTUIuIpJyCWkQk5RTUIiIpp6AWEUm5jILazK4ws9lmNsvMHjKzrXJdmIhI3qiogNJSaNIkPldU\nZPWXrzeozWx34DKgzN1bA02B07JahYhIvqqogO7d4d13wT0+d++e1bDOdOhjM2BrM9sMaAYszVoF\nIiL5rGdPWLXquz9btSp+niX1BrW7vwfcBiwClgEr3f3Z9d9nZt3NrNLMKquqqrJWoIhIqi1a1LCf\nb4RMhj52Ak4C9gJ2A7YxszPXf5+7l7t7mbuXlZTUugpSRKSwjBkT49K12WOPrP02mQx9HAW84+5V\n7r4GGAYcnLUKRETyzQcfwNlnQ5cusMsusOWW3329WTO48cas/XaZBPUioIOZNTMzAzoDc7NWgYhI\nvnCHoUOhZUsYMgT+539gwQIYNAj23BPM4nN5OXTtmrXftt5Nmdx9spkNBaYBa4HpQHnWKhARyQfL\nlsHFF8Pw4XDggfDss9CmTbzWtWtWg3l9Ge2e5+7XAdflrAoRkbRyh/vugx494Msv4eab4+vNcrL5\naK0a73cSEck377wTc6Kfew4OOwwGDICf/rTRy9ASchGR9a1bB3feCa1bw+TJ0LcvvPBCIiEN6qhF\nRL5rzhw47zx45RU49ljo3x9+/ONES1JHLSICsGYN3HADHHAAvPUWDB4MTz+deEiDOmoREZg6Fbp1\ngxkz4D//E3r3hh/+MOmq/kUdtYgUry++gD/+Edq3hxUr4Ikn4OGHUxXSoI5aRIrViy/C+efD22/H\n51tugR13TLqqWqmjFpHi8skncOGF8POfx+yO55+PlYQpDWlQUItIMRk1Clq1imDu0SPGpI88Mumq\n6qWgFpHCt2IFnHkmHHccbL89TJoEvXrBNtskXVlGFNQiUrjc4ZFHYhOlRx+F666DadPgZz9LurIG\n0cNEESlM770HF10ETz4JBx0UO9ztt1/SVW0UddQiUljcY0+Oli1h7Fi47bZYZZinIQ3qqEWkkPzj\nHzHV7oUXYlbHgAHw7/+edFWbTB21iOS/devg9tuja546NfbneP75gghpUEctIvlu1iz4r/+CKVPg\n+ONjp7sWLZKuKqvUUYtIfvrqK/jLX6BduzgO66GH4sFhgYU0qKMWkXw0ZUp00bNmwRlnxN7RzZsn\nXVXOqKMWkfyxahX84Q/QsSN89BE89RRUVBR0SIM6ahHJFy+8EBv6L1gAF1wQZxfusEPSVTUKddQi\nkm4rV0YwH3kkNGkSgd2vX9GENCioRSTNnnoqFq4MHAhXXglvvBHzo4uMglpE0qeqCk4/HU48EXbe\nOQ6YveUWaNYs6coSoaAWkfRwhyFDYN994fHH4frrobISysqSrixR9Qa1me1tZq9/6+MTM7u8MYoT\nkSKyeDGccAJ07RorCqdPh2uvhS22SLqyxNU768Pd5wFtAcysKfAeMDzHdYlIsaiujj05rrwyloL/\n/e9w6aXQtGnSlaVGQ6fndQb+4e7v5qIYESkyX59X+OKL0LlznLzyb/+WdFWp09Ax6tOAh2p7wcy6\nm1mlmVVWVVVtemUiUrjWroVbb4X994fXX4+9oseOVUjXIeOgNrMtgBOBx2p73d3L3b3M3ctKSkqy\nVZ+IFJoZM2Jl4VVXwX/8B8yZA926gVnSlaVWQzrqY4Fp7v5+rooRkQK2ejX86U9w4IGwaFEcjTV8\nOOy2W9KVpV5DxqhPp45hDxGRDXr11dhEac4cOOuseGC4885JV5U3MuqozWwb4GhgWG7LEZGC8vnn\ncMUVcPDB8OmnMGoUPPCAQrqBMuqo3f1zQP9mRSRzzz0XMzoWLoSLL4a//Q222y7pqvKSViaKSHZ9\n/HEMcxx9NGy+OUyYAHffrZDeBApqEcmeJ56ITZTuvx+uvjo2UTr00KSrynvaj1pENt3778dqwsce\ng7ZtYeTIOCJLskIdtYhsPHd48MHookeMgBtvjGOyFNJZpY5aRDbOokWxof/o0TGrY9Ag2GefpKsq\nSOqoRaRhqquhTx9o1Qpeegl6947PCumcUUctIpmbNy/OLZw4MWZ1lJdDaWnSVRU8ddQiUr+1a+Gm\nm6BNG5g1C+69F8aMUUg3EnXUIrJhr78e86KnTYNTTok50bvumnRVRUUdtYjU7ssvoWfPOAbrvfdg\n6ND4UEg3OnXUIvJ9L78cXfS8eXDOOdCrF/zgB0lXVbTUUYvINz77DC67LFYTfvlljEPfe69COmEK\nahEJzz4LrVvHGPQll8RDw2OOSboqQUEtIh9+COeeG6etbL31N3Ojt9026cqkhoJapJg9/ngs/37w\nwXhwOH06dOqUdFWyHj1MFClGy5bF8MawYXDAAbEMvG3bpKuSOqijFikm7nDffdFFP/10LGKZMkUh\nnXLqqEWKxcKF0L07jB0LhxwCAwfC3nsnXZVkQB21SKGrroa77ooZHa+8EhsqvfiiQjqPqKMWKWRz\n58YmSpMmQZcu0L8/7LFH0lVJA6mjFilEa9bEJv5t28Kbb8bJ36NGKaTzlDpqkUIzbRp06xbnFZ56\nasyJ3mWXpKuSTaCOWqRQfPFFHCjbvn2cYTh8ODzyiEK6AKijFikEL70UY9FvvRWbKd16K+y0U9JV\nSZZk1FGb2Y5mNtTM3jSzuWbWMdeFiUgdKipiw/4mTWLM+eij4bDDYlz6uedi2p1CuqBk2lHfCYx2\n91+Z2RZAsxzWJCJ1qaiIudCrVsX3ixfHR5cusVf0NtskW5/kRL1BbWY7AIcB5wC4+1fAV7ktS0Rq\n1bPnNyH9bXPnKqQLWCZDH3sBVcC9ZjbdzAaa2ff+izCz7mZWaWaVVVVVWS9UpOi5w6JFtb9W18+l\nIGQS1JsB7YC+7n4A8Dlw9fpvcvdydy9z97KSkpIslylS5JYuhV/+MsK6NpofXdAyCeolwBJ3n1zz\n/VAiuEUk19xh0KDYRGn0aDj9dGi23iOiZs1icYsUrHqD2t3/CSw2s683BugMzMlpVSICCxbAUUfF\ntLu2bWHmTBgyBMrLYc89wSw+l5dD165JVys5lOmsj0uBipoZHwuAc3NXkkiRW7cuNlHq2ROaNoV+\n/eD882M6HkQoK5iLSkZB7e6vA2U5rkVEZs+OBSuTJ8Nxx0VIt2iRdFWSMC0hF0mDr76Cv/41TluZ\nPz/mSz/1lEJaAC0hF0nea69FFz1zJpx2WmyipJlT8i3qqEWSsmoVXHkldOgAH3wAI0bAQw8ppOV7\n1FGLJGH8+HhAOH9+LAm/5RbYYYekq5KUUkct0phWroTf/haOOCLmSI8bF6euKKRlAxTUIo3l6aeh\nVSsYMAB+/3uYMSMCW6QeCmqRXKuqinnPxx8f24++8grcdtv3VxiK1EFBLZIr7vDww7H8+7HH4M9/\nhqlT4wQWkQbQw0SRXFiyBC66KOZCt28f+3W0bp10VZKn1FGLZFN1dey90apVnLbSqxdMmqSQlk2i\njlokW+bPjyl348fHQ8IBA+AnP0m6KikA6qhFNtW6ddE5778/TJsWAf388wppyRp11CKbYtYs6NYt\nloGfcAL07Qu77550VVJg1FGLbIyvvopZHO3awcKFMbtjxAiFtOSEOmqRhpo8OTZRmj075kffcQc0\nb550VVLA1FGLZOrzz6FHD+jYMZaCjxwJgwcrpCXn1FGLZGLcuJjRsWBB7NVx882w/fZJVyVFQh21\nyIZ8/HEEdOfOcRTW+PHxwFAhLY1IQS1SlyefjIUr99wDV10VmygdfnjSVUkRUlCLrG/58jhp5aST\nYOed4+HhzTfD1lsnXZkUKQW1yNfc46zCli1h+PA4w7CyEsp0rrMkSw8TRQAWL46HhKNGxdFYgwZF\nYIukgDpqKW7V1fFwsFWreFB4xx0wcaJCWlJFHbUUr7ffhvPOgwkT4KijYte7vfZKuiqR78koqM1s\nIfApsA5Y6+4atJP8tXYt3H47XHcdbLllDHOcey6YJV2ZSK0a0lEf4e4rclaJSGN4441Y/j11Kpx8\nMvTpA7vtlnRVIhukMWopDqtXw7XXxgyOxYvh0Udh2DCFtOSFTIPagWfNbKqZda/tDWbW3cwqzayy\nqqoqexWKbKpXXoEDDoAbboAzzoA5c+DXv9ZQh+SNTIP6EHdvBxwLXGxmh63/Bncvd/cydy8rKSnJ\napEiG+Wzz+Dyy6FTp/h61Ci4//5YxCKSRzIKand/r+bzcmA4oGOUJd3GjoX99oM774xDZmfPhmOP\nTboqkY1Sb1Cb2TZmtt3XXwPHALNyXZjIRvnoo3hYeMwxsMUWMfXu7rthu+2Srkxko2Uy62MXYLjF\neN5mwBB3H53TqkQ2xvDh0T1XVcHVV8f0u622SroqkU1Wb1C7+wKgTSPUIrJx3n8fLr0UHnsM2raF\np5+OI7JECoSm50n+cocHHoB9943zCm+8EaZMUUhLwdEScslP774LF1wAY8bAwQfH6sJ99km6KpGc\nUEct+aW6OlYTtm4dmyf17g0vvaSQloKmjlryx7x5sYnSxIkxq6N/fygtTboqkZxTRy3pt2YN3HQT\ntGkT86Hvuw9Gj1ZIS9FQRy3pNn16zIuePh1OOSXmRO+6a9JViTQqddSSTl9+CddcAwcdBEuXwtCh\n8aGQliKkjlrS5+WXo4ueNw/OOQd69YIf/CDpqkQSo45a0uPTT2PhyqGHRkc9Zgzce69CWoqeglrS\nYcyYmHLXpw9ccgnMmhUzO0REQS0J+/DDGN7o0gWaNYs50b17w7bbJl2ZSGooqCU5jz8ep30PHgw9\ne8bMjk6dkq5KJHX0MFEa37JlMbwxbFicvDJ6dGymJCK1Ukctjcc9Hg62bBk73N10U2yipJAW2SB1\n1NI4Fi6E7t3j5JVDDoGBA2HvvZOuSiQvqKOW3Fq3Lh4Otm4dh8z26QMvvqiQFmkAddSSO3PnxiZK\nkybFrI5+/WDPPZOuSiTvqKOW7FuzJjbxb9sW3nwzNvcfNUohLbKR1FFLdk2dCt26wYwZcOqpMeyx\nyy5JVyWS19RRS3Z88UUcKPuzn8Hy5XHQ7COPKKRFskAdtWy6CRNiLPrtt2MzpVtvhZ12SroqkYKh\njlo23iefwMUXw+GHx7j02LEx7U4hLZJVCmrZOM88E1Pu+vaFyy+PTZSOOirpqkQKkoJaGmbFCjjr\nLPjFL2LjpJdfhr//HbbZJunKRApWxkFtZk3NbLqZjcxlQZIiFRVxLmGTJjG17tJLY/n3ww/DtdfG\nJkodOyZdpUjBa8jDxN8Bc4Htc1SLpElFRSz5XrUqvl+0KM4rLC2F556D/fdPtDyRYpJRR21mLYDj\ngIG5LUdSo2fPb0L626qrFdIijSzToY87gKuA6rreYGbdzazSzCqrqqqyUpwkaNGi2n++eHHj1iEi\n9Qe1mR0PLHf3qRt6n7uXu3uZu5eVlJRkrUBpZOvWxcPBuuyxR+PVIiJAZh11J+BEM1sIPAwcaWaD\nc1qVJGP27DhhpUePGN7Yeuvvvt6sWezhISKNqt6gdvf/dvcW7l4KnAaMc/czc16ZNJ6vvoLrr4/T\nVubPjweJ06fDgAEx28MsPpeXQ9euSVcrUnS0hLzYvfZaLPueORNOOy02Ufp66KprVwWzSAo0aMGL\nu4939+NzVYw0olWr4MoroUMH+OADGDECHnrom5AWkdRQR12Mxo+H88+PYY7zz49NlHbYIemqRKQO\nWkJeTFauhN/+Fo44IuZDP/98jDsrpEVSTUFdLEaOhFat4gFhjx4xJn3kkUlXJSIZUFAXuqoqOOMM\nOOEE2HHHOL+wV6+YaicieUFBXajc4+Fgy5YwdCj8+c8wbVqcwCIieUUPEwvRkiVw4YUx3NG+PQwa\nFHtHi0heUkddSKqr4+Fgq1bxoLBXrxjqUEiL5DV11IXi66l248fHrI4BA+AnP0m6KhHJAnXU+W7t\nWrjtNthvvxiDLi+PblohLVIw1FHns5kzY/n3a6/FrI6+fWH33ZOuSkSyTB11Plq9Gq67Dtq1g4UL\n42isESMU0iIFSh11vpk8Obro2bNjw6Q77oDmzZOuSkRySB11vvj881hR2LFjLAUfORIGD1ZIixQB\nddT5YNy4mNGxYEHs1XHzzbC9zhgWKRbqqNPs448joDt3hiZNYupd374KaZEio6BOqxEjYvn3PffA\nVVfBjBlw+OFJVyUiCVBQp83y5XHSysknx/jz5Mkx1LH++YUiUjQU1GnhHg8H990Xhg+Hv/4VKiuh\nrCzpykQkYXqYmAaLF8dDwlGj4misQYNi2ENEBHXUyaqujoeDrVrFg8I77oCJExXSIvId6qiT8tZb\ncN558NJLcNRRsUfHXnslXZWIpJA66sa2di3ccgu0aRMzOQYNgmefVUiLSJ3UUTemN96Abt1il7uT\nT4Y+fWC33ZKuSkRSTh11Y1i9Gq69NmZwLFkCjz4Kw4YppEUkI/V21Ga2FTAB2LLm/UPd/bpcF1Yw\nJk2Ksei5c+E3v4Hbb4edd066KhHJI5l01KuBI929DdAW6GJmHXJbVgH47DP43e/gkENiQ6VnnoH7\n71dIi0iD1dtRu7sDn9V8u3nNh+eyqLw3dix07x57RV98Mfztb7DddklXJSJ5KqMxajNramavA8uB\nse4+uZb3dDezSjOrrKqqynad+eGjj+Jh4THHwBZbwIQJcPfdCmkR2SQZBbW7r3P3tkALoL2Zfe9Y\na3cvd/cydy8rKSnJdp3pN3x4LFR54AG4+uqY4XHooUlXJSIFoEGzPtz9Y+AFoEtuyslD//wn/PrX\n8Mtfwq67wpQpMdSx1VZJVyYiBaLeoDazEjPbsebrrYGjgTdzXVjqucfDwZYt4amn4H//N0K6Xbuk\nKxORApPJgpcfAfebWVMi2B9195G5LSvl3n0XLrgAxoyBgw+O1YX77JN0VSJSoDKZ9TEDOKARakm/\n6mr4v/+LMWiAu+6Ciy6K01dERHJES8gzNW9enP798ssxq6N/fygtTboqESkCagXrs2ZNPBxs0wbm\nzIH77oPRoxXSItJo1FFvyPTp0UVPnw6nnBJzonfdNemqRKTIqKOuzZdfwjXXwEEHwdKlMHRofCik\nRSQB6qjXN3FidNFvvQXnngu9esFOOyVdlYgUMXXUX/v0U7jkklhNuHp1TL275x6FtIgkTkENEcqt\nW8fUu8sug1mzYmaHiEgKFHdQf/ghnH02dOkCzZrF+YV33gnbbpt0ZSIi/1K8QT10KOy7LwwZAj17\nxsyOTp2SrkpE5HuK72HismWxR/Tw4bEvx5gx0LZt0lWJiNSpeDpqd7j33thEadQouOkmmDxZIS0i\nqVccHfU778SJK889F7M6Bg6En/406apERDJS2B31unXQu3fM6Hj1VejTB8aPV0iLSF4p3I567txY\nuPLKKzGro39/2GOPpKsSEWmwwuuo16yBG26Ised58+DBB2NMWiEtInmqsDrqqVPjcNkZM+DUU2O/\n6B/+MOmqREQ2SWF01F98AX/8I7RvD1VVMfXukUcU0iJSEPK/o54wAc47D95+O8akb7sNdtwx6apE\nRLImfzvqTz6JY7AOPxzWro2pdwMHKqRFpODkZ1CPGhVT7vr1gyuugJkzoXPnpKsSEcmJ/Br6WLEi\ngnnw4FhhOGkSdOiQdFUiIjmVHx21ezwcbNkSHn4Y/vQnmDZNIS0iRSH9HfXSpXDhhfDkk1BWFmPR\n+++fdFUiIo0mvR21ezwcbNkSnn0Wbr01VhkqpEWkyNQb1Gb2YzN7wczmmNlsM/tdTiqpqIDSUmjS\nBHbfPR4Wnn9+rDCcORP+8AfYLP1/ARARybZMkm8t8Ht3n2Zm2wFTzWysu8/JWhUVFbG73apV8f3S\npfFx7rnRVTdJb+MvIpJr9Saguy9z92k1X38KzAV2z2oVPXt+E9LfNm6cQlpEil6DUtDMSoEDgMm1\nvNbdzCrNrLKqqqphVSxa1LCfi4gUkYyD2sy2BR4HLnf3T9Z/3d3L3b3M3ctKSkoaVkVdO9tpxzsR\nkcyC2sw2J0K6wt2HZb2KG2+MU8C/rVmz+LmISJHLZNaHAYOAue5+e06q6NoVysthzz3BLD6Xl8fP\nRUSKnLn7ht9gdgjwEjATqK758TXuPqquf6asrMwrKyuzVqSISKEzs6nuXlbba/VOz3P3iYBlvSoR\nEcmI5r6JiKScglpEJOUU1CIiKaegFhFJuXpnfWzUL2pWBby7kf94c2BFFstJUqFcS6FcB+ha0qhQ\nrgM27Vr2dPdaVwvmJKg3hZlV1jVFJd8UyrUUynWAriWNCuU6IHfXoqEPEZGUU1CLiKRcGoO6POkC\nsqhQrqVQrgN0LWlUKNcBObqW1I1Ri4jId6WxoxYRkW9RUIuIpFwiQW1m95jZcjObVcfrZma9zWy+\nmc0ws3aNXWOmMriWn5vZSjN7vebjT41dYyYyOcQ4X+5LhteSL/dlKzObYmZv1FzLX2p5z5Zm9kjN\nfZlccxJTqmR4HeeYWdW37sl5SdSaKTNrambTzWxkLa9l9564e6N/AIcB7YBZdbz+C+AZYte+DsDk\nJOrM0rX8HBiZdJ0ZXMePgHY1X28HvAW0zMf7kuG15Mt9MWDbmq83J47B67Deey4C+tV8fRrwSNJ1\nb+R1nAPcnXStDbimHsCQ2v47yvY9SaSjdvcJwIcbeMtJwAMeXgV2NLMfNU51DZPBteQFz+wQ47y4\nLxleS16o+Xf9Wc23m9d8rD8D4CTg/pqvhwKdaw78SI0MryNvmFkL4DhgYB1vyeo9SesY9e7A4m99\nv4Q8/YNWo2PNX/meMbNWSRdTnw0cYpx392VDBzKTJ/el5q/YrwPLgbHuXud9cfe1wEpg58atsn4Z\nXAfAKTXDakPN7MeNXGJD3AFcxTeHqawvq/ckrUFdSKYRa/jbAHcBTyRczwbVd4hxPqnnWvLmvrj7\nOndvC7QA2ptZ66Rr2hgZXMdTQKm77w+M5ZuONFXM7HhgubtPbazfM61B/R7w7f+btqj5Wd5x90++\n/iufx/Flm5tZ84TLqlUGhxjnzX2p71ry6b58zd0/Bl4Auqz30r/ui5ltBuwAfNC41WWurutw9w/c\nfXXNtwOBAxu7tgx1Ak40s4XAw8CRZjZ4vfdk9Z6kNaifBH5TM8ugA7DS3ZclXdTGMLNdvx6bMrP2\nxL/z1P0hyvAQ47y4L5lcSx7dlxIz27Hm662Bo4E313vbk8DZNV//ChjnNU+x0iKT61jveceJxLOF\n1HH3/3b3Fu5eSjwoHOfuZ673tqzek3rPTMwFM3uIeOre3MyWANcRDxdw937AKGKGwXxgFXBuEnVm\nIoNr+RVwoZmtBb4ATkvbH6IanYCzgJk144gA1wB7QN7dl0yuJV/uy4+A+82sKfE/k0fdfaSZXQ9U\nuvuTxP+UHjSz+cSD7dOSK7dOmVzHZWZ2IrCWuI5zEqt2I+TynmgJuYhIyqV16ENERGooqEVEUk5B\nLSKScgpqEZGUU1CLiKScglpEJOUU1CIiKff/WIFvQA/70FEAAAAASUVORK5CYII=\n",
- "text/plain": [
- "