├── .gitignore
├── 01PythonBasics.ipynb
├── 02ConditionalsAndLists.ipynb
├── 03NumpyAndPlotting.ipynb
├── 04FlowPlotExample.ipynb
├── 05SciPy.ipynb
├── LICENSE
├── README.md
├── Scipy.ipynb
├── SharedScreenshot.jpg
└── TablesAnalysis.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 | *checkpoint.ipynb
2 |
--------------------------------------------------------------------------------
/01PythonBasics.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "colab_type": "text",
7 | "id": "view-in-github"
8 | },
9 | "source": [
10 | ""
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "metadata": {
16 | "id": "umNbE5lyd6bT"
17 | },
18 | "source": [
19 | "# Getting Started with Python Notebooks\n"
20 | ]
21 | },
22 | {
23 | "cell_type": "markdown",
24 | "metadata": {
25 | "id": "ozUUhZEZd6bT"
26 | },
27 | "source": [
28 | "**Hello**! Numerical programming and analysis is becoming a key skill for modern engineers and you will need to use Python in some of your modules for labs and coursework. This is a quick introduction to numerical programming in [Python](https://www.python.org/) to help you get started. "
29 | ]
30 | },
31 | {
32 | "cell_type": "markdown",
33 | "metadata": {
34 | "id": "xk7BbJKJd6bT"
35 | },
36 | "source": [
37 | "# Overview\n",
38 | "\n",
39 | "## Jupyter notebooks\n",
40 | "\n",
41 | "This is a [Jupyter](https://jupyter.org/) notebook. A notebook is a series of cells, which contain either markdown text, or python code. You can use the dropdown menu in the toolbar at the top to change between the two. Double click on this cell to see the markdown text. Click play on the toolbar, or [shift]+[enter], to render it.\n",
42 | "\n",
43 | "A markdown cell lets you write simple text (for your discussions), but it also lets you write equations $\\frac{d}{dt}(mv)=\\sum f$, embed [links](http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Working%20With%20Markdown%20Cells.html) ,and display images \n",
44 | "\n",
45 | "\n",
46 | "\n",
47 | "But we're not here for markdown, we're here for python! The next cell is a python cell. Click the \"play\" button on the left or hit [shift]+[enter] in the cell to evaluate it."
48 | ]
49 | },
50 | {
51 | "cell_type": "code",
52 | "execution_count": null,
53 | "metadata": {
54 | "id": "JuiUvatAGDl7"
55 | },
56 | "outputs": [],
57 | "source": [
58 | "name = \"Gabriel\"\n",
59 | "print(\"Hello World. My name is \"+name+\".\")"
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {
65 | "id": "YrEs3TJPGEEA"
66 | },
67 | "source": [
68 | "Try changing the name and rerun the cell to update the output.\n",
69 | "\n",
70 | "The rest of these notebooks will give a tutorial of the python language below, but as far as the notebook element of coding, the most import thing is that the cells don't run in order automatically. So #1 tip is: **Try to keep code together in a single cell block when possible. When it isn't possible, make sure to restart the kernel and `Run all` cells in the notebook frequently.** That will avoid unpleasant surprises."
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {
76 | "id": "2C2W2YcHfbUO"
77 | },
78 | "source": [
79 | "### Online versus local \n",
80 | "I've put a copy of this notebook on [google colab](https://research.google.com/colaboratory/faq.html) so you can run it using your web-browser. This is extremely convienient, but there are advantages to running notebooks on your own machine. To do this, you need to download the `ipynb` file and [install Anaconda](https://docs.anaconda.com/anaconda/install/). This will install `Python 3` on your machine and the `jupyter` notebook environment, letting you run the notebooks locally."
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "metadata": {
86 | "id": "Pp-tpCmAd6bW"
87 | },
88 | "source": [
89 | "# Python basics\n",
90 | "\n",
91 | "The example above sets the **variable** `name` and then uses the `+` **operation** and uses a `print` **function** to show the result. Variables operations and functions are probably the most fundamental elements of a programming language, and this notebook will introduce the syntax of these elements in python."
92 | ]
93 | },
94 | {
95 | "cell_type": "markdown",
96 | "metadata": {
97 | "id": "vNcd5wzHd6bW"
98 | },
99 | "source": [
100 | "## Variables\n",
101 | "\n",
102 | "Python doesn't require explicitly declared variable types, like C and other languages do. Just assign a variable and Python understands what you want:"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": null,
108 | "metadata": {
109 | "id": "5blI8MIRd6bW"
110 | },
111 | "outputs": [],
112 | "source": [
113 | "a = 5 # a is an integer 5\n",
114 | "b = 'five' # b is a string of the word 'five'\n",
115 | "c = 5.0 # c is a floating point 5 "
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "metadata": {
121 | "id": "Tx4eG9sOd6bW"
122 | },
123 | "source": [
124 | "Ask Python to tell you what type it has assigned to a given variable name like this:"
125 | ]
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": null,
130 | "metadata": {
131 | "id": "QUy5tSUWd6bW"
132 | },
133 | "outputs": [],
134 | "source": [
135 | "type(a), type(b), type(c)"
136 | ]
137 | },
138 | {
139 | "cell_type": "markdown",
140 | "metadata": {
141 | "id": "jZSvwD-sH8FW"
142 | },
143 | "source": [
144 | "Note that nothing was printed to screen after you ran the python cell assigning the variables. However, since nothing is assigned in the cell evalauting the variable types, the notebook defaults to printing the result."
145 | ]
146 | },
147 | {
148 | "cell_type": "markdown",
149 | "metadata": {
150 | "id": "we9yKEbWSGJI"
151 | },
152 | "source": [
153 | "## Operations\n",
154 | "\n",
155 | "Now that we have variables, we can combine them with operations. Python has many built in operations, but we will just need a few\n",
156 | "\n",
157 | "| Operator | Name | Description |\n",
158 | "|--------------|----------------|--------------------------------------------------------|\n",
159 | "| ``a + b`` | Addition | Sum of ``a`` and ``b`` |\n",
160 | "| ``a - b`` | Subtraction | Difference of ``a`` and ``b`` |\n",
161 | "| ``a * b`` | Multiplication | Product of ``a`` and ``b`` |\n",
162 | "| ``a / b`` | True division | Quotient of ``a`` and ``b`` |\n",
163 | "| ``a // b`` | Floor division | Quotient of ``a`` and ``b``, removing fractional parts |\n",
164 | "| ``a % b`` | Modulus | Integer remainder after division of ``a`` by ``b`` |\n",
165 | "| ``a ** b`` | Exponentiation | ``a`` raised to the power of ``b`` |\n",
166 | "| ``-a`` | Negation | The negative of ``a`` |\n",
167 | "\n",
168 | "\n",
169 | "\n",
170 | "These should all be familiar to you except possibly floor division and the modulus operator, but these are both very useful in engineering as we will see in the later examples. \n",
171 | "\n",
172 | "For now, take a look at the operations below and guess what they will evaluate to. Then run the cell and see if you were right. A few of these might surprise you!"
173 | ]
174 | },
175 | {
176 | "cell_type": "code",
177 | "execution_count": null,
178 | "metadata": {
179 | "id": "abbp1bE0UerS"
180 | },
181 | "outputs": [],
182 | "source": [
183 | "print(4*5)\n",
184 | "print(4.0*5.0)\n",
185 | "print(\"hi \"*5)\n",
186 | "\n",
187 | "print(5/4)\n",
188 | "print(5.0/4.0)\n",
189 | "print(5//4)\n",
190 | "print(5/2*2)\n",
191 | "\n",
192 | "print(4%5)\n",
193 | "print(5%4)"
194 | ]
195 | },
196 | {
197 | "cell_type": "markdown",
198 | "metadata": {
199 | "id": "S3Zmxz4Jd6ba"
200 | },
201 | "source": [
202 | "## Functions\n",
203 | "\n",
204 | "A function is simply a block of code that applies some operations on an input, and can (optionally) return an output. Functions help you by letting you write code once, and then use it as many times as you want.\n",
205 | "\n",
206 | "In python a function looks like this:"
207 | ]
208 | },
209 | {
210 | "cell_type": "code",
211 | "execution_count": null,
212 | "metadata": {
213 | "id": "KQ0ShWgsd6ba"
214 | },
215 | "outputs": [],
216 | "source": [
217 | "def multiply(a,b):\n",
218 | " c = a*b\n",
219 | " return c"
220 | ]
221 | },
222 | {
223 | "cell_type": "markdown",
224 | "metadata": {
225 | "id": "ZJEY03aud6ba"
226 | },
227 | "source": [
228 | "First, there is a keyword `def` that lets python know you're defining a function. On the same line, you give the function a name, define the input arguments of the function in parenthesis and put a colon on the end. In this case the name is `multiply` and it has two inputs, `a` and `b`.\n",
229 | "\n",
230 | "Next the _body_ of the function is defined. In this case we define a `c` as the product of `a` and `b`. Then we _return_ `c`, this is the function output. Note that this code is indented. Whitespace is important in python and we'll discuss it more below.\n",
231 | "\n",
232 | "To use the function, we just write the name and fill in the inputs:"
233 | ]
234 | },
235 | {
236 | "cell_type": "code",
237 | "execution_count": null,
238 | "metadata": {
239 | "id": "6tNptEjcd6ba"
240 | },
241 | "outputs": [],
242 | "source": [
243 | "multiply(3,4)"
244 | ]
245 | },
246 | {
247 | "cell_type": "markdown",
248 | "metadata": {
249 | "id": "k4BYn7rZd6ba"
250 | },
251 | "source": [
252 | "When writing your own functions, the most important thing to remember is that the function should only depend on the inputs, nothing else. The multiply function is a good simple example of this. Here is a bad example:"
253 | ]
254 | },
255 | {
256 | "cell_type": "code",
257 | "execution_count": null,
258 | "metadata": {
259 | "id": "FMbmvIvyd6ba"
260 | },
261 | "outputs": [],
262 | "source": [
263 | "def mult_bad(b):\n",
264 | " return a*b\n",
265 | "\n",
266 | "mult_bad(3)"
267 | ]
268 | },
269 | {
270 | "cell_type": "markdown",
271 | "metadata": {
272 | "id": "ND2sL7t9d6ba"
273 | },
274 | "source": [
275 | "In this example, we're using `a` without naming it as an input. This means the function uses whatever happens to be named `a`, in this case the variable we named above. This kind of function is worse than useless - it will almost certainly lead you to make mistakes later. "
276 | ]
277 | },
278 | {
279 | "cell_type": "markdown",
280 | "metadata": {
281 | "id": "UHO4JaEZd6bX"
282 | },
283 | "source": [
284 | "## Whitespace and loops"
285 | ]
286 | },
287 | {
288 | "cell_type": "markdown",
289 | "metadata": {
290 | "id": "tleHMdv5d6bX"
291 | },
292 | "source": [
293 | "Python uses indents and whitespace to group statements together, which can be confusing until you get used to it. Try running this function and then fix it."
294 | ]
295 | },
296 | {
297 | "cell_type": "code",
298 | "execution_count": null,
299 | "metadata": {
300 | "id": "LDlkHYtMK4o4"
301 | },
302 | "outputs": [],
303 | "source": [
304 | "def double_then_add(a,b):\n",
305 | "two_a = 2*a\n",
306 | "return two_a+b\n",
307 | "\n",
308 | "double_then_add(10,12)"
309 | ]
310 | },
311 | {
312 | "cell_type": "markdown",
313 | "metadata": {
314 | "id": "D38jk4E1QDte"
315 | },
316 | "source": [
317 | "So the whole function body after the colon must be indented. This is also true for **loops**, which are acheived using the `for` keyword in python."
318 | ]
319 | },
320 | {
321 | "cell_type": "code",
322 | "execution_count": null,
323 | "metadata": {
324 | "id": "nAxFcm1id6bX"
325 | },
326 | "outputs": [],
327 | "source": [
328 | "for i in range(5):\n",
329 | " print(\"Hi. i=\",i)"
330 | ]
331 | },
332 | {
333 | "cell_type": "markdown",
334 | "metadata": {
335 | "id": "tGxaQmhbd6bX"
336 | },
337 | "source": [
338 | "Did you notice the [`range()`](http://docs.python.org/release/1.5.1p1/tut/range.html) function? It is a neat built-in function of Python that gives you a list from an arithmetic progression. Notice that `range(5)` [starts at 0](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) and gives 5 numbers total. You can also adjust the starting value and step if you want."
339 | ]
340 | },
341 | {
342 | "cell_type": "code",
343 | "execution_count": null,
344 | "metadata": {
345 | "id": "K3qCqDzZRWBr"
346 | },
347 | "outputs": [],
348 | "source": [
349 | "for i in range(3,11,2):\n",
350 | " print(i)"
351 | ]
352 | },
353 | {
354 | "cell_type": "markdown",
355 | "metadata": {
356 | "id": "hQz3p8V9RTSS"
357 | },
358 | "source": [
359 | "If you have nested `for` loops (or functions), there is a further indent for the inner loop, like this:"
360 | ]
361 | },
362 | {
363 | "cell_type": "code",
364 | "execution_count": null,
365 | "metadata": {
366 | "id": "xLHS3Dpod6bX"
367 | },
368 | "outputs": [],
369 | "source": [
370 | "for i in range(3):\n",
371 | " for j in range(3):\n",
372 | " print(i, j)\n",
373 | " \n",
374 | " print(\"This statement is within the i-loop, but not the j-loop\")"
375 | ]
376 | },
377 | {
378 | "cell_type": "markdown",
379 | "metadata": {
380 | "id": "kWU8W5OUacRs"
381 | },
382 | "source": [
383 | "Note that if you ever need help with a built in python function, you can type `?` and the function name, or `help(function_name)` or just google it. The colab notebooks have a builtin feature that even displays help when you type the function name. Explore these options below."
384 | ]
385 | },
386 | {
387 | "cell_type": "code",
388 | "execution_count": null,
389 | "metadata": {
390 | "id": "o2DGBWdMaX5C"
391 | },
392 | "outputs": [],
393 | "source": [
394 | "?range\n",
395 | "#help(round)\n",
396 | "#print()"
397 | ]
398 | },
399 | {
400 | "cell_type": "markdown",
401 | "metadata": {
402 | "id": "X1ymyr-VhUsI"
403 | },
404 | "source": [
405 | "# Example exercises\n",
406 | "\n",
407 | "Let's finish this first notebook with a few examples for you to complete. \n",
408 | "\n",
409 | " 1. The fomula for the natural frequency of a spring-mass system is $\\omega_n = \\sqrt{k/m}$ where $k$ is the spring stiffness and $m$ is the mass. Determine the mass in kilograms if the natural frequency is measured as $2~\\text{rad/s}$ and $k=31~\\text{N/m}$."
410 | ]
411 | },
412 | {
413 | "cell_type": "code",
414 | "execution_count": null,
415 | "metadata": {
416 | "id": "FSdTlwFCiSgj"
417 | },
418 | "outputs": [],
419 | "source": [
420 | "omega_n = 2 # rad/s\n",
421 | "k = 31 # N/m\n",
422 | "mass = None # replace with a formula in terms of omega_n andk"
423 | ]
424 | },
425 | {
426 | "cell_type": "code",
427 | "execution_count": null,
428 | "metadata": {
429 | "deletable": false,
430 | "editable": false,
431 | "id": "H_x3SP8TieAK"
432 | },
433 | "outputs": [],
434 | "source": [
435 | "# You can't edit this cell, but run it to check your solution.\n",
436 | "assert(mass == 7.75)\n",
437 | "print(\"Correct!\")"
438 | ]
439 | },
440 | {
441 | "cell_type": "markdown",
442 | "metadata": {
443 | "id": "-b8eSYc2idLb"
444 | },
445 | "source": [
446 | "2. The drag force can be calculated as $$D = \\frac{1}{2} \\rho C_D U^2 A$$ where $\\rho$, $U$ are the density and velocity of the flow, and $C_D$, $A$ are the drag coefficient and frontal area of the body. Write a function to compute the drag given these four inputs."
447 | ]
448 | },
449 | {
450 | "cell_type": "code",
451 | "execution_count": null,
452 | "metadata": {
453 | "id": "fx1YmDkamaCZ"
454 | },
455 | "outputs": [],
456 | "source": [
457 | "def drag(rho,C_D,U,Area):\n",
458 | " # fill in function\n",
459 | " return None"
460 | ]
461 | },
462 | {
463 | "cell_type": "code",
464 | "execution_count": null,
465 | "metadata": {
466 | "deletable": false,
467 | "editable": false,
468 | "id": "fvbd4fFjmw1d"
469 | },
470 | "outputs": [],
471 | "source": [
472 | "# You can't edit this cell, but run it to check your solution.\n",
473 | "assert(drag(1,2,3,4) == 36)\n",
474 | "print(\"Correct!\")"
475 | ]
476 | },
477 | {
478 | "cell_type": "markdown",
479 | "metadata": {
480 | "id": "V8FiFvE7nKRt"
481 | },
482 | "source": [
483 | "3. Loop through $U=2,4,\\ldots,16~\\text{m/s}$ using a range statement and print the speed."
484 | ]
485 | },
486 | {
487 | "cell_type": "code",
488 | "execution_count": null,
489 | "metadata": {
490 | "id": "5LED0-EboMvu"
491 | },
492 | "outputs": [],
493 | "source": [
494 | "# Fix the range\n",
495 | "for U in range(???):\n",
496 | " print(U)"
497 | ]
498 | },
499 | {
500 | "cell_type": "markdown",
501 | "metadata": {
502 | "id": "8Evvl1Hno6SR"
503 | },
504 | "source": [
505 | "4. Evaluate the `drag` function at each speed above assuming a drag coefficient and area of $C_D=0.1$, $A=0.2~\\text{m}^2$ and a water density of $\\rho=1000~\\text{kg/m}^3$. Round the answer to the nearest $\\text{N}$ before printing. For example, the last value should be \"Drag=2560N\"."
506 | ]
507 | },
508 | {
509 | "cell_type": "code",
510 | "execution_count": null,
511 | "metadata": {
512 | "id": "nxRYJg81obN-"
513 | },
514 | "outputs": [],
515 | "source": [
516 | "# Loop over the range above and print the drag using round()"
517 | ]
518 | }
519 | ],
520 | "metadata": {
521 | "colab": {
522 | "collapsed_sections": [],
523 | "include_colab_link": true,
524 | "name": "GettingStartedPythonNotebooks.ipynb",
525 | "provenance": []
526 | },
527 | "kernelspec": {
528 | "display_name": "Python 3",
529 | "language": "python",
530 | "name": "python3"
531 | },
532 | "language_info": {
533 | "codemirror_mode": {
534 | "name": "ipython",
535 | "version": 3
536 | },
537 | "file_extension": ".py",
538 | "mimetype": "text/x-python",
539 | "name": "python",
540 | "nbconvert_exporter": "python",
541 | "pygments_lexer": "ipython3",
542 | "version": "3.6.10"
543 | }
544 | },
545 | "nbformat": 4,
546 | "nbformat_minor": 1
547 | }
548 |
--------------------------------------------------------------------------------
/02ConditionalsAndLists.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "colab_type": "text",
7 | "id": "view-in-github"
8 | },
9 | "source": [
10 | ""
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "metadata": {
16 | "id": "umNbE5lyd6bT"
17 | },
18 | "source": [
19 | "# Conditionals and Lists in Python\n",
20 | "\n",
21 | "We now have the basic building blocks of variables, operations and functions in Python, allowing us to perform a wide range of calculations. However, adding conditionals and lists to our Python capabilities will greatly increase the range of engineering problems we can solve.\n",
22 | "\n",
23 | "\n",
24 | "\n",
25 | "As an example to guide this notebook, we will determining wave conditions in an experimental tank such as the Bolderwood towing tank in the image above."
26 | ]
27 | },
28 | {
29 | "cell_type": "markdown",
30 | "metadata": {
31 | "id": "Pp-tpCmAd6bW"
32 | },
33 | "source": [
34 | "# Booleans and Conditionals\n",
35 | "\n",
36 | "You can test the value of a variable and use this to control the execution of a program using conditional statements. "
37 | ]
38 | },
39 | {
40 | "cell_type": "markdown",
41 | "metadata": {
42 | "id": "vNcd5wzHd6bW"
43 | },
44 | "source": [
45 | "## Booleans\n",
46 | "\n",
47 | "The values `True` and `False` are built-in Boolean objects. We can build up logical conditions and tests and the result will be one of these objects. Predict what the logical statements below will print. Then test your predictions by using the \"play\" button or pressing [shift]-[enter]."
48 | ]
49 | },
50 | {
51 | "cell_type": "code",
52 | "execution_count": null,
53 | "metadata": {
54 | "id": "5blI8MIRd6bW"
55 | },
56 | "outputs": [],
57 | "source": [
58 | "a = True\n",
59 | "b = True\n",
60 | "c = False\n",
61 | "print(type(a))\n",
62 | "print( not a )\n",
63 | "print( a and b )\n",
64 | "print( a and c )\n",
65 | "print( a or c )\n",
66 | "print( not a or (c or b) )"
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "metadata": {
72 | "id": "eVKg9cvjpVA8"
73 | },
74 | "source": [
75 | "In numerical programming and analysis, Booleans typically occur when testing the relationship between two numbers using boolean operators\n",
76 | "\n",
77 | "| Operation | Description || Operation | Description |\n",
78 | "|---------------|-----------------------------------||---------------|--------------------------------------|\n",
79 | "| ``a == b`` | ``a`` equal to ``b`` || ``a != b`` | ``a`` not equal to ``b`` |\n",
80 | "| ``a < b`` | ``a`` less than ``b`` || ``a > b`` | ``a`` greater than ``b`` |\n",
81 | "| ``a <= b`` | ``a`` less than or equal to ``b`` || ``a >= b`` | ``a`` greater than or equal to ``b`` |\n",
82 | "\n",
83 | "Like before, try to guess what these condional operations will return before running the code block below:"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": null,
89 | "metadata": {
90 | "id": "QUy5tSUWd6bW"
91 | },
92 | "outputs": [],
93 | "source": [
94 | "print( 5 == 5 )\n",
95 | "print( 5. != 5 )\n",
96 | "print( 'five' == 5 )\n",
97 | "print( 10/2 != 25**(.5) )\n",
98 | "\n",
99 | "print( 4 >= 4 )\n",
100 | "print( 4+1/1000000 > 4 )\n",
101 | "print( (4+1)/10000 >= 4 )\n",
102 | "\n",
103 | "print( 4>=5 or 5>=4 )"
104 | ]
105 | },
106 | {
107 | "cell_type": "markdown",
108 | "metadata": {
109 | "id": "jZSvwD-sH8FW"
110 | },
111 | "source": [
112 | "And, not surprisingly, you can also write functions to evaluate boolean checks. A classic example is to check if a number is even or odd using the mod operator:"
113 | ]
114 | },
115 | {
116 | "cell_type": "code",
117 | "execution_count": null,
118 | "metadata": {
119 | "id": "TFyJDABLvlQg"
120 | },
121 | "outputs": [],
122 | "source": [
123 | "def is_odd(i):\n",
124 | " return i%2==1\n",
125 | "\n",
126 | "for i in range(4):\n",
127 | " print(\"The number {} is odd: {}\".format(i,is_odd(i)))"
128 | ]
129 | },
130 | {
131 | "cell_type": "markdown",
132 | "metadata": {
133 | "id": "2e9FWp7jGCCH"
134 | },
135 | "source": [
136 | "As you can see, this correctly identifies even and odd digits.\n",
137 | "\n",
138 | "As usual, I'm sneaking in a few more ideas into this example. In this case, I've used a [format statement](https://www.w3schools.com/python/ref_string_format.asp) to insert the number we were checking `i` and the output of the function `is_odd(i)` into a string and then print it. This is just \"for looks\" in this case, but it is a nice technique to know."
139 | ]
140 | },
141 | {
142 | "cell_type": "markdown",
143 | "metadata": {
144 | "id": "UHO4JaEZd6bX"
145 | },
146 | "source": [
147 | "## Conditional statements\n",
148 | "\n",
149 | "One of the more powerful aspects of numerical programming is derived by combining booleans with conditional statements like `if`. The following example shows the syntax for a python `if` statement."
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": null,
155 | "metadata": {
156 | "id": "LDlkHYtMK4o4"
157 | },
158 | "outputs": [],
159 | "source": [
160 | "def describe_number(n):\n",
161 | " if is_odd(n):\n",
162 | " print(n,' is odd.')\n",
163 | " elif n>0:\n",
164 | " print(n,' is even and greater than 0.')\n",
165 | " else:\n",
166 | " print(n,' is even and not greater than 0.')\n",
167 | "\n",
168 | "for n in range(-2,3):\n",
169 | " describe_number(n)"
170 | ]
171 | },
172 | {
173 | "cell_type": "markdown",
174 | "metadata": {
175 | "id": "D38jk4E1QDte"
176 | },
177 | "source": [
178 | "This function uses a different print statement based on the properties of the number. In numerical computing we often use these code \"branches\" to choose different formulas depending on the input. \n",
179 | "\n",
180 | "**Example:** The towing take has a wave maker and we need to know how long those waves will be $\\lambda$ as a function of the frequency of the wave maker paddle $\\omega$ and the depth of the tank $h$. There is a simple formula for this, but it only holds in deep water,\n",
181 | "\n",
182 | "$$ \\lambda = \\frac{2\\pi g}{\\omega^2} \\quad \\text{if}\\quad \\lambda < 2h $$\n",
183 | "\n",
184 | "where $g$ is the acceleration of gravity. If the wave is too long, we need to use a more general form of the dispersion relationship which we will look at later. \n",
185 | "\n",
186 | "So let's write a function for this problem"
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": null,
192 | "metadata": {
193 | "id": "nAxFcm1id6bX"
194 | },
195 | "outputs": [],
196 | "source": [
197 | "def deep_wavelength(omega):\n",
198 | " # omega needs to be given in rad/s.\n",
199 | " g = 9.81 # m/s**2\n",
200 | " pi = 3.14159\n",
201 | " return 2*pi*g/omega**2 # length in m\n",
202 | "\n",
203 | "def check_wavelength(omega,h=3):\n",
204 | " # omega needs to be given in rad/s and h in m.\n",
205 | " l = deep_wavelength(omega)\n",
206 | " if l<2*h:\n",
207 | " print(\"Wavelength is {:.3g} m when omega = {} rad/s\".format(l,omega))\n",
208 | " else:\n",
209 | " print('Wave is not deep water ' \n",
210 | " 'when h = {} m and omega = {} rad/s.'.format(h,omega))\n",
211 | " \n",
212 | "for omega in range(2,8):\n",
213 | " check_wavelength(omega)"
214 | ]
215 | },
216 | {
217 | "cell_type": "markdown",
218 | "metadata": {
219 | "id": "tGxaQmhbd6bX"
220 | },
221 | "source": [
222 | "In the first function computes the deep water wavelength and the second either prints that length or that it isn't valid. \n",
223 | "\n",
224 | "Note that I have set `h=3` in the first line (the _header_) of `check_wavelength`. This makes `h` an _optional argument_ with a default value of 3 (since our wavetank is $3 \\text{m}$ deep). So `check_wavelength(10)==check_wavelength(10,3)`. In the function body I compute the value of wavelength by calling the deep water function. Then I check to make sure that formula was valid, and print the appropriate statement using `if-else`. The only new syntax here is in the last print statement: `{:.3g}` converts the float `l` to a string with 3 significant digits."
225 | ]
226 | },
227 | {
228 | "cell_type": "markdown",
229 | "metadata": {
230 | "id": "8Evvl1Hno6SR"
231 | },
232 | "source": [
233 | "# Lists\n",
234 | "\n",
235 | "A collection of variables can be grouped together in many types of [data structures](https://docs.python.org/3/tutorial/datastructures.html) in Python. In this notebook we focus on lists.\n",
236 | "\n",
237 | "A list in python is just sequence of variables. We've had many examples now that use `range()` to make a list of integers, but there are many more choices. For example"
238 | ]
239 | },
240 | {
241 | "cell_type": "code",
242 | "execution_count": null,
243 | "metadata": {
244 | "id": "nxRYJg81obN-"
245 | },
246 | "outputs": [],
247 | "source": [
248 | "primes = [2,3,5,7,11,13,17]\n",
249 | "primes"
250 | ]
251 | },
252 | {
253 | "cell_type": "code",
254 | "execution_count": null,
255 | "metadata": {
256 | "id": "IqCpvQ8Ji-C-"
257 | },
258 | "outputs": [],
259 | "source": [
260 | "single_name_singers = [\"Prince\",\"P!nk\",\"Eminem\",\"Rhianna\",\"Madonna\",\"Beyonc\\u00E9\"]\n",
261 | "single_name_singers"
262 | ]
263 | },
264 | {
265 | "cell_type": "markdown",
266 | "metadata": {
267 | "id": "46bnfwGklTC_"
268 | },
269 | "source": [
270 | "(Note the use of unicode to get the _grave_ in Beyoncé.)\n",
271 | "\n",
272 | "As you can see, the format for a list is to enclose the items in square brackets `[ ]` and to separate each item with a comma `,`. If nothing goes inside, you just get an empty list."
273 | ]
274 | },
275 | {
276 | "cell_type": "code",
277 | "execution_count": null,
278 | "metadata": {
279 | "id": "PvTOvlr7kdQ1"
280 | },
281 | "outputs": [],
282 | "source": [
283 | "empty = []\n",
284 | "empty"
285 | ]
286 | },
287 | {
288 | "cell_type": "markdown",
289 | "metadata": {
290 | "id": "eMWa3QTZlvv9"
291 | },
292 | "source": [
293 | "And you can loop through the items in any list just as we've done before using `for-in`"
294 | ]
295 | },
296 | {
297 | "cell_type": "code",
298 | "execution_count": null,
299 | "metadata": {
300 | "id": "u7DuuRHglnEO"
301 | },
302 | "outputs": [],
303 | "source": [
304 | "for name in single_name_singers:\n",
305 | " print(name)"
306 | ]
307 | },
308 | {
309 | "cell_type": "code",
310 | "execution_count": null,
311 | "metadata": {
312 | "id": "6qHYYP40p_D7"
313 | },
314 | "outputs": [],
315 | "source": [
316 | "household_ages = [1/3,2,4,9,11] # I have a full house\n",
317 | "for p in primes:\n",
318 | " if p in household_ages: \n",
319 | " print(p,' is prime AND the age of one of my children/pets')"
320 | ]
321 | },
322 | {
323 | "cell_type": "markdown",
324 | "metadata": {
325 | "id": "o_om2FiarQL3"
326 | },
327 | "source": [
328 | "## Sublists\n",
329 | "\n",
330 | "But we don't need to go through all the items in a list. We can also _index_ a particular item we want, or loop through a _slice_ of a list. \n",
331 | "\n",
332 | "Python uses 0-based indexing so the first item is has index 0, the second has index 1 and so on."
333 | ]
334 | },
335 | {
336 | "cell_type": "code",
337 | "execution_count": null,
338 | "metadata": {
339 | "id": "n6NkwJyLtf8k"
340 | },
341 | "outputs": [],
342 | "source": [
343 | "print(primes[0])\n",
344 | "print(primes[1])"
345 | ]
346 | },
347 | {
348 | "cell_type": "markdown",
349 | "metadata": {
350 | "id": "TS7Z8JPOt2if"
351 | },
352 | "source": [
353 | "So what is the index of the **last** item in our list of primes? Modify the code above to check your math. \n",
354 | "\n",
355 | "You can also count backwards through a list. Guess what the results before you run this cell:"
356 | ]
357 | },
358 | {
359 | "cell_type": "code",
360 | "execution_count": null,
361 | "metadata": {
362 | "id": "IrYXqrnIueNp"
363 | },
364 | "outputs": [],
365 | "source": [
366 | "print(primes[-1])\n",
367 | "print(primes[-2])"
368 | ]
369 | },
370 | {
371 | "cell_type": "markdown",
372 | "metadata": {
373 | "id": "2fLXAGAYuq5_"
374 | },
375 | "source": [
376 | "Finally, we can select only part of an array, a *slice*. Similar to the syntax of the `range` function, you can index using `[start]:span[:step]`, where the items in the square brackets are optional. For example, who are the first three singers in the list? "
377 | ]
378 | },
379 | {
380 | "cell_type": "code",
381 | "execution_count": null,
382 | "metadata": {
383 | "id": "jSGEla4Nu_7f"
384 | },
385 | "outputs": [],
386 | "source": [
387 | "single_name_singers[:3]"
388 | ]
389 | },
390 | {
391 | "cell_type": "markdown",
392 | "metadata": {
393 | "id": "ZUIn5chWvazV"
394 | },
395 | "source": [
396 | "Try some more advanced slicing on your own to get the sublists suggested below."
397 | ]
398 | },
399 | {
400 | "cell_type": "code",
401 | "execution_count": null,
402 | "metadata": {
403 | "id": "5HgUzSy2vNsJ"
404 | },
405 | "outputs": [],
406 | "source": [
407 | "# 1. Slice to give the last three singers\n",
408 | "# 2. Slice to give every other prime, starting at 3. ie 3,7,11,..."
409 | ]
410 | },
411 | {
412 | "cell_type": "markdown",
413 | "metadata": {
414 | "id": "XgmLflSXnwct"
415 | },
416 | "source": [
417 | "## Functions and methods\n",
418 | "\n",
419 | "There are many potentially helpful functions that can applied to lists. I've summarized a few below:\n",
420 | "\n"
421 | ]
422 | },
423 | {
424 | "cell_type": "code",
425 | "execution_count": null,
426 | "metadata": {
427 | "id": "7Emcc8ZqxyD9"
428 | },
429 | "outputs": [],
430 | "source": [
431 | "print(len(single_name_singers)) # length of a list\n",
432 | "print(sorted(single_name_singers)) # sort a list\n",
433 | "print(sum(primes),\",\",max(primes)) # sum and max of a list"
434 | ]
435 | },
436 | {
437 | "cell_type": "markdown",
438 | "metadata": {
439 | "id": "0fqr2hf1zRGk"
440 | },
441 | "source": [
442 | "There are also special functions \"attached\" to any list. Functions like this are called _methods_. (This is [object oriented programming](https://realpython.com/python3-object-oriented-programming/) termonology - but we don't need the details for now.) \n",
443 | "\n",
444 | "For example, we find the index for a given value using the `index` method"
445 | ]
446 | },
447 | {
448 | "cell_type": "code",
449 | "execution_count": null,
450 | "metadata": {
451 | "id": "cWFmzGJuzQxi"
452 | },
453 | "outputs": [],
454 | "source": [
455 | "i = primes.index(7)\n",
456 | "print(i,primes[i]==7)"
457 | ]
458 | },
459 | {
460 | "cell_type": "markdown",
461 | "metadata": {
462 | "id": "eEm8_wFKz_5F"
463 | },
464 | "source": [
465 | "Which shows that 7 is at index 3, agreeing with our slicing above.\n",
466 | "\n",
467 | "Note that the format of calling a method is different than a regular function. We write the object, then a dot, then the name of the method, then the arguments in parethesis. In this example `prime` is the object, `index` is the method, and `7` was the argument. The `format` statement is also a method. In the code `\"I am {} years old\".format(19)`, the string is the object, `format` is the method and `19` is the argument."
468 | ]
469 | },
470 | {
471 | "cell_type": "markdown",
472 | "metadata": {
473 | "id": "3Haa6ZIa-T_T"
474 | },
475 | "source": [
476 | "## Creating lists\n",
477 | "\n",
478 | "We've seen that we can write down a list inside square brackets, but this isn't very practical for long lists. If the list is just a simple repetition, you can create it using the multiply operation:"
479 | ]
480 | },
481 | {
482 | "cell_type": "code",
483 | "execution_count": null,
484 | "metadata": {},
485 | "outputs": [],
486 | "source": [
487 | "a = [4]*5\n",
488 | "b = [5]*4\n",
489 | "print(a,b)"
490 | ]
491 | },
492 | {
493 | "cell_type": "markdown",
494 | "metadata": {},
495 | "source": [
496 | "And we can append lists together using the addition operation:"
497 | ]
498 | },
499 | {
500 | "cell_type": "code",
501 | "execution_count": null,
502 | "metadata": {},
503 | "outputs": [],
504 | "source": [
505 | "print(a+[8])\n",
506 | "print(a+b+a)"
507 | ]
508 | },
509 | {
510 | "cell_type": "markdown",
511 | "metadata": {
512 | "id": "HZj4f8RpzEb2"
513 | },
514 | "source": [
515 | "This is called operator overloading. Python decides what the `*,+` operations mean depending on the data type.\n",
516 | "\n",
517 | "However, this kind of code can be confusing and still isn't very general. The best way to create lists is using a [list comprehension](https://docs.python.org/3/tutorial/datastructures.html?highlight=comprehension#list-comprehensions). Basically, we loop through a simple list like `range(n)` to create a new list inside the square brackets."
518 | ]
519 | },
520 | {
521 | "cell_type": "markdown",
522 | "metadata": {
523 | "id": "HZj4f8RpzEb2"
524 | },
525 | "source": [
526 | "Now instead of printing our wavelengths to the screen, let's make a list of them, so we can use them for other parts of our program later."
527 | ]
528 | },
529 | {
530 | "cell_type": "code",
531 | "execution_count": null,
532 | "metadata": {
533 | "id": "HhJ7Zn_U3u67"
534 | },
535 | "outputs": [],
536 | "source": [
537 | "omega_list = range(1,20) # simple list using range\n",
538 | "wavelength_list = [deep_wavelength(omega) for omega in omega_list] # list comprehension\n",
539 | "wavelength_list"
540 | ]
541 | },
542 | {
543 | "cell_type": "markdown",
544 | "metadata": {
545 | "id": "UboDdCJ28UFQ"
546 | },
547 | "source": [
548 | "Note that you *could* make the second list with a for loop\n",
549 | "\n",
550 | "```python\n",
551 | "wavelength_list=[]\n",
552 | "for omega in omega_list:\n",
553 | " wavelength_list = wavelength_list + [deep_wavelength(omega)]\n",
554 | "```\n",
555 | "\n",
556 | "**But don't!** A list comprehension is computationally faster and it is more readable.\n",
557 | "\n",
558 | "Let's also make a list of the frequencies in cycles per second."
559 | ]
560 | },
561 | {
562 | "cell_type": "code",
563 | "execution_count": null,
564 | "metadata": {
565 | "id": "VKFTqLXh6CUG"
566 | },
567 | "outputs": [],
568 | "source": [
569 | "freq_list = [omega/(2*3.14159) for omega in omega_list]\n",
570 | "freq_list"
571 | ]
572 | },
573 | {
574 | "cell_type": "markdown",
575 | "metadata": {
576 | "id": "qo3q5Vpw8hUH"
577 | },
578 | "source": [
579 | "Finally, let's join these two equal length lists \"side-by-side\" using `zip`. This lets me loop through them simultaneously and print both the frequency and wavelength, but only for waves that are short enough to be valid \"deep water\" waves and for $f<2\\text{Hz}$ to avoid overstressing the wavemaker hydraulics."
580 | ]
581 | },
582 | {
583 | "cell_type": "code",
584 | "execution_count": null,
585 | "metadata": {
586 | "id": "WAi8Dzzg8hjf"
587 | },
588 | "outputs": [],
589 | "source": [
590 | "for freq,wavelength in zip(freq_list,wavelength_list):\n",
591 | " if(wavelength<6 and freq<2):\n",
592 | " print(\"{:.3g} Hz| {:.3g} m\".format(freq,wavelength))"
593 | ]
594 | },
595 | {
596 | "cell_type": "markdown",
597 | "metadata": {
598 | "id": "iEaqq9Bd-m28"
599 | },
600 | "source": [
601 | "## Example exercise\n",
602 | "\n",
603 | "Create a list with the wave speed $c=\\lambda f$ and the time it would take for each wave to reach the middle of the $130\\text{m}$ long tank. You can make a really big breaking wave by timing it such that all of these individual waves meet-up at center at the same time. When do you need to generate each wave to achieve this? Limit your answers to the waves shorter than $6m$ and less than $2Hz$.\n",
604 | "\n",
605 | "To help you check your answers, the slowest wave has the following metrics:\n",
606 | "\n",
607 | "| freq $(Hz)$ | speed $(m/s)$ | time $(s)$ | delay $(s)$ |\n",
608 | "|--|--|--|--|--|\n",
609 | "| 1.91 | 0.818 | 79.5 | 0 |"
610 | ]
611 | },
612 | {
613 | "cell_type": "code",
614 | "execution_count": null,
615 | "metadata": {},
616 | "outputs": [],
617 | "source": [
618 | "# speed_list = []\n",
619 | "# time_list = [] \n",
620 | "# delay_list = []"
621 | ]
622 | }
623 | ],
624 | "metadata": {
625 | "colab": {
626 | "collapsed_sections": [],
627 | "include_colab_link": true,
628 | "name": "02Conditionals_Lists.ipynb",
629 | "provenance": []
630 | },
631 | "kernelspec": {
632 | "display_name": "Python 3",
633 | "language": "python",
634 | "name": "python3"
635 | },
636 | "language_info": {
637 | "codemirror_mode": {
638 | "name": "ipython",
639 | "version": 3
640 | },
641 | "file_extension": ".py",
642 | "mimetype": "text/x-python",
643 | "name": "python",
644 | "nbconvert_exporter": "python",
645 | "pygments_lexer": "ipython3",
646 | "version": "3.6.10"
647 | }
648 | },
649 | "nbformat": 4,
650 | "nbformat_minor": 1
651 | }
652 |
--------------------------------------------------------------------------------
/03NumpyAndPlotting.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "colab_type": "text",
7 | "id": "view-in-github"
8 | },
9 | "source": [
10 | ""
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "metadata": {
16 | "colab_type": "text",
17 | "id": "view-in-github"
18 | },
19 | "source": [
20 | "# Numerical Python and Plotting\n",
21 | "\n",
22 | "We have established the main features of Python, but up to this point we have only created simple functions and applied them to create lists of numbers. We need to extend the base-language of Python for more advanced numerical work because:\n",
23 | " - There are no built-in data structures for arrays, matrices, and tables (unlike, say, `Matlab` or `Julia`).\n",
24 | " - Using lists of `float` numbers is generally very slow and lacks useful built-in features like matrix multiplication.\n",
25 | " - There is no built-in method to visualize data in plots.\n",
26 | "\n",
27 | "This notebook will introduce the `NumPy` and `PyPlot` libraries to address these issues."
28 | ]
29 | },
30 | {
31 | "cell_type": "markdown",
32 | "metadata": {
33 | "id": "ivbCdFpECLl8"
34 | },
35 | "source": [
36 | "# NumPy\n",
37 | "\n",
38 | "The numerical python, or [NumPy](https://numpy.org/), library enables fast and simple numerical methods in Python. To starting using this library (or any other) we need to use a new python keyword `import`:"
39 | ]
40 | },
41 | {
42 | "cell_type": "code",
43 | "execution_count": null,
44 | "metadata": {
45 | "id": "NMoD8fv_B_J3"
46 | },
47 | "outputs": [],
48 | "source": [
49 | "import numpy as np # importing numpy\n",
50 | "\n",
51 | "np.set_printoptions(precision=3) # This sets numpy printing precision \n",
52 | "np.set_printoptions(suppress=True) # Don't use scientific notation by default"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "This gives us access to all the methods and functions in `NumPy` using the short name `np`. \n",
60 | "\n",
61 | "In the second and third line I've used the method `set_printoptions` to keep the notebook output pretty. Note that the format is `np` and then `.` and then the function name - just like when we used built-in methods for strings and lists in the previous notebooks. \n",
62 | "\n",
63 | "There are [far too many](https://numpy.org/doc/stable/reference/routines.html) new methods available to go through in this introduction, but most can be grouped into a few basic categories\n",
64 | "\n",
65 | "| Category | Sub module | Description |\n",
66 | "|----------------|--------------|-------------------------------------------------------------|\n",
67 | "| math | numpy | Scientific operations like $\\sqrt{a},\\log(a),\\sin(a)$, etc |\n",
68 | "| arrays | numpy | Array and matrix creation, and array operations like multiplication |\n",
69 | "| linear algebra | numpy.linalg | Matrix decomposition and solving linear systems |\n",
70 | "| fft | numpy.fft | Discrete Fourier Transform (of many types) and their inverse |\n",
71 | "| random sampling| numpy.random | Sample from different random variable distributions |\n",
72 | "\n",
73 | "\n",
74 | "\n",
75 | "Just as an example let see what is in the `numpy.random` submodule."
76 | ]
77 | },
78 | {
79 | "cell_type": "code",
80 | "execution_count": null,
81 | "metadata": {},
82 | "outputs": [],
83 | "source": [
84 | "print(\"it contains methods such as...\",dir(np.random)[30:])"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "metadata": {},
90 | "source": [
91 | "Let's get help on one of those. Notice we use the dot notation twice to access a submodule method."
92 | ]
93 | },
94 | {
95 | "cell_type": "code",
96 | "execution_count": null,
97 | "metadata": {},
98 | "outputs": [],
99 | "source": [
100 | "?np.random.randint"
101 | ]
102 | },
103 | {
104 | "cell_type": "markdown",
105 | "metadata": {},
106 | "source": [
107 | "All NumPy functions are well documented like this; with all the argument explained and useful examples. Using this function we can generate a sample of what might happen if we, say, roll a 20 sided-dice 4 times."
108 | ]
109 | },
110 | {
111 | "cell_type": "code",
112 | "execution_count": null,
113 | "metadata": {},
114 | "outputs": [],
115 | "source": [
116 | "samples = np.random.randint(1,21,size=4)\n",
117 | "samples"
118 | ]
119 | },
120 | {
121 | "cell_type": "markdown",
122 | "metadata": {},
123 | "source": [
124 | "Every time you run this code, the result will be different. Try it!\n",
125 | "\n",
126 | "## Arrays and matrices\n",
127 | "\n",
128 | "Note that `samples` is a new data type called `array`. This is the building block for everything in NumPy. The easiest way to make arrays from scratch is to pass a list to `np.array`. Arrays can also have two dimensions, defining _a matrix_, or more. These can be created by passing a list of lists to `np.array`."
129 | ]
130 | },
131 | {
132 | "cell_type": "code",
133 | "execution_count": null,
134 | "metadata": {},
135 | "outputs": [],
136 | "source": [
137 | "r_array = np.array(range(4))\n",
138 | "r_matrix = np.array([[1,2],[3,4],[5,6]])\n",
139 | "print(r_array)\n",
140 | "print('')\n",
141 | "print(r_matrix)"
142 | ]
143 | },
144 | {
145 | "cell_type": "markdown",
146 | "metadata": {},
147 | "source": [
148 | "Looping and slicing works on arrays just like it did on lists. "
149 | ]
150 | },
151 | {
152 | "cell_type": "code",
153 | "execution_count": null,
154 | "metadata": {},
155 | "outputs": [],
156 | "source": [
157 | "for x in r_array:\n",
158 | " print(x)\n",
159 | "print('')\n",
160 | "print(r_array[-2:])"
161 | ]
162 | },
163 | {
164 | "cell_type": "markdown",
165 | "metadata": {},
166 | "source": [
167 | "It also works on matrices (and higher dimensional arrays), but there are more options since there are more indices."
168 | ]
169 | },
170 | {
171 | "cell_type": "code",
172 | "execution_count": null,
173 | "metadata": {},
174 | "outputs": [],
175 | "source": [
176 | "for row in r_matrix: # row by row\n",
177 | " print(row)\n",
178 | " for element in row: # element by element\n",
179 | " print(element)\n",
180 | "print('')\n",
181 | "print(r_matrix[0,0]) # first element\n",
182 | "print(r_matrix[0]) # first row\n",
183 | "print(r_matrix[:,0]) # first column"
184 | ]
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "metadata": {},
189 | "source": [
190 | "There are also a few handy built-in functions to create arrays and matrices. See if you can guess what these functions will create before running the code block below."
191 | ]
192 | },
193 | {
194 | "cell_type": "code",
195 | "execution_count": null,
196 | "metadata": {},
197 | "outputs": [],
198 | "source": [
199 | "print(np.linspace(0,1,6))\n",
200 | "print('')\n",
201 | "print(np.ones([3,2]))\n",
202 | "print('')\n",
203 | "print(np.eye(4))\n",
204 | "print('')\n",
205 | "print(np.diag([1,2,3]))"
206 | ]
207 | },
208 | {
209 | "cell_type": "markdown",
210 | "metadata": {},
211 | "source": [
212 | "The `numpy.linspace` function is especially useful as it makes an array equally spaced floats between the values you specify.\n",
213 | "\n",
214 | "Get some practice with this by defining your own matrix and slicing it."
215 | ]
216 | },
217 | {
218 | "cell_type": "code",
219 | "execution_count": null,
220 | "metadata": {},
221 | "outputs": [],
222 | "source": [
223 | "#1. Define a 3x3 diagonal matrix called `my_matrix` with 2 along the diagonal in two different ways.\n",
224 | "#2. Slice this matrix to get the array [0 0 2] in two different ways."
225 | ]
226 | },
227 | {
228 | "cell_type": "markdown",
229 | "metadata": {},
230 | "source": [
231 | "## Array operations and broadcasting\n",
232 | "\n",
233 | "There are a number of built-in NumPy operations that only make sense to apply to arrays vectors and matrices. Things like the transpose, matrix multiplication, and vector products. "
234 | ]
235 | },
236 | {
237 | "cell_type": "code",
238 | "execution_count": null,
239 | "metadata": {},
240 | "outputs": [],
241 | "source": [
242 | "a = np.array([1,2,3])\n",
243 | "b = np.array([4,5,6])\n",
244 | "print(r_matrix.T) # transpose\n",
245 | "print(r_matrix.T @ a) # matrix multiplication\n",
246 | "print(np.inner(a,b)) # inner product\n",
247 | "print(np.cross(a,b)) # cross product"
248 | ]
249 | },
250 | {
251 | "cell_type": "markdown",
252 | "metadata": {},
253 | "source": [
254 | "These are certainly useful in many situations. However, the _really_ nice thing about NumPy arrays is that they automatically and efficiently apply simple operations to each element of the array. Contrast this to lists, which required us to use list-comprehensions to get the same results!"
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": null,
260 | "metadata": {},
261 | "outputs": [],
262 | "source": [
263 | "list_a,list_b = [1,2,3],[4,5,6] # lists, not arrays\n",
264 | "\n",
265 | "# list operators give unwanted \"string-like\" result\n",
266 | "print(2*list_a+list_b) \n",
267 | "\n",
268 | "# list-comprehensions give desired result, but add complexity\n",
269 | "print([2*a+b for a,b in zip(list_a,list_b)])\n",
270 | "\n",
271 | "# Numpy arrays give desired result automatically!\n",
272 | "print(2*a+b)"
273 | ]
274 | },
275 | {
276 | "cell_type": "markdown",
277 | "metadata": {},
278 | "source": [
279 | "This is called _operator broadcasting_ (or vectorizing) and is almost always the behavior we want in numerical programming. \n",
280 | "\n",
281 | "And this isn't limited to simple operations like `*,+`: NumPy has a slew of built-in scientific functions that will broadcast. Guess what these statements will print before running:"
282 | ]
283 | },
284 | {
285 | "cell_type": "code",
286 | "execution_count": null,
287 | "metadata": {},
288 | "outputs": [],
289 | "source": [
290 | "print(np.sqrt(9))\n",
291 | "print(np.sqrt(r_array))\n",
292 | "print(np.log(np.e))\n",
293 | "print(np.log(samples))\n",
294 | "print(np.sin(np.pi/2))\n",
295 | "print(np.sin(r_matrix*np.pi/4))"
296 | ]
297 | },
298 | {
299 | "cell_type": "markdown",
300 | "metadata": {},
301 | "source": [
302 | "Notice that NumPy defines some important constants like $e,\\pi$ that we can use as well. \n",
303 | "\n",
304 | "Get some more practice by completing the exercises below. "
305 | ]
306 | },
307 | {
308 | "cell_type": "code",
309 | "execution_count": null,
310 | "metadata": {},
311 | "outputs": [],
312 | "source": [
313 | "#3. Create an array `c` which is my_matrix times the array `a`.\n",
314 | "#4. Look up the help for np.linalg.solve and use it to solve the system `my_matrix * x = c`\n",
315 | "#5. Use assert to make sure `c` is `2a` and `x` is `a`."
316 | ]
317 | },
318 | {
319 | "cell_type": "markdown",
320 | "metadata": {},
321 | "source": [
322 | "## Array Functions\n",
323 | "\n",
324 | "Not surprisingly, you can write functions for arrays as well, building on any of the original variable operators or NumPy operators. As a first trivial example, let's copy-paste two of our old functions from previous notebooks:"
325 | ]
326 | },
327 | {
328 | "cell_type": "code",
329 | "execution_count": null,
330 | "metadata": {},
331 | "outputs": [],
332 | "source": [
333 | "def is_odd(n):\n",
334 | " return n%2==1\n",
335 | "def double_then_add(a,b):\n",
336 | " return 2*a+b\n",
337 | "\n",
338 | "print(is_odd(a))\n",
339 | "print(double_then_add(a,b))"
340 | ]
341 | },
342 | {
343 | "cell_type": "markdown",
344 | "metadata": {},
345 | "source": [
346 | "Notice that operator broadcasting has applied automatically. _We didn't have to change anything!_\n",
347 | "\n",
348 | "As a second example let's create a few functions that wouldn't make sense on individual numbers. Let's rotate a point $p$ in 2D space by an angle $q$ around the origin."
349 | ]
350 | },
351 | {
352 | "cell_type": "code",
353 | "execution_count": null,
354 | "metadata": {},
355 | "outputs": [],
356 | "source": [
357 | "def rotation_matrix(q):\n",
358 | " return np.array([[np.cos(q),-np.sin(q)],\n",
359 | " [np.sin(q), np.cos(q)]])\n",
360 | "\n",
361 | "def rotate_point(a,q):\n",
362 | " return rotation_matrix(q) @ a\n",
363 | "\n",
364 | "a = np.array([1,1])\n",
365 | "for q in np.linspace(0,np.pi,9):\n",
366 | " print(\"q={:.3g} rad, new p={}\".format(q,rotate_point(a,q)))"
367 | ]
368 | },
369 | {
370 | "cell_type": "markdown",
371 | "metadata": {},
372 | "source": [
373 | "The new `rotate_point` function seems to have worked but it is a little hard to tell... \n",
374 | "\n",
375 | "# PyPlot\n",
376 | "\n",
377 | "Even for the previous simple example, it would be much easier to check the results if we could plot them. [Matplotlib](https://matplotlib.org/) is the most developed plotting library in python. `Matplotlib.PyPlot` are a collection of functions to make plot copied after the popular Matplot interface."
378 | ]
379 | },
380 | {
381 | "cell_type": "code",
382 | "execution_count": null,
383 | "metadata": {},
384 | "outputs": [],
385 | "source": [
386 | "from matplotlib import pyplot as plt # import plotting library"
387 | ]
388 | },
389 | {
390 | "cell_type": "markdown",
391 | "metadata": {},
392 | "source": [
393 | "Note this `from-import` syntax lets you pick specific submodules or even specific functions to import. This can help with code readability. \n",
394 | "\n",
395 | "As a first example, let's just plot $\\sqrt{x}$ from $x=0...1$"
396 | ]
397 | },
398 | {
399 | "cell_type": "code",
400 | "execution_count": null,
401 | "metadata": {},
402 | "outputs": [],
403 | "source": [
404 | "x = np.linspace(0,1) # array of x values\n",
405 | "plt.plot(x,np.sqrt(x)) # plot x vs it's sqrt\n",
406 | "plt.xlabel('x') # label x-axis\n",
407 | "plt.ylabel('$\\sqrt{x}$',rotation=0) # label y-axis (don't rotate it)\n",
408 | "plt.show() # show the plot"
409 | ]
410 | },
411 | {
412 | "cell_type": "markdown",
413 | "metadata": {},
414 | "source": [
415 | "This example shows that you can control pretty much everything in PyPlot, the trick is to just build the figure up one element at a time. \n",
416 | "\n",
417 | "Lets use PyPlot to test the `rotate_point` function above. We'll exactly the same loop as before, but instead of printing, we will add each rotated point to a scatter plot."
418 | ]
419 | },
420 | {
421 | "cell_type": "code",
422 | "execution_count": null,
423 | "metadata": {},
424 | "outputs": [],
425 | "source": [
426 | "for q in np.linspace(0,np.pi,9):\n",
427 | " x,y = rotate_point(a,q)\n",
428 | " plt.scatter(x,y,label='q={:.3g}'.format(q)) # plot point and assign label\n",
429 | "plt.legend() # show legend\n",
430 | "plt.xlabel('x') # label x-axis\n",
431 | "plt.ylabel('y',rotation=0) # label y-axis (don't rotate it)\n",
432 | "plt.axis('equal') # scale the x,y axis equally \n",
433 | "plt.show() # show the plot"
434 | ]
435 | },
436 | {
437 | "cell_type": "markdown",
438 | "metadata": {},
439 | "source": [
440 | "The points form a half-circle starting from our original point $a=[1,1]$, visually confirming that the `rotate_point` function is working. \n",
441 | "\n",
442 | "Let's develop a function to create a polar plot of a given function $r(q)$ as a more advanced example."
443 | ]
444 | },
445 | {
446 | "cell_type": "code",
447 | "execution_count": null,
448 | "metadata": {},
449 | "outputs": [],
450 | "source": [
451 | "def polar_plot(func,q=np.linspace(0,2*np.pi)):\n",
452 | " r = func(q) # evaluate the function on q array\n",
453 | " plt.plot(r*np.cos(q),r*np.sin(q)) # compute and plot x,y arrays\n",
454 | " plt.xlabel('x') # label x-axis\n",
455 | " plt.ylabel('y',rotation=0) # label y-axis (don't rotate it)\n",
456 | " plt.axis('equal') # scale the x,y axis equally \n",
457 | " plt.show() # show the plot\n",
458 | "\n",
459 | "def cardiod(q): return 2*(1-np.cos(q))\n",
460 | "polar_plot(cardiod)"
461 | ]
462 | },
463 | {
464 | "cell_type": "markdown",
465 | "metadata": {},
466 | "source": [
467 | "Notice the function `polar_plot` doesn't `return` anything - it just `show`s a plot. The function `polar_plot` is also interesting because its first argument is a *function*, not a number or an array. This is common enough in Python that there is a [lambda](https://realpython.com/python-lambda/) syntax to create functions _in-place_ instead of giving the function a name and then using it."
468 | ]
469 | },
470 | {
471 | "cell_type": "code",
472 | "execution_count": null,
473 | "metadata": {},
474 | "outputs": [],
475 | "source": [
476 | "polar_plot(lambda q: q/np.pi) # anonymous function to make a spiral"
477 | ]
478 | },
479 | {
480 | "cell_type": "markdown",
481 | "metadata": {},
482 | "source": [
483 | "The `lambda` syntax is very handy when doing more advanced analysis like function optimization, root-finding, integration, etc. Try making a more interesting polar plot, like $2+\\sin(10q)$ or any other function you like.\n",
484 | "\n",
485 | "\n",
486 | "# Failure Modeling Example\n",
487 | "\n",
488 | "Let's finish with an engineering simulation. Let's model the potential failure of a fuel oil pump using a simplistic model. \n",
489 | "\n",
490 | "First we'll model the pressure upstream of the pump as a random walk: at each time step the pressure can go up, down or stay constant with equal probability. We could do this one random step at a time, but it is faster to generate all the steps at once, and then add them together."
491 | ]
492 | },
493 | {
494 | "cell_type": "code",
495 | "execution_count": null,
496 | "metadata": {},
497 | "outputs": [],
498 | "source": [
499 | "def pressure_walk(n_steps):\n",
500 | " pressure_steps = np.random.randint(-1,2,size=n_steps)\n",
501 | " return np.cumsum(pressure_steps) # cumulative summation of the steps\n",
502 | "\n",
503 | "for i in range(5):\n",
504 | " plt.plot(pressure_walk(200))\n",
505 | "plt.xlabel('time'); plt.ylabel('pressure')\n",
506 | "plt.show()"
507 | ]
508 | },
509 | {
510 | "cell_type": "markdown",
511 | "metadata": {},
512 | "source": [
513 | "Notice the cumulative summation function to get the signal from the steps. The plot shows 5 random pressure histories and every time you run the block above the histories will change.\n",
514 | "\n",
515 | "Now let's think about the FO pump. A pump only has a finite range of operation - if the pressure is too low, the pump will immediately stop working. So lets write a function to check if the pump fails, given a pressure signal. "
516 | ]
517 | },
518 | {
519 | "cell_type": "code",
520 | "execution_count": null,
521 | "metadata": {},
522 | "outputs": [],
523 | "source": [
524 | "def pump_failure(pressure,lower=-15):\n",
525 | " for i,p in enumerate(pressure):\n",
526 | " if p<=lower: return i\n",
527 | " return len(pressure)\n",
528 | "\n",
529 | "pressure_test = np.zeros(10) # zero array for testing\n",
530 | "i = np.random.randint(1,11) # pick a failure time\n",
531 | "pressure_test[i:] = -20 # replace test values after this time \n",
532 | "print(pressure_test)\n",
533 | "print(\"Failure time = {}\".format(pump_failure(pressure_test)))\n",
534 | "assert(i==pump_failure(pressure_test))\n",
535 | "print(\"Test passed!\")"
536 | ]
537 | },
538 | {
539 | "cell_type": "markdown",
540 | "metadata": {},
541 | "source": [
542 | "The `enumerate` function iterates through the index and values in a list or array. This lets us test if the pressure limit has been crossed and return the index when that happens.\n",
543 | "\n",
544 | "The code below the function is a test to see if the function is working properly. [Writing little tests like this lets you catch errors before integrating functions together!](https://docs.python-guide.org/writing/tests/)\n",
545 | "\n",
546 | "Finally, we can use these two function to get a feeling for how quickly our pump is likely to fail. Let's run our code a few thousand times and plot the results as a histogram. "
547 | ]
548 | },
549 | {
550 | "cell_type": "code",
551 | "execution_count": null,
552 | "metadata": {},
553 | "outputs": [],
554 | "source": [
555 | "n_runs = 10000\n",
556 | "n_steps = 1000\n",
557 | "failure_data = [pump_failure(pressure_walk(n_steps)) for i in range(n_runs)]\n",
558 | "plt.hist(failure_data,bins=range(1,n_steps,10))\n",
559 | "plt.xlabel('time of pump failure'); plt.ylabel('number of failures')\n",
560 | "plt.show();"
561 | ]
562 | },
563 | {
564 | "cell_type": "code",
565 | "execution_count": null,
566 | "metadata": {},
567 | "outputs": [],
568 | "source": [
569 | "np.count_nonzero(np.array(failure_data)==n_steps)"
570 | ]
571 | },
572 | {
573 | "cell_type": "markdown",
574 | "metadata": {},
575 | "source": [
576 | "We see that most failures happen between 100 and 200 time steps, but there is a \"long tail\" of pumps that last much much longer. \n",
577 | "\n",
578 | "## Additional exercises\n",
579 | "\n",
580 | "1. Our random walk function isn't very realistic. Find a function in the `numpy.random` submodule to generate steps from a Gaussian distribution instead. How does this change the signals and the failure results?\n",
581 | "1. A pump can also fail if it is exposed to excessively high pressure for too long and becomes damaged. Write a function to return the accumulated time the pump spends above a threshold value."
582 | ]
583 | },
584 | {
585 | "cell_type": "code",
586 | "execution_count": null,
587 | "metadata": {},
588 | "outputs": [],
589 | "source": []
590 | }
591 | ],
592 | "metadata": {
593 | "colab": {
594 | "authorship_tag": "ABX9TyPPRJif4V4H6mucN5PMLqW1",
595 | "include_colab_link": true,
596 | "name": "03NumpyAndPlotting.ipynb",
597 | "provenance": []
598 | },
599 | "kernelspec": {
600 | "display_name": "Python 3",
601 | "language": "python",
602 | "name": "python3"
603 | },
604 | "language_info": {
605 | "codemirror_mode": {
606 | "name": "ipython",
607 | "version": 3
608 | },
609 | "file_extension": ".py",
610 | "mimetype": "text/x-python",
611 | "name": "python",
612 | "nbconvert_exporter": "python",
613 | "pygments_lexer": "ipython3",
614 | "version": "3.6.10"
615 | }
616 | },
617 | "nbformat": 4,
618 | "nbformat_minor": 1
619 | }
620 |
--------------------------------------------------------------------------------
/04FlowPlotExample.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | ""
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "# Example: Potential Flow Plot\n",
15 | "\n",
16 | "The Matplotlib `plot` (line), `scatter`, and `hist`(histogram) functions are sufficient for 90% of engineering analysis, but more advanced plotting functions can also be useful. \n",
17 | "\n",
18 | "As an example, the function below plots the streamlines and velocity field given a potential flow stream-function. The youtube video walks through the construction of this function step-by-step."
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": 1,
24 | "metadata": {},
25 | "outputs": [],
26 | "source": [
27 | "import numpy as np # importing numpy\n",
28 | "from matplotlib import pyplot as plt # import plotting library"
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": 2,
34 | "metadata": {},
35 | "outputs": [
36 | {
37 | "data": {
38 | "image/png": "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qLIxqgvQCotI0Jdg1hRhUheiUc4dWdzV1UV+fj7ECT6S4Df+4HF6h2spX5ZM38gRypelkFuiwyv3zJoe1TWpR3Jbqd0/xMWrruPCFddh1kSviV977TW2bt3KkiVLcDgceDwevvOd70QkjpqOG2+8kaysLFasWMGRI0e44IILuO6662K+kP/85z956aWXWL16NatWrWLz5s3ccccdFBcXxzzGd77zHURRZPXq1SxdupS//e1vfPvb345JUsePH+f+73yLVZcXk7ckAdkyzqi/HUkbmSNfQEGyJpfRY376mp3kJFaytHANKsnIxMQEV155ZdRrkSSJa665hpKSEqqrq6mqqmJ4eBilUhlzmzfffJNXX32VkpISiouLKSkp4ciRI6SlpfG//tf/iprL4/bbb2dsbIzMzEyysrLIysrizTffZNmyZXzta19j0aJFM8rX1dXxrW99C7VajcViwWw2Yzab+fWvf83atWu56aab+NKXvjTj3m3cuJFt27bN2M/k5CSHDh3CarWyceNGbr/99nBLyWaz8aUvfenEPRQEzGYze/fuRafT8e///u/ccccdFBYWhsu89tprvPrqq2g0GjIzM8nMzKSvr4+uri6++c1vct1110XkZLnuuutwOp0UFhZSUlLCokWL+MMf/sADDzzA2rVrI+7xBx98wPe//30KCwupqqqiurqabdu2cfXVV4crTlmWmBQHGPG2M+Lp4P2Dm1AkOtAlRnKIgIIUbQEZulLS9UHSdw4quPWW2ygrK2P16tVkZGRQW1vLt7/9bQyG6GaN5557jr///e+sWrWK8847j3feeYe77747akI6CGZAveKKK8jOzmbVqlX09PRQWFjIHXfcEfUdgeC79fOf/5yqqiry8vJ4//33+cEPfjBrRtNbb72V0dFRlixZwr59+7j00kv51re+hc4ohcy2bdh9bThCc1GaiLkvjSIJo7oQk6YAk7owPD39w1fYvv2fFBQUsGvXLpqamiBO8JEEv7f/dkY9e6OWV6DGoM7HpC7ApCnEqC7ApC7AqM5HpTAQCARQKBQfSQV8VNVwNiHJAcZ8xxlwH6Xf3ciAu5Ex33GimVmSNLmk6xaHFNpirLrCBed7/VFT8/p8PiYmJmatqE/G4cOHsdlsXHTRRXOamiRJwmaz8fDDD1NaWsoll1zCkiVLoprOvF4vAwMD9PX18fLLL1NbW8uKFSu46qqr+PKXvxzxznk8Hjo7O2ltbWX37t386Ec/Ij09nVtuuYXvf//7EZV8IBCgs7OT+vp66urqeOqpp3A4HFxzzTU8//zz5OVFZl4dGxtj/5Gd1HXtoKZjO4ZMH0XLU9AnRt5jpaAhTVuC2pXGSFuAza/v4Y3/2UxWVhYbNmzgq1/9atRnMzk5ycGDB/nggw944oknUCgU3HnnnTz55JNR8+rIskxXVxf79+/nkUceobW1leXLl/Pyyy+zevXqqM/B5/PR0NDAr371K1566SUyMjK4//77+fa3vx3zGY6Pj1NTU8N//Md/YDAYWLZsGT/60Y8iBFfQE2oUh6+T3uHD7Dq4CZf/GFkFKiwpsQPpypMfJMd4Iy0tLdxyyy0cOHAA4gQfSfD1I/9Nn+PtEIFP1ZBBQterskNja3464A5MMuBuChP6oKcpageoSWUlQ182rcldglYZH9ThZJytyvyjVlTj4+MoFIo5k3NNwe1209DQQG5uLlar9ZSucdeuXSQkJJCRkYHa7GfI28Kgu5kBTzNDnuao75lWTsSqXkyuZSmLzNVYtcUxvaNGR0cZGBggLS2N5OTkOfuPZFnm2LFjpKWlzWlWm4Ldbkev159SHxB8tH69aJgawtIhduAUO3D4QuZi8Rir0l/AargYiNvgw4hG8AHJi0LQLBhV/XFBkgOMejtDyvwo/Z5GJnyRgR9qQUe6vowMfTmZunLS9WUYVcln4Yw/efgkLw6/Dbtow+Gfmiaxh5Z9khe/5Mcvi8FJEvHLfvySiDj9d2hZQkIpqFAJalSK4Fw9bTm4Xo1KUKFSBP/TKfWYVAmYVBZMKgtmlQWT2oJZlYBRZUZ5DnqpyLLEuK+HAc8JcTHq7YwI4lIKaqzaIjL0FWTqy8nQV2BWze2ocK5Bkv2AHI6BiRN8CPMp0OlMwxOw0e9upN/dEFTn7saonWJJmhwydOVk6ivI0JeTos1f8K5vsizj9NsZ9g0w4h1kxDvIuG90GonbsPsn8UmxB02fLzAoTUHSn0b8yRorqdoMUrVppGrS0SpP3yNjvsAnuRlyN9PvOcqAu5F+dyPuQKSt2qhKIVNfSaa+gkx9xawq/1xFnOBD+LQQvCxLjPm6Q2R+lH73UcZ9xyPKaRQGMnTlYUWUri9dsP7lkixhE8cZ9g4y4htkxHuCzIe9g3gl9yntRymoThCoKkigJnVwWavQhdV2pCoPKfHwshqFoCAgBxAlH/5QKgK/5EeUfdNaAn78of9FScQjubCLk9MqnuDc6bcjzzJg83RYVImkatPDk1WbQYomHas2HYNqYZrSZFnGJvbPaHGOeNojEr4pBTVpusVhws/UV2BUnZq30kJFnOBDOFcJ3htwMuhpCiv0AXcjXskRUS5RsyikdsrJ1FeSosk762HWpwO7OEmvu4te9zF63F30uY8z6h1EnCOZV6I6OaR000nWpAbJ+yQS1yn087LJL8kSLr8Du38m+dvECUa9g4z4hhjxDuAKRHo0TYdBaSRPZ5lVAAAgAElEQVRNm0W2IY9sfR7Z+nyy9Dlo5oHb4keFKHkY8rSERExQzLgDkbEoFnVGiOyDSj9FWzDDLXehI07wIZwLBB+0V3bT7w41XT1HGfV2cbJni0rQkq4rJdNQGXyxdeXoVQtLnUuyxIh3MEzkve4uel3HsPmju5UpUJKstWLVBJVrijaoWlM16SRr09B8CpruTr+DEe8go74TrZdR7yDD3oGY901AIE2bGSR8Q36I+POwqGMPXTkfIcsyk2If/e4G+txHGXAfZcTbSaxvI0N/whS5kPuV4gQfwkIkeE/AzkDIRTFoj2zCJ0WqNLMqnUxDBZm6CjINlaRqCxeUSpFkiSFvP53OFrpdHfSGlHk0u7iAgHWKkEJTmi6TJE3qOdkJ+XHBG/Aw6htiwNNLr/sYva5gpRmL+C2qxBDp55FrKKbAWLLgSN8bcIY7b/vc9bN/PyGyz9SXY9UVo1wgifzmJcELgnAJ8B1gJZAJXCfL8qZZyq8D3o/yV6Ysy7Fjg2fuY14TfEAWGfF2htzHmhhwN0a1nUcqkLIFZ2f0Bjwcd7XT6Wyh09nCMWdrVPOCWtCQpc8NE80ifT6ZupxzoiNxvsAmToRMXsEWUq+7iyFvf1Sbf4omjQLj4uBkWkymLgfFAjLznWoLOGjLLyEj5DmWoSvFop6fQwHOV4K/CrgYOAj8lVMn+FJg+kUMybJ8Skmx5xPBT71og55mBt3NDHqaGfa2R82gmKDODrmFlS9IG6Isy4yLoxxzttBua6LL3UavuwvpJDc4taAhz1hEnqGYRSFTgVWbiRSQTtn3OI6PB96Ah35PT9A85jpGp7OFAU9PBOlrFXryjcVh0s8zFqNXLqxkecE+rOYTAX2eRjyBSJ7UKS3hYL70UBTufBBW85LgZxxIEGROneCTZFmOHds7+3FiEvzExASbNm2isLCQwsJCsrKy5gwW+fDDD6mtrSUnJ4dFixZRXV0dNcBClmXs/iEG3E1s3vU6AeMIyiQHqCKHFtMqTOGXJ0Nfgcm/iEfXb0Cn02G1WvnqV78aEUZ/Mg4cOMB3v/tdLBYLVVVVPPjgg7MGc0iSxIsvvsgLL7yAyWTixhtv5Fvf+taspGq321m/fj1//OMfSUxM5JFHHuG2225DEAQkWaLf0027o5F2RxOdzlYmxcjcNGqfjqq0ZRQYSykwlZCtz5tRcU1FHT722GPhPDkPPfQQt91226zPZnx8nGeeeYZNmzaxaNEirr32Wu688845n+emTZt47733qKiooLKykjVr1sy5za5duzCbzVRUVMwZXDN1TZIknVLZ+QZ3wEWXs21aq6stwjtJQCBDt4gC42KKTOUUm8pJ1Cws+7Ysy0yIvSH3zKMMepoZ8XQgRRkK0KSyhr7XE8R/poP+Tpfg56tkOiwIghaoBx6XZfnDWAVD5aa7B0Qkad66dSvvvfcejY2NvPvuu6hUKr75zW/ywAMPRA1Bd7lcvPDCC9TW1tLU1MSBAwe45JJL2LBhA0qlMkzmQ55Whj2tDHla6bU3ICpC5od8mPq0lYJmRhKmDH0ZCeosBEHg5z//OTt2PMPAwABtbW2UlpbyzDPPxCT31tZWfvGLX3Do0CG0Wi21tbV84xvfYP369THJ/Q9/+ANvvPEG/f39LF68GFEUefzxx7n66qujlh8ZGeGZZ55hz549aLVaysrKWLZsGc8//zwpRYnsHPkHLfYG2hxHcQVmevHIARn/EOjtFobqx3nwq49SVbg04hiBQICf/OQn7Nmzh5GREfLz88nOzubf/u3f+PGPfxw1z0h9fT2vv/46tbW1TE5OkpiYSEZGBgqFgq9//etce+21EU3rP/7xj9TW1tLR0UF/fz8QDEk/dOgQN998M+vWrZtB7oODg7z55puMj48zPj7O2NgY4+PjNDQ00NjYSG5uLj/72c9mHGvXrl0cPx5pYnvmmWcoLS3lmmuu4corrwxHkIqiyNtvv01ycjJJSUnhqaOjgyNHjvDlL385Ii9LS0sLo6OjFBQUkJ4eHDNAkiTa29spKSmJ+hx37txJUVHRjEybXq8XrTa6J83g4CBDQ0NUVFRQZllCmWUJkiSBAP3u7jDhdzpbGPUN0e/ppt/Tza7R9wBI1WZQYqqg1FxFsakCszqys9/tdtPU1BROqub3++dstbW0tGCxWMjIyAjvY67MqXv27GHZsmXodDpGRkZITU2NKCMIQnjAE5OtjCyPk6LifNqGarBxnEnhOEPuZkZ9XcE8+45h2h07w9vrAqmk6UrITaxmpFPkkmXXoFfFthiMj4/T1NREdXU1x48fR6PRzJpDCuDIkSOoVCpKSkr405/+NGvZWJhvCr4UWAccIEjadwD/CZwvy/KhGNs8Djx28vrpCn7Pnj0AlJWV8Ze//IXrrruO5OTYikMURbZs2cKSJUtQqZQcad1N0fJUhr1tYVL3RMn/LsjBBEsqZyr5ycspTFlBijYvZhDRu+++S2VlJZmZmdTW1lJdXT2r/a+3t5eenh5WrFiBSqWit7d3TqW/Z88ecnNzycrKwuv1IsvyrFkIHQ4Hhw4d4vzzz8cp2Dg6foQOdxOt9oaIjjqNQkuhsRSjI5EcbREXlqxFp9ITCATmVK9btmxh5cqVWK1WgDk/9vb2dnp7e1myZAmJicFOQKfTiV6vj6nAt2zZgslkorCwMFwZ9PT0oNFoolbsg4ODvPfeeyQnJ5OYmIg5yYIxwcS+Q/uZcEyy7jOXIagV+CQxPNXUHma4fwilLKCQFOH5b//f18jLyuHKK67ki1d9gezMbCBIUC+//PKMCmR8fJyBgQEOHTpEYmIiGzdu5K677grfw927d/Pmm2/S2dnJ0NAQsixjNBrZuXMnl1xyCffffz/r1q2b8e48/fTTHDhwgP7+fvR6PVVVVezfv5+1a9fywAMPRLz/TU1NPPfccxw9ehS9Xs/q1avp6+ujrKyMe+65Z8Y7YxMn6HS28rc9f6bH34k2U4GgmPneZupyWGyuYrG5kiJTOXqlgaGhITZs2MCRI0dITU2loqKC+vp6fvazn1FQUBD1Gb799tu8+uqrDA0NUV1dzYcffsjDDz/MV77ylajfit/v53vf+x779+8HgukNysvLeeqpp2J+KwcPHuSll16ipaUFWZZpamri8ccf584770RWiAx5W8Mm1kFPc8xUyppAIrmJS0jTFZOmDaZRNqiCuXK6u7t5/vnnqa+vp7u7m8bGRm655RYeffTRmNf+5ptvsmXLFtra2ti1axculwsWsokmxnb/BI7LsvyfMf6PpuB7TscG75d8jPm6GPV2MuxpD+c6j5ZDQ4GKFG1+OB92mq4Eq7ZoXqRIPV04/DZa7PWhqYFR39CM/1WCmgLj4vCHm2NYWP0EnoCXUd8EY95JRn2TjPkmGPVOMuabZNQ3wbhvEnfAg1cSESX/KQcdnQoUKNAo1WgVaowqAymaRFI0CSRrE0nWJGDrHUO2+bl42YXkJGWhUsxeQXZ2drJ+/XpKS0upqKjgc5/7XMzUxC6XK5wUy2QyUVVVxcaNGykqKopa3uFwsH//fm6//Xa6u7tZtWoVL774IsuXL49a3u618W7dZv5n+29Q5khYF89MACYgkGsoosRcyWJzFYXGxYwNj7N+/Xr+8Ic/kJqaysaNG8Pmv2iQJIk///nP3HvvvZhMJq655ho2bNgwq1nSZrPxla98Ba/XS2ZmJo899hilpaUxywP85je/YfPmzaSkpHDxxRdz4403RpyT2z/JkKeFIW8rPbajtA8dRJ0QPT7DpLKSpivGGuKHFG0B//jbh3Qf7yYnJ4eioiKWL18+q7CbmJjgl7/8JQ8++CCcgwT/NLBGluULT7H8nJ2swbSn/aEBDjrD8wlfb9QBjpWCBqu2EKuuhLTQlKzJW/Dh0n5JpNPZQpO9liZbHb3uYzNITYGCPGMxJaZKFpsryTeWoJ7H1yzJEoOeUY67+uly9tHvGWY0ROLjvkmcgVOLdD0Z08lZo1CjUWjQKFRoFGqUghJR8s9Q9T7Jhze0fDoQEEhQm0jRBMk/RZvIIkMGeYZM8oxZJKg/+lBxp5MX3W63Y7FYTnm78fFxjEYjPsFDq+MoLfZ6Wh0NDHtnKl61oKHYVE6JqZKKhGVk6BbNS8+VjwJPwB4ShUHiH/a0Me7rIVpWVpWgI0WbR4q2gFRtQXg+pfaj4ZzoZI2x3T8AuyzLXz7F8hEE3+eqZ8DTxKi3k1HvMUa9Xfhj5IHXKcykaAuw6qZGqykhWZO74HO1QPAjH/T00mSvpdleR5ujMcIHPVufFyL0KopMZeiU8y8dcLCDzE6Xs5euEJkfd/Vz3NWPV4o99iiATqk9QZwhBZ2iSSA5NBlVBjSKmWQ+l5qe7TxFOUj+3oAvXAHYREe4BTHmm2TUG2xBTP0OzOEwlqA2k2fIIs+YSZ4hi1xjFrmGTPTK+dl6HPeN0mpvoMVRT7OtLsLUl6BOotRcTal5CaXmqqj2+4UIX8DFsLedYU/QtDvi7WDM1xVzHGK9MnEG6ecYlpGgyQTmKcELgmACpnoSaoD7CXrJjMmyfFwQhB8C2bIs3xwqfy/QCTQAOoI2+HuAz8qy/N4pHjOC4Dd1P0yXc9+MckpBQ4pmWi2qKyBFk49RlbLg1cR0TJldmmy1NNnrIjxdzKoEysxLKLVUU2qunndBLpIs0e0aoMneQZezLzi5+rH7o4frqwUVOYYMcg1ZLDKkB8lcmxA2iRhU86/Cmo5gvh1n0HwUan0Me8fodg3Q5epj0DMa03SUrk0hN0T6haZFlFuKSNbML7KUZZl+Tw/N9lqabLW0Oxoj0k8s0ucHyd5STaFx8bxuNX5USHKASV9faPD5ExaEaENdXp5+L9VJXwDmL8GvI3rg0m9kWb5VEIRfA/myLK8LlV8P3AlkAy6gFtggy3K0fcQ6ZgTBHxr7M32u+hnNoQRN1jmhyk9G0OzSGlTptlp6TjK7qAQ1RaayMKln6XLnVYUmSiJtjm6O2tppnGyn0d6Bwx+tD0QgU59GriEzbLrINWSRqT+3I109AW+Y7KdaLl2uPsZ90b/5DF0qFZYiyi1FVFiKyNbPr9S7ouSjw9FMs72OJnstve6uGf9PmXNKLUsoM1efE+acaBAlN2Pe4zNI/2Lr7aTrg30G85LgzwbmU6DTmYAsywx6+2iy1dJsr41qdsnS5VJmWUKpuZpCU9m8ytfi8rtpsndy1NbO0cl2Wh1dEbZrnULDYnMBRaYc8oxZ5Bkyydano1XOn+s427CJjnDfQ5erj1Z7F8ecvUgnqcIEtSlE9oVUWIopMC46bRPUJwGbOBFscdprZzHnBMm+1FyNSX3uf+MQJ/gwPg0E7/TbaQ6ZXZrttUycZHYxqSyUmYO+zIvNVSSo5x7U90zB5XdzZKKZuslWjtra6YpBQtNV53wjoYWC6ZVn42Q7LbNUnhWWQpYmllFqyZ83LSBZlunzHKfZVkezvS6GOaeAMks1ZeYlFBgXo1IsjNwyHxVxgg/hXCR4UfLR6Wyhxd5Ak72WHldnhNml0FQaJHXzEjL18yd3iCzLdLn6ODR+lINjDTTaOyI6EafMCFOkPt/MCOcKpsxfjaHWUjTzl1GpZ1lSOSuTKliRVEGSZv58Q1PmnCkngZPNORqFNmjOMVdTYq5ccDl0ZkOc4EM4Fwg+IAfodnWE/dE7nS34T1IumbqcYMSheck8NLt4ODLRzKHxBg6OH2XUN7OZnaVPY3liGRWWYsothaRo51fH7qcFUx3YR23tNEy2cXiiEftJhF9kzGFFcgUrkypZbM5HOY8I0yZO0Gyvo9kWtN/b/TPzxJtUFkpMFSw2V1FiriRVk75ghUOc4EM42wQvSRKyLH+kPCRj42Mcm2ijXzhOt9hGh6slIv9HgjqJElNl0J3MUkVv6wD9/f2oVCq0Wi2rV6+e85ibN28OR+sVFhZyzTXXzPnCv/baa+zcuZNAIMDnP/95rr/++ogysizT7RrgYIjQ6ydakYUT75VGoaY6YXFYFWbqrRH72Lp1K//93/+NSqXiM5/5DOvXr0ejmb3SGhgY4N5778VoNJKZmcm9994bNSx9OkRR5He/+x1ZWVnk5eWRl5c3Z+i7LMsEpABeOYDb78UV8OEJ+HAFfPgkPzqFBoNKg145NdeiVagWHJkEZIlW+zEOjh/l4HgD7Y7uGf+bVAaWJ5azIvQcEzUf3R//k8J0c06LvZ52Z1NEX1SSOjUUbBWcEtQLJ39OnOBDiEXwkiSxbds2HnnkEfLy8qiurubee+/FbI79kg4PD/Pggw+ye/dusrKy+MY3vsH1118f88P1er3s37+f66+/Hp/Px+rVq3n++ecjoudkWWbUN0SzrY6awX20OxuRNDOTHBmUJkrMFWGf9DRtMI2py+Vi+/btvPrqq/zlL3+hqqqKV155hQsuuCDqOcmyTHNzM2+99RZ//OMfOXjwIPfccw9PPvkkJlP0hElut5t3332XTZs2UVNTQ3t7O8899xy33HJL+Nr9UoD6yRb2jNVyYKyeYe/4jH14+h2sSKrghpVfoNJSHNEhKkkSNTU1bN68me3bt6PRaDh8+DAbNmyImWjM5/Nx8OBBduzYwYcffsj4+DjNzc1cdtllPPXUU+Tm5kZsI4oira2tNDQ0UF9fT0NDA++//z5Oj5t//+ZtXPvVGxFNKvrc4/S5x3CIHlwBHy6/F4fPjcvvxSOJiFEC4GaDAgG1rMCk1WNQaTGotOiUagxKLUkaI9mGZLL0ScHJkIxVa5535oQJny1oWhs/Ss14Y0SgWIkpj1XJVVyYspRcw/xKs+uX/HS52sLBVsecrQTkmUP/pWuzQoRfTbGpHOMZGurwdIPOQik64gTPSQS/YcMG3n//ffLz89m8eTN33HEH9913X8zQbpvNxtVXX43VakWv19Pf38+TTz4Zk0QhGOL82muvsWLFCtra2lizZg133303Wq0WWZYZ8vbT7mikLTSd7I8u+yBXV8yKjPNZbK4kS58X8cFv376djRs3sm7dOtatW8eOHTt44IEHZlW699xzD3a7nS9+8YtUV1fT19fHpZdeGrN8V1cXt956K5/5zGe47rrrSE1Nxev1kpOTgyj5qZ1oZtfoYfaOHpnZnPfLZPgSubzgYlYmV5CEOeb99fv9fPazn6WyspIrr7ySdevWYTAEc5Wkp6dH3ebNN9/kueeeY+XKlaxdu5aLLrqI1NRUmpubo4afS7LEf979NXoco6SV5pKQn4Eq1YxHL3DcPoxTIZ5WIgIB0Cu1YcU+MTKGw+dGVitBowD16XVQKhHQumRMARUrCsopSs4iS59EtiGZ3e9sY+/7O6mqqqKyspKqqirS0tJob28nMTExaqvlzjvvpLy8nDVr1rB8+XJUKhUulwu9PvpwhTt37mTTpk1cddVVrFmzBq1WOyM/UEAO0GI/xoGxBg6NH6XD2TNj+0X6dM5LrOaS9FXkG7OjHuP48eM8/fTTXHvttVxyySV4PB60Wu2s7+8rr7zCxMQEX/ziFykpKaGlpYWysrKY5f1+P3feeSeXX345n/3sZzl8+DCXXXYZkiIQ6seqp8XRENGPhQxmfxIpYgaZijy+sPo6TLMkD7vvvvuwWq1ceOGFvPXWW6xfvz6cEC0aamtrefbZZ1mxYgX9/f2kp6dz1113zZoX6sc//jG9vb1UVFTw29/+ll27dkGc4GObaAKBAJOTk7MmGjsZTqcTg8HwkWpcURQZ9Q/S6jgaTqd7sruXUlCSZygO5XWpIldfhFo5/zwAfJLI4fEmdo3WsG+0boaKS1CbOD95CeelVLMkofSsui2O+xzUT/TQMNlN/UQ3Ryd7cPijRytPQadUh1R0MtmG4DxJY0SvDJK3XqXBoNSgU2pQ+mUSjZY5TS8BWcITEBmeHOMXv/ol519yMXklhXglP+5Qy6DfOUb7WD89zhGGRDuTeJHneL1SlEasXg2KPjtjtceYaOzGNjpOW1sbd999Nw888EA4cRsEE6ft3LmTnTt3UlNTg06nIyUlhZGREX76059SUVExY/9+v5+9e/eyZcsWduzYEc7WKcsyP/7xj8MJ3qYw5pvk/a49bG3/J4O6SVCduIA0TTJr01ZyUepyiow54fslyzIHDx7kjTfe4IMPPiAhIYGenh5ee+01lixZEvW67XY7f//733nrrbdobW2ltraWRx99lAceeCBmcrr29na2bt3K1q1b2b59O/n5+bzyyiucd9554TIuv4M2R2MoCLCOYV9/xH5SVRmUJQbVfZGpfEYQ4NjYGHv27GHLli08//zzGI1GHnroIe6///6oJj9Zluno6ODQoUNs2LCB+vp6qqqqeOmll1izZk3U6/B4PDQ3N/Puu+/yne98hxBXxwmeM2yDn54bvS1E6A7/zGegEtTkG4vD+bPzjSXzdhBkb8DHofFGdo3WsH+sHnfgBFEmqS1cmLqMi1KWUZFQfFY63XySnxZbPw0T3dSHCL3XHZmLXiUoydQnzjCFZOmTyAz9TtIY54VZwS8FGPLY2NdWh2TRMCTa6XMFTUb97gmGvZHfs1JQkOLToBp0UaRJ4cKcSr586ZUxM2va7Xbuvvtudu7cSWpqKt/97ne57rrrYp7T0NAQN9xwA7t27SI3N5cXX3yRz33uc1HLuvxuPhw4xM+3/m/kfC1K7QniTdOmcFHqMi5KXc5iU96M+/3YY4/xxhtvoFQqefTRR6P270zHtm3beOWVV5BlmfPOO2/O8QyGh4d5+eWXw/1U//Ef/zGjEpwOmzhBu6OR9xq2MKYewKmJvOdp2qww2RebykjUpNDb28vAwADJyckkJydjNptnHV9gavwDq9U6a6K06XC5XLjd7qmWWpzg+YQJ3i5O0uVq45izjS5nG12u9ohOUbWgocC4mGJz8IXIMxTN65DrIKkfZcfIIQ6O1eOZltMlRZMY/EhTllNqKTjjpD7hc7FvtJX6iSCZN9v6EE+ypwLkG61UJeZQmZBDdWIOhab0c8J/3un30jjZE7z+UIU25nNElLOo9VQm5FCVmMPSpDyWJ+WjVpx+ts+PaisGcAe8HBxrYNdoDQfGGmbkBgq+R8tZa10ZQfbzDQ7RRruzKSTYGulzH49IEZGoTibfWEKeoZh8YwmLDAWfmDdbvJM1hI+b4P2Sn173sTChH3O2RqTRBdAqdDNGuMk1FKH6Fz6uMwFR8nN4opGdw4fYM1aLJ3DC6yBNmxwm9RJzZH/AJwlZlulyDrNjuIkdQ03UjndFCYYyUJWYQ1VCDpWJOVQmLMKsnt95Zj4uyLLMgGciXOHVTwYrPZ80s6PeqNRygbWEtdZyLrKWkqg5s8PsTYmGXaM17BurP+n9SmGNdQVrU1dQYJz/6QdcfkeQ8O1Bwj85BQiAAiXZ+twg6RuDpP9xuWbGCT6Ef4XgJVli2DsQJHRnO8dcrfS4jkX4oAsIpOuyyDOUkB96kBm6RfPOCyIaAnKAuokWdowcYs/okRmBLqnaJNakrmBN6gqKTWc2R41fCnBkvCtE6o10u0Zn/F9symBFckGQ1BNzyNYnz3tSOJMQJT+t9oEw6e8bbZuh8hUIVCflstZaziVpZeQZrWf0/vkkkZrxRnaOHGTfaN2MFmKWPo01qStYa11JriHzjJ3TvwJvwEO3u5MuZ2tY+J3czwZgVJrCZJ+jLyTbkHdakeVxgg/hVAneJ/nod3cHBxx2H6PX1UWf53iE7yxMPaQQmRtKyDUWLahBhyVZ4qitnZ3Dh9g1WsOkeOLDT1Jbws3mUnP+Ga2k7KKbXcMt7BhuZPdwC/ZpnaIqQcnK5ELWppWxNq2MTP38SbewECDJEo2TvXww1MiO4Sba7DNzsucYUlhjDd7bZUn5Z9SU5Q34ODBez47hgxwYa0CUT7Q88gyZrLGuZG3qyqjxEvMVU4POTxF+l6uNbldnhDiEYAbXbH1eaND5fBbp80nVps/67cUJPoRoBO/02+lxd9HrCpG5u4shTx9SFN9mjUJLli6XHENBuKm1ECPgJFmixX6MnSOH+HCkhjHfiSg/s8rIRanLWJO6ksoz3FE67nPw9/5atg8e5fD4sRlpCxLUBi62lnJJWjnnp5ZgVM3PTujpOB079dlAv3ucnUNN7Bhu4uBox4w+DLNKx4XWxfxbRjVrrKX/kt3+o8Lld7NvrI6dw4eomWjEP+28ikw5rEldycWpy0nXRXe5nc8Imne76HIFSb/HfYwhT1/UdM8ahZZsfV6Q+PX5ZBvyyNTlhPvt4gQfQjSC/3nrE7Q7miLKmlSW4M0M16Z5WLWZC8LUEg2SLNFsP8aHIzXsGqmZkSLAqNRzfsoS1lpXsiSh9IwqNp/k58OhJt7uq+HD4eYZpF5gSmOttYy1aeVUJeacFa8cWZYZ9Tpps40w7nNhF73YRQ8O0YtD9GIXvTj8obnoxSF6wuu8AT8GlQazWodJrcGs0mFSazGptZjV2uD68P/B9Rl6C8WWVAyqs9Pp7vR72TvSyo6hJj4cbmJCPGGmS1Ab+GzmEq7OXkG5Jbo/+ycFh9/FnpEj7Bg5SO1EywwBVmLK46LU5QuW7Kfgk7z0TVkOXEGx2ec+jihHDlRzbfZ/clna54E4wYcRjeD/2vMbGiZryDbkzSB0iyppQaiv2RAk9c4QqR+eQeo6pZbzkqtZk7qCFUnlqM9gpj1ZlmmY7OGdvkP8vb8Wm3jCy6jcks3nspZySVo5iwxn9mO1+Ty02oZptQ3RMnliPu6LzDn/SSPHmMRii5WSBCuLLWmUJFgpMKWgUZ45BR2QJRomutk+eJQt/YcZ8Z4YTL7AaOXz2Su4MnMp6fozmy9owmdn9+hhdg4fpMHWPkP1FptyuThM9rOnplgICMgBhr0DIcKfMhkf46sF91JirgTiBB9GNIKXZGnBqvJokGSJJluQ1HePziR1vVLHecnVXJy6nOVJ5WimkbokSTgcDiYnJ1EqlWRlZbmUbZkAACAASURBVM1+HEnigw8+YGhoiOHhYS644AJWrlw56zYD7gl+vOU3HAz04TGdaCWkaS1cmbWMq7KWU2SOjFZtbGzkvvvuY3x8nAsuuIAf/vCHGAyz93PIsszGjRs5cOAAAPfff384StcX8NNiG6ZlcojWafMBd/RvQwByTcmk6UyYQ0rbpNZhVp1Q4ya1NvQ7pNbVOrRKFU7Ri8PvC6v+sNKfpvrtU6pf9NLtHGfEG31EKqUgkG9KYXGClZIQ6Zda0sgzffKdygFZYt9IG+/01bB98CjeUGphAYFVKYVcnbWCdekVGM6w6WzcZ2P36GE+HKnh6GTbDI+qIlMOF6eu4OLU5WScA2Q/BVmWkZHDvBUn+BBm62Tt6OjA6XSSk5NDQkLCnB+MLMu88847GI1G0tLSyMjImDMKtq+vj7/85S+YTCbMZjOXX375nNts3bqVt956C4VCQXV1NbfffntEsERAlmgOkfqu0Zk2dY2s4qK05VwUhdSnMDY2xsUXX0xTUxOXX345v//972cNrZZlmX/+85/cddddtLa28uSTT7J+/fqo98zl97JtsIE3u/b9H/bOOzyqMm3jv5n03ntIh5CEQOgd6R0CKIjCgroCdorCWlFWlxVXpVkQVEBBEKQTEQKR3pOQhDTSSO+ZZNKmn++PCQMhFel+ua9rLsKZ97SZd+7zvE+5H2IqsrRsCegLIka4dWGsa1d62Pk26n5JS0vjp59+4siRI2RlZfHKK6/wzjvvNFsskpWVxb59+wgLC6OgoIDa2lo2b96MTYA3ZwozOFOYzqWSTGRqVaP7G9YokSRlYFBWzZT+Q3lu7GTaWzliot/0CkelUrFw4UJsbGwIDg6md+/ejWrftBZl8mpSKoq5Vrea0P5dRKWyYZAfwNHYnL6O3vR38qGfozeOJvdX6KtKJSOi4CphuVFES67rtpvoGTLUqRNj3brS3db7gRtO5Qop50pjOFMSTXxFSqNk388u5LEK0LYGbQRfh8YIPj4+nnPnznHkyBF27tyJubk5S5cuZcGCBRgYNPxRy+VyDh8+zIULF9i6dSuZmZlMmTKF5cuXN6p7AlqiOnDgACdOnODQoUO0a9eOVatWMW7cuEbHC4LA/v37+f3334mNjSUyMpK3336b9957DyMjrYWk0CiJKU/iXEkMF8vi6vUhFWRqauPKeHnoc0wIHt6k+yU8PJzvv/+e0tJSnXrie++916TyZEVFBevWrWPfvn307duX7t274+TkxLBhwxqMvSbN49fMcxzOvYKCm8GxzubudNNzZXbPMY0GSpVKJT/99BPbt2/H1taWWbNmMWrUKHJzc/H09Gz0uq5fv86WLVsIDw/H0dGRSZMm0X/EEE7lpxFdU8SF0iyKZfWLf8xFBphXKai9XoBQIKG9lQNDg7oztN8A/vjjD2bPnl1PcE0qlXL+/HmysrLIzMwkKyuL3NxcVCoVYrGYtLQ0jIyMeOONN5g9ezYWFhZkZWVRUVFx++UC2ofCJ598wsCBA5kwYQK+vr5oNBpycnJwdXVtUIUpCAKFtVJSpCVcu4X0UyqKkGvqF3b5Wzky0MmXgc6+dLdr18CtU1VV1aSYXGOQy+Xo6+s3Oi9ya8o4lBfN73nR5NTcrBh2MrJiskcvJrn3xNao5XNpNBoUCkWz+iu3o6ampsmV3A2yP1sSzdXbyN7dxIk+dl3oax9STy6hMVRXV2NsbIyenh5KpbJRTmhw7vJyrK2tqa6uRk9Pr8V7kslkqFQqzM3NSUlJwc/Pr0UDs7S0FCsrKyorK0lPT6dHjx7QRvANCX7nzp3U1tbi6upKREQEb775ZpNCWKD9MtauXUvv3r0pKCjA3d2dfv36NXvekydPUlhYyKBBgzh58iQTJ07UEXVTuKEC6evrS1paGsHBwdSoZERJEjhXeoXLkvh6xSEmIiNca22YEjSG9gbtsLG0blFSd8+ePQQHB+Pn51dPPKopSCQSwsPDCQ0NbfT6VRo1fxbGsyPrHDGSmw0XnPQtmOzdhzGuIS2mNGo0GjZv3kxoaGirdYEiIyPJyc3FuUcQFyQ5nCpMI7Yst14+grGePj3tPenv5MMAJx9O/rYfj3Ye9O7d+4b10ywyMjL45Zdf8PT0xMPDA09PT1xdXXU/+Li4OIKCguqtLjZv3kxUVFSTx9y0aROenp6MHz+eV155BWtra15++WXy8vJQq9WIxWKddLGHhweRkZFYWVnx2muv6R52v+76jZW7t6D2dkTcwY1yU3G9+zbVM6CPoxcDnf0Y6ORLOzNrpk+fTllZGWPHjmXSpEl4e3sDTWf9nDlzhsWLFzN48GBmz56Nv79/g7GCIBBXnkVYbhThBXE6rR89QcQIl85Mdu1BiINPk8SVlZXF9OnT6d27Ny+++CLW1tZYW1s3W7L/9ddf8+uvvzJ58mQGDx5MRUUFgwcPbjCuXFHJ+dIYThdHEiu5hkjv5jXY6VvRz1Fr2TdWiX3kyBH+/e9/4+npSWVlJYMHD2b+/PnNym+/8cYbREZG4ubmRlRUFKtWrWL8+PFNjk9KSmLhwoVUV1eTk5ODp6cnn332GT179mxyn6+++opdu3ahVCo5e/ZsmxYNPHw9+L+CSmU1l8riOFcaQ7QksV5esJ2hNX3sutDHrguBlr4PrfS+TF7FnpyL7Mm6SFGdNoqeSMxQpyCmevali/X9KT1XaTScKkxjf1YcpwvSqFDWFxBryZJ9mFCpVOTk5ODl5dXkGLVaTV5enm7F8OWXX5KZmUlISAjvv/9+PeXPqqoqLl26xLHzpzmZm0K5oykyD1swr1/B62Vuy2CX9oy29+PaqYvs3buXvLw8hg0bRkJCAu+9916j4l5qtZqIiAg2b95MXl4eY8eOJSUlhdWrVzewUOVqJRGFV9mScoKU2puV3SYlCt4cMI0xHt0aTbcUBIGTJ0/y/fffExUVhVgsZs+ePfj5+TX5GdXU1LB3716++OILrly5wrJly3j33XebdONVqWqIyDjLr9EHKLeRoW9y0yK3NrCgt11n+tqFEGzVod7vKSEhgeHDh1NRUUHfvn357rvv8PX1bfK6BEFg9erVbN26FXNzc6ZPn86cOXOadS/KZDLmzZuHnp4ejo6OvPjii83eO2iVPrdu3cq6deugjeAfD4IvlZdzsSyOc6VXiCtPqZcS5mxsTz+7EPrYh9De3OOhBogTKnL4NfMcR/NjdbnTtoZmTGnXm8nteuFgfH8+4+xqCbsyrrArM4bC2puZHZYGxvR38magkx8DnH1wNnl0v+O/gtLSUmxtWxdQVSqVvPDPf1JqoEHj54xJZ1+SZWWobklD7WLrxlTvrgx39GXnlm289NJLGBgYsGzZMhYvXtzkik4qlfLCCy+wa9cuQkJC2LlzZ5NEFF+ezfbrZzmcGw162rlqrWfKU159mNKuF/aNzBGNRsOHH37IpUuXUCgU/O9//2sxgH/w4EHS09OpqKigZ8+ejB49utnxRUVF5BblkViTQbwynXRxPjW3COeZ6ZnQyy6YvnYhdLH2x1jv0a27aPPB1+FRJXilRkmCNJ2ouuYJmTV59d73NHWln30Ifey64Gnq+lDTN5UaFccKrrIj8xxXK2529QmycmeaZ1+GOQdjeB+KYRRqFeF5yezMiOZsUYZuu7WhCZM8OjPaPYDOtm7oN2Mh3S9oBIFKhZwKhYxyeS3lchkVchm1ahWWBkZYGRljZWSMtaEx1kbGmOgbPJTvsEop50xROgeyrhKRd01H9qZ6Boxy7cg4xw74m9qh0WiwsbFp1ndcUVGBRCJBIpGg0WhaJOBSeSV7si/yW+Z5ypTaeJGeSMww505M8+hHsHXzvvD7DaVGRVzFNc6XxnC+NJYK5U3DQV+kT6ClL11tAuhmE/DQf4O3o43g6/CoELwgCOTLinWEHleRUk9ZT4SI9hae9K1zv7iaOD60a72BEnklu7MusDv7ok7HxECkx3CXzkzz6EOQdbv7ct6UiiJ2Xr/C3sxYyhU38+X7OXoz1bsrI1z976vrRSKr5UpJPnElBRTVVlEhl2kJXCHT/S1VyO6oQYiBWIyVkQlWhkZY3/KvpaExnpbWdLF3IdDWEeMWYiJ3gxJZFXszY9mZEU1G1c3gaHtLB6Z6dyXUIxgbo3svuaGL02SeI6b8Zpymo6Ub0zz7MtKl830xEO4EakFDkjSdc6UxXCiNpUheX/vI1tCKrtYBdLUJIMS6IxYGrZP2vV9oI/g6PEyCr1HJiKu4RrQkkShJAoW3TRobA0u62tycNJYGD6ZFWEvQumHOEp4fpysVdzCyZEpdhoSd0b1PyatWKTiUncDOjGiiy252B3I0tuApry486R1CO7N7rz8jU6mILyskpjifmJJ8rhTnk1nZUCSqKZjqG2BlaKyz2E30DKhUyimX11KhkFMhr0WpaV17PwOxmABbR7rYuxDi4EIXexd8rGwR32PLURAELpdksSMjmj9yEpHXqU4aiPUY6daRqV5d6ePodc/PC5AszWNH5jkO58fo1C5vuPimePTG/j7MrTuFIAjk1hYRXZ5ItCSBuIoUFJqbGjLiOmPsRj9aPwvPB15x3UbwdXiQBF+rlpNcmUFiRRpXK1JIqsyop6WhL9IjwNKXbjYBdLUOaLKV2cPADSvr18yzxJZn6bZ3tvbgac9+DHEKui8B3exqCT8kn2NfVhzVKu2KRk8kYohLB6Z6d2Wgk+89c8FoBIH0ijKu6Mg8j8Sy4no+6hvwsbKli70z7Syssapzs1gbGdf9bYKlodYNY9TCSkIQBGpVSsrlMsrrVgAVdauBcnktErmMlPISrhTnUyprWD1rYWhEF3vneqTvaHrvDAGpQsaB7KvsyIgisbxQt93dzJqZvj2Z7tPtvkgolCuq2ZdzmZ1Z5ymSadNK9UV6jHDpzHTPfgRYud3zc/5VKDRKEipSiS5PJEqSSFZN/W5P5vqmdLbyJ9DKlyBLXzzN3O474bcRfB3uJ8GXK6QkStOJl6aRKE0jvSqngWCZi7GDzo/XyaoDJo9Y4KZCUcPenEv8lnWewno/tGCe9uxHoJX7fTlvqrSY75LOcDD7Kuq6OedpbstUrxAme3XBwfjekJhCreZ4Tjp70uI5nZ9JpaJh4ZC9samOPEMcXOls74yVUetzs+8FBEEgp0pKTEk+McX5OhdRY8VZbuaWjPRoz5O+QQTZ3Tvhu3hJPjszotmfdZUqlfZzsjY04bn2vZnp2xNLw3v/mdwwLLZnniXuFsOii7UnT3v1Y7Bj4CPXpKVELiFakkh0eSJXJEkNmo+b6BnT0cKbQEtfAq18aW/uec/bVz6SBC8SiQYBi4HugAswWRCEvS3sMxj4EggCsoFPBEHYdAfnvCcEr22qUEKCNE33yqtt2OjD0ciWAEtfAi196WLt/8hW0KVVFrIj8yy/513RlaDbGJrxZLveTWY63AvES/JZl3SaI7lJOh/2ACcf5vj3o4+DVwOykkgkJCQk0KtXr1YVnIC26ORoYhwHs1OIUVUhkd/MlDDRNyDYzummRezggpuZ5SOzkroVKo2GZEnxTdIvzudaeUk93397azsm+wYxyScQV/N7853VqpTsz4pjQ/JZsqolAJjpGzLDtwfPt++DnfH98T8nVOSw/fpZjhbcdA06GVvxlEcfJrn3xOoBNyhpDdSCmpTKTOIqUkiUppEoTa+XmQNag8nXvB2Bln4EWPoQYOlz1+7YR5XgxwD9gUhgNy0QvEgk8gauAuuA74FhwCpgnCAIh1t5zr9E8OWKStKrs0mtyiKtKptkaQYSZf3PUYQID1MXAq20hB5g6YuD0aOrU64RNJwtvsavmWe5UJqq297BwoXpnv0Y4dIZoxYafavVatLS0khISGDEiBEt9pFUKBRs2bKFo6mxJDrpUe12U6RqhKs/8zoOoLNtQw2c6OhoQkNDKSgoYOvWrUydOrXF+8uurOD9XzdztDgbA+ebD1ZHEzMm+QYy3rsjgbZOjbp8EhISeOedd1Cr1XTq1ImlS5e2qH3zMFCllHOhIJs9aQmEZ6Uir7PwRUBfFw8m+wYxxqsD5gZ3v1JUaTQcyolnXdIZUqTFgLZ4bJp3N17s0Bdn0/tjBJTIpOzK1gb3JQpt9o2R2ICxbl152rMvPuYNtYseFagFDVnVefUMwVtlRG6gnakzHcy98DVvh695O7zN3O/Iyn8kCb7eiUQigZYJfgVaMu90y7btgLUgCM0nvd4c3yzBC4JAmaKCtKpsHaGnV+XUE+y6AX2RPu0tPLRLL0tfOlr6YK5/70lAEARUKhVKpRJBEFokUalUSkxMDCUlJZSWljJ+/Ph6ujJFsgoO5EayPyeS/FqtRSZChHW+AuFcFr76dnz+v/+1eJ7ExER69+6NTCZrFekKgsDXR3azOiYCkY/2esSIGO8RxDz//rS3apgppFAo2LhxIxs3bkStVrNs2TLGjh3b5Dkq5DLCMhL5/uIp0m+xnIzFeozx9meybxD9XTzRa4TU5XI5ERER7Nmzh6tXr5KamsqcOXNYunRpi1XHN3D48GG2bduGt7c3HTt25Mknn2yxOvheQaqQc+h6MrvT4rlQcDN91VhPn1Ge7ZnsG8QAV6+7jmFoBIGI/Gt8m3iaOIk2nddAJGaSVxfm+vfD07x11cd3CrlayZH8WLZnniWl8qbfu6uNF5Pa9WSIUyeMWzBIHjYEQaBIXkqCNJ2EilQSpOnk1BY0GCdGhLupMz5m7eqRvql+426xvwvBnwSiBEFYcMu254FVgiC0XGtO4wQfJUnQdjS6dpEikQR1I5+hCBFuJo74mLfD16wd7S08ccaWV+e9gqGhIU5OTrz++uvNCkwJgsAPP/zAmjVrMDIyYvr06cyfP7/Fzu/Dhg0jLi6O4OBgtmzZ0miV4Q0oFAr27NnD888/j76+Pps2bWLKlCmoBQ1nipPZl32JM8XJOl0Oc31j+hl6kvrLn/yxfS/jx4/nhx9+aJbQqqqq+Prrr9m3bx+2tra89tprzRaVCILAtycO8m3iaeQO2qWoPiKmeIcw178fHo0QglKpZNOmTfz4449Mnz6duXPnolKpsLBomFWhEQSO56SzPTGaYznpqOu8KyKgn4snk3wCGOPt36gVW1FRwaFDh9i7dy/5+fkMHTqUSZMm0blzZ9LT05usVJRKpSQmJpKQkKB7VVZWoq+vz7lz55gzZw5LlizB3b3lmEVRUREikQgHh3vnvsuurGBfegK70+JJr7iZAulgYkaoTwBT2wfjb3N35xMEgbNFGXybeJqLJdp0RzEixrUL4qWOjT+w7wUEQSBaksH2zLOcLEzUzWULfWNGu4YwqV1P2ls8Hq39ACqUlSRJM+qMyWzSqrIbeAdAy0EuJg741pF+D9tOtDPVGkp/F4K/BmwUBOG/t2wbC4QBpoIg1DayjxFw6y/bAsi5leCfP7iEMutbMhYEcDN2pIOVF77mHviatcPLzE339JRKpUybNo3a2loyMzMJDg5mxYoVBAYGNnl/27dvZ+3atfj4+HDq1CnWrFnDxIkTmxwvCAJPPvkkCoUCZ2dn7O3tWbZsWbPEe+rUKf71r38xfvx4jIyMGD9+PO6+XhzIucz2zLPk1VnrACE2Xkxy78m+Tzcgk1azcOFC1Go13bp1a9b/nJGRwYwZM3j55Zd55pln0Gg0zerdxEvyeengDxSaaOeRsZ4+T3t345/NLOllMhkjR45k8uTJvPTSS5iYNN4sWxAEjman8kXUaZIkxbrt7a3seLJ9J0J9AnExazrN7sUXXyQrK4uxY8cSGhqq02NpCklJSSxYsIDa2losLCwIDAzUvTp27IilpSWlpaUolUrdiun777/XyRU3BalUyvbt2+nbty8TJ06koqKCzp07ExgYSIcOHVoUqjp+/DhSqZQRI0Y0+KwEQSCmpIA9afHsT09EIr/5Exnh6s2/eg/Fz7plzf3ExESd4mhjiCzJ5tuk05ws0Lr6xIjoJbZhWf/JeDs1Lzt9A6WlpZw6dYrQ0NBWx0B+O3KAVIsazimydKtRgO62Pjzr1Z/+Dv71Kr01Gg3r16/nqaeewtDQEGNj4xb1mi5evEhWVhZjxowhNjaWvn37tnhd69evx8/PDz09PWxsbJo1ykD7u9qzZw99+/bl8OHDPP3Cs1SZy0mryiG9SutJuN2L0KPMF+dCczQaDVlZWSxfvhz+HxL8R8CHt2+v1/Aj4wg5yiJs5Gb4WXrSzb1Ts/4vQRCorq7G3NyctLS0ZvUobqCmpgYTExOUSiVKpbJF9wdoic7Y2LhVImCgtd4NDLQVknk1EnZknWNfziWq6zIgLA1MmOjWg1D3Hniaa6231qrj3YAgCKjV6havp1hWxcqrf7Lr+hUEwETPgFl+vXi+Q29sjVq+95bu+WxeJp9FneRKsXapbmFgyNT2wUzx60SQrWOrCOLG59sYBEEgW1rBxdwcLublsLjfQCz19FGpVK367m4gPz+fysrKZsekpqayY8cOJkyYwJAhQ4iLi2P/1VhiSoqpTUlDXFCInbm57mHi7+/Ptm3bmD9/Pp6enqSnp7N9+3bCw8Oxs7MjNDSUcePGNRBqU6jVnMhN57eUqxzJStHavYLACEcPPhw8FnfzphfB2dnZLFu2jIKCAt5//3369OnT6LgEST5fJ54iPC8ZAJFMSc9KI76a+TpmxibNzrWKigo+++wzTp06xaJFi3BzcyMkJKTZfSIjI/nss8+olcmw7eGLXm8PEoQSXVcwD1N7pnv1Y5xrN0z0DVGpVGzbto0tW7agUCiQy+Xs2LGj2ZVWTk4O27Zt448//uD8+fM8+eSTrFmzBmvrppucnD59msOHD/PLL7+QmZnJggUL+Oijj5pU8JRKpRw9epRTp06xatUqTE1NWbx4MW+99ZZunxtxwLQ6K7+PJoDsqHQ2b97MsWPHbhzqsSb4O3bRtMaC/7vghprfL9fPcLwwXrd09TJzYLpnP8a6dcX4Hqdn3Q6FWsWmlAt8m3Ral8c+0aMTb3Uadk+CcNHFeXweeYoz+VqXgIm+Ac8Hdmdup55YGzVu6bcGgiCQWlbGxbwcLubmcCk3h4Lqm/LCX42ZwNj2He76+luLRYd/Z29yIqB1NflZ2+BpYIh5eQUlsXH8vO479PX1eeONN1i2bJmOBLKzs9m/fz8HDx5EEATGjx+PRqOhb9++9ZQJk8qK+SLqFOHZWotbpNEwyMyeN0L64+3o1KSKZ3p6Op988gnFxcV88MEHFBYWMmHChAbjLhZn8smVwyRVaHPpxWVVeMQVMbPnEGbNmtXsvZeWlvLFF1/w1VdfMXToULZv397iKiY2Npb+/fsjk8lY+eO3aHq5sTf7kk7R0tLAhEnuPZnq2RcnYysEQWDx4sWEhYUhFov55ptv6gm3NYb4+Hg2bNiAQqHAxcWFt956q8nVJWjn1K5du6itrcXY2BhfX1+6devW7DkKCwtJSkrCzs4OOzs77O3tWzTAUlNTUavVdOzYER5zgl8BjBUEIfiWbb8AtvcqyPo4olJZy9GCOPbnXCa+4mbVZy87P5716k8f+/b3XZBMEASO5iXzaexRsutS6TrbuPJ+yChC7O4+dz6prJjPo05xtI6QDMRiZviH8ErnPn+p0Eet0ZBYUqyz0C/n5lImq78ANBCL6ezkTE9Xd0I7BuBv9+A6Ah1NTyUiI52LeTmkSyQN3ncxNqG3ezsGePvQ270dbhYN57JEIiEsLIxFixZRVlbGO++8wwcffFDPJXH7AxOlCvGlq+xY8C49gjo1OOYNpKam8tprrxEeHs4333zDvHnzGoxRCxp+y7jCyvg/KZNrXaDVUddY0GEAi59rOP5WZGdn880333D16lWcnJxYvXp1sysnjUZDQUEB6enpXL9+neHDh2Npb8PB3Ci2Z57R6dTricQMcQpivFs3etn56XLqH5fm6E3hkfTBi0Qic+CGBF00sAj4EygTBCFLJBL9F3ATBGFW3fgbaZJfAz8CQ4E1PIA0yUcNN9qnHcyN5ERRoq7M20Ckx2jXEJ7x6o+fRdMdme4lkisK+c+VI5wvvg5ouwu9GTyUUI/Od13efl0qYWX0afanJyIAYpGIJ/068UaXfrSzaFVcvR7kKhW/JcazIfISWdL66WrG+vp0dXall5sbvVzdCXF2weQO3Ff3C8U11VzKzeVS3eoiqaS4ge5N/3YevNSjF/3cPeoRlVwu5+LFi6hUKtRqNb6+vo3GGs7mZfJZ5EmulGhdXmKFime9Anh76NgmUyyPHj3KmTNniIuL4x//+AehoaGNjqtUyvg64RQ/pV5EJWgQC/CUezCLu4/CyrB1q667IWC1oOF0URLbrp8hSnJTpM7eyILRriGMc+3WaJvIxwmPKsEPRkvot2OzIAjPiUSiTYCXIAiDb9tnJRAI5AAfP4xCp4eFtMpCwvKi+CPvtgbI5o6Mc+3GOLeu90UbpjGUyatZHX+CX9Oj0CBgKNbjnx36Mrdjf8zuspw9r0rK2piz7EiJ01W2jvPyZ2HXAa0KCt6OSrmcrXEx/HglkpIarTVpbmBIjzoy7+XmTidHJwybaeLwqKBCJuNyfi6XcnO4mJtLbFEBmrrPqLOTMy/36MUIH787frgKgsCx7DQ+jzqlC1rbGZvySuc+zPAPuWvhs+uVpXwaG05EfgqgrYqdHzSYp727PTAF0GRpHvtzLnMkP5YK5c3Eio6Wboxz68ooly5YGz5c4bC/gkeS4B8GHkeCL1fUcCQ/hoO5USRJc3XbrQxMGenSmXFu3QiwfHA6NkqNmq1pl1mbcELXI3S0WwCLOw+7awGwaqWCNVfOsjExEoVaW704xN2HN7sNpJPdnVtZJTU1bLoSxc+xV3SyBK4WFszp1oOpgcGYPgIW+t0iR1rB91GX+TX+qq7QydfGlnndezLRP+COH1oaQeBARiIro05zvU5ozcXUgiU9BjHJJ/Cu59npgjSWxx4hWAMwBAAAIABJREFUVVoCQAdLB97tMop+Ts1nMd1LKDUqThcnE5YbxZniZF1QVl+kxwBHf8a5dqO/g/8jJ4vQFNoIvg6PC8GrNGrOFCcTlhfF6aJkXam2nkhMfwd/xrl1Y4CDf6Ndce4XBEHgREEq/40NJ6NSq4QZYO3Ee11G0cuh8V6pd4LjOem8d+4IuVXa+dnLyZ0l3QfRw6m+D7+6urrFTJYcaQUboi6z4xbSswUWDRrC1OAuGNwh6ZWXlyMSiVrV2u9hoaSmhs0xUfwUc/Nh5mJuwYvdevB00J0/zJQaNbtSr7I6+iz5NdrV4iA3b/7Td+Rfco/dCpVGw7b0SNYmnNBJQA9z7cDbnUfct0KppiBRVHEkL5aDeVEkS2/2YbA2MGWUaxfGuXbD3/LR0n+/HW0EX4dHmeClylrOFV/jVFEiZ0uu6TIAAPwtXRnn2pVRrl2wMXzwMsJXJfl8FntU52e3NTJlYdAQnvIO+ctKeXl5ebi6ulIqq+HjCxHsTU8AtOJZH/cZwRD3m/07CwsL2blzJ7/99hvLly9vsgducmkJ6y5f5OC1JJ1rR8gvoOTQYXZ8+hljWujycyvKy8uZPHkyMTExDB8+nM2bNzebNfGooFIuZ9vVWH6IjqS4Rlvab2NszOwu3ZjVJQRr4zu7B5lKxffxl1gTcxaFWo2JvgFvdhvA8wHdG60IvhOUK2pZm3CCX9IuoxYEDERipvt259WAga1Kp73XSK0sICw3ikN5V3Q9DwDcTW0Z4BDAIMeOhNh4PXKWfRvB1+FRI/icmlJOFSVxsiiRK5LruqUigK2hOWNcQxjn1u2BBUxvR3a1hFVXj3Mg+yqg1Qif5deLVwIGYGGgTV0rLi6msrISHx+fFo+XkZHBtm3b+PXXX/nkk09QBfrw7wsRSOS1iEUing/szqKuAzAzqO/Df/XVV/nmm2/44osvWLRoUYPjRubn8u2li0RcT9dt62prR2HYH5iXV/D0tGk8//zzrbpnpVLJzp07Wb9+PZWVlQwbNoxPP/202V6ajyLkKhW7kxJYH3mJzAqtq8XUwIBnOnXmn12742x+Z7GatIpS3jlzmIuF2kytLvbO/Lf/aAJt775iNVVazH9jwjlVmAZoxczm+vfnufa9MdF/8G40lUbNhdJUwnKjOHlLEgNoK2b7OnRgkGMAfe07YGHw8B/6bQRfh4dN8GpBQ3x5NieLEjlVnERGVX0FSm9zRwY5BDDQsSNB1u0eWOMAjUbDtWvXuHjxIgEBAfh1DuLbpNNsTbuMUqN1D030CGZB0GDczawRBIG1a9fyyy+/IJPJOHnyZKs+z9WrV7NgwQJe/+A9ygd04WSuNquho40Dn/YfTYhD/RLz6upqFixYgFgspnv37syZM6feUjmvUsrHJ49zOE0buBMBI7x9EUdGk3HhIqtXr8bT07NVWjJSqZQNGzbw22+/ERoayrx58xAEocmc8McFKo2GQ6nXWHf5Iokl2uCpkZ4+r/TsxdxuPTG6g+CpRhD49Vosyy8fp1IhR18kZm5wL97o0hfje0DEZwrT+V/cMRLKtfosjsYWzA96gsmeXR5KK0aAGpWcCyUpnCpO4nRREuW3BGf1RGK62ngxyDGAgY4BuJk+nLnSRvB1aI7g77Sqs7KyEo1Gg6Vl8/KyUmUtkaVpnCxK4mRBPJWamxrkeiIxIboJ0hF30/oZInl5eRw5coSqqiqUSiXPP/98sxV0AHv27OGnn34iKyuL119/ndmzZzd7fYIgMG7cOA4dOsRzc16kz8LZrL92VhdA7efozeLgYQTZ3CTf2NhYXnnlFTIyMjh79iyens374GtqanjzzTdRKJVYjhjAH/IyatUqDPX0mB/Sn7mdemJw27I3KiqK1157jcWLFzN58uR67ynUan6MjmTtxXPUqlToiURMCQiik0zBt8v/y8svv8zMmTNb5TfNzs5m9erVXLhwgTlz5jB9+vQWy9cfRwiCwInM63x96TyR+Vpfs6eVNcsGD2OQp9cdHauopooPzx/lUOY1ALwtbVjebxR9XZrWYmotNIJAWPZVVl49Tk6NduXhZ2nP4k7DGOzS/qH6wusZaEWJZFQX13vfx9yRgQ4BDHDsSKCV2wOLkbURfB0aI/jKykquXr3Kq6++SmFhIR06dOCTTz5pUndDEARSUlLYtWsX7777LgYGBsyfP5+PPvoIMzMzKhQ1REkyiCpLJ6rsOqmVBQi3ZC6rqmQYZ1fz0uApTOo0CMsmlnhxcXFs2bKFzz//HD8/P7Zv307Xrl2bvLfq6mo2b97Mli1bSEpKYuPGjU3mJt/A5cuXef/99/H18yPLEir6+1FQqw2odbRyYknwMAY435RikMvl/Oc//yEyMpKvv/4auVyOv79/s+eIiYnhlVdeYeYbr/KHqYqYEq111tu5Hf/tNwofq/pWj0ajYeXKlYSHh/P99983KCOPLSzgrSOHSJVoi1d6uLoxPziEbz/+BGNjY1asWIGdXfOplClFJRRkpPPd2jWUl5czf/58hg8f/kgH0ppCdE4eXdxcWp0WKQgCB64lsfz0CYqqtT76MX4dWD50BFYtVIzejsOZKSw9H05hjdZf/UyHLnzUZ1iLna1aA4Vaxda0y3ybdFoXiO1l78mHXUffNyGz5tBYLn52dSmnihM5VZTUwMVqJDYgyNKNHva+dLf1oZN1uyYJ/8axq6urEQShSUmDxvZJT0/HxsbmxkqzjeC5jeC//PJLioqKyMzMxMHBgbfffhtX16YFkiorK5k/fz59+/blwIEDvPX+2+Blw+XSdKLK0km5jdABbDRG2JZo8FVZ4atvx3P/mNWiVf32228zcuRIiouLGT9+fItf+rlz54iJiWHWrFlkZWXdKF1uFmvXrqXvyKFsLI3X6Ye4mFiysNMQJnoENyCN7Oxsjh49ynPPPddqMty+fTuWQR14P+4MZfJaLAyNeK/nYKa1b7wQqqamhk2bNjFv3jz0bst22ZuUwDvHwpGrVdiZmPD2gCeY0jGQo0ePYmxszMCBA1t1TWqNhvf/u4JnJ44nODi45R0eYXz2zTpSoiNZtGgRAQEBLY5Xq9VMmzaN1xYt4oxayeaYKNSCgKeVNRsmTMLPtuHDMSwsjPPnz7N06dIGq1ypQs6KyyfYmnwF0GY/fTd0Mu8sWMisWbOaDIjfjtTUVD744ANWrVqFk9PNlFipQsZ3yWfYnHIBhUaNgViP1wMH8WKHfnz26acIgsDChQuprKyst19jkMvljBs3junTp2NmZsb48eMbVSi9Fbt37+bHH3+kV69eVFdX88knnzS60pcqazlbnMypokQirl9BbVx/7hqJDehi40k3W2962PoQaOWuC9bGxMTw6quv4urqyunTp/n444957rnnGsz/W7Fs2TIiIiIoLy+ntraWlJQUaCP4pl00rRX1qlLKuCK5TmRZOpFl6VyT5ut0X27A28yB7nY+dLP1oauN1wMrProTCILAgeyrfHLlMOWKWvRFYl7qOIB5HfvfEwvsBn6/nszCk2HI1SqC7ZzYMGwKzs2oPDYGtUbDZ2dPsSFKq8w41MuHL0aOuWOL81ZoBOG+NJJ+0NAIApcuXuTLL79EJpOxcOFCnnjiiWYfwDk5OSxatAhHR0dmLlrIWyeOkVdZibmBIStHj2WYd30BPUEQ+O6779i5cycbNmxoNKB+Kvc6r/y5l0qlAh9LGz7vNoRlC96kR48evPvuu4jF4haNglOnTvH222/z0ksvYWRkxLRp03Tv5dVU8FHUIY4XaOMtnWxc+E/XcVw6GM63335LcXEx+/fvb1bVFbQG2oYNG1i8eDGdOnXi4MGDtGvXrtl9CgsLefbZZ4mIiKBXr15s27at2aQCQRA4HHWa+Ss/wqCDI+bB7dCY1P9NmegZ0sXGk+62PnS39cHfwoWtP2/h+++/x87OjqFDh/Lqq682y0mCILBkyRLy8/PZunUrtBH8nQVZpcpakqS5JEvzSKrIJUmaR3ZNaYNxHqb2dLf11pH6o9ANvjnEleXxxdUIzhZpg5yB1s78t8cEAqzvXbaOIAh8G3eBzyJPAjC8nS9rnpiAqcGd+belchnz/wjjROZ1AF7p0ZuFffrddYre3xEZGRmsWrWKmJgY5s6dy9SpU5uNKx04cIDly5czd/58DuvDxbxcRMCivgN4pUevBoSckJDAvHnzeOmll5gxY0YDoyhZUswL4bvIrZZia2TCt0MncWHHHg4ePEhgYCCfffZZi/GN2tpaRo8ezZkzZwgLC2PUqFG69wRBYF9WHP+5cpgKpQwDkZhnfXtgHZPD1yu+oLy8nIiIiBZXr3l5eURERHD9+nUUCgVLlixpcYVcVlZGVVUVVVVViMXiVq2Qb73u9KoinVEYVZZRr5IWwFCsj6+5Ex2t3Oho6UpHSzd8LZwwbIUfv80HX4fmCF6iqCKpIk9L5lItmd+qoX4r3Exs6W7nQw9bH7rZeuNo/OgWwNyKNGkJq+OP80euVqnQQCTm1cBBzPHv1yDIeTeoVSn515k/2J+uPc/sgG4s7TX0jkk5rayUOQf3cb1cgrG+Pp8NH8X4Dq3/Yf1/hUQi4bvvvmPfvn1MnTqVp59+GolEQqdODQXEqqqqWLZsGckpKfi8MJv9damm49v78+nwUQ0KpGQyGW+//Tb5+fm4ubnx5Zdf1nu/qKaKfx7dRVxpIfoiMcv6DCfh550sX76c119/nTVr1jR77YIgcPnyZcLCwjh9+jRr1qxpYJUX1VbyYfTvHMvTBnnN9A15rn1vnnIOpKKwpEUr/mFDI2hIqyoksjSdy2XpRJdlUKmSNRinL9LD18IJf0tXHen7WTg36FzVRvB1aNQHn3iQPwvjKZQ17JUIWjLXfcBWbvhbujyUYqO7QX5NBWsTTrL7egwaBETABI9g3gh8Ag/ze9s3NrdKyryIPVyt+4F/1GcYMzs2HRxuChEZ6Sw4HEaVQoGrhQXrx08i0OHBB9ceZygUCrZv387q1atJTU1l9+7dDBs2rNGxV65cYeHChaSZGmM0egRqQSDQ3oHvJkxqoFYpk8kYPnw4Z86cYefOnTz11FP13q9VKVl8+hAHM5IAmNGhC4Pl+vz26w6mTp3K8OHD7+geGrP6b3SU+uJqBFclWpE0a0MTXuo4gBm+Pe6pm/F+QyNoyKuVkFih9RYkS/NIlOYiVTZocYGeSIy3mSMv+A1huLM2ftRG8HVojOCXxuzgj3xtgMjD1P4WInfF39K1ySyXxwFl8hq+SzrD1rRLKOry2Ye6tGdhpyH4W917Bb1LhTm8HLGXElmNbone27l5/+btEASBdZEX+fzsaQSgp6sbX4+diJ2JCWVlZS1myLShPgRBYOPGjZw/f57s7GyWLFnCkCFDGh37888/s3btWgr0xNjPnkm5Qo6diQlfj51IL7f62UxqtZoTJ07w+++/869//atBy0FBEPgm7gKfR55EQBt8/XboJGyNTO5ptpIgCBzOTWRl/HGdhIaziSWvBw56qPnzdwtBECiQletcwzc8C2V1jcf/G/IMw9oIvj4aI/ikilxq1Ao6WLpg3kRT28cNVUo5G1PO8+O187rGGz3tPXiz01C62d8Z4bYW25JjWHo+HKVGQ4CtIxuGTW62S1BjkKtU/OvYYfYnay2/Z4O7sHTQEDJSU5k/fz7r1q3Dy8vrPlx9G25FaWkpMelprExNJr64CH2xmI8HD+PpTs23nmsMx7JTmX/iIFVKBW5mlqwfNpmgvyAc1xJUGg17MmNYm3CSglotx3lb2LEgaDCj3AL+FgF1QRAolktJluYRbO2hU75sI/g6POxK1vsNuVrFtvRIvk08jUShDeIEWjuzqNMQBjr53pc8b6VGzb8vRPBzUjSglfX934AxTQZTq6qqSE1NJSQkpN52qVzGvIP7uJCbg75YzIdPDGVGcBfCwsKYMmUKU6dOZcuWLff8+tvQNGqVSv519DAHU7QptG/06sv83n3veB6llpcy59huMqQSjPX0+XzgWMZ7359Yilyt4pfb8uc72biwKGgI/Z18Hstah5bQRvB1+LsSvEKtYn/WVdYmnCC/znrxMrdlQdBgRrsH3jfrpVRWw0vH9nCpSJt98Va3gbzSuU+jPyKNRsPWrVv54IMP+PPPP+s1nsivrOT5/bu5VlqCuaEh68aF0q+dtipyyZIlnD17lm+++abF5sVtuPcQBIGV58/y1aXzAEwL7MQnQ0fcseujQi7j9RMHdPIUr3Xuy6JuA+7b3KxSyvnx2nk2ptxcxfZ28GR+0GC627X7WxF9G8HX4e9G8FlVZfyaHsVv12N0FruTiQWvBQxiileXO86MkcvlrdJtEQSBn48c4uPUyyhNjTE3MGTVoPEM9/Brcp9jx44xfPhwXnzxRTZs2KDbnlxawgv7dpFfVYWjmRkbJ04hoC6YumbNGlJSUli5cmWrahTacP/wS1wMS48fQyMIDPby5qsxE+5Yglit0bAi8gTrr14CtKmzKweNx8Kw5Tn3V1Emr2Zd4hm2pt/UVfK3cuQZn+5M9AhusmPV44Q2gq/D34HglRo1EfnX2J4WxZmim+qJTiYWPOfXmxl+PagulxIfH09wcDA2Ns1nyWRlZXHgwAEOHDjAzJkzmTlzZovXMHfVCv4wViI2MsTLwpoNw6fQ3rrpnqW1tbVMnjyZIUOGMG3aNJ31fiEnm3lh+5DK5fjZ2LIx9Enc6r6XXbt2sX37drZv395sRV8bHhyOpqfyxh9hyFQqOjs58/2Eydibmt7xcfakxfOvM3+gUKvxs7Jjw7DJeFvdX6GuvJoKvk48xf7MOOR16pCmegZM8AjmGZ9uBNq4tHCERxdtBF+HpgheoVCwb98+PDw88Pb2xsHBocUl3PHjx8nMzMTR0REnJye6du3a7D4SiURniRoZGTFr1ixcXJqfVD/88AN79+6lsrKSf7z2ElUBzuzMiKZIptX+EAEDnf14xqcbTzi3p7iwkAEDBpCens6KFStYsmRJi5/JJ598wgcffMDChQsb5DTfDo0g8PYfu9lRoJV17e/cjm+GTsbKqOngtEKhYOrUqcyePZspU6botv+eksyiw4dQaNR0d3Flw4RJOq3yU6dO8e9//5v9+/c/Fhrs/58QlZ/HnAN7kMhkeFpZsyn0STxbEMBrDDHF+cyN2ENhTRWWhkZ8NXgig9zuf1enCkUtezNj2ZYeSXrlzaLFYBtXnvHpzth2gZjeZcvJB402gq9DYwS/c+dOwsLC2LNnD1VVVTz77LN89NFH+Pr6NnqMqqoqli5dysmTJ4mMjGTEiBGsWLGiWSGw8PBwVq9ezblz53Bzc2P9+vX06dOnyfGCIPDqq69y6fJlcsVyBr4zlxilRCeJYGdkxlNeIUzz6aprk3fx4kXmz5+PiYkJzzzzDHPmzGnx83j55ZcxMDCgR48ezJgxo1lLWSMIvBS2nSPF2QD8M6gH7/QY3KIvdsGCBXTt2pXZs2frth1NT2XewX0IwCjf9qwcNUYnN1taWkpoaCj79+9/7KV6/y6QSCSYmJhgXCcNkS4p47l9u8iRSnEwNePgs//AwbR+g478/PwWDZiimirmRewlujgPsUjEJ10G8WzX3q2+rrS0NLy8vFq9whMEgejoaAIDA1GpVCTWlrItPZLDOYko64TCrAyMmebTjX/49sTZ1JKMDG3MoLi4mO7du7fqXH/++Sf+/v5EREQwY8aMFo3FoqIioqKikMvl6OvrM3bs2FbJOgiCQEREBH369GHMmDHQRvANCT4hIQETExMiIiJ44okn8PNr2o8M2vzfqKgorKysuH79OiNHjmzxvNnZ2ZiZmREbG0v//v1blCVWatT8HHWCA5JU4isKddt7O3jyjE93hrt1xPA2/3pFRQX6+vqIRCJMW7lsLisraxWJylRK3jz1O2HXk9ETiVjRfwxPtW9YFdkYbm+xV1Zbw6gtmymtrWFaYCf+M3REgwrXmpqaVt9DG+4/Dh48yJYtW/jll190jU+Kq6uZsXsHqZIyRvr68e3YiTpSEgSBqVOnMmPGjAZSz7dDrlbxzpnD7E6LB42G17w7M3/giFZJd2/atImffvqJoUOH8s9//rPFB4pMJmP+/PkkJCSgVqsJCwvDxsaGUlk1uzNj2J4eRXa1tnpdTyRitHsg/TTW7Fy5jj///JPOnTuzdetWnJ2bl/T48ccf2bZtG0ePHmXo0KGsW7eO9u3bNzk+Pz9ft098fDxPPPEE//vf/+jZs2eT+0RERPDnn3/y1VdfUV5efmNzG8HziPrg5WoVZwvTCc9L5ljeNV3Q1FCsx0SPYGa373VfipNaQkltNXOO7SG6OA8DsZjPBoxhsm/QXz7eG4cOcjAlmQ62duybPvOOGk604eFh1apVZGZmsnLlSt22hOIiJv26FZVGw6pRY5nof1PNsra2lkmTJvHWW28xYsSIZo+t0mhYcvqQluQB2+Rswpcsw64VxsfBgweZMGECvXr14vjx4y269G4IdG3duhV7e3sOHz6sezCoBQ1/5qew6doFLpZk6vbxMrXB4Fo+ZpllOKr0+ejDD7G3bzrmBBAZGUl0dDSGhoZYWFgwYcKEFhMFjh8/jlgsxsHBAUdHxxaL+hQKBZcvX8be3v6GbHcbwfMIEXyVUs7xghTCc5M4WZCmS+cCrRvmWd/uPOvTAzvjB9+fEiClvITnw3eRU1WBlaEx64ZOuqumDr+nJPPaoYPoiUTsfnoGwY4P/oHVhr+Ot956C1dX13ptE9dcOMeqC2exMjLm8MzZOJrdlPGQSqWEhobStWtXli5d2myzGkEQWBtzji+jTwPgWFrFiQUftNiyT6lUcubMGfbv34+FhQUfffRRq1Mgy8rKqKysbLRhTYIkn40pF/g9O17nvgFwNbViuKs/I9060t3+wXVdaw5tPvg6PAoEXyqr5mheMuF5yZwrytClboE2E2aEa0dGuPnT097zoZZZn867zst/7qNSIcfTwpofRzyJr9Vflwkoqalh9JZNlMlqeb1XHxb2abyhShseXWg0GmbOnMnEiRMJDQ3FxMQEpVrNkzt+4WpxEcO8fVg/flI9gt2wYQNz587lo48+4sMPP2zxHPvSE3jr5O8oBQ2+JhZsm/gPHE1bp/10g6/uZY67VCGrZ4TVqpW692wMTRnm2oERrv70c/J5aPo3bQRfh4dF8DnV5YTnJhGel0xkSVY99XhvCztGuPoz0i2AYBuXR6IAY/u1GN4/G45K0NDD0Y31wyZja/zXfeKCIPDy7/s5kpZKR3sH9j49A8O21MfHEnK5nNDQUORyOUeOHMHAwIDk0hImbvsZpUbD5yNGMyXgpgtPEAR+/fVXli9fzsmTJ1tsOQlwuTCHOcf2IJHX4mpmwQ/DnyTgHjT3vlvI1EpOF6YTnpvEn/kpukpZ0CpaDnL2Y4SbP4Od2z/Q/Po2gq/DgyB4pUZNckURUaXZXCnNIbo0h9ya+kqVnWxcdJa6n6VDE0d68NAIAisun+C7qxcBCPUJYEX/MRjfpZ98f3IiCw7/jr5YzN6nZ7SpQj7GUKvVLFmyhC+//JItW7YwY8YMAL65dIHPz53GwtCIwzNn42xevydCTU0NJSUleHi0zsWXKZXw/NFdpFeUYW5gyNrBExni3nSTjQcNlUbDpZJMwnOTCc9LorCu1SWAGBEdrZ3oaueue7mbWt83462N4OtwPwheIq/hSmkOUaU5XCnLIbYsr94yDrRfeE8HD0a4dmS4mz+upo+efnytSsmik2G6RsrzQ/qxIKT/XU/KouoqRm3ZTIVcxvMBQXwwYvS9uNw2PGScPXuWdevWsXnzZkQiESqNhqd2biO2sIAnPL35ceLku547FXIZ8yL2cL4gG7FIxLLew/lHwJ1LT99vaASBOEmebpWeUdmwKZCDsTkhtm50s2tHVzt3gmxc7plLp43g63C3BC9Xq0irLCGuLI/o0hyiS7PJqCprMM7SwJgQO3e62bkTYutOZ1vXR7okuqimijnH9hBTko+BWMyK/mOY4vfXM2VuQBAE5h7cy7GMdFz09BlfLeedVhRfteHxgFKpNWRupDSmlJYyYfvPKNRqPh02kmlBd9/vVqFW8+7Zw/yWehWAFwK7817PIY90R6+CGinRZTl1HJFDgiS/XqAWwECsR5C1M13t2tHNzp0Aa2fczaz/kjbPI03wIpHoVWAx4AzEAK8LgnCxibHPARtv2ywXBKFVOr+tJfhalZL0yhJSpcWkSuv+rSwhu0rSoP8qgI+FHd3s2ulI3cfC/rGRJ721zZq1kTHfDZ3cKg33hIQEPD096+W4344d8XG8fewIekD2ii84uPmnO2r20IbHD+sjL/HpmZOYGxhy8Nl/4GF151Wut0MQBL6JPc//ok4BMLydH6ufGI/ZHbZ/fFiQqZVcleTrVvrRpTmUyqsbjDPW08fHwh4/S3v8LB20Lwt72pnbNJut88gSvEgkehr4CXgJuAAsAKYC/oIgFDUy/jlgNeB/y2ZBEITC28c2cb6GevDlhSSUF5AmLSZFWkJqZTG51eWN0LgWlgbGBFg76Qg9xNYNG6PHsyjnZG4Gr/65j0qlAjcTc7aMmd6iJoharebLL78kPDycI0eONDnuYm4Os/b8hkKjJlAiJeq7DcTHx7eojdOGxxtqjYbpu34lMj8PXxtbfpv6zF01R78VB9ITefP07yjUaoJsHflh+JN33MD9UYAgCGRXS3QWfnRZDqnSknoZdbfCUKyHt4WdjvD9rBwIsXXHyUR7748ywV8ALgmC8Frd/8VANrBWEIRPGxn/HLBKEIS/ZBY0RvD/PPULpwrTGoy1MTSlvaUDfpb2+Nb962fpgL2R2QPPdBEEAUEQdFWETUGtVpOamsqVK1fw9vamV69ejY5TatT8GH+ZzyJPohYE9HIKece3Ky8+27LQ2NKlS/n444/Ztm0b06dPb3RMRrmEJ3f8QrlMxkhvX64sX8FEC/ttAAAgAElEQVTBAwdaLA5pw98DhVVVTNmxlfyqKvq6e7AxdMo9y5qKLMpl7rE9lMpqcDY154uB4+jv2jCP/XGDSqMhp1pC6i2egzRpMWmVJcjUqgbjl3UdyzO+3YFHlOBFIpEhUAM8JQjC3lu2bwasBUEIbWSf54DvgVxADEQB7wqCEN/KczYg+G8ST3EiO5nc6AQ8Tazo5NiOqU+MxM/FvcnjaDQa3nzzTYqKinB3d2fChAkMGDCg2XPf6I1pZmbGqFGjWLhwYbOVbZmZmQwfPpySkhIGDx7M+vXrG7RFu/2aRo8eTXh4OLNmzeKHH35o9PjnC7L44Fw4KeXaQFD12SiG1+qzrRXNNORyOePGjaNz5858+umnjfbKLJfVMmXHNq6XS+ji5Ey/vEIsTc2YN29ei8dvw98HicVFTPttO9VKJVMDO/HpsJH3zDDKqiznhfBdpFZo5/BYL3+W9hraamteEIQ7uhalUolYLL4jVdMbHHMn5ykoKGggg6ARBHKqy0mrcxPfIP/3uoykq507169fx87O7i8R/P2OYtgDesDt7pVCtP74xpAMvACEAjPRXuNZkUjUKBuLRCIjkUhkeeMFNJgBJTv/RPL5DmL/8z2lv5+jt6MXvs5uTV60VCplyJAhnDx5ktjYWPr06UP//s0X7WzatIldu3Zx/fp1Zs2axVtvvdUsuQuCwOLFi3FycuKjjz5i9+7dzZI7aLUpgoKCmDt3Lhs3bmxw/CqlnPfPHWH6oe2klJdia2RC78Iavho+mW+/+qrZY9/AtWvXmDdvHl9++WWj5A6w6vxZrpdLcLOwZP2ESRjp6fPCCy+06vht+HtAo9Fweu8+1owZj1gkYmfCVc5kZzW7T0JCAmFhYa06voeFNbvHz6SbygAR8Pv1ZEbs/ZEd12JpziiVyWTMmzePuXPnUlJS0qpz7d27lzfeeIMRI0aQl5fXqn3mzJnD9OnTGTduHCkpKS2Oj42NZcyYMQwbNozQ0FBiYmJ074lFIjzMbRji2oE5/v1Y0TOUXcP+ycH/Y++8w6Mq2j58n/ReIT2BkAAJBAi9ijTpCEhVEPgoIk0QXl+xYkPEBr6CFaWoiFKkd6SFHnoKkEoaKaSXTbJlvj8SIiHJ7gYIRHLu69pLOeeZM3M2e54z88zM7/lmNcOHD6dt27ZMmDBBr3ZV4E5ooCY+gBsggM73HP8UOKPnNYyBSODDKs6/V1pHuU92dra4G41GI0JCQkR1iI2NFWq1Wm/7wsJCkZWVVa068vLyqmWvVquFRqOpcPxYQozo8ue3osHPS0WDn5eKN07sFVmFimpdWx9S8/OE34rlwvurz0VQXOxDv77Mv4fx48eLM2fOiPeOHBLeX30unt/0h1b7/Px80b17d7Fo0SJRXFysVx1XrlwRtk0aCd9PFpb9tl/c96dIyM2usszy5csFIJo3by5u3bqlsw61Wi1mzZolzMzMRL169cTOnTt1liksLBRjxowRrVq1Ej179hSHDx/WWSYjI0MMGjRIDB8+XLzyyisiPDxcZ5nr16+LIUOGiFmzZt3xbTaiGj641oVoqrjORkAlhHi+knOmwN3rE62BhNqiRVPT5BQX8fG5w2y4cQUATytblnbtT5caill+cuIYP5w/R2sXVzaNer5W7MqVeTzExMQwY8YMvl//G33Xr0Op0bBx1FjaulY9On7rrbf4+OOP2bx5c7ncAVWRk5PD+vXr+fGnn/Ad/xwX7U0oVquxMjbhjXY9eKFpqwq/QY1GQ1RUFJcuXUKhUPDiiy/q/TtVqVQUFBRgbW1dq37btTIGD2WTrGeFEHNK/20AxAErRCWTrJWUNwRCgd1CiPl62D92LZpHxeH4KN44uY/kgpLkIJP82/Ba2+41trQsu7CQbqt/IF+p5Mchw+jtXbmevkzdoKioiLFjx5Kenk6Xd9/iz7AQejZsxE/PVi0fXFxczIcffkhwcDB79uypVn0hISGYujmz8NR+zqcmAtDF1YulXfvjaf3gSzVrM/fr4B/FToIvgWmSJE2UJMkf+BawpHStuyRJ6yRJWnLHWJKkdyVJ6itJUiNJktoAvwINKJl4laFk99/8Y7v4v4ObSS7Io6G1HX8OeJ73OvWp0XXDay9fJF+pxK9efXo1rD1bymUeDyYmJnh5eXH8+HFGNPDGQJI4HBtNWFqF1c/lynz44Yd8+OGH5OdXXCeujYCAABo71OfPAc/zTodemBkacfJWHP22rmZt2AU0T9imzYdBjUujCSH+kCSpPvABJROrl4D+4p917V7A3VvA7IEfS20zgfNAFyFEWE239d/AgbgI3jy5nzRFPhIlWZcWtHlKp+Tqg5JfXMyayxcAmNmuQ60avso8HiRJYvny5QCkXL/BwMZN2HnjOt+cO8OKgUO0lm3Xrt1912toYMCU5u3o7enDf4P2cDYlgUVnDrIr9hqfdhtAQxt5H8YdZKmCfwlhGal8d/UM26PDAWhk68BnXQfQ1rnqeOfD5McL51gSdAxvO3v2j59Uq7eRyzxahBBER0ejtLVh4Pp1SMD+8ZPwcbh/6Wl90QjBr9cu8knwUQpUSswMjZjTqjNjmrSknvnjybFQE9TaGPyj5kly8LcV+WyLDmdTZAjhGSXDXgNJYlrz9rzaumtZjtOapkilovuaVaQV5LO0Tz9GNSufyk+lUlFYWIiVlX6a3jJPLi/t2MrBmChG+Dfns0coOhefm8XrJ/Zy8lbJUk0jyYAeHt6M9G1BL0+ff710tezgS/m3O/hitZrDCdFsirzK4fhoVKUCRiYGhvTx8mV6QAda1deel/Jh88uVSyw6cgg3a2sOT5iC8T0Py+bNm3F0dKRHjx6PtF0ytY/LybcY/ud6DCWJwxOn4GHz6FRVhRBsiQrll2sXuZR2q+y4vak5Qxv5M8I3gABH539leFF28KX8Gx28EILQjFQ2RYSwPTqMjKJ/kgy0qufCSN8WDGnkh52p9lyUD5OsrCzs7OxQqtX0Wvczibk5vPd0Lya0qijl2r17d0aNGsWcOXMeWftkai8v/rWRE/FxjGvRig97Ph7hucisdDZFhvBXVCgppavMAJra1WNk4wCGNmqmdxap2oDs4Ev5Nzn424p8tkaHsSkihGuZaWXHncwtGe7TnBG+ATSxfzjaLgUFBZibm+vVezl69Cjnzp3jP//5D5vCQvjvwX3Us7Dg2KSpFcJCFy9epHPnzkycOJHvv//+obRV5t/Nqfg4xv21ERNDQ45Nmlouh+ujRq3REHTrJpsirrIvLoJidYnYl6Ek0cOjESN8A+jt6fPYUvHpy/06+Np9Vw+RgoICzMzMdIp53eF+4sq3b9/G3t6+Sj0LhUrJxdQkzqYkcDYlnrPJ8ahKX7DGBgb09WrMyMYteMqtYaW5WjUaDfHx8URERJCZmcmIESN03k90dDRff/01jo6OvP322zrv4eTJkwwaNIj169ej1mj47nyJqvOU1m0rjfkHBATQtWtXva4tUzfo5OFJGxdXLiTf4qeL53mj29OPrS2GBgY87e7N0+7eZBcVsjPmGpsjQ7iQlsSh+CgOxUdha2JGN7cGdHD2pL2LB03t6j0xiwjqhIMPDg5mx44dLFu2jMDAQPr27cv8+fOxsKhcAriwsJAtW7YwZ84cLC0t6dixI0uWLMHX17fKOkJDQ5k6dSrnzp2jadOmLF++nNbduhCcmsj5lETOpSYQcjulLKZ+h6KoOKQr1/ngxam82ONZrT3stWvXMnnyZLy9vdm7d69O556WlkanTp2wtbUtp31RFUIINm3ahFqtJjAwkP3RkURnZmJrasa4FoGVljE2NkatVuPpqVtfXqZuIEkSM9t3YuqOv/jt6mVmtutYJidcXFxcpcZRZSgUCi5dukTnzp31LrNhwwY6duyIt7d3ueO2pmaM8wtknF8gUdnpbI4MZUtkCMkFeeyKvc6u2OsAWJuY0ra+O+2d3Wnn7EGrei4VOjcRERFs27aNZ599liZNmuhsU25uLnFxcfzyyy9MmjQJPz8/nWUiIiJIT0/njz/+4MUXX9T7/u+mToRodu/eTWRkJL///jszZ85k1KhRmGnRry4qKmL16tXs3r2bHj16MG3aNKyttavYnT17lh+3bCTb1gLXDq25mpVGdE5mBTsXCys6uHjS3tmDlFPBhBw9wRdffIGjo+4lZfHx8bz99tt8+umnODs767QH2LdvH6ampnpPgCoUChISEvD19eXV/bvZfv0aL7Vtz8Ku3assk5qaipOTnIO1LpGbm8srr7zC6tX35uYpQQhB31/XEJWZwdf9BzOwcRNWrlzJsmXLOHv2rF6/98LCQgYPHkxUVBRXr17VazSdn5+Pi4sLlpaW7Nmzh9attaf/U2s0rNq/i3d+/AYjH08s/XxR3tPHMjYwIMDRpcThO3nQ1tmd775YxuLFiykuLub//u//eO+993B3r3rJckxMDJ988gmrVq1Co9EwefJkli5dqlVe+/fff2f79u1s2LABc3NzFAoFyDH4ymPwarW6WlKgUBISubeXXKxWE5ebRUxOBjHZmcTkZBCdk0lE5u1yk6N3aGpXj7bO7rR39qCdkzseVrZlvXRRTUnT+y1zPwgh6Pzz96Tm5/Pb8FF09tQvkbJM3UAIQc+ePTlw4EBZOr97+eDo36y5fJEXWrTio559+PHHH3nppZc4ePAgvXv31quOgwcPsnz5cpo0acKyZct0lsnJySEoKIgbN26QnJzMggULdKq05uXlceTIEQwMDBCShFlDd+KEkuCUBM6lJJCqqLjj1sPSBjuVwMfWAX9nNwJcPfGxc8TFwqrK5zMjI4Pw8HCaNm2qd96EhIQEsrOzcXV1vfNSlB08DzjJqhGClII8orMziM7JICY7g5icTGKyM4jPy0ZdxXdmYmhIYD1X2jl70N7JnTZO7tiaPpxMN4+a6MwM+vyyGhNDQy5Pn42pFuljmbrHli1beOmll3jnnXeYO3dupTb7oyJ4edd2fOwdOPDi/wGwYsUKioqKWLBgQbXqi4yMpEGDBlW+TGoKIQQJedmcS0nkXEoC51MTuZFVtQyxuZExDW3saWRjTyNbB7xtHPC2taeRjcMD+QJ5klUPhBBkFRWSUpBHiiKP1ILSjyK/5FhBHqmKPNIK8imuIrUWgIWRMd53/oC2DmV/zKb29Wv9bLy+nE6IB6CNi5vs3GUq0LNnTxQKBT4+VQvOdXT3RAKiMjNIzc/DydKK2bNnc+PGjWrXp23+qyaRJAlPazs8re3KktRnFSkIz0gjOjujZCRf2vmLy81GoVISnpFatjHxbqyMTXCysMLJ3BJnCyucLaxwMrcqOWZhibN5yTGLh6gnVSee3PdOH+RAfKROx303RpIBXta2eNs64G1jj7eNA41sHWhka4+TedXDsCeFOw6+o0fVWa9k6i729vZMmDCBFi1aVGlja2ZGs/pOhKalcjohnmeb+gPoNSlZm7EzNaezqxedXcuHLZUaNfG52WXh26i7XgApBXnkKYvJy84gOjtD6/XvvAj+26Y7/Rs+2HdVJxx8dnEhiXn/jGrsTc1xtrCi/j1vUmcLK+pb/HPM2ODfvb35fhFCcDqxxMF39pBj7zKV89prr+Hlpf330cnDs4KDf1IxNjAs7QQ6wD2LyvKVxSQX5JJakE/qXdGCO5GDtNIoQoFKWfYiEDx4+LxOOPiXW3RkvF/rMqf+pIRRaorIjAxuFxRgamhEK+eqMivK1HUaNdItGd3Jw5OfLp7nVOmIsK5iaWyCj60jPrZVrx4SQpCnLC5z/E3ttU8O60Od8HQP44uqS5xKKBFsausmx99lHoz2bh4YSBI3s7O4lZuLq47lxnUZSZKwNjHF2sRU64ugOjwZ27VkHip34u+dPeTNSzIPho2pKQFOJXs2TtfxXvzjQHbwMuXQCMHZxASgZHgtI/OgdHIvmai/M68j8+iQHbxMOW6k3yajUIG5kREtneT4u8yD06l0ol7uwT966oyDDw8P5/jx4xQWFupln5uby5YtW0hKStLLXqPRsHHjRq5cuYJSqdRpL4Rg+/btnDp1iuTkZPTZcJaUlMS5c+c4f/48iYmJerUrKSmJ9evX633fR65cxgBo5+ZeQfddRuZ+aOfmjqEkkVVYSIaioMbr0/e3fof8/Hxu3bql2/AuFApFtcvk5OSQmlp1vtrKSElJISsrq1plyiGEeKI+gA0gsrOzxR0+/fRT0aFDBwEIKysr8fnnn4vi4mJRFTk5OaJv377CxMREAGLixIkiOTm5SnshhFi/fr1wcHAQgOjTp48IDQ3Vai+EEP7+/gIQAwYMEMHBwTrtd+/eLQwMDMSsWbNETk6OTnu1Wi169Ogh/vzzT522d/jll1/En3/9JeKzs/QuIyOji8j02+LajRsiMjKyWuV+//13cfbsWb3tVSqV6Nixo1i5cqVQq9V6lTlw4IAwNzcXc+bMEfHx8XqVWbhwoTAzMxNjx44VJ06cEBqNRqt9WFiYeOGFF4SxsbEYPny42Llzp1AqlVrLLF++XAwdOlSYmpqKoUOHCkAANqIa/rBOLJGYM2cOAwcO5MiRI4wZM0anDoSVlRVbt27l66+/ZsSIEVp3691h5MiRFBYW0rZtW1q2bKlXuxYtWoS3tzcdOnTQy75fv34cO3aMrl276mVvYGDAzp07sbTUPzfl+PHj9baVkdEXHwdHVm/bTkFBAbNmzdK7nBCClStX8umnn+olaFdcXEyXLl0ICQlh06ZNjBo1SuemRDMzMwYNGoSpqSl79+5l/PjxWsUIAYYNG0Z4eDhNmjQhJSWFvLw8rYKEPj4+TJkyheLiYjp16oSdnR1qtRojLavUJk2ahJubG3Z2drRv355t27Zpv/lKkLVoZGRkHgkajYawsDACAgJ0G8uU4361aOpMDF5GRubxYmBgIDv3R4zs4GVkZGSeUGQHLyMjI/OEIjt4GRkZmScU2cHLyMjIPKHIDl5GRkbmCeWROHhJkmZJkhQrSVKhJElnJEnSuvBbkqRRkiRdK7W/KknSwEfRThkZGZkniRp38JIkjQG+BN4H2gCXgX2SJFW6a0GSpC7A78BPQGtgK7BVkiR5fZWMjIxMNajxjU6SJJ0BzgkhZpf+2wCIB74WQnxSif0fgKUQYvBdx04Dl4QQL+tRX4WNTgV6aMPI/IPFI05sLFN3kJ9F/bn7OayVSbclSTIB2gJL7hwTQmgkSToIdK6iWGdKevx3sw8YVkUdpoDpXYcq7BcO+PZ/1Wi1TPQr1ct4LyOjD2q1Wn4Wq8GFCVMICgri2LFj9OrV676uUdNaNPUAQyDlnuMpgF8VZVyqsK9Ku/YNYNH9NlCmIrdv39ap1yMjU12OHDnyuJvwr+KXX35h+/btHDx4kEOHDt3XNZ4EsbEllO/xWwMJdxuEzHiFmzdvYmpqiouLfhrneXl5xMXdxN+/mU6xIigRRbpw4QKBgYEY6imzGxYWSpMmTbUKDt1NcXExxsbGerXnQZBDNDI1Qe/evQnp3r3G6ykuLsbExERv+9TUVDQajd6+AUpkfBUKBQ0bNtS7THJyMkVFRTRo0EAvewtjY4YMGYKpqSmWlpZ3QjTVokZj8KUhmgJgpBBi613H1wJ2QoihlZSJA74UQiy/69j7wDAhRCs96pTFxmRkZJ4oaqXYmBCiGDgP9L5zrHSStTdwqopip+62L+UZLfYyMjIyMpXwKEI0XwJrJUkKBs4C8wBLYDWAJEnrgEQhxBul9l8BRyVJWgDsAsYC7YCXHkFbZWRkZJ4YatzBCyH+kCSpPvABJROll4D+Qog7E6legOYu+5OSJL0AfAR8DERQEp4Jqem2ysjIyDxJyAk/ZGRkZGo5tTIGLyMjIyPz+JAdvIyMjMwTiuzgZWRkZJ5QZAcvIyPzyFCr1Y+7CXWKOuPg8/Pzqc6EslqtprCwsFp1FBQUVMs+Nze3WvZCiGrdg4xMbSIpKYmNGzdWq4wQgpCQENLS0vSyV6vVFBQUoFAoKCws1Ot5uXXrFnFxcdV6+aSmphIVFYVGo9FtXEpKSgpxcXHVeoYTEhK4ffu23vb3Uicc/ObNm3njjTdo3Lgx7733HhEREVrtFQoFS5cuxd/fnzlz5nDx4kWddQQFBdGrVy8mTZpEcHCwXu168cUXef755wkNDdXLPjY2ltGjR3Pr1i297AG2bdtGXl6e3va5ublkZWXpbS8joy9btmypdqdp2rRpfPrpp3prIxUXF1OvXj0aNWrE3r179ZL1OHnyJA0aNMDKyor3339frzauXLkSX19fbG1tee2113R21qKjo1m4cCENGjTA3d2dN998k/z8fK1l1q9fz7x586hfvz4dO3bU2abKeBK0aHTi5+dHSEgIDg4O5OXlER4eTsOGDTGuQnPF0NAQPz8/TExMuH37NseOHcPNzQ1nZ+cq63BxcaGoqIiEhAQ2bNiAhYUFzZo1q9JeCEFubi6pqaksWbKEefPm0a5dO633ER0dTUpKCjNnzmTJkiX4+VWl11aCRqPh4MGDbN26lVWrVumlkfPZDz8QY2xI3x49eLFloE57GRldCCF498ghmvfozpAm2n+z97JkyRJsbGz01l8yNzfnu+++Y8iQIdjb2+tVpn379kyZMoWFCxfi6+urV5nhw4eTmJjIyy+/rPO5Bahfvz4TJ07EwMCAyZMn06VLF5331KFDB0xMTHB2dmb48OE888wzerXtburMOnghRLVEuu7HvrR+vcs8KlQqFRqNRi8Bph3hYcw9sIfGDo7sGz+p5hsn88QTk5VJ73U/Y2xgwKXpszGXxeyqjbwOXgfVdbz3Y18bnTuAkZGR3up63Rp6IwERGemkFWgfQsrI6MPphHgAAl1cZef+iKkzDl5GP+zNzfGrVx+AM6UPpozMg3DHwXfy8HzMLal7yA5epgJ3HsRTsoOXeUCEEGUOvrOH12NuTd1DdvAyFehc6uBPyw5e5gGJzswgrSAfE0NDWru4Pu7m1DnqxCqaNacvcCEhCSdrS5ysLHGyssLJ2pL6VpY4WVthY2Zaa+Pnj4MO7h4YSBIxWZmk5OXhbGX1uJskUwvRZyHCnVFgW1c3TPXMXFZXUGs0pOcXkJqXT1puPql5eaX/zSc1N48ZT3WklfuDvRTrxDceHJfIgeuRVZ43NTIsc/Yl/7XE3daGhg72eDva425ng7GeafieBGxMzWhW34mQ1BROJcQzzM//cTdJphayceNGhgwZgrm5eZU2dTn+nl9czM30LGIzMolJzyQlN4/UUkeemptPen4BGi2rGPv7N5EdvD6M7xBIx4YepObmk5b3zxeclpdPlqKQIpWahKwcErIqX31kZGCAh50t3o52NHQscfoNHexp6GiPk5XlE9n77+zhSUhqCmcSZQcvUzlffvklPj4+tG3bttLzQgjOJFZ08NVdglybUapLfEdsemaZI48p/f/UXN2r0AwkiXpWFiUdy9LIglNpZ7O1p9sDt69OOPhODT3p1LDyHkSRSlXi9HNLhkVpefmk5OaRkJVT8odKz6RQpSI2o+SPRkRMufIWxsZlTr+lmwttvNzwd67/r+/xd3T35McLwXIcXqZSzpw5w5kzZ7h69WqVDv5GRjrpCgVmRka0dCpJaJ2RkcGOHTuYOHHio2zuQyG3sIhLibc4H5fEtZRUYtIzic/MRq2lF+5gYU5DR3saOtjhbmtTLkpQ38oSR0sLDA1qbiq0Tjh4bZgaGeFhZ4uHXeUZyzVCkJqbV+7NHJueRUx6BglZORQolYQlpxKWnMqu0OsAmBsb0dLdlbaebrT1dCfQwwUrU9NHeVsPTHs3dwwliZvZWSTl5uBmLSdPkfkHS0tLAgMD8fDwqNLmdEIc8E/8PTs7m379+jFnzpxq1RUeHs62bdtYsGBBlbvPK6OgoAC1Wo21tXW16rvDrexczscnciE+ifPxSVxPSaMyV25mZFTmxL0dHWjoaFc2yrc1N7uvuh8WdcbBZ2ZmEh4eTufOnfUeHgYFBdGxY0dcbKxxsbGms3f5ZV7FajXxmdnEpmcSkZbO8Ws3uJ6RTW5RMWdi4zkTW9L7NZAkmjrVo62XO2093Wjn5Y6TtRU5OTlkZGTQsGFDve/j2rVrOiUK7kahUABojZNWhrWpKQFOzlxOSeZ0QjzP+TevVnmZJxs/Pz8cHR3p06dPlTanyuLvXgghePPNNwkODiYwUD8JDIVCwdSpU1m/fj179uzRy7lnZmYyadIkLl68SPv27fn11191lsnJyWHc+PFkCwlTL298O3fjWkYWSdkV9WU87W1p4+GGr70NG1f9gKOxEY1cnZkxfhientrnGeLi4liwYAG+vr74+fkxaNAgnRo7wcHBfPvtt9V65u+mTjj4Dz/8kIMHD3Ls2DECAwOZOXMm48ePr9Lp5eTkMGTIEMLDwzE3N+fVV19l6tSpWN2zmsTE0BCfeg741HMg4XQQF79cSmpqKi8vfJOm3Z7manIa5+MTScjKITwljfCUNH49dwmABvZ2ZF0PJXL/bt6b9TKzZ8/GQMdQ7eDBg4wZM4aPP/6Y6dOn63XvM2bMwMXFhU8++UQv+7Nnz/LHH3+wePFiunh6cTklmR03rml18CtXrmTWrFl6XV/myUCpVDJ37twqz2cqFATF3QSgi4cnkiTx8ssv4+vri7+/fnM65ubm9OjRAxMTE/r3769XGQsLCxISEhg8eDBff/21Vv2lQqWKg9cj+fnQUW607AymJb3tuJiSF5OhJOHnUp+2niUdszaebjhZl/iA6dOnc/3o37Rt25ZxC17V6dyvXLnC66+/zunTpyksLGTAgAE4OjpqLfPBBx9w+vRp9u3bR9++ffW6/3upM1o0165dY8OGDbRu3ZrWrVvj6emptSevVCpZtGgRTk5O+Pn5ERgYiIuLi9a6V6xYQWxsLK6urnTo0IGnnnoKgJTcvJJhXlwiwXGJXLtnqGemLKK9ow3vTxyHe9CQx4EAACAASURBVBWhIigZUSxfvpyIiAjGjh3LwoULtd5DWloaM2fOZPv27Zw8ebLKWOkdhBDMmTOHlStXEhwcTD0fH3r/8jMaIdg2djwtnCoXW+vRowdHjhzRem2ZusWy0yf4+uxpmtWrz47nX3ygSVWFQlGtEWhycjLOzs6V1imE4FLiLf66HMau0OvkFRWXnTM3NiLQw5V2nu609XKnpbsLllokPmJjY6s1+hZCEBERQZMmTfQuk52dTWZmJg4ODvelRVNnHHxtIqewkAvxSewLj2BvWAQFSiUAEtDJ25PhLZvzjJ8vFiZVD0nv6FDr6vUDxMTEEBQUxPjx43U+aEVFRQwbNowRI0YwdepUXt23m23Xw+nv05hvBj1bwf7atWuMHj2a2bNn89JLL+lsi8yTT25REU+t+ZGcoiJWDBjMwMZNH3eTuJWdy7ar4fx1OZTYjH/ksN1srRnaohk9m3jTzMWp1i6OuF+xsToRoqlt2JiZ0aNxI3o0bsTb/XuyPzyCLZfDOHszgVMx8ZyKicdyjwn9mzXmuVbNaOvpXsEx6+PY7+Dt7Y23t7detqampmzZsoW9e/cCMKNdB7ZdD2dfVASRGen4OpQfVrq5uREbG0tsbKze7ZF5svnt6mVyiopoZG9PP5/Gj60dCqWSA9ci2XoljJPRcWWjZnNjI/r5N2Z4q+Z0aFCyqe9JRe7B1yISsrLZeqWkl3H3mnwve1uGtWzGsJbNcLd79Pf08q5t7I+KZLhfM77oO6DC+blz59KpUyeef/75R942mdpFoUrJU6tXka4o4LNn+jPiEU/OCyG4kJDEX5fD2B16g/zif0IwHRp4MLxVM/r6NcbKVD911drC/fbgZQdfC9EIwfm4RP66EsaesBsUFCvLzg0J8OOtfj2wt6jeqpgH4WpqCkM3/IqhJHFowmS8bO3KnY+MjKS4uFhrghOZusHayxd4/+hhPGxsOPTi5Eca8rialMzbOw9yLeWf9H4edjYMb9mMoS398bS301K6diM7+FKeBAd/NwXFSvZfi2DrlTBOx8QjAEdLCxYN6EU//0c3/J20dTPH4mJ5PqAli3tVP7OMzJONEAKlRkPPtau4lZfHBz16M/4RZQQrUqlYcfQ0P50KRi0EFsbG9G9WEoJp5+X+RIRgZAdfypPm4O/mSmIyb2zfR+TtDAD6+zfm3QG9cLS0qPG6zyYmMHbzH5gYGHJk0hRcrO5v84jMk0d0dDTh4eHkNfTijUP7cbK05OjEqY9EXOxSwi3e3LGfqNJnYlDzprzdrwcOj+CZeJTIGZ3qAC3dXfhr2jhe7toBQ0lib3gEg75bx+7Q69XK1H4/dHD3oL2bO8UaNT9dPF+jdcn8e4iOjqZHjx7Uq1+f74LPAjC1dTu9nPudlWD3Q6FSxdIDx3h+zR9E3c6gnqUFK0cN4cvnBj5xzv1BqFEHL0mSgyRJv0mSlCNJUpYkST9JkqRVe1aSpCOSJIl7Pt/VZDv/TZgYGfFqr65snPI8TZ3qkVmg4NUtu5mzcSdpeTWbYm9mu5LM7uuvXiZDUVCjdcnUfoqKipg2bRrJyckkWppzMzsLOzMzng9oqbVcZmYmc+fOpaio6L7qDY5LZOgPv/Dz6fNohGBoC392zZhIHz/9EmbXJWq6B/8b0Bx4BhgMdAd+0KPcj4DrXZ//1lQD/600d3Vm09QXmNO9E0YGBhy4Hsng79ax7Up4jfXmuzdoSICTMwqVijWXLtZIHTL/HoyNjVGpVKz66Sd+vHQBgEmt2mjdHLR3714CAgIwMjLSe/OSEIKjR4/ywcdL+HDP34xf+yexGVk4WVvy/dihfDqsP3ZVaL6o1eoaH93WZmrMwUuS5A/0B6YKIc4IIYKAOcBYSZJ06WAWCCGS7/roHXOqS5gYGjL76c5snvoCzV2cyFIU8t9te5nxxzZScvMeen2SJJX14tdevkjOffbAZJ4MNm7cSI8ePXDr2pnr6bexMjZhYqvWWsscPnyYvLw8Xn75Zb3qSEpKok2bNgyZOp0Dpnb8GnwZAYwIbM6ulyfQo3GjCmVOnTrFjBkz6Ny5M8uXL9ernr///ptvv/2Wzz77jC1btuh8KQghOHToEIcOHeL06dNERUXprEOpVBIUFERsbCzFdy3f1EZmZibXr19HqVTqNq6qoTXxASYDmfccMwJUwHAt5Y4AacBtIARYAlhosTcFbO76uAMiOztb3OHChQviwoULYt68eeLq1atCFyqVShw+fFisW7dO7N27V2g0Gp1loqOjRUREhPj222+FSqXSaS+EEKGhoWLv3r0iLy9PL/uMjAxx+/Ztcf369QrnilUq8e3xM6L54q9Ekw++FG2XrhRrz1wQyamporCwUK/rCyGEWq0W+fn5VZ/XaETfX1YL768+F0uDjgkhRLWuL/NkoFarRbdu3URqeroY9Nta4f3V5+KToKNay1y8eFH0799fxMXF6V3P4i++FM2nzhJNPvhSNPngS/H08h/FsciYKu0VCoUYPXq0MDc3F0uXLtWrjnPnzolu3boJQMyZM0coFAqdZX744Qfh5+cnADFy5Ehx8+ZNrfYJCQnigw8+EPXq1ROAeOaZZ0R4eLjWMvv37xevv/66kCRJGBoaCkAANqI6frg6xtW6MLwJXK/keCowQ0u5l4B+QAtgHJAAbNFi/17pjZf73O3gf/x9o5g4Z74wMDYWgJg6daq4fft2lV9sXl6eePvtt4WPj48ARLdu3URQUJDWP8bhw4dF//79BSBat24tTp48qdVeo9GIBQsWCCsrK+Hn5ycuXbqk1V4IIc6ePSvatm0rXFxcREhISKU2N1LSxMhV68seiMC3FovO/QaIgwcP6ry+EEKcP39eDB06VKSkpFRps+vGdeH91efC+6vPxbZrYWLkyJEiKipKr+vLPDlkZWeLOXt2CO+vPhetvvtapOZr76golUqRlZWl9/V3hVwTrT/5uuy3/M7OAyJXR2dCo9GIsLAwce7cOb3ruXnzpggJCRHbtm3Tu8zx48fFpk2bxP79+/WyT01NFfv37xeLFy8WR44c0WqbmVcggqPjxYEjx8TRo0fFlClTxIEDB+7LwVd7maQkSZ8Ar+sw8weeAyYKIcoJUUiSlAosEkJ8q2d9vYBDgK8QosI4SJIkU0p68XewBhLuXib51h/72HY+DAlws7emiWt9Gjk74uPkgI+zI95ODpXqvvz999/Y2tri4eFB/fr1dcoDXL58mYiICCwtLbG2tqZTp04YaVlNkJGRwbp168jNzUWlUjFz5kycnSsX9IKSl/GKFSvYu3cviYmJrFmzplLpVbVGw8aLISw7fIIsRSGaQgVF+7dzad9uHBwctN4DwBtvvMGqVas4d+5clWJKS4KO8uOFYEwMDHnLP4B9q9foJc0q8+RwR1DMyMCANUNH0MXTS3chPRBC8M3xM/zv6CkAmrk48U7/nrR5CBmOahPpeQVEpaQTnZJBVGo6USkZRKakk5FXsoBh+YQh9AkomTh+ZOvgJUmqD2jXuYRoYDzwhRDC/q6yRkAhMEoI8Zee9VkCeUB/IcQ+PewrrIN/f/NB9l+NILugsIoy4G5vQyMnR3ycS5y+n1t9fJ3rYWRYe1eSFhQUYGFR9ZKw23n5zPpzB5cSb2EA9LY0ZMX8V3Red+PGjcyYMYNp06axZMmSSm00QjBz93b2R0Vib2aGR9AZ+nbsyIwZM6qlkyPz72TrtTDm798DwNI+/RjVLOChXLdYpeKdXQfZeiUcgMmd2vKf3t1qNOtRTVNQVExIfAoRKbeJSskgOiWdqNQMMvMVVZZxt7dhbv+uDGxdogNf6zY6lU6yhgHthBDnS4/1BfYCHkKIJD2v0xUIAloJIa7oYV/pRichBBn5CqKS08vellGlX/SdN+a9mJsY09LLhcAGbgQ2cKWllyu2Fo83Q0t1KVSqWLh9H3vCbgAwo1tH5vbQnfQkOTmZd955hxUrVmBaRTYqhVLJ2M1/cDU1Bbt8BRffepdr4eE0bfr41QNlao647CwGrl9HgVLJjHYdeK3LUw/lulmKQmb/uZ1zcYkYShLvDujF2Lbal1zWNoQQJGRkc/nmLS7dvMWlm0ncuHW70uTakgQeDrb4lHYs70QVGjk5YHGPVk6tc/AAkiTtAZyBlwFjYDUQLIR4ofS8OyXhlwlCiLOSJPkALwC7gXSgJbAMSBBCPK1nndXeyZqZryA6NZ3I5BKHH5WSTmhCCnmFFWe6Gzk5ENjAjVYNXAls4Ip3fQcMDGr3VmiNEHx1+CTfnSjZiDKoeVOWPNtX52YUIQQqlUprJp3EnBz6/7aGfKUSk9PneHvgYMaPH/8wmy9Ti9AIwbgtf3ImMYEObh6sHzH6oUgBxKZnMn3DVmIzsrAyNeGrEYPp5tPgIbS4ZilSqghNTOFS7C0ux93iUmwS6ZV0GF3trPF3d8LnntCwmbF+u31rq4N3AFYAQwANsBl4RQiRV3q+IRAD9BRCHJEkyRP4FQgALIF44C/gI31v6mFJFag1GqJTM7gUm8Slm7e4fPMWsbczK9jZmJvSysuVwIZuPNW0If7uTrU2Y/zmS6G8u+sgKo2G1h6ufDP62Yey629DyBXe/PsApoaGTDKx4PVpsi78k8Lhw4fp2rUrJqVr2++IiVkYG7P7hQkVhOfuh+C4BGb9uYMsRSHutjZ8N3YoTZy0p7J7XBQpVZyOjONMZDyXbiYRlpiKSl1+R66RoQHN3Z3LOoGtGrjhbKt1f6dOaqWDfxzUpBZNRl4BV+JucbHU4YfEJ1OoVJWzcbO3oXdzH3oH+NK6oVutix2ejo1nzsYd5BQW4Wlvy/djh+FTT/fEqzaEEEzatpnjcTcJdHZh46jna919y1SPpKQkXn31VaysrPjpp58AiM3KZOD6dRSqVLzfozcvPgQxsW1Xwnlr5wGUajUt3Vz4Zsyz1LeyfODrPkzyCos4fi2WgyGRHL8WU07dFcDByoLABq4lodyGrjR3d8ZUz565vsgOvpRHKTamVKu5npTGpZu3OBsVz8kbN8s5fAdLc3o086F3gA+dG3th8gjEl/Qh6nYG0zdsJT4zGxszU/43cnCFhOLVJSk3h/6/rSWvuJjXuz7F9LYdHlJrZR41arWaUaNGsXXrVsLCwvDz80Ot0TB28x+cv5VEZw8vfhk+slxoRqPR8Msvv9C6dWtattQdNxdCsOLYaVYcOw1APz9flg7rj7keibUfBel5BRwOjeJQaBSnI+JQqtVl55xsLHnavxFtvN0JbOCKh4NtjY/aZQdfyuNUk1QUKzl54yaHQiM5EhZNjuKfnZ4WJsZ09/emd3Nfuvt5Y2n2eBMOZOQXMPPP7VxMuIWRgQHvD+rNyMAHWwmxKSyE/x7ch4mBITuef5HGOpIKy9ROzp49y+uvv8706dMZO3YsAKsuBPNx0FEsjY3ZO24S7nc9W6dOnWLu3LkYGRlx4sQJnc6uWKXizR0H2BFyDYBpXdoxv1e3xy7rm5iRzd+hURwMieRibFK5idEG9ezoE+BL7wBfAjxcHvm8m+zgS6ktcsFKtZrg6AQOhUTxd2gkqTn/CIEZGxrSqbEX/Vs2pn+rpg99OKcvRSoVb27fz87Q6wBM79qeeT273veDJoRg6o6tHI6NpoWTM5tHv4CRHKr5VxEZGcmECRPYunUrTk5OAERlpDP4918pUqv4uNczjL1HTGz58uW8+eabbN26lb59+2q9fkaBgilr/yTsdgZCo+atPt2Z2FW/0V5+fj7nzp3j6aeffmg95tTsPP4KDuXg1UjCk1LLnWvm7kTvAF96N/fBx9nxsc6tyQ6+lNri4O9GoxGEJCRzKCSSQyFR5SZrHawsGNOpJWM6t6Se9aOPPQoh+N/RU3xz/AxQojG/dGh/vWf37yUlL49+v60hp6iIBZ27Mat9x4fYWpmaJDU1lWHDhrFu3Tp8fUs22Kg1GkZt3MCllFs85dWANUNHlHN0e/bs4euvv2b58uU0btxYqxOMLg0NxmVmo1YoeMmvAf+dME5nu65evcqbb77J4cOH2bhxIwMGVEwbeTdCCK5evUpoaCihoaEMGTKEjh3L/w7DElJYF3SBvZdvlE2SGkgSrTydae/lRL9AP5p6uets26NCTrqtB/n5+Vha6u9ElUql1iWClSGEqCRBtkRLr5J19PMGdCM6NYMDVyPYfDaEW1m5fHvwNKsOn2NQaz/GdQ3E392pWnU+CJIkMbdHFxrY2/H2zgPsDY8gPb+An8Y9d18JG5ytrHjv6V7M37+H/505SS/vRvjXq18DLZd5mOTl5TFmzBiWL19e5twBfrwQzKWUW1iZmLCkd99yv+2jR4/y+eefs23bNqystK8SiUxL54W1f5KtKMSkuJAF7ZszcdizOtulUChYs2YNZ86c4YMPPtDp3KHkRTVlyhSCg4NZtmxZmXNXqTUcDovit6CLBMckltm3aehGq3oWvDN9ElezMnBesADv/trX9gshWL16NZ999hkmJiaMGzeO+fPna925LoTg7bffJiQkBCcnJ0aMGEH//v211lNUVMTs2bMxM7u//Td1ogf/ySefkJCQwJkzZ+jUqROvv/46Hh4eVV4jJyeH6dOn4+rqSlhYGEuXLqVVq1Za6/3999+5du0aUVFRjBs3Tq9extxXX8WuaSuOJ2SSpvznwWnh6cLoTi3p36oJ5ndJKAQFBREaGkqzZs0IDAzE2lp3VqWvvvqKMWPGYGNjo3XXK8C5mwlM//0v8pUqWrq5sGL0EJytdS/vWr9+PYMHDy77voUQvLxrGweio3CytOTnZ5+jWf1/XlpZWVkcPHiQkSNH6ry2zKNhy5YtmJqaMmjQoLJjv165xHtH/0YjRIXdqkIIXn31VRYtWoS9vX1llyzjZPRN5m3eRXZhEfYaJTvmz6S+Hr8rgI8//hhvb2+6dOmCl5eXzjBJYWEhI0aMYMyYMWg0GiZNmsStrFw2n73KlrMhZaFSIwMD+rVqwoSn2nD97AnWr1+PSqViypQpDBkyRGe7JkyYAMCJEyf46quvGDx4sFb7y5cvM3v27JINlxkZLF26lMGDB2u9n/fff5/Dhw8TFRVF/fr1uXjxIsghmooOvqioiJCQECZNmoStrS1+fn688cYb+Pj4VHoNIQQJCQksWrSI7du34+zszIIFC/i///u/Kv8ghYWF7Nu3jzFjxlBUVFTWG3JxcamyrdeuXePVV19l79699Bn5Av4DR3LsRlzZkNHazJRn2/ozulNLfJwdUSgU/PDDD7zzzjv4+fmxZ88eHHVMZJ48eZL//Oc/APz888/4+flptT8REc20XzejNjLCwdyM754fRit3V61l9u/fz5dffslff/1VpvGdXlDA+L82lsnIfjv4Wbp6lmxcUavVjB49mvHjxzN8+HCt15Z59Agh+OLUCb4JLgnbPR/Qko969ql2DFoIwdqzF1l64BgaIWjp5sL3zw/D4REkjFdrNJy4fpONZ65wNDymbMLUwdKc5zoE8HyXwApr01UqldYe+L2oVCoKCwt1jl7uJikpCScnp2rVExoaioeHB3Z2diA7+IcXg9doNNXSVSkqKqKgoAATExOMjY0xNjbW+VCEh4eTkpLCnfa2bN+RredC2XjmCgkZ//wd23q7M7pTSwJd7Pj9t18JDQ1FCMGyZct09qC2b9/O+PHjcXZ25syZMzoFx2LTM3juqx/INzbFyEDio8HPMLxVc61ltmzZwtq1a/nll1+wsrLCwMCAnKIiXt65jdOJ8RgbGLC0T3+G+fkDJTo6Q4YM4YMPPqBr165ary3z6FCq1bz59wE2h4cCMK9jF+Z06FRt516sUrFo9yG2XA4DYHjLZrw/qHeN52m9nZvPlnOhbDpzlaTMf56fDj6ejO7Ugt7NfTE2MqzRNtQE8iRrKbVxkvV+0GgEpyJu8ueZqxwJi0KtKfk7udnbMOuZTgxu41+tzURFRUUcPXqU/Px8vXrNf2zewvuHglC7egIwqWMbXuvzlNZVMevWrWPx4sUsXry4LPxSpFLx2oG97IwoWanzeteneKlNeyRJIj09nSFDhug1spCpefKKi5m9ewfH4mIxlCQ+6vUMY5q3qPZ1UnPzmLNxZ4nInSTx+jPdmdihdY2uQskuKOSnI+dYf+JS2V4UG3NThrVrzsiOLWjk9GCb+R43soMv5Ulx8HeTkp3HlrMh/HH6MrdzS3QufJwdeKVfV3o196mxB0et0fDaug3sik8BoGsjL758blCV6dFOnz7N6NGjsbW15fLly2WjH40QfBJ0lFWlybontmrN20/1wNDAgJiYGF544QU+/fRTOnToUKWwmUzNkpafz5TtWwhJS8XcyIivBwyhl3fFbEm6uJKYzKyN20nNzcfWzJRlIwbRtVHNacoUFCv5Negiq48Ek1tYsu+khacLY7u0ol/LJve9Gqy2ITv4Up5EB38HRbGS309eYtXhc2WbqFp6uTCvfzc6+HrWWL17w26wcPs+FEoVXva2fDP6WRpXoRVSUFDAZ599Rvv27Rk4cGC5cz9dPM/i40cA6OfTmC/6DsDC2Jg1a9YwdepUfvjhByZPnlxj9yFTOZEZ6UzZ/hfxOdk4mJnz07PDaeWifd6lMrZeCeOdnQcpVqvxqefAt2OG0sDhwbVqKkOpUrPp7FW+O3imTNyriWs95vbvSnc/71qrB3W/yA6+lCfZwd8hR1HI6qPn+fX4BRSlw9HOjb2Y278rAZ5VT+o+CNeS05i5cTuJWTlYmBjz+bAB9G5a+SQ1VL3EdOeNa/xn/16KNWqa1avP94OHIXJymDdvXtm6ZVlP/tFxKDqKV/ftJk9ZTANbO1YPfY6GdtrndO5FpdHwxaEgfj5dMkLr1aQRnw3rj1UNjMbUGg27L11n5f6TZXNUHg62zOnXhQGtmtZ6Zdf7RXbwpVTm4NfuD+ZSVCL+Xs74eTnh7+VE/QdUd6sN3M7N54dDZ/nzzJWylTd9WzRmdr8uNRJzzChQMHfTTs7eTABgbo8uzOjWodq9peCkRGbs2k66ogAHM3NWDhxCRw9Pdu3ahbu7e6VZqmR0k5OTQ0xMDJIkVakHo1QqOX36NPsPHCCnqS8709MQQAc3D1YOHIJjJUtpMzIyACqdnM9WFDJ/y26Com8CJfkGXunR+aHLDgghOBoezVd7TxCRnA5APWsLXu7TiRHtA/6VE6d3o9ZouJmSybX4VMLjUrkWl8qrI56iWYOSDpvs4EupzMHP/N9mTofHlbOrZ2OBn5cz/l5O+Hk64e/ljLO91b9yaJeQkc3K/afYeTEcIUp25A1r15wZz3TC1U73WvnqoFSrWbL/KL8FXwZKdr4uebZfpSkPtZGYm8OMndsISUvFyMCAd7v3ZPxDUCesiwghGDVqFJs3b6Zr165s27atyuWzs2bN4ttVq+jw1kJS7Uqej3EtWvFu954YG/7jJAsLC9m0aRMbNmxApVKxc+fOCkv7otLSmfHndm5mZGFubMSSZ/vR0aUeiYmJtGhR/cnZqjgXlcDyvUFcvnkLKJk8ndyjHS90bV3t311tQKXWEJOcwbW4lBJnHp/K9YQ0FEXlVSoXju3F6KdL9t/IDr6Uyhz81ZhbXI25VfZmjEnOqDTDir2VOX6lDr+FtyttfN2xsfz3ZHC6ces2X+87weGwaABMjAx5vnMrZjzTCSuzhztc/vPCVT7Y8zdKjQY/5/p8O+ZZ3GyrFxJTKJUsPLSfHTdKRKeeD2jJoqd7YWL47+6NPSqKi4vZsGEDq1atomnTpuTk5LB27dpKdz2mp6fz2muvkWcgcaOpLzmmJhgZGPDe0714oUXFTXy3bt3i6aefJj8/nwsXLlTIFXwkIpr5W/aQX1yMu60No51t2fnLGq5cucKZM2dwddUdw1epVJw8eRIHBwcCAioK3UWlpPPZzmMEXY8FwMzYiHHdWjP56Xb/msxqQggSb2dzPiKRsJsphMelEJGYRpFSXcHWzMQIP887HU4n2jbxxM2x5JmSHXwp+sTgFcVKbiSkca307Rkel0p0Ujoqjeaea0FTDyfaNfGgXVNPWvu6Y21e+1d5XLqZxPI9JwiOLgmluNhZ8/7IZ+ja5OGuZgiOS+SVTTtJzy+gvpUF340dRoBr1UnDK0MIwffnz/HZyeMIoJ2bO98MfJZ6Onbd1mUyMzP54Ycf2Lp1KyNHjmTq1KlYWlpiYGBQYf5CCMG6dev4/vvvGf/6f1mdmkS6QoGjuTkrBz5LB/eKO7p37tzJxx9/zOeff44kSXTu3Lnc+d/OXeKjfUfQCEGHBh4sGdCT/859hR07drBz506eflp78jUhBO+99x7/+9//6NSpEzt27Cg3OlCpNaw+Gsw3B06jVKsxMjBgcCtfWphruHb5Ai1btqzWBrns7GxMTEzKNuHVNEnpOQTfiOf8jQTO3YgnOSO3go2lmUmJMy8NGft5OtHA2b7Kpc+ygy/lfidZi5QqIhNvEx6fSvjNFC5GJhKbUj6Dk4Ek4e/lRLumnrRr7EGgr/tjl/2tCiEEJ27c5KO/DpVNRj3XPoDXBnd/qC+ppOwcpm/Yxo3U25gbG7HsuUH0bFL95XV/x0Qzb98u8oqLcbWy5vvBQwlwqt7L4kknJiaG5cuXc+nSJaZPn86oUaO0aiVdv36duXPn0qFDBxoNe5aPTx5HpdHQvL4T3w0eirt1+edDoVDw3//+l9zcXL7++usKUhgaIfj04DFWn74AwMjAAIa7OzJ3zhzmz59P06ZNK+2J38umTZv44osvKC4u5u+//77juICSUejbf+4jLLFE2bG7nzevD+nOu/95lXXr1jF58mR+/PFHnRPx0dHRjB49mqioKCZOnMgXX3yBoY6R4Zo1a7h48SJ5eXmMGDGiwiqwe8nKymLHjh0UagxIzFWjMLYhLD6dxPTy/tfI0ICAhi60bOSKnbEGRzMJP2936jk6Ymdnp9dmSEtLSxo0aACyg6/o4NPTO3MMvgAAIABJREFU07G1teXChQv4+Pjo3N4vhCArKwslRmw5eIK0YkOCrycQn5ZVzs7QQMLfy5m2TTzwd3egawtf8nKycHR01GsrckFBARYWFigUCr16F8XFxRgZGVVrlUlWbh7f/F2yAQTA2daKeQO6MSjQr9IVB0II1Gp1tbZSZ+UXsGDrXoKib2IgScx5ujPTurQrF9O9l8q2hUdlpPPSzm3EZGViZmTEwq7dGdeiFYYGBvcl/JZbkI+1Re3KDnQ/nL90kU+XfEJhYSHz5s2jR48eWp2CWq3mww8/5OTJk3zw2WdsSkliy7WSHaWDGzdlaZ9+FRJrhIeH89JLLzFr1qwyDfi7Sc3N491dBzkcEQPA/J5dKTx/it27d7Nq1So8PXUv001LS2PmzJm4ubmxePFiioqKyp7FXEURq46cY+2x86jUGmzMTVn4bA+sc1JYtGgRffv2RaVS8e6772r9/SuVSr799lt+//13cnNzmTFjBrNmzdLarpiYGFatWsWOHTuIiYnhu+++Y9y4qlUu07Ly+HnzHo5eCCcuqwgTm/JLhg0NJJo1cKFdEw/aN/WkVSM3ihT57N69m82bN7NlyxZcXV1ZvHgxEyZMqPLFExoayqlTp/jyyy8JDw+/c1h28Nzj4FesWMHmzZuJi4sjLS2NuXPnMn/+/Cq3+Ofk5DB+/HgyMzO5cOECw4YN4+OPP8bU2oHgiHiCrycQfCOepHve1GjUGOSloEmP4/O35tHrqc6VXh9KHOnIkSNRKpVkZWUxe/ZsRo8erfXejh8/zsKFCxk/fjyxsbF89NFHOp3etGnTsLa2ZuD4ySzZFURKrgIAfzcn5g/qRufG5cM2kZGRTJ48meXLl9OmTZtK1THv5Z133sHAyAhl605sLt2a3tSpHh8NfoaW7hWXbebl5TFw4EA+++yzCjKuOUWFzN27i6M3YwFoXt+JD3r05sax42zcuJFly5bp5UyS83OZMW8udirBggUL9OpZ1lZ+OrSXTi6eNG+uXS7iDkIIdu3eTY6XB5+fCiKrsBAJWNC5GzPaVb7qKSoqCkNDQxo2bFjuuEYINl68ymcHg8gtKsLY0JBPnu3L4AA/jhw5wlNPPaWzZ3yHrKwswsPDy4V8ilUq/jh9he8PniGroBCAns0a8e5zvalvY8Xx48dp2rRpmTa9LjQaDRs2bOC5554jPz9fZ2cO4MKFC6Snp+Pv709hYWE5NU0o+T6vJ6Rx7Eo0R69EER5XXjfeQJLw8yoJ5bZv4lnpyP7mzZvs2rULf39/jh07xn/+8x+dyrYbN25EoVAQHR2Nra0t8+fPB9nBVx2iWbFiBcXFxXh6ljwszZo103qta9eu8fnnn+Po6IiHhweTJ08u90dJSs/mQkQiFyISOXs9roLD97Q3Z1DXQJ5u2YgmHvUrfbCCg4MZOHAg2dnZTJgwga+++kqr6mNBQQGzZ89m9erV9OnTh40bN94RIaoUIQR79uzhvffe42ZCInOX/cD20FjyCosB6NKkAfMHPoWf2z+SvnFxccyePZvmzZtjYWHBO++8o/V7EkKwcuVK/tq6lXFvLeKH8yFkKgoxkCRe7BDI3B5dsDQp/4NPTk5m2rRpdOrUiYULF5ZzEmqNht9DrvD5qSByiv6fvfMOj6u61v5vpFHvvffeq1XcK80YGxyKQ0wNcCEOvoHkQgK5lxYHCCaAiSHB4QI3GJtuDLbBSJaLuixLo95HbdR7n3a+P0Yaa6xmY8uW8/l9nnmOdOb02efda6+91rs0CV13hkWwysiUl/7wBzZv3sy2bdtmJZbu0WGsDI05k5/P66+/Tn9/P9u3b2ft2gsXzbrS6B4dxtrI5LxDDwtbW3jueCqFba0ABNs78MLKNcS7Xpi+eW1nN//93Y/kNmikdSNcnXjp5nUEO128/LNaLXBEUslbR05pXYi+jrb85salrAz1veK/kVyhJLeyiROSGk4U1dLWM6j9TiSCMC9n4gLdifF3uyxzc9d88OO4UolOgiBQI+viRFEtxyW1FEtbmPxonW0sWBbhy/JIXxYFumM4TQq1XK4hXUPD2f36MpmM2tpapFIpZmZmbNq0adYXQhAE3n77bY4cOUJjYyP//L+PSanvYl9mIUqVGpEINsSG8OvrFuNio3lmarWa6667jpSUFN59910eeeSROZ+BRCLhsccew9DCksAt93GsXkMMblaWPHfTGpb7e+tsr1areeuttzh8+DB79uzB2tpax+/bOTzMK+kntMJXRvpiNoeEIi6QkPbNQXbu3ElcXNyc1wUaC+qtt94iJyeHhx56iLvuumvO53w1QRAEMpsaeScvm/RGTUiwuYEhv0lewtbI6AuqrCVXqfhnRh67T2YjV6kwMRDzn6uWsHVR9CUppp5T3cjOQycpadJIYDhYmPGr65LZFB+GWP/KJbn1DAxzsriOE5JaMsvqdcIWjQ3FJId4sTzSj2XhPthaXt4ggGsEP46Fksna1T+kbSxZZfWMys8W4zY1MiApxItV0f6sjvbHxOjyxfIKgqBtLI1dvbx1JIPDhRohMEOxPncvieaXqxKwMjWmr6+PH374gR9++IFt27bNqYkPmgo/mzZtIiAggL/u/5w/p6TT3KdpjxvCg/nDdSuwNdN9OQoKCti2bRtjY2N8/fXXuLnpWpp5smb+fOo4Z1o1cdD6IhGr3Dxo/OoAQXb2/OIXv8Df33/WkcwE+vr6eO+99/j888+59dZbCQgIIDExcco5rxaoBYGjtdW8m5ejtdj1RSI2BYfyu8VLcTS7sIQ+SXMrz3x7lMr2TgCW+nnx/E1rcLe2mmPPuVHV2snrh05xslzjxzczMuSBlfFsXRZ7xeLZW7r7+T6vghOSWgprZTpGmYOVGcsjfVkR6ceiII8rVloTrhG8FguF4CdjVK4kt6KBE0W1nJDU0tF3tj6riZEBa2MC2JAcSqy/+xVJtS5ubGXndyfJHQ+rtDY1Ztt1i/lZYsRPsqh6e3v5+uuvEYvF3HrHHbyVlslHOWdQCwLWJsb8/roVbIwI0Rl17NmzhyeeeIKIiAjS0tKmzC0IgkBOcxPv5OVwokGqXR9qbEr233YTbmvPkSNHzlusTKFQsH//fh599FGsra05dOjQJU3OmW/IVSq+qSjj76dzqenRZJoa6Yu5Myych2IX6RTFPh8MyeW8cSyD/8s5gwDYmJrwh+tWsCE8+KLdJd2Dw+z6PoMvcopRCwJiPT3uSIrkkbWJ2Jlf/nDYkTEFKWeqOJhVSl5low6pB3s4sjzSl+URvoR4Ol5xV9EErhH8OBYiwU+GWi1Q3tjGcUkth3PLaero037nYmvJzUkhrE8MxdNxfkSaZoIgCJwsl/L6oZNUt2lSwQOc7fivDSumTMT+FEhkrTx78CgV45Zhso8Hv12zTCdufmRkhMOHD2Nra8vKlStnPFZxexvv5uVwuLqSidZr2NFJYN8AX/31zfOe9Ovr6+PQoUPU1tbS2dnJU089NWuBloWAYYWC/SVF7MnPo2VQE19tYWjEPVHR3BsVe8H5Ayq1mm+LK3gjLR1Zn+Z4GyNCePq6FRddmEOhVPFJRiHv/JilVXq8LiKA7TcswcvhwvRuLhZqtUB+dRMHs0r5Mb9Kx/0SH+jOuthAlkX44mx7aTO/LxWuEfw4FjrBT4YgCBTWyjiYVcrRvErtxCdAtJ8rG5JCWRsXeFmTq5QqNZ9nF7Hrhwz6xqMaVof58dv1y/G0v7hOR6FS8X7mad4+kYVcpcnkuy7Yn+0rF+PvMHe0w7mo6+3hH6dz+bKsBMV4klqQrR2PJSRxo3/gBfmdFzp6R0f4qLCADwvz6RnV/C4OpmY8GBPHlvBILC5Q2EsQBH6sqOHNtAyqOjQdupuVJc+vX8MyP++Lvt4TZXW8evC4tsB8iKsjT92ygnjfmUtlzgcaO3r5NquU77LLdIIg3B2s2JAUyvrEEFztLt79NN+4RvDjuJoIfjJG5UrSCqs5mFVKdlmDVkrByECfVVH+bEgOIyHY45JMcp0P+oZH2X00k32ZhajUAmJ9PbYujeGRNYkXLXvQ2NPH2ycyOSApQ0ATZnZLRDDblifjYXPhL1vr4ADvnznN3mIJwwqNZeZlZc29UTEs9/LGx9pmwQy1LwRylYqi9laOVFexr1jC0Pi9eVpa8XDcIjaHhF1whSRBEMioa+Cvx9IpkmkmOS2NjfhlcjxbEy5e2+VceQFbc1O237CETfGhl63tDoyM8ePpSg5mlVJQI9OuNzc2ZF1cIBuSQ4nydb2q2sQ1gh/H1Urwk9HRO8h3OWV8m1VKbUu3dr2DlRmbloRzx4oo7CwvTwJPTVsXrx48TnqlRi3wUr6wVe2dvHU8kx/KqwEw0NPjZzHhPLos8byKfZ+L3tER/k9SwAcFZ61cADsTUxLc3ElwcyPB1Z1AO/vLRjYXgmGFgjOtMnKam8iTNZPf0sKY6uzkfIi9A/8Rn/CTRyf5jTJeTz2lDXs0FutzX1IcDybHYTmNfg1oOoSGhgZ6enpmVfmcT4PgfFHW0Mbe1DP8mF+p1XrRE4lIDPFkQ1IoK6P8MTa8OguALDiCF4lEzwDrgWhALgjCnON7kaZLfR54CLAG0oFHBUGouoDzXvUEPwFBECitb+NgVinf51XQN6QhLQOxPjcuCubuNbEEuE1feONSX8eJ8jr+cvCEdsgd6qYZcsf5XPyQWyJr5c1jGVrJWSOxPr9YFM1Dixdh8xP8wMMKBZ+WFPF9TRVnWlu07qAJWBoZEe/iNk767oQ5OM6adTtf6B8bJU8mI0fWRG5zE0XtbVP0kOxMTFjk6s4dYRGs8PKeYnWOjY3x61//mpaWFszMzHjhhRcIDAzU2aa8tYM30tK1WahqpRLXgW4+efa/cLWd2Rf+yiuvsGPHDhwcHEhJSZlIldeB1qX3fTp940VoLEd62P34fUQHzC5ZoVKpqKmpobS0lHXr1s2Z+DPtMdRqTkhq+Tg1n/yqZu16H2dbNiSHclNCCI7WV780+EIk+OeBXsAdePA8Cf4p4PfAvUAd8CIQAYQKgjA6276TjvFvQ/CTIVcoOVZYw97UMxTVtWjXJwR5cPeaWJaE+cx7BM50k2bXRwby5PpluNpc/LPOqW/ir8fSyW/UDKvNDA15ICmW+5Jif3LxiDGlEkl7KznNzeQ2N3G6pVnr6piAiVjMn9dcxy1BIRd9D+eL3x09wpdlJZz79jmbmRNkYUFjZjYhllbEeHlzxx13YG5+lqQEQUAikfD111+TkpJCa2srHh4evP/++zokXNfVw1tpGRwqrdSsUKtxGe5niZUxL/3+6RldFDKZjD//+c+Ul5fT29vLN998M606ZGZVPa8ePK7VZ1d0t2NQdZqUT/9vivrkuSgrKyMxMZHR0VE+/vhjbr/99lm37+3tZceOHVRVVWFoaMifX32N7JpOPkk7ow1UEOvpsS4+kLtWRhPm5UR+fj7t7e10dnbi7+/P4sWLZz2H5hGpkcvlKBQKjI2NL1geY76w4AheewKR6D7gjbkIftx6lwE7BUF4bXydFdAG3CcIwr7zPN8Ugu/oGcTIUHxVSf/OBkltCx+n5pN6pkpbjNvbyYYtq2JYnxiC6TwLoJ0b9mYk1ue+FfE8uDIeU6OLO7cgCJyolvLXY+mUtXUAYG1izCNLErgzLmJKRuyFQqlWU9rRTk5zE7myJnJlzfSOjvLF7VuIcXG9qGPPhPb2dkQiEQ4OZzNAX00/ybunc3AxNsFF0EQB9ZaUMtAsw8TYmFOnTrFx40aeeeYZwsPDUalUpKen8/XXX5OVlUV4eDi33norq1evprm5GW9vb61GS2NPH++eyubLghImxgNxtpa8dOcmvGysZ4wyamlp4ZVXXqG4uJinn36aNWvWMDg4OEV0rKGzl9e+O0FqSQ0AIoWcaDMV961OJjIiXOc+z8Xg4CB/+9vfOHDgALa2tmzbto0bbrhh1udXV1fHW2+9xSeffIJ/WBTrH3iCw6drGJwYMZgasXlZJHeujMbR2hxBEDhz5gyPPPIIeXl5bNu2jZ07d86a2KZWq/nnP//Jww8/jL6+Pn/84x/5wx/+MCvBKxQK7r33XkpLS7GxseH5559n+fLlc97Lb37zG2xsbPDy8mL79u1TJFMEQaC1cwBrCxNMjA3Yt28f6enpvP3223AVE7wvUAPECIJQMGn9caBAEITtM+xnBEw27yyApskE/8vfv0NxwzAWxiLMDBTcuDqZ6GBPgnwcsbaYGlY2MjLC66+/TlJSEvn5+dx0001z6oAcO3aM1tZWli5dyrFjx9i6deuckzi7d+9m5cqViMVi7Ozs5tTNKC0tpaqqSittIBcZsj+tgK/Si7WN3czYkOvjg7htaTghnk58+eWXJCcn4+rqel66Ml1dXZw8eZKNGzfOuW25rINXvknTxs9bmRqzdWkMWxZHz6rXrVKp2LdvH3fccceML5BaEPi+rIo30zKo69K4hQz19LghNJCNkaEk+8w94fzPf/6TqKgo4uLiZrwXtSBQ1d2Fr7UNjfX1fPLJJ4SGhhIaGoqfn9+comt79uwhLy9v1m36+/vZt28fycnJrF+/noyMDHoVcoyMjAn38dGeLyQkBHt7e5RKJTU1NQQFBQGwd+9e3n77bZYsWcKtt95KYmLiFJIeGB3jSFkV3xSVaStuASS4OvLMzdfNKS+QmprKSy+9xNNPP826deum16tp6+K9YzkcLqjQGBaCgBdD7Hr8AXw95k4Uq6ur4+677+bRRx9ly5YtqNXqObOJd+7cyfETJ1m9+R7ONA1S3NSljVv3crTh56tjuDkpVJssKJfLuf766/H392f58uXo6enNKhwGcOjQIV588UWSk5MpKSnhpZdeYtGiRbPu88gjj1BeXo6RkRH6+vq8/fbb+PnNXMKyrKyMbdu2IQgCRUVFbNy4kRdffBFnZ2ea2/sor2ujQto2vmynf3CUZf4ictIOMDAwQHl5OSMjI3AVE/xiND53V0EQWiat/xQQBEG4c4b9ngP+59z1kwn+P57/FwWVbdOe19nOgiAfJ4K8HQn2cSLI2wkLUwNSU1PJzMxk586djIyM8NBDD/H888/POPSUSqUcPHiQAwcOkJKSwooVK3jnnXcICZl+2C8IAt999x1ffvkl6enp9Pf3s2fPHtavXz/jM2ptbeW9997j8OHDDA0NccMNN2hU+ZRqDmaWsC+tgIb2s4qXge4OBNvqk3nwY5zsbTEzM+Pxxx+f8Zomnturr77KyZMneeKJJ7Ql9GYiYkEQ2P3FIf5x8gwqI40P1czIgLuSo7hnedy0iSwKhYK//e1v7N+/n1/+8pfcc889Mx5fqVZzQFLG39LSaR44myDmYGbKhsgQNkaEzEheqampfPHFF+Tn5xMfH8+tt97KsmXLZjzXwMAAmZmZlJaWUlpaSk1NDQqFAhcXFy0J29jYcPLkSbZv346trS0tLS0MDEzV+56M6upqPv30UzZs2MC6detQq9XnlXU7gdHRUYyMjKaQrkKlIr22ngOSMlIqaxhTauYaRECytwfbVy0h2v38imcrlUr09fWnJfaSpjbeS83hx+Jq7bplwT785/WLCXI7PxEwuHCl0qaOXj49XsChnAq6B4a16xODPfn56pgZ3ZJyufyCZCgmP9/pVE6nw4QSbE9Pz3lJ/srlclQqNe09wxzPltA7DOV17VTWtzM4PDZle319PX51RzJb1icilUpRq9UTImjzR/Aikehl4Kk5NgsRBKF80j73Mb8EP6cFD9A3OMKxjAKaO4aQdY1QWd9BY2vPuYcDwN7ajDB/F4K9HdAb6yIi0B1HRwfs7e2nrZYzGXl5eZSUlGBkZISlpSXXXXfdrA1GEAT++te/UldXh1Kp5J577plSYOFcdHd3c9ttt9HQ0EBERAT/+Mc/cHJyQhAETlc18dWpYlLOVCEff+HNjQ0Jdzbh/R3/hYmeiq+++mrWRCLQWPI7d+7k7bffZvXq1ezbt2/We1eqVPzlX5+zL7cMlakm1NFIrM/mxAjuXxE/benA4eFh3nnnHT7//HMefvhhHBwcuP7666clYEEQkMha+TS3gINF5Ux+JTwtzdiSEMfN4UE4ThN9o1arycvL4+uvv+bEiRP4+vpy6623IpFI+PWvfz1trdHJ521ra6OkpITS0lJSUlI4cOAAFhYWPPfcczz++OMXJK18sRAEgZLWdg5IyviupIKuobPE529vy8bIEDaEh+BidfEJO6frmngvNVcb8giwNtyfh1YnEOY+P1r9giCQW9HIJ8fOcKKoVmut21uZcUtyGBsXh+HhcHmTAH8qBoZGKapqobCiCUmljAppG8OjiinbGYj18fe0J8jbadzIdMTPw15Hr+qy+OBFIpEDMFdGSq0gCNqMnfl20UxznPOeZB0cHqOyvl07LKqoa6O+pZtzH4mRoZhwfxeig9yIDHIjIsB13v3cF4LpXC99Q6Mcyi7jsxOF2sIlIiDU1ZIQGxGP36c7cTcdGhsb2b17N8XFxTg5OfHmm2/OGekwMjJK8qY76XfwwdRVM+En1tfjlthQfrlq0bTJUkNDQ/z1r3/lv//7v7n55pv59NNPZ+1M5CoVaZU1/P1oGsU9AzDuqtETiVjs48nGyBDWBvnPGNNdUVHBRx99xI4dO3BxcWHPnj1zFneYQHV1NSMjI3h7e0/xTc8nWvoGOFhcxgFJGdWdZ0Nn7cxMWR8WxMbIEMKcLz61XhAEMirr+UdqDqfrNFEp+noibowO4qFVCfg5XXhC2vlgVK7kcE4Zn6QVUN3cqV2fHOrF7cujWBruc0WFyM4H7d0DFFY0az/VjR3TckmAp4OWyIO8nfB1t0M8R9Hwf6dJ1tcEQdg5vs4SaOciJ1kvBMOjcirr2ymuaqGgoglJhYz+Id0AHn09EQFejkQFuWk/dlYLs7CEWi2QWVbP3tR8MkvrtesD3OzZsiqGGxYFn3ds8Pn48Ce2q62t5fO0TM70qylu0URZ6IlE3BAVyEOrEwhw1g3vrK6u5ttvv6WoqAhHR0deeOGF84pgOJaewWuffUWD2Bi51VlL3NTQgNUBvsR4uBLp5kywk4NOrVeZTEZ1dTVKpRK1Ws3ixYtnlWm+3OgbGaVI1oZE1kqWtJEcaaM24sZIrM+aQD82RoawxNfrkoR4qtUCqaU1vJeao1V5NNDXZ1N8KA+sjMfDbn6s5raeAT49XshXp4roHX/PjA3FbEgK465V0fg4zzy6upIQBAGprFtL5gUVTbR0TOVddydrooLciAx0JdzfFS9X25/UUS04gheJRJ6ALXAL8Dtg2fhX1YIgDI5vUw78XhCEr8b/fwp4Gt0wyUiuYJikWi0glXWN/4iaH7O1c+rz9XC2YVG4J0mR3sSFemJmsnAs/AnUtXaz79gZDmaVatUtrc2MuW1ZJHesiJq3eOEzUhn/SM3RqgiCphTbHUkRLA26dJZZQ3cv3xSVcaCojIaePp3vDPT1CXV2INLVmUg3ZyJdnfGyndt3ejkgVyopb+tEImulsLkFSXMb0u6p7sMEL3c2RoZwfXAAFpcoeahveJTvzpSzP6uQmjbNyMDYQMztiRHctyIeJ6v5aROS2hb2puaTeqZaG/vvYmvJXaui2bQ4DIsFWFS7raufLImUrEIp+WWN9A3qUpKeSESAl8NZwy/QDXubS/P8FiLBf4CGqM/FKkEQ0sa3EYD7BUH4YPz/iUSnh9EkOp0CHhMEofICzjvvcfBtXf2Teu5maps6dYZiYn09IgPdSIr0IjHSmwBPxyuiEjkT+odG+TqjmP1phbSMF1sQ6+mxJjaALatiiPQ9v4m5C0VZczt7juXwQ1GV9nk5WJixaVEYty4Kw/MSWYmCIHCmqYX02nokslYkza30jky1D6yMjYgYJ/uo8eW5UsaXGoIgIO3uRdLcqr22srYOFOckYwF42lhpO6S1Qf64WV+a9iwIArm1TXyRU8zRIt25mi2Lo9m6NAbbeVB5VChV/JhfxSfHzlAsbdWujw1wY8uqGFZE+i0oN8yoXEFBeTNZEinZEil1zV063xsZiAnzdyEqyI3oIDfC/V0wM52frN0FR/BXClci0al/aJSC8iayJVKyJFKa23WtR1srUxIjvEmK9CYhwguby1wsYCYoVWqOS2r45NgZnSzAMC8n7lwVzXWxgdMWJrlYSDt6+Dy7iG9Ol9I9NKJdn+jvweZF4awJ97+k2tuCINDY00fhJFItbW2fkuEKYGpggKWJEVbGxliZGGuXliZGWE/8bWyEtYkxlibGmIjFDIzJ6RsZpW90dHw5plmOjNI/OkrvyCj9k9YpzslWBU2sf6SbM1GuztpO56dk8c6Gjv5BDpwu5cucEhq6JkVbudjzs4QINsSGzIuwXWffEF+eKuLzkxI6x6WyDcT63LAoiJ+viiHI4/wjceYTE26Xiff4TFkTY4qzUhF6IhFh/s4kRmje4xBfZwzm8J1fKlwj+HEshEzWxtYesiVSMiVS8ksbdaRJRSII9nEiMcKb5CgfIgJcF4R1X97YzifHznAktwLFuEVna2HK5mUR/GxZJA7z4L5RKFWkldXyRU4x6ZVSrVVvaWLEhtgQbksIJ8jl4svDTQe5SkVleyeFza0UNbdS2NxKbVf33DteAhjq6xPm4kjEpJGDh43VvLiLlCo1pyqkfJlbzPGyWm1inJmRITdFB7E5IZwwd6d5OXdRXQv70go4eroSpUrTqdlbmnL7iig2L4287FWRpsPwqJzsonqyCuvIlkhp7dINeXWwMScp0pukKG/iwzyxMr+0ne754hrBj2MhEPxkyBVKJJUy7TCvqqFD53s7azOWx/mzKiGA2GD3OWfT5xvd/cN8mV7E5ycktPdq6lCK9fRYHePPXaui502Fr6Wnn6/ySvgyt4TW3rMvWYSHM5sTwrkxKmhKIeNLjcExOd1Dw/SOjtI/MnaOVT5K36R1/SOj9I6OMiLzHHEnAAAgAElEQVRXYmlspLH2TYywNDbGetzytzQxmvS3Mdbj6+zNzXQmfOcDjV29fJVbwtd5JbT3n80fiPZy4WeJEVwXGTgvVZTkCiU/5Fey/1gBJfVnc08ifFy4a2U0a2MDLpvVOxP6h0Y5lV9DWm4V2ZJ6HSvd0ECf6CB3EiM1I25fd7sFMU9zjeDHsdAI/lx09gxqLAZJHZmFUp0kB0szY5bG+rFykT+JEd4YXUHlO4VKRVpBDfvSCjhTfdZ9E+zhyJ0ro7k+PmhelPlUajVZVQ18kVNMakmNdgLOxEDMkiBvlgZ5syTQS1s79v9nqFQqJBIJtra2WNvY0Ng3THplPacqpJyRnpXJtTEz4ZbYEOKcLEj95kuUSiWWlpZs37591rDXzz77jBMnTtDT08OGDRu4885pU1EAaO3uZ8eez8lr6GN03PNlINbnhvgg7lwZRaiXbiEVQRDo6OjQfhwdHQkNDT3vez/fiK4JdPUOceJ0NWm5VeSVNqJSnXWTuTlasSTGl6RIH2JD3DG+jCU0zxfXCH4cC53gJ0OhVJFX0kBabhUnTlfT03/WH21qbEBylA8rFwWwONr3ikblVDZ1sO9YAYdzy7QyrNZmxmxaEs7tK6JwsZ2f59w1OMw3p0v5MqeYug7dqBI/J1uWBnqzJMibOB+3K1ov81Liww8/RCQSERERQUhIyLT5AGq1msrKSo4eP8nrH+1HYe2EbWAEaoOz/nORCJIDvNicEE6glTGHvj3It99+y/Hjx1m1ahXvvfcerq7Ta+/IZDL+9a9/8fnnn1NUVMSuXbt48MEHpxCqIAgU1MjYm5rPsYJqxr0/mBuIuPfGxdy2NBybaaRAAJqbm1m7di3l5eVs3bqVd999d84w1d/97nd89tlniMVidu3axY033jjr9t99f4x/fPwdfSpzRjFHkwmigZ+7PSsX+bMyIRB/D3tEIhEjIyN8+OGHAOjr67N69epZ5QcAioqK6OnpwcLCAnNzc/z8/LSaQNNhbGwMqVSKo6PjeWXAgia7XKlUYm9vD9cIfirBT/T2Eynl55OgMnmfC01oOd9058lQqdVIKmSk5VWRlltF2yRfoKGBPovCvVgW68eyWD/srK9MzH3v4AhfZxTz2XGJNvpGTyRiRaQvtywOY3GY97zI7gqCQHFTG+kVUk5VSJE0tGoLooDGuo/382BxoCeLA7zwdbT9ScPqC7UKQUO2M71DSqWSP/3pTyxbtozly5djZGSEIAh0dnZSX19PQ0ODzrK7u5umpiakUimbN2/miSeeIDExkeHhYXJzczmZnkFaYSm9+qboO3sxJNb1B5saGpAU4MnSIG9cxSpOHj3CDz/8gJ2dHRs3bmT9+vWcOXOG1atXT7nPkZERDhw4wL/+9S/09PT4xS9+wZo1a2hqappSbL13cIQfTleyLyUP6aTYb18HM9aGe/DLzddPGw2jVCo5fPgw77//PgqFgqioKFxdXXnsscdmfO7t7e3s27ePL7/8EktLSzo7O9m/fz8eHh5TthUEgeqGDj797hRpuVUMyHXfwVA/Z1YuCmBlvD+eLmfj64eHh0lLS+Pw4cN8/PHH6Onp8eabb/Lzn/98xutqbm7m+PHj/Otf/+Lw4cOEhISwa9cu1qxZM+32KpWK0tJSsrOz+a//+i96enpYs2YN77zzDgEBAdPuMzAwQHFxsVaLaBzXCJ5zCP75558nJSUFZ2dnUlNT+e1vf8u2bdtmzOTs7+9n/fr12NraMjw8jLW1NTt27JjxhwD44IMP+Oijj1i0aBG5ubk8+uij/OxnP5uxgQiCwC233IKdnR0rV64kIyODV199FWtrawRBoKy2jbTcKo7lVulKKggCjpZ6JIS5EOppxW0bZ8/AnHh57rnnHmxtbbGzs5s1NV8qlXL//fdz9913c+edd6JWq7GwsNCxSiY0uPelFZBb0ahdb24sZkNyODcnhRLsMXNW5YQg1OrVq7nnnnum1Rk/F1999RXvvvsu69evZ/X1N9I0quZURT3pFVI6JmnUADhZmZPk70nhsSOMtjezJCqcZUsWk5iYqO2sJ7REJkMikfD4448jCALW1tZ4eXnh6empXR4+fJjY2FjWr1+vFfv605/+xLFjx2a87qysLARBYPPmzezYsQNLS0sefPDBKcf28vLS6tz4+Pjg4eFB3/AoVa2d7P32BwqbO+gRGSFX676vQS4OLA3yYkmQNzFerhiI9REEgd/+9rcsX76cdevWnVcCV2ZmJunp6dx9993TSgMrlCrSS6R8m1XKiaJa7aSpgb6I9Ymh3LUqhkD32SfEGxsb2bNnDw888ABeXl7n1aF++OGH6OnpceuttyISiTA0NNRJgBMEgar6DlJyKknNrtR5V/REIvzcrbh5ZQwrF/njZDf9SPP7778nJyeHm266ifb2duLj42dVxAT4/e9/j7u7OyEhIRQWFrJt27ZZE/OKi4vZvXs3CQkJFBYWsmbNGtavXz/r/b/++uu0t7fj5ubGoUOHOHLkCFwj+OldNIIgsG/fPt5//308PT2Ji4vjoYcemvVH6ezsZOvWrdqH/Pvf/35WnZixsTEOHjzIL37xC0xNTbn11lv5y1/+MiOhCoJAZWUlL7/8Mh988AEuLi7s3r2bTZs26WxT19xFWm41p/JrKK1tnfSdGktDOfffvo7rl4RNa9kLgkBubi4fffQRR44cQaFQsH//fpKSkma8j76+Pvbu3cu+ffvQ19fHwsKCjz76aMIHqIMaWSdfZ5RwMKOI/pGz0UImjPHLjau4OTkMh2mSZeRyOYcOHeKjjz5ieHiYLVu20N7ezkMPPTSjEFdvby+HDx/m66+/pq2tjTVr1uDu7s6QvhGmngFkVDWQX9esjeuegAgwE6lQ9XYiDHTjY2dNWW4Gv37gHn754IPTyuf29vbS0NCgY2V/+OGHtLW1sWrVKt5+++05fcZKpZLU1FSWLVuGicnM0RfDY3Jq2rqpauukurWLqlbN8tyOC8DWzISkAE+SA7xYEuiF4zwlIoGm7ZQ1tPNtVilH8iroHTzrQgxyd2B9UigbkkKxuswy3JNJPSWrgqa2syGfhgb6JEZ4szTWl2Wx/thaXflInYuFIAgMDAxc88HDlffBq9XqWX1w06G1tZWRkRH09PTQ19fH1dV1xmPIOvo4llPJVz/k0tR59oUTiSA6yJ3ViYGsWhQwJYNOpVLx2muvUV1dzfDwME8++SSxsbFz3svWrVs5ceIENjY27N27l/Dw8Gm3VarUZJZKOZBeRGpBFYg0pKknEpEU4sXNSSEzlkzr6Ojg73//O3/84x/x8fHhs88+Iy4ubtZrGx0dJSUlhWeeeYbCwkK2bNnC22+/jamFJfl1zWRW1VPU0EpVW5e2ePjUG1RhjorFESGEeDjj72SHg6UZFsZGmBkbYmFspPXtq9VqcnJyCA4OviAlSEEQGJErGBiVMzg6xsDoGE1dfVS1dVE9TuTNPTO/r242lvg72xHn487iQC8Cne3nPay2vXeQQzllfJdVRk3L2eQeO0tTbkoIYX1iyJzW+qWGIAhU1reTml1JSnalDqkbGYhJivJmTWIgS2L8FmQW+cXi2iTrOKYj+OLSZvr6h/H3dcLRwWJBhD1dCrR09JM6PjQtqTlb5Wkusv8pEASBsbGxOdU0AVrau/gmXcLRMzXUdpydSzA3NmRtbCA3J4UQ4++m8zvIZDKKi4vp7e1lZGSE22+/fU7XwsToZHR0FKVSiYODAxEREVO26Roc1lrFVa1dVLd1UtXaxYh8qrLfuTDQ18fc2FDzMTLC3NgQCxMjzIw0HYChWJ+hMTlDY3IGRuUMjY5pyXxoTM7gqFxnvmAmOFiY4edsR4CTHf7O9gQ42+HnaDfvoaETGJErOFZQzbdZZeSUny36bijWZ2WUHxuSQkkM8bqsmaZzkXpytA9rEgOveBDCpYRSqaK+sZvq2nbiY7yws9W8u9cIfhzTEfwLrxwk5XgZAJYWxvj7OhLg54S/ryP+vo54etgtqBTpn4LWzrNkX1ytS/YxwR6sTQpiVULAZc+ibWjv5bvsUr7LLkPWdbZdOttYsCzClxVRvsQHuM9LxuxsUKsFWnr7dQi/pq2L3uFRLTlfyldDX0+EubERFsaGOFqaE+BiT4CTPf7Odvg72WFtdvkTaHoGhjlVXMeJoloyS+sZnpSQF+3nyoakUNbGBlx2XZjapk6OZlbwY1aFjk99MqkvifFdUIquPwXDI3Jqatuprm2nqqaN6tp26uo7kY9Hqv3P07ewenkwcI3gtZiO4Pd8eJKMnGqkDV068a8TMDTQx8fbgQA/R/x9nQjwdcTP1wGTq7QBtXb2cyy3ipSsCh2y19cTER/mybrkYJbH+1/WEoZqtcCZ6ma+zS7lx/wqhka1itKYGhmQFOLF8khfloX7zBhadzmhVgsMy+Vaa3zwnOXAqMZqH1UoMTMy0HHrTGftGxuIr/jIURAEalu6OFFUywlJLZK6Fp1OzM3OkvVJoaxPDLnsmusNrT38mFnBj1nl1DaddQsZGYpZHOXD6quc1Lt7hqis1pB4dW0bVTXtNLf0TGtEmJoY4u/ryF0/S2BJoj9wjeC1mM0HL1cokdZ3UqXtNdupqW1neEQ+5Tj6eiJ8fRwICXIlLNiV0GAX3F1tF4SswIWgtbOflOwKjmZWUF53NrNQrK9HUpQ3a5OCWRZ7ef2Wo3IlORUNnJDUcrKolo6+s5OJIpEm63FFpC/LI3zxdVkYmYRXKxQqFflVzdpn3dSpq5MU5O7A8khfVkT6EuzhdFnbt6yjj5QsjaVeIW3XrjcQ65Mc5c3apCCWxvpddaQuVyipqmmntFym/bS2T8/JDnbmuh4FP0dcnKyn/A7XCH4cFzrJqlYLyFp7NT1rTTtVtW1U1bTR1T01gsHC3JiQIBfCgl0JCXIhJMgFS4sro03xU9DY2sOP4y9UTePZogpGBmIWR/uwLjmYxdE+lzWTT60WKG9s57ikhpNFdZQ3tut872ZnyfJxso/yc5uX7Nl/N3T3D5NVXs8JSS0ZJVIGJ42WDMX6LAryYHmEL8sifHG2vXxFSwA6egZJza7kaGb5lNHlonAvzegyzg+LyxyZ81MhCAKtbX2UlMsoLW+htFxGVW0bSqWup0AkAk93u3EyH/cU+DlifZ5RPtcIfhyXKoqmvXOAsnIZpRUySspbqKxuZWxMOWU7T3dbQoNdCQ1yISLMHW/P+Y9yuBSoa+rix6xyjmZV0NBy1s9pYmTA0lhfViUEsjjq8pI9aApAnCyq5URRHTnlDTohj2I9Pfzd7InwcSHCx5kIHxc8HReGpvuVglyhpLyxg2JpC8V1rRTVtdDcpfv+21qYsizCh+URviQGe152i7ijZ5C03CpSsyspqGjSuiVEIogL9WRtUhArF/ljvQBcc3NhTK6krEJGUWkzZRUaQu/pHZ6ynY216TgvaLghONAZ04uQEr5G8OOYrzBJpVJFrbSDkvFeurRcRpNsalEGK0sTosI9iIrwIDrCA19vhwVN+BMxxUezyvkxq0KnKo2xkZjFUb6sSgi4Iv7PkTEF2eUNHJfUkFEi1XHlTMDS1Igwbw3ZR3g7E+7jctnjsi8XBEGgubOPorpWiqUtFNW1UtHUoVX/nIBIBP6u9iyL0Ix8wr2dL3sbnJgHSs2upKhKpvNdZKAr65KDWZ0QeMWyss8Xo6MKSsplFBQ1UFjUSFlFi3YSdAJisR4Bvk4aQg/WjPCdnS6tOug1gh/H5YyD7+sf0fTiFTKKS5spKZMxOqYbemdhbkxkuDtR4RrC9/d1RH+BRuwIgkBpbSup2ZWk5lTqkP1ErPGqhECWxvhiPk+FDWa7traeQYrqWiiWaizVsoY2rTbOZHg6WhPh40KIpyNu9la42lnhZmd51fhyBUGgZ2CE5q4+ZF39NLT3jN9zq06y0QRszE0I9znbyYV6O0+r656SkqJNmHF0dCQsLGzW69i/fz9tbW2IxWLCwsJYsWLFrNv/7/99Sn5lO3UdCjoHdF0UEQGu2rBdZ3vNe5mXl0dPTw8qlQorK6s5i8339PQwOjqqlYZwcnKaNkntYjA8Iqe4tJmCokYKixopr2qZ4m6xtTEjKtydsBA3QoNd8fd1nHdhwGsEP44rmeikUKioqG6lsKiRgqJGikqbGBnRJXwzU0MiwjSEHxPlSaCf04IkfEEQqJC2cyxnagyygVifhAgvVicEsizO77JG40yGQqWiqqlTS/jFda3Ut08dVU3A2twENztLXO2ttEtXO0vc7KxwsbW4pKGaY2Nj6OnpzZgpPTgyRnNnH81d/ci6+pF19pF+WsLAmJphtR5y5dRoL9A8ex9HK+yMBUqy0sg5dhhvZztefPFFbrvttmk1ZnJycjh16hSffvopEomEm2++mZ07dxIYGDjl+AqFgoyMDI4cOcJXX31FTU0NTz/9NM8888y0ORANrT18fiiTHzLK6J3U94hEEB3szuqEQFbE++M4ydc/NjbGsWPHePnllzl+/Dg33XTTrOJnoEmG27FjB2+88Qa2trbs2rWLLVu2zCoFkpmZqc0KX7NmDW+99dYUCYLhETmS4kbOSBopKGqgvLKFyaJkAA72FkRHjI/Kwz1wd7PRnrekpIQdO3ZgY2ODt7c3jz766KwKnYIg8Oabb9Lf34+LiwuxsbFzJvXV1dWxd+9enn32WbhG8FMJ/uTJkwCEhISwd+9eHnjggRl1aEDTyA8cOEBcXBxDQ0PI5fI5sz6Li4sZHBwkJiaG/Px8YmJiEBsYUlXdph3eSUqaGBrWjdgxMzMkNsoLN2djbrwuAS+PmYWympqaaG5uJj4+HpVKhVwun/U+ADIyMggICMDBwQGFQoFYPHu43uDgIPn5+SxZskRrHQmCQHVjp5bs62VnC2Po64nwd7dmUZgH61fG4u02t9CXIAh8//33LFmy5LyF3Kqqqujo6CAuLg4jo5lHD31Do1rCT88vZkAu0DOsZGBkbMZ9QENI9hamoBrDzNgIC1NjLM1NsbY0x9zEmKb6Ovy8vXCws8HUyABTI0OMjcRICs5Q39iMUg0KNSgFzXJMriQ3vwBXD0+cXNywsrVDrhIYGhmjsa0LuXr2ZyQCHKzNcbWzRKwapa22lJ7GKhjuwcPNlbCwMAYHB+nv7+epp56aIr71xz/+kfT0dPT19UlISGDp0qWYm5szNDTEDTfcMOV8EomE559/ns7OThYvXswNN9yAtbU1enp6OsljCqUKSUUzGYV1HEzJo39SkrBIBAFuVmxcF8/K+IAp7peWlhaeeOIJmpubWb16NTExMXR2dvLAAw/M2Ga++OILdu/ejZmZGTfddBNpaWm88cYbODs7T7u9Uqlk+/btFBQU4Ofnh1Qq5d577+X+++9HT08PuUJJWXkLpwvryS9soLRcNiV0WlANkRDvz+oV0URHeODiPNXd8tprr3H06FFGR0fJz8/n7rvv5vnnn8fJyWna65JKpfzlL3+hrKyMpqYmOjs7efrpp9m2bduMCX2fffYZBw8eRCqVkpGRgUpTgewawXMOwaekpJCamopEIuHbb7/Fzs6OJ598km3btk1LMMPDw7z33nucPn2a06dPU1payu23386LL75IUFDQtOfNy8tj37595OfnU1FRgaGhIa+88gq33367tnGoVGpq6tq1w7/svGoU58zbWluZEB/jTWy0F3FRXjg7ndV/qa6u5m9/+xu5ubm4ubmRnZ3N7t27uemmmQXHPvnkE/bu3cvw8DBRUVHU1dXxzjvvzPiCdHZ28uqrr5Kenk5wcDCxsbF0dHTwzDPPaK3RuqYubVJVTVOnzv7GYjWR/o6YCr388bf/MW2NSqVSyc6dO0lJSUEkErF27VpuuOEGUlJS+NWvfjWt1VtSUsKHH37I6dOn0dPTIyEhgWXLlpGRkcG99947razrp59+Sk5ODqWlpQwMj2Jq64SrTyAV0mYc3X1w8Q6gc2CU5q4+bRHyywlLUyPcHaxxGx9RKAZ7iAz2I9DTVWdEUVdXx9jYGP7+/joqpbOJdRUWFhIcHDxrZzgZXV1djI2NTWtFt3cPkFlYR0ZBHbnFDQxPisrR19cjPtSDVeOW+myJdGNjYzQ3N+Pr63te1wRQX1+Pvb09ZmZmWtfMXAZEcXExoaGh6OnpMTQ0TGNzH/mFDeQX1iMpaZoSLOFgb0ZYsDOLE4PxcDMlONBrTrkRiUSCr68vZmZmlJeXExISMuv2fX19NDQ0EBISQlFRET4+PnNKXpSWlmJqaoq7uzsff/wx9913H1wj+JldNPX19WRnZ+Pt7Y2XlxeOjjMrHk7g4MGDtLW14eLigoeHBxEREbPuI5fL2bFjB2KxGFtbW9auXTvtUBg0+i1V1W38kJLPt4czUajMEdBtWK7O1sRGexIb5UVMpCe2Nhqr6J///Ce7du1CLBazdetWtm3bNqs/cnBwkIcffphPP/0UW1tb3nrrLe68885Zh7ilpaU8+eSTfP/998TGxvLhhx9O0aJpausl/UwtmYV15Jc26gp9CSoi/F1YnRxKcpQ3Xi5Trfu+vj5SU1PZv38/+/fvJyIigvfee4/ExMQZ72V4eJicnBzS0tJ46aWX0NfX54knnuAPf/jDrCOCvr4+JBIJ99xzD6ampoSFhfHiiy8SGBhI98AwzZ39DIyMMTImZ3hMwfCYgtExBUOjcrJy8jAxt8Dcyho1+prv5ArkShXGhmJMjQwwMTLExFCM6cTS2BATIwPtd5qlAbYWprjZW102GYILhVKpoqiqhYzCWjILpVSfU4XMxtKE5CgfkqN8SIr0XlAhjYIgUN/YxekCjYVeUNTA4KDu6M3G2pSYKE/ioryIjfLC1eXyJnX9FFzzwY/jSouNXQzG5EpKy2XkjzfOsgqZtobmBPx8HFgU6018jA+RYW4YzXMYY319PSMjI6hUKsRi8YwjGIDRMQWnSxv55sccCqvb6R3UnX9wdbBicbSGGOJCPXRCMMvKyigrK0MsFmNoaMjq1asxNJydALu6uqisrMTKygorKyvs7Ozm1Mo5Xyvw/zd09gySJZGSUVBHdpGUoUnJfyIRhPm5kBzlw+JoH4K8L29C1Fzo6R0i70w9eWek5ObXTclhMTM1JDpSYyTFRnni42V/1f3+1wh+HFczwZ+L4eExJCVNWmukulY3CcjQUExUuLuW8H29F07DFQSB+pZuMgulZBTUUlDerBPOp6+vR7C3E9HBbkQFuhEV5IbVVZQ0djVDEAQa23qRVDRTUNFEYYVMt+YAYG1hQmKkN4ujfEiM9FpQMepjciXFpc3k5teRmy+d9r2ICHUjLlpjoQf4O131WlPXCH4c/04Efy56+4Y5XVBPbr6UvPw6OroGdb63tTFjUaw3i2K9iYv21rpzFgKGR+XklTSQWSgls7CO1s6pbdTb1ZboYHcN4Qe74WJvuWA6rKsZSpWa6oYOCiuaKShvorCyme4+3eQckQhCfJxJHh9hhfg6oX+BstfzBUEQkDZ0aQm9sLhxih89wM+RRbE+LIrxJizU7YrWM54PXCP4cfw7E/xkTPgac/Ol5OZLKShqmNLo/X0dxwnfh/AF1OgFQaCls5/C8mYKKzWkI50UmTMBBxvzsxZ+sDs+ble/6uflwPConLKa1vFn20xxtYzhUV13mYFYn1A/Z6IC3YgOdiM8wPWKhbtOh96+4XGXi5S8M1I6zzFm7GzNWBTrQ3yMN/ExXtgs8ISpi8U1gh/HdATfIutBJBLhNE24078L5AolxSXN5I77IatqdIetRkZiosI9iI/RWPgLzQ/ZOzCMpEKmJaVyaduU8DUDsT7errb4etjj526Hn4cDvu72ONv/+2j8XwgUShX1sm5qmzqpaeykpqmT2sYuZB19U7Y1NzUiMtBVOzoK8XFeMB0+6LbfvDNSKqvbdL43NBQTHbFw2++lhEKhorG+EycXa8zMNFFQ1wh+HNMR/Gt/Osj33xViamqIt68j3r4O+Pg54uOnWVr+G5T1OheTJ55mt4C8Fpw7BzQTtiXVLRRUNFNYMb0VOgFTY0P8POzwdbfH190efw97fD3sL7v2/XxBrRaQdfRRO07iNY2d1DZ1Ut/SM638NUwd/fi62y0YlwucdbtMWOmFRY1TssD9fR2Jj/FiUawPEWHuC6pDuhRQqwXaWnupq+mgrqYdaW070poOGsdlzZ9/+XYWL9cENSw4gheJRM8A64FoQC4IwpyxSCKR6APg3nNWfy8IwtTMjJmPMYXg//zcV5xILZuScjwBW3tzfHwd8fE7S/7ePg4YGv17NKjz8WEuVHfOBNRqgdbO/rMEN0529bJubQHoc2FpboyjrQX21mY42JrjaGOOg60FDjbm2NuY4WBjgbWFyRWNCFEqVXT2DtHRM0hH96BmOfEZ/7+9e4CxGeL0zUwMdTo1X3d7/DzsFtSk6ATmcrss5Dmki0V/3zB1Ne1aMq+raUda18HI8FSpcgAzcyMe3X4d16+P0uy/AAn+eaAXcAcevACCdwLun7R6TBCEmfPPpx5jWh+8UqmiqaFb+3DratuR1rTT2jJ1OAugpy/C28cB/0BnAoJc8A9yxs/fCRPThRm7fCGYMwrBQJ/wUDdix6MQggKcF6zvW6FU0djao3FRjFu2NY2dNLdP/7ueC7G+HvY25jiME76ZqSFGhmKMDMQYG4o1fxsaaNcZja8zNNDHyFCMgVifMYWSMfk5n1nW9Q6MaAm8d2D4vCpHGRpo3FMat5Qdfh72+HnY42i7cN1Tg0NjFBY3cmY8a7SmTjeefiFHgV0MujoHqK5opaqyVbOsaKG9bXpOFov18PS2x8fXEW8/R61nwcFRN8BgwRG89gQi0X3AGxdA8NaCIGy6iPNd0CTr8NAY0roOpBO9a62mA+jvmyrqJBKBu6cdAUHO+Ae6jC+dMbdYOJNTPwXdPUOcLpg5jtjUxJCocHdixuOI/XwcF8+M8+AAACAASURBVFQc9HQYGVXQ3N6rYw139gzSPun/nv6pMq9XAmJ9Pe0ow8HGYnxpPt7xmONoa46Lg5VOJ9vX10d3dzdeXnNnXYImi/STTz7B1dUVDw8PPD09Z9VMUavV7Nq1C5FIhJ2dHTExMYSGhs56jvfee5/yqjYGR4wYHDFG1jqEepY8js62SqqqyjEzM8PFxYUbb7xxVoKvqakhJycHc3NzzMzMSExMnPUe5HI56enpWFlZYWlpiYODwwRJzoj6+nrEYvF551Q0NnRQXdlKg7R7nMxb6T5nZDIBZ1drfHwd8fKxx9ffCR8/R9w9bRGL5xZM+3ci+E2AHOgBUoFnBUHomm2/c44xK8GPjY3Nmb4tCAKdHQNUVbRSWS6jpqqN6opWOicVkJ4MF1dr/INcCApxISjEFf8gZ8zNr07S10TndGutrjOSBgYGR3W2sbI0ITrSk5hxAaarRQP/XCiUKrrOcY8Mj8p1LG65Qsng0ChqgSmWuFyhQqFUYThh2WstfH3aW1twcnTAzs56yojAwswYBxtzrC2MMRApUSmG+e7bb0lNTWXx4sUsWbKEFStW6GQmq9VqZDIZdXV11NXVUVlZyZ///GeMjIy4++67efnll7Gzs9NuLwgCzc3NFBYWIpFIKCws5PDhw4yNjfHoo4/y1FNPTZGrGBgYIDc3l8zMTLKzsykoKKC/v59nn32WX//611Pem9ExBd8fzeJoSj4V1V2MKU0QiXTJys3VejzByIuYSA8MxAKnTp3i2LFjpKamkpeXx89//nP+8pe/zCg2VldXR1paGj/88AP79u3D19eXN998k5tvvnn631WhID8/n5MnT/LKK6/Q2dnJ/fffz44dO2aU6Ojp6dFKfxw8eBB7e3tee+017rnnHkQikZYTKspkVJTJqCxrobJMNiVLFkBPT4SHl53O6N8/wAlDI30qKyt59tlnyc3NxdPTkyeffHJakbgJDA0NkZ2dzUMPPURtbS1c5QR/FzAM1AF+wA5gEEgWBGGqLqxmHyNgcsuzAJp0im6/8AJFRUWEhIRw6NAhbr/9dh599NEZLfz+/n5uu+02QkNDcXBwIDMzkxdeeAE/32CqK9uoqmjRDsFaZb3THEHAzEKPhKQQQiM8CA51xdffCcNz/Np33HEHnp6eJCcnk5eXx+bNm4mPj5/x+Rw/fpzdu3ezbNkyli1bxpEjR3jsscdmTc//7W9/C8DKlSuJjY3l1KlTOvo456K+vp7t27ezYsUKVq1ahYODA7mnKxlVmJFf2ICkuJGRcyY7DQ3AUDxEWLAL169NxN/XnrGxkRklGlQqFbfccguRkZEkJSWRnJyMUqmkr69vRk2PAwcO8P777xMVFUVUVBSRkZEYGBhQWVnJunXrpr2fBx54gN7eXnx8fLSfkZERiouLeeyxx3B0dNTZXiKRsH37dsRiMTY2NtpPWVkZzc3N3HfffWzZskWHSF966SVSU1OnnLu4uJiOjg7WrVvHrl27tBnA/f39bNy4EQADAwPtOQYHB0lNTWXr1q3ce++9OtbyRx99xP/+7//i6uqqvQ9vb28++OADHnzwQVasWDHl/jdt2oSJiYnO8zpx4gTLly/Hzc1tyvUeP36cl19+mYSEBJKSkkhMTKS0tJTg4GDs7e0BzcR9SZmM4rJmistkFJc2Igi657WxNiEhzlcrreHkePYdq6+v5+GHH2bJkiWsWrUKLy8vamtrWbly5bS/OcAbb7xBXl4eK1euZOXKlRw9epT7779/RutaqVRy0003ER4ezvLly5FKpSxZsoRFixbNeI4DBw6wZ88eEhMTsbe3RyqVsm3bb2hpGqSyTEZFWQsV5TK6O6da5iIRoDdEYLAr665PIiDIBR9/R0zOKYEpkUj4z//8T4KCglAqlXR3d/Pcc8/pCLmdixdeeIGsrCyCgoI4fPgwFRUVMJ8ELxKJXgaemmOzEEEQyiftcx/nSfDTnM8XqAHWCoKQMsM2zwH/c+76yQQvCALt7e1kZ2fzyCOP4O7uTlxcHM8999yMPfrY2BhlZWX8/e9/54MPPsDT05Nf/epX/OpXv9KxrPr7R6ipbKWiTEZhfi0VZTIG+qdGe4jFevj4O2mt/KAQV5xcLJBICsnIyODZZ59lZGREa425u7tPe13/r71zj2+sLvP/+6T3W3rLpUnatE3T21xgmBFwRIEBFZF5ra66zsgKwqoMOgj+uLkKKi6CsywivmBXBFR2wf2BC4oI/kZULssAMzAXmGmnt/SeNE3SNuktba7f3x8nSdtp0gsC03bO+/U6r3OanJOcnpzz+T7P832+z9dut/PKK6/w/PPP88gjj1BWVsaePXu47LLLkrrqoVCIQ4cO8dJLL7F3715efvllLrjgAu67776ULrfH4+Gll17ixRdf5I033uDw4cPs2rWLO+64A7W6kNb2QQ691cvbTf00tTjmddgKEWVizEFdjZYvffHv+MDmGrSauY2Q3+/n8OHDvP766+zfv5++vj6OHDnCVVddxW233TZPfEEuTXDs2LGEVXr06FEOHjzIhz/8YW6//fZ5QiGEYGRkJGH1dnd388ILL/CnP/2J0tJS7rzzTr7yla/Mu26hUAifz8fIyAher5eHH36Yvr4+PvShD7Ft2zbOPffcBUMJQgh+8YtfsGXLFjZt2rSkuLLX66WgoGBOMbGTSSQSpadvmOYWR0zQHTiSGDPx2uhnxEoBzC6lu1qYmgpiaxucY50POOZ3+8X75eoajTQ0GqltMFBl0ZKerlpSmCxOMBhctAzHiQwMDMQb5vdU4LVA6SK7dQkhEl3Df4vAx473IIdpfp7i/UUt+DjRaHRZPwTIQpSTk7Pkm1YIwfFmG8OeIN2dQ4kbZjTJtF7ZORk0NBqpqdNRUCQ4bVMV1ZZy8vLyFv2+np4eWlpaUKvVFBYWUlNTQ07OwkP9Dxw4wLFjx8jPz6eoqIiPfvSjiwrK3r17OXToEHl5eVRWVvLpT396zrmFQhHaO12xCU8cHDvuYMQ7f+YlvVbN+nVGNjSa2NBooqZaOyf2+Mc//pH9+/dTWlpKdXU127dvX/S32rdvHx0dHZSXl1NRUUFdXd2ix7S3t5OZmYnZbF7yvbBQ1ca1wqQ/QEubk6bjspgfbx2YV9oaoLpSw/rGmd9xtQm6EAJH/wjHm+y0NDtoaXLQ3eme11cAYCwvpr7RSMM6I3WNRqx1ZWRnv79TWMZZEzH4JMeWA33Ap4UQzyzxmBU3klUIgWtwVLYQjstWQkfbYNIUKYOxiHUbymnYYKJxvYmaWv2SOmFWCkIIBt1jCcFvanHQ2e2Z9wBlZqRRXaWVJyG26LBadNRUa/+meSsVFkcIwfDIBLYuNx2xieZtXW4cTu+8bJ6c7AzWNRgTgr6u3kjBKksomJyYpvX4AC3NDo432WltHmB8bH4CRammICbksodd12CgQL1yaiOtOIGXJMkMlAB/B9wEfCT2lk0IMRHbpxX4thDid5Ik5SOHWp4CBpFj8HchW+QbhRALz9gw870rTuCTEYlE6esZilkRsjXR2z00b7/MzHTqGg00rpcFf93Gcko1S5skY6XgnwrS2uZMxG6bWx1JO6dA7pSzWvQJ0bdadGhK81eVlbhSCEei2B0jdMREPL74RpNnDxnKCtnQaGJ9o4kNjUaqq7QrNj02GdGooK9nKGGdtzY56O3xzGu4MjPTqW0oY92Gcvm52mBCo125WgErU+AfYf6gJYBtQoiXYvsI4EohxCOSJOUATwNnAEXAAPA88F0hhCvJ56T63lUh8MmYGJ+m9bgj4Tq2NDuYGJ+et5++rJB1G8tZt7Gc9RvLsdToSUtfXQ+i0+VLWI8dMeHxDCXPUipU52C16KgwlWAyFmEyFGEyFmMoK1pxA7JOBmPjUzicPhwDXgacPhxOHz19Q3T1DBFMMkAqTSVhrihNNKDxpWiVjeienAzQdtxB8zE7x4/Joj6ZxHAoMxaxboOJxvXlNG4wYbHqychYPV4xrECBP1msZoE/kWj0xHihnZ6u+eGO7OwM6tcZadxgYv3GCtZtMK3K8gu+UT+d3R5sXa6E1dnXPzyvJv5stJoCWfBjom+MrU2GoqSzSa1GhBCMeCfniLjdGRPzAd+8NNbZ5ORkYK2eK+TVlZr3fB6BdxshBAMOL8eP2TneJAt6d6d7nnWenZ1BXaNBts43lNO43khxycLTWq4GFIGPsZYEPhmzrZaWJjmumMxqKTeXsG5DubxsLMdcpVmRk3svRiAYpqd3CFu3G8eAjwGnNyF0yToBZ1OQn01xUS5FhbkUF+VSGFsXF85sx98ryM9+X69PMBTGNzqFb9SPz+fHN+rHG1sntn1+fGN+RryT8zKVTqS0JA+ToTjRuFWUl2C16DCWFa3KMQrT0yHaWwY43uSQrfMmO74kiQplBtmbbdywOr3ZpaIIfIy1LvAnEo0K+nuHEm7q8WN2+vvmjwvLzc2kfp1xTtxxNVr5cYQQjI7JoYkBpxfHgByacMQsW28SMVgIlUpCXZBDTk4GGRlpZGbI5QgyMtLIyEgnMzO2jr2WmZFORkYaaWkqwuEIwfjAp9gSDIVj6wihUJhgUF6HQhEmJgNMTC6pS2nO+el1aoxlMx6KySB7LEZDITkrdPq/pRC3zuMGS2uzg06bi2hkrjZlZKRRW29IhCfXbTCtuv6od4oi8DFONYFPxtion5amuJVvp7VlgOmp+bn5poqSmdjkehPVNbo1Y/34/QFcnvGERezz+fHO3p5lLY8l6ed4P0hTSfM8iaLCXIpiXkZiuygXnUa96uLGqfBPBmhrGZjT15QsjbhUU0DjBhPrYta5tb5s3mDBUwVF4GMkE/hDr7bjGRilokaL2aKjoGj1Wq7vhEgkSk+Xh5ZmOazT0uRIauXH45cN60yxwVgGdGu4hn6ccDjC6JgcLpmaDiWs8FAoTCA4sx2cs5at9kgkOseyz8hIIzMzPWblz7b6Z/bJy8uiuCiX/LzsVRk+WQ6RcJSebk9sPIgs6sn6keLWedy7bNxgmldwa60TjUbxOEexd3vo63TzoY+uR28qBt65wJ8SzeHzTx3if//f0cTfRaX5lFdrMddoqbDoqKjRUlGtQ2tYm2KWlqaiplZPTa2e7Z/eAsgjcFubZzJ2Wo/LGQhHj/Rx9Ehf4tii4rzE6Nu6mOgXraEyrgDp6WmUluRT+i51xi13YFQ0GqW/v5/KysolH/PKK68QiUTYtGkTRUWLDzHp6Ojgscceo66ujvr6ejZs2LBgMa2pqSm++c1vUlxcjNls5pJLLlnw/IQQfOc7t2DvHyY3S4+2tJrxUYnO9sGk/Qf6skLyCwWeYRs6Qw5nb13P5Zd/ccHBZw6Hg+9///totVoMBgOXXXYZxcXFC57T3XffzfT0NHq9nrPPPpvTTz895f4Ar7/+Oq+//joGgwGj0cg555yz4GDAYDDIr371KwwGAxUVFVRXVy/6e7z22n662uxkUEBwQsLZ76W/001/t4fALE+7WJOP3lRMMBjkz3/+84KfmYpTQuBLDNms31KB2zGGZ3AU3/AEvuEJmg52z9kvOzeT8ioN5dVasvIFZ5zdiLGyBI/PwYc+tHXB73A6nahUKvR6PZ2dnZSWli76Q7e1tVFdXU1mZiadnZ1YLJYFhcHr9eL3+xO1RAYHB1OWWojT1dWFwWAgJyeHYDBIOBwmNzcXtTqHs7ZaOWurFZiJ5R890sORw504HRN029z4vJMceM3Ggddsic/U6dXUNhioazBQW28gXy0oM5QmHrapqalFR9U2NzdjtVoTBazGx8cXrKnjcrkYHx+nqqoq8cA5HA6MRmPKaxYftWswGBJi9sorr3DmmWcmFbfR0VGam5spKChArVZTUFBAdnY29957L5/5zGdoaGiYd0xTUxMu19ws3tdee42DBw+yfft2tm/fjsFgSLwXDAZ5/vnnGR8fZ3x8nLGxMcbHx/nZz35GTk4O27Zt49Zbb8VqtSaOaW9v58CBAwwMDCSW48ePc/z4ccxmM3v27GHnzp1zrsPvf/97WlpasNlsdHd3Ew6H2bdvH2eccQbf+ta3OOOMM+acs8Ph4JlnnuHYsWO0tbURDodpb2/HbDZz2223YTab5+wfjQqe+p/neOGvb9LV4SYUyEYSBaikCgC6cCT2zc3Loq7BQJWlBKe7nX57M929bTAEb731FjfffDP/8A+fTSruBw8e5LnnnuPNN9/E5/Nx7Ngxzj33XP7xH/8xqbhHIhEee+wxDh48SFNTE729vUxMTPDd7343ZZ2jzs5Ofv/733PkyBFsNhv79+/nYx/7GHfccUdKcX/uued44403OHr0KC+//DKBQIBvfOMbidpPANNTQfq7PPR2uGg92s3hN44zPDhBYBIkkjdkaekqjJUazBYtA65+7r33Jdrb2/n1r3+ddP/FOCUEPs8wif3Q/zIoBmkZaiNLpebvLt7B+rrNDDkn6O9yM9A3zLQ/iO34ALbjAwD86QnZ6o9EQ6iyHmfDZitnnbMRs1VPpVWP3lSUuCmPHTvGww8/jMvlYnR0lN7eXm644QauvfbalH0Bv/zlLzl48CDhcBin04lWq+XOO+/kvPPOS7p/X18fP/rRj3A4HBQXF3PkyBE+85nPcOutt6LVapMe84c//IFnn32WYDBIfX09e/fu5Xvf+x5XXHHFnJtXpZKorNaSlRPhyd/fT1dXF/n5akyaGtpbnZz1gQsZ8wn6e4dxu8Zwu8Z49eW2mS9SBYmIUXLzBDn5Eby+fm6/4zts3frBeecUjUb5+c9/TktLC4FAAIPBwNTUFH6/nx/84Aecc845847p7u7mwQcfpLe3l3A4TF5eHkNDQ4TDYW6++WY+97nPzXsY//rXv3L06FGcTifT03KcvbW1lVAoxD/90z9x9dVXY7FYEvv7fD6effbZOcI7Pj7Oyy+/zK233sqVV17Jnj175lzrjo4Ompub53xva2srBw4cQKvVotfrueSSSxL1i6LRKK2trYlGxGg0olar6ejo4Oyzz2bHjh3zGu2RkRFCoRCnn346F198MQaDgY6ODjweD5dccklSEfJ6vZx++ul89rOfpaqqikgkwmuvvca2bduSNojT09MUFxeze/du6urqyMjI4OjRo2zcuJEpf5CWJrnjs9vmptPmoqfTjd8fBDIAE2kAEmRkpmGtLYt5fHK4z1RRgkol4fP5eO45L5dfeRNWq5WRkRGEECnv3fh5nXPOOVx33XUUFRVx+PBhNm/enHJ/lUqFEIIvf/nLrF+/nqNHj1JXV7eg8TA1NYXFYuGzn/0s+fn5HD16lG3btqXcH+QSJtu2bePaa6/lz8//FWvlesZHQjz76CF6bS56bS4G+73MD4GrkID0TBXVdQbMNTo5imDRYq7RUVZeQnqsr2Xv3r3kFFfy8Y9/HJPJxK233rrgOSXjlIjBxwkGg9xzzz2YTCZqa2tpaGhIWNnhUARn/zD9XR45BmZzJ36oUDBpIUuycjIw1+ioqi3DbNVRXV9GdZ2Bp55+nObmZioqKti0aRMXXnjhguccDAbZtWsXkiRhNBq59NJLF6293dLSwq5du9BqtdTW1nLTTTfNqXJ4IpFIhF//+tfcfffd6HQ6zj//fG666aYFSyePjY1x55138tvf/hadTsdll13GpV+4jC6bm/ZWuZRyR9sgjv75E2YDCCJodLls+UA9Fqsei1WHxaqncFYfiBCCwcFBbrzxRjo6OjCbzXz5y1/m4osvXvD/n5iY4IYbbmB8fJz6+nouuugiPvjB+Y3JnPMRgh/+8IdotVo2b97Mxo0bF/U0xsbGePnllznnnHMoKSlZcN84Xq+XwsLCZdc9OtlEowKX0zdHyLtt7qSFt0AWc0uNntoGuSxuXb2BqhrtqiqtsRwikSjOvmG62wbp6Rikt8NFr83NQO9Q0lo2AOriPCqtOiqtesw1OsotWsqrtWj0y+tfUDpZY7zbWTSRSBSXfYTeWYLf2+Giv8tDOJRc+AtL8qiuK5MFv95AdZ3cAGSussElS8U/GaC7y02XzU2XzUWXTZ40JdV0ZCWafKqqtVRUllJeUUq5uYRycyk6feGa73RcCQQDYRz2Eez9Izj6hunvG5ZnO+tK/ZuVagoSDXR8vdTJKlYjY95JutsH6W6LLe1O+mxuAinmBc5XZ1NZW0alVYfZqqeqVvbyi0rfnX4dReBjvF9pkpFwBGf/CL0dLno6ZOHvaR/E0ZO8NVepJExVGqrryqiqN1BdX4al3rBmO3ZPtAa7Ot10dbhSWoMgW4Smclnsy80llFeUUhHbXs05+yeDaFTgcY1i7x+hv28YR19s3T+Ma3A05TSBGZlpVFXL8xLX1MqzDp3oda0lwqEI/d0eutucsmUeE/Vhd3INzcrOoKpOT1VtGZUxEa+s1VOifW+nTlQEPsbJzoMPTIfo63Qnbpj4eizFwJv8whws9QYsDTOLuUZHxhrN9/VPBujp8tDXO4S9bwR73zD2/mEG7F5CKTwigAJ1DvqyQrQ6NRpdATqdGo1OjVavRqstQKNVr5lJ0hdDCIF/MoDbNcaQZxyPaxSPZxyPawyPW15cztGkdWji5OVnUWEuxVQRa1ArSqiy6Kgwl66ZsRAnMjE2RVerk+42J52tTrpanPTaXCk98bKKknmeeFlFyUkZEa4IfIyTLfDJEEIw4hmfEf32QbpbnfR3e4iEo/P2T89Io8KixdJgoKbBSHWDAUt9Geo1lp44m0gkintwNBEuiAu/vX8ET4oJi0+kqChXFv1YI1CqKUCtziG/IJsCdTYFBTnyWp1DXv7KykEPhSJMjE8xPjbN+PgUE2PTjI3Jf4+N+Rn2jM8IunssZShlNunpKozlJZRXzPWKys2lFBXnrknPEWLluR1eulqdcxZXCu8xJy8r1n8mC7mlvozKujJy81ZOLSNF4GOsRIFPRTAYpr/TTVerk86WAbrbBulqHWBiLPnISq2hkOp6AzUNBiyNRiwNBsrKi1ddZ95ymZoK4nR48bjGcLvHGHLHLVVZ7DyusQWt1WRIEuTnZ8fEX24E1OocMrPkQUrpGWmkp6fNbKepSI8NVEpPj7+vipUqiBKOzc8aDsWWSOy1UIRwOLaOve6fDDAxPs342BTjsXWykcaLUVCQjVavRqOd8WS0etmz0ZcVUlZWtGat8TjBYJi+Dhedccu8RV5PphidrDMWzfKW5WdodjbcSkUR+BirSeCTIYTAPeCbc7N2tjoZTJGpErc+auI3bKOBSquerJM088zJQAjB+NjUrJCF3AB4RyYSAjpbUN+JmL4fSBLk5WdTUJBNvjqHgljjU6DORqMpmBWSkj2UE+f9XOuMeidnLPKWAbraBunvcqf0giut+jmhz+p6AwWFK2cSj+WgCHyM1S7wqZicmKa71UlXmxw77Gp10tPhIpTEclWpJMqrtVgaDYn4fnVdGcXvcUfQaiFVOGRifJpgMDzLGp9rmc+20EPhCJFwlHA4Ilv2Mas+vp67rZrZTk8jJyeTAvWM9xAX9Ly8rFVZ8fPdZnY6oizospgPDY4m3b+gMAdLozFm5MhLhUWXyCdfCygCH2OtCnwyIuEI9u4hOlsHYlaNbO2PJZkTFeSc3JlYo5zFs5bTNxVWPsnSEXs7XARTlEc2mEuwNMwS80bjsnPKVyOKwMc4lQQ+GfEO3c6WgYQ7u2j6ZrV2JlugroyqujJ0xqI1/9AovHvEdSTVPRMKhrH3DCXSELvbnHS3DzKcogM9KyeDqlo5HbGmUQ49VtXqyStYnSGWvxVF4GMsJvDLLQS1Vlhu+mZObibm2KCNSqs+MRpPs4qrS76T397r9VJUtLzG7rXXXmPz5s0LFvOaTby0wQUXXEBFRcWi+0ciEf75n/85UUBry5Yt5OYmz1MXQuD1enn44YfZt28fp512GmeddRbbt29P2rEohGBoaIhXX32Vm2++mbq6OjZt2sT111+fciTvxMQEra2tXHXVVYRDEayV6/nUJ3eQFs2LDQ50ywZGZH6sHCCqmsJUXcr5Hz8bS4MxaTri2NgYL774Il//+tcxmUycddZZ3HbbbWg0mpT/d19fH7t378blclFZWck3vvGNlGVAAAKBAE8++ST33nsvVVVVnHnmmVx77bUL/o4+n4+vfOUrpKWlYbFY2Llz54IFzYQQPPPMMzz66KPU1tayfv16duzYQUZGai86HA6ze/duHnzwQVCqSc7np/fch9PlwFpr5aWXXuK0005j165d8RZxHvFh8HV1dVgsFh577DGuu+46PvKRj6R80J9++mkOHDhAY2MjoVCInp4errvuupQ3IMCNN96IwWBg/fr1NDU1UVNTw6c+9amUPfpvvvkmv/nNb9i4cSMbNmzgj3/8I5///Oepq6tL+R1333034XCYjRs3UldXx6G2P7N7927UanXS9M2Opn7sPUNM+YO0HbXTdtQ+5/MSwl8jD7021+g4dPRV7K4u1q1bR2NjIwMDA0iSxCc/+cmk10sIwa5du6iqqsJqtWK1Wtm/fz9Wq5WPfexjSY/5y1/+wu9+9zsqKioSy+OPP85FF13E9u3bE7VeZvPtb3+biYkJNBoNGo2G3NxcHnroIa688kp27tw5rz5JS0sLt99+O7m5ueTl5ZGXl0d3dzf79+9n586d7Ny5k9NOO23O+f3kJz9h3759cz5n//79+Hw+PvGJT3DbbbexcePGxHvj4+N84QtfwO/3J6zeUCjEq6++ilqt5uabb+bGG2+cU0Li8ccf58EHH5xjJXd0dBCJRLjyyisxmUxzauoA7NixI1EErbi4GCEEhw8f5oMf/CBbt26dd4/t27ePW265BQCtVovVaiUcDrN582auueaaeeIemA5x43XfofmtTrJUhRRkadGFz0WKZDPVKfH4ffvn/R7ZuZlMhoYQ6X5KDbmoNZm0db3F93/w3ZQ1cu6//35+85vfUFBQwMaNG9Fo2RZVNwAAESRJREFUNHz+85/na1/7Gnl589OGo9EoF110EcFgELPZTCQSwWQyccstt8wrsBbn2Wef5a677iIrK4uamhrsdjs7duzg6quvTinuX/3qV2lvb0etVtPX14dGo+Hqq6/mtNNOS7p/U1MT11xzDQBqtZoXXniByspKzj///KTi7p+Y5sc/vpu/vPBnVCoVPT09ST93MU4JC/5fd/+Cl397iLyiTAa9diYjo+jNJXzpq5eyddtZGCo1ZM7KOolEIthsNtrb23nzzTe5/fbbKSws5IorruBf/uVfknoGo6OjNDU10draypNPPsnevXvR6XTcddddXH755UlvXpvNRnNzM83NzTz00EP09PSwZcsWHnjgAT7wgQ/M29/v93Ps2LHE8sADDxAOh/niF7/Inj175lQtjONwOHj77bc5duwYBw4c4He/+x3FxcXccMMNXH/99fNqsYRCIY4dbWL/vsMcO2Sj9WgPPs80xQUGstMKEcmNMNLSJXLV6Yj0aRzuLvqdNgyVJdz07Wv53I5Pzfn/hRB0dXVhs9kSy5NPPondbuess87iP/7jP9iyZcu8/727u5u+vj76+/vp7+/npz/9KYFAgEsuuYSf/OQn88rZut1u3G43Ho+HoaEh2tvbueOOO9iyZQsXXngh119//ZzfMhQKMTQ0hN/vZ3JyEr/fzzPPPMNbb73Ftm3buOCCCzjjjDPmiOPExASh0NysnHvvvReLxcInP/nJeYW0hBD4/X5ycnISn9Pb28vhw4e5+OKLkwpKMBgkPT19zvceOnSI008/PWW1w1AoNEc4hoaGKCwsTGkphsNh0tLSEr9TNBplfHyCCW8QR88Qjt4hed0zxEDvMG6nL+nngJzZZa7RJry/qlo9ZqueEm0+kiQl/o9AILBgLSSQn8XZjfdSKpXOPmZiYoL8/IXLBUQiEVQqFZIkMT09TTQaTekRxQmHw4lr73Q6kz57J35H/H/3+XxEo1EyVFkMdHtwdnsY6PEw0O3G2e3B2TOE1zPG9/5zF1s/sQmAAwcOxGstKSEaThD4W3fex6EXjy90DBpjEcYqLYZqHcZqLUaLjnKLjuyidFRpEhqNZsluemdnJ3l5eej1+iUdI4TgwIEDmEwmjEZjUmv0REZHRzl06BBms5mKiopFH5T4eXV1dSWOWewmBrlcq8/no7KyEkOZkRH3JH2dbvpsLnnd6V6wLg/IVr+xUoOpSoOpshRTlQaDuRSdsSgxxPvFF1/EbDbPKQe8EIFAgI6ODurr6xd0b2czOTlJZmbmkvc/FQgGw3gGfLgHfDh6hxiYJeSDDm/SFMQ4+epszNYZTy7u2Z0KnZ5LZdw3iaPTjaPLzUC3e46gj6dIhoiz+193sv0KOaSkxOBjJBN4IQRe91jiAscvsrPXg6PLjX+BKdskSUJXXoKpRofJosdUo6O8Ro/JokNbfnKGLa80IpEoQ4Oj2Hs8DPQMz1h7vUO47N6UlfZAzlfWlBWiMxShM8aXYnTGIvTGIjSGolN2mrZ3g8nxadwDXtwxEZcXL67YtndofMHjs7IzMFVpMMYaZlO8oa7SoC5au6Nhl0NgKshAtwdHlysh5o5OF/YuN2PDEwseW1pWiKFah6FSIxuWMQPTUKUlTz3jqSgCH2O5WTRCCMZGJmX3qMeTaAAGutzYO11Mjk2lPDY9Mx1jtRaTRYepRk95jZ6KWnmtfpdmB1rthIJhXA4v9p4TrEO7F8/gaMqOtziSJFGsyUdnLKJYU0CxJp+i0nyKS/Mp0hQktou1BeTkZp4SghONRhnz+fENTeCNTV4ze9s7NM6wewz3gC/liM7ZZOVkoDMWYzSXzvGyjJUaSnUFK36U5/tBNBrF4/Bi73Rh7xjE3jkj5m578kGIcTTGYkyxqICxOhYlqNJiqNSQvcRyCIrAx3g30ySFEIwOTyRaZPkHdWG3uRjo8RBKkasLoC7Np8Iqi315bRkV1jLKrXrKzKWkrdESq8slEoky4h6LWZPeORZmfDtVedZkZGVnUFQaawA0+aiL8sgryCI3P5vc/Cxy8+R1XkE2uXmzXs/PJjs38331xkLBMFOTAfyTASbHp/FPBPBPTOOfDMxsTwTwT04zOTaNb0QWcK9nglHv5KIN42wKCnMSXpHsGcW2TfJascRnmJ4MYO9yY7cNYre56E+IuWvOdHonkl+YkzDyEgafVY+xSrtkEV8IReBjvG/lgiNRhhxe7DG3zG5zya27bRDPAiVx0zPS5Ph+jV5uAGrLqKiVxf9UzfFNhRCCUe8k7gEfnlg4YcZKja1jf08vofjWYuTkZpKRmU5aujz6NC1dHnmalqYiLUMl16BJT0OVpiI9Xa5No1JJRCJRwuEokVjdmUg4Kr8WihAJR+T3IlF5OxQhMB1KOZBnORQU5lCkKZC9mVijNtujiQt5zgoqmrUSiIds+zsGZxbb8p7d8piAx0O36pI8pVzw+8FKGOiUsAJirX9/h2wNOLoWtgJKDUVUWPVUJES/DHNdGSX61Zt7/n4x7Q/KYj+rERj1+mUrOWYZJyzlybjFLL+3UAfxe01WTsYCnsWM15HwTErzKdLkU1ict2ZLSr9bhEMRBns99He4YiI+mHgWFwq9rkTvWxH4GCtB4FORiOPNcv362+Ubz5tiggGAnPxsWfjrYjdbbRnlNXqM1VrlIX8XCAbDciMwPk0oGIlZ5JGYRT7X+g6Ho0Rj74fDEaIRIVeVTJ9l8cf+lrdjVSdnrbOyM2QRz8tUwnXvApNjU/IzZRvEHrPG+22DOLtTZ3epVBJllZqE91xRa1jR/WcrTuAlSaoCvgtcAJQBA8BjwB1CiJT+tCRJ2cCPgZ1AFvAn4OtCCFeqY044fsUK/EKM+yblMI/NRV9H7EbtGMTZm3oEoCpNhaFSE7tBZYu/3CqHflbiTaqg8E450Tjqtw3isLnot7kYcSUvQgaQlZMpC7e1DHNtWcwi12O0rK4aTCtR4D8B7AD+L2ADNgAPAY8KIW5c4LifAZcAVwCjwP1AVAhxzhK/d1UKfCqCgRDOniH6O5z0tw/G4vzyjT01kTpDYrabaZrV8WOo0qyqG1vh1GJi1B9LaphJaFhKJ2eJvjDRp1VeM+Ptaowrv9b7UlhxAp/0yyTpJuBrQghLivcLAQ9wqRDiydhrDUALsFUIMX/88/zPWFMCnwohBCOu0URMMeGi2lwLpm2pVPG8/hnRN9XoKLfo0ZiKlbz+VUAoFCI9PX1Z/TJOp5OysrIlHzM4OEg4HMZkMi3pmEgkwptvvkl9fT3FxcUL7huYCuLs8fD6SwcZHhhj2hfB2TOEo9PF6AJ542kZaZSUFVDVYMKyrkL2WmOdnbNzxmdz+PBhtFotJpNpSULvcrkYHx9f8oA7gLa2NiorK5dce2h4eJiMjIxl6VNnZydWqxVWeC2aQmChpNEtQAbwl/gLQohWSZL6gK3APIGXJCkLOZQTpwDkFi/O1NQU2dnZSJLE22+/TX19/YI/hhCCQCBAdnY2Qgiamprm1BNJRiAQIDNTzsN2u91kZGQseqNPT08nzqOvr4/y8vIFb8JQKIRKpUqMdPX4BrGcbsRyunHu504GcPR4GOhy0dfuxN0/grPHg6PbzfREAHuPE3uPE/469/PTs9KprDfwwyeuITNTnkxieHgYtVq94OjP6elpsrKyEkIwMDCAwWBYUBj8fv+ckbSdnZ3U1NQs+B0qlSpxXlNTU3R0dKSs/QHyPZCbm5t4UFtbW5mammLTpk1Jzy0YDDIxMUF2djbZ2dmoVCqeeOIJTCYTW7duTTrC2OfzEQgEEn+Pjo5y//338/GPf5xt27bNq5cSjUZxOBwEAgGmpqYIBAI88cQTDA8Pc/7553PeeefNK7kwOjrK4OAgPp+P0dFRHA4HP/zhD9myZQtbt27lS1/60rxaMT09PbhcLlwuF263m//+7//GZrNxxhln8M1vfpNt27bN2X9ycpKOjg4cDgd2u52mpib+67/+i6KiIq666qqkZS3sdjs2m42enh56enp49NFHGRoa4sMf/jD33HMP9fX1c/ZvPdzFXV//JaNDk7CAXVmsU2Oo1mKs0qLW5RKUJhkLjODw9PDQw3cRPRTl0ksv5fadt6PRlBAhxNiYbN0LIWhpaaGjowObzcZTTz1Fc3MzFouFH//4x1xwwQXzvm90dJTjx4/T2dnJkSNHePjhh8nIyOBrX/sa3/rWt5KWOuju7sZms9HZ2cmDDz5IZ2cnmzdv5q677uLMM8+ct//09DStra2J2kYPPPAAZWVlXH755UmvLciNjc1mo7e3l3/7t39LfcEWQgjxviyAFTnk8tUF9rkUCCR5/Q3gX1Mccxvy7aIsyqIsyrLWF9NydHfZFrwkSXuAby2yW6MQonXWMSZgL/A/QoiHlvudi/Aj4J4TXithxlMoAOxAObDwuGwFUK7XclCu1fJQrtfSSXatCpCTVZbMOwnR/Bh4ZJF9uuIbkiQZgReB14CrFjluEMiUJKlICDG7XJ0+9t48hBABIHDCy4n4zCxXfHw5satTFeV6LR3lWi0P5XotnRTXatnXbNkCL4TwIHeELkrMcn8ROARcKUSqYrMJDgEh4ELgqdhn1ANm4PXlnquCgoLCqcx7ljIRE/eXgD7gRkArSVKZJElls/eRJKlVkqSzAIQQo8AvgHskSdomSdIW4FfA60vJoFFQUFBQmOG9zKL5GHLHqhU5ljSbuP+RAdQDswuT/x8gimzBJwY6/Q3nEQB+wPwwjkJylOu1dJRrtTyU67V03pVrteZKFSgoKCgoyCijWhQUFBTWKIrAKygoKKxRFIFXUFBQWKMoAq+goKCwRjmlBF6SpCpJkn4hSVK3JElTkiR1SpL0A0mSMk/2ua1EJEm6RZKk1yRJ8kuS5Fv8iFMLSZJ2S5LUI0nStCRJB+LpvgpzkSTpXEmS/iBJ0oAkSUKSpE+f7HNaqUiS9G1Jkt6UJGlckiS3JElPx8YCvSNOKYEHGpD/513AeuSUzKuBO0/mSa1gMoH/AX52sk9kpSFJ0g7kEhk/ADYDbwN/kiRJd1JPbGWSh3x9dp/sE1kFnAf8O/BB5FTzDOB5SZLyFjwqBad8muRiJYwVQJKkK4B7hRBFJ/tcVgqSJB0A3hRCXBP7WwX0A/cJIfac1JNbwUiSJIC/F0I8fbLPZTUgSZIWcAPnCSH+d7nHn2oWfDIWK2GsoDCHWEhvC3PLWkdjf289WeelsCYpjK3fkUad0gIvSZIV+Abw85N9LgqrCg2QBpw4jaQLeXpKBYW/mZhXeC/wqhCi6Z18xpoQeEmS9sQ6bxZaGk445r0sYbxieSfXSkFB4aTw78hTne58px/wfs/o9F7xXpYwXmss61opJGUIiCCXsZ5NyrLWCgrLQZKk+4HtwLlCiBNreS2ZNSHw73EJ4zXFcq6VQnKEEEFJkg4hl7V+GhLu9IXIk8QrKLwjJLkQ/H3A3wPnCyG6/5bPWxMCv1RmlTDuZaaEMQBCCMXyOgFJkszIs2OZgTRJkjbF3rIJIVLPjnxqcA/wn5IkHUSeUvKbyOmAvzqpZ7UCkSQpH7mqbJzq2L00IoToO0mntVL5d+SpSz8FjM8qrz4qhJha7oedUmmSsXS/pA+gEGLpU9SfIkiS9AjwpSRvbRNCvPT+ns3KQ5Kka4CbkDtW3wKuFUIcOLlntfKQJOl8ZK/5RP5TCHHF+3s2K5tYGmkyrhRCPLLszzuVBF5BQUHhVGJNZNEoKCgoKMxHEXgFBQWFNYoi8AoKCgprFEXgFRQUFNYoisArKCgorFEUgVdQUFBYoygCr6CgoLBGUQReQUFBYY2iCLyCgoLCGkUReAUFBYU1iiLwCgoKCmsUReAVFBQU1ij/H80WAB3CqWZZAAAAAElFTkSuQmCC\n",
39 | "text/plain": [
40 | ""
41 | ]
42 | },
43 | "metadata": {},
44 | "output_type": "display_data"
45 | }
46 | ],
47 | "source": [
48 | "def plot_flow(psi, mask = None,\n",
49 | " x=np.linspace(-2,2,25), y=np.linspace(-2,2,25), h=1e-6):\n",
50 | " \"\"\"\n",
51 | " Plot a potential flow using the streamfunction. The velocity\n",
52 | " vectors are approximated using finite differences.\n",
53 | " \n",
54 | " Inputs:\n",
55 | " - psi: stream function of x,y\n",
56 | " - mask: bool function of x,y for locations to be masked out\n",
57 | " - x,y: 1D spacing array in x,y. defaults to np.linspace(-2,2,25)\n",
58 | " - h: finite different spacing to compute U,V. defaults to 1e-6\n",
59 | " \"\"\"\n",
60 | " X,Y = np.meshgrid(x,y)\n",
61 | " if mask:\n",
62 | " X,Y = (np.ma.masked_where(mask(X,Y),X), \n",
63 | " np.ma.masked_where(mask(X,Y),Y))\n",
64 | " U = (psi(X,Y+h)-psi(X,Y-h))/(2*h)\n",
65 | " V = -(psi(X+h,Y)-psi(X-h,Y))/(2*h)\n",
66 | " \n",
67 | " plt.figure(figsize=(4,4),dpi=100) # make the plot\n",
68 | " plt.quiver(X,Y,U,V)\n",
69 | " plt.contour(X,Y,psi(X,Y),levels=20)\n",
70 | " plt.axis('equal')\n",
71 | " \n",
72 | "uniform = lambda x,y: y\n",
73 | "corner = lambda x,y: x*y\n",
74 | "circle = lambda x,y: y-y/(x**2+y**2)\n",
75 | "circle_mask = lambda x,y: x**2+y**2<0.8\n",
76 | "\n",
77 | "plot_flow(circle,circle_mask)"
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": 3,
83 | "metadata": {},
84 | "outputs": [
85 | {
86 | "name": "stdout",
87 | "output_type": "stream",
88 | "text": [
89 | "Help on function plot_flow in module __main__:\n",
90 | "\n",
91 | "plot_flow(psi, mask=None, x=array([-2. , -1.83333333, -1.66666667, -1.5 , -1.33333333,\n",
92 | " -1.16666667, -1. , -0.83333333, -0.66666667, -0.5 ,\n",
93 | " -0.33333333, -0.16666667, 0. , 0.16666667, 0.33333333,\n",
94 | " 0.5 , 0.66666667, 0.83333333, 1. , 1.16666667,\n",
95 | " 1.33333333, 1.5 , 1.66666667, 1.83333333, 2. ]), y=array([-2. , -1.83333333, -1.66666667, -1.5 , -1.33333333,\n",
96 | " -1.16666667, -1. , -0.83333333, -0.66666667, -0.5 ,\n",
97 | " -0.33333333, -0.16666667, 0. , 0.16666667, 0.33333333,\n",
98 | " 0.5 , 0.66666667, 0.83333333, 1. , 1.16666667,\n",
99 | " 1.33333333, 1.5 , 1.66666667, 1.83333333, 2. ]), h=1e-06)\n",
100 | " Plot a potential flow using the streamfunction. The velocity\n",
101 | " vectors are approximated using finite differences.\n",
102 | " \n",
103 | " Inputs:\n",
104 | " - psi: stream function of x,y\n",
105 | " - mask: bool function of x,y for locations to be masked out\n",
106 | " - x,y: 1D spacing array in x,y. defaults to np.linspace(-2,2,25)\n",
107 | " - h: finite different spacing to compute U,V. defaults to 1e-6\n",
108 | "\n"
109 | ]
110 | }
111 | ],
112 | "source": [
113 | "help(plot_flow)"
114 | ]
115 | },
116 | {
117 | "cell_type": "code",
118 | "execution_count": null,
119 | "metadata": {},
120 | "outputs": [],
121 | "source": []
122 | }
123 | ],
124 | "metadata": {
125 | "kernelspec": {
126 | "display_name": "Python 3",
127 | "language": "python",
128 | "name": "python3"
129 | },
130 | "language_info": {
131 | "codemirror_mode": {
132 | "name": "ipython",
133 | "version": 3
134 | },
135 | "file_extension": ".py",
136 | "mimetype": "text/x-python",
137 | "name": "python",
138 | "nbconvert_exporter": "python",
139 | "pygments_lexer": "ipython3",
140 | "version": "3.6.0"
141 | }
142 | },
143 | "nbformat": 4,
144 | "nbformat_minor": 4
145 | }
146 |
--------------------------------------------------------------------------------
/05SciPy.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | ""
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "# Scientific Python\n",
15 | "\n",
16 | "We've now had a few good examples of using NumPy for engineering computing and PyPlot for visualization. However, we haven't had much exposure to classic *numerical methods*. That's because this isn't a numerical methods class, it is a Python programming tutorial. However, there are some important aspect of programming which come up in using numerical methods. \n",
17 | "\n",
18 | "First and foremost is **don't reinvent the wheel**. When your focus is solving an engineering problem, you should not code your own numerical methods. Instead you should use methods which have been carefully implemented and tested already - letting you focus on your own work. Luckily the *Scientific Python* or [SciPy library](https://www.scipy.org/scipylib/index.html) has hundred of numerical methods for common mathematical and scientific problems such as:\n",
19 | "\n",
20 | "| Category | Sub module | Description |\n",
21 | "|-------------------|-------------------|--------------------------------------------------------|\n",
22 | "| Interpolation | scipy.interpolate | Numerical interpolation of 1D and multivariate data |\n",
23 | "| Optimization | scipy.optimize | Function optimization, curve fitting, and root finding |\n",
24 | "| Integration | scipy.integrate | Numerical integration quadratures and ODE integrators |\n",
25 | "| Signal processing | scipy.signal | Signal processing methods |\n",
26 | "| Special functions | scipy.special | Defines transcendental functions such as $J_n$ and $\\Gamma$|\n",
27 | "\n",
28 | "\n",
29 | "In this notebook, we will illustrate the use of SciPy with a few engineering applications to demonstrate a few more important programming issues. We won't attempt to go through all of the important numerical methods in SciPy - for that you can read the [SciPy book](http://scipy-lectures.org/intro/scipy.html)."
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "---\n",
37 | "\n",
38 | "## Ordinary Differential Equations\n",
39 | "\n",
40 | "Ordinary Differential Equations (ODEs) are ubiquitous in engineering and dynamics, and numerical methods are excellent at producing high-quality approximate solutions to ODEs that can't be solved analytically. \n",
41 | "\n",
42 | "As a warm up, the function $y=e^{t}$ is an exact solution of the initial value problem (IVP)\n",
43 | "\n",
44 | "$$ \\frac{dy}{dt} = y \\quad\\text{with}\\quad y(0) = 1 $$\n",
45 | "\n",
46 | "SciPy has a few functions to solve IVPs, but I like `solve_ivp` the best. Let's check it out."
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": null,
52 | "metadata": {},
53 | "outputs": [],
54 | "source": [
55 | "import numpy as np\n",
56 | "import matplotlib.pyplot as plt\n",
57 | "from scipy.integrate import solve_ivp\n",
58 | "\n",
59 | "?solve_ivp"
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {},
65 | "source": [
66 | "So the first argument is the ODE function itself `func=dy/dt`, then the span over which we want to integrate, and then the initial condition. Let's try it."
67 | ]
68 | },
69 | {
70 | "cell_type": "code",
71 | "execution_count": null,
72 | "metadata": {},
73 | "outputs": [],
74 | "source": [
75 | "fun = lambda t,y: y # lambda function syntax\n",
76 | "y0 = [1]\n",
77 | "t_span = [0,2]\n",
78 | "sol = solve_ivp(fun, t_span, y0)\n",
79 | "sol"
80 | ]
81 | },
82 | {
83 | "cell_type": "markdown",
84 | "metadata": {},
85 | "source": [
86 | "So the function outputs a bunch of useful information about what happened. Also note the times is stored in a 1D array `sol.t` and the solution is stored in a 2D array (more on that later). Let's plot this up."
87 | ]
88 | },
89 | {
90 | "cell_type": "code",
91 | "execution_count": null,
92 | "metadata": {},
93 | "outputs": [],
94 | "source": [
95 | "t = np.linspace(0,2,21)\n",
96 | "plt.plot(t,np.exp(t),label='exact')\n",
97 | "# sol = solve_ivp(fun, t_span = [0,2] , y0 = y0, t_eval = t) # distributed points for plot\n",
98 | "plt.plot(sol.t,sol.y[0],'ko',label='solve_ivp')\n",
99 | "plt.xlabel('t')\n",
100 | "plt.ylabel('y',rotation=0)\n",
101 | "plt.legend();"
102 | ]
103 | },
104 | {
105 | "cell_type": "markdown",
106 | "metadata": {},
107 | "source": [
108 | "First off, the numerical method matches the exact solution extremely well. But this plot seems a little weird. The solver used a small time step at first (`t[1]-t[0]=0.1`) and then took bigger steps (`t[3]-t[2]=0.99`). This is because the solver uses an [adaptive 4th order Runge-Kutta method](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta%E2%80%93Fehlberg_method) to integrate by default, which adjusts the time step to get the highest accuracy for the least number of function evaluations.\n",
109 | "\n",
110 | "That's great, but we want the results at a more regular interval for plotting, and the argument `t_eval` - do that by uncommenting the second line above. The result is evenly distributed and the accuracy is still excellent - it just took a few more evaluations."
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "metadata": {},
116 | "source": [
117 | "\n",
118 | "---\n",
119 | "\n",
120 | "That's nice, but most engineering systems are more complex than first order ODEs. For example, even a forced spring-mass-damper systems is second order:\n",
121 | "\n",
122 | "$$ m \\frac{d^2 x}{dt^2} + c \\frac{dx}{dt} + k x = f(t) $$ \n",
123 | "\n",
124 | "But it is actually very simple to deal with this additional derivative, we just define the position and velocity as two separate variables, the *states* of the oscillator:\n",
125 | "\n",
126 | "$$ y = \\left[x,\\ \\frac{dx}{dt}\\right] $$\n",
127 | "\n",
128 | "And therefore\n",
129 | "\n",
130 | "$$ \\frac{dy}{dt} = \\left[ \\frac{dx}{dt},\\ \\frac{d^2x}{dt^2}\\right] = \\left[y[1],\\ \\frac{f(t)-c y[1] - k y[0]}{m} \\right] $$ \n",
131 | "\n",
132 | "This trick can reduce any ODE of order `m` down to system of `m` states all governed by first order ODEs. `solve_ivp` assumes `y` is a 2D array of these states since it is the standard way to deal with dynamical systems. \n",
133 | "\n",
134 | "Let's try it on this example."
135 | ]
136 | },
137 | {
138 | "cell_type": "code",
139 | "execution_count": null,
140 | "metadata": {},
141 | "outputs": [],
142 | "source": [
143 | "# define forcing, mass-damping-stiffness, and ODE\n",
144 | "f = lambda t: np.sin(2*np.pi*t)\n",
145 | "m,c,k = 1,0.5,(2*np.pi)**2\n",
146 | "linear = lambda t,y: [y[1],(f(t)-c*y[1]-k*y[0])/m]\n",
147 | "\n",
148 | "t = np.linspace(40,42)\n",
149 | "y = solve_ivp(linear,[0,t[-1]],[0,0], t_eval=t).y\n",
150 | "\n",
151 | "plt.plot(t,y[0],label='$x$')\n",
152 | "plt.plot(t,y[1],label='$\\dot x$')\n",
153 | "plt.xlabel('t')\n",
154 | "plt.legend();"
155 | ]
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "metadata": {},
160 | "source": [
161 | "This gives a sinusoid, as expected but is it correct? Instead of using the exact solution (available in this case but not generally), let's *sanity check* the results based on physical understanding. **You should always do this when using numerical methods!**\n",
162 | "\n",
163 | " - If we could ignore dynamics, the expected deflection would simply be $x=f/k$. Since the magnitude of $f=1$ and $k=(2\\pi)^2$ this would mean we would have an amplitude of $x\\sim (2\\pi)^{-2} \\approx 0.025$. Instead we see an amplitude $x=0.4$! Is this reasonable??\n",
164 | " - The natural frequency given the parameters above is $\\omega_n = \\sqrt(k/m) = 2\\pi$. The force is *also* being applied at a frequency of $2\\pi$. This could explain the high amplitude - our spring-mass system is in resonance!\n",
165 | " \n",
166 | "Since we have an idea to explain our results - it is your turn to test it out:\n",
167 | " 1. Lower the forcing frequency x10. This should reduce the influence of dynamics and we should see amplitudes similar to our prediction. \n",
168 | " 2. Reset the frequency and increase the mass x10. Predict what this should do physically before running the simulation. Do the results match your predictions?\n",
169 | " \n",
170 | "\n",
171 | "Finally, one of the main advantages of the numerical approach to ODEs is that they extend trivially to nonlinear equations. For example, using a nonlinear damping $c\\dot x \\rightarrow d \\dot x|\\dot x|$ makes the dynamics difficult to solve analytically, but requires no change to our approach, only an updated ODE:"
172 | ]
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": null,
177 | "metadata": {},
178 | "outputs": [],
179 | "source": [
180 | "# define nonlinear damped ODE\n",
181 | "d = 100\n",
182 | "nonlinear = lambda t,y: [y[1],(f(t)-d*y[1]*abs(y[1])-k*y[0])/m]\n",
183 | "\n",
184 | "t = np.linspace(40,42)\n",
185 | "y = solve_ivp(nonlinear,[0,t[-1]],[0,0], t_eval=t).y\n",
186 | "\n",
187 | "plt.plot(t,y[0],label='$x$')\n",
188 | "plt.plot(t,y[1],label='$\\dot x$')\n",
189 | "plt.xlabel('t')\n",
190 | "plt.legend();"
191 | ]
192 | },
193 | {
194 | "cell_type": "markdown",
195 | "metadata": {},
196 | "source": [
197 | "## Root finding and implicit equations\n",
198 | "\n",
199 | "Another ubiquitous problem in engineering is *root finding*; determining the arguments which make a function zero. As before, there are a few SciPy routines for this, but `fsolve` is a good general purpose choice. Let's check it out."
200 | ]
201 | },
202 | {
203 | "cell_type": "code",
204 | "execution_count": null,
205 | "metadata": {},
206 | "outputs": [],
207 | "source": [
208 | "from scipy.optimize import fsolve\n",
209 | "\n",
210 | "?fsolve"
211 | ]
212 | },
213 | {
214 | "cell_type": "markdown",
215 | "metadata": {},
216 | "source": [
217 | "So `fsolve` also takes a function as the first argument, and the second argument is the starting point `x0` of the search for the root. \n",
218 | "\n",
219 | "As before, let's start with a simple example, say $\\text{func}=x\\sin x$ which is zero at $x=n\\pi$ for $n=0,1,2,\\ldots$."
220 | ]
221 | },
222 | {
223 | "cell_type": "code",
224 | "execution_count": null,
225 | "metadata": {},
226 | "outputs": [],
227 | "source": [
228 | "func = lambda x: x*np.sin(x)\n",
229 | "\n",
230 | "for x0 in range(1,8,2):\n",
231 | " print('x0={}, root={:.2f}'.format(x0,fsolve(func,x0)[0]))"
232 | ]
233 | },
234 | {
235 | "cell_type": "markdown",
236 | "metadata": {},
237 | "source": [
238 | "This example shows that a root finding method needs to be used with care when there is more than one root. Here we get different answers depending on `x0` and it's sometimes surprising; `x0=5` found the root at $5\\pi$ instead of $2\\pi$. Something to keep in mind.\n",
239 | "\n",
240 | "Root finding methods are especially useful for dealing with implicit equations. For example, the velocity of fluid through a pipe depends on the fluid friction, but this friction is itself a function of the flow velocity. The [semi-emperical equation](https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae#Colebrook%E2%80%93White_equation) for the Darcy friction factor $f$ is\n",
241 | "\n",
242 | "$$ \\frac 1 {\\sqrt f} = -2\\log_{10}\\left(\\frac \\epsilon{3.7 D}+ \\frac{2.51}{Re \\sqrt f} \\right)$$ \n",
243 | "\n",
244 | "where $\\epsilon/D$ is the pipe wall roughness to diameter ratio, $Re=UD/\\nu$ is the diameter-based Reynolds number, and the coefficients are determined from experimental tests. \n",
245 | "\n",
246 | "Directly solving this equation for $f$ is difficult, and engineers use charts like the [Moody Diagram](https://en.wikipedia.org/wiki/Moody_chart#/media/File:Moody_EN.svg) instead. But this is simple to solve with a root finding method; we just need to express this as function which is zero at the solution and this is always possible by simply subtracting the right-hand-side from the left!\n",
247 | "\n",
248 | "$$ \\text{func} = \\frac 1 {\\sqrt f} + 2\\log_{10}\\left(\\frac \\epsilon{3.7 D}+ \\frac{2.51}{Re \\sqrt f} \\right)$$ \n",
249 | "\n",
250 | "which is zero when $f$ satisfies our original equation."
251 | ]
252 | },
253 | {
254 | "cell_type": "code",
255 | "execution_count": null,
256 | "metadata": {},
257 | "outputs": [],
258 | "source": [
259 | "# @np.vectorize\n",
260 | "def darcy(Re,eps_D,f0=0.03):\n",
261 | " func = lambda f: 1/np.sqrt(f)+2*np.log10(eps_D/3.7+2.51/(Re*np.sqrt(f)))\n",
262 | " return fsolve(func,f0)[0]\n",
263 | "\n",
264 | "darcy(1e6,1e-3)"
265 | ]
266 | },
267 | {
268 | "cell_type": "markdown",
269 | "metadata": {},
270 | "source": [
271 | "Notice we have defined one function *inside* another. This lets us define $Re$ and $\\epsilon/D$ as *arguments* of `darcy`, while being *constants* in `func`. There are other ways to parameterize rooting finding, but I like this approach because the result is a function (like `darcy`) which behaves exactly like an explicit function (in this case, for $f$). \n",
272 | "\n",
273 | "This matches the results in the Moody Diagram, and in fact, we should be able to make our own version of the diagram to test it out fully:"
274 | ]
275 | },
276 | {
277 | "cell_type": "code",
278 | "execution_count": null,
279 | "metadata": {},
280 | "outputs": [],
281 | "source": [
282 | "Re = np.logspace(3.5,8)\n",
283 | "for i,eps_D in enumerate(np.logspace(-3,-1.5,7)):\n",
284 | " f = darcy(Re,eps_D)\n",
285 | " plt.loglog(Re,f, label='{:.1g}'.format(eps_D), color=plt.cm.cool(i/7))\n",
286 | "plt.xlabel('Re')\n",
287 | "plt.ylabel('f',rotation=0)\n",
288 | "plt.legend(title='$\\epsilon/D$',loc='upper right');"
289 | ]
290 | },
291 | {
292 | "cell_type": "markdown",
293 | "metadata": {},
294 | "source": [
295 | "Uh oh - this didn't work. Remember how functions such as `np.sin` *broadcast* the function across an array of arguments by default. Well, `fsolve` doesn't broadcast by default, so we need to do it ourselves. \n",
296 | "\n",
297 | "Luckily, this is trivial using [decorators](https://docs.python.org/3/library/functools.html). Decorators are a neat python feature which lets you add capabilities to a function without coding them yourself. There are tons of useful examples (like adding a `@cache` to avoid repeating expensive calculations) but the one we need is `@np.vectorize`. Uncomment that line above the function definition and run that block again - you should see that the output is now an array. Now try running the second code cell and you should see our version of the Moody Diagram. \n",
298 | "\n",
299 | "Notice I've used `np.logspace` to get logarithmically spaced points, `plt.loglog` to make a plot with log axis in both x and y, and `plt.cm.cool` to use a [sequential color palette](https://medium.com/nightingale/how-to-choose-the-colors-for-your-data-visualizations-50b2557fa335) instead of the PyPlot default. Use the help features to look up these functions for details.\n",
300 | "\n",
301 | "Your turn:\n",
302 | " 1. Write a function to solve the equation $r^{4}-2r^{2}\\cos 2\\theta = b^{4}-1$ for $r$. Test that your function gives $r=\\sqrt{2}$ when $b=1$ and $\\theta=0$.\n",
303 | " 2. Reproduce a plot of the [Cassini ovals](https://en.wikipedia.org/wiki/Cassini_oval) using this function for $1\\le b \\le 2$. Why doesn't your function work for $b<1$?\n",
304 | " \n",
305 | "*Hint:* Define `theta=np.linspace(0,2*np.pi)`, use `@np.vectorize`, and use `plt.polar` or convert $r,\\theta \\rightarrow x,y$ using the method in [notebook 3](https://github.com/weymouth/NumericalPython/blob/main/03NumpyAndPlotting.ipynb)."
306 | ]
307 | },
308 | {
309 | "attachments": {
310 | "Blasius1.png": {
311 | "image/png": "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"
312 | }
313 | },
314 | "cell_type": "markdown",
315 | "metadata": {},
316 | "source": [
317 | "## Blasius boundary layer\n",
318 | "\n",
319 | "As a last example, I want to show how you can **combine** these two techniques to solves a truly hard engineering equation with just a couple lines of code. Dividing complex problems down to pieces that you can solve with simple methods and combining them back together to obtain the solution is the secret sauce of programming and well worth learning. \n",
320 | "\n",
321 | "The governing equations for viscous fluids are very difficult to deal with, both [mathematically](https://www.claymath.org/millennium-problems/navier%E2%80%93stokes-equation) and [numerically](https://en.wikipedia.org/wiki/Turbulence_modeling). But these equations can be simplified in the case of a laminar flow along a flat plate. In this case we expect the velocity $u=0$ on the plate because of friction, but then to rapidly increase up to an asymptotic value $u\\rightarrow U$. \n",
322 | "\n",
323 | "\n",
324 | "\n",
325 | "This thin region of slowed down flow is called the boundary layer and we want to predict the shape of the *velocity profile* in this region. The [Blasius equation](https://en.wikipedia.org/wiki/Blasius_boundary_layer) governs this shape:\n",
326 | "\n",
327 | "$$ A'''+\\frac{1}{2} A A'' = 0 $$\n",
328 | "\n",
329 | "where $A'(z) = u/U$ is the scaled velocity function and $z$ is the scaled distance from the wall. The function $A$ has the boundary conditions\n",
330 | "\n",
331 | "$$ A(0) = A'(0) = 0 \\quad\\text{and}\\quad A'(\\infty) = 1 $$\n",
332 | "\n",
333 | "This equation is still too complex to solve analytically, and it might look too hard numerically as well. But we just need to take it one step at a time.\n",
334 | "\n",
335 | "### Step 1:\n",
336 | "\n",
337 | "We can reduce the Blasius equation to a first order ODE as before by defining \n",
338 | "\n",
339 | "$$ y = \\left[A,\\ A',\\ A'' \\right],\\quad y' = \\left[y[1],\\ y[2],\\ -\\frac{1}{2} y[0]y[2] \\right] $$\n",
340 | "\n",
341 | "Notice `y[1]`=$u/U$ is our goal, the velocity profile.\n",
342 | "\n",
343 | "But to use `solve_ivp` we also need our initial conditions. We don't know $A''(0)=$`C0`, but *if we did* the initial condition would be `y0 = [0,0,C0]` and we could solve for the profile:"
344 | ]
345 | },
346 | {
347 | "cell_type": "code",
348 | "execution_count": null,
349 | "metadata": {},
350 | "outputs": [],
351 | "source": [
352 | "def blasius(t,C0):\n",
353 | " return solve_ivp(lambda t,y: [y[1],y[2],-0.5*y[0]*y[2]], \n",
354 | " [0,t[-1]], [0,0,C0], t_eval = t).y[1]\n",
355 | "\n",
356 | "C0 = 1 # guess\n",
357 | "# C0 = fsolve(lambda C0: blasius([12],C0)[-1]-1,x0=1)[0] # solve!\n",
358 | "\n",
359 | "z = np.linspace(0,6,31)\n",
360 | "plt.plot(blasius(z,C0),z)\n",
361 | "plt.xlabel('u/U')\n",
362 | "plt.ylabel('z',rotation=0);"
363 | ]
364 | },
365 | {
366 | "cell_type": "markdown",
367 | "metadata": {},
368 | "source": [
369 | "### Step 2\n",
370 | "\n",
371 | "We can determine `C0` using the addition boundary condition, $A'(\\infty)=1$. It is hard to deal with infinity numerically, but we see in the plot above that the profile is pretty much constant for z>4 anyway, so we'll just apply this condition to the last point, ie `blasius(C0)[-1]=1`. This is an implicit equation for `C0`, and we can solve it using `fsolve` as we did above: we simply substract the right-hand-side and define `func = blasius(C0)[-1]-1` which is zero when `C0` satisfies the boundary condition. Uncomment the line in the code block above to check that it works.\n",
372 | "\n",
373 | "The value of `C0` is actually physically important as well - it's related to the friction coefficient, and we have that value \n",
374 | "as well:"
375 | ]
376 | },
377 | {
378 | "cell_type": "code",
379 | "execution_count": null,
380 | "metadata": {},
381 | "outputs": [],
382 | "source": [
383 | "print(\"Blasius C_F sqrt(Re) = {:.3f}\".format(4*C0))"
384 | ]
385 | },
386 | {
387 | "cell_type": "markdown",
388 | "metadata": {},
389 | "source": [
390 | "So $C_F = 1.328/\\sqrt{Re}$ for a laminar boundary layer. \n",
391 | "\n",
392 | "And just like that, we're done. We've numerically solved the Blasius equation in around two lines of code; determining one of the very few exact solutions for nonlinear flows in engineering and come up with a practical friction coefficient that we can use to determine the drag on immersed bodies. Not too shabby."
393 | ]
394 | }
395 | ],
396 | "metadata": {
397 | "kernelspec": {
398 | "display_name": "Python 3",
399 | "language": "python",
400 | "name": "python3"
401 | },
402 | "language_info": {
403 | "codemirror_mode": {
404 | "name": "ipython",
405 | "version": 3
406 | },
407 | "file_extension": ".py",
408 | "mimetype": "text/x-python",
409 | "name": "python",
410 | "nbconvert_exporter": "python",
411 | "pygments_lexer": "ipython3",
412 | "version": "3.6.10"
413 | }
414 | },
415 | "nbformat": 4,
416 | "nbformat_minor": 4
417 | }
418 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Dr Weymouth
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introduction to Numerical Python for Engineers
2 |
3 | **Hello**! Numerical programming is becoming a key skill for modern engineers and you will need to use Python in some of your modules for labs and coursework. This is a quick introduction to numerical programming in [Python](https://www.python.org/) using a set of interactive [Jupyter](https://jupyter.org/) notebooks.
4 | 1. [Python Basics](https://colab.research.google.com/github/weymouth/NumericalPython/blob/main/01PythonBasics.ipynb)
5 | 1. [Conditionals and Lists](https://colab.research.google.com/github/weymouth/NumericalPython/blob/main/02ConditionalsAndLists.ipynb)
6 | 1. [Numerical Python and Plotting](https://colab.research.google.com/github/weymouth/NumericalPython/blob/main/03NumpyAndPlotting.ipynb)
7 | 1. [Example: Potential flow plot](https://colab.research.google.com/github/weymouth/NumericalPython/blob/main/04FlowPlotExample.ipynb)
8 | 1. [Scientific Python library](https://colab.research.google.com/github/weymouth/NumericalPython/blob/main/05SciPy.ipynb)
9 |
10 | ## How to complete this introduction?
11 |
12 | You can use these notebooks in two ways:
13 | - You can run the notebooks on [google colab](https://research.google.com/colaboratory/faq.html). This is extremely convenient, requiring nothing but a Google account! Just click the links above to get started.
14 | - Alternatively, you can run the notebooks on your own machine. To do this, you need to download this github repository using the green `code` button above; for example as a [zip file](https://github.com/weymouth/NumericalPython/archive/main.zip) or using github desktop. If you haven't already, you should also [install Anaconda](https://docs.anaconda.com/anaconda/install/). This will install Python and the Jupyter notebook environment, letting you run the notebooks locally.
15 |
16 | I've also made a [playlist on youtube](https://youtube.com/playlist?list=PLQO9vKCRROdZkciP8FwS9SaQ1hlgy_wYt) going through the notebooks. **You won't learn much from watching the videos alone!** Make sure to complete the exercises by running the notebooks in one of the two ways described above.
17 |
18 | ## Why use programming for analysis?
19 |
20 | The programming approach is **highly** preferred to using spreadsheets (like `Excel`) which are [extremely dangerous to use for important work](https://www.forbes.com/sites/timworstall/2013/02/13/microsofts-excel-might-be-the-most-dangerous-software-on-the-planet/?sh=536d1fa0633d). Well documented spreadsheet errors have led to catastrophes in [business](https://www.marketwatch.com/story/88-of-spreadsheets-have-errors-2013-04-17), [economic policy](https://www.bloomberg.com/news/articles/2013-04-18/faq-reinhart-rogoff-and-the-excel-error-that-changed-history) and [health care](https://www.theguardian.com/politics/2020/oct/05/how-excel-may-have-caused-loss-of-16000-covid-tests-in-england). This is because:
21 | - Spreadsheets *hide their methodology* behind the data. This makes it difficult and error-prone to transfer methods to new data. In contrast, a program *is* the methodology, making testing and reproduction much easier.
22 | - Spreadsheets are not extensible. Their limited numerical methods make them inappropriate for any advanced engineering analysis.
23 |
24 | ## Why use Python?
25 |
26 | 
27 |
28 | Python has a few important advantages as a numerical programming language:
29 | - Python is in high demand. This means learning Python is a good way to improve your job prospects; particularly for engineering positions related to data-science and machine learning.
30 | - Python has the largest community of users and developers. [The figure above](https://insights.stackoverflow.com/trends) indicates that Python is around five times larger than any other programming language used for numerical work. This means Python is being continuously developed and tested, and it is very easy to get help.
31 | - It is open-source and free to use. This helps the community grow and also ensures future employers can use your skills - no need to pay for a Matlab license.
32 | - Python is easy to learn and the lessons are generally transferable to other languages. A basis in Python will help make other growing numerical languages like [Julia](https://julialang.org/) and [R](https://www.r-project.org/about.html) more approachable.
33 |
34 | ## What if I need more help?
35 |
36 | This introduction is meant to get you familiar with numerical Python as quickly as possible. It assumes very little knowledge of programming, but accelerates quickly up to fairly advanced examples. If you are a complete beginner or if you prefer a slower pace then you might try these additional resources:
37 | - https://www.udemy.com/course/python-for-absolute-beginners-u/
38 | - https://www.kaggle.com/learn/overview
39 | - https://docs.python.org/3/tutorial/
40 | - https://numpy.org/devdocs/user/quickstart.html
41 |
--------------------------------------------------------------------------------
/Scipy.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "IydfPV4sm89N"
7 | },
8 | "source": [
9 | ""
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {
15 | "id": "iWEVP8vUnfmv"
16 | },
17 | "source": [
18 | "# SciPy\n",
19 | "\n",
20 | "Congratulations! In the previous 3 notebooks we have covered the fundamentals of numerical computing with Python\n",
21 | " - Variables, operations and functions\n",
22 | " - Conditionals, lists and looping\n",
23 | " - Arrays, vector operations and plotting\n",
24 | "\n",
25 | "Plus some handy concepts like print formating, list comprehensions and lamda functions to keep your code tidy and efficient.\n",
26 | "\n",
27 | "For the final tutorial notebooks we will look at specialized Python libraries which are often useful in practise.\n",
28 | "These topics are covered in more detail in walk-throughs online, so we will only give an overview here and link to more complete tutorials. \n",
29 | "\n",
30 | "Typically, we only have access to the noisy measurements, not the true model. We can use the [curve_fit](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html) function to fit a `linear` model function to this data."
31 | ]
32 | },
33 | {
34 | "cell_type": "code",
35 | "execution_count": null,
36 | "metadata": {
37 | "colab": {
38 | "base_uri": "https://localhost:8080/"
39 | },
40 | "id": "vfbrXNqhmc6X",
41 | "outputId": "dfc313c8-f4ad-4e3c-e6d7-797c98c53c02"
42 | },
43 | "outputs": [],
44 | "source": [
45 | "from scipy.optimize import curve_fit\n",
46 | "def linear(x,m,b): return m*x+b\n",
47 | "params,_ = curve_fit(linear,data.time,data.y)\n",
48 | "params"
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": null,
54 | "metadata": {
55 | "colab": {
56 | "base_uri": "https://localhost:8080/",
57 | "height": 279
58 | },
59 | "id": "BJmbgRzUmc6Y",
60 | "outputId": "c076bb55-3e96-4f6a-9c50-d169891320a1"
61 | },
62 | "outputs": [],
63 | "source": [
64 | "data['y fit'] = linear(data.time,*params)\n",
65 | "data.plot(x='time');"
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {
71 | "id": "-GVqHcGzn-h3"
72 | },
73 | "source": [
74 | "Since the true model happend to be a linear model, the curve fit is nearly perfect! "
75 | ]
76 | },
77 | {
78 | "cell_type": "code",
79 | "execution_count": null,
80 | "metadata": {
81 | "id": "dfwGLtMfm7Up"
82 | },
83 | "outputs": [],
84 | "source": []
85 | }
86 | ],
87 | "metadata": {
88 | "colab": {
89 | "include_colab_link": true,
90 | "name": "DataFrameCurveFitExample.ipynb",
91 | "provenance": []
92 | },
93 | "kernelspec": {
94 | "display_name": "Python 3",
95 | "language": "python",
96 | "name": "python3"
97 | },
98 | "language_info": {
99 | "codemirror_mode": {
100 | "name": "ipython",
101 | "version": 3
102 | },
103 | "file_extension": ".py",
104 | "mimetype": "text/x-python",
105 | "name": "python",
106 | "nbconvert_exporter": "python",
107 | "pygments_lexer": "ipython3",
108 | "version": "3.6.0"
109 | }
110 | },
111 | "nbformat": 4,
112 | "nbformat_minor": 1
113 | }
114 |
--------------------------------------------------------------------------------
/SharedScreenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weymouth/NumericalPython/5f5086f0de5ef748e6f2b8646da3add0548f2a65/SharedScreenshot.jpg
--------------------------------------------------------------------------------
/TablesAnalysis.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "IydfPV4sm89N"
7 | },
8 | "source": [
9 | ""
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {
15 | "id": "IydfPV4sm89N"
16 | },
17 | "source": [
18 | "# Data Tables and Analysis in Pandas\n",
19 | "\n",
20 | "A common task in engineering is to perform analysis on field data, experimental data, or even simulation data. Wth the growth of Machine Learning techniques, this is more and more important to the state of the art, and the size of these data sets has grown larger and more complex. \n",
21 | "\n",
22 | "As a very simple example, consider this (simplified) table summarizing the vehicles used at the [National Oceanography Center](https://www.noc.ac.uk/facilities/national-marine-equipment-pool)\n",
23 | "\n",
24 | "| vehicle | count | speed (m/s) | size (m) | working fluid | flow type |\n",
25 | "|-------|----------|-------|------|---------------|-----------|\n",
26 | "| quad rotors | 4 | 18 | 0.06 | air | turbulent |\n",
27 | "| slocum gliders | 9 | 0.4 | 2 | water | transitional |\n",
28 | "| autosubs | 12 | 1.5 | 3 | water | turbulent\n",
29 | "| wave drones | 2 | 0.5 | 0.4 | water | laminar\n",
30 | "\n",
31 | "The data consists of labels, counts, floats and categories. Since every element in a NumPy array has to be the same data type, each column would need to be it's own array - which clashes with the natural grouping in terms of observations. \n",
32 | "\n",
33 | "The [Pandas](https://pandas.pydata.org/pandas-docs/stable/getting_started/index.html) library introduces a new data type called a [data frame](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html) to hold tables of data like this. "
34 | ]
35 | },
36 | {
37 | "cell_type": "code",
38 | "execution_count": null,
39 | "metadata": {
40 | "colab": {
41 | "base_uri": "https://localhost:8080/",
42 | "height": 204
43 | },
44 | "id": "bbCeS35Bmc6V",
45 | "outputId": "b0367351-42ba-487a-fccb-42c9bce7df9d"
46 | },
47 | "outputs": [],
48 | "source": [
49 | "import pandas as pd\n",
50 | "\n",
51 | "data = [['quad rotors', 4, 18, 0.06, 'air', 'turbulent'],\n",
52 | " ['slocum gliders', 9, 0.4, 2, 'water', 'transitional'],\n",
53 | " ['autosubs', 12, 1.5, 3, 'water', 'turbulent'],\n",
54 | " ['wave drones', 2, 0.5, 0.4, 'water', 'laminar']]\n",
55 | "names = ['vehicle', 'count', 'speed (m/s)', 'size (m)', 'working fluid', 'flow type']\n",
56 | "table = pd.DataFrame(data,columns = names)\n",
57 | "table"
58 | ]
59 | },
60 | {
61 | "cell_type": "markdown",
62 | "metadata": {},
63 | "source": [
64 | "The code above `import`ed Pandas using the nickname `np` and used the method `DataFrame` to convert the data (a list of lists) and the column names (a list of strings) into a table. \n",
65 | "\n",
66 | "** Important notes: ** \n",
67 | "1. In practical usage, you would not *create* the data, you would *read* it. The data with the header would already be stored in a spreadsheet or a csv file. Pandas is great at [reading in data](https://pandas.pydata.org/pandas-docs/stable/getting_started/intro_tutorials/02_read_write.html#min-tut-02-read-write) from sources like this.\n",
68 | "2. Such data would typically contain many thousands of observations. Keeping this data [tidy](https://vita.had.co.nz/papers/tidy-data.pdf) and developing a reliable analysis pipeline is where the advantages of the programming approach to data science are most obvious.\n",
69 | "\n",
70 | "Once we have the data table, Pandas has a ton of built-in statistical operations to perform on it. For example, we can take the standard deviation:"
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "execution_count": null,
76 | "metadata": {
77 | "colab": {
78 | "base_uri": "https://localhost:8080/",
79 | "height": 297
80 | },
81 | "id": "2-EahoVLmu95",
82 | "outputId": "5b8f96f0-758a-4bbc-8870-c7f748fb987a"
83 | },
84 | "outputs": [],
85 | "source": [
86 | "table.std()"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": [
93 | "This only works for columns with a numerical data type. We can loop through the columns and get a description of each using the `describe` function:"
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {
100 | "colab": {
101 | "base_uri": "https://localhost:8080/",
102 | "height": 279
103 | },
104 | "id": "lbSne63UmlWv",
105 | "outputId": "d96ac8f6-d7fd-42c2-abec-562635fcaafb"
106 | },
107 | "outputs": [],
108 | "source": [
109 | "for col in table:\n",
110 | " print(' ',col) # column name\n",
111 | " print(table[col].describe()) # descriptive stats\n",
112 | " print('---------')"
113 | ]
114 | },
115 | {
116 | "cell_type": "markdown",
117 | "metadata": {},
118 | "source": [
119 | "Since this table is so short, we could also loop through *rows* using the row's `index`. The [loc`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html) method lets us access the data similar to a numpy array."
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": null,
125 | "metadata": {},
126 | "outputs": [],
127 | "source": [
128 | "for i in table.index:\n",
129 | " print(table.loc[i])\n",
130 | " print('--------')"
131 | ]
132 | },
133 | {
134 | "cell_type": "markdown",
135 | "metadata": {},
136 | "source": [
137 | "We can generate plots very easily from the table. The `plot` function is a wrapper around `plt.plot()` and adds a legend with the column names automatically."
138 | ]
139 | },
140 | {
141 | "cell_type": "code",
142 | "execution_count": null,
143 | "metadata": {},
144 | "outputs": [],
145 | "source": [
146 | "table.plot(xlabel='row');"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {},
152 | "source": [
153 | "## Modifying the table\n",
154 | "\n",
155 | "Let's quickly show how to add rows and columns to the table. First, add a row for the NOC's research ships by selecting an index which isn't filled yet:"
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": null,
161 | "metadata": {},
162 | "outputs": [],
163 | "source": [
164 | "table.loc[4] = ['ships',2,8,100,'water','turbulent']\n",
165 | "table"
166 | ]
167 | },
168 | {
169 | "cell_type": "markdown",
170 | "metadata": {},
171 | "source": [
172 | "Note that we can overwrite a row this way as well.\n",
173 | "\n",
174 | "Lets add a Reynolds number $Re$ column to our table since it governs the transition from laminar to turbulent flow. Typically $Re$ 500k-1M marks the transition from laminar to turbulent, where $Re$ is defined as $\\text{speed} * \\text{size } / \\text{ kinematic viscosity}$ and the [kinematic viscosity](https://en.wikipedia.org/wiki/Viscosity) of water is $\\nu\\approx 10^{-6}~m^2/s$ and air is $\\nu\\approx 15\\times 10^{-6}~m^2/s$. \n",
175 | "\n",
176 | "We can use the NumPy [where](https://numpy.org/doc/stable/reference/generated/numpy.where.html) function to set the viscosity depending on the fluid and then apply the formula to get the Reynolds number:"
177 | ]
178 | },
179 | {
180 | "cell_type": "code",
181 | "execution_count": null,
182 | "metadata": {},
183 | "outputs": [],
184 | "source": [
185 | "import numpy as np\n",
186 | "table['kin visc (m*m/s)'] = np.where(table['working fluid']=='water', 1e-6, 15e-6)\n",
187 | "table['Re'] = table['speed (m/s)']*table['size (m)']/table['kin visc (m*m/s)']\n",
188 | "table"
189 | ]
190 | },
191 | {
192 | "cell_type": "markdown",
193 | "metadata": {},
194 | "source": [
195 | "Notice that the $Re$ predicts the flow type nicely for all the vehicles except the quad rotor. The jets from the rotor are much faster and more turbulent than the vehicle speed implies, leading to the disparity. "
196 | ]
197 | },
198 | {
199 | "cell_type": "markdown",
200 | "metadata": {},
201 | "source": [
202 | "## Further reading and extensions\n",
203 | "\n",
204 | "For better or worse, we've barely scratched the surface of what is possible with Pandas. The [Pandas documentation website](https://pandas.pydata.org/pandas-docs/stable/index.html) is the best reference and it even has a whole list of more complete [community tutorials](https://pandas.pydata.org/pandas-docs/stable/getting_started/tutorials.html). **Overall** I think Pandas is great for keeping tables of heterogenous data organized, but it is a bit awkward and verbose to work with, and uses a lot of specialized syntax. If you can get away with a multi-dimensional array or two, you may find those easier to work with. \n",
205 | "\n",
206 | "Once you have your data in a DataFrame or an array, the next step is likely to be more advanced analysis. Here are a list of potential applications along with librabries which might help\n",
207 | " - Perform an FFT of a measured signal to determine its fundamental frequencies: [numpy.fft](https://numpy.org/doc/stable/reference/routines.fft.html)\n",
208 | " - Filter a signal or perform other signal processing: [scipy.signal](https://docs.scipy.org/doc/scipy/reference/signal.html)\n",
209 | " - Build a covariance table from an exterimental test matrix to identify the key parameters: [stats models](https://www.statsmodels.org/stable/index.html)\n",
210 | " - Fit a linear model to data and estimate the confidence on the model parameters: [stats models](https://www.statsmodels.org/stable/index.html)\n",
211 | " - Cluster a set of unlabeled experimental data into groups: [scikit-learn](https://scikit-learn.org/)\n",
212 | " - Fit a nonlinear regression model to data, controlling for model complexity: [scikit-learn](https://scikit-learn.org/)\n",
213 | " - Use a deep convolutional neural network to identify key features of seabed maps and images: [tensorflow](https://www.tensorflow.org/)\n",
214 | " - Use a deep autoencoder and automatic differentiation model turbulent flow: [tensorflow](https://www.tensorflow.org/)\n",
215 | "\n",
216 | "This list builds from commonplace to cutting edge research, and it's really amazing that Python can be used across the whole range. \n",
217 | "\n",
218 | "**Again, congratulations on completing this introduction to numerical python. Good luck in your studies and your engineering applications in the future!**"
219 | ]
220 | },
221 | {
222 | "cell_type": "code",
223 | "execution_count": null,
224 | "metadata": {},
225 | "outputs": [],
226 | "source": []
227 | }
228 | ],
229 | "metadata": {
230 | "colab": {
231 | "include_colab_link": true,
232 | "name": "DataFrameCurveFitExample.ipynb",
233 | "provenance": []
234 | },
235 | "kernelspec": {
236 | "display_name": "Python 3",
237 | "language": "python",
238 | "name": "python3"
239 | },
240 | "language_info": {
241 | "codemirror_mode": {
242 | "name": "ipython",
243 | "version": 3
244 | },
245 | "file_extension": ".py",
246 | "mimetype": "text/x-python",
247 | "name": "python",
248 | "nbconvert_exporter": "python",
249 | "pygments_lexer": "ipython3",
250 | "version": "3.6.0"
251 | }
252 | },
253 | "nbformat": 4,
254 | "nbformat_minor": 1
255 | }
256 |
--------------------------------------------------------------------------------